1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Sunplus spca504(abc) spca533 spca536 library
4 * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
5 *
6 * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#define MODULE_NAME "sunplus"
12
13#include "gspca.h"
14#include "jpeg.h"
15
16MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
17MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
18MODULE_LICENSE("GPL");
19
20#define QUALITY 85
21
22/* specific webcam descriptor */
23struct sd {
24 struct gspca_dev gspca_dev; /* !! must be the first item */
25
26 bool autogain;
27
28 u8 bridge;
29#define BRIDGE_SPCA504 0
30#define BRIDGE_SPCA504B 1
31#define BRIDGE_SPCA504C 2
32#define BRIDGE_SPCA533 3
33#define BRIDGE_SPCA536 4
34 u8 subtype;
35#define AiptekMiniPenCam13 1
36#define LogitechClickSmart420 2
37#define LogitechClickSmart820 3
38#define MegapixV4 4
39#define MegaImageVI 5
40
41 u8 jpeg_hdr[JPEG_HDR_SZ];
42};
43
44static const struct v4l2_pix_format vga_mode[] = {
45 {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
46 .bytesperline = 320,
47 .sizeimage = 320 * 240 * 3 / 8 + 590,
48 .colorspace = V4L2_COLORSPACE_JPEG,
49 .priv = 2},
50 {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
51 .bytesperline = 640,
52 .sizeimage = 640 * 480 * 3 / 8 + 590,
53 .colorspace = V4L2_COLORSPACE_JPEG,
54 .priv = 1},
55};
56
57static const struct v4l2_pix_format custom_mode[] = {
58 {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
59 .bytesperline = 320,
60 .sizeimage = 320 * 240 * 3 / 8 + 590,
61 .colorspace = V4L2_COLORSPACE_JPEG,
62 .priv = 2},
63 {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
64 .bytesperline = 464,
65 .sizeimage = 464 * 480 * 3 / 8 + 590,
66 .colorspace = V4L2_COLORSPACE_JPEG,
67 .priv = 1},
68};
69
70static const struct v4l2_pix_format vga_mode2[] = {
71 {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
72 .bytesperline = 176,
73 .sizeimage = 176 * 144 * 3 / 8 + 590,
74 .colorspace = V4L2_COLORSPACE_JPEG,
75 .priv = 4},
76 {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
77 .bytesperline = 320,
78 .sizeimage = 320 * 240 * 3 / 8 + 590,
79 .colorspace = V4L2_COLORSPACE_JPEG,
80 .priv = 3},
81 {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
82 .bytesperline = 352,
83 .sizeimage = 352 * 288 * 3 / 8 + 590,
84 .colorspace = V4L2_COLORSPACE_JPEG,
85 .priv = 2},
86 {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
87 .bytesperline = 640,
88 .sizeimage = 640 * 480 * 3 / 8 + 590,
89 .colorspace = V4L2_COLORSPACE_JPEG,
90 .priv = 1},
91};
92
93#define SPCA50X_OFFSET_DATA 10
94#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3
95#define SPCA504_PCCAM600_OFFSET_COMPRESS 4
96#define SPCA504_PCCAM600_OFFSET_MODE 5
97#define SPCA504_PCCAM600_OFFSET_DATA 14
98 /* Frame packet header offsets for the spca533 */
99#define SPCA533_OFFSET_DATA 16
100#define SPCA533_OFFSET_FRAMSEQ 15
101/* Frame packet header offsets for the spca536 */
102#define SPCA536_OFFSET_DATA 4
103#define SPCA536_OFFSET_FRAMSEQ 1
104
105struct cmd {
106 u8 req;
107 u16 val;
108 u16 idx;
109};
110
111/* Initialisation data for the Creative PC-CAM 600 */
112static const struct cmd spca504_pccam600_init_data[] = {
113/* {0xa0, 0x0000, 0x0503}, * capture mode */
114 {0x00, 0x0000, 0x2000},
115 {0x00, 0x0013, 0x2301},
116 {0x00, 0x0003, 0x2000},
117 {0x00, 0x0001, 0x21ac},
118 {0x00, 0x0001, 0x21a6},
119 {0x00, 0x0000, 0x21a7}, /* brightness */
120 {0x00, 0x0020, 0x21a8}, /* contrast */
121 {0x00, 0x0001, 0x21ac}, /* sat/hue */
122 {0x00, 0x0000, 0x21ad}, /* hue */
123 {0x00, 0x001a, 0x21ae}, /* saturation */
124 {0x00, 0x0002, 0x21a3}, /* gamma */
125 {0x30, 0x0154, 0x0008},
126 {0x30, 0x0004, 0x0006},
127 {0x30, 0x0258, 0x0009},
128 {0x30, 0x0004, 0x0000},
129 {0x30, 0x0093, 0x0004},
130 {0x30, 0x0066, 0x0005},
131 {0x00, 0x0000, 0x2000},
132 {0x00, 0x0013, 0x2301},
133 {0x00, 0x0003, 0x2000},
134 {0x00, 0x0013, 0x2301},
135 {0x00, 0x0003, 0x2000},
136};
137
138/* Creative PC-CAM 600 specific open data, sent before using the
139 * generic initialisation data from spca504_open_data.
140 */
141static const struct cmd spca504_pccam600_open_data[] = {
142 {0x00, 0x0001, 0x2501},
143 {0x20, 0x0500, 0x0001}, /* snapshot mode */
144 {0x00, 0x0003, 0x2880},
145 {0x00, 0x0001, 0x2881},
146};
147
148/* Initialisation data for the logitech clicksmart 420 */
149static const struct cmd spca504A_clicksmart420_init_data[] = {
150/* {0xa0, 0x0000, 0x0503}, * capture mode */
151 {0x00, 0x0000, 0x2000},
152 {0x00, 0x0013, 0x2301},
153 {0x00, 0x0003, 0x2000},
154 {0x00, 0x0001, 0x21ac},
155 {0x00, 0x0001, 0x21a6},
156 {0x00, 0x0000, 0x21a7}, /* brightness */
157 {0x00, 0x0020, 0x21a8}, /* contrast */
158 {0x00, 0x0001, 0x21ac}, /* sat/hue */
159 {0x00, 0x0000, 0x21ad}, /* hue */
160 {0x00, 0x001a, 0x21ae}, /* saturation */
161 {0x00, 0x0002, 0x21a3}, /* gamma */
162 {0x30, 0x0004, 0x000a},
163 {0xb0, 0x0001, 0x0000},
164
165 {0xa1, 0x0080, 0x0001},
166 {0x30, 0x0049, 0x0000},
167 {0x30, 0x0060, 0x0005},
168 {0x0c, 0x0004, 0x0000},
169 {0x00, 0x0000, 0x0000},
170 {0x00, 0x0000, 0x2000},
171 {0x00, 0x0013, 0x2301},
172 {0x00, 0x0003, 0x2000},
173};
174
175/* clicksmart 420 open data ? */
176static const struct cmd spca504A_clicksmart420_open_data[] = {
177 {0x00, 0x0001, 0x2501},
178 {0x20, 0x0502, 0x0000},
179 {0x06, 0x0000, 0x0000},
180 {0x00, 0x0004, 0x2880},
181 {0x00, 0x0001, 0x2881},
182
183 {0xa0, 0x0000, 0x0503},
184};
185
186static const u8 qtable_creative_pccam[2][64] = {
187 { /* Q-table Y-components */
188 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
189 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
190 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
191 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
192 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
193 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
194 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
195 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},
196 { /* Q-table C-components */
197 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
198 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
199 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
200 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
201 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
202 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
203 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
204 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
205};
206
207/* FIXME: This Q-table is identical to the Creative PC-CAM one,
208 * except for one byte. Possibly a typo?
209 * NWG: 18/05/2003.
210 */
211static const u8 qtable_spca504_default[2][64] = {
212 { /* Q-table Y-components */
213 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
214 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
215 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
216 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
217 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
218 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
219 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
220 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e,
221 },
222 { /* Q-table C-components */
223 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
224 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
225 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
226 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
227 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
228 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
229 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
230 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
231};
232
233/* read <len> bytes to gspca_dev->usb_buf */
234static void reg_r(struct gspca_dev *gspca_dev,
235 u8 req,
236 u16 index,
237 u16 len)
238{
239 int ret;
240
241 if (len > USB_BUF_SZ) {
242 gspca_err(gspca_dev, "reg_r: buffer overflow\n");
243 return;
244 }
245 if (len == 0) {
246 gspca_err(gspca_dev, "reg_r: zero-length read\n");
247 return;
248 }
249 if (gspca_dev->usb_err < 0)
250 return;
251 ret = usb_control_msg(dev: gspca_dev->dev,
252 usb_rcvctrlpipe(gspca_dev->dev, 0),
253 request: req,
254 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
255 value: 0, /* value */
256 index,
257 data: gspca_dev->usb_buf, size: len,
258 timeout: 500);
259 if (ret < 0) {
260 pr_err("reg_r err %d\n", ret);
261 gspca_dev->usb_err = ret;
262 /*
263 * Make sure the buffer is zeroed to avoid uninitialized
264 * values.
265 */
266 memset(gspca_dev->usb_buf, 0, USB_BUF_SZ);
267 }
268}
269
270/* write one byte */
271static void reg_w_1(struct gspca_dev *gspca_dev,
272 u8 req,
273 u16 value,
274 u16 index,
275 u16 byte)
276{
277 int ret;
278
279 if (gspca_dev->usb_err < 0)
280 return;
281 gspca_dev->usb_buf[0] = byte;
282 ret = usb_control_msg(dev: gspca_dev->dev,
283 usb_sndctrlpipe(gspca_dev->dev, 0),
284 request: req,
285 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
286 value, index,
287 data: gspca_dev->usb_buf, size: 1,
288 timeout: 500);
289 if (ret < 0) {
290 pr_err("reg_w_1 err %d\n", ret);
291 gspca_dev->usb_err = ret;
292 }
293}
294
295/* write req / index / value */
296static void reg_w_riv(struct gspca_dev *gspca_dev,
297 u8 req, u16 index, u16 value)
298{
299 struct usb_device *dev = gspca_dev->dev;
300 int ret;
301
302 if (gspca_dev->usb_err < 0)
303 return;
304 ret = usb_control_msg(dev,
305 usb_sndctrlpipe(dev, 0),
306 request: req,
307 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
308 value, index, NULL, size: 0, timeout: 500);
309 if (ret < 0) {
310 pr_err("reg_w_riv err %d\n", ret);
311 gspca_dev->usb_err = ret;
312 return;
313 }
314 gspca_dbg(gspca_dev, D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x\n",
315 req, index, value);
316}
317
318static void write_vector(struct gspca_dev *gspca_dev,
319 const struct cmd *data, int ncmds)
320{
321 while (--ncmds >= 0) {
322 reg_w_riv(gspca_dev, req: data->req, index: data->idx, value: data->val);
323 data++;
324 }
325}
326
327static void setup_qtable(struct gspca_dev *gspca_dev,
328 const u8 qtable[2][64])
329{
330 int i;
331
332 /* loop over y components */
333 for (i = 0; i < 64; i++)
334 reg_w_riv(gspca_dev, req: 0x00, index: 0x2800 + i, value: qtable[0][i]);
335
336 /* loop over c components */
337 for (i = 0; i < 64; i++)
338 reg_w_riv(gspca_dev, req: 0x00, index: 0x2840 + i, value: qtable[1][i]);
339}
340
341static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
342 u8 req, u16 idx, u16 val)
343{
344 reg_w_riv(gspca_dev, req, index: idx, value: val);
345 reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1);
346 gspca_dbg(gspca_dev, D_FRAM, "before wait 0x%04x\n",
347 gspca_dev->usb_buf[0]);
348 reg_w_riv(gspca_dev, req, index: idx, value: val);
349
350 msleep(msecs: 200);
351 reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1);
352 gspca_dbg(gspca_dev, D_FRAM, "after wait 0x%04x\n",
353 gspca_dev->usb_buf[0]);
354}
355
356static void spca504_read_info(struct gspca_dev *gspca_dev)
357{
358 int i;
359 u8 info[6];
360
361 if (gspca_debug < D_STREAM)
362 return;
363
364 for (i = 0; i < 6; i++) {
365 reg_r(gspca_dev, req: 0, index: i, len: 1);
366 info[i] = gspca_dev->usb_buf[0];
367 }
368 gspca_dbg(gspca_dev, D_STREAM,
369 "Read info: %d %d %d %d %d %d. Should be 1,0,2,2,0,0\n",
370 info[0], info[1], info[2],
371 info[3], info[4], info[5]);
372}
373
374static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
375 u8 req,
376 u16 idx, u16 val, u8 endcode, u8 count)
377{
378 u16 status;
379
380 reg_w_riv(gspca_dev, req, index: idx, value: val);
381 reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1);
382 if (gspca_dev->usb_err < 0)
383 return;
384 gspca_dbg(gspca_dev, D_FRAM, "Status 0x%02x Need 0x%02x\n",
385 gspca_dev->usb_buf[0], endcode);
386 if (!count)
387 return;
388 count = 200;
389 while (--count > 0) {
390 msleep(msecs: 10);
391 /* gsmart mini2 write a each wait setting 1 ms is enough */
392/* reg_w_riv(gspca_dev, req, idx, val); */
393 reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1);
394 status = gspca_dev->usb_buf[0];
395 if (status == endcode) {
396 gspca_dbg(gspca_dev, D_FRAM, "status 0x%04x after wait %d\n",
397 status, 200 - count);
398 break;
399 }
400 }
401}
402
403static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
404{
405 int count = 10;
406
407 while (--count > 0) {
408 reg_r(gspca_dev, req: 0x21, index: 0, len: 1);
409 if ((gspca_dev->usb_buf[0] & 0x01) == 0)
410 break;
411 msleep(msecs: 10);
412 }
413}
414
415static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
416{
417 int count = 50;
418
419 while (--count > 0) {
420 reg_r(gspca_dev, req: 0x21, index: 1, len: 1);
421 if (gspca_dev->usb_buf[0] != 0) {
422 reg_w_1(gspca_dev, req: 0x21, value: 0, index: 1, byte: 0);
423 reg_r(gspca_dev, req: 0x21, index: 1, len: 1);
424 spca504B_PollingDataReady(gspca_dev);
425 break;
426 }
427 msleep(msecs: 10);
428 }
429}
430
431static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
432{
433 u8 *data;
434
435 if (gspca_debug < D_STREAM)
436 return;
437
438 data = gspca_dev->usb_buf;
439 reg_r(gspca_dev, req: 0x20, index: 0, len: 5);
440 gspca_dbg(gspca_dev, D_STREAM, "FirmWare: %d %d %d %d %d\n",
441 data[0], data[1], data[2], data[3], data[4]);
442 reg_r(gspca_dev, req: 0x23, index: 0, len: 64);
443 reg_r(gspca_dev, req: 0x23, index: 1, len: 64);
444}
445
446static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
447{
448 struct sd *sd = (struct sd *) gspca_dev;
449 u8 Size;
450
451 Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
452 switch (sd->bridge) {
453 case BRIDGE_SPCA533:
454 reg_w_riv(gspca_dev, req: 0x31, index: 0, value: 0);
455 spca504B_WaitCmdStatus(gspca_dev);
456 spca504B_PollingDataReady(gspca_dev);
457 spca50x_GetFirmware(gspca_dev);
458
459 reg_w_1(gspca_dev, req: 0x24, value: 0, index: 8, byte: 2); /* type */
460 reg_r(gspca_dev, req: 0x24, index: 8, len: 1);
461
462 reg_w_1(gspca_dev, req: 0x25, value: 0, index: 4, byte: Size);
463 reg_r(gspca_dev, req: 0x25, index: 4, len: 1); /* size */
464 spca504B_PollingDataReady(gspca_dev);
465
466 /* Init the cam width height with some values get on init ? */
467 reg_w_riv(gspca_dev, req: 0x31, index: 0x0004, value: 0x00);
468 spca504B_WaitCmdStatus(gspca_dev);
469 spca504B_PollingDataReady(gspca_dev);
470 break;
471 default:
472/* case BRIDGE_SPCA504B: */
473/* case BRIDGE_SPCA536: */
474 reg_w_1(gspca_dev, req: 0x25, value: 0, index: 4, byte: Size);
475 reg_r(gspca_dev, req: 0x25, index: 4, len: 1); /* size */
476 reg_w_1(gspca_dev, req: 0x27, value: 0, index: 0, byte: 6);
477 reg_r(gspca_dev, req: 0x27, index: 0, len: 1); /* type */
478 spca504B_PollingDataReady(gspca_dev);
479 break;
480 case BRIDGE_SPCA504:
481 Size += 3;
482 if (sd->subtype == AiptekMiniPenCam13) {
483 /* spca504a aiptek */
484 spca504A_acknowledged_command(gspca_dev,
485 req: 0x08, idx: Size, val: 0,
486 endcode: 0x80 | (Size & 0x0f), count: 1);
487 spca504A_acknowledged_command(gspca_dev,
488 req: 1, idx: 3, val: 0, endcode: 0x9f, count: 0);
489 } else {
490 spca504_acknowledged_command(gspca_dev, req: 0x08, idx: Size, val: 0);
491 }
492 break;
493 case BRIDGE_SPCA504C:
494 /* capture mode */
495 reg_w_riv(gspca_dev, req: 0xa0, index: (0x0500 | (Size & 0x0f)), value: 0x00);
496 reg_w_riv(gspca_dev, req: 0x20, index: 0x01, value: 0x0500 | (Size & 0x0f));
497 break;
498 }
499}
500
501static void spca504_wait_status(struct gspca_dev *gspca_dev)
502{
503 int cnt;
504
505 cnt = 256;
506 while (--cnt > 0) {
507 /* With this we get the status, when return 0 it's all ok */
508 reg_r(gspca_dev, req: 0x06, index: 0x00, len: 1);
509 if (gspca_dev->usb_buf[0] == 0)
510 return;
511 msleep(msecs: 10);
512 }
513}
514
515static void spca504B_setQtable(struct gspca_dev *gspca_dev)
516{
517 reg_w_1(gspca_dev, req: 0x26, value: 0, index: 0, byte: 3);
518 reg_r(gspca_dev, req: 0x26, index: 0, len: 1);
519 spca504B_PollingDataReady(gspca_dev);
520}
521
522static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
523{
524 struct sd *sd = (struct sd *) gspca_dev;
525 u16 reg;
526
527 reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
528 reg_w_riv(gspca_dev, req: 0x00, index: reg, value: val);
529}
530
531static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
532{
533 struct sd *sd = (struct sd *) gspca_dev;
534 u16 reg;
535
536 reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
537 reg_w_riv(gspca_dev, req: 0x00, index: reg, value: val);
538}
539
540static void setcolors(struct gspca_dev *gspca_dev, s32 val)
541{
542 struct sd *sd = (struct sd *) gspca_dev;
543 u16 reg;
544
545 reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
546 reg_w_riv(gspca_dev, req: 0x00, index: reg, value: val);
547}
548
549static void init_ctl_reg(struct gspca_dev *gspca_dev)
550{
551 struct sd *sd = (struct sd *) gspca_dev;
552 int pollreg = 1;
553
554 switch (sd->bridge) {
555 case BRIDGE_SPCA504:
556 case BRIDGE_SPCA504C:
557 pollreg = 0;
558 fallthrough;
559 default:
560/* case BRIDGE_SPCA533: */
561/* case BRIDGE_SPCA504B: */
562 reg_w_riv(gspca_dev, req: 0, index: 0x21ad, value: 0x00); /* hue */
563 reg_w_riv(gspca_dev, req: 0, index: 0x21ac, value: 0x01); /* sat/hue */
564 reg_w_riv(gspca_dev, req: 0, index: 0x21a3, value: 0x00); /* gamma */
565 break;
566 case BRIDGE_SPCA536:
567 reg_w_riv(gspca_dev, req: 0, index: 0x20f5, value: 0x40);
568 reg_w_riv(gspca_dev, req: 0, index: 0x20f4, value: 0x01);
569 reg_w_riv(gspca_dev, req: 0, index: 0x2089, value: 0x00);
570 break;
571 }
572 if (pollreg)
573 spca504B_PollingDataReady(gspca_dev);
574}
575
576/* this function is called at probe time */
577static int sd_config(struct gspca_dev *gspca_dev,
578 const struct usb_device_id *id)
579{
580 struct sd *sd = (struct sd *) gspca_dev;
581 struct cam *cam;
582
583 cam = &gspca_dev->cam;
584
585 sd->bridge = id->driver_info >> 8;
586 sd->subtype = id->driver_info;
587
588 if (sd->subtype == AiptekMiniPenCam13) {
589
590 /* try to get the firmware as some cam answer 2.0.1.2.2
591 * and should be a spca504b then overwrite that setting */
592 reg_r(gspca_dev, req: 0x20, index: 0, len: 1);
593 switch (gspca_dev->usb_buf[0]) {
594 case 1:
595 break; /* (right bridge/subtype) */
596 case 2:
597 sd->bridge = BRIDGE_SPCA504B;
598 sd->subtype = 0;
599 break;
600 default:
601 return -ENODEV;
602 }
603 }
604
605 switch (sd->bridge) {
606 default:
607/* case BRIDGE_SPCA504B: */
608/* case BRIDGE_SPCA504: */
609/* case BRIDGE_SPCA536: */
610 cam->cam_mode = vga_mode;
611 cam->nmodes = ARRAY_SIZE(vga_mode);
612 break;
613 case BRIDGE_SPCA533:
614 cam->cam_mode = custom_mode;
615 if (sd->subtype == MegaImageVI) /* 320x240 only */
616 cam->nmodes = ARRAY_SIZE(custom_mode) - 1;
617 else
618 cam->nmodes = ARRAY_SIZE(custom_mode);
619 break;
620 case BRIDGE_SPCA504C:
621 cam->cam_mode = vga_mode2;
622 cam->nmodes = ARRAY_SIZE(vga_mode2);
623 break;
624 }
625 return 0;
626}
627
628/* this function is called at probe and resume time */
629static int sd_init(struct gspca_dev *gspca_dev)
630{
631 struct sd *sd = (struct sd *) gspca_dev;
632
633 switch (sd->bridge) {
634 case BRIDGE_SPCA504B:
635 reg_w_riv(gspca_dev, req: 0x1d, index: 0x00, value: 0);
636 reg_w_riv(gspca_dev, req: 0x00, index: 0x2306, value: 0x01);
637 reg_w_riv(gspca_dev, req: 0x00, index: 0x0d04, value: 0x00);
638 reg_w_riv(gspca_dev, req: 0x00, index: 0x2000, value: 0x00);
639 reg_w_riv(gspca_dev, req: 0x00, index: 0x2301, value: 0x13);
640 reg_w_riv(gspca_dev, req: 0x00, index: 0x2306, value: 0x00);
641 fallthrough;
642 case BRIDGE_SPCA533:
643 spca504B_PollingDataReady(gspca_dev);
644 spca50x_GetFirmware(gspca_dev);
645 break;
646 case BRIDGE_SPCA536:
647 spca50x_GetFirmware(gspca_dev);
648 reg_r(gspca_dev, req: 0x00, index: 0x5002, len: 1);
649 reg_w_1(gspca_dev, req: 0x24, value: 0, index: 0, byte: 0);
650 reg_r(gspca_dev, req: 0x24, index: 0, len: 1);
651 spca504B_PollingDataReady(gspca_dev);
652 reg_w_riv(gspca_dev, req: 0x34, index: 0, value: 0);
653 spca504B_WaitCmdStatus(gspca_dev);
654 break;
655 case BRIDGE_SPCA504C: /* pccam600 */
656 gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504 (PC-CAM 600)\n");
657 reg_w_riv(gspca_dev, req: 0xe0, index: 0x0000, value: 0x0000);
658 reg_w_riv(gspca_dev, req: 0xe0, index: 0x0000, value: 0x0001); /* reset */
659 spca504_wait_status(gspca_dev);
660 if (sd->subtype == LogitechClickSmart420)
661 write_vector(gspca_dev,
662 data: spca504A_clicksmart420_open_data,
663 ARRAY_SIZE(spca504A_clicksmart420_open_data));
664 else
665 write_vector(gspca_dev, data: spca504_pccam600_open_data,
666 ARRAY_SIZE(spca504_pccam600_open_data));
667 setup_qtable(gspca_dev, qtable: qtable_creative_pccam);
668 break;
669 default:
670/* case BRIDGE_SPCA504: */
671 gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504\n");
672 if (sd->subtype == AiptekMiniPenCam13) {
673 spca504_read_info(gspca_dev);
674
675 /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
676 spca504A_acknowledged_command(gspca_dev, req: 0x24,
677 idx: 8, val: 3, endcode: 0x9e, count: 1);
678 /* Twice sequential need status 0xff->0x9e->0x9d */
679 spca504A_acknowledged_command(gspca_dev, req: 0x24,
680 idx: 8, val: 3, endcode: 0x9e, count: 0);
681
682 spca504A_acknowledged_command(gspca_dev, req: 0x24,
683 idx: 0, val: 0, endcode: 0x9d, count: 1);
684 /******************************/
685 /* spca504a aiptek */
686 spca504A_acknowledged_command(gspca_dev, req: 0x08,
687 idx: 6, val: 0, endcode: 0x86, count: 1);
688/* reg_write (dev, 0, 0x2000, 0); */
689/* reg_write (dev, 0, 0x2883, 1); */
690/* spca504A_acknowledged_command (gspca_dev, 0x08,
691 6, 0, 0x86, 1); */
692/* spca504A_acknowledged_command (gspca_dev, 0x24,
693 0, 0, 0x9D, 1); */
694 reg_w_riv(gspca_dev, req: 0x00, index: 0x270c, value: 0x05);
695 /* L92 sno1t.txt */
696 reg_w_riv(gspca_dev, req: 0x00, index: 0x2310, value: 0x05);
697 spca504A_acknowledged_command(gspca_dev, req: 0x01,
698 idx: 0x0f, val: 0, endcode: 0xff, count: 0);
699 }
700 /* setup qtable */
701 reg_w_riv(gspca_dev, req: 0, index: 0x2000, value: 0);
702 reg_w_riv(gspca_dev, req: 0, index: 0x2883, value: 1);
703 setup_qtable(gspca_dev, qtable: qtable_spca504_default);
704 break;
705 }
706 return gspca_dev->usb_err;
707}
708
709static int sd_start(struct gspca_dev *gspca_dev)
710{
711 struct sd *sd = (struct sd *) gspca_dev;
712 int enable;
713
714 /* create the JPEG header */
715 jpeg_define(jpeg_hdr: sd->jpeg_hdr, height: gspca_dev->pixfmt.height,
716 width: gspca_dev->pixfmt.width,
717 samplesY: 0x22); /* JPEG 411 */
718 jpeg_set_qual(jpeg_hdr: sd->jpeg_hdr, QUALITY);
719
720 if (sd->bridge == BRIDGE_SPCA504B)
721 spca504B_setQtable(gspca_dev);
722 spca504B_SetSizeType(gspca_dev);
723 switch (sd->bridge) {
724 default:
725/* case BRIDGE_SPCA504B: */
726/* case BRIDGE_SPCA533: */
727/* case BRIDGE_SPCA536: */
728 switch (sd->subtype) {
729 case MegapixV4:
730 case LogitechClickSmart820:
731 case MegaImageVI:
732 reg_w_riv(gspca_dev, req: 0xf0, index: 0, value: 0);
733 spca504B_WaitCmdStatus(gspca_dev);
734 reg_w_riv(gspca_dev, req: 0xf0, index: 4, value: 0);
735 spca504B_WaitCmdStatus(gspca_dev);
736 break;
737 default:
738 reg_w_riv(gspca_dev, req: 0x31, index: 0x0004, value: 0x00);
739 spca504B_WaitCmdStatus(gspca_dev);
740 spca504B_PollingDataReady(gspca_dev);
741 break;
742 }
743 break;
744 case BRIDGE_SPCA504:
745 if (sd->subtype == AiptekMiniPenCam13) {
746 spca504_read_info(gspca_dev);
747
748 /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
749 spca504A_acknowledged_command(gspca_dev, req: 0x24,
750 idx: 8, val: 3, endcode: 0x9e, count: 1);
751 /* Twice sequential need status 0xff->0x9e->0x9d */
752 spca504A_acknowledged_command(gspca_dev, req: 0x24,
753 idx: 8, val: 3, endcode: 0x9e, count: 0);
754 spca504A_acknowledged_command(gspca_dev, req: 0x24,
755 idx: 0, val: 0, endcode: 0x9d, count: 1);
756 } else {
757 spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 8, val: 3);
758 spca504_read_info(gspca_dev);
759 spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 8, val: 3);
760 spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 0, val: 0);
761 }
762 spca504B_SetSizeType(gspca_dev);
763 reg_w_riv(gspca_dev, req: 0x00, index: 0x270c, value: 0x05);
764 /* L92 sno1t.txt */
765 reg_w_riv(gspca_dev, req: 0x00, index: 0x2310, value: 0x05);
766 break;
767 case BRIDGE_SPCA504C:
768 if (sd->subtype == LogitechClickSmart420) {
769 write_vector(gspca_dev,
770 data: spca504A_clicksmart420_init_data,
771 ARRAY_SIZE(spca504A_clicksmart420_init_data));
772 } else {
773 write_vector(gspca_dev, data: spca504_pccam600_init_data,
774 ARRAY_SIZE(spca504_pccam600_init_data));
775 }
776 enable = (sd->autogain ? 0x04 : 0x01);
777 reg_w_riv(gspca_dev, req: 0x0c, index: 0x0000, value: enable);
778 /* auto exposure */
779 reg_w_riv(gspca_dev, req: 0xb0, index: 0x0000, value: enable);
780 /* auto whiteness */
781
782 /* set default exposure compensation and whiteness balance */
783 reg_w_riv(gspca_dev, req: 0x30, index: 0x0001, value: 800); /* ~ 20 fps */
784 reg_w_riv(gspca_dev, req: 0x30, index: 0x0002, value: 1600);
785 spca504B_SetSizeType(gspca_dev);
786 break;
787 }
788 init_ctl_reg(gspca_dev);
789 return gspca_dev->usb_err;
790}
791
792static void sd_stopN(struct gspca_dev *gspca_dev)
793{
794 struct sd *sd = (struct sd *) gspca_dev;
795
796 switch (sd->bridge) {
797 default:
798/* case BRIDGE_SPCA533: */
799/* case BRIDGE_SPCA536: */
800/* case BRIDGE_SPCA504B: */
801 reg_w_riv(gspca_dev, req: 0x31, index: 0, value: 0);
802 spca504B_WaitCmdStatus(gspca_dev);
803 spca504B_PollingDataReady(gspca_dev);
804 break;
805 case BRIDGE_SPCA504:
806 case BRIDGE_SPCA504C:
807 reg_w_riv(gspca_dev, req: 0x00, index: 0x2000, value: 0x0000);
808
809 if (sd->subtype == AiptekMiniPenCam13) {
810 /* spca504a aiptek */
811/* spca504A_acknowledged_command(gspca_dev, 0x08,
812 6, 0, 0x86, 1); */
813 spca504A_acknowledged_command(gspca_dev, req: 0x24,
814 idx: 0x00, val: 0x00, endcode: 0x9d, count: 1);
815 spca504A_acknowledged_command(gspca_dev, req: 0x01,
816 idx: 0x0f, val: 0x00, endcode: 0xff, count: 1);
817 } else {
818 spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 0, val: 0);
819 reg_w_riv(gspca_dev, req: 0x01, index: 0x000f, value: 0x0000);
820 }
821 break;
822 }
823}
824
825static void sd_pkt_scan(struct gspca_dev *gspca_dev,
826 u8 *data, /* isoc packet */
827 int len) /* iso packet length */
828{
829 struct sd *sd = (struct sd *) gspca_dev;
830 int i, sof = 0;
831 static u8 ffd9[] = {0xff, 0xd9};
832
833/* frames are jpeg 4.1.1 without 0xff escape */
834 switch (sd->bridge) {
835 case BRIDGE_SPCA533:
836 if (data[0] == 0xff) {
837 if (data[1] != 0x01) { /* drop packet */
838/* gspca_dev->last_packet_type = DISCARD_PACKET; */
839 return;
840 }
841 sof = 1;
842 data += SPCA533_OFFSET_DATA;
843 len -= SPCA533_OFFSET_DATA;
844 } else {
845 data += 1;
846 len -= 1;
847 }
848 break;
849 case BRIDGE_SPCA536:
850 if (data[0] == 0xff) {
851 sof = 1;
852 data += SPCA536_OFFSET_DATA;
853 len -= SPCA536_OFFSET_DATA;
854 } else {
855 data += 2;
856 len -= 2;
857 }
858 break;
859 default:
860/* case BRIDGE_SPCA504: */
861/* case BRIDGE_SPCA504B: */
862 switch (data[0]) {
863 case 0xfe: /* start of frame */
864 sof = 1;
865 data += SPCA50X_OFFSET_DATA;
866 len -= SPCA50X_OFFSET_DATA;
867 break;
868 case 0xff: /* drop packet */
869/* gspca_dev->last_packet_type = DISCARD_PACKET; */
870 return;
871 default:
872 data += 1;
873 len -= 1;
874 break;
875 }
876 break;
877 case BRIDGE_SPCA504C:
878 switch (data[0]) {
879 case 0xfe: /* start of frame */
880 sof = 1;
881 data += SPCA504_PCCAM600_OFFSET_DATA;
882 len -= SPCA504_PCCAM600_OFFSET_DATA;
883 break;
884 case 0xff: /* drop packet */
885/* gspca_dev->last_packet_type = DISCARD_PACKET; */
886 return;
887 default:
888 data += 1;
889 len -= 1;
890 break;
891 }
892 break;
893 }
894 if (sof) { /* start of frame */
895 gspca_frame_add(gspca_dev, packet_type: LAST_PACKET,
896 data: ffd9, len: 2);
897
898 /* put the JPEG header in the new frame */
899 gspca_frame_add(gspca_dev, packet_type: FIRST_PACKET,
900 data: sd->jpeg_hdr, JPEG_HDR_SZ);
901 }
902
903 /* add 0x00 after 0xff */
904 i = 0;
905 do {
906 if (data[i] == 0xff) {
907 gspca_frame_add(gspca_dev, packet_type: INTER_PACKET,
908 data, len: i + 1);
909 len -= i;
910 data += i;
911 *data = 0x00;
912 i = 0;
913 }
914 i++;
915 } while (i < len);
916 gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, data, len);
917}
918
919static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
920{
921 struct gspca_dev *gspca_dev =
922 container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
923 struct sd *sd = (struct sd *)gspca_dev;
924
925 gspca_dev->usb_err = 0;
926
927 if (!gspca_dev->streaming)
928 return 0;
929
930 switch (ctrl->id) {
931 case V4L2_CID_BRIGHTNESS:
932 setbrightness(gspca_dev, val: ctrl->val);
933 break;
934 case V4L2_CID_CONTRAST:
935 setcontrast(gspca_dev, val: ctrl->val);
936 break;
937 case V4L2_CID_SATURATION:
938 setcolors(gspca_dev, val: ctrl->val);
939 break;
940 case V4L2_CID_AUTOGAIN:
941 sd->autogain = ctrl->val;
942 break;
943 }
944 return gspca_dev->usb_err;
945}
946
947static const struct v4l2_ctrl_ops sd_ctrl_ops = {
948 .s_ctrl = sd_s_ctrl,
949};
950
951static int sd_init_controls(struct gspca_dev *gspca_dev)
952{
953 struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
954
955 gspca_dev->vdev.ctrl_handler = hdl;
956 v4l2_ctrl_handler_init(hdl, 4);
957 v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops,
958 V4L2_CID_BRIGHTNESS, min: -128, max: 127, step: 1, def: 0);
959 v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops,
960 V4L2_CID_CONTRAST, min: 0, max: 255, step: 1, def: 0x20);
961 v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops,
962 V4L2_CID_SATURATION, min: 0, max: 255, step: 1, def: 0x1a);
963 v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops,
964 V4L2_CID_AUTOGAIN, min: 0, max: 1, step: 1, def: 1);
965
966 if (hdl->error) {
967 pr_err("Could not initialize controls\n");
968 return hdl->error;
969 }
970 return 0;
971}
972
973/* sub-driver description */
974static const struct sd_desc sd_desc = {
975 .name = MODULE_NAME,
976 .config = sd_config,
977 .init = sd_init,
978 .init_controls = sd_init_controls,
979 .start = sd_start,
980 .stopN = sd_stopN,
981 .pkt_scan = sd_pkt_scan,
982};
983
984/* -- module initialisation -- */
985#define BS(bridge, subtype) \
986 .driver_info = (BRIDGE_ ## bridge << 8) \
987 | (subtype)
988static const struct usb_device_id device_table[] = {
989 {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)},
990 {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)},
991 {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)},
992 {USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)},
993 {USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)},
994 {USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)},
995 {USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)},
996 {USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)},
997 {USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)},
998 {USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)},
999 {USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)},
1000 {USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)},
1001 {USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)},
1002 {USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)},
1003 {USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)},
1004 {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},
1005 {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
1006 {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
1007 {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},
1008 {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
1009 {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
1010 {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
1011 {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)},
1012 {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)},
1013 {USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)},
1014 {USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)},
1015 {USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)},
1016 {USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)},
1017 {USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)},
1018 {USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)},
1019 {USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)},
1020 {USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)},
1021 {USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)},
1022 {USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)},
1023 {USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)},
1024 {USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)},
1025 {USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)},
1026 {USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)},
1027 {USB_DEVICE(0x06d6, 0x0041), BS(SPCA504B, 0)},
1028 {USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)},
1029 {USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)},
1030 {USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)},
1031 {USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)},
1032 {USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)},
1033 {USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)},
1034 {USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)},
1035 {USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)},
1036 {USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)},
1037 {USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)},
1038 {USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)},
1039 {USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)},
1040 {USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)},
1041 {USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)},
1042 {USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)},
1043 {USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)},
1044 {USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)},
1045 {USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)},
1046 {USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)},
1047 {USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)},
1048 {USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)},
1049 {}
1050};
1051MODULE_DEVICE_TABLE(usb, device_table);
1052
1053/* -- device connect -- */
1054static int sd_probe(struct usb_interface *intf,
1055 const struct usb_device_id *id)
1056{
1057 return gspca_dev_probe(intf, id, sd_desc: &sd_desc, dev_size: sizeof(struct sd),
1058 THIS_MODULE);
1059}
1060
1061static struct usb_driver sd_driver = {
1062 .name = MODULE_NAME,
1063 .id_table = device_table,
1064 .probe = sd_probe,
1065 .disconnect = gspca_disconnect,
1066#ifdef CONFIG_PM
1067 .suspend = gspca_suspend,
1068 .resume = gspca_resume,
1069 .reset_resume = gspca_resume,
1070#endif
1071};
1072
1073module_usb_driver(sd_driver);
1074

source code of linux/drivers/media/usb/gspca/sunplus.c