ChangeSet 1.775, 2002/12/05 14:19:21-08:00, joe@wavicle.org [PATCH] Vicam patch against 2.4.20-pre9 Hi Greg, I'm not sure what the correct way to submit these patches is, so I'm going to send you this one, feel free to reject it because it's in the wrong format, you want a patch against 2.5.x first or you want a patch against 2.4.20 instead. -Joe diff -urN linux-2.4.19/drivers/usb/Makefile linux-2.4.19-vicam/drivers/usb/Makefile diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Thu Dec 5 14:48:55 2002 +++ b/drivers/usb/Makefile Thu Dec 5 14:48:55 2002 @@ -77,7 +77,7 @@ obj-$(CONFIG_USB_MDC800) += mdc800.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_DABUSB) += dabusb.o -obj-$(CONFIG_USB_VICAM) += vicam.o +obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o diff -Nru a/drivers/usb/vicam.c b/drivers/usb/vicam.c --- a/drivers/usb/vicam.c Thu Dec 5 14:48:55 2002 +++ b/drivers/usb/vicam.c Thu Dec 5 14:48:55 2002 @@ -1,649 +1,1022 @@ -/* -*- linux-c -*- - * USB ViCAM driver - * - * Copyright (c) 2001 Christopher L Cheney (ccheney@cheney.cx) - * Copyright (c) 2001 Pavel Machek (pavel@suse.cz) sponsored by SuSE - * - * 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 driver is for the Vista Imaging ViCAM and 3Com HomeConnect USB - * - * Thanks to Greg Kroah-Hartman for the USB Skeleton driver - * - * TODO: - * - find out the ids for the Vista Imaging ViCAM +/* + * USB ViCam WebCam driver + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org) * - * History: + * Supports 3COM HomeConnect PC Digital WebCam * - * 2001_07_07 - 0.1 - christopher: first version - * 2001_08_28 - 0.2 - pavel: messed it up, but for some fun, try - while true; do dd if=/dev/video of=/dev/fb0 bs=$[0x1e480] count=1 2> /dev/null; done - yep, moving pictures. - * 2001_08_29 - 0.3 - pavel: played a little bit more. Experimental mmap support. For some fun, - get gqcam-0.9, compile it and run. Better than dd ;-). - * 2001_08_29 - 0.4 - pavel: added shutter speed control (not much functional) - kill update_params if it does not seem to work for you. - * 2001_08_30 - 0.5 - pavel: fixed stupid bug with update_params & vicam_bulk - + * 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. * - * FIXME: It crashes on rmmod with camera plugged. - */ -#define DEBUG 1 + * This source code is based heavily on the CPiA webcam driver + * */ -#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include +#include +#include #include - -#include -#include #include +#include +#include +#include "usbvideo.h" -#include +// #define VICAM_DEBUG + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(a) +#endif + +#ifndef bool +#define bool int +#endif -#include "vicam.h" -#include "vicamurbs.h" +#ifdef VICAM_DEBUG +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif /* Version Information */ -#define DRIVER_VERSION "v0" -#define DRIVER_AUTHOR "Christopher L Cheney , Pavel Machek " -#define DRIVER_DESC "USB ViCAM Driver" +#define DRIVER_VERSION "v1.0" +#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" +#define DRIVER_DESC "ViCam WebCam Driver" /* Define these values to match your device */ -#define USB_VICAM_VENDOR_ID 0x04C1 -#define USB_VICAM_PRODUCT_ID 0x009D +#define USB_VICAM_VENDOR_ID 0x04c1 +#define USB_VICAM_PRODUCT_ID 0x009d -/* table of devices that work with this driver */ -static struct usb_device_id vicam_table [] = { - { USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID) }, - { } /* Terminating entry */ +#define VICAM_BYTES_PER_PIXEL 3 +#define VICAM_MAX_READ_SIZE (512*242+128) +#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) +#define VICAM_FRAMES 2 + +/* Not sure what all the bytes in these char + * arrays do, but they're necessary to make + * the camera work. + */ + +static unsigned char setup1[] = { + 0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64, 0xE7, 0x67, + 0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09, 0xDE, 0x00, + 0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03, 0xC0, 0x17, + 0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07, 0x00, 0x00, + 0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00 }; -MODULE_DEVICE_TABLE (usb, vicam_table); +static unsigned char setup2[] = { + 0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64, 0x18, 0x00, + 0x00, 0x00 +}; -static int video_nr = -1; /* next avail video device */ -static struct usb_driver vicam_driver; +static unsigned char setup3[] = { + 0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 +}; -static char *buf, *buf2; -static volatile int change_pending = 0; +static unsigned char setup4[] = { + 0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64, 0xE7, 0x07, + 0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07, 0x00, 0x00, + 0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01, 0xAA, 0x00, + 0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00, 0xE7, 0x07, + 0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07, 0x7C, 0x00, + 0x16, 0x00, 0xE7, 0x07, 0x56, 0x00, 0x18, 0x00, + 0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0, 0xE7, 0x07, + 0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF, + 0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00, 0x24, 0xC0, + 0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0, 0xE7, 0x07, + 0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87, 0x01, 0x00, + 0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09, 0x00, 0xC0, + 0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0, 0x09, 0xC1, + 0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01, 0xE7, 0x09, + 0x04, 0x06, 0x26, 0x01, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0, 0xC0, 0xDF, + 0x97, 0xCF, 0x17, 0x00, 0x57, 0x00, 0x17, 0x02, + 0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00, + 0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57, 0xFF, 0xFF, + 0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57, 0x00, 0x00, + 0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF, 0xC6, 0x00, + 0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05, 0xC1, 0x05, + 0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF, 0x27, 0xDA, + 0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x0B, 0x06, + 0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01, 0x9F, 0xAF, + 0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09, 0xFC, 0x05, + 0x24, 0x01, 0xE7, 0x09, 0x02, 0x06, 0x26, 0x01, + 0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0, 0xE7, 0x09, + 0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09, 0x02, 0x06, + 0x04, 0x06, 0xE7, 0x09, 0x00, 0x06, 0xFC, 0x05, + 0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06, 0x27, 0xDA, + 0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00, 0xFA, 0x05, + 0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05, 0x9F, 0xAF, + 0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17, 0x02, 0x00, + 0xEF, 0x57, 0x81, 0x00, 0x09, 0x06, 0x9F, 0xA0, + 0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57, 0x01, 0x00, + 0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03, 0xE7, 0x07, + 0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF, 0x47, 0xAF, + 0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07, 0x2E, 0x00, + 0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00, 0xC0, 0x57, + 0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02, 0xC0, 0x57, + 0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57, 0x55, 0x00, + 0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00, 0x9F, 0xC0, + 0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02, 0xC1, 0x0B, + 0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90, 0x08, 0x06, + 0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06, + 0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF, 0x2F, 0x0E, + 0x02, 0x00, 0x08, 0x06, 0xC0, 0x07, 0x08, 0x00, + 0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05, + 0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E, 0x02, 0x00, + 0x09, 0x06, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67, 0x7F, 0xFF, + 0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD, 0x22, 0xC0, + 0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, + 0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF, 0xB8, 0x05, + 0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0, 0x9F, 0xAF, + 0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00, 0x24, 0xC0, + 0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0, 0xC8, 0x07, + 0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC1, 0x07, + 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0x9F, 0xAF, + 0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00, 0x9F, 0xAF, + 0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02, 0x0F, 0xC1, + 0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x08, 0x00, + 0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1, 0xEF, 0x07, + 0x80, 0x00, 0x09, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07, 0x00, 0x00, + 0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF, 0x09, 0x06, + 0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xE7, 0x67, + 0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67, 0x17, 0xD8, + 0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0, + 0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0, 0x97, 0xCF, + 0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF, 0xDA, 0x02, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0x0E, 0x06, + 0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C, 0x02, 0x00, + 0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00, 0x06, 0x06, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0xE2, 0x05, + 0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF, 0x66, 0x04, + 0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF, 0x97, 0xCF, + 0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B, 0x0C, 0x06, + 0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0x68, 0x00, + 0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF, 0x44, 0x05, + 0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00, 0xE0, 0x07, + 0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06, 0xE8, 0x07, + 0x00, 0x00, 0xE0, 0x07, 0x00, 0x02, 0xE0, 0x07, + 0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF, 0x97, 0xCF, + 0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07, 0x0E, 0x06, + 0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06, 0xFE, 0x05, + 0xE7, 0x07, 0x40, 0x00, 0x26, 0x01, 0xE7, 0x07, + 0x40, 0x00, 0x04, 0x06, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07, 0x02, 0x00, + 0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01, 0xEF, 0x77, + 0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0, 0x14, 0x04, + 0xEF, 0x77, 0x01, 0x00, 0x07, 0x06, 0x37, 0xC0, + 0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06, 0x0F, 0xC1, + 0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06, 0xC0, 0x07, + 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, + 0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, + 0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x77, + 0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA, 0x75, 0xC1, + 0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1, 0xC0, 0x07, + 0x01, 0x00, 0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x06, 0x06, + 0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x06, 0x06, 0x22, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57, 0x01, 0x00, + 0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07, 0x01, 0x00, + 0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, 0xFF, 0x4F, + 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, 0x38, 0x00, + 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67, 0x03, 0x00, + 0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0, 0x08, 0xDA, + 0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, + 0x12, 0x00, 0xEF, 0x57, 0x00, 0x00, 0x06, 0x06, + 0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x14, 0x00, 0xC1, 0x0B, + 0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x09, + 0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8, 0xFA, 0x05, + 0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05, 0xE7, 0x07, + 0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05, 0x9F, 0xAF, + 0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B, 0xE2, 0x05, + 0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17, 0x23, 0x00, + 0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07, 0x04, 0x00, + 0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09, 0xE6, 0x05, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x07, 0x00, + 0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00, 0xC1, 0x09, + 0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05, 0xC1, 0xD1, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0D, 0x00, + 0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07, 0x32, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0F, 0x00, + 0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9, 0x24, 0xC0, + 0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00, 0xC0, 0x67, + 0x00, 0x02, 0x27, 0x80, 0x24, 0xC0, 0xE7, 0x87, + 0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xF9, + 0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA, 0x72, 0xC1, + 0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0, 0x97, 0xCF, + 0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87, 0xFF, 0x00, + 0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF, 0x24, 0xC0, + 0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0xE7, 0x87, + 0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, + 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x67, + 0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, + 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07, 0x40, 0x00, + 0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67, 0x00, 0xFF, + 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA, 0xE8, 0x09, + 0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, 0x00, 0xDA, + 0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1, 0xE7, 0x87, + 0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0x09, 0x02, + 0x19, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09, + 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; -static int vicam_parameters(struct usb_vicam *vicam); +static unsigned char setup5[] = { + 0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64, 0x0E, 0x00, + 0x14, 0x00, 0x1A, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00, 0x92, 0x00, + 0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00, 0xB8, 0x00, + 0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00, 0xCE, 0x00, + 0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00, 0xE0, 0x00, + 0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00, 0xEC, 0x00, + 0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01, 0x0A, 0x01, + 0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01, 0x22, 0x01, + 0x28, 0x01, 0x2C, 0x01, 0x32, 0x01, 0x36, 0x01, + 0x44, 0x01, 0x50, 0x01, 0x5E, 0x01, 0x72, 0x01, + 0x76, 0x01, 0x7A, 0x01, 0x80, 0x01, 0x88, 0x01, + 0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01, 0xA0, 0x01, + 0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01, 0xB4, 0x01, + 0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01, 0xF6, 0x01, + 0xFA, 0x01, 0x02, 0x02, 0x34, 0x02, 0x3C, 0x02, + 0x44, 0x02, 0x4A, 0x02, 0x50, 0x02, 0x56, 0x02, + 0x74, 0x02, 0x78, 0x02, 0x7E, 0x02, 0x84, 0x02, + 0x8A, 0x02, 0x88, 0x02, 0x90, 0x02, 0x8E, 0x02, + 0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02, 0xAE, 0x02, + 0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02, 0xC0, 0x02, + 0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02, 0xD4, 0x02, + 0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02, 0xF8, 0x02, + 0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03, 0x24, 0x03, + 0x28, 0x03, 0x30, 0x03, 0x2E, 0x03, 0x3C, 0x03, + 0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03, 0x58, 0x03, + 0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03, 0x7A, 0x03, + 0x86, 0x03, 0x8E, 0x03, 0x96, 0x03, 0xB2, 0x03, + 0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03, 0xD4, 0x03, + 0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03, 0xFC, 0x03, + 0x04, 0x04, 0x20, 0x04, 0x2A, 0x04, 0x32, 0x04, + 0x36, 0x04, 0x3E, 0x04, 0x44, 0x04, 0x42, 0x04, + 0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04, 0x54, 0x04, + 0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04, 0x62, 0x04, + 0x68, 0x04, 0x74, 0x04, 0x7C, 0x04, 0x80, 0x04, + 0x88, 0x04, 0x8C, 0x04, 0x94, 0x04, 0x9A, 0x04, + 0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04, 0xB4, 0x04, + 0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04, 0x2A, 0x05, + 0x46, 0x05, 0x6C, 0x05, 0x00, 0x00 +}; -/****************************************************************************** - * - * Memory management functions - * - * Taken from bttv-drivers.c 2.4.7-pre3 - * - ******************************************************************************/ +struct vicam_camera { + u16 shutter_speed; // capture shutter speed + u16 gain; // capture gain -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the area. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long kva, ret; + u8 *raw_image; // raw data captured from the camera + u8 *framebuf; // processed data in RGB24 format - kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - ret = __pa(kva); - return ret; -} + struct video_device vdev; // v4l video device + struct usb_device *udev; // usb device -static void * rvmalloc(unsigned long size) -{ - void * mem; - unsigned long adr; + struct semaphore busy_lock; // guard against SMP multithreading - size=PAGE_ALIGN(size); - mem=vmalloc_32(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - mem_map_reserve(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} + bool is_initialized; + u8 open_count; + u8 bulkEndpoint; + bool needsDummyRead; -static void rvfree(void * mem, unsigned long size) -{ - unsigned long adr; + u32 framebuf_size; // # of valid bytes in framebuf + u32 framebuf_read_start; // position in frame buf that a read is happening at. - if (mem) - { - adr=(unsigned long) mem; - while ((long) size > 0) - { - mem_map_unreserve(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } -} - -/****************************************************************************** - * - * Foo Bar - * - ******************************************************************************/ - -/** - * usb_vicam_debug_data - */ -static inline void usb_vicam_debug_data (const char *function, int size, const unsigned char *data) -{ - int i; - - if (!debug) - return; - - printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", - function, size); - for (i = 0; i < size; ++i) { - printk ("%.2x ", data[i]); - } - printk ("\n"); -} +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_entry; +#endif -/***************************************************************************** - * - * Send command to vicam - * - *****************************************************************************/ +}; -static int vicam_sndctrl(int set, struct usb_vicam *vicam, unsigned short req, - unsigned short value, unsigned char *cp, int size) -{ - int ret; - unsigned char *transfer_buffer = kmalloc (size, GFP_KERNEL); +static void *vicam_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); +static void vicam_disconnect(struct usb_device *dev, void *ptr); +static void read_frame(struct vicam_camera *cam, int framenum); + +static int +send_control_msg(struct usb_device *udev, u8 request, u16 value, u16 index, + unsigned char *cp, u16 size) +{ + int status; + + // for reasons not yet known to me, you can't send USB control messages + // with data in the module (if you are compiled as a module). Whatever + // the reason, copying it to memory allocated as kernel memory then + // doing the usb control message fixes the problem. - /* Needs to return data I think, works for sending though */ + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); memcpy(transfer_buffer, cp, size); - - ret = usb_control_msg ( vicam->udev, set ? usb_sndctrlpipe(vicam->udev, 0) : usb_rcvctrlpipe(vicam->udev, 0), req, (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, 0, transfer_buffer, size, HZ); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, HZ); kfree(transfer_buffer); - if (ret) - printk("vicam: error: %d\n", ret); - mdelay(100); - return ret; -} - -/***************************************************************************** - * - * Video4Linux Helpers - * - *****************************************************************************/ - -static int vicam_get_capability(struct usb_vicam *vicam, struct video_capability *b) -{ - dbg("vicam_get_capability"); - - strcpy(b->name, vicam->camera_name); - b->type = VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME; - b->channels = 1; - b->audios = 0; - - b->maxwidth = vicam->width[vicam->sizes-1]; - b->maxheight = vicam->height[vicam->sizes-1]; - b->minwidth = vicam->width[0]; - b->minheight = vicam->height[0]; + if (status < 0) { + printk(KERN_INFO "Failed sending control message, error %d.\n", + status); + } + + return status; +} + +static int +initialize_camera(struct vicam_camera *cam) +{ + struct usb_device *udev = cam->udev; + int status; + + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup1, sizeof (setup1))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup2, sizeof (setup2))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup3, sizeof (setup3))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup4, sizeof (setup4))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup5, sizeof (setup5))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup3, sizeof (setup3))) < 0) + return status; return 0; } - -static int vicam_get_channel(struct usb_vicam *vicam, struct video_channel *v) + +static int +set_camera_power(struct vicam_camera *cam, int state) { - dbg("vicam_get_channel"); + int status; - if (v->channel != 0) - return -EINVAL; - - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); + if ((status = send_control_msg(cam->udev, 0x50, state, 0, NULL, 0)) < 0) + return status; - return 0; -} - -static int vicam_set_channel(struct usb_vicam *vicam, struct video_channel *v) -{ - dbg("vicam_set_channel"); + if (state) { + send_control_msg(cam->udev, 0x55, 1, 0, NULL, 0); + } - if (v->channel != 0) - return -EINVAL; - return 0; } - -static int vicam_get_mmapbuffer(struct usb_vicam *vicam, struct video_mbuf *vm) -{ - int i; - dbg("vicam_get_mmapbuffer"); +static int +vicam_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) +{ + struct vicam_camera *cam = dev->priv; + int retval = 0; - memset(vm, 0, sizeof(vm)); - vm->size = VICAM_NUMFRAMES * vicam->maxframesize; - vm->frames = VICAM_NUMFRAMES; + if (!cam) + return -ENODEV; - for (i=0; ioffsets[i] = vicam->maxframesize * i; + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; - return 0; -} + switch (ioctlnr) { + /* query capabilites */ + case VIDIOCGCAP: + { + struct video_capability b; -static int vicam_get_picture(struct usb_vicam *vicam, struct video_picture *p) -{ - dbg("vicam_get_picture"); + DBG("VIDIOCGCAP\n"); + strcpy(b.name, "ViCam-based Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 320; /* VIDEOSIZE_CIF */ + b.maxheight = 240; + b.minwidth = 320; /* VIDEOSIZE_48_48 */ + b.minheight = 240; - /* This is probably where that weird 0x56 call goes */ - p->brightness = vicam->win.brightness; - p->hue = vicam->win.hue; - p->colour = vicam->win.colour; - p->contrast = vicam->win.contrast; - p->whiteness = vicam->win.whiteness; - p->depth = vicam->win.depth; - p->palette = vicam->win.palette; + if (copy_to_user(arg, &b, sizeof (b))) + retval = -EFAULT; - return 0; -} + break; + } + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; -static void synchronize(struct usb_vicam *vicam) -{ - DECLARE_WAITQUEUE(wait, current); - change_pending = 1; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&vicam->wait, &wait); - if (change_pending) - schedule(); - remove_wait_queue(&vicam->wait, &wait); - set_current_state(TASK_RUNNING); - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); - mdelay(10); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); - mdelay(10); -} - -static void params_changed(struct usb_vicam *vicam) -{ -#if 1 - synchronize(vicam); - mdelay(10); - vicam_parameters(vicam); - printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb)); -#endif -} + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, arg, sizeof (v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(arg, &v, sizeof (v))) + retval = -EFAULT; + break; + } -static int vicam_set_picture(struct usb_vicam *vicam, struct video_picture *p) -{ - int changed = 0; - info("vicam_set_picture (%d)", p->brightness); + case VIDIOCSCHAN: + { + int v; + if (copy_from_user(&v, arg, sizeof (v))) + retval = -EFAULT; + DBG("VIDIOCSCHAN %d\n", v); -#define SET(x) \ - if (vicam->win.x != p->x) \ - vicam->win.x = p->x, changed = 1; - SET(brightness); - SET(hue); - SET(colour); - SET(contrast); - SET(whiteness); - SET(depth); - SET(palette); - if (changed) - params_changed(vicam); + if (retval == 0 && v != 0) + retval = -EINVAL; - return 0; - /* Investigate what should be done maybe 0x56 type call */ - if (p->depth != 8) return 1; - if (p->palette != VIDEO_PALETTE_GREY) return 1; + break; + } - return 0; -} + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture vp; + DBG("VIDIOCGPICT\n"); + memset(&vp, 0, sizeof (struct video_picture)); + vp.brightness = cam->gain << 8; + vp.depth = 24; + vp.palette = VIDEO_PALETTE_RGB24; + if (copy_to_user + (arg, &vp, sizeof (struct video_picture))) + retval = -EFAULT; + break; + } -/* FIXME - vicam_sync_frame - important */ -static int vicam_sync_frame(struct usb_vicam *vicam, int frame) -{ - dbg("vicam_sync_frame"); + case VIDIOCSPICT: + { + struct video_picture *vp = (struct video_picture *) arg; - if(frame <0 || frame >= VICAM_NUMFRAMES) - return -EINVAL; + DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp->depth, + vp->palette); - /* Probably need to handle various cases */ -/* ret=vicam_newframe(vicam, frame); - vicam->frame[frame].grabstate=FRAME_UNUSED; -*/ - return 0; -} - -static int vicam_get_window(struct usb_vicam *vicam, struct video_window *vw) -{ - dbg("vicam_get_window"); + cam->gain = vp->brightness >> 8; - vw->x = 0; - vw->y = 0; - vw->chromakey = 0; - vw->flags = 0; - vw->clipcount = 0; - vw->width = vicam->win.width; - vw->height = vicam->win.height; + if (vp->depth != 24 + || vp->palette != VIDEO_PALETTE_RGB24) + retval = -EINVAL; - return 0; -} + break; + } -static int vicam_set_window(struct usb_vicam *vicam, struct video_window *vw) -{ - info("vicam_set_window"); - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = 0; + vw.y = 0; + vw.width = 320; + vw.height = 240; + vw.chromakey = 0; + vw.flags = 0; + vw.clips = NULL; + vw.clipcount = 0; + + DBG("VIDIOCGWIN\n"); + + if (copy_to_user + ((void *) arg, (void *) &vw, sizeof (vw))) + retval = -EFAULT; + + // I'm not sure what the deal with a capture window is, it is very poorly described + // in the doc. So I won't support it now. + break; + } - if (vicam->win.width == vw->width && vicam->win.height == vw->height) - return 0; + case VIDIOCSWIN: + { - /* Pick largest mode that is smaller than specified res */ - /* If specified res is too small reject */ + struct video_window *vw = (struct video_window *) arg; + DBG("VIDIOCSWIN %d x %d\n", vw->width, vw->height); - /* Add urb send to device... */ + if ( vw->width != 320 || vw->height != 240 ) + retval = -EFAULT; + + break; + } - vicam->win.width = vw->width; - vicam->win.height = vw->height; - params_changed(vicam); + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; - return 0; -} + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof (vm)); + vm.size = + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; + vm.frames = VICAM_FRAMES; + for (i = 0; i < VICAM_FRAMES; i++) + vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; + + if (copy_to_user + ((void *) arg, (void *) &vm, sizeof (vm))) + retval = -EFAULT; -/* FIXME - vicam_mmap_capture - important */ -static int vicam_mmap_capture(struct usb_vicam *vicam, struct video_mmap *vm) -{ - dbg("vicam_mmap_capture"); + break; + } - /* usbvideo.c looks good for using here */ + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + // int video_size; - /* - if (vm->frame >= VICAM_NUMFRAMES) - return -EINVAL; - if (vicam->frame[vm->frame].grabstate != FRAME_UNUSED) - return -EBUSY; - vicam->frame[vm->frame].grabstate=FRAME_READY; - */ + if (copy_from_user + ((void *) &vm, (void *) arg, sizeof (vm))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); + + if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) + retval = -EINVAL; + + // in theory right here we'd start the image capturing + // (fill in a bulk urb and submit it asynchronously) + // + // Instead we're going to do a total hack job for now and + // retrieve the frame in VIDIOCSYNC - /* No need to vicam_set_window here according to Alan */ + break; + } - /* - if (!vicam->streaming) - vicam_start_stream(vicam); - */ + case VIDIOCSYNC: + { + int frame; - /* set frame as ready */ + if (copy_from_user((void *) &frame, arg, sizeof (int))) { + retval = -EFAULT; + break; + } + DBG("VIDIOCSYNC: %d\n", frame); - return 0; -} + read_frame(cam, frame); -/***************************************************************************** - * - * Video4Linux - * - *****************************************************************************/ + break; + } -static int vicam_v4l_open(struct video_device *vdev, int flags) -{ - struct usb_vicam *vicam = (struct usb_vicam *)vdev; - int err = 0; - - dbg("vicam_v4l_open"); + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + retval = -EINVAL; + break; - MOD_INC_USE_COUNT; - down(&vicam->sem); + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + retval = -EINVAL; + break; - if (vicam->open_count) /* Maybe not needed? */ - err = -EBUSY; - else { - vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES); - if (!vicam->fbuf) - err=-ENOMEM; - else { - vicam->open_count = 1; - } -#ifdef BLINKING - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); - info ("led on"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); -#endif + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; } - up(&vicam->sem); - if (err) - MOD_DEC_USE_COUNT; - return err; + up(&cam->busy_lock); + return retval; } -static void vicam_v4l_close(struct video_device *vdev) +static int +vicam_open(struct video_device *dev, int flags) { - struct usb_vicam *vicam = (struct usb_vicam *)vdev; + struct vicam_camera *cam = + (struct vicam_camera *) dev->priv; + DBG("open\n"); - dbg("vicam_v4l_close"); - - down(&vicam->sem); + if (!cam) { + printk(KERN_ERR + "vicam video_device improperly initialized"); + } -#ifdef BLINKING - info ("led off"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); -// vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); Leave it on -#endif + down_interruptible(&cam->busy_lock); + + if (cam->open_count > 0) { + printk(KERN_INFO + "vicam_open called on already opened camera"); + up(&cam->busy_lock); + return -EBUSY; + } + + if (!cam->raw_image) { + cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); + if (!cam->raw_image) { + up(&cam->busy_lock); + return -ENOMEM; + } + } + + if (!cam->framebuf) { + cam->framebuf = + usbvideo_rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + kfree(cam->raw_image); + up(&cam->busy_lock); + return -ENOMEM; + } + } + // First upload firmware, then turn the camera on - rvfree(vicam->fbuf, vicam->maxframesize * VICAM_NUMFRAMES); - vicam->fbuf = 0; - vicam->open_count=0; + if (!cam->is_initialized) { + initialize_camera(cam); - up(&vicam->sem); - /* Why does se401.c have a usbdevice check here? */ - /* If device is unplugged while open, I guess we only may unregister now */ - MOD_DEC_USE_COUNT; + cam->is_initialized = 1; + } + + set_camera_power(cam, 1); + + cam->needsDummyRead = 1; + cam->open_count++; + + up(&cam->busy_lock); + + return 0; } -static long vicam_v4l_read(struct video_device *vdev, char *user_buf, unsigned long buflen, int noblock) +static void +vicam_close(struct video_device *dev) { - //struct usb_vicam *vicam = (struct usb_vicam *)vdev; + DBG("close\n"); + set_camera_power((struct vicam_camera *) dev->priv, 0); - dbg("vicam_v4l_read(%ld)", buflen); + ((struct vicam_camera *) dev->priv)->open_count--; +} - if (!vdev || !buf) - return -EFAULT; - - if(buflen > 0x1e480) - buflen = 0x1e480; - if (copy_to_user(user_buf, buf2, buflen)) - return -EFAULT; - return buflen; +inline int pin(int x) +{ + return((x > 255) ? 255 : ((x < 0) ? 0 : x)); } -static long vicam_v4l_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) +inline void writepixel(char *rgb, int Y, int Cr, int Cb) { - info("vicam_v4l_write"); - return -EINVAL; + Y = 1160 * (Y - 16); + + rgb[2] = pin( ( ( Y + ( 1594 * Cr ) ) + 500 ) / 1300 ); + rgb[1] = pin( ( ( Y - ( 392 * Cb ) - ( 813 * Cr ) ) + 500 ) / 1000 ); + rgb[0] = pin( ( ( Y + ( 2017 * Cb ) ) + 500 ) / 900 ); } -static int vicam_v4l_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +#define DATA_HEADER_SIZE 64 + +// -------------------------------------------------------------------------------- +// vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB +// +// Copyright (C) 2002 Monroe Williams (monroe@pobox.com) +// -------------------------------------------------------------------------------- + +void vicam_decode_color( char *data, char *rgb) { - struct usb_vicam *vicam = (struct usb_vicam *)vdev; - int ret = -EL3RST; + int x,y; + int Cr, Cb; + int sign; + int prevX, nextX, prevY, nextY; + int skip; + unsigned char *src; + unsigned char *dst; - if (!vicam->udev) - return -EIO; + prevY = 512; + nextY = 512; - down(&vicam->sem); + src = data + DATA_HEADER_SIZE; + dst = rgb; - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability b; - ret = vicam_get_capability(vicam,&b); - dbg("name %s",b.name); - if (copy_to_user(arg, &b, sizeof(b))) - ret = -EFAULT; - } - case VIDIOCGFBUF: + for(y = 1; y < 241; y += 2) { - struct video_buffer vb; - info("vicam_v4l_ioctl - VIDIOCGBUF - query frame buffer param"); - /* frame buffer not supported, not used */ - memset(&vb, 0, sizeof(vb)); - vb.base = NULL; + // even line + sign = 1; + prevX = 1; + nextX = 1; + + skip = 0; + + dst = rgb + (y-1)*320*3; - /* FIXME - VIDIOCGFBUF - why the void */ - if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) - ret = -EFAULT; - ret = 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - ret = vicam_get_window(vicam, &vw); - if (copy_to_user(arg, &vw, sizeof(vw))) - ret = -EFAULT; - } - case VIDIOCSWIN: - { - struct video_window vw; - if (copy_from_user(&vw, arg, sizeof(vw))) - ret = -EFAULT; - else - ret = vicam_set_window(vicam, &vw); - return ret; - } - case VIDIOCGCHAN: - { - struct video_channel v; + for(x = 0; x < 512; x++) + { + if(x == 512-1) + nextX = -1; - if (copy_from_user(&v, arg, sizeof(v))) - ret = -EFAULT; - else { - ret = vicam_get_channel(vicam,&v); - if (copy_to_user(arg, &v, sizeof(v))) - ret = -EFAULT; + Cr = sign * ((src[prevX] - src[0]) + (src[nextX] - src[0])) >> 1; + Cb = sign * ((src[prevY] - src[prevX + prevY]) + (src[prevY] - src[nextX + prevY]) + (src[nextY] - src[prevX + nextY]) + (src[nextY] - src[nextX + nextY])) >> 2; + + writepixel( + dst + ((x*5)>>3)*3, + src[0] + (sign * (Cr >> 1)), + Cr, + Cb); + + src++; + sign *= -1; + prevX = -1; + } + + prevY = -512; + + if(y == (242 - 2)) + nextY = -512; + + // odd line + sign = 1; + prevX = 1; + nextX = 1; + + skip = 0; + + dst = rgb + (y)*320*3; + + for(x = 0; x < 512; x++) + { + if(x == 512-1) + nextX = -1; + + Cr = sign * ((src[prevX + prevY] - src[prevY]) + (src[nextX + prevY] - src[prevY]) + (src[prevX + nextY] - src[nextY]) + (src[nextX + nextY] - src[nextY])) >> 2; + Cb = sign * ((src[0] - src[prevX]) + (src[0] - src[nextX])) >> 1; + + writepixel( + dst + ((x * 5)>>3)*3, + src[0] - (sign * (Cb >> 1)), + Cr, + Cb); + + src++; + sign *= -1; + prevX = -1; } } - case VIDIOCSCHAN: - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - ret = -EFAULT; - else - ret = vicam_set_channel(vicam,&v); - } - case VIDIOCGPICT: - { - struct video_picture p; - ret = vicam_get_picture(vicam, &p); - if (copy_to_user(arg, &p, sizeof(p))) - ret = -EFAULT; - } - case VIDIOCSPICT: - { - struct video_picture p; - if (copy_from_user(&p, arg, sizeof(p))) - ret = -EFAULT; - else - ret = vicam_set_picture(vicam, &p); - } - case VIDIOCGMBUF: - { - struct video_mbuf vm; - ret = vicam_get_mmapbuffer(vicam,&vm); - /* FIXME - VIDIOCGMBUF - why the void */ - if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - ret = -EFAULT; +} + +static void +read_frame(struct vicam_camera *cam, int framenum) +{ + unsigned char request[16]; + int realShutter; + int n; + int actual_length; + + memset(request, 0, 16); + request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain + + request[1] = 0; // 512x242 capture + + request[2] = 0x90; // the function of these two bytes + request[3] = 0x07; // is not yet understood + + if (cam->shutter_speed > 60) { + // Short exposure + realShutter = + ((-15631900 / cam->shutter_speed) + 260533) / 1000; + request[4] = realShutter & 0xFF; + request[5] = (realShutter >> 8) & 0xFF; + request[6] = 0x03; + request[7] = 0x01; + } else { + // Long exposure + realShutter = 15600 / cam->shutter_speed - 1; + request[4] = 0; + request[5] = 0; + request[6] = realShutter & 0xFF; + request[7] = realShutter >> 8; + } + + // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 + request[8] = 0; + // bytes 9-15 do not seem to affect exposure or image quality + + n = send_control_msg(cam->udev, 0x51, 0x80, 0, request, 16); + + if (n < 0) { + printk(KERN_ERR + " Problem sending frame capture control message"); + return; } - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - ret = vicam_mmap_capture(vicam, &vm); - /* FIXME: This is probably not right */ + + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), + cam->raw_image, + 512 * 242 + 128, &actual_length, 500); + + if (n < 0) { + printk(KERN_ERR "Problem during bulk read of frame data: %d\n", + n); } - case VIDIOCSYNC: - { - int frame; - /* FIXME - VIDIOCSYNC - why the void */ - if (copy_from_user((void *)&frame, arg, sizeof(int))) - ret = -EFAULT; - else - ret = vicam_sync_frame(vicam,frame); + + vicam_decode_color(cam->raw_image, + cam->framebuf + + framenum * VICAM_MAX_FRAME_SIZE ); + + cam->framebuf_size = + 320 * 240 * VICAM_BYTES_PER_PIXEL; + cam->framebuf_read_start = 0; + + return; + +} + +static long +vicam_read(struct video_device *dev, char *buf, + unsigned long count, int noblock) +{ + struct vicam_camera *cam = dev->priv; + DBG("read %d bytes.\n", (int) count); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + // This is some code that will hopefully allow us to do shell copies from + // the /dev/videoX to a file and have it actually work. + if (cam->framebuf_size != 0) { + if (cam->framebuf_read_start == cam->framebuf_size) { + cam->framebuf_size = cam->framebuf_read_start = 0; + return 0; + } else { + if (cam->framebuf_read_start + count <= + cam->framebuf_size) { + // count does not exceed available bytes + copy_to_user(buf, + (cam->framebuf) + + cam->framebuf_read_start, count); + cam->framebuf_read_start += count; + return count; + } else { + count = + cam->framebuf_size - + cam->framebuf_read_start; + copy_to_user(buf, + (cam->framebuf) + + cam->framebuf_read_start, count); + cam->framebuf_read_start = cam->framebuf_size; + return count; + } + } } - case VIDIOCKEY: - ret = 0; - - case VIDIOCCAPTURE: - case VIDIOCSFBUF: - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - case VIDIOCGUNIT: - ret = -EINVAL; + down_interruptible(&cam->busy_lock); - default: - { - info("vicam_v4l_ioctl - %ui",cmd); - ret = -ENOIOCTLCMD; + if (cam->needsDummyRead) { + read_frame(cam, 0); + cam->needsDummyRead = 0; } - } /* end switch */ + // read_frame twice because the camera doesn't seem to take the shutter speed for the first one. + + read_frame(cam, 0); - up(&vicam->sem); - return ret; + if (count > cam->framebuf_size) + count = cam->framebuf_size; + + copy_to_user(buf, cam->framebuf, count); + + if (count != cam->framebuf_size) + cam->framebuf_read_start = count; + else + cam->framebuf_size = 0; + + up(&cam->busy_lock); + + return count; } -static int vicam_v4l_mmap(struct video_device *dev, const char *adr, unsigned long size) +static int +vicam_mmap(struct video_device *dev, const char *adr, unsigned long size) { - struct usb_vicam *vicam = (struct usb_vicam *)dev; - unsigned long start = (unsigned long)adr; + // TODO: allocate the raw frame buffer if necessary + unsigned long start = (unsigned long) adr; unsigned long page, pos; + struct vicam_camera *cam = dev->priv; - down(&vicam->sem); - - if (vicam->udev == NULL) { - up(&vicam->sem); - return -EIO; - } -#if 0 - if (size > (((VICAM_NUMFRAMES * vicam->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { - up(&vicam->sem); - return -EINVAL; + if (!cam) + return -ENODEV; + + DBG("vicam_mmap: %ld\n", size); + + /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes + * to the size the application requested for mmap and it was screwing apps up. + if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) + return -EINVAL; + */ + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->framebuf) { /* we do lazy allocation */ + cam->framebuf = + usbvideo_rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + up(&cam->busy_lock); + return -ENOMEM; + } } -#endif - pos = (unsigned long)vicam->fbuf; + + pos = (unsigned long) (cam->framebuf); while (size > 0) { - page = kvirt_to_pa(pos); + page = usbvideo_kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { - up(&vicam->sem); + up(&cam->busy_lock); return -EAGAIN; } start += PAGE_SIZE; @@ -653,288 +1026,316 @@ else size = 0; } - up(&vicam->sem); - return 0; + up(&cam->busy_lock); + + return 0; } -/* FIXME - vicam_v4l_init */ -static int vicam_v4l_init(struct video_device *dev) +#ifdef CONFIG_PROC_FS + +static struct proc_dir_entry *vicam_proc_root = NULL; + +static int +vicam_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { - /* stick proc fs stuff in here if wanted */ - dbg("vicam_v4l_init"); - return 0; + char *out = page; + int len; + struct vicam_camera *cam = (struct vicam_camera *) data; + + out += + sprintf(out, "Vicam-based WebCam Linux Driver.\n"); + out += sprintf(out, "(c) 2002 Joe Burks (jburks@wavicle.org)\n"); + out += sprintf(out, "vicam stats:\n"); + out += sprintf(out, " Shutter Speed: 1/%d\n", cam->shutter_speed); + out += sprintf(out, " Gain: %d\n", cam->gain); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; } -/* FIXME - vicam_template - important */ -static struct video_device vicam_template = { - name: "vicam USB camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_SE401, /* need to ask for own id */ - open: vicam_v4l_open, - close: vicam_v4l_close, - read: vicam_v4l_read, - write: vicam_v4l_write, - ioctl: vicam_v4l_ioctl, - mmap: vicam_v4l_mmap, - initialize: vicam_v4l_init, -}; +static int +vicam_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char *in; + char *start; + struct vicam_camera *cam = (struct vicam_camera *) data; -/****************************************************************************** - * - * Some Routines - * - ******************************************************************************/ + in = kmalloc(count + 1, GFP_KERNEL); + if (!in) + return -ENOMEM; -/* -Flash the led -vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); -info ("led on"); -vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); -info ("led off"); -vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); -vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); -*/ + in[count] = 0; // I'm not sure buffer is gauranteed to be null terminated + // so I do this to make sure I have a null in there. + + strncpy(in, buffer, count); -static void vicam_bulk(struct urb *urb) + start = strstr(in, "gain="); + if (start + && (start == in || *(start - 1) == ' ' || *(start - 1) == ',')) + cam->gain = simple_strtoul(start + 5, NULL, 10); + + start = strstr(in, "shutter="); + if (start + && (start == in || *(start - 1) == ' ' || *(start - 1) == ',')) + cam->shutter_speed = simple_strtoul(start + 8, NULL, 10); + + kfree(in); + return count; +} + +void +vicam_create_proc_root(void) { - struct usb_vicam *vicam = urb->context; + vicam_proc_root = create_proc_entry("video/vicam", S_IFDIR, 0); - /* if (!vicam || !vicam->dev || !vicam->used) - return; - */ + if (vicam_proc_root) + vicam_proc_root->owner = THIS_MODULE; + else + printk(KERN_ERR + "could not create /proc entry for vicam!"); +} - if (urb->status) - printk("vicam%d: nonzero read/write bulk status received: %d", - 0, urb->status); - - urb->actual_length = 0; - urb->dev = vicam->udev; - - memcpy(buf2, buf+64, 0x1e480); - if (vicam->fbuf) - memcpy(vicam->fbuf, buf+64, 0x1e480); - - if (!change_pending) { - if (usb_submit_urb(urb)) - dbg("failed resubmitting read urb"); - } else { - change_pending = 0; - wake_up_interruptible(&vicam->wait); - } +void +vicam_destroy_proc_root(void) +{ + if (vicam_proc_root) + remove_proc_entry("video/vicam", 0); } -static int vicam_parameters(struct usb_vicam *vicam) +void +vicam_create_proc_entry(void *ptr) { - unsigned char req[0x10]; - unsigned int shutter; - shutter = 10; + struct vicam_camera *cam = (struct vicam_camera *) ptr; - switch (vicam->win.width) { - case 512: - default: - memcpy(req, s512x242bw, 0x10); - break; - case 256: - memcpy(req, s256x242bw, 0x10); - break; - case 128: - memcpy(req, s128x122bw, 0x10); - break; + char name[7]; + struct proc_dir_entry *ent; + + DBG(KERN_INFO "vicam: creating proc entry\n"); + + if (!vicam_proc_root || !cam) { + printk(KERN_INFO + "vicam: could not create proc entry, %s pointer is null.\n", + (!cam ? "camera" : "root")); + return; } + sprintf(name, "video%d", cam->vdev.minor); - mdelay(10); - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); - info ("led on"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); - - mdelay(10); - - shutter = vicam->win.contrast / 256; - if (shutter == 0) - shutter = 1; - printk("vicam_parameters: brightness %d, shutter %d\n", vicam->win.brightness, shutter ); - req[0] = vicam->win.brightness /256; - shutter = 15600/shutter - 1; - req[6] = shutter & 0xff; - req[7] = (shutter >> 8) & 0xff; - vicam_sndctrl(1, vicam, VICAM_REQ_CAPTURE, 0x80, req, 0x10); - mdelay(10); - vicam_sndctrl(0, vicam, VICAM_REQ_GET_SOMETHIN, 0, buf, 0x10); - mdelay(10); + ent = + create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, + vicam_proc_root); + if (!ent) + return; - return 0; + ent->data = cam; + ent->read_proc = vicam_read_proc; + ent->write_proc = vicam_write_proc; + ent->size = 512; + cam->proc_entry = ent; } -static int vicam_init(struct usb_vicam *vicam) +void +vicam_destroy_proc_entry(void *ptr) { - int width[] = {128, 256, 512}; - int height[] = {122, 242, 242}; + struct vicam_camera *cam = (struct vicam_camera *) ptr; + char name[7]; + + if (!cam || !cam->proc_entry) + return; - dbg("vicam_init"); - buf = kmalloc(0x1e480, GFP_KERNEL); - buf2 = kmalloc(0x1e480, GFP_KERNEL); - if ((!buf) || (!buf2)) { - printk("Not enough memory for vicam!\n"); - goto error; - } - - /* do we do aspect correction in kernel or not? */ - vicam->sizes = 3; - vicam->width = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL); - vicam->height = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL); - memcpy(vicam->width, &width, sizeof(width)); - memcpy(vicam->height, &height, sizeof(height)); - vicam->maxframesize = vicam->width[vicam->sizes-1] * vicam->height[vicam->sizes-1]; - - /* Download firmware to camera */ - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware1, sizeof(firmware1)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex1, sizeof(findex1)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware2, sizeof(firmware2)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex2, sizeof(findex2)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup)); - - vicam_parameters(vicam); - - FILL_BULK_URB(&vicam->readurb, vicam->udev, usb_rcvbulkpipe(vicam->udev, 0x81), - buf, 0x1e480, vicam_bulk, vicam); - printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb)); + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry(name, vicam_proc_root); + cam->proc_entry = NULL; +} + +#endif + +int +vicam_video_init(struct video_device *vdev) +{ + // This would normally create the proc entry for this camera +#ifdef CONFIG_PROC_FS + vicam_create_proc_entry(vdev->priv); +#endif return 0; -error: - if (buf) - kfree(buf); - if (buf2) - kfree(buf2); - return 1; } -static void * vicam_probe(struct usb_device *udev, unsigned int ifnum, - const struct usb_device_id *id) +static long +vicam_write(struct video_device *v, const char *buf, unsigned long count, + int nonblock) { - struct usb_vicam *vicam; - char *camera_name=NULL; - dbg("vicam_probe"); + return -EINVAL; +} + +static struct video_device vicam_template = { + owner:THIS_MODULE, + name:"ViCam-based USB Camera", + type:VID_TYPE_CAPTURE, + hardware:VID_HARDWARE_VICAM, + open:vicam_open, + close:vicam_close, + read:vicam_read, + write:vicam_write, + ioctl:vicam_ioctl, + mmap:vicam_mmap, + initialize:vicam_video_init, + minor:-1, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id vicam_table[] = { + {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vicam_table); + +static struct usb_driver vicam_driver = { + name:"vicam", + probe:vicam_probe, + disconnect:vicam_disconnect, + id_table:vicam_table +}; + +/** + * vicam_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static void * +vicam_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + int nas, bulkEndpoint = 0; + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + struct vicam_camera *cam; /* See if the device offered us matches what we can accept */ - if ((udev->descriptor.idVendor != USB_VICAM_VENDOR_ID) || - (udev->descriptor.idProduct != USB_VICAM_PRODUCT_ID)) { + if ((dev->descriptor.idVendor != USB_VICAM_VENDOR_ID) || + (dev->descriptor.idProduct != USB_VICAM_PRODUCT_ID)) { return NULL; } - - camera_name="3Com HomeConnect USB"; - info("ViCAM camera found: %s", camera_name); - - vicam = kmalloc (sizeof(struct usb_vicam), GFP_KERNEL); - if (vicam == NULL) { - err ("couldn't kmalloc vicam struct"); + + printk(KERN_INFO "ViCam based webcam connected\n"); + + nas = dev->actconfig->interface[ifnum].num_altsetting; + if (nas != 1) { + printk(KERN_WARNING + "Expected only one alternate setting for this camera!\n"); return NULL; } - memset(vicam, 0, sizeof(*vicam)); - - vicam->udev = udev; - vicam->camera_name = camera_name; - vicam->win.brightness = 128; - vicam->win.contrast = 10; - - /* FIXME */ - if (vicam_init(vicam)) { - kfree(vicam); - return NULL; + + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", + ifnum, (unsigned) (interface->bNumEndpoints)); + endpoint = &interface->endpoint[0]; + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk in endpoint */ + bulkEndpoint = endpoint->bEndpointAddress; + } else { + printk(KERN_ERR + "No bulk in endpoint was found ?! (this is bad)\n"); } - memcpy(&vicam->vdev, &vicam_template, sizeof(vicam_template)); - memcpy(vicam->vdev.name, vicam->camera_name, strlen(vicam->camera_name)); - - if (video_register_device(&vicam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { - err("video_register_device"); - kfree(vicam); + + if ((cam = + kmalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING + "could not allocate kernel memory for vicam_camera struct\n"); return NULL; } - info("registered new video device: video%d", vicam->vdev.minor); - - init_MUTEX (&vicam->sem); - init_waitqueue_head(&vicam->wait); - - return vicam; -} + memset(cam, 0, sizeof (struct vicam_camera)); + cam->shutter_speed = 15; -/* FIXME - vicam_disconnect - important */ -static void vicam_disconnect(struct usb_device *udev, void *ptr) -{ - struct usb_vicam *vicam; + init_MUTEX(&cam->busy_lock); - vicam = (struct usb_vicam *) ptr; + memcpy(&cam->vdev, &vicam_template, + sizeof (vicam_template)); + cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only - if (!vicam->open_count) - video_unregister_device(&vicam->vdev); - vicam->udev = NULL; -/* - vicam->frame[0].grabstate = FRAME_ERROR; - vicam->frame[1].grabstate = FRAME_ERROR; -*/ + cam->udev = dev; + cam->bulkEndpoint = bulkEndpoint; - /* Free buffers and shit */ + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + kfree(cam); + printk(KERN_WARNING "video_register_device failed\n"); + return NULL; + } - info("%s disconnected", vicam->camera_name); - synchronize(vicam); + printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",cam->vdev.minor); - if (!vicam->open_count) { - /* Other random junk */ - kfree(vicam); - vicam = NULL; - } + return cam; } -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver vicam_driver = { - name: "vicam", - probe: vicam_probe, - disconnect: vicam_disconnect, - id_table: vicam_table, -}; - -/****************************************************************************** - * - * Module Routines - * - ******************************************************************************/ +static void +vicam_disconnect(struct usb_device *dev, void *ptr) +{ + struct vicam_camera *cam = ptr; + video_unregister_device(&cam->vdev); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); +#ifdef CONFIG_PROC_FS + vicam_destroy_proc_entry(cam); +#endif -/* Module paramaters */ -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debug enabled or not"); + if (cam->raw_image) + kfree(cam->raw_image); + if (cam->framebuf) + usbvideo_rvfree(cam->framebuf, + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); -static int __init usb_vicam_init(void) -{ - int result; + kfree(cam); - printk("VICAM: initializing\n"); - /* register this driver with the USB subsystem */ - result = usb_register(&vicam_driver); - if (result < 0) { - err("usb_register failed for the "__FILE__" driver. Error number %d", - result); - return -1; - } + printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); +} - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); +/* + */ +static int __init +usb_vicam_init(void) +{ + DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); +#ifdef CONFIG_PROC_FS + vicam_create_proc_root(); +#endif + if (usb_register(&vicam_driver) != 0) + printk(KERN_WARNING "usb_register failed!\n"); return 0; } -static void __exit usb_vicam_exit(void) +static void __exit +usb_vicam_exit(void) { - /* deregister this driver with the USB subsystem */ + DBG(KERN_INFO + "ViCam-based WebCam driver shutdown\n"); + usb_deregister(&vicam_driver); +#ifdef CONFIG_PROC_FS + vicam_destroy_proc_root(); +#endif } module_init(usb_vicam_init); module_exit(usb_vicam_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/vicam.h b/drivers/usb/vicam.h --- a/drivers/usb/vicam.h Thu Dec 5 14:48:55 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,81 +0,0 @@ -/* - * - * Vista Imaging ViCAM / 3Com HomeConnect Usermode Driver - * Christopher L Cheney (C) 2001 - * - */ - -#ifndef __LINUX_VICAM_H -#define __LINUX_VICAM_H - - -#ifdef CONFIG_USB_DEBUG - static int debug = 1; -#else - static int debug; -#endif - -/* Use our own dbg macro */ -#undef dbg -#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0) - -#define VICAM_NUMFRAMES 30 -#define VICAM_NUMSBUF 1 - -/* USB REQUEST NUMBERS */ -#define VICAM_REQ_VENDOR 0xff -#define VICAM_REQ_CAMERA_POWER 0x50 -#define VICAM_REQ_CAPTURE 0x51 -#define VICAM_REQ_LED_CONTROL 0x55 -#define VICAM_REQ_GET_SOMETHIN 0x56 - -/* not required but lets you know camera is on */ -/* camera must be on to turn on led */ -/* 0x01 always on 0x03 on when picture taken (flashes) */ - -struct picture_parm -{ - int width; - int height; - int brightness; - int hue; - int colour; - int contrast; - int whiteness; - int depth; - int palette; -}; - -struct vicam_scratch { - unsigned char *data; - volatile int state; - int offset; - int length; -}; - -/* Structure to hold all of our device specific stuff */ -struct usb_vicam -{ - struct video_device vdev; - struct usb_device *udev; - - int open_count; /* number of times this port has been opened */ - struct semaphore sem; /* locks this structure */ - wait_queue_head_t wait; /* Processes waiting */ - - int streaming; - - /* v4l stuff */ - char *camera_name; - char *fbuf; - struct urb *urb[VICAM_NUMSBUF]; - int sizes; - int *width; - int *height; - int maxframesize; - struct picture_parm win; - struct proc_dir_entry *proc_entry; /* /proc/se401/videoX */ - struct urb readurb; -}; - -#endif diff -Nru a/include/linux/videodev.h b/include/linux/videodev.h --- a/include/linux/videodev.h Thu Dec 5 14:48:55 2002 +++ b/include/linux/videodev.h Thu Dec 5 14:48:55 2002 @@ -403,6 +403,7 @@ #define VID_HARDWARE_PWC 31 /* Philips webcams */ #define VID_HARDWARE_MEYE 32 /* Sony Vaio MotionEye cameras */ #define VID_HARDWARE_CPIA2 33 +#define VID_HARDWARE_VICAM 34 /* ViCam, 3Com Homeconnect */ #endif /* __LINUX_VIDEODEV_H */