blob: 85ffc2cba039b3e6114e5fc8d3bd4671bf9fea20 [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>
Alexey Dobriyan405f5572009-07-11 22:08:37 +040034#include <linux/smp_lock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/pagemap.h>
36#include <linux/usb.h>
37#include "se401.h"
38
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030039static int flickerless;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040static int video_nr = -1;
41
Alan Coxaae40fd2009-06-09 10:02:11 -030042static struct usb_device_id device_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */
44 { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */
45 { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */
46 { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */
47 { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */
48 { }
49};
50
51MODULE_DEVICE_TABLE(usb, device_table);
52
53MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
54MODULE_DESCRIPTION("SE401 USB Camera Driver");
55MODULE_LICENSE("GPL");
56module_param(flickerless, int, 0);
Alan Coxaae40fd2009-06-09 10:02:11 -030057MODULE_PARM_DESC(flickerless,
58 "Net frequency to adjust exposure time to (0/50/60)");
Linus Torvalds1da177e2005-04-16 15:20:36 -070059module_param(video_nr, int, 0);
60
61static struct usb_driver se401_driver;
62
63
64/**********************************************************************
65 *
66 * Memory management
67 *
68 **********************************************************************/
69static void *rvmalloc(unsigned long size)
70{
71 void *mem;
72 unsigned long adr;
73
74 size = PAGE_ALIGN(size);
75 mem = vmalloc_32(size);
76 if (!mem)
77 return NULL;
78
79 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
80 adr = (unsigned long) mem;
81 while (size > 0) {
82 SetPageReserved(vmalloc_to_page((void *)adr));
Alan Coxaae40fd2009-06-09 10:02:11 -030083 adr += PAGE_SIZE;
84 size -= PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 }
86
87 return mem;
88}
89
90static void rvfree(void *mem, unsigned long size)
91{
92 unsigned long adr;
93
94 if (!mem)
95 return;
96
97 adr = (unsigned long) mem;
98 while ((long) size > 0) {
99 ClearPageReserved(vmalloc_to_page((void *)adr));
Alan Coxaae40fd2009-06-09 10:02:11 -0300100 adr += PAGE_SIZE;
101 size -= PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 }
103 vfree(mem);
104}
105
106
107
108/****************************************************************************
109 *
110 * se401 register read/write functions
111 *
112 ***************************************************************************/
113
114static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req,
115 unsigned short value, unsigned char *cp, int size)
116{
Alan Coxaae40fd2009-06-09 10:02:11 -0300117 return usb_control_msg(
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300118 se401->dev,
119 set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0),
120 req,
121 (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
122 value,
123 0,
124 cp,
125 size,
126 1000
127 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128}
129
130static int se401_set_feature(struct usb_se401 *se401, unsigned short selector,
131 unsigned short param)
132{
133 /* specs say that the selector (address) should go in the value field
134 and the param in index, but in the logs of the windows driver they do
135 this the other way around...
136 */
Alan Coxaae40fd2009-06-09 10:02:11 -0300137 return usb_control_msg(
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 se401->dev,
139 usb_sndctrlpipe(se401->dev, 0),
140 SE401_REQ_SET_EXT_FEATURE,
141 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
142 param,
143 selector,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300144 NULL,
145 0,
146 1000
147 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300150static unsigned short se401_get_feature(struct usb_se401 *se401,
151 unsigned short selector)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152{
153 /* For 'set' the selecetor should be in index, not sure if the spec is
154 wrong here to....
155 */
156 unsigned char cp[2];
Alan Coxaae40fd2009-06-09 10:02:11 -0300157 usb_control_msg(
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300158 se401->dev,
159 usb_rcvctrlpipe(se401->dev, 0),
160 SE401_REQ_GET_EXT_FEATURE,
161 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
162 0,
163 selector,
164 cp,
165 2,
166 1000
167 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 return cp[0]+cp[1]*256;
169}
170
171/****************************************************************************
172 *
173 * Camera control
174 *
175 ***************************************************************************/
176
177
178static int se401_send_pict(struct usb_se401 *se401)
179{
Alan Coxaae40fd2009-06-09 10:02:11 -0300180 /* integration time low */
181 se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);
182 /* integration time mid */
183 se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);
184 /* integration time mid */
185 se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);
186 /* reset level value */
187 se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
188 /* red color gain */
189 se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);
190 /* green color gain */
191 se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);
192 /* blue color gain */
193 se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 return 0;
196}
197
198static void se401_set_exposure(struct usb_se401 *se401, int brightness)
199{
Alan Coxaae40fd2009-06-09 10:02:11 -0300200 int integration = brightness << 5;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300201
Alan Coxaae40fd2009-06-09 10:02:11 -0300202 if (flickerless == 50)
203 integration = integration-integration % 106667;
204 if (flickerless == 60)
205 integration = integration-integration % 88889;
206 se401->brightness = integration >> 5;
207 se401->expose_h = (integration >> 16) & 0xff;
208 se401->expose_m = (integration >> 8) & 0xff;
209 se401->expose_l = integration & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210}
211
212static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p)
213{
Alan Coxaae40fd2009-06-09 10:02:11 -0300214 p->brightness = se401->brightness;
215 if (se401->enhance)
216 p->whiteness = 32768;
217 else
218 p->whiteness = 0;
219
220 p->colour = 65535;
221 p->contrast = 65535;
222 p->hue = se401->rgain << 10;
223 p->palette = se401->palette;
224 p->depth = 3; /* rgb24 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 return 0;
226}
227
228
229static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p)
230{
231 if (p->palette != VIDEO_PALETTE_RGB24)
232 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -0300233 se401->palette = p->palette;
234 if (p->hue != se401->hue) {
235 se401->rgain = p->hue >> 10;
236 se401->bgain = 0x40-(p->hue >> 10);
237 se401->hue = p->hue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300239 if (p->brightness != se401->brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 se401_set_exposure(se401, p->brightness);
Alan Coxaae40fd2009-06-09 10:02:11 -0300241
242 if (p->whiteness >= 32768)
243 se401->enhance = 1;
244 else
245 se401->enhance = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 se401_send_pict(se401);
247 se401_send_pict(se401);
248 return 0;
249}
250
251/*
252 Hyundai have some really nice docs about this and other sensor related
253 stuff on their homepage: www.hei.co.kr
254*/
255static void se401_auto_resetlevel(struct usb_se401 *se401)
256{
257 unsigned int ahrc, alrc;
Alan Coxaae40fd2009-06-09 10:02:11 -0300258 int oldreset = se401->resetlevel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 /* For some reason this normally read-only register doesn't get reset
261 to zero after reading them just once...
262 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300263 se401_get_feature(se401, HV7131_REG_HIREFNOH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 se401_get_feature(se401, HV7131_REG_HIREFNOL);
265 se401_get_feature(se401, HV7131_REG_LOREFNOH);
266 se401_get_feature(se401, HV7131_REG_LOREFNOL);
Alan Coxaae40fd2009-06-09 10:02:11 -0300267 ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 se401_get_feature(se401, HV7131_REG_HIREFNOL);
Alan Coxaae40fd2009-06-09 10:02:11 -0300269 alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 se401_get_feature(se401, HV7131_REG_LOREFNOL);
271
272 /* Not an exact science, but it seems to work pretty well... */
273 if (alrc > 10) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300274 while (alrc >= 10 && se401->resetlevel < 63) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 se401->resetlevel++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300276 alrc /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 }
278 } else if (ahrc > 20) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300279 while (ahrc >= 20 && se401->resetlevel > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 se401->resetlevel--;
Alan Coxaae40fd2009-06-09 10:02:11 -0300281 ahrc /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 }
283 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300284 if (se401->resetlevel != oldreset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
286
287 return;
288}
289
290/* irq handler for snapshot button */
David Howells7d12e782006-10-05 14:55:46 +0100291static void se401_button_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293 struct usb_se401 *se401 = urb->context;
294 int status;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 if (!se401->dev) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300297 dev_info(&urb->dev->dev, "device vapourished\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 return;
299 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 switch (urb->status) {
302 case 0:
303 /* success */
304 break;
305 case -ECONNRESET:
306 case -ENOENT:
307 case -ESHUTDOWN:
308 /* this urb is terminated, clean up */
Alan Coxaae40fd2009-06-09 10:02:11 -0300309 dbg("%s - urb shutting down with status: %d",
310 __func__, urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 return;
312 default:
Alan Coxaae40fd2009-06-09 10:02:11 -0300313 dbg("%s - nonzero urb status received: %d",
314 __func__, urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 goto exit;
316 }
317
Alan Coxaae40fd2009-06-09 10:02:11 -0300318 if (urb->actual_length >= 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 if (se401->button)
Alan Coxaae40fd2009-06-09 10:02:11 -0300320 se401->buttonpressed = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321exit:
Alan Coxaae40fd2009-06-09 10:02:11 -0300322 status = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 if (status)
Alan Coxaae40fd2009-06-09 10:02:11 -0300324 err("%s - usb_submit_urb failed with result %d",
Harvey Harrison7e28adb2008-04-08 23:20:00 -0300325 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326}
327
David Howells7d12e782006-10-05 14:55:46 +0100328static void se401_video_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 struct usb_se401 *se401 = urb->context;
331 int length = urb->actual_length;
332
333 /* ohoh... */
334 if (!se401->streaming)
335 return;
336
337 if (!se401->dev) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300338 dev_info(&urb->dev->dev, "device vapourished\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 return;
340 }
341
342 /* 0 sized packets happen if we are to fast, but sometimes the camera
343 keeps sending them forever...
344 */
345 if (length && !urb->status) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300346 se401->nullpackets = 0;
347 switch (se401->scratch[se401->scratch_next].state) {
348 case BUFFER_READY:
349 case BUFFER_BUSY:
350 se401->dropped++;
351 break;
352 case BUFFER_UNUSED:
353 memcpy(se401->scratch[se401->scratch_next].data,
354 (unsigned char *)urb->transfer_buffer, length);
355 se401->scratch[se401->scratch_next].state
356 = BUFFER_READY;
357 se401->scratch[se401->scratch_next].offset
358 = se401->bayeroffset;
359 se401->scratch[se401->scratch_next].length = length;
360 if (waitqueue_active(&se401->wq))
361 wake_up_interruptible(&se401->wq);
362 se401->scratch_overflow = 0;
363 se401->scratch_next++;
364 if (se401->scratch_next >= SE401_NUMSCRATCH)
365 se401->scratch_next = 0;
366 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300368 se401->bayeroffset += length;
369 if (se401->bayeroffset >= se401->cheight * se401->cwidth)
370 se401->bayeroffset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 } else {
372 se401->nullpackets++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300373 if (se401->nullpackets > SE401_MAX_NULLPACKETS)
374 if (waitqueue_active(&se401->wq))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 wake_up_interruptible(&se401->wq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
377
378 /* Resubmit urb for new data */
Alan Coxaae40fd2009-06-09 10:02:11 -0300379 urb->status = 0;
380 urb->dev = se401->dev;
381 if (usb_submit_urb(urb, GFP_KERNEL))
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300382 dev_info(&urb->dev->dev, "urb burned down\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 return;
384}
385
386static void se401_send_size(struct usb_se401 *se401, int width, int height)
387{
Alan Coxaae40fd2009-06-09 10:02:11 -0300388 int i = 0;
389 int mode = 0x03; /* No compression */
390 int sendheight = height;
391 int sendwidth = width;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
393 /* JangGu compression can only be used with the camera supported sizes,
394 but bayer seems to work with any size that fits on the sensor.
395 We check if we can use compression with the current size with either
396 4 or 16 times subcapturing, if not we use uncompressed bayer data
397 but this will result in cutouts of the maximum size....
398 */
Alan Coxaae40fd2009-06-09 10:02:11 -0300399 while (i < se401->sizes && !(se401->width[i] == width &&
400 se401->height[i] == height))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 i++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300402 while (i < se401->sizes) {
403 if (se401->width[i] == width * 2 &&
404 se401->height[i] == height * 2) {
405 sendheight = se401->height[i];
406 sendwidth = se401->width[i];
407 mode = 0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300409 if (se401->width[i] == width * 4 &&
410 se401->height[i] == height * 4) {
411 sendheight = se401->height[i];
412 sendwidth = se401->width[i];
413 mode = 0x42;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 }
415 i++;
416 }
417
418 se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0);
419 se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0);
420 se401_set_feature(se401, SE401_OPERATINGMODE, mode);
421
Alan Coxaae40fd2009-06-09 10:02:11 -0300422 if (mode == 0x03)
423 se401->format = FMT_BAYER;
424 else
425 se401->format = FMT_JANGGU;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
428/*
429 In this function se401_send_pict is called several times,
430 for some reason (depending on the state of the sensor and the phase of
431 the moon :) doing this only in either place doesn't always work...
432*/
433static int se401_start_stream(struct usb_se401 *se401)
434{
435 struct urb *urb;
Alan Coxaae40fd2009-06-09 10:02:11 -0300436 int err = 0, i;
437 se401->streaming = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300439 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
440 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 /* Set picture settings */
Alan Coxaae40fd2009-06-09 10:02:11 -0300443 /* windowed + pix intg */
444 se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 se401_send_pict(se401);
446
447 se401_send_size(se401, se401->cwidth, se401->cheight);
448
Alan Coxaae40fd2009-06-09 10:02:11 -0300449 se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE,
450 0, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
452 /* Do some memory allocation */
Alan Coxaae40fd2009-06-09 10:02:11 -0300453 for (i = 0; i < SE401_NUMFRAMES; i++) {
454 se401->frame[i].data = se401->fbuf + i * se401->maxframesize;
455 se401->frame[i].curpix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300457 for (i = 0; i < SE401_NUMSBUF; i++) {
458 se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
Amit Choudharyfd51c692007-03-30 17:48:59 -0300459 if (!se401->sbuf[i].data) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300460 for (i = i - 1; i >= 0; i--) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300461 kfree(se401->sbuf[i].data);
462 se401->sbuf[i].data = NULL;
463 }
464 return -ENOMEM;
465 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 }
467
Alan Coxaae40fd2009-06-09 10:02:11 -0300468 se401->bayeroffset = 0;
469 se401->scratch_next = 0;
470 se401->scratch_use = 0;
471 se401->scratch_overflow = 0;
472 for (i = 0; i < SE401_NUMSCRATCH; i++) {
473 se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
Amit Choudharyfd51c692007-03-30 17:48:59 -0300474 if (!se401->scratch[i].data) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300475 for (i = i - 1; i >= 0; i--) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300476 kfree(se401->scratch[i].data);
477 se401->scratch[i].data = NULL;
478 }
479 goto nomem_sbuf;
480 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300481 se401->scratch[i].state = BUFFER_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 }
483
Alan Coxaae40fd2009-06-09 10:02:11 -0300484 for (i = 0; i < SE401_NUMSBUF; i++) {
485 urb = usb_alloc_urb(0, GFP_KERNEL);
486 if (!urb) {
487 for (i = i - 1; i >= 0; i--) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300488 usb_kill_urb(se401->urb[i]);
489 usb_free_urb(se401->urb[i]);
490 se401->urb[i] = NULL;
491 }
492 goto nomem_scratch;
493 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
495 usb_fill_bulk_urb(urb, se401->dev,
496 usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
497 se401->sbuf[i].data, SE401_PACKETSIZE,
498 se401_video_irq,
499 se401);
500
Alan Coxaae40fd2009-06-09 10:02:11 -0300501 se401->urb[i] = urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Alan Coxaae40fd2009-06-09 10:02:11 -0300503 err = usb_submit_urb(se401->urb[i], GFP_KERNEL);
504 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 err("urb burned down");
506 }
507
Alan Coxaae40fd2009-06-09 10:02:11 -0300508 se401->framecount = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 return 0;
Amit Choudharyfd51c692007-03-30 17:48:59 -0300511
512 nomem_scratch:
Alan Coxaae40fd2009-06-09 10:02:11 -0300513 for (i = 0; i < SE401_NUMSCRATCH; i++) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300514 kfree(se401->scratch[i].data);
515 se401->scratch[i].data = NULL;
516 }
517 nomem_sbuf:
Alan Coxaae40fd2009-06-09 10:02:11 -0300518 for (i = 0; i < SE401_NUMSBUF; i++) {
Amit Choudharyfd51c692007-03-30 17:48:59 -0300519 kfree(se401->sbuf[i].data);
520 se401->sbuf[i].data = NULL;
521 }
522 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523}
524
525static int se401_stop_stream(struct usb_se401 *se401)
526{
527 int i;
528
529 if (!se401->streaming || !se401->dev)
530 return 1;
531
Alan Coxaae40fd2009-06-09 10:02:11 -0300532 se401->streaming = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534 se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0);
535
536 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
537 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
538
Alan Coxaae40fd2009-06-09 10:02:11 -0300539 for (i = 0; i < SE401_NUMSBUF; i++)
540 if (se401->urb[i]) {
541 usb_kill_urb(se401->urb[i]);
542 usb_free_urb(se401->urb[i]);
543 se401->urb[i] = NULL;
544 kfree(se401->sbuf[i].data);
545 }
546 for (i = 0; i < SE401_NUMSCRATCH; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 kfree(se401->scratch[i].data);
Alan Coxaae40fd2009-06-09 10:02:11 -0300548 se401->scratch[i].data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
550
551 return 0;
552}
553
554static int se401_set_size(struct usb_se401 *se401, int width, int height)
555{
Alan Coxaae40fd2009-06-09 10:02:11 -0300556 int wasstreaming = se401->streaming;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 /* Check to see if we need to change */
Alan Coxaae40fd2009-06-09 10:02:11 -0300558 if (se401->cwidth == width && se401->cheight == height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 return 0;
560
561 /* Check for a valid mode */
562 if (!width || !height)
563 return 1;
564 if ((width & 1) || (height & 1))
565 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -0300566 if (width > se401->width[se401->sizes-1])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -0300568 if (height > se401->height[se401->sizes-1])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 return 1;
570
571 /* Stop a current stream and start it again at the new size */
572 if (wasstreaming)
573 se401_stop_stream(se401);
Alan Coxaae40fd2009-06-09 10:02:11 -0300574 se401->cwidth = width;
575 se401->cheight = height;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 if (wasstreaming)
577 se401_start_stream(se401);
578 return 0;
579}
580
581
582/****************************************************************************
583 *
584 * Video Decoding
585 *
586 ***************************************************************************/
587
588/*
589 This shouldn't really be done in a v4l driver....
590 But it does make the image look a lot more usable.
591 Basically it lifts the dark pixels more than the light pixels.
592*/
593static inline void enhance_picture(unsigned char *frame, int len)
594{
595 while (len--) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300596 *frame = (((*frame^255)*(*frame^255))/255)^255;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 frame++;
598 }
599}
600
601static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data)
602{
Alan Coxaae40fd2009-06-09 10:02:11 -0300603 struct se401_frame *frame = &se401->frame[se401->curframe];
604 int linelength = se401->cwidth * 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
606 if (frame->curlinepix >= linelength) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300607 frame->curlinepix = 0;
608 frame->curline += linelength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 }
610
611 /* First three are absolute, all others relative.
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300612 * Format is rgb from right to left (mirrorred image),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 * we flip it to get bgr from left to right. */
Alan Coxaae40fd2009-06-09 10:02:11 -0300614 if (frame->curlinepix < 3)
615 *(frame->curline-frame->curlinepix) = 1 + data * 4;
616 else
617 *(frame->curline-frame->curlinepix) =
618 *(frame->curline-frame->curlinepix + 3) + data * 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 frame->curlinepix++;
620}
621
Alan Coxaae40fd2009-06-09 10:02:11 -0300622static inline void decode_JangGu_vlc(struct usb_se401 *se401,
623 unsigned char *data, int bit_exp, int packetlength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
Alan Coxaae40fd2009-06-09 10:02:11 -0300625 int pos = 0;
626 int vlc_cod = 0;
627 int vlc_size = 0;
628 int vlc_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 int bit_cur;
630 int bit;
Alan Coxaae40fd2009-06-09 10:02:11 -0300631 data += 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 while (pos < packetlength) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300633 bit_cur = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 while (bit_cur && bit_exp) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300635 bit = ((*data) >> (bit_cur-1))&1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 if (!vlc_cod) {
637 if (bit) {
638 vlc_size++;
639 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300640 if (!vlc_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 decode_JangGu_integrate(se401, 0);
Alan Coxaae40fd2009-06-09 10:02:11 -0300642 else {
643 vlc_cod = 2;
644 vlc_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 }
646 }
647 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300648 if (vlc_cod == 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 if (!bit)
Alan Coxaae40fd2009-06-09 10:02:11 -0300650 vlc_data = -(1 << vlc_size) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 vlc_cod--;
652 }
653 vlc_size--;
Alan Coxaae40fd2009-06-09 10:02:11 -0300654 vlc_data += bit << vlc_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 if (!vlc_size) {
656 decode_JangGu_integrate(se401, vlc_data);
Alan Coxaae40fd2009-06-09 10:02:11 -0300657 vlc_cod = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 }
659 }
660 bit_cur--;
661 bit_exp--;
662 }
663 pos++;
664 data++;
665 }
666}
667
Alan Coxaae40fd2009-06-09 10:02:11 -0300668static inline void decode_JangGu(struct usb_se401 *se401,
669 struct se401_scratch *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
Alan Coxaae40fd2009-06-09 10:02:11 -0300671 unsigned char *data = buffer->data;
672 int len = buffer->length;
673 int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size;
674 int datapos = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
676 /* New image? */
677 if (!se401->frame[se401->curframe].curpix) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300678 se401->frame[se401->curframe].curlinepix = 0;
679 se401->frame[se401->curframe].curline =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 se401->frame[se401->curframe].data+
Alan Coxaae40fd2009-06-09 10:02:11 -0300681 se401->cwidth * 3 - 1;
682 if (se401->frame[se401->curframe].grabstate == FRAME_READY)
683 se401->frame[se401->curframe].grabstate = FRAME_GRABBING;
684 se401->vlcdatapos = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 }
686 while (datapos < len) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300687 size = 1024 - se401->vlcdatapos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 if (size+datapos > len)
Alan Coxaae40fd2009-06-09 10:02:11 -0300689 size = len-datapos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size);
Alan Coxaae40fd2009-06-09 10:02:11 -0300691 se401->vlcdatapos += size;
692 packetlength = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 if (se401->vlcdatapos >= 4) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300694 bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8);
695 pix_exp = se401->vlcdata[1] +
696 ((se401->vlcdata[0] & 0x3f) << 8);
697 frameinfo = se401->vlcdata[0] & 0xc0;
698 packetlength = ((bit_exp + 47) >> 4) << 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 if (packetlength > 1024) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300700 se401->vlcdatapos = 0;
701 datapos = len;
702 packetlength = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 se401->error++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300704 se401->frame[se401->curframe].curpix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 }
706 }
707 if (packetlength && se401->vlcdatapos >= packetlength) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300708 decode_JangGu_vlc(se401, se401->vlcdata, bit_exp,
709 packetlength);
710 se401->frame[se401->curframe].curpix += pix_exp * 3;
711 datapos += size-(se401->vlcdatapos-packetlength);
712 se401->vlcdatapos = 0;
713 if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) {
714 if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) {
715 if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) {
716 se401->frame[se401->curframe].grabstate = FRAME_DONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 se401->framecount++;
718 se401->readcount++;
719 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300720 if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY)
721 se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1);
722 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 se401->error++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300724 se401->frame[se401->curframe].curpix = 0;
725 datapos = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300727 } else
728 datapos += size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 }
730}
731
Alan Coxaae40fd2009-06-09 10:02:11 -0300732static inline void decode_bayer(struct usb_se401 *se401,
733 struct se401_scratch *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734{
Alan Coxaae40fd2009-06-09 10:02:11 -0300735 unsigned char *data = buffer->data;
736 int len = buffer->length;
737 int offset = buffer->offset;
738 int datasize = se401->cwidth * se401->cheight;
739 struct se401_frame *frame = &se401->frame[se401->curframe];
740 unsigned char *framedata = frame->data, *curline, *nextline;
741 int width = se401->cwidth;
742 int blineoffset = 0, bline;
743 int linelength = width * 3, i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Alan Coxaae40fd2009-06-09 10:02:11 -0300746 if (frame->curpix == 0) {
747 if (frame->grabstate == FRAME_READY)
748 frame->grabstate = FRAME_GRABBING;
749
750 frame->curline = framedata + linelength;
751 frame->curlinepix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 }
753
Alan Coxaae40fd2009-06-09 10:02:11 -0300754 if (offset != frame->curpix) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 /* Regard frame as lost :( */
Alan Coxaae40fd2009-06-09 10:02:11 -0300756 frame->curpix = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 se401->error++;
758 return;
759 }
760
761 /* Check if we have to much data */
Alan Coxaae40fd2009-06-09 10:02:11 -0300762 if (frame->curpix + len > datasize)
763 len = datasize-frame->curpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Alan Coxaae40fd2009-06-09 10:02:11 -0300765 if (se401->cheight % 4)
766 blineoffset = 1;
767 bline = frame->curpix / se401->cwidth+blineoffset;
768
769 curline = frame->curline;
770 nextline = curline + linelength;
771 if (nextline >= framedata+datasize * 3)
772 nextline = curline;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 while (len) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300774 if (frame->curlinepix >= width) {
775 frame->curlinepix -= width;
776 bline = frame->curpix / width + blineoffset;
777 curline += linelength*2;
778 nextline += linelength*2;
779 if (curline >= framedata+datasize * 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 frame->curlinepix++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300781 curline -= 3;
782 nextline -= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 len--;
784 data++;
785 frame->curpix++;
786 }
787 if (nextline >= framedata+datasize*3)
Alan Coxaae40fd2009-06-09 10:02:11 -0300788 nextline = curline;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300790 if (bline & 1) {
791 if (frame->curlinepix & 1) {
792 *(curline + 2) = *data;
793 *(curline - 1) = *data;
794 *(nextline + 2) = *data;
795 *(nextline - 1) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300797 *(curline + 1) =
798 (*(curline + 1) + *data) / 2;
799 *(curline-2) =
800 (*(curline - 2) + *data) / 2;
801 *(nextline + 1) = *data;
802 *(nextline - 2) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 }
804 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300805 if (frame->curlinepix & 1) {
806 *(curline + 1) =
807 (*(curline + 1) + *data) / 2;
808 *(curline - 2) =
809 (*(curline - 2) + *data) / 2;
810 *(nextline + 1) = *data;
811 *(nextline - 2) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300813 *curline = *data;
814 *(curline - 3) = *data;
815 *nextline = *data;
816 *(nextline - 3) = *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 }
818 }
819 frame->curlinepix++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300820 curline -= 3;
821 nextline -= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 len--;
823 data++;
824 frame->curpix++;
825 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300826 frame->curline = curline;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Alan Coxaae40fd2009-06-09 10:02:11 -0300828 if (frame->curpix >= datasize) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 /* Fix the top line */
Alan Coxaae40fd2009-06-09 10:02:11 -0300830 framedata += linelength;
831 for (i = 0; i < linelength; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 framedata--;
Alan Coxaae40fd2009-06-09 10:02:11 -0300833 *framedata = *(framedata + linelength);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 }
835 /* Fix the left side (green is already present) */
Alan Coxaae40fd2009-06-09 10:02:11 -0300836 for (i = 0; i < se401->cheight; i++) {
837 *framedata = *(framedata + 3);
838 *(framedata + 1) = *(framedata + 4);
839 *(framedata + 2) = *(framedata + 5);
840 framedata += linelength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300842 frame->curpix = 0;
843 frame->grabstate = FRAME_DONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 se401->framecount++;
845 se401->readcount++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300846 if (se401->frame[(se401->curframe + 1) &
847 (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) {
848 se401->curframe = (se401->curframe+1) &
849 (SE401_NUMFRAMES-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 }
851 }
852}
853
854static int se401_newframe(struct usb_se401 *se401, int framenr)
855{
856 DECLARE_WAITQUEUE(wait, current);
Alan Coxaae40fd2009-06-09 10:02:11 -0300857 int errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
859 while (se401->streaming &&
Alan Coxaae40fd2009-06-09 10:02:11 -0300860 (se401->frame[framenr].grabstate == FRAME_READY ||
861 se401->frame[framenr].grabstate == FRAME_GRABBING)) {
862 if (!se401->frame[framenr].curpix)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 errors++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300864
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 wait_interruptible(
Alan Coxaae40fd2009-06-09 10:02:11 -0300866 se401->scratch[se401->scratch_use].state != BUFFER_READY,
867 &se401->wq, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300869 se401->nullpackets = 0;
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300870 dev_info(&se401->dev->dev,
Alan Coxaae40fd2009-06-09 10:02:11 -0300871 "too many null length packets, restarting capture\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 se401_stop_stream(se401);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300873 se401_start_stream(se401);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300875 if (se401->scratch[se401->scratch_use].state !=
876 BUFFER_READY) {
877 se401->frame[framenr].grabstate = FRAME_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 return -EIO;
879 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300880 se401->scratch[se401->scratch_use].state = BUFFER_BUSY;
881 if (se401->format == FMT_JANGGU)
882 decode_JangGu(se401,
883 &se401->scratch[se401->scratch_use]);
884 else
885 decode_bayer(se401,
886 &se401->scratch[se401->scratch_use]);
887
888 se401->scratch[se401->scratch_use].state =
889 BUFFER_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 se401->scratch_use++;
Alan Coxaae40fd2009-06-09 10:02:11 -0300891 if (se401->scratch_use >= SE401_NUMSCRATCH)
892 se401->scratch_use = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 if (errors > SE401_MAX_ERRORS) {
Alan Coxaae40fd2009-06-09 10:02:11 -0300894 errors = 0;
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300895 dev_info(&se401->dev->dev,
Alan Coxaae40fd2009-06-09 10:02:11 -0300896 "too many errors, restarting capture\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 se401_stop_stream(se401);
898 se401_start_stream(se401);
899 }
900 }
901 }
902
Alan Coxaae40fd2009-06-09 10:02:11 -0300903 if (se401->frame[framenr].grabstate == FRAME_DONE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 if (se401->enhance)
Alan Coxaae40fd2009-06-09 10:02:11 -0300905 enhance_picture(se401->frame[framenr].data,
906 se401->cheight * se401->cwidth * 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return 0;
908}
909
Alan Coxaae40fd2009-06-09 10:02:11 -0300910static void usb_se401_remove_disconnected(struct usb_se401 *se401)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911{
912 int i;
913
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300914 se401->dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Alan Coxaae40fd2009-06-09 10:02:11 -0300916 for (i = 0; i < SE401_NUMSBUF; i++)
Jesper Juhl1bc3c9e2005-04-18 17:39:34 -0700917 if (se401->urb[i]) {
918 usb_kill_urb(se401->urb[i]);
919 usb_free_urb(se401->urb[i]);
920 se401->urb[i] = NULL;
921 kfree(se401->sbuf[i].data);
922 }
Alan Coxaae40fd2009-06-09 10:02:11 -0300923
924 for (i = 0; i < SE401_NUMSCRATCH; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 kfree(se401->scratch[i].data);
Alan Coxaae40fd2009-06-09 10:02:11 -0300926
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 if (se401->inturb) {
928 usb_kill_urb(se401->inturb);
929 usb_free_urb(se401->inturb);
930 }
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300931 dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300933 /* Free the memory */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 kfree(se401->width);
935 kfree(se401->height);
936 kfree(se401);
937}
938
939
940
941/****************************************************************************
942 *
943 * Video4Linux
944 *
945 ***************************************************************************/
946
947
Hans Verkuilbec43662008-12-30 06:58:20 -0300948static int se401_open(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949{
950 struct video_device *dev = video_devdata(file);
951 struct usb_se401 *se401 = (struct usb_se401 *)dev;
952 int err = 0;
953
Hans Verkuild56dc612008-07-30 08:43:36 -0300954 lock_kernel();
955 if (se401->user) {
956 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 return -EBUSY;
Hans Verkuild56dc612008-07-30 08:43:36 -0300958 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
960 if (se401->fbuf)
961 file->private_data = dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300962 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 err = -ENOMEM;
964 se401->user = !err;
Hans Verkuild56dc612008-07-30 08:43:36 -0300965 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
967 return err;
968}
969
Hans Verkuilbec43662008-12-30 06:58:20 -0300970static int se401_close(struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971{
972 struct video_device *dev = file->private_data;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300973 struct usb_se401 *se401 = (struct usb_se401 *)dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 int i;
975
976 rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300977 if (se401->removed) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -0300978 dev_info(&se401->dev->dev, "device unregistered\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 usb_se401_remove_disconnected(se401);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 } else {
Alan Coxaae40fd2009-06-09 10:02:11 -0300981 for (i = 0; i < SE401_NUMFRAMES; i++)
982 se401->frame[i].grabstate = FRAME_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 if (se401->streaming)
984 se401_stop_stream(se401);
Alan Coxaae40fd2009-06-09 10:02:11 -0300985 se401->user = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 }
987 file->private_data = NULL;
988 return 0;
989}
990
Hans Verkuil069b7472008-12-30 07:04:34 -0300991static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
993 struct video_device *vdev = file->private_data;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300994 struct usb_se401 *se401 = (struct usb_se401 *)vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300996 if (!se401->dev)
997 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300999 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 case VIDIOCGCAP:
1001 {
1002 struct video_capability *b = arg;
1003 strcpy(b->name, se401->camera_name);
1004 b->type = VID_TYPE_CAPTURE;
1005 b->channels = 1;
1006 b->audios = 0;
1007 b->maxwidth = se401->width[se401->sizes-1];
1008 b->maxheight = se401->height[se401->sizes-1];
1009 b->minwidth = se401->width[0];
1010 b->minheight = se401->height[0];
1011 return 0;
1012 }
1013 case VIDIOCGCHAN:
1014 {
1015 struct video_channel *v = arg;
1016
1017 if (v->channel != 0)
1018 return -EINVAL;
1019 v->flags = 0;
1020 v->tuners = 0;
1021 v->type = VIDEO_TYPE_CAMERA;
1022 strcpy(v->name, "Camera");
1023 return 0;
1024 }
1025 case VIDIOCSCHAN:
1026 {
1027 struct video_channel *v = arg;
1028
1029 if (v->channel != 0)
1030 return -EINVAL;
1031 return 0;
1032 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001033 case VIDIOCGPICT:
1034 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 struct video_picture *p = arg;
1036
1037 se401_get_pict(se401, p);
1038 return 0;
1039 }
1040 case VIDIOCSPICT:
1041 {
1042 struct video_picture *p = arg;
1043
1044 if (se401_set_pict(se401, p))
1045 return -EINVAL;
1046 return 0;
1047 }
1048 case VIDIOCSWIN:
1049 {
1050 struct video_window *vw = arg;
1051
1052 if (vw->flags)
1053 return -EINVAL;
1054 if (vw->clipcount)
1055 return -EINVAL;
1056 if (se401_set_size(se401, vw->width, vw->height))
1057 return -EINVAL;
1058 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001059 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 case VIDIOCGWIN:
1061 {
1062 struct video_window *vw = arg;
1063
1064 vw->x = 0; /* FIXME */
1065 vw->y = 0;
1066 vw->chromakey = 0;
1067 vw->flags = 0;
1068 vw->clipcount = 0;
1069 vw->width = se401->cwidth;
1070 vw->height = se401->cheight;
1071 return 0;
1072 }
1073 case VIDIOCGMBUF:
1074 {
1075 struct video_mbuf *vm = arg;
1076 int i;
1077
1078 memset(vm, 0, sizeof(*vm));
1079 vm->size = SE401_NUMFRAMES * se401->maxframesize;
1080 vm->frames = SE401_NUMFRAMES;
Alan Coxaae40fd2009-06-09 10:02:11 -03001081 for (i = 0; i < SE401_NUMFRAMES; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 vm->offsets[i] = se401->maxframesize * i;
1083 return 0;
1084 }
1085 case VIDIOCMCAPTURE:
1086 {
1087 struct video_mmap *vm = arg;
1088
1089 if (vm->format != VIDEO_PALETTE_RGB24)
1090 return -EINVAL;
1091 if (vm->frame >= SE401_NUMFRAMES)
1092 return -EINVAL;
1093 if (se401->frame[vm->frame].grabstate != FRAME_UNUSED)
1094 return -EBUSY;
1095
1096 /* Is this according to the v4l spec??? */
1097 if (se401_set_size(se401, vm->width, vm->height))
1098 return -EINVAL;
Alan Coxaae40fd2009-06-09 10:02:11 -03001099 se401->frame[vm->frame].grabstate = FRAME_READY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
1101 if (!se401->streaming)
1102 se401_start_stream(se401);
1103
1104 /* Set the picture properties */
Alan Coxaae40fd2009-06-09 10:02:11 -03001105 if (se401->framecount == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 se401_send_pict(se401);
1107 /* Calibrate the reset level after a few frames. */
Alan Coxaae40fd2009-06-09 10:02:11 -03001108 if (se401->framecount % 20 == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 se401_auto_resetlevel(se401);
1110
1111 return 0;
1112 }
1113 case VIDIOCSYNC:
1114 {
1115 int *frame = arg;
Alan Coxaae40fd2009-06-09 10:02:11 -03001116 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
Alan Coxaae40fd2009-06-09 10:02:11 -03001118 if (*frame < 0 || *frame >= SE401_NUMFRAMES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 return -EINVAL;
1120
Alan Coxaae40fd2009-06-09 10:02:11 -03001121 ret = se401_newframe(se401, *frame);
1122 se401->frame[*frame].grabstate = FRAME_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 return ret;
1124 }
1125 case VIDIOCGFBUF:
1126 {
1127 struct video_buffer *vb = arg;
1128
1129 memset(vb, 0, sizeof(*vb));
1130 return 0;
1131 }
1132 case VIDIOCKEY:
1133 return 0;
1134 case VIDIOCCAPTURE:
1135 return -EINVAL;
1136 case VIDIOCSFBUF:
1137 return -EINVAL;
1138 case VIDIOCGTUNER:
1139 case VIDIOCSTUNER:
1140 return -EINVAL;
1141 case VIDIOCGFREQ:
1142 case VIDIOCSFREQ:
1143 return -EINVAL;
1144 case VIDIOCGAUDIO:
1145 case VIDIOCSAUDIO:
1146 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001147 default:
1148 return -ENOIOCTLCMD;
1149 } /* end switch */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001151 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152}
1153
Hans Verkuil069b7472008-12-30 07:04:34 -03001154static long se401_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 unsigned int cmd, unsigned long arg)
1156{
Hans Verkuilf473bf72008-11-01 08:25:11 -03001157 return video_usercopy(file, cmd, arg, se401_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158}
1159
1160static ssize_t se401_read(struct file *file, char __user *buf,
1161 size_t count, loff_t *ppos)
1162{
Alan Coxaae40fd2009-06-09 10:02:11 -03001163 int realcount = count, ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 struct video_device *dev = file->private_data;
1165 struct usb_se401 *se401 = (struct usb_se401 *)dev;
1166
1167
Alan Coxaae40fd2009-06-09 10:02:11 -03001168 if (se401->dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 return -EIO;
1170 if (realcount > se401->cwidth*se401->cheight*3)
Alan Coxaae40fd2009-06-09 10:02:11 -03001171 realcount = se401->cwidth*se401->cheight*3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
1173 /* Shouldn't happen: */
Alan Coxaae40fd2009-06-09 10:02:11 -03001174 if (se401->frame[0].grabstate == FRAME_GRABBING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 return -EBUSY;
Alan Coxaae40fd2009-06-09 10:02:11 -03001176 se401->frame[0].grabstate = FRAME_READY;
1177 se401->frame[1].grabstate = FRAME_UNUSED;
1178 se401->curframe = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
1180 if (!se401->streaming)
1181 se401_start_stream(se401);
1182
1183 /* Set the picture properties */
Alan Coxaae40fd2009-06-09 10:02:11 -03001184 if (se401->framecount == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 se401_send_pict(se401);
1186 /* Calibrate the reset level after a few frames. */
Alan Coxaae40fd2009-06-09 10:02:11 -03001187 if (se401->framecount%20 == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 se401_auto_resetlevel(se401);
1189
Alan Coxaae40fd2009-06-09 10:02:11 -03001190 ret = se401_newframe(se401, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
Alan Coxaae40fd2009-06-09 10:02:11 -03001192 se401->frame[0].grabstate = FRAME_UNUSED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 if (ret)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001194 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 if (copy_to_user(buf, se401->frame[0].data, realcount))
1196 return -EFAULT;
1197
1198 return realcount;
1199}
1200
1201static int se401_mmap(struct file *file, struct vm_area_struct *vma)
1202{
1203 struct video_device *dev = file->private_data;
1204 struct usb_se401 *se401 = (struct usb_se401 *)dev;
1205 unsigned long start = vma->vm_start;
1206 unsigned long size = vma->vm_end-vma->vm_start;
1207 unsigned long page, pos;
1208
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001209 mutex_lock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
Alan Coxaae40fd2009-06-09 10:02:11 -03001211 if (se401->dev == NULL) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001212 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 return -EIO;
1214 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001215 if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1)
1216 & ~(PAGE_SIZE - 1))) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001217 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 return -EINVAL;
1219 }
1220 pos = (unsigned long)se401->fbuf;
1221 while (size > 0) {
1222 page = vmalloc_to_pfn((void *)pos);
1223 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001224 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 return -EAGAIN;
1226 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001227 start += PAGE_SIZE;
1228 pos += PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 if (size > PAGE_SIZE)
Alan Coxaae40fd2009-06-09 10:02:11 -03001230 size -= PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 else
1232 size = 0;
1233 }
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001234 mutex_unlock(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001236 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237}
1238
Hans Verkuilbec43662008-12-30 06:58:20 -03001239static const struct v4l2_file_operations se401_fops = {
Alan Coxaae40fd2009-06-09 10:02:11 -03001240 .owner = THIS_MODULE,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001241 .open = se401_open,
1242 .release = se401_close,
1243 .read = se401_read,
1244 .mmap = se401_mmap,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 .ioctl = se401_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246};
1247static struct video_device se401_template = {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001248 .name = "se401 USB camera",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 .fops = &se401_fops,
Hans Verkuilaa5e90a2008-08-23 06:23:55 -03001250 .release = video_device_release_empty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251};
1252
1253
1254
1255/***************************/
1256static int se401_init(struct usb_se401 *se401, int button)
1257{
Alan Coxaae40fd2009-06-09 10:02:11 -03001258 int i = 0, rc;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001259 unsigned char cp[0x40];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 char temp[200];
Alan Cox6f4d7232009-06-11 11:04:11 -03001261 int slen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
1263 /* led on */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001264 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
1266 /* get camera descriptor */
Alan Coxaae40fd2009-06-09 10:02:11 -03001267 rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0,
1268 cp, sizeof(cp));
Alan Cox6f4d7232009-06-11 11:04:11 -03001269 if (cp[1] != 0x41) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 err("Wrong descriptor type");
1271 return 1;
1272 }
Alan Cox6f4d7232009-06-11 11:04:11 -03001273 slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Alan Coxaae40fd2009-06-09 10:02:11 -03001275 se401->sizes = cp[4] + cp[5] * 256;
1276 se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 if (!se401->width)
1278 return 1;
Alan Coxaae40fd2009-06-09 10:02:11 -03001279 se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 if (!se401->height) {
1281 kfree(se401->width);
1282 return 1;
1283 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001284 for (i = 0; i < se401->sizes; i++) {
1285 se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256;
1286 se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 }
Alan Coxaae40fd2009-06-09 10:02:11 -03001288 slen += snprintf(temp + slen, 200 - slen, " Sizes:");
1289 for (i = 0; i < se401->sizes; i++) {
1290 slen += snprintf(temp + slen, 200 - slen,
Alan Cox6f4d7232009-06-11 11:04:11 -03001291 " %dx%d", se401->width[i], se401->height[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 }
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001293 dev_info(&se401->dev->dev, "%s\n", temp);
Alan Coxaae40fd2009-06-09 10:02:11 -03001294 se401->maxframesize = se401->width[se401->sizes-1] *
1295 se401->height[se401->sizes - 1] * 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
Alan Coxaae40fd2009-06-09 10:02:11 -03001297 rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
1298 se401->cwidth = cp[0]+cp[1]*256;
1299 rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp));
1300 se401->cheight = cp[0]+cp[1]*256;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301
Roel Kluin33b4af92008-04-22 14:46:02 -03001302 if (!(cp[2] & SE401_FORMAT_BAYER)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 err("Bayer format not supported!");
1304 return 1;
1305 }
1306 /* set output mode (BAYER) */
Alan Coxaae40fd2009-06-09 10:02:11 -03001307 se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE,
1308 SE401_FORMAT_BAYER, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
Alan Coxaae40fd2009-06-09 10:02:11 -03001310 rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp));
1311 se401->brightness = cp[0]+cp[1]*256;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 /* some default values */
Alan Coxaae40fd2009-06-09 10:02:11 -03001313 se401->resetlevel = 0x2d;
1314 se401->rgain = 0x20;
1315 se401->ggain = 0x20;
1316 se401->bgain = 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 se401_set_exposure(se401, 20000);
Alan Coxaae40fd2009-06-09 10:02:11 -03001318 se401->palette = VIDEO_PALETTE_RGB24;
1319 se401->enhance = 1;
1320 se401->dropped = 0;
1321 se401->error = 0;
1322 se401->framecount = 0;
1323 se401->readcount = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
1325 /* Start interrupt transfers for snapshot button */
1326 if (button) {
Alan Coxaae40fd2009-06-09 10:02:11 -03001327 se401->inturb = usb_alloc_urb(0, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 if (!se401->inturb) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001329 dev_info(&se401->dev->dev,
1330 "Allocation of inturb failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 return 1;
1332 }
1333 usb_fill_int_urb(se401->inturb, se401->dev,
1334 usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT),
1335 &se401->button, sizeof(se401->button),
1336 se401_button_irq,
1337 se401,
1338 8
1339 );
1340 if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001341 dev_info(&se401->dev->dev, "int urb burned down\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 return 1;
1343 }
1344 } else
Alan Coxaae40fd2009-06-09 10:02:11 -03001345 se401->inturb = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001347 /* Flash the led */
1348 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
1349 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
1350 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
1352
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001353 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355
1356static int se401_probe(struct usb_interface *intf,
1357 const struct usb_device_id *id)
1358{
1359 struct usb_device *dev = interface_to_usbdev(intf);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001360 struct usb_interface_descriptor *interface;
1361 struct usb_se401 *se401;
Alan Coxaae40fd2009-06-09 10:02:11 -03001362 char *camera_name = NULL;
1363 int button = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001365 /* We don't handle multi-config cameras */
1366 if (dev->descriptor.bNumConfigurations != 1)
1367 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001369 interface = &intf->cur_altsetting->desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001371 /* Is it an se401? */
Alan Coxaae40fd2009-06-09 10:02:11 -03001372 if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 &&
1373 le16_to_cpu(dev->descriptor.idProduct) == 0x0004) {
1374 camera_name = "Endpoints/Aox SE401";
1375 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 &&
1376 le16_to_cpu(dev->descriptor.idProduct) == 0x030b) {
1377 camera_name = "Philips PCVC665K";
1378 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1379 le16_to_cpu(dev->descriptor.idProduct) == 0x5001) {
1380 camera_name = "Kensington VideoCAM 67014";
1381 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1382 le16_to_cpu(dev->descriptor.idProduct) == 0x5002) {
1383 camera_name = "Kensington VideoCAM 6701(5/7)";
1384 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1385 le16_to_cpu(dev->descriptor.idProduct) == 0x5003) {
1386 camera_name = "Kensington VideoCAM 67016";
1387 button = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 } else
1389 return -ENODEV;
1390
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001391 /* Checking vendor/product should be enough, but what the hell */
1392 if (interface->bInterfaceClass != 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return -ENODEV;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001394 if (interface->bInterfaceSubClass != 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 return -ENODEV;
1396
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001397 /* We found one */
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001398 dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
Alan Coxaae40fd2009-06-09 10:02:11 -03001400 se401 = kzalloc(sizeof(*se401), GFP_KERNEL);
1401 if (se401 == NULL) {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001402 err("couldn't kmalloc se401 struct");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001404 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001406 se401->dev = dev;
1407 se401->iface = interface->bInterfaceNumber;
1408 se401->camera_name = camera_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001410 dev_info(&intf->dev, "firmware version: %02x\n",
1411 le16_to_cpu(dev->descriptor.bcdDevice) & 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001413 if (se401_init(se401, button)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 kfree(se401);
1415 return -EIO;
1416 }
1417
1418 memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
Alan Coxaae40fd2009-06-09 10:02:11 -03001419 memcpy(se401->vdev.name, se401->camera_name,
1420 strlen(se401->camera_name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 init_waitqueue_head(&se401->wq);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001422 mutex_init(&se401->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 wmb();
1424
Alan Coxaae40fd2009-06-09 10:02:11 -03001425 if (video_register_device(&se401->vdev,
1426 VFL_TYPE_GRABBER, video_nr) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 kfree(se401);
1428 err("video_register_device failed");
1429 return -EIO;
1430 }
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001431 dev_info(&intf->dev, "registered new video device: video%d\n",
Hans Verkuilc6330fb2008-10-19 18:54:26 -03001432 se401->vdev.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
Alan Coxaae40fd2009-06-09 10:02:11 -03001434 usb_set_intfdata(intf, se401);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001435 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436}
1437
1438static void se401_disconnect(struct usb_interface *intf)
1439{
Alan Coxaae40fd2009-06-09 10:02:11 -03001440 struct usb_se401 *se401 = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441
Alan Coxaae40fd2009-06-09 10:02:11 -03001442 usb_set_intfdata(intf, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 if (se401) {
1444 video_unregister_device(&se401->vdev);
Alan Coxaae40fd2009-06-09 10:02:11 -03001445 if (!se401->user)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 usb_se401_remove_disconnected(se401);
Alan Coxaae40fd2009-06-09 10:02:11 -03001447 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 se401->frame[0].grabstate = FRAME_ERROR;
1449 se401->frame[0].grabstate = FRAME_ERROR;
1450
1451 se401->streaming = 0;
1452
1453 wake_up_interruptible(&se401->wq);
1454 se401->removed = 1;
1455 }
1456 }
1457}
1458
1459static struct usb_driver se401_driver = {
Alan Coxaae40fd2009-06-09 10:02:11 -03001460 .name = "se401",
1461 .id_table = device_table,
1462 .probe = se401_probe,
1463 .disconnect = se401_disconnect,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464};
1465
1466
1467
1468/****************************************************************************
1469 *
1470 * Module routines
1471 *
1472 ***************************************************************************/
1473
1474static int __init usb_se401_init(void)
1475{
Alan Coxaae40fd2009-06-09 10:02:11 -03001476 printk(KERN_INFO "SE401 usb camera driver version %s registering\n",
1477 version);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 if (flickerless)
Alan Coxaae40fd2009-06-09 10:02:11 -03001479 if (flickerless != 50 && flickerless != 60) {
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001480 printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 return -1;
1482 }
1483 return usb_register(&se401_driver);
1484}
1485
1486static void __exit usb_se401_exit(void)
1487{
1488 usb_deregister(&se401_driver);
Greg Kroah-Hartmana482f322008-10-10 05:08:23 -03001489 printk(KERN_INFO "SE401 driver deregistered\frame");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490}
1491
1492module_init(usb_se401_init);
1493module_exit(usb_se401_exit);