diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/gspca/stk014.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/gspca/stk014.c')
-rw-r--r-- | drivers/media/video/gspca/stk014.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c new file mode 100644 index 00000000000..763747700f1 --- /dev/null +++ b/drivers/media/video/gspca/stk014.c | |||
@@ -0,0 +1,531 @@ | |||
1 | /* | ||
2 | * Syntek DV4000 (STK014) subdriver | ||
3 | * | ||
4 | * Copyright (C) 2008 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 | |||
21 | #define MODULE_NAME "stk014" | ||
22 | |||
23 | #include "gspca.h" | ||
24 | #include "jpeg.h" | ||
25 | |||
26 | MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); | ||
27 | MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | /* controls */ | ||
31 | enum e_ctrl { | ||
32 | BRIGHTNESS, | ||
33 | CONTRAST, | ||
34 | COLORS, | ||
35 | LIGHTFREQ, | ||
36 | NCTRLS /* number of controls */ | ||
37 | }; | ||
38 | |||
39 | /* specific webcam descriptor */ | ||
40 | struct sd { | ||
41 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
42 | |||
43 | struct gspca_ctrl ctrls[NCTRLS]; | ||
44 | |||
45 | u8 quality; | ||
46 | #define QUALITY_MIN 70 | ||
47 | #define QUALITY_MAX 95 | ||
48 | #define QUALITY_DEF 88 | ||
49 | |||
50 | u8 jpeg_hdr[JPEG_HDR_SZ]; | ||
51 | }; | ||
52 | |||
53 | /* V4L2 controls supported by the driver */ | ||
54 | static void setbrightness(struct gspca_dev *gspca_dev); | ||
55 | static void setcontrast(struct gspca_dev *gspca_dev); | ||
56 | static void setcolors(struct gspca_dev *gspca_dev); | ||
57 | static void setlightfreq(struct gspca_dev *gspca_dev); | ||
58 | |||
59 | static const struct ctrl sd_ctrls[NCTRLS] = { | ||
60 | [BRIGHTNESS] = { | ||
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 | .default_value = 127, | ||
69 | }, | ||
70 | .set_control = setbrightness | ||
71 | }, | ||
72 | [CONTRAST] = { | ||
73 | { | ||
74 | .id = V4L2_CID_CONTRAST, | ||
75 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
76 | .name = "Contrast", | ||
77 | .minimum = 0, | ||
78 | .maximum = 255, | ||
79 | .step = 1, | ||
80 | .default_value = 127, | ||
81 | }, | ||
82 | .set_control = setcontrast | ||
83 | }, | ||
84 | [COLORS] = { | ||
85 | { | ||
86 | .id = V4L2_CID_SATURATION, | ||
87 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
88 | .name = "Color", | ||
89 | .minimum = 0, | ||
90 | .maximum = 255, | ||
91 | .step = 1, | ||
92 | .default_value = 127, | ||
93 | }, | ||
94 | .set_control = setcolors | ||
95 | }, | ||
96 | [LIGHTFREQ] = { | ||
97 | { | ||
98 | .id = V4L2_CID_POWER_LINE_FREQUENCY, | ||
99 | .type = V4L2_CTRL_TYPE_MENU, | ||
100 | .name = "Light frequency filter", | ||
101 | .minimum = 1, | ||
102 | .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ | ||
103 | .step = 1, | ||
104 | .default_value = 1, | ||
105 | }, | ||
106 | .set_control = setlightfreq | ||
107 | }, | ||
108 | }; | ||
109 | |||
110 | static const struct v4l2_pix_format vga_mode[] = { | ||
111 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, | ||
112 | .bytesperline = 320, | ||
113 | .sizeimage = 320 * 240 * 3 / 8 + 590, | ||
114 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
115 | .priv = 1}, | ||
116 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, | ||
117 | .bytesperline = 640, | ||
118 | .sizeimage = 640 * 480 * 3 / 8 + 590, | ||
119 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
120 | .priv = 0}, | ||
121 | }; | ||
122 | |||
123 | /* -- read a register -- */ | ||
124 | static u8 reg_r(struct gspca_dev *gspca_dev, | ||
125 | __u16 index) | ||
126 | { | ||
127 | struct usb_device *dev = gspca_dev->dev; | ||
128 | int ret; | ||
129 | |||
130 | if (gspca_dev->usb_err < 0) | ||
131 | return 0; | ||
132 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), | ||
133 | 0x00, | ||
134 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
135 | 0x00, | ||
136 | index, | ||
137 | gspca_dev->usb_buf, 1, | ||
138 | 500); | ||
139 | if (ret < 0) { | ||
140 | err("reg_r err %d", ret); | ||
141 | gspca_dev->usb_err = ret; | ||
142 | return 0; | ||
143 | } | ||
144 | return gspca_dev->usb_buf[0]; | ||
145 | } | ||
146 | |||
147 | /* -- write a register -- */ | ||
148 | static void reg_w(struct gspca_dev *gspca_dev, | ||
149 | __u16 index, __u16 value) | ||
150 | { | ||
151 | struct usb_device *dev = gspca_dev->dev; | ||
152 | int ret; | ||
153 | |||
154 | if (gspca_dev->usb_err < 0) | ||
155 | return; | ||
156 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | ||
157 | 0x01, | ||
158 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
159 | value, | ||
160 | index, | ||
161 | NULL, | ||
162 | 0, | ||
163 | 500); | ||
164 | if (ret < 0) { | ||
165 | err("reg_w err %d", ret); | ||
166 | gspca_dev->usb_err = ret; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /* -- get a bulk value (4 bytes) -- */ | ||
171 | static void rcv_val(struct gspca_dev *gspca_dev, | ||
172 | int ads) | ||
173 | { | ||
174 | struct usb_device *dev = gspca_dev->dev; | ||
175 | int alen, ret; | ||
176 | |||
177 | reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff); | ||
178 | reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff); | ||
179 | reg_w(gspca_dev, 0x636, ads & 0xff); | ||
180 | reg_w(gspca_dev, 0x637, 0); | ||
181 | reg_w(gspca_dev, 0x638, 4); /* len & 0xff */ | ||
182 | reg_w(gspca_dev, 0x639, 0); /* len >> 8 */ | ||
183 | reg_w(gspca_dev, 0x63a, 0); | ||
184 | reg_w(gspca_dev, 0x63b, 0); | ||
185 | reg_w(gspca_dev, 0x630, 5); | ||
186 | if (gspca_dev->usb_err < 0) | ||
187 | return; | ||
188 | ret = usb_bulk_msg(dev, | ||
189 | usb_rcvbulkpipe(dev, 0x05), | ||
190 | gspca_dev->usb_buf, | ||
191 | 4, /* length */ | ||
192 | &alen, | ||
193 | 500); /* timeout in milliseconds */ | ||
194 | if (ret < 0) { | ||
195 | err("rcv_val err %d", ret); | ||
196 | gspca_dev->usb_err = ret; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /* -- send a bulk value -- */ | ||
201 | static void snd_val(struct gspca_dev *gspca_dev, | ||
202 | int ads, | ||
203 | unsigned int val) | ||
204 | { | ||
205 | struct usb_device *dev = gspca_dev->dev; | ||
206 | int alen, ret; | ||
207 | __u8 seq = 0; | ||
208 | |||
209 | if (ads == 0x003f08) { | ||
210 | reg_r(gspca_dev, 0x0704); | ||
211 | seq = reg_r(gspca_dev, 0x0705); | ||
212 | reg_r(gspca_dev, 0x0650); | ||
213 | reg_w(gspca_dev, 0x654, seq); | ||
214 | } else { | ||
215 | reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff); | ||
216 | } | ||
217 | reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff); | ||
218 | reg_w(gspca_dev, 0x656, ads & 0xff); | ||
219 | reg_w(gspca_dev, 0x657, 0); | ||
220 | reg_w(gspca_dev, 0x658, 0x04); /* size */ | ||
221 | reg_w(gspca_dev, 0x659, 0); | ||
222 | reg_w(gspca_dev, 0x65a, 0); | ||
223 | reg_w(gspca_dev, 0x65b, 0); | ||
224 | reg_w(gspca_dev, 0x650, 5); | ||
225 | if (gspca_dev->usb_err < 0) | ||
226 | return; | ||
227 | gspca_dev->usb_buf[0] = val >> 24; | ||
228 | gspca_dev->usb_buf[1] = val >> 16; | ||
229 | gspca_dev->usb_buf[2] = val >> 8; | ||
230 | gspca_dev->usb_buf[3] = val; | ||
231 | ret = usb_bulk_msg(dev, | ||
232 | usb_sndbulkpipe(dev, 6), | ||
233 | gspca_dev->usb_buf, | ||
234 | 4, | ||
235 | &alen, | ||
236 | 500); /* timeout in milliseconds */ | ||
237 | if (ret < 0) { | ||
238 | err("snd_val err %d", ret); | ||
239 | gspca_dev->usb_err = ret; | ||
240 | } else { | ||
241 | if (ads == 0x003f08) { | ||
242 | seq += 4; | ||
243 | seq &= 0x3f; | ||
244 | reg_w(gspca_dev, 0x705, seq); | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | /* set a camera parameter */ | ||
250 | static void set_par(struct gspca_dev *gspca_dev, | ||
251 | int parval) | ||
252 | { | ||
253 | snd_val(gspca_dev, 0x003f08, parval); | ||
254 | } | ||
255 | |||
256 | static void setbrightness(struct gspca_dev *gspca_dev) | ||
257 | { | ||
258 | struct sd *sd = (struct sd *) gspca_dev; | ||
259 | int parval; | ||
260 | |||
261 | parval = 0x06000000 /* whiteness */ | ||
262 | + (sd->ctrls[BRIGHTNESS].val << 16); | ||
263 | set_par(gspca_dev, parval); | ||
264 | } | ||
265 | |||
266 | static void setcontrast(struct gspca_dev *gspca_dev) | ||
267 | { | ||
268 | struct sd *sd = (struct sd *) gspca_dev; | ||
269 | int parval; | ||
270 | |||
271 | parval = 0x07000000 /* contrast */ | ||
272 | + (sd->ctrls[CONTRAST].val << 16); | ||
273 | set_par(gspca_dev, parval); | ||
274 | } | ||
275 | |||
276 | static void setcolors(struct gspca_dev *gspca_dev) | ||
277 | { | ||
278 | struct sd *sd = (struct sd *) gspca_dev; | ||
279 | int parval; | ||
280 | |||
281 | parval = 0x08000000 /* saturation */ | ||
282 | + (sd->ctrls[COLORS].val << 16); | ||
283 | set_par(gspca_dev, parval); | ||
284 | } | ||
285 | |||
286 | static void setlightfreq(struct gspca_dev *gspca_dev) | ||
287 | { | ||
288 | struct sd *sd = (struct sd *) gspca_dev; | ||
289 | |||
290 | set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1 | ||
291 | ? 0x33640000 /* 50 Hz */ | ||
292 | : 0x33780000); /* 60 Hz */ | ||
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.ctrls = sd->ctrls; | ||
304 | sd->quality = QUALITY_DEF; | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | /* this function is called at probe and resume time */ | ||
309 | static int sd_init(struct gspca_dev *gspca_dev) | ||
310 | { | ||
311 | u8 ret; | ||
312 | |||
313 | /* check if the device responds */ | ||
314 | usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); | ||
315 | ret = reg_r(gspca_dev, 0x0740); | ||
316 | if (gspca_dev->usb_err >= 0) { | ||
317 | if (ret != 0xff) { | ||
318 | err("init reg: 0x%02x", ret); | ||
319 | gspca_dev->usb_err = -EIO; | ||
320 | } | ||
321 | } | ||
322 | return gspca_dev->usb_err; | ||
323 | } | ||
324 | |||
325 | /* -- start the camera -- */ | ||
326 | static int sd_start(struct gspca_dev *gspca_dev) | ||
327 | { | ||
328 | struct sd *sd = (struct sd *) gspca_dev; | ||
329 | int ret, value; | ||
330 | |||
331 | /* create the JPEG header */ | ||
332 | jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, | ||
333 | 0x22); /* JPEG 411 */ | ||
334 | jpeg_set_qual(sd->jpeg_hdr, sd->quality); | ||
335 | |||
336 | /* work on alternate 1 */ | ||
337 | usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); | ||
338 | |||
339 | set_par(gspca_dev, 0x10000000); | ||
340 | set_par(gspca_dev, 0x00000000); | ||
341 | set_par(gspca_dev, 0x8002e001); | ||
342 | set_par(gspca_dev, 0x14000000); | ||
343 | if (gspca_dev->width > 320) | ||
344 | value = 0x8002e001; /* 640x480 */ | ||
345 | else | ||
346 | value = 0x4001f000; /* 320x240 */ | ||
347 | set_par(gspca_dev, value); | ||
348 | ret = usb_set_interface(gspca_dev->dev, | ||
349 | gspca_dev->iface, | ||
350 | gspca_dev->alt); | ||
351 | if (ret < 0) { | ||
352 | err("set intf %d %d failed", | ||
353 | gspca_dev->iface, gspca_dev->alt); | ||
354 | gspca_dev->usb_err = ret; | ||
355 | goto out; | ||
356 | } | ||
357 | reg_r(gspca_dev, 0x0630); | ||
358 | rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ | ||
359 | reg_r(gspca_dev, 0x0650); | ||
360 | snd_val(gspca_dev, 0x000020, 0xffffffff); | ||
361 | reg_w(gspca_dev, 0x0620, 0); | ||
362 | reg_w(gspca_dev, 0x0630, 0); | ||
363 | reg_w(gspca_dev, 0x0640, 0); | ||
364 | reg_w(gspca_dev, 0x0650, 0); | ||
365 | reg_w(gspca_dev, 0x0660, 0); | ||
366 | setbrightness(gspca_dev); /* whiteness */ | ||
367 | setcontrast(gspca_dev); /* contrast */ | ||
368 | setcolors(gspca_dev); /* saturation */ | ||
369 | set_par(gspca_dev, 0x09800000); /* Red ? */ | ||
370 | set_par(gspca_dev, 0x0a800000); /* Green ? */ | ||
371 | set_par(gspca_dev, 0x0b800000); /* Blue ? */ | ||
372 | set_par(gspca_dev, 0x0d030000); /* Gamma ? */ | ||
373 | setlightfreq(gspca_dev); | ||
374 | |||
375 | /* start the video flow */ | ||
376 | set_par(gspca_dev, 0x01000000); | ||
377 | set_par(gspca_dev, 0x01000000); | ||
378 | if (gspca_dev->usb_err >= 0) | ||
379 | PDEBUG(D_STREAM, "camera started alt: 0x%02x", | ||
380 | gspca_dev->alt); | ||
381 | out: | ||
382 | return gspca_dev->usb_err; | ||
383 | } | ||
384 | |||
385 | static void sd_stopN(struct gspca_dev *gspca_dev) | ||
386 | { | ||
387 | struct usb_device *dev = gspca_dev->dev; | ||
388 | |||
389 | set_par(gspca_dev, 0x02000000); | ||
390 | set_par(gspca_dev, 0x02000000); | ||
391 | usb_set_interface(dev, gspca_dev->iface, 1); | ||
392 | reg_r(gspca_dev, 0x0630); | ||
393 | rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ | ||
394 | reg_r(gspca_dev, 0x0650); | ||
395 | snd_val(gspca_dev, 0x000020, 0xffffffff); | ||
396 | reg_w(gspca_dev, 0x0620, 0); | ||
397 | reg_w(gspca_dev, 0x0630, 0); | ||
398 | reg_w(gspca_dev, 0x0640, 0); | ||
399 | reg_w(gspca_dev, 0x0650, 0); | ||
400 | reg_w(gspca_dev, 0x0660, 0); | ||
401 | PDEBUG(D_STREAM, "camera stopped"); | ||
402 | } | ||
403 | |||
404 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | ||
405 | u8 *data, /* isoc packet */ | ||
406 | int len) /* iso packet length */ | ||
407 | { | ||
408 | struct sd *sd = (struct sd *) gspca_dev; | ||
409 | static unsigned char ffd9[] = {0xff, 0xd9}; | ||
410 | |||
411 | /* a frame starts with: | ||
412 | * - 0xff 0xfe | ||
413 | * - 0x08 0x00 - length (little endian ?!) | ||
414 | * - 4 bytes = size of whole frame (BE - including header) | ||
415 | * - 0x00 0x0c | ||
416 | * - 0xff 0xd8 | ||
417 | * - .. JPEG image with escape sequences (ff 00) | ||
418 | * (without ending - ff d9) | ||
419 | */ | ||
420 | if (data[0] == 0xff && data[1] == 0xfe) { | ||
421 | gspca_frame_add(gspca_dev, LAST_PACKET, | ||
422 | ffd9, 2); | ||
423 | |||
424 | /* put the JPEG 411 header */ | ||
425 | gspca_frame_add(gspca_dev, FIRST_PACKET, | ||
426 | sd->jpeg_hdr, JPEG_HDR_SZ); | ||
427 | |||
428 | /* beginning of the frame */ | ||
429 | #define STKHDRSZ 12 | ||
430 | data += STKHDRSZ; | ||
431 | len -= STKHDRSZ; | ||
432 | } | ||
433 | gspca_frame_add(gspca_dev, INTER_PACKET, data, len); | ||
434 | } | ||
435 | |||
436 | static int sd_querymenu(struct gspca_dev *gspca_dev, | ||
437 | struct v4l2_querymenu *menu) | ||
438 | { | ||
439 | static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; | ||
440 | |||
441 | switch (menu->id) { | ||
442 | case V4L2_CID_POWER_LINE_FREQUENCY: | ||
443 | if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm)) | ||
444 | break; | ||
445 | strcpy((char *) menu->name, freq_nm[menu->index]); | ||
446 | return 0; | ||
447 | } | ||
448 | return -EINVAL; | ||
449 | } | ||
450 | |||
451 | static int sd_set_jcomp(struct gspca_dev *gspca_dev, | ||
452 | struct v4l2_jpegcompression *jcomp) | ||
453 | { | ||
454 | struct sd *sd = (struct sd *) gspca_dev; | ||
455 | |||
456 | if (jcomp->quality < QUALITY_MIN) | ||
457 | sd->quality = QUALITY_MIN; | ||
458 | else if (jcomp->quality > QUALITY_MAX) | ||
459 | sd->quality = QUALITY_MAX; | ||
460 | else | ||
461 | sd->quality = jcomp->quality; | ||
462 | if (gspca_dev->streaming) | ||
463 | jpeg_set_qual(sd->jpeg_hdr, sd->quality); | ||
464 | return gspca_dev->usb_err; | ||
465 | } | ||
466 | |||
467 | static int sd_get_jcomp(struct gspca_dev *gspca_dev, | ||
468 | struct v4l2_jpegcompression *jcomp) | ||
469 | { | ||
470 | struct sd *sd = (struct sd *) gspca_dev; | ||
471 | |||
472 | memset(jcomp, 0, sizeof *jcomp); | ||
473 | jcomp->quality = sd->quality; | ||
474 | jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | ||
475 | | V4L2_JPEG_MARKER_DQT; | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | /* sub-driver description */ | ||
480 | static const struct sd_desc sd_desc = { | ||
481 | .name = MODULE_NAME, | ||
482 | .ctrls = sd_ctrls, | ||
483 | .nctrls = NCTRLS, | ||
484 | .config = sd_config, | ||
485 | .init = sd_init, | ||
486 | .start = sd_start, | ||
487 | .stopN = sd_stopN, | ||
488 | .pkt_scan = sd_pkt_scan, | ||
489 | .querymenu = sd_querymenu, | ||
490 | .get_jcomp = sd_get_jcomp, | ||
491 | .set_jcomp = sd_set_jcomp, | ||
492 | }; | ||
493 | |||
494 | /* -- module initialisation -- */ | ||
495 | static const struct usb_device_id device_table[] = { | ||
496 | {USB_DEVICE(0x05e1, 0x0893)}, | ||
497 | {} | ||
498 | }; | ||
499 | MODULE_DEVICE_TABLE(usb, device_table); | ||
500 | |||
501 | /* -- device connect -- */ | ||
502 | static int sd_probe(struct usb_interface *intf, | ||
503 | const struct usb_device_id *id) | ||
504 | { | ||
505 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | ||
506 | THIS_MODULE); | ||
507 | } | ||
508 | |||
509 | static struct usb_driver sd_driver = { | ||
510 | .name = MODULE_NAME, | ||
511 | .id_table = device_table, | ||
512 | .probe = sd_probe, | ||
513 | .disconnect = gspca_disconnect, | ||
514 | #ifdef CONFIG_PM | ||
515 | .suspend = gspca_suspend, | ||
516 | .resume = gspca_resume, | ||
517 | #endif | ||
518 | }; | ||
519 | |||
520 | /* -- module insert / remove -- */ | ||
521 | static int __init sd_mod_init(void) | ||
522 | { | ||
523 | return usb_register(&sd_driver); | ||
524 | } | ||
525 | static void __exit sd_mod_exit(void) | ||
526 | { | ||
527 | usb_deregister(&sd_driver); | ||
528 | } | ||
529 | |||
530 | module_init(sd_mod_init); | ||
531 | module_exit(sd_mod_exit); | ||