diff options
author | John McMaster <johndmcmaster@gmail.com> | 2014-11-30 21:27:53 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2015-01-29 15:13:13 -0500 |
commit | 7cc42d9f75766246faab4a1436eb02b97041fb41 (patch) | |
tree | e36785d2e8e2423aacc073309abae7961c5be1d1 | |
parent | afad4dd50acbe6457413ec6a68708433d9824d37 (diff) |
[media] gspca_touptek: Add support for ToupTek UCMOS series USB cameras
Adds support for AmScope MU800 / ToupTek UCMOS08000KPB USB microscope camera.
Signed-off-by: John McMaster <johndmcmaster@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
-rw-r--r-- | drivers/media/usb/gspca/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/usb/gspca/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/usb/gspca/touptek.c | 732 |
3 files changed, 744 insertions, 0 deletions
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index eed10d782535..60af3b167f3b 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig | |||
@@ -395,6 +395,16 @@ config USB_GSPCA_TOPRO | |||
395 | To compile this driver as a module, choose M here: the | 395 | To compile this driver as a module, choose M here: the |
396 | module will be called gspca_topro. | 396 | module will be called gspca_topro. |
397 | 397 | ||
398 | config USB_GSPCA_TOUPTEK | ||
399 | tristate "Touptek USB Camera Driver" | ||
400 | depends on VIDEO_V4L2 && USB_GSPCA | ||
401 | help | ||
402 | Say Y here if you want support for cameras based on the ToupTek UCMOS | ||
403 | / AmScope MU series camera. | ||
404 | |||
405 | To compile this driver as a module, choose M here: the | ||
406 | module will be called gspca_touptek. | ||
407 | |||
398 | config USB_GSPCA_TV8532 | 408 | config USB_GSPCA_TV8532 |
399 | tristate "TV8532 USB Camera Driver" | 409 | tristate "TV8532 USB Camera Driver" |
400 | depends on VIDEO_V4L2 && USB_GSPCA | 410 | depends on VIDEO_V4L2 && USB_GSPCA |
diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile index f46975e4c82d..9f5ccecb9c8a 100644 --- a/drivers/media/usb/gspca/Makefile +++ b/drivers/media/usb/gspca/Makefile | |||
@@ -39,6 +39,7 @@ obj-$(CONFIG_USB_GSPCA_STK1135) += gspca_stk1135.o | |||
39 | obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o | 39 | obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o |
40 | obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o | 40 | obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o |
41 | obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o | 41 | obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o |
42 | obj-$(CONFIG_USB_GSPCA_TOUPTEK) += gspca_touptek.o | ||
42 | obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o | 43 | obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o |
43 | obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o | 44 | obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o |
44 | obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o | 45 | obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o |
@@ -86,6 +87,7 @@ gspca_stv0680-objs := stv0680.o | |||
86 | gspca_sunplus-objs := sunplus.o | 87 | gspca_sunplus-objs := sunplus.o |
87 | gspca_t613-objs := t613.o | 88 | gspca_t613-objs := t613.o |
88 | gspca_topro-objs := topro.o | 89 | gspca_topro-objs := topro.o |
90 | gspca_touptek-objs := touptek.o | ||
89 | gspca_tv8532-objs := tv8532.o | 91 | gspca_tv8532-objs := tv8532.o |
90 | gspca_vc032x-objs := vc032x.o | 92 | gspca_vc032x-objs := vc032x.o |
91 | gspca_vicam-objs := vicam.o | 93 | gspca_vicam-objs := vicam.o |
diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c new file mode 100644 index 000000000000..8b7c01e4b772 --- /dev/null +++ b/drivers/media/usb/gspca/touptek.c | |||
@@ -0,0 +1,732 @@ | |||
1 | /* | ||
2 | * ToupTek UCMOS / AmScope MU series camera driver | ||
3 | * TODO: contrast with ScopeTek / AmScope MDC cameras | ||
4 | * | ||
5 | * Copyright (C) 2012-2014 John McMaster <JohnDMcMaster@gmail.com> | ||
6 | * | ||
7 | * Special thanks to Bushing for helping with the decrypt algorithm and | ||
8 | * Sean O'Sullivan / the Rensselaer Center for Open Source | ||
9 | * Software (RCOS) for helping me learn kernel development | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include "gspca.h" | ||
27 | |||
28 | #define MODULE_NAME "touptek" | ||
29 | |||
30 | MODULE_AUTHOR("John McMaster"); | ||
31 | MODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | /* | ||
35 | Exposure reg is linear with exposure time | ||
36 | Exposure (sec), E (reg) | ||
37 | 0.000400, 0x0002 | ||
38 | 0.001000, 0x0005 | ||
39 | 0.005000, 0x0019 | ||
40 | 0.020000, 0x0064 | ||
41 | 0.080000, 0x0190 | ||
42 | 0.400000, 0x07D0 | ||
43 | 1.000000, 0x1388 | ||
44 | 2.000000, 0x2710 | ||
45 | |||
46 | Three gain stages | ||
47 | 0x1000: master channel enable bit | ||
48 | 0x007F: low gain bits | ||
49 | 0x0080: medium gain bit | ||
50 | 0x0100: high gain bit | ||
51 | gain = enable * (1 + regH) * (1 + regM) * z * regL | ||
52 | |||
53 | Gain implementation | ||
54 | Want to do something similar to mt9v011.c's set_balance | ||
55 | |||
56 | Gain does not vary with resolution (checked 640x480 vs 1600x1200) | ||
57 | |||
58 | Constant derivation: | ||
59 | |||
60 | Raw data: | ||
61 | Gain, GTOP, B, R, GBOT | ||
62 | 1.00, 0x105C, 0x1068, 0x10C8, 0x105C | ||
63 | 1.20, 0x106E, 0x107E, 0x10D6, 0x106E | ||
64 | 1.40, 0x10C0, 0x10CA, 0x10E5, 0x10C0 | ||
65 | 1.60, 0x10C9, 0x10D4, 0x10F3, 0x10C9 | ||
66 | 1.80, 0x10D2, 0x10DE, 0x11C1, 0x10D2 | ||
67 | 2.00, 0x10DC, 0x10E9, 0x11C8, 0x10DC | ||
68 | 2.20, 0x10E5, 0x10F3, 0x11CF, 0x10E5 | ||
69 | 2.40, 0x10EE, 0x10FE, 0x11D7, 0x10EE | ||
70 | 2.60, 0x10F7, 0x11C4, 0x11DE, 0x10F7 | ||
71 | 2.80, 0x11C0, 0x11CA, 0x11E5, 0x11C0 | ||
72 | 3.00, 0x11C5, 0x11CF, 0x11ED, 0x11C5 | ||
73 | |||
74 | zR = 0.0069605943152454778 | ||
75 | about 3/431 = 0.0069605568445475635 | ||
76 | zB = 0.0095695970695970703 | ||
77 | about 6/627 = 0.0095693779904306216 | ||
78 | zG = 0.010889328063241107 | ||
79 | about 6/551 = 0.010889292196007259 | ||
80 | about 10 bits for constant + 7 bits for value => at least 17 bit intermediate | ||
81 | with 32 bit ints should be fine for overflow etc | ||
82 | Essentially gains are in range 0-0x001FF | ||
83 | |||
84 | However, V4L expects a main gain channel + R and B balance | ||
85 | To keep things simple for now saturate the values of balance is too high/low | ||
86 | This isn't really ideal but easy way to fit the Linux model | ||
87 | |||
88 | Converted using gain model turns out to be quite linear: | ||
89 | Gain, GTOP, B, R, GBOT | ||
90 | 1.00, 92, 104, 144, 92 | ||
91 | 1.20, 110, 126, 172, 110 | ||
92 | 1.40, 128, 148, 202, 128 | ||
93 | 1.60, 146, 168, 230, 146 | ||
94 | 1.80, 164, 188, 260, 164 | ||
95 | 2.00, 184, 210, 288, 184 | ||
96 | 2.20, 202, 230, 316, 202 | ||
97 | 2.40, 220, 252, 348, 220 | ||
98 | 2.60, 238, 272, 376, 238 | ||
99 | 2.80, 256, 296, 404, 256 | ||
100 | 3.00, 276, 316, 436, 276 | ||
101 | |||
102 | Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188) | ||
103 | or about 13 effective bits of gain | ||
104 | The highest the commercial driver goes in my setup 436 | ||
105 | However, because could *maybe* damage circuits | ||
106 | limit the gain until have a reason to go higher | ||
107 | Solution: gain clipped and warning emitted | ||
108 | */ | ||
109 | #define GAIN_MAX 511 | ||
110 | |||
111 | /* Frame sync is a short read */ | ||
112 | #define BULK_SIZE 0x4000 | ||
113 | |||
114 | /* MT9E001 reg names to give a rough approximation */ | ||
115 | #define REG_COARSE_INTEGRATION_TIME_ 0x3012 | ||
116 | #define REG_GROUPED_PARAMETER_HOLD_ 0x3022 | ||
117 | #define REG_MODE_SELECT 0x0100 | ||
118 | #define REG_OP_SYS_CLK_DIV 0x030A | ||
119 | #define REG_VT_SYS_CLK_DIV 0x0302 | ||
120 | #define REG_PRE_PLL_CLK_DIV 0x0304 | ||
121 | #define REG_VT_PIX_CLK_DIV 0x0300 | ||
122 | #define REG_OP_PIX_CLK_DIV 0x0308 | ||
123 | #define REG_PLL_MULTIPLIER 0x0306 | ||
124 | #define REG_COARSE_INTEGRATION_TIME_ 0x3012 | ||
125 | #define REG_FRAME_LENGTH_LINES 0x0340 | ||
126 | #define REG_FRAME_LENGTH_LINES_ 0x300A | ||
127 | #define REG_GREEN1_GAIN 0x3056 | ||
128 | #define REG_GREEN2_GAIN 0x305C | ||
129 | #define REG_GROUPED_PARAMETER_HOLD 0x0104 | ||
130 | #define REG_LINE_LENGTH_PCK_ 0x300C | ||
131 | #define REG_MODE_SELECT 0x0100 | ||
132 | #define REG_PLL_MULTIPLIER 0x0306 | ||
133 | #define REG_READ_MODE 0x3040 | ||
134 | #define REG_BLUE_GAIN 0x3058 | ||
135 | #define REG_RED_GAIN 0x305A | ||
136 | #define REG_RESET_REGISTER 0x301A | ||
137 | #define REG_SCALE_M 0x0404 | ||
138 | #define REG_SCALING_MODE 0x0400 | ||
139 | #define REG_SOFTWARE_RESET 0x0103 | ||
140 | #define REG_X_ADDR_END 0x0348 | ||
141 | #define REG_X_ADDR_START 0x0344 | ||
142 | #define REG_X_ADDR_START 0x0344 | ||
143 | #define REG_X_OUTPUT_SIZE 0x034C | ||
144 | #define REG_Y_ADDR_END 0x034A | ||
145 | #define REG_Y_ADDR_START 0x0346 | ||
146 | #define REG_Y_OUTPUT_SIZE 0x034E | ||
147 | |||
148 | |||
149 | /* specific webcam descriptor */ | ||
150 | struct sd { | ||
151 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
152 | /* How many bytes this frame */ | ||
153 | unsigned int this_f; | ||
154 | |||
155 | /* | ||
156 | Device has separate gains for each Bayer quadrant | ||
157 | V4L supports master gain which is referenced to G1/G2 and supplies | ||
158 | individual balance controls for R/B | ||
159 | */ | ||
160 | struct v4l2_ctrl *blue; | ||
161 | struct v4l2_ctrl *red; | ||
162 | }; | ||
163 | |||
164 | /* Used to simplify reg write error handling */ | ||
165 | struct cmd { | ||
166 | u16 value; | ||
167 | u16 index; | ||
168 | }; | ||
169 | |||
170 | static const struct v4l2_pix_format vga_mode[] = { | ||
171 | {800, 600, | ||
172 | V4L2_PIX_FMT_SGRBG8, | ||
173 | V4L2_FIELD_NONE, | ||
174 | .bytesperline = 800, | ||
175 | .sizeimage = 800 * 600, | ||
176 | .colorspace = V4L2_COLORSPACE_SRGB}, | ||
177 | {1600, 1200, | ||
178 | V4L2_PIX_FMT_SGRBG8, | ||
179 | V4L2_FIELD_NONE, | ||
180 | .bytesperline = 1600, | ||
181 | .sizeimage = 1600 * 1200, | ||
182 | .colorspace = V4L2_COLORSPACE_SRGB}, | ||
183 | {3264, 2448, | ||
184 | V4L2_PIX_FMT_SGRBG8, | ||
185 | V4L2_FIELD_NONE, | ||
186 | .bytesperline = 3264, | ||
187 | .sizeimage = 3264 * 2448, | ||
188 | .colorspace = V4L2_COLORSPACE_SRGB}, | ||
189 | }; | ||
190 | |||
191 | /* | ||
192 | As theres no known frame sync, the only way to keep synced is to try hard | ||
193 | to never miss any packets | ||
194 | */ | ||
195 | #if MAX_NURBS < 4 | ||
196 | #error "Not enough URBs in the gspca table" | ||
197 | #endif | ||
198 | |||
199 | static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) | ||
200 | { | ||
201 | if (rc < 0) { | ||
202 | PERR("reply has error %d", rc); | ||
203 | return -EIO; | ||
204 | } | ||
205 | if (rc != 1) { | ||
206 | PERR("Bad reply size %d", rc); | ||
207 | return -EIO; | ||
208 | } | ||
209 | if (reply[0] != 0x08) { | ||
210 | PERR("Bad reply 0x%02X", reply[0]); | ||
211 | return -EIO; | ||
212 | } | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) | ||
217 | { | ||
218 | char buff[1]; | ||
219 | int rc; | ||
220 | |||
221 | PDEBUG(D_USBO, "reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n", | ||
222 | value, index); | ||
223 | rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), | ||
224 | 0x0B, 0xC0, value, index, buff, 1, 500); | ||
225 | PDEBUG(D_USBO, "rc=%d, ret={0x%02X}", rc, buff[0]); | ||
226 | if (rc < 0) { | ||
227 | PERR("Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n", | ||
228 | value, index, rc); | ||
229 | gspca_dev->usb_err = rc; | ||
230 | return; | ||
231 | } | ||
232 | if (val_reply(gspca_dev, buff, rc)) { | ||
233 | PERR("Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n", | ||
234 | value, index); | ||
235 | gspca_dev->usb_err = -EIO; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static void reg_w_buf(struct gspca_dev *gspca_dev, | ||
240 | const struct cmd *p, int l) | ||
241 | { | ||
242 | do { | ||
243 | reg_w(gspca_dev, p->value, p->index); | ||
244 | p++; | ||
245 | } while (--l > 0); | ||
246 | } | ||
247 | |||
248 | static void setexposure(struct gspca_dev *gspca_dev, s32 val) | ||
249 | { | ||
250 | u16 value; | ||
251 | unsigned int w = gspca_dev->pixfmt.width; | ||
252 | |||
253 | if (w == 800) | ||
254 | value = val * 5; | ||
255 | else if (w == 1600) | ||
256 | value = val * 3; | ||
257 | else if (w == 3264) | ||
258 | value = val * 3 / 2; | ||
259 | else { | ||
260 | PERR("Invalid width %u\n", w); | ||
261 | gspca_dev->usb_err = -EINVAL; | ||
262 | return; | ||
263 | } | ||
264 | PDEBUG(D_STREAM, "exposure: 0x%04X ms\n", value); | ||
265 | /* Wonder if theres a good reason for sending it twice */ | ||
266 | /* probably not but leave it in because...why not */ | ||
267 | reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); | ||
268 | reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); | ||
269 | } | ||
270 | |||
271 | static int gainify(int in) | ||
272 | { | ||
273 | /* | ||
274 | TODO: check if there are any issues with corner cases | ||
275 | 0x000 (0):0x07F (127): regL | ||
276 | 0x080 (128) - 0x0FF (255): regM, regL | ||
277 | 0x100 (256) - max: regH, regM, regL | ||
278 | */ | ||
279 | if (in <= 0x7F) | ||
280 | return 0x1000 | in; | ||
281 | else if (in <= 0xFF) | ||
282 | return 0x1080 | in / 2; | ||
283 | else | ||
284 | return 0x1180 | in / 4; | ||
285 | } | ||
286 | |||
287 | static void setggain(struct gspca_dev *gspca_dev, u16 global_gain) | ||
288 | { | ||
289 | u16 normalized; | ||
290 | |||
291 | normalized = gainify(global_gain); | ||
292 | PDEBUG(D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n", | ||
293 | REG_GREEN1_GAIN, | ||
294 | normalized, global_gain); | ||
295 | |||
296 | reg_w(gspca_dev, normalized, REG_GREEN1_GAIN); | ||
297 | reg_w(gspca_dev, normalized, REG_GREEN2_GAIN); | ||
298 | } | ||
299 | |||
300 | static void setbgain(struct gspca_dev *gspca_dev, | ||
301 | u16 gain, u16 global_gain) | ||
302 | { | ||
303 | u16 normalized; | ||
304 | |||
305 | normalized = global_gain + | ||
306 | ((u32)global_gain) * gain / GAIN_MAX; | ||
307 | if (normalized > GAIN_MAX) { | ||
308 | PDEBUG(D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n", | ||
309 | GAIN_MAX, normalized); | ||
310 | normalized = GAIN_MAX; | ||
311 | } | ||
312 | normalized = gainify(normalized); | ||
313 | PDEBUG(D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n", | ||
314 | REG_BLUE_GAIN, normalized, gain); | ||
315 | |||
316 | reg_w(gspca_dev, normalized, REG_BLUE_GAIN); | ||
317 | } | ||
318 | |||
319 | static void setrgain(struct gspca_dev *gspca_dev, | ||
320 | u16 gain, u16 global_gain) | ||
321 | { | ||
322 | u16 normalized; | ||
323 | |||
324 | normalized = global_gain + | ||
325 | ((u32)global_gain) * gain / GAIN_MAX; | ||
326 | if (normalized > GAIN_MAX) { | ||
327 | PDEBUG(D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n", | ||
328 | GAIN_MAX, normalized); | ||
329 | normalized = GAIN_MAX; | ||
330 | } | ||
331 | normalized = gainify(normalized); | ||
332 | PDEBUG(D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n", | ||
333 | REG_RED_GAIN, normalized, gain); | ||
334 | |||
335 | reg_w(gspca_dev, normalized, REG_RED_GAIN); | ||
336 | } | ||
337 | |||
338 | static void configure_wh(struct gspca_dev *gspca_dev) | ||
339 | { | ||
340 | unsigned int w = gspca_dev->pixfmt.width; | ||
341 | |||
342 | PDEBUG(D_STREAM, "configure_wh\n"); | ||
343 | |||
344 | if (w == 800) { | ||
345 | static const struct cmd reg_init_res[] = { | ||
346 | {0x0060, REG_X_ADDR_START}, | ||
347 | {0x0CD9, REG_X_ADDR_END}, | ||
348 | {0x0036, REG_Y_ADDR_START}, | ||
349 | {0x098F, REG_Y_ADDR_END}, | ||
350 | {0x07C7, REG_READ_MODE}, | ||
351 | }; | ||
352 | |||
353 | reg_w_buf(gspca_dev, | ||
354 | reg_init_res, ARRAY_SIZE(reg_init_res)); | ||
355 | } else if (w == 1600) { | ||
356 | static const struct cmd reg_init_res[] = { | ||
357 | {0x009C, REG_X_ADDR_START}, | ||
358 | {0x0D19, REG_X_ADDR_END}, | ||
359 | {0x0068, REG_Y_ADDR_START}, | ||
360 | {0x09C5, REG_Y_ADDR_END}, | ||
361 | {0x06C3, REG_READ_MODE}, | ||
362 | }; | ||
363 | |||
364 | reg_w_buf(gspca_dev, | ||
365 | reg_init_res, ARRAY_SIZE(reg_init_res)); | ||
366 | } else if (w == 3264) { | ||
367 | static const struct cmd reg_init_res[] = { | ||
368 | {0x00E8, REG_X_ADDR_START}, | ||
369 | {0x0DA7, REG_X_ADDR_END}, | ||
370 | {0x009E, REG_Y_ADDR_START}, | ||
371 | {0x0A2D, REG_Y_ADDR_END}, | ||
372 | {0x0241, REG_READ_MODE}, | ||
373 | }; | ||
374 | |||
375 | reg_w_buf(gspca_dev, | ||
376 | reg_init_res, ARRAY_SIZE(reg_init_res)); | ||
377 | } else { | ||
378 | PERR("bad width %u\n", w); | ||
379 | gspca_dev->usb_err = -EINVAL; | ||
380 | return; | ||
381 | } | ||
382 | |||
383 | reg_w(gspca_dev, 0x0000, REG_SCALING_MODE); | ||
384 | reg_w(gspca_dev, 0x0010, REG_SCALE_M); | ||
385 | reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE); | ||
386 | reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE); | ||
387 | |||
388 | if (w == 800) { | ||
389 | reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_); | ||
390 | reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_); | ||
391 | } else if (w == 1600) { | ||
392 | reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_); | ||
393 | reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_); | ||
394 | } else if (w == 3264) { | ||
395 | reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_); | ||
396 | reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_); | ||
397 | } else { | ||
398 | PERR("bad width %u\n", w); | ||
399 | gspca_dev->usb_err = -EINVAL; | ||
400 | return; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | /* Packets that were encrypted, no idea if the grouping is significant */ | ||
405 | static void configure_encrypted(struct gspca_dev *gspca_dev) | ||
406 | { | ||
407 | static const struct cmd reg_init_begin[] = { | ||
408 | {0x0100, REG_SOFTWARE_RESET}, | ||
409 | {0x0000, REG_MODE_SELECT}, | ||
410 | {0x0100, REG_GROUPED_PARAMETER_HOLD}, | ||
411 | {0x0004, REG_VT_PIX_CLK_DIV}, | ||
412 | {0x0001, REG_VT_SYS_CLK_DIV}, | ||
413 | {0x0008, REG_OP_PIX_CLK_DIV}, | ||
414 | {0x0001, REG_OP_SYS_CLK_DIV}, | ||
415 | {0x0004, REG_PRE_PLL_CLK_DIV}, | ||
416 | {0x0040, REG_PLL_MULTIPLIER}, | ||
417 | {0x0000, REG_GROUPED_PARAMETER_HOLD}, | ||
418 | {0x0100, REG_GROUPED_PARAMETER_HOLD}, | ||
419 | }; | ||
420 | static const struct cmd reg_init_end[] = { | ||
421 | {0x0000, REG_GROUPED_PARAMETER_HOLD}, | ||
422 | {0x0301, 0x31AE}, | ||
423 | {0x0805, 0x3064}, | ||
424 | {0x0071, 0x3170}, | ||
425 | {0x10DE, REG_RESET_REGISTER}, | ||
426 | {0x0000, REG_MODE_SELECT}, | ||
427 | {0x0010, REG_PLL_MULTIPLIER}, | ||
428 | {0x0100, REG_MODE_SELECT}, | ||
429 | }; | ||
430 | |||
431 | PDEBUG(D_STREAM, "Encrypted begin, w = %u\n", gspca_dev->pixfmt.width); | ||
432 | reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin)); | ||
433 | configure_wh(gspca_dev); | ||
434 | reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end)); | ||
435 | reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD); | ||
436 | reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD); | ||
437 | |||
438 | PDEBUG(D_STREAM, "Encrypted end\n"); | ||
439 | } | ||
440 | |||
441 | static int configure(struct gspca_dev *gspca_dev) | ||
442 | { | ||
443 | int rc; | ||
444 | uint8_t buff[4]; | ||
445 | |||
446 | PDEBUG(D_STREAM, "configure()\n"); | ||
447 | |||
448 | /* | ||
449 | First driver sets a sort of encryption key | ||
450 | A number of futur requests of this type have wValue and wIndex encrypted | ||
451 | as follows: | ||
452 | -Compute key = this wValue rotate left by 4 bits | ||
453 | (decrypt.py rotates right because we are decrypting) | ||
454 | -Later packets encrypt packets by XOR'ing with key | ||
455 | XOR encrypt/decrypt is symmetrical | ||
456 | wValue, and wIndex are encrypted | ||
457 | bRequest is not and bRequestType is always 0xC0 | ||
458 | This allows resyncing if key is unknown? | ||
459 | By setting 0 we XOR with 0 and the shifting and XOR drops out | ||
460 | */ | ||
461 | rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), | ||
462 | 0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500); | ||
463 | if (val_reply(gspca_dev, buff, rc)) { | ||
464 | PERR("failed key req"); | ||
465 | return -EIO; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | Next does some sort of 2 packet challenge / response | ||
470 | evidence suggests its an Atmel I2C crypto part but nobody cares to look | ||
471 | (to make sure its not cloned hardware?) | ||
472 | Ignore: I want to work with their hardware, not clone it | ||
473 | 16 bytes out challenge, requestType: 0x40 | ||
474 | 16 bytes in response, requestType: 0xC0 | ||
475 | */ | ||
476 | |||
477 | rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), | ||
478 | 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); | ||
479 | if (rc < 0) { | ||
480 | PERR("failed to replay packet 176 w/ rc %d\n", rc); | ||
481 | return rc; | ||
482 | } | ||
483 | |||
484 | rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), | ||
485 | 0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500); | ||
486 | if (rc < 0) { | ||
487 | PERR("failed to replay packet 178 w/ rc %d\n", rc); | ||
488 | return rc; | ||
489 | } | ||
490 | |||
491 | rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), | ||
492 | 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); | ||
493 | if (rc < 0) { | ||
494 | PERR("failed to replay packet 180 w/ rc %d\n", rc); | ||
495 | return rc; | ||
496 | } | ||
497 | |||
498 | /* | ||
499 | Serial number? Doesn't seem to be required | ||
500 | cam1: \xE6\x0D\x00\x00, cam2: \x70\x19\x00\x00 | ||
501 | rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), | ||
502 | 0x20, 0xC0, 0x0000, 0x0000, buff, 4, 500); | ||
503 | */ | ||
504 | |||
505 | /* Large (EEPROM?) read, skip it since no idea what to do with it */ | ||
506 | gspca_dev->usb_err = 0; | ||
507 | configure_encrypted(gspca_dev); | ||
508 | if (gspca_dev->usb_err) | ||
509 | return gspca_dev->usb_err; | ||
510 | |||
511 | /* Omitted this by accident, does not work without it */ | ||
512 | rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), | ||
513 | 0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500); | ||
514 | if (rc < 0) { | ||
515 | PERR("failed to replay final packet w/ rc %d\n", rc); | ||
516 | return rc; | ||
517 | } | ||
518 | |||
519 | PDEBUG(D_STREAM, "Configure complete\n"); | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | static int sd_config(struct gspca_dev *gspca_dev, | ||
524 | const struct usb_device_id *id) | ||
525 | { | ||
526 | gspca_dev->cam.cam_mode = vga_mode; | ||
527 | gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); | ||
528 | |||
529 | /* Yes we want URBs and we want them now! */ | ||
530 | gspca_dev->cam.no_urb_create = 0; | ||
531 | gspca_dev->cam.bulk_nurbs = 4; | ||
532 | /* Largest size the windows driver uses */ | ||
533 | gspca_dev->cam.bulk_size = BULK_SIZE; | ||
534 | /* Def need to use bulk transfers */ | ||
535 | gspca_dev->cam.bulk = 1; | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int sd_start(struct gspca_dev *gspca_dev) | ||
541 | { | ||
542 | struct sd *sd = (struct sd *) gspca_dev; | ||
543 | int rc; | ||
544 | |||
545 | sd->this_f = 0; | ||
546 | |||
547 | rc = configure(gspca_dev); | ||
548 | if (rc < 0) { | ||
549 | PERR("Failed configure"); | ||
550 | return rc; | ||
551 | } | ||
552 | /* First two frames have messed up gains | ||
553 | Drop them to avoid special cases in user apps? */ | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | ||
558 | u8 *data, /* isoc packet */ | ||
559 | int len) /* iso packet length */ | ||
560 | { | ||
561 | struct sd *sd = (struct sd *) gspca_dev; | ||
562 | |||
563 | if (len != BULK_SIZE) { | ||
564 | /* can we finish a frame? */ | ||
565 | if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) { | ||
566 | gspca_frame_add(gspca_dev, LAST_PACKET, data, len); | ||
567 | PDEBUG(D_FRAM, "finish frame sz %u/%u w/ len %u\n", | ||
568 | sd->this_f, gspca_dev->pixfmt.sizeimage, len); | ||
569 | /* lost some data, discard the frame */ | ||
570 | } else { | ||
571 | gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0); | ||
572 | PDEBUG(D_FRAM, "abort frame sz %u/%u w/ len %u\n", | ||
573 | sd->this_f, gspca_dev->pixfmt.sizeimage, len); | ||
574 | } | ||
575 | sd->this_f = 0; | ||
576 | } else { | ||
577 | if (sd->this_f == 0) | ||
578 | gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); | ||
579 | else | ||
580 | gspca_frame_add(gspca_dev, INTER_PACKET, data, len); | ||
581 | sd->this_f += len; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | static int sd_init(struct gspca_dev *gspca_dev) | ||
586 | { | ||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | static int sd_s_ctrl(struct v4l2_ctrl *ctrl) | ||
591 | { | ||
592 | struct gspca_dev *gspca_dev = | ||
593 | container_of(ctrl->handler, struct gspca_dev, ctrl_handler); | ||
594 | struct sd *sd = (struct sd *) gspca_dev; | ||
595 | |||
596 | gspca_dev->usb_err = 0; | ||
597 | |||
598 | if (!gspca_dev->streaming) | ||
599 | return 0; | ||
600 | |||
601 | switch (ctrl->id) { | ||
602 | case V4L2_CID_EXPOSURE: | ||
603 | setexposure(gspca_dev, ctrl->val); | ||
604 | break; | ||
605 | case V4L2_CID_GAIN: | ||
606 | /* gspca_dev->gain automatically updated */ | ||
607 | setggain(gspca_dev, gspca_dev->gain->val); | ||
608 | break; | ||
609 | case V4L2_CID_BLUE_BALANCE: | ||
610 | sd->blue->val = ctrl->val; | ||
611 | setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val); | ||
612 | break; | ||
613 | case V4L2_CID_RED_BALANCE: | ||
614 | sd->red->val = ctrl->val; | ||
615 | setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val); | ||
616 | break; | ||
617 | } | ||
618 | return gspca_dev->usb_err; | ||
619 | } | ||
620 | |||
621 | static const struct v4l2_ctrl_ops sd_ctrl_ops = { | ||
622 | .s_ctrl = sd_s_ctrl, | ||
623 | }; | ||
624 | |||
625 | static int sd_init_controls(struct gspca_dev *gspca_dev) | ||
626 | { | ||
627 | struct sd *sd = (struct sd *) gspca_dev; | ||
628 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; | ||
629 | |||
630 | gspca_dev->vdev.ctrl_handler = hdl; | ||
631 | v4l2_ctrl_handler_init(hdl, 4); | ||
632 | |||
633 | gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, | ||
634 | /* Mostly limited by URB timeouts */ | ||
635 | /* XXX: make dynamic based on frame rate? */ | ||
636 | V4L2_CID_EXPOSURE, 0, 800, 1, 350); | ||
637 | gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, | ||
638 | V4L2_CID_GAIN, 0, 511, 1, 128); | ||
639 | sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, | ||
640 | V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80); | ||
641 | sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, | ||
642 | V4L2_CID_RED_BALANCE, 0, 1023, 1, 295); | ||
643 | |||
644 | if (hdl->error) { | ||
645 | PERR("Could not initialize controls\n"); | ||
646 | return hdl->error; | ||
647 | } | ||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | /* sub-driver description */ | ||
652 | static const struct sd_desc sd_desc = { | ||
653 | .name = MODULE_NAME, | ||
654 | .config = sd_config, | ||
655 | .init = sd_init, | ||
656 | .init_controls = sd_init_controls, | ||
657 | .start = sd_start, | ||
658 | .pkt_scan = sd_pkt_scan, | ||
659 | }; | ||
660 | |||
661 | /* Table of supported USB devices */ | ||
662 | static const struct usb_device_id device_table[] = { | ||
663 | /* Commented out devices should be related */ | ||
664 | /* AS: AmScope, TT: ToupTek */ | ||
665 | /* { USB_DEVICE(0x0547, 0x6035) }, TT UCMOS00350KPA */ | ||
666 | /* { USB_DEVICE(0x0547, 0x6130) }, TT UCMOS01300KPA */ | ||
667 | /* { USB_DEVICE(0x0547, 0x6200) }, TT UCMOS02000KPA */ | ||
668 | /* { USB_DEVICE(0x0547, 0x6310) }, TT UCMOS03100KPA */ | ||
669 | /* { USB_DEVICE(0x0547, 0x6510) }, TT UCMOS05100KPA */ | ||
670 | /* { USB_DEVICE(0x0547, 0x6800) }, TT UCMOS08000KPA */ | ||
671 | /* { USB_DEVICE(0x0547, 0x6801) }, TT UCMOS08000KPB */ | ||
672 | { USB_DEVICE(0x0547, 0x6801) }, /* TT UCMOS08000KPB, AS MU800 */ | ||
673 | /* { USB_DEVICE(0x0547, 0x6900) }, TT UCMOS09000KPA */ | ||
674 | /* { USB_DEVICE(0x0547, 0x6901) }, TT UCMOS09000KPB */ | ||
675 | /* { USB_DEVICE(0x0547, 0x6010) }, TT UCMOS10000KPA */ | ||
676 | /* { USB_DEVICE(0x0547, 0x6014) }, TT UCMOS14000KPA */ | ||
677 | /* { USB_DEVICE(0x0547, 0x6131) }, TT UCMOS01300KMA */ | ||
678 | /* { USB_DEVICE(0x0547, 0x6511) }, TT UCMOS05100KMA */ | ||
679 | /* { USB_DEVICE(0x0547, 0x8080) }, TT UHCCD00800KPA */ | ||
680 | /* { USB_DEVICE(0x0547, 0x8140) }, TT UHCCD01400KPA */ | ||
681 | /* { USB_DEVICE(0x0547, 0x8141) }, TT EXCCD01400KPA */ | ||
682 | /* { USB_DEVICE(0x0547, 0x8200) }, TT UHCCD02000KPA */ | ||
683 | /* { USB_DEVICE(0x0547, 0x8201) }, TT UHCCD02000KPB */ | ||
684 | /* { USB_DEVICE(0x0547, 0x8310) }, TT UHCCD03100KPA */ | ||
685 | /* { USB_DEVICE(0x0547, 0x8500) }, TT UHCCD05000KPA */ | ||
686 | /* { USB_DEVICE(0x0547, 0x8510) }, TT UHCCD05100KPA */ | ||
687 | /* { USB_DEVICE(0x0547, 0x8600) }, TT UHCCD06000KPA */ | ||
688 | /* { USB_DEVICE(0x0547, 0x8800) }, TT UHCCD08000KPA */ | ||
689 | /* { USB_DEVICE(0x0547, 0x8315) }, TT UHCCD03150KPA */ | ||
690 | /* { USB_DEVICE(0x0547, 0x7800) }, TT UHCCD00800KMA */ | ||
691 | /* { USB_DEVICE(0x0547, 0x7140) }, TT UHCCD01400KMA */ | ||
692 | /* { USB_DEVICE(0x0547, 0x7141) }, TT UHCCD01400KMB */ | ||
693 | /* { USB_DEVICE(0x0547, 0x7200) }, TT UHCCD02000KMA */ | ||
694 | /* { USB_DEVICE(0x0547, 0x7315) }, TT UHCCD03150KMA */ | ||
695 | { } | ||
696 | }; | ||
697 | MODULE_DEVICE_TABLE(usb, device_table); | ||
698 | |||
699 | static int sd_probe(struct usb_interface *intf, | ||
700 | const struct usb_device_id *id) | ||
701 | { | ||
702 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | ||
703 | THIS_MODULE); | ||
704 | } | ||
705 | |||
706 | static struct usb_driver sd_driver = { | ||
707 | .name = MODULE_NAME, | ||
708 | .id_table = device_table, | ||
709 | .probe = sd_probe, | ||
710 | .disconnect = gspca_disconnect, | ||
711 | #ifdef CONFIG_PM | ||
712 | .suspend = gspca_suspend, | ||
713 | .resume = gspca_resume, | ||
714 | #endif | ||
715 | }; | ||
716 | |||
717 | static int __init sd_mod_init(void) | ||
718 | { | ||
719 | int ret; | ||
720 | |||
721 | ret = usb_register(&sd_driver); | ||
722 | if (ret < 0) | ||
723 | return ret; | ||
724 | return 0; | ||
725 | } | ||
726 | static void __exit sd_mod_exit(void) | ||
727 | { | ||
728 | usb_deregister(&sd_driver); | ||
729 | } | ||
730 | |||
731 | module_init(sd_mod_init); | ||
732 | module_exit(sd_mod_exit); | ||