diff options
Diffstat (limited to 'drivers/media/video/gspca/sonixb.c')
-rw-r--r-- | drivers/media/video/gspca/sonixb.c | 484 |
1 files changed, 129 insertions, 355 deletions
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index dbeebe8625c5..e18748c5a14d 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c | |||
@@ -24,9 +24,6 @@ | |||
24 | 24 | ||
25 | #include "gspca.h" | 25 | #include "gspca.h" |
26 | 26 | ||
27 | #define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 8) | ||
28 | static const char version[] = "2.1.8"; | ||
29 | |||
30 | MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); | 27 | MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); |
31 | MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); | 28 | MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); |
32 | MODULE_LICENSE("GPL"); | 29 | MODULE_LICENSE("GPL"); |
@@ -44,25 +41,29 @@ struct sd { | |||
44 | unsigned char brightness; | 41 | unsigned char brightness; |
45 | unsigned char autogain; | 42 | unsigned char autogain; |
46 | unsigned char autogain_ignore_frames; | 43 | unsigned char autogain_ignore_frames; |
44 | unsigned char frames_to_drop; | ||
47 | unsigned char freq; /* light freq filter setting */ | 45 | unsigned char freq; /* light freq filter setting */ |
48 | unsigned char saturation; | ||
49 | unsigned char hue; | ||
50 | unsigned char contrast; | ||
51 | 46 | ||
52 | unsigned char fr_h_sz; /* size of frame header */ | 47 | unsigned char fr_h_sz; /* size of frame header */ |
53 | char sensor; /* Type of image sensor chip */ | 48 | char sensor; /* Type of image sensor chip */ |
54 | #define SENSOR_HV7131R 0 | 49 | #define SENSOR_HV7131R 0 |
55 | #define SENSOR_OV6650 1 | 50 | #define SENSOR_OV6650 1 |
56 | #define SENSOR_OV7630 2 | 51 | #define SENSOR_OV7630 2 |
57 | #define SENSOR_OV7630_3 3 | 52 | #define SENSOR_PAS106 3 |
58 | #define SENSOR_PAS106 4 | 53 | #define SENSOR_PAS202 4 |
59 | #define SENSOR_PAS202 5 | 54 | #define SENSOR_TAS5110 5 |
60 | #define SENSOR_TAS5110 6 | 55 | #define SENSOR_TAS5130CXX 6 |
61 | #define SENSOR_TAS5130CXX 7 | ||
62 | char sensor_has_gain; | 56 | char sensor_has_gain; |
63 | __u8 sensor_addr; | 57 | __u8 sensor_addr; |
58 | __u8 reg11; | ||
64 | }; | 59 | }; |
65 | 60 | ||
61 | /* flags used in the device id table */ | ||
62 | #define F_GAIN 0x01 /* has gain */ | ||
63 | #define F_AUTO 0x02 /* has autogain */ | ||
64 | #define F_SIF 0x04 /* sif or vga */ | ||
65 | #define F_H18 0x08 /* long (18 b) or short (12 b) frame header */ | ||
66 | |||
66 | #define COMP2 0x8f | 67 | #define COMP2 0x8f |
67 | #define COMP 0xc7 /* 0x87 //0x07 */ | 68 | #define COMP 0xc7 /* 0x87 //0x07 */ |
68 | #define COMP1 0xc9 /* 0x89 //0x09 */ | 69 | #define COMP1 0xc9 /* 0x89 //0x09 */ |
@@ -92,12 +93,6 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); | |||
92 | static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); | 93 | static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); |
93 | static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); | 94 | static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); |
94 | static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); | 95 | static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); |
95 | static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val); | ||
96 | static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val); | ||
97 | static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); | ||
98 | static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); | ||
99 | static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); | ||
100 | static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); | ||
101 | 96 | ||
102 | static struct ctrl sd_ctrls[] = { | 97 | static struct ctrl sd_ctrls[] = { |
103 | { | 98 | { |
@@ -174,48 +169,6 @@ static struct ctrl sd_ctrls[] = { | |||
174 | .set = sd_setfreq, | 169 | .set = sd_setfreq, |
175 | .get = sd_getfreq, | 170 | .get = sd_getfreq, |
176 | }, | 171 | }, |
177 | { | ||
178 | { | ||
179 | .id = V4L2_CID_SATURATION, | ||
180 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
181 | .name = "Saturation", | ||
182 | .minimum = 0, | ||
183 | .maximum = 255, | ||
184 | .step = 1, | ||
185 | #define SATURATION_DEF 127 | ||
186 | .default_value = SATURATION_DEF, | ||
187 | }, | ||
188 | .set = sd_setsaturation, | ||
189 | .get = sd_getsaturation, | ||
190 | }, | ||
191 | { | ||
192 | { | ||
193 | .id = V4L2_CID_HUE, | ||
194 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
195 | .name = "Hue", | ||
196 | .minimum = 0, | ||
197 | .maximum = 255, | ||
198 | .step = 1, | ||
199 | #define HUE_DEF 127 | ||
200 | .default_value = HUE_DEF, | ||
201 | }, | ||
202 | .set = sd_sethue, | ||
203 | .get = sd_gethue, | ||
204 | }, | ||
205 | { | ||
206 | { | ||
207 | .id = V4L2_CID_CONTRAST, | ||
208 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
209 | .name = "Contrast", | ||
210 | .minimum = 0, | ||
211 | .maximum = 255, | ||
212 | .step = 1, | ||
213 | #define CONTRAST_DEF 127 | ||
214 | .default_value = CONTRAST_DEF, | ||
215 | }, | ||
216 | .set = sd_setcontrast, | ||
217 | .get = sd_getcontrast, | ||
218 | }, | ||
219 | }; | 172 | }; |
220 | 173 | ||
221 | static struct v4l2_pix_format vga_mode[] = { | 174 | static struct v4l2_pix_format vga_mode[] = { |
@@ -248,8 +201,6 @@ static struct v4l2_pix_format sif_mode[] = { | |||
248 | .priv = 0}, | 201 | .priv = 0}, |
249 | }; | 202 | }; |
250 | 203 | ||
251 | static const __u8 probe_ov7630[] = {0x08, 0x44}; | ||
252 | |||
253 | static const __u8 initHv7131[] = { | 204 | static const __u8 initHv7131[] = { |
254 | 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, | 205 | 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, |
255 | 0x00, 0x00, | 206 | 0x00, 0x00, |
@@ -321,7 +272,7 @@ static const __u8 initOv7630_3[] = { | |||
321 | 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, /* r21 .. r28 */ | 272 | 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, /* r21 .. r28 */ |
322 | 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff /* r29 .. r30 */ | 273 | 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff /* r29 .. r30 */ |
323 | }; | 274 | }; |
324 | static const __u8 ov7630_sensor_init_com[][8] = { | 275 | static const __u8 ov7630_sensor_init[][8] = { |
325 | {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, | 276 | {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, |
326 | {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, | 277 | {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, |
327 | /* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ | 278 | /* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ |
@@ -342,17 +293,6 @@ static const __u8 ov7630_sensor_init_com[][8] = { | |||
342 | {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, | 293 | {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, |
343 | {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, | 294 | {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, |
344 | }; | 295 | }; |
345 | static const __u8 ov7630_sensor_init[][8] = { | ||
346 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 200ms */ | ||
347 | {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x10}, /* jfm */ | ||
348 | {0xa0, 0x21, 0x10, 0x57, 0xbd, 0x06, 0xf6, 0x16}, | ||
349 | {0xa0, 0x21, 0x76, 0x02, 0xbd, 0x06, 0xf6, 0x16}, | ||
350 | {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ | ||
351 | }; | ||
352 | static const __u8 ov7630_sensor_init_3[][8] = { | ||
353 | {0xa0, 0x21, 0x2a, 0xa0, 0x00, 0x00, 0x00, 0x10}, | ||
354 | {0xa0, 0x21, 0x2a, 0x80, 0x00, 0x00, 0x00, 0x10}, | ||
355 | }; | ||
356 | 296 | ||
357 | static const __u8 initPas106[] = { | 297 | static const __u8 initPas106[] = { |
358 | 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, | 298 | 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, |
@@ -542,7 +482,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) | |||
542 | 482 | ||
543 | switch (sd->sensor) { | 483 | switch (sd->sensor) { |
544 | case SENSOR_OV6650: | 484 | case SENSOR_OV6650: |
545 | case SENSOR_OV7630_3: | ||
546 | case SENSOR_OV7630: { | 485 | case SENSOR_OV7630: { |
547 | __u8 i2cOV[] = | 486 | __u8 i2cOV[] = |
548 | {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; | 487 | {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; |
@@ -635,7 +574,7 @@ static void setsensorgain(struct gspca_dev *gspca_dev) | |||
635 | case SENSOR_OV6650: | 574 | case SENSOR_OV6650: |
636 | gain >>= 1; | 575 | gain >>= 1; |
637 | /* fall thru */ | 576 | /* fall thru */ |
638 | case SENSOR_OV7630_3: { | 577 | case SENSOR_OV7630: { |
639 | __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; | 578 | __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; |
640 | 579 | ||
641 | i2c[1] = sd->sensor_addr; | 580 | i2c[1] = sd->sensor_addr; |
@@ -690,7 +629,7 @@ static void setexposure(struct gspca_dev *gspca_dev) | |||
690 | break; | 629 | break; |
691 | } | 630 | } |
692 | case SENSOR_OV6650: | 631 | case SENSOR_OV6650: |
693 | case SENSOR_OV7630_3: { | 632 | case SENSOR_OV7630: { |
694 | /* The ov6650 / ov7630 have 2 registers which both influence | 633 | /* The ov6650 / ov7630 have 2 registers which both influence |
695 | exposure, register 11, whose low nibble sets the nr off fps | 634 | exposure, register 11, whose low nibble sets the nr off fps |
696 | according to: fps = 30 / (low_nibble + 1) | 635 | according to: fps = 30 / (low_nibble + 1) |
@@ -705,16 +644,20 @@ static void setexposure(struct gspca_dev *gspca_dev) | |||
705 | The code maps our 0 - 510 ms exposure ctrl to these 2 | 644 | The code maps our 0 - 510 ms exposure ctrl to these 2 |
706 | registers, trying to keep fps as high as possible. | 645 | registers, trying to keep fps as high as possible. |
707 | */ | 646 | */ |
708 | __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0xc0, 0x00, 0x00, 0x10}; | 647 | __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}; |
709 | int reg10, reg11; | 648 | int reg10, reg11, reg10_max; |
649 | |||
710 | /* ov6645 datasheet says reg10_max is 9a, but that uses | 650 | /* ov6645 datasheet says reg10_max is 9a, but that uses |
711 | tline * 2 * reg10 as formula for calculating texpo, the | 651 | tline * 2 * reg10 as formula for calculating texpo, the |
712 | ov6650 probably uses the same formula as the 7730 which uses | 652 | ov6650 probably uses the same formula as the 7730 which uses |
713 | tline * 4 * reg10, which explains why the reg10max we've | 653 | tline * 4 * reg10, which explains why the reg10max we've |
714 | found experimentally for the ov6650 is exactly half that of | 654 | found experimentally for the ov6650 is exactly half that of |
715 | the ov6645. The ov7630 datasheet says the max is 0x41. */ | 655 | the ov6645. The ov7630 datasheet says the max is 0x41. */ |
716 | const int reg10_max = (sd->sensor == SENSOR_OV6650) | 656 | if (sd->sensor == SENSOR_OV6650) { |
717 | ? 0x4d : 0x41; | 657 | reg10_max = 0x4d; |
658 | i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */ | ||
659 | } else | ||
660 | reg10_max = 0x41; | ||
718 | 661 | ||
719 | reg11 = (60 * sd->exposure + 999) / 1000; | 662 | reg11 = (60 * sd->exposure + 999) / 1000; |
720 | if (reg11 < 1) | 663 | if (reg11 < 1) |
@@ -735,20 +678,23 @@ static void setexposure(struct gspca_dev *gspca_dev) | |||
735 | else if (reg10 > reg10_max) | 678 | else if (reg10 > reg10_max) |
736 | reg10 = reg10_max; | 679 | reg10 = reg10_max; |
737 | 680 | ||
681 | /* In 640x480, if the reg11 has less than 3, the image is | ||
682 | unstable (not enough bandwidth). */ | ||
683 | if (gspca_dev->width == 640 && reg11 < 3) | ||
684 | reg11 = 3; | ||
685 | |||
738 | /* Write reg 10 and reg11 low nibble */ | 686 | /* Write reg 10 and reg11 low nibble */ |
739 | i2c[1] = sd->sensor_addr; | 687 | i2c[1] = sd->sensor_addr; |
740 | i2c[3] = reg10; | 688 | i2c[3] = reg10; |
741 | i2c[4] |= reg11 - 1; | 689 | i2c[4] |= reg11 - 1; |
742 | if (sd->sensor == SENSOR_OV7630_3) { | 690 | |
743 | __u8 reg76 = reg10 & 0x03; | 691 | /* If register 11 didn't change, don't change it */ |
744 | __u8 i2c_reg76[] = {0xa0, 0x21, 0x76, 0x00, | 692 | if (sd->reg11 == reg11 ) |
745 | 0x00, 0x00, 0x00, 0x10}; | 693 | i2c[0] = 0xa0; |
746 | reg10 >>= 2; | 694 | |
747 | i2c_reg76[3] = reg76; | 695 | if (i2c_w(gspca_dev, i2c) == 0) |
748 | if (i2c_w(gspca_dev, i2c_reg76) < 0) | 696 | sd->reg11 = reg11; |
749 | PDEBUG(D_ERR, "i2c error exposure"); | 697 | else |
750 | } | ||
751 | if (i2c_w(gspca_dev, i2c) < 0) | ||
752 | PDEBUG(D_ERR, "i2c error exposure"); | 698 | PDEBUG(D_ERR, "i2c error exposure"); |
753 | break; | 699 | break; |
754 | } | 700 | } |
@@ -761,11 +707,11 @@ static void setfreq(struct gspca_dev *gspca_dev) | |||
761 | 707 | ||
762 | switch (sd->sensor) { | 708 | switch (sd->sensor) { |
763 | case SENSOR_OV6650: | 709 | case SENSOR_OV6650: |
764 | case SENSOR_OV7630_3: { | 710 | case SENSOR_OV7630: { |
765 | /* Framerate adjust register for artificial light 50 hz flicker | 711 | /* Framerate adjust register for artificial light 50 hz flicker |
766 | compensation, identical to ov6630 0x2b register, see ov6630 | 712 | compensation, for the ov6650 this is identical to ov6630 |
767 | datasheet. | 713 | 0x2b register, see ov6630 datasheet. |
768 | 0x4f -> (30 fps -> 25 fps), 0x00 -> no adjustment */ | 714 | 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */ |
769 | __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; | 715 | __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; |
770 | switch (sd->freq) { | 716 | switch (sd->freq) { |
771 | default: | 717 | default: |
@@ -786,69 +732,6 @@ static void setfreq(struct gspca_dev *gspca_dev) | |||
786 | } | 732 | } |
787 | } | 733 | } |
788 | 734 | ||
789 | static void setsaturation(struct gspca_dev *gspca_dev) | ||
790 | { | ||
791 | struct sd *sd = (struct sd *) gspca_dev; | ||
792 | |||
793 | switch (sd->sensor) { | ||
794 | /* case SENSOR_OV6650: */ | ||
795 | case SENSOR_OV7630_3: | ||
796 | case SENSOR_OV7630: { | ||
797 | __u8 i2c[] = {0xa0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}; | ||
798 | i2c[1] = sd->sensor_addr; | ||
799 | i2c[3] = sd->saturation & 0xf0; | ||
800 | if (i2c_w(gspca_dev, i2c) < 0) | ||
801 | PDEBUG(D_ERR, "i2c error setsaturation"); | ||
802 | else | ||
803 | PDEBUG(D_CONF, "saturation set to: %d", | ||
804 | (int)sd->saturation); | ||
805 | break; | ||
806 | } | ||
807 | } | ||
808 | } | ||
809 | |||
810 | static void sethue(struct gspca_dev *gspca_dev) | ||
811 | { | ||
812 | struct sd *sd = (struct sd *) gspca_dev; | ||
813 | |||
814 | switch (sd->sensor) { | ||
815 | /* case SENSOR_OV6650: */ | ||
816 | case SENSOR_OV7630_3: | ||
817 | case SENSOR_OV7630: { | ||
818 | __u8 i2c[] = {0xa0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}; | ||
819 | i2c[1] = sd->sensor_addr; | ||
820 | i2c[3] = 0x20 | (sd->hue >> 3); | ||
821 | if (i2c_w(gspca_dev, i2c) < 0) | ||
822 | PDEBUG(D_ERR, "i2c error setsaturation"); | ||
823 | else | ||
824 | PDEBUG(D_CONF, "hue set to: %d", (int)sd->hue); | ||
825 | break; | ||
826 | } | ||
827 | } | ||
828 | } | ||
829 | |||
830 | static void setcontrast(struct gspca_dev *gspca_dev) | ||
831 | { | ||
832 | struct sd *sd = (struct sd *) gspca_dev; | ||
833 | |||
834 | switch (sd->sensor) { | ||
835 | /* case SENSOR_OV6650: */ | ||
836 | case SENSOR_OV7630_3: | ||
837 | case SENSOR_OV7630: { | ||
838 | __u8 i2c[] = {0xa0, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}; | ||
839 | i2c[1] = sd->sensor_addr; | ||
840 | i2c[3] = 0x20 | (sd->contrast >> 3); | ||
841 | if (i2c_w(gspca_dev, i2c) < 0) | ||
842 | PDEBUG(D_ERR, "i2c error setcontrast"); | ||
843 | else | ||
844 | PDEBUG(D_CONF, "contrast set to: %d", | ||
845 | (int)sd->contrast); | ||
846 | break; | ||
847 | } | ||
848 | } | ||
849 | } | ||
850 | |||
851 | |||
852 | static void do_autogain(struct gspca_dev *gspca_dev) | 735 | static void do_autogain(struct gspca_dev *gspca_dev) |
853 | { | 736 | { |
854 | struct sd *sd = (struct sd *) gspca_dev; | 737 | struct sd *sd = (struct sd *) gspca_dev; |
@@ -874,88 +757,32 @@ static int sd_config(struct gspca_dev *gspca_dev, | |||
874 | { | 757 | { |
875 | struct sd *sd = (struct sd *) gspca_dev; | 758 | struct sd *sd = (struct sd *) gspca_dev; |
876 | struct cam *cam; | 759 | struct cam *cam; |
877 | __u16 product; | ||
878 | int sif = 0; | 760 | int sif = 0; |
879 | 761 | ||
880 | /* nctrls depends upon the sensor, so we use a per cam copy */ | 762 | /* nctrls depends upon the sensor, so we use a per cam copy */ |
881 | memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc)); | 763 | memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc)); |
882 | gspca_dev->sd_desc = &sd->sd_desc; | 764 | gspca_dev->sd_desc = &sd->sd_desc; |
883 | 765 | ||
884 | sd->fr_h_sz = 12; /* default size of the frame header */ | 766 | /* copy the webcam info from the device id */ |
885 | sd->sd_desc.nctrls = 2; /* default nb of ctrls */ | 767 | sd->sensor = (id->driver_info >> 24) & 0xff; |
886 | sd->autogain = AUTOGAIN_DEF; /* default is autogain active */ | 768 | if (id->driver_info & (F_GAIN << 16)) |
887 | 769 | sd->sensor_has_gain = 1; | |
888 | product = id->idProduct; | 770 | if (id->driver_info & (F_AUTO << 16)) |
889 | /* switch (id->idVendor) { */ | 771 | sd->sd_desc.dq_callback = do_autogain; |
890 | /* case 0x0c45: * Sonix */ | 772 | if (id->driver_info & (F_SIF << 16)) |
891 | switch (product) { | 773 | sif = 1; |
892 | case 0x6001: /* SN9C102 */ | 774 | if (id->driver_info & (F_H18 << 16)) |
893 | case 0x6005: /* SN9C101 */ | 775 | sd->fr_h_sz = 18; /* size of frame header */ |
894 | case 0x6007: /* SN9C101 */ | 776 | else |
895 | sd->sensor = SENSOR_TAS5110; | 777 | sd->fr_h_sz = 12; |
896 | sd->sensor_has_gain = 1; | 778 | sd->sd_desc.nctrls = (id->driver_info >> 8) & 0xff; |
897 | sd->sd_desc.nctrls = 4; | 779 | sd->sensor_addr = id->driver_info & 0xff; |
898 | sd->sd_desc.dq_callback = do_autogain; | ||
899 | sif = 1; | ||
900 | break; | ||
901 | case 0x6009: /* SN9C101 */ | ||
902 | case 0x600d: /* SN9C101 */ | ||
903 | case 0x6029: /* SN9C101 */ | ||
904 | sd->sensor = SENSOR_PAS106; | ||
905 | sif = 1; | ||
906 | break; | ||
907 | case 0x6011: /* SN9C101 - SN9C101G */ | ||
908 | sd->sensor = SENSOR_OV6650; | ||
909 | sd->sensor_has_gain = 1; | ||
910 | sd->sensor_addr = 0x60; | ||
911 | sd->sd_desc.nctrls = 5; | ||
912 | sd->sd_desc.dq_callback = do_autogain; | ||
913 | sif = 1; | ||
914 | break; | ||
915 | case 0x6019: /* SN9C101 */ | ||
916 | case 0x602c: /* SN9C102 */ | ||
917 | case 0x602e: /* SN9C102 */ | ||
918 | sd->sensor = SENSOR_OV7630; | ||
919 | sd->sensor_addr = 0x21; | ||
920 | break; | ||
921 | case 0x60b0: /* SN9C103 */ | ||
922 | sd->sensor = SENSOR_OV7630_3; | ||
923 | sd->sensor_addr = 0x21; | ||
924 | sd->fr_h_sz = 18; /* size of frame header */ | ||
925 | sd->sensor_has_gain = 1; | ||
926 | sd->sd_desc.nctrls = 8; | ||
927 | sd->sd_desc.dq_callback = do_autogain; | ||
928 | sd->autogain = 0; | ||
929 | break; | ||
930 | case 0x6024: /* SN9C102 */ | ||
931 | case 0x6025: /* SN9C102 */ | ||
932 | sd->sensor = SENSOR_TAS5130CXX; | ||
933 | break; | ||
934 | case 0x6028: /* SN9C102 */ | ||
935 | sd->sensor = SENSOR_PAS202; | ||
936 | break; | ||
937 | case 0x602d: /* SN9C102 */ | ||
938 | sd->sensor = SENSOR_HV7131R; | ||
939 | break; | ||
940 | case 0x60af: /* SN9C103 */ | ||
941 | sd->sensor = SENSOR_PAS202; | ||
942 | sd->fr_h_sz = 18; /* size of frame header (?) */ | ||
943 | break; | ||
944 | } | ||
945 | /* break; */ | ||
946 | /* } */ | ||
947 | 780 | ||
948 | cam = &gspca_dev->cam; | 781 | cam = &gspca_dev->cam; |
949 | cam->dev_name = (char *) id->driver_info; | ||
950 | cam->epaddr = 0x01; | 782 | cam->epaddr = 0x01; |
951 | if (!sif) { | 783 | if (!sif) { |
952 | cam->cam_mode = vga_mode; | 784 | cam->cam_mode = vga_mode; |
953 | cam->nmodes = ARRAY_SIZE(vga_mode); | 785 | cam->nmodes = ARRAY_SIZE(vga_mode); |
954 | if (sd->sensor == SENSOR_OV7630_3) { | ||
955 | /* We only have 320x240 & 640x480 */ | ||
956 | cam->cam_mode++; | ||
957 | cam->nmodes--; | ||
958 | } | ||
959 | } else { | 786 | } else { |
960 | cam->cam_mode = sif_mode; | 787 | cam->cam_mode = sif_mode; |
961 | cam->nmodes = ARRAY_SIZE(sif_mode); | 788 | cam->nmodes = ARRAY_SIZE(sif_mode); |
@@ -963,12 +790,9 @@ static int sd_config(struct gspca_dev *gspca_dev, | |||
963 | sd->brightness = BRIGHTNESS_DEF; | 790 | sd->brightness = BRIGHTNESS_DEF; |
964 | sd->gain = GAIN_DEF; | 791 | sd->gain = GAIN_DEF; |
965 | sd->exposure = EXPOSURE_DEF; | 792 | sd->exposure = EXPOSURE_DEF; |
793 | sd->autogain = AUTOGAIN_DEF; | ||
966 | sd->freq = FREQ_DEF; | 794 | sd->freq = FREQ_DEF; |
967 | sd->contrast = CONTRAST_DEF; | 795 | |
968 | sd->saturation = SATURATION_DEF; | ||
969 | sd->hue = HUE_DEF; | ||
970 | if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */ | ||
971 | reg_w(gspca_dev, 0x01, probe_ov7630, sizeof probe_ov7630); | ||
972 | return 0; | 796 | return 0; |
973 | } | 797 | } |
974 | 798 | ||
@@ -1002,9 +826,8 @@ static void pas106_i2cinit(struct gspca_dev *gspca_dev) | |||
1002 | static void sd_start(struct gspca_dev *gspca_dev) | 826 | static void sd_start(struct gspca_dev *gspca_dev) |
1003 | { | 827 | { |
1004 | struct sd *sd = (struct sd *) gspca_dev; | 828 | struct sd *sd = (struct sd *) gspca_dev; |
1005 | int mode, l; | 829 | int mode, l = 0x1f; |
1006 | const __u8 *sn9c10x; | 830 | const __u8 *sn9c10x; |
1007 | __u8 reg01, reg17; | ||
1008 | __u8 reg17_19[3]; | 831 | __u8 reg17_19[3]; |
1009 | 832 | ||
1010 | mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; | 833 | mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; |
@@ -1022,13 +845,11 @@ static void sd_start(struct gspca_dev *gspca_dev) | |||
1022 | reg17_19[2] = 0x20; | 845 | reg17_19[2] = 0x20; |
1023 | break; | 846 | break; |
1024 | case SENSOR_OV7630: | 847 | case SENSOR_OV7630: |
1025 | sn9c10x = initOv7630; | 848 | if (sd->fr_h_sz == 18) { /* SN9C103 */ |
1026 | reg17_19[0] = 0x68; | 849 | sn9c10x = initOv7630_3; |
1027 | reg17_19[1] = (mode << 4) | COMP2; | 850 | l = sizeof initOv7630_3; |
1028 | reg17_19[2] = MCK_INIT1; | 851 | } else |
1029 | break; | 852 | sn9c10x = initOv7630; |
1030 | case SENSOR_OV7630_3: | ||
1031 | sn9c10x = initOv7630_3; | ||
1032 | reg17_19[0] = 0x68; | 853 | reg17_19[0] = 0x68; |
1033 | reg17_19[1] = (mode << 4) | COMP2; | 854 | reg17_19[1] = (mode << 4) | COMP2; |
1034 | reg17_19[2] = MCK_INIT1; | 855 | reg17_19[2] = MCK_INIT1; |
@@ -1059,30 +880,11 @@ static void sd_start(struct gspca_dev *gspca_dev) | |||
1059 | reg17_19[2] = mode ? 0x23 : 0x43; | 880 | reg17_19[2] = mode ? 0x23 : 0x43; |
1060 | break; | 881 | break; |
1061 | } | 882 | } |
1062 | switch (sd->sensor) { | ||
1063 | case SENSOR_OV7630: | ||
1064 | reg01 = 0x06; | ||
1065 | reg17 = 0x29; | ||
1066 | l = sizeof initOv7630; | ||
1067 | break; | ||
1068 | case SENSOR_OV7630_3: | ||
1069 | reg01 = 0x44; | ||
1070 | reg17 = 0x68; | ||
1071 | l = sizeof initOv7630_3; | ||
1072 | break; | ||
1073 | default: | ||
1074 | reg01 = sn9c10x[0]; | ||
1075 | reg17 = sn9c10x[0x17 - 1]; | ||
1076 | l = 0x1f; | ||
1077 | break; | ||
1078 | } | ||
1079 | 883 | ||
1080 | /* reg 0x01 bit 2 video transfert on */ | 884 | /* reg 0x01 bit 2 video transfert on */ |
1081 | reg_w(gspca_dev, 0x01, ®01, 1); | 885 | reg_w(gspca_dev, 0x01, &sn9c10x[0x01 - 1], 1); |
1082 | /* reg 0x17 SensorClk enable inv Clk 0x60 */ | 886 | /* reg 0x17 SensorClk enable inv Clk 0x60 */ |
1083 | reg_w(gspca_dev, 0x17, ®17, 1); | 887 | reg_w(gspca_dev, 0x17, &sn9c10x[0x17 - 1], 1); |
1084 | /*fixme: for ov7630 102 | ||
1085 | reg_w(gspca_dev, 0x01, {0x06, sn9c10x[1]}, 2); */ | ||
1086 | /* Set the registers from the template */ | 888 | /* Set the registers from the template */ |
1087 | reg_w_big(gspca_dev, 0x01, sn9c10x, l); | 889 | reg_w_big(gspca_dev, 0x01, sn9c10x, l); |
1088 | switch (sd->sensor) { | 890 | switch (sd->sensor) { |
@@ -1095,17 +897,13 @@ static void sd_start(struct gspca_dev *gspca_dev) | |||
1095 | sizeof ov6650_sensor_init); | 897 | sizeof ov6650_sensor_init); |
1096 | break; | 898 | break; |
1097 | case SENSOR_OV7630: | 899 | case SENSOR_OV7630: |
1098 | i2c_w_vector(gspca_dev, ov7630_sensor_init_com, | ||
1099 | sizeof ov7630_sensor_init_com); | ||
1100 | msleep(200); | ||
1101 | i2c_w_vector(gspca_dev, ov7630_sensor_init, | 900 | i2c_w_vector(gspca_dev, ov7630_sensor_init, |
1102 | sizeof ov7630_sensor_init); | 901 | sizeof ov7630_sensor_init); |
1103 | break; | 902 | if (sd->fr_h_sz == 18) { /* SN9C103 */ |
1104 | case SENSOR_OV7630_3: | 903 | const __u8 i2c[] = { 0xa0, 0x21, 0x13, 0x80, 0x00, |
1105 | i2c_w_vector(gspca_dev, ov7630_sensor_init_com, | 904 | 0x00, 0x00, 0x10 }; |
1106 | sizeof ov7630_sensor_init_com); | 905 | i2c_w(gspca_dev, i2c); |
1107 | msleep(200); | 906 | } |
1108 | i2c_w(gspca_dev, ov7630_sensor_init_3[mode]); | ||
1109 | break; | 907 | break; |
1110 | case SENSOR_PAS106: | 908 | case SENSOR_PAS106: |
1111 | pas106_i2cinit(gspca_dev); | 909 | pas106_i2cinit(gspca_dev); |
@@ -1145,14 +943,14 @@ static void sd_start(struct gspca_dev *gspca_dev) | |||
1145 | reg_w(gspca_dev, 0x18, ®17_19[1], 2); | 943 | reg_w(gspca_dev, 0x18, ®17_19[1], 2); |
1146 | msleep(20); | 944 | msleep(20); |
1147 | 945 | ||
946 | sd->reg11 = -1; | ||
947 | |||
1148 | setgain(gspca_dev); | 948 | setgain(gspca_dev); |
1149 | setbrightness(gspca_dev); | 949 | setbrightness(gspca_dev); |
1150 | setexposure(gspca_dev); | 950 | setexposure(gspca_dev); |
1151 | setfreq(gspca_dev); | 951 | setfreq(gspca_dev); |
1152 | setsaturation(gspca_dev); | ||
1153 | sethue(gspca_dev); | ||
1154 | setcontrast(gspca_dev); | ||
1155 | 952 | ||
953 | sd->frames_to_drop = 0; | ||
1156 | sd->autogain_ignore_frames = 0; | 954 | sd->autogain_ignore_frames = 0; |
1157 | atomic_set(&sd->avg_lum, -1); | 955 | atomic_set(&sd->avg_lum, -1); |
1158 | } | 956 | } |
@@ -1198,21 +996,31 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |||
1198 | && data[3 + i] == 0xc4 | 996 | && data[3 + i] == 0xc4 |
1199 | && data[4 + i] == 0xc4 | 997 | && data[4 + i] == 0xc4 |
1200 | && data[5 + i] == 0x96) { /* start of frame */ | 998 | && data[5 + i] == 0x96) { /* start of frame */ |
1201 | frame = gspca_frame_add(gspca_dev, LAST_PACKET, | 999 | int lum = -1; |
1202 | frame, data, 0); | 1000 | int pkt_type = LAST_PACKET; |
1001 | |||
1203 | if (len - i < sd->fr_h_sz) { | 1002 | if (len - i < sd->fr_h_sz) { |
1204 | atomic_set(&sd->avg_lum, -1); | ||
1205 | PDEBUG(D_STREAM, "packet too short to" | 1003 | PDEBUG(D_STREAM, "packet too short to" |
1206 | " get avg brightness"); | 1004 | " get avg brightness"); |
1207 | } else if (sd->fr_h_sz == 12) { | 1005 | } else if (sd->fr_h_sz == 12) { |
1208 | atomic_set(&sd->avg_lum, | 1006 | lum = data[i + 8] + (data[i + 9] << 8); |
1209 | data[i + 8] + | ||
1210 | (data[i + 9] << 8)); | ||
1211 | } else { | 1007 | } else { |
1212 | atomic_set(&sd->avg_lum, | 1008 | lum = data[i + 9] + |
1213 | data[i + 9] + | 1009 | (data[i + 10] << 8); |
1214 | (data[i + 10] << 8)); | 1010 | } |
1011 | if (lum == 0) { | ||
1012 | lum = -1; | ||
1013 | sd->frames_to_drop = 2; | ||
1014 | } | ||
1015 | atomic_set(&sd->avg_lum, lum); | ||
1016 | |||
1017 | if (sd->frames_to_drop) { | ||
1018 | sd->frames_to_drop--; | ||
1019 | pkt_type = DISCARD_PACKET; | ||
1215 | } | 1020 | } |
1021 | |||
1022 | frame = gspca_frame_add(gspca_dev, pkt_type, | ||
1023 | frame, data, 0); | ||
1216 | data += i + sd->fr_h_sz; | 1024 | data += i + sd->fr_h_sz; |
1217 | len -= i + sd->fr_h_sz; | 1025 | len -= i + sd->fr_h_sz; |
1218 | gspca_frame_add(gspca_dev, FIRST_PACKET, | 1026 | gspca_frame_add(gspca_dev, FIRST_PACKET, |
@@ -1327,60 +1135,6 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) | |||
1327 | return 0; | 1135 | return 0; |
1328 | } | 1136 | } |
1329 | 1137 | ||
1330 | static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val) | ||
1331 | { | ||
1332 | struct sd *sd = (struct sd *) gspca_dev; | ||
1333 | |||
1334 | sd->saturation = val; | ||
1335 | if (gspca_dev->streaming) | ||
1336 | setsaturation(gspca_dev); | ||
1337 | return 0; | ||
1338 | } | ||
1339 | |||
1340 | static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val) | ||
1341 | { | ||
1342 | struct sd *sd = (struct sd *) gspca_dev; | ||
1343 | |||
1344 | *val = sd->saturation; | ||
1345 | return 0; | ||
1346 | } | ||
1347 | |||
1348 | static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) | ||
1349 | { | ||
1350 | struct sd *sd = (struct sd *) gspca_dev; | ||
1351 | |||
1352 | sd->hue = val; | ||
1353 | if (gspca_dev->streaming) | ||
1354 | sethue(gspca_dev); | ||
1355 | return 0; | ||
1356 | } | ||
1357 | |||
1358 | static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) | ||
1359 | { | ||
1360 | struct sd *sd = (struct sd *) gspca_dev; | ||
1361 | |||
1362 | *val = sd->hue; | ||
1363 | return 0; | ||
1364 | } | ||
1365 | |||
1366 | static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) | ||
1367 | { | ||
1368 | struct sd *sd = (struct sd *) gspca_dev; | ||
1369 | |||
1370 | sd->contrast = val; | ||
1371 | if (gspca_dev->streaming) | ||
1372 | setcontrast(gspca_dev); | ||
1373 | return 0; | ||
1374 | } | ||
1375 | |||
1376 | static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) | ||
1377 | { | ||
1378 | struct sd *sd = (struct sd *) gspca_dev; | ||
1379 | |||
1380 | *val = sd->contrast; | ||
1381 | return 0; | ||
1382 | } | ||
1383 | |||
1384 | static int sd_querymenu(struct gspca_dev *gspca_dev, | 1138 | static int sd_querymenu(struct gspca_dev *gspca_dev, |
1385 | struct v4l2_querymenu *menu) | 1139 | struct v4l2_querymenu *menu) |
1386 | { | 1140 | { |
@@ -1418,27 +1172,47 @@ static const struct sd_desc sd_desc = { | |||
1418 | }; | 1172 | }; |
1419 | 1173 | ||
1420 | /* -- module initialisation -- */ | 1174 | /* -- module initialisation -- */ |
1421 | #define DVNM(name) .driver_info = (kernel_ulong_t) name | 1175 | #define SFCI(sensor, flags, nctrls, i2c_addr) \ |
1176 | .driver_info = (SENSOR_ ## sensor << 24) \ | ||
1177 | | ((flags) << 16) \ | ||
1178 | | ((nctrls) << 8) \ | ||
1179 | | (i2c_addr) | ||
1422 | static __devinitdata struct usb_device_id device_table[] = { | 1180 | static __devinitdata struct usb_device_id device_table[] = { |
1423 | #ifndef CONFIG_USB_SN9C102 | 1181 | #ifndef CONFIG_USB_SN9C102 |
1424 | {USB_DEVICE(0x0c45, 0x6001), DVNM("Genius VideoCAM NB")}, | 1182 | {USB_DEVICE(0x0c45, 0x6001), /* SN9C102 */ |
1425 | {USB_DEVICE(0x0c45, 0x6005), DVNM("Sweex Tas5110")}, | 1183 | SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, |
1426 | {USB_DEVICE(0x0c45, 0x6007), DVNM("Sonix sn9c101 + Tas5110D")}, | 1184 | {USB_DEVICE(0x0c45, 0x6005), /* SN9C101 */ |
1427 | {USB_DEVICE(0x0c45, 0x6009), DVNM("spcaCam@120")}, | 1185 | SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, |
1428 | {USB_DEVICE(0x0c45, 0x600d), DVNM("spcaCam@120")}, | 1186 | {USB_DEVICE(0x0c45, 0x6007), /* SN9C101 */ |
1187 | SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, | ||
1188 | {USB_DEVICE(0x0c45, 0x6009), /* SN9C101 */ | ||
1189 | SFCI(PAS106, F_SIF, 2, 0)}, | ||
1190 | {USB_DEVICE(0x0c45, 0x600d), /* SN9C101 */ | ||
1191 | SFCI(PAS106, F_SIF, 2, 0)}, | ||
1429 | #endif | 1192 | #endif |
1430 | {USB_DEVICE(0x0c45, 0x6011), DVNM("MAX Webcam Microdia")}, | 1193 | {USB_DEVICE(0x0c45, 0x6011), /* SN9C101 - SN9C101G */ |
1194 | SFCI(OV6650, F_GAIN|F_AUTO|F_SIF, 5, 0x60)}, | ||
1431 | #ifndef CONFIG_USB_SN9C102 | 1195 | #ifndef CONFIG_USB_SN9C102 |
1432 | {USB_DEVICE(0x0c45, 0x6019), DVNM("Generic Sonix OV7630")}, | 1196 | {USB_DEVICE(0x0c45, 0x6019), /* SN9C101 */ |
1433 | {USB_DEVICE(0x0c45, 0x6024), DVNM("Generic Sonix Tas5130c")}, | 1197 | SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, |
1434 | {USB_DEVICE(0x0c45, 0x6025), DVNM("Xcam Shanga")}, | 1198 | {USB_DEVICE(0x0c45, 0x6024), /* SN9C102 */ |
1435 | {USB_DEVICE(0x0c45, 0x6028), DVNM("Sonix Btc Pc380")}, | 1199 | SFCI(TAS5130CXX, 0, 2, 0)}, |
1436 | {USB_DEVICE(0x0c45, 0x6029), DVNM("spcaCam@150")}, | 1200 | {USB_DEVICE(0x0c45, 0x6025), /* SN9C102 */ |
1437 | {USB_DEVICE(0x0c45, 0x602c), DVNM("Generic Sonix OV7630")}, | 1201 | SFCI(TAS5130CXX, 0, 2, 0)}, |
1438 | {USB_DEVICE(0x0c45, 0x602d), DVNM("LIC-200 LG")}, | 1202 | {USB_DEVICE(0x0c45, 0x6028), /* SN9C102 */ |
1439 | {USB_DEVICE(0x0c45, 0x602e), DVNM("Genius VideoCam Messenger")}, | 1203 | SFCI(PAS202, 0, 2, 0)}, |
1440 | {USB_DEVICE(0x0c45, 0x60af), DVNM("Trust WB3100P")}, | 1204 | {USB_DEVICE(0x0c45, 0x6029), /* SN9C101 */ |
1441 | {USB_DEVICE(0x0c45, 0x60b0), DVNM("Genius VideoCam Look")}, | 1205 | SFCI(PAS106, F_SIF, 2, 0)}, |
1206 | {USB_DEVICE(0x0c45, 0x602c), /* SN9C102 */ | ||
1207 | SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, | ||
1208 | {USB_DEVICE(0x0c45, 0x602d), /* SN9C102 */ | ||
1209 | SFCI(HV7131R, 0, 2, 0)}, | ||
1210 | {USB_DEVICE(0x0c45, 0x602e), /* SN9C102 */ | ||
1211 | SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, | ||
1212 | {USB_DEVICE(0x0c45, 0x60af), /* SN9C103 */ | ||
1213 | SFCI(PAS202, F_H18, 2, 0)}, | ||
1214 | {USB_DEVICE(0x0c45, 0x60b0), /* SN9C103 */ | ||
1215 | SFCI(OV7630, F_GAIN|F_AUTO|F_H18, 5, 0x21)}, | ||
1442 | #endif | 1216 | #endif |
1443 | {} | 1217 | {} |
1444 | }; | 1218 | }; |
@@ -1464,7 +1238,7 @@ static int __init sd_mod_init(void) | |||
1464 | { | 1238 | { |
1465 | if (usb_register(&sd_driver) < 0) | 1239 | if (usb_register(&sd_driver) < 0) |
1466 | return -1; | 1240 | return -1; |
1467 | PDEBUG(D_PROBE, "v%s registered", version); | 1241 | PDEBUG(D_PROBE, "registered"); |
1468 | return 0; | 1242 | return 0; |
1469 | } | 1243 | } |
1470 | static void __exit sd_mod_exit(void) | 1244 | static void __exit sd_mod_exit(void) |