Messages in this thread | | | Date | Wed, 17 May 2006 14:14:17 +0800 | From | "Jaya Kumar" <> | Subject | Re: [linux-usb-devel] [PATCH/RFC 2.6.17-rc4 1/1] usbvideo/quickcam_messenger driver v4 |
| |
[ My apologies again. Adding Mauro and video4linux-list to CC list. ]
On 5/17/06, jayakumar.video@gmail.com <jayakumar.video@gmail.com> wrote: > [ sorry, bungled the l-u-devel list address and subject in prev email ] > > Hi Greg KH, USB folk, kernel folk, > > Appended is v4 of my patch adding a usbvideo driver for the Logitech > Quickcam Messenger USB webcam. This patch has been rebased against > 2.6.17-rc4 as per Greg's feedback. > > Please let me know if it looks okay and if you have any feedback > or suggestions. > > Thanks, > Jaya Kumar > > Signed-off-by: Jaya Kumar <jayakumar.video@gmail.com> > > --- > > Kconfig | 12 > Makefile | 1 > quickcam_messenger.c | 1120 +++++++++++++++++++++++++++++++++++++++++++++++++++ > quickcam_messenger.h | 126 +++++ > 4 files changed, 1259 insertions(+) > > --- > > diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig > --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Kconfig 2006-05-17 08:11:36.000000000 +0800 > +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Kconfig 2006-05-17 08:39:51.674492008 +0800 > @@ -36,3 +36,15 @@ config USB_KONICAWC > > To compile this driver as a module, choose M here: the > module will be called konicawc. > + > +config USB_QUICKCAM_MESSENGER > + tristate "USB Logitech Quickcam Messenger" > + depends on USB && VIDEO_DEV > + select VIDEO_USBVIDEO > + ---help--- > + Say Y or M here to enable support for the USB Logitech Quickcam > + Messenger webcam. > + > + To compile this driver as a module, choose M here: the > + module will be called quickcam_messenger. > + > diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Makefile linux-2.6.17-rc4/drivers/media/video/usbvideo/Makefile > --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/Makefile 2006-05-17 08:11:36.000000000 +0800 > +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/Makefile 2006-05-17 08:40:14.000000000 +0800 > @@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO_USBVIDEO) += usbvi > obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o > obj-$(CONFIG_USB_KONICAWC) += konicawc.o > obj-$(CONFIG_USB_VICAM) += vicam.o > +obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o > diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.c linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.c > --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.c 1970-01-01 07:30:00.000000000 +0730 > +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.c 2006-05-17 08:37:05.000000000 +0800 > @@ -0,0 +1,1120 @@ > +/* > + * Driver for Logitech Quickcam Messenger usb video camera > + * Copyright (C) Jaya Kumar > + * > + * This work was sponsored by CIS(M) Sdn Bhd. > + * History: > + * 05/08/2006 - Jaya Kumar > + * I wrote this based on the konicawc by Simon Evans. > + * - > + * Full credit for reverse engineering and creating an initial > + * working linux driver for the VV6422 goes to the qce-ga project by > + * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, > + * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as > + * others. > + * --- > + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/input.h> > +#include <linux/usb_input.h> > + > +#include "usbvideo.h" > +#include "quickcam_messenger.h" > + > +/* > + * Version Information > + */ > + > +#ifdef CONFIG_USB_DEBUG > +static int debug; > +#define DEBUG(n, format, arg...) \ > + if (n <= debug) { \ > + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ > + } > +#else > +#define DEBUG(n, arg...) > +static const int debug = 0; > +#endif > + > +#define DRIVER_VERSION "v0.01" > +#define DRIVER_DESC "Logitech Quickcam Messenger USB" > + > +#define USB_LOGITECH_VENDOR_ID 0x046D > +#define USB_QCM_PRODUCT_ID 0x08F0 > + > +#define MAX_CAMERAS 1 > + > +#define MAX_COLOUR 32768 > +#define MAX_HUE 32768 > +#define MAX_BRIGHTNESS 32768 > +#define MAX_CONTRAST 32768 > +#define MAX_WHITENESS 32768 > + > +static int size = SIZE_320X240; > +static int colour = MAX_COLOUR; > +static int hue = MAX_HUE; > +static int brightness = MAX_BRIGHTNESS; > +static int contrast = MAX_CONTRAST; > +static int whiteness = MAX_WHITENESS; > + > +static struct usbvideo *cams; > + > +static struct usb_device_id qcm_table [] = { > + { USB_DEVICE(USB_LOGITECH_VENDOR_ID, USB_QCM_PRODUCT_ID) }, > + { } > +}; > +MODULE_DEVICE_TABLE(usb, qcm_table); > + > +#ifdef CONFIG_INPUT > +static void qcm_register_input(struct qcm *cam, struct usb_device *dev) > +{ > + struct input_dev *input_dev; > + > + usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); > + strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); > + > + cam->input = input_dev = input_allocate_device(); > + if (!input_dev) { > + warn("insufficient mem for cam input device"); > + return; > + } > + > + input_dev->name = "QCM button"; > + input_dev->phys = cam->input_physname; > + usb_to_input_id(dev, &input_dev->id); > + input_dev->cdev.dev = &dev->dev; > + > + input_dev->evbit[0] = BIT(EV_KEY); > + input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); > + > + input_dev->private = cam; > + > + input_register_device(cam->input); > +} > + > +static void qcm_unregister_input(struct qcm *cam) > +{ > + if (cam->input) { > + input_unregister_device(cam->input); > + cam->input = NULL; > + } > +} > + > +static void qcm_report_buttonstat(struct qcm *cam) > +{ > + if (cam->input) { > + input_report_key(cam->input, BTN_0, cam->button_sts); > + input_sync(cam->input); > + } > +} > + > +static void qcm_int_irq(struct urb *urb, struct pt_regs *regs) > +{ > + int ret; > + struct uvd *uvd = urb->context; > + struct qcm *cam; > + > + if (!CAMERA_IS_OPERATIONAL(uvd)) > + return; > + > + if (!uvd->streaming) > + return; > + > + uvd->stats.urb_count++; > + > + if (urb->status < 0) > + uvd->stats.iso_err_count++; > + else { > + if (urb->actual_length > 0 ) { > + cam = (struct qcm *) uvd->user_data; > + if (cam->button_sts_buf == 0x88) > + cam->button_sts = 0x0; > + else if (cam->button_sts_buf == 0x80) > + cam->button_sts = 0x1; > + qcm_report_buttonstat(cam); > + } > + } > + > + ret = usb_submit_urb(urb, GFP_ATOMIC); > + if (ret < 0) > + err("usb_submit_urb error (%d)", ret); > +} > + > +static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd) > +{ > + int errflag; > + usb_fill_int_urb(cam->button_urb, uvd->dev, > + usb_rcvintpipe(uvd->dev, uvd->video_endp + 1), > + &cam->button_sts_buf, > + 1, > + qcm_int_irq, > + uvd, 16); > + > + errflag = usb_submit_urb(cam->button_urb, GFP_KERNEL); > + if (errflag) > + err ("usb_submit_int ret %d", errflag); > + return errflag; > +} > + > +static void qcm_stop_int_data(struct qcm *cam) > +{ > + usb_kill_urb(cam->button_urb); > +} > + > +static int qcm_alloc_int_urb(struct qcm *cam) > +{ > + cam->button_urb = usb_alloc_urb(0, GFP_KERNEL); > + > + if (!cam->button_urb) > + return -ENOMEM; > + > + return 0; > +} > + > +static void qcm_free_int(struct qcm *cam) > +{ > + if (cam->button_urb) > + usb_free_urb(cam->button_urb); > +} > +#endif /* CONFIG_INPUT */ > + > +static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val) > +{ > + int ret; > + > + /* we'll wait up to 3 slices but no more */ > + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), > + 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, > + reg, 0, &val, 1, 3*HZ); > + return ret; > +} > + > +static int qcm_stv_setw(struct usb_device *dev, u16 reg, u16 val) > +{ > + int ret; > + > + /* we'll wait up to 3 slices but no more */ > + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), > + 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, > + reg, 0, &val, 2, 3*HZ); > + return ret; > +} > + > +static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, > + __le16 *val) > +{ > + int ret; > + > + /* we'll wait up to 3 slices but no more */ > + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), > + 0x04, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, > + reg, 0, val, 2, 3*HZ); > + return ret; > +} > + > +static int qcm_camera_on(struct uvd *uvd) > +{ > + int ret; > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x01)); > + return 0; > +} > + > +static int qcm_camera_off(struct uvd *uvd) > +{ > + int ret; > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); > + return 0; > +} > + > +static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) > +{ > + unsigned int segment, valsat; > + signed int h = (signed int) hue; > + unsigned int s = (sat - 32768) * 2; /* rescale */ > + unsigned int v = val; > + unsigned int p; > + > + /* > + the registers controling gain are 8 bit of which > + we affect only the last 4 bits with our gain. > + we know that if saturation is 0, (unsaturated) then > + we're grayscale (center axis of the colour cone) so > + we set rgb=value. we use a formula obtained from > + wikipedia to map the cone to the RGB plane. it's > + as follows for the human value case of h=0..360, > + s=0..1, v=0..1 > + h_i = h/60 % 6 , f = h/60 - h_i , p = v(1-s) > + q = v(1 - f*s) , t = v(1 - (1-f)s) > + h_i==0 => r=v , g=t, b=p > + h_i==1 => r=q , g=v, b=p > + h_i==2 => r=p , g=v, b=t > + h_i==3 => r=p , g=q, b=v > + h_i==4 => r=t , g=p, b=v > + h_i==5 => r=v , g=p, b=q > + the bottom side (the point) and the stuff just up > + of that is black so we simplify those two cases. > + */ > + if (sat < 32768) { > + /* anything less than this is unsaturated */ > + *r = val; > + *g = val; > + *b = val; > + return; > + } > + if (val <= (0xFFFF/8)) { > + /* anything less than this is black */ > + *r = 0; > + *g = 0; > + *b = 0; > + return; > + } > + > + /* the rest of this code is copying tukkat's > + implementation of the hsv2rgb conversion as taken > + from qc-usb-messenger code. the 10923 is 0xFFFF/6 > + to divide the cone into 6 sectors. */ > + > + segment = (h + 10923) & 0xFFFF; > + segment = segment*3 >> 16; /* 0..2: 0=R, 1=G, 2=B */ > + hue -= segment * 21845; /* -10923..10923 */ > + h = hue; > + h *= 3; > + valsat = v*s >> 16; /* 0..65534 */ > + p = v - valsat; > + if (h >= 0) { > + unsigned int t = v - (valsat * (32769 - h) >> 15); > + switch (segment) { > + case 0: /* R-> */ > + *r = v; > + *g = t; > + *b = p; > + break; > + case 1: /* G-> */ > + *r = p; > + *g = v; > + *b = t; > + break; > + case 2: /* B-> */ > + *r = t; > + *g = p; > + *b = v; > + break; > + } > + } else { > + unsigned int q = v - (valsat * (32769 + h) >> 15); > + switch (segment) { > + case 0: /* ->R */ > + *r = v; > + *g = p; > + *b = q; > + break; > + case 1: /* ->G */ > + *r = q; > + *g = v; > + *b = p; > + break; > + case 2: /* ->B */ > + *r = p; > + *g = q; > + *b = v; > + break; > + } > + } > +} > + > +static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, > + u16 saturation, u16 value) > +{ > + int ret; > + u16 r,g,b; > + > + /* this code is based on qc-usb-messenger */ > + qcm_hsv2rgb(hue, saturation, value, &r, &g, &b); > + > + r >>= 12; > + g >>= 12; > + b >>= 12; > + > + /* min val is 8 */ > + r = max((u16) 8, r); > + g = max((u16) 8, g); > + b = max((u16) 8, b); > + > + r |= 0x30; > + g |= 0x30; > + b |= 0x30; > + > + /* set the r,g,b gain registers */ > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b)); > + > + /* doing as qc-usb did */ > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050C, 0x2A)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050D, 0x01)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); > + > + return 0; > +} > + > +static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure) > +{ > + int ret; > + int formedval; > + > + /* calculation was from qc-usb-messenger driver */ > + formedval = ( exposure >> 12 ); > + > + /* max value for formedval is 14 */ > + formedval = min(formedval, 14); > + > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, > + 0x143A, 0xF0 | formedval)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); > + return 0; > +} > + > +static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast, > + int hue, int colour) > +{ > + int ret; > + /* brightness is exposure, contrast is gain, colour is saturation */ > + CHECK_RET(ret, > + qcm_sensor_set_exposure(uvd, brightness)); > + CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast)); > + > + return 0; > +} > + > +static int qcm_sensor_setsize(struct uvd *uvd, u8 size) > +{ > + int ret; > + > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x1505, size)); > + return 0; > +} > + > +static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness) > +{ > + int ret; > + /* some rescaling as done by the qc-usb-messenger code */ > + if (whiteness > 0xC000) > + whiteness = 0xC000 + (whiteness & 0x3FFF)*8; > + > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, > + (whiteness >> 8) & 0xFF)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, > + (whiteness >> 16) & 0x03)); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); > + > + return 0; > +} > + > +static int qcm_sensor_init(struct uvd *uvd) > +{ > + struct qcm *cam = (struct qcm *) uvd->user_data; > + int ret; > + int i; > + > + for (i=0; i < sizeof(regval_table)/sizeof(regval_table[0]) ; i++) { > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, > + regval_table[i].reg, > + regval_table[i].val)); > + } > + > + CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, > + cpu_to_le16(ISOC_PACKET_SIZE))); > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08)); > + CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01)); > + > + CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); > + > + CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); > + > + CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, > + uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour)); > + > + CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); > + CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); > + > + return 0; > +} > + > +static int qcm_set_camera_size(struct uvd *uvd) > +{ > + int ret; > + struct qcm *cam = (struct qcm *) uvd->user_data; > + > + CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); > + cam->width = camera_sizes[cam->size].width; > + cam->height = camera_sizes[cam->size].height; > + uvd->videosize = VIDEOSIZE(cam->width, cam->height); > + > + return 0; > +} > + > +static int qcm_setup_on_open(struct uvd *uvd) > +{ > + int ret; > + > + CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, > + uvd->vpic.colour, uvd->vpic.contrast)); > + CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness)); > + CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); > + CHECK_RET(ret, qcm_set_camera_size(uvd)); > + CHECK_RET(ret, qcm_camera_on(uvd)); > + return 0; > +} > + > +static void qcm_adjust_picture(struct uvd *uvd) > +{ > + int ret; > + struct qcm *cam = (struct qcm *) uvd->user_data; > + > + ret = qcm_camera_off(uvd); > + if (ret) { > + err("can't turn camera off. abandoning pic adjustment"); > + return; > + } > + > + /* if there's been a change in contrast, hue, or > + colour then we need to recalculate hsv in order > + to update gains */ > + if ((cam->contrast != uvd->vpic.contrast) || > + (cam->hue != uvd->vpic.hue) || > + (cam->colour != uvd->vpic.colour)) { > + cam->contrast = uvd->vpic.contrast; > + cam->hue = uvd->vpic.hue; > + cam->colour = uvd->vpic.colour; > + ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, > + cam->contrast); > + if (ret) { > + err("can't set gains. abandoning pic adjustment"); > + return; > + } > + } > + > + if (cam->brightness != uvd->vpic.brightness) { > + cam->brightness = uvd->vpic.brightness; > + ret = qcm_sensor_set_exposure(uvd, cam->brightness); > + if (ret) { > + err("can't set exposure. abandoning pic adjustment"); > + return; > + } > + } > + > + if (cam->whiteness != uvd->vpic.whiteness) { > + cam->whiteness = uvd->vpic.whiteness; > + qcm_sensor_set_shutter(uvd, cam->whiteness); > + if (ret) { > + err("can't set shutter. abandoning pic adjustment"); > + return; > + } > + } > + > + ret = qcm_camera_on(uvd); > + if (ret) { > + err("can't reenable camera. pic adjustment failed"); > + return; > + } > +} > + > +static int qcm_process_frame(struct uvd *uvd, u8 *cdata, int framelen) > +{ > + int datalen; > + int totaldata; > + struct framehdr { > + __be16 id; > + __be16 len; > + }; > + struct framehdr *fhdr; > + > + totaldata = 0; > + while (framelen) { > + fhdr = (struct framehdr *) cdata; > + datalen = be16_to_cpu(fhdr->len); > + framelen -= 4; > + cdata += 4; > + > + if ((fhdr->id) == cpu_to_be16(0x8001)) { > + RingQueue_Enqueue(&uvd->dp, marker, 4); > + totaldata += 4; > + continue; > + } > + if ((fhdr->id & cpu_to_be16(0xFF00)) == cpu_to_be16(0x0200)) { > + RingQueue_Enqueue(&uvd->dp, cdata, datalen); > + totaldata += datalen; > + } > + framelen -= datalen; > + cdata += datalen; > + } > + return totaldata; > +} > + > +static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb) > +{ > + int totlen; > + int i; > + unsigned char *cdata; > + > + totlen=0; > + for (i = 0; i < dataurb->number_of_packets; i++) { > + int n = dataurb->iso_frame_desc[i].actual_length; > + int st = dataurb->iso_frame_desc[i].status; > + > + cdata = dataurb->transfer_buffer + > + dataurb->iso_frame_desc[i].offset; > + > + if (st < 0) { > + warn("Data error: packet=%d. len=%d. status=%d.", > + i, n, st); > + uvd->stats.iso_err_count++; > + continue; > + } > + if (!n) > + continue; > + > + totlen += qcm_process_frame(uvd, cdata, n); > + } > + return totlen; > +} > + > +static void resubmit_urb(struct uvd *uvd, struct urb *urb) > +{ > + int ret; > + > + urb->dev = uvd->dev; > + ret = usb_submit_urb(urb, GFP_ATOMIC); > + if (ret) > + err("usb_submit_urb error (%d)", ret); > +} > + > +static void qcm_isoc_irq(struct urb *urb, struct pt_regs *regs) > +{ > + int len; > + struct uvd *uvd = urb->context; > + > + if (!CAMERA_IS_OPERATIONAL(uvd)) > + return; > + > + if (!uvd->streaming) > + return; > + > + uvd->stats.urb_count++; > + > + if (!urb->actual_length) { > + resubmit_urb(uvd, urb); > + return; > + } > + > + len = qcm_compress_iso(uvd, urb); > + resubmit_urb(uvd, urb); > + uvd->stats.urb_length = len; > + uvd->stats.data_count += len; > + if (len) > + RingQueue_WakeUpInterruptible(&uvd->dp); > +} > + > +static int qcm_start_data(struct uvd *uvd) > +{ > + struct qcm *cam = (struct qcm *) uvd->user_data; > + int i; > + int errflag; > + int pktsz; > + int err; > + > + pktsz = uvd->iso_packet_len; > + if (!CAMERA_IS_OPERATIONAL(uvd)) { > + err("Camera is not operational"); > + return -EFAULT; > + } > + > + err = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltActive); > + if (err < 0) { > + err("usb_set_interface error"); > + uvd->last_error = err; > + return -EBUSY; > + } > + > + for (i=0; i < USBVIDEO_NUMSBUF; i++) { > + int j, k; > + struct urb *urb = uvd->sbuf[i].urb; > + urb->dev = uvd->dev; > + urb->context = uvd; > + urb->pipe = usb_rcvisocpipe(uvd->dev, uvd->video_endp); > + urb->interval = 1; > + urb->transfer_flags = URB_ISO_ASAP; > + urb->transfer_buffer = uvd->sbuf[i].data; > + urb->complete = qcm_isoc_irq; > + urb->number_of_packets = FRAMES_PER_DESC; > + urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; > + for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { > + urb->iso_frame_desc[j].offset = k; > + urb->iso_frame_desc[j].length = pktsz; > + } > + } > + > + uvd->streaming = 1; > + uvd->curframe = -1; > + for (i=0; i < USBVIDEO_NUMSBUF; i++) { > + errflag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); > + if (errflag) > + err ("usb_submit_isoc(%d) ret %d", i, errflag); > + } > + > + CHECK_RET(err, qcm_setup_input_int(cam, uvd)); > + CHECK_RET(err, qcm_camera_on(uvd)); > + return 0; > +} > + > +static void qcm_stop_data(struct uvd *uvd) > +{ > + struct qcm *cam = (struct qcm *) uvd->user_data; > + int i, j; > + int ret; > + > + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) > + return; > + > + ret = qcm_camera_off(uvd); > + if (ret) > + warn("couldn't turn the cam off."); > + > + uvd->streaming = 0; > + > + /* Unschedule all of the iso td's */ > + for (i=0; i < USBVIDEO_NUMSBUF; i++) > + usb_kill_urb(uvd->sbuf[i].urb); > + > + qcm_stop_int_data(cam); > + > + if (!uvd->remove_pending) { > + /* Set packet size to 0 */ > + j = usb_set_interface(uvd->dev, uvd->iface, > + uvd->ifaceAltInactive); > + if (j < 0) { > + err("usb_set_interface() error %d.", j); > + uvd->last_error = j; > + } > + } > +} > + > +static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) > +{ > + struct qcm *cam = (struct qcm *) uvd->user_data; > + int x; > + struct rgb *rgbL0; > + struct rgb *rgbL1; > + struct bayL0 *bayL0; > + struct bayL1 *bayL1; > + int hor,ver,hordel,verdel; > + assert(frame != NULL); > + > + switch (cam->size) { > + case SIZE_160X120: > + hor = 162; ver = 124; hordel = 1; verdel = 2; > + break; > + case SIZE_320X240: > + default: > + hor = 324; ver = 248; hordel = 2; verdel = 4; > + break; > + } > + > + if (frame->scanstate == ScanState_Scanning) { > + while (RingQueue_GetLength(&uvd->dp) >= > + 4 + (hor*verdel + hordel)) { > + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && > + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && > + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && > + (RING_QUEUE_PEEK(&uvd->dp, 3) == 0xff)) { > + frame->curline = 0; > + frame->scanstate = ScanState_Lines; > + frame->frameState = FrameState_Grabbing; > + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); > + /* > + * if we're starting, we need to discard the first > + * 4 lines of y bayer data > + * and the first 2 gr elements of x bayer data > + */ > + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, > + (hor*verdel + hordel)); > + break; > + } > + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); > + } > + } > + > + if (frame->scanstate == ScanState_Scanning) > + return; > + > + /* now we can start processing bayer data so long as we have at least > + * 2 lines worth of data. this is the simplest demosaicing method that > + * I could think of. I use each 2x2 bayer element without interpolation > + * to generate 4 rgb pixels. > + */ > + while ( frame->curline < cam->height && > + (RingQueue_GetLength(&uvd->dp) >= hor*2)) { > + /* get 2 lines of bayer for demosaicing > + * into 2 lines of RGB */ > + RingQueue_Dequeue(&uvd->dp, cam->scratch, hor*2); > + bayL0 = (struct bayL0 *) cam->scratch; > + bayL1 = (struct bayL1 *) (cam->scratch + hor); > + /* frame->curline is the rgb y line */ > + rgbL0 = (struct rgb *) > + ( frame->data + (cam->width*3*frame->curline)); > + /* w/2 because we're already doing 2 pixels */ > + rgbL1 = rgbL0 + (cam->width/2); > + > + for (x=0; x < cam->width; x+=2) { > + rgbL0->r = bayL0->r; > + rgbL0->g = bayL0->g; > + rgbL0->b = bayL1->b; > + > + rgbL0->r2 = bayL0->r; > + rgbL0->g2 = bayL1->g; > + rgbL0->b2 = bayL1->b; > + > + rgbL1->r = bayL0->r; > + rgbL1->g = bayL1->g; > + rgbL1->b = bayL1->b; > + > + rgbL1->r2 = bayL0->r; > + rgbL1->g2 = bayL1->g; > + rgbL1->b2 = bayL1->b; > + > + rgbL0++; > + rgbL1++; > + > + bayL0++; > + bayL1++; > + } > + > + frame->seqRead_Length += cam->width*3*2; > + frame->curline += 2; > + } > + /* See if we filled the frame */ > + if (frame->curline == cam->height) { > + frame->frameState = FrameState_Done_Hold; > + frame->curline = 0; > + uvd->curframe = -1; > + uvd->stats.frame_num++; > + } > +} > + > +/* taken from konicawc */ > +static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw) > +{ > + int ret; > + int newsize; > + int oldsize; > + int x = vw->width; > + int y = vw->height; > + struct qcm *cam = (struct qcm *) uvd->user_data; > + > + if (x > 0 && y > 0) { > + DEBUG(2, "trying to find size %d,%d", x, y); > + for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { > + if ((camera_sizes[newsize].width == x) && > + (camera_sizes[newsize].height == y)) > + break; > + } > + } else > + newsize = cam->size; > + > + if (newsize > MAX_FRAME_SIZE) { > + DEBUG(1, "couldn't find size %d,%d", x, y); > + return -EINVAL; > + } > + > + if (newsize == cam->size) { > + DEBUG(1, "Nothing to do"); > + return 0; > + } > + > + qcm_stop_data(uvd); > + > + if (cam->size != newsize) { > + oldsize = cam->size; > + cam->size = newsize; > + ret = qcm_set_camera_size(uvd); > + if (ret) { > + err("Couldn't set camera size, err=%d",ret); > + /* restore the original size */ > + cam->size = oldsize; > + return ret; > + } > + } > + > + /* Flush the input queue and clear any current frame in progress */ > + > + RingQueue_Flush(&uvd->dp); > + if (uvd->curframe != -1) { > + uvd->frame[uvd->curframe].curline = 0; > + uvd->frame[uvd->curframe].seqRead_Length = 0; > + uvd->frame[uvd->curframe].seqRead_Index = 0; > + } > + > + CHECK_RET(ret, qcm_start_data(uvd)); > + return 0; > +} > + > +static int qcm_configure_video(struct uvd *uvd) > +{ > + int ret; > + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); > + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); > + > + uvd->vpic.colour = colour; > + uvd->vpic.hue = hue; > + uvd->vpic.brightness = brightness; > + uvd->vpic.contrast = contrast; > + uvd->vpic.whiteness = whiteness; > + uvd->vpic.depth = 24; > + uvd->vpic.palette = VIDEO_PALETTE_RGB24; > + > + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); > + strcpy(uvd->vcap.name, "QCM USB Camera"); > + uvd->vcap.type = VID_TYPE_CAPTURE; > + uvd->vcap.channels = 1; > + uvd->vcap.audios = 0; > + > + uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width; > + uvd->vcap.minheight = camera_sizes[SIZE_160X120].height; > + uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width; > + uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height; > + > + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); > + uvd->vchan.flags = 0 ; > + uvd->vchan.tuners = 0; > + uvd->vchan.channel = 0; > + uvd->vchan.type = VIDEO_TYPE_CAMERA; > + strcpy(uvd->vchan.name, "Camera"); > + > + CHECK_RET(ret, qcm_sensor_init(uvd)); > + return 0; > +} > + > +static int qcm_probe(struct usb_interface *intf, > + const struct usb_device_id *devid) > +{ > + int err; > + struct uvd *uvd; > + struct usb_device *dev = interface_to_usbdev(intf); > + struct qcm *cam; > + size_t buffer_size; > + unsigned char video_ep; > + struct usb_host_interface *interface; > + struct usb_endpoint_descriptor *endpoint; > + int i,j; > + unsigned int ifacenum, ifacenum_inact=0; > + __le16 sensor_id; > + > + /* we don't support multiconfig cams */ > + if (dev->descriptor.bNumConfigurations != 1) > + return -ENODEV; > + > + /* first check for the video interface and not > + * the audio interface */ > + interface = &intf->cur_altsetting[0]; > + if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) > + || (interface->desc.bInterfaceSubClass != > + USB_CLASS_VENDOR_SPEC)) > + return -ENODEV; > + > + /* > + walk through each endpoint in each setting in the interface > + stop when we find the one that's an isochronous IN endpoint. > + */ > + for (i=0; i < intf->num_altsetting; i++) { > + interface = &intf->cur_altsetting[i]; > + ifacenum = interface->desc.bAlternateSetting; > + /* walk the end points */ > + for (j=0; j < interface->desc.bNumEndpoints; j++) { > + endpoint = &interface->endpoint[j].desc; > + > + if ((endpoint->bEndpointAddress & > + USB_ENDPOINT_DIR_MASK) != USB_DIR_IN) > + continue; /* not input then not good */ > + > + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); > + if (!buffer_size) { > + ifacenum_inact = ifacenum; > + continue; /* 0 pkt size is not what we want */ > + } > + > + if ((endpoint->bmAttributes & > + USB_ENDPOINT_XFERTYPE_MASK) == > + USB_ENDPOINT_XFER_ISOC) { > + video_ep = endpoint->bEndpointAddress; > + /* break out of the search */ > + goto good_videoep; > + } > + } > + } > + /* failed out since nothing useful was found */ > + err("No suitable endpoint was found\n"); > + return -ENODEV; > + > +good_videoep: > + /* disable isochronous stream before doing anything else */ > + err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); > + if (err < 0) { > + err("Failed to disable sensor stream"); > + return -EIO; > + } > + > + /* > + Check that this is the same unknown sensor that is known to work. This > + sensor is suspected to be the ST VV6422C001. I'll check the same value > + that the qc-usb driver checks. This value is probably not even the > + sensor ID since it matches the USB dev ID. Oh well. If it doesn't > + match, it's probably a diff sensor so exit and apologize. > + */ > + err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id); > + if (err < 0) { > + err("Couldn't read sensor values. Err %d\n",err); > + return err; > + } > + if (sensor_id != cpu_to_le16(0x08F0)) { > + err("Sensor ID %x != %x. Unsupported. Sorry\n", > + le16_to_cpu(sensor_id), (0x08F0)); > + return -ENODEV; > + } > + > + uvd = usbvideo_AllocateDevice(cams); > + if (!uvd) > + return -ENOMEM; > + > + cam = (struct qcm *) uvd->user_data; > + > + /* buf for doing demosaicing */ > + cam->scratch = kmalloc(324*2, GFP_KERNEL); > + if (!cam->scratch) /* uvd freed in dereg */ > + return -ENOMEM; > + > + /* yes, if we fail after here, cam->scratch gets freed > + by qcm_free_uvd */ > + > + err = qcm_alloc_int_urb(cam); > + if (err < 0) > + return err; > + > + /* yes, if we fail after here, int urb gets freed > + by qcm_free_uvd */ > + > + RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); > + cam->width = camera_sizes[size].width; > + cam->height = camera_sizes[size].height; > + cam->size = size; > + > + uvd->debug = debug; > + uvd->flags = 0; > + uvd->dev = dev; > + uvd->iface = intf->altsetting->desc.bInterfaceNumber; > + uvd->ifaceAltActive = ifacenum; > + uvd->ifaceAltInactive = ifacenum_inact; > + uvd->video_endp = video_ep; > + uvd->iso_packet_len = buffer_size; > + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; > + uvd->defaultPalette = VIDEO_PALETTE_RGB24; > + uvd->canvas = VIDEOSIZE(320, 240); > + uvd->videosize = VIDEOSIZE(cam->width, cam->height); > + err = qcm_configure_video(uvd); > + if (err) { > + err("failed to configure video settings"); > + return err; > + } > + > + err = usbvideo_RegisterVideoDevice(uvd); > + if (err) { /* the uvd gets freed in Deregister */ > + err("usbvideo_RegisterVideoDevice() failed."); > + return err; > + } > + > + uvd->max_frame_size = (320 * 240 * 3); > + qcm_register_input(cam, dev); > + usb_set_intfdata(intf, uvd); > + return 0; > +} > + > +static void qcm_free_uvd(struct uvd *uvd) > +{ > + struct qcm *cam = (struct qcm *) uvd->user_data; > + > + kfree(cam->scratch); > + qcm_unregister_input(cam); > + qcm_free_int(cam); > +} > + > +static struct usbvideo_cb qcm_driver = { > + .probe = qcm_probe, > + .setupOnOpen = qcm_setup_on_open, > + .processData = qcm_process_isoc, > + .setVideoMode = qcm_set_video_mode, > + .startDataPump = qcm_start_data, > + .stopDataPump = qcm_stop_data, > + .adjustPicture = qcm_adjust_picture, > + .userFree = qcm_free_uvd > +}; > + > +static int __init qcm_init(void) > +{ > + info(DRIVER_DESC " " DRIVER_VERSION); > + > + return usbvideo_register( > + &cams, > + MAX_CAMERAS, > + sizeof(struct qcm), > + "QCM", > + &qcm_driver, > + THIS_MODULE, > + qcm_table); > +} > + > +static void __exit qcm_exit(void) > +{ > + usbvideo_Deregister(&cams); > +} > + > +module_param(size, int, 0); > +MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 320x240"); > +module_param(colour, int, 0); > +MODULE_PARM_DESC(colour, "Initial colour"); > +module_param(hue, int, 0); > +MODULE_PARM_DESC(hue, "Initial hue"); > +module_param(brightness, int, 0); > +MODULE_PARM_DESC(brightness, "Initial brightness"); > +module_param(contrast, int, 0); > +MODULE_PARM_DESC(contrast, "Initial contrast"); > +module_param(whiteness, int, 0); > +MODULE_PARM_DESC(whiteness, "Initial whiteness"); > + > +#ifdef CONFIG_USB_DEBUG > +module_param(debug, int, S_IRUGO | S_IWUSR); > +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); > +#endif > + > +module_init(qcm_init); > +module_exit(qcm_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Jaya Kumar"); > +MODULE_DESCRIPTION("QCM USB Camera"); > +MODULE_SUPPORTED_DEVICE("QCM USB Camera"); > diff -X linux-2.6.17-rc4/Documentation/dontdiff -uprN linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.h linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.h > --- linux-2.6.17-rc4-vanilla/drivers/media/video/usbvideo/quickcam_messenger.h 1970-01-01 07:30:00.000000000 +0730 > +++ linux-2.6.17-rc4/drivers/media/video/usbvideo/quickcam_messenger.h 2006-05-17 08:37:05.000000000 +0800 > @@ -0,0 +1,126 @@ > +#ifndef quickcam_messenger_h > +#define quickcam_messenger_h > + > +#ifndef CONFIG_INPUT > +/* if we're not using input we dummy out these functions */ > +#define qcm_register_input(...) > +#define qcm_unregister_input(...) > +#define qcm_report_buttonstat(...) > +#define qcm_setup_input_int(...) 0 > +#define qcm_stop_int_data(...) > +#define qcm_alloc_int_urb(...) 0 > +#define qcm_free_int(...) > +#endif > + > + > +#define CHECK_RET(ret, expr) \ > + if ((ret = expr) < 0) return ret > + > +/* Control Registers for the STVV6422 ASIC > + * - this define is taken from the qc-usb-messenger code > + */ > +#define STV_ISO_ENABLE 0x1440 > +#define ISOC_PACKET_SIZE 1023 > + > +/* Chip identification number including revision indicator */ > +#define CMOS_SENSOR_IDREV 0xE00A > + > +struct rgb { > + u8 b; > + u8 g; > + u8 r; > + u8 b2; > + u8 g2; > + u8 r2; > +}; > + > +struct bayL0 { > +#ifdef __BIG_ENDIAN > + u8 r; > + u8 g; > +#elif __LITTLE_ENDIAN > + u8 g; > + u8 r; > +#else > +#error not byte order defined > +#endif > +}; > + > +struct bayL1 { > +#ifdef __BIG_ENDIAN > + u8 g; > + u8 b; > +#elif __LITTLE_ENDIAN > + u8 b; > + u8 g; > +#else > +#error not byte order defined > +#endif > +}; > + > +struct cam_size { > + u16 width; > + u16 height; > + u8 cmd; > +}; > + > +static const struct cam_size camera_sizes[] = { > + { 160, 120, 0xf }, > + { 320, 240, 0x2 }, > +}; > + > +enum frame_sizes { > + SIZE_160X120 = 0, > + SIZE_320X240 = 1, > +}; > + > +#define MAX_FRAME_SIZE SIZE_320X240 > + > +struct qcm { > + u16 colour; > + u16 hue; > + u16 brightness; > + u16 contrast; > + u16 whiteness; > + > + u8 size; > + int height; > + int width; > + u8 *scratch; > + struct urb *button_urb; > + u8 button_sts; > + u8 button_sts_buf; > + > +#ifdef CONFIG_INPUT > + struct input_dev *input; > + char input_physname[64]; > +#endif > +}; > + > +struct regval { > + u16 reg; > + u8 val; > +}; > +/* this table is derived from the > +qc-usb-messenger code */ > +static const struct regval regval_table[] = { > + { STV_ISO_ENABLE, 0x00 }, > + { 0x1436, 0x00 }, { 0x1432, 0x03 }, > + { 0x143a, 0xF9 }, { 0x0509, 0x38 }, > + { 0x050a, 0x38 }, { 0x050b, 0x38 }, > + { 0x050c, 0x2A }, { 0x050d, 0x01 }, > + { 0x1431, 0x00 }, { 0x1433, 0x34 }, > + { 0x1438, 0x18 }, { 0x1439, 0x00 }, > + { 0x143b, 0x05 }, { 0x143c, 0x00 }, > + { 0x143e, 0x01 }, { 0x143d, 0x00 }, > + { 0x1442, 0xe2 }, { 0x1500, 0xd0 }, > + { 0x1500, 0xd0 }, { 0x1500, 0x50 }, > + { 0x1501, 0xaf }, { 0x1502, 0xc2 }, > + { 0x1503, 0x45 }, { 0x1505, 0x02 }, > + { 0x150e, 0x8e }, { 0x150f, 0x37 }, > + { 0x15c0, 0x00 }, > +}; > + > +static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF }; > + > +#endif /* quickcam_messenger_h */ > > > ------------------------------------------------------- > Using Tomcat but need to do more? Need to support web services, security? > Get stuff done quickly with pre-integrated technology to make your job easier > Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo > http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 > _______________________________________________ > linux-usb-devel@lists.sourceforge.net > To unsubscribe, use the last form field at: > https://lists.sourceforge.net/lists/listinfo/linux-usb-devel >
[ My apologies again. Adding Mauro and video4linux-list to CC list. ] - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |