IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
[deliverable/linux.git] / drivers / media / video / se401.c
1 /*
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.
7 *
8 * 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
28 static const char version[] = "0.24";
29
30 #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
38 static int flickerless=0;
39 static int video_nr = -1;
40
41 static struct usb_device_id device_table [] = {
42 { 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
50 MODULE_DEVICE_TABLE(usb, device_table);
51
52 MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
53 MODULE_DESCRIPTION("SE401 USB Camera Driver");
54 MODULE_LICENSE("GPL");
55 module_param(flickerless, int, 0);
56 MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)");
57 module_param(video_nr, int, 0);
58
59 static struct usb_driver se401_driver;
60
61
62 /**********************************************************************
63 *
64 * Memory management
65 *
66 **********************************************************************/
67 static void *rvmalloc(unsigned long size)
68 {
69 void *mem;
70 unsigned long adr;
71
72 size = PAGE_ALIGN(size);
73 mem = vmalloc_32(size);
74 if (!mem)
75 return NULL;
76
77 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
78 adr = (unsigned long) mem;
79 while (size > 0) {
80 SetPageReserved(vmalloc_to_page((void *)adr));
81 adr += PAGE_SIZE;
82 size -= PAGE_SIZE;
83 }
84
85 return mem;
86 }
87
88 static void rvfree(void *mem, unsigned long size)
89 {
90 unsigned long adr;
91
92 if (!mem)
93 return;
94
95 adr = (unsigned long) mem;
96 while ((long) size > 0) {
97 ClearPageReserved(vmalloc_to_page((void *)adr));
98 adr += PAGE_SIZE;
99 size -= PAGE_SIZE;
100 }
101 vfree(mem);
102 }
103
104
105
106 /****************************************************************************
107 *
108 * se401 register read/write functions
109 *
110 ***************************************************************************/
111
112 static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req,
113 unsigned short value, unsigned char *cp, int size)
114 {
115 return usb_control_msg (
116 se401->dev,
117 set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0),
118 req,
119 (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
120 value,
121 0,
122 cp,
123 size,
124 1000
125 );
126 }
127
128 static int se401_set_feature(struct usb_se401 *se401, unsigned short selector,
129 unsigned short param)
130 {
131 /* specs say that the selector (address) should go in the value field
132 and the param in index, but in the logs of the windows driver they do
133 this the other way around...
134 */
135 return usb_control_msg (
136 se401->dev,
137 usb_sndctrlpipe(se401->dev, 0),
138 SE401_REQ_SET_EXT_FEATURE,
139 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
140 param,
141 selector,
142 NULL,
143 0,
144 1000
145 );
146 }
147
148 static unsigned short se401_get_feature(struct usb_se401 *se401,
149 unsigned short selector)
150 {
151 /* For 'set' the selecetor should be in index, not sure if the spec is
152 wrong here to....
153 */
154 unsigned char cp[2];
155 usb_control_msg (
156 se401->dev,
157 usb_rcvctrlpipe(se401->dev, 0),
158 SE401_REQ_GET_EXT_FEATURE,
159 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
160 0,
161 selector,
162 cp,
163 2,
164 1000
165 );
166 return cp[0]+cp[1]*256;
167 }
168
169 /****************************************************************************
170 *
171 * Camera control
172 *
173 ***************************************************************************/
174
175
176 static int se401_send_pict(struct usb_se401 *se401)
177 {
178 se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */
179 se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */
180 se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */
181 se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */
182 se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */
183 se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */
184 se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */
185
186 return 0;
187 }
188
189 static void se401_set_exposure(struct usb_se401 *se401, int brightness)
190 {
191 int integration=brightness<<5;
192
193 if (flickerless==50) {
194 integration=integration-integration%106667;
195 }
196 if (flickerless==60) {
197 integration=integration-integration%88889;
198 }
199 se401->brightness=integration>>5;
200 se401->expose_h=(integration>>16)&0xff;
201 se401->expose_m=(integration>>8)&0xff;
202 se401->expose_l=integration&0xff;
203 }
204
205 static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p)
206 {
207 p->brightness=se401->brightness;
208 if (se401->enhance) {
209 p->whiteness=32768;
210 } else {
211 p->whiteness=0;
212 }
213 p->colour=65535;
214 p->contrast=65535;
215 p->hue=se401->rgain<<10;
216 p->palette=se401->palette;
217 p->depth=3; /* rgb24 */
218 return 0;
219 }
220
221
222 static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p)
223 {
224 if (p->palette != VIDEO_PALETTE_RGB24)
225 return 1;
226 se401->palette=p->palette;
227 if (p->hue!=se401->hue) {
228 se401->rgain= p->hue>>10;
229 se401->bgain= 0x40-(p->hue>>10);
230 se401->hue=p->hue;
231 }
232 if (p->brightness!=se401->brightness) {
233 se401_set_exposure(se401, p->brightness);
234 }
235 if (p->whiteness>=32768) {
236 se401->enhance=1;
237 } else {
238 se401->enhance=0;
239 }
240 se401_send_pict(se401);
241 se401_send_pict(se401);
242 return 0;
243 }
244
245 /*
246 Hyundai have some really nice docs about this and other sensor related
247 stuff on their homepage: www.hei.co.kr
248 */
249 static void se401_auto_resetlevel(struct usb_se401 *se401)
250 {
251 unsigned int ahrc, alrc;
252 int oldreset=se401->resetlevel;
253
254 /* For some reason this normally read-only register doesn't get reset
255 to zero after reading them just once...
256 */
257 se401_get_feature(se401, HV7131_REG_HIREFNOH);
258 se401_get_feature(se401, HV7131_REG_HIREFNOL);
259 se401_get_feature(se401, HV7131_REG_LOREFNOH);
260 se401_get_feature(se401, HV7131_REG_LOREFNOL);
261 ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +
262 se401_get_feature(se401, HV7131_REG_HIREFNOL);
263 alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) +
264 se401_get_feature(se401, HV7131_REG_LOREFNOL);
265
266 /* Not an exact science, but it seems to work pretty well... */
267 if (alrc > 10) {
268 while (alrc>=10 && se401->resetlevel < 63) {
269 se401->resetlevel++;
270 alrc /=2;
271 }
272 } else if (ahrc > 20) {
273 while (ahrc>=20 && se401->resetlevel > 0) {
274 se401->resetlevel--;
275 ahrc /=2;
276 }
277 }
278 if (se401->resetlevel!=oldreset)
279 se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
280
281 return;
282 }
283
284 /* irq handler for snapshot button */
285 static void se401_button_irq(struct urb *urb)
286 {
287 struct usb_se401 *se401 = urb->context;
288 int status;
289
290 if (!se401->dev) {
291 info("ohoh: device vapourished");
292 return;
293 }
294
295 switch (urb->status) {
296 case 0:
297 /* success */
298 break;
299 case -ECONNRESET:
300 case -ENOENT:
301 case -ESHUTDOWN:
302 /* this urb is terminated, clean up */
303 dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
304 return;
305 default:
306 dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
307 goto exit;
308 }
309
310 if (urb->actual_length >=2) {
311 if (se401->button)
312 se401->buttonpressed=1;
313 }
314 exit:
315 status = usb_submit_urb (urb, GFP_ATOMIC);
316 if (status)
317 err ("%s - usb_submit_urb failed with result %d",
318 __FUNCTION__, status);
319 }
320
321 static void se401_video_irq(struct urb *urb)
322 {
323 struct usb_se401 *se401 = urb->context;
324 int length = urb->actual_length;
325
326 /* ohoh... */
327 if (!se401->streaming)
328 return;
329
330 if (!se401->dev) {
331 info ("ohoh: device vapourished");
332 return;
333 }
334
335 /* 0 sized packets happen if we are to fast, but sometimes the camera
336 keeps sending them forever...
337 */
338 if (length && !urb->status) {
339 se401->nullpackets=0;
340 switch(se401->scratch[se401->scratch_next].state) {
341 case BUFFER_READY:
342 case BUFFER_BUSY: {
343 se401->dropped++;
344 break;
345 }
346 case BUFFER_UNUSED: {
347 memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length);
348 se401->scratch[se401->scratch_next].state=BUFFER_READY;
349 se401->scratch[se401->scratch_next].offset=se401->bayeroffset;
350 se401->scratch[se401->scratch_next].length=length;
351 if (waitqueue_active(&se401->wq)) {
352 wake_up_interruptible(&se401->wq);
353 }
354 se401->scratch_overflow=0;
355 se401->scratch_next++;
356 if (se401->scratch_next>=SE401_NUMSCRATCH)
357 se401->scratch_next=0;
358 break;
359 }
360 }
361 se401->bayeroffset+=length;
362 if (se401->bayeroffset>=se401->cheight*se401->cwidth) {
363 se401->bayeroffset=0;
364 }
365 } else {
366 se401->nullpackets++;
367 if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
368 if (waitqueue_active(&se401->wq)) {
369 wake_up_interruptible(&se401->wq);
370 }
371 }
372 }
373
374 /* Resubmit urb for new data */
375 urb->status=0;
376 urb->dev=se401->dev;
377 if(usb_submit_urb(urb, GFP_KERNEL))
378 info("urb burned down");
379 return;
380 }
381
382 static void se401_send_size(struct usb_se401 *se401, int width, int height)
383 {
384 int i=0;
385 int mode=0x03; /* No compression */
386 int sendheight=height;
387 int sendwidth=width;
388
389 /* JangGu compression can only be used with the camera supported sizes,
390 but bayer seems to work with any size that fits on the sensor.
391 We check if we can use compression with the current size with either
392 4 or 16 times subcapturing, if not we use uncompressed bayer data
393 but this will result in cutouts of the maximum size....
394 */
395 while (i<se401->sizes && !(se401->width[i]==width && se401->height[i]==height))
396 i++;
397 while (i<se401->sizes) {
398 if (se401->width[i]==width*2 && se401->height[i]==height*2) {
399 sendheight=se401->height[i];
400 sendwidth=se401->width[i];
401 mode=0x40;
402 }
403 if (se401->width[i]==width*4 && se401->height[i]==height*4) {
404 sendheight=se401->height[i];
405 sendwidth=se401->width[i];
406 mode=0x42;
407 }
408 i++;
409 }
410
411 se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0);
412 se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0);
413 se401_set_feature(se401, SE401_OPERATINGMODE, mode);
414
415 if (mode==0x03) {
416 se401->format=FMT_BAYER;
417 } else {
418 se401->format=FMT_JANGGU;
419 }
420
421 return;
422 }
423
424 /*
425 In this function se401_send_pict is called several times,
426 for some reason (depending on the state of the sensor and the phase of
427 the moon :) doing this only in either place doesn't always work...
428 */
429 static int se401_start_stream(struct usb_se401 *se401)
430 {
431 struct urb *urb;
432 int err=0, i;
433 se401->streaming=1;
434
435 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
436 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
437
438 /* Set picture settings */
439 se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */
440 se401_send_pict(se401);
441
442 se401_send_size(se401, se401->cwidth, se401->cheight);
443
444 se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0);
445
446 /* Do some memory allocation */
447 for (i=0; i<SE401_NUMFRAMES; i++) {
448 se401->frame[i].data=se401->fbuf + i * se401->maxframesize;
449 se401->frame[i].curpix=0;
450 }
451 for (i=0; i<SE401_NUMSBUF; i++) {
452 se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
453 }
454
455 se401->bayeroffset=0;
456 se401->scratch_next=0;
457 se401->scratch_use=0;
458 se401->scratch_overflow=0;
459 for (i=0; i<SE401_NUMSCRATCH; i++) {
460 se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
461 se401->scratch[i].state=BUFFER_UNUSED;
462 }
463
464 for (i=0; i<SE401_NUMSBUF; i++) {
465 urb=usb_alloc_urb(0, GFP_KERNEL);
466 if(!urb)
467 return -ENOMEM;
468
469 usb_fill_bulk_urb(urb, se401->dev,
470 usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
471 se401->sbuf[i].data, SE401_PACKETSIZE,
472 se401_video_irq,
473 se401);
474
475 se401->urb[i]=urb;
476
477 err=usb_submit_urb(se401->urb[i], GFP_KERNEL);
478 if(err)
479 err("urb burned down");
480 }
481
482 se401->framecount=0;
483
484 return 0;
485 }
486
487 static int se401_stop_stream(struct usb_se401 *se401)
488 {
489 int i;
490
491 if (!se401->streaming || !se401->dev)
492 return 1;
493
494 se401->streaming=0;
495
496 se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0);
497
498 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
499 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
500
501 for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
502 usb_kill_urb(se401->urb[i]);
503 usb_free_urb(se401->urb[i]);
504 se401->urb[i]=NULL;
505 kfree(se401->sbuf[i].data);
506 }
507 for (i=0; i<SE401_NUMSCRATCH; i++) {
508 kfree(se401->scratch[i].data);
509 se401->scratch[i].data=NULL;
510 }
511
512 return 0;
513 }
514
515 static int se401_set_size(struct usb_se401 *se401, int width, int height)
516 {
517 int wasstreaming=se401->streaming;
518 /* Check to see if we need to change */
519 if (se401->cwidth==width && se401->cheight==height)
520 return 0;
521
522 /* Check for a valid mode */
523 if (!width || !height)
524 return 1;
525 if ((width & 1) || (height & 1))
526 return 1;
527 if (width>se401->width[se401->sizes-1])
528 return 1;
529 if (height>se401->height[se401->sizes-1])
530 return 1;
531
532 /* Stop a current stream and start it again at the new size */
533 if (wasstreaming)
534 se401_stop_stream(se401);
535 se401->cwidth=width;
536 se401->cheight=height;
537 if (wasstreaming)
538 se401_start_stream(se401);
539 return 0;
540 }
541
542
543 /****************************************************************************
544 *
545 * Video Decoding
546 *
547 ***************************************************************************/
548
549 /*
550 This shouldn't really be done in a v4l driver....
551 But it does make the image look a lot more usable.
552 Basically it lifts the dark pixels more than the light pixels.
553 */
554 static inline void enhance_picture(unsigned char *frame, int len)
555 {
556 while (len--) {
557 *frame=(((*frame^255)*(*frame^255))/255)^255;
558 frame++;
559 }
560 }
561
562 static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data)
563 {
564 struct se401_frame *frame=&se401->frame[se401->curframe];
565 int linelength=se401->cwidth*3;
566
567 if (frame->curlinepix >= linelength) {
568 frame->curlinepix=0;
569 frame->curline+=linelength;
570 }
571
572 /* First three are absolute, all others relative.
573 * Format is rgb from right to left (mirrorred image),
574 * we flip it to get bgr from left to right. */
575 if (frame->curlinepix < 3) {
576 *(frame->curline-frame->curlinepix)=1+data*4;
577 } else {
578 *(frame->curline-frame->curlinepix)=
579 *(frame->curline-frame->curlinepix+3)+data*4;
580 }
581 frame->curlinepix++;
582 }
583
584 static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength)
585 {
586 int pos=0;
587 int vlc_cod=0;
588 int vlc_size=0;
589 int vlc_data=0;
590 int bit_cur;
591 int bit;
592 data+=4;
593 while (pos < packetlength) {
594 bit_cur=8;
595 while (bit_cur && bit_exp) {
596 bit=((*data)>>(bit_cur-1))&1;
597 if (!vlc_cod) {
598 if (bit) {
599 vlc_size++;
600 } else {
601 if (!vlc_size) {
602 decode_JangGu_integrate(se401, 0);
603 } else {
604 vlc_cod=2;
605 vlc_data=0;
606 }
607 }
608 } else {
609 if (vlc_cod==2) {
610 if (!bit)
611 vlc_data = -(1<<vlc_size) + 1;
612 vlc_cod--;
613 }
614 vlc_size--;
615 vlc_data+=bit<<vlc_size;
616 if (!vlc_size) {
617 decode_JangGu_integrate(se401, vlc_data);
618 vlc_cod=0;
619 }
620 }
621 bit_cur--;
622 bit_exp--;
623 }
624 pos++;
625 data++;
626 }
627 }
628
629 static inline void decode_JangGu (struct usb_se401 *se401, struct se401_scratch *buffer)
630 {
631 unsigned char *data=buffer->data;
632 int len=buffer->length;
633 int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size;
634 int datapos=0;
635
636 /* New image? */
637 if (!se401->frame[se401->curframe].curpix) {
638 se401->frame[se401->curframe].curlinepix=0;
639 se401->frame[se401->curframe].curline=
640 se401->frame[se401->curframe].data+
641 se401->cwidth*3-1;
642 if (se401->frame[se401->curframe].grabstate==FRAME_READY)
643 se401->frame[se401->curframe].grabstate=FRAME_GRABBING;
644 se401->vlcdatapos=0;
645 }
646 while (datapos < len) {
647 size=1024-se401->vlcdatapos;
648 if (size+datapos > len)
649 size=len-datapos;
650 memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size);
651 se401->vlcdatapos+=size;
652 packetlength=0;
653 if (se401->vlcdatapos >= 4) {
654 bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8);
655 pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8);
656 frameinfo=se401->vlcdata[0]&0xc0;
657 packetlength=((bit_exp+47)>>4)<<1;
658 if (packetlength > 1024) {
659 se401->vlcdatapos=0;
660 datapos=len;
661 packetlength=0;
662 se401->error++;
663 se401->frame[se401->curframe].curpix=0;
664 }
665 }
666 if (packetlength && se401->vlcdatapos >= packetlength) {
667 decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength);
668 se401->frame[se401->curframe].curpix+=pix_exp*3;
669 datapos+=size-(se401->vlcdatapos-packetlength);
670 se401->vlcdatapos=0;
671 if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) {
672 if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) {
673 if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) {
674 se401->frame[se401->curframe].grabstate=FRAME_DONE;
675 se401->framecount++;
676 se401->readcount++;
677 }
678 if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) {
679 se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1);
680 }
681 } else {
682 se401->error++;
683 }
684 se401->frame[se401->curframe].curpix=0;
685 datapos=len;
686 }
687 } else {
688 datapos+=size;
689 }
690 }
691 }
692
693 static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer)
694 {
695 unsigned char *data=buffer->data;
696 int len=buffer->length;
697 int offset=buffer->offset;
698 int datasize=se401->cwidth*se401->cheight;
699 struct se401_frame *frame=&se401->frame[se401->curframe];
700
701 unsigned char *framedata=frame->data, *curline, *nextline;
702 int width=se401->cwidth;
703 int blineoffset=0, bline;
704 int linelength=width*3, i;
705
706
707 if (frame->curpix==0) {
708 if (frame->grabstate==FRAME_READY) {
709 frame->grabstate=FRAME_GRABBING;
710 }
711 frame->curline=framedata+linelength;
712 frame->curlinepix=0;
713 }
714
715 if (offset!=frame->curpix) {
716 /* Regard frame as lost :( */
717 frame->curpix=0;
718 se401->error++;
719 return;
720 }
721
722 /* Check if we have to much data */
723 if (frame->curpix+len > datasize) {
724 len=datasize-frame->curpix;
725 }
726 if (se401->cheight%4)
727 blineoffset=1;
728 bline=frame->curpix/se401->cwidth+blineoffset;
729
730 curline=frame->curline;
731 nextline=curline+linelength;
732 if (nextline >= framedata+datasize*3)
733 nextline=curline;
734 while (len) {
735 if (frame->curlinepix>=width) {
736 frame->curlinepix-=width;
737 bline=frame->curpix/width+blineoffset;
738 curline+=linelength*2;
739 nextline+=linelength*2;
740 if (curline >= framedata+datasize*3) {
741 frame->curlinepix++;
742 curline-=3;
743 nextline-=3;
744 len--;
745 data++;
746 frame->curpix++;
747 }
748 if (nextline >= framedata+datasize*3)
749 nextline=curline;
750 }
751 if ((bline&1)) {
752 if ((frame->curlinepix&1)) {
753 *(curline+2)=*data;
754 *(curline-1)=*data;
755 *(nextline+2)=*data;
756 *(nextline-1)=*data;
757 } else {
758 *(curline+1)=
759 (*(curline+1)+*data)/2;
760 *(curline-2)=
761 (*(curline-2)+*data)/2;
762 *(nextline+1)=*data;
763 *(nextline-2)=*data;
764 }
765 } else {
766 if ((frame->curlinepix&1)) {
767 *(curline+1)=
768 (*(curline+1)+*data)/2;
769 *(curline-2)=
770 (*(curline-2)+*data)/2;
771 *(nextline+1)=*data;
772 *(nextline-2)=*data;
773 } else {
774 *curline=*data;
775 *(curline-3)=*data;
776 *nextline=*data;
777 *(nextline-3)=*data;
778 }
779 }
780 frame->curlinepix++;
781 curline-=3;
782 nextline-=3;
783 len--;
784 data++;
785 frame->curpix++;
786 }
787 frame->curline=curline;
788
789 if (frame->curpix>=datasize) {
790 /* Fix the top line */
791 framedata+=linelength;
792 for (i=0; i<linelength; i++) {
793 framedata--;
794 *framedata=*(framedata+linelength);
795 }
796 /* Fix the left side (green is already present) */
797 for (i=0; i<se401->cheight; i++) {
798 *framedata=*(framedata+3);
799 *(framedata+1)=*(framedata+4);
800 *(framedata+2)=*(framedata+5);
801 framedata+=linelength;
802 }
803 frame->curpix=0;
804 frame->grabstate=FRAME_DONE;
805 se401->framecount++;
806 se401->readcount++;
807 if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) {
808 se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1);
809 }
810 }
811 }
812
813 static int se401_newframe(struct usb_se401 *se401, int framenr)
814 {
815 DECLARE_WAITQUEUE(wait, current);
816 int errors=0;
817
818 while (se401->streaming &&
819 (se401->frame[framenr].grabstate==FRAME_READY ||
820 se401->frame[framenr].grabstate==FRAME_GRABBING) ) {
821 if(!se401->frame[framenr].curpix) {
822 errors++;
823 }
824 wait_interruptible(
825 se401->scratch[se401->scratch_use].state!=BUFFER_READY,
826 &se401->wq,
827 &wait
828 );
829 if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
830 se401->nullpackets=0;
831 info("to many null length packets, restarting capture");
832 se401_stop_stream(se401);
833 se401_start_stream(se401);
834 } else {
835 if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) {
836 se401->frame[framenr].grabstate=FRAME_ERROR;
837 return -EIO;
838 }
839 se401->scratch[se401->scratch_use].state=BUFFER_BUSY;
840 if (se401->format==FMT_JANGGU) {
841 decode_JangGu(se401, &se401->scratch[se401->scratch_use]);
842 } else {
843 decode_bayer(se401, &se401->scratch[se401->scratch_use]);
844 }
845 se401->scratch[se401->scratch_use].state=BUFFER_UNUSED;
846 se401->scratch_use++;
847 if (se401->scratch_use>=SE401_NUMSCRATCH)
848 se401->scratch_use=0;
849 if (errors > SE401_MAX_ERRORS) {
850 errors=0;
851 info("to much errors, restarting capture");
852 se401_stop_stream(se401);
853 se401_start_stream(se401);
854 }
855 }
856 }
857
858 if (se401->frame[framenr].grabstate==FRAME_DONE)
859 if (se401->enhance)
860 enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3);
861 return 0;
862 }
863
864 static void usb_se401_remove_disconnected (struct usb_se401 *se401)
865 {
866 int i;
867
868 se401->dev = NULL;
869
870 for (i=0; i<SE401_NUMSBUF; i++)
871 if (se401->urb[i]) {
872 usb_kill_urb(se401->urb[i]);
873 usb_free_urb(se401->urb[i]);
874 se401->urb[i] = NULL;
875 kfree(se401->sbuf[i].data);
876 }
877 for (i=0; i<SE401_NUMSCRATCH; i++) {
878 kfree(se401->scratch[i].data);
879 }
880 if (se401->inturb) {
881 usb_kill_urb(se401->inturb);
882 usb_free_urb(se401->inturb);
883 }
884 info("%s disconnected", se401->camera_name);
885
886 /* Free the memory */
887 kfree(se401->width);
888 kfree(se401->height);
889 kfree(se401);
890 }
891
892
893
894 /****************************************************************************
895 *
896 * Video4Linux
897 *
898 ***************************************************************************/
899
900
901 static int se401_open(struct inode *inode, struct file *file)
902 {
903 struct video_device *dev = video_devdata(file);
904 struct usb_se401 *se401 = (struct usb_se401 *)dev;
905 int err = 0;
906
907 if (se401->user)
908 return -EBUSY;
909 se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
910 if (se401->fbuf)
911 file->private_data = dev;
912 else
913 err = -ENOMEM;
914 se401->user = !err;
915
916 return err;
917 }
918
919 static int se401_close(struct inode *inode, struct file *file)
920 {
921 struct video_device *dev = file->private_data;
922 struct usb_se401 *se401 = (struct usb_se401 *)dev;
923 int i;
924
925 rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
926 if (se401->removed) {
927 usb_se401_remove_disconnected(se401);
928 info("device unregistered");
929 } else {
930 for (i=0; i<SE401_NUMFRAMES; i++)
931 se401->frame[i].grabstate=FRAME_UNUSED;
932 if (se401->streaming)
933 se401_stop_stream(se401);
934 se401->user=0;
935 }
936 file->private_data = NULL;
937 return 0;
938 }
939
940 static int se401_do_ioctl(struct inode *inode, struct file *file,
941 unsigned int cmd, void *arg)
942 {
943 struct video_device *vdev = file->private_data;
944 struct usb_se401 *se401 = (struct usb_se401 *)vdev;
945
946 if (!se401->dev)
947 return -EIO;
948
949 switch (cmd) {
950 case VIDIOCGCAP:
951 {
952 struct video_capability *b = arg;
953 strcpy(b->name, se401->camera_name);
954 b->type = VID_TYPE_CAPTURE;
955 b->channels = 1;
956 b->audios = 0;
957 b->maxwidth = se401->width[se401->sizes-1];
958 b->maxheight = se401->height[se401->sizes-1];
959 b->minwidth = se401->width[0];
960 b->minheight = se401->height[0];
961 return 0;
962 }
963 case VIDIOCGCHAN:
964 {
965 struct video_channel *v = arg;
966
967 if (v->channel != 0)
968 return -EINVAL;
969 v->flags = 0;
970 v->tuners = 0;
971 v->type = VIDEO_TYPE_CAMERA;
972 strcpy(v->name, "Camera");
973 return 0;
974 }
975 case VIDIOCSCHAN:
976 {
977 struct video_channel *v = arg;
978
979 if (v->channel != 0)
980 return -EINVAL;
981 return 0;
982 }
983 case VIDIOCGPICT:
984 {
985 struct video_picture *p = arg;
986
987 se401_get_pict(se401, p);
988 return 0;
989 }
990 case VIDIOCSPICT:
991 {
992 struct video_picture *p = arg;
993
994 if (se401_set_pict(se401, p))
995 return -EINVAL;
996 return 0;
997 }
998 case VIDIOCSWIN:
999 {
1000 struct video_window *vw = arg;
1001
1002 if (vw->flags)
1003 return -EINVAL;
1004 if (vw->clipcount)
1005 return -EINVAL;
1006 if (se401_set_size(se401, vw->width, vw->height))
1007 return -EINVAL;
1008 return 0;
1009 }
1010 case VIDIOCGWIN:
1011 {
1012 struct video_window *vw = arg;
1013
1014 vw->x = 0; /* FIXME */
1015 vw->y = 0;
1016 vw->chromakey = 0;
1017 vw->flags = 0;
1018 vw->clipcount = 0;
1019 vw->width = se401->cwidth;
1020 vw->height = se401->cheight;
1021 return 0;
1022 }
1023 case VIDIOCGMBUF:
1024 {
1025 struct video_mbuf *vm = arg;
1026 int i;
1027
1028 memset(vm, 0, sizeof(*vm));
1029 vm->size = SE401_NUMFRAMES * se401->maxframesize;
1030 vm->frames = SE401_NUMFRAMES;
1031 for (i=0; i<SE401_NUMFRAMES; i++)
1032 vm->offsets[i] = se401->maxframesize * i;
1033 return 0;
1034 }
1035 case VIDIOCMCAPTURE:
1036 {
1037 struct video_mmap *vm = arg;
1038
1039 if (vm->format != VIDEO_PALETTE_RGB24)
1040 return -EINVAL;
1041 if (vm->frame >= SE401_NUMFRAMES)
1042 return -EINVAL;
1043 if (se401->frame[vm->frame].grabstate != FRAME_UNUSED)
1044 return -EBUSY;
1045
1046 /* Is this according to the v4l spec??? */
1047 if (se401_set_size(se401, vm->width, vm->height))
1048 return -EINVAL;
1049 se401->frame[vm->frame].grabstate=FRAME_READY;
1050
1051 if (!se401->streaming)
1052 se401_start_stream(se401);
1053
1054 /* Set the picture properties */
1055 if (se401->framecount==0)
1056 se401_send_pict(se401);
1057 /* Calibrate the reset level after a few frames. */
1058 if (se401->framecount%20==1)
1059 se401_auto_resetlevel(se401);
1060
1061 return 0;
1062 }
1063 case VIDIOCSYNC:
1064 {
1065 int *frame = arg;
1066 int ret=0;
1067
1068 if(*frame <0 || *frame >= SE401_NUMFRAMES)
1069 return -EINVAL;
1070
1071 ret=se401_newframe(se401, *frame);
1072 se401->frame[*frame].grabstate=FRAME_UNUSED;
1073 return ret;
1074 }
1075 case VIDIOCGFBUF:
1076 {
1077 struct video_buffer *vb = arg;
1078
1079 memset(vb, 0, sizeof(*vb));
1080 return 0;
1081 }
1082 case VIDIOCKEY:
1083 return 0;
1084 case VIDIOCCAPTURE:
1085 return -EINVAL;
1086 case VIDIOCSFBUF:
1087 return -EINVAL;
1088 case VIDIOCGTUNER:
1089 case VIDIOCSTUNER:
1090 return -EINVAL;
1091 case VIDIOCGFREQ:
1092 case VIDIOCSFREQ:
1093 return -EINVAL;
1094 case VIDIOCGAUDIO:
1095 case VIDIOCSAUDIO:
1096 return -EINVAL;
1097 default:
1098 return -ENOIOCTLCMD;
1099 } /* end switch */
1100
1101 return 0;
1102 }
1103
1104 static int se401_ioctl(struct inode *inode, struct file *file,
1105 unsigned int cmd, unsigned long arg)
1106 {
1107 return video_usercopy(inode, file, cmd, arg, se401_do_ioctl);
1108 }
1109
1110 static ssize_t se401_read(struct file *file, char __user *buf,
1111 size_t count, loff_t *ppos)
1112 {
1113 int realcount=count, ret=0;
1114 struct video_device *dev = file->private_data;
1115 struct usb_se401 *se401 = (struct usb_se401 *)dev;
1116
1117
1118 if (se401->dev == NULL)
1119 return -EIO;
1120 if (realcount > se401->cwidth*se401->cheight*3)
1121 realcount=se401->cwidth*se401->cheight*3;
1122
1123 /* Shouldn't happen: */
1124 if (se401->frame[0].grabstate==FRAME_GRABBING)
1125 return -EBUSY;
1126 se401->frame[0].grabstate=FRAME_READY;
1127 se401->frame[1].grabstate=FRAME_UNUSED;
1128 se401->curframe=0;
1129
1130 if (!se401->streaming)
1131 se401_start_stream(se401);
1132
1133 /* Set the picture properties */
1134 if (se401->framecount==0)
1135 se401_send_pict(se401);
1136 /* Calibrate the reset level after a few frames. */
1137 if (se401->framecount%20==1)
1138 se401_auto_resetlevel(se401);
1139
1140 ret=se401_newframe(se401, 0);
1141
1142 se401->frame[0].grabstate=FRAME_UNUSED;
1143 if (ret)
1144 return ret;
1145 if (copy_to_user(buf, se401->frame[0].data, realcount))
1146 return -EFAULT;
1147
1148 return realcount;
1149 }
1150
1151 static int se401_mmap(struct file *file, struct vm_area_struct *vma)
1152 {
1153 struct video_device *dev = file->private_data;
1154 struct usb_se401 *se401 = (struct usb_se401 *)dev;
1155 unsigned long start = vma->vm_start;
1156 unsigned long size = vma->vm_end-vma->vm_start;
1157 unsigned long page, pos;
1158
1159 mutex_lock(&se401->lock);
1160
1161 if (se401->dev == NULL) {
1162 mutex_unlock(&se401->lock);
1163 return -EIO;
1164 }
1165 if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
1166 mutex_unlock(&se401->lock);
1167 return -EINVAL;
1168 }
1169 pos = (unsigned long)se401->fbuf;
1170 while (size > 0) {
1171 page = vmalloc_to_pfn((void *)pos);
1172 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
1173 mutex_unlock(&se401->lock);
1174 return -EAGAIN;
1175 }
1176 start += PAGE_SIZE;
1177 pos += PAGE_SIZE;
1178 if (size > PAGE_SIZE)
1179 size -= PAGE_SIZE;
1180 else
1181 size = 0;
1182 }
1183 mutex_unlock(&se401->lock);
1184
1185 return 0;
1186 }
1187
1188 static struct file_operations se401_fops = {
1189 .owner = THIS_MODULE,
1190 .open = se401_open,
1191 .release = se401_close,
1192 .read = se401_read,
1193 .mmap = se401_mmap,
1194 .ioctl = se401_ioctl,
1195 .compat_ioctl = v4l_compat_ioctl32,
1196 .llseek = no_llseek,
1197 };
1198 static struct video_device se401_template = {
1199 .owner = THIS_MODULE,
1200 .name = "se401 USB camera",
1201 .type = VID_TYPE_CAPTURE,
1202 .hardware = VID_HARDWARE_SE401,
1203 .fops = &se401_fops,
1204 };
1205
1206
1207
1208 /***************************/
1209 static int se401_init(struct usb_se401 *se401, int button)
1210 {
1211 int i=0, rc;
1212 unsigned char cp[0x40];
1213 char temp[200];
1214
1215 /* led on */
1216 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
1217
1218 /* get camera descriptor */
1219 rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp));
1220 if (cp[1]!=0x41) {
1221 err("Wrong descriptor type");
1222 return 1;
1223 }
1224 sprintf (temp, "ExtraFeatures: %d", cp[3]);
1225
1226 se401->sizes=cp[4]+cp[5]*256;
1227 se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
1228 if (!se401->width)
1229 return 1;
1230 se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
1231 if (!se401->height) {
1232 kfree(se401->width);
1233 return 1;
1234 }
1235 for (i=0; i<se401->sizes; i++) {
1236 se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256;
1237 se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256;
1238 }
1239 sprintf (temp, "%s Sizes:", temp);
1240 for (i=0; i<se401->sizes; i++) {
1241 sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]);
1242 }
1243 info("%s", temp);
1244 se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3;
1245
1246 rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
1247 se401->cwidth=cp[0]+cp[1]*256;
1248 rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp));
1249 se401->cheight=cp[0]+cp[1]*256;
1250
1251 if (!cp[2] && SE401_FORMAT_BAYER) {
1252 err("Bayer format not supported!");
1253 return 1;
1254 }
1255 /* set output mode (BAYER) */
1256 se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0);
1257
1258 rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp));
1259 se401->brightness=cp[0]+cp[1]*256;
1260 /* some default values */
1261 se401->resetlevel=0x2d;
1262 se401->rgain=0x20;
1263 se401->ggain=0x20;
1264 se401->bgain=0x20;
1265 se401_set_exposure(se401, 20000);
1266 se401->palette=VIDEO_PALETTE_RGB24;
1267 se401->enhance=1;
1268 se401->dropped=0;
1269 se401->error=0;
1270 se401->framecount=0;
1271 se401->readcount=0;
1272
1273 /* Start interrupt transfers for snapshot button */
1274 if (button) {
1275 se401->inturb=usb_alloc_urb(0, GFP_KERNEL);
1276 if (!se401->inturb) {
1277 info("Allocation of inturb failed");
1278 return 1;
1279 }
1280 usb_fill_int_urb(se401->inturb, se401->dev,
1281 usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT),
1282 &se401->button, sizeof(se401->button),
1283 se401_button_irq,
1284 se401,
1285 8
1286 );
1287 if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
1288 info("int urb burned down");
1289 return 1;
1290 }
1291 } else
1292 se401->inturb=NULL;
1293
1294 /* Flash the led */
1295 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
1296 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
1297 se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
1298 se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
1299
1300 return 0;
1301 }
1302
1303 static int se401_probe(struct usb_interface *intf,
1304 const struct usb_device_id *id)
1305 {
1306 struct usb_device *dev = interface_to_usbdev(intf);
1307 struct usb_interface_descriptor *interface;
1308 struct usb_se401 *se401;
1309 char *camera_name=NULL;
1310 int button=1;
1311
1312 /* We don't handle multi-config cameras */
1313 if (dev->descriptor.bNumConfigurations != 1)
1314 return -ENODEV;
1315
1316 interface = &intf->cur_altsetting->desc;
1317
1318 /* Is it an se401? */
1319 if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 &&
1320 le16_to_cpu(dev->descriptor.idProduct) == 0x0004) {
1321 camera_name="Endpoints/Aox SE401";
1322 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 &&
1323 le16_to_cpu(dev->descriptor.idProduct) == 0x030b) {
1324 camera_name="Philips PCVC665K";
1325 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1326 le16_to_cpu(dev->descriptor.idProduct) == 0x5001) {
1327 camera_name="Kensington VideoCAM 67014";
1328 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1329 le16_to_cpu(dev->descriptor.idProduct) == 0x5002) {
1330 camera_name="Kensington VideoCAM 6701(5/7)";
1331 } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
1332 le16_to_cpu(dev->descriptor.idProduct) == 0x5003) {
1333 camera_name="Kensington VideoCAM 67016";
1334 button=0;
1335 } else
1336 return -ENODEV;
1337
1338 /* Checking vendor/product should be enough, but what the hell */
1339 if (interface->bInterfaceClass != 0x00)
1340 return -ENODEV;
1341 if (interface->bInterfaceSubClass != 0x00)
1342 return -ENODEV;
1343
1344 /* We found one */
1345 info("SE401 camera found: %s", camera_name);
1346
1347 if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) {
1348 err("couldn't kmalloc se401 struct");
1349 return -ENOMEM;
1350 }
1351
1352 se401->dev = dev;
1353 se401->iface = interface->bInterfaceNumber;
1354 se401->camera_name = camera_name;
1355
1356 info("firmware version: %02x", le16_to_cpu(dev->descriptor.bcdDevice) & 255);
1357
1358 if (se401_init(se401, button)) {
1359 kfree(se401);
1360 return -EIO;
1361 }
1362
1363 memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
1364 memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name));
1365 init_waitqueue_head(&se401->wq);
1366 mutex_init(&se401->lock);
1367 wmb();
1368
1369 if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
1370 kfree(se401);
1371 err("video_register_device failed");
1372 return -EIO;
1373 }
1374 info("registered new video device: video%d", se401->vdev.minor);
1375
1376 usb_set_intfdata (intf, se401);
1377 return 0;
1378 }
1379
1380 static void se401_disconnect(struct usb_interface *intf)
1381 {
1382 struct usb_se401 *se401 = usb_get_intfdata (intf);
1383
1384 usb_set_intfdata (intf, NULL);
1385 if (se401) {
1386 video_unregister_device(&se401->vdev);
1387 if (!se401->user){
1388 usb_se401_remove_disconnected(se401);
1389 } else {
1390 se401->frame[0].grabstate = FRAME_ERROR;
1391 se401->frame[0].grabstate = FRAME_ERROR;
1392
1393 se401->streaming = 0;
1394
1395 wake_up_interruptible(&se401->wq);
1396 se401->removed = 1;
1397 }
1398 }
1399 }
1400
1401 static struct usb_driver se401_driver = {
1402 .name = "se401",
1403 .id_table = device_table,
1404 .probe = se401_probe,
1405 .disconnect = se401_disconnect,
1406 };
1407
1408
1409
1410 /****************************************************************************
1411 *
1412 * Module routines
1413 *
1414 ***************************************************************************/
1415
1416 static int __init usb_se401_init(void)
1417 {
1418 info("SE401 usb camera driver version %s registering", version);
1419 if (flickerless)
1420 if (flickerless!=50 && flickerless!=60) {
1421 info("Invallid flickerless value, use 0, 50 or 60.");
1422 return -1;
1423 }
1424 return usb_register(&se401_driver);
1425 }
1426
1427 static void __exit usb_se401_exit(void)
1428 {
1429 usb_deregister(&se401_driver);
1430 info("SE401 driver deregistered");
1431 }
1432
1433 module_init(usb_se401_init);
1434 module_exit(usb_se401_exit);
This page took 0.073034 seconds and 5 git commands to generate.