diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-10 23:25:31 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-10 23:25:31 -0800 |
commit | 91a2cf51903acbc90f5b4d2453ddcafa09f8d20a (patch) | |
tree | 98226aac3ed024a27055226fec833bc93178de51 /usb | |
parent | 99eb0023004a97a8cfdec3b41328d5ea1de38dd8 (diff) | |
download | patches-91a2cf51903acbc90f5b4d2453ddcafa09f8d20a.tar.gz |
added more patches
Diffstat (limited to 'usb')
-rw-r--r-- | usb/ftdi-two-new-atik-based-usb-astronomical-ccd-cameras.patch | 52 | ||||
-rw-r--r-- | usb/usb-add-et61x51-video4linux2-driver.patch | 3523 | ||||
-rw-r--r-- | usb/usb-ftdi_sio-new-pid-for-pcdj-dac2.patch | 45 | ||||
-rw-r--r-- | usb/usb-usbip-build-fix.patch | 59 | ||||
-rw-r--r-- | usb/usb-usbip-more-dead-code-fix.patch | 40 |
5 files changed, 3719 insertions, 0 deletions
diff --git a/usb/ftdi-two-new-atik-based-usb-astronomical-ccd-cameras.patch b/usb/ftdi-two-new-atik-based-usb-astronomical-ccd-cameras.patch new file mode 100644 index 0000000000000..d7158375a03e1 --- /dev/null +++ b/usb/ftdi-two-new-atik-based-usb-astronomical-ccd-cameras.patch @@ -0,0 +1,52 @@ +From linux-usb-devel-admin@lists.sourceforge.net Mon Jan 9 05:18:20 2006 +Message-ID: <43C26148.5090709@grupopie.com> +From: Rui Santos <rsantos@grupopie.com> +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Rui Tripa <rtripa@perseu.pt>, Steve Chambers <Steve@pmdo.com>, Rui Santos <rsantos@ruisantos.com> +Subject: [linux-usb-devel] [PATCH] ftdi: Two new ATIK based USB astronomical CCD cameras +Date: Mon, 09 Jan 2006 13:12:40 +0000 + +Documentation: Specify grayscale specification on ATIK-ATK16 + and ATIK-ATK16HR comments. +New: Add ProductID and VendorID for devices ATIK-ATK16C and + ATIK-ATK16HRC. These devices are also USB Astronomical CCD + cameras that work through an FTDI 245BM chip, share the + same base hardware but, it has a colour CCD chip instead + of a grayscale one. + +Signed-off-by: Rui Santos <rsantos@grupopie.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/serial/ftdi_sio.c | 2 ++ + drivers/usb/serial/ftdi_sio.h | 6 ++++-- + 2 files changed, 6 insertions(+), 2 deletions(-) + +--- gregkh-2.6.orig/drivers/usb/serial/ftdi_sio.c ++++ gregkh-2.6/drivers/usb/serial/ftdi_sio.c +@@ -476,7 +476,9 @@ static struct usb_device_id id_table_com + { USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HRC_PID) }, + { USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) }, + { USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) }, + { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) }, +--- gregkh-2.6.orig/drivers/usb/serial/ftdi_sio.h ++++ gregkh-2.6/drivers/usb/serial/ftdi_sio.h +@@ -226,8 +226,10 @@ + * Definitions for ATIK Instruments astronomical USB based cameras + * Check it at http://www.atik-instruments.com/ + */ +-#define FTDI_ATIK_ATK16_PID 0xDF30 /* ATIK ATK-16 Camera */ +-#define FTDI_ATIK_ATK16HR_PID 0xDF31 /* ATIK ATK-16HR Camera */ ++#define FTDI_ATIK_ATK16_PID 0xDF30 /* ATIK ATK-16 Grayscale Camera */ ++#define FTDI_ATIK_ATK16C_PID 0xDF32 /* ATIK ATK-16C Colour Camera */ ++#define FTDI_ATIK_ATK16HR_PID 0xDF31 /* ATIK ATK-16HR Grayscale Camera */ ++#define FTDI_ATIK_ATK16HRC_PID 0xDF33 /* ATIK ATK-16HRC Colour Camera */ + + /* + * Protego product ids diff --git a/usb/usb-add-et61x51-video4linux2-driver.patch b/usb/usb-add-et61x51-video4linux2-driver.patch new file mode 100644 index 0000000000000..bbec13a7e53ad --- /dev/null +++ b/usb/usb-add-et61x51-video4linux2-driver.patch @@ -0,0 +1,3523 @@ +From luca.risolia@studio.unibo.it Tue Jan 10 17:01:52 2006 +Date: Wed, 11 Jan 2006 02:06:59 +0000 +From: Luca Risolia <luca.risolia@studio.unibo.it> +To: Greg Kroah-Hartman <gregkh@suse.de> +Cc: Mauro Carvalho Chehab <mchehab@brturbo.com.br>, Laurent Pinchart <laurent.pinchart@skynet.be> +Subject: USB: Add ET61X[12]51 Video4Linux2 driver +Message-ID: <20060111020658.GA6846@studio.unibo.it> +Content-Disposition: inline + +This patch adds a Video4Linux2 driver giving support +to ET61X151 and ET61X251 PC Camera Controllers made by +Etoms Electronics. + +Signed-off-by: Luca Risolia <luca.risolia@studio.unibo.it> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + Documentation/usb/et61x251.txt | 306 +++ + MAINTAINERS | 10 + drivers/usb/Makefile | 1 + drivers/usb/media/Kconfig | 17 + drivers/usb/media/Makefile | 2 + drivers/usb/media/et61x251.h | 220 ++ + drivers/usb/media/et61x251_core.c | 2605 ++++++++++++++++++++++++++++++++ + drivers/usb/media/et61x251_sensor.h | 115 + + drivers/usb/media/et61x251_tas5130d1b.c | 137 + + include/linux/videodev2.h | 1 + 10 files changed, 3412 insertions(+), 2 deletions(-) + +--- /dev/null ++++ gregkh-2.6/Documentation/usb/et61x251.txt +@@ -0,0 +1,306 @@ ++ ++ ET61X[12]51 PC Camera Controllers ++ 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. Optional device control through "sysfs" ++9. Supported devices ++10. Notes for V4L2 application developers ++11. Contact information ++ ++ ++1. Copyright ++============ ++Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> ++ ++ ++2. Disclaimer ++============= ++Etoms is a trademark of Etoms Electronics Corp. ++This software is not developed or sponsored by Etoms Electronics. ++ ++ ++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 ET61X151 ++or ET61X251 PC Camera Controllers. ++ ++It's worth to note that Etoms Electronics has never collaborated with the ++author during the development of this project; despite several requests, ++Etoms Electronics also refused to release enough detailed specifications of ++the video compression engine. ++ ++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 ET61X[12]51 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; ++- support for any window resolutions and optional panning within the maximum ++ pixel area of image sensor; ++- image downscaling with arbitrary scaling factors from 1 and 2 in both ++ directions (see "Notes for V4L2 application developers" paragraph); ++- two different video formats for uncompressed or compressed data in low or ++ high compression quality (see also "Notes for V4L2 application developers" ++ paragraph); ++- full support for the capabilities of every possible image sensors that can ++ be connected to the ET61X[12]51 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 I/O interface for both ET61X[12]51 and image sensor control (see ++ "Optional device control through 'sysfs'" paragraph); ++- 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; ++- no known bugs. ++ ++ ++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 ++ ++To enable advanced debugging functionality on the device through /sysfs: ++ ++ # Multimedia devices ++ # ++ CONFIG_VIDEO_ADV_DEBUG=y ++ ++ # 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 ++ ++And finally: ++ ++ # USB Multimedia devices ++ # ++ CONFIG_USB_ET61X251=m ++ ++ ++6. Module loading ++================= ++To use the driver, it is necessary to load the "et61x251" 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 et61x251 ++ ++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. Optional device control through "sysfs" ++========================================== ++If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, ++it is possible to read and write both the ET61X[12]51 and the image sensor ++registers by using the "sysfs" filesystem interface. ++ ++There are four files in the /sys/class/video4linux/videoX directory for each ++registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files ++control the ET61X[12]51 bridge, while the other two control the sensor chip. ++"reg" and "i2c_reg" hold the values of the current register index where the ++following reading/writing operations are addressed at through "val" and ++"i2c_val". Their use is not intended for end-users, unless you know what you ++are doing. Remember that you must be logged in as root before writing to them. ++ ++As an example, suppose we were to want to read the value contained in the ++register number 1 of the sensor register table - which is usually the product ++identifier - of the camera registered as "/dev/video0": ++ ++ [root@localhost #] cd /sys/class/video4linux/video0 ++ [root@localhost #] echo 1 > i2c_reg ++ [root@localhost #] cat i2c_val ++ ++Note that if the sensor registers can not be read, "cat" will fail. ++To avoid race conditions, all the I/O accesses to the files are serialized. ++ ++ ++9. 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 ET61X[12]51 PC camera controllers: ++ ++Vendor ID Product ID ++--------- ---------- ++0x102c 0x6151 ++0x102c 0x6251 ++0x102c 0x6253 ++0x102c 0x6254 ++0x102c 0x6255 ++0x102c 0x6256 ++0x102c 0x6257 ++0x102c 0x6258 ++0x102c 0x6259 ++0x102c 0x625a ++0x102c 0x625b ++0x102c 0x625c ++0x102c 0x625d ++0x102c 0x625e ++0x102c 0x625f ++0x102c 0x6260 ++0x102c 0x6261 ++0x102c 0x6262 ++0x102c 0x6263 ++0x102c 0x6264 ++0x102c 0x6265 ++0x102c 0x6266 ++0x102c 0x6267 ++0x102c 0x6268 ++0x102c 0x6269 ++ ++The following image sensors are supported: ++ ++Model Manufacturer ++----- ------------ ++TAS5130D1B Taiwan Advanced Sensor Corporation ++ ++All the available control settings of each image sensor are supported through ++the V4L2 interface. ++ ++ ++10. 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. ++ ++Consistently with the hardware limits, this driver also supports image ++downscaling with arbitrary scaling factors from 1 and 2 in both directions. ++However, the V4L2 API specifications don't correctly define how the scaling ++factor can be chosen arbitrarily by the "negotiation" of the "source" and ++"target" rectangles. To work around this flaw, we have added the convention ++that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the ++scaling factor is restored to 1. ++ ++This driver supports two different video formats: the first one is the "8-bit ++Sequential Bayer" format and can be used to obtain uncompressed video data ++from the device through the current I/O method, while the second one provides ++"raw" compressed video data (without frame headers not related to the ++compressed data). The current compression quality may vary from 0 to 1 and can ++be selected or queried thanks to the VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP ++V4L2 ioctl's. ++ ++ ++11. 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'. +--- gregkh-2.6.orig/drivers/usb/Makefile ++++ gregkh-2.6/drivers/usb/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_XPAD) += input/ + + obj-$(CONFIG_USB_DABUSB) += media/ + obj-$(CONFIG_USB_DSBR) += media/ ++obj-$(CONFIG_USB_ET61X251) += media/ + obj-$(CONFIG_USB_IBMCAM) += media/ + obj-$(CONFIG_USB_KONICAWC) += media/ + obj-$(CONFIG_USB_OV511) += media/ +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/et61x251_core.c +@@ -0,0 +1,2605 @@ ++/*************************************************************************** ++ * V4L2 driver for ET61X[12]51 PC Camera Controllers * ++ * * ++ * 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. * ++ ***************************************************************************/ ++ ++#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 "et61x251.h" ++ ++/*****************************************************************************/ ++ ++#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ ++ "PC Camera Controllers" ++#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" ++#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" ++#define ET61X251_MODULE_LICENSE "GPL" ++#define ET61X251_MODULE_VERSION "1:1.01" ++#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) ++ ++/*****************************************************************************/ ++ ++MODULE_DEVICE_TABLE(usb, et61x251_id_table); ++ ++MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL); ++MODULE_DESCRIPTION(ET61X251_MODULE_NAME); ++MODULE_VERSION(ET61X251_MODULE_VERSION); ++MODULE_LICENSE(ET61X251_MODULE_LICENSE); ++ ++static short video_nr[] = {[0 ... ET61X251_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(ET61X251_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 ... ET61X251_MAX_DEVICES-1] = ++ ET61X251_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 ET61X251_DEBUG ++static unsigned short debug = ET61X251_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(ET61X251_DEBUG_LEVEL)"." ++ "\n"); ++#endif ++ ++/*****************************************************************************/ ++ ++static u32 ++et61x251_request_buffers(struct et61x251_device* cam, u32 count, ++ enum et61x251_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 > ET61X251_MAX_FRAMES) ++ count = ET61X251_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 et61x251_release_buffers(struct et61x251_device* cam) ++{ ++ if (cam->nbuffers) { ++ vfree(cam->frame[0].bufmem); ++ cam->nbuffers = 0; ++ } ++ cam->frame_current = NULL; ++} ++ ++ ++static void et61x251_empty_framequeues(struct et61x251_device* cam) ++{ ++ u32 i; ++ ++ INIT_LIST_HEAD(&cam->inqueue); ++ INIT_LIST_HEAD(&cam->outqueue); ++ ++ for (i = 0; i < ET61X251_MAX_FRAMES; i++) { ++ cam->frame[i].state = F_UNUSED; ++ cam->frame[i].buf.bytesused = 0; ++ } ++} ++ ++ ++static void et61x251_requeue_outqueue(struct et61x251_device* cam) ++{ ++ struct et61x251_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 et61x251_queue_unusedframes(struct et61x251_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 et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index) ++{ ++ struct usb_device* udev = cam->usbdev; ++ u8* buff = cam->control_buffer; ++ int res; ++ ++ *buff = value; ++ ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) { ++ DBG(3, "Failed to write a register (value 0x%02X, index " ++ "0x%02X, error %d)", value, index, res); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++int et61x251_read_reg(struct et61x251_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), 0x00, 0xc1, ++ 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ DBG(3, "Failed to read a register (index 0x%02X, error %d)", ++ index, res); ++ ++ return (res >= 0) ? (int)(*buff) : -1; ++} ++ ++ ++static int ++et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor) ++{ ++ int i, r; ++ ++ for (i = 1; i <= 8; i++) { ++ if (sensor->interface == ET61X251_I2C_3WIRES) { ++ r = et61x251_read_reg(cam, 0x8e); ++ if (!(r & 0x02) && (r >= 0)) ++ return 0; ++ } else { ++ r = et61x251_read_reg(cam, 0x8b); ++ if (!(r & 0x01) && (r >= 0)) ++ return 0; ++ } ++ if (r < 0) ++ return -EIO; ++ udelay(8*8); /* minimum for sensors at 400kHz */ ++ } ++ ++ return -EBUSY; ++} ++ ++ ++int ++et61x251_i2c_try_read(struct et61x251_device* cam, ++ struct et61x251_sensor* sensor, u8 address) ++{ ++ struct usb_device* udev = cam->usbdev; ++ u8* data = cam->control_buffer; ++ int err = 0, res; ++ ++ data[0] = address; ++ data[1] = cam->sensor->i2c_slave_id; ++ data[2] = cam->sensor->rsta | 0x10; ++ data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ err += et61x251_i2c_wait(cam, sensor); ++ ++ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, ++ 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ if (err) ++ DBG(3, "I2C read failed for %s image sensor", sensor->name); ++ ++ PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]); ++ ++ return err ? -1 : (int)data[0]; ++} ++ ++ ++int ++et61x251_i2c_try_write(struct et61x251_device* cam, ++ struct et61x251_sensor* sensor, u8 address, u8 value) ++{ ++ struct usb_device* udev = cam->usbdev; ++ u8* data = cam->control_buffer; ++ int err = 0, res; ++ ++ data[0] = address; ++ data[1] = cam->sensor->i2c_slave_id; ++ data[2] = cam->sensor->rsta | 0x12; ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ data[0] = value; ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ err += et61x251_i2c_wait(cam, sensor); ++ ++ if (err) ++ DBG(3, "I2C write failed for %s image sensor", sensor->name); ++ ++ PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value); ++ ++ return err ? -1 : 0; ++} ++ ++ ++int ++et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, ++ u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, ++ u8 data8, u8 address) ++{ ++ struct usb_device* udev = cam->usbdev; ++ u8* data = cam->control_buffer; ++ int err = 0, res; ++ ++ if (!cam->sensor) ++ return -1; ++ ++ data[0] = data2; ++ data[1] = data3; ++ data[2] = data4; ++ data[3] = data5; ++ data[4] = data6; ++ data[5] = data7; ++ data[6] = data8; ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ data[0] = address; ++ data[1] = cam->sensor->i2c_slave_id; ++ data[2] = cam->sensor->rsta | 0x02 | (n << 4); ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ /* Start writing through the serial interface */ ++ data[0] = data1; ++ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, ++ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); ++ if (res < 0) ++ err += res; ++ ++ err += et61x251_i2c_wait(cam, cam->sensor); ++ ++ if (err) ++ DBG(3, "I2C raw write failed for %s image sensor", ++ cam->sensor->name); ++ ++ PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " ++ "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," ++ " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address, ++ data1, data2, data3, data4, data5, data6, data7, data8); ++ ++ return err ? -1 : 0; ++ ++} ++ ++ ++int et61x251_i2c_read(struct et61x251_device* cam, u8 address) ++{ ++ if (!cam->sensor) ++ return -1; ++ ++ return et61x251_i2c_try_read(cam, cam->sensor, address); ++} ++ ++ ++int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value) ++{ ++ if (!cam->sensor) ++ return -1; ++ ++ return et61x251_i2c_try_write(cam, cam->sensor, address, value); ++} ++ ++/*****************************************************************************/ ++ ++static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs) ++{ ++ struct et61x251_device* cam = urb->context; ++ struct et61x251_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 et61x251_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; ++ u8* b1, * b2, sof; ++ const u8 VOID_BYTES = 6; ++ size_t imglen; ++ ++ 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; ++ } ++ ++ b1 = pos++; ++ b2 = pos++; ++ sof = ((*b1 & 0x3f) == 63); ++ imglen = ((*b1 & 0xc0) << 2) | *b2; ++ ++ PDBGG("Isochrnous frame: length %u, #%u i, image length %zu", ++ len, i, imglen); ++ ++ 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); ++ pos += 22; ++ DBG(3, "SOF detected: new video frame"); ++ } ++ ++ if ((*f)->state == F_GRABBING) { ++ if (sof && (*f)->buf.bytesused) { ++ if (cam->sensor->pix_format.pixelformat == ++ V4L2_PIX_FMT_ET61X251) ++ goto end_of_frame; ++ else { ++ DBG(3, "Not expected SOF detected " ++ "after %lu bytes", ++ (unsigned long)(*f)->buf.bytesused); ++ (*f)->state = F_ERROR; ++ continue; ++ } ++ } ++ ++ if ((*f)->buf.bytesused + imglen > imagesize) { ++ DBG(3, "Video frame size exceeded"); ++ (*f)->state = F_ERROR; ++ continue; ++ } ++ ++ pos += VOID_BYTES; ++ ++ memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen); ++ (*f)->buf.bytesused += imglen; ++ ++ 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 et61x251_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 && ++ cam->sensor->pix_format.pixelformat == ++ V4L2_PIX_FMT_ET61X251) ++ 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 et61x251_start_transfer(struct et61x251_device* cam) ++{ ++ struct usb_device *udev = cam->usbdev; ++ struct urb* urb; ++ const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832, ++ 864, 896, 920, 956, 980, 1000, ++ 1022}; ++ const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING]; ++ s8 i, j; ++ int err = 0; ++ ++ for (i = 0; i < ET61X251_URBS; i++) { ++ cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz, ++ GFP_KERNEL); ++ if (!cam->transfer_buffer[i]) { ++ err = -ENOMEM; ++ DBG(1, "Not enough memory"); ++ goto free_buffers; ++ } ++ } ++ ++ for (i = 0; i < ET61X251_URBS; i++) { ++ urb = usb_alloc_urb(ET61X251_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 = ET61X251_ISO_PACKETS; ++ urb->complete = et61x251_urb_complete; ++ urb->transfer_buffer = cam->transfer_buffer[i]; ++ urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS; ++ urb->interval = 1; ++ for (j = 0; j < ET61X251_ISO_PACKETS; j++) { ++ urb->iso_frame_desc[j].offset = psz * j; ++ urb->iso_frame_desc[j].length = psz; ++ } ++ } ++ ++ err = et61x251_write_reg(cam, 0x01, 0x03); ++ err = et61x251_write_reg(cam, 0x00, 0x03); ++ err = et61x251_write_reg(cam, 0x08, 0x03); ++ if (err) { ++ err = -EIO; ++ DBG(1, "I/O hardware error"); ++ goto free_urbs; ++ } ++ ++ err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING); ++ if (err) { ++ DBG(1, "usb_set_interface() failed"); ++ goto free_urbs; ++ } ++ ++ cam->frame_current = NULL; ++ ++ for (i = 0; i < ET61X251_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 < ET61X251_URBS) && cam->urb[i]; i++) ++ usb_free_urb(cam->urb[i]); ++ ++free_buffers: ++ for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++) ++ kfree(cam->transfer_buffer[i]); ++ ++ return err; ++} ++ ++ ++static int et61x251_stop_transfer(struct et61x251_device* cam) ++{ ++ struct usb_device *udev = cam->usbdev; ++ s8 i; ++ int err = 0; ++ ++ if (cam->state & DEV_DISCONNECTED) ++ return 0; ++ ++ for (i = ET61X251_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 et61x251_stream_interrupt(struct et61x251_device* cam) ++{ ++ int err = 0; ++ ++ cam->stream = STREAM_INTERRUPT; ++ err = wait_event_timeout(cam->wait_stream, ++ (cam->stream == STREAM_OFF) || ++ (cam->state & DEV_DISCONNECTED), ++ ET61X251_URB_TIMEOUT); ++ if (cam->state & DEV_DISCONNECTED) ++ return -ENODEV; ++ else if (err) { ++ 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 err; ++ } ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) ++{ ++ char str[5]; ++ char* endp; ++ unsigned long val; ++ ++ if (len < 4) { ++ strncpy(str, buff, len); ++ str[len+1] = '\0'; ++ } else { ++ strncpy(str, buff, 4); ++ str[4] = '\0'; ++ } ++ ++ val = simple_strtoul(str, &endp, 0); ++ ++ *count = 0; ++ if (val <= 0xff) ++ *count = (ssize_t)(endp - str); ++ if ((*count) && (len == *count+1) && (buff[*count] == '\n')) ++ *count += 1; ++ ++ return (u8)val; ++} ++ ++/* ++ NOTE 1: being inside one of the following methods implies that the v4l ++ device exists for sure (see kobjects and reference counters) ++ NOTE 2: buffers are PAGE_SIZE long ++*/ ++ ++static ssize_t et61x251_show_reg(struct class_device* cd, char* buf) ++{ ++ struct et61x251_device* cam; ++ ssize_t count; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ count = sprintf(buf, "%u\n", cam->sysfs.reg); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t ++et61x251_store_reg(struct class_device* cd, const char* buf, size_t len) ++{ ++ struct et61x251_device* cam; ++ u8 index; ++ ssize_t count; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ index = et61x251_strtou8(buf, len, &count); ++ if (index > 0x8e || !count) { ++ up(&et61x251_sysfs_lock); ++ return -EINVAL; ++ } ++ ++ cam->sysfs.reg = index; ++ ++ DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); ++ DBG(3, "Written bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t et61x251_show_val(struct class_device* cd, char* buf) ++{ ++ struct et61x251_device* cam; ++ ssize_t count; ++ int val; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { ++ up(&et61x251_sysfs_lock); ++ return -EIO; ++ } ++ ++ count = sprintf(buf, "%d\n", val); ++ ++ DBG(3, "Read bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t ++et61x251_store_val(struct class_device* cd, const char* buf, size_t len) ++{ ++ struct et61x251_device* cam; ++ u8 value; ++ ssize_t count; ++ int err; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ value = et61x251_strtou8(buf, len, &count); ++ if (!count) { ++ up(&et61x251_sysfs_lock); ++ return -EINVAL; ++ } ++ ++ err = et61x251_write_reg(cam, value, cam->sysfs.reg); ++ if (err) { ++ up(&et61x251_sysfs_lock); ++ return -EIO; ++ } ++ ++ DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X", ++ cam->sysfs.reg, value); ++ DBG(3, "Written bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf) ++{ ++ struct et61x251_device* cam; ++ ssize_t count; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); ++ ++ DBG(3, "Read bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t ++et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) ++{ ++ struct et61x251_device* cam; ++ u8 index; ++ ssize_t count; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ index = et61x251_strtou8(buf, len, &count); ++ if (!count) { ++ up(&et61x251_sysfs_lock); ++ return -EINVAL; ++ } ++ ++ cam->sysfs.i2c_reg = index; ++ ++ DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); ++ DBG(3, "Written bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf) ++{ ++ struct et61x251_device* cam; ++ ssize_t count; ++ int val; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) { ++ up(&et61x251_sysfs_lock); ++ return -ENOSYS; ++ } ++ ++ if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { ++ up(&et61x251_sysfs_lock); ++ return -EIO; ++ } ++ ++ count = sprintf(buf, "%d\n", val); ++ ++ DBG(3, "Read bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static ssize_t ++et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len) ++{ ++ struct et61x251_device* cam; ++ u8 value; ++ ssize_t count; ++ int err; ++ ++ if (down_interruptible(&et61x251_sysfs_lock)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(to_video_device(cd)); ++ if (!cam) { ++ up(&et61x251_sysfs_lock); ++ return -ENODEV; ++ } ++ ++ if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) { ++ up(&et61x251_sysfs_lock); ++ return -ENOSYS; ++ } ++ ++ value = et61x251_strtou8(buf, len, &count); ++ if (!count) { ++ up(&et61x251_sysfs_lock); ++ return -EINVAL; ++ } ++ ++ err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); ++ if (err) { ++ up(&et61x251_sysfs_lock); ++ return -EIO; ++ } ++ ++ DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", ++ cam->sysfs.i2c_reg, value); ++ DBG(3, "Written bytes: %zd", count); ++ ++ up(&et61x251_sysfs_lock); ++ ++ return count; ++} ++ ++ ++static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, ++ et61x251_show_reg, et61x251_store_reg); ++static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR, ++ et61x251_show_val, et61x251_store_val); ++static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, ++ et61x251_show_i2c_reg, et61x251_store_i2c_reg); ++static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, ++ et61x251_show_i2c_val, et61x251_store_i2c_val); ++ ++ ++static void et61x251_create_sysfs(struct et61x251_device* cam) ++{ ++ struct video_device *v4ldev = cam->v4ldev; ++ ++ video_device_create_file(v4ldev, &class_device_attr_reg); ++ video_device_create_file(v4ldev, &class_device_attr_val); ++ if (cam->sensor && cam->sensor->sysfs_ops) { ++ video_device_create_file(v4ldev, &class_device_attr_i2c_reg); ++ video_device_create_file(v4ldev, &class_device_attr_i2c_val); ++ } ++} ++#endif /* CONFIG_VIDEO_ADV_DEBUG */ ++ ++/*****************************************************************************/ ++ ++static int ++et61x251_set_pix_format(struct et61x251_device* cam, ++ struct v4l2_pix_format* pix) ++{ ++ int r, err = 0; ++ ++ if ((r = et61x251_read_reg(cam, 0x12)) < 0) ++ err += r; ++ if (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ++ err += et61x251_write_reg(cam, r & 0xfd, 0x12); ++ else ++ err += et61x251_write_reg(cam, r | 0x02, 0x12); ++ ++ return err ? -EIO : 0; ++} ++ ++ ++static int ++et61x251_set_compression(struct et61x251_device* cam, ++ struct v4l2_jpegcompression* compression) ++{ ++ int r, err = 0; ++ ++ if ((r = et61x251_read_reg(cam, 0x12)) < 0) ++ err += r; ++ if (compression->quality == 0) ++ err += et61x251_write_reg(cam, r & 0xfb, 0x12); ++ else ++ err += et61x251_write_reg(cam, r | 0x04, 0x12); ++ ++ return err ? -EIO : 0; ++} ++ ++ ++static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) ++{ ++ int r = 0, err = 0; ++ ++ r = et61x251_read_reg(cam, 0x12); ++ if (r < 0) ++ err += r; ++ ++ if (scale == 1) ++ err += et61x251_write_reg(cam, r & ~0x01, 0x12); ++ else if (scale == 2) ++ err += et61x251_write_reg(cam, r | 0x01, 0x12); ++ ++ if (err) ++ return -EIO; ++ ++ PDBGG("Scaling factor: %u", scale); ++ ++ return 0; ++} ++ ++ ++static int ++et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) ++{ ++ struct et61x251_sensor* s = cam->sensor; ++ u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + ++ s->active_pixel.left), ++ fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + ++ s->active_pixel.top), ++ fmw_length = (u16)(rect->width), ++ fmw_height = (u16)(rect->height); ++ int err = 0; ++ ++ err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69); ++ err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a); ++ err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b); ++ err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c); ++ err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6) ++ | ((fmw_length & 0x300) >> 4) ++ | ((fmw_height & 0x300) >> 2), 0x6d); ++ if (err) ++ return -EIO; ++ ++ PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u", ++ fmw_sx, fmw_sy, fmw_length, fmw_height); ++ ++ return 0; ++} ++ ++ ++static int et61x251_init(struct et61x251_device* cam) ++{ ++ struct et61x251_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 = ET61X251_COMPRESSION_QUALITY; ++ } else { /* use current values */ ++ qctrl = s->_qctrl; ++ rect = &(s->_rect); ++ } ++ ++ err += et61x251_set_scale(cam, rect->width / s->pix_format.width); ++ err += et61x251_set_crop(cam, rect); ++ if (err) ++ return err; ++ ++ if (s->init) { ++ err = s->init(cam); ++ if (err) { ++ DBG(3, "Sensor initialization failed"); ++ return err; ++ } ++ } ++ ++ err += et61x251_set_compression(cam, &cam->compression); ++ err += et61x251_set_pix_format(cam, &s->pix_format); ++ if (s->set_pix_format) ++ err += s->set_pix_format(cam, &s->pix_format); ++ if (err) ++ return err; ++ ++ if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) ++ DBG(3, "Compressed video format is active, quality %d", ++ cam->compression.quality); ++ else ++ DBG(3, "Uncompressed video format is active"); ++ ++ 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)) { ++ init_MUTEX(&cam->fileop_sem); ++ 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 et61x251_release_resources(struct et61x251_device* cam) ++{ ++ down(&et61x251_sysfs_lock); ++ ++ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); ++ video_set_drvdata(cam->v4ldev, NULL); ++ video_unregister_device(cam->v4ldev); ++ ++ up(&et61x251_sysfs_lock); ++ ++ kfree(cam->control_buffer); ++} ++ ++/*****************************************************************************/ ++ ++static int et61x251_open(struct inode* inode, struct file* filp) ++{ ++ struct et61x251_device* cam; ++ int err = 0; ++ ++ /* ++ This is the only safe way to prevent race conditions with ++ disconnect ++ */ ++ if (!down_read_trylock(&et61x251_disconnect)) ++ return -ERESTARTSYS; ++ ++ cam = video_get_drvdata(video_devdata(filp)); ++ ++ if (down_interruptible(&cam->dev_sem)) { ++ up_read(&et61x251_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; ++ } ++ up(&cam->dev_sem); ++ err = wait_event_interruptible_exclusive(cam->open, ++ cam->state & DEV_DISCONNECTED ++ || !cam->users); ++ if (err) { ++ up_read(&et61x251_disconnect); ++ return err; ++ } ++ if (cam->state & DEV_DISCONNECTED) { ++ up_read(&et61x251_disconnect); ++ return -ENODEV; ++ } ++ down(&cam->dev_sem); ++ } ++ ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ err = et61x251_init(cam); ++ if (err) { ++ DBG(1, "Initialization failed again. " ++ "I will retry on next open()."); ++ goto out; ++ } ++ cam->state &= ~DEV_MISCONFIGURED; ++ } ++ ++ if ((err = et61x251_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; ++ et61x251_empty_framequeues(cam); ++ ++ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); ++ ++out: ++ up(&cam->dev_sem); ++ up_read(&et61x251_disconnect); ++ return err; ++} ++ ++ ++static int et61x251_release(struct inode* inode, struct file* filp) ++{ ++ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); ++ ++ down(&cam->dev_sem); /* prevent disconnect() to be called */ ++ ++ et61x251_stop_transfer(cam); ++ ++ et61x251_release_buffers(cam); ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ et61x251_release_resources(cam); ++ up(&cam->dev_sem); ++ kfree(cam); ++ return 0; ++ } ++ ++ cam->users--; ++ wake_up_interruptible_nr(&cam->open, 1); ++ ++ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); ++ ++ up(&cam->dev_sem); ++ ++ return 0; ++} ++ ++ ++static ssize_t ++et61x251_read(struct file* filp, char __user * buf, ++ size_t count, loff_t* f_pos) ++{ ++ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); ++ struct et61x251_frame_t* f, * i; ++ unsigned long lock_flags; ++ int err = 0; ++ ++ if (down_interruptible(&cam->fileop_sem)) ++ return -ERESTARTSYS; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ up(&cam->fileop_sem); ++ return -ENODEV; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ up(&cam->fileop_sem); ++ return -EIO; ++ } ++ ++ if (cam->io == IO_MMAP) { ++ DBG(3, "Close and open the device again to choose the read " ++ "method"); ++ up(&cam->fileop_sem); ++ return -EINVAL; ++ } ++ ++ if (cam->io == IO_NONE) { ++ if (!et61x251_request_buffers(cam, cam->nreadbuffers, ++ IO_READ)) { ++ DBG(1, "read() failed, not enough memory"); ++ up(&cam->fileop_sem); ++ return -ENOMEM; ++ } ++ cam->io = IO_READ; ++ cam->stream = STREAM_ON; ++ } ++ ++ if (list_empty(&cam->inqueue)) { ++ if (!list_empty(&cam->outqueue)) ++ et61x251_empty_framequeues(cam); ++ et61x251_queue_unusedframes(cam); ++ } ++ ++ if (!count) { ++ up(&cam->fileop_sem); ++ return 0; ++ } ++ ++ if (list_empty(&cam->outqueue)) { ++ if (filp->f_flags & O_NONBLOCK) { ++ up(&cam->fileop_sem); ++ return -EAGAIN; ++ } ++ err = wait_event_interruptible ++ ( cam->wait_frame, ++ (!list_empty(&cam->outqueue)) || ++ (cam->state & DEV_DISCONNECTED) || ++ (cam->state & DEV_MISCONFIGURED) ); ++ if (err) { ++ up(&cam->fileop_sem); ++ return err; ++ } ++ if (cam->state & DEV_DISCONNECTED) { ++ up(&cam->fileop_sem); ++ return -ENODEV; ++ } ++ if (cam->state & DEV_MISCONFIGURED) { ++ up(&cam->fileop_sem); ++ return -EIO; ++ } ++ } ++ ++ f = list_entry(cam->outqueue.prev, struct et61x251_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); ++ ++ et61x251_queue_unusedframes(cam); ++ ++ PDBGG("Frame #%lu, bytes read: %zu", ++ (unsigned long)f->buf.index, count); ++ ++ up(&cam->fileop_sem); ++ ++ return err ? err : count; ++} ++ ++ ++static unsigned int et61x251_poll(struct file *filp, poll_table *wait) ++{ ++ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); ++ struct et61x251_frame_t* f; ++ unsigned long lock_flags; ++ unsigned int mask = 0; ++ ++ if (down_interruptible(&cam->fileop_sem)) ++ 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 (!et61x251_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); ++ et61x251_queue_unusedframes(cam); ++ } ++ ++ poll_wait(filp, &cam->wait_frame, wait); ++ ++ if (!list_empty(&cam->outqueue)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ up(&cam->fileop_sem); ++ ++ return mask; ++ ++error: ++ up(&cam->fileop_sem); ++ return POLLERR; ++} ++ ++ ++static void et61x251_vm_open(struct vm_area_struct* vma) ++{ ++ struct et61x251_frame_t* f = vma->vm_private_data; ++ f->vma_use_count++; ++} ++ ++ ++static void et61x251_vm_close(struct vm_area_struct* vma) ++{ ++ /* NOTE: buffers are not freed here */ ++ struct et61x251_frame_t* f = vma->vm_private_data; ++ f->vma_use_count--; ++} ++ ++ ++static struct vm_operations_struct et61x251_vm_ops = { ++ .open = et61x251_vm_open, ++ .close = et61x251_vm_close, ++}; ++ ++ ++static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) ++{ ++ struct et61x251_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 (down_interruptible(&cam->fileop_sem)) ++ return -ERESTARTSYS; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ up(&cam->fileop_sem); ++ return -ENODEV; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ up(&cam->fileop_sem); ++ return -EIO; ++ } ++ ++ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || ++ size != PAGE_ALIGN(cam->frame[0].buf.length)) { ++ up(&cam->fileop_sem); ++ 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) { ++ up(&cam->fileop_sem); ++ 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))) { ++ up(&cam->fileop_sem); ++ return -EAGAIN; ++ } ++ start += PAGE_SIZE; ++ pos += PAGE_SIZE; ++ size -= PAGE_SIZE; ++ } ++ ++ vma->vm_ops = &et61x251_vm_ops; ++ vma->vm_private_data = &cam->frame[i]; ++ ++ et61x251_vm_open(vma); ++ ++ up(&cam->fileop_sem); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++static int ++et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) ++{ ++ struct v4l2_capability cap = { ++ .driver = "et61x251", ++ .version = ET61X251_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 ++et61x251_vidioc_enuminput(struct et61x251_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 ++et61x251_vidioc_gs_input(struct et61x251_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 ++et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) ++{ ++ struct et61x251_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 ++et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) ++{ ++ struct et61x251_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 ++et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) ++{ ++ struct et61x251_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 ++et61x251_vidioc_cropcap(struct et61x251_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 ++et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) ++{ ++ struct et61x251_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 ++et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) ++{ ++ struct et61x251_sensor* s = cam->sensor; ++ struct v4l2_crop crop; ++ struct v4l2_rect* rect; ++ struct v4l2_rect* bounds = &(s->cropcap.bounds); ++ struct v4l2_pix_format* pix_format = &(s->pix_format); ++ u8 scale; ++ const enum et61x251_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; ++ } ++ ++ /* Preserve R,G or B origin */ ++ rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; ++ rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; ++ ++ if (rect->width < 4) ++ rect->width = 4; ++ if (rect->height < 4) ++ rect->height = 4; ++ 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 &= ~3L; ++ rect->height &= ~3L; ++ ++ if (ET61X251_PRESERVE_IMGSCALE) { ++ /* Calculate the actual scaling factor */ ++ u32 a, b; ++ a = rect->width * rect->height; ++ b = pix_format->width * pix_format->height; ++ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; ++ } else ++ scale = 1; ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = et61x251_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) ++ et61x251_release_buffers(cam); ++ ++ err = et61x251_set_crop(cam, rect); ++ if (s->set_crop) ++ err += s->set_crop(cam, rect); ++ err += et61x251_set_scale(cam, scale); ++ ++ 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/scale; ++ s->pix_format.height = rect->height/scale; ++ memcpy(&(s->_rect), rect, sizeof(*rect)); ++ ++ if ((cam->module_param.force_munmap || cam->io == IO_READ) && ++ nbuffers != et61x251_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) ++ et61x251_empty_framequeues(cam); ++ else if (cam->module_param.force_munmap) ++ et61x251_requeue_outqueue(cam); ++ ++ cam->stream = stream; ++ ++ return 0; ++} ++ ++ ++static int ++et61x251_vidioc_enum_fmt(struct et61x251_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, "bayer rgb"); ++ fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; ++ } else if (fmtd.index == 1) { ++ strcpy(fmtd.description, "compressed"); ++ fmtd.pixelformat = V4L2_PIX_FMT_ET61X251; ++ 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 ++et61x251_vidioc_g_fmt(struct et61x251_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 = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) ++ ? 0 : (pfmt->width * pfmt->priv) / 8; ++ 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 ++et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, ++ void __user * arg) ++{ ++ struct et61x251_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; ++ u8 scale; ++ const enum et61x251_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)); ++ ++ { /* calculate the actual scaling factor */ ++ u32 a, b; ++ a = rect.width * rect.height; ++ b = pix->width * pix->height; ++ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; ++ } ++ ++ rect.width = scale * pix->width; ++ rect.height = scale * pix->height; ++ ++ if (rect.width < 4) ++ rect.width = 4; ++ if (rect.height < 4) ++ rect.height = 4; ++ 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 &= ~3L; ++ rect.height &= ~3L; ++ ++ { /* adjust the scaling factor */ ++ u32 a, b; ++ a = rect.width * rect.height; ++ b = pix->width * pix->height; ++ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; ++ } ++ ++ pix->width = rect.width / scale; ++ pix->height = rect.height / scale; ++ ++ if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 && ++ pix->pixelformat != V4L2_PIX_FMT_SBGGR8) ++ pix->pixelformat = pfmt->pixelformat; ++ pix->priv = pfmt->priv; /* bpp */ ++ pix->colorspace = pfmt->colorspace; ++ pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ++ ? 0 : (pix->width * pix->priv) / 8; ++ 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 = et61x251_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) ++ et61x251_release_buffers(cam); ++ ++ err += et61x251_set_pix_format(cam, pix); ++ err += et61x251_set_crop(cam, &rect); ++ if (s->set_pix_format) ++ err += s->set_pix_format(cam, pix); ++ if (s->set_crop) ++ err += s->set_crop(cam, &rect); ++ err += et61x251_set_scale(cam, scale); ++ ++ 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 != et61x251_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) ++ et61x251_empty_framequeues(cam); ++ else if (cam->module_param.force_munmap) ++ et61x251_requeue_outqueue(cam); ++ ++ cam->stream = stream; ++ ++ return 0; ++} ++ ++ ++static int ++et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg) ++{ ++ if (copy_to_user(arg, &cam->compression, ++ sizeof(cam->compression))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ++et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg) ++{ ++ struct v4l2_jpegcompression jc; ++ const enum et61x251_stream_state stream = cam->stream; ++ int err = 0; ++ ++ if (copy_from_user(&jc, arg, sizeof(jc))) ++ return -EFAULT; ++ ++ if (jc.quality != 0 && jc.quality != 1) ++ return -EINVAL; ++ ++ if (cam->stream == STREAM_ON) ++ if ((err = et61x251_stream_interrupt(cam))) ++ return err; ++ ++ err += et61x251_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 ++et61x251_vidioc_reqbufs(struct et61x251_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 = et61x251_stream_interrupt(cam))) ++ return err; ++ ++ et61x251_empty_framequeues(cam); ++ ++ et61x251_release_buffers(cam); ++ if (rb.count) ++ rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP); ++ ++ if (copy_to_user(arg, &rb, sizeof(rb))) { ++ et61x251_release_buffers(cam); ++ cam->io = IO_NONE; ++ return -EFAULT; ++ } ++ ++ cam->io = rb.count ? IO_MMAP : IO_NONE; ++ ++ return 0; ++} ++ ++ ++static int ++et61x251_vidioc_querybuf(struct et61x251_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 ++et61x251_vidioc_qbuf(struct et61x251_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 ++et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, ++ void __user * arg) ++{ ++ struct v4l2_buffer b; ++ struct et61x251_frame_t *f; ++ unsigned long lock_flags; ++ int err = 0; ++ ++ 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; ++ err = wait_event_interruptible ++ ( cam->wait_frame, ++ (!list_empty(&cam->outqueue)) || ++ (cam->state & DEV_DISCONNECTED) || ++ (cam->state & DEV_MISCONFIGURED) ); ++ if (err) ++ return err; ++ if (cam->state & DEV_DISCONNECTED) ++ return -ENODEV; ++ if (cam->state & DEV_MISCONFIGURED) ++ return -EIO; ++ } ++ ++ spin_lock_irqsave(&cam->queue_lock, lock_flags); ++ f = list_entry(cam->outqueue.next, struct et61x251_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 ++et61x251_vidioc_streamon(struct et61x251_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 ++et61x251_vidioc_streamoff(struct et61x251_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 = et61x251_stream_interrupt(cam))) ++ return err; ++ ++ et61x251_empty_framequeues(cam); ++ ++ DBG(3, "Stream off"); ++ ++ return 0; ++} ++ ++ ++static int ++et61x251_vidioc_g_parm(struct et61x251_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 ++et61x251_vidioc_s_parm(struct et61x251_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 > ET61X251_MAX_FRAMES) ++ sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES; ++ ++ if (copy_to_user(arg, &sp, sizeof(sp))) ++ return -EFAULT; ++ ++ cam->nreadbuffers = sp.parm.capture.readbuffers; ++ ++ return 0; ++} ++ ++ ++static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, ++ unsigned int cmd, void __user * arg) ++{ ++ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); ++ ++ switch (cmd) { ++ ++ case VIDIOC_QUERYCAP: ++ return et61x251_vidioc_querycap(cam, arg); ++ ++ case VIDIOC_ENUMINPUT: ++ return et61x251_vidioc_enuminput(cam, arg); ++ ++ case VIDIOC_G_INPUT: ++ case VIDIOC_S_INPUT: ++ return et61x251_vidioc_gs_input(cam, arg); ++ ++ case VIDIOC_QUERYCTRL: ++ return et61x251_vidioc_query_ctrl(cam, arg); ++ ++ case VIDIOC_G_CTRL: ++ return et61x251_vidioc_g_ctrl(cam, arg); ++ ++ case VIDIOC_S_CTRL_OLD: ++ case VIDIOC_S_CTRL: ++ return et61x251_vidioc_s_ctrl(cam, arg); ++ ++ case VIDIOC_CROPCAP_OLD: ++ case VIDIOC_CROPCAP: ++ return et61x251_vidioc_cropcap(cam, arg); ++ ++ case VIDIOC_G_CROP: ++ return et61x251_vidioc_g_crop(cam, arg); ++ ++ case VIDIOC_S_CROP: ++ return et61x251_vidioc_s_crop(cam, arg); ++ ++ case VIDIOC_ENUM_FMT: ++ return et61x251_vidioc_enum_fmt(cam, arg); ++ ++ case VIDIOC_G_FMT: ++ return et61x251_vidioc_g_fmt(cam, arg); ++ ++ case VIDIOC_TRY_FMT: ++ case VIDIOC_S_FMT: ++ return et61x251_vidioc_try_s_fmt(cam, cmd, arg); ++ ++ case VIDIOC_G_JPEGCOMP: ++ return et61x251_vidioc_g_jpegcomp(cam, arg); ++ ++ case VIDIOC_S_JPEGCOMP: ++ return et61x251_vidioc_s_jpegcomp(cam, arg); ++ ++ case VIDIOC_REQBUFS: ++ return et61x251_vidioc_reqbufs(cam, arg); ++ ++ case VIDIOC_QUERYBUF: ++ return et61x251_vidioc_querybuf(cam, arg); ++ ++ case VIDIOC_QBUF: ++ return et61x251_vidioc_qbuf(cam, arg); ++ ++ case VIDIOC_DQBUF: ++ return et61x251_vidioc_dqbuf(cam, filp, arg); ++ ++ case VIDIOC_STREAMON: ++ return et61x251_vidioc_streamon(cam, arg); ++ ++ case VIDIOC_STREAMOFF: ++ return et61x251_vidioc_streamoff(cam, arg); ++ ++ case VIDIOC_G_PARM: ++ return et61x251_vidioc_g_parm(cam, arg); ++ ++ case VIDIOC_S_PARM_OLD: ++ case VIDIOC_S_PARM: ++ return et61x251_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 et61x251_ioctl(struct inode* inode, struct file* filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); ++ int err = 0; ++ ++ if (down_interruptible(&cam->fileop_sem)) ++ return -ERESTARTSYS; ++ ++ if (cam->state & DEV_DISCONNECTED) { ++ DBG(1, "Device not present"); ++ up(&cam->fileop_sem); ++ return -ENODEV; ++ } ++ ++ if (cam->state & DEV_MISCONFIGURED) { ++ DBG(1, "The camera is misconfigured. Close and open it " ++ "again."); ++ up(&cam->fileop_sem); ++ return -EIO; ++ } ++ ++ V4LDBG(3, "et61x251", cmd); ++ ++ err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); ++ ++ up(&cam->fileop_sem); ++ ++ return err; ++} ++ ++ ++static struct file_operations et61x251_fops = { ++ .owner = THIS_MODULE, ++ .open = et61x251_open, ++ .release = et61x251_release, ++ .ioctl = et61x251_ioctl, ++ .read = et61x251_read, ++ .poll = et61x251_poll, ++ .mmap = et61x251_mmap, ++ .llseek = no_llseek, ++}; ++ ++/*****************************************************************************/ ++ ++/* It exists a single interface only. We do not need to validate anything. */ ++static int ++et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ++{ ++ struct usb_device *udev = interface_to_usbdev(intf); ++ struct et61x251_device* cam; ++ static unsigned int dev_nr = 0; ++ unsigned int i; ++ int err = 0; ++ ++ if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ cam->usbdev = udev; ++ ++ if (!(cam->control_buffer = kzalloc(8, 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; ++ } ++ ++ init_MUTEX(&cam->dev_sem); ++ ++ DBG(2, "ET61X[12]51 PC Camera Controller detected " ++ "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); ++ ++ for (i = 0; et61x251_sensor_table[i]; i++) { ++ err = et61x251_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 (et61x251_init(cam)) { ++ DBG(1, "Initialization failed. I will retry on open()."); ++ cam->state |= DEV_MISCONFIGURED; ++ } ++ ++ strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); ++ cam->v4ldev->owner = THIS_MODULE; ++ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; ++ cam->v4ldev->hardware = 0; ++ cam->v4ldev->fops = &et61x251_fops; ++ cam->v4ldev->minor = video_nr[dev_nr]; ++ cam->v4ldev->release = video_device_release; ++ video_set_drvdata(cam->v4ldev, cam); ++ ++ down(&cam->dev_sem); ++ ++ 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 < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; ++ up(&cam->dev_sem); ++ 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 < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ et61x251_create_sysfs(cam); ++ DBG(2, "Optional device control through 'sysfs' interface ready"); ++#endif ++ ++ usb_set_intfdata(intf, cam); ++ ++ up(&cam->dev_sem); ++ ++ return 0; ++ ++fail: ++ if (cam) { ++ kfree(cam->control_buffer); ++ if (cam->v4ldev) ++ video_device_release(cam->v4ldev); ++ kfree(cam); ++ } ++ return err; ++} ++ ++ ++static void et61x251_usb_disconnect(struct usb_interface* intf) ++{ ++ struct et61x251_device* cam = usb_get_intfdata(intf); ++ ++ if (!cam) ++ return; ++ ++ down_write(&et61x251_disconnect); ++ ++ down(&cam->dev_sem); ++ ++ 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; ++ et61x251_stop_transfer(cam); ++ cam->state |= DEV_DISCONNECTED; ++ wake_up_interruptible(&cam->wait_frame); ++ wake_up_interruptible(&cam->wait_stream); ++ } else { ++ cam->state |= DEV_DISCONNECTED; ++ et61x251_release_resources(cam); ++ } ++ ++ up(&cam->dev_sem); ++ ++ if (!cam->users) ++ kfree(cam); ++ ++ up_write(&et61x251_disconnect); ++} ++ ++ ++static struct usb_driver et61x251_usb_driver = { ++ .name = "et61x251", ++ .id_table = et61x251_id_table, ++ .probe = et61x251_usb_probe, ++ .disconnect = et61x251_usb_disconnect, ++}; ++ ++/*****************************************************************************/ ++ ++static int __init et61x251_module_init(void) ++{ ++ int err = 0; ++ ++ KDBG(2, ET61X251_MODULE_NAME " v" ET61X251_MODULE_VERSION); ++ KDBG(3, ET61X251_MODULE_AUTHOR); ++ ++ if ((err = usb_register(&et61x251_usb_driver))) ++ KDBG(1, "usb_register() failed"); ++ ++ return err; ++} ++ ++ ++static void __exit et61x251_module_exit(void) ++{ ++ usb_deregister(&et61x251_usb_driver); ++} ++ ++ ++module_init(et61x251_module_init); ++module_exit(et61x251_module_exit); +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/et61x251.h +@@ -0,0 +1,220 @@ ++/*************************************************************************** ++ * V4L2 driver for ET61X[12]51 PC Camera Controllers * ++ * * ++ * 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 _ET61X251_H_ ++#define _ET61X251_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/rwsem.h> ++#include <asm/semaphore.h> ++ ++#include "et61x251_sensor.h" ++ ++/*****************************************************************************/ ++ ++#define ET61X251_DEBUG ++#define ET61X251_DEBUG_LEVEL 2 ++#define ET61X251_MAX_DEVICES 64 ++#define ET61X251_PRESERVE_IMGSCALE 0 ++#define ET61X251_FORCE_MUNMAP 0 ++#define ET61X251_MAX_FRAMES 32 ++#define ET61X251_COMPRESSION_QUALITY 0 ++#define ET61X251_URBS 2 ++#define ET61X251_ISO_PACKETS 7 ++#define ET61X251_ALTERNATE_SETTING 13 ++#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) ++#define ET61X251_CTRL_TIMEOUT 100 ++ ++/*****************************************************************************/ ++ ++static const struct usb_device_id et61x251_id_table[] = { ++ { USB_DEVICE(0x102c, 0x6151), }, ++ { USB_DEVICE(0x102c, 0x6251), }, ++ { USB_DEVICE(0x102c, 0x6253), }, ++ { USB_DEVICE(0x102c, 0x6254), }, ++ { USB_DEVICE(0x102c, 0x6255), }, ++ { USB_DEVICE(0x102c, 0x6256), }, ++ { USB_DEVICE(0x102c, 0x6257), }, ++ { USB_DEVICE(0x102c, 0x6258), }, ++ { USB_DEVICE(0x102c, 0x6259), }, ++ { USB_DEVICE(0x102c, 0x625a), }, ++ { USB_DEVICE(0x102c, 0x625b), }, ++ { USB_DEVICE(0x102c, 0x625c), }, ++ { USB_DEVICE(0x102c, 0x625d), }, ++ { USB_DEVICE(0x102c, 0x625e), }, ++ { USB_DEVICE(0x102c, 0x625f), }, ++ { USB_DEVICE(0x102c, 0x6260), }, ++ { USB_DEVICE(0x102c, 0x6261), }, ++ { USB_DEVICE(0x102c, 0x6262), }, ++ { USB_DEVICE(0x102c, 0x6263), }, ++ { USB_DEVICE(0x102c, 0x6264), }, ++ { USB_DEVICE(0x102c, 0x6265), }, ++ { USB_DEVICE(0x102c, 0x6266), }, ++ { USB_DEVICE(0x102c, 0x6267), }, ++ { USB_DEVICE(0x102c, 0x6268), }, ++ { USB_DEVICE(0x102c, 0x6269), }, ++ { } ++}; ++ ++ET61X251_SENSOR_TABLE ++ ++/*****************************************************************************/ ++ ++enum et61x251_frame_state { ++ F_UNUSED, ++ F_QUEUED, ++ F_GRABBING, ++ F_DONE, ++ F_ERROR, ++}; ++ ++struct et61x251_frame_t { ++ void* bufmem; ++ struct v4l2_buffer buf; ++ enum et61x251_frame_state state; ++ struct list_head frame; ++ unsigned long vma_use_count; ++}; ++ ++enum et61x251_dev_state { ++ DEV_INITIALIZED = 0x01, ++ DEV_DISCONNECTED = 0x02, ++ DEV_MISCONFIGURED = 0x04, ++}; ++ ++enum et61x251_io_method { ++ IO_NONE, ++ IO_READ, ++ IO_MMAP, ++}; ++ ++enum et61x251_stream_state { ++ STREAM_OFF, ++ STREAM_INTERRUPT, ++ STREAM_ON, ++}; ++ ++struct et61x251_sysfs_attr { ++ u8 reg, i2c_reg; ++}; ++ ++struct et61x251_module_param { ++ u8 force_munmap; ++}; ++ ++static DECLARE_MUTEX(et61x251_sysfs_lock); ++static DECLARE_RWSEM(et61x251_disconnect); ++ ++struct et61x251_device { ++ struct video_device* v4ldev; ++ ++ struct et61x251_sensor* sensor; ++ ++ struct usb_device* usbdev; ++ struct urb* urb[ET61X251_URBS]; ++ void* transfer_buffer[ET61X251_URBS]; ++ u8* control_buffer; ++ ++ struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES]; ++ struct list_head inqueue, outqueue; ++ u32 frame_count, nbuffers, nreadbuffers; ++ ++ enum et61x251_io_method io; ++ enum et61x251_stream_state stream; ++ ++ struct v4l2_jpegcompression compression; ++ ++ struct et61x251_sysfs_attr sysfs; ++ struct et61x251_module_param module_param; ++ ++ enum et61x251_dev_state state; ++ u8 users; ++ ++ struct semaphore dev_sem, fileop_sem; ++ spinlock_t queue_lock; ++ wait_queue_head_t open, wait_frame, wait_stream; ++}; ++ ++/*****************************************************************************/ ++ ++void ++et61x251_attach_sensor(struct et61x251_device* cam, ++ struct et61x251_sensor* sensor) ++{ ++ cam->sensor = sensor; ++ cam->sensor->usbdev = cam->usbdev; ++} ++ ++/*****************************************************************************/ ++ ++#undef DBG ++#undef KDBG ++#ifdef ET61X251_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("et61x251: " fmt "\n", ## args); \ ++ else if ((level) == 3) \ ++ pr_debug("et61x251: [%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->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args) ++ ++#undef PDBGG ++#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ ++ ++#endif /* _ET61X251_H_ */ +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/et61x251_sensor.h +@@ -0,0 +1,115 @@ ++/*************************************************************************** ++ * API for image sensors connected to ET61X[12]51 PC Camera Controllers * ++ * * ++ * 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 _ET61X251_SENSOR_H_ ++#define _ET61X251_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 et61x251_device; ++struct et61x251_sensor; ++ ++/*****************************************************************************/ ++ ++extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam); ++ ++#define ET61X251_SENSOR_TABLE \ ++/* Weak detections must go at the end of the list */ \ ++static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ ++ &et61x251_probe_tas5130d1b, \ ++ NULL, \ ++}; ++ ++extern void ++et61x251_attach_sensor(struct et61x251_device* cam, ++ struct et61x251_sensor* sensor); ++ ++/*****************************************************************************/ ++ ++extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index); ++extern int et61x251_read_reg(struct et61x251_device*, u16 index); ++extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value); ++extern int et61x251_i2c_read(struct et61x251_device*, u8 address); ++extern int et61x251_i2c_try_write(struct et61x251_device*, ++ struct et61x251_sensor*, u8 address, ++ u8 value); ++extern int et61x251_i2c_try_read(struct et61x251_device*, ++ struct et61x251_sensor*, u8 address); ++extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, ++ u8 data2, u8 data3, u8 data4, u8 data5, ++ u8 data6, u8 data7, u8 data8, u8 address); ++ ++/*****************************************************************************/ ++ ++enum et61x251_i2c_sysfs_ops { ++ ET61X251_I2C_READ = 0x01, ++ ET61X251_I2C_WRITE = 0x02, ++}; ++ ++enum et61x251_i2c_interface { ++ ET61X251_I2C_2WIRES, ++ ET61X251_I2C_3WIRES, ++}; ++ ++/* Repeat start condition when RSTA is high */ ++enum et61x251_i2c_rsta { ++ ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */ ++ ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ ++}; ++ ++#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 ++ ++struct et61x251_sensor { ++ char name[32]; ++ ++ enum et61x251_i2c_sysfs_ops sysfs_ops; ++ ++ enum et61x251_i2c_interface interface; ++ u8 i2c_slave_id; ++ enum et61x251_i2c_rsta rsta; ++ struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */ ++ ++ struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS]; ++ struct v4l2_cropcap cropcap; ++ struct v4l2_pix_format pix_format; ++ ++ int (*init)(struct et61x251_device* cam); ++ int (*get_ctrl)(struct et61x251_device* cam, ++ struct v4l2_control* ctrl); ++ int (*set_ctrl)(struct et61x251_device* cam, ++ const struct v4l2_control* ctrl); ++ int (*set_crop)(struct et61x251_device* cam, ++ const struct v4l2_rect* rect); ++ int (*set_pix_format)(struct et61x251_device* cam, ++ const struct v4l2_pix_format* pix); ++ ++ const struct usb_device* usbdev; ++ ++ /* Private */ ++ struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; ++ struct v4l2_rect _rect; ++}; ++ ++#endif /* _ET61X251_SENSOR_H_ */ +--- /dev/null ++++ gregkh-2.6/drivers/usb/media/et61x251_tas5130d1b.c +@@ -0,0 +1,137 @@ ++/*************************************************************************** ++ * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * ++ * PC Camera Controllers * ++ * * ++ * 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. * ++ ***************************************************************************/ ++ ++#include "et61x251_sensor.h" ++ ++ ++static int tas5130d1b_init(struct et61x251_device* cam) ++{ ++ int err = 0; ++ ++ err += et61x251_write_reg(cam, 0x14, 0x01); ++ err += et61x251_write_reg(cam, 0x1b, 0x02); ++ err += et61x251_write_reg(cam, 0x02, 0x12); ++ err += et61x251_write_reg(cam, 0x0e, 0x60); ++ err += et61x251_write_reg(cam, 0x80, 0x61); ++ err += et61x251_write_reg(cam, 0xf0, 0x62); ++ err += et61x251_write_reg(cam, 0x03, 0x63); ++ err += et61x251_write_reg(cam, 0x14, 0x64); ++ err += et61x251_write_reg(cam, 0xf4, 0x65); ++ err += et61x251_write_reg(cam, 0x01, 0x66); ++ err += et61x251_write_reg(cam, 0x05, 0x67); ++ err += et61x251_write_reg(cam, 0x8f, 0x68); ++ err += et61x251_write_reg(cam, 0x0f, 0x8d); ++ err += et61x251_write_reg(cam, 0x08, 0x8e); ++ ++ return err; ++} ++ ++ ++static int tas5130d1b_set_ctrl(struct et61x251_device* cam, ++ const struct v4l2_control* ctrl) ++{ ++ int err = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_GAIN: ++ err += et61x251_i2c_raw_write(cam, 2, 0x20, ++ 0xf6-ctrl->value, 0, 0, 0, ++ 0, 0, 0, 0); ++ break; ++ case V4L2_CID_EXPOSURE: ++ err += et61x251_i2c_raw_write(cam, 2, 0x40, ++ 0x47-ctrl->value, 0, 0, 0, ++ 0, 0, 0, 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return err ? -EIO : 0; ++} ++ ++ ++static struct et61x251_sensor tas5130d1b = { ++ .name = "TAS5130D1B", ++ .interface = ET61X251_I2C_3WIRES, ++ .rsta = ET61X251_I2C_RSTA_STOP, ++ .active_pixel = { ++ .left = 106, ++ .top = 13, ++ }, ++ .init = &tas5130d1b_init, ++ .qctrl = { ++ { ++ .id = V4L2_CID_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "global gain", ++ .minimum = 0x00, ++ .maximum = 0xf6, ++ .step = 0x02, ++ .default_value = 0x0d, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "exposure", ++ .minimum = 0x00, ++ .maximum = 0x47, ++ .step = 0x01, ++ .default_value = 0x23, ++ .flags = 0, ++ }, ++ }, ++ .set_ctrl = &tas5130d1b_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_SBGGR8, ++ .priv = 8, ++ }, ++}; ++ ++ ++int et61x251_probe_tas5130d1b(struct et61x251_device* cam) ++{ ++ /* This sensor has no identifiers, so let's attach it anyway */ ++ et61x251_attach_sensor(cam, &tas5130d1b); ++ ++ /* Sensor detection is based on USB pid/vid */ ++ if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6251) ++ return -ENODEV; ++ ++ return 0; ++} +--- gregkh-2.6.orig/drivers/usb/media/Kconfig ++++ gregkh-2.6/drivers/usb/media/Kconfig +@@ -53,6 +53,21 @@ config USB_DSBR + To compile this driver as a module, choose M here: the + module will be called dsbr100. + ++config USB_ET61X251 ++ tristate "USB ET61X[12]51 PC Camera Controller support" ++ depends on USB && VIDEO_DEV ++ ---help--- ++ Say Y here if you want support for cameras based on Etoms ET61X151 ++ or ET61X251 PC Camera Controllers. ++ ++ See <file:Documentation/usb/et61x251.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 et61x251. ++ + config USB_IBMCAM + tristate "USB IBM (Xirlink) C-it Camera support" + depends on USB && VIDEO_DEV +@@ -209,5 +224,3 @@ config USB_PWC + + To compile this driver as a module, choose M here: the + module will be called pwc. +- +- +--- gregkh-2.6.orig/drivers/usb/media/Makefile ++++ gregkh-2.6/drivers/usb/media/Makefile +@@ -3,9 +3,11 @@ + # + + 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 + + obj-$(CONFIG_USB_DABUSB) += dabusb.o + obj-$(CONFIG_USB_DSBR) += dsbr100.o ++obj-$(CONFIG_USB_ET61X251) += et61x251.o + obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o + obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o + obj-$(CONFIG_USB_OV511) += ov511.o +--- gregkh-2.6.orig/include/linux/videodev2.h ++++ gregkh-2.6/include/linux/videodev2.h +@@ -314,6 +314,7 @@ struct v4l2_pix_format + #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ + #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */ + #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ ++#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ + + /* + * F O R M A T E N U M E R A T I O N +--- gregkh-2.6.orig/MAINTAINERS ++++ gregkh-2.6/MAINTAINERS +@@ -2641,6 +2641,14 @@ M: dbrownell@users.sourceforge.net + L: linux-usb-devel@lists.sourceforge.net + S: Maintained + ++USB ET61X[12]51 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 HID/HIDBP DRIVERS + P: Vojtech Pavlik + M: vojtech@suse.cz +@@ -2804,6 +2812,7 @@ USB SN9C10x 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 + +@@ -2833,6 +2842,7 @@ USB W996[87]CF 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 + diff --git a/usb/usb-ftdi_sio-new-pid-for-pcdj-dac2.patch b/usb/usb-ftdi_sio-new-pid-for-pcdj-dac2.patch new file mode 100644 index 0000000000000..da13cf255323a --- /dev/null +++ b/usb/usb-ftdi_sio-new-pid-for-pcdj-dac2.patch @@ -0,0 +1,45 @@ +From linux-usb-devel-admin@lists.sourceforge.net Tue Jan 3 05:34:29 2006 +From: Wouter Paesen <wouter@kangaroot.net> +Message-ID: <20060103133031.GA10080@tougher.kangaroot.net> +Content-Disposition: inline +Subject: USB: ftdi_sio: new PID for PCDJ DAC2 +Date: Tue, 3 Jan 2006 14:30:31 +0100 + + +The attached patch adds a new PID for the ftdi_sio driver. It will +enable support for PC-DJ's DAC-2 controller module +(more information on http://www.pcdjhardware.com/DAC2.asp) + +Signed-off-by: Wouter Paesen <wouter@kangaroot.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/ftdi_sio.c | 1 + + drivers/usb/serial/ftdi_sio.h | 6 ++++++ + 2 files changed, 7 insertions(+) + +--- gregkh-2.6.orig/drivers/usb/serial/ftdi_sio.c ++++ gregkh-2.6/drivers/usb/serial/ftdi_sio.c +@@ -483,6 +483,7 @@ static struct usb_device_id id_table_com + { USB_DEVICE(FTDI_VID, FTDI_TTUSB_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, + { }, /* Optional parameter entry */ + { } /* Terminating entry */ + }; +--- gregkh-2.6.orig/drivers/usb/serial/ftdi_sio.h ++++ gregkh-2.6/drivers/usb/serial/ftdi_sio.h +@@ -56,6 +56,12 @@ + #define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */ + + /* ++ * PCDJ use ftdi based dj-controllers. The following PID is for their DAC-2 device ++ * http://www.pcdjhardware.com/DAC2.asp (PID sent by Wouter Paesen) ++ * (the VID is the standard ftdi vid (FTDI_VID) */ ++#define FTDI_PCDJ_DAC2_PID 0xFA88 ++ ++/* + * The following are the values for the Matrix Orbital LCD displays, + * which are the FT232BM ( similar to the 8U232AM ) + */ diff --git a/usb/usb-usbip-build-fix.patch b/usb/usb-usbip-build-fix.patch new file mode 100644 index 0000000000000..4ff26199efae7 --- /dev/null +++ b/usb/usb-usbip-build-fix.patch @@ -0,0 +1,59 @@ +From akpm@osdl.org Thu Jan 5 22:54:11 2006 +Message-Id: <200601060650.k066omZZ008821@shell0.pdx.osdl.net> +From: Andrew Morton <akpm@osdl.org> +Subject: USB: usbip build fix +To: greg@kroah.com +Cc: akpm@osdl.org, taka-hir@is.naist.jp +Date: Thu, 05 Jan 2006 22:50:36 -0800 + + +From: Andrew Morton <akpm@osdl.org> + +Fix this: + +drivers/usb/ip/usbip_common.c: In function `socket_to_addrstr': +drivers/usb/ip/usbip_common.c:582: dereferencing pointer to incomplete type +drivers/usb/ip/usbip_common.c:582: dereferencing pointer to incomplete type +drivers/usb/ip/usbip_common.c:582: dereferencing pointer to incomplete type +drivers/usb/ip/usbip_common.c:582: dereferencing pointer to incomplete type + +And this: + +drivers/usb/ip/usbip_common.h: At top level: +drivers/usb/ip/usbip_common.c:441: warning: `usbip_dump_urb' defined but not used + +Cc: Takahiro Hirofuchi <taka-hir@is.naist.jp> +Signed-off-by: Andrew Morton <akpm@osdl.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/ip/usbip_common.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- gregkh-2.6.orig/drivers/usb/ip/usbip_common.c ++++ gregkh-2.6/drivers/usb/ip/usbip_common.c +@@ -23,6 +23,7 @@ + #include <linux/kernel.h> + #include <linux/file.h> + #include <linux/tcp.h> ++#include <linux/in.h> + #include "usbip_common.h" + + static void usbip_dump_buffer(char *, int size); +@@ -254,7 +255,7 @@ static void usbip_dump_pipe(unsigned int + + } + +- ++#if 0 + static void usbip_dump_usb_device(struct usb_device *dev) + { + if (dev == NULL) { +@@ -466,7 +467,7 @@ static void usbip_dump_urb (struct urb * + printk(" context :%p\n", purb->context); + printk(" complete :%p\n", purb->complete); + } +- ++#endif + + void setquickack(struct socket *socket) + { diff --git a/usb/usb-usbip-more-dead-code-fix.patch b/usb/usb-usbip-more-dead-code-fix.patch new file mode 100644 index 0000000000000..f1ff1eb5705b3 --- /dev/null +++ b/usb/usb-usbip-more-dead-code-fix.patch @@ -0,0 +1,40 @@ +From akpm@osdl.org Thu Jan 5 22:54:28 2006 +Message-Id: <200601060650.k066ooIj008824@shell0.pdx.osdl.net> +From: Andrew Morton <akpm@osdl.org> +Subject: USB: usbip more dead code fix +To: greg@kroah.com +Cc: akpm@osdl.org, taka-hir@is.naist.jp +From: akpm@osdl.org +Date: Thu, 05 Jan 2006 22:50:37 -0800 + + +From: Andrew Morton <akpm@osdl.org> + +drivers/usb/ip/vhci_hcd.c:639: warning: `vhci_device_unlink_all_urb' defined but not used + +Cc: Takahiro Hirofuchi <taka-hir@is.naist.jp> +Signed-off-by: Andrew Morton <akpm@osdl.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/ip/vhci_hcd.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- gregkh-2.6.orig/drivers/usb/ip/vhci_hcd.c ++++ gregkh-2.6/drivers/usb/ip/vhci_hcd.c +@@ -634,7 +634,7 @@ static int vhci_urb_dequeue(struct usb_h + return 0; + } + +- ++#if 0 + static void vhci_device_unlink_all_urb(struct vhci_device *vdev) + { + struct vhci_priv *priv, *tmp; +@@ -674,6 +674,7 @@ static void vhci_device_unlink_all_urb(s + + spin_unlock(&vdev->priv_lock); + } ++#endif + + /* + * The important thing is that only one context begins cleanup. |