diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Fri Dec 12 15:06:58 2003 +++ b/CREDITS Fri Dec 12 15:06:58 2003 @@ -2669,6 +2669,13 @@ S: 70110 Kuopio S: Finland +N: Luca Risolia +E: luca_ing@libero.it +D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip +S: Via Libertà 41/a +S: Osio Sotto, 24046, Bergamo +S: Italy + N: William E. Roadcap E: roadcapw@cfw.com W: http://www.cfw.com/~roadcapw @@ -3569,4 +3576,4 @@ # alphabetically. Leonard used to be very proud of being the # last entry, and he'll get positively pissed if he can't even # be second-to-last. (and this file really _is_ supposed to be -# in alphabetic order) +# in alphabetic order) diff -Nru a/Documentation/usb/w9968cf.txt b/Documentation/usb/w9968cf.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/usb/w9968cf.txt Fri Dec 12 15:06:58 2003 @@ -0,0 +1,463 @@ + + W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.6 + ============================================================== + + - Documentation - + + +Index +===== +1. Copyright +2. License +3. Overview +4. Supported devices +5. Kernel configuration and third-part module compilation +6. Module loading +7. Module paramaters +8. Credits + + +1. Copyright +============ +Copyright (C) 2002 2003 by Luca Risolia + + +2. License +========== +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. + + +3. Overview +=========== +This driver supports the video streaming capabilities of the devices mounting +Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they +are being commanded by USB. + +The driver relies on the Video4Linux, USB and I2C core modules of the Linux +kernel, version 2.6.0 or greater, and is not compatible in any way with +previous versions. It has been designed to run properly on SMP systems +as well. At the moment, an additional module, "ovcamchip", is mandatory; it +provides support for some OmniVision CMOS sensors connected to the W996[87]CF +chips. + +The driver is split into two modules: the basic one, "w9968cf", is needed for +the supported devices to work; the second one, "w9968cf-vpp", is an optional +module, which provides some useful video post-processing functions like video +decoding, up-scaling and colour conversions. These routines can't be included +into official kernels for performance purposes. Once the driver is installed, +every time an application tries to open a recognized device, "w9968cf" checks +the presence of the "w9968cf-vpp" module and loads it automatically by default. + +Up to 32 cameras can be handled at the same time. They can be connected and +disconnected from the host many times without turning off the computer, if +your system supports the hotplug facility. + +To change the default settings for each camera, many paramaters can be passed +through command line when the module is loaded into memory. + +The latest and full featured version of the W996[87]CF driver can be found at: +http://go.lamarinapunto.com/ + +The "ovcamchip" module is part of the OV511 driver, version 2.25, which can be +downloaded from internet: +http://alpha.dyndns.org/ov511/ +To know how to patch, compile and load it, read the paragraphs below. + + +4. Supported devices +==================== +At the moment, known W996[87]CF based devices are: +- Aroma Digi Pen ADG-5000 Refurbished +- AVerTV USB +- Creative Labs Video Blaster WebCam Go +- Creative Labs Video Blaster WebCam Go Plus +- Die Lebon LDC-D35A Digital Kamera +- Ezonics EZ-802 EZMega Cam +- OPCOM Digi Pen VGA Dual Mode Pen Camera + +If you know any other W996[87]CF based cameras, please contact me. + +The list above does NOT imply that all those devices work with this driver: up +until now only webcams that have a CMOS sensor supported by the "ovcamchip" +module work. +For a list of supported CMOS sensors, please visit the module author homepage: +http://alpha.dyndns.org/ov511/ + +Possible external microcontrollers of those webcams are not supported: this +means that still images can't be downloaded from the device memory. + +Furthermore, it's worth to note that I was only able to run tests on my +"Creative Labs Video Blaster WebCam Go". Donations of other models, for +additional testing and full support, would be much appreciated. + + +5. Kernel configuration and third-part module compilation +========================================================= +As noted above, kernel 2.6.0 is the minimum for this driver; for it to work +properly, the driver needs kernel support for Video4Linux, USB and I2C, and a +third-part module for the CMOS sensor. + +The following options of the kernel configuration file must be enabled and +corresponding modules must be compiled: + + # Multimedia devices + # + CONFIG_VIDEO_DEV=m + + # I2C support + # + CONFIG_I2C=m + +The I2C core module can be compiled statically in the kernel as well. + + # USB support + # + CONFIG_USB=m + +In addition, depending on the hardware being used, just one of the modules +below is necessary: + + # USB Host Controller Drivers + # + CONFIG_USB_EHCI_HCD=m + CONFIG_USB_UHCI_HCD=m + CONFIG_USB_OHCI_HCD=m + +Also, make sure "Enforce bandwidth allocation" is NOT enabled. + + # USB Multimedia devices + # + CONFIG_USB_W9968CF=m + +The last module we need is "ovcamchip.o". To obtain it, you have to download +the OV511 driver, version 2.25 - don't use other versions - which is available +at http://alpha.dyndns.org/ov511/ . Then you have to download the latest +version of the full featured W996[87]CF driver, which contains a patch for the +"ovcamchip" module; it is available at http://go.lamarinapunto.com . +Once you have obtained the packages, decompress, patch and compile the +"ovcamchip" module. In other words: + + [user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz + [user@localhost home]$ tar xvjf ov511-2.25.tar.bz2 + [user@localhost home]$ cd ov511-2.25 + [user@localhost ov511-2.25]$ patch -p1 < \ + /path/to/w9968cf-x.x/ov511-2.25.patch + [user@localhost ov511-2.25]$ make + +It's worth to note that the full featured version of the W996[87]CF driver +can also be installed overwriting the one in the kernel; in this case, read the +documentation included in the package. + +If everything went well, the W996[87]CF driver can be immediatly used (see next +paragraph). + + +6. Module loading +================= +To use the driver, it is necessary to load the "w9968cf" module into memory +after every other module required. + +For example, loading can be done this way, as root: + + [root@localhost home]# modprobe usbcore + [root@localhost home]# modprobe i2c-core + [root@localhost ov511-x.xx]# insmod ./ovcamchip.ko + [root@localhost home]# modprobe w9968cf + +At this point the devices should be recognized: "dmesg" can be used to analyze +kernel messages: + + [user@localhost home]$ dmesg + +There are a lot of parameters the module can use to change the default +settings for each device. To list every possible parameter with a brief +explanation about them and which syntax to use, it is recommended to run the +"modinfo" command: + + [root@locahost home]# modinfo w9968cf + + +7. Module paramaters +==================== + +Module paramaters are listed below: +------------------------------------------------------------------------------- +Name: vppmod_load +Type: int +Syntax: <0|1> +Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled. + If enabled, every time an application attempts to open a + camera, 'insmod' searches for the video post-processing module + in the system and loads it automatically (if present). + The 'w9968cf-vpp' module adds extra image manipulation + capabilities to the 'w9968cf' module,like software up-scaling, + colour conversions and video decoding. +Default: 1 +------------------------------------------------------------------------------- +Name: simcams +Type: int +Syntax: +Description: Number of cameras allowed to stream simultaneously. + n may vary from 0 to 32. +Default: 32 +------------------------------------------------------------------------------- +Name: video_nr +Type: int array (min = 0, max = 32) +Syntax: <-1|n[,...]> +Description: Specify V4L minor mode number. + -1 = use next available + n = use minor number n + You can specify 32 cameras this way. + For example: + video_nr=-1,2,-1 would assign minor number 2 to the second + recognized camera and use auto for the first one and for every + other camera. +Default: -1 +------------------------------------------------------------------------------- +Name: packet_size +Type: int array (min = 0, max = 32) +Syntax: +Description: Specify the maximum data payload size in bytes for alternate + settings, for each device. n is scaled between 63 and 1023. +Default: 1023 +------------------------------------------------------------------------------- +Name: max_buffers +Type: int array (min = 0, max = 32) +Syntax: +Description: Only for advanced users. + Specify the maximum number of video frame buffers to allocate + for each device, from 2 to 32. +Default: 2 +------------------------------------------------------------------------------- +Name: double_buffer +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Hardware double buffering: 0 disabled, 1 enabled. + It should be enabled if you want smooth video output: if you + obtain out of sync. video, disable it at all, or try to + decrease the 'clockdiv' module paramater value. +Default: 1 for every device. +------------------------------------------------------------------------------- +Name: clamping +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Video data clamping: 0 disabled, 1 enabled. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: filter_type +Type: int array (min = 0, max = 32) +Syntax: <0|1|2[,...]> +Description: Video filter type. + 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter. + The filter is used to reduce noise and aliasing artifacts + produced by the CCD or CMOS sensor. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: largeview +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Large view: 0 disabled, 1 enabled. +Default: 1 for every device. +------------------------------------------------------------------------------- +Name: upscaling +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Software scaling (for non-compressed video only): + 0 disabled, 1 enabled. + Disable it if you have a slow CPU or you don't have enough + memory. +Default: 0 for every device. +Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 0. +------------------------------------------------------------------------------- +Name: decompression +Type: int array (min = 0, max = 32) +Syntax: <0|1|2[,...]> +Description: Software video decompression: + 0 = disables decompression + (doesn't allow formats needing decompression). + 1 = forces decompression + (allows formats needing decompression only). + 2 = allows any permitted formats. + Formats supporting (de)compressed video are YUV422P and + YUV420P/YUV420 in any resolutions where width and height are + multiples of 16. +Default: 2 for every device. +Note: If 'w9968cf-vpp' is not loaded, forcing decompression is not + allowed; in this case this paramater is set to 2. +------------------------------------------------------------------------------- +Name: force_palette +Type: int array (min = 0, max = 32) +Syntax: <0|9|10|13|15|8|7|1|6|3|4|5[,...]> +Description: Force picture palette. + In order: + 0 = Off - allows any of the following formats: + 9 = UYVY 16 bpp - Original video, compression disabled + 10 = YUV420 12 bpp - Original video, compression enabled + 13 = YUV422P 16 bpp - Original video, compression enabled + 15 = YUV420P 12 bpp - Original video, compression enabled + 8 = YUVY 16 bpp - Software conversion from UYVY + 7 = YUV422 16 bpp - Software conversion from UYVY + 1 = GREY 8 bpp - Software conversion from UYVY + 6 = RGB555 16 bpp - Software conversion from UYVY + 3 = RGB565 16 bpp - Software conversion from UYVY + 4 = RGB24 24 bpp - Software conversion from UYVY + 5 = RGB32 32 bpp - Software conversion from UYVY + When not 0, this paramater will override 'decompression'. +Default: 0 for every device. Initial palette is 9 (UYVY). +Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9. +------------------------------------------------------------------------------- +Name: force_rgb +Type: int array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Read RGB video data instead of BGR: + 1 = use RGB component ordering. + 0 = use BGR component ordering. + This parameter has effect when using RGBX palettes only. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: autobright +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: CMOS sensor automatically changes brightness: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: autoexp +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: CMOS sensor automatically changes exposure: + 0 = no, 1 = yes +Default: 1 for every device. +------------------------------------------------------------------------------- +Name: lightfreq +Type: long array (min = 0, max = 32) +Syntax: <50|60[,...]> +Description: Light frequency in Hz: + 50 for European and Asian lighting, 60 for American lighting. +Default: 50 for every device. +------------------------------------------------------------------------------- +Name: bandingfilter +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Banding filter to reduce effects of fluorescent + lighting: + 0 disabled, 1 enabled. + This filter tries to reduce the pattern of horizontal + light/dark bands caused by some (usually fluorescent) lighting. +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: clockdiv +Type: long array (min = 0, max = 32) +Syntax: <-1|n[,...]> +Description: Force pixel clock divisor to a specific value (for experts): + n may vary from 0 to 127. + -1 for automatic value. + See also the 'double_buffer' module paramater. +Default: -1 for every device. +------------------------------------------------------------------------------- +Name: backlight +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Objects are lit from behind: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: mirror +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: Reverse image horizontally: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: sensor_mono +Type: long array (min = 0, max = 32) +Syntax: <0|1[,...]> +Description: The CMOS sensor is monochrome: + 0 = no, 1 = yes +Default: 0 for every device. +------------------------------------------------------------------------------- +Name: brightness +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture brightness (0-65535). + This parameter has no effect if 'autobright' is enabled. +Default: 31000 for every device. +------------------------------------------------------------------------------- +Name: hue +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture hue (0-65535). +Default: 32768 for every device. +------------------------------------------------------------------------------- +Name: colour +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture saturation (0-65535). +Default: 32768 for every device. +------------------------------------------------------------------------------- +Name: contrast +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture contrast (0-65535). +Default: 50000 for every device. +------------------------------------------------------------------------------- +Name: whiteness +Type: long array (min = 0, max = 32) +Syntax: +Description: Set picture whiteness (0-65535). +Default: 32768 for every device. +------------------------------------------------------------------------------- +Name: debug +Type: int +Syntax: +Description: Debugging information level, from 0 to 6: + 0 = none (be cautious) + 1 = critical errors + 2 = significant informations + 3 = configuration or general messages + 4 = warnings + 5 = called functions + 6 = function internals + Level 5 and 6 are useful for testing only, when just one + device is used. +Default: 2 +------------------------------------------------------------------------------- +Name: specific_debug +Type: int +Syntax: <0|1> +Description: Enable or disable specific debugging messages: + 0 = print messages concerning every level <= 'debug' level. + 1 = print messages concerning the level indicated by 'debug'. +Default: 0 +------------------------------------------------------------------------------- + + +8. Credits +========== +The development would not have proceed much further without having looked at +the source code of other drivers and without the help of several persons; in +particular: + +- the I2C interface to kernel and high-level CMOS sensor control routines have + been taken from the OV511 driver by Mark McClelland; + +- memory management code has been copied from the bttv driver by Ralph Metzler, + Marcus Metzler and Gerd Knorr; + +- the low-level I2C read function has been written by Frédéric Jouault, who + also gave me commented logs about sniffed USB traffic taken from another + driver for another system; + +- the low-level I2C fast write function has been written by Piotr Czerczak; diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Fri Dec 12 15:06:58 2003 +++ b/MAINTAINERS Fri Dec 12 15:06:58 2003 @@ -2218,6 +2218,13 @@ L: linux-usb-devel@lists.sourceforge.net S: Maintained +USB W996[87]CF DRIVER +P: Luca Risolia +M: luca_ing@libero.it +L: linux-usb-devel@lists.sourceforge.net +W: http://go.lamarinapunto.com +S: Maintained + USER-MODE LINUX P: Jeff Dike M: jdike@karaya.com diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/Makefile Fri Dec 12 15:06:58 2003 @@ -34,6 +34,7 @@ obj-$(CONFIG_USB_SE401) += media/ obj-$(CONFIG_USB_STV680) += media/ obj-$(CONFIG_USB_VICAM) += media/ +obj-$(CONFIG_USB_W9968CF) += media/ obj-$(CONFIG_USB_CATC) += net/ obj-$(CONFIG_USB_KAWETH) += net/ @@ -58,4 +59,3 @@ obj-$(CONFIG_USB_TEST) += misc/ obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_USS720) += misc/ - diff -Nru a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c --- a/drivers/usb/class/cdc-acm.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/class/cdc-acm.c Fri Dec 12 15:06:58 2003 @@ -1,5 +1,5 @@ /* - * acm.c Version 0.22 + * cdc-acm.c * * Copyright (c) 1999 Armin Fuerst * Copyright (c) 1999 Pavel Machek @@ -26,6 +26,7 @@ * v0.21 - revert to probing on device for devices with multiple configs * v0.22 - probe only the control interface. if usbcore doesn't choose the * config we want, sysadmin changes bConfigurationValue in sysfs. + * v0.23 - use softirq for rx processing, as needed by tty layer */ /* @@ -44,6 +45,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#undef DEBUG + #include #include #include @@ -54,14 +57,13 @@ #include #include #include -#undef DEBUG #include #include /* * Version Information */ -#define DRIVER_VERSION "v0.21" +#define DRIVER_VERSION "v0.23" #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" @@ -146,7 +148,8 @@ struct tty_struct *tty; /* the corresponding tty */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct acm_line line; /* line coding (bits, stop, parity) */ - struct work_struct work; /* work queue entry for line discipline waking up */ + struct work_struct work; /* work queue entry for line discipline waking up */ + struct tasklet_struct bh; /* rx processing */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ unsigned int writesize; /* max packet size for the output bulk endpoint */ @@ -184,9 +187,10 @@ #define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0) /* - * Interrupt handler for various ACM control events + * Interrupt handlers for various ACM device responses */ +/* control interface reports status changes with "interrupt" transfers */ static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs) { struct acm *acm = urb->context; @@ -251,20 +255,30 @@ __FUNCTION__, status); } +/* data interface returns incoming bytes, or we got unthrottled */ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs) { struct acm *acm = urb->context; - struct tty_struct *tty = acm->tty; - unsigned char *data = urb->transfer_buffer; - int i = 0; if (!ACM_READY(acm)) return; if (urb->status) - dbg("nonzero read bulk status received: %d", urb->status); + dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status); + + /* calling tty_flip_buffer_push() in_irq() isn't allowed */ + tasklet_schedule(&acm->bh); +} + +static void acm_rx_tasklet(unsigned long _acm) +{ + struct acm *acm = (void *)_acm; + struct urb *urb = acm->readurb; + struct tty_struct *tty = acm->tty; + unsigned char *data = urb->transfer_buffer; + int i = 0; - if (!urb->status && !acm->throttle) { + if (urb->actual_length > 0 && !acm->throttle) { for (i = 0; i < urb->actual_length && !acm->throttle; i++) { /* if we insert more than TTY_FLIPBUF_SIZE characters, * we drop them. */ @@ -285,10 +299,12 @@ urb->actual_length = 0; urb->dev = acm->dev; - if (usb_submit_urb(urb, GFP_ATOMIC)) - dbg("failed resubmitting read urb"); + i = usb_submit_urb(urb, GFP_ATOMIC); + if (i) + dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i); } +/* data interface wrote those outgoing bytes */ static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) { struct acm *acm = (struct acm *)urb->context; @@ -621,6 +637,8 @@ acm->minor = minor; acm->dev = dev; + acm->bh.func = acm_rx_tasklet; + acm->bh.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint, acm); if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { diff -Nru a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c --- a/drivers/usb/class/usblp.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/class/usblp.c Fri Dec 12 15:06:57 2003 @@ -610,7 +610,7 @@ if (!usblp->wcomplete) { barrier(); if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + return writecount ? writecount : -EAGAIN; timeout = USBLP_WRITE_TIMEOUT; add_wait_queue(&usblp->wait, &wait); @@ -673,8 +673,12 @@ usblp->writeurb->dev = usblp->dev; usblp->wcomplete = 0; - if (usb_submit_urb(usblp->writeurb, GFP_KERNEL)) { - count = -EIO; + err = usb_submit_urb(usblp->writeurb, GFP_KERNEL); + if (err) { + if (err != -ENOMEM) + count = -EIO; + else + count = writecount ? writecount : -ENOMEM; up (&usblp->sem); break; } @@ -706,8 +710,6 @@ goto done; } - // FIXME: only use urb->status inside completion - // callbacks; this way is racey... add_wait_queue(&usblp->wait, &wait); while (1==1) { if (signal_pending(current)) { @@ -1093,7 +1095,7 @@ /* First two bytes are length in big-endian. * They count themselves, and we copy them into * the user's buffer. */ - length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; + length = be16_to_cpu(*((u16 *)usblp->device_id_string)); if (length < 2) length = 2; else if (length >= USBLP_DEVICE_ID_SIZE) diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/core/hcd.c Fri Dec 12 15:06:58 2003 @@ -1165,6 +1165,7 @@ struct device *sys = 0; unsigned long flags; struct completion_splice splice; + struct list_head *tmp; int retval; if (!urb) @@ -1203,7 +1204,12 @@ */ WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT); - if (!urb->hcpriv) { + /* insist the urb is still queued */ + list_for_each(tmp, &dev->urb_list) { + if (tmp == &urb->urb_list) + break; + } + if (tmp != &urb->urb_list) { retval = -EINVAL; goto done; } diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/core/hub.c Fri Dec 12 15:06:57 2003 @@ -126,14 +126,20 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs) { struct usb_hub *hub = (struct usb_hub *)urb->context; - unsigned long flags; int status; + spin_lock(&hub_event_lock); + hub->urb_active = 0; + if (hub->urb_complete) { /* disconnect or rmmod */ + complete(hub->urb_complete); + goto done; + } + switch (urb->status) { case -ENOENT: /* synchronous unlink */ case -ECONNRESET: /* async unlink */ case -ESHUTDOWN: /* hardware going away */ - return; + goto done; default: /* presumably an error */ /* Cause a hub reset after 10 consecutive errors */ @@ -151,18 +157,20 @@ hub->nerrors = 0; /* Something happened, let khubd figure it out */ - spin_lock_irqsave(&hub_event_lock, flags); if (list_empty(&hub->event_list)) { list_add(&hub->event_list, &hub_event_list); wake_up(&khubd_wait); } - spin_unlock_irqrestore(&hub_event_lock, flags); resubmit: if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0 /* ENODEV means we raced disconnect() */ && status != -ENODEV) dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status); + if (status == 0) + hub->urb_active = 1; +done: + spin_unlock(&hub_event_lock); } /* USB 2.0 spec Section 11.24.2.3 */ @@ -467,7 +475,8 @@ message = "couldn't submit status urb"; goto fail; } - + hub->urb_active = 1; + /* Wake up khubd */ wake_up(&khubd_wait); @@ -485,6 +494,7 @@ static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); + DECLARE_COMPLETION(urb_complete); unsigned long flags; if (!hub) @@ -492,12 +502,11 @@ usb_set_intfdata (intf, NULL); spin_lock_irqsave(&hub_event_lock, flags); + hub->urb_complete = &urb_complete; /* Delete it and then reset it */ - list_del(&hub->event_list); - INIT_LIST_HEAD(&hub->event_list); - list_del(&hub->hub_list); - INIT_LIST_HEAD(&hub->hub_list); + list_del_init(&hub->event_list); + list_del_init(&hub->hub_list); spin_unlock_irqrestore(&hub_event_lock, flags); @@ -510,6 +519,8 @@ if (hub->urb) { usb_unlink_urb(hub->urb); + if (hub->urb_active) + wait_for_completion(&urb_complete); usb_free_urb(hub->urb); hub->urb = NULL; } diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/core/hub.h Fri Dec 12 15:06:57 2003 @@ -172,6 +172,8 @@ struct usb_hub { struct usb_interface *intf; /* the "real" device */ struct urb *urb; /* for interrupt polling pipe */ + struct completion *urb_complete; /* wait for urb to end */ + unsigned int urb_active:1; /* buffer for urb ... 1 bit each for hub and children, rounded up */ char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8]; diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c --- a/drivers/usb/core/message.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/core/message.c Fri Dec 12 15:06:57 2003 @@ -1086,6 +1086,11 @@ ret = -EINVAL; goto out; } + + /* The USB spec says configuration 0 means unconfigured. + * But if a device includes a configuration numbered 0, + * we will accept it as a correctly configured state. + */ if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); @@ -1101,7 +1106,7 @@ goto out; dev->actconfig = cp; - if (!configuration) + if (!cp) dev->state = USB_STATE_ADDRESS; else { dev->state = USB_STATE_CONFIGURED; diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/core/usb.c Fri Dec 12 15:06:58 2003 @@ -80,7 +80,7 @@ static int usb_generic_driver_data; -/* needs to be called with BKL held */ +/* called from driver core with usb_bus_type.subsys writelock */ int usb_probe_interface(struct device *dev) { struct usb_interface * intf = to_usb_interface(dev); @@ -93,12 +93,14 @@ if (!driver->probe) return error; + /* driver claim() doesn't yet affect dev->driver... */ + if (intf->driver) + return error; + id = usb_match_id (intf, driver->id_table); if (id) { dev_dbg (dev, "%s - got id\n", __FUNCTION__); - down (&driver->serialize); error = driver->probe (intf, id); - up (&driver->serialize); } if (!error) intf->driver = driver; @@ -106,23 +108,24 @@ return error; } +/* called from driver core with usb_bus_type.subsys writelock */ int usb_unbind_interface(struct device *dev) { struct usb_interface *intf = to_usb_interface(dev); - struct usb_driver *driver = to_usb_driver(dev->driver); - - down(&driver->serialize); + struct usb_driver *driver = intf->driver; /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); - if (intf->driver && intf->driver->disconnect) - intf->driver->disconnect(intf); - - /* force a release and re-initialize the interface */ - usb_driver_release_interface(driver, intf); + if (driver && driver->disconnect) + driver->disconnect(intf); - up(&driver->serialize); + /* reset other interface state */ + usb_set_interface(interface_to_usbdev(intf), + intf->altsetting[0].desc.bInterfaceNumber, + 0); + usb_set_intfdata(intf, NULL); + intf->driver = NULL; return 0; } @@ -152,8 +155,6 @@ new_driver->driver.probe = usb_probe_interface; new_driver->driver.remove = usb_unbind_interface; - init_MUTEX(&new_driver->serialize); - retval = driver_register(&new_driver->driver); if (!retval) { @@ -170,7 +171,7 @@ /** * usb_deregister - unregister a USB driver * @driver: USB operations of the driver to unregister - * Context: !in_interrupt (), must be called with BKL held + * Context: must be able to sleep * * Unlinks the specified driver from the internal USB driver list. * @@ -264,26 +265,22 @@ * Few drivers should need to use this routine, since the most natural * way to bind to an interface is to return the private data from * the driver's probe() method. + * + * Callers must own the driver model's usb bus writelock. So driver + * probe() entries don't need extra locking, but other call contexts + * may need to explicitly claim that lock. */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) { if (!iface || !driver) return -EINVAL; - /* this is mainly to lock against usbfs */ - lock_kernel(); - if (iface->driver) { - unlock_kernel(); - err ("%s driver booted %s off interface %p", - driver->name, iface->driver->name, iface); + if (iface->driver) return -EBUSY; - } else { - dbg("%s driver claimed interface %p", driver->name, iface); - } + /* FIXME should device_bind_driver() */ iface->driver = driver; usb_set_intfdata(iface, priv); - unlock_kernel(); return 0; } @@ -323,13 +320,22 @@ * usually won't need to call this. * * This call is synchronous, and may not be used in an interrupt context. + * Callers must own the driver model's usb bus writelock. So driver + * disconnect() entries don't need extra locking, but other call contexts + * may need to explicitly claim that lock. */ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) { /* this should never happen, don't release something that's not ours */ - if (iface->driver && iface->driver != driver) + if (!iface || !iface->driver || iface->driver != driver) return; + if (iface->dev.driver) { + /* FIXME should be the ONLY case here */ + device_release_driver(&iface->dev); + return; + } + usb_set_interface(interface_to_usbdev(iface), iface->altsetting[0].desc.bInterfaceNumber, 0); @@ -991,6 +997,7 @@ int err = -EINVAL; int i; int j; + int config; /* * Set the driver for the usb device to point to the "generic" driver. @@ -1108,18 +1115,30 @@ /* choose and set the configuration. that registers the interfaces * with the driver core, and lets usb device drivers bind to them. + * NOTE: should interact with hub power budgeting. */ + config = dev->config[0].desc.bConfigurationValue; if (dev->descriptor.bNumConfigurations != 1) { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + /* heuristic: Linux is more likely to have class + * drivers, so avoid vendor-specific interfaces. + */ + if (dev->config[i].interface[0]->altsetting + ->desc.bInterfaceClass + == USB_CLASS_VENDOR_SPEC) + continue; + config = dev->config[i].desc.bConfigurationValue; + break; + } dev_info(&dev->dev, "configuration #%d chosen from %d choices\n", - dev->config[0].desc.bConfigurationValue, + config, dev->descriptor.bNumConfigurations); } - err = usb_set_configuration(dev, - dev->config[0].desc.bConfigurationValue); + err = usb_set_configuration(dev, config); if (err) { dev_err(&dev->dev, "can't set config #%d, error %d\n", - dev->config[0].desc.bConfigurationValue, err); + config, err); goto fail; } diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c --- a/drivers/usb/host/ohci-dbg.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/host/ohci-dbg.c Fri Dec 12 15:06:58 2003 @@ -269,18 +269,19 @@ ohci_dump_status (controller, NULL, 0); if (controller->hcca) ohci_dbg (controller, - "hcca frame #%04x\n", controller->hcca->frame_no); + "hcca frame #%04x\n", OHCI_FRAME_NO(controller->hcca)); ohci_dump_roothub (controller, 1, NULL, 0); } static const char data0 [] = "DATA0"; static const char data1 [] = "DATA1"; -static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td) +static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label, + const struct td *td) { u32 tmp = le32_to_cpup (&td->hwINFO); - ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x", + ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n", label, td, (tmp & TD_DONE) ? " (DONE)" : "", td->urb, td->index, @@ -301,28 +302,28 @@ case TD_DP_OUT: pid = "OUT"; break; default: pid = "(bad pid)"; break; } - ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s", tmp, + ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s\n", tmp, TD_CC_GET(tmp), /* EC, */ toggle, (tmp & TD_DI) >> 21, pid, (tmp & TD_R) ? "R" : ""); cbp = le32_to_cpup (&td->hwCBP); be = le32_to_cpup (&td->hwBE); - ohci_dbg (ohci, " cbp %08x be %08x (len %d)", cbp, be, + ohci_dbg (ohci, " cbp %08x be %08x (len %d)\n", cbp, be, cbp ? (be + 1 - cbp) : 0); } else { unsigned i; - ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x", tmp, + ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp, TD_CC_GET(tmp), (tmp >> 24) & 0x07, (tmp & TD_DI) >> 21, tmp & 0x0000ffff); - ohci_dbg (ohci, " bp0 %08x be %08x", + ohci_dbg (ohci, " bp0 %08x be %08x\n", le32_to_cpup (&td->hwCBP) & ~0x0fff, le32_to_cpup (&td->hwBE)); for (i = 0; i < MAXPSW; i++) { u16 psw = le16_to_cpup (&td->hwPSW [i]); int cc = (psw >> 12) & 0x0f; - ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d", i, + ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i, psw, cc, (cc >= 0x0e) ? "OFFSET" : "SIZE", psw & 0x0fff); @@ -332,12 +333,13 @@ /* caller MUST own hcd spinlock if verbose is set! */ static void __attribute__((unused)) -ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose) +ohci_dump_ed (const struct ohci_hcd *ohci, const char *label, + const struct ed *ed, int verbose) { u32 tmp = ed->hwINFO; char *type = ""; - ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x", + ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n", label, ed, ed->state, edstring (ed->type), le32_to_cpup (&ed->hwNextED)); @@ -347,7 +349,7 @@ /* else from TDs ... control */ } ohci_dbg (ohci, - " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp), + " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", le32_to_cpu (tmp), 0x03ff & (le32_to_cpu (tmp) >> 16), (tmp & ED_DEQUEUE) ? " DQ" : "", (tmp & ED_ISO) ? " ISO" : "", @@ -356,7 +358,7 @@ 0x000f & (le32_to_cpu (tmp) >> 7), type, 0x007f & le32_to_cpu (tmp)); - ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s", + ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s\n", tmp = le32_to_cpup (&ed->hwHeadP), (ed->hwHeadP & ED_C) ? data1 : data0, (ed->hwHeadP & ED_H) ? " HALT" : "", @@ -541,24 +543,29 @@ if (temp == seen_count) { u32 info = ed->hwINFO; u32 scratch = cpu_to_le32p (&ed->hwINFO); + struct list_head *entry; + unsigned qlen = 0; + + /* qlen measured here in TDs, not urbs */ + list_for_each (entry, &ed->td_list) + qlen++; temp = snprintf (next, size, - " (%cs dev%d%s ep%d%s" + " (%cs dev%d ep%d%s-%s qlen %u" " max %d %08x%s%s)", (info & ED_LOWSPEED) ? 'l' : 'f', scratch & 0x7f, - (info & ED_ISO) ? " iso" : "", (scratch >> 7) & 0xf, (info & ED_IN) ? "in" : "out", + (info & ED_ISO) ? "iso" : "int", + qlen, 0x03ff & (scratch >> 16), scratch, - (info & ED_SKIP) ? " s" : "", + (info & ED_SKIP) ? " K" : "", (ed->hwHeadP & ED_H) ? " H" : ""); size -= temp; next += temp; - // FIXME some TD info too - if (seen_count < DBG_SCHED_LIMIT) seen [seen_count++] = ed; @@ -617,7 +624,7 @@ /* hcca */ if (ohci->hcca) ohci_dbg_sw (ohci, &next, &size, - "hcca frame 0x%04x\n", ohci->hcca->frame_no); + "hcca frame 0x%04x\n", OHCI_FRAME_NO(ohci->hcca)); /* other registers mostly affect frame timings */ rdata = readl (®s->fminterval); diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/host/ohci-hcd.c Fri Dec 12 15:06:57 2003 @@ -226,7 +226,7 @@ if (retval < 0) goto fail; if (ed->type == PIPE_ISOCHRONOUS) { - u16 frame = le16_to_cpu (ohci->hcca->frame_no); + u16 frame = OHCI_FRAME_NO(ohci->hcca); /* delay a few frames before the first TD */ frame += max_t (u16, 8, ed->interval); @@ -281,7 +281,7 @@ urb_priv = urb->hcpriv; if (urb_priv) { if (urb_priv->ed->state == ED_OPER) - start_urb_unlink (ohci, urb_priv->ed); + start_ed_unlink (ohci, urb_priv->ed); } } else { /* @@ -363,7 +363,7 @@ { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - return le16_to_cpu (ohci->hcca->frame_no); + return OHCI_FRAME_NO(ohci->hcca); } /*-------------------------------------------------------------------------* @@ -591,7 +591,7 @@ */ spin_lock (&ohci->lock); if (ohci->ed_rm_list) - finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no), + finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca), ptregs); if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && HCD_IS_RUNNING(ohci->hcd.state)) diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c --- a/drivers/usb/host/ohci-q.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/host/ohci-q.c Fri Dec 12 15:06:58 2003 @@ -430,7 +430,7 @@ * put the ep on the rm_list * real work is done at the next start frame (SF) hardware interrupt */ -static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed) +static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed) { ed->hwINFO |= ED_DEQUEUE; ed->state = ED_UNLINK; @@ -441,7 +441,7 @@ * behave. frame_no wraps every 2^16 msec, and changes right before * SF is triggered. */ - ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1; + ed->tick = OHCI_FRAME_NO(ohci->hcca) + 1; /* rm_list is just singly linked, for simplicity */ ed->ed_next = ohci->ed_rm_list; @@ -479,7 +479,8 @@ * and iso; other urbs rarely need more than one TD per urb. * this way, only final tds (or ones with an error) cause IRQs. * at least immediately; use DI=6 in case any control request is - * tempted to die part way through. + * tempted to die part way through. (and to force the hc to flush + * its donelist soonish, even on unlink paths.) * * NOTE: could delay interrupts even for the last TD, and get fewer * interrupts ... increasing per-urb latency by sharing interrupts. @@ -879,12 +880,27 @@ u32 *prev; /* only take off EDs that the HC isn't using, accounting for - * frame counter wraps. + * frame counter wraps and EDs with partially retired TDs */ - if (tick_before (tick, ed->tick) - && HCD_IS_RUNNING(ohci->hcd.state)) { - last = &ed->ed_next; - continue; + if (likely (HCD_IS_RUNNING(ohci->hcd.state))) { + if (tick_before (tick, ed->tick)) { +skip_ed: + last = &ed->ed_next; + continue; + } + + if (!list_empty (&ed->td_list)) { + struct td *td; + u32 head; + + td = list_entry (ed->td_list.next, struct td, + td_list); + head = cpu_to_le32 (ed->hwHeadP) & TD_MASK; + + /* INTR_WDH may need to clean up first */ + if (td->td_dma != head) + goto skip_ed; + } } /* reentrancy: if we drop the schedule lock, someone might diff -Nru a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h --- a/drivers/usb/host/ohci.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/host/ohci.h Fri Dec 12 15:06:58 2003 @@ -172,8 +172,14 @@ struct ohci_hcca { #define NUM_INTS 32 __u32 int_table [NUM_INTS]; /* periodic schedule */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ + + /* + * OHCI defines u16 frame_no, followed by u16 zero pad. + * Since some processors can't do 16 bit bus accesses, + * portable access must be a 32 bit byteswapped access. + */ + u32 frame_no; /* current frame number */ +#define OHCI_FRAME_NO(hccap) ((u16)le32_to_cpup(&(hccap)->frame_no)) __u32 done_head; /* info returned for an interrupt */ u8 reserved_for_hc [116]; u8 what [4]; /* spec only identifies 252 bytes :) */ diff -Nru a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c --- a/drivers/usb/image/scanner.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/image/scanner.c Fri Dec 12 15:06:57 2003 @@ -380,6 +380,10 @@ * Visioneer scanners. * - Added test for USB_CLASS_CDC_DATA which is used by some fingerprint scanners. * + * 0.4.16 2003-11-04 + * - Added vendor/product ids for Epson, Genius, Microtek, Plustek, Reflecta, and + * Visioneer scanners. Removed ids for HP PSC devices as these are supported by + * the hpoj userspace driver. * * TODO * - Performance diff -Nru a/drivers/usb/image/scanner.h b/drivers/usb/image/scanner.h --- a/drivers/usb/image/scanner.h Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/image/scanner.h Fri Dec 12 15:06:57 2003 @@ -43,7 +43,7 @@ // #define DEBUG -#define DRIVER_VERSION "0.4.15" +#define DRIVER_VERSION "0.4.16" #define DRIVER_DESC "USB Scanner Driver" #include @@ -146,7 +146,12 @@ { USB_DEVICE(0x0458, 0x2015) }, /* ColorPage HR7LE */ { USB_DEVICE(0x0458, 0x2016) }, /* ColorPage HR6X */ { USB_DEVICE(0x0458, 0x2018) }, /* ColorPage HR7X */ + { USB_DEVICE(0x0458, 0x201b) }, /* Colorpage Vivid 4x */ /* Hewlett Packard */ + /* IMPORTANT: Hewlett-Packard multi-function peripherals (OfficeJet, + Printer/Scanner/Copier (PSC), LaserJet, or PhotoSmart printer) + should not be added to this table because they are accessed by a + userspace driver (hpoj) */ { USB_DEVICE(0x03f0, 0x0101) }, /* ScanJet 4100C */ { USB_DEVICE(0x03f0, 0x0102) }, /* PhotoSmart S20 */ { USB_DEVICE(0x03f0, 0x0105) }, /* ScanJet 4200C */ @@ -168,10 +173,10 @@ { USB_DEVICE(0x03F0, 0x1105) }, /* ScanJet 5470C */ { USB_DEVICE(0x03f0, 0x1205) }, /* ScanJet 5550C */ { USB_DEVICE(0x03f0, 0x1305) }, /* Scanjet 4570c */ - { USB_DEVICE(0x03f0, 0x1411) }, /* PSC 750 */ + // { USB_DEVICE(0x03f0, 0x1411) }, /* PSC 750 - NOT SUPPORTED - use hpoj userspace driver */ { USB_DEVICE(0x03f0, 0x2005) }, /* ScanJet 3570c */ { USB_DEVICE(0x03f0, 0x2205) }, /* ScanJet 3500c */ - { USB_DEVICE(0x03f0, 0x2f11) }, /* PSC 1210 */ + // { USB_DEVICE(0x03f0, 0x2f11) }, /* PSC 1210 - NOT SUPPORTED - use hpoj userspace driver */ /* Lexmark */ { USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */ { USB_DEVICE(0x043d, 0x003d) }, /* X83 */ @@ -187,6 +192,7 @@ { USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */ { USB_DEVICE(0x05da, 0x30cf) }, /* ScanMaker 4800 */ { USB_DEVICE(0x05da, 0x30d4) }, /* ScanMaker 3830 + 3840 */ + { USB_DEVICE(0x05da, 0x30d8) }, /* ScanMaker 5900 */ { USB_DEVICE(0x04a7, 0x0224) }, /* Scanport 3000 (actually Visioneer?)*/ /* The following SCSI-over-USB Microtek devices are supported by the microtek driver: Enable SCSI and USB Microtek in kernel config */ @@ -245,6 +251,7 @@ { USB_DEVICE(0x07b3, 0x0400) }, /* OpticPro 1248U */ { USB_DEVICE(0x07b3, 0x0401) }, /* OpticPro 1248U (another one) */ { USB_DEVICE(0x07b3, 0x0403) }, /* U16B */ + { USB_DEVICE(0x07b3, 0x0413) }, /* OpticSlim 1200 */ /* Primax/Colorado */ { USB_DEVICE(0x0461, 0x0300) }, /* G2-300 #1 */ { USB_DEVICE(0x0461, 0x0301) }, /* G2E-300 #1 */ @@ -261,6 +268,8 @@ { USB_DEVICE(0x0461, 0x0383) }, /* G2E-600 */ /* Prolink */ { USB_DEVICE(0x06dc, 0x0014) }, /* Winscan Pro 2448U */ + /* Reflecta */ + { USB_DEVICE(0x05e3, 0x0120) }, /* iScan 1800 */ /* Relisis */ // { USB_DEVICE(0x0475, 0x0103) }, /* Episode - undetected endpoint */ { USB_DEVICE(0x0475, 0x0210) }, /* Scorpio Ultra 3 */ @@ -285,6 +294,7 @@ { USB_DEVICE(0x04b8, 0x011c) }, /* Perfection 3200 */ { USB_DEVICE(0x04b8, 0x011d) }, /* Perfection 1260 */ { USB_DEVICE(0x04b8, 0x011e) }, /* Perfection 1660 Photo */ + { USB_DEVICE(0x04b8, 0x011f) }, /* Perfection 1670 */ { USB_DEVICE(0x04b8, 0x0801) }, /* Stylus CX5200 */ { USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */ /* Siemens */ @@ -309,6 +319,7 @@ { USB_DEVICE(0x04a7, 0x0221) }, /* OneTouch 5300 USB */ { USB_DEVICE(0x04a7, 0x0224) }, /* OneTouch 4800 USB */ { USB_DEVICE(0x04a7, 0x0226) }, /* OneTouch 5800 USB */ + { USB_DEVICE(0x04a7, 0x0229) }, /* OneTouch 7100 USB */ { USB_DEVICE(0x04a7, 0x022c) }, /* OneTouch 9020 USB */ { USB_DEVICE(0x04a7, 0x0231) }, /* 6100 USB */ { USB_DEVICE(0x04a7, 0x0311) }, /* 6200 EPP/USB */ diff -Nru a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig --- a/drivers/usb/media/Kconfig Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/media/Kconfig Fri Dec 12 15:06:57 2003 @@ -113,16 +113,17 @@ webcams: * Philips PCA645, PCA646 * Philips PCVC675, PCVC680, PCVC690 - * Philips PCVC730, PCVC740, PCVC750 + * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 * Askey VC010 - * Logitech QuickCam Pro 3000, 4000, 'Zoom' and 'Notebook' - * Samsung MPC-C10, MPC-C30 - * Creative Webcam 5 - * SOTECT Afina Eye + * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' + and 'Orbit'/'Sphere' + * Samsung MPC-C10, MPC-C30 + * Creative Webcam 5, Pro Ex + * SOTEC Afina Eye * Visionite VCS-UC300, VCS-UM100 - The PCA635, PCVC665 and PCVC720 are not supported by this driver - and never will be, but the 665 and 720 are supported by other + The PCA635, PCVC665 and PCVC720/20 are not supported by this driver + and never will be, but the 665 and 720/20 are supported by other drivers. This driver has an optional plugin (called PWCX), which is @@ -177,3 +178,30 @@ To compile this driver as a module, choose M here: the module will be called stv680. +config USB_W9968CF + tristate "USB W996[87]CF JPEG Dual Mode Camera support" + depends on USB && VIDEO_DEV && I2C + ---help--- + Say Y here if you want support for cameras based on + Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. + + This driver has an optional plugin, which is distributed as a + separate module only (released under GPL). It contains code that + allows you to use higher resolutions and framerates, and can't + be included into the official Linux kernel for performance + purposes. + At the moment the driver needs a third-part module for the CMOS + sensors, which is available on internet: it is recommended to read + for more informations and for + a list of supported cameras. + + This driver uses the Video For Linux and the I2C APIs. + You must say Y or M to both "Video For Linux" and + "I2C Support" to use this driver. + Information on this API and pointers to "v4l" programs may be found + on the WWW at . + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called w9968cf.o. If you want to compile it as a + module, say M here and read . diff -Nru a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile --- a/drivers/usb/media/Makefile Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/media/Makefile Fri Dec 12 15:06:57 2003 @@ -13,3 +13,4 @@ obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o +obj-$(CONFIG_USB_W9968CF) += w9968cf.o diff -Nru a/drivers/usb/media/pwc-ctrl.c b/drivers/usb/media/pwc-ctrl.c --- a/drivers/usb/media/pwc-ctrl.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/media/pwc-ctrl.c Fri Dec 12 15:06:58 2003 @@ -44,6 +44,8 @@ #define GET_STATUS_CTL 0x06 #define SET_EP_STREAM_CTL 0x07 #define GET_EP_STREAM_CTL 0x08 +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E /* Selectors for the Luminance controls [GS]ET_LUM_CTL */ #define AGC_MODE_FORMATTER 0x2000 @@ -88,6 +90,11 @@ /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + static char *size2name[PSZ_MAX] = { "subQCIF", @@ -435,6 +442,7 @@ ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); break; + case 720: case 730: case 740: case 750: @@ -745,6 +753,7 @@ buf[1] = speed >> 8; buf[0] = speed & 0xff; break; + case 720: case 730: case 740: case 750: @@ -1243,6 +1252,46 @@ return buf; } +int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + unsigned char buf; + + buf = flags & 0x03; // only lower two bits are currently used + return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); +} + +static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + unsigned char buf[4]; + + /* set new relative angle; angles are expressed in degrees * 100, + but cam as .5 degree resolution, hence devide by 200. Also + the angle must be multiplied by 64 before it's send to + the cam (??) + */ + pan = 64 * pan / 100; + tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ + buf[0] = pan & 0xFF; + buf[1] = (pan >> 8) & 0xFF; + buf[2] = tilt & 0xFF; + buf[3] = (tilt >> 8) & 0xFF; + return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); +} + +static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +{ + int ret; + unsigned char buf[5]; + + ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); + if (ret < 0) + return ret; + status->status = buf[0] & 0x7; // 3 bits are used for reporting + status->time_pan = (buf[1] << 8) + buf[2]; + status->time_tilt = (buf[3] << 8) + buf[4]; + return 0; +} + int pwc_get_cmos_sensor(struct pwc_device *pdev) { @@ -1512,8 +1561,128 @@ size->width = pdev->image.x; size->height = pdev->image.y; break; - } - + } + + case VIDIOCPWCMPTRESET: + { + int *flags = arg; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ret = pwc_mpt_reset(pdev, *flags); + if (ret >= 0) + { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + } + else + { + ret = -ENXIO; + } + break; + } + case VIDIOCPWCMPTGRANGE: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + memcpy(arg, &pdev->angle_range, sizeof(struct pwc_mpt_range)); + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSANGLE: + { + struct pwc_mpt_angles *angles = arg; + int new_pan, new_tilt; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + /* The camera can only set relative angles, so + do some calculations when getting an absolute angle . + */ + if (angles->absolute) + { + new_pan = angles->pan; + new_tilt = angles->tilt; + } + else + { + new_pan = pdev->pan_angle + angles->pan; + new_tilt = pdev->tilt_angle + angles->tilt; + } + /* check absolute ranges */ + if (new_pan < pdev->angle_range.pan_min || + new_pan > pdev->angle_range.pan_max || + new_tilt < pdev->angle_range.tilt_min || + new_tilt > pdev->angle_range.tilt_max) + { + ret = -ERANGE; + } + else + { + /* go to relative range, check again */ + new_pan -= pdev->pan_angle; + new_tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) + ret = -ERANGE; + } + if (ret == 0) /* no errors so far */ + { + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); + if (ret >= 0) + { + pdev->pan_angle += new_pan; + pdev->tilt_angle += new_tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGANGLE: + { + struct pwc_mpt_angles *angles = arg; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + angles->absolute = 1; + angles->pan = pdev->pan_angle; + angles->tilt = pdev->tilt_angle; + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSTATUS: + { + struct pwc_mpt_status *status = arg; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ret = pwc_mpt_get_status(pdev, status); + } + else + { + ret = -ENXIO; + } + break; + } + default: ret = -ENOIOCTLCMD; break; diff -Nru a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c --- a/drivers/usb/media/pwc-if.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/media/pwc-if.c Fri Dec 12 15:06:58 2003 @@ -79,9 +79,9 @@ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ - { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom */ - { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech (reserved) */ - { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ @@ -129,6 +129,7 @@ static int pwc_video_open(struct inode *inode, struct file *file); static int pwc_video_close(struct inode *inode, struct file *file); +static int pwc_video_release(struct video_device *); static ssize_t pwc_video_read(struct file *file, char *buf, size_t count, loff_t *ppos); static unsigned int pwc_video_poll(struct file *file, poll_table *wait); @@ -918,21 +919,32 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) { - int ret; + int ret, start; /* Stop isoc stuff */ pwc_isoc_cleanup(pdev); /* Reset parameters */ pwc_reset_buffers(pdev); /* Try to set video mode... */ - ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); - if (ret) /* That failed... restore old mode (we know that worked) */ - ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - if (!ret) - if (pwc_isoc_init(pdev) < 0) - Info("Failed to restart ISOC transfer in pwc_try_video_mode.\n"); + start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); + if (ret) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + /* That failed... restore old mode (we know that worked) */ + start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (start) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + } + } + if (start == 0) + { + if (pwc_isoc_init(pdev) < 0) + { + Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + ret = -EAGAIN; /* let's try again, who knows if it works a second time */ + } + } pdev->drop_frames++; /* try to avoid garbage during switch */ - return ret; + return ret; /* Return original error code */ } @@ -997,6 +1009,7 @@ #if PWC_DEBUG Debug("Found decompressor for %d at 0x%p\n", pdev->type, pdev->decompressor); #endif + pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); @@ -1018,6 +1031,7 @@ #if PWC_DEBUG pdev->sequence = 0; #endif + pwc_construct(pdev); /* set min/max sizes correct */ /* Set some defaults */ pdev->vsnapshot = 0; @@ -1104,6 +1118,12 @@ return 0; } +static int pwc_video_release(struct video_device *vfd) +{ + Trace(TRACE_OPEN, "pwc_video_release() called. Now what?\n"); +} + + /* * FIXME: what about two parallel reads ???? * ANSWER: Not supported. You can't open the device more than once, @@ -1124,7 +1144,7 @@ int noblock = file->f_flags & O_NONBLOCK; DECLARE_WAITQUEUE(wait, current); - Trace(TRACE_READ, "video_read(0x%p, %p, %Zd) called.\n", vdev, buf, count); + Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; @@ -1568,6 +1588,7 @@ struct pwc_device *pdev = NULL; int vendor_id, product_id, type_id; int i, hint; + int features = 0; int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; @@ -1677,8 +1698,17 @@ name = "Logitech QuickCam Zoom"; type_id = 740; /* CCD sensor */ break; - case 0x08b4: + case 0x08B4: + Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; case 0x08b5: + Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + name = "Logitech QuickCam Orbit"; + type_id = 740; /* CCD sensor */ + features |= FEATURE_MOTOR_PANTILT; + break; case 0x08b6: case 0x08b7: case 0x08b8: @@ -1776,9 +1806,22 @@ } memset(pdev, 0, sizeof(struct pwc_device)); pdev->type = type_id; - pwc_construct(pdev); pdev->vsize = default_size; pdev->vframes = default_fps; + pdev->features = features; + if (vendor_id == 0x046D && product_id == 0x08B5) + { + /* Logitech QuickCam Orbit + The ranges have been determined experimentally; they may differ from cam to cam. + Also, the exact ranges left-right and up-down are different for my cam + */ + pdev->angle_range.pan_min = -7000; + pdev->angle_range.pan_max = 7000; + pdev->angle_range.tilt_min = -3000; + pdev->angle_range.tilt_max = 2500; + pdev->angle_range.zoom_min = -1; + pdev->angle_range.zoom_max = -1; + } init_MUTEX(&pdev->modlock); pdev->ptrlock = SPIN_LOCK_UNLOCKED; @@ -1791,7 +1834,7 @@ strcpy(pdev->vdev.name, name); pdev->vdev.owner = THIS_MODULE; pdev->vdev.priv = pdev; - + pdev->release = udev->descriptor.bcdDevice; Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); @@ -1809,6 +1852,7 @@ } } + pdev->vdev.release = pwc_video_release; i = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr); if (i < 0) { Err("Failed to register as video device (%d).\n", i); @@ -1818,6 +1862,7 @@ else { Info("Registered as /dev/video%d.\n", pdev->vdev.minor & 0x3F); } + /* occupy slot */ if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = pdev; diff -Nru a/drivers/usb/media/pwc-ioctl.h b/drivers/usb/media/pwc-ioctl.h --- a/drivers/usb/media/pwc-ioctl.h Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/media/pwc-ioctl.h Fri Dec 12 15:06:57 2003 @@ -112,6 +112,43 @@ int height; }; +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute = 1, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ + int zoom; /* N/A, set to -1 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + The zoom is not used, maybe in the future... + + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; + int zoom_min, zoom_max; /* -1, -1 */ +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + /* Restore user settings */ #define VIDIOCPWCRUSER _IO('v', 192) /* Save user settings */ @@ -181,5 +218,12 @@ /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ #define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) #endif diff -Nru a/drivers/usb/media/pwc-misc.c b/drivers/usb/media/pwc-misc.c --- a/drivers/usb/media/pwc-misc.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/media/pwc-misc.c Fri Dec 12 15:06:57 2003 @@ -52,7 +52,7 @@ return find; } -/* initialize variables depending on type */ +/* initialize variables depending on type and decompressor*/ void pwc_construct(struct pwc_device *pdev) { switch(pdev->type) { @@ -73,9 +73,17 @@ case 690: pdev->view_min.x = 128; pdev->view_min.y = 96; - pdev->view_max.x = 640; - pdev->view_max.y = 480; - pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; + /* Anthill bug #38: PWC always reports max size, even without PWCX */ + if (pdev->decompressor != NULL) { + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; + } + else { + pdev->view_max.x = 352; + pdev->view_max.y = 288; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF; + } pdev->vcinterface = 3; pdev->vendpoint = 4; pdev->frame_header_size = 0; @@ -87,9 +95,18 @@ case 750: pdev->view_min.x = 160; pdev->view_min.y = 120; - pdev->view_max.x = 640; - pdev->view_max.y = 480; - pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + /* Anthill bug #38: PWC always reports max size, even without PWCX */ + if (pdev->decompressor != NULL) { + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + } + else { + /* Tell CIF, even though SIF really is the maximum, but some tools really need CIF */ + pdev->view_max.x = 352; + pdev->view_max.y = 288; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF; + } pdev->vcinterface = 3; pdev->vendpoint = 5; pdev->frame_header_size = TOUCAM_HEADER_SIZE; diff -Nru a/drivers/usb/media/pwc.h b/drivers/usb/media/pwc.h --- a/drivers/usb/media/pwc.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/media/pwc.h Fri Dec 12 15:06:58 2003 @@ -18,17 +18,21 @@ #ifndef PWC_H #define PWC_H +#include + #include #include -#include +#include #include +#include #include #include -#include #include #include +#include "pwc-ioctl.h" + /* Defines and structures for the Philips webcam */ /* Used for checking memory corruption/pointer validation */ #define PWC_MAGIC 0x89DC10ABUL @@ -58,10 +62,12 @@ #define TOUCAM_HEADER_SIZE 8 #define TOUCAM_TRAILER_SIZE 4 +#define FEATURE_MOTOR_PANTILT 0x0001 + /* Version block */ #define PWC_MAJOR 8 -#define PWC_MINOR 11 -#define PWC_VERSION "8.11" +#define PWC_MINOR 12 +#define PWC_VERSION "8.12" #define PWC_NAME "pwc" /* Turn certain features on/off */ @@ -119,8 +125,9 @@ /* Pointer to our usb_device */ struct usb_device *udev; - int type; /* type of cam (645, 646, 675, 680, 690) */ + int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int release; /* release number */ + int features; /* feature bits */ int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ int usb_init; /* set when the cam has been initialized over USB */ @@ -193,6 +200,11 @@ struct semaphore modlock; /* to prevent races in video_open(), etc */ spinlock_t ptrlock; /* for manipulating the buffer pointers */ + + /*** motorized pan/tilt feature */ + struct pwc_mpt_range angle_range; + int pan_angle; /* in degrees * 100 */ + int tilt_angle; /* absolute angle; 0,0 is home position */ /*** Misc. data ***/ wait_queue_head_t frameq; /* When waiting for a frame to finish... */ diff -Nru a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/media/w9968cf.c Fri Dec 12 15:06:58 2003 @@ -0,0 +1,3712 @@ +/*************************************************************************** + * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * + * * + * Copyright (C) 2002 2003 by Luca Risolia * + * * + * - Memory management code from bttv driver by Ralph Metzler, * + * Marcus Metzler and Gerd Knorr. * + * - I2C interface to kernel, high-level CMOS sensor control routines and * + * some symbolic names from OV511 driver by Mark W. McClelland. * + * - Low-level I2C fast write function by Piotr Czerczak. * + * - Low-level I2C read function by Frédéric Jouault. * + * * + * 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. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "w9968cf.h" +#include "w9968cf_decoder.h" + + + +/**************************************************************************** + * Modules paramaters * + ****************************************************************************/ + +static u8 vppmod_load = W9968CF_VPPMOD_LOAD; +static u8 simcams = W9968CF_SIMCAMS; +static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */ +static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE}; +static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS}; +static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DOUBLE_BUFFER}; +static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; +static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE}; +static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; +static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DECOMPRESSION}; +static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; +static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; +static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; +static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; +static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; +static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ}; +static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= + W9968CF_BANDINGFILTER}; +static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; +static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; +static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; +static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO}; +static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS}; +static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; +static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; +static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST}; +static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS}; +#ifdef W9968CF_DEBUG +static u8 debug = W9968CF_DEBUG_LEVEL; +static u8 specific_debug = W9968CF_SPECIFIC_DEBUG; +#endif + +MODULE_AUTHOR("Luca Risolia "); + +MODULE_DESCRIPTION("Video4Linux driver for " + "W996[87]CF JPEG USB Dual Mode Camera Chip"); + +MODULE_SUPPORTED_DEVICE("Video"); + +MODULE_LICENSE("GPL"); + +MODULE_PARM(vppmod_load, "i"); +MODULE_PARM(simcams, "i"); +MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); +MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +#ifdef W9968CF_DEBUG +MODULE_PARM(debug, "i"); +MODULE_PARM(specific_debug, "i"); +#endif + +MODULE_PARM_DESC(vppmod_load, + "\n<0|1> Automatic 'w9968cf-vpp' module loading." + "\n0 disable, 1 enable." + "\nIf enabled, every time an application attempts to open a" + "\ncamera, 'insmod' searches for the video post-processing" + "\nmodule in the system and loads it automatically (if" + "\npresent). The 'w9968cf-vpp' module adds extra image" + "\nmanipulation functions to the 'w9968cf' module, like" + "\nsoftware up-scaling,colour conversions and video decoding." + "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"." + "\n"); +MODULE_PARM_DESC(simcams, + "\n Number of cameras allowed to stream simultaneously." + "\nn may vary from 0 to " + __MODULE_STRING(W9968CF_MAX_DEVICES)"." + "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"." + "\n"); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify " __MODULE_STRING(W9968CF_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); +MODULE_PARM_DESC(packet_size, + "\n Specify the maximum data payload" + "\nsize in bytes for alternate settings, for each device." + "\nn is scaled between 63 and 1023 " + "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")." + "\n"); +MODULE_PARM_DESC(max_buffers, + "\n Only for advanced users." + "\nSpecify the maximum number of video frame buffers" + "\nto allocate for each device, from 2 to " + __MODULE_STRING(W9968CF_MAX_BUFFERS) + ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")." + "\n"); +MODULE_PARM_DESC(double_buffer, + "\n<0|1[,...]> " + "Hardware double buffering: 0 disabled, 1 enabled." + "\nIt should be enabled if you want smooth video output: if" + "\nyou obtain out of sync. video, disable it at all, or try" + "\nto decrease the 'clockdiv' module paramater value." + "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) + " for every device." + "\n"); +MODULE_PARM_DESC(clamping, + "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled." + "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING) + " for every device." + "\n"); +MODULE_PARM_DESC(filter_type, + "\n<0|1|2[,...]> Video filter type." + "\n0 none, 1 (1-2-1) 3-tap filter, " + "2 (2-3-6-3-2) 5-tap filter." + "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE) + " for every device." + "\nThe filter is used to reduce noise and aliasing artifacts" + "\nproduced by the CCD or CMOS sensor, and the scaling" + " process." + "\n"); +MODULE_PARM_DESC(largeview, + "\n<0|1[,...]> Large view: 0 disabled, 1 enabled." + "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW) + " for every device." + "\n"); +MODULE_PARM_DESC(upscaling, + "\n<0|1[,...]> Software scaling (for non-compressed video):" + "\n0 disabled, 1 enabled." + "\nDisable it if you have a slow CPU or you don't have" + " enough memory." + "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) + " for every device." + "\nIf 'w9968cf-vpp' is not loaded, this paramater is" + " set to 0."); +MODULE_PARM_DESC(decompression, + "\n<0|1|2[,...]> Software video decompression:" + "\n- 0 disables decompression (doesn't allow formats needing" + " decompression)" + "\n- 1 forces decompression (allows formats needing" + " decompression only);" + "\n- 2 allows any permitted formats." + "\nFormats supporting compressed video are YUV422P and" + " YUV420P/YUV420 " + "\nin any resolutions where both width and height are " + "a multiple of 16." + "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION) + " for every device." + "\nIf 'w9968cf-vpp' is not loaded, forcing decompression is " + "\nnot allowed; in this case this paramater is set to 2."); +MODULE_PARM_DESC(force_palette, + "\n<0" + "|" __MODULE_STRING(VIDEO_PALETTE_UYVY) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV420) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P) + "|" __MODULE_STRING(VIDEO_PALETTE_YUYV) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV422) + "|" __MODULE_STRING(VIDEO_PALETTE_GREY) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB555) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB565) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB24) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB32) + "[,...]>" + " Force picture palette." + "\nIn order:" + "\n- 0 allows any of the following formats:" + "\n- UYVY 16 bpp - Original video, compression disabled" + "\n- YUV420 12 bpp - Original video, compression enabled" + "\n- YUV422P 16 bpp - Original video, compression enabled" + "\n- YUV420P 12 bpp - Original video, compression enabled" + "\n- YUVY 16 bpp - Software conversion from UYVY" + "\n- YUV422 16 bpp - Software conversion from UYVY" + "\n- GREY 8 bpp - Software conversion from UYVY" + "\n- RGB555 16 bpp - Software conversion from UYVY" + "\n- RGB565 16 bpp - Software conversion from UYVY" + "\n- RGB24 24 bpp - Software conversion from UYVY" + "\n- RGB32 32 bpp - Software conversion from UYVY" + "\nWhen not 0, this paramater will override 'decompression'." + "\nDefault value is 0 for every device." + "\nInitial palette is " + __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." + "\nIf 'w9968cf-vpp' is not loaded, this paramater is" + " set to 9 (UYVY)."); +MODULE_PARM_DESC(force_rgb, + "\n<0|1[,...]> Read RGB video data instead of BGR:" + "\n 1 = use RGB component ordering." + "\n 0 = use BGR component ordering." + "\nThis parameter has effect when using RGBX palettes only." + "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB) + " for every device." + "\n"); +MODULE_PARM_DESC(autobright, + "\n<0|1[,...]> CMOS sensor automatically changes brightness:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT) + " for every device." + "\n"); +MODULE_PARM_DESC(autoexp, + "\n<0|1[,...]> CMOS sensor automatically changes exposure:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) + " for every device." + "\n"); +MODULE_PARM_DESC(lightfreq, + "\n<50|60[,...]> Light frequency in Hz:" + "\n 50 for European and Asian lighting," + " 60 for American lighting." + "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ) + " for every device." + "\n"); +MODULE_PARM_DESC(bandingfilter, + "\n<0|1[,...]> Banding filter to reduce effects of" + " fluorescent lighting:" + "\n 0 disabled, 1 enabled." + "\nThis filter tries to reduce the pattern of horizontal" + "\nlight/dark bands caused by some (usually fluorescent)" + " lighting." + "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER) + " for every device." + "\n"); +MODULE_PARM_DESC(clockdiv, + "\n<-1|n[,...]> " + "Force pixel clock divisor to a specific value (for experts):" + "\n n may vary from 0 to 127." + "\n -1 for automatic value." + "\nSee also the 'double_buffer' module paramater." + "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV) + " for every device." + "\n"); +MODULE_PARM_DESC(backlight, + "\n<0|1[,...]> Objects are lit from behind:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT) + " for every device." + "\n"); +MODULE_PARM_DESC(mirror, + "\n<0|1[,...]> Reverse image horizontally:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR) + " for every device." + "\n"); +MODULE_PARM_DESC(sensor_mono, + "\n<0|1[,...]> The OV CMOS sensor is monochrome:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO) + " for every device." + "\n"); +MODULE_PARM_DESC(brightness, + "\n Set picture brightness (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS) + " for every device." + "\nThis parameter has no effect if 'autobright' is enabled." + "\n"); +MODULE_PARM_DESC(hue, + "\n Set picture hue (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_HUE) + " for every device." + "\n"); +MODULE_PARM_DESC(colour, + "\n Set picture saturation (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR) + " for every device." + "\n"); +MODULE_PARM_DESC(contrast, + "\n Set picture contrast (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST) + " for every device." + "\n"); +MODULE_PARM_DESC(whiteness, + "\n Set picture whiteness (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS) + " for every device." + "\n"); +#ifdef W9968CF_DEBUG +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 6:" + "\n0 = none (be cautious)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = configuration or general messages" + "\n4 = warnings" + "\n5 = called functions" + "\n6 = function internals" + "\nLevel 5 and 6 are useful for testing only, when just " + "one device is used." + "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." + "\n"); +MODULE_PARM_DESC(specific_debug, + "\n<0|1> Enable or disable specific debugging messages:" + "\n0 = print messages concerning every level" + " <= 'debug' level." + "\n1 = print messages concerning the level" + " indicated by 'debug'." + "\nDefault value is " + __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"." + "\n"); +#endif /* W9968CF_DEBUG */ + + + +/**************************************************************************** + * Some prototypes * + ****************************************************************************/ + +/* Video4linux interface */ +static struct file_operations w9968cf_fops; +static int w9968cf_open(struct inode*, struct file*); +static int w9968cf_release(struct inode*, struct file*); +static ssize_t w9968cf_read(struct file*, char*, size_t, loff_t*); +static int w9968cf_mmap(struct file*, struct vm_area_struct*); +static int w9968cf_ioctl(struct inode*, struct file*, + unsigned int, unsigned long); +static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*); + +/* USB-specific */ +static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs); +static int w9968cf_start_transfer(struct w9968cf_device*); +static int w9968cf_stop_transfer(struct w9968cf_device*); +static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index); +static int w9968cf_read_reg(struct w9968cf_device*, u16 index); +static int w9968cf_write_fsb(struct w9968cf_device*, u16* data); +static int w9968cf_write_sb(struct w9968cf_device*, u16 value); +static int w9968cf_read_sb(struct w9968cf_device*); +static int w9968cf_upload_quantizationtables(struct w9968cf_device*); + +/* Low-level I2C (SMBus) I/O */ +static int w9968cf_smbus_start(struct w9968cf_device*); +static int w9968cf_smbus_stop(struct w9968cf_device*); +static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v); +static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v); +static int w9968cf_smbus_write_ack(struct w9968cf_device*); +static int w9968cf_smbus_read_ack(struct w9968cf_device*); +static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, + u16 address, u8* value); +static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, + u8 subaddress, u8* value); +static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*, + u16 address, u8 subaddress); +static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*, + u16 address, u8 subaddress, + u8 value); + +/* I2C interface to kernel */ +static int w9968cf_i2c_init(struct w9968cf_device*); +static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data*); +static u32 w9968cf_i2c_func(struct i2c_adapter*); +static int w9968cf_i2c_attach_inform(struct i2c_client*); +static int w9968cf_i2c_detach_inform(struct i2c_client*); +static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd, + unsigned long arg); + +/* Memory management */ +static inline unsigned long kvirt_to_pa(unsigned long adr); +static void* rvmalloc(unsigned long size); +static void rvfree(void *mem, unsigned long size); +static void w9968cf_deallocate_memory(struct w9968cf_device*); +static int w9968cf_allocate_memory(struct w9968cf_device*); +static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device*); + +/* High-level CMOS sensor control functions */ +static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val); +static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val); +static inline int w9968cf_sensor_cmd(struct w9968cf_device*, + unsigned int cmd, void *arg); +static void w9968cf_sensor_configure(struct w9968cf_device*); +static int w9968cf_sensor_change_settings(struct w9968cf_device*); +static int w9968cf_sensor_get_picture(struct w9968cf_device*, + struct video_picture*); +static int w9968cf_sensor_set_picture(struct w9968cf_device*, + struct video_picture pict); + +/* Other helper functions */ +static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, + enum w9968cf_model_id, + const unsigned short dev_nr); +static int w9968cf_turn_on_led(struct w9968cf_device*); +static int w9968cf_init_chip(struct w9968cf_device*); +static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); +static int w9968cf_set_window(struct w9968cf_device*, struct video_window); +static inline u16 w9968cf_valid_palette(u16 palette); +static inline u16 w9968cf_valid_depth(u16 palette); +static inline u8 w9968cf_need_decompression(u16 palette); +static int w9968cf_postprocess_frame(struct w9968cf_device*, + struct w9968cf_frame_t*); +static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h); +static void w9968cf_init_framelist(struct w9968cf_device*); +static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); +static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); +static void w9968cf_release_resources(struct w9968cf_device*); + +/* Intermodule communication */ +static int w9968cf_vppmod_detect(void); +static void w9968cf_vppmod_release(void); + +/* Pointers to registered video post-processing functions */ +static void (*w9968cf_vpp_init_decoder)(void); +static int (*w9968cf_vpp_check_headers)(const unsigned char*, + const unsigned long); +static int (*w9968cf_vpp_decode)(const char*, const unsigned, + const unsigned, const unsigned, char*); +static void (*w9968cf_vpp_swap_yuvbytes)(void*, unsigned long); +static void (*w9968cf_vpp_uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8); +static void (*w9968cf_vpp_scale_up)(u8*, u8*, u16, u16, u16, u16, u16); + + + +/**************************************************************************** + * Symbolic names * + ****************************************************************************/ + +/* Used to represent a list of values and their respective symbolic names */ +struct w9968cf_symbolic_list { + const int num; + const char *name; +}; + +/*-------------------------------------------------------------------------- + Returns the name of the matching element in the symbolic_list array. The + end of the list must be marked with an element that has a NULL name. + --------------------------------------------------------------------------*/ +static inline const char * +symbolic(struct w9968cf_symbolic_list list[], const int num) +{ + int i; + + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return (list[i].name); + + return "Unknown"; +} + +static struct w9968cf_symbolic_list camlist[] = { + { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" }, + { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" }, + + /* Other cameras (having the same descriptors as Generic W996[87]CF) */ + { W9968CF_MOD_ADPA5R, "Aroma Digi Pen ADG-5000 Refurbished" }, + { W9986CF_MOD_AU, "AVerTV USB" }, + { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, + { W9968CF_MOD_DLLDK, "Die Lebon LDC-D35A Digital Kamera" }, + { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" }, + { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" }, + + { -1, NULL } +}; + +static struct w9968cf_symbolic_list senlist[] = { + { CC_OV76BE, "OV76BE" }, + { CC_OV7610, "OV7610" }, + { CC_OV7620, "OV7620" }, + { CC_OV7620AE, "OV7620AE" }, + { CC_OV6620, "OV6620" }, + { CC_OV6630, "OV6630" }, + { CC_OV6630AE, "OV6630AE" }, + { CC_OV6630AF, "OV6630AF" }, + { -1, NULL } +}; + +/* Video4Linux1 palettes */ +static struct w9968cf_symbolic_list v4l1_plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P, "YUV422P" }, + { VIDEO_PALETTE_YUV411P, "YUV411P" }, + { VIDEO_PALETTE_YUV420P, "YUV420P" }, + { VIDEO_PALETTE_YUV410P, "YUV410P" }, + { -1, NULL } +}; + +/* Decoder error codes: */ +static struct w9968cf_symbolic_list decoder_errlist[] = { + { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" }, + { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" }, + { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" }, + { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" }, + { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" }, + { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" }, + { -1, NULL } +}; + +/* URB error codes: */ +static struct w9968cf_symbolic_list urb_errlist[] = { + { -ENOMEM, "No memory for allocation of internal structures" }, + { -ENOSPC, "The host controller's bandwidth is already consumed" }, + { -ENOENT, "URB was canceled by unlink_urb" }, + { -EXDEV, "ISO transfer only partially completed" }, + { -EAGAIN, "Too match scheduled for the future" }, + { -ENXIO, "URB already queued" }, + { -EFBIG, "Too much ISO frames requested" }, + { -ENOSR, "Buffer error (overrun)" }, + { -EPIPE, "Specified endpoint is stalled (device not responding)"}, + { -EOVERFLOW, "Babble (bad cable?)" }, + { -EPROTO, "Bit-stuff error (bad cable?)" }, + { -EILSEQ, "CRC/Timeout" }, + { -ETIMEDOUT, "NAK (device does not respond)" }, + { -1, NULL } +}; + + + +/**************************************************************************** + * Memory management functions * + ****************************************************************************/ + +/* Shameless copy from bttv-driver.c */ + +/* 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; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + + +static void* rvmalloc(unsigned long size) +{ + void* mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + + +static void rvfree(void* mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} +/* End of shameless copy */ + + +/*-------------------------------------------------------------------------- + Return the maximum size (in bytes) of a frame buffer. + --------------------------------------------------------------------------*/ +static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device* cam) +{ + u8 bpp = (w9968cf_vppmod_present) ? 4 : 2; + return (cam->upscaling) ? W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp : + cam->maxwidth*cam->maxheight*bpp; +} + + +/*-------------------------------------------------------------------------- + Deallocate previously allocated memory. + --------------------------------------------------------------------------*/ +static void w9968cf_deallocate_memory(struct w9968cf_device* cam) +{ + u8 i; + + /* Free the isochronous transfer buffers */ + for (i = 0; i < W9968CF_URBS; i++) { + kfree(cam->transfer_buffer[i]); + cam->transfer_buffer[i] = NULL; + } + + /* Free temporary frame buffer */ + if (cam->frame_tmp.buffer) { + rvfree(cam->frame_tmp.buffer, W9968CF_HW_BUF_SIZE); + cam->frame_tmp.buffer = NULL; + } + + /* Free helper buffer */ + if (cam->vpp_buffer) { + rvfree(cam->vpp_buffer, w9968cf_get_max_bufsize(cam)); + cam->vpp_buffer = NULL; + } + + /* Free video frame buffers */ + if (cam->frame[0].buffer) { + rvfree(cam->frame[0].buffer, + cam->nbuffers * w9968cf_get_max_bufsize(cam)); + cam->frame[0].buffer = NULL; + } + + cam->nbuffers = 0; + + DBG(5, "Memory successfully deallocated.") +} + + +/*-------------------------------------------------------------------------- + Allocate memory buffers for USB transfers and video frames. + This function is called by open() only. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_allocate_memory(struct w9968cf_device* cam) +{ + const unsigned long bufsize = w9968cf_get_max_bufsize(cam); + const u16 p_size = wMaxPacketSize[cam->altsetting-1]; + void* buff = NULL; + u8 i; + + /* NOTE: Deallocation is done elsewhere in case of error */ + + /* Allocate memory for the isochronous transfer buffers */ + for (i = 0; i < W9968CF_URBS; i++) { + if (!(cam->transfer_buffer[i] = + kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { + DBG(1, "Couldn't allocate memory for the isochronous " + "transfer buffers (%d bytes).", + p_size * W9968CF_ISO_PACKETS) + return -ENOMEM; + } + memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size); + } + + /* Allocate memory for the temporary frame buffer */ + if (!(cam->frame_tmp.buffer = rvmalloc(W9968CF_HW_BUF_SIZE))) { + DBG(1, "Couldn't allocate memory for the temporary " + "video frame buffer (%i bytes).", W9968CF_HW_BUF_SIZE) + return -ENOMEM; + } + + /* Allocate memory for the helper buffer */ + if (w9968cf_vppmod_present) { + if (!(cam->vpp_buffer = rvmalloc(bufsize))) { + DBG(1, "Couldn't allocate memory for the helper buffer" + " (%li bytes).", bufsize) + return -ENOMEM; + } + } else + cam->vpp_buffer = NULL; + + /* Allocate memory for video frame buffers */ + cam->nbuffers = cam->max_buffers; + while (cam->nbuffers >= 2) { + if ((buff = rvmalloc(cam->nbuffers * bufsize))) + break; + else + cam->nbuffers--; + } + + if (!buff) { + DBG(1, "Couldn't allocate memory for the video frame buffers.") + cam->nbuffers = 0; + return -ENOMEM; + } + + if (cam->nbuffers != cam->max_buffers) + DBG(2, "Couldn't allocate memory for %d video frame buffers. " + "Only memory for %d buffers has been allocated.", + cam->max_buffers, cam->nbuffers) + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].buffer = buff + i*bufsize; + /* Circular list */ + if (i != cam->nbuffers-1) + cam->frame[i].next = &cam->frame[i+1]; + else + cam->frame[i].next = &cam->frame[0]; + cam->frame[i].status = F_UNUSED; + } + + DBG(5, "Memory successfully allocated.") + return 0; +} + + + +/**************************************************************************** + * USB-specific functions * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + This is an handler function which is called after the URBs are completed. + It collects multiple data packets coming from the camera by putting them + into frame buffers: one or more zero data length data packets are used to + mark the end of a video frame; the first non-zero data packet is the start + of the next video frame; if an error is encountered in a packet, the entire + video frame is discarded and grabbed again. + If there are no requested frames in the FIFO list, packets are collected into + a temporary buffer. + --------------------------------------------------------------------------*/ +static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) +{ + struct w9968cf_device* cam = (struct w9968cf_device*)urb->context; + struct w9968cf_frame_t** f; + unsigned long maxbufsize; + unsigned int len, status; + void* pos; + u8 i; + int err = 0; + + if ((!cam->streaming) || cam->disconnected) { + DBG(4, "Got interrupt, but not streaming.") + return; + } + + maxbufsize = min( (unsigned long)W9968CF_HW_BUF_SIZE, + w9968cf_get_max_bufsize(cam) ); + + /* "(*f)" will be used instead of "cam->frame_current" */ + f = &cam->frame_current; + + /* If a frame has been requested and we are grabbing into + the temporary frame, we'll switch to that requested frame */ + if ((*f) == &cam->frame_tmp && *cam->requested_frame) { + if (cam->frame_tmp.status == F_GRABBING) { + w9968cf_pop_frame(cam, &cam->frame_current); + (*f)->status = F_GRABBING; + (*f)->length = cam->frame_tmp.length; + memcpy((*f)->buffer, cam->frame_tmp.buffer, + (*f)->length); + DBG(6, "Switched from temp. frame to frame #%d", + (*f) - &cam->frame[0]) + } + } + + for (i = 0; i < urb->number_of_packets; i++) { + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status && len != 0) { + DBG(4, "URB failed, error in data packet " + "(error #%d, %s).", + status, symbolic(urb_errlist, status)) + (*f)->status = F_ERROR; + continue; + } + + if (len) { /* start of frame */ + + if ((*f)->status == F_UNUSED) { + (*f)->status = F_GRABBING; + (*f)->length = 0; + } + + /* Buffer overflows shouldn't happen, however...*/ + if ((*f)->length + len > maxbufsize) { + DBG(4, "Buffer overflow: bad data packets.") + (*f)->status = F_ERROR; + } + + if ((*f)->status == F_GRABBING) { + memcpy((*f)->buffer + (*f)->length, pos, len); + (*f)->length += len; + } + + } else if ((*f)->status == F_GRABBING) { /* end of frame */ + + DBG(6, "Frame #%d successfully grabbed.", + ((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0])) + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + err=(*w9968cf_vpp_check_headers)((*f)->buffer, + (*f)->length); + if (err) { + DBG(4, "Skip corrupted frame: %s", + symbolic(decoder_errlist, err)) + (*f)->status = F_UNUSED; + continue; /* grab this frame again */ + } + } + + (*f)->status = F_READY; + (*f)->queued = 0; + + /* Take a pointer to the new frame from the FIFO list. + If the list is empty,we'll use the temporary frame*/ + if (*cam->requested_frame) + w9968cf_pop_frame(cam, &cam->frame_current); + else { + cam->frame_current = &cam->frame_tmp; + (*f)->status = F_UNUSED; + } + + } else if ((*f)->status == F_ERROR) + (*f)->status = F_UNUSED; /* grab it again */ + + PDBGG("Frame length %li | pack.#%d | pack.len. %d | state %d", + (unsigned long)(*f)->length, i, len, (*f)->status) + + } /* end for */ + + /* Resubmit this URB */ + urb->dev = cam->usbdev; + urb->status = 0; + spin_lock(&cam->urb_lock); + if (cam->streaming) + if ((err = usb_submit_urb(urb, GFP_ATOMIC))) { + cam->misconfigured = 1; + DBG(1, "Couldn't resubmit the URB: error %d, %s", + err, symbolic(urb_errlist, err)); + } + spin_unlock(&cam->urb_lock); + + /* Wake up the user process */ + if (waitqueue_active(&cam->wait_queue)) + wake_up_interruptible(&cam->wait_queue); +} + + +/*--------------------------------------------------------------------------- + Setup the URB structures for the isochronous transfer. + Submit the URBs so that the data transfer begins. + Return 0 on success, a negative number otherwise. + ---------------------------------------------------------------------------*/ +static int w9968cf_start_transfer(struct w9968cf_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const u16 p_size = wMaxPacketSize[cam->altsetting-1]; + u16 w, h, d; + int vidcapt; + u32 t_size; + int err = 0; + s8 i, j; + + for (i = 0; i < W9968CF_URBS; i++) { + urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + + if (!urb) { + for (j = 0; j < i; j++) + usb_free_urb(cam->urb[j]); + DBG(1, "Couldn't allocate the URB structures.") + return -ENOMEM; + } + + urb->dev = udev; + urb->context = (void*)cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = W9968CF_ISO_PACKETS; + urb->complete = w9968cf_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < W9968CF_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = p_size*j; + urb->iso_frame_desc[j].length = p_size; + } + } + + /* Transfer size per frame, in WORD ! */ + d = cam->hw_depth; + w = cam->hw_width; + h = cam->hw_height; + + t_size = (w*h*d)/16; + + err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ + + /* Transfer size */ + err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */ + err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */ + + if (cam->vpp_flag & VPP_DECOMPRESSION) + err += w9968cf_upload_quantizationtables(cam); + + vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */ + err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */ + + err += usb_set_interface(udev, 0, cam->altsetting); + err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */ + + if (err || (vidcapt < 0)) { + for (i = 0; i < W9968CF_URBS; i++) + usb_free_urb(cam->urb[i]); + DBG(1, "Couldn't tell the camera to start the data transfer.") + return err; + } + + w9968cf_init_framelist(cam); + + /* Begin to grab into the temporary buffer */ + cam->frame_tmp.status = F_UNUSED; + cam->frame_tmp.queued = 0; + cam->frame_current = &cam->frame_tmp; + + if (!(cam->vpp_flag & VPP_DECOMPRESSION)) + DBG(5, "Isochronous transfer size: %li bytes/frame.", + (unsigned long)t_size*2) + + DBG(5, "Starting the isochronous transfer...") + + /* Submit the URBs */ + for (i = 0; i < W9968CF_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + if (!usb_unlink_urb(cam->urb[j])) + usb_free_urb(cam->urb[j]); + DBG(1, "Couldn't send a transfer request to the " + "USB core (error #%d, %s).", err, + symbolic(urb_errlist, err)) + } + } + + cam->streaming = 1; + + return 0; +} + + +/*-------------------------------------------------------------------------- + Stop the isochronous transfer and set alternate setting to 0 (0Mb/s). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_stop_transfer(struct w9968cf_device* cam) +{ + struct usb_device *udev = cam->usbdev; + unsigned long lock_flags; + int err = 0; + s8 i; + + /* This avoids race conditions with usb_submit_urb() + in the URB completition handler */ + spin_lock_irqsave(&cam->urb_lock, lock_flags); + cam->streaming = 0; + spin_unlock_irqrestore(&cam->urb_lock, lock_flags); + + for (i = W9968CF_URBS-1; i >= 0; i--) + if (cam->urb[i]) + if (!usb_unlink_urb(cam->urb[i])) { + usb_free_urb(cam->urb[i]); + cam->urb[i] = NULL; + } + + if (cam->disconnected) + goto exit; + + err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */ + err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */ + err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */ + + if (err) { + DBG(2, "Failed to tell the camera to stop the isochronous " + "transfer. However this is not a critical error.") + return -EIO; + } + +exit: + DBG(5, "Isochronous transfer stopped.") + return 0; +} + + +/*-------------------------------------------------------------------------- + Write a W9968CF register. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + int res; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to write a register " + "(value 0x%04X, index 0x%02X, error #%d, %s).", + value, index, res, symbolic(urb_errlist, res)) + + return (res >= 0) ? 0 : -1; +} + + +/*-------------------------------------------------------------------------- + Read a W9968CF register. + Return the register value on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u16* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, (void*)buff, + 2, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to read a register " + "(index 0x%02X, error #%d, %s).", + index, res, symbolic(urb_errlist, res)) + + return (res >= 0) ? (int)(*buff) : -1; +} + + +/*-------------------------------------------------------------------------- + Write data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) +{ + struct usb_device* udev = cam->usbdev; + u16 value; + int res; + + value = *data++; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, (void*)data, 6, + W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to write the FSB registers " + "(error #%d, %s).", res, symbolic(urb_errlist, res)) + + return (res >= 0) ? 0 : -1; +} + + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value) +{ + int err = 0; + + err = w9968cf_write_reg(cam, value, 0x01); + udelay(W9968CF_I2C_BUS_DELAY); + + return err; +} + + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct w9968cf_device* cam) +{ + int v = 0; + + v = w9968cf_read_reg(cam, 0x01); + udelay(W9968CF_I2C_BUS_DELAY); + + return v; +} + + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam) +{ + u16 a, b; + int err = 0, i, j; + + err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); + err += w9968cf_write_reg(cam, a, 0x40+i); + err += w9968cf_write_reg(cam, b, 0x60+i); + } + err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */ + + return err; +} + + + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static int w9968cf_smbus_start(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return err; +} + + +static int w9968cf_smbus_stop(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + + return err; +} + + +static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v) +{ + u8 bit; + int err = 0, sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + err += w9968cf_write_sb(cam, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + err += w9968cf_write_sb(cam, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + err += w9968cf_write_sb(cam, 0x10 | sda); + } + + return err; +} + + +static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v) +{ + u8 bit; + int err = 0; + + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + err += w9968cf_write_sb(cam, 0x0013); + *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0; + err += w9968cf_write_sb(cam, 0x0012); + } + + return err; +} + + +static int w9968cf_smbus_write_ack(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return err; +} + + +static int w9968cf_smbus_read_ack(struct w9968cf_device* cam) +{ + int err = 0, sda; + + err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */ + err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda < 0) + err += sda; + if (sda == 1) { + DBG(6, "Couldn't receive the ACK.") + err += -1; + } + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static int +w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, + u16 address, u8 subaddress,u8 value) +{ + u16* data = cam->data_buffer; + int err = 0; + + /* Enable SBUS outputs */ + err += w9968cf_write_reg(cam, 0x0020, 0x01); + + data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0); + data[0] |= (address & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0); + data[1] |= (address & 0x20) ? 0x0150 : 0x0; + data[1] |= (address & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0); + data[2] |= (address & 0x04) ? 0x0540 : 0x0; + data[2] |= (address & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0); + data[3] |= (address & 0x01) ? 0x0054 : 0x0; + + err += w9968cf_write_fsb(cam, data); + + data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0); + data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0; + data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0); + data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0; + data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0; + data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0); + data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0; + data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + err += w9968cf_write_fsb(cam, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + err += w9968cf_write_fsb(cam, data); + + /* Disable SBUS outputs */ + err += w9968cf_write_reg(cam, 0x0000, 0x01); + + if (!err) + DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " + "value 0x%02X.", address, subaddress, value) + else + DBG(5, "I2C write byte data failed, addr.0x%04X, " + "subaddr.0x%02X, value 0x%02X.", + address, subaddress, value) + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int +w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, + u16 address, u8 subaddress, + u8* value) +{ + int err = 0; + + /* Serial data enable */ + err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */ + + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_write_byte(cam, subaddress); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_stop(cam); + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address + 1); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_read_byte(cam, value); + err += w9968cf_smbus_write_ack(cam); + err += w9968cf_smbus_stop(cam); + + /* Serial data disable */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + DBG(5, "I2C read byte data done, addr.0x%04X, " + "subaddr.0x%02X, value 0x%02X.", + address, subaddress, *value) + else + DBG(5, "I2C read byte data failed, addr.0x%04X, " + "subaddr.0x%02X, wrong value 0x%02X.", + address, subaddress, *value) + + return err; +} + + +/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */ +static int +w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, + u16 address, u8* value) +{ + int err = 0; + + /* Serial data enable */ + err += w9968cf_write_sb(cam, 0x0013); + + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address + 1); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_read_byte(cam, value); + err += w9968cf_smbus_write_ack(cam); + err += w9968cf_smbus_stop(cam); + + /* Serial data disable */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + DBG(5, "I2C read byte done, addr.0x%04X." + "value 0x%02X.", address, *value) + else + DBG(5, "I2C read byte failed, addr.0x%04X." + "wrong value 0x%02X.", address, *value) + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Value [A] P */ +static int +w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, + u16 address, u8 value) +{ + DBG(4, "i2c_write_byte() is an unsupported transfer mode.") + return -EINVAL; +} + + + +/**************************************************************************** + * I2C interface to kernel * + ****************************************************************************/ + +static int +w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct w9968cf_device* cam = i2c_get_adapdata(adapter); + u8 i, j; + int rc = 0, err = 0; + + switch (addr) { + case OV6xx0_SID: + case OV7xx0_SID: + break; + default: + DBG(4, "Rejected slave ID 0x%04X", addr) + return -EINVAL; + } + + if (size == I2C_SMBUS_BYTE) { + /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ + addr <<= 1; + + if (read_write == I2C_SMBUS_WRITE) + rc = w9968cf_i2c_adap_write_byte(cam, addr, command); + else if (read_write == I2C_SMBUS_READ) + rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte); + + } else if (size == I2C_SMBUS_BYTE_DATA) { + addr <<= 1; + + if (read_write == I2C_SMBUS_WRITE) + rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, + command, data->byte); + else if (read_write == I2C_SMBUS_READ) { + for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { + rc = w9968cf_i2c_adap_read_byte_data(cam, addr, + command, &data->byte); + if (rc < 0) { + /* Work around: this seems to wake up + the EEPROM from the stall state */ + for (j = 0; j <= 10; j++) { + err += w9968cf_write_sb(cam,0x0020); + err += w9968cf_write_sb(cam,0x0000); + if (err) + break; + } + } + else + break; + } + + } else + return -EINVAL; + + } else { + DBG(4, "Unsupported I2C transfer mode (%d)", size) + return -EINVAL; + } + + /* This works around a bug in the I2C core */ + if (rc > 0) + rc = 0; + + return rc; +} + + +static u32 w9968cf_i2c_func(struct i2c_adapter* adap) +{ + return I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + + +static int w9968cf_i2c_attach_inform(struct i2c_client* client) +{ + struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); + const char* clientname = i2c_clientname(client); + int id = client->driver->id; + + if (id == I2C_DRIVERID_OVCAMCHIP) { + int rc = 0; + + cam->sensor_client = client; + + rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, + &cam->sensor_mono); + if (rc < 0) { + DBG(1, "CMOS sensor initialization failed (rc=%d)",rc); + cam->sensor_client = NULL; + return rc; + } + + if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, + &cam->sensor) < 0) + rc = -EIO; + else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID) + w9968cf_sensor_configure(cam); + else + rc = -EINVAL; + + if (rc < 0) { + cam->sensor_client = NULL; + cam->sensor = CC_UNKNOWN; + return rc; + } + } else { + DBG(4, "Rejected client [%s] with [%s]", + clientname, client->driver->name) + return -1; + } + + DBG(2, "I2C attach client [%s] with [%s]", + clientname, client->driver->name) + + return 0; +} + + +static int w9968cf_i2c_detach_inform(struct i2c_client* client) +{ + struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); + const char* clientname = i2c_clientname(client); + + if (cam->sensor_client == client) { + cam->sensor_client = NULL; + } + + DBG(2, "I2C detach [%s]", clientname) + + return 0; +} + + +static int +w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd, + unsigned long arg) +{ + return 0; +} + + +static int w9968cf_i2c_init(struct w9968cf_device* cam) +{ + int rc = 0; + + static struct i2c_algorithm algo = { + .name = "W996[87]CF algorithm", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = w9968cf_i2c_smbus_xfer, + .algo_control = w9968cf_i2c_control, + .functionality = w9968cf_i2c_func, + }; + + static struct i2c_adapter adap = { + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF, + .class = I2C_ADAP_CLASS_CAM_DIGITAL, + .owner = THIS_MODULE, + .client_register = w9968cf_i2c_attach_inform, + .client_unregister = w9968cf_i2c_detach_inform, + .algo = &algo, + }; + + memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); + strcpy(cam->i2c_adapter.name, "w9968cf"); + i2c_set_adapdata(&cam->i2c_adapter, cam); + + DBG(6, "Registering I2C bus with kernel...") + + rc = i2c_add_adapter(&cam->i2c_adapter); + if (rc) + DBG(5, "Failed to register the I2C bus.") + else + DBG(5, "I2C bus registered.") + + return rc; +} + + + +/**************************************************************************** + * Helper functions * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_turn_on_led(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */ + err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ + err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */ + err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */ + err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ + + if (err) + DBG(2, "Couldn't turn on the LED.") + + DBG(5, "LED turned on.") + + return err; +} + + +/*-------------------------------------------------------------------------- + Write some registers for the device initialization. + This function is called once on open(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_init_chip(struct w9968cf_device* cam) +{ + int err = 0, rc = 0; + + err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */ + + err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */ + err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */ + + err += w9968cf_write_reg(cam, 0x0000, 0x20); /* Y frame buf.0, low */ + err += w9968cf_write_reg(cam, 0x0000, 0x21); /* Y frame buf.0, high */ + err += w9968cf_write_reg(cam, 0xb000, 0x22); /* Y frame buf.1, low */ + err += w9968cf_write_reg(cam, 0x0004, 0x23); /* Y frame buf.1, high */ + err += w9968cf_write_reg(cam, 0x5800, 0x24); /* U frame buf.0, low */ + err += w9968cf_write_reg(cam, 0x0002, 0x25); /* U frame buf.0, high */ + err += w9968cf_write_reg(cam, 0x0800, 0x26); /* U frame buf.1, low */ + err += w9968cf_write_reg(cam, 0x0007, 0x27); /* U frame buf.1, high */ + err += w9968cf_write_reg(cam, 0x8400, 0x28); /* V frame buf.0, low */ + err += w9968cf_write_reg(cam, 0x0003, 0x29); /* V frame buf.0, high */ + err += w9968cf_write_reg(cam, 0x3400, 0x2a); /* V frame buf.1, low */ + err += w9968cf_write_reg(cam, 0x0008, 0x2b); /* V frame buf.1, high */ + + err += w9968cf_write_reg(cam, 0x6000, 0x32); /* JPEG bitstream buf 0 */ + err += w9968cf_write_reg(cam, 0x0009, 0x33); /* JPEG bitstream buf 0 */ + err += w9968cf_write_reg(cam, 0x2000, 0x34); /* JPEG bitstream buf 1 */ + err += w9968cf_write_reg(cam, 0x000d, 0x35); /* JPEG bitstream buf 1 */ + + err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */ + err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ + err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */ + err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */ + + err += w9968cf_set_picture(cam, cam->picture); /* this before */ + err += w9968cf_set_window(cam, cam->window); + + if (err) + goto error; + + rc = w9968cf_sensor_change_settings(cam); + if (rc) + goto error; + + DBG(5, "Chip successfully initialized."); + + return 0; + +error: + DBG(1, "Chip initialization failed.") + if (err) + return err; + else + return rc; +} + + +/*-------------------------------------------------------------------------- + Change the picture settings of the camera. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) +{ + u16 fmt, hw_depth, hw_palette, reg_v = 0x0000; + int err = 0, rc = 0; + + /* Make sure we are using a valid depth */ + pict.depth = w9968cf_valid_depth(pict.palette); + + fmt = pict.palette; + + hw_depth = pict.depth; /* depth used by the winbond chip */ + hw_palette = pict.palette; /* palette used by the winbond chip */ + + /* VS & HS polarities */ + reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11); + + switch (fmt) + { + case VIDEO_PALETTE_UYVY: + reg_v |= 0x0000; + cam->vpp_flag = VPP_NONE; + break; + case VIDEO_PALETTE_YUV422P: + reg_v |= 0x0002; + cam->vpp_flag = VPP_DECOMPRESSION; + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + reg_v |= 0x0003; + cam->vpp_flag = VPP_DECOMPRESSION; + break; + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_YUV422: + reg_v |= 0x0000; + cam->vpp_flag = VPP_SWAP_YUV_BYTES; + hw_palette = VIDEO_PALETTE_UYVY; + break; + /* Original video is used instead of RGBX palettes. + Software conversion later. */ + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + reg_v |= 0x0000; /* UYVY 16 bit is used */ + hw_depth = 16; + hw_palette = VIDEO_PALETTE_UYVY; + cam->vpp_flag = VPP_UYVY_TO_RGBX; + break; + } + + /* FIXME: 'hardware double buffer' doesn't work when compressed video + is enabled (corrupted frames). */ + if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) + reg_v |= 0x0080; + + if (cam->clamping) + reg_v |= 0x0020; + + if (cam->filter_type == 1) + reg_v |= 0x0008; + else if (cam->filter_type == 2) + reg_v |= 0x000c; + + err = w9968cf_write_reg(cam, reg_v, 0x16); + if (err) + goto error; + + rc = w9968cf_sensor_set_picture(cam, pict); + if (rc) + goto error; + + /* If all went well, update the device data structure */ + memcpy(&cam->picture, &pict, sizeof(pict)); + cam->hw_depth = hw_depth; + cam->hw_palette = hw_palette; + + /* Settings changed, so we clear the frame buffers */ + memset(cam->frame[0].buffer, 0, + cam->nbuffers*w9968cf_get_max_bufsize(cam)); + + DBG(4, "Palette is %s, depth is %d bpp.", + symbolic(v4l1_plist, pict.palette), pict.depth) + + return 0; + +error: + DBG(1, "Failed to change picture settings.") + if (err) + return err; + else + return rc; +} + + +/*-------------------------------------------------------------------------- + Change the capture area size of the camera. + This function _must_ be called _after_ w9968cf_set_picture(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) +{ + u16 x, y, w, h, scx, scy, cw, ch, ax, ay; + unsigned long fw, fh; + struct ovcamchip_window s_win; + int err=0, rc=0; + + /* Work around to avoid FP arithmetics */ + #define __SC(x) ((x) << 10) + #define __UNSC(x) ((x) >> 10) + + /* Make sure we are using a supported resolution */ + if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, + (u16*)&win.height))) + goto error; + + /* Scaling factors */ + fw = __SC(win.width) / cam->maxwidth; + fh = __SC(win.height) / cam->maxheight; + + /* Set up the width and height values used by the chip */ + if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) { + cam->vpp_flag |= VPP_UPSCALE; + /* Calculate largest w,h mantaining the same w/h ratio */ + w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh; + h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight; + if (w < cam->minwidth) /* just in case */ + w = cam->minwidth; + if (h < cam->minheight) /* just in case */ + h = cam->minheight; + } else { + cam->vpp_flag &= ~VPP_UPSCALE; + w = win.width; + h = win.height; + } + + /* x,y offsets of the cropped area */ + scx = cam->start_cropx; + scy = cam->start_cropy; + + /* Calculate cropped area manteining the right w/h ratio */ + if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) { + cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh; + ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight; + } else { + cw = w; + ch = h; + } + + /* Setup the sensor window */ + s_win.format = SENSOR_FORMAT; + s_win.width = cam->maxwidth; + s_win.height = cam->maxheight; + s_win.quarter = 0; /* full progressive video */ + + /* Center it */ + s_win.x = (s_win.width - cw) / 2; + s_win.y = (s_win.height - ch) / 2; + + /* Clock divisor */ + if (cam->clockdiv >= 0) + s_win.clockdiv = cam->clockdiv; /* manual override */ + else + switch (cam->sensor) { + case CC_OV6620: + s_win.clockdiv = 0; + break; + case CC_OV6630: + s_win.clockdiv = 0; + break; + case CC_OV76BE: + case CC_OV7610: + case CC_OV7620: + s_win.clockdiv = 0; + break; + default: + s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR; + } + + /* We have to scale win.x and win.y offsets */ + if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) + || (cam->vpp_flag & VPP_UPSCALE) ) { + ax = __SC(win.x)/fw; + ay = __SC(win.y)/fh; + } else { + ax = win.x; + ay = win.y; + } + + if ((ax + cw) > cam->maxwidth) + ax = cam->maxwidth - cw; + + if ((ay + ch) > cam->maxheight) + ay = cam->maxheight - ch; + + /* Adjust win.x, win.y */ + if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) + || (cam->vpp_flag & VPP_UPSCALE) ) { + win.x = __UNSC(ax*fw); + win.y = __UNSC(ay*fh); + } else { + win.x = ax; + win.y = ay; + } + + /* Offsets used by the chip */ + x = ax + s_win.x; + y = ay + s_win.y; + + /* Go ! */ + if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win))) + goto error; + + err += w9968cf_write_reg(cam, scx + x, 0x10); + err += w9968cf_write_reg(cam, scy + y, 0x11); + err += w9968cf_write_reg(cam, scx + x + cw, 0x12); + err += w9968cf_write_reg(cam, scy + y + ch, 0x13); + err += w9968cf_write_reg(cam, w, 0x14); + err += w9968cf_write_reg(cam, h, 0x15); + + /* JPEG width & height */ + err += w9968cf_write_reg(cam, w, 0x30); + err += w9968cf_write_reg(cam, h, 0x31); + + /* Y & UV frame buffer strides (in WORD) */ + if (cam->vpp_flag & VPP_DECOMPRESSION) { + err += w9968cf_write_reg(cam, w/2, 0x2c); + err += w9968cf_write_reg(cam, w/4, 0x2d); + } else + err += w9968cf_write_reg(cam, w, 0x2c); + + if (err) + goto error; + + /* If all went well, update the device data structure */ + memcpy(&cam->window, &win, sizeof(win)); + cam->hw_width = w; + cam->hw_height = h; + + /* Settings changed, so we clear the frame buffers */ + memset(cam->frame[0].buffer, 0, + cam->nbuffers*w9968cf_get_max_bufsize(cam)); + + DBG(4, "The capture area is %dx%d, Offset (x,y)=(%d,%d).", + win.width, win.height, win.x, win.y) + + PDBGG("x=%d ,y=%d, w=%d, h=%d, ax=%d, ay=%d, s_win.x=%d, s_win.y=%d, " + "cw=%d, ch=%d, win.x=%d ,win.y=%d, win.width=%d, win.height=%d", + x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y, + win.width, win.height) + + return 0; + +error: + DBG(1, "Failed to change the capture area size.") + if (err) + return err; + else + return rc; +} + + +/*-------------------------------------------------------------------------- + Return non-zero if the palette is supported, 0 otherwise. + --------------------------------------------------------------------------*/ +static inline u16 w9968cf_valid_palette(u16 palette) +{ + u8 i = 0; + while (w9968cf_formatlist[i].palette != 0) { + if (palette == w9968cf_formatlist[i].palette) + return palette; + i++; + } + return 0; +} + + +/*-------------------------------------------------------------------------- + Return the depth corresponding to the given palette. + Palette _must_ be supported ! + --------------------------------------------------------------------------*/ +static inline u16 w9968cf_valid_depth(u16 palette) +{ + u8 i=0; + while (w9968cf_formatlist[i].palette != palette) + i++; + + return w9968cf_formatlist[i].depth; +} + + +/*-------------------------------------------------------------------------- + Return non-zero if the format requires decompression, 0 otherwise. + --------------------------------------------------------------------------*/ +static inline u8 w9968cf_need_decompression(u16 palette) +{ + u8 i = 0; + while (w9968cf_formatlist[i].palette != 0) { + if (palette == w9968cf_formatlist[i].palette) + return w9968cf_formatlist[i].compression; + i++; + } + return 0; +} + + +/*-------------------------------------------------------------------------- + Adjust the asked values for window width and height. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) +{ + u16 maxw, maxh; + + if ((*width < cam->minwidth) || (*height < cam->minheight)) + return -ERANGE; + + maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) + && w9968cf_vppmod_present ? W9968CF_MAX_WIDTH : cam->maxwidth; + maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) + && w9968cf_vppmod_present ? W9968CF_MAX_HEIGHT : cam->maxheight; + + if (*width > maxw) + *width = maxw; + if (*height > maxh) + *height = maxh; + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + *width &= ~15L; /* multiple of 16 */ + *height &= ~15L; + } + + PDBGG("Window size adjusted w=%d, h=%d ", *width, *height) + + return 0; +} + + +/*-------------------------------------------------------------------------- + Initialize the FIFO list of requested frames. + --------------------------------------------------------------------------*/ +static void w9968cf_init_framelist(struct w9968cf_device* cam) +{ + u8 i; + + for (i = 0; i < cam->nbuffers; i++) { + cam->requested_frame[i] = NULL; + cam->frame[i].queued = 0; + cam->frame[i].status = F_UNUSED; + } +} + + +/*-------------------------------------------------------------------------- + Add a frame in the FIFO list of requested frames. + This function is called in process context. + --------------------------------------------------------------------------*/ +static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num) +{ + u8 f; + unsigned long lock_flags; + + spin_lock_irqsave(&cam->flist_lock, lock_flags); + + for (f=0; cam->requested_frame[f] != NULL; f++); + cam->requested_frame[f] = &cam->frame[f_num]; + cam->frame[f_num].queued = 1; + cam->frame[f_num].status = F_UNUSED; /* clear the status */ + + spin_unlock_irqrestore(&cam->flist_lock, lock_flags); + + DBG(6, "Frame #%d pushed into the FIFO list. Position %d.", f_num, f) +} + + +/*-------------------------------------------------------------------------- + Read, store and remove the first pointer in the FIFO list of requested + frames. This function is called in interrupt context. + --------------------------------------------------------------------------*/ +static void +w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep) +{ + u8 i; + + spin_lock(&cam->flist_lock); + + *framep = cam->requested_frame[0]; + + /* Shift the list of pointers */ + for (i = 0; i < cam->nbuffers-1; i++) + cam->requested_frame[i] = cam->requested_frame[i+1]; + cam->requested_frame[i] = NULL; + + spin_unlock(&cam->flist_lock); + + DBG(6,"Popped frame #%d from the list.",*framep-&cam->frame[0]) +} + + +/*-------------------------------------------------------------------------- + High-level video post-processing routine on grabbed frames. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_postprocess_frame(struct w9968cf_device* cam, + struct w9968cf_frame_t* fr) +{ + void *pIn = fr->buffer, *pOut = cam->vpp_buffer, *tmp; + u16 w = cam->window.width, + h = cam->window.height, + d = cam->picture.depth, + fmt = cam->picture.palette, + rgb = cam->force_rgb, + hw_w = cam->hw_width, + hw_h = cam->hw_height, + hw_d = cam->hw_depth; + int err = 0; + + #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;} + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + memcpy(pOut, pIn, fr->length); + _PSWAP(pIn, pOut) + err = (*w9968cf_vpp_decode)(pIn, fr->length, hw_w, hw_h, pOut); + PDBGG("Compressed frame length: %li",(unsigned long)fr->length) + fr->length = (hw_w*hw_h*hw_d)/8; + _PSWAP(pIn, pOut) + if (err) { + DBG(4, "An error occurred while decoding the frame: " + "%s.", symbolic(decoder_errlist, err)) + return err; + } else + DBG(6, "Frame decoded") + } + + if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) { + (*w9968cf_vpp_swap_yuvbytes)(pIn, fr->length); + DBG(6, "Original UYVY component ordering changed.") + } + + if (cam->vpp_flag & VPP_UPSCALE) { + (*w9968cf_vpp_scale_up)(pIn, pOut, hw_w, hw_h, hw_d, w, h); + fr->length = (w*h*hw_d)/8; + _PSWAP(pIn, pOut) + DBG(6, "Vertical up-scaling done: %d,%d,%dbpp->%d,%d", + hw_w, hw_h, hw_d, w, h) + } + + if (cam->vpp_flag & VPP_UYVY_TO_RGBX) { + (*w9968cf_vpp_uyvy_to_rgbx)(pIn, fr->length, pOut, fmt, rgb); + fr->length = (w*h*d)/8; + _PSWAP(pIn, pOut) + DBG(6, "UYVY-16bit to %s conversion done.", + symbolic(v4l1_plist, fmt)) + } + + if (pOut == fr->buffer) + memcpy(fr->buffer, cam->vpp_buffer, fr->length); + + return 0; +} + + + +/**************************************************************************** + * CMOS sensor control routines * + ****************************************************************************/ + +static int +w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val) +{ + struct ovcamchip_control ctl; + int rc; + + ctl.id = cid; + ctl.value = val; + + rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl); + + return rc; +} + +static int +w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val) +{ + struct ovcamchip_control ctl; + int rc; + + ctl.id = cid; + + rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl); + if (rc >= 0) + *val = ctl.value; + + return rc; +} + + +static inline int +w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg) +{ + struct i2c_client* c = cam->sensor_client; + + DBG(6, "Executing CMOS sensor command...") + + if (c && c->driver->command) + return c->driver->command(cam->sensor_client, cmd, arg); + else + return -ENODEV; +} + + +/*-------------------------------------------------------------------------- + Change some settings of the CMOS sensor. + Returns: 0 for success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_sensor_change_settings(struct w9968cf_device* cam) +{ + int rc; + + /* Auto brightness */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, + cam->auto_brt); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Auto exposure */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, + cam->auto_exp); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Banding filter */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, + cam->bandfilt); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Light frequency */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ, + cam->lightfreq); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Back light */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT, + cam->backlight); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + /* Mirror */ + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR, + cam->mirror); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + return 0; +} + + +/*-------------------------------------------------------------------------- + Get some current picture settings from the CMOS sensor. + Returns: 0 for success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_sensor_get_picture(struct w9968cf_device* cam, + struct video_picture* pict) +{ + int rc, v; + + /* Don't return error if a setting is unsupported, or rest of settings + will not be performed */ + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->contrast = v; + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->brightness = v; + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->colour = v; + + rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + pict->hue = v; + + pict->whiteness = W9968CF_WHITENESS; /* to do! */ + + DBG(5, "Got picture settings from the CMOS sensor.") + + PDBGG("Brightness, contrast, hue, colour, whiteness are " + "%d,%d,%d,%d,%d.", pict->brightness, pict->contrast, + pict->hue, pict->colour, pict->whiteness) + + return 0; +} + + +/*-------------------------------------------------------------------------- + Change picture settings of the CMOS sensor. + Returns: 0 for success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_sensor_set_picture(struct w9968cf_device* cam, + struct video_picture pict) +{ + int rc; + + rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + if (!cam->auto_brt) { + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, + pict.brightness); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + } + + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue); + if (SENSOR_FATAL_ERROR(rc)) + return rc; + + PDBGG("Brightness, contrast, hue, colour, whiteness are " + "%d,%d,%d,%d,%d.", pict.brightness, pict.contrast, + pict.hue, pict.colour, pict.whiteness) + + return 0; +} + + + +/**************************************************************************** + * Camera configuration * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + This function is called when the CMOS sensor is detected. + --------------------------------------------------------------------------*/ +static void w9968cf_sensor_configure(struct w9968cf_device* cam) +{ + /* NOTE: Make sure width and height are a multiple of 16 */ + + switch (cam->sensor_client->addr) { + case OV6xx0_SID: + cam->maxwidth = 352; + cam->maxheight = 288; + cam->minwidth = 64; + cam->minheight = 48; + break; + case OV7xx0_SID: + cam->maxwidth = 640; + cam->maxheight = 480; + cam->minwidth = 64; + cam->minheight = 48; + break; + } + + /* These values depend on the ones in the ovxxx0.c sources */ + switch (cam->sensor) { + case CC_OV7620: + cam->start_cropx = 287; + cam->start_cropy = 35; + /* Seems to work around a bug in the CMOS sensor */ + cam->vs_polarity = 1; + cam->hs_polarity = 1; + break; + default: + cam->start_cropx = 320; + cam->start_cropy = 35; + cam->vs_polarity = 1; + cam->hs_polarity = 0; + } + + DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor)) +} + + +/*-------------------------------------------------------------------------- + Fill some basic fields in the main device data structure. + This function is called once on w9968cf_usb_probe() for each recognized + camera. + --------------------------------------------------------------------------*/ +static void +w9968cf_configure_camera(struct w9968cf_device* cam, + struct usb_device* udev, + enum w9968cf_model_id mod_id, + const unsigned short dev_nr) +{ + init_MUTEX(&cam->fileop_sem); + init_waitqueue_head(&cam->open); + spin_lock_init(&cam->urb_lock); + spin_lock_init(&cam->flist_lock); + + cam->users = 0; + cam->disconnected = 0; + cam->usbdev = udev; + cam->id = mod_id; + cam->sensor = CC_UNKNOWN; + + strcpy(cam->v4ldev.name, symbolic(camlist, mod_id)); + cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev.hardware = VID_HARDWARE_W9968CF; + cam->v4ldev.fops = &w9968cf_fops; + cam->v4ldev.priv = (void*)cam; + cam->v4ldev.minor = video_nr[dev_nr]; + + /* Calculate the alternate setting number (from 1 to 16) + according to the 'packet_size' module parameter */ + if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE) + packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE; + for (cam->altsetting = 1; + packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1]; + cam->altsetting++); + + cam->max_buffers = (max_buffers[dev_nr] < 2 || + max_buffers[dev_nr] > W9968CF_MAX_BUFFERS) + ? W9968CF_BUFFERS : max_buffers[dev_nr]; + + cam->double_buffer = (double_buffer[dev_nr] == 0 || + double_buffer[dev_nr] == 1) + ? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER; + + cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1) + ? clamping[dev_nr] : W9968CF_CLAMPING; + + cam->filter_type = (filter_type[dev_nr] == 0 || + filter_type[dev_nr] == 1 || + filter_type[dev_nr] == 2) + ? filter_type[dev_nr] : W9968CF_FILTER_TYPE; + + cam->capture = 1; + + cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1) + ? largeview[dev_nr] : W9968CF_LARGEVIEW; + + cam->decompression = (decompression[dev_nr] == 0 || + decompression[dev_nr] == 1 || + decompression[dev_nr] == 2) + ? decompression[dev_nr] : W9968CF_DECOMPRESSION; + + cam->upscaling = (upscaling[dev_nr] == 0 || + upscaling[dev_nr] == 1) + ? upscaling[dev_nr] : W9968CF_UPSCALING; + + cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1) + ? autobright[dev_nr] : W9968CF_AUTOBRIGHT; + + cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1) + ? autoexp[dev_nr] : W9968CF_AUTOEXP; + + cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60) + ? lightfreq[dev_nr] : W9968CF_LIGHTFREQ; + + cam->bandfilt = (bandingfilter[dev_nr] == 0 || + bandingfilter[dev_nr] == 1) + ? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER; + + cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1) + ? backlight[dev_nr] : W9968CF_BACKLIGHT; + + cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0) + ? clockdiv[dev_nr] : W9968CF_CLOCKDIV; + + cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1) + ? mirror[dev_nr] : W9968CF_MIRROR; + + cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1) + ? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO; + + cam->picture.brightness = brightness[dev_nr]; + cam->picture.hue = hue[dev_nr]; + cam->picture.colour = colour[dev_nr]; + cam->picture.contrast = contrast[dev_nr]; + cam->picture.whiteness = whiteness[dev_nr]; + if (w9968cf_valid_palette(force_palette[dev_nr])) { + cam->picture.palette = force_palette[dev_nr]; + cam->force_palette = 1; + } else { + cam->force_palette = 0; + if (cam->decompression == 0) + cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF; + else if (cam->decompression == 1) + cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE; + else + cam->picture.palette = W9968CF_PALETTE_DECOMP_ON; + } + + cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1) + ? force_rgb[dev_nr] : W9968CF_FORCE_RGB; + + cam->window.x = 0; + cam->window.y = 0; + cam->window.width = W9968CF_WIDTH; + cam->window.height = W9968CF_HEIGHT; + cam->window.chromakey = 0; + cam->window.clipcount = 0; + cam->window.flags = 0; + + /* If the video post-processing module is not present, some paramaters + must be overridden: */ + if (!w9968cf_vppmod_present) { + if (cam->decompression == 1) + cam->decompression = 2; + cam->upscaling = 0; + if (cam->picture.palette != VIDEO_PALETTE_UYVY) + cam->force_palette = 0; + cam->picture.palette = VIDEO_PALETTE_UYVY; + } + + cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); + + DBG(3, "%s configured with settings #%d:", + symbolic(camlist, cam->id), dev_nr) + + DBG(3, "- Data packet size for USB isochrnous transfer: %d bytes.", + wMaxPacketSize[cam->altsetting-1]) + + DBG(3, "- Number of requested video frame buffers: %d", + cam->max_buffers) + + if (cam->double_buffer) + DBG(3, "- Hardware double buffering enabled.") + else + DBG(3, "- Hardware double buffering disabled.") + + if (cam->filter_type == 0) + DBG(3, "- Video filtering disabled.") + else if (cam->filter_type == 1) + DBG(3, "- Video filtering enabled: type 1-2-1.") + else if (cam->filter_type == 2) + DBG(3, "- Video filtering enabled: type 2-3-6-3-2.") + + if (cam->clamping) + DBG(3, "- Video data clamping (CCIR-601 format) enabled.") + else + DBG(3, "- Video data clamping (CCIR-601 format) disabled.") + + if (cam->largeview) + DBG(3, "- Large view enabled.") + else + DBG(3, "- Large view disabled.") + + if ((cam->decompression) == 0 && (!cam->force_palette)) + DBG(3, "- Decompression disabled.") + else if ((cam->decompression) == 1 && (!cam->force_palette)) + DBG(3, "- Decompression forced.") + else if ((cam->decompression) == 2 && (!cam->force_palette)) + DBG(3, "- Decompression allowed.") + + if (cam->upscaling) + DBG(3, "- Software image scaling enabled.") + else + DBG(3, "- Software image scaling disabled.") + + if (cam->force_palette) + DBG(3, "- Image palette forced to %s.", + symbolic(v4l1_plist, cam->picture.palette)) + + if (cam->force_rgb) + DBG(3, "- RGB component ordering will be used instead of BGR.") + + if (cam->auto_brt) + DBG(3, "- Auto brightness enabled.") + else + DBG(3, "- Auto brightness disabled.") + + if (cam->auto_exp) + DBG(3, "- Auto exposure enabled.") + else + DBG(3, "- Auto exposure disabled.") + + if (cam->backlight) + DBG(3, "- Backlight exposure algorithm enabled.") + else + DBG(3, "- Backlight exposure algorithm disabled.") + + if (cam->mirror) + DBG(3, "- Mirror enabled.") + else + DBG(3, "- Mirror disabled.") + + if (cam->bandfilt) + DBG(3, "- Banding filter enabled.") + else + DBG(3, "- Banding filter disabled.") + + DBG(3, "- Power lighting frequency: %d", cam->lightfreq) + + if (cam->clockdiv == -1) + DBG(3, "- Automatic clock divisor enabled.") + else + DBG(3, "- Clock divisor: %d", cam->clockdiv) + + if (cam->sensor_mono) + DBG(3, "- CMOS sensor used as monochrome.") + else + DBG(3, "- CMOS sensor not used as monochrome.") +} + + +/*-------------------------------------------------------------------------- + Release the resources used by the driver. + This function is called on disconnect + (or on close if deallocation has been deferred) + --------------------------------------------------------------------------*/ +static void w9968cf_release_resources(struct w9968cf_device* cam) +{ + down(&w9968cf_devlist_sem); + + DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor) + + video_unregister_device(&cam->v4ldev); + list_del(&cam->v4llist); + i2c_del_adapter(&cam->i2c_adapter); + w9968cf_deallocate_memory(cam); + kfree(cam->control_buffer); + kfree(cam->data_buffer); + + up(&w9968cf_devlist_sem); + + DBG(5, "Resources released.") +} + + + +/**************************************************************************** + * Video4Linux interface * + ****************************************************************************/ + +static int w9968cf_open(struct inode* inode, struct file* filp) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + int err; + + down(&cam->dev_sem); + + if (cam->sensor == CC_UNKNOWN) { + DBG(2, "No supported CMOS sensor has been detected by the " + "'ovcamchip' module for the %s (/dev/video%d). Make " + "sure it is loaded *before* the 'w9968cf' module.", + symbolic(camlist, cam->id),cam->v4ldev.minor) + up(&cam->dev_sem); + return -ENODEV; + } + + if (cam->users) { + DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.", + symbolic(camlist, cam->id),cam->v4ldev.minor, cam->command) + if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { + up(&cam->dev_sem); + return -EWOULDBLOCK; + } + up(&cam->dev_sem); + err = wait_event_interruptible(cam->open, cam->disconnected || + (cam->users == 0)); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + down(&cam->dev_sem); + } + + DBG(5, "Opening the %s, /dev/video%d ...", + symbolic(camlist, cam->id), cam->v4ldev.minor) + + cam->streaming = 0; + cam->misconfigured = 0; + + if (!w9968cf_vppmod_present) + w9968cf_vppmod_detect(); + + if ((err = w9968cf_allocate_memory(cam))) + goto deallocate_memory; + + if ((err = w9968cf_init_chip(cam))) + goto deallocate_memory; + + if ((err = w9968cf_start_transfer(cam))) + goto deallocate_memory; + + filp->private_data = (void*)cam; + + cam->users++; + strcpy(cam->command, current->comm); + + init_waitqueue_head(&cam->wait_queue); + + up(&cam->dev_sem); + + DBG(5, "Video device is open.") + return 0; + +deallocate_memory: + w9968cf_deallocate_memory(cam); + DBG(2, "Failed to open the video device.") + up(&cam->dev_sem); + return err; +} + + +static int w9968cf_release(struct inode* inode, struct file* filp) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + + down(&cam->dev_sem); /* prevent disconnect() to be called */ + + w9968cf_stop_transfer(cam); + + if (cam->disconnected) { + w9968cf_release_resources(cam); + up(&cam->dev_sem); + kfree(cam); + return 0; + } + + cam->users--; + w9968cf_deallocate_memory(cam); + + if (waitqueue_active(&cam->open)) + wake_up_interruptible(&cam->open); + + DBG(5, "Video device closed.") + up(&cam->dev_sem); + return 0; +} + + +static ssize_t +w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + struct w9968cf_frame_t* fr; + int err = 0; + + if (filp->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + if (down_interruptible(&cam->fileop_sem)) + return -ERESTARTSYS; + + if (cam->disconnected) { + DBG(2, "Device not present.") + up(&cam->fileop_sem); + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + up(&cam->fileop_sem); + return -EIO; + } + + if (!cam->frame[0].queued) + w9968cf_push_frame(cam, 0); + + if (!cam->frame[1].queued) + w9968cf_push_frame(cam, 1); + + err = wait_event_interruptible(cam->wait_queue, + cam->frame[0].status == F_READY || + cam->frame[1].status == F_READY || + cam->disconnected); + if (err) { + up(&cam->fileop_sem); + return err; + } + if (cam->disconnected) { + up(&cam->fileop_sem); + return -ENODEV; + } + + fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1]; + + if (w9968cf_vppmod_present) + w9968cf_postprocess_frame(cam, fr); + + if (count > fr->length) + count = fr->length; + + if (copy_to_user(buf, fr->buffer, count)) { + fr->status = F_UNUSED; + up(&cam->fileop_sem); + return -EFAULT; + } + *f_pos += count; + + fr->status = F_UNUSED; + + DBG(5, "%d bytes read.", count) + + up(&cam->fileop_sem); + return count; +} + + +static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + + unsigned long vsize = vma->vm_end - vma->vm_start, + psize = cam->nbuffers * w9968cf_get_max_bufsize(cam), + start = vma->vm_start, + pos = (unsigned long)cam->frame[0].buffer, + page; + + if (cam->disconnected) { + DBG(2, "Device not present.") + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + return -EIO; + } + + PDBGG("mmapping %li bytes...", vsize) + + if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) + return -EAGAIN; + + while (vsize > 0) { + page = kvirt_to_pa(pos) + vma->vm_pgoff; + if (remap_page_range(vma, start, page, PAGE_SIZE, + vma->vm_page_prot)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + vsize = (vsize > PAGE_SIZE) ? vsize-PAGE_SIZE : 0; + } + + DBG(5, "mmap method successfully called.") + return 0; +} + + +static int +w9968cf_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)video_devdata(filp)->priv; + int err; + + if (down_interruptible(&cam->fileop_sem)) + return -ERESTARTSYS; + + if (cam->disconnected) { + DBG(2, "Device not present.") + up(&cam->fileop_sem); + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + up(&cam->fileop_sem); + return -EIO; + } + + err = w9968cf_do_ioctl(cam, cmd, (void*)arg); + + up(&cam->fileop_sem); + return err; +} + + +static int +w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg) +{ + const char* v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", + "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", + "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", + "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", + "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", + "GVBIFMT", "SVBIFMT" + }; + + #define V4L1_IOCTL(cmd) \ + ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \ + v4l1_ioctls[_IOC_NR((cmd))] : "???") + + switch (cmd) { + + case VIDIOCGCAP: /* get video capability */ + { + struct video_capability cap = { + .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, + .channels = 1, + .audios = 0, + .minwidth = cam->minwidth, + .minheight = cam->minheight, + }; + sprintf(cap.name, "W996[87]CF USB Camera #%d", + cam->v4ldev.minor); + cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present) + ? W9968CF_MAX_WIDTH : cam->maxwidth; + cap.maxheight = (cam->upscaling && w9968cf_vppmod_present) + ? W9968CF_MAX_HEIGHT : cam->maxheight; + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + DBG(5, "VIDIOCGCAP successfully called.") + return 0; + } + + case VIDIOCGCHAN: /* get video channel informations */ + { + struct video_channel chan; + if (copy_from_user(&chan, arg, sizeof(chan))) + return -EFAULT; + + if (chan.channel != 0) + return -EINVAL; + + strcpy(chan.name, "Camera"); + chan.tuners = 0; + chan.flags = 0; + chan.type = VIDEO_TYPE_CAMERA; + chan.norm = VIDEO_MODE_AUTO; + + if (copy_to_user(arg, &chan, sizeof(chan))) + return -EFAULT; + + DBG(5, "VIDIOCGCHAN successfully called.") + return 0; + } + + case VIDIOCSCHAN: /* set active channel */ + { + struct video_channel chan; + + if (copy_from_user(&chan, arg, sizeof(chan))) + return -EFAULT; + + if (chan.channel != 0) + return -EINVAL; + + DBG(5, "VIDIOCSCHAN successfully called.") + return 0; + } + + case VIDIOCGPICT: /* get image properties of the picture */ + { + struct video_picture pict; + + if (w9968cf_sensor_get_picture(cam, &pict)) + return -EIO; + + pict.depth = cam->picture.depth; + pict.palette = cam->picture.palette; + + if (copy_to_user(arg, &pict, sizeof(pict))) + return -EFAULT; + + DBG(5, "VIDIOCGPICT successfully called.") + return 0; + } + + case VIDIOCSPICT: /* change picture settings */ + { + struct video_picture pict; + int err = 0; + + if (copy_from_user(&pict, arg, sizeof(pict))) + return -EFAULT; + + if ( (cam->force_palette || !w9968cf_vppmod_present) + && pict.palette != cam->picture.palette ) { + DBG(4, "Palette %s rejected. Only %s is allowed.", + symbolic(v4l1_plist, pict.palette), + symbolic(v4l1_plist, cam->picture.palette)) + return -EINVAL; + } + + if (!w9968cf_valid_palette(pict.palette)) { + DBG(4, "Palette %s not supported. VIDIOCSPICT failed.", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + + if (pict.depth != w9968cf_valid_depth(pict.palette)) { + DBG(4, "Depth %d bpp is not supported for %s palette. " + "VIDIOCSPICT failed.", + pict.depth, symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + + if (!cam->force_palette) { + if (cam->decompression == 0) { + if (w9968cf_need_decompression(pict.palette)) { + DBG(4, "Decompression disabled: palette %s is not " + "allowed. VIDIOCSPICT failed.", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + } else if (cam->decompression == 1) { + if (!w9968cf_need_decompression(pict.palette)) { + DBG(4, "Decompression forced: palette %s is not " + "allowed. VIDIOCSPICT failed.", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + } + } + + if (pict.palette != cam->picture.palette || + pict.depth != cam->picture.depth) + { + if(*cam->requested_frame + || cam->frame_current->queued) { + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + if (w9968cf_set_picture(cam, pict)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + + } else if ( ((pict.brightness != cam->picture.brightness) && + (!cam->auto_brt)) || + pict.hue != cam->picture.hue || + pict.colour != cam->picture.colour || + pict.contrast != cam->picture.contrast || + pict.whiteness != cam->picture.whiteness ) { + if (w9968cf_sensor_set_picture(cam, pict)) + return -EIO; + } + + DBG(5, "VIDIOCSPICT successfully called.") + return 0; + } + + case VIDIOCSWIN: /* set capture area */ + { + struct video_window win; + int err = 0; + + if (copy_from_user(&win, arg, sizeof(win))) + return -EFAULT; + + DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%d, " + "x=%d, y=%d, %dx%d", win.clipcount, win.flags, + win.x, win.y, win.width, win.height) + + if (win.clipcount != 0 || win.flags != 0) + return -EINVAL; + + if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, + (u16*)&win.height))) { + DBG(4, "Resolution not supported (%dx%d)." + "VIDIOCSWIN failed.", win.width, win.height) + return err; + } + + if (win.x != cam->window.x || + win.y != cam->window.y || + win.width != cam->window.width || + win.height != cam->window.height) { + + if(*cam->requested_frame + || cam->frame_current->queued) { + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + /* This _must_ be called before set_window() */ + if (w9968cf_set_picture(cam, cam->picture)) + goto ioctl_fail; + + if (w9968cf_set_window(cam, win)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + } + + DBG(5, "VIDIOCSWIN successfully called. ") + return 0; + } + + case VIDIOCGWIN: /* get current window properties */ + { + if (copy_to_user(arg,&cam->window,sizeof(struct video_window))) + return -EFAULT; + + DBG(5, "VIDIOCGWIN successfully called.") + return 0; + } + + case VIDIOCGMBUF: /* request for memory (mapped) buffer */ + { + struct video_mbuf mbuf; + u8 i; + + mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam); + mbuf.frames = cam->nbuffers; + for (i = 0; i < cam->nbuffers; i++) + mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer - + (unsigned long)cam->frame[0].buffer; + + if (copy_to_user(arg, &mbuf, sizeof(mbuf))) + return -EFAULT; + + DBG(5, "VIDIOCGMBUF successfully called.") + return 0; + } + + case VIDIOCMCAPTURE: /* start the capture to a frame */ + { + struct video_mmap mmap; + struct w9968cf_frame_t* fr; + int err = 0; + + if (copy_from_user(&mmap, arg, sizeof(mmap))) + return -EFAULT; + + DBG(6, "VIDIOCMCAPTURE called: frame #%d, format=%s, %dx%d", + mmap.frame, symbolic(v4l1_plist, mmap.format), + mmap.width, mmap.height) + + if (mmap.frame >= cam->nbuffers) { + DBG(4, "Invalid frame number (%d). " + "VIDIOCMCAPTURE failed.", mmap.frame) + return -EINVAL; + } + + if (mmap.format!=cam->picture.palette && + (cam->force_palette || !w9968cf_vppmod_present)) { + DBG(4, "Palette %s rejected. Only %s is allowed.", + symbolic(v4l1_plist, mmap.format), + symbolic(v4l1_plist, cam->picture.palette)) + return -EINVAL; + } + + if (!w9968cf_valid_palette(mmap.format)) { + DBG(4, "Palette %s not supported. " + "VIDIOCMCAPTURE failed.", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + + if (!cam->force_palette) { + if (cam->decompression == 0) { + if (w9968cf_need_decompression(mmap.format)) { + DBG(4, "Decompression disabled: palette %s is not " + "allowed. VIDIOCSPICT failed.", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + } else if (cam->decompression == 1) { + if (!w9968cf_need_decompression(mmap.format)) { + DBG(4, "Decompression forced: palette %s is not " + "allowed. VIDIOCSPICT failed.", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + } + } + + if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width, + (u16*)&mmap.height)) { + DBG(4, "Resolution not supported (%dx%d). " + "VIDIOCMCAPTURE failed.", + mmap.width, mmap.height) + return -EINVAL; + } + + fr = &cam->frame[mmap.frame]; + + if (mmap.width != cam->window.width || + mmap.height != cam->window.height || + mmap.format != cam->picture.palette) { + + struct video_window win; + struct video_picture pict; + + if(*cam->requested_frame + || cam->frame_current->queued) { + DBG(6, "VIDIOCMCAPTURE. Change settings for " + "frame #%d: %dx%d, format %s. Wait...", + mmap.frame, mmap.width, mmap.height, + symbolic(v4l1_plist, mmap.format)) + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + memcpy(&win, &cam->window, sizeof(win)); + memcpy(&pict, &cam->picture, sizeof(pict)); + win.width = mmap.width; + win.height = mmap.height; + pict.palette = mmap.format; + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + /* This before set_window */ + if (w9968cf_set_picture(cam, pict)) + goto ioctl_fail; + + if (w9968cf_set_window(cam, win)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + + } else if (fr->queued) { + + DBG(6, "Wait until frame #%d is free.", mmap.frame) + + err = wait_event_interruptible(cam->wait_queue, + cam->disconnected || + (!fr->queued)); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + w9968cf_push_frame(cam, mmap.frame); + DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame) + return 0; + } + + case VIDIOCSYNC: /* wait until the capture of a frame is finished */ + { + unsigned int f_num = *((unsigned int *) arg); + struct w9968cf_frame_t* fr; + int err = 0; + + if (f_num >= cam->nbuffers) { + DBG(4, "Invalid frame number (%d). " + "VIDIOCMCAPTURE failed.", f_num) + return -EINVAL; + } + + DBG(6, "VIDIOCSYNC called for frame #%d", f_num) + + fr = &cam->frame[f_num]; + + switch (fr->status) { + case F_UNUSED: + if (!fr->queued) { + DBG(4, "VIDIOSYNC: Frame #%d not requested!", + f_num) + return -EFAULT; + } + case F_ERROR: + case F_GRABBING: + err = wait_event_interruptible(cam->wait_queue, + (fr->status == F_READY) + || cam->disconnected); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + break; + case F_READY: + break; + } + + if (w9968cf_vppmod_present) + w9968cf_postprocess_frame(cam, fr); + + fr->status = F_UNUSED; + + DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num) + return 0; + } + + case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/ + { + struct video_unit unit = { + .video = cam->v4ldev.minor, + .vbi = VIDEO_NO_UNIT, + .radio = VIDEO_NO_UNIT, + .audio = VIDEO_NO_UNIT, + .teletext = VIDEO_NO_UNIT, + }; + + if (copy_to_user(arg, &unit, sizeof(unit))) + return -EFAULT; + + DBG(5, "VIDIOCGUNIT successfully called.") + return 0; + } + + case VIDIOCKEY: + return 0; + + case VIDIOCGFBUF: + { + struct video_buffer* buffer = (struct video_buffer*)arg; + + memset(buffer, 0, sizeof(struct video_buffer)); + + DBG(5, "VIDIOCGFBUF successfully called.") + return 0; + } + + case VIDIOCGTUNER: + { + struct video_tuner tuner; + if (copy_from_user(&tuner, arg, sizeof(tuner))) + return -EFAULT; + + if (tuner.tuner != 0); + return -EINVAL; + + strcpy(tuner.name, "no_tuner"); + tuner.rangelow = 0; + tuner.rangehigh = 0; + tuner.flags = VIDEO_TUNER_NORM; + tuner.mode = VIDEO_MODE_AUTO; + tuner.signal = 0xffff; + + if (copy_to_user(arg, &tuner, sizeof(tuner))) + return -EFAULT; + + DBG(5, "VIDIOCGTUNER successfully called.") + return 0; + } + + case VIDIOCSTUNER: + { + struct video_tuner tuner; + if (copy_from_user(&tuner, arg, sizeof(tuner))) + return -EFAULT; + + if (tuner.tuner != 0) + return -EINVAL; + + if (tuner.mode != VIDEO_MODE_AUTO) + return -EINVAL; + + DBG(5, "VIDIOCSTUNER successfully called.") + return 0; + } + + case VIDIOCSFBUF: + case VIDIOCCAPTURE: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + case VIDIOCSPLAYMODE: + case VIDIOCSWRITEMODE: + case VIDIOCGPLAYINFO: + case VIDIOCSMICROCODE: + case VIDIOCGVBIFMT: + case VIDIOCSVBIFMT: + DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s " + "(type 0x%01X, " + "n. 0x%01X, " + "dir. 0x%01X, " + "size 0x%02X).", + V4L1_IOCTL(cmd), + _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) + + return -EINVAL; + + default: + DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s " + "type 0x%01X, " + "n. 0x%01X, " + "dir. 0x%01X, " + "size 0x%02X.", + V4L1_IOCTL(cmd), + _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) + + return -ENOIOCTLCMD; + + } /* end of switch */ + +ioctl_fail: + cam->misconfigured = 1; + DBG(1, "VIDIOC%s failed because of hardware problems. " + "To use the camera, close and open it again.", V4L1_IOCTL(cmd)) + return -EFAULT; +} + + +static struct file_operations w9968cf_fops = { + .owner = THIS_MODULE, + .open = w9968cf_open, + .release = w9968cf_release, + .read = w9968cf_read, + .ioctl = w9968cf_ioctl, + .mmap = w9968cf_mmap, + .llseek = no_llseek, +}; + + + +/**************************************************************************** + * USB probe and V4L registration, disconnect and id_table[] definition * + ****************************************************************************/ + +static int +w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct w9968cf_device* cam; + int err = 0; + enum w9968cf_model_id mod_id; + struct list_head* ptr; + u8 sc = 0; /* number of simultaneous cameras */ + static unsigned short dev_nr = 0; /* we are handling device number n */ + + if (udev->descriptor.idVendor == winbond_id_table[0].idVendor && + udev->descriptor.idProduct == winbond_id_table[0].idProduct) + mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */ + + else if (udev->descriptor.idVendor == winbond_id_table[1].idVendor && + udev->descriptor.idProduct == winbond_id_table[1].idProduct) + mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */ + + else + return -ENODEV; + + /* We don't handle multi-config cameras */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + DBG(2, "%s detected.", symbolic(camlist, mod_id)) + + if (simcams > W9968CF_MAX_DEVICES) + simcams = W9968CF_SIMCAMS; + + /* How many cameras are connected ? */ + down(&w9968cf_devlist_sem); + list_for_each(ptr, &w9968cf_dev_list) + sc++; + up(&w9968cf_devlist_sem); + + if (sc >= simcams) { + DBG(2, "Device rejected: too many connected cameras " + "(max. %d)", simcams) + return -EPERM; + } + + err = usb_set_configuration(udev, 1); + err += usb_set_interface(udev, 0, 0); + + if (err) { + DBG(1, "Device configuration failed.") + return -EIO; + } + + cam = (struct w9968cf_device*) + kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL); + + if (!cam) { + DBG(1, "Couldn't allocate %d bytes of kernel memory.", + sizeof(struct w9968cf_device)) + err = -ENOMEM; + goto fail; + } + memset(cam, 0, sizeof(*cam)); + + init_MUTEX(&cam->dev_sem); + down(&cam->dev_sem); + + /* Allocate 2 bytes of memory for camera control USB transfers */ + if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) { + DBG(1,"Couldn't allocate memory for camera control transfers.") + err = -ENOMEM; + goto fail; + } + memset(cam->control_buffer, 0, 2); + + /* Allocate 8 bytes of memory for USB data transfers to the FSB */ + if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) { + DBG(1, "Couldn't allocate memory for data " + "transfers to the FSB.") + err = -ENOMEM; + goto fail; + } + memset(cam->data_buffer, 0, 8); + + /* Set some basic constants */ + w9968cf_configure_camera(cam, udev, mod_id, dev_nr); + + err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L device registration failed.") + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(2, "Couldn't find a free /dev/videoX node.") + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; + goto fail; + } + + DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev.minor) + + /* Ok, add a new entry into the list of V4L registered devices */ + down(&w9968cf_devlist_sem); + list_add(&cam->v4llist, &w9968cf_dev_list); + up(&w9968cf_devlist_sem); + + dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; + + w9968cf_turn_on_led(cam); + + w9968cf_i2c_init(cam); + + up(&cam->dev_sem); + + dev_set_drvdata(&intf->dev, (void*)cam); + + return 0; + +fail: /* Free unused memory */ + if (cam) { + if (cam->control_buffer) + kfree(cam->control_buffer); + if (cam->data_buffer) + kfree(cam->data_buffer); + up(&cam->dev_sem); + kfree(cam); + } + return err; +} + + +static void w9968cf_usb_disconnect(struct usb_interface* intf) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)dev_get_drvdata(&intf->dev); + + dev_set_drvdata(&intf->dev, NULL); + + if (cam) { + /* Prevent concurrent accesses to data */ + down(&cam->dev_sem); + + cam->streaming = 0; + cam->disconnected = 1; + + DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)) + + if (waitqueue_active(&cam->open)) + wake_up_interruptible(&cam->open); + + if (cam->users) { + DBG(2, "The device is open (/dev/video%d)! " + "Process name: %s. Deregistration and memory " + "deallocation are deferred on close.", + cam->v4ldev.minor, cam->command) + + cam->misconfigured = 1; + + if (waitqueue_active(&cam->wait_queue)) + wake_up_interruptible(&cam->wait_queue); + } else + w9968cf_release_resources(cam); + + up(&cam->dev_sem); + + if (!cam->users) + kfree(cam); + } +} + + +static struct usb_driver w9968cf_usb_driver = { + .owner = THIS_MODULE, + .name = "w9968cf", + .id_table = winbond_id_table, + .probe = w9968cf_usb_probe, + .disconnect = w9968cf_usb_disconnect, +}; + + + +/**************************************************************************** + * Module init, exit and intermodule communication * + ****************************************************************************/ + +static int w9968cf_vppmod_detect(void) +{ + w9968cf_vpp_init_decoder = inter_module_get("w9968cf_init_decoder"); + + if (!w9968cf_vpp_init_decoder) { + if (vppmod_load) + w9968cf_vpp_init_decoder = inter_module_get_request + ( "w9968cf_init_decoder", + "w9968cf-vpp" ); + if (!w9968cf_vpp_init_decoder) { + w9968cf_vppmod_present = 0; + DBG(4, "Video post-processing module not detected.") + return -ENODEV; + } + } + + w9968cf_vpp_check_headers = inter_module_get("w9968cf_check_headers"); + w9968cf_vpp_decode = inter_module_get("w9968cf_decode"); + w9968cf_vpp_swap_yuvbytes = inter_module_get("w9968cf_swap_yuvbytes"); + w9968cf_vpp_uyvy_to_rgbx = inter_module_get("w9968cf_uyvy_to_rgbx"); + w9968cf_vpp_scale_up = inter_module_get("w9968cf_scale_up"); + + w9968cf_vppmod_present = 1; + + /* Initialization */ + (*w9968cf_vpp_init_decoder)(); + + DBG(2, "Video post-processing module detected.") + return 0; +} + + +static void w9968cf_vppmod_release(void) +{ + inter_module_put("w9968cf_init_decoder"); + inter_module_put("w9968cf_check_headers"); + inter_module_put("w9968cf_decode"); + inter_module_put("w9968cf_swap_yuvbytes"); + inter_module_put("w9968cf_uyvy_to_rgbx"); + inter_module_put("w9968cf_scale_up"); + + DBG(2, "Video post-processing module released.") +} + + +static int __init w9968cf_module_init(void) +{ + int err; + + DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) + DBG(3, W9968CF_MODULE_AUTHOR) + + init_MUTEX(&w9968cf_devlist_sem); + + w9968cf_vppmod_detect(); + + if ((err = usb_register(&w9968cf_usb_driver))) { + if (w9968cf_vppmod_present) + w9968cf_vppmod_release(); + return err; + } + + return 0; +} + + +static void __exit w9968cf_module_exit(void) +{ + /* w9968cf_usb_disconnect() will be called */ + usb_deregister(&w9968cf_usb_driver); + + if (w9968cf_vppmod_present) + w9968cf_vppmod_release(); + + DBG(2, W9968CF_MODULE_NAME" deregistered.") +} + + +module_init(w9968cf_module_init); +module_exit(w9968cf_module_exit); diff -Nru a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/media/w9968cf.h Fri Dec 12 15:06:58 2003 @@ -0,0 +1,312 @@ +/*************************************************************************** + * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * + * * + * Copyright (C) 2002 2003 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#ifndef _W9968CF_H_ +#define _W9968CF_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "w9968cf_externaldef.h" + + +/**************************************************************************** + * Default values * + ****************************************************************************/ + +#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ + +/* Comment/uncomment the following line to enable/disable debugging messages */ +#define W9968CF_DEBUG + +/* These have effect only if W9968CF_DEBUG is defined */ +#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */ +#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */ + +#define W9968CF_MAX_DEVICES 32 +#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */ + +#define W9968CF_MAX_BUFFERS 32 +#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */ + +/* Maximum data payload sizes in bytes for alternate settings */ +static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575, + 511, 447, 383, 319, 255, 191, 127, 63}; +#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */ +#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */ +#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */ +#define W9968CF_USB_CTRL_TIMEOUT HZ /* timeout for usb control commands */ +#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */ + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ +#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */ + +/* Available video formats */ +struct w9968cf_format { + const u16 palette; + const u16 depth; + const u8 compression; +}; + +static const struct w9968cf_format w9968cf_formatlist[] = { + { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */ + { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */ + { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */ + { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */ + { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */ + { 0, 0, 0 } /* 0 is a terminating entry */ +}; + +#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */ +#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */ +#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */ +#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */ + +#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */ + +#define W9968CF_MAX_WIDTH 800 /* should be >= 640 */ +#define W9968CF_MAX_HEIGHT 600 /* should be >= 480 */ +#define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */ +#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */ + +#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */ +#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */ +#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */ +#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */ +#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */ + +#define W9968CF_SENSOR_MONO 0 /* 0 not monochrome, 1 monochrome sensor */ +#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */ +#define W9968CF_HUE 32768 /* from 0 to 65535 */ +#define W9968CF_COLOUR 32768 /* from 0 to 65535 */ +#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */ +#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */ + +#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */ +#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */ +#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */ +#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */ +#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */ +#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/ + +#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */ +#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */ + + +/**************************************************************************** + * Globals * + ****************************************************************************/ + +#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ + "Dual Mode Camera Chip" +#define W9968CF_MODULE_VERSION "v1.22" +#define W9968CF_MODULE_AUTHOR "(C) 2002 2003 Luca Risolia" +#define W9968CF_AUTHOR_EMAIL "" + +static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */ + +static const struct usb_device_id winbond_id_table[] = { + { + /* Creative Labs Video Blaster WebCam Go Plus */ + USB_DEVICE(0x041e, 0x4003), + .driver_info = (unsigned long)"w9968cf", + }, + { + /* Generic W996[87]CF JPEG USB Dual Mode Camera */ + USB_DEVICE(0x1046, 0x9967), + .driver_info = (unsigned long)"w9968cf", + }, + { } /* terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, winbond_id_table); + +/* W996[87]CF camera models, internal ids: */ +enum w9968cf_model_id { + W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */ + W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/ + W9968CF_MOD_ADPA5R = 21, /* Aroma Digi Pen ADG-5000 Refurbished */ + W9986CF_MOD_AU = 31, /* AVerTV USB */ + W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */ + W9968CF_MOD_DLLDK = 37, /* Die Lebon LDC-D35A Digital Kamera */ + W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */ + W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */ +}; + +enum w9968cf_frame_status { + F_READY, /* finished grabbing & ready to be read/synced */ + F_GRABBING, /* in the process of being grabbed into */ + F_ERROR, /* something bad happened while processing */ + F_UNUSED /* unused (no VIDIOCMCAPTURE) */ +}; + +struct w9968cf_frame_t { + void* buffer; + u32 length; + enum w9968cf_frame_status status; + struct w9968cf_frame_t* next; + u8 queued; +}; + +enum w9968cf_vpp_flag { + VPP_NONE = 0x00, + VPP_UPSCALE = 0x01, + VPP_SWAP_YUV_BYTES = 0x02, + VPP_DECOMPRESSION = 0x04, + VPP_UYVY_TO_RGBX = 0x08, +}; + +struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */ +LIST_HEAD(w9968cf_dev_list); +struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */ + +/* Main device driver structure */ +struct w9968cf_device { + enum w9968cf_model_id id; /* private device identifier */ + + struct video_device v4ldev; /* V4L structure */ + struct list_head v4llist; /* entry of the list of V4L cameras */ + + struct usb_device* usbdev; /* -> main USB structure */ + struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */ + void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */ + u16* control_buffer; /* -> buffer for control req.*/ + u16* data_buffer; /* -> data to send to the FSB */ + + struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS]; + struct w9968cf_frame_t frame_tmp; /* temporary frame */ + struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */ + struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; + void* vpp_buffer; /* -> helper buffer for post-processing routines */ + + u8 max_buffers, /* number of requested buffers */ + force_palette, /* yes=1/no=0 */ + force_rgb, /* read RGB instead of BGR, yes=1, no=0 */ + double_buffer, /* hardware double buffering yes=1/no=0 */ + clamping, /* video data clamping yes=1/no=0 */ + filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */ + capture, /* 0=disabled, 1=enabled */ + largeview, /* 0=disabled, 1=enabled */ + decompression, /* 0=disabled, 1=forced, 2=allowed */ + upscaling; /* software image scaling, 0=enabled, 1=disabled */ + + struct video_picture picture; /* current window settings */ + struct video_window window; /* current picture settings */ + + u16 hw_depth, /* depth (used by the chip) */ + hw_palette, /* palette (used by the chip) */ + hw_width, /* width (used by the chip) */ + hw_height, /* height (used by the chip) */ + hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ + vs_polarity; /* 0=negative sync pulse, 1=positive sync pulse */ + + enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */ + + u8 nbuffers, /* number of allocated frame buffers */ + altsetting, /* camera alternate setting */ + disconnected, /* flag: yes=1, no=0 */ + misconfigured, /* flag: yes=1, no=0 */ + users, /* flag: number of users holding the device */ + streaming; /* flag: yes=1, no=0 */ + + int sensor; /* type of image CMOS sensor chip (CC_*) */ + + /* Determined by CMOS sensor type */ + u16 maxwidth, + maxheight, + minwidth, + minheight, + start_cropx, + start_cropy; + + u8 auto_brt, /* auto brightness enabled flag */ + auto_exp, /* auto exposure enabled flag */ + backlight, /* backlight exposure algorithm flag */ + mirror, /* image is reversed horizontally */ + lightfreq, /* power (lighting) frequency */ + bandfilt; /* banding filter enabled flag */ + s8 clockdiv; /* clock divisor */ + int sensor_mono; /* CMOS sensor is (probably) monochrome */ + + /* I2C interface to kernel */ + struct i2c_adapter i2c_adapter; + struct i2c_client* sensor_client; + + /* Locks */ + struct semaphore dev_sem, /* for probe, disconnect,open and close */ + fileop_sem; /* for read and ioctl */ + spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */ + flist_lock; /* for requested frame list accesses */ + char command[16]; /* name of the program holding the device */ + wait_queue_head_t open, wait_queue; +}; + +#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */ + +#define SENSOR_FORMAT VIDEO_PALETTE_UYVY +#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) + + +/**************************************************************************** + * Macros and other constants * + ****************************************************************************/ + +#undef DBG +#ifdef W9968CF_DEBUG +# define DBG(level, fmt, args...) \ +{ \ +if ( ((specific_debug) && (debug == (level))) || \ + ((!specific_debug) && (debug >= (level))) ) { \ + if ((level) == 1) \ + err(fmt, ## args); \ + else if ((level) == 2 || (level) == 3) \ + info(fmt, ## args); \ + else if ((level) == 4) \ + warn(fmt, ## args); \ + else if ((level) >= 5) \ + info("[%s,%d] " fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args); \ +} \ +} +#else + /* Not debugging: nothing */ +# define DBG(level, fmt, args...) do {;} while(0); +#endif + +#undef PDBG +#undef PDBGG +#define PDBG(fmt, args...) info("[%s, %d] "fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args); +#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ + +#endif /* _W9968CF_H_ */ diff -Nru a/drivers/usb/media/w9968cf_decoder.h b/drivers/usb/media/w9968cf_decoder.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/media/w9968cf_decoder.h Fri Dec 12 15:06:58 2003 @@ -0,0 +1,86 @@ +/*************************************************************************** + * Video decoder for the W996[87]CF driver for Linux. * + * * + * Copyright (C) 2003 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#ifndef _W9968CF_DECODER_H_ +#define _W9968CF_DECODER_H_ + +/* Comment/uncomment this for high/low quality of compressed video */ +#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO + +#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO +static const unsigned char Y_QUANTABLE[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const unsigned char UV_QUANTABLE[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; +#else +static const unsigned char Y_QUANTABLE[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const unsigned char UV_QUANTABLE[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; +#endif + +#define W9968CF_DEC_ERR_CORRUPTED_DATA -1 +#define W9968CF_DEC_ERR_BUF_OVERFLOW -2 +#define W9968CF_DEC_ERR_NO_SOI -3 +#define W9968CF_DEC_ERR_NO_SOF0 -4 +#define W9968CF_DEC_ERR_NO_SOS -5 +#define W9968CF_DEC_ERR_NO_EOI -6 + +extern void w9968cf_init_decoder(void); +extern int w9968cf_check_headers(const unsigned char* Pin, + const unsigned long BUF_SIZE); +extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE, + const unsigned W, const unsigned H, char* Pout); + +#endif /* _W9968CF_DECODER_H_ */ diff -Nru a/drivers/usb/media/w9968cf_externaldef.h b/drivers/usb/media/w9968cf_externaldef.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/media/w9968cf_externaldef.h Fri Dec 12 15:06:58 2003 @@ -0,0 +1,95 @@ +/*************************************************************************** + * Various definitions for compatibility with external modules. * + * This file is part of the W996[87]CF driver for Linux. * + * * + * Copyright (C) 2002 2003 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#ifndef _W9968CF_EXTERNALDEF_H_ +#define _W9968CF_EXTERNALDEF_H_ + +#include +#include +#include +#include + +/* The following values have been copied from the "ovcamchip" module. */ + +#ifndef I2C_DRIVERID_OVCAMCHIP +# define I2C_DRIVERID_OVCAMCHIP 0xf00f +#endif + +/* Controls */ +enum { + OVCAMCHIP_CID_CONT, /* Contrast */ + OVCAMCHIP_CID_BRIGHT, /* Brightness */ + OVCAMCHIP_CID_SAT, /* Saturation */ + OVCAMCHIP_CID_HUE, /* Hue */ + OVCAMCHIP_CID_EXP, /* Exposure */ + OVCAMCHIP_CID_FREQ, /* Light frequency */ + OVCAMCHIP_CID_BANDFILT, /* Banding filter */ + OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */ + OVCAMCHIP_CID_AUTOEXP, /* Auto exposure */ + OVCAMCHIP_CID_BACKLIGHT, /* Back light compensation */ + OVCAMCHIP_CID_MIRROR, /* Mirror horizontally */ +}; + +/* I2C addresses */ +#define OV7xx0_SID (0x42 >> 1) +#define OV6xx0_SID (0xC0 >> 1) + +/* Sensor types */ +enum { + CC_UNKNOWN, + CC_OV76BE, + CC_OV7610, + CC_OV7620, + CC_OV7620AE, + CC_OV6620, + CC_OV6630, + CC_OV6630AE, + CC_OV6630AF, +}; + +/* API */ +struct ovcamchip_control { + __u32 id; + __s32 value; +}; + +struct ovcamchip_window { + int x; + int y; + int width; + int height; + int format; + int quarter; /* Scale width and height down 2x */ + + /* This stuff will be removed eventually */ + int clockdiv; /* Clock divisor setting */ +}; + +/* Commands. + You must call OVCAMCHIP_CMD_INITIALIZE before any of other commands */ +#define OVCAMCHIP_CMD_Q_SUBTYPE _IOR (0x88, 0x00, int) +#define OVCAMCHIP_CMD_INITIALIZE _IOW (0x88, 0x01, int) +#define OVCAMCHIP_CMD_S_CTRL _IOW (0x88, 0x02, struct ovcamchip_control) +#define OVCAMCHIP_CMD_G_CTRL _IOWR (0x88, 0x03, struct ovcamchip_control) +#define OVCAMCHIP_CMD_S_MODE _IOW (0x88, 0x04, struct ovcamchip_window) +#define OVCAMCHIP_MAX_CMD _IO (0x88, 0x3f) + +#endif /* _W9968CF_EXTERNALDEF_H_ */ diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig --- a/drivers/usb/misc/Kconfig Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/misc/Kconfig Fri Dec 12 15:06:58 2003 @@ -58,6 +58,18 @@ To compile this driver as a module, choose M here: the module will be called rio500. +config USB_LEGOTOWER + tristate "USB Lego Infrared Tower support (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + Say Y here if you want to connect a USB Lego Infrared Tower to your + computer's USB port. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called legousbtower. If you want to compile it as + a module, say M here and read . + config USB_BRLVGER tristate "Tieman Voyager USB Braille display support (EXPERIMENTAL)" depends on USB && EXPERIMENTAL diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/misc/Makefile Fri Dec 12 15:06:58 2003 @@ -12,3 +12,4 @@ obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o diff -Nru a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/legousbtower.c Fri Dec 12 15:06:58 2003 @@ -0,0 +1,878 @@ +/* + * LEGO USB Tower driver + * + * Copyright (C) 2003 David Glance + * 2001 Juergen Stuber + * + * 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. + * + * derived from USB Skeleton driver - 0.5 + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * + * History: + * + * 2001-10-13 - 0.1 js + * - first version + * 2001-11-03 - 0.2 js + * - simplified buffering, one-shot URBs for writing + * 2001-11-10 - 0.3 js + * - removed IOCTL (setting power/mode is more complicated, postponed) + * 2001-11-28 - 0.4 js + * - added vendor commands for mode of operation and power level in open + * 2001-12-04 - 0.5 js + * - set IR mode by default (by oversight 0.4 set VLL mode) + * 2002-01-11 - 0.5? pcchan + * - make read buffer reusable and work around bytes_to_write issue between + * uhci and legusbtower + * 2002-09-23 - 0.52 david (david@csse.uwa.edu.au) + * - imported into lejos project + * - changed wake_up to wake_up_interruptible + * - changed to use lego0 rather than tower0 + * - changed dbg() to use __func__ rather than deprecated __FUNCTION__ + * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au) + * - changed read and write to write everything or timeout (from a patch by Chris Riesen and + * Brett Thaeler driver) + * - added ioctl functionality to set timeouts + * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au) + * - initial import into LegoUSB project + * - merge of existing LegoUSB.c driver + * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au) + * - port to 2.6 style driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_USB_DEBUG + static int debug = 4; +#else + static int debug = 1; +#endif + +/* Use our own dbg macro */ +#undef dbg +#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); } while (0) + + +/* Version Information */ +#define DRIVER_VERSION "v0.56" +#define DRIVER_AUTHOR "David Glance, davidgsf@sourceforge.net" +#define DRIVER_DESC "LEGO USB Tower Driver" + +/* Module paramaters */ +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + + +/* Define these values to match your device */ +#define LEGO_USB_TOWER_VENDOR_ID 0x0694 +#define LEGO_USB_TOWER_PRODUCT_ID 0x0001 + +/* table of devices that work with this driver */ +static struct usb_device_id tower_table [] = { + { USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, tower_table); + +#define LEGO_USB_TOWER_MINOR_BASE 160 + +/* we can have up to this number of device plugged in at once */ +#define MAX_DEVICES 16 + +#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ + +/* Structure to hold all of our device specific stuff */ +struct lego_usb_tower { + struct semaphore sem; /* locks this structure */ + struct usb_device* udev; /* save off the usb device pointer */ + struct usb_interface* interface; + unsigned char minor; /* the starting minor number for this device */ + + int open_count; /* number of times this port has been opened */ + + char* read_buffer; + int read_buffer_length; + + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + char* interrupt_in_buffer; + struct usb_endpoint_descriptor* interrupt_in_endpoint; + struct urb* interrupt_in_urb; + + char* interrupt_out_buffer; + struct usb_endpoint_descriptor* interrupt_out_endpoint; + struct urb* interrupt_out_urb; + +}; + +/* Note that no locking is needed: + * read_buffer is arbitrated by read_buffer_length == 0 + * interrupt_out_buffer is arbitrated by interrupt_out_urb->status == -EINPROGRESS + * interrupt_in_buffer belongs to urb alone and is overwritten on overflow + */ + +/* local function prototypes */ +static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos); +static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos); +static inline void tower_delete (struct lego_usb_tower *dev); +static int tower_open (struct inode *inode, struct file *file); +static int tower_release (struct inode *inode, struct file *file); +static int tower_release_internal (struct lego_usb_tower *dev); +static void tower_abort_transfers (struct lego_usb_tower *dev); +static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs); +static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs); + +static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id); +static void tower_disconnect (struct usb_interface *interface); + + +/* prevent races between open() and disconnect */ +static DECLARE_MUTEX (disconnect_sem); + +/* file operations needed when we register this driver */ +static struct file_operations tower_fops = { + .owner = THIS_MODULE, + .read = tower_read, + .write = tower_write, + .open = tower_open, + .release = tower_release, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver tower_class = { + .name = "usb/legousbtower%d", + .fops = &tower_fops, + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, + .minor_base = LEGO_USB_TOWER_MINOR_BASE, +}; + + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver tower_driver = { + .owner = THIS_MODULE, + .name = "legousbtower", + .probe = tower_probe, + .disconnect = tower_disconnect, + .id_table = tower_table, +}; + + +/** + * lego_usb_tower_debug_data + */ +static inline void lego_usb_tower_debug_data (int level, const char *function, int size, const unsigned char *data) +{ + int i; + + if (debug < level) + return; + + printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); + for (i = 0; i < size; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); +} + + +/** + * tower_delete + */ +static inline void tower_delete (struct lego_usb_tower *dev) +{ + dbg(2, "%s enter", __func__); + + tower_abort_transfers (dev); + + /* free data structures */ + if (dev->interrupt_in_urb != NULL) { + usb_free_urb (dev->interrupt_in_urb); + } + if (dev->interrupt_out_urb != NULL) { + usb_free_urb (dev->interrupt_out_urb); + } + kfree (dev->read_buffer); + kfree (dev->interrupt_in_buffer); + kfree (dev->interrupt_out_buffer); + kfree (dev); + + dbg(2, "%s : leave", __func__); +} + + +/** + * tower_open + */ +static int tower_open (struct inode *inode, struct file *file) +{ + struct lego_usb_tower *dev = NULL; + int subminor; + int retval = 0; + struct usb_interface *interface; + + dbg(2,"%s : enter", __func__); + + subminor = iminor(inode); + + down (&disconnect_sem); + + interface = usb_find_interface (&tower_driver, subminor); + + if (!interface) { + err ("%s - error, can't find device for minor %d", + __FUNCTION__, subminor); + retval = -ENODEV; + goto exit_no_device; + } + + dev = usb_get_intfdata(interface); + + if (!dev) { + retval = -ENODEV; + goto exit_no_device; + } + + /* lock this device */ + down (&dev->sem); + + + /* increment our usage count for the device */ + ++dev->open_count; + + /* save device in the file's private structure */ + file->private_data = dev; + + + /* initialize in direction */ + dev->read_buffer_length = 0; + + up (&dev->sem); + +exit_no_device: + + up (&disconnect_sem); + + dbg(2,"%s : leave, return value %d ", __func__, retval); + + return retval; +} + +/** + * tower_release + */ +static int tower_release (struct inode *inode, struct file *file) +{ + struct lego_usb_tower *dev; + int retval = 0; + + dbg(2," %s : enter", __func__); + + dev = (struct lego_usb_tower *)file->private_data; + + if (dev == NULL) { + dbg(1," %s : object is NULL", __func__); + retval = -ENODEV; + goto exit; + } + + + /* lock our device */ + down (&dev->sem); + + if (dev->open_count <= 0) { + dbg(1," %s : device not opened", __func__); + retval = -ENODEV; + goto exit; + } + + /* do the work */ + retval = tower_release_internal (dev); + +exit: + up (&dev->sem); + dbg(2," %s : leave, return value %d", __func__, retval); + return retval; +} + + +/** + * tower_release_internal + */ +static int tower_release_internal (struct lego_usb_tower *dev) +{ + int retval = 0; + + dbg(2," %s : enter", __func__); + + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + tower_delete (dev); + goto exit; + } + + /* decrement our usage count for the device */ + --dev->open_count; + if (dev->open_count <= 0) { + tower_abort_transfers (dev); + dev->open_count = 0; + } + +exit: + dbg(2," %s : leave", __func__); + return retval; +} + + +/** + * tower_abort_transfers + * aborts transfers and frees associated data structures + */ +static void tower_abort_transfers (struct lego_usb_tower *dev) +{ + dbg(2," %s : enter", __func__); + + if (dev == NULL) { + dbg(1," %s : dev is null", __func__); + goto exit; + } + + /* shutdown transfer */ + if (dev->interrupt_in_urb != NULL) { + usb_unlink_urb (dev->interrupt_in_urb); + } + if (dev->interrupt_out_urb != NULL) { + usb_unlink_urb (dev->interrupt_out_urb); + } + +exit: + dbg(2," %s : leave", __func__); +} + + +/** + * tower_read + */ +static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct lego_usb_tower *dev; + size_t bytes_read = 0; + size_t bytes_to_read; + int i; + int retval = 0; + int timeout = 0; + + dbg(2," %s : enter, count = %Zd", __func__, count); + + dev = (struct lego_usb_tower *)file->private_data; + + /* lock this object */ + down (&dev->sem); + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL) { + retval = -ENODEV; + err("No device or device unplugged %d", retval); + goto exit; + } + + /* verify that we actually have some data to read */ + if (count == 0) { + dbg(1," %s : read request of 0 bytes", __func__); + goto exit; + } + + + timeout = COMMAND_TIMEOUT; + + while (1) { + if (dev->read_buffer_length == 0) { + + /* start reading */ + usb_fill_int_urb (dev->interrupt_in_urb,dev->udev, + usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + dev->interrupt_in_endpoint->wMaxPacketSize, + tower_interrupt_in_callback, + dev, + dev->interrupt_in_endpoint->bInterval); + + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); + + if (retval < 0) { + err("Couldn't submit interrupt_in_urb"); + goto exit; + } + + if (timeout <= 0) { + retval = -ETIMEDOUT; + goto exit; + } + + if (signal_pending(current)) { + retval = -EINTR; + goto exit; + } + + up (&dev->sem); + timeout = interruptible_sleep_on_timeout (&dev->read_wait, timeout); + down (&dev->sem); + + } else { + /* copy the data from read_buffer into userspace */ + bytes_to_read = count > dev->read_buffer_length ? dev->read_buffer_length : count; + if (copy_to_user (buffer, dev->read_buffer, bytes_to_read) != 0) { + retval = -EFAULT; + goto exit; + } + dev->read_buffer_length -= bytes_to_read; + for (i=0; iread_buffer_length; i++) { + dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; + } + + buffer += bytes_to_read; + count -= bytes_to_read; + bytes_read += bytes_to_read; + if (count == 0) { + break; + } + } + } + + retval = bytes_read; + +exit: + /* unlock the device */ + up (&dev->sem); + + dbg(2," %s : leave, return value %d", __func__, retval); + return retval; +} + + +/** + * tower_write + */ +static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct lego_usb_tower *dev; + size_t bytes_written = 0; + size_t bytes_to_write; + size_t buffer_size; + int retval = 0; + int timeout = 0; + + dbg(2," %s : enter, count = %Zd", __func__, count); + + dev = (struct lego_usb_tower *)file->private_data; + + /* lock this object */ + down (&dev->sem); + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL) { + retval = -ENODEV; + err("No device or device unplugged %d", retval); + goto exit; + } + + /* verify that we actually have some data to write */ + if (count == 0) { + dbg(1," %s : write request of 0 bytes", __func__); + goto exit; + } + + + while (count > 0) { + if (dev->interrupt_out_urb->status == -EINPROGRESS) { + timeout = COMMAND_TIMEOUT; + + while (timeout > 0) { + if (signal_pending(current)) { + dbg(1," %s : interrupted", __func__); + retval = -EINTR; + goto exit; + } + up (&dev->sem); + timeout = interruptible_sleep_on_timeout (&dev->write_wait, timeout); + down (&dev->sem); + if (timeout > 0) { + break; + } + dbg(1," %s : interrupted timeout: %d", __func__, timeout); + } + + + dbg(1," %s : final timeout: %d", __func__, timeout); + + if (timeout == 0) { + dbg(1, "%s - command timed out.", __func__); + retval = -ETIMEDOUT; + goto exit; + } + + dbg(4," %s : in progress, count = %Zd", __func__, count); + } else { + dbg(4," %s : sending, count = %Zd", __func__, count); + + /* write the data into interrupt_out_buffer from userspace */ + buffer_size = dev->interrupt_out_endpoint->wMaxPacketSize; + bytes_to_write = count > buffer_size ? buffer_size : count; + dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write); + + if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { + retval = -EFAULT; + goto exit; + } + + /* send off the urb */ + usb_fill_int_urb(dev->interrupt_out_urb, + dev->udev, + usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), + dev->interrupt_out_buffer, + bytes_to_write, + tower_interrupt_out_callback, + dev, + dev->interrupt_in_endpoint->bInterval); + + dev->interrupt_out_urb->actual_length = bytes_to_write; + retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); + + if (retval < 0) { + err("Couldn't submit interrupt_out_urb %d", retval); + goto exit; + } + + buffer += bytes_to_write; + count -= bytes_to_write; + + bytes_written += bytes_to_write; + } + } + + retval = bytes_written; + +exit: + /* unlock the device */ + up (&dev->sem); + + dbg(2," %s : leave, return value %d", __func__, retval); + + return retval; +} + + +/** + * tower_interrupt_in_callback + */ +static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs) +{ + struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; + + dbg(4," %s : enter, status %d", __func__, urb->status); + + lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + + if (urb->status != 0) { + if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { + dbg(1," %s : nonzero status received: %d", __func__, urb->status); + } + goto exit; + } + + down (&dev->sem); + + if (urb->actual_length > 0) { + if (dev->read_buffer_length < (4 * dev->interrupt_in_endpoint->wMaxPacketSize) - (urb->actual_length)) { + + memcpy (dev->read_buffer+dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length); + + dev->read_buffer_length += urb->actual_length; + dbg(1," %s reading %d ", __func__, urb->actual_length); + wake_up_interruptible (&dev->read_wait); + + } else { + dbg(1," %s : read_buffer overflow", __func__); + } + } + + up (&dev->sem); + +exit: + lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + dbg(4," %s : leave, status %d", __func__, urb->status); +} + + +/** + * tower_interrupt_out_callback + */ +static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs) +{ + struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; + + dbg(4," %s : enter, status %d", __func__, urb->status); + lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + + if (urb->status != 0) { + if ((urb->status != -ENOENT) && + (urb->status != -ECONNRESET)) { + dbg(1, " %s :nonzero status received: %d", __func__, urb->status); + } + goto exit; + } + + wake_up_interruptible(&dev->write_wait); +exit: + + lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + dbg(4," %s : leave, status %d", __func__, urb->status); +} + + +/** + * tower_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct lego_usb_tower *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor* endpoint; + int i; + int retval = -ENOMEM; + + dbg(2," %s : enter", __func__); + + if (udev == NULL) { + info ("udev is NULL."); + } + + /* See if the device offered us matches what we can accept */ + if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) || + (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) { + return -ENODEV; + } + + + /* allocate memory for our device state and intialize it */ + + dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL); + + if (dev == NULL) { + err ("Out of memory"); + goto exit; + } + + init_MUTEX (&dev->sem); + + dev->udev = udev; + dev->open_count = 0; + + dev->read_buffer = NULL; + dev->read_buffer_length = 0; + + init_waitqueue_head (&dev->read_wait); + init_waitqueue_head (&dev->write_wait); + + dev->interrupt_in_buffer = NULL; + dev->interrupt_in_endpoint = NULL; + dev->interrupt_in_urb = NULL; + + dev->interrupt_out_buffer = NULL; + dev->interrupt_out_endpoint = NULL; + dev->interrupt_out_urb = NULL; + + + iface_desc = &interface->altsetting[0]; + + /* set up the endpoint information */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { + dev->interrupt_in_endpoint = endpoint; + } + + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { + dev->interrupt_out_endpoint = endpoint; + } + } + if(dev->interrupt_in_endpoint == NULL) { + err("interrupt in endpoint not found"); + goto error; + } + if (dev->interrupt_out_endpoint == NULL) { + err("interrupt out endpoint not found"); + goto error; + } + + dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + if (!dev->read_buffer) { + err("Couldn't allocate read_buffer"); + goto error; + } + dev->interrupt_in_buffer = kmalloc (dev->interrupt_in_endpoint->wMaxPacketSize, GFP_KERNEL); + if (!dev->interrupt_in_buffer) { + err("Couldn't allocate interrupt_in_buffer"); + goto error; + } + dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->interrupt_in_urb) { + err("Couldn't allocate interrupt_in_urb"); + goto error; + } + dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL); + if (!dev->interrupt_out_buffer) { + err("Couldn't allocate interrupt_out_buffer"); + goto error; + } + dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->interrupt_out_urb) { + err("Couldn't allocate interrupt_out_urb"); + goto error; + } + + /* we can register the device now, as it is ready */ + usb_set_intfdata (interface, dev); + + retval = usb_register_dev (interface, &tower_class); + + if (retval) { + /* something prevented us from registering this driver */ + err ("Not able to get a minor for this device."); + usb_set_intfdata (interface, NULL); + goto error; + } + + dev->minor = interface->minor; + + /* let the user know what node this device is now attached to */ + info ("LEGO USB Tower device now attached to /dev/usb/lego%d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE)); + + + +exit: + dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev); + + return retval; + +error: + tower_delete(dev); + return retval; +} + + +/** + * tower_disconnect + * + * Called by the usb core when the device is removed from the system. + */ +static void tower_disconnect (struct usb_interface *interface) +{ + struct lego_usb_tower *dev; + int minor; + + dbg(2," %s : enter", __func__); + + down (&disconnect_sem); + + dev = usb_get_intfdata (interface); + usb_set_intfdata (interface, NULL); + + + down (&dev->sem); + + minor = dev->minor; + + /* give back our minor */ + usb_deregister_dev (interface, &tower_class); + + /* if the device is not opened, then we clean up right now */ + if (!dev->open_count) { + up (&dev->sem); + tower_delete (dev); + } else { + dev->udev = NULL; + up (&dev->sem); + } + + up (&disconnect_sem); + + info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); + + dbg(2," %s : leave", __func__); +} + + + +/** + * lego_usb_tower_init + */ +static int __init lego_usb_tower_init(void) +{ + int result; + int retval = 0; + + dbg(2," %s : enter", __func__); + + /* register this driver with the USB subsystem */ + result = usb_register(&tower_driver); + if (result < 0) { + err("usb_register failed for the "__FILE__" driver. Error number %d", result); + retval = -1; + goto exit; + } + + info(DRIVER_DESC " " DRIVER_VERSION); + +exit: + dbg(2," %s : leave, return value %d", __func__, retval); + + return retval; +} + + +/** + * lego_usb_tower_exit + */ +static void __exit lego_usb_tower_exit(void) +{ + dbg(2," %s : enter", __func__); + + /* deregister this driver with the USB subsystem */ + usb_deregister (&tower_driver); + + dbg(2," %s : leave", __func__); +} + +module_init (lego_usb_tower_init); +module_exit (lego_usb_tower_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif diff -Nru a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig --- a/drivers/usb/net/Kconfig Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/net/Kconfig Fri Dec 12 15:06:58 2003 @@ -247,7 +247,7 @@ config USB_AX8817X boolean "ASIX AX88172 Based USB 2.0 Ethernet Devices" - depends on USB_USBNET && EXPERIMENTAL + depends on USB_USBNET default y help diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h --- a/drivers/usb/net/pegasus.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/net/pegasus.h Fri Dec 12 15:06:58 2003 @@ -137,6 +137,7 @@ #define VENDOR_MELCO 0x0411 #define VENDOR_MOBILITY 0x1342 #define VENDOR_NETGEAR 0x0846 +#define VENDOR_OCT 0x0b39 #define VENDOR_SMARTBRIDGES 0x08d1 #define VENDOR_SMC 0x0707 #define VENDOR_SOHOWARE 0x15e8 @@ -173,7 +174,7 @@ DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet", VENDOR_ADMTEK, 0x8511, - DEFAULT_GPIO_RESET | PEGASUS_II ) + DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA ) PEGASUS_DEV( "ADMtek ADM8513 \"Pegasus II\" USB Ethernet", VENDOR_ADMTEK, 0x8513, DEFAULT_GPIO_RESET | PEGASUS_II ) @@ -259,6 +260,8 @@ PEGASUS_DEV( "MELCO/BUFFALO LUA2-TX", VENDOR_MELCO, 0x0009, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "NETGEAR FA101", VENDOR_NETGEAR, 0x1020, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "OCT Inc.", VENDOR_OCT, 0x0109, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "smartNIC 2 PnP Adapter", VENDOR_SMARTBRIDGES, 0x0003, DEFAULT_GPIO_RESET | PEGASUS_II ) diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c --- a/drivers/usb/net/usbnet.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/net/usbnet.c Fri Dec 12 15:06:58 2003 @@ -637,6 +637,27 @@ info->eedump_len = 0x3e; } +static u32 ax8817x_get_link (struct net_device *net) +{ + struct usbnet *dev = (struct usbnet *)net->priv; + + return (u32)mii_link_ok(&dev->mii); +} + +static int ax8817x_get_settings(struct net_device *net, struct ethtool_cmd *cmd) +{ + struct usbnet *dev = (struct usbnet *)net->priv; + + return mii_ethtool_gset(&dev->mii,cmd); +} + +static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd) +{ + struct usbnet *dev = (struct usbnet *)net->priv; + + return mii_ethtool_sset(&dev->mii,cmd); +} + static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf) { int ret; @@ -670,16 +691,6 @@ } memcpy(dev->net->dev_addr, buf, ETH_ALEN); - /* Get IPG values */ - if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf)) < 0) { - dbg("Error reading IPG values: %d", ret); - return ret; - } - - for(i = 0;i < 3;i++) { - ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0 + i, 0, 0, 1, &buf[i]); - } - /* Get the PHY id */ if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) { dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret); @@ -735,9 +746,12 @@ dev->net->set_multicast_list = ax8817x_set_multicast; usbnet_ethtool_ops.get_drvinfo = &ax8817x_get_drvinfo; + usbnet_ethtool_ops.get_link = &ax8817x_get_link; usbnet_ethtool_ops.get_wol = &ax8817x_get_wol; usbnet_ethtool_ops.set_wol = &ax8817x_set_wol; usbnet_ethtool_ops.get_eeprom = &ax8817x_get_eeprom; + usbnet_ethtool_ops.get_settings = &ax8817x_get_settings; + usbnet_ethtool_ops.set_settings = &ax8817x_set_settings; return 0; } diff -Nru a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c --- a/drivers/usb/serial/cyberjack.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/cyberjack.c Fri Dec 12 15:06:58 2003 @@ -295,7 +295,6 @@ { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); - unsigned long flags; struct usb_serial *serial; unsigned char *data = urb->transfer_buffer; int result; @@ -323,13 +322,13 @@ /* This is a announcement of coming bulk_ins. */ unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3; - spin_lock_irqsave(&priv->lock, flags); + spin_lock(&priv->lock); old_rdtodo = priv->rdtodo; if( (old_rdtodo+size)<(old_rdtodo) ) { dbg( "To many bulk_in urbs to do." ); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock(&priv->lock); goto resubmit; } @@ -338,11 +337,11 @@ dbg("%s - rdtodo: %d", __FUNCTION__, priv->rdtodo); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock(&priv->lock); if( !old_rdtodo ) { port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if( result ) err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); dbg("%s - usb_submit_urb(read urb)", __FUNCTION__); @@ -351,7 +350,7 @@ resubmit: port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result) err(" usb_submit_urb(read int) failed"); dbg("%s - usb_submit_urb(int urb)", __FUNCTION__); @@ -361,7 +360,6 @@ { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); - unsigned long flags; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; @@ -397,7 +395,7 @@ tty_flip_buffer_push(tty); } - spin_lock_irqsave(&priv->lock, flags); + spin_lock(&priv->lock); /* Reduce urbs to do by one. */ priv->rdtodo-=urb->actual_length; @@ -405,14 +403,14 @@ if ( priv->rdtodo<0 ) priv->rdtodo = 0; todo = priv->rdtodo; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock(&priv->lock); dbg("%s - rdtodo: %d", __FUNCTION__, todo); /* Continue to read if we have still urbs to do. */ if( todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/ ) { port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); dbg("%s - usb_submit_urb(read urb)", __FUNCTION__); @@ -423,7 +421,6 @@ { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); - unsigned long flags; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); dbg("%s - port %d", __FUNCTION__, port->number); @@ -438,7 +435,7 @@ return; } - spin_lock_irqsave(&priv->lock, flags); + spin_lock(&priv->lock); /* only do something if we have more data to send */ if( priv->wrfilled ) { @@ -446,7 +443,7 @@ if (port->write_urb->status == -EINPROGRESS) { dbg("%s - already writing", __FUNCTION__); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock(&priv->lock); return; } @@ -492,7 +489,7 @@ } exit: - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock(&priv->lock); schedule_work(&port->work); } diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/ftdi_sio.c Fri Dec 12 15:06:58 2003 @@ -342,6 +342,10 @@ { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0, 0x3ff) }, { USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0, 0x3ff) }, { USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_1, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_R2X0, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0, 0x3ff) }, { } /* Terminating entry */ }; @@ -416,6 +420,10 @@ { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_1, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_R2X0, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0x400, 0xffff) }, { } /* Terminating entry */ }; @@ -505,6 +513,10 @@ { USB_DEVICE(OCT_VID, OCT_US101_PID) }, { USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) }, { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) }, + { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, + { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, + { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, + { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) }, { } /* Terminating entry */ }; diff -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h --- a/drivers/usb/serial/ftdi_sio.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/ftdi_sio.h Fri Dec 12 15:06:58 2003 @@ -145,6 +145,14 @@ /* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */ #define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */ +/* + * Protego product ids + */ +#define PROTEGO_SPECIAL_1 0xFC70 /* special/unknown device */ +#define PROTEGO_R2X0 0xFC71 /* R200-USB TRNG unit (R210, R220, and R230) */ +#define PROTEGO_SPECIAL_3 0xFC72 /* special/unknown device */ +#define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff -Nru a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c --- a/drivers/usb/serial/io_edgeport.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/serial/io_edgeport.c Fri Dec 12 15:06:57 2003 @@ -1488,16 +1488,20 @@ usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint), buffer, count+2, edge_bulk_out_data_callback, edge_port); + /* decrement the number of credits we have by the number we just sent */ + edge_port->txCredits -= count; + edge_port->icount.tx += count; + urb->dev = edge_serial->serial->dev; status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { /* something went wrong */ dbg("%s - usb_submit_urb(write bulk) failed", __FUNCTION__); edge_port->write_in_progress = FALSE; - } else { - /* decrement the number of credits we have by the number we just sent */ - edge_port->txCredits -= count; - edge_port->icount.tx += count; + + /* revert the credits as something bad happened. */ + edge_port->txCredits += count; + edge_port->icount.tx -= count; } dbg("%s wrote %d byte(s) TxCredit %d, Fifo %d", __FUNCTION__, count, edge_port->txCredits, fifo->count); } diff -Nru a/drivers/usb/serial/io_fw_boot.h b/drivers/usb/serial/io_fw_boot.h --- a/drivers/usb/serial/io_fw_boot.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/io_fw_boot.h Fri Dec 12 15:06:58 2003 @@ -17,7 +17,7 @@ unsigned short Addr; unsigned short Len; unsigned char Data[0]; - }; + } __attribute__ ((packed)); struct edge_firmware_version_info { unsigned char MajorVersion; diff -Nru a/drivers/usb/serial/io_fw_boot2.h b/drivers/usb/serial/io_fw_boot2.h --- a/drivers/usb/serial/io_fw_boot2.h Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/serial/io_fw_boot2.h Fri Dec 12 15:06:57 2003 @@ -17,7 +17,7 @@ unsigned short Addr; unsigned short Len; unsigned char Data[0]; - }; + } __attribute__ ((packed)); struct edge_firmware_version_info { unsigned char MajorVersion; diff -Nru a/drivers/usb/serial/io_fw_down.h b/drivers/usb/serial/io_fw_down.h --- a/drivers/usb/serial/io_fw_down.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/io_fw_down.h Fri Dec 12 15:06:58 2003 @@ -17,7 +17,7 @@ unsigned short Addr; unsigned short Len; unsigned char Data[0]; - }; + } __attribute ((packed)); struct edge_firmware_version_info { unsigned char MajorVersion; diff -Nru a/drivers/usb/serial/io_fw_down2.h b/drivers/usb/serial/io_fw_down2.h --- a/drivers/usb/serial/io_fw_down2.h Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/serial/io_fw_down2.h Fri Dec 12 15:06:57 2003 @@ -17,7 +17,7 @@ unsigned short Addr; unsigned short Len; unsigned char Data[0]; - }; + } __attribute__ ((packed)); struct edge_firmware_version_info { unsigned char MajorVersion; diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c --- a/drivers/usb/serial/pl2303.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/serial/pl2303.c Fri Dec 12 15:06:57 2003 @@ -71,6 +71,7 @@ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, + { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, @@ -169,6 +170,7 @@ struct pl2303_private { spinlock_t lock; + wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; u8 termios_initialized; @@ -186,6 +188,7 @@ return -ENOMEM; memset (priv, 0x00, sizeof (struct pl2303_private)); spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->delta_msr_wait); usb_set_serial_port_data(serial->port[i], priv); } return 0; @@ -556,11 +559,51 @@ return result; } +static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int prevstatus; + unsigned int status; + unsigned int changed; + + spin_lock_irqsave (&priv->lock, flags); + prevstatus = priv->line_status; + spin_unlock_irqrestore (&priv->lock, flags); + + while (1) { + interruptible_sleep_on(&priv->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave (&priv->lock, flags); + status = priv->line_status; + spin_unlock_irqrestore (&priv->lock, flags); + + changed=prevstatus^status; + + if (((arg & TIOCM_RNG) && (changed & UART_RING)) || + ((arg & TIOCM_DSR) && (changed & UART_DSR)) || + ((arg & TIOCM_CD) && (changed & UART_DCD)) || + ((arg & TIOCM_CTS) && (changed & UART_CTS)) ) { + return 0; + } + prevstatus = status; + } + /* NOTREACHED */ + return 0; +} + static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) { dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); switch (cmd) { + case TIOCMIWAIT: + dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number); + return wait_modem_info(port, arg); + default: dbg("%s not supported = 0x%04x", __FUNCTION__, cmd); break; @@ -703,6 +746,7 @@ spin_lock_irqsave(&priv->lock, flags); status = priv->line_status; spin_unlock_irqrestore(&priv->lock, flags); + wake_up_interruptible (&priv->delta_msr_wait); /* break takes precedence over parity, */ /* which takes precedence over framing errors */ diff -Nru a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h --- a/drivers/usb/serial/pl2303.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/pl2303.h Fri Dec 12 15:06:58 2003 @@ -12,6 +12,7 @@ #define PL2303_PRODUCT_ID_RSAQ2 0x04bb #define ATEN_VENDOR_ID 0x0557 +#define ATEN_VENDOR_ID2 0x0547 #define ATEN_PRODUCT_ID 0x2008 #define IODATA_VENDOR_ID 0x04bb diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c --- a/drivers/usb/serial/visor.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/serial/visor.c Fri Dec 12 15:06:57 2003 @@ -231,6 +231,8 @@ .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID), @@ -266,6 +268,7 @@ { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) }, { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) }, { }, /* optional parameter entry */ diff -Nru a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h --- a/drivers/usb/serial/visor.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/serial/visor.h Fri Dec 12 15:06:58 2003 @@ -41,6 +41,7 @@ #define SONY_CLIE_4_1_ID 0x009A #define SONY_CLIE_NX60_ID 0x00DA #define SONY_CLIE_NZ90V_ID 0x00E9 +#define SONY_CLIE_UX50_ID 0x0144 #define SAMSUNG_VENDOR_ID 0x04E8 #define SAMSUNG_SCH_I330_ID 0x8001 diff -Nru a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile --- a/drivers/usb/storage/Makefile Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/Makefile Fri Dec 12 15:06:58 2003 @@ -10,14 +10,14 @@ obj-$(CONFIG_USB_STORAGE) += usb-storage.o usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o -usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o raw_bulk.o -usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o raw_bulk.o -usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o raw_bulk.o +usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o +usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o +usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o -usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o raw_bulk.o -usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o raw_bulk.o +usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o +usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ initializers.o $(usb-storage-obj-y) diff -Nru a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c --- a/drivers/usb/storage/datafab.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/datafab.c Fri Dec 12 15:06:58 2003 @@ -51,7 +51,6 @@ */ #include "transport.h" -#include "raw_bulk.h" #include "protocol.h" #include "usb.h" #include "debug.h" @@ -91,16 +90,14 @@ struct datafab_info *info, u32 sector, u32 sectors, - unsigned char *dest, + unsigned char *buffer, int use_sg) { unsigned char *command = us->iobuf; - unsigned char *buffer = NULL; - unsigned char *ptr; unsigned char thistime; - int totallen, len, result; - int sg_idx = 0, sg_offset = 0; - int rc; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_idx = 0, sg_offset = 0; // we're working in LBA mode. according to the ATA spec, // we can support up to 28-bit addressing. I don't know if Datafab @@ -111,23 +108,32 @@ return USB_STOR_TRANSPORT_ERROR; if (info->lun == -1) { - rc = datafab_determine_lun(us, info); - if (rc != USB_STOR_TRANSPORT_GOOD) - return rc; + result = datafab_determine_lun(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; } totallen = sectors * info->ssize; + // Since we don't read more than 64 KB at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + alloclen = min(totallen, 65536u); + if (use_sg) { + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + } + do { // loop, never allocate or transfer more than 64k at once // (min(128k, 255*info->ssize) is the real limit) - len = min_t(int, totallen, 65536); - - ptr = buffer = (use_sg ? kmalloc(len, GFP_NOIO) : dest); - if (buffer == NULL) - return USB_STOR_TRANSPORT_ERROR; - + len = min(totallen, alloclen); thistime = (len / info->ssize) & 0xff; command[0] = 0; @@ -135,7 +141,7 @@ command[2] = sector & 0xFF; command[3] = (sector >> 8) & 0xFF; command[4] = (sector >> 16) & 0xFF; - + command[5] = 0xE0 + (info->lun << 4); command[5] |= (sector >> 24) & 0x0F; command[6] = 0x20; @@ -147,24 +153,23 @@ goto leave; // read the result - result = datafab_bulk_read(us, ptr, len); + result = datafab_bulk_read(us, buffer, len); if (result != USB_STOR_XFER_GOOD) goto leave; - sectors -= thistime; - sector += thistime; - - if (use_sg) { - us_copy_to_sgbuf(buffer, len, dest, - &sg_idx, &sg_offset, use_sg); - kfree(buffer); - } else { - dest += len; - } + // Store the data (s-g) or update the pointer (!s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg_idx, &sg_offset, TO_XFER_BUF); + else + buffer += len; + sector += thistime; totallen -= len; } while (totallen > 0); + if (use_sg) + kfree(buffer); return USB_STOR_TRANSPORT_GOOD; leave: @@ -178,16 +183,15 @@ struct datafab_info *info, u32 sector, u32 sectors, - unsigned char *src, + unsigned char *buffer, int use_sg) { unsigned char *command = us->iobuf; unsigned char *reply = us->iobuf; - unsigned char *buffer = NULL; - unsigned char *ptr; unsigned char thistime; - int totallen, len, result, rc; - int sg_idx = 0, sg_offset = 0; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_idx = 0, sg_offset = 0; // we're working in LBA mode. according to the ATA spec, // we can support up to 28-bit addressing. I don't know if Datafab @@ -198,38 +202,39 @@ return USB_STOR_TRANSPORT_ERROR; if (info->lun == -1) { - rc = datafab_determine_lun(us, info); - if (rc != USB_STOR_TRANSPORT_GOOD) - return rc; + result = datafab_determine_lun(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; } - // If we're using scatter-gather, we have to create a new - // buffer to read all of the data in first, since a - // scatter-gather buffer could in theory start in the middle - // of a page, which would be bad. A developer who wants a - // challenge might want to write a limited-buffer - // version of this code. - totallen = sectors * info->ssize; - do { - // loop, never allocate or transfer more than 64k at once - // (min(128k, 255*info->ssize) is the real limit) - - len = min_t(int, totallen, 65536); - - // if we are using scatter-gather, - // first copy all to one big buffer - - buffer = us_copy_from_sgbuf(src, len, &sg_idx, - &sg_offset, use_sg); + // Since we don't write more than 64 KB at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + alloclen = min(totallen, 65536u); + if (use_sg) { + buffer = kmalloc(alloclen, GFP_NOIO); if (buffer == NULL) return USB_STOR_TRANSPORT_ERROR; + } - ptr = buffer; + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + len = min(totallen, alloclen); thistime = (len / info->ssize) & 0xff; + // Get the data from the transfer buffer (s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg_idx, &sg_offset, FROM_XFER_BUF); + command[0] = 0; command[1] = thistime; command[2] = sector & 0xFF; @@ -247,7 +252,7 @@ goto leave; // send the data - result = datafab_bulk_write(us, ptr, len); + result = datafab_bulk_write(us, buffer, len); if (result != USB_STOR_XFER_GOOD) goto leave; @@ -264,17 +269,16 @@ goto leave; } - sectors -= thistime; - sector += thistime; - - if (use_sg) - kfree(buffer); - else - src += len; + // Update the transfer buffer pointer (!s-g) + if (!use_sg) + buffer += len; + sector += thistime; totallen -= len; } while (totallen > 0); + if (use_sg) + kfree(buffer); return USB_STOR_TRANSPORT_GOOD; leave: @@ -435,7 +439,7 @@ // datafab reader doesn't present a SCSI interface so we // fudge the SCSI commands... // - + if (sense_6) param_len = srb->cmnd[4]; else diff -Nru a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c --- a/drivers/usb/storage/debug.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/debug.c Fri Dec 12 15:06:57 2003 @@ -150,65 +150,6 @@ US_DEBUGPX("\n"); } -void usb_stor_print_Scsi_Cmnd(Scsi_Cmnd *cmd) -{ - int i=0, bufferSize = cmd->request_bufflen; - u8 *buffer = cmd->request_buffer; - struct scatterlist *sg = (struct scatterlist*)cmd->request_buffer; - - US_DEBUGP("Dumping information about %p.\n", cmd); - US_DEBUGP("cmd->cmnd[0] value is %d.\n", cmd->cmnd[0]); - US_DEBUGP("(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n", - MODE_SENSE, MODE_SENSE_10); - - US_DEBUGP("buffer is %p with length %d.\n", buffer, bufferSize); - for (i=0; iuse_sg ); - for (i=0; iuse_sg; i++) { - char *adr = sg_address(sg[i]); - - US_DEBUGP("Length of scatterlist %d is %d.\n",i,sg[i].length); - US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n" - "%02x %02x %02x %02x %02x %02x %02x %02x\n", - adr[0], - adr[1], - adr[2], - adr[3], - adr[4], - adr[5], - adr[6], - adr[7], - adr[8], - adr[9], - adr[10], - adr[11], - adr[12], - adr[13], - adr[14], - adr[15]); - } -} - void usb_stor_show_sense( unsigned char key, unsigned char asc, diff -Nru a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h --- a/drivers/usb/storage/debug.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/debug.h Fri Dec 12 15:06:58 2003 @@ -53,7 +53,6 @@ #ifdef CONFIG_USB_STORAGE_DEBUG void usb_stor_show_command(Scsi_Cmnd *srb); -void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ); void usb_stor_show_sense( unsigned char key, unsigned char asc, unsigned char ascq ); #define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x ) diff -Nru a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c --- a/drivers/usb/storage/isd200.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/isd200.c Fri Dec 12 15:06:58 2003 @@ -543,7 +543,6 @@ int result; /* send the command to the transport layer */ - srb->resid = 0; memcpy(srb->cmnd, ataCdb, sizeof(ataCdb->generic)); srb->cmd_len = sizeof(ataCdb->generic); transferStatus = usb_stor_Bulk_transport(srb, us); @@ -1117,60 +1116,6 @@ /************************************************************************** - * isd200_data_copy - * - * Copy data into the srb request buffer. Use scatter gather if required. - * - * RETURNS: - * void - */ -void isd200_data_copy(Scsi_Cmnd *srb, char * src, int length) -{ - unsigned int len = length; - struct scatterlist *sg; - - if (srb->use_sg) { - int i; - unsigned int total = 0; - - /* Add up the sizes of all the sg segments */ - sg = (struct scatterlist *) srb->request_buffer; - for (i = 0; i < srb->use_sg; i++) - total += sg[i].length; - - if (length > total) - len = total; - - total = 0; - - /* Copy data into sg buffer(s) */ - for (i = 0; i < srb->use_sg; i++) { - if ((len > total) && (len > 0)) { - /* transfer the lesser of the next buffer or the - * remaining data */ - if (len - total >= sg[i].length) { - memcpy(sg_address(sg[i]), src + total, sg[i].length); - total += sg[i].length; - } else { - memcpy(sg_address(sg[i]), src + total, len - total); - total = len; - } - } - else - break; - } - } else { - /* Make sure length does not exceed buffer length */ - if (length > srb->request_bufflen) - len = srb->request_bufflen; - - if (len > 0) - memcpy(srb->request_buffer, src, len); - } -} - - -/************************************************************************** * isd200_scsi_to_ata * * Translate SCSI commands to ATA commands. @@ -1198,11 +1143,9 @@ case INQUIRY: US_DEBUGP(" ATA OUT - INQUIRY\n"); - if (srb->request_bufflen > sizeof(struct inquiry_data)) - srb->request_bufflen = sizeof(struct inquiry_data); - /* copy InquiryData */ - isd200_data_copy(srb, (char *) &info->InquiryData, srb->request_bufflen); + usb_stor_set_xfer_buf((unsigned char *) &info->InquiryData, + sizeof(info->InquiryData), srb); srb->result = SAM_STAT_GOOD; sendToTransport = FALSE; break; @@ -1211,7 +1154,7 @@ US_DEBUGP(" ATA OUT - SCSIOP_MODE_SENSE\n"); /* Initialize the return buffer */ - isd200_data_copy(srb, (char *) &senseData, 8); + usb_stor_set_xfer_buf(senseData, sizeof(senseData), srb); if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED) { @@ -1231,9 +1174,6 @@ case TEST_UNIT_READY: US_DEBUGP(" ATA OUT - SCSIOP_TEST_UNIT_READY\n"); - /* Initialize the return buffer */ - isd200_data_copy(srb, (char *) &senseData, 8); - if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED) { ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; @@ -1266,10 +1206,8 @@ readCapacityData.LogicalBlockAddress = cpu_to_be32(capacity); readCapacityData.BytesPerBlock = cpu_to_be32(0x200); - if (srb->request_bufflen > sizeof(struct read_capacity_data)) - srb->request_bufflen = sizeof(struct read_capacity_data); - - isd200_data_copy(srb, (char *) &readCapacityData, srb->request_bufflen); + usb_stor_set_xfer_buf((unsigned char *) &readCapacityData, + sizeof(readCapacityData), srb); srb->result = SAM_STAT_GOOD; sendToTransport = FALSE; } @@ -1363,9 +1301,6 @@ US_DEBUGP(" ATA OUT - SCSIOP_START_STOP_UNIT\n"); US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]); - /* Initialize the return buffer */ - isd200_data_copy(srb, (char *) &senseData, 8); - if ((srb->cmnd[4] & 0x3) == 0x2) { US_DEBUGP(" Media Eject\n"); ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; @@ -1500,6 +1435,7 @@ US_DEBUGP("ERROR Driver not initialized\n"); /* Convert command */ + srb->resid = 0; sendToTransport = isd200_scsi_to_ata(srb, us, &ataCdb); /* send the command to the transport layer */ diff -Nru a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c --- a/drivers/usb/storage/jumpshot.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/jumpshot.c Fri Dec 12 15:06:57 2003 @@ -48,7 +48,6 @@ */ #include "transport.h" -#include "raw_bulk.h" #include "protocol.h" #include "usb.h" #include "debug.h" @@ -111,15 +110,14 @@ struct jumpshot_info *info, u32 sector, u32 sectors, - unsigned char *dest, + unsigned char *buffer, int use_sg) { unsigned char *command = us->iobuf; - unsigned char *buffer = NULL; - unsigned char *ptr; unsigned char thistime; - int totallen, len, result; - int sg_idx = 0, current_sg_offset = 0; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_idx = 0, sg_offset = 0; // we're working in LBA mode. according to the ATA spec, // we can support up to 28-bit addressing. I don't know if Jumpshot @@ -131,20 +129,24 @@ totallen = sectors * info->ssize; + // Since we don't read more than 64 KB at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + alloclen = min(totallen, 65536u); + if (use_sg) { + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + } + do { // loop, never allocate or transfer more than 64k at once // (min(128k, 255*info->ssize) is the real limit) - len = min_t(int, totallen, 65536); - - if (use_sg) { - buffer = kmalloc(len, GFP_NOIO); - if (buffer == NULL) - return USB_STOR_TRANSPORT_ERROR; - ptr = buffer; - } else { - ptr = dest; - } - + len = min(totallen, alloclen); thistime = (len / info->ssize) & 0xff; command[0] = 0; @@ -163,26 +165,25 @@ goto leave; // read the result - result = jumpshot_bulk_read(us, ptr, len); + result = jumpshot_bulk_read(us, buffer, len); if (result != USB_STOR_XFER_GOOD) goto leave; US_DEBUGP("jumpshot_read_data: %d bytes\n", len); - - sectors -= thistime; - sector += thistime; - - if (use_sg) { - us_copy_to_sgbuf(buffer, len, dest, - &sg_idx, ¤t_sg_offset, use_sg); - kfree(buffer); - } else { - dest += len; - } + // Store the data (s-g) or update the pointer (!s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg_idx, &sg_offset, TO_XFER_BUF); + else + buffer += len; + + sector += thistime; totallen -= len; } while (totallen > 0); + if (use_sg) + kfree(buffer); return USB_STOR_TRANSPORT_GOOD; leave: @@ -196,15 +197,14 @@ struct jumpshot_info *info, u32 sector, u32 sectors, - unsigned char *src, + unsigned char *buffer, int use_sg) { unsigned char *command = us->iobuf; - unsigned char *buffer = NULL; - unsigned char *ptr; unsigned char thistime; - int totallen, len, result, waitcount; - int sg_idx = 0, sg_offset = 0; + unsigned int totallen, alloclen; + int len, result, waitcount; + unsigned int sg_idx = 0, sg_offset = 0; // we're working in LBA mode. according to the ATA spec, // we can support up to 28-bit addressing. I don't know if Jumpshot @@ -216,24 +216,32 @@ totallen = sectors * info->ssize; - do { - // loop, never allocate or transfer more than 64k at once - // (min(128k, 255*info->ssize) is the real limit) - - len = min_t(int, totallen, 65536); - - // if we are using scatter-gather, - // first copy all to one big buffer - - buffer = us_copy_from_sgbuf(src, len, &sg_idx, - &sg_offset, use_sg); + // Since we don't write more than 64 KB at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + alloclen = min(totallen, 65536u); + if (use_sg) { + buffer = kmalloc(alloclen, GFP_NOIO); if (buffer == NULL) return USB_STOR_TRANSPORT_ERROR; + } - ptr = buffer; + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + len = min(totallen, alloclen); thistime = (len / info->ssize) & 0xff; + // Get the data from the transfer buffer (s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg_idx, &sg_offset, FROM_XFER_BUF); + command[0] = 0; command[1] = thistime; command[2] = sector & 0xFF; @@ -250,7 +258,7 @@ goto leave; // send the data - result = jumpshot_bulk_write(us, ptr, len); + result = jumpshot_bulk_write(us, buffer, len); if (result != USB_STOR_XFER_GOOD) goto leave; @@ -269,18 +277,17 @@ if (result != USB_STOR_TRANSPORT_GOOD) US_DEBUGP("jumpshot_write_data: Gah! Waitcount = 10. Bad write!?\n"); - - sectors -= thistime; - sector += thistime; - if (use_sg) - kfree(buffer); - else - src += len; + // Update the transfer buffer pointer (!s-g) + if (!use_sg) + buffer += len; + sector += thistime; totallen -= len; } while (totallen > 0); + if (use_sg) + kfree(buffer); return result; leave: @@ -605,14 +612,14 @@ US_DEBUGP("jumpshot_transport: MODE_SENSE_10 detected\n"); return jumpshot_handle_mode_sense(us, srb, ptr, FALSE); } - + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { // sure. whatever. not like we can stop the user from popping // the media out of the device (no locking doors, etc) // return USB_STOR_TRANSPORT_GOOD; } - + if (srb->cmnd[0] == START_STOP) { /* this is used by sd.c'check_scsidisk_media_change to detect media change */ diff -Nru a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c --- a/drivers/usb/storage/protocol.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/protocol.c Fri Dec 12 15:06:57 2003 @@ -44,6 +44,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include "protocol.h" #include "usb.h" #include "debug.h" @@ -54,48 +55,36 @@ * Helper routines ***********************************************************************/ -static void * -find_data_location(Scsi_Cmnd *srb) { - if (srb->use_sg) { - /* - * This piece of code only works if the first page is - * big enough to hold more than 3 bytes -- which is - * _very_ likely. - */ - struct scatterlist *sg; - - sg = (struct scatterlist *) srb->request_buffer; - return (void *) sg_address(sg[0]); - } else - return (void *) srb->request_buffer; -} - /* * Fix-up the return data from an INQUIRY command to show * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us */ static void fix_inquiry_data(Scsi_Cmnd *srb) { - unsigned char *data_ptr; + unsigned char databuf[3]; + unsigned int index, offset; /* verify that it's an INQUIRY command */ if (srb->cmnd[0] != INQUIRY) return; - /* oddly short buffer -- bail out */ - if (srb->request_bufflen < 3) + index = offset = 0; + if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, + &index, &offset, FROM_XFER_BUF) != sizeof(databuf)) return; - data_ptr = find_data_location(srb); - - if ((data_ptr[2] & 7) == 2) + if ((databuf[2] & 7) == 2) return; US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n", - data_ptr[2] & 7); + databuf[2] & 7); /* Change the SCSI revision number */ - data_ptr[2] = (data_ptr[2] & ~7) | 2; + databuf[2] = (databuf[2] & ~7) | 2; + + index = offset = 0; + usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, + &index, &offset, TO_XFER_BUF); } /* @@ -104,23 +93,27 @@ */ static void fix_read_capacity(Scsi_Cmnd *srb) { - unsigned char *dp; + unsigned int index, offset; + u32 c; unsigned long capacity; /* verify that it's a READ CAPACITY command */ if (srb->cmnd[0] != READ_CAPACITY) return; - dp = find_data_location(srb); + index = offset = 0; + if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb, + &index, &offset, FROM_XFER_BUF) != 4) + return; - capacity = (dp[0]<<24) + (dp[1]<<16) + (dp[2]<<8) + (dp[3]); + capacity = be32_to_cpu(c); US_DEBUGP("US: Fixing capacity: from %ld to %ld\n", capacity+1, capacity); - capacity--; - dp[0] = (capacity >> 24); - dp[1] = (capacity >> 16); - dp[2] = (capacity >> 8); - dp[3] = (capacity); + c = cpu_to_be32(capacity - 1); + + index = offset = 0; + usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb, + &index, &offset, TO_XFER_BUF); } /*********************************************************************** @@ -233,4 +226,113 @@ if (us->flags & US_FL_FIX_CAPACITY) fix_read_capacity(srb); } +} + +/*********************************************************************** + * Scatter-gather transfer buffer access routines + ***********************************************************************/ + +/* Copy a buffer of length buflen to/from the srb's transfer buffer. + * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer + * points to a list of s-g entries and we ignore srb->request_bufflen. + * For non-scatter-gather transfers, srb->request_buffer points to the + * transfer buffer itself and srb->request_bufflen is the buffer's length.) + * Update the *index and *offset variables so that the next copy will + * pick up from where this one left off. */ + +unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, + unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index, + unsigned int *offset, enum xfer_buf_dir dir) +{ + unsigned int cnt; + + /* If not using scatter-gather, just transfer the data directly. + * Make certain it will fit in the available buffer space. */ + if (srb->use_sg == 0) { + if (*offset >= srb->request_bufflen) + return 0; + cnt = min(buflen, srb->request_bufflen - *offset); + if (dir == TO_XFER_BUF) + memcpy((unsigned char *) srb->request_buffer + *offset, + buffer, cnt); + else + memcpy(buffer, (unsigned char *) srb->request_buffer + + *offset, cnt); + *offset += cnt; + + /* Using scatter-gather. We have to go through the list one entry + * at a time. Each s-g entry contains some number of pages, and + * each page has to be kmap()'ed separately. If the page is already + * in kernel-addressable memory then kmap() will return its address. + * If the page is not directly accessible -- such as a user buffer + * located in high memory -- then kmap() will map it to a temporary + * position in the kernel's virtual address space. */ + } else { + struct scatterlist *sg = + (struct scatterlist *) srb->request_buffer + + *index; + + /* This loop handles a single s-g list entry, which may + * include multiple pages. Find the initial page structure + * and the starting offset within the page, and update + * the *offset and *index values for the next loop. */ + cnt = 0; + while (cnt < buflen && *index < srb->use_sg) { + struct page *page = sg->page + + ((sg->offset + *offset) >> PAGE_SHIFT); + unsigned int poff = + (sg->offset + *offset) & (PAGE_SIZE-1); + unsigned int sglen = sg->length - *offset; + + if (sglen > buflen - cnt) { + + /* Transfer ends within this s-g entry */ + sglen = buflen - cnt; + *offset += sglen; + } else { + + /* Transfer continues to next s-g entry */ + *offset = 0; + ++*index; + ++sg; + } + + /* Transfer the data for all the pages in this + * s-g entry. For each page: call kmap(), do the + * transfer, and call kunmap() immediately after. */ + while (sglen > 0) { + unsigned int plen = min(sglen, (unsigned int) + PAGE_SIZE - poff); + unsigned char *ptr = kmap(page); + + if (dir == TO_XFER_BUF) + memcpy(ptr + poff, buffer + cnt, plen); + else + memcpy(buffer + cnt, ptr + poff, plen); + kunmap(page); + + /* Start at the beginning of the next page */ + poff = 0; + ++page; + cnt += plen; + sglen -= plen; + } + } + } + + /* Return the amount actually transferred */ + return cnt; +} + +/* Store the contents of buffer into srb's transfer buffer and set the + * SCSI residue. */ +void usb_stor_set_xfer_buf(unsigned char *buffer, + unsigned int buflen, Scsi_Cmnd *srb) +{ + unsigned int index = 0, offset = 0; + + usb_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset, + TO_XFER_BUF); + if (buflen < srb->request_bufflen) + srb->resid = srb->request_bufflen - buflen; } diff -Nru a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h --- a/drivers/usb/storage/protocol.h Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/protocol.h Fri Dec 12 15:06:57 2003 @@ -59,9 +59,19 @@ #define US_SC_DEVICE 0xff /* Use device's value */ +/* Protocol handling routines */ extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*); extern void usb_stor_qic157_command(Scsi_Cmnd*, struct us_data*); extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*); extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*); +/* Scsi_Cmnd transfer buffer access utilities */ +enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF}; + +extern unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, + unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index, + unsigned int *offset, enum xfer_buf_dir dir); + +extern void usb_stor_set_xfer_buf(unsigned char *buffer, + unsigned int buflen, Scsi_Cmnd *srb); #endif diff -Nru a/drivers/usb/storage/raw_bulk.c b/drivers/usb/storage/raw_bulk.c --- a/drivers/usb/storage/raw_bulk.c Fri Dec 12 15:06:58 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,116 +0,0 @@ -/* - * Common routines for a handful of drivers. - * Unrelated to CF/SM - just scatter-gather stuff. - */ - -#include "usb.h" -#include "raw_bulk.h" - -/* - * The routines below convert scatter-gather to single buffer. - * Some drivers claim this is necessary. - * Nothing is done when use_sg is zero. - */ - -/* - * Copy from scatter-gather buffer into a newly allocated single buffer, - * starting at a given index and offset. - * When done, update index and offset. - * Return a pointer to the single buffer. - */ -unsigned char * -us_copy_from_sgbuf(unsigned char *content, int len, - int *index, int *offset, int use_sg) { - struct scatterlist *sg; - unsigned char *buffer; - int transferred, i; - - if (!use_sg) - return content; - - sg = (struct scatterlist *)content; - buffer = kmalloc(len, GFP_NOIO); - if (buffer == NULL) - return NULL; - - transferred = 0; - i = *index; - while (i < use_sg && transferred < len) { - unsigned char *ptr; - unsigned int length, room; - - ptr = sg_address(sg[i]) + *offset; - - room = sg[i].length - *offset; - length = len - transferred; - if (length > room) - length = room; - - memcpy(buffer+transferred, ptr, length); - transferred += length; - *offset += length; - if (length == room) { - i++; - *offset = 0; - } - } - *index = i; - - return buffer; -} - -unsigned char * -us_copy_from_sgbuf_all(unsigned char *content, int len, int use_sg) { - int index, offset; - - index = offset = 0; - return us_copy_from_sgbuf(content, len, &index, &offset, use_sg); -} - -/* - * Copy from a single buffer into a scatter-gather buffer, - * starting at a given index and offset. - * When done, update index and offset. - */ -void -us_copy_to_sgbuf(unsigned char *buffer, int buflen, - void *content, int *index, int *offset, int use_sg) { - struct scatterlist *sg; - int i, transferred; - - if (!use_sg) - return; - - transferred = 0; - sg = content; - i = *index; - while (i < use_sg && transferred < buflen) { - unsigned char *ptr; - unsigned int length, room; - - ptr = sg_address(sg[i]) + *offset; - - room = sg[i].length - *offset; - length = buflen - transferred; - if (length > room) - length = room; - - memcpy(ptr, buffer+transferred, length); - transferred += sg[i].length; - *offset += length; - if (length == room) { - i++; - *offset = 0; - } - } - *index = i; -} - -void -us_copy_to_sgbuf_all(unsigned char *buffer, int buflen, - void *content, int use_sg) { - int index, offset; - - index = offset = 0; - us_copy_to_sgbuf(buffer, buflen, content, &index, &offset, use_sg); -} diff -Nru a/drivers/usb/storage/raw_bulk.h b/drivers/usb/storage/raw_bulk.h --- a/drivers/usb/storage/raw_bulk.h Fri Dec 12 15:06:57 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,20 +0,0 @@ -#ifndef _USB_STORAGE_RAW_BULK_H_ -#define _USB_STORAGE_RAW_BULK_H_ - -/* scatter-gather */ -extern unsigned char *us_copy_from_sgbuf( - unsigned char *content, int buflen, - int *index, int *offset, int use_sg); - -extern unsigned char *us_copy_from_sgbuf_all( - unsigned char *content, int len, int use_sg); - -extern void us_copy_to_sgbuf( - unsigned char *buffer, int buflen, - void *content, int *index, int *offset, int use_sg); - -extern void us_copy_to_sgbuf_all( - unsigned char *buffer, int buflen, - void *content, int use_sg); - -#endif diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c --- a/drivers/usb/storage/scsiglue.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/scsiglue.c Fri Dec 12 15:06:58 2003 @@ -325,7 +325,8 @@ .emulated = TRUE, /* modify scsi_device bits on probe */ - .flags = (BLIST_MS_SKIP_PAGE_08 | BLIST_USE_10_BYTE_MS), + .flags = (BLIST_MS_SKIP_PAGE_08 | BLIST_MS_SKIP_PAGE_3F | + BLIST_USE_10_BYTE_MS), /* module management */ .module = THIS_MODULE diff -Nru a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c --- a/drivers/usb/storage/sddr09.c Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/sddr09.c Fri Dec 12 15:06:58 2003 @@ -28,7 +28,6 @@ */ #include "transport.h" -#include "raw_bulk.h" #include "protocol.h" #include "usb.h" #include "debug.h" @@ -66,7 +65,7 @@ * NAND Flash Manufacturer ID Codes */ #define NAND_MFR_AMD 0x01 -#define NAND_MFR_NS 0x8f +#define NAND_MFR_NATSEMI 0x8f #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec @@ -74,8 +73,8 @@ switch(manuf_id) { case NAND_MFR_AMD: return "AMD"; - case NAND_MFR_NS: - return "NS"; + case NAND_MFR_NATSEMI: + return "NATSEMI"; case NAND_MFR_TOSHIBA: return "Toshiba"; case NAND_MFR_SAMSUNG: @@ -302,8 +301,7 @@ if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("request sense bulk in failed\n"); return USB_STOR_TRANSPORT_ERROR; - } - else { + } else { US_DEBUGP("request sense worked\n"); return USB_STOR_TRANSPORT_GOOD; } @@ -469,6 +467,8 @@ unsigned char *command = us->iobuf; int result; + US_DEBUGP("sddr09_erase: erase address %lu\n", Eaddress); + memset(command, 0, 12); command[0] = 0xEA; command[1] = LUNBITS; @@ -663,30 +663,31 @@ sddr09_read_data(struct us_data *us, unsigned long address, unsigned int sectors, - unsigned char *content, + unsigned char *buffer, int use_sg) { struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; unsigned int lba, maxlba, pba; unsigned int page, pages; - unsigned char *buffer = NULL; - unsigned char *ptr; - int result, len; - - // If we're using scatter-gather, we have to create a new - // buffer to read all of the data in first, since a - // scatter-gather buffer could in theory start in the middle - // of a page, which would be bad. A developer who wants a - // challenge might want to write a limited-buffer - // version of this code. - - len = sectors*info->pagesize; - - buffer = (use_sg ? kmalloc(len, GFP_NOIO) : content); - if (buffer == NULL) - return USB_STOR_TRANSPORT_ERROR; + unsigned int len, index, offset; + int result; - ptr = buffer; + // Since we only read in one block at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + if (use_sg) { + len = min(sectors, (unsigned int) info->blocksize) * + info->pagesize; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) { + printk("sddr09_read_data: Out of memory\n"); + return USB_STOR_TRANSPORT_ERROR; + } + } // Figure out the initial LBA and page lba = address >> info->blockshift; @@ -697,13 +698,13 @@ // contiguous LBA's. Another exercise left to the student. result = USB_STOR_TRANSPORT_GOOD; + index = offset = 0; while (sectors > 0) { /* Find number of pages we can read in this block */ - pages = info->blocksize - page; - if (pages > sectors) - pages = sectors; + pages = min(sectors, info->blocksize - page); + len = pages << info->pageshift; /* Not overflowing capacity? */ if (lba >= maxlba) { @@ -726,7 +727,7 @@ Instead of returning USB_STOR_TRANSPORT_ERROR it is better to return all zero data. */ - memset(ptr, 0, pages << info->pageshift); + memset(buffer, 0, len); } else { US_DEBUGP("Read %d pages, from PBA %d" @@ -737,37 +738,50 @@ info->pageshift; result = sddr09_read20(us, address>>1, - pages, info->pageshift, ptr, 0); + pages, info->pageshift, buffer, 0); if (result != USB_STOR_TRANSPORT_GOOD) break; } + // Store the data (s-g) or update the pointer (!s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &index, &offset, TO_XFER_BUF); + else + buffer += len; + page = 0; lba++; sectors -= pages; - ptr += (pages << info->pageshift); } - if (use_sg && result == USB_STOR_TRANSPORT_GOOD) - us_copy_to_sgbuf_all(buffer, len, content, use_sg); - if (use_sg) kfree(buffer); return result; } -/* we never free blocks, so lastpba can only increase */ static unsigned int -sddr09_find_unused_pba(struct sddr09_card_info *info) { +sddr09_find_unused_pba(struct sddr09_card_info *info, unsigned int lba) { static unsigned int lastpba = 1; - int numblocks = info->capacity >> (info->blockshift + info->pageshift); - int i; + int zonestart, end, i; - for (i = lastpba+1; i < numblocks; i++) { - if (info->pba_to_lba[i] == UNDEF) { + zonestart = (lba/1000) << 10; + end = info->capacity >> (info->blockshift + info->pageshift); + end -= zonestart; + if (end > 1024) + end = 1024; + + for (i = lastpba+1; i < end; i++) { + if (info->pba_to_lba[zonestart+i] == UNDEF) { + lastpba = i; + return zonestart+i; + } + } + for (i = 0; i <= lastpba; i++) { + if (info->pba_to_lba[zonestart+i] == UNDEF) { lastpba = i; - return i; + return zonestart+i; } } return 0; @@ -776,29 +790,31 @@ static int sddr09_write_lba(struct us_data *us, unsigned int lba, unsigned int page, unsigned int pages, - unsigned char *ptr) { + unsigned char *ptr, unsigned char *blockbuffer) { struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; unsigned long address; unsigned int pba, lbap; - unsigned int pagelen, blocklen; - unsigned char *blockbuffer, *bptr, *cptr, *xptr; + unsigned int pagelen; + unsigned char *bptr, *cptr, *xptr; unsigned char ecc[3]; - int i, result; + int i, result, isnew; - lbap = ((lba & 0x3ff) << 1) | 0x1000; + lbap = ((lba % 1000) << 1) | 0x1000; if (parity[MSB_of(lbap) ^ LSB_of(lbap)]) lbap ^= 1; pba = info->lba_to_pba[lba]; + isnew = 0; if (pba == UNDEF) { - pba = sddr09_find_unused_pba(info); + pba = sddr09_find_unused_pba(info, lba); if (!pba) { printk("sddr09_write_lba: Out of unused blocks\n"); return USB_STOR_TRANSPORT_ERROR; } info->pba_to_lba[pba] = lba; info->lba_to_pba[lba] = pba; + isnew = 1; } if (pba == 1) { @@ -809,22 +825,16 @@ } pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); - blocklen = (pagelen << info->blockshift); - blockbuffer = kmalloc(blocklen, GFP_NOIO); - if (!blockbuffer) { - printk("sddr09_write_lba: Out of memory\n"); - return USB_STOR_TRANSPORT_ERROR; - } /* read old contents */ address = (pba << (info->pageshift + info->blockshift)); result = sddr09_read22(us, address>>1, info->blocksize, info->pageshift, blockbuffer, 0); if (result != USB_STOR_TRANSPORT_GOOD) - goto err; + return result; - /* check old contents */ - for (i = 0; i < info->blockshift; i++) { + /* check old contents and fill lba */ + for (i = 0; i < info->blocksize; i++) { bptr = blockbuffer + i*pagelen; cptr = bptr + info->pagesize; nand_compute_ecc(bptr, ecc); @@ -839,6 +849,8 @@ i, pba); nand_store_ecc(cptr+8, ecc); } + cptr[6] = cptr[11] = MSB_of(lbap); + cptr[7] = cptr[12] = LSB_of(lbap); } /* copy in new stuff and compute ECC */ @@ -852,8 +864,6 @@ nand_store_ecc(cptr+13, ecc); nand_compute_ecc(bptr+(info->pagesize / 2), ecc); nand_store_ecc(cptr+8, ecc); - cptr[6] = cptr[11] = MSB_of(lbap); - cptr[7] = cptr[12] = LSB_of(lbap); } US_DEBUGP("Rewrite PBA %d (LBA %d)\n", pba, lba); @@ -880,11 +890,6 @@ int result2 = sddr09_test_unit_ready(us); } #endif - err: - kfree(blockbuffer); - - /* TODO: instead of doing kmalloc/kfree for each block, - add a bufferpointer to the info structure */ return result; } @@ -893,49 +898,84 @@ sddr09_write_data(struct us_data *us, unsigned long address, unsigned int sectors, - unsigned char *content, + unsigned char *buffer, int use_sg) { struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; unsigned int lba, page, pages; - unsigned char *buffer = NULL; - unsigned char *ptr; - int result, len; + unsigned int pagelen, blocklen; + unsigned char *blockbuffer; + unsigned int len, index, offset; + int result; - len = sectors*info->pagesize; + // blockbuffer is used for reading in the old data, overwriting + // with the new data, and performing ECC calculations - buffer = us_copy_from_sgbuf_all(content, len, use_sg); - if (buffer == NULL) + /* TODO: instead of doing kmalloc/kfree for each write, + add a bufferpointer to the info structure */ + + pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); + blocklen = (pagelen << info->blockshift); + blockbuffer = kmalloc(blocklen, GFP_NOIO); + if (!blockbuffer) { + printk("sddr09_write_data: Out of memory\n"); return USB_STOR_TRANSPORT_ERROR; + } - ptr = buffer; + // Since we don't write the user data directly to the device, + // we have to create a bounce buffer if the transfer uses + // scatter-gather. We will move the data a piece at a time + // between the bounce buffer and the actual transfer buffer. + // If we're not using scatter-gather, we can simply update + // the transfer buffer pointer to get the same effect. + + if (use_sg) { + len = min(sectors, (unsigned int) info->blocksize) * + info->pagesize; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) { + printk("sddr09_write_data: Out of memory\n"); + kfree(blockbuffer); + return USB_STOR_TRANSPORT_ERROR; + } + } // Figure out the initial LBA and page lba = address >> info->blockshift; page = (address & info->blockmask); result = USB_STOR_TRANSPORT_GOOD; + index = offset = 0; while (sectors > 0) { // Write as many sectors as possible in this block - pages = info->blocksize - page; - if (pages > sectors) - pages = sectors; + pages = min(sectors, info->blocksize - page); + len = (pages << info->pageshift); + + // Get the data from the transfer buffer (s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &index, &offset, FROM_XFER_BUF); - result = sddr09_write_lba(us, lba, page, pages, ptr); + result = sddr09_write_lba(us, lba, page, pages, + buffer, blockbuffer); if (result != USB_STOR_TRANSPORT_GOOD) break; + // Update the transfer buffer pointer (!s-g) + if (!use_sg) + buffer += len; + page = 0; lba++; sectors -= pages; - ptr += (pages << info->pageshift); } if (use_sg) kfree(buffer); + kfree(blockbuffer); return result; } @@ -947,10 +987,11 @@ unsigned char *content, int use_sg) { - US_DEBUGP("Read control address %08lX blocks %04X\n", + US_DEBUGP("Read control address %lu, blocks %d\n", address, blocks); - return sddr09_read21(us, address, blocks, CONTROL_SHIFT, content, use_sg); + return sddr09_read21(us, address, blocks, + CONTROL_SHIFT, content, use_sg); } /* @@ -997,7 +1038,7 @@ US_DEBUGP("sddr09_get_wp: read_status fails\n"); return result; } - US_DEBUGP("sddr09_get_wp: status %02X", status); + US_DEBUGP("sddr09_get_wp: status 0x%02X", status); if ((status & 0x80) == 0) { info->flags |= SDDR09_WP; /* write protected */ US_DEBUGP(" WP"); @@ -1092,69 +1133,36 @@ static int sddr09_read_map(struct us_data *us) { - struct scatterlist *sg; struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; int numblocks, alloc_len, alloc_blocks; int i, j, result; - unsigned char *ptr; + unsigned char *buffer, *buffer_end, *ptr; unsigned int lba, lbact; if (!info->capacity) return -1; - // read 64 (1<<6) bytes for every block - // ( 1 << ( blockshift + pageshift ) bytes) - // of capacity: - // (1<<6)*capacity/(1<<(b+p)) = - // ((1<<6)*capacity)>>(b+p) = - // capacity>>(b+p-6) - - alloc_len = info->capacity >> - (info->blockshift + info->pageshift - CONTROL_SHIFT); - - // Allocate a number of scatterlist structures according to - // the number of 128k blocks in the alloc_len. Adding 128k-1 - // and then dividing by 128k gives the correct number of blocks. - // 128k = 1<<17 - - alloc_blocks = (alloc_len + (1<<17) - 1) >> 17; - sg = kmalloc(alloc_blocks*sizeof(struct scatterlist), - GFP_NOIO); - if (sg == NULL) - return 0; - - for (i=0; i= KERNEL_VERSION(2,5,3) - sg[i].page = virt_to_page(vaddr); - sg[i].offset = offset_in_page(vaddr); -#else - sg[i].address = vaddr; -#endif - sg[i].length = alloc_req; - alloc_len -= alloc_req; - } - - for (i=0; icapacity >> (info->blockshift + info->pageshift); - result = sddr09_read_control(us, 0, numblocks, - (unsigned char *)sg, alloc_blocks); - if (result != USB_STOR_TRANSPORT_GOOD) { - for (i=0; i> CONTROL_SHIFT); + alloc_len = (alloc_blocks << CONTROL_SHIFT); + buffer = kmalloc(alloc_len, GFP_NOIO); + if (buffer == NULL) { + printk("sddr09_read_map: out of memory\n"); + result = -1; + goto done; } + buffer_end = buffer + alloc_len; + +#undef SDDR09_READ_MAP_BUFSZ kfree(info->lba_to_pba); kfree(info->pba_to_lba); @@ -1162,29 +1170,35 @@ info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO); if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { - kfree(info->lba_to_pba); - kfree(info->pba_to_lba); - info->lba_to_pba = NULL; - info->pba_to_lba = NULL; - for (i=0; ilba_to_pba[i] = info->pba_to_lba[i] = UNDEF; - ptr = sg_address(sg[0]); - /* * Define lba-pba translation table */ - // Each block is 64 bytes of control data, so block i is located in - // scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11) - for (i=0; i>11]) + ((i&0x7ff)<<6); + ptr = buffer_end; + for (i = 0; i < numblocks; i++) { + ptr += (1 << CONTROL_SHIFT); + if (ptr >= buffer_end) { + unsigned long address; + + address = i << (info->pageshift + info->blockshift); + result = sddr09_read_control( + us, address>>1, + min(alloc_blocks, numblocks - i), + buffer, 0); + if (result != USB_STOR_TRANSPORT_GOOD) { + result = -1; + goto done; + } + ptr = buffer; + } if (i == 0 || i == 1) { info->pba_to_lba[i] = UNUSABLE; @@ -1196,7 +1210,7 @@ if (ptr[j] != 0) goto nonz; info->pba_to_lba[i] = UNUSABLE; - printk("sddr09: PBA %04X has no logical mapping\n", i); + printk("sddr09: PBA %d has no logical mapping\n", i); continue; nonz: @@ -1209,7 +1223,7 @@ nonff: /* normal PBAs start with six FFs */ if (j < 6) { - printk("sddr09: PBA %04X has no logical mapping: " + printk("sddr09: PBA %d has no logical mapping: " "reserved area = %02X%02X%02X%02X " "data status %02X block status %02X\n", i, ptr[0], ptr[1], ptr[2], ptr[3], @@ -1219,7 +1233,7 @@ } if ((ptr[6] >> 4) != 0x01) { - printk("sddr09: PBA %04X has invalid address field " + printk("sddr09: PBA %d has invalid address field " "%02X%02X/%02X%02X\n", i, ptr[6], ptr[7], ptr[11], ptr[12]); info->pba_to_lba[i] = UNUSABLE; @@ -1228,7 +1242,7 @@ /* check even parity */ if (parity[ptr[6] ^ ptr[7]]) { - printk("sddr09: Bad parity in LBA for block %04X" + printk("sddr09: Bad parity in LBA for block %d" " (%02X %02X)\n", i, ptr[6], ptr[7]); info->pba_to_lba[i] = UNUSABLE; continue; @@ -1247,27 +1261,32 @@ */ if (lba >= 1000) { - unsigned long address; - - printk("sddr09: Bad LBA %04X for block %04X\n", + printk("sddr09: Bad low LBA %d for block %d\n", lba, i); - info->pba_to_lba[i] = UNDEF /* UNUSABLE */; - if (erase_bad_lba_entries) { - /* some cameras cannot erase a card if it has - bad entries, so we supply this function */ - address = (i << (info->pageshift + info->blockshift)); - sddr09_erase(us, address>>1); - } - continue; + goto possibly_erase; } lba += 1000*(i/0x400); - if (lba<0x10 || (lba >= 0x3E0 && lba < 0x3EF)) - US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i); + if (info->lba_to_pba[lba] != UNDEF) { + printk("sddr09: LBA %d seen for PBA %d and %d\n", + lba, info->lba_to_pba[lba], i); + goto possibly_erase; + } info->pba_to_lba[i] = lba; info->lba_to_pba[lba] = i; + continue; + + possibly_erase: + if (erase_bad_lba_entries) { + unsigned long address; + + address = (i << (info->pageshift + info->blockshift)); + sddr09_erase(us, address>>1); + info->pba_to_lba[i] = UNDEF; + } else + info->pba_to_lba[i] = UNUSABLE; } /* @@ -1292,11 +1311,17 @@ } info->lbact = lbact; US_DEBUGP("Found %d LBA's\n", lbact); + result = 0; - for (i=0; ilba_to_pba); + kfree(info->pba_to_lba); + info->lba_to_pba = NULL; + info->pba_to_lba = NULL; + } + kfree(buffer); + return result; } static void @@ -1438,6 +1463,7 @@ cardinfo = sddr09_get_cardinfo(us, info->flags); if (!cardinfo) { /* probably no media */ + init_error: sensekey = 0x02; /* not ready */ sensecode = 0x3a; /* medium not present */ return USB_STOR_TRANSPORT_FAILED; @@ -1451,7 +1477,10 @@ info->blockmask = info->blocksize - 1; // map initialization, must follow get_cardinfo() - sddr09_read_map(us); + if (sddr09_read_map(us)) { + /* probably out of memory */ + goto init_error; + } // Report capacity @@ -1472,12 +1501,13 @@ return USB_STOR_TRANSPORT_GOOD; } - if (srb->cmnd[0] == MODE_SENSE) { + if (srb->cmnd[0] == MODE_SENSE || srb->cmnd[0] == MODE_SENSE_10) { int modepage = (srb->cmnd[2] & 0x3F); int len; /* They ask for the Read/Write error recovery page, or for all pages. Give as much as they have room for. */ + /* %% We should check DBD %% */ if (modepage == 0x01 || modepage == 0x3F) { US_DEBUGP("SDDR09: Dummy up request for " @@ -1496,20 +1526,14 @@ return USB_STOR_TRANSPORT_GOOD; } - return USB_STOR_TRANSPORT_ERROR; + sensekey = 0x05; /* illegal request */ + sensecode = 0x24; /* invalid field in CDB */ + return USB_STOR_TRANSPORT_FAILED; } - if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { - - US_DEBUGP( - "SDDR09: %s medium removal. Not that I can do" - " anything about it...\n", - (srb->cmnd[4]&0x03) ? "Prevent" : "Allow"); - + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) return USB_STOR_TRANSPORT_GOOD; - } - havefakesense = 0; if (srb->cmnd[0] == READ_10) { @@ -1542,8 +1566,10 @@ if (srb->cmnd[0] != TEST_UNIT_READY && srb->cmnd[0] != REQUEST_SENSE) { + sensekey = 0x05; /* illegal request */ + sensecode = 0x20; /* invalid command */ havefakesense = 1; - return USB_STOR_TRANSPORT_ERROR; + return USB_STOR_TRANSPORT_FAILED; } for (; srb->cmd_len<12; srb->cmd_len++) @@ -1555,8 +1581,7 @@ for (i=0; i<12; i++) sprintf(string+strlen(string), "%02X ", srb->cmnd[i]); - US_DEBUGP("SDDR09: Send control for command %s\n", - string); + US_DEBUGP("SDDR09: Send control for command %s\n", string); result = sddr09_send_scsi_command(us, srb->cmnd, 12); if (result != USB_STOR_TRANSPORT_GOOD) { diff -Nru a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c --- a/drivers/usb/storage/sddr55.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/sddr55.c Fri Dec 12 15:06:57 2003 @@ -25,7 +25,6 @@ */ #include "transport.h" -#include "raw_bulk.h" #include "protocol.h" #include "usb.h" #include "debug.h" @@ -155,7 +154,7 @@ unsigned int lba, unsigned int page, unsigned short sectors, - unsigned char *content, + unsigned char *buffer, int use_sg) { int result = USB_STOR_TRANSPORT_GOOD; @@ -167,17 +166,24 @@ unsigned long address; unsigned short pages; - unsigned char *buffer = NULL; - unsigned char *ptr; - int len; + unsigned int len, index, offset; - len = sectors * PAGESIZE; - - buffer = (use_sg ? kmalloc(len, GFP_NOIO) : content); - if (buffer == NULL) - return USB_STOR_TRANSPORT_ERROR; /* out of memory */ - - ptr = buffer; + // Since we only read in one block at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + if (use_sg) { + len = min((unsigned int) sectors, + (unsigned int) info->blocksize >> + info->smallpageshift) * PAGESIZE; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; /* out of memory */ + } + index = offset = 0; while (sectors>0) { @@ -189,9 +195,9 @@ // Read as many sectors as possible in this block - pages = info->blocksize - page; - if (pages > (sectors << info->smallpageshift)) - pages = (sectors << info->smallpageshift); + pages = min((unsigned int) sectors << info->smallpageshift, + info->blocksize - page); + len = pages << info->pageshift; US_DEBUGP("Read %02X pages, from PBA %04X" " (LBA %04X) page %02X\n", @@ -199,7 +205,7 @@ if (pba == NOT_ALLOCATED) { /* no pba for this lba, fill with zeroes */ - memset (ptr, 0, pages << info->pageshift); + memset (buffer, 0, len); } else { address = (pba << info->blockshift) + page; @@ -228,8 +234,7 @@ /* read data */ result = sddr55_bulk_transport(us, - SCSI_DATA_READ, ptr, - pages<pageshift); + SCSI_DATA_READ, buffer, len); if (result != USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; @@ -253,13 +258,18 @@ } } + // Store the data (s-g) or update the pointer (!s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &index, &offset, TO_XFER_BUF); + else + buffer += len; + page = 0; lba++; sectors -= pages >> info->smallpageshift; - ptr += (pages << info->pageshift); } - us_copy_to_sgbuf_all(buffer, len, content, use_sg); result = USB_STOR_TRANSPORT_GOOD; leave: @@ -273,7 +283,7 @@ unsigned int lba, unsigned int page, unsigned short sectors, - unsigned char *content, + unsigned char *buffer, int use_sg) { int result = USB_STOR_TRANSPORT_GOOD; @@ -286,9 +296,8 @@ unsigned long address; unsigned short pages; - unsigned char *buffer = NULL; - unsigned char *ptr; - int i, len; + int i; + unsigned int len, index, offset; /* check if we are allowed to write */ if (info->read_only || info->force_read_only) { @@ -296,13 +305,22 @@ return USB_STOR_TRANSPORT_FAILED; } - len = sectors * PAGESIZE; - - buffer = us_copy_from_sgbuf_all(content, len, use_sg); - if (buffer == NULL) - return USB_STOR_TRANSPORT_ERROR; - - ptr = buffer; + // Since we only write one block at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + + if (use_sg) { + len = min((unsigned int) sectors, + (unsigned int) info->blocksize >> + info->smallpageshift) * PAGESIZE; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + } + index = offset = 0; while (sectors > 0) { @@ -314,9 +332,14 @@ // Write as many sectors as possible in this block - pages = info->blocksize - page; - if (pages > (sectors << info->smallpageshift)) - pages = (sectors << info->smallpageshift); + pages = min((unsigned int) sectors << info->smallpageshift, + info->blocksize - page); + len = pages << info->pageshift; + + // Get the data from the transfer buffer (s-g) + if (use_sg) + usb_stor_access_xfer_buf(buffer, len, us->srb, + &index, &offset, FROM_XFER_BUF); US_DEBUGP("Write %02X pages, to PBA %04X" " (LBA %04X) page %02X\n", @@ -400,8 +423,7 @@ /* send the data */ result = sddr55_bulk_transport(us, - SCSI_DATA_WRITE, ptr, - pages<pageshift); + SCSI_DATA_WRITE, buffer, len); if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for send_data in write_data %d\n", @@ -458,10 +480,12 @@ /* update the pba<->lba maps for new_pba */ info->pba_to_lba[new_pba] = lba % 1000; + // Update the transfer buffer pointer (!s-g) + if (!use_sg) + buffer += len; page = 0; lba++; sectors -= pages >> info->smallpageshift; - ptr += (pages << info->pageshift); } result = USB_STOR_TRANSPORT_GOOD; diff -Nru a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c --- a/drivers/usb/storage/shuttle_usbat.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/shuttle_usbat.c Fri Dec 12 15:06:57 2003 @@ -40,7 +40,6 @@ */ #include "transport.h" -#include "raw_bulk.h" #include "protocol.h" #include "usb.h" #include "debug.h" @@ -529,9 +528,8 @@ unsigned char *buffer; unsigned int len; unsigned int sector; - struct scatterlist *sg = NULL; - int sg_segment = 0; - int sg_offset = 0; + unsigned int sg_segment = 0; + unsigned int sg_offset = 0; US_DEBUGP("handle_read10: transfersize %d\n", srb->transfersize); @@ -570,21 +568,29 @@ srb->transfersize); } + // Since we only read in one block at a time, we have to create + // a bounce buffer if the transfer uses scatter-gather. We will + // move the data a piece at a time between the bounce buffer and + // the actual transfer buffer. If we're not using scatter-gather, + // we can simply update the transfer buffer pointer to get the + // same effect. + len = (65535/srb->transfersize) * srb->transfersize; US_DEBUGP("Max read is %d bytes\n", len); - buffer = kmalloc(len, GFP_NOIO); - if (buffer == NULL) // bloody hell! - return USB_STOR_TRANSPORT_FAILED; + len = min(len, srb->request_bufflen); + if (srb->use_sg) { + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) // bloody hell! + return USB_STOR_TRANSPORT_FAILED; + } else + buffer = srb->request_buffer; sector = short_pack(data[7+3], data[7+2]); sector <<= 16; sector |= short_pack(data[7+5], data[7+4]); transferred = 0; - if (srb->use_sg) { - sg = (struct scatterlist *)srb->request_buffer; - sg_segment = 0; // for keeping track of where we are in - sg_offset = 0; // the scatter/gather list - } + sg_segment = 0; // for keeping track of where we are in + sg_offset = 0; // the scatter/gather list while (transferred != srb->request_bufflen) { @@ -615,13 +621,12 @@ if (result != USB_STOR_TRANSPORT_GOOD) break; - // Transfer the received data into the srb buffer - + // Store the data (s-g) or update the pointer (!s-g) if (srb->use_sg) - us_copy_to_sgbuf(buffer, len, sg, - &sg_segment, &sg_offset, srb->use_sg); + usb_stor_access_xfer_buf(buffer, len, srb, + &sg_segment, &sg_offset, TO_XFER_BUF); else - memcpy(srb->request_buffer+transferred, buffer, len); + buffer += len; // Update the amount transferred and the sector number @@ -630,7 +635,8 @@ } // while transferred != srb->request_bufflen - kfree(buffer); + if (srb->use_sg) + kfree(buffer); return result; } diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/transport.c Fri Dec 12 15:06:57 2003 @@ -760,6 +760,7 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us) { unsigned int transfer_length = srb->request_bufflen; + unsigned int pipe = 0; int result; /* COMMAND STAGE */ @@ -785,7 +786,7 @@ /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if (transfer_length) { - unsigned int pipe = srb->sc_data_direction == SCSI_DATA_READ ? + pipe = srb->sc_data_direction == SCSI_DATA_READ ? us->recv_bulk_pipe : us->send_bulk_pipe; result = usb_stor_bulk_transfer_sg(us, pipe, srb->request_buffer, transfer_length, @@ -813,12 +814,9 @@ if (srb->cmnd[0] == REQUEST_SENSE || srb->cmnd[0] == INQUIRY) return USB_STOR_TRANSPORT_GOOD; - else { - if (us->iobuf[0]) - return USB_STOR_TRANSPORT_FAILED; - else - return USB_STOR_TRANSPORT_GOOD; - } + if (us->iobuf[0]) + goto Failed; + return USB_STOR_TRANSPORT_GOOD; } /* If not UFI, we interpret the data as a result code @@ -835,13 +833,17 @@ case 0x00: return USB_STOR_TRANSPORT_GOOD; case 0x01: - return USB_STOR_TRANSPORT_FAILED; - default: - return USB_STOR_TRANSPORT_ERROR; + goto Failed; } - - /* we should never get here, but if we do, we're in trouble */ return USB_STOR_TRANSPORT_ERROR; + + /* the CBI spec requires that the bulk pipe must be cleared + * following any data-in/out command failure (section 2.4.3.1.3) + */ + Failed: + if (pipe) + usb_stor_clear_halt(us, pipe); + return USB_STOR_TRANSPORT_FAILED; } /* @@ -924,6 +926,7 @@ struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; unsigned int transfer_length = srb->request_bufflen; + unsigned int residue; int result; int fake_sense = 0; @@ -999,9 +1002,10 @@ return USB_STOR_TRANSPORT_ERROR; /* check bulk status */ - US_DEBUGP("Bulk Status S 0x%x T 0x%x R %d Stat 0x%x\n", + residue = le32_to_cpu(bcs->Residue); + US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", le32_to_cpu(bcs->Signature), bcs->Tag, - bcs->Residue, bcs->Status); + residue, bcs->Status); if ((bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) && bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) || bcs->Tag != srb->serial_number || @@ -1009,6 +1013,11 @@ US_DEBUGP("Bulk logical error\n"); return USB_STOR_TRANSPORT_ERROR; } + + /* try to compute the actual residue, based on how much data + * was really transferred and what the device tells us */ + residue = min(residue, transfer_length); + srb->resid = max(srb->resid, (int) residue); /* based on the status code, we report good or bad */ switch (bcs->Status) { diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h --- a/drivers/usb/storage/unusual_devs.h Fri Dec 12 15:06:58 2003 +++ b/drivers/usb/storage/unusual_devs.h Fri Dec 12 15:06:58 2003 @@ -45,6 +45,13 @@ * */ +/* Patch submitted by Martin Berentsen */ +#define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */ +UNUSUAL_DEV( 0x0686, 0x4014, 0x0001, 0x0001, + "Minolta", + "Dimage S414", + US_SC_SCSI, US_PR_BULK, NULL, US_FL_START_STOP), + UNUSUAL_DEV( 0x03ee, 0x0000, 0x0000, 0x0245, "Mitsumi", "CD-R/RW Drive", @@ -53,7 +60,7 @@ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100, "Mitsumi", "USB FDD", - US_SC_UFI, US_PR_CBI, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200, @@ -102,6 +109,12 @@ "Finecam S4", US_SC_8070, US_PR_CB, NULL, US_FL_FIX_INQUIRY), +/* Patch submitted by Stephane Galles */ +UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100, + "Kyocera", + "Finecam S5", + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + /* Reported by Paul Stewart * This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x04a4, 0x0004, 0x0001, 0x0001, @@ -257,7 +270,7 @@ UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100, "Sony", "Memorystick MSAC-US1", - US_SC_UFI, US_PR_CB, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), /* Submitted by Klaus Mueller */ @@ -298,6 +311,12 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN), +/* Fabrizio Fellini */ +UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210, + "Fujifilm", + "Digital Camera EX-20 DSC", + US_SC_8070, US_PR_CBI, NULL, 0 ), + UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200, "LaCie", "USB Hard Disk", @@ -354,6 +373,13 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Submitted Alexander Oltu */ +UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff, + "", + "USB TO IDE", + US_SC_SCSI, US_PR_BULK, NULL, + US_FL_MODE_XLATE ), + /* Reported by Peter Marks * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly * 36 bytes of data. No more, no less. That is the only reason this entry @@ -400,6 +426,28 @@ "DIMAGE E223", US_SC_SCSI, US_PR_DEVICE, NULL, 0 ), +/* Following three Minolta cameras reported by Martin Pool + * . Originally discovered by Kedar Petankar, + * Matthew Geier, Mikael Lofj"ard, Marcel de Boer. + */ +UNUSUAL_DEV( 0x0686, 0x4006, 0x0001, 0x0001, + "Minolta", + "DiMAGE 7", + US_SC_SCSI, US_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0686, 0x400b, 0x0001, 0x0001, + "Minolta", + "DiMAGE 7i", + US_SC_SCSI, US_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0686, 0x400f, 0x0001, 0x0001, + "Minolta", + "DiMAGE 7Hi", + US_SC_SCSI, US_PR_DEVICE, NULL, + 0 ), + UNUSUAL_DEV( 0x0693, 0x0002, 0x0100, 0x0100, "Hagiwara", "FlashGate SmartMedia", @@ -441,6 +489,11 @@ "Freecom", "USB-IDE", US_SC_QIC, US_PR_FREECOM, freecom_init, 0), + +UNUSUAL_DEV( 0x07ab, 0xfc84, 0x0000, 0x9999, + "Freecom", + "FX-5/FX-50", + US_SC_QIC, US_PR_FREECOM, freecom_init, 0), #endif UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, @@ -528,6 +581,14 @@ US_SC_SCSI, US_PR_DATAFAB, NULL, US_FL_MODE_XLATE ), #endif +#ifdef CONFIG_USB_STORAGE_SDDR55 +/* SM part - aeb */ +UNUSUAL_DEV( 0x07c4, 0xa109, 0x0000, 0xffff, + "Datafab Systems, Inc.", + "USB to CF + SM Combo (LC1)", + US_SC_SCSI, US_PR_SDDR55, NULL, + US_FL_SINGLE_LUN ), +#endif /* Datafab KECF-USB / Sagatek DCS-CF / Simpletech Flashlink UCF-100 * Only revision 1.13 tested (same for all of the above devices, @@ -570,6 +631,14 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), +/*Medion 6047 Digital Camera +Davide Andrian <_nessuno_@katamail.com> +*/ +UNUSUAL_DEV( 0x08ca, 0x2011, 0x0001, 0x0001, + "3MegaCam", + "3MegaCam", + US_SC_DEVICE, US_PR_BULK, NULL, + US_FL_MODE_XLATE ), /* aeb */ UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff, "Feiya", @@ -605,8 +674,8 @@ /* Submitted by Per Winkvist */ UNUSUAL_DEV( 0x0a17, 0x006, 0x1000, 0x9009, "Pentax", - "Optio S", - US_SC_8070, US_PR_CBI, NULL, + "Optio S/S4", + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), #ifdef CONFIG_USB_STORAGE_ISD200 @@ -617,13 +686,6 @@ 0 ), #endif -/* Submitted by Antoine Mairesse */ -UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, - "USB", - "Solid state disk", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - /* Submitted by Joris Struyve */ UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff, "Medion", @@ -640,6 +702,13 @@ "Jenoptik", "JD 5200 z3", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + +/* Submitted by Antoine Mairesse */ +UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, + "USB", + "Solid state disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), /* Reported by Kevin Cernekee * Tested on hardware version 1.10. diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c Fri Dec 12 15:06:57 2003 +++ b/drivers/usb/storage/usb.c Fri Dec 12 15:06:57 2003 @@ -234,16 +234,8 @@ */ void fill_inquiry_response(struct us_data *us, unsigned char *data, - unsigned int data_len) { - - int i; - struct scatterlist *sg; - int len = - us->srb->request_bufflen > data_len ? data_len : - us->srb->request_bufflen; - int transferred; - int amt; - + unsigned int data_len) +{ if (data_len<36) // You lose. return; @@ -270,22 +262,7 @@ data[35] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice) & 0x0F); } - if (us->srb->use_sg) { - sg = (struct scatterlist *)us->srb->request_buffer; - for (i=0; isrb->use_sg; i++) - memset(sg_address(sg[i]), 0, sg[i].length); - for (i=0, transferred=0; - isrb->use_sg && transferred < len; - i++) { - amt = sg[i].length > len-transferred ? - len-transferred : sg[i].length; - memcpy(sg_address(sg[i]), data+transferred, amt); - transferred -= amt; - } - } else { - memset(us->srb->request_buffer, 0, us->srb->request_bufflen); - memcpy(us->srb->request_buffer, data, len); - } + usb_stor_set_xfer_buf(data, data_len, us->srb); } static int usb_stor_control_thread(void * __us) diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h --- a/include/linux/i2c-id.h Fri Dec 12 15:06:57 2003 +++ b/include/linux/i2c-id.h Fri Dec 12 15:06:57 2003 @@ -258,6 +258,7 @@ #define I2C_HW_SMBUS_AMD8111 0x0a #define I2C_HW_SMBUS_SCX200 0x0b #define I2C_HW_SMBUS_NFORCE2 0x0c +#define I2C_HW_SMBUS_W9968CF 0x0d /* --- ISA pseudo-adapter */ #define I2C_HW_ISA 0x00 diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Fri Dec 12 15:06:57 2003 +++ b/include/linux/usb.h Fri Dec 12 15:06:57 2003 @@ -414,9 +414,6 @@ * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set * or your driver's probe function will never get called. * @driver: the driver model core driver structure. - * @serialize: a semaphore used to serialize access to this driver. Used - * in the probe and disconnect functions. Only the USB core should use - * this lock. * * USB drivers must provide a name, probe() and disconnect() methods, * and an id_table. Other driver fields are optional. @@ -451,8 +448,6 @@ const struct usb_device_id *id_table; struct device_driver driver; - - struct semaphore serialize; }; #define to_usb_driver(d) container_of(d, struct usb_driver, driver) diff -Nru a/include/linux/videodev.h b/include/linux/videodev.h --- a/include/linux/videodev.h Fri Dec 12 15:06:58 2003 +++ b/include/linux/videodev.h Fri Dec 12 15:06:58 2003 @@ -429,6 +429,7 @@ #define VID_HARDWARE_CPIA2 33 #define VID_HARDWARE_VICAM 34 #define VID_HARDWARE_SF16FMR2 35 +#define VID_HARDWARE_W9968CF 36 /* W996[87]CF JPEG USB Dual Mode Cam */ #endif /* __LINUX_VIDEODEV_H */ /*