1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SN9C2028 library
4 *
5 * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
6 */
7
8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10#define MODULE_NAME "sn9c2028"
11
12#include "gspca.h"
13
14MODULE_AUTHOR("Theodore Kilgore");
15MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver");
16MODULE_LICENSE("GPL");
17
18/* specific webcam descriptor */
19struct sd {
20	struct gspca_dev gspca_dev;  /* !! must be the first item */
21	u8 sof_read;
22	u16 model;
23
24#define MIN_AVG_LUM 8500
25#define MAX_AVG_LUM 10000
26	int avg_lum;
27	u8 avg_lum_l;
28
29	struct { /* autogain and gain control cluster */
30		struct v4l2_ctrl *autogain;
31		struct v4l2_ctrl *gain;
32	};
33};
34
35struct init_command {
36	unsigned char instruction[6];
37	unsigned char to_read; /* length to read. 0 means no reply requested */
38};
39
40/* How to change the resolution of any of the VGA cams is unknown */
41static const struct v4l2_pix_format vga_mode[] = {
42	{640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
43		.bytesperline = 640,
44		.sizeimage = 640 * 480 * 3 / 4,
45		.colorspace = V4L2_COLORSPACE_SRGB,
46		.priv = 0},
47};
48
49/* No way to change the resolution of the CIF cams is known */
50static const struct v4l2_pix_format cif_mode[] = {
51	{352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
52		.bytesperline = 352,
53		.sizeimage = 352 * 288 * 3 / 4,
54		.colorspace = V4L2_COLORSPACE_SRGB,
55		.priv = 0},
56};
57
58/* the bytes to write are in gspca_dev->usb_buf */
59static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
60{
61	int rc;
62
63	gspca_dbg(gspca_dev, D_USBO, "sending command %02x%02x%02x%02x%02x%02x\n",
64		  command[0], command[1], command[2],
65		  command[3], command[4], command[5]);
66
67	memcpy(gspca_dev->usb_buf, command, 6);
68	rc = usb_control_msg(gspca_dev->dev,
69			usb_sndctrlpipe(gspca_dev->dev, 0),
70			USB_REQ_GET_CONFIGURATION,
71			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
72			2, 0, gspca_dev->usb_buf, 6, 500);
73	if (rc < 0) {
74		pr_err("command write [%02x] error %d\n",
75		       gspca_dev->usb_buf[0], rc);
76		return rc;
77	}
78
79	return 0;
80}
81
82static int sn9c2028_read1(struct gspca_dev *gspca_dev)
83{
84	int rc;
85
86	rc = usb_control_msg(gspca_dev->dev,
87			usb_rcvctrlpipe(gspca_dev->dev, 0),
88			USB_REQ_GET_STATUS,
89			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
90			1, 0, gspca_dev->usb_buf, 1, 500);
91	if (rc != 1) {
92		pr_err("read1 error %d\n", rc);
93		return (rc < 0) ? rc : -EIO;
94	}
95	gspca_dbg(gspca_dev, D_USBI, "read1 response %02x\n",
96		  gspca_dev->usb_buf[0]);
97	return gspca_dev->usb_buf[0];
98}
99
100static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
101{
102	int rc;
103	rc = usb_control_msg(gspca_dev->dev,
104			usb_rcvctrlpipe(gspca_dev->dev, 0),
105			USB_REQ_GET_STATUS,
106			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
107			4, 0, gspca_dev->usb_buf, 4, 500);
108	if (rc != 4) {
109		pr_err("read4 error %d\n", rc);
110		return (rc < 0) ? rc : -EIO;
111	}
112	memcpy(reading, gspca_dev->usb_buf, 4);
113	gspca_dbg(gspca_dev, D_USBI, "read4 response %02x%02x%02x%02x\n",
114		  reading[0], reading[1], reading[2], reading[3]);
115	return rc;
116}
117
118static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
119{
120	int i, status;
121	__u8 reading[4];
122
123	status = sn9c2028_command(gspca_dev, command);
124	if (status < 0)
125		return status;
126
127	status = -1;
128	for (i = 0; i < 256 && status < 2; i++)
129		status = sn9c2028_read1(gspca_dev);
130	if (status < 0) {
131		pr_err("long command status read error %d\n", status);
132		return status;
133	}
134
135	memset(reading, 0, 4);
136	status = sn9c2028_read4(gspca_dev, reading);
137	if (status < 0)
138		return status;
139
140	/* in general, the first byte of the response is the first byte of
141	 * the command, or'ed with 8 */
142	status = sn9c2028_read1(gspca_dev);
143	if (status < 0)
144		return status;
145
146	return 0;
147}
148
149static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command)
150{
151	int err_code;
152
153	err_code = sn9c2028_command(gspca_dev, command);
154	if (err_code < 0)
155		return err_code;
156
157	err_code = sn9c2028_read1(gspca_dev);
158	if (err_code < 0)
159		return err_code;
160
161	return 0;
162}
163
164/* this function is called at probe time */
165static int sd_config(struct gspca_dev *gspca_dev,
166		     const struct usb_device_id *id)
167{
168	struct sd *sd = (struct sd *) gspca_dev;
169	struct cam *cam = &gspca_dev->cam;
170
171	gspca_dbg(gspca_dev, D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)\n",
172		  id->idVendor, id->idProduct);
173
174	sd->model = id->idProduct;
175
176	switch (sd->model) {
177	case 0x7005:
178		gspca_dbg(gspca_dev, D_PROBE, "Genius Smart 300 camera\n");
179		break;
180	case 0x7003:
181		gspca_dbg(gspca_dev, D_PROBE, "Genius Videocam Live v2\n");
182		break;
183	case 0x8000:
184		gspca_dbg(gspca_dev, D_PROBE, "DC31VC\n");
185		break;
186	case 0x8001:
187		gspca_dbg(gspca_dev, D_PROBE, "Spy camera\n");
188		break;
189	case 0x8003:
190		gspca_dbg(gspca_dev, D_PROBE, "CIF camera\n");
191		break;
192	case 0x8008:
193		gspca_dbg(gspca_dev, D_PROBE, "Mini-Shotz ms-350 camera\n");
194		break;
195	case 0x800a:
196		gspca_dbg(gspca_dev, D_PROBE, "Vivitar 3350b type camera\n");
197		cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
198		break;
199	}
200
201	switch (sd->model) {
202	case 0x8000:
203	case 0x8001:
204	case 0x8003:
205		cam->cam_mode = cif_mode;
206		cam->nmodes = ARRAY_SIZE(cif_mode);
207		break;
208	default:
209		cam->cam_mode = vga_mode;
210		cam->nmodes = ARRAY_SIZE(vga_mode);
211	}
212	return 0;
213}
214
215/* this function is called at probe and resume time */
216static int sd_init(struct gspca_dev *gspca_dev)
217{
218	int status;
219
220	sn9c2028_read1(gspca_dev);
221	sn9c2028_read1(gspca_dev);
222	status = sn9c2028_read1(gspca_dev);
223
224	return (status < 0) ? status : 0;
225}
226
227static int run_start_commands(struct gspca_dev *gspca_dev,
228			      struct init_command *cam_commands, int n)
229{
230	int i, err_code = -1;
231
232	for (i = 0; i < n; i++) {
233		switch (cam_commands[i].to_read) {
234		case 4:
235			err_code = sn9c2028_long_command(gspca_dev,
236					cam_commands[i].instruction);
237			break;
238		case 1:
239			err_code = sn9c2028_short_command(gspca_dev,
240					cam_commands[i].instruction);
241			break;
242		case 0:
243			err_code = sn9c2028_command(gspca_dev,
244					cam_commands[i].instruction);
245			break;
246		}
247		if (err_code < 0)
248			return err_code;
249	}
250	return 0;
251}
252
253static void set_gain(struct gspca_dev *gspca_dev, s32 g)
254{
255	struct sd *sd = (struct sd *) gspca_dev;
256
257	struct init_command genius_vcam_live_gain_cmds[] = {
258		{{0x1d, 0x25, 0x10 /* This byte is gain */,
259		  0x20, 0xab, 0x00}, 0},
260	};
261	if (!gspca_dev->streaming)
262		return;
263
264	switch (sd->model) {
265	case 0x7003:
266		genius_vcam_live_gain_cmds[0].instruction[2] = g;
267		run_start_commands(gspca_dev, genius_vcam_live_gain_cmds,
268				   ARRAY_SIZE(genius_vcam_live_gain_cmds));
269		break;
270	default:
271		break;
272	}
273}
274
275static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
276{
277	struct gspca_dev *gspca_dev =
278		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
279	struct sd *sd = (struct sd *)gspca_dev;
280
281	gspca_dev->usb_err = 0;
282
283	if (!gspca_dev->streaming)
284		return 0;
285
286	switch (ctrl->id) {
287	/* standalone gain control */
288	case V4L2_CID_GAIN:
289		set_gain(gspca_dev, ctrl->val);
290		break;
291	/* autogain */
292	case V4L2_CID_AUTOGAIN:
293		set_gain(gspca_dev, sd->gain->val);
294		break;
295	}
296	return gspca_dev->usb_err;
297}
298
299static const struct v4l2_ctrl_ops sd_ctrl_ops = {
300	.s_ctrl = sd_s_ctrl,
301};
302
303
304static int sd_init_controls(struct gspca_dev *gspca_dev)
305{
306	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
307	struct sd *sd = (struct sd *)gspca_dev;
308
309	gspca_dev->vdev.ctrl_handler = hdl;
310	v4l2_ctrl_handler_init(hdl, 2);
311
312	switch (sd->model) {
313	case 0x7003:
314		sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
315			V4L2_CID_GAIN, 0, 20, 1, 0);
316		sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
317			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
318		break;
319	default:
320		break;
321	}
322
323	return 0;
324}
325static int start_spy_cam(struct gspca_dev *gspca_dev)
326{
327	struct init_command spy_start_commands[] = {
328		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
329		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
330		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
331		{{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
332		{{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
333		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
334		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width  352 */
335		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */
336		/* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */
337		{{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4},
338		{{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/
339		/* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */
340		{{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
341		/* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */
342		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
343		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
344		/* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */
345		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
346		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
347		/* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */
348		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
349		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
350		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
351		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
352		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
353		{{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4},
354		{{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/
355		/*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
356		{{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
357		/*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */
358		{{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
359		{{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */
360		{{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4},
361		/*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
362		{{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4},
363		{{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4},
364		{{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4},
365		{{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
366		{{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4},
367		{{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4},
368		{{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4},
369		/* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */
370		/* brightness or gain. 0 is default. 4 is good
371		 * indoors at night with incandescent lighting */
372		{{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4},
373		{{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/
374		{{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4},
375		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
376		{{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4},
377		{{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4},
378		/* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */
379		{{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */
380		/* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */
381		{{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1},
382		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */
383		/* Camera should start to capture now. */
384	};
385
386	return run_start_commands(gspca_dev, spy_start_commands,
387				  ARRAY_SIZE(spy_start_commands));
388}
389
390static int start_cif_cam(struct gspca_dev *gspca_dev)
391{
392	struct init_command cif_start_commands[] = {
393		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
394		/* The entire sequence below seems redundant */
395		/* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
396		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
397		{{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4},
398		{{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4},
399		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
400		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width?
401		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height?
402		{{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
403		{{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
404		{{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
405		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
406		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
407		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
408		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
409		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
410		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
411		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
412		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
413		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/
414		{{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1},
415		{{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1},
416		{{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1},
417		{{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
418		{{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1},
419		{{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1},
420		{{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1},
421		{{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1},
422		{{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
423		{{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1},
424		{{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1},
425		{{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1},
426		{{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1},
427		{{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1},
428		{{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1},
429		{{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1},
430		{{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1},
431		{{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1},
432		{{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
433		{{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1},
434		{{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1},
435		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
436		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */
437		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */
438		/* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
439		 * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing
440		 * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */
441		/* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
442		 * causes subsampling
443		 * but not a change in the resolution setting! */
444		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
445		{{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4},
446		{{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4},
447		{{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4},
448		{{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
449		{{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1},
450		{{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1},
451		{{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
452		{{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1},
453		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
454		{{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
455		{{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
456		{{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1},
457		{{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
458		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
459		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */
460		/* Camera should start to capture now. */
461	};
462
463	return run_start_commands(gspca_dev, cif_start_commands,
464				  ARRAY_SIZE(cif_start_commands));
465}
466
467static int start_ms350_cam(struct gspca_dev *gspca_dev)
468{
469	struct init_command ms350_start_commands[] = {
470		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
471		{{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
472		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
473		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
474		{{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
475		{{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
476		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
477		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
478		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
479		{{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
480		{{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4},
481		{{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
482		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
483		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
484		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
485		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
486		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
487		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
488		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
489		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
490		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
491		{{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4},
492		{{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4},
493		{{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4},
494		{{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4},
495		{{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4},
496		{{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4},
497		{{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4},
498		{{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4},
499		{{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4},
500		{{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4},
501		{{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4},
502		{{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
503		{{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4},
504		{{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4},
505		{{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4},
506		{{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4},
507		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
508		{{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4},
509		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
510		{{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4},
511		{{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4},
512		{{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4},
513		{{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4},
514		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width  */
515		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */
516		{{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */
517		{{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
518		{{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */
519		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
520		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
521		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
522		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
523		{{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1},
524		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
525		{{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1},
526		{{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1},
527		{{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0},
528		/* Camera should start to capture now. */
529	};
530
531	return run_start_commands(gspca_dev, ms350_start_commands,
532				  ARRAY_SIZE(ms350_start_commands));
533}
534
535static int start_genius_cam(struct gspca_dev *gspca_dev)
536{
537	struct init_command genius_start_commands[] = {
538		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
539		{{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
540		{{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
541		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
542		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
543		/* "preliminary" width and height settings */
544		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
545		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
546		{{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
547		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
548		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
549		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
550		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
551		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
552		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
553		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
554		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
555		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
556		{{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
557		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
558		{{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
559		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
560		{{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
561		{{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
562		{{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
563		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
564		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
565		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
566		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
567		{{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
568		{{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
569		{{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
570		{{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
571		{{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
572		{{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
573		{{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
574		{{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
575		{{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
576		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */
577		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */
578		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
579		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
580		{{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
581		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
582		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
583		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
584		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
585		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
586		{{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
587		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
588		{{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
589		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
590		{{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
591		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
592		{{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
593		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
594		{{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
595		{{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
596		{{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
597		{{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
598		{{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
599		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0}
600		/* Camera should start to capture now. */
601	};
602
603	return run_start_commands(gspca_dev, genius_start_commands,
604				  ARRAY_SIZE(genius_start_commands));
605}
606
607static int start_genius_videocam_live(struct gspca_dev *gspca_dev)
608{
609	int r;
610	struct sd *sd = (struct sd *) gspca_dev;
611	struct init_command genius_vcam_live_start_commands[] = {
612		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 0},
613		{{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
614		{{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
615		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
616		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
617
618		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
619		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
620		{{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
621		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
622		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
623		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
624		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
625		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
626		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
627		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
628		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
629		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
630		{{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
631		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
632		{{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
633		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
634		{{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
635		{{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
636		{{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
637		{{0x1c, 0x20, 0x00, 0x2d, 0x00, 0x00}, 4},
638		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
639		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
640		{{0x13, 0x22, 0x01, 0x00, 0x00, 0x00}, 4},
641		{{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
642		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
643		{{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
644		{{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
645		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
646		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
647		{{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
648		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
649		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
650		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
651		{{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
652		{{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
653		{{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
654		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
655		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
656		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
657		{{0x11, 0x01, 0x04, 0x00, 0x00, 0x00}, 4},
658		{{0x11, 0x02, 0x92, 0x00, 0x00, 0x00}, 4},
659		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
660		{{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
661		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
662		{{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
663		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
664		{{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
665		{{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
666		{{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
667		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
668		{{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
669		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
670		{{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
671		{{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
672		{{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
673		{{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
674		{{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
675		{{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
676		{{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
677		{{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
678		{{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
679		{{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
680		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
681		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
682		{{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
683		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
684		{{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
685		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
686		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
687		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
688		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
689		{{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
690		{{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
691		{{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
692		{{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
693		{{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
694		{{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
695		{{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
696		{{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
697		{{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
698		{{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
699		{{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
700		{{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
701		{{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
702		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0},
703		/* Camera should start to capture now. */
704		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 0},
705		{{0x1b, 0x32, 0x26, 0x00, 0x00, 0x00}, 0},
706		{{0x1d, 0x25, 0x10, 0x20, 0xab, 0x00}, 0},
707	};
708
709	r = run_start_commands(gspca_dev, genius_vcam_live_start_commands,
710				  ARRAY_SIZE(genius_vcam_live_start_commands));
711	if (r < 0)
712		return r;
713
714	if (sd->gain)
715		set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
716
717	return r;
718}
719
720static int start_vivitar_cam(struct gspca_dev *gspca_dev)
721{
722	struct init_command vivitar_start_commands[] = {
723		{{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
724		{{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
725		{{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
726		{{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4},
727		{{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
728		{{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
729		{{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
730		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
731		{{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
732		{{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4},
733		/*
734		 * Above is changed from OEM 0x0b. Fixes Bayer tiling.
735		 * Presumably gives a vertical shift of one row.
736		 */
737		{{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
738		/* Above seems to do horizontal shift. */
739		{{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
740		{{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
741		{{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
742		{{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
743		{{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
744		{{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
745		/* Above three commands seem to relate to brightness. */
746		{{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
747		{{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
748		{{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
749		{{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1},
750		{{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1},
751		{{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1},
752		{{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1},
753		{{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
754		{{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1},
755		{{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1},
756		{{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1},
757		{{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1},
758		{{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1},
759		{{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1},
760		{{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1},
761		{{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1},
762		{{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
763		{{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1},
764		{{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1},
765		{{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1},
766		{{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1},
767		{{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1},
768		{{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1},
769		{{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1},
770		{{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1},
771		{{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1},
772		{{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1},
773		{{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1},
774		{{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1},
775		{{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1},
776		{{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1},
777		{{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1},
778		{{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1},
779		{{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1},
780		{{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1},
781		{{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1},
782		{{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1},
783		/* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
784		{{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
785		{{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */
786		{{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
787		{{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1},
788		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
789		{{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1},
790		{{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
791		{{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1},
792		/* Above is brightness; OEM driver setting is 0x10 */
793		{{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
794		{{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
795		{{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}
796	};
797
798	return run_start_commands(gspca_dev, vivitar_start_commands,
799				  ARRAY_SIZE(vivitar_start_commands));
800}
801
802static int sd_start(struct gspca_dev *gspca_dev)
803{
804	struct sd *sd = (struct sd *) gspca_dev;
805	int err_code;
806
807	sd->sof_read = 0;
808
809	switch (sd->model) {
810	case 0x7005:
811		err_code = start_genius_cam(gspca_dev);
812		break;
813	case 0x7003:
814		err_code = start_genius_videocam_live(gspca_dev);
815		break;
816	case 0x8001:
817		err_code = start_spy_cam(gspca_dev);
818		break;
819	case 0x8003:
820		err_code = start_cif_cam(gspca_dev);
821		break;
822	case 0x8008:
823		err_code = start_ms350_cam(gspca_dev);
824		break;
825	case 0x800a:
826		err_code = start_vivitar_cam(gspca_dev);
827		break;
828	default:
829		pr_err("Starting unknown camera, please report this\n");
830		return -ENXIO;
831	}
832
833	sd->avg_lum = -1;
834
835	return err_code;
836}
837
838static void sd_stopN(struct gspca_dev *gspca_dev)
839{
840	int result;
841	__u8 data[6];
842
843	result = sn9c2028_read1(gspca_dev);
844	if (result < 0)
845		gspca_err(gspca_dev, "Camera Stop read failed\n");
846
847	memset(data, 0, 6);
848	data[0] = 0x14;
849	result = sn9c2028_command(gspca_dev, data);
850	if (result < 0)
851		gspca_err(gspca_dev, "Camera Stop command failed\n");
852}
853
854static void do_autogain(struct gspca_dev *gspca_dev, int avg_lum)
855{
856	struct sd *sd = (struct sd *) gspca_dev;
857	s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
858
859	if (avg_lum == -1)
860		return;
861
862	if (avg_lum < MIN_AVG_LUM) {
863		if (cur_gain == sd->gain->maximum)
864			return;
865		cur_gain++;
866		v4l2_ctrl_s_ctrl(sd->gain, cur_gain);
867	}
868	if (avg_lum > MAX_AVG_LUM) {
869		if (cur_gain == sd->gain->minimum)
870			return;
871		cur_gain--;
872		v4l2_ctrl_s_ctrl(sd->gain, cur_gain);
873	}
874
875}
876
877static void sd_dqcallback(struct gspca_dev *gspca_dev)
878{
879	struct sd *sd = (struct sd *) gspca_dev;
880
881	if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain))
882		return;
883
884	do_autogain(gspca_dev, sd->avg_lum);
885}
886
887/* Include sn9c2028 sof detection functions */
888#include "sn9c2028.h"
889
890static void sd_pkt_scan(struct gspca_dev *gspca_dev,
891			__u8 *data,			/* isoc packet */
892			int len)			/* iso packet length */
893{
894	unsigned char *sof;
895
896	sof = sn9c2028_find_sof(gspca_dev, data, len);
897	if (sof) {
898		int n;
899
900		/* finish decoding current frame */
901		n = sof - data;
902		if (n > sizeof sn9c2028_sof_marker)
903			n -= sizeof sn9c2028_sof_marker;
904		else
905			n = 0;
906		gspca_frame_add(gspca_dev, LAST_PACKET, data, n);
907		/* Start next frame. */
908		gspca_frame_add(gspca_dev, FIRST_PACKET,
909			sn9c2028_sof_marker, sizeof sn9c2028_sof_marker);
910		len -= sof - data;
911		data = sof;
912	}
913	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
914}
915
916/* sub-driver description */
917static const struct sd_desc sd_desc = {
918	.name = MODULE_NAME,
919	.config = sd_config,
920	.init = sd_init,
921	.init_controls = sd_init_controls,
922	.start = sd_start,
923	.stopN = sd_stopN,
924	.dq_callback = sd_dqcallback,
925	.pkt_scan = sd_pkt_scan,
926};
927
928/* -- module initialisation -- */
929static const struct usb_device_id device_table[] = {
930	{USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */
931	{USB_DEVICE(0x0458, 0x7003)}, /* Genius Videocam Live v2  */
932	/* The Genius Smart is untested. I can't find an owner ! */
933	/* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */
934	{USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */
935	{USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */
936	/* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */
937	{USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */
938	{USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */
939	{}
940};
941MODULE_DEVICE_TABLE(usb, device_table);
942
943/* -- device connect -- */
944static int sd_probe(struct usb_interface *intf,
945			const struct usb_device_id *id)
946{
947	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
948			       THIS_MODULE);
949}
950
951static struct usb_driver sd_driver = {
952	.name = MODULE_NAME,
953	.id_table = device_table,
954	.probe = sd_probe,
955	.disconnect = gspca_disconnect,
956#ifdef CONFIG_PM
957	.suspend = gspca_suspend,
958	.resume = gspca_resume,
959	.reset_resume = gspca_resume,
960#endif
961};
962
963module_usb_driver(sd_driver);
964