diff -ruN linux/drivers/usb.org/Config.in linux/drivers/usb/Config.in --- linux/drivers/usb.org/Config.in Tue Sep 3 23:59:14 2002 +++ linux/drivers/usb/Config.in Tue Sep 3 23:55:33 2002 @@ -79,6 +79,7 @@ dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB SE402 and EP800 Camera support' CONFIG_USB_EPCAM $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL diff -ruN linux/drivers/usb.org/Makefile linux/drivers/usb/Makefile --- linux/drivers/usb.org/Makefile Tue Sep 3 23:58:54 2002 +++ linux/drivers/usb/Makefile Tue Sep 3 23:54:00 2002 @@ -80,6 +80,7 @@ obj-$(CONFIG_USB_VICAM) += vicam.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o +obj-$(CONFIG_USB_EPCAM) += epcam.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_RTL8150) += rtl8150.o diff -ruN linux/drivers/usb.org/epcam.c linux/drivers/usb/epcam.c --- linux/drivers/usb.org/epcam.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/usb/epcam.c Wed Sep 4 00:26:37 2002 @@ -0,0 +1,1800 @@ +/* + * Endpoints EPCAM USB Camera Driver + * + * Copyright (c) 2001 Jeroen B. Vreeken (pe1rxq@amsat.org) + * + * Based on the se401 driver, which in turn is based on the ov511 driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on + * their chipset available and supporting me while writing this driver. + * - Jeroen Vreeken + */ + +static const char version[] = "0.04"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "epcam.h" +#define INT2QT(val, buf) { (buf)[0]=(val)&255; (buf)[1]=(val)/256; } +#define QT2INT(buf) ((buf)[0]+(buf)[1]*256) + +static int flickerless=0; +static int video_nr=-1; + +static __devinitdata struct usb_device_id device_table [] = { + { USB_DEVICE(0x03e8, 0x1005) },/* Endpoints EP800 Reference model*/ + { USB_DEVICE(0x03e8, 0x1003) },/* Endpoints SE402 Reference model*/ + { USB_DEVICE(0x03e8, 0x1000) },/* Endpoints SE401(new firmware) Reference model*/ + { USB_DEVICE(0x03e8, 0x2112) },/* SpyPen Actor */ + { USB_DEVICE(0x03e8, 0x2040) },/* Rimax Slim Multicam */ + { USB_DEVICE(0x041e, 0x400d) },/* Creative PD1001 */ + { USB_DEVICE(0x04f2, 0xa001) },/* Chicony DC-100 */ + { } +}; + + +MODULE_AUTHOR("Jeroen Vreeken "); +MODULE_DESCRIPTION("EPCAM USB Camera Driver"); +MODULE_PARM(flickerless, "i"); +MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)"); +MODULE_PARM(video_nr, "i"); +MODULE_DEVICE_TABLE(usb, device_table); +EXPORT_NO_SYMBOLS; + +#define PACKETBUFS 32 + +static struct usb_driver epcam_driver; + + +/********************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.29 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says + * there will be one, but apparentely not yet -jerdfelt + * + * So I copied it again for the ov511 driver -claudio + * + * Same for the se401 and epcam driver -Jeroen + **********************************************************************/ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) { + ret = (unsigned long) page_address(pte_page(pte)); + ret |= (adr & (PAGE_SIZE - 1)); + } + } + } + + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + + + +/**************************************************************************** + * + * /proc interface + * + ***************************************************************************/ + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry *epcam_proc_entry = NULL; +extern struct proc_dir_entry *video_proc_entry; + +#define YES_NO(x) ((x) ? "yes" : "no") + +static int epcam_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *out = page; + int len; + struct usb_epcam *epcam = data; + + /* Stay under PAGE_SIZE or else bla bla bla.... */ + + out+=sprintf(out, "driver_version : %s\n", version); + out+=sprintf(out, "model : %s\n", epcam->camera_name); + out+=sprintf(out, "in use : %s\n", YES_NO (epcam->user)); + out+=sprintf(out, "streaming : %s\n", YES_NO (epcam->streaming)); + + out+=sprintf(out, "button state : %s\n", YES_NO (epcam->button)); + out+=sprintf(out, "button pressed : %s\n", YES_NO (epcam->buttonpressed)); + if (epcam->buttonpressed) + epcam->buttonpressed--; + + out+=sprintf(out, "num_frames : %d\n", EPCAM_NUMFRAMES); + out+=sprintf(out, "maximum-width : %d\n", epcam->maxwidth); + out+=sprintf(out, "maximum-height : %d\n", epcam->maxheight); + out+=sprintf(out, "Packets dropped : %d\n", epcam->dropped); + out+=sprintf(out, "Packets corrupt : %d\n", epcam->datacorrupt); + out+=sprintf(out, "Frames total : %d\n", epcam->readcount); + out+=sprintf(out, "Frames read : %d\n", epcam->framecount); + out+=sprintf(out, "Frames cancelled: %d\n", epcam->cancel); + out+=sprintf(out, "Que underruns : %d\n", epcam->underrun); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else + len = count; + + *start = page + off; + + return len; +} + +static int epcam_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return -EINVAL; +} + +static void create_proc_epcam_cam (struct usb_epcam *epcam) +{ + char name[7]; + struct proc_dir_entry *ent; + + if (!epcam_proc_entry || !epcam) + return; + + sprintf (name, "video%d", epcam->vdev.minor); + + ent = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, + epcam_proc_entry); + + if (!ent) + return; + + ent->data = epcam; + ent->read_proc = epcam_read_proc; + ent->write_proc = epcam_write_proc; + epcam->proc_entry = ent; +} + +static void destroy_proc_epcam_cam (struct usb_epcam *epcam) +{ + /* One to much, just to be sure :) */ + char name[9]; + + if (!epcam || !epcam->proc_entry) + return; + + sprintf(name, "video%d", epcam->vdev.minor); + remove_proc_entry(name, epcam_proc_entry); + epcam->proc_entry = NULL; +} + +static void proc_epcam_create (void) +{ + if (video_proc_entry == NULL) { + err("/proc/video/ doesn't exist"); + return; + } + + epcam_proc_entry=create_proc_entry("epcam", S_IFDIR, video_proc_entry); + + if (epcam_proc_entry) + epcam_proc_entry->owner = THIS_MODULE; + else + err("Unable to initialize /proc/video/epcam"); +} + +static void proc_epcam_destroy(void) +{ + if (epcam_proc_entry == NULL) + return; + + remove_proc_entry("epcam", video_proc_entry); +} +#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ + + +/**************************************************************************** + * + * epcam register read/write functions + * + ***************************************************************************/ + +static int epcam_sndctrl(int set, struct usb_epcam *epcam, unsigned short req, + unsigned short value, unsigned char *cp, int size) +{ + return usb_control_msg ( + epcam->dev, + set ? usb_sndctrlpipe(epcam->dev, 0) : usb_rcvctrlpipe(epcam->dev, 0), + req, + (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, + cp, + size, + HZ + ); +} + +static int epcam_set_feature(struct usb_epcam *epcam, unsigned short reg, + unsigned short value) +{ + /* specs say that the selector (address) should go in the value field + and the param in index, but in the logs of the windows driver they do + this the other way around... + */ + unsigned char cp[2]; + INT2QT(value, cp); + return usb_control_msg ( + epcam->dev, + usb_sndctrlpipe(epcam->dev, 0), + VENDOR_REQ_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + reg, + 0, + cp, + 2, + HZ + ); +} + +static unsigned short epcam_get_feature(struct usb_epcam *epcam, + unsigned short reg) +{ + /* For 'set' the selecetor should be in index, not sure if the spec is + wrong here to.... + */ + unsigned char cp[2]; + usb_control_msg ( + epcam->dev, + usb_rcvctrlpipe(epcam->dev, 0), + VENDOR_REQ_EXT_FEATURE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + reg, + 0, + cp, + 2, + HZ + ); + return cp[0]+cp[1]*256; +} + +static unsigned short epcam_read_bios(struct usb_epcam *epcam, + unsigned short address) +{ + unsigned char cp[2]; + usb_control_msg ( + epcam->dev, + usb_rcvctrlpipe(epcam->dev, 0), + VENDOR_REQ_BIOS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VENDOR_CMD_BIOS_READ, + address, + cp, + 2, + HZ + ); + return QT2INT(cp+cp[1]*256); +} + +/**************************************************************************** + * + * Camera control + * + ***************************************************************************/ + + +static int epcam_send_pict(struct usb_epcam *epcam) +{ + unsigned char cp[40]; + + if (epcam->brightness<16384) + epcam->brightness=16384; + + epcam_sndctrl(0, epcam, VENDOR_REQ_IMAGE_INFO, 0, cp, sizeof(cp)); +// info("imginfosize: %d", cp[1]*256+cp[0]); +// info("exposure : %d", cp[3]*256+cp[2]); + INT2QT(epcam->brightness>>5, cp+2); + + epcam_sndctrl(1, epcam, VENDOR_REQ_IMAGE_INFO, 0, cp, cp[1]*256+cp[0]); + + return 0; +} + +static int epcam_recv_pict(struct usb_epcam *epcam) +{ + unsigned char cp[40]; + + epcam_sndctrl(0, epcam, VENDOR_REQ_IMAGE_INFO, 0, cp, sizeof(cp)); + epcam->brightness=QT2INT(cp+2)<<5; + + return 0; +} + +//static void epcam_set_exposure(struct usb_epcam *epcam, int brightness) +//{ +// int integration=brightness<<5; +// +// if (flickerless==50) { +// integration=integration-integration%106667; +// } +// if (flickerless==60) { +// integration=integration-integration%88889; +// } +// epcam->brightness=integration>>5; +// epcam->expose_h=(integration>>16)&0xff; +// epcam->expose_m=(integration>>8)&0xff; +// epcam->expose_l=integration&0xff; +//} + +static int epcam_get_pict(struct usb_epcam *epcam, struct video_picture *p) +{ + epcam_recv_pict(epcam); + p->brightness=epcam->brightness; + if (epcam->enhance) { + p->whiteness=32768; + } else { + p->whiteness=0; + } + p->colour=65535; + p->contrast=65535; + p->hue=65535; + p->palette=epcam->palette; + p->depth=3; /* rgb24 */ + + return 0; +} + + +static int epcam_set_pict(struct usb_epcam *epcam, struct video_picture *p) +{ + if (p->palette != VIDEO_PALETTE_RGB24) + return 1; + epcam->palette=p->palette; + +// if (p->brightness!=epcam->brightness) { +// epcam_set_exposure(epcam, p->brightness); +// } + if (p->whiteness>=32768) { + epcam->enhance=1; + } else { + epcam->enhance=0; + } + + p->colour=65535; + p->contrast=65535; + p->hue=65535; + epcam->brightness=p->brightness; +// info("setting: %d", epcam->brightness); + epcam_send_pict(epcam); + return 0; +} + +/* + Hyundai have some really nice docs about this and other sensor related + stuff on their homepage: www.hei.co.kr +*/ +static void epcam_auto_resetlevel(struct usb_epcam *epcam) +{ + unsigned int ahrc, alrc; + int oldreset=epcam->resetlevel; + + /* For some reason these normally read-only registers don't get reset + to zero after reading them just once... + */ + epcam_get_feature(epcam, HV7131_REG_HIREFNOH); + epcam_get_feature(epcam, HV7131_REG_HIREFNOL); + epcam_get_feature(epcam, HV7131_REG_LOREFNOH); + epcam_get_feature(epcam, HV7131_REG_LOREFNOL); + ahrc=256*epcam_get_feature(epcam, HV7131_REG_HIREFNOH) + + epcam_get_feature(epcam, HV7131_REG_HIREFNOL); + alrc=256*epcam_get_feature(epcam, HV7131_REG_LOREFNOH) + + epcam_get_feature(epcam, HV7131_REG_LOREFNOL); + + /* Not an exact science, but it seems to work pretty well... */ + if (alrc > 10) { + while (alrc>=10 && epcam->resetlevel < 63) { + epcam->resetlevel++; + alrc /=2; + } + } else if (ahrc > 20) { + while (ahrc>=20 && epcam->resetlevel > 0) { + epcam->resetlevel--; + ahrc /=2; + } + } + if (epcam->resetlevel!=oldreset) + epcam_set_feature(epcam, HV7131_REG_ARLV, epcam->resetlevel); + + return; +} + +/* irq handler for snapshot button */ +static void epcam_button_irq(struct urb *urb) +{ + struct usb_epcam *epcam=urb->context; + int length=urb->actual_length; + + /* ohoh... */ + if (!urb) { + info ("ohoh: null urb"); + return; + } + if (!epcam->dev) { + info ("ohoh: device vapourished"); + return; + } + + if (length >=2 && !urb->status) { + if (epcam->button) + epcam->buttonpressed=1; + } +} + +static void epcam_video_irq(struct urb *urb) +{ + struct usb_epcam *epcam=urb->context; + int length=0, i; + + /* ohoh... */ + if (!epcam->streaming) { + return; + } + if (!urb) { + info ("ohoh: null urb"); + return; + } + if (!epcam->dev) { + info ("ohoh: device vapourished"); + return; + } + + /* 0 sized packets happen if we are to fast, but sometimes the camera + keeps sending them forever... + */ +// if (urb->status) { +// printk(KERN_INFO "urb->status: %d\n", urb->status); +// return; +// } + switch(epcam->scratch[epcam->scratch_next].state) { + case BUFFER_READY: + case BUFFER_BUSY: { + epcam->dropped++; + break; + } + case BUFFER_UNUSED: { +// printk(KERN_INFO __FILE__); + for (i=0; iiso_frame_desc[i].actual_length); + if (urb->iso_frame_desc[i].actual_length) { + epcam->nullpackets=0; + memcpy( + epcam->scratch[epcam->scratch_next].data+length, + (unsigned char *)urb->transfer_buffer+urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + length+=urb->iso_frame_desc[i].actual_length; + } + } +// printk("\n"); + if (length) { + epcam->scratch[epcam->scratch_next].state=BUFFER_READY; + epcam->scratch[epcam->scratch_next].offset=epcam->scratch_offset; + epcam->scratch[epcam->scratch_next].length=length; + if (waitqueue_active(&epcam->wq)) + wake_up_interruptible(&epcam->wq); + epcam->scratch_overflow=0; +// info("scratch_offset: %d", epcam->scratch_offset); + epcam->scratch_next++; + if (epcam->scratch_next>=EPCAM_NUMSCRATCH) + epcam->scratch_next=0; + } + } + } +//info("length: %d offset: %d", length, epcam->scratch_offset); + if (length) + epcam->scratch_offset++; + else + epcam->scratch_offset=0; + return; +} + +static void epcam_send_size(struct usb_epcam *epcam, int width, int height) +{ + unsigned char cp[40]; + int compress=0; + + epcam_sndctrl(0, epcam, VENDOR_REQ_AUTO_CONTROL, 1, NULL, 0); + + epcam->format=FMT_BAYER; + if (epcam->camid==0x800) { + /* Don't use compression on small images: camera goes crazy */ + if (width*height>=38400) { + compress=0x02; + epcam->format=FMT_EPLITE; + if (width <= epcam->maxwidth/2 && + height <= epcam->maxheight/2) { + width*=2; + height*=2; + compress|=0x80; + } + } + } + if (epcam->camid==0x402) { + width=width; + } + + info("vendor_req_capture_info: %d", epcam_sndctrl(0, epcam, VENDOR_REQ_CAPTURE_INFO, 0, cp, sizeof(cp))); + INT2QT(1, cp+2); + INT2QT(0, cp+4); + INT2QT(0, cp+6); + INT2QT((epcam->maxwidth-width)/2, cp+4); + INT2QT((epcam->maxheight-height)/2, cp+6); + INT2QT(width, cp+8); + INT2QT(height, cp+10); + epcam_sndctrl(1, epcam, VENDOR_REQ_CAPTURE_INFO, 0, cp, cp[1]*256+cp[0]); + info("vendor_req_capture_info: %d", epcam_sndctrl(0, epcam, VENDOR_REQ_CAPTURE_INFO, 0, cp, sizeof(cp))); + info("capture info size: %d", QT2INT(cp)); + info("mode : %d", QT2INT(cp+2)); + info("xstart : %d", QT2INT(cp+4)); + info("ystart : %d", QT2INT(cp+6)); + info("width : %d", QT2INT(cp+8)); + info("height : %d", QT2INT(cp+10)); + info("framerate: %d", QT2INT(cp+12)); + info("zoom : %d", QT2INT(cp+14)); + + info("vendor_req_compression: %d", epcam_sndctrl(0, epcam, VENDOR_REQ_COMPRESSION, 0, cp, sizeof(cp))); +// info("compression: %d", cp[1]*256+cp[0]); + INT2QT(compress, cp); + epcam_sndctrl(1, epcam, VENDOR_REQ_COMPRESSION, 0, cp, 2); +// info("%d", epcam_sndctrl(0, epcam, VENDOR_REQ_COMPRESSION, 0, cp, sizeof(cp))); + info("compression: %d", cp[1]*256+cp[0]); + + return; +} + +/* + In this function epcam_send_pict is called several times, + for some reason (depending on the state of the sensor and the phase of + the moon :) doing this only in either place doesn't always work... +*/ +static int epcam_start_stream(struct usb_epcam *epcam) +{ + struct urb *urb; + int err=0, i, fx; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + + epcam->streaming=1; + + + /* Set the camera to Iso transfers */ + if (usb_set_interface(epcam->dev, epcam->iface, 4)< 0) { + info("interface set failed"); + } +info("interface set"); + interface=&epcam->dev->actconfig->interface[epcam->iface].altsetting[4]; + endpoint=&interface->endpoint[0]; + epcam->packetsize=endpoint->wMaxPacketSize; +info("packetsize: %d", epcam->packetsize); + + epcam_sndctrl(1, epcam, VENDOR_REQ_CAM_POWER, 1, NULL, 0); + epcam_sndctrl(1, epcam, VENDOR_REQ_LED_CONTROL, 1, NULL, 0); +info("led and power on"); + /* Set picture settings */ + + epcam_send_pict(epcam); + + epcam_send_size(epcam, epcam->cwidth, epcam->cheight); + + + epcam_sndctrl(1, epcam, VENDOR_REQ_CONT_CAPTURE, 1, NULL, 0); +info("capture on"); + /* Do some memory allocation */ + for (i=0; iframe[i].data=epcam->fbuf + i * epcam->maxframesize; + epcam->frame[i].curpix=0; + } + for (i=0; isbuf[i].data=kmalloc(epcam->packetsize*PACKETBUFS, GFP_KERNEL); + } + + epcam->scratch_offset=0; + epcam->lastoffset=-1; + epcam->scratch_next=0; + epcam->scratch_use=0; + epcam->scratch_overflow=0; + for (i=0; iscratch[i].data=kmalloc(epcam->packetsize*PACKETBUFS, GFP_KERNEL); + epcam->scratch[i].state=BUFFER_UNUSED; + } + + for (i=0; idev=epcam->dev; + urb->context=epcam; + urb->pipe=usb_rcvisocpipe(epcam->dev, EPCAM_VIDEO_ENDPOINT); + urb->transfer_flags=USB_ISO_ASAP; + urb->transfer_buffer=epcam->sbuf[i].data; + urb->complete=epcam_video_irq; + urb->number_of_packets=PACKETBUFS; + urb->transfer_buffer_length=epcam->packetsize*PACKETBUFS; + for (fx=0; fxiso_frame_desc[fx].offset=epcam->packetsize*fx; + urb->iso_frame_desc[fx].length=epcam->packetsize; + } + epcam->urb[i]=urb; + } + for (i=0; iurb[i]->next=epcam->urb[i+1]; + } + epcam->urb[EPCAM_NUMSBUF-1]->next=epcam->urb[0]; + for (i=0; iurb[i]); + if(err) + err("urb burned down"); + } +info("urbs flying!"); +// epcam->framecount=0; + + return 0; +} + +static int epcam_stop_stream(struct usb_epcam *epcam) +{ + int i; + + if (!epcam->streaming || !epcam->dev) + return 1; + + epcam->streaming=0; + + for (i=0; iurb[i]) { + epcam->urb[i]->next=NULL; + usb_unlink_urb(epcam->urb[i]); + usb_free_urb(epcam->urb[i]); + epcam->urb[i]=NULL; + kfree(epcam->sbuf[i].data); + } + for (i=0; iscratch[i].data); + epcam->scratch[i].data=NULL; + } +info("shot down urbs"); + epcam_sndctrl(1, epcam, VENDOR_REQ_CONT_CAPTURE, 0, NULL, 0); +info("capture off"); + epcam_sndctrl(1, epcam, VENDOR_REQ_LED_CONTROL, 0, NULL, 0); + epcam_sndctrl(1, epcam, VENDOR_REQ_CAM_POWER, 0, NULL, 0); +info("led and power off"); + + return 0; +} + +static int epcam_set_size(struct usb_epcam *epcam, int width, int height) +{ + int wasstreaming=epcam->streaming; + + /* Check to see if we need to change */ + if (epcam->cwidth==width && epcam->cheight==height) + return 0; + + /* Check for a valid mode */ + if (!width || !height) + return 1; + if ((width & 1) || (height & 1)) + return 1; + if (width>epcam->maxwidth) + return 1; + if (height>epcam->maxheight) + return 1; + + /* Stop a current stream and start it again at the new size */ + if (wasstreaming) + epcam_stop_stream(epcam); + epcam->cwidth=width; + epcam->cheight=height; + if (wasstreaming) + epcam_start_stream(epcam); + + return 0; +} + + +/**************************************************************************** + * + * Video Decoding + * + ***************************************************************************/ + +/* + This shouldn't really be done in a v4l driver.... + But it does make the image look a lot more usable. + Basicly it lifts the dark pixels more than the light pixels. +*/ +static inline void enhance_picture(unsigned char *frame, int len) +{ + while (len--) { + *frame++=(((*frame^255)*(*frame^255))/255)^255; + } +} + +static inline void decode_bayer (struct usb_epcam *epcam, unsigned char *data, int len) +{ + int datasize=epcam->cwidth*epcam->cheight; + struct epcam_frame *frame=&epcam->frame[epcam->curframe]; + + unsigned char *framedata=frame->data, *curline, *nextline; + int width=epcam->cwidth; + int blineoffset=0, bline; + int linelength=width*3, i; + + +// info("curpix: %d", frame->curpix); + /* Check if we have to much data */ + if (frame->curpix+len > datasize) { + info("to much! %d %d %d %d", datasize, frame->curpix, len, frame->curpix+len-datasize); +// frame->curpix=0; +// return; + len=datasize-frame->curpix; + } + + if (frame->curpix==0) { + if (frame->grabstate==FRAME_READY) { + frame->grabstate=FRAME_GRABBING; + } + /* Clear stuff the user might have put on this line */ + memset(framedata+datasize*3-linelength, 0, linelength); + frame->curline=framedata+linelength; + frame->curlinepix=0; + } + + if (epcam->cheight%4) + blineoffset=1; + bline=frame->curpix/epcam->cwidth+blineoffset; + + curline=frame->curline; + nextline=curline+linelength; + if (nextline >= framedata+datasize*3) + nextline=curline; + while (len) { + if (frame->curlinepix>=width) { + frame->curlinepix-=width; + bline=frame->curpix/width+blineoffset; + curline+=linelength*2; + nextline+=linelength*2; + if (curline >= framedata+datasize*3) { + frame->curlinepix++; + curline-=3; + nextline-=3; + len--; + data++; + frame->curpix++; + } + if (nextline >= framedata+datasize*3) + nextline=curline; + } + if ((bline&1)) { + if ((frame->curlinepix&1)) { + *(curline+2)=*data; + *(curline-1)=*data; + *(nextline+2)=*data; + *(nextline-1)=*data; + } else { + *(curline+1)= + (*(curline+1)+*data)/2; + *(curline-2)= + (*(curline-2)+*data)/2; + *(nextline+1)=*data; + *(nextline-2)=*data; + } + } else { + if ((frame->curlinepix&1)) { + *(curline+1)= + (*(curline+1)+*data)/2; + *(curline-2)= + (*(curline-2)+*data)/2; + *(nextline+1)=*data; + *(nextline-2)=*data; + } else { + *curline=*data; + *(curline-3)=*data; + *nextline=*data; + *(nextline-3)=*data; + } + } +// if (epcam->camid==0x402) { +// if (frame->curlinepix==1) +// if ((frame->curpix/width)%10==9) { +// frame->curlinepix+=10; +// data+=10; +// curline-=30; +// nextline-=30; +// len-=10; +// frame->curpix+=10; +// } +// } + frame->curlinepix++; + curline-=3; + nextline-=3; + len--; + data++; + frame->curpix++; + } + frame->curline=curline; + + if (frame->curpix>=datasize-epcam->packetsize) { +// info("%d %d", frame->curpix, datasize-frame->curpix); + /* Fix the top line */ + framedata+=linelength; + for (i=0; icheight; i++) { + *framedata=*(framedata+3); + *(framedata+1)=*(framedata+4); + *(framedata+2)=*(framedata+5); + framedata+=linelength; + } + frame->curpix=0; + frame->grabstate=FRAME_DONE; + epcam->framecount++; + epcam->readcount++; + if (epcam->frame[(epcam->curframe+1)&(EPCAM_NUMFRAMES-1)].grabstate==FRAME_READY) { + epcam->curframe=(epcam->curframe+1) & (EPCAM_NUMFRAMES-1); + } + } +} + +static inline void decode_eplite_integrate(struct usb_epcam *epcam, int data) +{ + int linelength=epcam->cwidth; + + /* First two are absolute, all others relative. + */ + if (epcam->eplite_curpix < 2) { + *(epcam->eplite_data+epcam->eplite_curpix)=1+data*4; + } else { + *(epcam->eplite_data+epcam->eplite_curpix)= + *(epcam->eplite_data+epcam->eplite_curpix-2)+data*4; + } + + epcam->eplite_curpix++; + + if (epcam->eplite_curpix>=linelength) { + decode_bayer(epcam, epcam->eplite_data, linelength); + + epcam->eplite_curpix=0; + epcam->eplite_curline+=linelength; + if (epcam->eplite_curline>=epcam->cheight*linelength) + epcam->eplite_curline=0; + } +} + +static inline void decode_eplite (struct usb_epcam *epcam, struct epcam_scratch *buffer) +{ + int i; + + unsigned char *data=buffer->data; + int len=buffer->length; + struct epcam_frame *frame=&epcam->frame[epcam->curframe]; + + int pos=0; + int vlc_cod; + int vlc_size; + int vlc_data; + int bit_cur; + + int bit; + + /* Check for cancelled frames: */ + for (i=0; icancel++; + frame->curpix=0; + return; + } + } + +// /* Check if we lost packets in the que */ +// if (buffer->offset && buffer->offset!=epcam->lastoffset+1) { +// epcam->underrun++; +// epcam->lastoffset=-1; +// return; +// } else { +// epcam->lastoffset=buffer->offset; +// } + + /* New image? */ + if (!buffer->offset) { + frame->curpix=0; + } + if (!frame->curpix) { + epcam->eplite_curline=0; + epcam->eplite_curpix=0; + epcam->vlc_cod=0; + epcam->vlc_data=0; + epcam->vlc_size=0; + if (frame->grabstate==FRAME_READY) + frame->grabstate=FRAME_GRABBING; + } + + vlc_cod=epcam->vlc_cod; + vlc_size=epcam->vlc_size; + vlc_data=epcam->vlc_data; + + while (pos < len && frame->grabstate==FRAME_GRABBING) { + bit_cur=8; + while (bit_cur) { + bit=((*data)>>(bit_cur-1))&1; + if (!vlc_cod) { + if (bit) { + vlc_size++; + } else { + if (!vlc_size) { + decode_eplite_integrate(epcam, 0); + } else { + vlc_cod=2; + vlc_data=0; + } + } + } else { + if (vlc_size > 7) { + epcam->datacorrupt++; + epcam->lastoffset=-1; + return; + } + if (vlc_cod==2) { + if (!bit) vlc_data=-(1<grabstate==FRAME_GRABBING) { + epcam->vlc_size=vlc_size; + epcam->vlc_cod=vlc_cod; + epcam->vlc_data=vlc_data; + } else { + /* If there is data left regard image as corrupt */ + if (len-pos > 512) { + info("len-pos: %d\n", len-pos); + frame->grabstate=FRAME_GRABBING; + epcam->datacorrupt++; + } + epcam->lastoffset=-1; + } + +} + +static int epcam_newframe(struct usb_epcam *epcam, int framenr) +{ + DECLARE_WAITQUEUE(wait, current); + int errors=0; + int process; + + while (epcam->streaming && + (epcam->frame[framenr].grabstate==FRAME_READY || + epcam->frame[framenr].grabstate==FRAME_GRABBING) ) { + if(!epcam->frame[framenr].curpix) { + errors++; + } + wait_interruptible( + epcam->scratch[epcam->scratch_use].state!=BUFFER_READY, + &epcam->wq, + &wait + ); + if (epcam->nullpackets > EPCAM_MAX_NULLPACKETS) { + epcam->nullpackets=0; + info("to many null length packets, restarting capture"); + epcam_stop_stream(epcam); + epcam_start_stream(epcam); + } else { + struct epcam_scratch *buffer=&epcam->scratch[epcam->scratch_use]; + unsigned char *data=epcam->scratch[epcam->scratch_use].data; + int len=epcam->scratch[epcam->scratch_use].length; + struct epcam_frame *frame=&epcam->frame[epcam->curframe]; + int i; + + if (epcam->scratch[epcam->scratch_use].state!=BUFFER_READY) { + epcam->frame[framenr].grabstate=FRAME_ERROR; + return -EIO; + } + epcam->scratch[epcam->scratch_use].state=BUFFER_BUSY; + + process=1; +// /* Check for cancelled frames: */ +// for (i=0; icancel++; +// frame->curpix=0; +// process=0; +// } +// } + /* Check if we lost packets in the que */ + if (buffer->offset && buffer->offset!=epcam->lastoffset+1) { +// info("lost in queue: %d %d", epcam->lastoffset, buffer->offset); + epcam->underrun++; + epcam->lastoffset=-1; + process=0; + } else { + epcam->lastoffset=buffer->offset; + } + + /* New image? */ + if (!buffer->offset) { + frame->curpix=0; + } + + if (process) { +// info("%d %d %d %d", buffer->offset, frame->curpix, frame->curlinepix, len); + if (epcam->format==FMT_EPLITE) { +// info("eplite"); + decode_eplite(epcam, &epcam->scratch[epcam->scratch_use]); +// if (epcam->lastoffset==-1) +// info("lastoffset: -1"); + } else { +// info("bayer"); + decode_bayer(epcam, data, len); + } + } + + epcam->scratch[epcam->scratch_use].state=BUFFER_UNUSED; + epcam->scratch_use++; + if (epcam->scratch_use>=EPCAM_NUMSCRATCH) + epcam->scratch_use=0; + if (errors > EPCAM_MAX_ERRORS) { + errors=0; + info("to much errors, restarting capture"); + epcam_stop_stream(epcam); + epcam_start_stream(epcam); + } + } + } + + if (epcam->frame[framenr].grabstate==FRAME_DONE) + if (epcam->enhance) + enhance_picture(epcam->frame[framenr].data, epcam->cheight*epcam->cwidth*3); + return 0; +} + + +/**************************************************************************** + * + * Video4Linux + * + ***************************************************************************/ + + +static int epcam_open(struct video_device *dev, int flags) +{ + struct usb_epcam *epcam = (struct usb_epcam *)dev; + int err = 0; +info("opening"); + MOD_INC_USE_COUNT; + down(&epcam->lock); + + epcam->fbuf=rvmalloc(epcam->maxframesize * EPCAM_NUMFRAMES); + if(!epcam->fbuf) err=-ENOMEM; + + if (err) { + MOD_DEC_USE_COUNT; + up(&epcam->lock); + return err; + } + + epcam->user=1; + + up(&epcam->lock); +info("opened"); + return 0; +} + +static void epcam_close(struct video_device *dev) +{ + struct usb_epcam *epcam = (struct usb_epcam *)dev; + int i; +info("closing"); + down(&epcam->lock); + + for (i=0; iframe[i].grabstate=FRAME_UNUSED; + if (epcam->streaming) + epcam_stop_stream(epcam); + + rvfree(epcam->fbuf, epcam->maxframesize * EPCAM_NUMFRAMES); + epcam->user=0; + up(&epcam->lock); + + if (!epcam->dev) { + video_unregister_device(&epcam->vdev); + kfree(epcam); + epcam = NULL; + info("device unregistered"); + } +info("closed"); + MOD_DEC_USE_COUNT; +} + +static int epcam_init_done(struct video_device *dev) +{ +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + create_proc_epcam_cam((struct usb_epcam *)dev); +#endif + + return 0; +} + +static long epcam_write(struct video_device *dev, const char *buf, unsigned long + count, int noblock) +{ + return -EINVAL; +} + +static int epcam_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + struct usb_epcam *epcam = (struct usb_epcam *)vdev; + + if (!epcam->dev) + return -EIO; + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, epcam->camera_name); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = epcam->maxwidth; + b.maxheight = epcam->maxheight; + b.minwidth = 2;//FIXME!!!!! + b.minheight = 2;//FIXME!!!!! + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.channel != 0) + return -EINVAL; + + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + strcpy(v.name, "Camera"); + + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v != 0) + return -EINVAL; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p; + + epcam_get_pict(epcam, &p); + + if (copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + + if (epcam_set_pict(epcam, &p)) + return -EINVAL; + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (epcam_set_size(epcam, vw.width, vw.height)) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; + vw.y = 0; + vw.chromakey = 0; + vw.flags = 0; + vw.clipcount = 0; + vw.width = epcam->cwidth; + vw.height = epcam->cheight; + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + memset(&vm, 0, sizeof(vm)); + vm.size = EPCAM_NUMFRAMES * epcam->maxframesize; + vm.frames = EPCAM_NUMFRAMES; + for (i=0; imaxframesize * i; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user(&vm, arg, sizeof(vm))) + return -EFAULT; + + if (vm.format != VIDEO_PALETTE_RGB24) + return -EINVAL; + if (vm.frame < 0 || vm.frame >= EPCAM_NUMFRAMES) + return -EINVAL; + if (epcam->frame[vm.frame].grabstate != FRAME_UNUSED) + return -EBUSY; + + if (epcam_set_size(epcam, vm.width, vm.height)) + return -EINVAL; + epcam->frame[vm.frame].grabstate=FRAME_READY; + + if (!epcam->streaming) + epcam_start_stream(epcam); + + /* Calibrate the reset level after a few frames. */ + if (epcam->framecount%8==1) + epcam_auto_resetlevel(epcam); + + return 0; + } + case VIDIOCSYNC: + { + int frame, ret=0; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + ret=epcam_newframe(epcam, frame); + epcam->frame[frame].grabstate=FRAME_UNUSED; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static long epcam_read(struct video_device *dev, char *buf, unsigned long count, + int noblock) +{ + int realcount=count, ret=0; + struct usb_epcam *epcam = (struct usb_epcam *)dev; + + + if (epcam->dev == NULL) + return -EIO; + if (realcount > epcam->cwidth*epcam->cheight*3) + realcount=epcam->cwidth*epcam->cheight*3; + + /* Shouldn't happen: */ + if (epcam->frame[0].grabstate==FRAME_GRABBING) + return -EBUSY; + epcam->frame[0].grabstate=FRAME_READY; + epcam->frame[1].grabstate=FRAME_UNUSED; + epcam->curframe=0; + + if (!epcam->streaming) + epcam_start_stream(epcam); + +// /* Set the picture properties */ +// if (epcam->framecount==0) +// epcam_send_pict(epcam); + /* Calibrate the reset level after a few frames. */ + if (epcam->framecount%8==1) + epcam_auto_resetlevel(epcam); + + ret=epcam_newframe(epcam, 0); + + if (!ret) { + copy_to_user(buf, epcam->frame[0].data, realcount); + } else { + realcount=ret; + } + epcam->frame[0].grabstate=FRAME_UNUSED; + + return realcount; +} + +static int epcam_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + struct usb_epcam *epcam = (struct usb_epcam *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + down(&epcam->lock); + + if (epcam->dev == NULL) { + up(&epcam->lock); + return -EIO; + } + if (size > (((EPCAM_NUMFRAMES * epcam->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + up(&epcam->lock); + return -EINVAL; + } + pos = (unsigned long)epcam->fbuf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { + up(&epcam->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + up(&epcam->lock); + + return 0; +} + +static struct video_device epcam_template = { + name: "EPCAM USB camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_SE401, + open: epcam_open, + close: epcam_close, + read: epcam_read, + write: epcam_write, + ioctl: epcam_ioctl, + mmap: epcam_mmap, + initialize: epcam_init_done, +}; + + + +/***************************/ +static int epcam_init(struct usb_epcam *epcam) +{ + int i=0, rc; + unsigned char cp[0x80]; + /* led on */ + epcam_sndctrl(1, epcam, VENDOR_REQ_LED_CONTROL, 1, NULL, 0); + + /* get camera descriptor */ + memset(cp, 0, 0x80); + rc=epcam_sndctrl(0, epcam, VENDOR_REQ_CAMERA_INFO, 0, cp, sizeof(cp)); + info("vendor_req_camera_info: %d", rc); + if (rc<0) { + info("Error reading camera descriptor"); + return 1; + } + info("size :%d %x %x", cp[1]*256+cp[0], cp[0], cp[1]); + info("rev :%d %x %x", cp[5]*256+cp[4], cp[4], cp[5]); + info("maxwidth :%d %x %x", cp[7]*256+cp[6], cp[6], cp[7]); + info("maxheight:%d %x %x", cp[9]*256+cp[8], cp[8], cp[9]); + info("zoomcaps :%d %x %x", cp[11]*256+cp[10], cp[10], cp[11]); + info("ISPCaps :%d %x %x", cp[13]*256+cp[12], cp[12], cp[13]); + info("Formats :%d %x %x", cp[15]*256+cp[14], cp[14], cp[15]); + for (i=0; imaxwidth=cp[7]*256+cp[6]; + epcam->maxheight=cp[9]*256+cp[8]; + epcam->camid=cp[3]*256+cp[2]; + + info("camid: %x", epcam->camid); + if (epcam->camid!=0x800 && + epcam->camid!=0x402 ) { + err("Not a supported camid: %d!", epcam->camid); + return 1; + } + + epcam->cwidth=epcam->maxwidth/2; + epcam->cheight=epcam->maxheight/2; + epcam->maxframesize=epcam->maxwidth*epcam->maxheight*3; + +// rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); +// epcam->brightness=cp[0]+cp[1]*256; + /* some default values */ + epcam->brightness=32768; + epcam->resetlevel=0x2d; +// epcam_set_exposure(epcam, 20000); + epcam->palette=VIDEO_PALETTE_RGB24; + epcam->enhance=1; + epcam->dropped=0; + epcam->framecount=0; + epcam->readcount=0; + epcam_recv_pict(epcam); + + /* Start interrupt transfers for snapshot button */ + epcam->inturb=usb_alloc_urb(0); + if (!epcam->inturb) { + info("Allocation of inturb failed"); + return 1; + } + FILL_INT_URB(epcam->inturb, epcam->dev, + usb_rcvintpipe(epcam->dev, EPCAM_BUTTON_ENDPOINT), + &epcam->button, sizeof(epcam->button), + epcam_button_irq, + epcam, + HZ/10 + ); + if (usb_submit_urb(epcam->inturb)) { + info("int urb burned down"); + return 1; + } + + /* Flash the led */ + epcam_sndctrl(1, epcam, VENDOR_REQ_CAM_POWER, 1, NULL, 0); + epcam_sndctrl(1, epcam, VENDOR_REQ_LED_CONTROL, 1, NULL, 0); + epcam_sndctrl(1, epcam, VENDOR_REQ_CAM_POWER, 0, NULL, 0); + epcam_sndctrl(1, epcam, VENDOR_REQ_LED_CONTROL, 0, NULL, 0); + + + return 0; +} + +static void* __devinit epcam_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct usb_interface_descriptor *interface; + struct usb_epcam *epcam; + char *camera_name=NULL; + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it an epcam? */ + if (dev->descriptor.idVendor == 0x03e8 && + dev->descriptor.idProduct == 0x1000) { + camera_name="Endpoints SE401"; + } else if (dev->descriptor.idVendor == 0x03e8 && + dev->descriptor.idProduct == 0x1005) { + camera_name="Endpoints EP800"; + } else if (dev->descriptor.idVendor == 0x03e8 && + dev->descriptor.idProduct == 0x1003 && + interface->bInterfaceNumber == 0 ) { /* This is a dualmode camera */ + camera_name="Endpoints SE402"; + } else if (dev->descriptor.idVendor == 0x03e8 && + dev->descriptor.idProduct == 0x2040 && + interface->bInterfaceNumber == 0 ) { + camera_name="Rimax Slim Multicam"; + } else if (dev->descriptor.idVendor == 0x03e8 && + dev->descriptor.idProduct == 0x2112 && + interface->bInterfaceNumber == 0 ) { + camera_name="SpyPen Actor"; + } else if (dev->descriptor.idVendor == 0x041e && + dev->descriptor.idProduct == 0x400d && + interface->bInterfaceNumber == 0 ) { + camera_name="Creative PD1001"; + } else if (dev->descriptor.idVendor == 0x04f2 && + dev->descriptor.idProduct == 0xa001 && + interface->bInterfaceNumber == 0 ) { + camera_name="Chicony DC-100"; + } else + return NULL; + + /* We found one */ + info("epcam camera found: %s", camera_name); + + if ((epcam = kmalloc(sizeof(*epcam), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc epcam struct"); + return NULL; + } + + memset(epcam, 0, sizeof(*epcam)); + + epcam->dev = dev; + epcam->iface = interface->bInterfaceNumber; + epcam->camera_name = camera_name; + info("firmware version: %02x", dev->descriptor.bcdDevice & 255); + + if (epcam_init(epcam)) + return NULL; + memcpy(&epcam->vdev, &epcam_template, sizeof(epcam_template)); + memcpy(epcam->vdev.name, epcam->camera_name, strlen(epcam->camera_name)); + if (video_register_device(&epcam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + err("video_register_device failed"); + return NULL; + } + info("registered new video device: video%d", epcam->vdev.minor); + + init_waitqueue_head(&epcam->wq); + init_MUTEX(&epcam->lock); + + return epcam; +} + +static void epcam_disconnect(struct usb_device *dev, void *ptr) +{ + int i; + struct usb_epcam *epcam = (struct usb_epcam *) ptr; + + /* We don't want people trying to open up the device */ + if (!epcam->user) + video_unregister_device(&epcam->vdev); + + usb_driver_release_interface(&epcam_driver, + &epcam->dev->actconfig->interface[epcam->iface]); + + epcam->dev = NULL; + epcam->frame[0].grabstate = FRAME_ERROR; + epcam->frame[1].grabstate = FRAME_ERROR; + + epcam->streaming = 0; + + if (waitqueue_active(&epcam->wq)) + wake_up_interruptible(&epcam->wq); + + for (i=0; iurb[i]) { + epcam->urb[i]->next = NULL; + usb_unlink_urb(epcam->urb[i]); + usb_free_urb(epcam->urb[i]); + epcam->urb[i] = NULL; + kfree(epcam->sbuf[i].data); + } + for (i=0; iscratch[i].data) { + kfree(epcam->scratch[i].data); + } + if (epcam->inturb) { + usb_unlink_urb(epcam->inturb); + usb_free_urb(epcam->inturb); + } + + info("%s disconnected", epcam->camera_name); + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + destroy_proc_epcam_cam(epcam); +#endif + + /* Free the memory */ + if (!epcam->user) { + kfree(epcam); + epcam = NULL; + } +} + + +static struct usb_driver epcam_driver = { + name: "epcam", + id_table: device_table, + probe: epcam_probe, + disconnect: epcam_disconnect +}; + + + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +static int __init usb_epcam_init(void) +{ +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + proc_epcam_create(); +#endif + + info("EPcam usb camera driver version %s registering", version); + if (flickerless) + if (flickerless!=50 && flickerless!=60) { + info("Invallid flickerless value, use 0, 50 or 60."); + return -1; + } + if (usb_register(&epcam_driver) < 0) + return -1; + return 0; +} + +static void __exit usb_epcam_exit(void) +{ + usb_deregister(&epcam_driver); + info("EPcam driver deregistered"); + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + proc_epcam_destroy(); +#endif +} + +module_init(usb_epcam_init); +module_exit(usb_epcam_exit); diff -ruN linux/drivers/usb.org/epcam.h linux/drivers/usb/epcam.h --- linux/drivers/usb.org/epcam.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/usb/epcam.h Tue Sep 3 23:55:56 2002 @@ -0,0 +1,232 @@ + +#ifndef __LINUX_epcam_H +#define __LINUX_epcam_H + +#include +#include +#include + +#define epcam_DEBUG /* Turn on debug messages */ + +#ifdef epcam_DEBUG +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +#else +# define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* An almost drop-in replacement for sleep_on_interruptible */ +#define wait_interruptible(test, queue, wait) \ +{ \ + add_wait_queue(queue, wait); \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (test) \ + schedule(); \ + remove_wait_queue(queue, wait); \ + set_current_state(TASK_RUNNING); \ + if (signal_pending(current)) \ + break; \ +} + +/* EPCAM controls: */ + +#define VENDOR_REQ_CAMERA_INFO 0x00 +#define VENDOR_REQ_CAPTURE_INFO 0x01 +#define VENDOR_REQ_COMPRESSION 0x02 +#define VENDOR_REQ_CONT_CAPTURE 0x03 +#define VENDOR_REQ_CAPTURE_FRAME 0x04 +#define VENDOR_REQ_IMAGE_INFO 0x05 +#define VENDOR_REQ_EXT_FEATURE 0x06 +#define VENDOR_REQ_CAM_POWER 0x07 +#define VENDOR_REQ_LED_CONTROL 0x08 +#define VENDOR_DEAD_PIXEL 0x09 +#define VENDOR_REQ_AUTO_CONTROL 0x0a +#define VENDOR_REQ_BIOS 0xff + +#define VENDOR_CMD_BIOS_READ 0x07 + +#define EPCAM_FORMAT_BAYER 1 + +/* Hyundai hv7131b registers + 7121 and 7141 should be the same (haven't really checked...) */ +/* Mode registers: */ +#define HV7131_REG_MODE_A 0x00 +#define HV7131_REG_MODE_B 0x01 +#define HV7131_REG_MODE_C 0x02 +/* Frame registers: */ +#define HV7131_REG_FRSU 0x10 +#define HV7131_REG_FRSL 0x11 +#define HV7131_REG_FCSU 0x12 +#define HV7131_REG_FCSL 0x13 +#define HV7131_REG_FWHU 0x14 +#define HV7131_REG_FWHL 0x15 +#define HV7131_REG_FWWU 0x16 +#define HV7131_REG_FWWL 0x17 +/* Timing registers: */ +#define HV7131_REG_THBU 0x20 +#define HV7131_REG_THBL 0x21 +#define HV7131_REG_TVBU 0x22 +#define HV7131_REG_TVBL 0x23 +#define HV7131_REG_TITU 0x25 +#define HV7131_REG_TITM 0x26 +#define HV7131_REG_TITL 0x27 +#define HV7131_REG_TMCD 0x28 +/* Adjust Registers: */ +#define HV7131_REG_ARLV 0x30 +#define HV7131_REG_ARCG 0x31 +#define HV7131_REG_AGCG 0x32 +#define HV7131_REG_ABCG 0x33 +#define HV7131_REG_APBV 0x34 +#define HV7131_REG_ASLP 0x54 +/* Offset Registers: */ +#define HV7131_REG_OFSR 0x50 +#define HV7131_REG_OFSG 0x51 +#define HV7131_REG_OFSB 0x52 +/* Reset level statistics registers: */ +#define HV7131_REG_LOREFNOH 0x57 +#define HV7131_REG_LOREFNOL 0x58 +#define HV7131_REG_HIREFNOH 0x59 +#define HV7131_REG_HIREFNOL 0x5a + +///* se401 registers */ +//#define SE401_OPERATINGMODE 0x2000 + + +/* size of usb transfers */ +#define EPCAM_PACKETBUF (8*1024) +/* number of iso urbs to use */ +#define EPCAM_NUMSBUF 2 +/* read the usb specs for this one :) */ +#define EPCAM_VIDEO_ENDPOINT 1 +#define EPCAM_BUTTON_ENDPOINT 2 +/* number of frames supported by the v4l part */ +#define EPCAM_NUMFRAMES 2 +/* scratch buffers for passing data to the decoders */ +#define EPCAM_NUMSCRATCH 64 +/* maximum amount of data in a JangGu packet */ +#define EPCAM_VLCDATALEN 1024 +/* number of nul sized packets to receive before kicking the camera */ +#define EPCAM_MAX_NULLPACKETS 4000 +/* number of decoding errors before kicking the camera */ +#define EPCAM_MAX_ERRORS 200 + + +struct usb_device; + +struct epcam_sbuf { + unsigned char *data; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +enum { + FMT_BAYER, + FMT_JANGGU, + FMT_EPLITE, +}; + +enum { + BUFFER_UNUSED, + BUFFER_READY, + BUFFER_BUSY, + BUFFER_DONE, +}; + +struct epcam_scratch { + unsigned char *data; + volatile int state; + int offset; + int length; +}; + +struct epcam_frame { + unsigned char *data; /* Frame buffer */ + + volatile int grabstate; /* State of grabbing */ + + unsigned char *curline; + int curlinepix; + int curpix; +}; + +struct usb_epcam { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + + char *camera_name; + unsigned int camid; + + int brightness; + int resetlevel; + + int enhance; + + int format; + int maxwidth; /* max width */ + int maxheight; /* max height */ + int cwidth; /* current width */ + int cheight; /* current height */ + int palette; + int maxframesize; + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int streaming; /* Are we streaming video? */ + + char *fbuf; /* Videodev buffer area */ + + int packetsize; + struct urb *urb[EPCAM_NUMSBUF]; + struct urb *inturb; + + int button; + int buttonpressed; + + int curframe; /* Current receiving frame */ + struct epcam_frame frame[EPCAM_NUMFRAMES]; + int readcount; + int framecount; + + int cancel; + int dropped; + int underrun; + int datacorrupt; + + int scratch_next; + int scratch_use; + int scratch_overflow; + struct epcam_scratch scratch[EPCAM_NUMSCRATCH]; + int scratch_offset; + + /* Decoder specific data: */ + int lastoffset; + int eplite_curpix; + int eplite_curline; + unsigned char eplite_data[1024]; + int vlc_size; + int vlc_cod; + int vlc_data; + + struct epcam_sbuf sbuf[EPCAM_NUMSBUF]; + + wait_queue_head_t wq; /* Processes waiting */ + + /* proc interface */ + struct proc_dir_entry *proc_entry; /* /proc/epcam/videoX */ + + int nullpackets; +}; + + +#endif Binary files linux/drivers/usb.org/epcam.o and linux/drivers/usb/epcam.o differ