diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-02-07 10:28:34 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-02-07 10:28:34 -0800 |
commit | a32458d8b2a0c3d079c986913b2ea072f29540f7 (patch) | |
tree | 2585e6155c897ba1d5418cfa23c022b9cde2f018 /usb | |
parent | 336a62afd24e907ce13f10f59c2524f61d06e9c4 (diff) | |
download | patches-a32458d8b2a0c3d079c986913b2ea072f29540f7.tar.gz |
more usb patches added
Diffstat (limited to 'usb')
-rw-r--r-- | usb/uhci-hcd-fix-mistaken-usage-of-list_prepare_entry.patch | 29 | ||||
-rw-r--r-- | usb/usb-add-zc0301-video4linux2-driver.patch | 3023 | ||||
-rw-r--r-- | usb/usb-gotemp.patch | 2 | ||||
-rw-r--r-- | usb/usb-pegasus-linksys-usbvpn1-support-cleanup.patch | 104 | ||||
-rw-r--r-- | usb/usbhid-add-error-handling.patch | 298 |
5 files changed, 3455 insertions, 1 deletions
diff --git a/usb/uhci-hcd-fix-mistaken-usage-of-list_prepare_entry.patch b/usb/uhci-hcd-fix-mistaken-usage-of-list_prepare_entry.patch new file mode 100644 index 0000000000000..e387d350f101e --- /dev/null +++ b/usb/uhci-hcd-fix-mistaken-usage-of-list_prepare_entry.patch @@ -0,0 +1,29 @@ +From stern@rowland.harvard.edu Tue Jan 31 07:03:01 2006 +Date: Tue, 31 Jan 2006 10:02:55 -0500 (EST) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com>, Andrew Morton <akpm@osdl.org> +cc: Arnaud Patard <arnaud.patard@rtp-net.org> +Subject: [PATCH] uhci-hcd: fix mistaken usage of list_prepare_entry +Message-ID: <Pine.LNX.4.44L0.0601310958570.5380-100000@iolanthe.rowland.org> + +A recent update to the uhci-hcd driver invoked the list_prepare_entry +macro incorrectly. This patch (as646) corrects it. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/uhci-q.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- gregkh-2.6.orig/drivers/usb/host/uhci-q.c ++++ gregkh-2.6/drivers/usb/host/uhci-q.c +@@ -259,7 +259,7 @@ static void uhci_fixup_toggles(struct uh + /* Fix up the toggle for the URBs in the queue. Normally this + * loop won't run more than once: When an error or short transfer + * occurs, the queue usually gets emptied. */ +- list_prepare_entry(urbp, &qh->queue, node); ++ urbp = list_prepare_entry(urbp, &qh->queue, node); + list_for_each_entry_continue(urbp, &qh->queue, node) { + + /* If the first TD has the right toggle value, we don't diff --git a/usb/usb-add-zc0301-video4linux2-driver.patch b/usb/usb-add-zc0301-video4linux2-driver.patch new file mode 100644 index 0000000000000..2771a3dcb0c9e --- /dev/null +++ b/usb/usb-add-zc0301-video4linux2-driver.patch @@ -0,0 +1,3023 @@ +From luca.risolia@studio.unibo.it Mon Feb 6 07:28:12 2006 +Date: Mon, 6 Feb 2006 16:29:35 +0000 +From: Luca Risolia <luca.risolia@studio.unibo.it> +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Mauro Carvalho <mchehab@brturbo.com.br> +Subject: USB: Add ZC0301 Video4Linux2 driver +Message-ID: <20060206162935.GA10356@studio.unibo.it> +Content-Disposition: inline + +This patch adds a Video4Linux2 driver for ZC0301 +Image Processor and Control Chip. + +Signed-off-by: Luca Risolia <luca.risolia@studio.unibo.it> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/usb/zc0301.txt | 250 ++++ + MAINTAINERS | 8 + drivers/usb/Makefile | 1 + drivers/usb/media/Kconfig | 15 + drivers/usb/media/Makefile | 2 + drivers/usb/media/zc0301.h | 185 +++ + drivers/usb/media/zc0301_core.c | 2028 +++++++++++++++++++++++++++++++++++ + drivers/usb/media/zc0301_pas202bcb.c | 353 ++++++ + drivers/usb/media/zc0301_sensor.h | 98 + + 9 files changed, 2940 insertions(+) + +--- /dev/null ++++ gregkh-2.6/Documentation/usb/zc0301.txt +@@ -0,0 +1,250 @@ ++ ++ ZC0301 Image Processor and Control Chip ++ Driver for Linux ++ ======================================= ++ ++ - Documentation - ++ ++ ++Index ++===== ++1. Copyright ++2. Disclaimer ++3. License ++4. Overview and features ++5. Module dependencies ++6. Module loading ++7. Module parameters ++8. Supported devices ++9. Notes for V4L2 application developers ++10. Contact information ++11. Credits ++ ++ ++1. Copyright ++============ ++Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> ++ ++ ++2. Disclaimer ++============= ++This software is not developed or sponsored by Z-Star Microelectronics Corp. ++Trademarks are property of their respective owner. ++ ++ ++3. 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. ++ ++ ++4. Overview and features ++======================== ++This driver supports the video interface of the devices mounting the ZC0301 ++Image Processor and Control Chip. ++ ++The driver relies on the Video4Linux2 and USB core modules. It has been ++designed to run properly on SMP systems as well. ++ ++The latest version of the ZC0301 driver can be found at the following URL: ++http://www.linux-projects.org/ ++ ++Some of the features of the driver are: ++ ++- full compliance with the Video4Linux2 API (see also "Notes for V4L2 ++ application developers" paragraph); ++- available mmap or read/poll methods for video streaming through isochronous ++ data transfers; ++- automatic detection of image sensor; ++- video formats is standard JPEG in various compression qualities ++ (see also "Notes for V4L2 application developers" paragraph); ++- full support for the capabilities of every possible image sensors that can ++ be connected to the ZC0301 bridges, including, for istance, red, green, ++ blue and global gain adjustments and exposure control (see "Supported ++ devices" paragraph for details); ++- use of default color settings for sunlight conditions; ++- dynamic driver control thanks to various module parameters (see "Module ++ parameters" paragraph); ++- up to 64 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 ++ the system supports hotplugging; ++ ++ ++5. Module dependencies ++====================== ++For it to work properly, the driver needs kernel support for Video4Linux and ++USB. ++ ++The following options of the kernel configuration file must be enabled and ++corresponding modules must be compiled: ++ ++ # Multimedia devices ++ # ++ CONFIG_VIDEO_DEV=m ++ ++ # USB support ++ # ++ CONFIG_USB=m ++ ++In addition, depending on the hardware being used, the modules below are ++necessary: ++ ++ # USB Host Controller Drivers ++ # ++ CONFIG_USB_EHCI_HCD=m ++ CONFIG_USB_UHCI_HCD=m ++ CONFIG_USB_OHCI_HCD=m ++ ++The ZC0301 controller also provides a built-in microphone interface. It is ++supported by the USB Audio driver thanks to the ALSA API: ++ ++ # Sound ++ # ++ CONFIG_SOUND=y ++ ++ # Advanced Linux Sound Architecture ++ # ++ CONFIG_SND=m ++ ++ # USB devices ++ # ++ CONFIG_SND_USB_AUDIO=m ++ ++And finally: ++ ++ # USB Multimedia devices ++ # ++ CONFIG_USB_ZC0301=m ++ ++ ++6. Module loading ++================= ++To use the driver, it is necessary to load the "zc0301" module into memory ++after every other module required: "videodev", "usbcore" and, depending on ++the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd". ++ ++Loading can be done as shown below: ++ ++ [root@localhost home]# modprobe zc0301 ++ ++At this point the devices should be recognized. You can invoke "dmesg" to ++analyze kernel messages and verify that the loading process has gone well: ++ ++ [user@localhost home]$ dmesg ++ ++ ++7. Module parameters ++==================== ++Module parameters are listed below: ++------------------------------------------------------------------------------- ++Name: video_nr ++Type: short array (min = 0, max = 64) ++Syntax: <-1|n[,...]> ++Description: Specify V4L2 minor mode number: ++ -1 = use next available ++ n = use minor number n ++ You can specify up to 64 cameras this way. ++ For example: ++ video_nr=-1,2,-1 would assign minor number 2 to the second ++ registered camera and use auto for the first one and for every ++ other camera. ++Default: -1 ++------------------------------------------------------------------------------- ++Name: force_munmap ++Type: bool array (min = 0, max = 64) ++Syntax: <0|1[,...]> ++Description: Force the application to unmap previously mapped buffer memory ++ before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not ++ all the applications support this feature. This parameter is ++ specific for each detected camera. ++ 0 = do not force memory unmapping ++ 1 = force memory unmapping (save memory) ++Default: 0 ++------------------------------------------------------------------------------- ++Name: debug ++Type: ushort ++Syntax: <n> ++Description: Debugging information level, from 0 to 3: ++ 0 = none (use carefully) ++ 1 = critical errors ++ 2 = significant informations ++ 3 = more verbose messages ++ Level 3 is useful for testing only, when only one device ++ is used at the same time. It also shows some more informations ++ about the hardware being detected. This module parameter can be ++ changed at runtime thanks to the /sys filesystem interface. ++Default: 2 ++------------------------------------------------------------------------------- ++ ++ ++8. Supported devices ++==================== ++None of the names of the companies as well as their products will be mentioned ++here. They have never collaborated with the author, so no advertising. ++ ++From the point of view of a driver, what unambiguously identify a device are ++its vendor and product USB identifiers. Below is a list of known identifiers of ++devices mounting the ZC0301 Image Processor and Control Chips: ++ ++Vendor ID Product ID ++--------- ---------- ++0x046d 0x08ae ++ ++The following image sensors are supported: ++ ++Model Manufacturer ++----- ------------ ++PAS202BCB PixArt Imaging, Inc. ++ ++All the available control settings of each image sensor are supported through ++the V4L2 interface. ++ ++ ++9. Notes for V4L2 application developers ++======================================== ++This driver follows the V4L2 API specifications. In particular, it enforces two ++rules: ++ ++- exactly one I/O method, either "mmap" or "read", is associated with each ++file descriptor. Once it is selected, the application must close and reopen the ++device to switch to the other I/O method; ++ ++- although it is not mandatory, previously mapped buffer memory should always ++be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. ++The same number of buffers as before will be allocated again to match the size ++of the new video frames, so you have to map the buffers again before any I/O ++attempts on them. ++ ++This driver supports the standard JPEG video format. The current compression ++quality may vary from 0 to 3 and can be selected or queried thanks to the ++VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. ++ ++ ++10. Contact information ++======================= ++The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>. ++ ++GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is ++'FCE635A4'; the public 1024-bit key should be available at any keyserver; ++the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. ++ ++ ++11. Credits ++=========== ++- Informations about the chip internals to enable the I2C protocol have been ++ taken from the documentation of the ZC030x Video4Linux1 driver written by ++ Andrew Birkett <andy@nobugs.org>; ++- Initialization values of the ZC0301 connected to the PAS202BCB image sensor ++ have been taken from the SPCA5XX driver maintained by ++ Michel Xhaard <mxhaard@magic.fr> +--- gregkh-2.6.orig/drivers/usb/Makefile ++++ gregkh-2.6/drivers/usb/Makefile +@@ -48,6 +48,7 @@ obj-$(CONFIG_USB_SN9C102) += media/ + obj-$(CONFIG_USB_STV680) += media/ + obj-$(CONFIG_USB_VICAM) += media/ + obj-$(CONFIG_USB_W9968CF) += media/ ++obj-$(CONFIG_USB_ZC0301) += media/ + + obj-$(CONFIG_USB_CATC) += net/ + obj-$(CONFIG_USB_KAWETH) += net/ +--- gregkh-2.6.orig/drivers/usb/media/Kconfig ++++ gregkh-2.6/drivers/usb/media/Kconfig +@@ -191,6 +191,21 @@ config USB_W9968CF + To compile this driver as a module, choose M here: the + module will be called w9968cf. + ++config USB_ZC0301 ++ tristate "USB ZC0301 Image Processor and Control Chip support" ++ depends on USB && VIDEO_DEV ++ ---help--- ++ Say Y here if you want support for cameras based on the ZC0301 ++ Image Processor and Control Chip. ++ ++ See <file:Documentation/usb/zc0301.txt> for more informations. ++ ++ This driver uses the Video For Linux API. You must say Y or M to ++ "Video For Linux" to use this driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called zc0301. ++ + config USB_PWC + tristate "USB Philips Cameras" + depends on USB && VIDEO_DEV +--- gregkh-2.6.orig/drivers/usb/media/Makefile ++++ gregkh-2.6/drivers/usb/media/Makefile +@@ -4,6 +4,7 @@ + + sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o + et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o ++zc0301-objs := zc0301_core.o zc0301_pas202bcb.o + + obj-$(CONFIG_USB_DABUSB) += dabusb.o + obj-$(CONFIG_USB_DSBR) += dsbr100.o +@@ -16,4 +17,5 @@ obj-$(CONFIG_USB_SN9C102) += sn9c102.o + obj-$(CONFIG_USB_STV680) += stv680.o + obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o + obj-$(CONFIG_USB_W9968CF) += w9968cf.o ++obj-$(CONFIG_USB_ZC0301) += zc0301.o + obj-$(CONFIG_USB_PWC) += pwc/ +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/zc0301_core.c +@@ -0,0 +1,2028 @@ ++/*************************************************************************** ++ * Video4Linux2 driver for ZC0301 Image Processor and Control Chip * ++ * * ++ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * ++ * * ++ * Informations about the chip internals to enable the I2C protocol have * ++ * been taken from the documentation of the ZC030x Video4Linux1 driver * ++ * written by Andrew Birkett <andy@nobugs.org> * ++ * * ++ * 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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/param.h> ++#include <linux/moduleparam.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/string.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/delay.h> ++#include <linux/stddef.h> ++#include <linux/compiler.h> ++#include <linux/ioctl.h> ++#include <linux/poll.h> ++#include <linux/stat.h> ++#include <linux/mm.h> ++#include <linux/vmalloc.h> ++#include <linux/page-flags.h> ++#include <linux/byteorder/generic.h> ++#include <asm/page.h> ++#include <asm/uaccess.h> ++ ++#include "zc0301.h" ++ ++/*****************************************************************************/ ++ ++#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ ++ "Image Processor and Control Chip" ++#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" ++#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" ++#define ZC0301_MODULE_LICENSE "GPL" ++#define ZC0301_MODULE_VERSION "1:1.00" ++#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 0) ++ ++/*****************************************************************************/ ++ ++MODULE_DEVICE_TABLE(usb, zc0301_id_table); ++ ++MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL); ++MODULE_DESCRIPTION(ZC0301_MODULE_NAME); ++MODULE_VERSION(ZC0301_MODULE_VERSION); ++MODULE_LICENSE(ZC0301_MODULE_LICENSE); ++ ++static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; ++module_param_array(video_nr, short, NULL, 0444); ++MODULE_PARM_DESC(video_nr, ++ "\n<-1|n[,...]> Specify V4L2 minor mode number." ++ "\n -1 = use next available (default)" ++ "\n n = use minor number n (integer >= 0)" ++ "\nYou can specify up to " ++ __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." ++ "\nFor example:" ++ "\nvideo_nr=-1,2,-1 would assign minor number 2 to" ++ "\nthe second registered camera and use auto for the first" ++ "\none and for every other camera." ++ "\n"); ++ ++static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = ++ ZC0301_FORCE_MUNMAP}; ++module_param_array(force_munmap, bool, NULL, 0444); ++MODULE_PARM_DESC(force_munmap, ++ "\n<0|1[,...]> Force the application to unmap previously" ++ "\nmapped buffer memory before calling any VIDIOC_S_CROP or" ++ "\nVIDIOC_S_FMT ioctl's. Not all the applications support" ++ "\nthis feature. This parameter is specific for each" ++ "\ndetected camera." ++ "\n 0 = do not force memory unmapping" ++ "\n 1 = force memory unmapping (save memory)" ++ "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." ++ "\n"); ++ ++#ifdef ZC0301_DEBUG ++static unsigned short debug = ZC0301_DEBUG_LEVEL; ++module_param(debug, ushort, 0644); ++MODULE_PARM_DESC(debug, ++ "\n<n> Debugging information level, from 0 to 3:" ++ "\n0 = none (use carefully)" ++ "\n1 = critical errors" ++ "\n2 = significant informations" ++ "\n3 = more verbose messages" ++ "\nLevel 3 is useful for testing only, when only " ++ "one device is used." ++ "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." ++ "\n"); ++#endif ++ ++/*****************************************************************************/ ++ ++static u32 ++zc0301_request_buffers(struct zc0301_device* cam, u32 count, ++ enum zc0301_io_method io) ++{ ++ struct v4l2_pix_format* p = &(cam->sensor->pix_format); ++ struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); ++ const size_t imagesize = cam->module_param.force_munmap || ++ io == IO_READ ? ++ (p->width * p->height * p->priv) / 8 : ++ (r->width * r->height * p->priv) / 8; ++ void* buff = NULL; ++ u32 i; ++ ++ if (count > ZC0301_MAX_FRAMES) ++ count = ZC0301_MAX_FRAMES; ++ ++ cam->nbuffers = count; ++ while (cam->nbuffers > 0) { ++ if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) ++ break; ++ cam->nbuffers--; ++ } ++ ++ for (i = 0; i < cam->nbuffers; i++) { ++ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); ++ cam->frame[i].buf.index = i; ++ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); ++ cam->frame[i].buf.length = imagesize; ++ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cam->frame[i].buf.sequence = 0; ++ cam->frame[i].buf.field = V4L2_FIELD_NONE; ++ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; ++ cam->frame[i].buf.flags = 0; ++ } ++ ++ return cam->nbuffers; ++} ++ ++ ++static void zc0301_release_buffers(struct zc0301_device* cam) ++{ ++ if (cam->nbuffers) { ++ vfree(cam->frame[0].bufmem); ++ cam->nbuffers = 0; ++ } ++ cam->frame_current = NULL; ++} ++ ++ ++static void zc0301_empty_framequeues(struct zc0301_device* cam) ++{ ++ u32 i; ++ ++ INIT_LIST_HEAD(&cam->inqueue); ++ INIT_LIST_HEAD(&cam->outqueue); ++ ++ for (i = 0; i < ZC0301_MAX_FRAMES; i++) { ++ cam->frame[i].state = F_UNUSED; ++ cam->frame[i].buf.bytesused = 0; ++ } ++} ++ ++ ++static void zc0301_requeue_outqueue(struct zc0301_device* cam) ++{ ++ struct zc0301_frame_t *i; ++ ++ list_for_each_entry(i, &cam->outqueue, frame) { ++ i->state = F_QUEUED; ++ list_add(&i->frame, &cam->inqueue); ++ } ++ ++ INIT_LIST_HEAD(&cam->outqueue); ++} ++ ++ ++static void zc0301_queue_unusedframes(struct zc0301_device* cam) ++{ ++ unsigned long lock_flags; ++ u32 i; ++ ++ for (i = 0; i < cam->nbuffers; i++) ++ if (cam->frame[i].state == F_UNUSED) { ++ cam->frame[i].state = F_QUEUED; ++ spin_lock_irqsave(&cam->queue_lock, lock_flags); ++ list_add_tail(&cam->frame[i].frame, &cam->inqueue); ++ spin_unlock_irqrestore(&cam->queue_lock, lock_flags); ++ } ++} ++ ++/*****************************************************************************/ ++ ++int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) ++{ ++ struct usb_device* udev = cam->usbdev; ++ int res; ++ ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, ++ value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); ++ if (res < 0) { ++ DBG(3, "Failed to write a register (index 0x%04X, " ++ "value 0x%02X, error %d)",index, value, res); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++int zc0301_read_reg(struct zc0301_device* cam, u16 index) ++{ ++ struct usb_device* udev = cam->usbdev; ++ u8* buff = cam->control_buffer; ++ int res; ++ ++ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, ++ 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); ++ if (res < 0) ++ DBG(3, "Failed to read a register (index 0x%04X, error %d)", ++ index, res); ++ ++ PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); ++ ++ return (res >= 0) ? (int)(*buff) : -1; ++} ++ ++ ++int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length) ++{ ++ int err = 0, res, r0, r1; ++ ++ err += zc0301_write_reg(cam, 0x0092, address); ++ err += zc0301_write_reg(cam, 0x0090, 0x02); ++ ++ msleep(1); ++ ++ res = zc0301_read_reg(cam, 0x0091); ++ if (res < 0) ++ err += res; ++ r0 = zc0301_read_reg(cam, 0x0095); ++ if (r0 < 0) ++ err += r0; ++ r1 = zc0301_read_reg(cam, 0x0096); ++ if (r1 < 0) ++ err += r1; ++ ++ res = (length <= 1) ? r0 : r0 | (r1 << 8); ++ ++ if (err) ++ DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", ++ address, res); ++ ++ ++ PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); ++ ++ return err ? -1 : res; ++} ++ ++ ++int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value) ++{ ++ int err = 0, res; ++ ++ err += zc0301_write_reg(cam, 0x0092, address); ++ err += zc0301_write_reg(cam, 0x0093, value & 0xff); ++ err += zc0301_write_reg(cam, 0x0094, value >> 8); ++ err += zc0301_write_reg(cam, 0x0090, 0x01); ++ ++ msleep(1); ++ ++ res = zc0301_read_reg(cam, 0x0091); ++ if (res < 0) ++ err += res; ++ ++ if (err) ++ DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", ++ address, value); ++ ++ PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); ++ ++ return err ? -1 : 0; ++} ++ ++/*****************************************************************************/ ++ ++static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) ++{ ++ struct zc0301_device* cam = urb->context; ++ struct zc0301_frame_t** f; ++ size_t imagesize; ++ u8 i; ++ int err = 0; ++ ++ if (urb->status == -ENOENT) ++ return; ++ ++ f = &cam->frame_current; ++ ++ if (cam->stream == STREAM_INTERRUPT) { ++ cam->stream = STREAM_OFF; ++ if ((*f)) ++ (*f)->state = F_QUEUED; ++ DBG(3, "Stream interrupted"); ++ wake_up_interruptible(&cam->wait_stream); ++ } ++ ++ if (cam->state & DEV_DISCONNECTED) ++ return; ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ wake_up_interruptible(&cam->wait_frame); ++ return; ++ } ++ ++ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) ++ goto resubmit_urb; ++ ++ if (!(*f)) ++ (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, ++ frame); ++ ++ imagesize = (cam->sensor->pix_format.width * ++ cam->sensor->pix_format.height * ++ cam->sensor->pix_format.priv) / 8; ++ ++ for (i = 0; i < urb->number_of_packets; i++) { ++ unsigned int len, status; ++ void *pos; ++ u16* soi; ++ u8 sof; ++ ++ 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) { ++ DBG(3, "Error in isochronous frame"); ++ (*f)->state = F_ERROR; ++ continue; ++ } ++ ++ sof = (*(soi = pos) == 0xd8ff); ++ ++ PDBGG("Isochrnous frame: length %u, #%u i,", len, i); ++ ++ if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) ++start_of_frame: ++ if (sof) { ++ (*f)->state = F_GRABBING; ++ (*f)->buf.bytesused = 0; ++ do_gettimeofday(&(*f)->buf.timestamp); ++ DBG(3, "SOF detected: new video frame"); ++ } ++ ++ if ((*f)->state == F_GRABBING) { ++ if (sof && (*f)->buf.bytesused) ++ goto end_of_frame; ++ ++ if ((*f)->buf.bytesused + len > imagesize) { ++ DBG(3, "Video frame size exceeded"); ++ (*f)->state = F_ERROR; ++ continue; ++ } ++ ++ memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); ++ (*f)->buf.bytesused += len; ++ ++ if ((*f)->buf.bytesused == imagesize) { ++ u32 b; ++end_of_frame: ++ b = (*f)->buf.bytesused; ++ (*f)->state = F_DONE; ++ (*f)->buf.sequence= ++cam->frame_count; ++ spin_lock(&cam->queue_lock); ++ list_move_tail(&(*f)->frame, &cam->outqueue); ++ if (!list_empty(&cam->inqueue)) ++ (*f) = list_entry(cam->inqueue.next, ++ struct zc0301_frame_t, ++ frame); ++ else ++ (*f) = NULL; ++ spin_unlock(&cam->queue_lock); ++ DBG(3, "Video frame captured: : %lu bytes", ++ (unsigned long)(b)); ++ ++ if (!(*f)) ++ goto resubmit_urb; ++ ++ if (sof) ++ goto start_of_frame; ++ } ++ } ++ } ++ ++resubmit_urb: ++ urb->dev = cam->usbdev; ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err < 0 && err != -EPERM) { ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "usb_submit_urb() failed"); ++ } ++ ++ wake_up_interruptible(&cam->wait_frame); ++} ++ ++ ++static int zc0301_start_transfer(struct zc0301_device* cam) ++{ ++ struct usb_device *udev = cam->usbdev; ++ struct urb* urb; ++ const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, ++ 512, 768, 1023}; ++ const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; ++ s8 i, j; ++ int err = 0; ++ ++ for (i = 0; i < ZC0301_URBS; i++) { ++ cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, ++ GFP_KERNEL); ++ if (!cam->transfer_buffer[i]) { ++ err = -ENOMEM; ++ DBG(1, "Not enough memory"); ++ goto free_buffers; ++ } ++ } ++ ++ for (i = 0; i < ZC0301_URBS; i++) { ++ urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); ++ cam->urb[i] = urb; ++ if (!urb) { ++ err = -ENOMEM; ++ DBG(1, "usb_alloc_urb() failed"); ++ goto free_urbs; ++ } ++ urb->dev = udev; ++ urb->context = cam; ++ urb->pipe = usb_rcvisocpipe(udev, 1); ++ urb->transfer_flags = URB_ISO_ASAP; ++ urb->number_of_packets = ZC0301_ISO_PACKETS; ++ urb->complete = zc0301_urb_complete; ++ urb->transfer_buffer = cam->transfer_buffer[i]; ++ urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; ++ urb->interval = 1; ++ for (j = 0; j < ZC0301_ISO_PACKETS; j++) { ++ urb->iso_frame_desc[j].offset = psz * j; ++ urb->iso_frame_desc[j].length = psz; ++ } ++ } ++ ++ err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); ++ if (err) { ++ DBG(1, "usb_set_interface() failed"); ++ goto free_urbs; ++ } ++ ++ cam->frame_current = NULL; ++ ++ for (i = 0; i < ZC0301_URBS; i++) { ++ err = usb_submit_urb(cam->urb[i], GFP_KERNEL); ++ if (err) { ++ for (j = i-1; j >= 0; j--) ++ usb_kill_urb(cam->urb[j]); ++ DBG(1, "usb_submit_urb() failed, error %d", err); ++ goto free_urbs; ++ } ++ } ++ ++ return 0; ++ ++free_urbs: ++ for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) ++ usb_free_urb(cam->urb[i]); ++ ++free_buffers: ++ for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) ++ kfree(cam->transfer_buffer[i]); ++ ++ return err; ++} ++ ++ ++static int zc0301_stop_transfer(struct zc0301_device* cam) ++{ ++ struct usb_device *udev = cam->usbdev; ++ s8 i; ++ int err = 0; ++ ++ if (cam->state & DEV_DISCONNECTED) ++ return 0; ++ ++ for (i = ZC0301_URBS-1; i >= 0; i--) { ++ usb_kill_urb(cam->urb[i]); ++ usb_free_urb(cam->urb[i]); ++ kfree(cam->transfer_buffer[i]); ++ } ++ ++ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ ++ if (err) ++ DBG(3, "usb_set_interface() failed"); ++ ++ return err; ++} ++ ++ ++static int zc0301_stream_interrupt(struct zc0301_device* cam) ++{ ++ long timeout; ++ ++ cam->stream = STREAM_INTERRUPT; ++ timeout = wait_event_timeout(cam->wait_stream, ++ (cam->stream == STREAM_OFF) || ++ (cam->state & DEV_DISCONNECTED), ++ ZC0301_URB_TIMEOUT); ++ if (cam->state & DEV_DISCONNECTED) ++ return -ENODEV; ++ else if (!timeout) { ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "URB timeout reached. The camera is misconfigured. To " ++ "use it, close and open /dev/video%d again.", ++ cam->v4ldev->minor); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++static int ++zc0301_set_compression(struct zc0301_device* cam, ++ struct v4l2_jpegcompression* compression) ++{ ++ int r, err = 0; ++ ++ if ((r = zc0301_read_reg(cam, 0x0008)) < 0) ++ err += r; ++ err += zc0301_write_reg(cam, 0x0008, ++ r | 0x11 | (compression->quality >> 1)); ++ ++ return err ? -EIO : 0; ++} ++ ++ ++static int zc0301_init(struct zc0301_device* cam) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_control ctrl; ++ struct v4l2_queryctrl *qctrl; ++ struct v4l2_rect* rect; ++ u8 i = 0; ++ int err = 0; ++ ++ if (!(cam->state & DEV_INITIALIZED)) { ++ init_waitqueue_head(&cam->open); ++ qctrl = s->qctrl; ++ rect = &(s->cropcap.defrect); ++ cam->compression.quality = ZC0301_COMPRESSION_QUALITY; ++ } else { /* use current values */ ++ qctrl = s->_qctrl; ++ rect = &(s->_rect); ++ } ++ ++ if (s->init) { ++ err = s->init(cam); ++ if (err) { ++ DBG(3, "Sensor initialization failed"); ++ return err; ++ } ++ } ++ ++ if ((err = zc0301_set_compression(cam, &cam->compression))) { ++ DBG(3, "set_compression() failed"); ++ return err; ++ } ++ ++ if (s->set_crop) ++ if ((err = s->set_crop(cam, rect))) { ++ DBG(3, "set_crop() failed"); ++ return err; ++ } ++ ++ if (s->set_ctrl) { ++ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) ++ if (s->qctrl[i].id != 0 && ++ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { ++ ctrl.id = s->qctrl[i].id; ++ ctrl.value = qctrl[i].default_value; ++ err = s->set_ctrl(cam, &ctrl); ++ if (err) { ++ DBG(3, "Set %s control failed", ++ s->qctrl[i].name); ++ return err; ++ } ++ DBG(3, "Image sensor supports '%s' control", ++ s->qctrl[i].name); ++ } ++ } ++ ++ if (!(cam->state & DEV_INITIALIZED)) { ++ mutex_init(&cam->fileop_mutex); ++ spin_lock_init(&cam->queue_lock); ++ init_waitqueue_head(&cam->wait_frame); ++ init_waitqueue_head(&cam->wait_stream); ++ cam->nreadbuffers = 2; ++ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); ++ memcpy(&(s->_rect), &(s->cropcap.defrect), ++ sizeof(struct v4l2_rect)); ++ cam->state |= DEV_INITIALIZED; ++ } ++ ++ DBG(2, "Initialization succeeded"); ++ return 0; ++} ++ ++ ++static void zc0301_release_resources(struct zc0301_device* cam) ++{ ++ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); ++ video_set_drvdata(cam->v4ldev, NULL); ++ video_unregister_device(cam->v4ldev); ++ kfree(cam->control_buffer); ++} ++ ++/*****************************************************************************/ ++ ++static int zc0301_open(struct inode* inode, struct file* filp) ++{ ++ struct zc0301_device* cam; ++ int err = 0; ++ ++ /* ++ This is the only safe way to prevent race conditions with ++ disconnect ++ */ ++ if (!down_read_trylock(&zc0301_disconnect)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(video_devdata(filp)); ++ ++ if (mutex_lock_interruptible(&cam->dev_mutex)) { ++ up_read(&zc0301_disconnect); ++ return -ERESTARTSYS; ++ } ++ ++ if (cam->users) { ++ DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); ++ if ((filp->f_flags & O_NONBLOCK) || ++ (filp->f_flags & O_NDELAY)) { ++ err = -EWOULDBLOCK; ++ goto out; ++ } ++ mutex_unlock(&cam->dev_mutex); ++ err = wait_event_interruptible_exclusive(cam->open, ++ cam->state & DEV_DISCONNECTED ++ || !cam->users); ++ if (err) { ++ up_read(&zc0301_disconnect); ++ return err; ++ } ++ if (cam->state & DEV_DISCONNECTED) { ++ up_read(&zc0301_disconnect); ++ return -ENODEV; ++ } ++ mutex_lock(&cam->dev_mutex); ++ } ++ ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ err = zc0301_init(cam); ++ if (err) { ++ DBG(1, "Initialization failed again. " ++ "I will retry on next open()."); ++ goto out; ++ } ++ cam->state &= ~DEV_MISCONFIGURED; ++ } ++ ++ if ((err = zc0301_start_transfer(cam))) ++ goto out; ++ ++ filp->private_data = cam; ++ cam->users++; ++ cam->io = IO_NONE; ++ cam->stream = STREAM_OFF; ++ cam->nbuffers = 0; ++ cam->frame_count = 0; ++ zc0301_empty_framequeues(cam); ++ ++ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); ++ ++out: ++ mutex_unlock(&cam->dev_mutex); ++ up_read(&zc0301_disconnect); ++ return err; ++} ++ ++ ++static int zc0301_release(struct inode* inode, struct file* filp) ++{ ++ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); ++ ++ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ ++ ++ zc0301_stop_transfer(cam); ++ ++ zc0301_release_buffers(cam); ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ zc0301_release_resources(cam); ++ mutex_unlock(&cam->dev_mutex); ++ kfree(cam); ++ return 0; ++ } ++ ++ cam->users--; ++ wake_up_interruptible_nr(&cam->open, 1); ++ ++ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); ++ ++ mutex_unlock(&cam->dev_mutex); ++ ++ return 0; ++} ++ ++ ++static ssize_t ++zc0301_read(struct file* filp, char __user * buf, ++ size_t count, loff_t* f_pos) ++{ ++ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); ++ struct zc0301_frame_t* f, * i; ++ unsigned long lock_flags; ++ long timeout; ++ int err = 0; ++ ++ if (mutex_lock_interruptible(&cam->fileop_mutex)) ++ return -ERESTARTSYS; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ mutex_unlock(&cam->fileop_mutex); ++ return -ENODEV; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ mutex_unlock(&cam->fileop_mutex); ++ return -EIO; ++ } ++ ++ if (cam->io == IO_MMAP) { ++ DBG(3, "Close and open the device again to choose the read " ++ "method"); ++ mutex_unlock(&cam->fileop_mutex); ++ return -EINVAL; ++ } ++ ++ if (cam->io == IO_NONE) { ++ if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { ++ DBG(1, "read() failed, not enough memory"); ++ mutex_unlock(&cam->fileop_mutex); ++ return -ENOMEM; ++ } ++ cam->io = IO_READ; ++ cam->stream = STREAM_ON; ++ } ++ ++ if (list_empty(&cam->inqueue)) { ++ if (!list_empty(&cam->outqueue)) ++ zc0301_empty_framequeues(cam); ++ zc0301_queue_unusedframes(cam); ++ } ++ ++ if (!count) { ++ mutex_unlock(&cam->fileop_mutex); ++ return 0; ++ } ++ ++ if (list_empty(&cam->outqueue)) { ++ if (filp->f_flags & O_NONBLOCK) { ++ mutex_unlock(&cam->fileop_mutex); ++ return -EAGAIN; ++ } ++ timeout = wait_event_interruptible_timeout ++ ( cam->wait_frame, ++ (!list_empty(&cam->outqueue)) || ++ (cam->state & DEV_DISCONNECTED) || ++ (cam->state & DEV_MISCONFIGURED), ++ ZC0301_FRAME_TIMEOUT ); ++ if (timeout < 0) { ++ mutex_unlock(&cam->fileop_mutex); ++ return timeout; ++ } ++ if (cam->state & DEV_DISCONNECTED) { ++ mutex_unlock(&cam->fileop_mutex); ++ return -ENODEV; ++ } ++ if (!timeout || (cam->state & DEV_MISCONFIGURED)) { ++ mutex_unlock(&cam->fileop_mutex); ++ return -EIO; ++ } ++ } ++ ++ f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame); ++ ++ if (count > f->buf.bytesused) ++ count = f->buf.bytesused; ++ ++ if (copy_to_user(buf, f->bufmem, count)) { ++ err = -EFAULT; ++ goto exit; ++ } ++ *f_pos += count; ++ ++exit: ++ spin_lock_irqsave(&cam->queue_lock, lock_flags); ++ list_for_each_entry(i, &cam->outqueue, frame) ++ i->state = F_UNUSED; ++ INIT_LIST_HEAD(&cam->outqueue); ++ spin_unlock_irqrestore(&cam->queue_lock, lock_flags); ++ ++ zc0301_queue_unusedframes(cam); ++ ++ PDBGG("Frame #%lu, bytes read: %zu", ++ (unsigned long)f->buf.index, count); ++ ++ mutex_unlock(&cam->fileop_mutex); ++ ++ return err ? err : count; ++} ++ ++ ++static unsigned int zc0301_poll(struct file *filp, poll_table *wait) ++{ ++ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); ++ struct zc0301_frame_t* f; ++ unsigned long lock_flags; ++ unsigned int mask = 0; ++ ++ if (mutex_lock_interruptible(&cam->fileop_mutex)) ++ return POLLERR; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ goto error; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ goto error; ++ } ++ ++ if (cam->io == IO_NONE) { ++ if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { ++ DBG(1, "poll() failed, not enough memory"); ++ goto error; ++ } ++ cam->io = IO_READ; ++ cam->stream = STREAM_ON; ++ } ++ ++ if (cam->io == IO_READ) { ++ spin_lock_irqsave(&cam->queue_lock, lock_flags); ++ list_for_each_entry(f, &cam->outqueue, frame) ++ f->state = F_UNUSED; ++ INIT_LIST_HEAD(&cam->outqueue); ++ spin_unlock_irqrestore(&cam->queue_lock, lock_flags); ++ zc0301_queue_unusedframes(cam); ++ } ++ ++ poll_wait(filp, &cam->wait_frame, wait); ++ ++ if (!list_empty(&cam->outqueue)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ mutex_unlock(&cam->fileop_mutex); ++ ++ return mask; ++ ++error: ++ mutex_unlock(&cam->fileop_mutex); ++ return POLLERR; ++} ++ ++ ++static void zc0301_vm_open(struct vm_area_struct* vma) ++{ ++ struct zc0301_frame_t* f = vma->vm_private_data; ++ f->vma_use_count++; ++} ++ ++ ++static void zc0301_vm_close(struct vm_area_struct* vma) ++{ ++ /* NOTE: buffers are not freed here */ ++ struct zc0301_frame_t* f = vma->vm_private_data; ++ f->vma_use_count--; ++} ++ ++ ++static struct vm_operations_struct zc0301_vm_ops = { ++ .open = zc0301_vm_open, ++ .close = zc0301_vm_close, ++}; ++ ++ ++static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) ++{ ++ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); ++ unsigned long size = vma->vm_end - vma->vm_start, ++ start = vma->vm_start; ++ void *pos; ++ u32 i; ++ ++ if (mutex_lock_interruptible(&cam->fileop_mutex)) ++ return -ERESTARTSYS; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ mutex_unlock(&cam->fileop_mutex); ++ return -ENODEV; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ mutex_unlock(&cam->fileop_mutex); ++ return -EIO; ++ } ++ ++ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || ++ size != PAGE_ALIGN(cam->frame[0].buf.length)) { ++ mutex_unlock(&cam->fileop_mutex); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < cam->nbuffers; i++) { ++ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) ++ break; ++ } ++ if (i == cam->nbuffers) { ++ mutex_unlock(&cam->fileop_mutex); ++ return -EINVAL; ++ } ++ ++ vma->vm_flags |= VM_IO; ++ vma->vm_flags |= VM_RESERVED; ++ ++ pos = cam->frame[i].bufmem; ++ while (size > 0) { /* size is page-aligned */ ++ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { ++ mutex_unlock(&cam->fileop_mutex); ++ return -EAGAIN; ++ } ++ start += PAGE_SIZE; ++ pos += PAGE_SIZE; ++ size -= PAGE_SIZE; ++ } ++ ++ vma->vm_ops = &zc0301_vm_ops; ++ vma->vm_private_data = &cam->frame[i]; ++ ++ zc0301_vm_open(vma); ++ ++ mutex_unlock(&cam->fileop_mutex); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++static int ++zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_capability cap = { ++ .driver = "zc0301", ++ .version = ZC0301_MODULE_VERSION_CODE, ++ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING, ++ }; ++ ++ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); ++ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) ++ strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, ++ sizeof(cap.bus_info)); ++ ++ if (copy_to_user(arg, &cap, sizeof(cap))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_input i; ++ ++ if (copy_from_user(&i, arg, sizeof(i))) ++ return -EFAULT; ++ ++ if (i.index) ++ return -EINVAL; ++ ++ memset(&i, 0, sizeof(i)); ++ strcpy(i.name, "Camera"); ++ ++ if (copy_to_user(arg, &i, sizeof(i))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_gs_input(struct zc0301_device* cam, void __user * arg) ++{ ++ int index; ++ ++ if (copy_from_user(&index, arg, sizeof(index))) ++ return -EFAULT; ++ ++ if (index != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_queryctrl qc; ++ u8 i; ++ ++ if (copy_from_user(&qc, arg, sizeof(qc))) ++ return -EFAULT; ++ ++ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) ++ if (qc.id && qc.id == s->qctrl[i].id) { ++ memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); ++ if (copy_to_user(arg, &qc, sizeof(qc))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++ ++static int ++zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_control ctrl; ++ int err = 0; ++ u8 i; ++ ++ if (!s->get_ctrl && !s->set_ctrl) ++ return -EINVAL; ++ ++ if (copy_from_user(&ctrl, arg, sizeof(ctrl))) ++ return -EFAULT; ++ ++ if (!s->get_ctrl) { ++ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) ++ if (ctrl.id == s->qctrl[i].id) { ++ ctrl.value = s->_qctrl[i].default_value; ++ goto exit; ++ } ++ return -EINVAL; ++ } else ++ err = s->get_ctrl(cam, &ctrl); ++ ++exit: ++ if (copy_to_user(arg, &ctrl, sizeof(ctrl))) ++ return -EFAULT; ++ ++ return err; ++} ++ ++ ++static int ++zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_control ctrl; ++ u8 i; ++ int err = 0; ++ ++ if (!s->set_ctrl) ++ return -EINVAL; ++ ++ if (copy_from_user(&ctrl, arg, sizeof(ctrl))) ++ return -EFAULT; ++ ++ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) ++ if (ctrl.id == s->qctrl[i].id) { ++ if (ctrl.value < s->qctrl[i].minimum || ++ ctrl.value > s->qctrl[i].maximum) ++ return -ERANGE; ++ ctrl.value -= ctrl.value % s->qctrl[i].step; ++ break; ++ } ++ ++ if ((err = s->set_ctrl(cam, &ctrl))) ++ return err; ++ ++ s->_qctrl[i].default_value = ctrl.value; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_cropcap* cc = &(cam->sensor->cropcap); ++ ++ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cc->pixelaspect.numerator = 1; ++ cc->pixelaspect.denominator = 1; ++ ++ if (copy_to_user(arg, cc, sizeof(*cc))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_crop crop = { ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ }; ++ ++ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); ++ ++ if (copy_to_user(arg, &crop, sizeof(crop))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_crop crop; ++ struct v4l2_rect* rect; ++ struct v4l2_rect* bounds = &(s->cropcap.bounds); ++ const enum zc0301_stream_state stream = cam->stream; ++ const u32 nbuffers = cam->nbuffers; ++ u32 i; ++ int err = 0; ++ ++ if (copy_from_user(&crop, arg, sizeof(crop))) ++ return -EFAULT; ++ ++ rect = &(crop.c); ++ ++ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ if (cam->module_param.force_munmap) ++ for (i = 0; i < cam->nbuffers; i++) ++ if (cam->frame[i].vma_use_count) { ++ DBG(3, "VIDIOC_S_CROP failed. " ++ "Unmap the buffers first."); ++ return -EINVAL; ++ } ++ ++ if (!s->set_crop) { ++ memcpy(rect, &(s->_rect), sizeof(*rect)); ++ if (copy_to_user(arg, &crop, sizeof(crop))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ rect->left &= ~7L; ++ rect->top &= ~7L; ++ if (rect->width < 8) ++ rect->width = 8; ++ if (rect->height < 8) ++ rect->height = 8; ++ if (rect->width > bounds->width) ++ rect->width = bounds->width; ++ if (rect->height > bounds->height) ++ rect->height = bounds->height; ++ if (rect->left < bounds->left) ++ rect->left = bounds->left; ++ if (rect->top < bounds->top) ++ rect->top = bounds->top; ++ if (rect->left + rect->width > bounds->left + bounds->width) ++ rect->left = bounds->left+bounds->width - rect->width; ++ if (rect->top + rect->height > bounds->top + bounds->height) ++ rect->top = bounds->top+bounds->height - rect->height; ++ rect->width &= ~7L; ++ rect->height &= ~7L; ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = zc0301_stream_interrupt(cam))) ++ return err; ++ ++ if (copy_to_user(arg, &crop, sizeof(crop))) { ++ cam->stream = stream; ++ return -EFAULT; ++ } ++ ++ if (cam->module_param.force_munmap || cam->io == IO_READ) ++ zc0301_release_buffers(cam); ++ ++ if (s->set_crop) ++ err += s->set_crop(cam, rect); ++ ++ if (err) { /* atomic, no rollback in ioctl() */ ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " ++ "use the camera, close and open /dev/video%d again.", ++ cam->v4ldev->minor); ++ return -EIO; ++ } ++ ++ s->pix_format.width = rect->width; ++ s->pix_format.height = rect->height; ++ memcpy(&(s->_rect), rect, sizeof(*rect)); ++ ++ if ((cam->module_param.force_munmap || cam->io == IO_READ) && ++ nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " ++ "use the camera, close and open /dev/video%d again.", ++ cam->v4ldev->minor); ++ return -ENOMEM; ++ } ++ ++ if (cam->io == IO_READ) ++ zc0301_empty_framequeues(cam); ++ else if (cam->module_param.force_munmap) ++ zc0301_requeue_outqueue(cam); ++ ++ cam->stream = stream; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_fmtdesc fmtd; ++ ++ if (copy_from_user(&fmtd, arg, sizeof(fmtd))) ++ return -EFAULT; ++ ++ if (fmtd.index == 0) { ++ strcpy(fmtd.description, "JPEG"); ++ fmtd.pixelformat = V4L2_PIX_FMT_JPEG; ++ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; ++ } else ++ return -EINVAL; ++ ++ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); ++ ++ if (copy_to_user(arg, &fmtd, sizeof(fmtd))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_format format; ++ struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format); ++ ++ if (copy_from_user(&format, arg, sizeof(format))) ++ return -EFAULT; ++ ++ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ pfmt->bytesperline = 0; ++ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); ++ pfmt->field = V4L2_FIELD_NONE; ++ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); ++ ++ if (copy_to_user(arg, &format, sizeof(format))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, ++ void __user * arg) ++{ ++ struct zc0301_sensor* s = cam->sensor; ++ struct v4l2_format format; ++ struct v4l2_pix_format* pix; ++ struct v4l2_pix_format* pfmt = &(s->pix_format); ++ struct v4l2_rect* bounds = &(s->cropcap.bounds); ++ struct v4l2_rect rect; ++ const enum zc0301_stream_state stream = cam->stream; ++ const u32 nbuffers = cam->nbuffers; ++ u32 i; ++ int err = 0; ++ ++ if (copy_from_user(&format, arg, sizeof(format))) ++ return -EFAULT; ++ ++ pix = &(format.fmt.pix); ++ ++ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memcpy(&rect, &(s->_rect), sizeof(rect)); ++ ++ if (!s->set_crop) { ++ pix->width = rect.width; ++ pix->height = rect.height; ++ } else { ++ rect.width = pix->width; ++ rect.height = pix->height; ++ } ++ ++ if (rect.width < 8) ++ rect.width = 8; ++ if (rect.height < 8) ++ rect.height = 8; ++ if (rect.width > bounds->left + bounds->width - rect.left) ++ rect.width = bounds->left + bounds->width - rect.left; ++ if (rect.height > bounds->top + bounds->height - rect.top) ++ rect.height = bounds->top + bounds->height - rect.top; ++ rect.width &= ~7L; ++ rect.height &= ~7L; ++ ++ pix->width = rect.width; ++ pix->height = rect.height; ++ pix->pixelformat = pfmt->pixelformat; ++ pix->priv = pfmt->priv; ++ pix->colorspace = pfmt->colorspace; ++ pix->bytesperline = 0; ++ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); ++ pix->field = V4L2_FIELD_NONE; ++ ++ if (cmd == VIDIOC_TRY_FMT) { ++ if (copy_to_user(arg, &format, sizeof(format))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ if (cam->module_param.force_munmap) ++ for (i = 0; i < cam->nbuffers; i++) ++ if (cam->frame[i].vma_use_count) { ++ DBG(3, "VIDIOC_S_FMT failed. " ++ "Unmap the buffers first."); ++ return -EINVAL; ++ } ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = zc0301_stream_interrupt(cam))) ++ return err; ++ ++ if (copy_to_user(arg, &format, sizeof(format))) { ++ cam->stream = stream; ++ return -EFAULT; ++ } ++ ++ if (cam->module_param.force_munmap || cam->io == IO_READ) ++ zc0301_release_buffers(cam); ++ ++ if (s->set_crop) ++ err += s->set_crop(cam, &rect); ++ ++ if (err) { /* atomic, no rollback in ioctl() */ ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " ++ "use the camera, close and open /dev/video%d again.", ++ cam->v4ldev->minor); ++ return -EIO; ++ } ++ ++ memcpy(pfmt, pix, sizeof(*pix)); ++ memcpy(&(s->_rect), &rect, sizeof(rect)); ++ ++ if ((cam->module_param.force_munmap || cam->io == IO_READ) && ++ nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " ++ "use the camera, close and open /dev/video%d again.", ++ cam->v4ldev->minor); ++ return -ENOMEM; ++ } ++ ++ if (cam->io == IO_READ) ++ zc0301_empty_framequeues(cam); ++ else if (cam->module_param.force_munmap) ++ zc0301_requeue_outqueue(cam); ++ ++ cam->stream = stream; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg) ++{ ++ if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_jpegcompression jc; ++ const enum zc0301_stream_state stream = cam->stream; ++ int err = 0; ++ ++ if (copy_from_user(&jc, arg, sizeof(jc))) ++ return -EFAULT; ++ ++ if (jc.quality < 0 || jc.quality > 3) ++ return -EINVAL; ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = zc0301_stream_interrupt(cam))) ++ return err; ++ ++ err += zc0301_set_compression(cam, &jc); ++ if (err) { /* atomic, no rollback in ioctl() */ ++ cam->state |= DEV_MISCONFIGURED; ++ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " ++ "problems. To use the camera, close and open " ++ "/dev/video%d again.", cam->v4ldev->minor); ++ return -EIO; ++ } ++ ++ cam->compression.quality = jc.quality; ++ ++ cam->stream = stream; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_requestbuffers rb; ++ u32 i; ++ int err; ++ ++ if (copy_from_user(&rb, arg, sizeof(rb))) ++ return -EFAULT; ++ ++ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || ++ rb.memory != V4L2_MEMORY_MMAP) ++ return -EINVAL; ++ ++ if (cam->io == IO_READ) { ++ DBG(3, "Close and open the device again to choose the mmap " ++ "I/O method"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < cam->nbuffers; i++) ++ if (cam->frame[i].vma_use_count) { ++ DBG(3, "VIDIOC_REQBUFS failed. " ++ "Previous buffers are still mapped."); ++ return -EINVAL; ++ } ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = zc0301_stream_interrupt(cam))) ++ return err; ++ ++ zc0301_empty_framequeues(cam); ++ ++ zc0301_release_buffers(cam); ++ if (rb.count) ++ rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP); ++ ++ if (copy_to_user(arg, &rb, sizeof(rb))) { ++ zc0301_release_buffers(cam); ++ cam->io = IO_NONE; ++ return -EFAULT; ++ } ++ ++ cam->io = rb.count ? IO_MMAP : IO_NONE; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_buffer b; ++ ++ if (copy_from_user(&b, arg, sizeof(b))) ++ return -EFAULT; ++ ++ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || ++ b.index >= cam->nbuffers || cam->io != IO_MMAP) ++ return -EINVAL; ++ ++ memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); ++ ++ if (cam->frame[b.index].vma_use_count) ++ b.flags |= V4L2_BUF_FLAG_MAPPED; ++ ++ if (cam->frame[b.index].state == F_DONE) ++ b.flags |= V4L2_BUF_FLAG_DONE; ++ else if (cam->frame[b.index].state != F_UNUSED) ++ b.flags |= V4L2_BUF_FLAG_QUEUED; ++ ++ if (copy_to_user(arg, &b, sizeof(b))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_buffer b; ++ unsigned long lock_flags; ++ ++ if (copy_from_user(&b, arg, sizeof(b))) ++ return -EFAULT; ++ ++ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || ++ b.index >= cam->nbuffers || cam->io != IO_MMAP) ++ return -EINVAL; ++ ++ if (cam->frame[b.index].state != F_UNUSED) ++ return -EINVAL; ++ ++ cam->frame[b.index].state = F_QUEUED; ++ ++ spin_lock_irqsave(&cam->queue_lock, lock_flags); ++ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); ++ spin_unlock_irqrestore(&cam->queue_lock, lock_flags); ++ ++ PDBGG("Frame #%lu queued", (unsigned long)b.index); ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, ++ void __user * arg) ++{ ++ struct v4l2_buffer b; ++ struct zc0301_frame_t *f; ++ unsigned long lock_flags; ++ long timeout; ++ ++ if (copy_from_user(&b, arg, sizeof(b))) ++ return -EFAULT; ++ ++ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) ++ return -EINVAL; ++ ++ if (list_empty(&cam->outqueue)) { ++ if (cam->stream == STREAM_OFF) ++ return -EINVAL; ++ if (filp->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ timeout = wait_event_interruptible_timeout ++ ( cam->wait_frame, ++ (!list_empty(&cam->outqueue)) || ++ (cam->state & DEV_DISCONNECTED) || ++ (cam->state & DEV_MISCONFIGURED), ++ ZC0301_FRAME_TIMEOUT ); ++ if (timeout < 0) ++ return timeout; ++ if (cam->state & DEV_DISCONNECTED) ++ return -ENODEV; ++ if (!timeout || (cam->state & DEV_MISCONFIGURED)) ++ return -EIO; ++ } ++ ++ spin_lock_irqsave(&cam->queue_lock, lock_flags); ++ f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame); ++ list_del(cam->outqueue.next); ++ spin_unlock_irqrestore(&cam->queue_lock, lock_flags); ++ ++ f->state = F_UNUSED; ++ ++ memcpy(&b, &f->buf, sizeof(b)); ++ if (f->vma_use_count) ++ b.flags |= V4L2_BUF_FLAG_MAPPED; ++ ++ if (copy_to_user(arg, &b, sizeof(b))) ++ return -EFAULT; ++ ++ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) ++{ ++ int type; ++ ++ if (copy_from_user(&type, arg, sizeof(type))) ++ return -EFAULT; ++ ++ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) ++ return -EINVAL; ++ ++ if (list_empty(&cam->inqueue)) ++ return -EINVAL; ++ ++ cam->stream = STREAM_ON; ++ ++ DBG(3, "Stream on"); ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg) ++{ ++ int type, err; ++ ++ if (copy_from_user(&type, arg, sizeof(type))) ++ return -EFAULT; ++ ++ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) ++ return -EINVAL; ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = zc0301_stream_interrupt(cam))) ++ return err; ++ ++ zc0301_empty_framequeues(cam); ++ ++ DBG(3, "Stream off"); ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_streamparm sp; ++ ++ if (copy_from_user(&sp, arg, sizeof(sp))) ++ return -EFAULT; ++ ++ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ sp.parm.capture.extendedmode = 0; ++ sp.parm.capture.readbuffers = cam->nreadbuffers; ++ ++ if (copy_to_user(arg, &sp, sizeof(sp))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) ++{ ++ struct v4l2_streamparm sp; ++ ++ if (copy_from_user(&sp, arg, sizeof(sp))) ++ return -EFAULT; ++ ++ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ sp.parm.capture.extendedmode = 0; ++ ++ if (sp.parm.capture.readbuffers == 0) ++ sp.parm.capture.readbuffers = cam->nreadbuffers; ++ ++ if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES) ++ sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES; ++ ++ if (copy_to_user(arg, &sp, sizeof(sp))) ++ return -EFAULT; ++ ++ cam->nreadbuffers = sp.parm.capture.readbuffers; ++ ++ return 0; ++} ++ ++ ++static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, ++ unsigned int cmd, void __user * arg) ++{ ++ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); ++ ++ switch (cmd) { ++ ++ case VIDIOC_QUERYCAP: ++ return zc0301_vidioc_querycap(cam, arg); ++ ++ case VIDIOC_ENUMINPUT: ++ return zc0301_vidioc_enuminput(cam, arg); ++ ++ case VIDIOC_G_INPUT: ++ case VIDIOC_S_INPUT: ++ return zc0301_vidioc_gs_input(cam, arg); ++ ++ case VIDIOC_QUERYCTRL: ++ return zc0301_vidioc_query_ctrl(cam, arg); ++ ++ case VIDIOC_G_CTRL: ++ return zc0301_vidioc_g_ctrl(cam, arg); ++ ++ case VIDIOC_S_CTRL_OLD: ++ case VIDIOC_S_CTRL: ++ return zc0301_vidioc_s_ctrl(cam, arg); ++ ++ case VIDIOC_CROPCAP_OLD: ++ case VIDIOC_CROPCAP: ++ return zc0301_vidioc_cropcap(cam, arg); ++ ++ case VIDIOC_G_CROP: ++ return zc0301_vidioc_g_crop(cam, arg); ++ ++ case VIDIOC_S_CROP: ++ return zc0301_vidioc_s_crop(cam, arg); ++ ++ case VIDIOC_ENUM_FMT: ++ return zc0301_vidioc_enum_fmt(cam, arg); ++ ++ case VIDIOC_G_FMT: ++ return zc0301_vidioc_g_fmt(cam, arg); ++ ++ case VIDIOC_TRY_FMT: ++ case VIDIOC_S_FMT: ++ return zc0301_vidioc_try_s_fmt(cam, cmd, arg); ++ ++ case VIDIOC_G_JPEGCOMP: ++ return zc0301_vidioc_g_jpegcomp(cam, arg); ++ ++ case VIDIOC_S_JPEGCOMP: ++ return zc0301_vidioc_s_jpegcomp(cam, arg); ++ ++ case VIDIOC_REQBUFS: ++ return zc0301_vidioc_reqbufs(cam, arg); ++ ++ case VIDIOC_QUERYBUF: ++ return zc0301_vidioc_querybuf(cam, arg); ++ ++ case VIDIOC_QBUF: ++ return zc0301_vidioc_qbuf(cam, arg); ++ ++ case VIDIOC_DQBUF: ++ return zc0301_vidioc_dqbuf(cam, filp, arg); ++ ++ case VIDIOC_STREAMON: ++ return zc0301_vidioc_streamon(cam, arg); ++ ++ case VIDIOC_STREAMOFF: ++ return zc0301_vidioc_streamoff(cam, arg); ++ ++ case VIDIOC_G_PARM: ++ return zc0301_vidioc_g_parm(cam, arg); ++ ++ case VIDIOC_S_PARM_OLD: ++ case VIDIOC_S_PARM: ++ return zc0301_vidioc_s_parm(cam, arg); ++ ++ case VIDIOC_G_STD: ++ case VIDIOC_S_STD: ++ case VIDIOC_QUERYSTD: ++ case VIDIOC_ENUMSTD: ++ case VIDIOC_QUERYMENU: ++ return -EINVAL; ++ ++ default: ++ return -EINVAL; ++ ++ } ++} ++ ++ ++static int zc0301_ioctl(struct inode* inode, struct file* filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); ++ int err = 0; ++ ++ if (mutex_lock_interruptible(&cam->fileop_mutex)) ++ return -ERESTARTSYS; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ mutex_unlock(&cam->fileop_mutex); ++ return -ENODEV; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ mutex_unlock(&cam->fileop_mutex); ++ return -EIO; ++ } ++ ++ V4LDBG(3, "zc0301", cmd); ++ ++ err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); ++ ++ mutex_unlock(&cam->fileop_mutex); ++ ++ return err; ++} ++ ++ ++static struct file_operations zc0301_fops = { ++ .owner = THIS_MODULE, ++ .open = zc0301_open, ++ .release = zc0301_release, ++ .ioctl = zc0301_ioctl, ++ .read = zc0301_read, ++ .poll = zc0301_poll, ++ .mmap = zc0301_mmap, ++ .llseek = no_llseek, ++}; ++ ++/*****************************************************************************/ ++ ++static int ++zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ++{ ++ struct usb_device *udev = interface_to_usbdev(intf); ++ struct zc0301_device* cam; ++ static unsigned int dev_nr = 0; ++ unsigned int i; ++ int err = 0; ++ ++ if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ cam->usbdev = udev; ++ ++ if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { ++ DBG(1, "kmalloc() failed"); ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ if (!(cam->v4ldev = video_device_alloc())) { ++ DBG(1, "video_device_alloc() failed"); ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ mutex_init(&cam->dev_mutex); ++ ++ DBG(2, "ZC0301 Image Processor and Control Chip detected " ++ "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); ++ ++ for (i = 0; zc0301_sensor_table[i]; i++) { ++ err = zc0301_sensor_table[i](cam); ++ if (!err) ++ break; ++ } ++ ++ if (!err && cam->sensor) ++ DBG(2, "%s image sensor detected", cam->sensor->name); ++ else { ++ DBG(1, "No supported image sensor detected"); ++ err = -ENODEV; ++ goto fail; ++ } ++ ++ if (zc0301_init(cam)) { ++ DBG(1, "Initialization failed. I will retry on open()."); ++ cam->state |= DEV_MISCONFIGURED; ++ } ++ ++ strcpy(cam->v4ldev->name, "ZC0301 PC Camera"); ++ cam->v4ldev->owner = THIS_MODULE; ++ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; ++ cam->v4ldev->hardware = 0; ++ cam->v4ldev->fops = &zc0301_fops; ++ cam->v4ldev->minor = video_nr[dev_nr]; ++ cam->v4ldev->release = video_device_release; ++ video_set_drvdata(cam->v4ldev, cam); ++ ++ mutex_lock(&cam->dev_mutex); ++ ++ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, ++ video_nr[dev_nr]); ++ if (err) { ++ DBG(1, "V4L2 device registration failed"); ++ if (err == -ENFILE && video_nr[dev_nr] == -1) ++ DBG(1, "Free /dev/videoX node not found"); ++ video_nr[dev_nr] = -1; ++ dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; ++ mutex_unlock(&cam->dev_mutex); ++ goto fail; ++ } ++ ++ DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); ++ ++ cam->module_param.force_munmap = force_munmap[dev_nr]; ++ ++ dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; ++ ++ usb_set_intfdata(intf, cam); ++ ++ mutex_unlock(&cam->dev_mutex); ++ ++ return 0; ++ ++fail: ++ if (cam) { ++ kfree(cam->control_buffer); ++ if (cam->v4ldev) ++ video_device_release(cam->v4ldev); ++ kfree(cam); ++ } ++ return err; ++} ++ ++ ++static void zc0301_usb_disconnect(struct usb_interface* intf) ++{ ++ struct zc0301_device* cam = usb_get_intfdata(intf); ++ ++ if (!cam) ++ return; ++ ++ down_write(&zc0301_disconnect); ++ ++ mutex_lock(&cam->dev_mutex); ++ ++ DBG(2, "Disconnecting %s...", cam->v4ldev->name); ++ ++ wake_up_interruptible_all(&cam->open); ++ ++ if (cam->users) { ++ DBG(2, "Device /dev/video%d is open! Deregistration and " ++ "memory deallocation are deferred on close.", ++ cam->v4ldev->minor); ++ cam->state |= DEV_MISCONFIGURED; ++ zc0301_stop_transfer(cam); ++ cam->state |= DEV_DISCONNECTED; ++ wake_up_interruptible(&cam->wait_frame); ++ wake_up_interruptible(&cam->wait_stream); ++ } else { ++ cam->state |= DEV_DISCONNECTED; ++ zc0301_release_resources(cam); ++ } ++ ++ mutex_unlock(&cam->dev_mutex); ++ ++ if (!cam->users) ++ kfree(cam); ++ ++ up_write(&zc0301_disconnect); ++} ++ ++ ++static struct usb_driver zc0301_usb_driver = { ++ .name = "zc0301", ++ .id_table = zc0301_id_table, ++ .probe = zc0301_usb_probe, ++ .disconnect = zc0301_usb_disconnect, ++}; ++ ++/*****************************************************************************/ ++ ++static int __init zc0301_module_init(void) ++{ ++ int err = 0; ++ ++ KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION); ++ KDBG(3, ZC0301_MODULE_AUTHOR); ++ ++ if ((err = usb_register(&zc0301_usb_driver))) ++ KDBG(1, "usb_register() failed"); ++ ++ return err; ++} ++ ++ ++static void __exit zc0301_module_exit(void) ++{ ++ usb_deregister(&zc0301_usb_driver); ++} ++ ++ ++module_init(zc0301_module_init); ++module_exit(zc0301_module_exit); +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/zc0301.h +@@ -0,0 +1,185 @@ ++/*************************************************************************** ++ * V4L2 driver for ZC0301 Image Processor and Control Chip * ++ * * ++ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * ++ * * ++ * 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 _ZC0301_H_ ++#define _ZC0301_H_ ++ ++#include <linux/version.h> ++#include <linux/usb.h> ++#include <linux/videodev2.h> ++#include <media/v4l2-common.h> ++#include <linux/device.h> ++#include <linux/list.h> ++#include <linux/spinlock.h> ++#include <linux/time.h> ++#include <linux/wait.h> ++#include <linux/types.h> ++#include <linux/param.h> ++#include <linux/mutex.h> ++#include <linux/rwsem.h> ++#include <asm/semaphore.h> ++ ++#include "zc0301_sensor.h" ++ ++/*****************************************************************************/ ++ ++#define ZC0301_DEBUG ++#define ZC0301_DEBUG_LEVEL 2 ++#define ZC0301_MAX_DEVICES 64 ++#define ZC0301_FORCE_MUNMAP 0 ++#define ZC0301_MAX_FRAMES 32 ++#define ZC0301_COMPRESSION_QUALITY 2 ++#define ZC0301_URBS 2 ++#define ZC0301_ISO_PACKETS 7 ++#define ZC0301_ALTERNATE_SETTING 7 ++#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) ++#define ZC0301_CTRL_TIMEOUT 100 ++#define ZC0301_FRAME_TIMEOUT 2 * 1000 * msecs_to_jiffies(1) ++ ++/*****************************************************************************/ ++ ++ZC0301_ID_TABLE ++ZC0301_SENSOR_TABLE ++ ++enum zc0301_frame_state { ++ F_UNUSED, ++ F_QUEUED, ++ F_GRABBING, ++ F_DONE, ++ F_ERROR, ++}; ++ ++struct zc0301_frame_t { ++ void* bufmem; ++ struct v4l2_buffer buf; ++ enum zc0301_frame_state state; ++ struct list_head frame; ++ unsigned long vma_use_count; ++}; ++ ++enum zc0301_dev_state { ++ DEV_INITIALIZED = 0x01, ++ DEV_DISCONNECTED = 0x02, ++ DEV_MISCONFIGURED = 0x04, ++}; ++ ++enum zc0301_io_method { ++ IO_NONE, ++ IO_READ, ++ IO_MMAP, ++}; ++ ++enum zc0301_stream_state { ++ STREAM_OFF, ++ STREAM_INTERRUPT, ++ STREAM_ON, ++}; ++ ++struct zc0301_module_param { ++ u8 force_munmap; ++}; ++ ++static DECLARE_RWSEM(zc0301_disconnect); ++ ++struct zc0301_device { ++ struct video_device* v4ldev; ++ ++ struct zc0301_sensor* sensor; ++ ++ struct usb_device* usbdev; ++ struct urb* urb[ZC0301_URBS]; ++ void* transfer_buffer[ZC0301_URBS]; ++ u8* control_buffer; ++ ++ struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES]; ++ struct list_head inqueue, outqueue; ++ u32 frame_count, nbuffers, nreadbuffers; ++ ++ enum zc0301_io_method io; ++ enum zc0301_stream_state stream; ++ ++ struct v4l2_jpegcompression compression; ++ ++ struct zc0301_module_param module_param; ++ ++ enum zc0301_dev_state state; ++ u8 users; ++ ++ struct mutex dev_mutex, fileop_mutex; ++ spinlock_t queue_lock; ++ wait_queue_head_t open, wait_frame, wait_stream; ++}; ++ ++/*****************************************************************************/ ++ ++void ++zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) ++{ ++ cam->sensor = sensor; ++ cam->sensor->usbdev = cam->usbdev; ++} ++ ++/*****************************************************************************/ ++ ++#undef DBG ++#undef KDBG ++#ifdef ZC0301_DEBUG ++# define DBG(level, fmt, args...) \ ++do { \ ++ if (debug >= (level)) { \ ++ if ((level) == 1) \ ++ dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ ++ else if ((level) == 2) \ ++ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ ++ else if ((level) >= 3) \ ++ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ ++ __FUNCTION__, __LINE__ , ## args); \ ++ } \ ++} while (0) ++# define KDBG(level, fmt, args...) \ ++do { \ ++ if (debug >= (level)) { \ ++ if ((level) == 1 || (level) == 2) \ ++ pr_info("zc0301: " fmt "\n", ## args); \ ++ else if ((level) == 3) \ ++ pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ ++ __LINE__ , ## args); \ ++ } \ ++} while (0) ++# define V4LDBG(level, name, cmd) \ ++do { \ ++ if (debug >= (level)) \ ++ v4l_print_ioctl(name, cmd); \ ++} while (0) ++#else ++# define DBG(level, fmt, args...) do {;} while(0) ++# define KDBG(level, fmt, args...) do {;} while(0) ++# define V4LDBG(level, name, cmd) do {;} while(0) ++#endif ++ ++#undef PDBG ++#define PDBG(fmt, args...) \ ++dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ ++ __FUNCTION__, __LINE__ , ## args) ++ ++#undef PDBGG ++#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ ++ ++#endif /* _ZC0301_H_ */ +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/zc0301_pas202bcb.c +@@ -0,0 +1,353 @@ ++/*************************************************************************** ++ * Plug-in for PAS202BCB image sensor connected to the ZC030! Image * ++ * Processor and Control Chip * ++ * * ++ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * ++ * * ++ * Initialization values of the ZC0301 have been taken from the SPCA5XX * ++ * driver maintained by Michel Xhaard <mxhaard@magic.fr> * ++ * * ++ * 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 <linux/delay.h> ++#include "zc0301_sensor.h" ++ ++ ++static struct zc0301_sensor pas202bcb; ++ ++ ++static int pas202bcb_init(struct zc0301_device* cam) ++{ ++ int err = 0; ++ ++ err += zc0301_write_reg(cam, 0x0002, 0x00); ++ err += zc0301_write_reg(cam, 0x0003, 0x02); ++ err += zc0301_write_reg(cam, 0x0004, 0x80); ++ err += zc0301_write_reg(cam, 0x0005, 0x01); ++ err += zc0301_write_reg(cam, 0x0006, 0xE0); ++ err += zc0301_write_reg(cam, 0x0098, 0x00); ++ err += zc0301_write_reg(cam, 0x009A, 0x03); ++ err += zc0301_write_reg(cam, 0x011A, 0x00); ++ err += zc0301_write_reg(cam, 0x011C, 0x03); ++ err += zc0301_write_reg(cam, 0x009B, 0x01); ++ err += zc0301_write_reg(cam, 0x009C, 0xE6); ++ err += zc0301_write_reg(cam, 0x009D, 0x02); ++ err += zc0301_write_reg(cam, 0x009E, 0x86); ++ ++ err += zc0301_i2c_write(cam, 0x02, 0x02); ++ err += zc0301_i2c_write(cam, 0x0A, 0x01); ++ err += zc0301_i2c_write(cam, 0x0B, 0x01); ++ err += zc0301_i2c_write(cam, 0x0D, 0x00); ++ err += zc0301_i2c_write(cam, 0x12, 0x05); ++ err += zc0301_i2c_write(cam, 0x13, 0x63); ++ err += zc0301_i2c_write(cam, 0x15, 0x70); ++ ++ err += zc0301_write_reg(cam, 0x0101, 0xB7); ++ err += zc0301_write_reg(cam, 0x0100, 0x0D); ++ err += zc0301_write_reg(cam, 0x0189, 0x06); ++ err += zc0301_write_reg(cam, 0x01AD, 0x00); ++ err += zc0301_write_reg(cam, 0x01C5, 0x03); ++ err += zc0301_write_reg(cam, 0x01CB, 0x13); ++ err += zc0301_write_reg(cam, 0x0250, 0x08); ++ err += zc0301_write_reg(cam, 0x0301, 0x08); ++ err += zc0301_write_reg(cam, 0x018D, 0x70); ++ err += zc0301_write_reg(cam, 0x0008, 0x03); ++ err += zc0301_write_reg(cam, 0x01C6, 0x04); ++ err += zc0301_write_reg(cam, 0x01CB, 0x07); ++ err += zc0301_write_reg(cam, 0x0120, 0x11); ++ err += zc0301_write_reg(cam, 0x0121, 0x37); ++ err += zc0301_write_reg(cam, 0x0122, 0x58); ++ err += zc0301_write_reg(cam, 0x0123, 0x79); ++ err += zc0301_write_reg(cam, 0x0124, 0x91); ++ err += zc0301_write_reg(cam, 0x0125, 0xA6); ++ err += zc0301_write_reg(cam, 0x0126, 0xB8); ++ err += zc0301_write_reg(cam, 0x0127, 0xC7); ++ err += zc0301_write_reg(cam, 0x0128, 0xD3); ++ err += zc0301_write_reg(cam, 0x0129, 0xDE); ++ err += zc0301_write_reg(cam, 0x012A, 0xE6); ++ err += zc0301_write_reg(cam, 0x012B, 0xED); ++ err += zc0301_write_reg(cam, 0x012C, 0xF3); ++ err += zc0301_write_reg(cam, 0x012D, 0xF8); ++ err += zc0301_write_reg(cam, 0x012E, 0xFB); ++ err += zc0301_write_reg(cam, 0x012F, 0xFF); ++ err += zc0301_write_reg(cam, 0x0130, 0x26); ++ err += zc0301_write_reg(cam, 0x0131, 0x23); ++ err += zc0301_write_reg(cam, 0x0132, 0x20); ++ err += zc0301_write_reg(cam, 0x0133, 0x1C); ++ err += zc0301_write_reg(cam, 0x0134, 0x16); ++ err += zc0301_write_reg(cam, 0x0135, 0x13); ++ err += zc0301_write_reg(cam, 0x0136, 0x10); ++ err += zc0301_write_reg(cam, 0x0137, 0x0D); ++ err += zc0301_write_reg(cam, 0x0138, 0x0B); ++ err += zc0301_write_reg(cam, 0x0139, 0x09); ++ err += zc0301_write_reg(cam, 0x013A, 0x07); ++ err += zc0301_write_reg(cam, 0x013B, 0x06); ++ err += zc0301_write_reg(cam, 0x013C, 0x05); ++ err += zc0301_write_reg(cam, 0x013D, 0x04); ++ err += zc0301_write_reg(cam, 0x013E, 0x03); ++ err += zc0301_write_reg(cam, 0x013F, 0x02); ++ err += zc0301_write_reg(cam, 0x010A, 0x4C); ++ err += zc0301_write_reg(cam, 0x010B, 0xF5); ++ err += zc0301_write_reg(cam, 0x010C, 0xFF); ++ err += zc0301_write_reg(cam, 0x010D, 0xF9); ++ err += zc0301_write_reg(cam, 0x010E, 0x51); ++ err += zc0301_write_reg(cam, 0x010F, 0xF5); ++ err += zc0301_write_reg(cam, 0x0110, 0xFB); ++ err += zc0301_write_reg(cam, 0x0111, 0xED); ++ err += zc0301_write_reg(cam, 0x0112, 0x5F); ++ err += zc0301_write_reg(cam, 0x0180, 0x00); ++ err += zc0301_write_reg(cam, 0x0019, 0x00); ++ err += zc0301_write_reg(cam, 0x0087, 0x20); ++ err += zc0301_write_reg(cam, 0x0088, 0x21); ++ ++ err += zc0301_i2c_write(cam, 0x20, 0x02); ++ err += zc0301_i2c_write(cam, 0x21, 0x1B); ++ err += zc0301_i2c_write(cam, 0x03, 0x44); ++ err += zc0301_i2c_write(cam, 0x0E, 0x01); ++ err += zc0301_i2c_write(cam, 0x0F, 0x00); ++ ++ err += zc0301_write_reg(cam, 0x01A9, 0x14); ++ err += zc0301_write_reg(cam, 0x01AA, 0x24); ++ err += zc0301_write_reg(cam, 0x0190, 0x00); ++ err += zc0301_write_reg(cam, 0x0191, 0x02); ++ err += zc0301_write_reg(cam, 0x0192, 0x1B); ++ err += zc0301_write_reg(cam, 0x0195, 0x00); ++ err += zc0301_write_reg(cam, 0x0196, 0x00); ++ err += zc0301_write_reg(cam, 0x0197, 0x4D); ++ err += zc0301_write_reg(cam, 0x018C, 0x10); ++ err += zc0301_write_reg(cam, 0x018F, 0x20); ++ err += zc0301_write_reg(cam, 0x001D, 0x44); ++ err += zc0301_write_reg(cam, 0x001E, 0x6F); ++ err += zc0301_write_reg(cam, 0x001F, 0xAD); ++ err += zc0301_write_reg(cam, 0x0020, 0xEB); ++ err += zc0301_write_reg(cam, 0x0087, 0x0F); ++ err += zc0301_write_reg(cam, 0x0088, 0x0E); ++ err += zc0301_write_reg(cam, 0x0180, 0x40); ++ err += zc0301_write_reg(cam, 0x0192, 0x1B); ++ err += zc0301_write_reg(cam, 0x0191, 0x02); ++ err += zc0301_write_reg(cam, 0x0190, 0x00); ++ err += zc0301_write_reg(cam, 0x0116, 0x1D); ++ err += zc0301_write_reg(cam, 0x0117, 0x40); ++ err += zc0301_write_reg(cam, 0x0118, 0x99); ++ err += zc0301_write_reg(cam, 0x0180, 0x42); ++ err += zc0301_write_reg(cam, 0x0116, 0x1D); ++ err += zc0301_write_reg(cam, 0x0117, 0x40); ++ err += zc0301_write_reg(cam, 0x0118, 0x99); ++ err += zc0301_write_reg(cam, 0x0007, 0x00); ++ ++ err += zc0301_i2c_write(cam, 0x11, 0x01); ++ ++ msleep(100); ++ ++ return err; ++} ++ ++ ++static int pas202bcb_get_ctrl(struct zc0301_device* cam, ++ struct v4l2_control* ctrl) ++{ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE: ++ { ++ int r1 = zc0301_i2c_read(cam, 0x04, 1), ++ r2 = zc0301_i2c_read(cam, 0x05, 1); ++ if (r1 < 0 || r2 < 0) ++ return -EIO; ++ ctrl->value = (r1 << 6) | (r2 & 0x3f); ++ } ++ return 0; ++ case V4L2_CID_RED_BALANCE: ++ if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0) ++ return -EIO; ++ ctrl->value &= 0x0f; ++ return 0; ++ case V4L2_CID_BLUE_BALANCE: ++ if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0) ++ return -EIO; ++ ctrl->value &= 0x0f; ++ return 0; ++ case V4L2_CID_GAIN: ++ if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0) ++ return -EIO; ++ ctrl->value &= 0x1f; ++ return 0; ++ case ZC0301_V4L2_CID_GREEN_BALANCE: ++ if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0) ++ return -EIO; ++ ctrl->value &= 0x0f; ++ return 0; ++ case ZC0301_V4L2_CID_DAC_MAGNITUDE: ++ if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0) ++ return -EIO; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++ ++static int pas202bcb_set_ctrl(struct zc0301_device* cam, ++ const struct v4l2_control* ctrl) ++{ ++ int err = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE: ++ err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6); ++ err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f); ++ break; ++ case V4L2_CID_RED_BALANCE: ++ err += zc0301_i2c_write(cam, 0x09, ctrl->value); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ err += zc0301_i2c_write(cam, 0x07, ctrl->value); ++ break; ++ case V4L2_CID_GAIN: ++ err += zc0301_i2c_write(cam, 0x10, ctrl->value); ++ break; ++ case ZC0301_V4L2_CID_GREEN_BALANCE: ++ err += zc0301_i2c_write(cam, 0x08, ctrl->value); ++ break; ++ case ZC0301_V4L2_CID_DAC_MAGNITUDE: ++ err += zc0301_i2c_write(cam, 0x0c, ctrl->value); ++ break; ++ default: ++ return -EINVAL; ++ } ++ err += zc0301_i2c_write(cam, 0x11, 0x01); ++ ++ return err ? -EIO : 0; ++} ++ ++ ++static struct zc0301_sensor pas202bcb = { ++ .name = "PAS202BCB", ++ .init = &pas202bcb_init, ++ .qctrl = { ++ { ++ .id = V4L2_CID_EXPOSURE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "exposure", ++ .minimum = 0x01e5, ++ .maximum = 0x3fff, ++ .step = 0x0001, ++ .default_value = 0x01e5, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "global gain", ++ .minimum = 0x00, ++ .maximum = 0x1f, ++ .step = 0x01, ++ .default_value = 0x0c, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_RED_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "red balance", ++ .minimum = 0x00, ++ .maximum = 0x0f, ++ .step = 0x01, ++ .default_value = 0x01, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_BLUE_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "blue balance", ++ .minimum = 0x00, ++ .maximum = 0x0f, ++ .step = 0x01, ++ .default_value = 0x05, ++ .flags = 0, ++ }, ++ { ++ .id = ZC0301_V4L2_CID_GREEN_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "green balance", ++ .minimum = 0x00, ++ .maximum = 0x0f, ++ .step = 0x01, ++ .default_value = 0x00, ++ .flags = 0, ++ }, ++ { ++ .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "DAC magnitude", ++ .minimum = 0x00, ++ .maximum = 0xff, ++ .step = 0x01, ++ .default_value = 0x04, ++ .flags = 0, ++ }, ++ }, ++ .get_ctrl = &pas202bcb_get_ctrl, ++ .set_ctrl = &pas202bcb_set_ctrl, ++ .cropcap = { ++ .bounds = { ++ .left = 0, ++ .top = 0, ++ .width = 640, ++ .height = 480, ++ }, ++ .defrect = { ++ .left = 0, ++ .top = 0, ++ .width = 640, ++ .height = 480, ++ }, ++ }, ++ .pix_format = { ++ .width = 640, ++ .height = 480, ++ .pixelformat = V4L2_PIX_FMT_JPEG, ++ .priv = 16, ++ }, ++}; ++ ++ ++int zc0301_probe_pas202bcb(struct zc0301_device* cam) ++{ ++ int r0 = 0, r1 = 0, err = 0; ++ unsigned int pid = 0; ++ ++ err += zc0301_write_reg(cam, 0x0000, 0x01); ++ err += zc0301_write_reg(cam, 0x0010, 0x0e); ++ err += zc0301_write_reg(cam, 0x0001, 0x01); ++ err += zc0301_write_reg(cam, 0x0012, 0x03); ++ err += zc0301_write_reg(cam, 0x0012, 0x01); ++ err += zc0301_write_reg(cam, 0x008d, 0x08); ++ ++ msleep(10); ++ ++ r0 = zc0301_i2c_read(cam, 0x00, 1); ++ r1 = zc0301_i2c_read(cam, 0x01, 1); ++ ++ if (r0 < 0 || r1 < 0 || err) ++ return -EIO; ++ ++ pid = (r0 << 4) | ((r1 & 0xf0) >> 4); ++ if (pid != 0x017) ++ return -ENODEV; ++ ++ zc0301_attach_sensor(cam, &pas202bcb); ++ ++ return 0; ++} +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/zc0301_sensor.h +@@ -0,0 +1,98 @@ ++/*************************************************************************** ++ * API for image sensors connected to the ZC030! Image Processor and * ++ * Control Chip * ++ * * ++ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * ++ * * ++ * 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 _ZC0301_SENSOR_H_ ++#define _ZC0301_SENSOR_H_ ++ ++#include <linux/usb.h> ++#include <linux/videodev.h> ++#include <linux/device.h> ++#include <linux/stddef.h> ++#include <linux/errno.h> ++#include <asm/types.h> ++ ++struct zc0301_device; ++struct zc0301_sensor; ++ ++/*****************************************************************************/ ++ ++extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); ++ ++#define ZC0301_SENSOR_TABLE \ ++/* Weak detections must go at the end of the list */ \ ++static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ ++ &zc0301_probe_pas202bcb, \ ++ NULL, \ ++}; ++ ++extern void ++zc0301_attach_sensor(struct zc0301_device* cam, ++ struct zc0301_sensor* sensor); ++ ++#define ZC0301_USB_DEVICE(vend, prod, intclass) \ ++ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ ++ USB_DEVICE_ID_MATCH_INT_CLASS, \ ++ .idVendor = (vend), \ ++ .idProduct = (prod), \ ++ .bInterfaceClass = (intclass) ++ ++#define ZC0301_ID_TABLE \ ++static const struct usb_device_id zc0301_id_table[] = { \ ++ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ ++ { } \ ++}; ++ ++/*****************************************************************************/ ++ ++extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value); ++extern int zc0301_read_reg(struct zc0301_device*, u16 index); ++extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value); ++extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); ++ ++/*****************************************************************************/ ++ ++#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 ++#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE ++#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 ++ ++struct zc0301_sensor { ++ char name[32]; ++ ++ struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS]; ++ struct v4l2_cropcap cropcap; ++ struct v4l2_pix_format pix_format; ++ ++ int (*init)(struct zc0301_device* cam); ++ int (*get_ctrl)(struct zc0301_device* cam, ++ struct v4l2_control* ctrl); ++ int (*set_ctrl)(struct zc0301_device* cam, ++ const struct v4l2_control* ctrl); ++ int (*set_crop)(struct zc0301_device* cam, ++ const struct v4l2_rect* rect); ++ ++ const struct usb_device* usbdev; ++ ++ /* Private */ ++ struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; ++ struct v4l2_rect _rect; ++}; ++ ++#endif /* _ZC0301_SENSOR_H_ */ +--- gregkh-2.6.orig/MAINTAINERS ++++ gregkh-2.6/MAINTAINERS +@@ -2884,6 +2884,14 @@ L: video4linux-list@redhat.com + W: http://www.linux-projects.org + S: Maintained + ++USB ZC0301 DRIVER ++P: Luca Risolia ++M: luca.risolia@studio.unibo.it ++L: linux-usb-devel@lists.sourceforge.net ++L: video4linux-list@redhat.com ++W: http://www.linux-projects.org ++S: Maintained ++ + USB ZD1201 DRIVER + P: Jeroen Vreeken + M: pe1rxq@amsat.org diff --git a/usb/usb-gotemp.patch b/usb/usb-gotemp.patch index 15b86c9307375..030eb51d557c7 100644 --- a/usb/usb-gotemp.patch +++ b/usb/usb-gotemp.patch @@ -331,7 +331,7 @@ +MODULE_LICENSE("GPL"); --- gregkh-2.6.orig/drivers/usb/Makefile +++ gregkh-2.6/drivers/usb/Makefile -@@ -67,6 +67,7 @@ obj-$(CONFIG_USB_AUERSWALD) += misc/ +@@ -68,6 +68,7 @@ obj-$(CONFIG_USB_AUERSWALD) += misc/ obj-$(CONFIG_USB_CYTHERM) += misc/ obj-$(CONFIG_USB_EMI26) += misc/ obj-$(CONFIG_USB_EMI62) += misc/ diff --git a/usb/usb-pegasus-linksys-usbvpn1-support-cleanup.patch b/usb/usb-pegasus-linksys-usbvpn1-support-cleanup.patch new file mode 100644 index 0000000000000..1b44ddefdd396 --- /dev/null +++ b/usb/usb-pegasus-linksys-usbvpn1-support-cleanup.patch @@ -0,0 +1,104 @@ +From linux-usb-devel-admin@lists.sourceforge.net Sat Jan 28 08:49:09 2006 +From: Malte Doersam <mdoersam@arcor.de> +Message-Id: <200601281748.33177.mdoersam@arcor.de> +Subject: USB: Pegasus: Linksys USBVPN1 support + cleanup +Date: Sat, 28 Jan 2006 17:48:33 +0100 + +This patch adds a second linksys vendor-id (077b) and the product id of the +pegasus based adapter USBVPN1 + +http://www1.linksys.com/Products/product.asp?prid=3D543&scid=3D30 + +Furthermore it replaces all LINKSYS_GPIO_RESET with DEFAULT_GPIO_RESET as both +are declared like this: +#define DEFAULT_GPIO_RESET 0x24 +#define LINKSYS_GPIO_RESET 0x24 + +This is misleading and confusing. +The check is now done via the VENDOR_ID in pegasus.c: +if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS + +Signed-off-by: Malte Doersam <mdoersam@arcor.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/net/pegasus.c | 1 + + drivers/usb/net/pegasus.h | 26 ++++++++++++++------------ + 2 files changed, 15 insertions(+), 12 deletions(-) + +--- gregkh-2.6.orig/drivers/usb/net/pegasus.c ++++ gregkh-2.6/drivers/usb/net/pegasus.c +@@ -524,6 +524,7 @@ static int enable_net_traffic(struct net + ret = set_registers(pegasus, EthCtrl0, 3, data); + + if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || ++ usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 || + usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { + u16 auxmode; + read_mii_word(pegasus, 0, 0x1b, &auxmode); +--- gregkh-2.6.orig/drivers/usb/net/pegasus.h ++++ gregkh-2.6/drivers/usb/net/pegasus.h +@@ -25,7 +25,6 @@ + #define PHY_READ 0x40 + #define PHY_WRITE 0x20 + #define DEFAULT_GPIO_RESET 0x24 +-#define LINKSYS_GPIO_RESET 0x24 + #define DEFAULT_GPIO_SET 0x26 + + #define PEGASUS_PRESENT 0x00000001 +@@ -140,6 +139,7 @@ struct usb_eth_dev { + #define VENDOR_KINGSTON 0x0951 + #define VENDOR_LANEED 0x056e + #define VENDOR_LINKSYS 0x066b ++#define VENDOR_LINKSYS2 0x077b + #define VENDOR_MELCO 0x0411 + #define VENDOR_MICROSOFT 0x045e + #define VENDOR_MOBILITY 0x1342 +@@ -218,15 +218,15 @@ PEGASUS_DEV( "Corega FEter USB-TX", VEND + PEGASUS_DEV( "Corega FEter USB-TXS", VENDOR_COREGA, 0x000d, + DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001, +- LINKSYS_GPIO_RESET ) ++ DEFAULT_GPIO_RESET ) + PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002, +- LINKSYS_GPIO_RESET ) ++ DEFAULT_GPIO_RESET ) + PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4102, +- LINKSYS_GPIO_RESET | PEGASUS_II ) ++ DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x400b, +- LINKSYS_GPIO_RESET | PEGASUS_II ) ++ DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x200c, +- LINKSYS_GPIO_RESET | PEGASUS_II ) ++ DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) + PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1, +@@ -260,17 +260,19 @@ PEGASUS_DEV( "LANEED USB Ethernet LD-USB + PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c, + DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202, +- LINKSYS_GPIO_RESET ) ++ DEFAULT_GPIO_RESET ) + PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203, +- LINKSYS_GPIO_RESET ) ++ DEFAULT_GPIO_RESET ) + PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204, +- LINKSYS_GPIO_RESET | HAS_HOME_PNA ) ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA ) + PEGASUS_DEV( "Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206, +- LINKSYS_GPIO_RESET | PEGASUS_II) ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV( "Linksys USBVPN1", VENDOR_LINKSYS2, 0x08b4, ++ DEFAULT_GPIO_RESET ) + PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b, +- LINKSYS_GPIO_RESET | PEGASUS_II ) ++ DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c, +- LINKSYS_GPIO_RESET | PEGASUS_II ) ++ DEFAULT_GPIO_RESET | PEGASUS_II ) + PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001, + DEFAULT_GPIO_RESET ) + PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0005, diff --git a/usb/usbhid-add-error-handling.patch b/usb/usbhid-add-error-handling.patch new file mode 100644 index 0000000000000..2da84304d379a --- /dev/null +++ b/usb/usbhid-add-error-handling.patch @@ -0,0 +1,298 @@ +From stern@rowland.harvard.edu Tue Jan 31 09:59:01 2006 +Date: Tue, 31 Jan 2006 12:58:38 -0500 (EST) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com> +cc: Pete Zaitcev <zaitcev@redhat.com>, Dmitry Torokhov <dtor_core@ameritech.net> +Subject: [PATCH] usbhid: add error handling +Message-ID: <Pine.LNX.4.44L0.0601311251270.5380-100000@iolanthe.rowland.org> + +This patch (as628c) adds error handling to the USB HID core. When an +error is reported for an interrupt URB, the driver will do delayed +retries, at increasing intervals, for up to one second. If that doesn't +work, it will try to reset the device. Testing by users has shown that +both the retries and the resets end up getting used. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/input/hid-core.c | 153 +++++++++++++++++++++++++++++++++++++------ + drivers/usb/input/hid.h | 10 ++ + 2 files changed, 142 insertions(+), 21 deletions(-) + +--- gregkh-2.6.orig/drivers/usb/input/hid-core.c ++++ gregkh-2.6/drivers/usb/input/hid-core.c +@@ -903,6 +903,99 @@ static int hid_input_report(int type, st + } + + /* ++ * Input submission and I/O error handler. ++ */ ++ ++static void hid_io_error(struct hid_device *hid); ++ ++/* Start up the input URB */ ++static int hid_start_in(struct hid_device *hid) ++{ ++ unsigned long flags; ++ int rc = 0; ++ ++ spin_lock_irqsave(&hid->inlock, flags); ++ if (hid->open > 0 && !test_bit(HID_SUSPENDED, &hid->iofl) && ++ !test_and_set_bit(HID_IN_RUNNING, &hid->iofl)) { ++ rc = usb_submit_urb(hid->urbin, GFP_ATOMIC); ++ if (rc != 0) ++ clear_bit(HID_IN_RUNNING, &hid->iofl); ++ } ++ spin_unlock_irqrestore(&hid->inlock, flags); ++ return rc; ++} ++ ++/* I/O retry timer routine */ ++static void hid_retry_timeout(unsigned long _hid) ++{ ++ struct hid_device *hid = (struct hid_device *) _hid; ++ ++ dev_dbg(&hid->intf->dev, "retrying intr urb\n"); ++ if (hid_start_in(hid)) ++ hid_io_error(hid); ++} ++ ++/* Workqueue routine to reset the device */ ++static void hid_reset(void *_hid) ++{ ++ struct hid_device *hid = (struct hid_device *) _hid; ++ int rc_lock, rc; ++ ++ dev_dbg(&hid->intf->dev, "resetting device\n"); ++ rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf); ++ if (rc_lock >= 0) { ++ rc = usb_reset_device(hid->dev); ++ if (rc_lock) ++ usb_unlock_device(hid->dev); ++ } ++ clear_bit(HID_RESET_PENDING, &hid->iofl); ++ ++ if (rc == 0) { ++ hid->retry_delay = 0; ++ if (hid_start_in(hid)) ++ hid_io_error(hid); ++ } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR)) ++ err("can't reset device, %s-%s/input%d, status %d", ++ hid->dev->bus->bus_name, ++ hid->dev->devpath, ++ hid->ifnum, rc); ++} ++ ++/* Main I/O error handler */ ++static void hid_io_error(struct hid_device *hid) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&hid->inlock, flags); ++ ++ /* Stop when disconnected */ ++ if (usb_get_intfdata(hid->intf) == NULL) ++ goto done; ++ ++ /* When an error occurs, retry at increasing intervals */ ++ if (hid->retry_delay == 0) { ++ hid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */ ++ hid->stop_retry = jiffies + msecs_to_jiffies(1000); ++ } else if (hid->retry_delay < 100) ++ hid->retry_delay *= 2; ++ ++ if (time_after(jiffies, hid->stop_retry)) { ++ ++ /* Retries failed, so do a port reset */ ++ if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) { ++ if (schedule_work(&hid->reset_work)) ++ goto done; ++ clear_bit(HID_RESET_PENDING, &hid->iofl); ++ } ++ } ++ ++ mod_timer(&hid->io_retry, ++ jiffies + msecs_to_jiffies(hid->retry_delay)); ++done: ++ spin_unlock_irqrestore(&hid->inlock, flags); ++} ++ ++/* + * Input interrupt completion handler. + */ + +@@ -913,25 +1006,35 @@ static void hid_irq_in(struct urb *urb, + + switch (urb->status) { + case 0: /* success */ ++ hid->retry_delay = 0; + hid_input_report(HID_INPUT_REPORT, urb, 1, regs); + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: +- case -EPERM: + case -ESHUTDOWN: /* unplug */ +- case -EILSEQ: /* unplug timeout on uhci */ ++ clear_bit(HID_IN_RUNNING, &hid->iofl); + return; ++ case -EILSEQ: /* protocol error or unplug */ ++ case -EPROTO: /* protocol error or unplug */ + case -ETIMEDOUT: /* NAK */ +- break; ++ clear_bit(HID_IN_RUNNING, &hid->iofl); ++ hid_io_error(hid); ++ return; + default: /* error */ + warn("input irq status %d received", urb->status); + } + + status = usb_submit_urb(urb, SLAB_ATOMIC); +- if (status) +- err("can't resubmit intr, %s-%s/input%d, status %d", +- hid->dev->bus->bus_name, hid->dev->devpath, +- hid->ifnum, status); ++ if (status) { ++ clear_bit(HID_IN_RUNNING, &hid->iofl); ++ if (status != -EPERM) { ++ err("can't resubmit intr, %s-%s/input%d, status %d", ++ hid->dev->bus->bus_name, ++ hid->dev->devpath, ++ hid->ifnum, status); ++ hid_io_error(hid); ++ } ++ } + } + + /* +@@ -1093,8 +1196,9 @@ static void hid_irq_out(struct urb *urb, + case 0: /* success */ + break; + case -ESHUTDOWN: /* unplug */ +- case -EILSEQ: /* unplug timeout on uhci */ + unplug = 1; ++ case -EILSEQ: /* protocol error or unplug */ ++ case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + break; +@@ -1141,8 +1245,9 @@ static void hid_ctrl(struct urb *urb, st + hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, 0, regs); + break; + case -ESHUTDOWN: /* unplug */ +- case -EILSEQ: /* unplug timectrl on uhci */ + unplug = 1; ++ case -EILSEQ: /* protocol error or unplug */ ++ case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -EPIPE: /* report not available */ +@@ -1255,14 +1360,9 @@ static int hid_get_class_descriptor(stru + + int hid_open(struct hid_device *hid) + { +- if (hid->open++) +- return 0; +- +- hid->urbin->dev = hid->dev; +- +- if (usb_submit_urb(hid->urbin, GFP_KERNEL)) +- return -EIO; +- ++ ++hid->open; ++ if (hid_start_in(hid)) ++ hid_io_error(hid); + return 0; + } + +@@ -1785,6 +1885,10 @@ static struct hid_device *usb_hid_config + + init_waitqueue_head(&hid->wait); + ++ INIT_WORK(&hid->reset_work, hid_reset, hid); ++ setup_timer(&hid->io_retry, hid_retry_timeout, (unsigned long) hid); ++ ++ spin_lock_init(&hid->inlock); + spin_lock_init(&hid->outlock); + spin_lock_init(&hid->ctrllock); + +@@ -1853,11 +1957,16 @@ static void hid_disconnect(struct usb_in + if (!hid) + return; + ++ spin_lock_irq(&hid->inlock); /* Sync with error handler */ + usb_set_intfdata(intf, NULL); ++ spin_unlock_irq(&hid->inlock); + usb_kill_urb(hid->urbin); + usb_kill_urb(hid->urbout); + usb_kill_urb(hid->urbctrl); + ++ del_timer_sync(&hid->io_retry); ++ flush_scheduled_work(); ++ + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + if (hid->claimed & HID_CLAIMED_HIDDEV) +@@ -1932,6 +2041,10 @@ static int hid_suspend(struct usb_interf + { + struct hid_device *hid = usb_get_intfdata (intf); + ++ spin_lock_irq(&hid->inlock); /* Sync with error handler */ ++ set_bit(HID_SUSPENDED, &hid->iofl); ++ spin_unlock_irq(&hid->inlock); ++ del_timer(&hid->io_retry); + usb_kill_urb(hid->urbin); + dev_dbg(&intf->dev, "suspend\n"); + return 0; +@@ -1942,10 +2055,8 @@ static int hid_resume(struct usb_interfa + struct hid_device *hid = usb_get_intfdata (intf); + int status; + +- if (hid->open) +- status = usb_submit_urb(hid->urbin, GFP_NOIO); +- else +- status = 0; ++ clear_bit(HID_SUSPENDED, &hid->iofl); ++ status = hid_start_in(hid); + dev_dbg(&intf->dev, "resume status %d\n", status); + return status; + } +--- gregkh-2.6.orig/drivers/usb/input/hid.h ++++ gregkh-2.6/drivers/usb/input/hid.h +@@ -31,6 +31,8 @@ + #include <linux/types.h> + #include <linux/slab.h> + #include <linux/list.h> ++#include <linux/timer.h> ++#include <linux/workqueue.h> + + /* + * USB HID (Human Interface Device) interface class code +@@ -370,6 +372,9 @@ struct hid_control_fifo { + + #define HID_CTRL_RUNNING 1 + #define HID_OUT_RUNNING 2 ++#define HID_IN_RUNNING 3 ++#define HID_RESET_PENDING 4 ++#define HID_SUSPENDED 5 + + struct hid_input { + struct list_head list; +@@ -393,12 +398,17 @@ struct hid_device { /* device repo + int ifnum; /* USB interface number */ + + unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ ++ struct timer_list io_retry; /* Retry timer */ ++ unsigned long stop_retry; /* Time to give up, in jiffies */ ++ unsigned int retry_delay; /* Delay length in ms */ ++ struct work_struct reset_work; /* Task context for resets */ + + unsigned int bufsize; /* URB buffer size */ + + struct urb *urbin; /* Input URB */ + char *inbuf; /* Input buffer */ + dma_addr_t inbuf_dma; /* Input buffer dma */ ++ spinlock_t inlock; /* Input fifo spinlock */ + + struct urb *urbctrl; /* Control URB */ + struct usb_ctrlrequest *cr; /* Control request struct */ |