]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/media/video/gspca/pac207.c
V4L/DVB (8717): gspca: Frame buffer too small for small resolutions (sonixj and t613).
[mirror_ubuntu-artful-kernel.git] / drivers / media / video / gspca / pac207.c
CommitLineData
e2997a72
HG
1/*
2 * Pixart PAC207BCA library
3 *
4 * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
5 * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
6 * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
7 *
8 * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26#define MODULE_NAME "pac207"
27
28#include "gspca.h"
29
e2997a72
HG
30MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
31MODULE_DESCRIPTION("Pixart PAC207");
32MODULE_LICENSE("GPL");
33
34#define PAC207_CTRL_TIMEOUT 100 /* ms */
35
36#define PAC207_BRIGHTNESS_MIN 0
37#define PAC207_BRIGHTNESS_MAX 255
38#define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */
39
46ccdafa
HG
40/* An exposure value of 4 also works (3 does not) but then we need to lower
41 the compression balance setting when in 352x288 mode, otherwise the usb
42 bandwidth is not enough and packets get dropped resulting in corrupt
43 frames. The problem with this is that when the compression balance gets
44 lowered below 0x80, the pac207 starts using a different compression
45 algorithm for some lines, these lines get prefixed with a 0x2dd2 prefix
46 and currently we do not know how to decompress these lines, so for now
47 we use a minimum exposure value of 5 */
48#define PAC207_EXPOSURE_MIN 5
e2997a72 49#define PAC207_EXPOSURE_MAX 26
46ccdafa 50#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 ?? */
e2997a72
HG
51#define PAC207_EXPOSURE_KNEE 11 /* 4 = 30 fps, 11 = 8, 15 = 6 */
52
53#define PAC207_GAIN_MIN 0
54#define PAC207_GAIN_MAX 31
55#define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */
56#define PAC207_GAIN_KNEE 20
57
58#define PAC207_AUTOGAIN_DEADZONE 30
59/* We calculating the autogain at the end of the transfer of a frame, at this
60 moment a frame with the old settings is being transmitted, and a frame is
61 being captured with the old settings. So if we adjust the autogain we must
62 ignore atleast the 2 next frames for the new settings to come into effect
63 before doing any other adjustments */
64#define PAC207_AUTOGAIN_IGNORE_FRAMES 3
65
e2997a72
HG
66/* specific webcam descriptor */
67struct sd {
68 struct gspca_dev gspca_dev; /* !! must be the first item */
69
e2997a72
HG
70 u8 mode;
71
72 u8 brightness;
73 u8 exposure;
74 u8 autogain;
75 u8 gain;
76
77 u8 sof_read;
ab8f12cf 78 u8 header_read;
e2997a72
HG
79 u8 autogain_ignore_frames;
80
81 atomic_t avg_lum;
82};
83
84/* V4L2 controls supported by the driver */
85static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
86static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
87static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
88static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
89static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
90static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
91static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
92static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
93
94static struct ctrl sd_ctrls[] = {
95#define SD_BRIGHTNESS 0
96 {
97 {
98 .id = V4L2_CID_BRIGHTNESS,
99 .type = V4L2_CTRL_TYPE_INTEGER,
100 .name = "Brightness",
101 .minimum = PAC207_BRIGHTNESS_MIN,
102 .maximum = PAC207_BRIGHTNESS_MAX,
103 .step = 1,
104 .default_value = PAC207_BRIGHTNESS_DEFAULT,
105 .flags = 0,
106 },
107 .set = sd_setbrightness,
108 .get = sd_getbrightness,
109 },
110#define SD_EXPOSURE 1
111 {
112 {
113 .id = V4L2_CID_EXPOSURE,
114 .type = V4L2_CTRL_TYPE_INTEGER,
115 .name = "exposure",
116 .minimum = PAC207_EXPOSURE_MIN,
117 .maximum = PAC207_EXPOSURE_MAX,
118 .step = 1,
119 .default_value = PAC207_EXPOSURE_DEFAULT,
120 .flags = 0,
121 },
122 .set = sd_setexposure,
123 .get = sd_getexposure,
124 },
125#define SD_AUTOGAIN 2
126 {
127 {
128 .id = V4L2_CID_AUTOGAIN,
129 .type = V4L2_CTRL_TYPE_BOOLEAN,
130 .name = "Auto Gain",
131 .minimum = 0,
132 .maximum = 1,
133 .step = 1,
134 .default_value = 1,
135 .flags = 0,
136 },
137 .set = sd_setautogain,
138 .get = sd_getautogain,
139 },
140#define SD_GAIN 3
141 {
142 {
143 .id = V4L2_CID_GAIN,
144 .type = V4L2_CTRL_TYPE_INTEGER,
145 .name = "gain",
146 .minimum = PAC207_GAIN_MIN,
147 .maximum = PAC207_GAIN_MAX,
148 .step = 1,
149 .default_value = PAC207_GAIN_DEFAULT,
150 .flags = 0,
151 },
152 .set = sd_setgain,
153 .get = sd_getgain,
154 },
155};
156
c2446b3e
JFM
157static struct v4l2_pix_format sif_mode[] = {
158 {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
159 .bytesperline = 176,
160 .sizeimage = (176 + 2) * 144,
161 /* uncompressed, add 2 bytes / line for line header */
162 .colorspace = V4L2_COLORSPACE_SRGB,
163 .priv = 1},
164 {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
165 .bytesperline = 352,
80544d3c
HG
166 /* compressed, but only when needed (not compressed
167 when the framerate is low) */
168 .sizeimage = (352 + 2) * 288,
c2446b3e
JFM
169 .colorspace = V4L2_COLORSPACE_SRGB,
170 .priv = 0},
e2997a72
HG
171};
172
173static const __u8 pac207_sensor_init[][8] = {
174 {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0},
175 {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
176 {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
177 {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xa2, 0x02},
178 {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00},
179};
180
181 /* 48 reg_72 Rate Control end BalSize_4a =0x36 */
182static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 };
183
d43fa32f
JFM
184static const unsigned char pac207_sof_marker[5] =
185 { 0xff, 0xff, 0x00, 0xff, 0x96 };
e2997a72 186
739570bb 187static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
e2997a72
HG
188 const u8 *buffer, u16 length)
189{
190 struct usb_device *udev = gspca_dev->dev;
191 int err;
e2997a72 192
739570bb 193 memcpy(gspca_dev->usb_buf, buffer, length);
e2997a72
HG
194
195 err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
196 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
739570bb
JFM
197 0x00, index,
198 gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
e2997a72
HG
199 if (err < 0)
200 PDEBUG(D_ERR,
201 "Failed to write registers to index 0x%04X, error %d)",
202 index, err);
203
204 return err;
205}
206
207
903e10aa 208static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
e2997a72
HG
209{
210 struct usb_device *udev = gspca_dev->dev;
211 int err;
212
213 err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
214 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
215 value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
216 if (err)
217 PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
218 " value 0x%02X, error %d)", index, value, err);
219
220 return err;
221}
222
903e10aa 223static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
e2997a72
HG
224{
225 struct usb_device *udev = gspca_dev->dev;
e2997a72
HG
226 int res;
227
228 res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
229 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
739570bb
JFM
230 0x00, index,
231 gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
e2997a72
HG
232 if (res < 0) {
233 PDEBUG(D_ERR,
234 "Failed to read a register (index 0x%04X, error %d)",
235 index, res);
236 return res;
237 }
238
739570bb 239 return gspca_dev->usb_buf[0];
e2997a72
HG
240}
241
e2997a72
HG
242/* this function is called at probe time */
243static int sd_config(struct gspca_dev *gspca_dev,
244 const struct usb_device_id *id)
245{
4aa0d037 246 struct sd *sd = (struct sd *) gspca_dev;
e2997a72
HG
247 struct cam *cam;
248 u8 idreg[2];
249
250 idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
251 idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
252 idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
253 idreg[1] = idreg[1] & 0x0f;
254 PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
255 idreg[0], idreg[1]);
256
257 if (idreg[0] != 0x27) {
258 PDEBUG(D_PROBE, "Error invalid sensor ID!");
259 return -ENODEV;
260 }
261
262 pac207_write_reg(gspca_dev, 0x41, 0x00);
263 /* Bit_0=Image Format,
264 * Bit_1=LED,
265 * Bit_2=Compression test mode enable */
266 pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
267 pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */
268
269 PDEBUG(D_PROBE,
270 "Pixart PAC207BCA Image Processor and Control Chip detected"
271 " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
272
273 cam = &gspca_dev->cam;
e2997a72
HG
274 cam->epaddr = 0x05;
275 cam->cam_mode = sif_mode;
276 cam->nmodes = ARRAY_SIZE(sif_mode);
4aa0d037
JFM
277 sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
278 sd->exposure = PAC207_EXPOSURE_DEFAULT;
279 sd->gain = PAC207_GAIN_DEFAULT;
e2997a72
HG
280
281 return 0;
282}
283
284/* this function is called at open time */
285static int sd_open(struct gspca_dev *gspca_dev)
286{
287 struct sd *sd = (struct sd *) gspca_dev;
288
e2997a72 289 sd->autogain = 1;
e2997a72
HG
290 return 0;
291}
292
293/* -- start the camera -- */
294static void sd_start(struct gspca_dev *gspca_dev)
295{
296 struct sd *sd = (struct sd *) gspca_dev;
297 __u8 mode;
298
299 pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
300 pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
301 pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
302 pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
303 pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8);
304 pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8);
305 pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4);
306
307 /* Compression Balance */
308 if (gspca_dev->width == 176)
309 pac207_write_reg(gspca_dev, 0x4a, 0xff);
310 else
311 pac207_write_reg(gspca_dev, 0x4a, 0x88);
312 pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
313 pac207_write_reg(gspca_dev, 0x08, sd->brightness);
314
315 /* PGA global gain (Bit 4-0) */
316 pac207_write_reg(gspca_dev, 0x0e, sd->gain);
317 pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
318
319 mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
d43fa32f 320 if (gspca_dev->width == 176) { /* 176x144 */
e2997a72
HG
321 mode |= 0x01;
322 PDEBUG(D_STREAM, "pac207_start mode 176x144");
d43fa32f 323 } else { /* 352x288 */
e2997a72 324 PDEBUG(D_STREAM, "pac207_start mode 352x288");
d43fa32f 325 }
e2997a72
HG
326 pac207_write_reg(gspca_dev, 0x41, mode);
327
328 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
329 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
6a7eba24 330 msleep(10);
e2997a72
HG
331 pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
332
333 sd->sof_read = 0;
334 sd->autogain_ignore_frames = 0;
335 atomic_set(&sd->avg_lum, -1);
336}
337
338static void sd_stopN(struct gspca_dev *gspca_dev)
339{
340 pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
341 pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
342 pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
343}
344
345static void sd_stop0(struct gspca_dev *gspca_dev)
346{
347}
348
349/* this function is called at close time */
350static void sd_close(struct gspca_dev *gspca_dev)
351{
352}
353
e2997a72
HG
354static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
355{
356 struct sd *sd = (struct sd *) gspca_dev;
e2997a72
HG
357 int avg_lum = atomic_read(&sd->avg_lum);
358
dcef3237 359 if (avg_lum == -1)
e2997a72
HG
360 return;
361
dcef3237 362 if (sd->autogain_ignore_frames > 0)
e2997a72 363 sd->autogain_ignore_frames--;
dcef3237
HG
364 else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
365 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE,
366 PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
e2997a72 367 sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
e2997a72
HG
368}
369
370static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
50a871fe 371 unsigned char *m, int len)
e2997a72
HG
372{
373 struct sd *sd = (struct sd *) gspca_dev;
374 int i;
375
376 /* Search for the SOF marker (fixed part) in the header */
377 for (i = 0; i < len; i++) {
378 if (m[i] == pac207_sof_marker[sd->sof_read]) {
379 sd->sof_read++;
380 if (sd->sof_read == sizeof(pac207_sof_marker)) {
381 PDEBUG(D_STREAM,
382 "SOF found, bytes to analyze: %u."
383 " Frame starts at byte #%u",
384 len, i + 1);
385 sd->sof_read = 0;
386 return m + i + 1;
387 }
d43fa32f 388 } else {
e2997a72 389 sd->sof_read = 0;
d43fa32f 390 }
e2997a72
HG
391 }
392
393 return NULL;
394}
395
e2997a72
HG
396static void sd_pkt_scan(struct gspca_dev *gspca_dev,
397 struct gspca_frame *frame,
a5ae2062 398 __u8 *data,
e2997a72
HG
399 int len)
400{
ab8f12cf 401 struct sd *sd = (struct sd *) gspca_dev;
e2997a72 402 unsigned char *sof;
e2997a72
HG
403
404 sof = pac207_find_sof(gspca_dev, data, len);
e2997a72 405 if (sof) {
ab8f12cf
HG
406 int n;
407
e2997a72 408 /* finish decoding current frame */
ab8f12cf
HG
409 n = sof - data;
410 if (n > sizeof pac207_sof_marker)
411 n -= sizeof pac207_sof_marker;
412 else
413 n = 0;
414 frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
415 data, n);
416 sd->header_read = 0;
6a7eba24 417 gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
e2997a72
HG
418 len -= sof - data;
419 data = sof;
420 }
ab8f12cf
HG
421 if (sd->header_read < 11) {
422 int needed;
e2997a72 423
ab8f12cf
HG
424 /* get average lumination from frame header (byte 5) */
425 if (sd->header_read < 5) {
426 needed = 5 - sd->header_read;
427 if (len >= needed)
428 atomic_set(&sd->avg_lum, data[needed - 1]);
429 }
430 /* skip the rest of the header */
431 needed = 11 - sd->header_read;
432 if (len <= needed) {
433 sd->header_read += len;
434 return;
435 }
436 data += needed;
437 len -= needed;
438 sd->header_read = 11;
439 }
e2997a72 440
ab8f12cf 441 gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
e2997a72
HG
442}
443
444static void setbrightness(struct gspca_dev *gspca_dev)
445{
446 struct sd *sd = (struct sd *) gspca_dev;
447
448 pac207_write_reg(gspca_dev, 0x08, sd->brightness);
449 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
450 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
451}
452
453static void setexposure(struct gspca_dev *gspca_dev)
454{
455 struct sd *sd = (struct sd *) gspca_dev;
456
457 pac207_write_reg(gspca_dev, 0x02, sd->exposure);
458 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
459 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
460}
461
462static void setgain(struct gspca_dev *gspca_dev)
463{
464 struct sd *sd = (struct sd *) gspca_dev;
465
466 pac207_write_reg(gspca_dev, 0x0e, sd->gain);
467 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
468 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
469}
470
471static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
472{
473 struct sd *sd = (struct sd *) gspca_dev;
474
475 sd->brightness = val;
476 if (gspca_dev->streaming)
477 setbrightness(gspca_dev);
478 return 0;
479}
480
481static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
482{
483 struct sd *sd = (struct sd *) gspca_dev;
484
485 *val = sd->brightness;
486 return 0;
487}
488
489static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
490{
491 struct sd *sd = (struct sd *) gspca_dev;
492
e2997a72
HG
493 sd->exposure = val;
494 if (gspca_dev->streaming)
495 setexposure(gspca_dev);
496 return 0;
497}
498
499static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
500{
501 struct sd *sd = (struct sd *) gspca_dev;
502
503 *val = sd->exposure;
504 return 0;
505}
506
507static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
508{
509 struct sd *sd = (struct sd *) gspca_dev;
510
e2997a72
HG
511 sd->gain = val;
512 if (gspca_dev->streaming)
513 setgain(gspca_dev);
514 return 0;
515}
516
517static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
518{
519 struct sd *sd = (struct sd *) gspca_dev;
520
521 *val = sd->gain;
522 return 0;
523}
524
525static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
526{
527 struct sd *sd = (struct sd *) gspca_dev;
528
529 sd->autogain = val;
530 /* when switching to autogain set defaults to make sure
531 we are on a valid point of the autogain gain /
532 exposure knee graph, and give this change time to
533 take effect before doing autogain. */
534 if (sd->autogain) {
535 sd->exposure = PAC207_EXPOSURE_DEFAULT;
536 sd->gain = PAC207_GAIN_DEFAULT;
537 if (gspca_dev->streaming) {
538 sd->autogain_ignore_frames =
539 PAC207_AUTOGAIN_IGNORE_FRAMES;
540 setexposure(gspca_dev);
541 setgain(gspca_dev);
542 }
543 }
544
545 return 0;
546}
547
548static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
549{
550 struct sd *sd = (struct sd *) gspca_dev;
551
552 *val = sd->autogain;
553 return 0;
554}
555
556/* sub-driver description */
a5ae2062 557static const struct sd_desc sd_desc = {
e2997a72
HG
558 .name = MODULE_NAME,
559 .ctrls = sd_ctrls,
560 .nctrls = ARRAY_SIZE(sd_ctrls),
561 .config = sd_config,
562 .open = sd_open,
563 .start = sd_start,
564 .stopN = sd_stopN,
565 .stop0 = sd_stop0,
566 .close = sd_close,
567 .dq_callback = pac207_do_auto_gain,
568 .pkt_scan = sd_pkt_scan,
569};
570
571/* -- module initialisation -- */
a5ae2062 572static const __devinitdata struct usb_device_id device_table[] = {
9d64fdb1
JFM
573 {USB_DEVICE(0x041e, 0x4028)},
574 {USB_DEVICE(0x093a, 0x2460)},
575 {USB_DEVICE(0x093a, 0x2463)},
576 {USB_DEVICE(0x093a, 0x2464)},
577 {USB_DEVICE(0x093a, 0x2468)},
578 {USB_DEVICE(0x093a, 0x2470)},
579 {USB_DEVICE(0x093a, 0x2471)},
580 {USB_DEVICE(0x093a, 0x2472)},
581 {USB_DEVICE(0x2001, 0xf115)},
e2997a72
HG
582 {}
583};
584MODULE_DEVICE_TABLE(usb, device_table);
585
586/* -- device connect -- */
587static int sd_probe(struct usb_interface *intf,
588 const struct usb_device_id *id)
589{
d43fa32f
JFM
590 return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
591 THIS_MODULE);
e2997a72
HG
592}
593
594static struct usb_driver sd_driver = {
595 .name = MODULE_NAME,
596 .id_table = device_table,
597 .probe = sd_probe,
598 .disconnect = gspca_disconnect,
599};
600
601/* -- module insert / remove -- */
602static int __init sd_mod_init(void)
603{
e2997a72
HG
604 if (usb_register(&sd_driver) < 0)
605 return -1;
903e10aa 606 PDEBUG(D_PROBE, "registered");
e2997a72
HG
607 return 0;
608}
609static void __exit sd_mod_exit(void)
610{
611 usb_deregister(&sd_driver);
612 PDEBUG(D_PROBE, "deregistered");
613}
614
615module_init(sd_mod_init);
616module_exit(sd_mod_exit);