1 /*
2  * Pixart PAC207BCA library
3  *
4  * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
27 
28 #define MODULE_NAME "pac207"
29 
30 #include <linux/input.h>
31 #include "gspca.h"
32 
33 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
34 MODULE_DESCRIPTION("Pixart PAC207");
35 MODULE_LICENSE("GPL");
36 
37 #define PAC207_CTRL_TIMEOUT		100  /* ms */
38 
39 #define PAC207_BRIGHTNESS_MIN		0
40 #define PAC207_BRIGHTNESS_MAX		255
41 #define PAC207_BRIGHTNESS_DEFAULT	46
42 
43 #define PAC207_EXPOSURE_MIN		3
44 #define PAC207_EXPOSURE_MAX		90 /* 1 sec expo time / 1 fps */
45 #define PAC207_EXPOSURE_DEFAULT		5 /* power on default: 3 */
46 #define PAC207_EXPOSURE_KNEE		9 /* fps: 90 / exposure -> 9: 10 fps */
47 
48 #define PAC207_GAIN_MIN			0
49 #define PAC207_GAIN_MAX			31
50 #define PAC207_GAIN_DEFAULT		7 /* power on default: 9 */
51 #define PAC207_GAIN_KNEE		15
52 
53 #define PAC207_AUTOGAIN_DEADZONE	30
54 
55 /* specific webcam descriptor */
56 struct sd {
57 	struct gspca_dev gspca_dev;		/* !! must be the first item */
58 
59 	u8 mode;
60 
61 	u8 brightness;
62 	u8 exposure;
63 	u8 autogain;
64 	u8 gain;
65 
66 	u8 sof_read;
67 	u8 header_read;
68 	u8 autogain_ignore_frames;
69 
70 	atomic_t avg_lum;
71 };
72 
73 /* V4L2 controls supported by the driver */
74 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
75 static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
76 static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
77 static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
78 static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
79 static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
80 static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
81 static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
82 
83 static const struct ctrl sd_ctrls[] = {
84 #define SD_BRIGHTNESS 0
85 	{
86 	    {
87 		.id      = V4L2_CID_BRIGHTNESS,
88 		.type    = V4L2_CTRL_TYPE_INTEGER,
89 		.name    = "Brightness",
90 		.minimum = PAC207_BRIGHTNESS_MIN,
91 		.maximum = PAC207_BRIGHTNESS_MAX,
92 		.step = 1,
93 		.default_value = PAC207_BRIGHTNESS_DEFAULT,
94 		.flags = 0,
95 	    },
96 	    .set = sd_setbrightness,
97 	    .get = sd_getbrightness,
98 	},
99 #define SD_EXPOSURE 1
100 	{
101 	    {
102 		.id = V4L2_CID_EXPOSURE,
103 		.type = V4L2_CTRL_TYPE_INTEGER,
104 		.name = "Exposure",
105 		.minimum = PAC207_EXPOSURE_MIN,
106 		.maximum = PAC207_EXPOSURE_MAX,
107 		.step = 1,
108 		.default_value = PAC207_EXPOSURE_DEFAULT,
109 		.flags = 0,
110 	    },
111 	    .set = sd_setexposure,
112 	    .get = sd_getexposure,
113 	},
114 #define SD_AUTOGAIN 2
115 	{
116 	    {
117 		.id	  = V4L2_CID_AUTOGAIN,
118 		.type	= V4L2_CTRL_TYPE_BOOLEAN,
119 		.name	= "Auto Gain",
120 		.minimum = 0,
121 		.maximum = 1,
122 		.step	= 1,
123 #define AUTOGAIN_DEF 1
124 		.default_value = AUTOGAIN_DEF,
125 		.flags = 0,
126 	    },
127 	    .set = sd_setautogain,
128 	    .get = sd_getautogain,
129 	},
130 #define SD_GAIN 3
131 	{
132 	    {
133 		.id = V4L2_CID_GAIN,
134 		.type = V4L2_CTRL_TYPE_INTEGER,
135 		.name = "Gain",
136 		.minimum = PAC207_GAIN_MIN,
137 		.maximum = PAC207_GAIN_MAX,
138 		.step = 1,
139 		.default_value = PAC207_GAIN_DEFAULT,
140 		.flags = 0,
141 	    },
142 	    .set = sd_setgain,
143 	    .get = sd_getgain,
144 	},
145 };
146 
147 static const struct v4l2_pix_format sif_mode[] = {
148 	{176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
149 		.bytesperline = 176,
150 		.sizeimage = (176 + 2) * 144,
151 			/* uncompressed, add 2 bytes / line for line header */
152 		.colorspace = V4L2_COLORSPACE_SRGB,
153 		.priv = 1},
154 	{352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
155 		.bytesperline = 352,
156 			/* compressed, but only when needed (not compressed
157 			   when the framerate is low) */
158 		.sizeimage = (352 + 2) * 288,
159 		.colorspace = V4L2_COLORSPACE_SRGB,
160 		.priv = 0},
161 };
162 
163 static const __u8 pac207_sensor_init[][8] = {
164 	{0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84},
165 	{0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
166 	{0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
167 	{0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00},
168 };
169 
pac207_write_regs(struct gspca_dev * gspca_dev,u16 index,const u8 * buffer,u16 length)170 static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
171 	const u8 *buffer, u16 length)
172 {
173 	struct usb_device *udev = gspca_dev->dev;
174 	int err;
175 
176 	memcpy(gspca_dev->usb_buf, buffer, length);
177 
178 	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
179 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
180 			0x00, index,
181 			gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
182 	if (err < 0)
183 		pr_err("Failed to write registers to index 0x%04X, error %d\n",
184 		       index, err);
185 
186 	return err;
187 }
188 
189 
pac207_write_reg(struct gspca_dev * gspca_dev,u16 index,u16 value)190 static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
191 {
192 	struct usb_device *udev = gspca_dev->dev;
193 	int err;
194 
195 	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
196 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
197 			value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
198 	if (err)
199 		pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
200 		       index, value, err);
201 
202 	return err;
203 }
204 
pac207_read_reg(struct gspca_dev * gspca_dev,u16 index)205 static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
206 {
207 	struct usb_device *udev = gspca_dev->dev;
208 	int res;
209 
210 	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
211 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
212 			0x00, index,
213 			gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
214 	if (res < 0) {
215 		pr_err("Failed to read a register (index 0x%04X, error %d)\n",
216 		       index, res);
217 		return res;
218 	}
219 
220 	return gspca_dev->usb_buf[0];
221 }
222 
223 /* this function is called at probe time */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)224 static int sd_config(struct gspca_dev *gspca_dev,
225 			const struct usb_device_id *id)
226 {
227 	struct sd *sd = (struct sd *) gspca_dev;
228 	struct cam *cam;
229 	u8 idreg[2];
230 
231 	idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
232 	idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
233 	idreg[0] = ((idreg[0] & 0x0f) << 4) | ((idreg[1] & 0xf0) >> 4);
234 	idreg[1] = idreg[1] & 0x0f;
235 	PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
236 		idreg[0], idreg[1]);
237 
238 	if (idreg[0] != 0x27) {
239 		PDEBUG(D_PROBE, "Error invalid sensor ID!");
240 		return -ENODEV;
241 	}
242 
243 	PDEBUG(D_PROBE,
244 		"Pixart PAC207BCA Image Processor and Control Chip detected"
245 		" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
246 
247 	cam = &gspca_dev->cam;
248 	cam->cam_mode = sif_mode;
249 	cam->nmodes = ARRAY_SIZE(sif_mode);
250 	sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
251 	sd->exposure = PAC207_EXPOSURE_DEFAULT;
252 	sd->gain = PAC207_GAIN_DEFAULT;
253 	sd->autogain = AUTOGAIN_DEF;
254 
255 	return 0;
256 }
257 
258 /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)259 static int sd_init(struct gspca_dev *gspca_dev)
260 {
261 	pac207_write_reg(gspca_dev, 0x41, 0x00);
262 				/* Bit_0=Image Format,
263 				 * Bit_1=LED,
264 				 * Bit_2=Compression test mode enable */
265 	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
266 
267 	return 0;
268 }
269 
270 /* -- start the camera -- */
sd_start(struct gspca_dev * gspca_dev)271 static int sd_start(struct gspca_dev *gspca_dev)
272 {
273 	struct sd *sd = (struct sd *) gspca_dev;
274 	__u8 mode;
275 
276 	pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
277 	pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
278 	pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
279 	pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
280 	pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8);
281 
282 	/* Compression Balance */
283 	if (gspca_dev->width == 176)
284 		pac207_write_reg(gspca_dev, 0x4a, 0xff);
285 	else
286 		pac207_write_reg(gspca_dev, 0x4a, 0x30);
287 	pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
288 	pac207_write_reg(gspca_dev, 0x08, sd->brightness);
289 
290 	/* PGA global gain (Bit 4-0) */
291 	pac207_write_reg(gspca_dev, 0x0e, sd->gain);
292 	pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
293 
294 	mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
295 	if (gspca_dev->width == 176) {	/* 176x144 */
296 		mode |= 0x01;
297 		PDEBUG(D_STREAM, "pac207_start mode 176x144");
298 	} else {				/* 352x288 */
299 		PDEBUG(D_STREAM, "pac207_start mode 352x288");
300 	}
301 	pac207_write_reg(gspca_dev, 0x41, mode);
302 
303 	pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
304 	pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
305 	msleep(10);
306 	pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
307 
308 	sd->sof_read = 0;
309 	sd->autogain_ignore_frames = 0;
310 	atomic_set(&sd->avg_lum, -1);
311 	return 0;
312 }
313 
sd_stopN(struct gspca_dev * gspca_dev)314 static void sd_stopN(struct gspca_dev *gspca_dev)
315 {
316 	pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
317 	pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
318 	pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
319 }
320 
321 /* Include pac common sof detection functions */
322 #include "pac_common.h"
323 
pac207_do_auto_gain(struct gspca_dev * gspca_dev)324 static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
325 {
326 	struct sd *sd = (struct sd *) gspca_dev;
327 	int avg_lum = atomic_read(&sd->avg_lum);
328 
329 	if (avg_lum == -1)
330 		return;
331 
332 	if (sd->autogain_ignore_frames > 0)
333 		sd->autogain_ignore_frames--;
334 	else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
335 			90, PAC207_AUTOGAIN_DEADZONE,
336 			PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
337 		sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
338 }
339 
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)340 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
341 			u8 *data,
342 			int len)
343 {
344 	struct sd *sd = (struct sd *) gspca_dev;
345 	unsigned char *sof;
346 
347 	sof = pac_find_sof(&sd->sof_read, data, len);
348 	if (sof) {
349 		int n;
350 
351 		/* finish decoding current frame */
352 		n = sof - data;
353 		if (n > sizeof pac_sof_marker)
354 			n -= sizeof pac_sof_marker;
355 		else
356 			n = 0;
357 		gspca_frame_add(gspca_dev, LAST_PACKET,
358 				data, n);
359 		sd->header_read = 0;
360 		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
361 		len -= sof - data;
362 		data = sof;
363 	}
364 	if (sd->header_read < 11) {
365 		int needed;
366 
367 		/* get average lumination from frame header (byte 5) */
368 		if (sd->header_read < 5) {
369 			needed = 5 - sd->header_read;
370 			if (len >= needed)
371 				atomic_set(&sd->avg_lum, data[needed - 1]);
372 		}
373 		/* skip the rest of the header */
374 		needed = 11 - sd->header_read;
375 		if (len <= needed) {
376 			sd->header_read += len;
377 			return;
378 		}
379 		data += needed;
380 		len -= needed;
381 		sd->header_read = 11;
382 	}
383 
384 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
385 }
386 
setbrightness(struct gspca_dev * gspca_dev)387 static void setbrightness(struct gspca_dev *gspca_dev)
388 {
389 	struct sd *sd = (struct sd *) gspca_dev;
390 
391 	pac207_write_reg(gspca_dev, 0x08, sd->brightness);
392 	pac207_write_reg(gspca_dev, 0x13, 0x01);	/* Bit 0, auto clear */
393 	pac207_write_reg(gspca_dev, 0x1c, 0x01);	/* not documented */
394 }
395 
setexposure(struct gspca_dev * gspca_dev)396 static void setexposure(struct gspca_dev *gspca_dev)
397 {
398 	struct sd *sd = (struct sd *) gspca_dev;
399 
400 	pac207_write_reg(gspca_dev, 0x02, sd->exposure);
401 	pac207_write_reg(gspca_dev, 0x13, 0x01);	/* Bit 0, auto clear */
402 	pac207_write_reg(gspca_dev, 0x1c, 0x01);	/* not documented */
403 }
404 
setgain(struct gspca_dev * gspca_dev)405 static void setgain(struct gspca_dev *gspca_dev)
406 {
407 	struct sd *sd = (struct sd *) gspca_dev;
408 
409 	pac207_write_reg(gspca_dev, 0x0e, sd->gain);
410 	pac207_write_reg(gspca_dev, 0x13, 0x01);	/* Bit 0, auto clear */
411 	pac207_write_reg(gspca_dev, 0x1c, 0x01);	/* not documented */
412 }
413 
sd_setbrightness(struct gspca_dev * gspca_dev,__s32 val)414 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
415 {
416 	struct sd *sd = (struct sd *) gspca_dev;
417 
418 	sd->brightness = val;
419 	if (gspca_dev->streaming)
420 		setbrightness(gspca_dev);
421 	return 0;
422 }
423 
sd_getbrightness(struct gspca_dev * gspca_dev,__s32 * val)424 static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
425 {
426 	struct sd *sd = (struct sd *) gspca_dev;
427 
428 	*val = sd->brightness;
429 	return 0;
430 }
431 
sd_setexposure(struct gspca_dev * gspca_dev,__s32 val)432 static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
433 {
434 	struct sd *sd = (struct sd *) gspca_dev;
435 
436 	sd->exposure = val;
437 	if (gspca_dev->streaming)
438 		setexposure(gspca_dev);
439 	return 0;
440 }
441 
sd_getexposure(struct gspca_dev * gspca_dev,__s32 * val)442 static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
443 {
444 	struct sd *sd = (struct sd *) gspca_dev;
445 
446 	*val = sd->exposure;
447 	return 0;
448 }
449 
sd_setgain(struct gspca_dev * gspca_dev,__s32 val)450 static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
451 {
452 	struct sd *sd = (struct sd *) gspca_dev;
453 
454 	sd->gain = val;
455 	if (gspca_dev->streaming)
456 		setgain(gspca_dev);
457 	return 0;
458 }
459 
sd_getgain(struct gspca_dev * gspca_dev,__s32 * val)460 static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
461 {
462 	struct sd *sd = (struct sd *) gspca_dev;
463 
464 	*val = sd->gain;
465 	return 0;
466 }
467 
sd_setautogain(struct gspca_dev * gspca_dev,__s32 val)468 static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
469 {
470 	struct sd *sd = (struct sd *) gspca_dev;
471 
472 	sd->autogain = val;
473 	/* when switching to autogain set defaults to make sure
474 	   we are on a valid point of the autogain gain /
475 	   exposure knee graph, and give this change time to
476 	   take effect before doing autogain. */
477 	if (sd->autogain) {
478 		sd->exposure = PAC207_EXPOSURE_DEFAULT;
479 		sd->gain = PAC207_GAIN_DEFAULT;
480 		if (gspca_dev->streaming) {
481 			sd->autogain_ignore_frames =
482 				PAC_AUTOGAIN_IGNORE_FRAMES;
483 			setexposure(gspca_dev);
484 			setgain(gspca_dev);
485 		}
486 	}
487 
488 	return 0;
489 }
490 
sd_getautogain(struct gspca_dev * gspca_dev,__s32 * val)491 static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
492 {
493 	struct sd *sd = (struct sd *) gspca_dev;
494 
495 	*val = sd->autogain;
496 	return 0;
497 }
498 
499 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
sd_int_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)500 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
501 			u8 *data,		/* interrupt packet data */
502 			int len)		/* interrput packet length */
503 {
504 	int ret = -EINVAL;
505 
506 	if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) {
507 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
508 		input_sync(gspca_dev->input_dev);
509 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
510 		input_sync(gspca_dev->input_dev);
511 		ret = 0;
512 	}
513 
514 	return ret;
515 }
516 #endif
517 
518 /* sub-driver description */
519 static const struct sd_desc sd_desc = {
520 	.name = MODULE_NAME,
521 	.ctrls = sd_ctrls,
522 	.nctrls = ARRAY_SIZE(sd_ctrls),
523 	.config = sd_config,
524 	.init = sd_init,
525 	.start = sd_start,
526 	.stopN = sd_stopN,
527 	.dq_callback = pac207_do_auto_gain,
528 	.pkt_scan = sd_pkt_scan,
529 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
530 	.int_pkt_scan = sd_int_pkt_scan,
531 #endif
532 };
533 
534 /* -- module initialisation -- */
535 static const struct usb_device_id device_table[] = {
536 	{USB_DEVICE(0x041e, 0x4028)},
537 	{USB_DEVICE(0x093a, 0x2460)},
538 	{USB_DEVICE(0x093a, 0x2461)},
539 	{USB_DEVICE(0x093a, 0x2463)},
540 	{USB_DEVICE(0x093a, 0x2464)},
541 	{USB_DEVICE(0x093a, 0x2468)},
542 	{USB_DEVICE(0x093a, 0x2470)},
543 	{USB_DEVICE(0x093a, 0x2471)},
544 	{USB_DEVICE(0x093a, 0x2472)},
545 	{USB_DEVICE(0x093a, 0x2474)},
546 	{USB_DEVICE(0x093a, 0x2476)},
547 	{USB_DEVICE(0x145f, 0x013a)},
548 	{USB_DEVICE(0x2001, 0xf115)},
549 	{}
550 };
551 MODULE_DEVICE_TABLE(usb, device_table);
552 
553 /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)554 static int sd_probe(struct usb_interface *intf,
555 			const struct usb_device_id *id)
556 {
557 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
558 				THIS_MODULE);
559 }
560 
561 static struct usb_driver sd_driver = {
562 	.name = MODULE_NAME,
563 	.id_table = device_table,
564 	.probe = sd_probe,
565 	.disconnect = gspca_disconnect,
566 #ifdef CONFIG_PM
567 	.suspend = gspca_suspend,
568 	.resume = gspca_resume,
569 #endif
570 };
571 
572 module_usb_driver(sd_driver);
573