blob: c8f05297d0f0680acccc7513231a5e561c57b3db [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Endpoints (formerly known as AOX) se401 USB Camera Driver
3 *
4 * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
5 *
6 * Still somewhat based on the Linux ov511 driver.
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 *
23 * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on
24 * their chipset available and supporting me while writing this driver.
25 * - Jeroen Vreeken
26 */
27
28static const char version[] = "0.24";
29
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/vmalloc.h>
33#include <linux/slab.h>
34#include <linux/pagemap.h>
35#include <linux/usb.h>
36#include "se401.h"
37
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030038static int flickerless;
Linus Torvalds1da177e2005-04-16 15:20:36 -070039static int video_nr = -1;
40
Alan Coxaae40fd2009-06-09 10:02:11 -030041static struct usb_device_id device_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */
43 { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */
44 { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */
45 { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */
46 { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */
47 { }
48};
49
50MODULE_DEVICE_TABLE(usb, device_table);
51
52MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
53MODULE_DESCRIPTION("SE401 USB Camera Driver");
54MODULE_LICENSE("GPL");
55module_param(flickerless, int, 0);
Alan Coxaae40fd2009-06-09 10:02:11 -030056MODULE_PARM_DESC(flickerless,
57 "Net frequency to adjust exposure time to (0/50/60)");
Linus Torvalds1da177e2005-04-16 15:20:36 -070058module_param(video_nr, int, 0);
59
60static struct usb_driver se401_driver;
61
62
63/**********************************************************************
64 *
65 * Memory management
66 *
67 **********************************************************************/
68static void *rvmalloc(unsigned long size)
69{
70 void *mem;
71 unsigned long adr;
72
73 size = PAGE_ALIGN(size);
74 mem = vmalloc_32(size);
75 if (!mem)
76 return NULL;
77
78 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
79 adr = (unsigned long) mem;
80 while (size > 0) {
81 SetPageReserved(vmalloc_to_page((void *)adr));
Alan Coxaae40fd2009-06-09 10:02:11 -030082 adr += PAGE_SIZE;
83 size -= PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 }
85
86 return mem;
87}
88
89static void rvfree(void *mem, unsigned long size)
90{
91 unsigned long adr;
92
93 if (!mem)
94 return;
95
96 adr = (unsigned long) mem;
97 while ((long) size > 0) {
98 ClearPageReserved(vmalloc_to_page((void *)adr));
Alan Coxaae40fd2009-06-09 10:02:11 -030099 adr += PAGE_SIZE;
100 size -= PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 }
102 vfree(mem);
103}
104
105
106
107/****************************************************************************
108 *
109 * se401 register read/write functions
110 *
111 ***************************************************************************/
112
113static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req,
114 unsigned short value, unsigned char *cp, int size)
115{
Alan Coxaae40fd2009-06-09 10:02:11 -0300116 return usb_control_msg(
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300117 se401->dev,
118 set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0),
119 req,
120 (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
121 value,
122 0,
123 cp,
124 size,
125 1000
126 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
128
129static int se401_set_feature(struct usb_se401 *se401, unsigned short selector,
130 unsigned short param)
131{
132 /* specs say that the selector (address) should go in the value field
133 and the param in index, but in the logs of the windows driver they do
134 this the other way around...
135 */
Alan Coxaae40fd2009-06-09 10:02:11 -0300136 return usb_control_msg(
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 se401->dev,
138 usb_sndctrlpipe(se401->dev, 0),
139 SE401_REQ_SET_EXT_FEATURE,
140 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
141 param,
142 selector,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300143 NULL,
144 0,
145 1000
146 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300149static unsigned short se401_get_feature(struct usb_se401 *se401,
150 unsigned short selector)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
152 /* For 'set' the selecetor should be in index, not sure if the spec is
153 wrong here to....
154 */
155 unsigned char cp[2];
Alan Coxaae40fd2009-06-09 10:02:11 -0300156 usb_control_msg(
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300157 se401->dev,
158 usb_rcvctrlpipe(se401->dev, 0),
159 SE401_REQ_GET_EXT_FEATURE,
160 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
161 0,
162 selector,
163 cp,
164 2,
165 1000
166 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return cp[0]+cp[1]*256;
168}
169
170/****************************************************************************
171 *
172 * Camera control
173 *
174 ***************************************************************************/
175
176
177static int se401_send_pict(struct usb_se401 *se401)
178{
Alan Coxaae40fd2009-06-09 10:02:11 -0300179 /* integration time low */
180 se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);
181 /* integration time mid */
182 se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);
183 /* integration time mid */
184 se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);
185 /* reset level value */
186 se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
187 /* red color gain */
188 se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);
189 /* green color gain */
190 se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);
191 /* blue color gain */
192 se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 return 0;
195}
196
197static void se401_set_exposure(struct usb_se401 *se401, int brightness)
198{
Alan Coxaae40fd2009-06-09 10:02:11 -0300199 int integration = brightness << 5;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300200
Alan Coxaae40fd2009-06-09 10:02:11 -0300201 if (flickerless == 50)
202 integration = integration-integration % 106667;
203 if (flickerless == 60)
204 integration = integration-integration % 88889;
205 se401->brightness = integration >> 5;
206 se401->expose_h = (integration >> 16) & 0xff;
207 se401->expose_m = (integration >> 8) & 0xff;
208 se401->expose_l = integration & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209}
210
211static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p)
212{
Alan Coxaae40fd2009-06-09 10:02:11 -0300213 p->brightness = se401->brightness;
214 if (se401->enhance)
215 p->whiteness = 32768;
216 else
217 p->whiteness = 0;
218
219 p->colour = 65535;
220 p->contrast = 65535;
221 p->hue = se401->rgain << 10;
222 p->palette = se401->palette;
223 p->depth = 3; /* rgb24 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 return 0;
225}
226
227
228static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p)
229{
230 if (p->palette != VIDEO_PALETTE_RGB24)
231 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -0300232 se401->palette = p->palette;
233 if (p->hue != se401->hue) {
234 se401->rgain = p->hue >> 10;
235 se401->bgain = 0x40-(p->hue >> 10);
236 se401->hue = p->hue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300238 if (p->brightness != se401->brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 se401_set_exposure(se401, p->brightness);
Alan Coxaae40fd2009-06-09 10:02:11 -0300240
241 if (p->whiteness >= 32768)
242 se401->enhance = 1;
243 else
244 se401->enhance = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 se401_send_pict(se401);
246 se401_send_pict(se401);
247 return 0;
248}
249
250/*
251 Hyundai have some really nice docs about this and other sensor related
252 stuff on their homepage: www.hei.co.kr
253*/
254static void se401_auto_resetlevel(struct usb_se401 *se401)
255{
256 unsigned int ahrc, alrc;
Alan Coxaae40fd2009-06-09 10:02:11 -0300257 int oldreset = se401->resetlevel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
259 /* For some reason this normally read-only register doesn't get reset
260 to zero after reading them just once...
261 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300262 se401_get_feature(se401, HV7131_REG_HIREFNOH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 se401_get_feature(se401, HV7131_REG_HIREFNOL);
264 se401_get_feature(se401, HV7131_REG_LOREFNOH);
265 se401_get_feature(se401, HV7131_REG_LOREFNOL);
Alan Coxaae40fd2009-06-09 10:02:11 -0300266 ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 se401_get_feature(se401, HV7131_REG_HIREFNOL);
Alan Coxaae40fd2009-06-09 10:02:11 -0300268 alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 se401_get_feature(se401, HV7131_REG_LOREFNOL);
270
271 /* Not an exact science, but it seems to work pretty well... */
272 if (alrc > 10) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300273 while (alrc >= 10 && se401->resetlevel < 63) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 se401->resetlevel++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300275 alrc /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 }
277 } else if (ahrc > 20) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300278 while (ahrc >= 20 && se401->resetlevel > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 se401->resetlevel--;
Alan Coxaae40fd2009-06-09 10:02:11 -0300280 ahrc /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 }
282 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300283 if (se401->resetlevel != oldreset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
285
286 return;
287}
288
289/* irq handler for snapshot button */
David Howells7d12e782006-10-05 14:55:46 +0100290static void se401_button_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291{
292 struct usb_se401 *se401 = urb->context;
293 int status;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 if (!se401->dev) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300296 dev_info(&urb->dev->dev, "device vapourished\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 return;
298 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300299
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 switch (urb->status) {
301 case 0:
302 /* success */
303 break;
304 case -ECONNRESET:
305 case -ENOENT:
306 case -ESHUTDOWN:
307 /* this urb is terminated, clean up */
Alan Coxaae40fd2009-06-09 10:02:11 -0300308 dbg("%s - urb shutting down with status: %d",
309 __func__, urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 return;
311 default:
Alan Coxaae40fd2009-06-09 10:02:11 -0300312 dbg("%s - nonzero urb status received: %d",
313 __func__, urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 goto exit;
315 }
316
Alan Coxaae40fd2009-06-09 10:02:11 -0300317 if (urb->actual_length >= 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 if (se401->button)
Alan Coxaae40fd2009-06-09 10:02:11 -0300319 se401->buttonpressed = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320exit:
Alan Coxaae40fd2009-06-09 10:02:11 -0300321 status = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 if (status)
Alan Coxaae40fd2009-06-09 10:02:11 -0300323 err("%s - usb_submit_urb failed with result %d",
Harvey Harrison7e28adb2008-04-08 23:20:00 -0300324 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325}
326
David Howells7d12e782006-10-05 14:55:46 +0100327static void se401_video_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328{
329 struct usb_se401 *se401 = urb->context;
330 int length = urb->actual_length;
331
332 /* ohoh... */
333 if (!se401->streaming)
334 return;
335
336 if (!se401->dev) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300337 dev_info(&urb->dev->dev, "device vapourished\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 return;
339 }
340
341 /* 0 sized packets happen if we are to fast, but sometimes the camera
342 keeps sending them forever...
343 */
344 if (length && !urb->status) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300345 se401->nullpackets = 0;
346 switch (se401->scratch[se401->scratch_next].state) {
347 case BUFFER_READY:
348 case BUFFER_BUSY:
349 se401->dropped++;
350 break;
351 case BUFFER_UNUSED:
352 memcpy(se401->scratch[se401->scratch_next].data,
353 (unsigned char *)urb->transfer_buffer, length);
354 se401->scratch[se401->scratch_next].state
355 = BUFFER_READY;
356 se401->scratch[se401->scratch_next].offset
357 = se401->bayeroffset;
358 se401->scratch[se401->scratch_next].length = length;
359 if (waitqueue_active(&se401->wq))
360 wake_up_interruptible(&se401->wq);
361 se401->scratch_overflow = 0;
362 se401->scratch_next++;
363 if (se401->scratch_next >= SE401_NUMSCRATCH)
364 se401->scratch_next = 0;
365 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300367 se401->bayeroffset += length;
368 if (se401->bayeroffset >= se401->cheight * se401->cwidth)
369 se401->bayeroffset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 } else {
371 se401->nullpackets++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300372 if (se401->nullpackets > SE401_MAX_NULLPACKETS)
373 if (waitqueue_active(&se401->wq))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 wake_up_interruptible(&se401->wq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 }
376
377 /* Resubmit urb for new data */
Alan Coxaae40fd2009-06-09 10:02:11 -0300378 urb->status = 0;
379 urb->dev = se401->dev;
380 if (usb_submit_urb(urb, GFP_KERNEL))
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300381 dev_info(&urb->dev->dev, "urb burned down\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 return;
383}
384
385static void se401_send_size(struct usb_se401 *se401, int width, int height)
386{
Alan Coxaae40fd2009-06-09 10:02:11 -0300387 int i = 0;
388 int mode = 0x03; /* No compression */
389 int sendheight = height;
390 int sendwidth = width;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 /* JangGu compression can only be used with the camera supported sizes,
393 but bayer seems to work with any size that fits on the sensor.
394 We check if we can use compression with the current size with either
395 4 or 16 times subcapturing, if not we use uncompressed bayer data
396 but this will result in cutouts of the maximum size....
397 */
Alan Coxaae40fd2009-06-09 10:02:11 -0300398 while (i < se401->sizes && !(se401->width[i] == width &&
399 se401->height[i] == height))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 i++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300401 while (i < se401->sizes) {
402 if (se401->width[i] == width * 2 &&
403 se401->height[i] == height * 2) {
404 sendheight = se401->height[i];
405 sendwidth = se401->width[i];
406 mode = 0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300408 if (se401->width[i] == width * 4 &&
409 se401->height[i] == height * 4) {
410 sendheight = se401->height[i];
411 sendwidth = se401->width[i];
412 mode = 0x42;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 }
414 i++;
415 }
416
417 se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0);
418 se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0);
419 se401_set_feature(se401, SE401_OPERATINGMODE, mode);
420
Alan Coxaae40fd2009-06-09 10:02:11 -0300421 if (mode == 0x03)
422 se401->format = FMT_BAYER;
423 else
424 se401->format = FMT_JANGGU;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
427/*
428 In this function se401_send_pict is called several times,
429 for some reason (depending on the state of the sensor and the phase of
430 the moon :) doing this only in either place doesn't always work...
431*/
432static int se401_start_stream(struct usb_se401 *se401)
433{
434 struct urb *urb;
Alan Coxaae40fd2009-06-09 10:02:11 -0300435 int err = 0, i;
436 se401->streaming = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300438 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
439 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
441 /* Set picture settings */
Alan Coxaae40fd2009-06-09 10:02:11 -0300442 /* windowed + pix intg */
443 se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 se401_send_pict(se401);
445
446 se401_send_size(se401, se401->cwidth, se401->cheight);
447
Alan Coxaae40fd2009-06-09 10:02:11 -0300448 se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE,
449 0, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
451 /* Do some memory allocation */
Alan Coxaae40fd2009-06-09 10:02:11 -0300452 for (i = 0; i < SE401_NUMFRAMES; i++) {
453 se401->frame[i].data = se401->fbuf + i * se401->maxframesize;
454 se401->frame[i].curpix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300456 for (i = 0; i < SE401_NUMSBUF; i++) {
457 se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
Amit Choudharyfd51c692007-03-30 17:48:59 -0300458 if (!se401->sbuf[i].data) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300459 for (i = i - 1; i >= 0; i--) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300460 kfree(se401->sbuf[i].data);
461 se401->sbuf[i].data = NULL;
462 }
463 return -ENOMEM;
464 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 }
466
Alan Coxaae40fd2009-06-09 10:02:11 -0300467 se401->bayeroffset = 0;
468 se401->scratch_next = 0;
469 se401->scratch_use = 0;
470 se401->scratch_overflow = 0;
471 for (i = 0; i < SE401_NUMSCRATCH; i++) {
472 se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
Amit Choudharyfd51c692007-03-30 17:48:59 -0300473 if (!se401->scratch[i].data) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300474 for (i = i - 1; i >= 0; i--) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300475 kfree(se401->scratch[i].data);
476 se401->scratch[i].data = NULL;
477 }
478 goto nomem_sbuf;
479 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300480 se401->scratch[i].state = BUFFER_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 }
482
Alan Coxaae40fd2009-06-09 10:02:11 -0300483 for (i = 0; i < SE401_NUMSBUF; i++) {
484 urb = usb_alloc_urb(0, GFP_KERNEL);
485 if (!urb) {
486 for (i = i - 1; i >= 0; i--) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300487 usb_kill_urb(se401->urb[i]);
488 usb_free_urb(se401->urb[i]);
489 se401->urb[i] = NULL;
490 }
491 goto nomem_scratch;
492 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
494 usb_fill_bulk_urb(urb, se401->dev,
495 usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
496 se401->sbuf[i].data, SE401_PACKETSIZE,
497 se401_video_irq,
498 se401);
499
Alan Coxaae40fd2009-06-09 10:02:11 -0300500 se401->urb[i] = urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Alan Coxaae40fd2009-06-09 10:02:11 -0300502 err = usb_submit_urb(se401->urb[i], GFP_KERNEL);
503 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 err("urb burned down");
505 }
506
Alan Coxaae40fd2009-06-09 10:02:11 -0300507 se401->framecount = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 return 0;
Amit Choudharyfd51c692007-03-30 17:48:59 -0300510
511 nomem_scratch:
Alan Coxaae40fd2009-06-09 10:02:11 -0300512 for (i = 0; i < SE401_NUMSCRATCH; i++) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300513 kfree(se401->scratch[i].data);
514 se401->scratch[i].data = NULL;
515 }
516 nomem_sbuf:
Alan Coxaae40fd2009-06-09 10:02:11 -0300517 for (i = 0; i < SE401_NUMSBUF; i++) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300518 kfree(se401->sbuf[i].data);
519 se401->sbuf[i].data = NULL;
520 }
521 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522}
523
524static int se401_stop_stream(struct usb_se401 *se401)
525{
526 int i;
527
528 if (!se401->streaming || !se401->dev)
529 return 1;
530
Alan Coxaae40fd2009-06-09 10:02:11 -0300531 se401->streaming = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
533 se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0);
534
535 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
536 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
537
Alan Coxaae40fd2009-06-09 10:02:11 -0300538 for (i = 0; i < SE401_NUMSBUF; i++)
539 if (se401->urb[i]) {
540 usb_kill_urb(se401->urb[i]);
541 usb_free_urb(se401->urb[i]);
542 se401->urb[i] = NULL;
543 kfree(se401->sbuf[i].data);
544 }
545 for (i = 0; i < SE401_NUMSCRATCH; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 kfree(se401->scratch[i].data);
Alan Coxaae40fd2009-06-09 10:02:11 -0300547 se401->scratch[i].data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
549
550 return 0;
551}
552
553static int se401_set_size(struct usb_se401 *se401, int width, int height)
554{
Alan Coxaae40fd2009-06-09 10:02:11 -0300555 int wasstreaming = se401->streaming;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 /* Check to see if we need to change */
Alan Coxaae40fd2009-06-09 10:02:11 -0300557 if (se401->cwidth == width && se401->cheight == height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 return 0;
559
560 /* Check for a valid mode */
561 if (!width || !height)
562 return 1;
563 if ((width & 1) || (height & 1))
564 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -0300565 if (width > se401->width[se401->sizes-1])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -0300567 if (height > se401->height[se401->sizes-1])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 return 1;
569
570 /* Stop a current stream and start it again at the new size */
571 if (wasstreaming)
572 se401_stop_stream(se401);
Alan Coxaae40fd2009-06-09 10:02:11 -0300573 se401->cwidth = width;
574 se401->cheight = height;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 if (wasstreaming)
576 se401_start_stream(se401);
577 return 0;
578}
579
580
581/****************************************************************************
582 *
583 * Video Decoding
584 *
585 ***************************************************************************/
586
587/*
588 This shouldn't really be done in a v4l driver....
589 But it does make the image look a lot more usable.
590 Basically it lifts the dark pixels more than the light pixels.
591*/
592static inline void enhance_picture(unsigned char *frame, int len)
593{
594 while (len--) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300595 *frame = (((*frame^255)*(*frame^255))/255)^255;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 frame++;
597 }
598}
599
600static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data)
601{
Alan Coxaae40fd2009-06-09 10:02:11 -0300602 struct se401_frame *frame = &se401->frame[se401->curframe];
603 int linelength = se401->cwidth * 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
605 if (frame->curlinepix >= linelength) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300606 frame->curlinepix = 0;
607 frame->curline += linelength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 }
609
610 /* First three are absolute, all others relative.
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300611 * Format is rgb from right to left (mirrorred image),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 * we flip it to get bgr from left to right. */
Alan Coxaae40fd2009-06-09 10:02:11 -0300613 if (frame->curlinepix < 3)
614 *(frame->curline-frame->curlinepix) = 1 + data * 4;
615 else
616 *(frame->curline-frame->curlinepix) =
617 *(frame->curline-frame->curlinepix + 3) + data * 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 frame->curlinepix++;
619}
620
Alan Coxaae40fd2009-06-09 10:02:11 -0300621static inline void decode_JangGu_vlc(struct usb_se401 *se401,
622 unsigned char *data, int bit_exp, int packetlength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623{
Alan Coxaae40fd2009-06-09 10:02:11 -0300624 int pos = 0;
625 int vlc_cod = 0;
626 int vlc_size = 0;
627 int vlc_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 int bit_cur;
629 int bit;
Alan Coxaae40fd2009-06-09 10:02:11 -0300630 data += 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 while (pos < packetlength) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300632 bit_cur = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 while (bit_cur && bit_exp) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300634 bit = ((*data) >> (bit_cur-1))&1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 if (!vlc_cod) {
636 if (bit) {
637 vlc_size++;
638 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300639 if (!vlc_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 decode_JangGu_integrate(se401, 0);
Alan Coxaae40fd2009-06-09 10:02:11 -0300641 else {
642 vlc_cod = 2;
643 vlc_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
645 }
646 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300647 if (vlc_cod == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 if (!bit)
Alan Coxaae40fd2009-06-09 10:02:11 -0300649 vlc_data = -(1 << vlc_size) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 vlc_cod--;
651 }
652 vlc_size--;
Alan Coxaae40fd2009-06-09 10:02:11 -0300653 vlc_data += bit << vlc_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 if (!vlc_size) {
655 decode_JangGu_integrate(se401, vlc_data);
Alan Coxaae40fd2009-06-09 10:02:11 -0300656 vlc_cod = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 }
658 }
659 bit_cur--;
660 bit_exp--;
661 }
662 pos++;
663 data++;
664 }
665}
666
Alan Coxaae40fd2009-06-09 10:02:11 -0300667static inline void decode_JangGu(struct usb_se401 *se401,
668 struct se401_scratch *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
Alan Coxaae40fd2009-06-09 10:02:11 -0300670 unsigned char *data = buffer->data;
671 int len = buffer->length;
672 int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size;
673 int datapos = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 /* New image? */
676 if (!se401->frame[se401->curframe].curpix) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300677 se401->frame[se401->curframe].curlinepix = 0;
678 se401->frame[se401->curframe].curline =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 se401->frame[se401->curframe].data+
Alan Coxaae40fd2009-06-09 10:02:11 -0300680 se401->cwidth * 3 - 1;
681 if (se401->frame[se401->curframe].grabstate == FRAME_READY)
682 se401->frame[se401->curframe].grabstate = FRAME_GRABBING;
683 se401->vlcdatapos = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 }
685 while (datapos < len) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300686 size = 1024 - se401->vlcdatapos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 if (size+datapos > len)
Alan Coxaae40fd2009-06-09 10:02:11 -0300688 size = len-datapos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size);
Alan Coxaae40fd2009-06-09 10:02:11 -0300690 se401->vlcdatapos += size;
691 packetlength = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 if (se401->vlcdatapos >= 4) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300693 bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8);
694 pix_exp = se401->vlcdata[1] +
695 ((se401->vlcdata[0] & 0x3f) << 8);
696 frameinfo = se401->vlcdata[0] & 0xc0;
697 packetlength = ((bit_exp + 47) >> 4) << 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 if (packetlength > 1024) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300699 se401->vlcdatapos = 0;
700 datapos = len;
701 packetlength = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 se401->error++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300703 se401->frame[se401->curframe].curpix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 }
705 }
706 if (packetlength && se401->vlcdatapos >= packetlength) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300707 decode_JangGu_vlc(se401, se401->vlcdata, bit_exp,
708 packetlength);
709 se401->frame[se401->curframe].curpix += pix_exp * 3;
710 datapos += size-(se401->vlcdatapos-packetlength);
711 se401->vlcdatapos = 0;
712 if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) {
713 if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) {
714 if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) {
715 se401->frame[se401->curframe].grabstate = FRAME_DONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 se401->framecount++;
717 se401->readcount++;
718 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300719 if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY)
720 se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1);
721 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 se401->error++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300723 se401->frame[se401->curframe].curpix = 0;
724 datapos = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300726 } else
727 datapos += size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 }
729}
730
Alan Coxaae40fd2009-06-09 10:02:11 -0300731static inline void decode_bayer(struct usb_se401 *se401,
732 struct se401_scratch *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733{
Alan Coxaae40fd2009-06-09 10:02:11 -0300734 unsigned char *data = buffer->data;
735 int len = buffer->length;
736 int offset = buffer->offset;
737 int datasize = se401->cwidth * se401->cheight;
738 struct se401_frame *frame = &se401->frame[se401->curframe];
739 unsigned char *framedata = frame->data, *curline, *nextline;
740 int width = se401->cwidth;
741 int blineoffset = 0, bline;
742 int linelength = width * 3, i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Alan Coxaae40fd2009-06-09 10:02:11 -0300745 if (frame->curpix == 0) {
746 if (frame->grabstate == FRAME_READY)
747 frame->grabstate = FRAME_GRABBING;
748
749 frame->curline = framedata + linelength;
750 frame->curlinepix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 }
752
Alan Coxaae40fd2009-06-09 10:02:11 -0300753 if (offset != frame->curpix) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 /* Regard frame as lost :( */
Alan Coxaae40fd2009-06-09 10:02:11 -0300755 frame->curpix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 se401->error++;
757 return;
758 }
759
760 /* Check if we have to much data */
Alan Coxaae40fd2009-06-09 10:02:11 -0300761 if (frame->curpix + len > datasize)
762 len = datasize-frame->curpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Alan Coxaae40fd2009-06-09 10:02:11 -0300764 if (se401->cheight % 4)
765 blineoffset = 1;
766 bline = frame->curpix / se401->cwidth+blineoffset;
767
768 curline = frame->curline;
769 nextline = curline + linelength;
770 if (nextline >= framedata+datasize * 3)
771 nextline = curline;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 while (len) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300773 if (frame->curlinepix >= width) {
774 frame->curlinepix -= width;
775 bline = frame->curpix / width + blineoffset;
776 curline += linelength*2;
777 nextline += linelength*2;
778 if (curline >= framedata+datasize * 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 frame->curlinepix++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300780 curline -= 3;
781 nextline -= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 len--;
783 data++;
784 frame->curpix++;
785 }
786 if (nextline >= framedata+datasize*3)
Alan Coxaae40fd2009-06-09 10:02:11 -0300787 nextline = curline;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300789 if (bline & 1) {
790 if (frame->curlinepix & 1) {
791 *(curline + 2) = *data;
792 *(curline - 1) = *data;
793 *(nextline + 2) = *data;
794 *(nextline - 1) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300796 *(curline + 1) =
797 (*(curline + 1) + *data) / 2;
798 *(curline-2) =
799 (*(curline - 2) + *data) / 2;
800 *(nextline + 1) = *data;
801 *(nextline - 2) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 }
803 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300804 if (frame->curlinepix & 1) {
805 *(curline + 1) =
806 (*(curline + 1) + *data) / 2;
807 *(curline - 2) =
808 (*(curline - 2) + *data) / 2;
809 *(nextline + 1) = *data;
810 *(nextline - 2) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300812 *curline = *data;
813 *(curline - 3) = *data;
814 *nextline = *data;
815 *(nextline - 3) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 }
817 }
818 frame->curlinepix++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300819 curline -= 3;
820 nextline -= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 len--;
822 data++;
823 frame->curpix++;
824 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300825 frame->curline = curline;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Alan Coxaae40fd2009-06-09 10:02:11 -0300827 if (frame->curpix >= datasize) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 /* Fix the top line */
Alan Coxaae40fd2009-06-09 10:02:11 -0300829 framedata += linelength;
830 for (i = 0; i < linelength; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 framedata--;
Alan Coxaae40fd2009-06-09 10:02:11 -0300832 *framedata = *(framedata + linelength);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 }
834 /* Fix the left side (green is already present) */
Alan Coxaae40fd2009-06-09 10:02:11 -0300835 for (i = 0; i < se401->cheight; i++) {
836 *framedata = *(framedata + 3);
837 *(framedata + 1) = *(framedata + 4);
838 *(framedata + 2) = *(framedata + 5);
839 framedata += linelength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300841 frame->curpix = 0;
842 frame->grabstate = FRAME_DONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 se401->framecount++;
844 se401->readcount++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300845 if (se401->frame[(se401->curframe + 1) &
846 (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) {
847 se401->curframe = (se401->curframe+1) &
848 (SE401_NUMFRAMES-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 }
850 }
851}
852
853static int se401_newframe(struct usb_se401 *se401, int framenr)
854{
855 DECLARE_WAITQUEUE(wait, current);
Alan Coxaae40fd2009-06-09 10:02:11 -0300856 int errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 while (se401->streaming &&
Alan Coxaae40fd2009-06-09 10:02:11 -0300859 (se401->frame[framenr].grabstate == FRAME_READY ||
860 se401->frame[framenr].grabstate == FRAME_GRABBING)) {
861 if (!se401->frame[framenr].curpix)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 errors++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300863
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 wait_interruptible(
Alan Coxaae40fd2009-06-09 10:02:11 -0300865 se401->scratch[se401->scratch_use].state != BUFFER_READY,
866 &se401->wq, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300868 se401->nullpackets = 0;
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300869 dev_info(&se401->dev->dev,
Alan Coxaae40fd2009-06-09 10:02:11 -0300870 "too many null length packets, restarting capture\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 se401_stop_stream(se401);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300872 se401_start_stream(se401);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300874 if (se401->scratch[se401->scratch_use].state !=
875 BUFFER_READY) {
876 se401->frame[framenr].grabstate = FRAME_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 return -EIO;
878 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300879 se401->scratch[se401->scratch_use].state = BUFFER_BUSY;
880 if (se401->format == FMT_JANGGU)
881 decode_JangGu(se401,
882 &se401->scratch[se401->scratch_use]);
883 else
884 decode_bayer(se401,
885 &se401->scratch[se401->scratch_use]);
886
887 se401->scratch[se401->scratch_use].state =
888 BUFFER_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 se401->scratch_use++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300890 if (se401->scratch_use >= SE401_NUMSCRATCH)
891 se401->scratch_use = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (errors > SE401_MAX_ERRORS) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300893 errors = 0;
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300894 dev_info(&se401->dev->dev,
Alan Coxaae40fd2009-06-09 10:02:11 -0300895 "too many errors, restarting capture\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 se401_stop_stream(se401);
897 se401_start_stream(se401);
898 }
899 }
900 }
901
Alan Coxaae40fd2009-06-09 10:02:11 -0300902 if (se401->frame[framenr].grabstate == FRAME_DONE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 if (se401->enhance)
Alan Coxaae40fd2009-06-09 10:02:11 -0300904 enhance_picture(se401->frame[framenr].data,
905 se401->cheight * se401->cwidth * 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 return 0;
907}
908
Alan Coxaae40fd2009-06-09 10:02:11 -0300909static void usb_se401_remove_disconnected(struct usb_se401 *se401)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910{
911 int i;
912
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300913 se401->dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Alan Coxaae40fd2009-06-09 10:02:11 -0300915 for (i = 0; i < SE401_NUMSBUF; i++)
Jesper Juhl1bc3c9e2005-04-18 17:39:34 -0700916 if (se401->urb[i]) {
917 usb_kill_urb(se401->urb[i]);
918 usb_free_urb(se401->urb[i]);
919 se401->urb[i] = NULL;
920 kfree(se401->sbuf[i].data);
921 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300922
923 for (i = 0; i < SE401_NUMSCRATCH; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 kfree(se401->scratch[i].data);
Alan Coxaae40fd2009-06-09 10:02:11 -0300925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 if (se401->inturb) {
927 usb_kill_urb(se401->inturb);
928 usb_free_urb(se401->inturb);
929 }
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300930 dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300932 /* Free the memory */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 kfree(se401->width);
934 kfree(se401->height);
935 kfree(se401);
936}
937
938
939
940/****************************************************************************
941 *
942 * Video4Linux
943 *
944 ***************************************************************************/
945
946
Hans Verkuilbec43662008-12-30 06:58:20 -0300947static int se401_open(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948{
949 struct video_device *dev = video_devdata(file);
950 struct usb_se401 *se401 = (struct usb_se401 *)dev;
951 int err = 0;
952
Hans Verkuild56dc612008-07-30 08:43:36 -0300953 lock_kernel();
954 if (se401->user) {
955 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 return -EBUSY;
Hans Verkuild56dc612008-07-30 08:43:36 -0300957 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
959 if (se401->fbuf)
960 file->private_data = dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300961 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 err = -ENOMEM;
963 se401->user = !err;
Hans Verkuild56dc612008-07-30 08:43:36 -0300964 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 return err;
967}
968
Hans Verkuilbec43662008-12-30 06:58:20 -0300969static int se401_close(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970{
971 struct video_device *dev = file->private_data;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300972 struct usb_se401 *se401 = (struct usb_se401 *)dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 int i;
974
975 rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300976 if (se401->removed) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300977 dev_info(&se401->dev->dev, "device unregistered\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 usb_se401_remove_disconnected(se401);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300980 for (i = 0; i < SE401_NUMFRAMES; i++)
981 se401->frame[i].grabstate = FRAME_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 if (se401->streaming)
983 se401_stop_stream(se401);
Alan Coxaae40fd2009-06-09 10:02:11 -0300984 se401->user = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 }
986 file->private_data = NULL;
987 return 0;
988}
989
Hans Verkuil069b7472008-12-30 07:04:34 -0300990static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991{
992 struct video_device *vdev = file->private_data;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300993 struct usb_se401 *se401 = (struct usb_se401 *)vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300995 if (!se401->dev)
996 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300998 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 case VIDIOCGCAP:
1000 {
1001 struct video_capability *b = arg;
1002 strcpy(b->name, se401->camera_name);
1003 b->type = VID_TYPE_CAPTURE;
1004 b->channels = 1;
1005 b->audios = 0;
1006 b->maxwidth = se401->width[se401->sizes-1];
1007 b->maxheight = se401->height[se401->sizes-1];
1008 b->minwidth = se401->width[0];
1009 b->minheight = se401->height[0];
1010 return 0;
1011 }
1012 case VIDIOCGCHAN:
1013 {
1014 struct video_channel *v = arg;
1015
1016 if (v->channel != 0)
1017 return -EINVAL;
1018 v->flags = 0;
1019 v->tuners = 0;
1020 v->type = VIDEO_TYPE_CAMERA;
1021 strcpy(v->name, "Camera");
1022 return 0;
1023 }
1024 case VIDIOCSCHAN:
1025 {
1026 struct video_channel *v = arg;
1027
1028 if (v->channel != 0)
1029 return -EINVAL;
1030 return 0;
1031 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001032 case VIDIOCGPICT:
1033 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 struct video_picture *p = arg;
1035
1036 se401_get_pict(se401, p);
1037 return 0;
1038 }
1039 case VIDIOCSPICT:
1040 {
1041 struct video_picture *p = arg;
1042
1043 if (se401_set_pict(se401, p))
1044 return -EINVAL;
1045 return 0;
1046 }
1047 case VIDIOCSWIN:
1048 {
1049 struct video_window *vw = arg;
1050
1051 if (vw->flags)
1052 return -EINVAL;
1053 if (vw->clipcount)
1054 return -EINVAL;
1055 if (se401_set_size(se401, vw->width, vw->height))
1056 return -EINVAL;
1057 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001058 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 case VIDIOCGWIN:
1060 {
1061 struct video_window *vw = arg;
1062
1063 vw->x = 0; /* FIXME */
1064 vw->y = 0;
1065 vw->chromakey = 0;
1066 vw->flags = 0;
1067 vw->clipcount = 0;
1068 vw->width = se401->cwidth;
1069 vw->height = se401->cheight;
1070 return 0;
1071 }
1072 case VIDIOCGMBUF:
1073 {
1074 struct video_mbuf *vm = arg;
1075 int i;
1076
1077 memset(vm, 0, sizeof(*vm));
1078 vm->size = SE401_NUMFRAMES * se401->maxframesize;
1079 vm->frames = SE401_NUMFRAMES;
Alan Coxaae40fd2009-06-09 10:02:11 -03001080 for (i = 0; i < SE401_NUMFRAMES; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 vm->offsets[i] = se401->maxframesize * i;
1082 return 0;
1083 }
1084 case VIDIOCMCAPTURE:
1085 {
1086 struct video_mmap *vm = arg;
1087
1088 if (vm->format != VIDEO_PALETTE_RGB24)
1089 return -EINVAL;
1090 if (vm->frame >= SE401_NUMFRAMES)
1091 return -EINVAL;
1092 if (se401->frame[vm->frame].grabstate != FRAME_UNUSED)
1093 return -EBUSY;
1094
1095 /* Is this according to the v4l spec??? */
1096 if (se401_set_size(se401, vm->width, vm->height))
1097 return -EINVAL;
Alan Coxaae40fd2009-06-09 10:02:11 -03001098 se401->frame[vm->frame].grabstate = FRAME_READY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099
1100 if (!se401->streaming)
1101 se401_start_stream(se401);
1102
1103 /* Set the picture properties */
Alan Coxaae40fd2009-06-09 10:02:11 -03001104 if (se401->framecount == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 se401_send_pict(se401);
1106 /* Calibrate the reset level after a few frames. */
Alan Coxaae40fd2009-06-09 10:02:11 -03001107 if (se401->framecount % 20 == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 se401_auto_resetlevel(se401);
1109
1110 return 0;
1111 }
1112 case VIDIOCSYNC:
1113 {
1114 int *frame = arg;
Alan Coxaae40fd2009-06-09 10:02:11 -03001115 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
Alan Coxaae40fd2009-06-09 10:02:11 -03001117 if (*frame < 0 || *frame >= SE401_NUMFRAMES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 return -EINVAL;
1119
Alan Coxaae40fd2009-06-09 10:02:11 -03001120 ret = se401_newframe(se401, *frame);
1121 se401->frame[*frame].grabstate = FRAME_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 return ret;
1123 }
1124 case VIDIOCGFBUF:
1125 {
1126 struct video_buffer *vb = arg;
1127
1128 memset(vb, 0, sizeof(*vb));
1129 return 0;
1130 }
1131 case VIDIOCKEY:
1132 return 0;
1133 case VIDIOCCAPTURE:
1134 return -EINVAL;
1135 case VIDIOCSFBUF:
1136 return -EINVAL;
1137 case VIDIOCGTUNER:
1138 case VIDIOCSTUNER:
1139 return -EINVAL;
1140 case VIDIOCGFREQ:
1141 case VIDIOCSFREQ:
1142 return -EINVAL;
1143 case VIDIOCGAUDIO:
1144 case VIDIOCSAUDIO:
1145 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001146 default:
1147 return -ENOIOCTLCMD;
1148 } /* end switch */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001150 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151}
1152
Hans Verkuil069b7472008-12-30 07:04:34 -03001153static long se401_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 unsigned int cmd, unsigned long arg)
1155{
Hans Verkuilf473bf72008-11-01 08:25:11 -03001156 return video_usercopy(file, cmd, arg, se401_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157}
1158
1159static ssize_t se401_read(struct file *file, char __user *buf,
1160 size_t count, loff_t *ppos)
1161{
Alan Coxaae40fd2009-06-09 10:02:11 -03001162 int realcount = count, ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 struct video_device *dev = file->private_data;
1164 struct usb_se401 *se401 = (struct usb_se401 *)dev;
1165
1166
Alan Coxaae40fd2009-06-09 10:02:11 -03001167 if (se401->dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 return -EIO;
1169 if (realcount > se401->cwidth*se401->cheight*3)
Alan Coxaae40fd2009-06-09 10:02:11 -03001170 realcount = se401->cwidth*se401->cheight*3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
1172 /* Shouldn't happen: */
Alan Coxaae40fd2009-06-09 10:02:11 -03001173 if (se401->frame[0].grabstate == FRAME_GRABBING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 return -EBUSY;
Alan Coxaae40fd2009-06-09 10:02:11 -03001175 se401->frame[0].grabstate = FRAME_READY;
1176 se401->frame[1].grabstate = FRAME_UNUSED;
1177 se401->curframe = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
1179 if (!se401->streaming)
1180 se401_start_stream(se401);
1181
1182 /* Set the picture properties */
Alan Coxaae40fd2009-06-09 10:02:11 -03001183 if (se401->framecount == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 se401_send_pict(se401);
1185 /* Calibrate the reset level after a few frames. */
Alan Coxaae40fd2009-06-09 10:02:11 -03001186 if (se401->framecount%20 == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 se401_auto_resetlevel(se401);
1188
Alan Coxaae40fd2009-06-09 10:02:11 -03001189 ret = se401_newframe(se401, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
Alan Coxaae40fd2009-06-09 10:02:11 -03001191 se401->frame[0].grabstate = FRAME_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 if (ret)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001193 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 if (copy_to_user(buf, se401->frame[0].data, realcount))
1195 return -EFAULT;
1196
1197 return realcount;
1198}
1199
1200static int se401_mmap(struct file *file, struct vm_area_struct *vma)
1201{
1202 struct video_device *dev = file->private_data;
1203 struct usb_se401 *se401 = (struct usb_se401 *)dev;
1204 unsigned long start = vma->vm_start;
1205 unsigned long size = vma->vm_end-vma->vm_start;
1206 unsigned long page, pos;
1207
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001208 mutex_lock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
Alan Coxaae40fd2009-06-09 10:02:11 -03001210 if (se401->dev == NULL) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001211 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 return -EIO;
1213 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001214 if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1)
1215 & ~(PAGE_SIZE - 1))) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001216 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 return -EINVAL;
1218 }
1219 pos = (unsigned long)se401->fbuf;
1220 while (size > 0) {
1221 page = vmalloc_to_pfn((void *)pos);
1222 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001223 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 return -EAGAIN;
1225 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001226 start += PAGE_SIZE;
1227 pos += PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 if (size > PAGE_SIZE)
Alan Coxaae40fd2009-06-09 10:02:11 -03001229 size -= PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 else
1231 size = 0;
1232 }
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001233 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001235 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236}
1237
Hans Verkuilbec43662008-12-30 06:58:20 -03001238static const struct v4l2_file_operations se401_fops = {
Alan Coxaae40fd2009-06-09 10:02:11 -03001239 .owner = THIS_MODULE,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001240 .open = se401_open,
1241 .release = se401_close,
1242 .read = se401_read,
1243 .mmap = se401_mmap,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 .ioctl = se401_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245};
1246static struct video_device se401_template = {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001247 .name = "se401 USB camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 .fops = &se401_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03001249 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250};
1251
1252
1253
1254/***************************/
1255static int se401_init(struct usb_se401 *se401, int button)
1256{
Alan Coxaae40fd2009-06-09 10:02:11 -03001257 int i = 0, rc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001258 unsigned char cp[0x40];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 char temp[200];
Alan Cox6f4d7232009-06-11 11:04:11 -03001260 int slen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
1262 /* led on */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001263 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264
1265 /* get camera descriptor */
Alan Coxaae40fd2009-06-09 10:02:11 -03001266 rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0,
1267 cp, sizeof(cp));
Alan Cox6f4d7232009-06-11 11:04:11 -03001268 if (cp[1] != 0x41) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 err("Wrong descriptor type");
1270 return 1;
1271 }
Alan Cox6f4d7232009-06-11 11:04:11 -03001272 slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Alan Coxaae40fd2009-06-09 10:02:11 -03001274 se401->sizes = cp[4] + cp[5] * 256;
1275 se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 if (!se401->width)
1277 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -03001278 se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 if (!se401->height) {
1280 kfree(se401->width);
1281 return 1;
1282 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001283 for (i = 0; i < se401->sizes; i++) {
1284 se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256;
1285 se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001287 slen += snprintf(temp + slen, 200 - slen, " Sizes:");
1288 for (i = 0; i < se401->sizes; i++) {
1289 slen += snprintf(temp + slen, 200 - slen,
Alan Cox6f4d7232009-06-11 11:04:11 -03001290 " %dx%d", se401->width[i], se401->height[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 }
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001292 dev_info(&se401->dev->dev, "%s\n", temp);
Alan Coxaae40fd2009-06-09 10:02:11 -03001293 se401->maxframesize = se401->width[se401->sizes-1] *
1294 se401->height[se401->sizes - 1] * 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
Alan Coxaae40fd2009-06-09 10:02:11 -03001296 rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
1297 se401->cwidth = cp[0]+cp[1]*256;
1298 rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp));
1299 se401->cheight = cp[0]+cp[1]*256;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
Roel Kluin33b4af92008-04-22 14:46:02 -03001301 if (!(cp[2] & SE401_FORMAT_BAYER)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 err("Bayer format not supported!");
1303 return 1;
1304 }
1305 /* set output mode (BAYER) */
Alan Coxaae40fd2009-06-09 10:02:11 -03001306 se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE,
1307 SE401_FORMAT_BAYER, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Alan Coxaae40fd2009-06-09 10:02:11 -03001309 rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp));
1310 se401->brightness = cp[0]+cp[1]*256;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 /* some default values */
Alan Coxaae40fd2009-06-09 10:02:11 -03001312 se401->resetlevel = 0x2d;
1313 se401->rgain = 0x20;
1314 se401->ggain = 0x20;
1315 se401->bgain = 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 se401_set_exposure(se401, 20000);
Alan Coxaae40fd2009-06-09 10:02:11 -03001317 se401->palette = VIDEO_PALETTE_RGB24;
1318 se401->enhance = 1;
1319 se401->dropped = 0;
1320 se401->error = 0;
1321 se401->framecount = 0;
1322 se401->readcount = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 /* Start interrupt transfers for snapshot button */
1325 if (button) {
Alan Coxaae40fd2009-06-09 10:02:11 -03001326 se401->inturb = usb_alloc_urb(0, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 if (!se401->inturb) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001328 dev_info(&se401->dev->dev,
1329 "Allocation of inturb failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 return 1;
1331 }
1332 usb_fill_int_urb(se401->inturb, se401->dev,
1333 usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT),
1334 &se401->button, sizeof(se401->button),
1335 se401_button_irq,
1336 se401,
1337 8
1338 );
1339 if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001340 dev_info(&se401->dev->dev, "int urb burned down\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 return 1;
1342 }
1343 } else
Alan Coxaae40fd2009-06-09 10:02:11 -03001344 se401->inturb = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001346 /* Flash the led */
1347 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
1348 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
1349 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
1351
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001352 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353}
1354
1355static int se401_probe(struct usb_interface *intf,
1356 const struct usb_device_id *id)
1357{
1358 struct usb_device *dev = interface_to_usbdev(intf);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001359 struct usb_interface_descriptor *interface;
1360 struct usb_se401 *se401;
Alan Coxaae40fd2009-06-09 10:02:11 -03001361 char *camera_name = NULL;
1362 int button = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001364 /* We don't handle multi-config cameras */
1365 if (dev->descriptor.bNumConfigurations != 1)
1366 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001368 interface = &intf->cur_altsetting->desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001370 /* Is it an se401? */
Alan Coxaae40fd2009-06-09 10:02:11 -03001371 if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 &&
1372 le16_to_cpu(dev->descriptor.idProduct) == 0x0004) {
1373 camera_name = "Endpoints/Aox SE401";
1374 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 &&
1375 le16_to_cpu(dev->descriptor.idProduct) == 0x030b) {
1376 camera_name = "Philips PCVC665K";
1377 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1378 le16_to_cpu(dev->descriptor.idProduct) == 0x5001) {
1379 camera_name = "Kensington VideoCAM 67014";
1380 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1381 le16_to_cpu(dev->descriptor.idProduct) == 0x5002) {
1382 camera_name = "Kensington VideoCAM 6701(5/7)";
1383 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1384 le16_to_cpu(dev->descriptor.idProduct) == 0x5003) {
1385 camera_name = "Kensington VideoCAM 67016";
1386 button = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 } else
1388 return -ENODEV;
1389
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001390 /* Checking vendor/product should be enough, but what the hell */
1391 if (interface->bInterfaceClass != 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001393 if (interface->bInterfaceSubClass != 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 return -ENODEV;
1395
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001396 /* We found one */
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001397 dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Alan Coxaae40fd2009-06-09 10:02:11 -03001399 se401 = kzalloc(sizeof(*se401), GFP_KERNEL);
1400 if (se401 == NULL) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001401 err("couldn't kmalloc se401 struct");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001405 se401->dev = dev;
1406 se401->iface = interface->bInterfaceNumber;
1407 se401->camera_name = camera_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001409 dev_info(&intf->dev, "firmware version: %02x\n",
1410 le16_to_cpu(dev->descriptor.bcdDevice) & 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001412 if (se401_init(se401, button)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 kfree(se401);
1414 return -EIO;
1415 }
1416
1417 memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
Alan Coxaae40fd2009-06-09 10:02:11 -03001418 memcpy(se401->vdev.name, se401->camera_name,
1419 strlen(se401->camera_name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 init_waitqueue_head(&se401->wq);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001421 mutex_init(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 wmb();
1423
Alan Coxaae40fd2009-06-09 10:02:11 -03001424 if (video_register_device(&se401->vdev,
1425 VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 kfree(se401);
1427 err("video_register_device failed");
1428 return -EIO;
1429 }
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001430 dev_info(&intf->dev, "registered new video device: video%d\n",
Hans Verkuilc6330fb2008-10-19 18:54:26 -03001431 se401->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
Alan Coxaae40fd2009-06-09 10:02:11 -03001433 usb_set_intfdata(intf, se401);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001434 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435}
1436
1437static void se401_disconnect(struct usb_interface *intf)
1438{
Alan Coxaae40fd2009-06-09 10:02:11 -03001439 struct usb_se401 *se401 = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
Alan Coxaae40fd2009-06-09 10:02:11 -03001441 usb_set_intfdata(intf, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 if (se401) {
1443 video_unregister_device(&se401->vdev);
Alan Coxaae40fd2009-06-09 10:02:11 -03001444 if (!se401->user)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 usb_se401_remove_disconnected(se401);
Alan Coxaae40fd2009-06-09 10:02:11 -03001446 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 se401->frame[0].grabstate = FRAME_ERROR;
1448 se401->frame[0].grabstate = FRAME_ERROR;
1449
1450 se401->streaming = 0;
1451
1452 wake_up_interruptible(&se401->wq);
1453 se401->removed = 1;
1454 }
1455 }
1456}
1457
1458static struct usb_driver se401_driver = {
Alan Coxaae40fd2009-06-09 10:02:11 -03001459 .name = "se401",
1460 .id_table = device_table,
1461 .probe = se401_probe,
1462 .disconnect = se401_disconnect,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463};
1464
1465
1466
1467/****************************************************************************
1468 *
1469 * Module routines
1470 *
1471 ***************************************************************************/
1472
1473static int __init usb_se401_init(void)
1474{
Alan Coxaae40fd2009-06-09 10:02:11 -03001475 printk(KERN_INFO "SE401 usb camera driver version %s registering\n",
1476 version);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 if (flickerless)
Alan Coxaae40fd2009-06-09 10:02:11 -03001478 if (flickerless != 50 && flickerless != 60) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001479 printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 return -1;
1481 }
1482 return usb_register(&se401_driver);
1483}
1484
1485static void __exit usb_se401_exit(void)
1486{
1487 usb_deregister(&se401_driver);
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001488 printk(KERN_INFO "SE401 driver deregistered\frame");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489}
1490
1491module_init(usb_se401_init);
1492module_exit(usb_se401_exit);