diff options
-rw-r--r-- | Documentation/usb/zc0301.txt | 250 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/media/Kconfig | 15 | ||||
-rw-r--r-- | drivers/usb/media/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/media/zc0301.h | 185 | ||||
-rw-r--r-- | drivers/usb/media/zc0301_core.c | 2028 | ||||
-rw-r--r-- | drivers/usb/media/zc0301_pas202bcb.c | 353 | ||||
-rw-r--r-- | drivers/usb/media/zc0301_sensor.h | 98 |
9 files changed, 2940 insertions, 0 deletions
diff --git a/Documentation/usb/zc0301.txt b/Documentation/usb/zc0301.txt new file mode 100644 index 00000000000..10590bf625e --- /dev/null +++ b/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> diff --git a/MAINTAINERS b/MAINTAINERS index 8db5c339845..ad0d77cbfbc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2902,6 +2902,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/drivers/usb/Makefile b/drivers/usb/Makefile index 36e476dd912..bade87b62cf 100644 --- a/drivers/usb/Makefile +++ b/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/ diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig index 0d3d2cc5d7b..189d40f96be 100644 --- a/drivers/usb/media/Kconfig +++ b/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 diff --git a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile index 3957aa1be0f..333bf09ca2d 100644 --- a/drivers/usb/media/Makefile +++ b/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/ diff --git a/drivers/usb/media/zc0301.h b/drivers/usb/media/zc0301.h new file mode 100644 index 00000000000..6fa5075f1e3 --- /dev/null +++ b/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_ */ diff --git a/drivers/usb/media/zc0301_core.c b/drivers/usb/media/zc0301_core.c new file mode 100644 index 00000000000..238f60f7f47 --- /dev/null +++ b/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_buffe |