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