Commit | Line | Data |
---|---|---|
5b0ff8c4 JFM |
1 | /* |
2 | * spca1528 subdriver | |
3 | * | |
4 | * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
133a9fe9 JP |
21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
22 | ||
5b0ff8c4 JFM |
23 | #define MODULE_NAME "spca1528" |
24 | ||
25 | #include "gspca.h" | |
26 | #include "jpeg.h" | |
27 | ||
28 | MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); | |
29 | MODULE_DESCRIPTION("SPCA1528 USB Camera Driver"); | |
30 | MODULE_LICENSE("GPL"); | |
31 | ||
32 | /* specific webcam descriptor */ | |
33 | struct sd { | |
34 | struct gspca_dev gspca_dev; /* !! must be the first item */ | |
35 | ||
36 | u8 brightness; | |
37 | u8 contrast; | |
38 | u8 hue; | |
39 | u8 color; | |
40 | u8 sharpness; | |
41 | ||
42 | u8 pkt_seq; | |
43 | ||
44 | u8 jpeg_hdr[JPEG_HDR_SZ]; | |
45 | }; | |
46 | ||
47 | /* V4L2 controls supported by the driver */ | |
48 | static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); | |
49 | static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); | |
50 | static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); | |
51 | static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); | |
52 | static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); | |
53 | static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); | |
54 | static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val); | |
55 | static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val); | |
56 | static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); | |
57 | static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); | |
58 | ||
59 | static const struct ctrl sd_ctrls[] = { | |
60 | { | |
61 | { | |
62 | .id = V4L2_CID_BRIGHTNESS, | |
63 | .type = V4L2_CTRL_TYPE_INTEGER, | |
64 | .name = "Brightness", | |
65 | .minimum = 0, | |
66 | .maximum = 255, | |
67 | .step = 1, | |
68 | #define BRIGHTNESS_DEF 128 | |
69 | .default_value = BRIGHTNESS_DEF, | |
70 | }, | |
71 | .set = sd_setbrightness, | |
72 | .get = sd_getbrightness, | |
73 | }, | |
74 | { | |
75 | { | |
76 | .id = V4L2_CID_CONTRAST, | |
77 | .type = V4L2_CTRL_TYPE_INTEGER, | |
78 | .name = "Contrast", | |
79 | .minimum = 0, | |
80 | .maximum = 8, | |
81 | .step = 1, | |
82 | #define CONTRAST_DEF 1 | |
83 | .default_value = CONTRAST_DEF, | |
84 | }, | |
85 | .set = sd_setcontrast, | |
86 | .get = sd_getcontrast, | |
87 | }, | |
88 | { | |
89 | { | |
90 | .id = V4L2_CID_HUE, | |
91 | .type = V4L2_CTRL_TYPE_INTEGER, | |
92 | .name = "Hue", | |
93 | .minimum = 0, | |
94 | .maximum = 255, | |
95 | .step = 1, | |
96 | #define HUE_DEF 0 | |
97 | .default_value = HUE_DEF, | |
98 | }, | |
99 | .set = sd_sethue, | |
100 | .get = sd_gethue, | |
101 | }, | |
102 | { | |
103 | { | |
104 | .id = V4L2_CID_SATURATION, | |
105 | .type = V4L2_CTRL_TYPE_INTEGER, | |
106 | .name = "Saturation", | |
107 | .minimum = 0, | |
108 | .maximum = 8, | |
109 | .step = 1, | |
110 | #define COLOR_DEF 1 | |
111 | .default_value = COLOR_DEF, | |
112 | }, | |
113 | .set = sd_setcolor, | |
114 | .get = sd_getcolor, | |
115 | }, | |
116 | { | |
117 | { | |
118 | .id = V4L2_CID_SHARPNESS, | |
119 | .type = V4L2_CTRL_TYPE_INTEGER, | |
120 | .name = "Sharpness", | |
121 | .minimum = 0, | |
122 | .maximum = 255, | |
123 | .step = 1, | |
124 | #define SHARPNESS_DEF 0 | |
125 | .default_value = SHARPNESS_DEF, | |
126 | }, | |
127 | .set = sd_setsharpness, | |
128 | .get = sd_getsharpness, | |
129 | }, | |
130 | }; | |
131 | ||
132 | static const struct v4l2_pix_format vga_mode[] = { | |
133 | /* (does not work correctly) | |
134 | {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, | |
135 | .bytesperline = 176, | |
136 | .sizeimage = 176 * 144 * 5 / 8 + 590, | |
137 | .colorspace = V4L2_COLORSPACE_JPEG, | |
138 | .priv = 3}, | |
139 | */ | |
140 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, | |
141 | .bytesperline = 320, | |
142 | .sizeimage = 320 * 240 * 4 / 8 + 590, | |
143 | .colorspace = V4L2_COLORSPACE_JPEG, | |
144 | .priv = 2}, | |
145 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, | |
146 | .bytesperline = 640, | |
147 | .sizeimage = 640 * 480 * 3 / 8 + 590, | |
148 | .colorspace = V4L2_COLORSPACE_JPEG, | |
149 | .priv = 1}, | |
150 | }; | |
151 | ||
152 | /* read <len> bytes to gspca usb_buf */ | |
153 | static void reg_r(struct gspca_dev *gspca_dev, | |
154 | u8 req, | |
155 | u16 index, | |
156 | int len) | |
157 | { | |
158 | #if USB_BUF_SZ < 64 | |
159 | #error "USB buffer too small" | |
160 | #endif | |
161 | struct usb_device *dev = gspca_dev->dev; | |
162 | int ret; | |
163 | ||
164 | if (gspca_dev->usb_err < 0) | |
165 | return; | |
166 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), | |
167 | req, | |
168 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
169 | 0x0000, /* value */ | |
170 | index, | |
171 | gspca_dev->usb_buf, len, | |
172 | 500); | |
173 | PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, | |
174 | gspca_dev->usb_buf[0]); | |
175 | if (ret < 0) { | |
133a9fe9 | 176 | pr_err("reg_r err %d\n", ret); |
5b0ff8c4 JFM |
177 | gspca_dev->usb_err = ret; |
178 | } | |
179 | } | |
180 | ||
181 | static void reg_w(struct gspca_dev *gspca_dev, | |
182 | u8 req, | |
183 | u16 value, | |
184 | u16 index) | |
185 | { | |
186 | struct usb_device *dev = gspca_dev->dev; | |
187 | int ret; | |
188 | ||
189 | if (gspca_dev->usb_err < 0) | |
190 | return; | |
191 | PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); | |
192 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | |
193 | req, | |
194 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
195 | value, index, | |
196 | NULL, 0, 500); | |
197 | if (ret < 0) { | |
133a9fe9 | 198 | pr_err("reg_w err %d\n", ret); |
5b0ff8c4 JFM |
199 | gspca_dev->usb_err = ret; |
200 | } | |
201 | } | |
202 | ||
203 | static void reg_wb(struct gspca_dev *gspca_dev, | |
204 | u8 req, | |
205 | u16 value, | |
206 | u16 index, | |
207 | u8 byte) | |
208 | { | |
209 | struct usb_device *dev = gspca_dev->dev; | |
210 | int ret; | |
211 | ||
212 | if (gspca_dev->usb_err < 0) | |
213 | return; | |
214 | PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte); | |
215 | gspca_dev->usb_buf[0] = byte; | |
216 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | |
217 | req, | |
218 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
219 | value, index, | |
220 | gspca_dev->usb_buf, 1, 500); | |
221 | if (ret < 0) { | |
133a9fe9 | 222 | pr_err("reg_w err %d\n", ret); |
5b0ff8c4 JFM |
223 | gspca_dev->usb_err = ret; |
224 | } | |
225 | } | |
226 | ||
227 | static void wait_status_0(struct gspca_dev *gspca_dev) | |
228 | { | |
229 | int i; | |
230 | ||
231 | i = 20; | |
232 | do { | |
233 | reg_r(gspca_dev, 0x21, 0x0000, 1); | |
234 | if (gspca_dev->usb_buf[0] == 0) | |
235 | return; | |
236 | msleep(30); | |
237 | } while (--i > 0); | |
238 | PDEBUG(D_ERR, "wait_status_0 timeout"); | |
239 | gspca_dev->usb_err = -ETIME; | |
240 | } | |
241 | ||
242 | static void wait_status_1(struct gspca_dev *gspca_dev) | |
243 | { | |
244 | int i; | |
245 | ||
246 | i = 10; | |
247 | do { | |
248 | reg_r(gspca_dev, 0x21, 0x0001, 1); | |
249 | msleep(10); | |
250 | if (gspca_dev->usb_buf[0] == 1) { | |
251 | reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00); | |
252 | reg_r(gspca_dev, 0x21, 0x0001, 1); | |
253 | return; | |
254 | } | |
255 | } while (--i > 0); | |
256 | PDEBUG(D_ERR, "wait_status_1 timeout"); | |
257 | gspca_dev->usb_err = -ETIME; | |
258 | } | |
259 | ||
260 | static void setbrightness(struct gspca_dev *gspca_dev) | |
261 | { | |
262 | struct sd *sd = (struct sd *) gspca_dev; | |
263 | ||
264 | reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness); | |
265 | } | |
266 | ||
267 | static void setcontrast(struct gspca_dev *gspca_dev) | |
268 | { | |
269 | struct sd *sd = (struct sd *) gspca_dev; | |
270 | ||
271 | reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast); | |
272 | } | |
273 | ||
274 | static void sethue(struct gspca_dev *gspca_dev) | |
275 | { | |
276 | struct sd *sd = (struct sd *) gspca_dev; | |
277 | ||
278 | reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue); | |
279 | } | |
280 | ||
281 | static void setcolor(struct gspca_dev *gspca_dev) | |
282 | { | |
283 | struct sd *sd = (struct sd *) gspca_dev; | |
284 | ||
285 | reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color); | |
286 | } | |
287 | ||
288 | static void setsharpness(struct gspca_dev *gspca_dev) | |
289 | { | |
290 | struct sd *sd = (struct sd *) gspca_dev; | |
291 | ||
292 | reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness); | |
293 | } | |
294 | ||
295 | /* this function is called at probe time */ | |
296 | static int sd_config(struct gspca_dev *gspca_dev, | |
297 | const struct usb_device_id *id) | |
298 | { | |
299 | struct sd *sd = (struct sd *) gspca_dev; | |
300 | ||
301 | gspca_dev->cam.cam_mode = vga_mode; | |
302 | gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); | |
303 | gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */ | |
304 | /*fixme: 256 in ms-win traces*/ | |
305 | ||
306 | sd->brightness = BRIGHTNESS_DEF; | |
307 | sd->contrast = CONTRAST_DEF; | |
308 | sd->hue = HUE_DEF; | |
309 | sd->color = COLOR_DEF; | |
310 | sd->sharpness = SHARPNESS_DEF; | |
311 | ||
312 | gspca_dev->nbalt = 4; /* use alternate setting 3 */ | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | /* this function is called at probe and resume time */ | |
318 | static int sd_init(struct gspca_dev *gspca_dev) | |
319 | { | |
320 | reg_w(gspca_dev, 0x00, 0x0001, 0x2067); | |
321 | reg_w(gspca_dev, 0x00, 0x00d0, 0x206b); | |
322 | reg_w(gspca_dev, 0x00, 0x0000, 0x206c); | |
323 | reg_w(gspca_dev, 0x00, 0x0001, 0x2069); | |
324 | msleep(8); | |
325 | reg_w(gspca_dev, 0x00, 0x00c0, 0x206b); | |
326 | reg_w(gspca_dev, 0x00, 0x0000, 0x206c); | |
327 | reg_w(gspca_dev, 0x00, 0x0001, 0x2069); | |
328 | ||
329 | reg_r(gspca_dev, 0x20, 0x0000, 1); | |
330 | reg_r(gspca_dev, 0x20, 0x0000, 5); | |
331 | reg_r(gspca_dev, 0x23, 0x0000, 64); | |
332 | PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c], | |
333 | &gspca_dev->usb_buf[0x30]); | |
334 | reg_r(gspca_dev, 0x23, 0x0001, 64); | |
335 | return gspca_dev->usb_err; | |
336 | } | |
337 | ||
338 | /* function called at start time before URB creation */ | |
339 | static int sd_isoc_init(struct gspca_dev *gspca_dev) | |
340 | { | |
341 | u8 mode; | |
342 | ||
343 | reg_r(gspca_dev, 0x00, 0x2520, 1); | |
344 | wait_status_0(gspca_dev); | |
345 | reg_w(gspca_dev, 0xc5, 0x0003, 0x0000); | |
346 | wait_status_1(gspca_dev); | |
347 | ||
348 | wait_status_0(gspca_dev); | |
349 | mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; | |
350 | reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode); | |
351 | reg_r(gspca_dev, 0x25, 0x0004, 1); | |
352 | reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); | |
353 | reg_r(gspca_dev, 0x27, 0x0000, 1); | |
354 | return gspca_dev->usb_err; | |
355 | } | |
356 | ||
357 | /* -- start the camera -- */ | |
358 | static int sd_start(struct gspca_dev *gspca_dev) | |
359 | { | |
360 | struct sd *sd = (struct sd *) gspca_dev; | |
361 | ||
362 | /* initialize the JPEG header */ | |
363 | jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, | |
364 | 0x22); /* JPEG 411 */ | |
365 | ||
366 | /* the JPEG quality seems to be 82% */ | |
367 | jpeg_set_qual(sd->jpeg_hdr, 82); | |
368 | ||
369 | /* set the controls */ | |
370 | setbrightness(gspca_dev); | |
371 | setcontrast(gspca_dev); | |
372 | sethue(gspca_dev); | |
373 | setcolor(gspca_dev); | |
374 | setsharpness(gspca_dev); | |
375 | ||
376 | msleep(5); | |
377 | reg_r(gspca_dev, 0x00, 0x2520, 1); | |
378 | msleep(8); | |
379 | ||
380 | /* start the capture */ | |
381 | wait_status_0(gspca_dev); | |
382 | reg_w(gspca_dev, 0x31, 0x0000, 0x0004); | |
383 | wait_status_1(gspca_dev); | |
384 | wait_status_0(gspca_dev); | |
385 | msleep(200); | |
386 | ||
387 | sd->pkt_seq = 0; | |
388 | return gspca_dev->usb_err; | |
389 | } | |
390 | ||
391 | static void sd_stopN(struct gspca_dev *gspca_dev) | |
392 | { | |
393 | /* stop the capture */ | |
394 | wait_status_0(gspca_dev); | |
395 | reg_w(gspca_dev, 0x31, 0x0000, 0x0000); | |
396 | wait_status_1(gspca_dev); | |
397 | wait_status_0(gspca_dev); | |
398 | } | |
399 | ||
400 | /* move a packet adding 0x00 after 0xff */ | |
401 | static void add_packet(struct gspca_dev *gspca_dev, | |
402 | u8 *data, | |
403 | int len) | |
404 | { | |
405 | int i; | |
406 | ||
407 | i = 0; | |
408 | do { | |
409 | if (data[i] == 0xff) { | |
410 | gspca_frame_add(gspca_dev, INTER_PACKET, | |
411 | data, i + 1); | |
412 | len -= i; | |
413 | data += i; | |
414 | *data = 0x00; | |
415 | i = 0; | |
416 | } | |
417 | } while (++i < len); | |
418 | gspca_frame_add(gspca_dev, INTER_PACKET, data, len); | |
419 | } | |
420 | ||
421 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |
422 | u8 *data, /* isoc packet */ | |
423 | int len) /* iso packet length */ | |
424 | { | |
425 | struct sd *sd = (struct sd *) gspca_dev; | |
426 | static const u8 ffd9[] = {0xff, 0xd9}; | |
427 | ||
428 | /* image packets start with: | |
429 | * 02 8n | |
430 | * with <n> bit: | |
431 | * 0x01: even (0) / odd (1) image | |
432 | * 0x02: end of image when set | |
433 | */ | |
434 | if (len < 3) | |
435 | return; /* empty packet */ | |
436 | if (*data == 0x02) { | |
437 | if (data[1] & 0x02) { | |
438 | sd->pkt_seq = !(data[1] & 1); | |
439 | add_packet(gspca_dev, data + 2, len - 2); | |
440 | gspca_frame_add(gspca_dev, LAST_PACKET, | |
441 | ffd9, 2); | |
442 | return; | |
443 | } | |
444 | if ((data[1] & 1) != sd->pkt_seq) | |
445 | goto err; | |
446 | if (gspca_dev->last_packet_type == LAST_PACKET) | |
447 | gspca_frame_add(gspca_dev, FIRST_PACKET, | |
448 | sd->jpeg_hdr, JPEG_HDR_SZ); | |
449 | add_packet(gspca_dev, data + 2, len - 2); | |
450 | return; | |
451 | } | |
452 | err: | |
453 | gspca_dev->last_packet_type = DISCARD_PACKET; | |
454 | } | |
455 | ||
456 | static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) | |
457 | { | |
458 | struct sd *sd = (struct sd *) gspca_dev; | |
459 | ||
460 | sd->brightness = val; | |
461 | if (gspca_dev->streaming) | |
462 | setbrightness(gspca_dev); | |
463 | return gspca_dev->usb_err; | |
464 | } | |
465 | ||
466 | static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) | |
467 | { | |
468 | struct sd *sd = (struct sd *) gspca_dev; | |
469 | ||
470 | *val = sd->brightness; | |
471 | return 0; | |
472 | } | |
473 | ||
474 | static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) | |
475 | { | |
476 | struct sd *sd = (struct sd *) gspca_dev; | |
477 | ||
478 | sd->contrast = val; | |
479 | if (gspca_dev->streaming) | |
480 | setcontrast(gspca_dev); | |
481 | return gspca_dev->usb_err; | |
482 | } | |
483 | ||
484 | static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) | |
485 | { | |
486 | struct sd *sd = (struct sd *) gspca_dev; | |
487 | ||
488 | *val = sd->contrast; | |
489 | return 0; | |
490 | } | |
491 | ||
492 | static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) | |
493 | { | |
494 | struct sd *sd = (struct sd *) gspca_dev; | |
495 | ||
496 | sd->hue = val; | |
497 | if (gspca_dev->streaming) | |
498 | sethue(gspca_dev); | |
499 | return gspca_dev->usb_err; | |
500 | } | |
501 | ||
502 | static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) | |
503 | { | |
504 | struct sd *sd = (struct sd *) gspca_dev; | |
505 | ||
506 | *val = sd->hue; | |
507 | return 0; | |
508 | } | |
509 | ||
510 | static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val) | |
511 | { | |
512 | struct sd *sd = (struct sd *) gspca_dev; | |
513 | ||
514 | sd->color = val; | |
515 | if (gspca_dev->streaming) | |
516 | setcolor(gspca_dev); | |
517 | return gspca_dev->usb_err; | |
518 | } | |
519 | ||
520 | static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val) | |
521 | { | |
522 | struct sd *sd = (struct sd *) gspca_dev; | |
523 | ||
524 | *val = sd->color; | |
525 | return 0; | |
526 | } | |
527 | ||
528 | static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) | |
529 | { | |
530 | struct sd *sd = (struct sd *) gspca_dev; | |
531 | ||
532 | sd->sharpness = val; | |
533 | if (gspca_dev->streaming) | |
534 | setsharpness(gspca_dev); | |
535 | return gspca_dev->usb_err; | |
536 | } | |
537 | ||
538 | static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) | |
539 | { | |
540 | struct sd *sd = (struct sd *) gspca_dev; | |
541 | ||
542 | *val = sd->sharpness; | |
543 | return 0; | |
544 | } | |
545 | ||
546 | /* sub-driver description */ | |
547 | static const struct sd_desc sd_desc = { | |
548 | .name = MODULE_NAME, | |
549 | .ctrls = sd_ctrls, | |
550 | .nctrls = ARRAY_SIZE(sd_ctrls), | |
551 | .config = sd_config, | |
552 | .init = sd_init, | |
553 | .isoc_init = sd_isoc_init, | |
554 | .start = sd_start, | |
555 | .stopN = sd_stopN, | |
556 | .pkt_scan = sd_pkt_scan, | |
557 | }; | |
558 | ||
559 | /* -- module initialisation -- */ | |
95c967c1 | 560 | static const struct usb_device_id device_table[] = { |
5b0ff8c4 JFM |
561 | {USB_DEVICE(0x04fc, 0x1528)}, |
562 | {} | |
563 | }; | |
564 | MODULE_DEVICE_TABLE(usb, device_table); | |
565 | ||
566 | /* -- device connect -- */ | |
567 | static int sd_probe(struct usb_interface *intf, | |
568 | const struct usb_device_id *id) | |
569 | { | |
570 | /* the video interface for isochronous transfer is 1 */ | |
571 | if (intf->cur_altsetting->desc.bInterfaceNumber != 1) | |
572 | return -ENODEV; | |
573 | ||
574 | return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd), | |
575 | THIS_MODULE); | |
576 | } | |
577 | ||
578 | static struct usb_driver sd_driver = { | |
579 | .name = MODULE_NAME, | |
580 | .id_table = device_table, | |
581 | .probe = sd_probe, | |
582 | .disconnect = gspca_disconnect, | |
583 | #ifdef CONFIG_PM | |
584 | .suspend = gspca_suspend, | |
585 | .resume = gspca_resume, | |
586 | #endif | |
587 | }; | |
588 | ||
589 | /* -- module insert / remove -- */ | |
590 | static int __init sd_mod_init(void) | |
591 | { | |
54826437 | 592 | return usb_register(&sd_driver); |
5b0ff8c4 JFM |
593 | } |
594 | static void __exit sd_mod_exit(void) | |
595 | { | |
596 | usb_deregister(&sd_driver); | |
5b0ff8c4 JFM |
597 | } |
598 | ||
599 | module_init(sd_mod_init); | |
600 | module_exit(sd_mod_exit); |