]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
authorLinus Torvalds <torvalds@g5.osdl.org>
Wed, 1 Feb 2006 03:24:43 +0000 (19:24 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 1 Feb 2006 03:24:43 +0000 (19:24 -0800)
86 files changed:
Documentation/feature-removal-schedule.txt
Documentation/usb/et61x251.txt [new file with mode: 0644]
Documentation/usb/sn9c102.txt
Documentation/usb/w9968cf.txt
MAINTAINERS
arch/i386/pci/irq.c
arch/i386/pci/mmconfig.c
arch/x86_64/pci/mmconfig.c
drivers/block/ub.c
drivers/pci/hotplug/Kconfig
drivers/pci/hotplug/acpiphp_ibm.c
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/rpadlpar_core.c
drivers/pci/hotplug/rpaphp.h
drivers/pci/hotplug/rpaphp_core.c
drivers/pci/hotplug/rpaphp_pci.c
drivers/pci/hotplug/rpaphp_slot.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_ctrl.c
drivers/pci/msi.c
drivers/pci/msi.h
drivers/pci/pci.c
drivers/pci/setup-res.c
drivers/usb/Makefile
drivers/usb/atm/cxacru.c
drivers/usb/atm/speedtch.c
drivers/usb/atm/ueagle-atm.c
drivers/usb/atm/usbatm.c
drivers/usb/atm/usbatm.h
drivers/usb/atm/xusbatm.c
drivers/usb/class/cdc-acm.c
drivers/usb/class/usblp.c
drivers/usb/core/message.c
drivers/usb/core/urb.c
drivers/usb/gadget/inode.c
drivers/usb/gadget/net2280.c
drivers/usb/gadget/zero.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/ohci-au1xxx.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/uhci-q.c
drivers/usb/input/hid-core.c
drivers/usb/input/hiddev.c
drivers/usb/input/touchkitusb.c
drivers/usb/input/yealink.c
drivers/usb/media/Kconfig
drivers/usb/media/Makefile
drivers/usb/media/et61x251.h [new file with mode: 0644]
drivers/usb/media/et61x251_core.c [new file with mode: 0644]
drivers/usb/media/et61x251_sensor.h [new file with mode: 0644]
drivers/usb/media/et61x251_tas5130d1b.c [new file with mode: 0644]
drivers/usb/media/ov511.c
drivers/usb/media/pwc/pwc-ctrl.c
drivers/usb/media/sn9c102.h
drivers/usb/media/sn9c102_core.c
drivers/usb/media/sn9c102_hv7131d.c
drivers/usb/media/sn9c102_mi0343.c
drivers/usb/media/sn9c102_ov7630.c
drivers/usb/media/sn9c102_pas106b.c
drivers/usb/media/sn9c102_sensor.h
drivers/usb/media/sn9c102_tas5110c1b.c
drivers/usb/media/sn9c102_tas5130d1b.c
drivers/usb/media/w9968cf.c
drivers/usb/media/w9968cf.h
drivers/usb/media/w9968cf_vpp.h
drivers/usb/misc/auerswald.c
drivers/usb/misc/ldusb.c
drivers/usb/net/asix.c
drivers/usb/serial/cp2101.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h
drivers/usb/serial/pl2303.c
drivers/usb/serial/pl2303.h
drivers/usb/storage/initializers.c
drivers/usb/storage/initializers.h
drivers/usb/storage/libusual.c
drivers/usb/storage/unusual_devs.h
drivers/usb/usb-skeleton.c
drivers/video/cyblafb.c
include/linux/pci.h
include/linux/pci_ids.h
include/linux/usb_ch9.h
include/linux/videodev2.h
sound/oss/au1550_ac97.c

index b4a1ea76269857137a659e69102b1223b715f6cf..4d4897c8ef963284664e23729ca792f302fc464b 100644 (file)
@@ -148,3 +148,17 @@ Why:       The 8250 serial driver now has the ability to deal with the differences
        brother on Alchemy SOCs.  The loss of features is not considered an
        issue.
 Who:   Ralf Baechle <ralf@linux-mips.org>
+
+---------------------------
+
+What:  Legacy /proc/pci interface (PCI_LEGACY_PROC)
+When:  March 2006
+Why:   deprecated since 2.5.53 in favor of lspci(8)
+Who:   Adrian Bunk <bunk@stusta.de>
+
+---------------------------
+
+What:  pci_module_init(driver)
+When:  January 2007
+Why:   Is replaced by pci_register_driver(pci_driver).
+Who:   Richard Knutsson <ricknu-0@student.ltu.se> and Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/Documentation/usb/et61x251.txt b/Documentation/usb/et61x251.txt
new file mode 100644 (file)
index 0000000..b44dda4
--- /dev/null
@@ -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'.
index 3f8a119db31b3018ad30da389bbc065eb7a35983..c6b76414172cd74455103acbec763b2e49882b90 100644 (file)
@@ -17,16 +17,15 @@ Index
 7.  Module parameters
 8.  Optional device control through "sysfs"
 9.  Supported devices
-10. How to add plug-in's for new image sensors
-11. Notes for V4L2 application developers
-12. Video frame formats
-13. Contact information
-14. Credits
+10. Notes for V4L2 application developers
+11. Video frame formats
+12. Contact information
+13. Credits
 
 
 1. Copyright
 ============
-Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>
+Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it>
 
 
 2. Disclaimer
@@ -54,9 +53,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 4. Overview and features
 ========================
-This driver attempts to support the video and audio streaming capabilities of
-the devices mounting the SONiX SN9C101, SN9C102 and SN9C103 PC Camera
-Controllers.
+This driver attempts to support the video interface of the devices mounting the
+SONiX SN9C101, SN9C102 and SN9C103 PC Camera Controllers.
 
 It's worth to note that SONiX has never collaborated with the author during the
 development of this project, despite several requests for enough detailed
@@ -78,6 +76,7 @@ Some of the features of the driver are:
 - available mmap or read/poll methods for video streaming through isochronous
   data transfers;
 - automatic detection of image sensor;
+- support for built-in microphone interface;
 - support for any window resolutions and optional panning within the maximum
   pixel area of image sensor;
 - image downscaling with arbitrary scaling factors from 1, 2 and 4 in both
@@ -96,7 +95,7 @@ Some of the features of the driver are:
   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
-  your system supports hotplugging;
+  the system supports hotplugging;
 - no known bugs.
 
 
@@ -112,6 +111,12 @@ corresponding modules must be compiled:
        #
        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
@@ -125,6 +130,21 @@ necessary:
        CONFIG_USB_UHCI_HCD=m
        CONFIG_USB_OHCI_HCD=m
 
+The SN9C103 controller also provides a built-in microphone interface. It is
+supported by the USB Audio driver thanks to the ALSA API:
+
+       # Sound
+       #
+       CONFIG_SOUND=y
+
+       # Advanced Linux Sound Architecture
+       #
+       CONFIG_SND=m
+
+       # USB devices
+       #
+       CONFIG_SND_USB_AUDIO=m
+
 And finally:
 
        # USB Multimedia devices
@@ -153,7 +173,7 @@ analyze kernel messages and verify that the loading process has gone well:
 Module parameters are listed below:
 -------------------------------------------------------------------------------
 Name:           video_nr
-Type:           int array (min = 0, max = 64)
+Type:           short array (min = 0, max = 64)
 Syntax:         <-1|n[,...]> 
 Description:    Specify V4L2 minor mode number:
                 -1 = use next available
@@ -165,19 +185,19 @@ Description:    Specify V4L2 minor mode number:
                 other camera.
 Default:        -1
 -------------------------------------------------------------------------------
-Name:           force_munmap;
+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)"
+                0 = do not force memory unmapping
+                1 = force memory unmapping (save memory)
 Default:        0
 -------------------------------------------------------------------------------
 Name:           debug
-Type:           int
+Type:           ushort
 Syntax:         <n> 
 Description:    Debugging information level, from 0 to 3:
                 0 = none (use carefully)
@@ -187,14 +207,15 @@ Description:    Debugging information level, from 0 to 3:
                 Level 3 is useful for testing only, when only one device
                 is used. It also shows some more informations about the
                 hardware being detected. This parameter can be changed at
-                runtime thanks to the /sys filesystem.
+                runtime thanks to the /sys filesystem interface.
 Default:        2
 -------------------------------------------------------------------------------
 
 
 8. Optional device control through "sysfs" [1]
 ==========================================
-It is possible to read and write both the SN9C10x and the image sensor
+If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled,
+it is possible to read and write both the SN9C10x and the image sensor
 registers by using the "sysfs" filesystem interface.
 
 Every time a supported device is recognized, a write-only file named "green" is
@@ -236,7 +257,7 @@ serialized.
 
 The sysfs interface also provides the "frame_header" entry, which exports the
 frame header of the most recent requested and captured video frame. The header
-is 12-bytes long and is appended to every video frame by the SN9C10x
+is always 18-bytes long and is appended to every video frame by the SN9C10x
 controllers. As an example, this additional information can be used by the user
 application for implementing auto-exposure features via software. 
 
@@ -250,7 +271,8 @@ Byte #  Value         Description
 0x03    0xC4          Frame synchronisation pattern.
 0x04    0xC4          Frame synchronisation pattern.
 0x05    0x96          Frame synchronisation pattern.
-0x06    0x00 or 0x01  Unknown meaning. The exact value depends on the chip.
+0x06    0xXX          Unknown meaning. The exact value depends on the chip;
+                      possible values are 0x00, 0x01 and 0x20.
 0x07    0xXX          Variable value, whose bits are ff00uzzc, where ff is a
                       frame counter, u is unknown, zz is a size indicator
                       (00 = VGA, 01 = SIF, 10 = QSIF) and c stands for
@@ -267,12 +289,23 @@ Byte #  Value         Description
                       times the area outside of the specified AE area. For
                       images that are not pure white, the value scales down
                       according to relative whiteness.
+                      according to relative whiteness.
+
+The following bytes are used by the SN9C103 bridge only:
+
+0x0C    0xXX          Unknown meaning
+0x0D    0xXX          Unknown meaning
+0x0E    0xXX          Unknown meaning
+0x0F    0xXX          Unknown meaning
+0x10    0xXX          Unknown meaning
+0x11    0xXX          Unknown meaning
 
 The AE area (sx, sy, ex, ey) in the active window can be set by programming the
 registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C10x controllers, where one unit
 corresponds to 32 pixels.
 
-[1] The frame header has been documented by Bertrik Sikken.
+[1] Part of the meaning of the frame header has been documented by Bertrik
+    Sikken.
 
 
 9. Supported devices
@@ -298,6 +331,7 @@ Vendor ID  Product ID
 0x0c45     0x602b
 0x0c45     0x602c
 0x0c45     0x602d
+0x0c45     0x602e
 0x0c45     0x6030
 0x0c45     0x6080
 0x0c45     0x6082
@@ -348,18 +382,7 @@ appreciated. Non-available hardware will not be supported by the author of this
 driver.
 
 
-10. How to add plug-in's for new image sensors
-==============================================
-It should be easy to write plug-in's for new sensors by using the small API
-that has been created for this purpose, which is present in "sn9c102_sensor.h"
-(documentation is included there). As an example, have a look at the code in
-"sn9c102_pas106b.c", which uses the mentioned interface.
-
-At the moment, possible unsupported image sensors are: CIS-VF10 (VGA),
-OV7620 (VGA), OV7630 (VGA).
-
-
-11. Notes for V4L2 application developers
+10. Notes for V4L2 application developers
 =========================================
 This driver follows the V4L2 API specifications. In particular, it enforces two
 rules:
@@ -394,7 +417,7 @@ initialized (as described in the documentation of the API for the image sensors
 supplied by this driver).
 
 
-12. Video frame formats [1]
+11. Video frame formats [1]
 =======================
 The SN9C10x PC Camera Controllers can send images in two possible video
 formats over the USB: either native "Sequential RGB Bayer" or Huffman
@@ -455,7 +478,7 @@ The following Huffman codes have been found:
     documented by Bertrik Sikken.
 
 
-13. Contact information
+12. Contact information
 =======================
 The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
 
@@ -464,7 +487,7 @@ GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is
 the fingerprint is: '88E8 F32F 7244 68BA 3958  5D40 99DA 5D2A FCE6 35A4'.
 
 
-14. Credits
+13. Credits
 ===========
 Many thanks to following persons for their contribute (listed in alphabetical
 order):
@@ -480,5 +503,5 @@ order):
 - Bertrik Sikken, who reverse-engineered and documented the Huffman compression
   algorithm used in the SN9C10x controllers and implemented the first decoder;
 - Mizuno Takafumi for the donation of a webcam;
-- An "anonymous" donator (who didn't want his name to be revealed) for the
+- an "anonymous" donator (who didn't want his name to be revealed) for the
   donation of a webcam.
index 18a47738d56c075c4ca37f6f724f3ca8d37401de..9d46cd0b19e3190064da339dbe108b3184e1c7f0 100644 (file)
@@ -57,16 +57,12 @@ based cameras should be supported as well.
 The driver is divided into two modules: the basic one, "w9968cf", is needed for
 the supported devices to work; the second one, "w9968cf-vpp", is an optional
 module, which provides some useful video post-processing functions like video
-decoding, up-scaling and colour conversions. Once the driver is installed,
-every time an application tries to open a recognized device, "w9968cf" checks
-the presence of the "w9968cf-vpp" module and loads it automatically by default.
+decoding, up-scaling and colour conversions.
 
-Please keep in mind that official kernels do not include the second module for
-performance purposes. However it is always recommended to download and install
-the latest and complete release of the driver, replacing the existing one, if
-present: it will be still even possible not to load the "w9968cf-vpp" module at
-all, if you ever want to. Another important missing feature of the version in
-the official Linux 2.4 kernels is the writeable /proc filesystem interface.
+Note that the official kernels do neither include nor support the second
+module for performance purposes. Therefore, it is always recommended to
+download and install the latest and complete release of the driver,
+replacing the existing one, if present.
 
 The latest and full-featured version of the W996[87]CF driver can be found at:
 http://www.linux-projects.org. Please refer to the documentation included in
@@ -201,22 +197,6 @@ Note:            The kernel must be compiled with the CONFIG_KMOD option
                  enabled for the 'ovcamchip' module to be loaded and for
                  this parameter to be present.
 -------------------------------------------------------------------------------
-Name:           vppmod_load
-Type:           bool
-Syntax:         <0|1>
-Description:    Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled.
-                If enabled, every time an application attempts to open a
-                camera, 'insmod' searches for the video post-processing module
-                in the system and loads it automatically (if present).
-                The optional 'w9968cf-vpp' module adds extra image manipulation
-                capabilities to the 'w9968cf' module,like software up-scaling,
-                colour conversions and video decompression for very high frame
-                rates.
-Default:        1
-Note:           The kernel must be compiled with the CONFIG_KMOD option
-                enabled for the 'w9968cf-vpp' module to be loaded and for
-                this parameter to be present.
--------------------------------------------------------------------------------
 Name:           simcams 
 Type:           int 
 Syntax:         <n> 
index a37a2b38a5574418fa5060b946343ebdf21b74b1..42955fe1ffa056df2f412280b7d9e45a17060d31 100644 (file)
@@ -2673,6 +2673,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
@@ -2836,6 +2844,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
 
@@ -2865,6 +2874,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
 
index e715aa930036aa1590aae77b09ad27f7c87c7cbb..3ca59cad05f33d41ac3e663070d4e44223449a37 100644 (file)
@@ -539,6 +539,11 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route
                case PCI_DEVICE_ID_INTEL_ICH7_30:
                case PCI_DEVICE_ID_INTEL_ICH7_31:
                case PCI_DEVICE_ID_INTEL_ESB2_0:
+               case PCI_DEVICE_ID_INTEL_ICH8_0:
+               case PCI_DEVICE_ID_INTEL_ICH8_1:
+               case PCI_DEVICE_ID_INTEL_ICH8_2:
+               case PCI_DEVICE_ID_INTEL_ICH8_3:
+               case PCI_DEVICE_ID_INTEL_ICH8_4:
                        r->name = "PIIX/ICH";
                        r->get = pirq_piix_get;
                        r->set = pirq_piix_set;
index 4bb4d4b0f73ad00ce6e66e50a29ad877a2102c24..0ee8a983708c078b0e3e8f9b7c27548cd1936845 100644 (file)
@@ -36,8 +36,7 @@ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
        while (1) {
                ++cfg_num;
                if (cfg_num >= pci_mmcfg_config_num) {
-                       /* Not found - fallback to type 1 */
-                       return 0;
+                       break;
                }
                cfg = &pci_mmcfg_config[cfg_num];
                if (cfg->pci_segment_group_number != seg)
@@ -46,6 +45,18 @@ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
                    (cfg->end_bus_number >= bus))
                        return cfg->base_address;
        }
+
+       /* Handle more broken MCFG tables on Asus etc.
+          They only contain a single entry for bus 0-0. Assume
+          this applies to all busses. */
+       cfg = &pci_mmcfg_config[0];
+       if (pci_mmcfg_config_num == 1 &&
+               cfg->pci_segment_group_number == 0 &&
+               (cfg->start_bus_number | cfg->end_bus_number) == 0)
+               return cfg->base_address;
+
+       /* Fall back to type 0 */
+       return 0;
 }
 
 static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
index f16c0d57c552f1fade440fb7a683fa649bc7099f..00d4ddbf980ccdddf23153f3e2758ac72ef90f9a 100644 (file)
@@ -29,11 +29,8 @@ static char __iomem *get_virt(unsigned int seg, unsigned bus)
 
        while (1) {
                ++cfg_num;
-               if (cfg_num >= pci_mmcfg_config_num) {
-                       /* Not found - fall back to type 1. This happens
-                          e.g. on the internal devices of a K8 northbridge. */
-                       return NULL;
-               }
+               if (cfg_num >= pci_mmcfg_config_num)
+                       break;
                cfg = pci_mmcfg_virt[cfg_num].cfg;
                if (cfg->pci_segment_group_number != seg)
                        continue;
@@ -41,6 +38,18 @@ static char __iomem *get_virt(unsigned int seg, unsigned bus)
                    (cfg->end_bus_number >= bus))
                        return pci_mmcfg_virt[cfg_num].virt;
        }
+
+       /* Handle more broken MCFG tables on Asus etc.
+          They only contain a single entry for bus 0-0. Assume
+          this applies to all busses. */
+       cfg = &pci_mmcfg_config[0];
+       if (pci_mmcfg_config_num == 1 &&
+               cfg->pci_segment_group_number == 0 &&
+               (cfg->start_bus_number | cfg->end_bus_number) == 0)
+               return cfg->base_address;
+
+       /* Fall back to type 0 */
+       return 0;
 }
 
 static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
index a05fe5843e6c7ee5645d9c5b71572c5d0632d4e6..f04d864770add163a966c2cd947a6ec6d4f345f9 100644 (file)
@@ -14,7 +14,6 @@
  *  -- special case some senses, e.g. 3a/0 -> no media present, reduce retries
  *  -- verify the 13 conditions and do bulk resets
  *  -- kill last_pipe and simply do two-state clearing on both pipes
- *  -- verify protocol (bulk) from USB descriptors (maybe...)
  *  -- highmem
  *  -- move top_sense and work_bcs into separate allocations (if they survive)
  *     for cache purists and esoteric architectures.
@@ -355,7 +354,7 @@ struct ub_lun {
  * The USB device instance.
  */
 struct ub_dev {
-       spinlock_t lock;
+       spinlock_t *lock;
        atomic_t poison;                /* The USB device is disconnected */
        int openc;                      /* protected by ub_lock! */
                                        /* kref is too implicit for our taste */
@@ -420,11 +419,13 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
     int stalled_pipe);
 static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);
-static void ub_reset_enter(struct ub_dev *sc);
+static void ub_reset_enter(struct ub_dev *sc, int try);
 static void ub_reset_task(void *arg);
 static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun);
 static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
     struct ub_capacity *ret);
+static int ub_sync_reset(struct ub_dev *sc);
+static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe);
 static int ub_probe_lun(struct ub_dev *sc, int lnum);
 
 /*
@@ -452,6 +453,10 @@ MODULE_DEVICE_TABLE(usb, ub_usb_ids);
 #define UB_MAX_HOSTS  26
 static char ub_hostv[UB_MAX_HOSTS];
 
+#define UB_QLOCK_NUM 5
+static spinlock_t ub_qlockv[UB_QLOCK_NUM];
+static int ub_qlock_next = 0;
+
 static DEFINE_SPINLOCK(ub_lock);       /* Locks globals and ->openc */
 
 /*
@@ -531,7 +536,7 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr,
                return 0;
 
        cnt = 0;
-       spin_lock_irqsave(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
 
        cnt += sprintf(page + cnt,
            "poison %d reset %d\n",
@@ -579,7 +584,7 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr,
                if (++nc == SCMD_TRACE_SZ) nc = 0;
        }
 
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
        return cnt;
 }
 
@@ -626,6 +631,24 @@ static void ub_id_put(int id)
        spin_unlock_irqrestore(&ub_lock, flags);
 }
 
+/*
+ * This is necessitated by the fact that blk_cleanup_queue does not
+ * necesserily destroy the queue. Instead, it may merely decrease q->refcnt.
+ * Since our blk_init_queue() passes a spinlock common with ub_dev,
+ * we have life time issues when ub_cleanup frees ub_dev.
+ */
+static spinlock_t *ub_next_lock(void)
+{
+       unsigned long flags;
+       spinlock_t *ret;
+
+       spin_lock_irqsave(&ub_lock, flags);
+       ret = &ub_qlockv[ub_qlock_next];
+       ub_qlock_next = (ub_qlock_next + 1) % UB_QLOCK_NUM;
+       spin_unlock_irqrestore(&ub_lock, flags);
+       return ret;
+}
+
 /*
  * Downcount for deallocation. This rides on two assumptions:
  *  - once something is poisoned, its refcount cannot grow
@@ -961,7 +984,7 @@ static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
        if (atomic_read(&sc->poison))
                return -ENXIO;
 
-       ub_reset_enter(sc);
+       ub_reset_enter(sc, urq->current_try);
 
        if (urq->current_try >= 3)
                return -EIO;
@@ -997,8 +1020,6 @@ static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
  * No exceptions.
  *
  * Host is assumed locked.
- *
- * XXX We only support Bulk for the moment.
  */
 static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
 {
@@ -1083,9 +1104,10 @@ static void ub_urb_timeout(unsigned long arg)
        struct ub_dev *sc = (struct ub_dev *) arg;
        unsigned long flags;
 
-       spin_lock_irqsave(&sc->lock, flags);
-       usb_unlink_urb(&sc->work_urb);
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
+       if (!ub_is_completed(&sc->work_done))
+               usb_unlink_urb(&sc->work_urb);
+       spin_unlock_irqrestore(sc->lock, flags);
 }
 
 /*
@@ -1108,10 +1130,9 @@ static void ub_scsi_action(unsigned long _dev)
        struct ub_dev *sc = (struct ub_dev *) _dev;
        unsigned long flags;
 
-       spin_lock_irqsave(&sc->lock, flags);
-       del_timer(&sc->work_timer);
+       spin_lock_irqsave(sc->lock, flags);
        ub_scsi_dispatch(sc);
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
 }
 
 static void ub_scsi_dispatch(struct ub_dev *sc)
@@ -1133,6 +1154,7 @@ static void ub_scsi_dispatch(struct ub_dev *sc)
                } else {
                        if (!ub_is_completed(&sc->work_done))
                                break;
+                       del_timer(&sc->work_timer);
                        ub_scsi_urb_compl(sc, cmd);
                }
        }
@@ -1680,16 +1702,18 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
 
 /*
  * Reset management
+ * XXX Move usb_reset_device to khubd. Hogging kevent is not a good thing.
+ * XXX Make usb_sync_reset asynchronous.
  */
 
-static void ub_reset_enter(struct ub_dev *sc)
+static void ub_reset_enter(struct ub_dev *sc, int try)
 {
 
        if (sc->reset) {
                /* This happens often on multi-LUN devices. */
                return;
        }
-       sc->reset = 1;
+       sc->reset = try + 1;
 
 #if 0 /* Not needed because the disconnect waits for us. */
        unsigned long flags;
@@ -1727,6 +1751,11 @@ static void ub_reset_task(void *arg)
        if (atomic_read(&sc->poison)) {
                printk(KERN_NOTICE "%s: Not resetting disconnected device\n",
                    sc->name); /* P3 This floods. Remove soon. XXX */
+       } else if ((sc->reset & 1) == 0) {
+               ub_sync_reset(sc);
+               msleep(700);    /* usb-storage sleeps 6s (!) */
+               ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
+               ub_probe_clear_stall(sc, sc->send_bulk_pipe);
        } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
                printk(KERN_NOTICE "%s: Not resetting multi-interface device\n",
                    sc->name); /* P3 This floods. Remove soon. XXX */
@@ -1754,7 +1783,7 @@ static void ub_reset_task(void *arg)
         * queues of resets or anything. We do need a spinlock though,
         * to interact with block layer.
         */
-       spin_lock_irqsave(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
        sc->reset = 0;
        tasklet_schedule(&sc->tasklet);
        list_for_each(p, &sc->luns) {
@@ -1762,7 +1791,7 @@ static void ub_reset_task(void *arg)
                blk_start_queue(lun->disk->queue);
        }
        wake_up(&sc->reset_wait);
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
 }
 
 /*
@@ -1990,11 +2019,11 @@ static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun)
        cmd->done = ub_probe_done;
        cmd->back = &compl;
 
-       spin_lock_irqsave(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
        cmd->tag = sc->tagcnt++;
 
        rc = ub_submit_scsi(sc, cmd);
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
 
        if (rc != 0) {
                printk("ub: testing ready: submit error (%d)\n", rc); /* P3 */
@@ -2052,11 +2081,11 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
        cmd->done = ub_probe_done;
        cmd->back = &compl;
 
-       spin_lock_irqsave(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
        cmd->tag = sc->tagcnt++;
 
        rc = ub_submit_scsi(sc, cmd);
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
 
        if (rc != 0) {
                printk("ub: reading capacity: submit error (%d)\n", rc); /* P3 */
@@ -2117,6 +2146,52 @@ static void ub_probe_timeout(unsigned long arg)
        complete(cop);
 }
 
+/*
+ * Reset with a Bulk reset.
+ */
+static int ub_sync_reset(struct ub_dev *sc)
+{
+       int ifnum = sc->intf->cur_altsetting->desc.bInterfaceNumber;
+       struct usb_ctrlrequest *cr;
+       struct completion compl;
+       struct timer_list timer;
+       int rc;
+
+       init_completion(&compl);
+
+       cr = &sc->work_cr;
+       cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+       cr->bRequest = US_BULK_RESET_REQUEST;
+       cr->wValue = cpu_to_le16(0);
+       cr->wIndex = cpu_to_le16(ifnum);
+       cr->wLength = cpu_to_le16(0);
+
+       usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
+           (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl);
+       sc->work_urb.actual_length = 0;
+       sc->work_urb.error_count = 0;
+       sc->work_urb.status = 0;
+
+       if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
+               printk(KERN_WARNING
+                    "%s: Unable to submit a bulk reset (%d)\n", sc->name, rc);
+               return rc;
+       }
+
+       init_timer(&timer);
+       timer.function = ub_probe_timeout;
+       timer.data = (unsigned long) &compl;
+       timer.expires = jiffies + UB_CTRL_TIMEOUT;
+       add_timer(&timer);
+
+       wait_for_completion(&compl);
+
+       del_timer_sync(&timer);
+       usb_kill_urb(&sc->work_urb);
+
+       return sc->work_urb.status;
+}
+
 /*
  * Get number of LUNs by the way of Bulk GetMaxLUN command.
  */
@@ -2333,7 +2408,7 @@ static int ub_probe(struct usb_interface *intf,
        if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
                goto err_core;
        memset(sc, 0, sizeof(struct ub_dev));
-       spin_lock_init(&sc->lock);
+       sc->lock = ub_next_lock();
        INIT_LIST_HEAD(&sc->luns);
        usb_init_urb(&sc->work_urb);
        tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc);
@@ -2483,7 +2558,7 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum)
        disk->driverfs_dev = &sc->intf->dev;
 
        rc = -ENOMEM;
-       if ((q = blk_init_queue(ub_request_fn, &sc->lock)) == NULL)
+       if ((q = blk_init_queue(ub_request_fn, sc->lock)) == NULL)
                goto err_blkqinit;
 
        disk->queue = q;
@@ -2554,7 +2629,7 @@ static void ub_disconnect(struct usb_interface *intf)
         * and the whole queue drains. So, we just use this code to
         * print warnings.
         */
-       spin_lock_irqsave(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
        {
                struct ub_scsi_cmd *cmd;
                int cnt = 0;
@@ -2571,7 +2646,7 @@ static void ub_disconnect(struct usb_interface *intf)
                            "%d was queued after shutdown\n", sc->name, cnt);
                }
        }
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
 
        /*
         * Unregister the upper layer.
@@ -2590,19 +2665,15 @@ static void ub_disconnect(struct usb_interface *intf)
        }
 
        /*
-        * Taking a lock on a structure which is about to be freed
-        * is very nonsensual. Here it is largely a way to do a debug freeze,
-        * and a bracket which shows where the nonsensual code segment ends.
-        *
         * Testing for -EINPROGRESS is always a bug, so we are bending
         * the rules a little.
         */
-       spin_lock_irqsave(&sc->lock, flags);
+       spin_lock_irqsave(sc->lock, flags);
        if (sc->work_urb.status == -EINPROGRESS) {      /* janitors: ignore */
                printk(KERN_WARNING "%s: "
                    "URB is active after disconnect\n", sc->name);
        }
-       spin_unlock_irqrestore(&sc->lock, flags);
+       spin_unlock_irqrestore(sc->lock, flags);
 
        /*
         * There is virtually no chance that other CPU runs times so long
@@ -2636,6 +2707,10 @@ static struct usb_driver ub_driver = {
 static int __init ub_init(void)
 {
        int rc;
+       int i;
+
+       for (i = 0; i < UB_QLOCK_NUM; i++)
+               spin_lock_init(&ub_qlockv[i]);
 
        if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0)
                goto err_regblkdev;
index 2f1289eebb3c5e597ec5ffa828606cdb1bd1ac85..222a1cc4aa28814a118c9424dbe2e2cb8deb5fa9 100644 (file)
@@ -11,8 +11,7 @@ config HOTPLUG_PCI
        ---help---
          Say Y here if you have a motherboard with a PCI Hotplug controller.
          This allows you to add and remove PCI cards while the machine is
-         powered up and running.  The file system pcihpfs must be mounted
-         in order to interact with any PCI Hotplug controllers.
+         powered up and running.
 
          To compile this driver as a module, choose M here: the
          module will be called pci_hotplug.
index 7e7f913ba7b9e47712a9dc6cfd0d4ca5764bbe89..317457dd401451b2797f8c8135165d983f09d5f1 100644 (file)
@@ -302,7 +302,7 @@ static int ibm_get_table_from_acpi(char **bufp)
        }
 
        package = (union acpi_object *) buffer.pointer;
-       if(!(package) ||
+       if (!(package) ||
                        (package->type != ACPI_TYPE_PACKAGE) ||
                        !(package->package.elements)) {
                err("%s:  Invalid APCI object\n", __FUNCTION__);
@@ -405,7 +405,7 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
        }
        info.hardware_id.value[sizeof(info.hardware_id.value) - 1] = '\0';
 
-       if(info.current_status && (info.valid & ACPI_VALID_HID) &&
+       if (info.current_status && (info.valid & ACPI_VALID_HID) &&
                        (!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) ||
                        !strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) {
                dbg("found hardware: %s, handle: %p\n", info.hardware_id.value,
@@ -449,13 +449,11 @@ static int __init ibm_acpiphp_init(void)
        }
 
        ibm_note.device = device;
-       status = acpi_install_notify_handler(
-                       ibm_acpi_handle,
-                       ACPI_DEVICE_NOTIFY,
-                       ibm_handle_events,
+       status = acpi_install_notify_handler(ibm_acpi_handle,
+                       ACPI_DEVICE_NOTIFY, ibm_handle_events,
                        &ibm_note);
        if (ACPI_FAILURE(status)) {
-               err("%s:  Failed to register notification handler\n",
+               err("%s: Failed to register notification handler\n",
                                __FUNCTION__);
                retval = -EBUSY;
                goto init_cleanup;
@@ -482,14 +480,13 @@ static void __exit ibm_acpiphp_exit(void)
        if (acpiphp_unregister_attention(&ibm_attention_info))
                err("%s: attention info deregistration failed", __FUNCTION__);
 
-          status = acpi_remove_notify_handler(
+       status = acpi_remove_notify_handler(
                           ibm_acpi_handle,
                           ACPI_DEVICE_NOTIFY,
                           ibm_handle_events);
-          if (ACPI_FAILURE(status))
-                  err("%s:  Notification handler removal failed\n",
-                                  __FUNCTION__);
-       // remove the /sys entries
+       if (ACPI_FAILURE(status))
+               err("%s: Notification handler removal failed\n", __FUNCTION__);
+       /* remove the /sys entries */
        if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
                err("%s: removal of sysfs file apci_table failed\n",
                                __FUNCTION__);
index aabf1e70b5280f70210c7f9efe840d3964ab1a31..dc59da675c0807e3913a1dbf420bf2d15817fbde 100644 (file)
@@ -235,12 +235,12 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
 {
        int rc = 0;
        struct slot *pslot;
-       u8 cmd;
+       u8 cmd = 0x00;     /* avoid compiler warning */
 
        debug("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n",
                        (ulong) hotplug_slot, value);
        ibmphp_lock_operations();
-       cmd = 0x00;     // avoid compiler warning
+
 
        if (hotplug_slot) {
                switch (value) {
index 7d93dbaf628deda9dc32811b5c0ed95c023ea500..3eefe2cec72d1aef2ad849820fd3b683d7704645 100644 (file)
@@ -103,13 +103,13 @@ static struct slot *find_slot(struct device_node *dn)
        struct list_head *tmp, *n;
        struct slot *slot;
 
-        list_for_each_safe(tmp, n, &rpaphp_slot_head) {
-                slot = list_entry(tmp, struct slot, rpaphp_slot_list);
-                if (slot->dn == dn)
-                        return slot;
-        }
+       list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+               slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+               if (slot->dn == dn)
+                       return slot;
+       }
 
-        return NULL;
+       return NULL;
 }
 
 static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
@@ -126,9 +126,9 @@ static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
        return NULL;
 }
 
-static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
+static void dlpar_pci_add_bus(struct device_node *dn)
 {
-       struct pci_dn *pdn = dn->data;
+       struct pci_dn *pdn = PCI_DN(dn);
        struct pci_controller *phb = pdn->phb;
        struct pci_dev *dev = NULL;
 
@@ -139,52 +139,52 @@ static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
        if (!dev) {
                printk(KERN_ERR "%s: failed to create pci dev for %s\n",
                                __FUNCTION__, dn->full_name);
-               return NULL;
+               return;
        }
 
        if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
            dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
                of_scan_pci_bridge(dn, dev);
 
-       rpaphp_init_new_devs(dev->subordinate);
+       pcibios_fixup_new_pci_devices(dev->subordinate,0);
 
        /* Claim new bus resources */
        pcibios_claim_one_bus(dev->bus);
 
        /* ioremap() for child bus, which may or may not succeed */
-       (void) remap_bus_range(dev->bus);
+       remap_bus_range(dev->subordinate);
 
        /* Add new devices to global lists.  Register in proc, sysfs. */
        pci_bus_add_devices(phb->bus);
-
-       /* Confirm new bridge dev was created */
-       dev = dlpar_find_new_dev(phb->bus, dn);
-       if (dev) {
-               if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
-                       printk(KERN_ERR "%s: unexpected header type %d\n",
-                               __FUNCTION__, dev->hdr_type);
-                       return NULL;
-               }
-       }
-
-       return dev;
 }
 
 static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
 {
        struct pci_dev *dev;
+       struct pci_controller *phb;
 
-       if (rpaphp_find_pci_bus(dn))
+       if (pcibios_find_pci_bus(dn))
                return -EINVAL;
 
        /* Add pci bus */
-       dev = dlpar_pci_add_bus(dn);
+       dlpar_pci_add_bus(dn);
+
+       /* Confirm new bridge dev was created */
+       phb = PCI_DN(dn)->phb;
+       dev = dlpar_find_new_dev(phb->bus, dn);
+
        if (!dev) {
                printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
                        drc_name);
                return -EIO;
        }
 
+       if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+               printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
+                       __FUNCTION__, dev->hdr_type, drc_name);
+               return -EIO;
+       }
+
        /* Add hotplug slot */
        if (rpaphp_add_slot(dn)) {
                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
@@ -221,13 +221,13 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
        struct pci_dn *pdn;
        int rc = 0;
 
-       if (!rpaphp_find_pci_bus(dn))
+       if (!pcibios_find_pci_bus(dn))
                return -EINVAL;
 
        slot = find_slot(dn);
        if (slot) {
                /* Remove hotplug slot */
-               if (rpaphp_remove_slot(slot)) {
+               if (rpaphp_deregister_slot(slot)) {
                        printk(KERN_ERR
                                "%s: unable to remove hotplug slot %s\n",
                                __FUNCTION__, drc_name);
@@ -366,21 +366,25 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
        struct pci_bus *bus;
        struct slot *slot;
 
-       bus = rpaphp_find_pci_bus(dn);
+       bus = pcibios_find_pci_bus(dn);
        if (!bus)
                return -EINVAL;
 
        slot = find_slot(dn);
        if (slot) {
                /* Remove hotplug slot */
-               if (rpaphp_remove_slot(slot)) {
+               if (rpaphp_deregister_slot(slot)) {
                        printk(KERN_ERR
                                "%s: unable to remove hotplug slot %s\n",
                                __FUNCTION__, drc_name);
                        return -EIO;
                }
        } else {
-               rpaphp_unconfig_pci_adapter(bus);
+               struct pci_dev *dev, *tmp;
+               list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
+                       eeh_remove_bus_device(dev);
+                       pci_remove_bus_device(dev);
+               }
        }
 
        if (unmap_bus_range(bus)) {
index 57ea71a7bda5049210b418cfb357a703a945a85c..310b6186c0e5f45aa19f11654a49c38fcb839458 100644 (file)
@@ -88,16 +88,10 @@ extern int num_slots;
 /* function prototypes */
 
 /* rpaphp_pci.c */
-extern struct pci_bus *rpaphp_find_pci_bus(struct device_node *dn);
-extern int rpaphp_claim_resource(struct pci_dev *dev, int resource);
 extern int rpaphp_enable_pci_slot(struct slot *slot);
-extern int register_pci_slot(struct slot *slot);
+extern int rpaphp_register_pci_slot(struct slot *slot);
 extern int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value);
-extern void rpaphp_init_new_devs(struct pci_bus *bus);
-extern void rpaphp_eeh_init_nodes(struct device_node *dn);
-
-extern int rpaphp_config_pci_adapter(struct pci_bus *bus);
-extern int rpaphp_unconfig_pci_adapter(struct pci_bus *bus);
+extern int rpaphp_get_sensor_state(struct slot *slot, int *state);
 
 /* rpaphp_core.c */
 extern int rpaphp_add_slot(struct device_node *dn);
@@ -108,8 +102,8 @@ extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
 /* rpaphp_slot.c */
 extern void dealloc_slot_struct(struct slot *slot);
 extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
-extern int register_slot(struct slot *slot);
-extern int deregister_slot(struct slot *slot);
+extern int rpaphp_register_slot(struct slot *slot);
+extern int rpaphp_deregister_slot(struct slot *slot);
 extern int rpaphp_get_power_status(struct slot *slot, u8 * value);
 extern int rpaphp_set_attention_status(struct slot *slot, u8 status);
        
index cf075c34b578c4e4048b47da205efb882acff96f..6e79f5675b0d9b804a8c8465e07f0884d59dc63a 100644 (file)
@@ -56,25 +56,6 @@ MODULE_LICENSE("GPL");
 
 module_param(debug, bool, 0644);
 
-static int enable_slot(struct hotplug_slot *slot);
-static int disable_slot(struct hotplug_slot *slot);
-static int set_attention_status(struct hotplug_slot *slot, u8 value);
-static int get_power_status(struct hotplug_slot *slot, u8 * value);
-static int get_attention_status(struct hotplug_slot *slot, u8 * value);
-static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
-
-struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
-       .owner = THIS_MODULE,
-       .enable_slot = enable_slot,
-       .disable_slot = disable_slot,
-       .set_attention_status = set_attention_status,
-       .get_power_status = get_power_status,
-       .get_attention_status = get_attention_status,
-       .get_adapter_status = get_adapter_status,
-       .get_max_bus_speed = get_max_bus_speed,
-};
-
 static int rpaphp_get_attention_status(struct slot *slot)
 {
        return slot->hotplug_slot->info->attention_status;
@@ -196,11 +177,6 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe
        return 0;
 }
 
-int rpaphp_remove_slot(struct slot *slot)
-{
-       return deregister_slot(slot);
-}
-
 static int get_children_props(struct device_node *dn, int **drc_indexes,
                int **drc_names, int **drc_types, int **drc_power_domains)
 {
@@ -307,13 +283,15 @@ static int is_php_dn(struct device_node *dn, int **indexes, int **names,
        return 0;
 }
 
-/****************************************************************
+/**
+ * rpaphp_add_slot -- add hotplug or dlpar slot
+ *
  *     rpaphp not only registers PCI hotplug slots(HOTPLUG), 
  *     but also logical DR slots(EMBEDDED).
  *     HOTPLUG slot: An adapter can be physically added/removed. 
  *     EMBEDDED slot: An adapter can be logically removed/added
  *               from/to a partition with the slot.
- ***************************************************************/
+ */
 int rpaphp_add_slot(struct device_node *dn)
 {
        struct slot *slot;
@@ -344,7 +322,7 @@ int rpaphp_add_slot(struct device_node *dn)
                        dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
                                        indexes[i + 1], name, type);
 
-                       retval = register_pci_slot(slot);
+                       retval = rpaphp_register_pci_slot(slot);
                }
        }
 exit:
@@ -393,53 +371,85 @@ static void __exit rpaphp_exit(void)
        cleanup_slots();
 }
 
-static int enable_slot(struct hotplug_slot *hotplug_slot)
+static int __enable_slot(struct slot *slot)
 {
-       int retval = 0;
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       int state;
+       int retval;
 
-       if (slot->state == CONFIGURED) {
-               dbg("%s: %s is already enabled\n", __FUNCTION__, slot->name);
-               goto exit;
+       if (slot->state == CONFIGURED)
+               return 0;
+
+       retval = rpaphp_get_sensor_state(slot, &state);
+       if (retval)
+               return retval;
+
+       if (state == PRESENT) {
+               pcibios_add_pci_devices(slot->bus);
+               slot->state = CONFIGURED;
+       } else if (state == EMPTY) {
+               slot->state = EMPTY;
+       } else {
+               err("%s: slot[%s] is in invalid state\n", __FUNCTION__, slot->name);
+               slot->state = NOT_VALID;
+               return -EINVAL;
        }
+       return 0;
+}
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+       int retval;
+       struct slot *slot = (struct slot *)hotplug_slot->private;
 
-       dbg("ENABLING SLOT %s\n", slot->name);
        down(&rpaphp_sem);
-       retval = rpaphp_enable_pci_slot(slot);
+       retval = __enable_slot(slot);
        up(&rpaphp_sem);
-exit:
-       dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+
        return retval;
 }
 
-static int disable_slot(struct hotplug_slot *hotplug_slot)
+static int __disable_slot(struct slot *slot)
 {
-       int retval = -EINVAL;
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       struct pci_dev *dev, *tmp;
 
-       dbg("%s - Entry: slot[%s]\n", __FUNCTION__, slot->name);
+       if (slot->state == NOT_CONFIGURED)
+               return -EINVAL;
 
-       if (slot->state == NOT_CONFIGURED) {
-               dbg("%s: %s is already disabled\n", __FUNCTION__, slot->name);
-               goto exit;
+       list_for_each_entry_safe(dev, tmp, &slot->bus->devices, bus_list) {
+               eeh_remove_bus_device(dev);
+               pci_remove_bus_device(dev);
        }
 
-       dbg("DISABLING SLOT %s\n", slot->name);
+       slot->state = NOT_CONFIGURED;
+       return 0;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+       struct slot *slot = (struct slot *)hotplug_slot->private;
+       int retval;
+
        down(&rpaphp_sem);
-       retval = rpaphp_unconfig_pci_adapter(slot->bus);
+       retval = __disable_slot (slot);
        up(&rpaphp_sem);
-       slot->state = NOT_CONFIGURED;
-       info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__,
-            slot->name);
-exit:
-       dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+
        return retval;
 }
 
+struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
+       .owner = THIS_MODULE,
+       .enable_slot = enable_slot,
+       .disable_slot = disable_slot,
+       .set_attention_status = set_attention_status,
+       .get_power_status = get_power_status,
+       .get_attention_status = get_attention_status,
+       .get_adapter_status = get_adapter_status,
+       .get_max_bus_speed = get_max_bus_speed,
+};
+
 module_init(rpaphp_init);
 module_exit(rpaphp_exit);
 
 EXPORT_SYMBOL_GPL(rpaphp_add_slot);
-EXPORT_SYMBOL_GPL(rpaphp_remove_slot);
 EXPORT_SYMBOL_GPL(rpaphp_slot_head);
 EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
index 396b54b0c8474cc6fa38fc04aba030c073a5641e..6f6cbede5135cde8cd7f9a8a2b68c6a77b7c8ea9 100644 (file)
 #include "../pci.h"            /* for pci_add_new_bus */
 #include "rpaphp.h"
 
-static struct pci_bus *find_bus_among_children(struct pci_bus *bus,
-                                       struct device_node *dn)
-{
-       struct pci_bus *child = NULL;
-       struct list_head *tmp;
-       struct device_node *busdn;
-
-       busdn = pci_bus_to_OF_node(bus);
-       if (busdn == dn)
-               return bus;
-
-       list_for_each(tmp, &bus->children) {
-               child = find_bus_among_children(pci_bus_b(tmp), dn);
-               if (child)
-                       break;
-       }
-       return child;
-}
-
-struct pci_bus *rpaphp_find_pci_bus(struct device_node *dn)
-{
-       struct pci_dn *pdn = dn->data;
-
-       if (!pdn  || !pdn->phb || !pdn->phb->bus)
-               return NULL;
-
-       return find_bus_among_children(pdn->phb->bus, dn);
-}
-EXPORT_SYMBOL_GPL(rpaphp_find_pci_bus);
-
-static int rpaphp_get_sensor_state(struct slot *slot, int *state)
+int rpaphp_get_sensor_state(struct slot *slot, int *state)
 {
        int rc;
        int setlevel;
@@ -120,7 +90,7 @@ int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value)
                        /* config/unconfig adapter */
                        *value = slot->state;
                } else {
-                       bus = rpaphp_find_pci_bus(slot->dn);
+                       bus = pcibios_find_pci_bus(slot->dn);
                        if (bus && !list_empty(&bus->devices))
                                *value = CONFIGURED;
                        else
@@ -131,140 +101,6 @@ exit:
        return rc;
 }
 
-/* Must be called before pci_bus_add_devices */
-void rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus)
-{
-       struct pci_dev *dev;
-
-       list_for_each_entry(dev, &bus->devices, bus_list) {
-               /*
-                * Skip already-present devices (which are on the
-                * global device list.)
-                */
-               if (list_empty(&dev->global_list)) {
-                       int i;
-                       
-                       /* Need to setup IOMMU tables */
-                       ppc_md.iommu_dev_setup(dev);
-
-                       if(fix_bus)
-                               pcibios_fixup_device_resources(dev, bus);
-                       pci_read_irq_line(dev);
-                       for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-                               struct resource *r = &dev->resource[i];
-
-                               if (r->parent || !r->start || !r->flags)
-                                       continue;
-                               pci_claim_resource(dev, i);
-                       }
-               }
-       }
-}
-
-static void rpaphp_eeh_add_bus_device(struct pci_bus *bus)
-{
-       struct pci_dev *dev;
-
-       list_for_each_entry(dev, &bus->devices, bus_list) {
-               eeh_add_device_late(dev);
-               if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-                       struct pci_bus *subbus = dev->subordinate;
-                       if (subbus)
-                               rpaphp_eeh_add_bus_device (subbus);
-               }
-       }
-}
-
-static int rpaphp_pci_config_bridge(struct pci_dev *dev)
-{
-       u8 sec_busno;
-       struct pci_bus *child_bus;
-       struct pci_dev *child_dev;
-
-       dbg("Enter %s:  BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev));
-
-       /* get busno of downstream bus */
-       pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
-               
-       /* add to children of PCI bridge dev->bus */
-       child_bus = pci_add_new_bus(dev->bus, dev, sec_busno);
-       if (!child_bus) {
-               err("%s: could not add second bus\n", __FUNCTION__);
-               return -EIO;
-       }
-       sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number);
-       /* do pci_scan_child_bus */
-       pci_scan_child_bus(child_bus);
-
-       list_for_each_entry(child_dev, &child_bus->devices, bus_list) {
-               eeh_add_device_late(child_dev);
-       }
-
-        /* fixup new pci devices without touching bus struct */
-       rpaphp_fixup_new_pci_devices(child_bus, 0);
-
-       /* Make the discovered devices available */
-       pci_bus_add_devices(child_bus);
-       return 0;
-}
-
-void rpaphp_init_new_devs(struct pci_bus *bus)
-{
-       rpaphp_fixup_new_pci_devices(bus, 0);
-       rpaphp_eeh_add_bus_device(bus);
-}
-EXPORT_SYMBOL_GPL(rpaphp_init_new_devs);
-
-/*****************************************************************************
- rpaphp_pci_config_slot() will  configure all devices under the
- given slot->dn and return the the first pci_dev.
- *****************************************************************************/
-static struct pci_dev *
-rpaphp_pci_config_slot(struct pci_bus *bus)
-{
-       struct device_node *dn = pci_bus_to_OF_node(bus);
-       struct pci_dev *dev = NULL;
-       int slotno;
-       int num;
-
-       dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name);
-       if (!dn || !dn->child)
-               return NULL;
-
-       if (_machine == PLATFORM_PSERIES_LPAR) {
-               of_scan_bus(dn, bus);
-               if (list_empty(&bus->devices)) {
-                       err("%s: No new device found\n", __FUNCTION__);
-                       return NULL;
-               }
-
-               rpaphp_init_new_devs(bus);
-               pci_bus_add_devices(bus);
-               dev = list_entry(&bus->devices, struct pci_dev, bus_list);
-       } else {
-               slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
-
-               /* pci_scan_slot should find all children */
-               num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
-               if (num) {
-                       rpaphp_fixup_new_pci_devices(bus, 1);
-                       pci_bus_add_devices(bus);
-               }
-               if (list_empty(&bus->devices)) {
-                       err("%s: No new device found\n", __FUNCTION__);
-                       return NULL;
-               }
-               list_for_each_entry(dev, &bus->devices, bus_list) {
-                       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
-                               rpaphp_pci_config_bridge(dev);
-
-                       rpaphp_eeh_add_bus_device(bus);
-               }
-       }
-
-       return dev;
-}
-
 static void print_slot_pci_funcs(struct pci_bus *bus)
 {
        struct device_node *dn;
@@ -280,60 +116,6 @@ static void print_slot_pci_funcs(struct pci_bus *bus)
        return;
 }
 
-int rpaphp_config_pci_adapter(struct pci_bus *bus)
-{
-       struct device_node *dn = pci_bus_to_OF_node(bus);
-       struct pci_dev *dev;
-       int rc = -ENODEV;
-
-       dbg("Entry %s: slot[%s]\n", __FUNCTION__, dn->full_name);
-       if (!dn)
-               goto exit;
-
-       eeh_add_device_tree_early(dn);
-       dev = rpaphp_pci_config_slot(bus);
-       if (!dev) {
-               err("%s: can't find any devices.\n", __FUNCTION__);
-               goto exit;
-       }
-       print_slot_pci_funcs(bus);
-       rc = 0;
-exit:
-       dbg("Exit %s:  rc=%d\n", __FUNCTION__, rc);
-       return rc;
-}
-EXPORT_SYMBOL_GPL(rpaphp_config_pci_adapter);
-
-static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev)
-{
-       eeh_remove_device(dev);
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-               struct pci_bus *bus = dev->subordinate;
-               struct list_head *ln;
-               if (!bus)
-                       return; 
-               for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
-                       struct pci_dev *pdev = pci_dev_b(ln);
-                       if (pdev)
-                               rpaphp_eeh_remove_bus_device(pdev);
-               }
-
-       }
-       return;
-}
-
-int rpaphp_unconfig_pci_adapter(struct pci_bus *bus)
-{
-       struct pci_dev *dev, *tmp;
-
-       list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
-               rpaphp_eeh_remove_bus_device(dev);
-               pci_remove_bus_device(dev);
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rpaphp_unconfig_pci_adapter);
-
 static int setup_pci_hotplug_slot_info(struct slot *slot)
 {
        struct hotplug_slot_info *hotplug_slot_info = slot->hotplug_slot->info;
@@ -370,7 +152,7 @@ static int setup_pci_slot(struct slot *slot)
        struct pci_bus *bus;
 
        BUG_ON(!dn);
-       bus = rpaphp_find_pci_bus(dn);
+       bus = pcibios_find_pci_bus(dn);
        if (!bus) {
                err("%s: no pci_bus for dn %s\n", __FUNCTION__, dn->full_name);
                goto exit_rc;
@@ -395,10 +177,7 @@ static int setup_pci_slot(struct slot *slot)
                if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) {
                        dbg("%s CONFIGURING pci adapter in slot[%s]\n",  
                                __FUNCTION__, slot->name);
-                       if (rpaphp_config_pci_adapter(slot->bus)) {
-                               err("%s: CONFIG pci adapter failed\n", __FUNCTION__);
-                               goto exit_rc;           
-                       }
+                       pcibios_add_pci_devices(slot->bus);
 
                } else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) {
                        err("%s: slot[%s]'s adapter_status is NOT_VALID.\n",
@@ -420,7 +199,7 @@ exit_rc:
        return -EINVAL;
 }
 
-int register_pci_slot(struct slot *slot)
+int rpaphp_register_pci_slot(struct slot *slot)
 {
        int rc = -EINVAL;
 
@@ -428,42 +207,8 @@ int register_pci_slot(struct slot *slot)
                goto exit_rc;
        if (setup_pci_slot(slot))
                goto exit_rc;
-       rc = register_slot(slot);
+       rc = rpaphp_register_slot(slot);
 exit_rc:
        return rc;
 }
 
-int rpaphp_enable_pci_slot(struct slot *slot)
-{
-       int retval = 0, state;
-
-       retval = rpaphp_get_sensor_state(slot, &state);
-       if (retval)
-               goto exit;
-       dbg("%s: sensor state[%d]\n", __FUNCTION__, state);
-       /* if slot is not empty, enable the adapter */
-       if (state == PRESENT) {
-               dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name);
-               retval = rpaphp_config_pci_adapter(slot->bus);
-               if (!retval) {
-                       slot->state = CONFIGURED;
-                       info("%s: devices in slot[%s] configured\n",
-                                       __FUNCTION__, slot->name);
-               } else {
-                       slot->state = NOT_CONFIGURED;
-                       dbg("%s: no pci_dev struct for adapter in slot[%s]\n",
-                           __FUNCTION__, slot->name);
-               }
-       } else if (state == EMPTY) {
-               dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name);
-               slot->state = EMPTY;
-       } else {
-               err("%s: slot[%s] is in invalid state\n", __FUNCTION__,
-                   slot->name);
-               slot->state = NOT_VALID;
-               retval = -EINVAL;
-       }
-exit:
-       dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
-       return retval;
-}
index daa89ae57123b52b97c0c4678c41867118ed39f9..04cc1e7275ce1c57cb78a768ed6808b88cba8124 100644 (file)
 
 static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf)
 {
-        char *value;
-        int retval = -ENOENT;
+       char *value;
+       int retval = -ENOENT;
        struct slot *slot = (struct slot *)php_slot->private;
 
        if (!slot)
                return retval;
 
-        value = slot->location;
-        retval = sprintf (buf, "%s\n", value);
-        return retval;
+       value = slot->location;
+       retval = sprintf (buf, "%s\n", value);
+       return retval;
 }
 
 static struct hotplug_slot_attribute hotplug_slot_attr_location = {
@@ -137,7 +137,7 @@ static int is_registered(struct slot *slot)
        return 0;
 }
 
-int deregister_slot(struct slot *slot)
+int rpaphp_deregister_slot(struct slot *slot)
 {
        int retval = 0;
        struct hotplug_slot *php_slot = slot->hotplug_slot;
@@ -160,7 +160,7 @@ int deregister_slot(struct slot *slot)
        return retval;
 }
 
-int register_slot(struct slot *slot)
+int rpaphp_register_slot(struct slot *slot)
 {
        int retval;
 
@@ -169,7 +169,7 @@ int register_slot(struct slot *slot)
                slot->power_domain, slot->type);
        /* should not try to register the same slot twice */
        if (is_registered(slot)) { /* should't be here */
-               err("register_slot: slot[%s] is already registered\n", slot->name);
+               err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
                rpaphp_release_slot(slot->hotplug_slot);
                return -EAGAIN;
        }       
index ce0e9b6ce83378fa416e9f6ed46a7562a0c64689..7d6f521d02ea128adf2fa0472f9afcd093186476 100644 (file)
@@ -95,6 +95,7 @@ struct controller {
        u8 function;
        u8 slot_device_offset;
        u8 add_support;
+       u32 pcix_misc2_reg;     /* for amd pogo errata */
        enum pci_bus_speed speed;
        u32 first_slot;         /* First physical slot number */
        u8 slot_bus;            /* Bus where the slots handled by this controller sit */
@@ -113,6 +114,26 @@ struct hotplug_params {
 
 /* Define AMD SHPC ID  */
 #define PCI_DEVICE_ID_AMD_GOLAM_7450   0x7450 
+#define PCI_DEVICE_ID_AMD_POGO_7458    0x7458
+
+/* AMD PCIX bridge registers */
+
+#define PCIX_MEM_BASE_LIMIT_OFFSET     0x1C
+#define PCIX_MISCII_OFFSET             0x48
+#define PCIX_MISC_BRIDGE_ERRORS_OFFSET 0x80
+
+/* AMD PCIX_MISCII masks and offsets */
+#define PERRNONFATALENABLE_MASK                0x00040000
+#define PERRFATALENABLE_MASK           0x00080000
+#define PERRFLOODENABLE_MASK           0x00100000
+#define SERRNONFATALENABLE_MASK                0x00200000
+#define SERRFATALENABLE_MASK           0x00400000
+
+/* AMD PCIX_MISC_BRIDGE_ERRORS masks and offsets */
+#define PERR_OBSERVED_MASK             0x00000001
+
+/* AMD PCIX_MEM_BASE_LIMIT masks */
+#define RSE_MASK                       0x40000000
 
 #define INT_BUTTON_IGNORE              0
 #define INT_PRESENCE_ON                        1
@@ -333,6 +354,79 @@ static inline int wait_for_ctrl_irq (struct controller *ctrl)
        return retval;
 }
 
+static inline void amd_pogo_errata_save_misc_reg(struct slot *p_slot)
+{
+       u32 pcix_misc2_temp;
+
+       /* save MiscII register */
+       pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp);
+
+       p_slot->ctrl->pcix_misc2_reg = pcix_misc2_temp;
+
+       /* clear SERR/PERR enable bits */
+       pcix_misc2_temp &= ~SERRFATALENABLE_MASK;
+       pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK;
+       pcix_misc2_temp &= ~PERRFLOODENABLE_MASK;
+       pcix_misc2_temp &= ~PERRFATALENABLE_MASK;
+       pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK;
+       pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp);
+}
+
+static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot)
+{
+       u32 pcix_misc2_temp;
+       u32 pcix_bridge_errors_reg;
+       u32 pcix_mem_base_reg;
+       u8  perr_set;
+       u8  rse_set;
+
+       /* write-one-to-clear Bridge_Errors[ PERR_OBSERVED ] */
+       pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg);
+       perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK;
+       if (perr_set) {
+               dbg ("%s  W1C: Bridge_Errors[ PERR_OBSERVED = %08X]\n",__FUNCTION__ , perr_set);
+
+               pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set);
+       }
+
+       /* write-one-to-clear Memory_Base_Limit[ RSE ] */
+       pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg);
+       rse_set = pcix_mem_base_reg & RSE_MASK;
+       if (rse_set) {
+               dbg ("%s  W1C: Memory_Base_Limit[ RSE ]\n",__FUNCTION__ );
+
+               pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set);
+       }
+       /* restore MiscII register */
+       pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
+
+       if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK)
+               pcix_misc2_temp |= SERRFATALENABLE_MASK;
+       else
+               pcix_misc2_temp &= ~SERRFATALENABLE_MASK;
+
+       if (p_slot->ctrl->pcix_misc2_reg & SERRNONFATALENABLE_MASK)
+               pcix_misc2_temp |= SERRNONFATALENABLE_MASK;
+       else
+               pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK;
+
+       if (p_slot->ctrl->pcix_misc2_reg & PERRFLOODENABLE_MASK)
+               pcix_misc2_temp |= PERRFLOODENABLE_MASK;
+       else
+               pcix_misc2_temp &= ~PERRFLOODENABLE_MASK;
+
+       if (p_slot->ctrl->pcix_misc2_reg & PERRFATALENABLE_MASK)
+               pcix_misc2_temp |= PERRFATALENABLE_MASK;
+       else
+               pcix_misc2_temp &= ~PERRFATALENABLE_MASK;
+
+       if (p_slot->ctrl->pcix_misc2_reg & PERRNONFATALENABLE_MASK)
+               pcix_misc2_temp |= PERRNONFATALENABLE_MASK;
+       else
+               pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK;
+       pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp);
+}
+
 #define SLOT_NAME_SIZE 10
 
 static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
index 25ccb0e475936b93e1fbda46d3b5154fa98d6e16..643252d9bf3b61e0b2333a082761a09950461c38 100644 (file)
@@ -894,7 +894,17 @@ int shpchp_enable_slot (struct slot *p_slot)
        dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save);
        p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
 
-       rc = board_added(p_slot);
+       if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
+           (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458))
+            && p_slot->ctrl->num_slots == 1) {
+               /* handle amd pogo errata; this must be done before enable  */
+               amd_pogo_errata_save_misc_reg(p_slot);
+               rc = board_added(p_slot);
+               /* handle amd pogo errata; this must be done after enable  */
+               amd_pogo_errata_restore_misc_reg(p_slot);
+       } else
+               rc = board_added(p_slot);
+
        if (rc) {
                p_slot->hpc_ops->get_adapter_status(p_slot,
                                &(p_slot->presence_save));
index 8e1ba0b7a8e41770a2ab4e18c835b682c2a2a0a1..48723d6fa60f249343ee4bc7b065aed0150d8c01 100644 (file)
@@ -137,6 +137,8 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask)
                break;
        }
 }
+#else
+#define set_msi_affinity NULL
 #endif /* CONFIG_SMP */
 
 static void mask_MSI_irq(unsigned int vector)
@@ -214,7 +216,7 @@ static struct hw_interrupt_type msix_irq_type = {
        .disable        = mask_MSI_irq,
        .ack            = mask_MSI_irq,
        .end            = end_msi_irq_w_maskbit,
-       .set_affinity   = set_msi_irq_affinity
+       .set_affinity   = set_msi_affinity
 };
 
 /*
@@ -230,7 +232,7 @@ static struct hw_interrupt_type msi_irq_w_maskbit_type = {
        .disable        = mask_MSI_irq,
        .ack            = mask_MSI_irq,
        .end            = end_msi_irq_w_maskbit,
-       .set_affinity   = set_msi_irq_affinity
+       .set_affinity   = set_msi_affinity
 };
 
 /*
@@ -246,7 +248,7 @@ static struct hw_interrupt_type msi_irq_wo_maskbit_type = {
        .disable        = do_nothing,
        .ack            = do_nothing,
        .end            = end_msi_irq_wo_maskbit,
-       .set_affinity   = set_msi_irq_affinity
+       .set_affinity   = set_msi_affinity
 };
 
 static void msi_data_init(struct msg_data *msi_data,
index 402136a5c9e43c1af7ff107e8e20cd992657c30c..4ac52d441e472d86e9e15e0f48d761e6a3c51223 100644 (file)
@@ -22,12 +22,6 @@ extern int vector_irq[NR_VECTORS];
 extern void (*interrupt[NR_IRQS])(void);
 extern int pci_vector_resources(int last, int nr_released);
 
-#ifdef CONFIG_SMP
-#define set_msi_irq_affinity   set_msi_affinity
-#else
-#define set_msi_irq_affinity   NULL
-#endif
-
 /*
  * MSI-X Address Register
  */
index d2a633efa10ab0822c3f8a6431a98f413c3b24bb..d2d1879166436cfee7f82c6e0fa0493cfdc8c3b0 100644 (file)
@@ -163,6 +163,7 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
        return __pci_bus_find_cap(bus, devfn, hdr_type & 0x7f, cap);
 }
 
+#if 0
 /**
  * pci_find_ext_capability - Find an extended capability
  * @dev: PCI device to query
@@ -210,6 +211,7 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
 
        return 0;
 }
+#endif  /*  0  */
 
 /**
  * pci_find_parent_resource - return resource region of parent bus of given region
index 50d6685dcbcce801682c9600a81be2a98f90f8a1..ea9277b7f8994120aed0b3d058da25de31e99c57 100644 (file)
@@ -112,6 +112,7 @@ pci_claim_resource(struct pci_dev *dev, int resource)
 
        return err;
 }
+EXPORT_SYMBOL_GPL(pci_claim_resource);
 
 int pci_assign_resource(struct pci_dev *dev, int resno)
 {
index 3639c3f8d3578f0fdb9fa5841ed04a410f379b99..36e476dd912388d0257ccdd82c15af888c5e8256 100644 (file)
@@ -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/
index af0a41e7870e0955eaf04e4e63608be04150982d..04631dcbabbce2673529aeffc7e16131d796d817 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/init.h>
 #include <linux/device.h>      /* FIXME: linux/firmware.h should include it itself */
 #include <linux/firmware.h>
+#include <linux/mutex.h>
 
 #include "usbatm.h"
 
@@ -160,7 +161,7 @@ struct cxacru_data {
        struct work_struct poll_work;
 
        /* contol handles */
-       struct semaphore cm_serialize;
+       struct mutex cm_serialize;
        u8 *rcv_buf;
        u8 *snd_buf;
        struct urb *rcv_urb;
@@ -219,7 +220,7 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
                goto fail;
        }
 
-       down(&instance->cm_serialize);
+       mutex_lock(&instance->cm_serialize);
 
        /* submit reading urb before the writing one */
        init_completion(&instance->rcv_done);
@@ -288,7 +289,7 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
        ret = offd;
        dbg("cm %#x", cm);
 fail:
-       up(&instance->cm_serialize);
+       mutex_unlock(&instance->cm_serialize);
        return ret;
 }
 
@@ -352,7 +353,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
                struct atm_dev *atm_dev)
 {
        struct cxacru_data *instance = usbatm_instance->driver_data;
-       struct device *dev = &usbatm_instance->usb_intf->dev;
        /*
        struct atm_dev *atm_dev = usbatm_instance->atm_dev;
        */
@@ -364,14 +364,14 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
        ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0,
                        atm_dev->esi, sizeof(atm_dev->esi));
        if (ret < 0) {
-               dev_err(dev, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);
+               atm_err(usbatm_instance, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);
                return ret;
        }
 
        /* start ADSL */
        ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
        if (ret < 0) {
-               dev_err(dev, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
+               atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
                return ret;
        }
 
@@ -383,13 +383,13 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
 static void cxacru_poll_status(struct cxacru_data *instance)
 {
        u32 buf[CXINF_MAX] = {};
-       struct device *dev = &instance->usbatm->usb_intf->dev;
-       struct atm_dev *atm_dev = instance->usbatm->atm_dev;
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct atm_dev *atm_dev = usbatm->atm_dev;
        int ret;
 
        ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
        if (ret < 0) {
-               dev_warn(dev, "poll status: error %d\n", ret);
+               atm_warn(usbatm, "poll status: error %d\n", ret);
                goto reschedule;
        }
 
@@ -400,50 +400,50 @@ static void cxacru_poll_status(struct cxacru_data *instance)
        switch (instance->line_status) {
        case 0:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: down\n");
+               atm_info(usbatm, "ADSL line: down\n");
                break;
 
        case 1:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: attemtping to activate\n");
+               atm_info(usbatm, "ADSL line: attempting to activate\n");
                break;
 
        case 2:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: training\n");
+               atm_info(usbatm, "ADSL line: training\n");
                break;
 
        case 3:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: channel analysis\n");
+               atm_info(usbatm, "ADSL line: channel analysis\n");
                break;
 
        case 4:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: exchange\n");
+               atm_info(usbatm, "ADSL line: exchange\n");
                break;
 
        case 5:
                atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424;
                atm_dev->signal = ATM_PHY_SIG_FOUND;
 
-               dev_info(dev, "ADSL line: up (%d kb/s down | %d kb/s up)\n",
+               atm_info(usbatm, "ADSL line: up (%d kb/s down | %d kb/s up)\n",
                     buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]);
                break;
 
        case 6:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: waiting\n");
+               atm_info(usbatm, "ADSL line: waiting\n");
                break;
 
        case 7:
                atm_dev->signal = ATM_PHY_SIG_LOST;
-               dev_info(dev, "ADSL line: initializing\n");
+               atm_info(usbatm, "ADSL line: initializing\n");
                break;
 
        default:
                atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-               dev_info(dev, "Unknown line state %02x\n", instance->line_status);
+               atm_info(usbatm, "Unknown line state %02x\n", instance->line_status);
                break;
        }
 reschedule:
@@ -504,8 +504,8 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
 {
        int ret;
        int off;
-       struct usb_device *usb_dev = instance->usbatm->usb_dev;
-       struct device *dev = &instance->usbatm->usb_intf->dev;
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct usb_device *usb_dev = usbatm->usb_dev;
        u16 signature[] = { usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct };
        u32 val;
 
@@ -515,7 +515,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
        val = cpu_to_le32(instance->modem_type->pll_f_clk);
        ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4);
        if (ret) {
-               dev_err(dev, "FirmwarePllFClkValue failed: %d\n", ret);
+               usb_err(usbatm, "FirmwarePllFClkValue failed: %d\n", ret);
                return;
        }
 
@@ -523,7 +523,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
        val = cpu_to_le32(instance->modem_type->pll_b_clk);
        ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4);
        if (ret) {
-               dev_err(dev, "FirmwarePllBClkValue failed: %d\n", ret);
+               usb_err(usbatm, "FirmwarePllBClkValue failed: %d\n", ret);
                return;
        }
 
@@ -531,14 +531,14 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
        val = cpu_to_le32(SDRAM_ENA);
        ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4);
        if (ret) {
-               dev_err(dev, "Enable SDRAM failed: %d\n", ret);
+               usb_err(usbatm, "Enable SDRAM failed: %d\n", ret);
                return;
        }
 
        /* Firmware */
        ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size);
        if (ret) {
-               dev_err(dev, "Firmware upload failed: %d\n", ret);
+               usb_err(usbatm, "Firmware upload failed: %d\n", ret);
                return;
        }
 
@@ -546,7 +546,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
        if (instance->modem_type->boot_rom_patch) {
                ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size);
                if (ret) {
-                       dev_err(dev, "Boot ROM patching failed: %d\n", ret);
+                       usb_err(usbatm, "Boot ROM patching failed: %d\n", ret);
                        return;
                }
        }
@@ -554,7 +554,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
        /* Signature */
        ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4);
        if (ret) {
-               dev_err(dev, "Signature storing failed: %d\n", ret);
+               usb_err(usbatm, "Signature storing failed: %d\n", ret);
                return;
        }
 
@@ -566,7 +566,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
                ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0);
        }
        if (ret) {
-               dev_err(dev, "Passing control to firmware failed: %d\n", ret);
+               usb_err(usbatm, "Passing control to firmware failed: %d\n", ret);
                return;
        }
 
@@ -580,7 +580,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
 
        ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
        if (ret < 0) {
-               dev_err(dev, "modem failed to initialize: %d\n", ret);
+               usb_err(usbatm, "modem failed to initialize: %d\n", ret);
                return;
        }
 
@@ -597,7 +597,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
                        ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
                                        (u8 *) buf, len, NULL, 0);
                        if (ret < 0) {
-                               dev_err(dev, "load config data failed: %d\n", ret);
+                               usb_err(usbatm, "load config data failed: %d\n", ret);
                                return;
                        }
                }
@@ -608,18 +608,19 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
 static int cxacru_find_firmware(struct cxacru_data *instance,
                                char* phase, const struct firmware **fw_p)
 {
-       struct device *dev = &instance->usbatm->usb_intf->dev;
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct device *dev = &usbatm->usb_intf->dev;
        char buf[16];
 
        sprintf(buf, "cxacru-%s.bin", phase);
        dbg("cxacru_find_firmware: looking for %s", buf);
 
        if (request_firmware(fw_p, buf, dev)) {
-               dev_dbg(dev, "no stage %s firmware found\n", phase);
+               usb_dbg(usbatm, "no stage %s firmware found\n", phase);
                return -ENOENT;
        }
 
-       dev_info(dev, "found firmware %s\n", buf);
+       usb_info(usbatm, "found firmware %s\n", buf);
 
        return 0;
 }
@@ -627,20 +628,19 @@ static int cxacru_find_firmware(struct cxacru_data *instance,
 static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
                             struct usb_interface *usb_intf)
 {
-       struct device *dev = &usbatm_instance->usb_intf->dev;
        const struct firmware *fw, *bp, *cf;
        struct cxacru_data *instance = usbatm_instance->driver_data;
 
        int ret = cxacru_find_firmware(instance, "fw", &fw);
        if (ret) {
-               dev_warn(dev, "firmware (cxacru-fw.bin) unavailable (hotplug misconfiguration?)\n");
+               usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n");
                return ret;
        }
 
        if (instance->modem_type->boot_rom_patch) {
                ret = cxacru_find_firmware(instance, "bp", &bp);
                if (ret) {
-                       dev_warn(dev, "boot ROM patch (cxacru-bp.bin) unavailable (hotplug misconfiguration?)\n");
+                       usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n");
                        release_firmware(fw);
                        return ret;
                }
@@ -667,22 +667,19 @@ static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
 }
 
 static int cxacru_bind(struct usbatm_data *usbatm_instance,
-                      struct usb_interface *intf, const struct usb_device_id *id,
-                      int *need_heavy_init)
+                      struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct cxacru_data *instance;
        struct usb_device *usb_dev = interface_to_usbdev(intf);
        int ret;
 
        /* instance init */
-       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+       instance = kzalloc(sizeof(*instance), GFP_KERNEL);
        if (!instance) {
                dbg("cxacru_bind: no memory for instance data");
                return -ENOMEM;
        }
 
-       memset(instance, 0, sizeof(*instance));
-
        instance->usbatm = usbatm_instance;
        instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
 
@@ -721,13 +718,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
                        instance->snd_buf, PAGE_SIZE,
                        cxacru_blocking_completion, &instance->snd_done, 4);
 
-       init_MUTEX(&instance->cm_serialize);
+       mutex_init(&instance->cm_serialize);
 
        INIT_WORK(&instance->poll_work, (void *)cxacru_poll_status, instance);
 
        usbatm_instance->driver_data = instance;
 
-       *need_heavy_init = cxacru_card_status(instance);
+       usbatm_instance->flags = (cxacru_card_status(instance) ? 0 : UDSL_SKIP_HEAVY_INIT);
 
        return 0;
 
@@ -787,12 +784,12 @@ static const struct usb_device_id cxacru_usb_ids[] = {
        { /* V = Conexant                       P = ADSL modem (Hasbani project)        */
                USB_DEVICE(0x0572, 0xcb00),     .driver_info = (unsigned long) &cxacru_cb00
        },
-       { /* V = Conexant             P = ADSL modem (Well PTI-800 */
-               USB_DEVICE(0x0572, 0xcb02),     .driver_info = (unsigned long) &cxacru_cb00
-       },
        { /* V = Conexant                       P = ADSL modem                          */
                USB_DEVICE(0x0572, 0xcb01),     .driver_info = (unsigned long) &cxacru_cb00
        },
+       { /* V = Conexant                       P = ADSL modem (Well PTI-800) */
+               USB_DEVICE(0x0572, 0xcb02),     .driver_info = (unsigned long) &cxacru_cb00
+       },
        { /* V = Conexant                       P = ADSL modem                          */
                USB_DEVICE(0x0572, 0xcb06),     .driver_info = (unsigned long) &cxacru_cb00
        },
@@ -835,14 +832,13 @@ static const struct usb_device_id cxacru_usb_ids[] = {
 MODULE_DEVICE_TABLE(usb, cxacru_usb_ids);
 
 static struct usbatm_driver cxacru_driver = {
-       .owner          = THIS_MODULE,
        .driver_name    = cxacru_driver_name,
        .bind           = cxacru_bind,
        .heavy_init     = cxacru_heavy_init,
        .unbind         = cxacru_unbind,
        .atm_start      = cxacru_atm_start,
-       .in             = CXACRU_EP_DATA,
-       .out            = CXACRU_EP_DATA,
+       .bulk_in        = CXACRU_EP_DATA,
+       .bulk_out       = CXACRU_EP_DATA,
        .rx_padding     = 3,
        .tx_padding     = 11,
 };
index c1b47d74e206a468fdc9c2bce7792d19740256e1..7860c8a5800d5a184adc8abea047dbef16c37b89 100644 (file)
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/usb_ch9.h>
 #include <linux/workqueue.h>
 
 #include "usbatm.h"
 
 #define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
-#define DRIVER_VERSION "1.9"
+#define DRIVER_VERSION "1.10"
 #define DRIVER_DESC    "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
 
 static const char speedtch_driver_name[] = "speedtch";
@@ -66,31 +68,42 @@ static const char speedtch_driver_name[] = "speedtch";
 
 #define RESUBMIT_DELAY         1000    /* milliseconds */
 
-#define DEFAULT_ALTSETTING     1
+#define DEFAULT_BULK_ALTSETTING        1
+#define DEFAULT_ISOC_ALTSETTING        2
 #define DEFAULT_DL_512_FIRST   0
+#define DEFAULT_ENABLE_ISOC    0
 #define DEFAULT_SW_BUFFERING   0
 
-static int altsetting = DEFAULT_ALTSETTING;
+static unsigned int altsetting = 0; /* zero means: use the default */
 static int dl_512_first = DEFAULT_DL_512_FIRST;
+static int enable_isoc = DEFAULT_ENABLE_ISOC;
 static int sw_buffering = DEFAULT_SW_BUFFERING;
 
-module_param(altsetting, int, S_IRUGO | S_IWUSR);
+module_param(altsetting, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(altsetting,
-                "Alternative setting for data interface (default: "
-                __MODULE_STRING(DEFAULT_ALTSETTING) ")");
+               "Alternative setting for data interface (bulk_default: "
+               __MODULE_STRING(DEFAULT_BULK_ALTSETTING) "; isoc_default: "
+               __MODULE_STRING(DEFAULT_ISOC_ALTSETTING) ")");
 
 module_param(dl_512_first, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(dl_512_first,
                 "Read 512 bytes before sending firmware (default: "
                 __MODULE_STRING(DEFAULT_DL_512_FIRST) ")");
 
+module_param(enable_isoc, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_isoc,
+               "Use isochronous transfers if available (default: "
+               __MODULE_STRING(DEFAULT_ENABLE_ISOC) ")");
+
 module_param(sw_buffering, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(sw_buffering,
                 "Enable software buffering (default: "
                 __MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
 
+#define INTERFACE_DATA         1
 #define ENDPOINT_INT           0x81
-#define ENDPOINT_DATA          0x07
+#define ENDPOINT_BULK_DATA     0x07
+#define ENDPOINT_ISOC_DATA     0x07
 #define ENDPOINT_FIRMWARE      0x05
 
 #define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
@@ -98,6 +111,8 @@ MODULE_PARM_DESC(sw_buffering,
 struct speedtch_instance_data {
        struct usbatm_data *usbatm;
 
+       unsigned int altsetting;
+
        struct work_struct status_checker;
 
        unsigned char last_status;
@@ -205,7 +220,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
                                   buffer, 0x200, &actual_length, 2000);
 
                if (ret < 0 && ret != -ETIMEDOUT)
-                       usb_dbg(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
+                       usb_warn(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
                else
                        usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret);
        }
@@ -219,7 +234,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
                                   buffer, thislen, &actual_length, DATA_TIMEOUT);
 
                if (ret < 0) {
-                       usb_dbg(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
+                       usb_err(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
                        goto out_free;
                }
                usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size);
@@ -232,7 +247,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
                           buffer, 0x200, &actual_length, DATA_TIMEOUT);
 
        if (ret < 0) {
-               usb_dbg(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
+               usb_err(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
                goto out_free;
        }
        usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length);
@@ -246,7 +261,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
                                   buffer, thislen, &actual_length, DATA_TIMEOUT);
 
                if (ret < 0) {
-                       usb_dbg(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
+                       usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
                        goto out_free;
                }
        }
@@ -259,7 +274,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
                           buffer, 0x200, &actual_length, DATA_TIMEOUT);
 
        if (ret < 0) {
-               usb_dbg(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
+               usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
                goto out_free;
        }
 
@@ -270,6 +285,11 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
           because we're in our own kernel thread anyway. */
        msleep_interruptible(1000);
 
+       if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->altsetting)) < 0) {
+               usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->altsetting, ret);
+               goto out_free;
+       }
+
        /* Enable software buffering, if requested */
        if (sw_buffering)
                speedtch_set_swbuff(instance, 1);
@@ -285,8 +305,8 @@ out:
        return ret;
 }
 
-static int speedtch_find_firmware(struct usb_interface *intf, int phase,
-                                 const struct firmware **fw_p)
+static int speedtch_find_firmware(struct usbatm_data *usbatm, struct usb_interface *intf,
+                                 int phase, const struct firmware **fw_p)
 {
        struct device *dev = &intf->dev;
        const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice);
@@ -295,24 +315,24 @@ static int speedtch_find_firmware(struct usb_interface *intf, int phase,
        char buf[24];
 
        sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
-       dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+       usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
 
        if (request_firmware(fw_p, buf, dev)) {
                sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
-               dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+               usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
 
                if (request_firmware(fw_p, buf, dev)) {
                        sprintf(buf, "speedtch-%d.bin", phase);
-                       dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+                       usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
 
                        if (request_firmware(fw_p, buf, dev)) {
-                               dev_warn(dev, "no stage %d firmware found!\n", phase);
+                               usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase);
                                return -ENOENT;
                        }
                }
        }
 
-       dev_info(dev, "found stage %d firmware %s\n", phase, buf);
+       usb_info(usbatm, "found stage %d firmware %s\n", phase, buf);
 
        return 0;
 }
@@ -323,15 +343,16 @@ static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface
        struct speedtch_instance_data *instance = usbatm->driver_data;
        int ret;
 
-       if ((ret = speedtch_find_firmware(intf, 1, &fw1)) < 0)
-                       return ret;
+       if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0)
+               return ret;
 
-       if ((ret = speedtch_find_firmware(intf, 2, &fw2)) < 0) {
+       if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) {
                release_firmware(fw1);
                return ret;
        }
 
-       ret = speedtch_upload_firmware(instance, fw1, fw2);
+       if ((ret = speedtch_upload_firmware(instance, fw1, fw2)) < 0)
+               usb_err(usbatm, "%s: firmware upload failed (%d)!\n", __func__, ret);
 
        release_firmware(fw2);
        release_firmware(fw1);
@@ -428,7 +449,9 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
        int down_speed, up_speed, ret;
        unsigned char status;
 
+#ifdef VERBOSE_DEBUG
        atm_dbg(usbatm, "%s entered\n", __func__);
+#endif
 
        ret = speedtch_read_status(instance);
        if (ret < 0) {
@@ -441,9 +464,9 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
 
        status = buf[OFFSET_7];
 
-       atm_dbg(usbatm, "%s: line state %02x\n", __func__, status);
-
        if ((status != instance->last_status) || !status) {
+               atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status);
+
                switch (status) {
                case 0:
                        atm_dev->signal = ATM_PHY_SIG_LOST;
@@ -484,7 +507,7 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
 
                default:
                        atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-                       atm_info(usbatm, "Unknown line state %02x\n", status);
+                       atm_info(usbatm, "unknown line state %02x\n", status);
                        break;
                }
 
@@ -583,11 +606,6 @@ static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_de
 
        atm_dbg(usbatm, "%s entered\n", __func__);
 
-       if ((ret = usb_set_interface(usb_dev, 1, altsetting)) < 0) {
-               atm_dbg(usbatm, "%s: usb_set_interface returned %d!\n", __func__, ret);
-               return ret;
-       }
-
        /* Set MAC address, it is stored in the serial number */
        memset(atm_dev->esi, 0, sizeof(atm_dev->esi));
        if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
@@ -678,20 +696,27 @@ static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_inte
 
 static int speedtch_bind(struct usbatm_data *usbatm,
                         struct usb_interface *intf,
-                        const struct usb_device_id *id,
-                        int *need_heavy_init)
+                        const struct usb_device_id *id)
 {
        struct usb_device *usb_dev = interface_to_usbdev(intf);
-       struct usb_interface *cur_intf;
+       struct usb_interface *cur_intf, *data_intf;
        struct speedtch_instance_data *instance;
        int ifnum = intf->altsetting->desc.bInterfaceNumber;
        int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces;
        int i, ret;
+       int use_isoc;
 
        usb_dbg(usbatm, "%s entered\n", __func__);
 
+       /* sanity checks */
+
        if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
-               usb_dbg(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
+               usb_err(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
+               return -ENODEV;
+       }
+
+       if (!(data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA))) {
+               usb_err(usbatm, "%s: data interface not found!\n", __func__);
                return -ENODEV;
        }
 
@@ -704,25 +729,71 @@ static int speedtch_bind(struct usbatm_data *usbatm,
                        ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm);
 
                        if (ret < 0) {
-                               usb_dbg(usbatm, "%s: failed to claim interface %d (%d)\n", __func__, i, ret);
+                               usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret);
                                speedtch_release_interfaces(usb_dev, i);
                                return ret;
                        }
                }
        }
 
-       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+       instance = kzalloc(sizeof(*instance), GFP_KERNEL);
 
        if (!instance) {
-               usb_dbg(usbatm, "%s: no memory for instance data!\n", __func__);
+               usb_err(usbatm, "%s: no memory for instance data!\n", __func__);
                ret = -ENOMEM;
                goto fail_release;
        }
 
-       memset(instance, 0, sizeof(struct speedtch_instance_data));
-
        instance->usbatm = usbatm;
 
+       /* altsetting and enable_isoc may change at any moment, so take a snapshot */
+       instance->altsetting = altsetting;
+       use_isoc = enable_isoc;
+
+       if (instance->altsetting)
+               if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->altsetting)) < 0) {
+                       usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->altsetting, ret);
+                       instance->altsetting = 0; /* fall back to default */
+               }
+
+       if (!instance->altsetting && use_isoc)
+               if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_ISOC_ALTSETTING)) < 0) {
+                       usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret);
+                       use_isoc = 0; /* fall back to bulk */
+               }
+
+       if (use_isoc) {
+               const struct usb_host_interface *desc = data_intf->cur_altsetting;
+               const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in;
+               int i;
+
+               use_isoc = 0; /* fall back to bulk if endpoint not found */
+
+               for (i=0; i<desc->desc.bNumEndpoints; i++) {
+                       const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc;
+
+                       if ((endpoint_desc->bEndpointAddress == target_address)) {
+                               use_isoc = (endpoint_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                                       USB_ENDPOINT_XFER_ISOC;
+                               break;
+                       }
+               }
+
+               if (!use_isoc)
+                       usb_info(usbatm, "isochronous transfer not supported - using bulk\n");
+       }
+
+       if (!use_isoc && !instance->altsetting)
+               if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_BULK_ALTSETTING)) < 0) {
+                       usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret);
+                       goto fail_free;
+               }
+
+       if (!instance->altsetting)
+               instance->altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING;
+
+       usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0);
+
        INIT_WORK(&instance->status_checker, (void *)speedtch_check_status, instance);
 
        instance->status_checker.timer.function = speedtch_status_poll;
@@ -749,13 +820,15 @@ static int speedtch_bind(struct usbatm_data *usbatm,
                              0x12, 0xc0, 0x07, 0x00,
                              instance->scratch_buffer + OFFSET_7, SIZE_7, 500);
 
-       *need_heavy_init = (ret != SIZE_7);
+       usbatm->flags |= (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0);
 
-       usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, need_heavy_init ? "not" : "already");
+       usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not");
 
-       if (*need_heavy_init)
-               if ((ret = usb_reset_device(usb_dev)) < 0)
+       if (!(usbatm->flags & UDSL_SKIP_HEAVY_INIT))
+               if ((ret = usb_reset_device(usb_dev)) < 0) {
+                       usb_err(usbatm, "%s: device reset failed (%d)!\n", __func__, ret);
                        goto fail_free;
+               }
 
         usbatm->driver_data = instance;
 
@@ -787,15 +860,15 @@ static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *in
 ***********/
 
 static struct usbatm_driver speedtch_usbatm_driver = {
-       .owner          = THIS_MODULE,
        .driver_name    = speedtch_driver_name,
        .bind           = speedtch_bind,
        .heavy_init     = speedtch_heavy_init,
        .unbind         = speedtch_unbind,
        .atm_start      = speedtch_atm_start,
        .atm_stop       = speedtch_atm_stop,
-       .in             = ENDPOINT_DATA,
-       .out            = ENDPOINT_DATA
+       .bulk_in        = ENDPOINT_BULK_DATA,
+       .bulk_out       = ENDPOINT_BULK_DATA,
+       .isoc_in        = ENDPOINT_ISOC_DATA
 };
 
 static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
index 7d2a679989edf5d8b11a803d15d13dd79b5eb8e7..830d2c982670db964f0263b1958617342c3f7f85 100644 (file)
 #include <linux/ctype.h>
 #include <linux/kthread.h>
 #include <linux/version.h>
+#include <linux/mutex.h>
 #include <asm/unaligned.h>
 
 #include "usbatm.h"
 
-#define EAGLEUSBVERSION "ueagle 1.1"
+#define EAGLEUSBVERSION "ueagle 1.2"
 
 
 /*
@@ -358,16 +359,19 @@ struct intr_pkt {
 #define INTR_PKT_SIZE 28
 
 static struct usb_driver uea_driver;
-static DECLARE_MUTEX(uea_semaphore);
+static DEFINE_MUTEX(uea_mutex);
 static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
 
 static int modem_index;
 static unsigned int debug;
+static int use_iso[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = 1};
 static int sync_wait[NB_MODEM];
 static char *cmv_file[NB_MODEM];
 
 module_param(debug, uint, 0644);
 MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
+module_param_array(use_iso, bool, NULL, 0644);
+MODULE_PARM_DESC(use_iso, "use isochronous usb pipe for incoming traffic");
 module_param_array(sync_wait, bool, NULL, 0644);
 MODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM");
 module_param_array(cmv_file, charp, NULL, 0644);
@@ -628,8 +632,7 @@ static int request_dsp(struct uea_softc *sc)
                        dsp_name = FW_DIR "DSPep.bin";
        }
 
-       ret = request_firmware(&sc->dsp_firm,
-                               dsp_name, &sc->usb_dev->dev);
+       ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev);
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
                       "requesting firmware %s failed with error %d\n",
@@ -744,7 +747,6 @@ static inline int wait_cmv_ack(struct uea_softc *sc)
                return ret;
 
        return (ret == 0) ? -ETIMEDOUT : 0;
-
 }
 
 #define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
@@ -935,6 +937,7 @@ static int uea_stat(struct uea_softc *sc)
         * ADI930 don't support it (-EPIPE error).
         */
        if (UEA_CHIP_VERSION(sc) != ADI930
+                   && !use_iso[sc->modem_index]
                    && sc->stats.phy.dsrate != (data >> 16) * 32) {
                /* Original timming from ADI(used in windows driver)
                 * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits
@@ -1010,7 +1013,7 @@ static int request_cmvs(struct uea_softc *sc,
        int ret, size;
        u8 *data;
        char *file;
-       static char cmv_name[256] = FW_DIR;
+       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
 
        if (cmv_file[sc->modem_index] == NULL) {
                if (UEA_CHIP_VERSION(sc) == ADI930)
@@ -1184,8 +1187,7 @@ static int load_XILINX_firmware(struct uea_softc *sc)
                }
        }
 
-       /* finish to send the fpga
-        */
+       /* finish to send the fpga */
        ret = uea_request(sc, 0xe, 1, 0, NULL);
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
@@ -1193,9 +1195,7 @@ static int load_XILINX_firmware(struct uea_softc *sc)
                goto err1;
        }
 
-       /*
-        * Tell the modem we finish : de-assert reset
-        */
+       /* Tell the modem we finish : de-assert reset */
        value = 0;
        ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value);
        if (ret < 0)
@@ -1209,6 +1209,7 @@ err0:
        return ret;
 }
 
+/* The modem send us an ack. First with check if it right */
 static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
 {
        uea_enters(INS_TO_USBDEV(sc));
@@ -1268,23 +1269,19 @@ bad1:
  */
 static void uea_intr(struct urb *urb, struct pt_regs *regs)
 {
-       struct uea_softc *sc = (struct uea_softc *)urb->context;
-       struct intr_pkt *intr;
+       struct uea_softc *sc = urb->context;
+       struct intr_pkt *intr = urb->transfer_buffer;
        uea_enters(INS_TO_USBDEV(sc));
 
-       if (urb->status < 0) {
+       if (unlikely(urb->status < 0)) {
                uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
                       urb->status);
                return;
        }
 
-       intr = (struct intr_pkt *) urb->transfer_buffer;
-
        /* device-to-host interrupt */
        if (intr->bType != 0x08 || sc->booting) {
-               uea_err(INS_TO_USBDEV(sc), "wrong intr\n");
-               // rebooting ?
-               // sc->reset = 1;
+               uea_err(INS_TO_USBDEV(sc), "wrong interrupt\n");
                goto resubmit;
        }
 
@@ -1300,7 +1297,7 @@ static void uea_intr(struct urb *urb, struct pt_regs *regs)
                break;
 
        default:
-               uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n",
+               uea_err(INS_TO_USBDEV(sc), "unknown interrupt %u\n",
                       le16_to_cpu(intr->wInterrupt));
        }
 
@@ -1379,7 +1376,7 @@ static void uea_stop(struct uea_softc *sc)
        int ret;
        uea_enters(INS_TO_USBDEV(sc));
        ret = kthread_stop(sc->kthread);
-       uea_info(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
+       uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
 
        /* stop any pending boot process */
        flush_scheduled_work();
@@ -1418,13 +1415,13 @@ static ssize_t read_status(struct device *dev, struct device_attribute *attr,
        int ret = -ENODEV;
        struct uea_softc *sc;
 
-       down(&uea_semaphore);
+       mutex_lock(&uea_mutex);
        sc = dev_to_uea(dev);
        if (!sc)
                goto out;
        ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
 out:
-       up(&uea_semaphore);
+       mutex_unlock(&uea_mutex);
        return ret;
 }
 
@@ -1434,14 +1431,14 @@ static ssize_t reboot(struct device *dev, struct device_attribute *attr,
        int ret = -ENODEV;
        struct uea_softc *sc;
 
-       down(&uea_semaphore);
+       mutex_lock(&uea_mutex);
        sc = dev_to_uea(dev);
        if (!sc)
                goto out;
        sc->reset = 1;
        ret = count;
 out:
-       up(&uea_semaphore);
+       mutex_unlock(&uea_mutex);
        return ret;
 }
 
@@ -1453,7 +1450,7 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
        int ret = -ENODEV;
        struct uea_softc *sc;
 
-       down(&uea_semaphore);
+       mutex_lock(&uea_mutex);
        sc = dev_to_uea(dev);
        if (!sc)
                goto out;
@@ -1473,7 +1470,7 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
                break;
        }
 out:
-       up(&uea_semaphore);
+       mutex_unlock(&uea_mutex);
        return ret;
 }
 
@@ -1485,7 +1482,7 @@ static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
        int ret = -ENODEV;
        struct uea_softc *sc;
 
-       down(&uea_semaphore);
+       mutex_lock(&uea_mutex);
        sc = dev_to_uea(dev);
        if (!sc)
                goto out;
@@ -1497,7 +1494,7 @@ static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
        else
                ret = sprintf(buf, "GOOD\n");
 out:
-       up(&uea_semaphore);
+       mutex_unlock(&uea_mutex);
        return ret;
 }
 
@@ -1511,7 +1508,7 @@ static ssize_t read_##name(struct device *dev,                    \
        int ret = -ENODEV;                                      \
        struct uea_softc *sc;                                   \
                                                                \
-       down(&uea_semaphore);                                   \
+       mutex_lock(&uea_mutex);                                         \
        sc = dev_to_uea(dev);                                   \
        if (!sc)                                                \
                goto out;                                       \
@@ -1519,7 +1516,7 @@ static ssize_t read_##name(struct device *dev,                    \
        if (reset)                                              \
                sc->stats.phy.name = 0;                         \
 out:                                                           \
-       up(&uea_semaphore);                                     \
+       mutex_unlock(&uea_mutex);                                       \
        return ret;                                             \
 }                                                              \
                                                                \
@@ -1617,7 +1614,7 @@ static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
 }
 
 static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
-                  const struct usb_device_id *id, int *heavy)
+                  const struct usb_device_id *id)
 {
        struct usb_device *usb = interface_to_usbdev(intf);
        struct uea_softc *sc;
@@ -1629,16 +1626,14 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
        if (ifnum != UEA_INTR_IFACE_NO)
                return -ENODEV;
 
-       *heavy = sync_wait[modem_index];
+       usbatm->flags = (sync_wait[modem_index] ? 0 : UDSL_SKIP_HEAVY_INIT);
 
        /* interface 1 is for outbound traffic */
        ret = claim_interface(usb, usbatm, UEA_US_IFACE_NO);
        if (ret < 0)
                return ret;
 
-       /* ADI930 has only 2 interfaces and inbound traffic
-        * is on interface 1
-        */
+       /* ADI930 has only 2 interfaces and inbound traffic is on interface 1 */
        if (UEA_CHIP_VERSION(id) != ADI930) {
                /* interface 2 is for inbound traffic */
                ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO);
@@ -1658,6 +1653,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
        sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0;
        sc->driver_info = id->driver_info;
 
+       /* ADI930 don't support iso */
+       if (UEA_CHIP_VERSION(id) != ADI930 && use_iso[sc->modem_index]) {
+               int i;
+
+               /* try set fastest alternate for inbound traffic interface */
+               for (i = FASTEST_ISO_INTF; i > 0; i--)
+                       if (usb_set_interface(usb, UEA_DS_IFACE_NO, i) == 0)
+                               break;
+
+               if (i > 0) {
+                       uea_dbg(usb, "set alternate %d for 2 interface\n", i);
+                       uea_info(usb, "using iso mode\n");
+                       usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ;
+               } else {
+                       uea_err(usb, "setting any alternate failed for "
+                                       "2 interface, using bulk mode\n");
+               }
+       }
+
        ret = uea_boot(sc);
        if (ret < 0) {
                kfree(sc);
@@ -1701,13 +1715,13 @@ static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 
 static struct usbatm_driver uea_usbatm_driver = {
        .driver_name = "ueagle-atm",
-       .owner = THIS_MODULE,
        .bind = uea_bind,
        .atm_start = uea_atm_open,
        .unbind = uea_unbind,
        .heavy_init = uea_heavy,
-       .in = UEA_BULK_DATA_PIPE,
-       .out = UEA_BULK_DATA_PIPE,
+       .bulk_in = UEA_BULK_DATA_PIPE,
+       .bulk_out = UEA_BULK_DATA_PIPE,
+       .isoc_in = UEA_ISO_DATA_PIPE,
 };
 
 static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -1738,9 +1752,9 @@ static void uea_disconnect(struct usb_interface *intf)
         * Pre-firmware device has one interface
         */
        if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) {
-               down(&uea_semaphore);
+               mutex_lock(&uea_mutex);
                usbatm_usb_disconnect(intf);
-               up(&uea_semaphore);
+               mutex_unlock(&uea_mutex);
                uea_info(usb, "ADSL device removed\n");
        }
 
index 7af1883d4bf9cf127c183089ef52c8fbd5f2703d..c1211fc037d997b8a30cee323ded090d72d9da63 100644 (file)
@@ -72,6 +72,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/netdevice.h>
 #include <linux/proc_fs.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
@@ -91,19 +92,18 @@ static int usbatm_print_packet(const unsigned char *data, int len);
 #endif
 
 #define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
-#define DRIVER_VERSION "1.9"
+#define DRIVER_VERSION "1.10"
 #define DRIVER_DESC    "Generic USB ATM/DSL I/O, version " DRIVER_VERSION
 
 static const char usbatm_driver_name[] = "usbatm";
 
 #define UDSL_MAX_RCV_URBS              16
 #define UDSL_MAX_SND_URBS              16
-#define UDSL_MAX_RCV_BUF_SIZE          1024    /* ATM cells */
-#define UDSL_MAX_SND_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_MAX_BUF_SIZE              64 * 1024       /* bytes */
 #define UDSL_DEFAULT_RCV_URBS          4
 #define UDSL_DEFAULT_SND_URBS          4
-#define UDSL_DEFAULT_RCV_BUF_SIZE      64      /* ATM cells */
-#define UDSL_DEFAULT_SND_BUF_SIZE      64      /* ATM cells */
+#define UDSL_DEFAULT_RCV_BUF_SIZE      64 * ATM_CELL_SIZE      /* bytes */
+#define UDSL_DEFAULT_SND_BUF_SIZE      64 * ATM_CELL_SIZE      /* bytes */
 
 #define ATM_CELL_HEADER                        (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
 
@@ -111,8 +111,8 @@ static const char usbatm_driver_name[] = "usbatm";
 
 static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
 static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
-static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
-static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+static unsigned int rcv_buf_bytes = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_bytes = UDSL_DEFAULT_SND_BUF_SIZE;
 
 module_param(num_rcv_urbs, uint, S_IRUGO);
 MODULE_PARM_DESC(num_rcv_urbs,
@@ -126,15 +126,15 @@ MODULE_PARM_DESC(num_snd_urbs,
                 __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
                 __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
 
-module_param(rcv_buf_size, uint, S_IRUGO);
-MODULE_PARM_DESC(rcv_buf_size,
-                "Size of the buffers used for reception in ATM cells (range: 1-"
-                __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+module_param(rcv_buf_bytes, uint, S_IRUGO);
+MODULE_PARM_DESC(rcv_buf_bytes,
+                "Size of the buffers used for reception, in bytes (range: 1-"
+                __MODULE_STRING(UDSL_MAX_BUF_SIZE) ", default: "
                 __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
 
-module_param(snd_buf_size, uint, S_IRUGO);
-MODULE_PARM_DESC(snd_buf_size,
-                "Size of the buffers used for transmission in ATM cells (range: 1-"
+module_param(snd_buf_bytes, uint, S_IRUGO);
+MODULE_PARM_DESC(snd_buf_bytes,
+                "Size of the buffers used for transmission, in bytes (range: 1-"
                 __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
                 __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
 
@@ -166,10 +166,10 @@ struct usbatm_control {
 
 /* ATM */
 
-static void usbatm_atm_dev_close(struct atm_dev *dev);
+static void usbatm_atm_dev_close(struct atm_dev *atm_dev);
 static int usbatm_atm_open(struct atm_vcc *vcc);
 static void usbatm_atm_close(struct atm_vcc *vcc);
-static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user * arg);
 static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
 static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
 
@@ -199,7 +199,7 @@ static inline void usbatm_pop(struct atm_vcc *vcc, struct sk_buff *skb)
        if (vcc->pop)
                vcc->pop(vcc, skb);
        else
-               dev_kfree_skb(skb);
+               dev_kfree_skb_any(skb);
 }
 
 
@@ -234,8 +234,9 @@ static int usbatm_submit_urb(struct urb *urb)
 
        ret = usb_submit_urb(urb, GFP_ATOMIC);
        if (ret) {
-               atm_dbg(channel->usbatm, "%s: urb 0x%p submission failed (%d)!\n",
-                       __func__, urb, ret);
+               if (printk_ratelimit())
+                       atm_warn(channel->usbatm, "%s: urb 0x%p submission failed (%d)!\n",
+                               __func__, urb, ret);
 
                /* consider all errors transient and return the buffer back to the queue */
                urb->status = -EAGAIN;
@@ -269,10 +270,16 @@ static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
 
        spin_unlock_irqrestore(&channel->lock, flags);
 
-       if (unlikely(urb->status))
+       if (unlikely(urb->status) &&
+                       (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
+                        urb->status != -EILSEQ ))
+       {
+               if (printk_ratelimit())
+                       atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
+                               __func__, urb, urb->status);
                /* throttle processing in case of an error */
                mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS));
-       else
+       else
                tasklet_schedule(&channel->tasklet);
 }
 
@@ -284,129 +291,167 @@ static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
 static inline struct usbatm_vcc_data *usbatm_find_vcc(struct usbatm_data *instance,
                                                  short vpi, int vci)
 {
-       struct usbatm_vcc_data *vcc;
+       struct usbatm_vcc_data *vcc_data;
 
-       list_for_each_entry(vcc, &instance->vcc_list, list)
-               if ((vcc->vci == vci) && (vcc->vpi == vpi))
-                       return vcc;
+       list_for_each_entry(vcc_data, &instance->vcc_list, list)
+               if ((vcc_data->vci == vci) && (vcc_data->vpi == vpi))
+                       return vcc_data;
        return NULL;
 }
 
-static void usbatm_extract_cells(struct usbatm_data *instance,
-                              unsigned char *source, unsigned int avail_data)
+static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char *source)
 {
-       struct usbatm_vcc_data *cached_vcc = NULL;
        struct atm_vcc *vcc;
        struct sk_buff *sarb;
-       unsigned int stride = instance->rx_channel.stride;
-       int vci, cached_vci = 0;
-       short vpi, cached_vpi = 0;
-       u8 pti;
+       short vpi = ((source[0] & 0x0f) << 4)  | (source[1] >> 4);
+       int vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+       u8 pti = ((source[3] & 0xe) >> 1);
 
-       for (; avail_data >= stride; avail_data -= stride, source += stride) {
-               vpi = ((source[0] & 0x0f) << 4)  | (source[1] >> 4);
-               vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
-               pti = ((source[3] & 0xe) >> 1);
+       vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
 
-               vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
+       if ((vci != instance->cached_vci) || (vpi != instance->cached_vpi)) {
+               instance->cached_vpi = vpi;
+               instance->cached_vci = vci;
 
-               if ((vci != cached_vci) || (vpi != cached_vpi)) {
-                       cached_vpi = vpi;
-                       cached_vci = vci;
+               instance->cached_vcc = usbatm_find_vcc(instance, vpi, vci);
 
-                       cached_vcc = usbatm_find_vcc(instance, vpi, vci);
+               if (!instance->cached_vcc)
+                       atm_rldbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci);
+       }
 
-                       if (!cached_vcc)
-                               atm_dbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci);
-               }
+       if (!instance->cached_vcc)
+               return;
 
-               if (!cached_vcc)
-                       continue;
+       vcc = instance->cached_vcc->vcc;
 
-               vcc = cached_vcc->vcc;
+       /* OAM F5 end-to-end */
+       if (pti == ATM_PTI_E2EF5) {
+               if (printk_ratelimit())
+                       atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n",
+                               __func__, vpi, vci);
+               atomic_inc(&vcc->stats->rx_err);
+               return;
+       }
 
-               /* OAM F5 end-to-end */
-               if (pti == ATM_PTI_E2EF5) {
-                       atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n", __func__, vpi, vci);
-                       atomic_inc(&vcc->stats->rx_err);
-                       continue;
-               }
+       sarb = instance->cached_vcc->sarb;
 
-               sarb = cached_vcc->sarb;
+       if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+               atm_rldbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n",
+                               __func__, sarb->len, vcc);
+               /* discard cells already received */
+               skb_trim(sarb, 0);
+               UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
+       }
 
-               if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
-                       atm_dbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n",
-                                       __func__, sarb->len, vcc);
-                       /* discard cells already received */
-                       skb_trim(sarb, 0);
-                       UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
-               }
+       memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+       __skb_put(sarb, ATM_CELL_PAYLOAD);
 
-               memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
-               __skb_put(sarb, ATM_CELL_PAYLOAD);
+       if (pti & 1) {
+               struct sk_buff *skb;
+               unsigned int length;
+               unsigned int pdu_length;
 
-               if (pti & 1) {
-                       struct sk_buff *skb;
-                       unsigned int length;
-                       unsigned int pdu_length;
+               length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
 
-                       length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+               /* guard against overflow */
+               if (length > ATM_MAX_AAL5_PDU) {
+                       atm_rldbg(instance, "%s: bogus length %u (vcc: 0x%p)!\n",
+                                 __func__, length, vcc);
+                       atomic_inc(&vcc->stats->rx_err);
+                       goto out;
+               }
 
-                       /* guard against overflow */
-                       if (length > ATM_MAX_AAL5_PDU) {
-                               atm_dbg(instance, "%s: bogus length %u (vcc: 0x%p)!\n",
-                                               __func__, length, vcc);
-                               atomic_inc(&vcc->stats->rx_err);
-                               goto out;
-                       }
+               pdu_length = usbatm_pdu_length(length);
 
-                       pdu_length = usbatm_pdu_length(length);
+               if (sarb->len < pdu_length) {
+                       atm_rldbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n",
+                                 __func__, pdu_length, sarb->len, vcc);
+                       atomic_inc(&vcc->stats->rx_err);
+                       goto out;
+               }
 
-                       if (sarb->len < pdu_length) {
-                               atm_dbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n",
-                                               __func__, pdu_length, sarb->len, vcc);
-                               atomic_inc(&vcc->stats->rx_err);
-                               goto out;
-                       }
+               if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+                       atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
+                                 __func__, vcc);
+                       atomic_inc(&vcc->stats->rx_err);
+                       goto out;
+               }
 
-                       if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
-                               atm_dbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
-                                               __func__, vcc);
-                               atomic_inc(&vcc->stats->rx_err);
-                               goto out;
-                       }
+               vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc);
 
-                       vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc);
+               if (!(skb = dev_alloc_skb(length))) {
+                       if (printk_ratelimit())
+                               atm_err(instance, "%s: no memory for skb (length: %u)!\n",
+                                       __func__, length);
+                       atomic_inc(&vcc->stats->rx_drop);
+                       goto out;
+               }
 
-                       if (!(skb = dev_alloc_skb(length))) {
-                               atm_dbg(instance, "%s: no memory for skb (length: %u)!\n", __func__, length);
-                               atomic_inc(&vcc->stats->rx_drop);
-                               goto out;
-                       }
+               vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize);
 
-                       vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize);
+               if (!atm_charge(vcc, skb->truesize)) {
+                       atm_rldbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n",
+                                 __func__, skb->truesize);
+                       dev_kfree_skb_any(skb);
+                       goto out;       /* atm_charge increments rx_drop */
+               }
 
-                       if (!atm_charge(vcc, skb->truesize)) {
-                               atm_dbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n", __func__, skb->truesize);
-                               dev_kfree_skb(skb);
-                               goto out;       /* atm_charge increments rx_drop */
-                       }
+               memcpy(skb->data, sarb->tail - pdu_length, length);
+               __skb_put(skb, length);
 
-                       memcpy(skb->data, sarb->tail - pdu_length, length);
-                       __skb_put(skb, length);
+               vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
+                    __func__, skb, skb->len, skb->truesize);
 
-                       vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
-                            __func__, skb, skb->len, skb->truesize);
+               PACKETDEBUG(skb->data, skb->len);
 
-                       PACKETDEBUG(skb->data, skb->len);
+               vcc->push(vcc, skb);
 
-                       vcc->push(vcc, skb);
+               atomic_inc(&vcc->stats->rx);
+       out:
+               skb_trim(sarb, 0);
+       }
+}
 
-                       atomic_inc(&vcc->stats->rx);
-               out:
-                       skb_trim(sarb, 0);
+static void usbatm_extract_cells(struct usbatm_data *instance,
+               unsigned char *source, unsigned int avail_data)
+{
+       unsigned int stride = instance->rx_channel.stride;
+       unsigned int buf_usage = instance->buf_usage;
+
+       /* extract cells from incoming data, taking into account that
+        * the length of avail data may not be a multiple of stride */
+
+       if (buf_usage > 0) {
+               /* we have a partially received atm cell */
+               unsigned char *cell_buf = instance->cell_buf;
+               unsigned int space_left = stride - buf_usage;
+
+               UDSL_ASSERT(buf_usage <= stride);
+
+               if (avail_data >= space_left) {
+                       /* add new data and process cell */
+                       memcpy(cell_buf + buf_usage, source, space_left);
+                       source += space_left;
+                       avail_data -= space_left;
+                       usbatm_extract_one_cell(instance, cell_buf);
+                       instance->buf_usage = 0;
+               } else {
+                       /* not enough data to fill the cell */
+                       memcpy(cell_buf + buf_usage, source, avail_data);
+                       instance->buf_usage = buf_usage + avail_data;
+                       return;
                }
        }
+
+       for (; avail_data >= stride; avail_data -= stride, source += stride)
+               usbatm_extract_one_cell(instance, source);
+
+       if (avail_data > 0) {
+               /* length was not a multiple of stride -
+                * save remaining data for next call */
+               memcpy(instance->cell_buf, source, avail_data);
+               instance->buf_usage = avail_data;
+       }
 }
 
 
@@ -420,14 +465,14 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
 {
        struct usbatm_control *ctrl = UDSL_SKB(skb);
        struct atm_vcc *vcc = ctrl->atm.vcc;
-       unsigned int num_written;
+       unsigned int bytes_written;
        unsigned int stride = instance->tx_channel.stride;
 
        vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space);
        UDSL_ASSERT(!(avail_space % stride));
 
-       for (num_written = 0; num_written < avail_space && ctrl->len;
-            num_written += stride, target += stride) {
+       for (bytes_written = 0; bytes_written < avail_space && ctrl->len;
+            bytes_written += stride, target += stride) {
                unsigned int data_len = min_t(unsigned int, skb->len, ATM_CELL_PAYLOAD);
                unsigned int left = ATM_CELL_PAYLOAD - data_len;
                u8 *ptr = target;
@@ -470,7 +515,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
                        ctrl->crc = crc32_be(ctrl->crc, ptr, left);
        }
 
-       return num_written;
+       return bytes_written;
 }
 
 
@@ -487,16 +532,40 @@ static void usbatm_rx_process(unsigned long data)
                vdbg("%s: processing urb 0x%p", __func__, urb);
 
                if (usb_pipeisoc(urb->pipe)) {
+                       unsigned char *merge_start = NULL;
+                       unsigned int merge_length = 0;
+                       const unsigned int packet_size = instance->rx_channel.packet_size;
                        int i;
-                       for (i = 0; i < urb->number_of_packets; i++)
-                               if (!urb->iso_frame_desc[i].status)
-                                       usbatm_extract_cells(instance,
-                                                            (u8 *)urb->transfer_buffer + urb->iso_frame_desc[i].offset,
-                                                            urb->iso_frame_desc[i].actual_length);
-               }
-               else
+
+                       for (i = 0; i < urb->number_of_packets; i++) {
+                               if (!urb->iso_frame_desc[i].status) {
+                                       unsigned int actual_length = urb->iso_frame_desc[i].actual_length;
+
+                                       UDSL_ASSERT(actual_length <= packet_size);
+
+                                       if (!merge_length)
+                                               merge_start = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+                                       merge_length += actual_length;
+                                       if (merge_length && (actual_length < packet_size)) {
+                                               usbatm_extract_cells(instance, merge_start, merge_length);
+                                               merge_length = 0;
+                                       }
+                               } else {
+                                       atm_rldbg(instance, "%s: status %d in frame %d!\n", __func__, urb->status, i);
+                                       if (merge_length)
+                                               usbatm_extract_cells(instance, merge_start, merge_length);
+                                       merge_length = 0;
+                                       instance->buf_usage = 0;
+                               }
+                       }
+
+                       if (merge_length)
+                               usbatm_extract_cells(instance, merge_start, merge_length);
+               } else
                        if (!urb->status)
                                usbatm_extract_cells(instance, urb->transfer_buffer, urb->actual_length);
+                       else
+                               instance->buf_usage = 0;
 
                if (usbatm_submit_urb(urb))
                        return;
@@ -514,7 +583,7 @@ static void usbatm_tx_process(unsigned long data)
        struct sk_buff *skb = instance->current_skb;
        struct urb *urb = NULL;
        const unsigned int buf_size = instance->tx_channel.buf_size;
-       unsigned int num_written = 0;
+       unsigned int bytes_written = 0;
        u8 *buffer = NULL;
 
        if (!skb)
@@ -526,16 +595,16 @@ static void usbatm_tx_process(unsigned long data)
                        if (!urb)
                                break;          /* no more senders */
                        buffer = urb->transfer_buffer;
-                       num_written = (urb->status == -EAGAIN) ?
+                       bytes_written = (urb->status == -EAGAIN) ?
                                urb->transfer_buffer_length : 0;
                }
 
-               num_written += usbatm_write_cells(instance, skb,
-                                                 buffer + num_written,
-                                                 buf_size - num_written);
+               bytes_written += usbatm_write_cells(instance, skb,
+                                                 buffer + bytes_written,
+                                                 buf_size - bytes_written);
 
                vdbg("%s: wrote %u bytes from skb 0x%p to urb 0x%p",
-                    __func__, num_written, skb, urb);
+                    __func__, bytes_written, skb, urb);
 
                if (!UDSL_SKB(skb)->len) {
                        struct atm_vcc *vcc = UDSL_SKB(skb)->atm.vcc;
@@ -546,8 +615,8 @@ static void usbatm_tx_process(unsigned long data)
                        skb = skb_dequeue(&instance->sndqueue);
                }
 
-               if (num_written == buf_size || (!skb && num_written)) {
-                       urb->transfer_buffer_length = num_written;
+               if (bytes_written == buf_size || (!skb && bytes_written)) {
+                       urb->transfer_buffer_length = bytes_written;
 
                        if (usbatm_submit_urb(urb))
                                break;
@@ -593,20 +662,24 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
 
        vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len);
 
-       if (!instance) {
-               dbg("%s: NULL data!", __func__);
+       /* racy disconnection check - fine */
+       if (!instance || instance->disconnected) {
+#ifdef DEBUG
+               if (printk_ratelimit())
+                       printk(KERN_DEBUG "%s: %s!\n", __func__, instance ? "disconnected" : "NULL instance");
+#endif
                err = -ENODEV;
                goto fail;
        }
 
        if (vcc->qos.aal != ATM_AAL5) {
-               atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+               atm_rldbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
                err = -EINVAL;
                goto fail;
        }
 
        if (skb->len > ATM_MAX_AAL5_PDU) {
-               atm_dbg(instance, "%s: packet too long (%d vs %d)!\n",
+               atm_rldbg(instance, "%s: packet too long (%d vs %d)!\n",
                                __func__, skb->len, ATM_MAX_AAL5_PDU);
                err = -EINVAL;
                goto fail;
@@ -665,16 +738,16 @@ static void usbatm_put_instance(struct usbatm_data *instance)
 **  ATM  **
 **********/
 
-static void usbatm_atm_dev_close(struct atm_dev *dev)
+static void usbatm_atm_dev_close(struct atm_dev *atm_dev)
 {
-       struct usbatm_data *instance = dev->dev_data;
+       struct usbatm_data *instance = atm_dev->dev_data;
 
        dbg("%s", __func__);
 
        if (!instance)
                return;
 
-       dev->dev_data = NULL;
+       atm_dev->dev_data = NULL; /* catch bugs */
        usbatm_put_instance(instance);  /* taken in usbatm_atm_init */
 }
 
@@ -706,15 +779,19 @@ static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *pag
                               atomic_read(&atm_dev->stats.aal5.rx_err),
                               atomic_read(&atm_dev->stats.aal5.rx_drop));
 
-       if (!left--)
-               switch (atm_dev->signal) {
-               case ATM_PHY_SIG_FOUND:
-                       return sprintf(page, "Line up\n");
-               case ATM_PHY_SIG_LOST:
-                       return sprintf(page, "Line down\n");
-               default:
-                       return sprintf(page, "Line state unknown\n");
-               }
+       if (!left--) {
+               if (instance->disconnected)
+                       return sprintf(page, "Disconnected\n");
+               else
+                       switch (atm_dev->signal) {
+                       case ATM_PHY_SIG_FOUND:
+                               return sprintf(page, "Line up\n");
+                       case ATM_PHY_SIG_LOST:
+                               return sprintf(page, "Line down\n");
+                       default:
+                               return sprintf(page, "Line state unknown\n");
+                       }
+       }
 
        return 0;
 }
@@ -735,13 +812,24 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
        atm_dbg(instance, "%s: vpi %hd, vci %d\n", __func__, vpi, vci);
 
        /* only support AAL5 */
-       if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
-           || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
-               atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+       if ((vcc->qos.aal != ATM_AAL5)) {
+               atm_warn(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+               return -EINVAL;
+       }
+
+       /* sanity checks */
+       if ((vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+               atm_dbg(instance, "%s: max_sdu %d out of range!\n", __func__, vcc->qos.rxtp.max_sdu);
                return -EINVAL;
        }
 
-       down(&instance->serialize);     /* vs self, usbatm_atm_close */
+       mutex_lock(&instance->serialize);       /* vs self, usbatm_atm_close, usbatm_usb_disconnect */
+
+       if (instance->disconnected) {
+               atm_dbg(instance, "%s: disconnected!\n", __func__);
+               ret = -ENODEV;
+               goto fail;
+       }
 
        if (usbatm_find_vcc(instance, vpi, vci)) {
                atm_dbg(instance, "%s: %hd/%d already in use!\n", __func__, vpi, vci);
@@ -749,20 +837,19 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
                goto fail;
        }
 
-       if (!(new = kmalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) {
-               atm_dbg(instance, "%s: no memory for vcc_data!\n", __func__);
+       if (!(new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) {
+               atm_err(instance, "%s: no memory for vcc_data!\n", __func__);
                ret = -ENOMEM;
                goto fail;
        }
 
-       memset(new, 0, sizeof(struct usbatm_vcc_data));
        new->vcc = vcc;
        new->vpi = vpi;
        new->vci = vci;
 
        new->sarb = alloc_skb(usbatm_pdu_length(vcc->qos.rxtp.max_sdu), GFP_KERNEL);
        if (!new->sarb) {
-               atm_dbg(instance, "%s: no memory for SAR buffer!\n", __func__);
+               atm_err(instance, "%s: no memory for SAR buffer!\n", __func__);
                ret = -ENOMEM;
                goto fail;
        }
@@ -770,6 +857,9 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
        vcc->dev_data = new;
 
        tasklet_disable(&instance->rx_channel.tasklet);
+       instance->cached_vcc = new;
+       instance->cached_vpi = vpi;
+       instance->cached_vci = vci;
        list_add(&new->list, &instance->vcc_list);
        tasklet_enable(&instance->rx_channel.tasklet);
 
@@ -777,7 +867,7 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
        set_bit(ATM_VF_PARTIAL, &vcc->flags);
        set_bit(ATM_VF_READY, &vcc->flags);
 
-       up(&instance->serialize);
+       mutex_unlock(&instance->serialize);
 
        atm_dbg(instance, "%s: allocated vcc data 0x%p\n", __func__, new);
 
@@ -785,7 +875,7 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
 
 fail:
        kfree(new);
-       up(&instance->serialize);
+       mutex_unlock(&instance->serialize);
        return ret;
 }
 
@@ -806,9 +896,14 @@ static void usbatm_atm_close(struct atm_vcc *vcc)
 
        usbatm_cancel_send(instance, vcc);
 
-       down(&instance->serialize);     /* vs self, usbatm_atm_open */
+       mutex_lock(&instance->serialize);       /* vs self, usbatm_atm_open, usbatm_usb_disconnect */
 
        tasklet_disable(&instance->rx_channel.tasklet);
+       if (instance->cached_vcc == vcc_data) {
+               instance->cached_vcc = NULL;
+               instance->cached_vpi = ATM_VPI_UNSPEC;
+               instance->cached_vci = ATM_VCI_UNSPEC;
+       }
        list_del(&vcc_data->list);
        tasklet_enable(&instance->rx_channel.tasklet);
 
@@ -824,14 +919,21 @@ static void usbatm_atm_close(struct atm_vcc *vcc)
        clear_bit(ATM_VF_PARTIAL, &vcc->flags);
        clear_bit(ATM_VF_ADDR, &vcc->flags);
 
-       up(&instance->serialize);
+       mutex_unlock(&instance->serialize);
 
        atm_dbg(instance, "%s successful\n", __func__);
 }
 
-static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
+static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd,
                          void __user * arg)
 {
+       struct usbatm_data *instance = atm_dev->dev_data;
+
+       if (!instance || instance->disconnected) {
+               dbg("%s: %s!", __func__, instance ? "disconnected" : "NULL instance");
+               return -ENODEV;
+       }
+
        switch (cmd) {
        case ATM_QUERYLOOP:
                return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
@@ -845,10 +947,13 @@ static int usbatm_atm_init(struct usbatm_data *instance)
        struct atm_dev *atm_dev;
        int ret, i;
 
-       /* ATM init */
+       /* ATM init.  The ATM initialization scheme suffers from an intrinsic race
+        * condition: callbacks we register can be executed at once, before we have
+        * initialized the struct atm_dev.  To protect against this, all callbacks
+        * abort if atm_dev->dev_data is NULL. */
        atm_dev = atm_dev_register(instance->driver_name, &usbatm_atm_devops, -1, NULL);
        if (!atm_dev) {
-               usb_dbg(instance, "%s: failed to register ATM device!\n", __func__);
+               usb_err(instance, "%s: failed to register ATM device!\n", __func__);
                return -1;
        }
 
@@ -862,12 +967,13 @@ static int usbatm_atm_init(struct usbatm_data *instance)
        atm_dev->link_rate = 128 * 1000 / 424;
 
        if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
-               atm_dbg(instance, "%s: atm_start failed: %d!\n", __func__, ret);
+               atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
                goto fail;
        }
 
-       /* ready for ATM callbacks */
        usbatm_get_instance(instance);  /* dropped in usbatm_atm_dev_close */
+
+       /* ready for ATM callbacks */
        mb();
        atm_dev->dev_data = instance;
 
@@ -903,9 +1009,9 @@ static int usbatm_do_heavy_init(void *arg)
        if (!ret)
                ret = usbatm_atm_init(instance);
 
-       down(&instance->serialize);
+       mutex_lock(&instance->serialize);
        instance->thread_pid = -1;
-       up(&instance->serialize);
+       mutex_unlock(&instance->serialize);
 
        complete_and_exit(&instance->thread_exited, ret);
 }
@@ -915,13 +1021,13 @@ static int usbatm_heavy_init(struct usbatm_data *instance)
        int ret = kernel_thread(usbatm_do_heavy_init, instance, CLONE_KERNEL);
 
        if (ret < 0) {
-               usb_dbg(instance, "%s: failed to create kernel_thread (%d)!\n", __func__, ret);
+               usb_err(instance, "%s: failed to create kernel_thread (%d)!\n", __func__, ret);
                return ret;
        }
 
-       down(&instance->serialize);
+       mutex_lock(&instance->serialize);
        instance->thread_pid = ret;
-       up(&instance->serialize);
+       mutex_unlock(&instance->serialize);
 
        wait_for_completion(&instance->thread_started);
 
@@ -951,9 +1057,9 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
        char *buf;
        int error = -ENOMEM;
        int i, length;
-       int need_heavy;
+       unsigned int maxpacket, num_packets;
 
-       dev_dbg(dev, "%s: trying driver %s with vendor=0x%x, product=0x%x, ifnum %d\n",
+       dev_dbg(dev, "%s: trying driver %s with vendor=%04x, product=%04x, ifnum %2d\n",
                        __func__, driver->driver_name,
                        le16_to_cpu(usb_dev->descriptor.idVendor),
                        le16_to_cpu(usb_dev->descriptor.idProduct),
@@ -962,7 +1068,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
        /* instance init */
        instance = kzalloc(sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL);
        if (!instance) {
-               dev_dbg(dev, "%s: no memory for instance data!\n", __func__);
+               dev_err(dev, "%s: no memory for instance data!\n", __func__);
                return -ENOMEM;
        }
 
@@ -996,66 +1102,96 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
        snprintf(buf, length, ")");
 
  bind:
-       need_heavy = 1;
-       if (driver->bind && (error = driver->bind(instance, intf, id, &need_heavy)) < 0) {
-                       dev_dbg(dev, "%s: bind failed: %d!\n", __func__, error);
+       if (driver->bind && (error = driver->bind(instance, intf, id)) < 0) {
+                       dev_err(dev, "%s: bind failed: %d!\n", __func__, error);
                        goto fail_free;
        }
 
        /* private fields */
 
        kref_init(&instance->refcount);         /* dropped in usbatm_usb_disconnect */
-       init_MUTEX(&instance->serialize);
+       mutex_init(&instance->serialize);
 
        instance->thread_pid = -1;
        init_completion(&instance->thread_started);
        init_completion(&instance->thread_exited);
 
        INIT_LIST_HEAD(&instance->vcc_list);
+       skb_queue_head_init(&instance->sndqueue);
 
        usbatm_init_channel(&instance->rx_channel);
        usbatm_init_channel(&instance->tx_channel);
        tasklet_init(&instance->rx_channel.tasklet, usbatm_rx_process, (unsigned long)instance);
        tasklet_init(&instance->tx_channel.tasklet, usbatm_tx_process, (unsigned long)instance);
-       instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->in);
-       instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->out);
        instance->rx_channel.stride = ATM_CELL_SIZE + driver->rx_padding;
        instance->tx_channel.stride = ATM_CELL_SIZE + driver->tx_padding;
-       instance->rx_channel.buf_size = rcv_buf_size * instance->rx_channel.stride;
-       instance->tx_channel.buf_size = snd_buf_size * instance->tx_channel.stride;
        instance->rx_channel.usbatm = instance->tx_channel.usbatm = instance;
 
-       skb_queue_head_init(&instance->sndqueue);
+       if ((instance->flags & UDSL_USE_ISOC) && driver->isoc_in)
+               instance->rx_channel.endpoint = usb_rcvisocpipe(usb_dev, driver->isoc_in);
+       else
+               instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->bulk_in);
+
+       instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->bulk_out);
+
+       /* tx buffer size must be a positive multiple of the stride */
+       instance->tx_channel.buf_size = max (instance->tx_channel.stride,
+                       snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride));
+
+       /* rx buffer size must be a positive multiple of the endpoint maxpacket */
+       maxpacket = usb_maxpacket(usb_dev, instance->rx_channel.endpoint, 0);
+
+       if ((maxpacket < 1) || (maxpacket > UDSL_MAX_BUF_SIZE)) {
+               dev_err(dev, "%s: invalid endpoint %02x!\n", __func__,
+                               usb_pipeendpoint(instance->rx_channel.endpoint));
+               error = -EINVAL;
+               goto fail_unbind;
+       }
+
+       num_packets = max (1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
+
+       if (num_packets * maxpacket > UDSL_MAX_BUF_SIZE)
+               num_packets--;
+
+       instance->rx_channel.buf_size = num_packets * maxpacket;
+       instance->rx_channel.packet_size = maxpacket;
+
+#ifdef DEBUG
+       for (i = 0; i < 2; i++) {
+               struct usbatm_channel *channel = i ?
+                       &instance->tx_channel : &instance->rx_channel;
+
+               dev_dbg(dev, "%s: using %d byte buffer for %s channel 0x%p\n", __func__, channel->buf_size, i ? "tx" : "rx", channel);
+       }
+#endif
+
+       /* initialize urbs */
 
        for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
-               struct urb *urb;
                u8 *buffer;
-               unsigned int iso_packets = 0, iso_size = 0;
                struct usbatm_channel *channel = i < num_rcv_urbs ?
                        &instance->rx_channel : &instance->tx_channel;
+               struct urb *urb;
+               unsigned int iso_packets = usb_pipeisoc(channel->endpoint) ? channel->buf_size / channel->packet_size : 0;
 
-               if (usb_pipeisoc(channel->endpoint)) {
-                       /* don't expect iso out endpoints */
-                       iso_size = usb_maxpacket(instance->usb_dev, channel->endpoint, 0);
-                       iso_size -= iso_size % channel->stride; /* alignment */
-                       BUG_ON(!iso_size);
-                       iso_packets = (channel->buf_size - 1) / iso_size + 1;
-               }
+               UDSL_ASSERT(!usb_pipeisoc(channel->endpoint) || usb_pipein(channel->endpoint));
 
                urb = usb_alloc_urb(iso_packets, GFP_KERNEL);
                if (!urb) {
-                       dev_dbg(dev, "%s: no memory for urb %d!\n", __func__, i);
+                       dev_err(dev, "%s: no memory for urb %d!\n", __func__, i);
+                       error = -ENOMEM;
                        goto fail_unbind;
                }
 
                instance->urbs[i] = urb;
 
-               buffer = kmalloc(channel->buf_size, GFP_KERNEL);
+               /* zero the tx padding to avoid leaking information */
+               buffer = kzalloc(channel->buf_size, GFP_KERNEL);
                if (!buffer) {
-                       dev_dbg(dev, "%s: no memory for buffer %d!\n", __func__, i);
+                       dev_err(dev, "%s: no memory for buffer %d!\n", __func__, i);
+                       error = -ENOMEM;
                        goto fail_unbind;
                }
-               memset(buffer, 0, channel->buf_size);
 
                usb_fill_bulk_urb(urb, instance->usb_dev, channel->endpoint,
                                  buffer, channel->buf_size, usbatm_complete, channel);
@@ -1065,9 +1201,8 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
                        urb->transfer_flags = URB_ISO_ASAP;
                        urb->number_of_packets = iso_packets;
                        for (j = 0; j < iso_packets; j++) {
-                               urb->iso_frame_desc[j].offset = iso_size * j;
-                               urb->iso_frame_desc[j].length = min_t(int, iso_size,
-                                                                     channel->buf_size - urb->iso_frame_desc[j].offset);
+                               urb->iso_frame_desc[j].offset = channel->packet_size * j;
+                               urb->iso_frame_desc[j].length = channel->packet_size;
                        }
                }
 
@@ -1079,7 +1214,17 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
                     __func__, urb->transfer_buffer, urb->transfer_buffer_length, urb);
        }
 
-       if (need_heavy && driver->heavy_init) {
+       instance->cached_vpi = ATM_VPI_UNSPEC;
+       instance->cached_vci = ATM_VCI_UNSPEC;
+       instance->cell_buf = kmalloc(instance->rx_channel.stride, GFP_KERNEL);
+
+       if (!instance->cell_buf) {
+               dev_err(dev, "%s: no memory for cell buffer!\n", __func__);
+               error = -ENOMEM;
+               goto fail_unbind;
+       }
+
+       if (!(instance->flags & UDSL_SKIP_HEAVY_INIT) && driver->heavy_init) {
                error = usbatm_heavy_init(instance);
        } else {
                complete(&instance->thread_exited);     /* pretend that heavy_init was run */
@@ -1098,6 +1243,8 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
        if (instance->driver->unbind)
                instance->driver->unbind(instance, intf);
  fail_free:
+       kfree(instance->cell_buf);
+
        for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
                if (instance->urbs[i])
                        kfree(instance->urbs[i]->transfer_buffer);
@@ -1114,6 +1261,7 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
 {
        struct device *dev = &intf->dev;
        struct usbatm_data *instance = usb_get_intfdata(intf);
+       struct usbatm_vcc_data *vcc_data;
        int i;
 
        dev_dbg(dev, "%s entered\n", __func__);
@@ -1125,13 +1273,19 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
 
        usb_set_intfdata(intf, NULL);
 
-       down(&instance->serialize);
+       mutex_lock(&instance->serialize);
+       instance->disconnected = 1;
        if (instance->thread_pid >= 0)
                kill_proc(instance->thread_pid, SIGTERM, 1);
-       up(&instance->serialize);
+       mutex_unlock(&instance->serialize);
 
        wait_for_completion(&instance->thread_exited);
 
+       mutex_lock(&instance->serialize);
+       list_for_each_entry(vcc_data, &instance->vcc_list, list)
+               vcc_release_async(vcc_data->vcc, -EPIPE);
+       mutex_unlock(&instance->serialize);
+
        tasklet_disable(&instance->rx_channel.tasklet);
        tasklet_disable(&instance->tx_channel.tasklet);
 
@@ -1141,6 +1295,14 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
        del_timer_sync(&instance->rx_channel.delay);
        del_timer_sync(&instance->tx_channel.delay);
 
+       /* turn usbatm_[rt]x_process into something close to a no-op */
+       /* no need to take the spinlock */
+       INIT_LIST_HEAD(&instance->rx_channel.list);
+       INIT_LIST_HEAD(&instance->tx_channel.list);
+
+       tasklet_enable(&instance->rx_channel.tasklet);
+       tasklet_enable(&instance->tx_channel.tasklet);
+
        if (instance->atm_dev && instance->driver->atm_stop)
                instance->driver->atm_stop(instance, instance->atm_dev);
 
@@ -1149,19 +1311,13 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
 
        instance->driver_data = NULL;
 
-       /* turn usbatm_[rt]x_process into noop */
-       /* no need to take the spinlock */
-       INIT_LIST_HEAD(&instance->rx_channel.list);
-       INIT_LIST_HEAD(&instance->tx_channel.list);
-
-       tasklet_enable(&instance->rx_channel.tasklet);
-       tasklet_enable(&instance->tx_channel.tasklet);
-
        for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
                kfree(instance->urbs[i]->transfer_buffer);
                usb_free_urb(instance->urbs[i]);
        }
 
+       kfree(instance->cell_buf);
+
        /* ATM finalize */
        if (instance->atm_dev)
                atm_dev_deregister(instance->atm_dev);
@@ -1186,10 +1342,10 @@ static int __init usbatm_usb_init(void)
 
        if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
            || (num_snd_urbs > UDSL_MAX_SND_URBS)
-           || (rcv_buf_size < 1)
-           || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
-           || (snd_buf_size < 1)
-           || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+           || (rcv_buf_bytes < 1)
+           || (rcv_buf_bytes > UDSL_MAX_BUF_SIZE)
+           || (snd_buf_bytes < 1)
+           || (snd_buf_bytes > UDSL_MAX_BUF_SIZE))
                return -EINVAL;
 
        return 0;
index 1adacd60d713fc24815cd8fd1141d3852ba79e33..ff8551e933723c25e16f9524a88458e413e4454b 100644 (file)
 #ifndef        _USBATM_H_
 #define        _USBATM_H_
 
-#include <linux/config.h>
-
-/*
-#define VERBOSE_DEBUG
-*/
-
 #include <asm/semaphore.h>
 #include <linux/atm.h>
 #include <linux/atmdev.h>
 #include <linux/completion.h>
 #include <linux/device.h>
+#include <linux/kernel.h>
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/stringify.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
+
+/*
+#define VERBOSE_DEBUG
+*/
 
 #ifdef DEBUG
 #define UDSL_ASSERT(x) BUG_ON(!(x))
        dev_info(&(instance)->usb_intf->dev , format , ## arg)
 #define usb_warn(instance, format, arg...)     \
        dev_warn(&(instance)->usb_intf->dev , format , ## arg)
+#ifdef DEBUG
 #define usb_dbg(instance, format, arg...)      \
-       dev_dbg(&(instance)->usb_intf->dev , format , ## arg)
+        dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
+#else
+#define usb_dbg(instance, format, arg...)      \
+       do {} while (0)
+#endif
 
 /* FIXME: move to dev_* once ATM is driver model aware */
 #define atm_printk(level, instance, format, arg...)    \
 #ifdef DEBUG
 #define atm_dbg(instance, format, arg...)      \
        atm_printk(KERN_DEBUG, instance , format , ## arg)
+#define atm_rldbg(instance, format, arg...)    \
+       if (printk_ratelimit())                         \
+               atm_printk(KERN_DEBUG, instance , format , ## arg)
 #else
 #define atm_dbg(instance, format, arg...)      \
        do {} while (0)
+#define atm_rldbg(instance, format, arg...)    \
+       do {} while (0)
 #endif
 
 
+/* flags, set by mini-driver in bind() */
+
+#define UDSL_SKIP_HEAVY_INIT   (1<<0)
+#define UDSL_USE_ISOC          (1<<1)
+#define UDSL_IGNORE_EILSEQ     (1<<2)
+
+
 /* mini driver */
 
 struct usbatm_data;
@@ -86,16 +103,11 @@ struct usbatm_data;
 */
 
 struct usbatm_driver {
-       struct module *owner;
-
        const char *driver_name;
 
-       /*
-       *  init device ... can sleep, or cause probe() failure.  Drivers with a heavy_init
-       *  method can avoid having it called by setting need_heavy_init to zero.
-       */
+       /* init device ... can sleep, or cause probe() failure */
         int (*bind) (struct usbatm_data *, struct usb_interface *,
-                    const struct usb_device_id *id, int *need_heavy_init);
+                    const struct usb_device_id *id);
 
        /* additional device initialization that is too slow to be done in probe() */
         int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
@@ -109,8 +121,9 @@ struct usbatm_driver {
        /* cleanup ATM device ... can sleep, but can't fail */
        void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
 
-        int in;                /* rx endpoint */
-        int out;       /* tx endpoint */
+        int bulk_in;   /* bulk rx endpoint */
+        int isoc_in;   /* isochronous rx endpoint */
+        int bulk_out;  /* bulk tx endpoint */
 
        unsigned rx_padding;
        unsigned tx_padding;
@@ -125,6 +138,7 @@ struct usbatm_channel {
        int endpoint;                   /* usb pipe */
        unsigned int stride;            /* ATM cell size + padding */
        unsigned int buf_size;          /* urb buffer size */
+       unsigned int packet_size;       /* endpoint maxpacket */
        spinlock_t lock;
        struct list_head list;
        struct tasklet_struct tasklet;
@@ -143,6 +157,7 @@ struct usbatm_data {
        struct usbatm_driver *driver;
        void *driver_data;
        char driver_name[16];
+       unsigned int flags; /* set by mini-driver in bind() */
 
        /* USB device */
        struct usb_device *usb_dev;
@@ -157,7 +172,8 @@ struct usbatm_data {
         ********************************/
 
        struct kref refcount;
-       struct semaphore serialize;
+       struct mutex serialize;
+       int disconnected;
 
        /* heavy init */
        int thread_pid;
@@ -171,7 +187,14 @@ struct usbatm_data {
        struct usbatm_channel tx_channel;
 
        struct sk_buff_head sndqueue;
-       struct sk_buff *current_skb;                    /* being emptied */
+       struct sk_buff *current_skb;    /* being emptied */
+
+       struct usbatm_vcc_data *cached_vcc;
+       int cached_vci;
+       short cached_vpi;
+
+       unsigned char *cell_buf;        /* holds partial rx cell */
+       unsigned int buf_usage;
 
        struct urb *urbs[0];
 };
index 5c76e3aaaa5e9aaa290936da377ed574011500c4..42d6823b82b3d4efac2658bba02302c48f9baff1 100644 (file)
@@ -41,6 +41,8 @@ XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number");
 XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number");
 XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)");
 XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)");
+XUSBATM_PARM(rx_altsetting, unsigned char, byte, "rx altsetting (default 0)");
+XUSBATM_PARM(tx_altsetting, unsigned char, byte, "rx altsetting (default 0)");
 
 static const char xusbatm_driver_name[] = "xusbatm";
 
@@ -48,82 +50,118 @@ static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX];
 static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
 static struct usb_driver xusbatm_usb_driver;
 
-static int usb_intf_has_ep(const struct usb_interface *intf, u8 ep)
+static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int altsetting, u8 ep)
 {
+       struct usb_host_interface *alt;
+       struct usb_interface *intf;
        int i, j;
 
-       for (i = 0; i < intf->num_altsetting; i++) {
-               struct usb_host_interface *alt = intf->altsetting;
-               for (j = 0; j < alt->desc.bNumEndpoints; j++)
-                       if ((alt->endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) == ep)
-                               return 1;
+       for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
+               if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting)))
+                       for (j = 0; j < alt->desc.bNumEndpoints; j++)
+                               if (alt->endpoint[j].desc.bEndpointAddress == ep)
+                                       return intf;
+       return NULL;
+}
+
+static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *usb_dev,
+               struct usb_interface *intf, int altsetting, int claim)
+{
+       int ifnum = intf->altsetting->desc.bInterfaceNumber;
+       int ret;
+
+       if (claim && (ret = usb_driver_claim_interface(&xusbatm_usb_driver, intf, usbatm))) {
+               usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret);
+               return ret;
+       }
+       if ((ret = usb_set_interface(usb_dev, ifnum, altsetting))) {
+               usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret);
+               return ret;
        }
        return 0;
 }
 
-static int xusbatm_bind(struct usbatm_data *usbatm_instance,
-                       struct usb_interface *intf, const struct usb_device_id *id,
-                       int *need_heavy_init)
+static void xusbatm_release_intf (struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
+{
+       if (claimed) {
+               usb_set_intfdata(intf, NULL);
+               usb_driver_release_interface(&xusbatm_usb_driver, intf);
+       }
+}
+
+static int xusbatm_bind(struct usbatm_data *usbatm,
+                       struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *usb_dev = interface_to_usbdev(intf);
        int drv_ix = id - xusbatm_usb_ids;
-       int rx_ep_present = usb_intf_has_ep(intf, rx_endpoint[drv_ix]);
-       int tx_ep_present = usb_intf_has_ep(intf, tx_endpoint[drv_ix]);
-       u8 searched_ep = rx_ep_present ? tx_endpoint[drv_ix] : rx_endpoint[drv_ix];
-       int i, ret;
-
-       usb_dbg(usbatm_instance, "%s: binding driver %d: vendor %#x product %#x"
-               " rx: ep %#x padd %d tx: ep %#x padd %d\n",
+       int rx_alt = rx_altsetting[drv_ix];
+       int tx_alt = tx_altsetting[drv_ix];
+       struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, rx_alt, rx_endpoint[drv_ix]);
+       struct usb_interface *tx_intf = xusbatm_find_intf(usb_dev, tx_alt, tx_endpoint[drv_ix]);
+       int ret;
+
+       usb_dbg(usbatm, "%s: binding driver %d: vendor %04x product %04x"
+               " rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d\n",
                __func__, drv_ix, vendor[drv_ix], product[drv_ix],
-               rx_endpoint[drv_ix], rx_padding[drv_ix],
-               tx_endpoint[drv_ix], tx_padding[drv_ix]);
+               rx_endpoint[drv_ix], rx_padding[drv_ix], rx_alt,
+               tx_endpoint[drv_ix], tx_padding[drv_ix], tx_alt);
+
+       if (!rx_intf || !tx_intf) {
+               if (!rx_intf)
+                       usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n",
+                               __func__, rx_endpoint[drv_ix], rx_alt);
+               if (!tx_intf)
+                       usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n",
+                               __func__, tx_endpoint[drv_ix], tx_alt);
+               return -ENODEV;
+       }
 
-       if (!rx_ep_present && !tx_ep_present) {
-               usb_dbg(usbatm_instance, "%s: intf #%d has neither rx (%#x) nor tx (%#x) endpoint\n",
-                       __func__, intf->altsetting->desc.bInterfaceNumber,
-                       rx_endpoint[drv_ix], tx_endpoint[drv_ix]);
+       if ((rx_intf != intf) && (tx_intf != intf))
                return -ENODEV;
+
+       if ((rx_intf == tx_intf) && (rx_alt != tx_alt)) {
+               usb_err(usbatm, "%s: altsettings clash on interface %2d (%2d vs %2d)!\n", __func__,
+                               rx_intf->altsetting->desc.bInterfaceNumber, rx_alt, tx_alt);
+               return -EINVAL;
        }
 
-       if (rx_ep_present && tx_ep_present)
-               return 0;
+       usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n", __func__,
+                       rx_intf->altsetting->desc.bInterfaceNumber,
+                       tx_intf->altsetting->desc.bInterfaceNumber);
 
-       for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
-               struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
-
-               if (cur_if != intf && usb_intf_has_ep(cur_if, searched_ep)) {
-                       ret = usb_driver_claim_interface(&xusbatm_usb_driver,
-                                                        cur_if, usbatm_instance);
-                       if (!ret)
-                               usb_err(usbatm_instance, "%s: failed to claim interface #%d (%d)\n",
-                                       __func__, cur_if->altsetting->desc.bInterfaceNumber, ret);
-                       return ret;
-               }
+       if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf)))
+               return ret;
+
+       if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) {
+               xusbatm_release_intf(usb_dev, rx_intf, rx_intf != intf);
+               return ret;
        }
 
-       usb_err(usbatm_instance, "%s: no interface has endpoint %#x\n",
-               __func__, searched_ep);
-       return -ENODEV;
+       return 0;
 }
 
-static void xusbatm_unbind(struct usbatm_data *usbatm_instance,
+static void xusbatm_unbind(struct usbatm_data *usbatm,
                           struct usb_interface *intf)
 {
        struct usb_device *usb_dev = interface_to_usbdev(intf);
        int i;
-       usb_dbg(usbatm_instance, "%s entered\n", __func__);
+
+       usb_dbg(usbatm, "%s entered\n", __func__);
 
        for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
-               struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
-               usb_set_intfdata(cur_if, NULL);
-               usb_driver_release_interface(&xusbatm_usb_driver, cur_if);
+               struct usb_interface *cur_intf = usb_dev->actconfig->interface[i];
+
+               if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) {
+                       usb_set_intfdata(cur_intf, NULL);
+                       usb_driver_release_interface(&xusbatm_usb_driver, cur_intf);
+               }
        }
 }
 
-static int xusbatm_atm_start(struct usbatm_data *usbatm_instance,
+static int xusbatm_atm_start(struct usbatm_data *usbatm,
                             struct atm_dev *atm_dev)
 {
-       atm_dbg(usbatm_instance, "%s entered\n", __func__);
+       atm_dbg(usbatm, "%s entered\n", __func__);
 
        /* use random MAC as we've no way to get it from the device */
        random_ether_addr(atm_dev->esi);
@@ -161,18 +199,19 @@ static int __init xusbatm_init(void)
        }
 
        for (i = 0; i < num_vendor; i++) {
+               rx_endpoint[i] |= USB_DIR_IN;
+               tx_endpoint[i] &= USB_ENDPOINT_NUMBER_MASK;
+
                xusbatm_usb_ids[i].match_flags  = USB_DEVICE_ID_MATCH_DEVICE;
                xusbatm_usb_ids[i].idVendor     = vendor[i];
                xusbatm_usb_ids[i].idProduct    = product[i];
 
-
-               xusbatm_drivers[i].owner        = THIS_MODULE;
                xusbatm_drivers[i].driver_name  = xusbatm_driver_name;
                xusbatm_drivers[i].bind         = xusbatm_bind;
                xusbatm_drivers[i].unbind       = xusbatm_unbind;
                xusbatm_drivers[i].atm_start    = xusbatm_atm_start;
-               xusbatm_drivers[i].in           = rx_endpoint[i];
-               xusbatm_drivers[i].out          = tx_endpoint[i];
+               xusbatm_drivers[i].bulk_in      = rx_endpoint[i];
+               xusbatm_drivers[i].bulk_out     = tx_endpoint[i];
                xusbatm_drivers[i].rx_padding   = rx_padding[i];
                xusbatm_drivers[i].tx_padding   = tx_padding[i];
        }
index b9fd39fd1b5b3c37c9a8dc6c729a20bfc6b39fba..97bdeb1c2181650428841826f1c185f53ee81be0 100644 (file)
@@ -1014,8 +1014,13 @@ static void acm_disconnect(struct usb_interface *intf)
        }
 
        down(&open_sem);
+       if (!usb_get_intfdata(intf)) {
+               up(&open_sem);
+               return;
+       }
        acm->dev = NULL;
-       usb_set_intfdata (intf, NULL);
+       usb_set_intfdata(acm->control, NULL);
+       usb_set_intfdata(acm->data, NULL);
 
        tasklet_disable(&acm->urb_task);
 
@@ -1036,7 +1041,7 @@ static void acm_disconnect(struct usb_interface *intf)
        for (i = 0; i < ACM_NRB; i++)
                usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
 
-       usb_driver_release_interface(&acm_driver, acm->data);
+       usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
 
        if (!acm->used) {
                acm_tty_unregister(acm);
index dba4cc0260770ebf8d6c39963cd8345c80a98b2a..d34848ac30b0a9571db2dd6cd596cdf139b5c8df 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (c) 2000 Vojtech Pavlik   <vojtech@suse.cz>
  # Copyright (c) 2001 Pete Zaitcev     <zaitcev@redhat.com>
  # Copyright (c) 2001 David Paschal    <paschal@rcsis.com>
+ * Copyright (c) 2006 Oliver Neukum    <oliver@neukum.name>
  *
  * USB Printer Device Class driver for USB printers and printer cables
  *
@@ -273,13 +274,16 @@ static void usblp_bulk_read(struct urb *urb, struct pt_regs *regs)
 {
        struct usblp *usblp = urb->context;
 
-       if (!usblp || !usblp->dev || !usblp->used || !usblp->present)
+       if (unlikely(!usblp || !usblp->dev || !usblp->used))
                return;
 
+       if (unlikely(!usblp->present))
+               goto unplug;
        if (unlikely(urb->status))
                warn("usblp%d: nonzero read/write bulk status received: %d",
                        usblp->minor, urb->status);
        usblp->rcomplete = 1;
+unplug:
        wake_up_interruptible(&usblp->wait);
 }
 
@@ -287,13 +291,15 @@ static void usblp_bulk_write(struct urb *urb, struct pt_regs *regs)
 {
        struct usblp *usblp = urb->context;
 
-       if (!usblp || !usblp->dev || !usblp->used || !usblp->present)
+       if (unlikely(!usblp || !usblp->dev || !usblp->used))
                return;
-
+       if (unlikely(!usblp->present))
+               goto unplug;
        if (unlikely(urb->status))
                warn("usblp%d: nonzero read/write bulk status received: %d",
                        usblp->minor, urb->status);
        usblp->wcomplete = 1;
+unplug:
        wake_up_interruptible(&usblp->wait);
 }
 
@@ -627,9 +633,8 @@ done:
 
 static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
 {
-       DECLARE_WAITQUEUE(wait, current);
        struct usblp *usblp = file->private_data;
-       int timeout, err = 0, transfer_length = 0;
+       int timeout, rv, err = 0, transfer_length = 0;
        size_t writecount = 0;
 
        while (writecount < count) {
@@ -641,24 +646,11 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
                        }
 
                        timeout = USBLP_WRITE_TIMEOUT;
-                       add_wait_queue(&usblp->wait, &wait);
-                       while ( 1==1 ) {
 
-                               if (signal_pending(current)) {
-                                       remove_wait_queue(&usblp->wait, &wait);
-                                       return writecount ? writecount : -EINTR;
-                               }
-                               set_current_state(TASK_INTERRUPTIBLE);
-                               if (timeout && !usblp->wcomplete) {
-                                       timeout = schedule_timeout(timeout);
-                               } else {
-                                       set_current_state(TASK_RUNNING);
-                                       break;
-                               }
-                       }
-                       remove_wait_queue(&usblp->wait, &wait);
+                       rv = wait_event_interruptible_timeout(usblp->wait, usblp->wcomplete || !usblp->present , timeout);
+                       if (rv < 0)
+                               return writecount ? writecount : -EINTR;
                }
-
                down (&usblp->sem);
                if (!usblp->present) {
                        up (&usblp->sem);
@@ -724,7 +716,7 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
 static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
 {
        struct usblp *usblp = file->private_data;
-       DECLARE_WAITQUEUE(wait, current);
+       int rv;
 
        if (!usblp->bidir)
                return -EINVAL;
@@ -742,26 +734,13 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
                        count = -EAGAIN;
                        goto done;
                }
-
-               add_wait_queue(&usblp->wait, &wait);
-               while (1==1) {
-                       if (signal_pending(current)) {
-                               count = -EINTR;
-                               remove_wait_queue(&usblp->wait, &wait);
-                               goto done;
-                       }
-                       up (&usblp->sem);
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       if (!usblp->rcomplete) {
-                               schedule();
-                       } else {
-                               set_current_state(TASK_RUNNING);
-                               down(&usblp->sem);
-                               break;
-                       }
-                       down (&usblp->sem);
+               up(&usblp->sem);
+               rv = wait_event_interruptible(usblp->wait, usblp->rcomplete || !usblp->present);
+               down(&usblp->sem);
+               if (rv < 0) {
+                       count = -EINTR;
+                       goto done;
                }
-               remove_wait_queue(&usblp->wait, &wait);
        }
 
        if (!usblp->present) {
@@ -874,11 +853,10 @@ static int usblp_probe(struct usb_interface *intf,
 
        /* Malloc and start initializing usblp structure so we can use it
         * directly. */
-       if (!(usblp = kmalloc(sizeof(struct usblp), GFP_KERNEL))) {
+       if (!(usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL))) {
                err("out of memory for usblp");
                goto abort;
        }
-       memset(usblp, 0, sizeof(struct usblp));
        usblp->dev = dev;
        init_MUTEX (&usblp->sem);
        init_waitqueue_head(&usblp->wait);
@@ -1214,10 +1192,9 @@ static int __init usblp_init(void)
 {
        int retval;
        retval = usb_register(&usblp_driver);
-       if (retval)
-               goto out;
-       info(DRIVER_VERSION ": " DRIVER_DESC);
-out:
+       if (!retval)
+               info(DRIVER_VERSION ": " DRIVER_DESC);
+
        return retval;
 }
 
index 319de03944e7a39d9d0bac12a46afc4f8c32a023..7135e542679d73431d145cee917da3fc18a492f2 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/ctype.h>
 #include <linux/device.h>
 #include <asm/byteorder.h>
+#include <asm/scatterlist.h>
 
 #include "hcd.h"       /* for usbcore internals */
 #include "usb.h"
index 081796726b951bf449b8b827b970cf7eec1b8571..dad4d8fd8180afc8ee779a04b380a7d364cdc9db 100644 (file)
@@ -468,6 +468,7 @@ int usb_unlink_urb(struct urb *urb)
  */
 void usb_kill_urb(struct urb *urb)
 {
+       might_sleep();
        if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
                return;
        spin_lock_irq(&urb->lock);
index 9a4edc5657aa695cb7f98030084433f6369435c9..0aab7d24c768d03c99c5a67aa154bf2df1e4e640 100644 (file)
@@ -135,6 +135,7 @@ struct dev_data {
                                        setup_out_ready : 1,
                                        setup_out_error : 1,
                                        setup_abort : 1;
+       unsigned                        setup_wLength;
 
        /* the rest is basically write-once */
        struct usb_config_descriptor    *config, *hs_config;
@@ -942,6 +943,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
        }
        req->complete = ep0_complete;
        req->length = len;
+       req->zero = 0;
        return 0;
 }
 
@@ -1161,10 +1163,13 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                                spin_unlock_irq (&dev->lock);
                                if (copy_from_user (dev->req->buf, buf, len))
                                        retval = -EFAULT;
-                               else
+                               else {
+                                       if (len < dev->setup_wLength)
+                                               dev->req->zero = 1;
                                        retval = usb_ep_queue (
                                                dev->gadget->ep0, dev->req,
                                                GFP_KERNEL);
+                               }
                                if (retval < 0) {
                                        spin_lock_irq (&dev->lock);
                                        clean_req (dev->gadget->ep0, dev->req);
@@ -1483,6 +1488,7 @@ unrecognized:
 delegate:
                        dev->setup_in = (ctrl->bRequestType & USB_DIR_IN)
                                                ? 1 : 0;
+                       dev->setup_wLength = w_length;
                        dev->setup_out_ready = 0;
                        dev->setup_out_error = 0;
                        value = 0;
index c32e1f7476da46d67b4e2f8336ab8cd5929b1b75..67b13ab2f3f5a18ef80456fc9362a701777ce797 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/dma-mapping.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
index 2fc110d3ad5ab05b960578280f108de6304b9502..ae7a1c0f57488ca8f28533d767a21e61f8ee7143 100644 (file)
@@ -165,8 +165,8 @@ static unsigned buflen = 4096;
 static unsigned qlen = 32;
 static unsigned pattern = 0;
 
-module_param (buflen, uint, S_IRUGO|S_IWUSR);
-module_param (qlen, uint, S_IRUGO|S_IWUSR);
+module_param (buflen, uint, S_IRUGO);
+module_param (qlen, uint, S_IRUGO);
 module_param (pattern, uint, S_IRUGO|S_IWUSR);
 
 /*
@@ -1127,8 +1127,10 @@ zero_unbind (struct usb_gadget *gadget)
        DBG (dev, "unbind\n");
 
        /* we've already been disconnected ... no i/o is active */
-       if (dev->req)
+       if (dev->req) {
+               dev->req->length = USB_BUFSIZ;
                free_ep_req (gadget->ep0, dev->req);
+       }
        del_timer_sync (&dev->resume);
        kfree (dev);
        set_gadget_data (gadget, NULL);
index 08ca0f849dab03ddbf9688aeb1e77dd811dbf8c6..3a6687df559419c0cb6206fca819dc79f21c45eb 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
-/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
- * off the controller (maybe it can boot from highspeed USB disks).
- */
-static int bios_handoff(struct ehci_hcd *ehci, int where, u32 cap)
-{
-       struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
-
-       /* always say Linux will own the hardware */
-       pci_write_config_byte(pdev, where + 3, 1);
-
-       /* maybe wait a while for BIOS to respond */
-       if (cap & (1 << 16)) {
-               int msec = 5000;
-
-               do {
-                       msleep(10);
-                       msec -= 10;
-                       pci_read_config_dword(pdev, where, &cap);
-               } while ((cap & (1 << 16)) && msec);
-               if (cap & (1 << 16)) {
-                       ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n",
-                               where, cap);
-                       // some BIOS versions seem buggy...
-                       // return 1;
-                       ehci_warn(ehci, "continuing after BIOS bug...\n");
-                       /* disable all SMIs, and clear "BIOS owns" flag */
-                       pci_write_config_dword(pdev, where + 4, 0);
-                       pci_write_config_byte(pdev, where + 2, 0);
-               } else
-                       ehci_dbg(ehci, "BIOS handoff succeeded\n");
-       }
-       return 0;
-}
-
 /* called after powerup, by probe or system-pm "wakeup" */
 static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
 {
        u32                     temp;
        int                     retval;
-       unsigned                count = 256/4;
 
        /* optional debug port, normally in the first BAR */
        temp = pci_find_capability(pdev, 0x0a);
@@ -84,32 +49,9 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
                }
        }
 
-       temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params));
-
-       /* EHCI 0.96 and later may have "extended capabilities" */
-       while (temp && count--) {
-               u32             cap;
-
-               pci_read_config_dword(pdev, temp, &cap);
-               ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp);
-               switch (cap & 0xff) {
-               case 1:                 /* BIOS/SMM/... handoff */
-                       if (bios_handoff(ehci, temp, cap) != 0)
-                               return -EOPNOTSUPP;
-                       break;
-               case 0:                 /* illegal reserved capability */
-                       ehci_dbg(ehci, "illegal capability!\n");
-                       cap = 0;
-                       /* FALLTHROUGH */
-               default:                /* unknown */
-                       break;
-               }
-               temp = (cap >> 8) & 0xff;
-       }
-       if (!count) {
-               ehci_err(ehci, "bogus capabilities ... PCI problems!\n");
-               return -EIO;
-       }
+       /* we expect static quirk code to handle the "extended capabilities"
+        * (currently just BIOS handoff) allowed starting with EHCI 0.96
+        */
 
        /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
        retval = pci_set_mwi(pdev);
index 57e77374d228dd083053b78a26d331a44c4e20cd..ebcca97006712e3da3df4b8508f0fbf51dcd726d 100644 (file)
@@ -1063,6 +1063,7 @@ sitd_slot_ok (
 
                /* for IN, check CSPLIT */
                if (stream->c_usecs) {
+                       uf = uframe & 7;
                        max_used = 100 - stream->c_usecs;
                        do {
                                tmp = 1 << uf;
@@ -1843,8 +1844,7 @@ done:
 #else
 
 static inline int
-sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
-       unsigned mem_flags)
+sitd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags)
 {
        ehci_dbg (ehci, "split iso support is disabled\n");
        return -ENOSYS;
index 584b8dc65119f80187efc3aa2368125bfba870f4..972ce04889f8a9d602c060e896c4efa83b2ab1af 100644 (file)
@@ -1420,20 +1420,22 @@ static int isp116x_bus_suspend(struct usb_hcd *hcd)
        int ret = 0;
 
        spin_lock_irqsave(&isp116x->lock, flags);
-
        val = isp116x_read_reg32(isp116x, HCCONTROL);
+
        switch (val & HCCONTROL_HCFS) {
        case HCCONTROL_USB_OPER:
+               spin_unlock_irqrestore(&isp116x->lock, flags);
                val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
                val |= HCCONTROL_USB_SUSPEND;
                if (device_may_wakeup(&hcd->self.root_hub->dev))
                        val |= HCCONTROL_RWE;
                /* Wait for usb transfers to finish */
-               mdelay(2);
+               msleep(2);
+               spin_lock_irqsave(&isp116x->lock, flags);
                isp116x_write_reg32(isp116x, HCCONTROL, val);
+               spin_unlock_irqrestore(&isp116x->lock, flags);
                /* Wait for devices to suspend */
-               mdelay(5);
-       case HCCONTROL_USB_SUSPEND:
+               msleep(5);
                break;
        case HCCONTROL_USB_RESUME:
                isp116x_write_reg32(isp116x, HCCONTROL,
@@ -1441,12 +1443,11 @@ static int isp116x_bus_suspend(struct usb_hcd *hcd)
                                    HCCONTROL_USB_RESET);
        case HCCONTROL_USB_RESET:
                ret = -EBUSY;
+       default:                /* HCCONTROL_USB_SUSPEND */
+               spin_unlock_irqrestore(&isp116x->lock, flags);
                break;
-       default:
-               ret = -EINVAL;
        }
 
-       spin_unlock_irqrestore(&isp116x->lock, flags);
        return ret;
 }
 
@@ -1715,9 +1716,9 @@ static struct platform_driver isp116x_driver = {
        .remove = isp116x_remove,
        .suspend = isp116x_suspend,
        .resume = isp116x_resume,
-       .driver = {
-               .name = (char *)hcd_name,
-       },
+       .driver = {
+                  .name = (char *)hcd_name,
+                  },
 };
 
 /*-----------------------------------------------------------------*/
index 77cd6ac07e3c5d4076129c13aad23211197db4ab..db280ca7b7a0f1c0ad7444fa38801e40cc12eccb 100644 (file)
@@ -67,7 +67,7 @@ static void au1xxx_stop_hc(struct platform_device *dev)
               ": stopping Au1xxx OHCI USB Controller\n");
 
        /* Disable clock */
-       au_writel(readl((void *)USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
+       au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
 }
 
 
index 3ef2c0cdf1db2fe26c6235ea3410a36e543be617..e9e5bc178cef2e9a71eb2e70e96ced328ec7d00d 100644 (file)
@@ -190,7 +190,7 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
                        msleep(10);
                }
                if (wait_time <= 0)
-                       printk(KERN_WARNING "%s %s: early BIOS handoff "
+                       printk(KERN_WARNING "%s %s: BIOS handoff "
                                        "failed (BIOS bug ?)\n",
                                        pdev->dev.bus_id, "OHCI");
 
@@ -212,8 +212,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
 {
        int wait_time, delta;
        void __iomem *base, *op_reg_base;
-       u32 hcc_params, val, temp;
-       u8 cap_length;
+       u32     hcc_params, val;
+       u8      offset, cap_length;
+       int     count = 256/4;
 
        if (!mmio_resource_enabled(pdev, 0))
                return;
@@ -224,51 +225,80 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
 
        cap_length = readb(base);
        op_reg_base = base + cap_length;
+
+       /* EHCI 0.96 and later may have "extended capabilities"
+        * spec section 5.1 explains the bios handoff, e.g. for
+        * booting from USB disk or using a usb keyboard
+        */
        hcc_params = readl(base + EHCI_HCC_PARAMS);
-       hcc_params = (hcc_params >> 8) & 0xff;
-       if (hcc_params) {
-               pci_read_config_dword(pdev,
-                                       hcc_params + EHCI_USBLEGSUP,
-                                       &val);
-               if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
-                       /*
-                        * Ok, BIOS is in smm mode, try to hand off...
+       offset = (hcc_params >> 8) & 0xff;
+       while (offset && count--) {
+               u32             cap;
+               int             msec;
+
+               pci_read_config_dword(pdev, offset, &cap);
+               switch (cap & 0xff) {
+               case 1:                 /* BIOS/SMM/... handoff support */
+                       if ((cap & EHCI_USBLEGSUP_BIOS)) {
+                               pr_debug("%s %s: BIOS handoff\n",
+                                               pdev->dev.bus_id, "EHCI");
+
+                               /* BIOS workaround (?): be sure the
+                                * pre-Linux code receives the SMI
+                                */
+                               pci_read_config_dword(pdev,
+                                               offset + EHCI_USBLEGCTLSTS,
+                                               &val);
+                               pci_write_config_dword(pdev,
+                                               offset + EHCI_USBLEGCTLSTS,
+                                               val | EHCI_USBLEGCTLSTS_SOOE);
+                       }
+
+                       /* always say Linux will own the hardware
+                        * by setting EHCI_USBLEGSUP_OS.
                         */
-                       pci_read_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGCTLSTS,
-                                               &temp);
-                       pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGCTLSTS,
-                                               temp | EHCI_USBLEGCTLSTS_SOOE);
-                       val |= EHCI_USBLEGSUP_OS;
-                       pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGSUP,
-                                               val);
+                       pci_write_config_byte(pdev, offset + 3, 1);
 
-                       wait_time = 500;
-                       do {
+                       /* if boot firmware now owns EHCI, spin till
+                        * it hands it over.
+                        */
+                       msec = 5000;
+                       while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
                                msleep(10);
-                               wait_time -= 10;
-                               pci_read_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGSUP,
-                                               &val);
-                       } while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
-                       if (!wait_time) {
-                               /*
-                                * well, possibly buggy BIOS...
+                               msec -= 10;
+                               pci_read_config_dword(pdev, offset, &cap);
+                       }
+
+                       if (cap & EHCI_USBLEGSUP_BIOS) {
+                               /* well, possibly buggy BIOS... try to shut
+                                * it down, and hope nothing goes too wrong
                                 */
-                               printk(KERN_WARNING "%s %s: early BIOS handoff "
+                               printk(KERN_WARNING "%s %s: BIOS handoff "
                                                "failed (BIOS bug ?)\n",
                                        pdev->dev.bus_id, "EHCI");
-                               pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGSUP,
-                                               EHCI_USBLEGSUP_OS);
-                               pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGCTLSTS,
-                                               0);
+                               pci_write_config_byte(pdev, offset + 2, 0);
                        }
+
+                       /* just in case, always disable EHCI SMIs */
+                       pci_write_config_dword(pdev,
+                                       offset + EHCI_USBLEGCTLSTS,
+                                       0);
+                       break;
+               case 0:                 /* illegal reserved capability */
+                       cap = 0;
+                       /* FALLTHROUGH */
+               default:
+                       printk(KERN_WARNING "%s %s: unrecognized "
+                                       "capability %02x\n",
+                                       pdev->dev.bus_id, "EHCI",
+                                       cap & 0xff);
+                       break;
                }
+               offset = (cap >> 8) & 0xff;
        }
+       if (!count)
+               printk(KERN_DEBUG "%s %s: capability loop?\n",
+                               pdev->dev.bus_id, "EHCI");
 
        /*
         * halt EHCI & disable its interrupts in any case
index b6076004a4374d54c3ae641a5f606e8a0c0da529..782398045f9f477fa952294f0d31fbfa959ff5e2 100644 (file)
@@ -672,9 +672,9 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
        /* Low-speed transfers get a different queue, and won't hog the bus.
         * Also, some devices enumerate better without FSBR; the easiest way
         * to do that is to put URBs on the low-speed queue while the device
-        * is in the DEFAULT state. */
+        * isn't in the CONFIGURED state. */
        if (urb->dev->speed == USB_SPEED_LOW ||
-                       urb->dev->state == USB_STATE_DEFAULT)
+                       urb->dev->state != USB_STATE_CONFIGURED)
                skelqh = uhci->skel_ls_control_qh;
        else {
                skelqh = uhci->skel_fs_control_qh;
index a91e72c41415c213b8ece5824c16f4873161dc73..6f7a684c3e076fcc2467766e136a08f2ca6d90c9 100644 (file)
@@ -1307,7 +1307,7 @@ void hid_init_reports(struct hid_device *hid)
        }
 
        if (err)
-               warn("timeout initializing reports\n");
+               warn("timeout initializing reports");
 }
 
 #define USB_VENDOR_ID_WACOM            0x056a
@@ -1453,6 +1453,9 @@ void hid_init_reports(struct hid_device *hid)
 #define USB_VENDOR_ID_CHERRY           0x046a
 #define USB_DEVICE_ID_CHERRY_CYMOTION  0x0023
 
+#define USB_VENDOR_ID_HP               0x03f0
+#define USB_DEVICE_ID_HP_USBHUB_KB     0x020c
+
 /*
  * Alphabetically sorted blacklist by quirk type.
  */
@@ -1566,6 +1569,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_KEYBOARD, HID_QUIRK_NOGET},
        { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_USBHUB_KB, HID_QUIRK_NOGET},
+       { USB_VENDOR_ID_HP, USB_DEVICE_ID_HP_USBHUB_KB, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
 
        { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_POWERMOUSE, HID_QUIRK_2WHEEL_POWERMOUSE },
@@ -1828,9 +1832,6 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
        hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
        hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
 
-       /* May be needed for some devices */
-       usb_clear_halt(hid->dev, hid->urbin->pipe);
-
        return hid;
 
 fail:
index 4dff8473553dd9fc8eba3ebe33378741c8bc7898..77be6b9a820ff4fddffe0727e8d3f7add23ac63e 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/usb.h>
 #include "hid.h"
 #include <linux/hiddev.h>
-#include <linux/devfs_fs_kernel.h>
 
 #ifdef CONFIG_USB_DYNAMIC_MINORS
 #define HIDDEV_MINOR_BASE      0
@@ -832,12 +831,10 @@ static /* const */ struct usb_driver hiddev_driver = {
 
 int __init hiddev_init(void)
 {
-       devfs_mk_dir("usb/hid");
        return usb_register(&hiddev_driver);
 }
 
 void hiddev_exit(void)
 {
        usb_deregister(&hiddev_driver);
-       devfs_remove("usb/hid");
 }
index 3b3c7b4120a26c51fb8e8af875f75aa02a201242..697c5e573a115cbe1d38548448032705f4846543 100644 (file)
@@ -337,6 +337,9 @@ static int touchkit_probe(struct usb_interface *intf,
                         touchkit->data, TOUCHKIT_REPORT_DATA_SIZE,
                         touchkit_irq, touchkit, endpoint->bInterval);
 
+       touchkit->irq->transfer_dma = touchkit->data_dma;
+       touchkit->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
        input_register_device(touchkit->input);
 
        usb_set_intfdata(intf, touchkit);
index 1bfc105ad4d6811adda2d403870b591bb33c8a6b..37d2f0ba0319c28e73e688394c27c3f65ab28459 100644 (file)
@@ -59,7 +59,7 @@
 #include "map_to_7segment.h"
 #include "yealink.h"
 
-#define DRIVER_VERSION "yld-20050816"
+#define DRIVER_VERSION "yld-20051230"
 #define DRIVER_AUTHOR "Henk Vergonet"
 #define DRIVER_DESC "Yealink phone driver"
 
@@ -786,16 +786,25 @@ static struct attribute_group yld_attr_group = {
  * Linux interface and usb initialisation
  ******************************************************************************/
 
-static const struct yld_device {
-       u16 idVendor;
-       u16 idProduct;
+struct driver_info {
        char *name;
-} yld_device[] = {
-       { 0x6993, 0xb001, "Yealink usb-p1k" },
 };
 
-static struct usb_device_id usb_table [] = {
-       { USB_INTERFACE_INFO(USB_CLASS_HID, 0, 0) },
+static const struct driver_info info_P1K = {
+       .name   = "Yealink usb-p1k",
+};
+
+static const struct usb_device_id usb_table [] = {
+       {
+               .match_flags            = USB_DEVICE_ID_MATCH_DEVICE |
+                                               USB_DEVICE_ID_MATCH_INT_INFO,
+               .idVendor               = 0x6993,
+               .idProduct              = 0xb001,
+               .bInterfaceClass        = USB_CLASS_HID,
+               .bInterfaceSubClass     = 0,
+               .bInterfaceProtocol     = 0,
+               .driver_info            = (kernel_ulong_t)&info_P1K
+       },
        { }
 };
 
@@ -842,33 +851,16 @@ static void usb_disconnect(struct usb_interface *intf)
        usb_cleanup(yld, 0);
 }
 
-static int usb_match(struct usb_device *udev)
-{
-       int i;
-       u16 idVendor = le16_to_cpu(udev->descriptor.idVendor);
-       u16 idProduct = le16_to_cpu(udev->descriptor.idProduct);
-
-       for (i = 0; i < ARRAY_SIZE(yld_device); i++) {
-               if ((idVendor == yld_device[i].idVendor) &&
-                   (idProduct == yld_device[i].idProduct))
-                       return i;
-       }
-       return -ENODEV;
-}
-
 static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev (intf);
+       struct driver_info *nfo = (struct driver_info *)id->driver_info;
        struct usb_host_interface *interface;
        struct usb_endpoint_descriptor *endpoint;
        struct yealink_dev *yld;
        struct input_dev *input_dev;
        int ret, pipe, i;
 
-       i = usb_match(udev);
-       if (i < 0)
-               return -ENODEV;
-
        interface = intf->cur_altsetting;
        endpoint = &interface->endpoint[0].desc;
        if (!(endpoint->bEndpointAddress & USB_DIR_IN))
@@ -915,7 +907,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
        pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
        ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
        if (ret != USB_PKT_LEN)
-               err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+               err("invalid payload size %d, expected %zd", ret, USB_PKT_LEN);
 
        /* initialise irq urb */
        usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
@@ -948,7 +940,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
        strlcat(yld->phys,  "/input0", sizeof(yld->phys));
 
        /* register settings for the input device */
-       input_dev->name = yld_device[i].name;
+       input_dev->name = nfo->name;
        input_dev->phys = yld->phys;
        usb_to_input_id(udev, &input_dev->id);
        input_dev->cdev.dev = &intf->dev;
index 21232ee2974cb56902b3e59c6ef843da4fac169a..0d3d2cc5d7be7dfd67fb4b4b878abeb97683bbc5 100644 (file)
@@ -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.
-
-
index d83adffa925f6c68f82fdae5cb527069a6bc5784..3957aa1be0f2ed6644acdfb248c80537d9a12cd2 100644 (file)
@@ -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
diff --git a/drivers/usb/media/et61x251.h b/drivers/usb/media/et61x251.h
new file mode 100644 (file)
index 0000000..652238f
--- /dev/null
@@ -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_ */
diff --git a/drivers/usb/media/et61x251_core.c b/drivers/usb/media/et61x251_core.c
new file mode 100644 (file)
index 0000000..2c0171a
--- /dev/null
@@ -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);
diff --git a/drivers/usb/media/et61x251_sensor.h b/drivers/usb/media/et61x251_sensor.h
new file mode 100644 (file)
index 0000000..b9df910
--- /dev/null
@@ -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_ */
diff --git a/drivers/usb/media/et61x251_tas5130d1b.c b/drivers/usb/media/et61x251_tas5130d1b.c
new file mode 100644 (file)
index 0000000..65f1ae9
--- /dev/null
@@ -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;
+}
index 8af665bbe330b40478b7d2588a263ccc8849f739..51e9cc06f7e313ebeda40572114d527399afb5bc 100644 (file)
@@ -204,22 +204,10 @@ MODULE_LICENSE("GPL");
 
 static struct usb_driver ov511_driver;
 
-static struct ov51x_decomp_ops *ov511_decomp_ops;
-static struct ov51x_decomp_ops *ov511_mmx_decomp_ops;
-static struct ov51x_decomp_ops *ov518_decomp_ops;
-static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
-
 /* Number of times to retry a failed I2C transaction. Increase this if you
  * are getting "Failed to read sensor ID..." */
 static const int i2c_detect_tries = 5;
 
-/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
-#if defined(__i386__) || defined(__x86_64__)
-#define ov51x_mmx_available (cpu_has_mmx)
-#else
-#define ov51x_mmx_available (0)
-#endif
-
 static struct usb_device_id device_table [] = {
        { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
        { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
@@ -3012,93 +3000,18 @@ yuv420raw_to_yuv420p(struct ov511_frame *frame,
  *
  **********************************************************************/
 
-/* Chooses a decompression module, locks it, and sets ov->decomp_ops
- * accordingly. Returns -ENXIO if decompressor is not available, otherwise
- * returns 0 if no other error.
- */
 static int
 request_decompressor(struct usb_ov511 *ov)
 {
-       if (!ov)
-               return -ENODEV;
-
-       if (ov->decomp_ops) {
-               err("ERROR: Decompressor already requested!");
-               return -EINVAL;
-       }
-
-       lock_kernel();
-
-       /* Try to get MMX, and fall back on no-MMX if necessary */
-       if (ov->bclass == BCL_OV511) {
-               if (ov511_mmx_decomp_ops) {
-                       PDEBUG(3, "Using OV511 MMX decompressor");
-                       ov->decomp_ops = ov511_mmx_decomp_ops;
-               } else if (ov511_decomp_ops) {
-                       PDEBUG(3, "Using OV511 decompressor");
-                       ov->decomp_ops = ov511_decomp_ops;
-               } else {
-                       err("No decompressor available");
-               }
-       } else if (ov->bclass == BCL_OV518) {
-               if (ov518_mmx_decomp_ops) {
-                       PDEBUG(3, "Using OV518 MMX decompressor");
-                       ov->decomp_ops = ov518_mmx_decomp_ops;
-               } else if (ov518_decomp_ops) {
-                       PDEBUG(3, "Using OV518 decompressor");
-                       ov->decomp_ops = ov518_decomp_ops;
-               } else {
-                       err("No decompressor available");
-               }
+       if (ov->bclass == BCL_OV511 || ov->bclass == BCL_OV518) {
+               err("No decompressor available");
        } else {
                err("Unknown bridge");
        }
 
-       if (!ov->decomp_ops)
-               goto nosys;
-
-       if (!ov->decomp_ops->owner) {
-               ov->decomp_ops = NULL;
-               goto nosys;
-       }
-       
-       if (!try_module_get(ov->decomp_ops->owner))
-               goto nosys;
-
-       unlock_kernel();
-       return 0;
-
- nosys:
-       unlock_kernel();
        return -ENOSYS;
 }
 
-/* Unlocks decompression module and nulls ov->decomp_ops. Safe to call even
- * if ov->decomp_ops is NULL.
- */
-static void
-release_decompressor(struct usb_ov511 *ov)
-{
-       int released = 0;       /* Did we actually do anything? */
-
-       if (!ov)
-               return;
-
-       lock_kernel();
-
-       if (ov->decomp_ops) {
-               module_put(ov->decomp_ops->owner);
-               released = 1;
-       }
-
-       ov->decomp_ops = NULL;
-
-       unlock_kernel();
-
-       if (released)
-               PDEBUG(3, "Decompressor released");
-}
-
 static void
 decompress(struct usb_ov511 *ov, struct ov511_frame *frame,
           unsigned char *pIn0, unsigned char *pOut0)
@@ -3107,31 +3020,6 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame,
                if (request_decompressor(ov))
                        return;
 
-       PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd);
-
-       if (frame->format == VIDEO_PALETTE_GREY
-           && ov->decomp_ops->decomp_400) {
-               int ret = ov->decomp_ops->decomp_400(
-                       pIn0,
-                       pOut0,
-                       frame->compbuf,
-                       frame->rawwidth,
-                       frame->rawheight,
-                       frame->bytes_recvd);
-               PDEBUG(4, "DEBUG: decomp_400 returned %d", ret);
-       } else if (frame->format != VIDEO_PALETTE_GREY
-                  && ov->decomp_ops->decomp_420) {
-               int ret = ov->decomp_ops->decomp_420(
-                       pIn0,
-                       pOut0,
-                       frame->compbuf,
-                       frame->rawwidth,
-                       frame->rawheight,
-                       frame->bytes_recvd);
-               PDEBUG(4, "DEBUG: decomp_420 returned %d", ret);
-       } else {
-               err("Decompressor does not support this format");
-       }
 }
 
 /**********************************************************************
@@ -4087,8 +3975,6 @@ ov51x_v4l1_close(struct inode *inode, struct file *file)
        ov->user--;
        ov51x_stop_isoc(ov);
 
-       release_decompressor(ov);
-
        if (ov->led_policy == LED_AUTO)
                ov51x_led_control(ov, 0);
 
@@ -6021,82 +5907,6 @@ static struct usb_driver ov511_driver = {
  *
  ***************************************************************************/
 
-/* Returns 0 for success */
-int
-ov511_register_decomp_module(int ver, struct ov51x_decomp_ops *ops, int ov518,
-                            int mmx)
-{
-       if (ver != DECOMP_INTERFACE_VER) {
-               err("Decompression module has incompatible");
-               err("interface version %d", ver);
-               err("Interface version %d is required", DECOMP_INTERFACE_VER);
-               return -EINVAL;
-       }
-
-       if (!ops)
-               return -EFAULT;
-
-       if (mmx && !ov51x_mmx_available) {
-               err("MMX not available on this system or kernel");
-               return -EINVAL;
-       }
-
-       lock_kernel();
-
-       if (ov518) {
-               if (mmx) {
-                       if (ov518_mmx_decomp_ops)
-                               goto err_in_use;
-                       else
-                               ov518_mmx_decomp_ops = ops;
-               } else {
-                       if (ov518_decomp_ops)
-                               goto err_in_use;
-                       else
-                               ov518_decomp_ops = ops;
-               }
-       } else {
-               if (mmx) {
-                       if (ov511_mmx_decomp_ops)
-                               goto err_in_use;
-                       else
-                               ov511_mmx_decomp_ops = ops;
-               } else {
-                       if (ov511_decomp_ops)
-                               goto err_in_use;
-                       else
-                               ov511_decomp_ops = ops;
-               }
-       }
-
-       unlock_kernel();
-       return 0;
-
-err_in_use:
-       unlock_kernel();
-       return -EBUSY;
-}
-
-void
-ov511_deregister_decomp_module(int ov518, int mmx)
-{
-       lock_kernel();
-
-       if (ov518) {
-               if (mmx)
-                       ov518_mmx_decomp_ops = NULL;
-               else
-                       ov518_decomp_ops = NULL;
-       } else {
-               if (mmx)
-                       ov511_mmx_decomp_ops = NULL;
-               else
-                       ov511_decomp_ops = NULL;
-       }
-
-       unlock_kernel();
-}
-
 static int __init
 usb_ov511_init(void)
 {
@@ -6123,5 +5933,3 @@ usb_ov511_exit(void)
 module_init(usb_ov511_init);
 module_exit(usb_ov511_exit);
 
-EXPORT_SYMBOL(ov511_register_decomp_module);
-EXPORT_SYMBOL(ov511_deregister_decomp_module);
index 359c4b2df735917ff685920f160a1fa7ee1c7476..3ebb6e9cdf92cee2179fd3d43fc6bbbf3b50b26d 100644 (file)
@@ -1152,45 +1152,6 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
  /* End of Add-Ons                                    */
  /* ************************************************* */
 
-/* Linux 2.5.something and 2.6 pass direct pointers to arguments of
-   ioctl() calls. With 2.4, you have to do tedious copy_from_user()
-   and copy_to_user() calls. With these macros we circumvent this,
-   and let me maintain only one source file. The functionality is
-   exactly the same otherwise.
- */   
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
-
-/* define local variable for arg */
-#define ARG_DEF(ARG_type, ARG_name)\
-       ARG_type *ARG_name = arg;
-/* copy arg to local variable */       
-#define ARG_IN(ARG_name) /* nothing */
-/* argument itself (referenced) */
-#define ARGR(ARG_name) (*ARG_name)
-/* argument address */
-#define ARGA(ARG_name) ARG_name
-/* copy local variable to arg */
-#define ARG_OUT(ARG_name) /* nothing */
-
-#else
-
-#define ARG_DEF(ARG_type, ARG_name)\
-       ARG_type ARG_name;
-#define ARG_IN(ARG_name)\
-       if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\
-               ret = -EFAULT;\
-               break;\
-       }
-#define ARGR(ARG_name) ARG_name
-#define ARGA(ARG_name) &ARG_name
-#define ARG_OUT(ARG_name)\
-       if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\
-               ret = -EFAULT;\
-               break;\
-       }
-
-#endif
 
 int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
 {
@@ -1220,243 +1181,206 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
        
        case VIDIOCPWCSCQUAL:
        {       
-               ARG_DEF(int, qual)
+               int *qual = arg;
 
-               ARG_IN(qual)
-               if (ARGR(qual) < 0 || ARGR(qual) > 3)
+               if (*qual < 0 || *qual > 3)
                        ret = -EINVAL;
                else
-                       ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
+                       ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot);
                if (ret >= 0)
-                       pdev->vcompression = ARGR(qual);
+                       pdev->vcompression = *qual;
                break;
        }
        
        case VIDIOCPWCGCQUAL:
        {
-               ARG_DEF(int, qual)
-               
-               ARGR(qual) = pdev->vcompression;
-               ARG_OUT(qual)
+               int *qual = arg;
+               *qual = pdev->vcompression;
                break;
        }
        
        case VIDIOCPWCPROBE:
        {
-               ARG_DEF(struct pwc_probe, probe)
-               
-               strcpy(ARGR(probe).name, pdev->vdev->name);
-               ARGR(probe).type = pdev->type;
-               ARG_OUT(probe)
+               struct pwc_probe *probe = arg;
+               strcpy(probe->name, pdev->vdev->name);
+               probe->type = pdev->type;
                break;
        }
 
        case VIDIOCPWCGSERIAL:
        {
-               ARG_DEF(struct pwc_serial, serial)
-               
-               strcpy(ARGR(serial).serial, pdev->serial);
-               ARG_OUT(serial)
+               struct pwc_serial *serial = arg;
+               strcpy(serial->serial, pdev->serial);
                break;
        }
 
        case VIDIOCPWCSAGC:
        {
-               ARG_DEF(int, agc)
-
-               ARG_IN(agc)
-               if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc)))
+               int *agc = arg;
+               if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc))
                        ret = -EINVAL;
                break;
        }
        
        case VIDIOCPWCGAGC:
        {
-               ARG_DEF(int, agc)
+               int *agc = arg;
                
-               if (pwc_get_agc(pdev, ARGA(agc)))
+               if (pwc_get_agc(pdev, agc))
                        ret = -EINVAL;
-               ARG_OUT(agc)
                break;
        }
        
        case VIDIOCPWCSSHUTTER:
        {
-               ARG_DEF(int, shutter_speed)
-
-               ARG_IN(shutter_speed)
-               ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed));
+               int *shutter_speed = arg;
+               ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed);
                break;
        }
        
         case VIDIOCPWCSAWB:
        {
-               ARG_DEF(struct pwc_whitebalance, wb)
+               struct pwc_whitebalance *wb = arg;
                
-               ARG_IN(wb)
-               ret = pwc_set_awb(pdev, ARGR(wb).mode);
-               if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) {
-                       pwc_set_red_gain(pdev, ARGR(wb).manual_red);
-                       pwc_set_blue_gain(pdev, ARGR(wb).manual_blue);
+               ret = pwc_set_awb(pdev, wb->mode);
+               if (ret >= 0 && wb->mode == PWC_WB_MANUAL) {
+                       pwc_set_red_gain(pdev, wb->manual_red);
+                       pwc_set_blue_gain(pdev, wb->manual_blue);
                }
                break;
        }
 
        case VIDIOCPWCGAWB:
        {
-               ARG_DEF(struct pwc_whitebalance, wb)
+               struct pwc_whitebalance *wb = arg;
 
-               memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance));
-               ARGR(wb).mode = pwc_get_awb(pdev);
-               if (ARGR(wb).mode < 0)
+               memset(wb, 0, sizeof(struct pwc_whitebalance));
+               wb->mode = pwc_get_awb(pdev);
+               if (wb->mode < 0)
                        ret = -EINVAL;
                else {
-                       if (ARGR(wb).mode == PWC_WB_MANUAL) {
-                               ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red);
+                       if (wb->mode == PWC_WB_MANUAL) {
+                               ret = pwc_get_red_gain(pdev, &wb->manual_red);
                                if (ret < 0)
                                        break;
-                               ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue);
+                               ret = pwc_get_blue_gain(pdev, &wb->manual_blue);
                                if (ret < 0)
                                        break;
                        }
-                       if (ARGR(wb).mode == PWC_WB_AUTO) {
-                               ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
+                       if (wb->mode == PWC_WB_AUTO) {
+                               ret = pwc_read_red_gain(pdev, &wb->read_red);
                                if (ret < 0)
                                        break;
-                               ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
+                               ret = pwc_read_blue_gain(pdev, &wb->read_blue);
                                if (ret < 0)
                                        break;
                        }
                }
-               ARG_OUT(wb)
                break;
        }
        
        case VIDIOCPWCSAWBSPEED:
        {
-               ARG_DEF(struct pwc_wb_speed, wbs)
+               struct pwc_wb_speed *wbs = arg;
                
-               if (ARGR(wbs).control_speed > 0) {
-                       ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed);
+               if (wbs->control_speed > 0) {
+                       ret = pwc_set_wb_speed(pdev, wbs->control_speed);
                }
-               if (ARGR(wbs).control_delay > 0) {
-                       ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay);
+               if (wbs->control_delay > 0) {
+                       ret = pwc_set_wb_delay(pdev, wbs->control_delay);
                }
                break;
        }
        
        case VIDIOCPWCGAWBSPEED:
        {
-               ARG_DEF(struct pwc_wb_speed, wbs)
+               struct pwc_wb_speed *wbs = arg;
                
-               ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed);
+               ret = pwc_get_wb_speed(pdev, &wbs->control_speed);
                if (ret < 0)
                        break;
-               ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay);
+               ret = pwc_get_wb_delay(pdev, &wbs->control_delay);
                if (ret < 0)
                        break;
-               ARG_OUT(wbs)
                break;
        }
 
         case VIDIOCPWCSLED:
        {
-               ARG_DEF(struct pwc_leds, leds)
-
-               ARG_IN(leds)
-               ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off);
+               struct pwc_leds *leds = arg;
+               ret = pwc_set_leds(pdev, leds->led_on, leds->led_off);
                break;
        }
 
 
        case VIDIOCPWCGLED:
        {
-               ARG_DEF(struct pwc_leds, leds)
-               
-               ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off);
-               ARG_OUT(leds)
+               struct pwc_leds *leds = arg;
+               ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off);
                break;
        }
 
        case VIDIOCPWCSCONTOUR:
        {
-               ARG_DEF(int, contour)
-
-               ARG_IN(contour)
-               ret = pwc_set_contour(pdev, ARGR(contour));
+               int *contour = arg;
+               ret = pwc_set_contour(pdev, *contour);
                break;
        }
                        
        case VIDIOCPWCGCONTOUR:
        {
-               ARG_DEF(int, contour)
-               
-               ret = pwc_get_contour(pdev, ARGA(contour));
-               ARG_OUT(contour)
+               int *contour = arg;
+               ret = pwc_get_contour(pdev, contour);
                break;
        }
        
        case VIDIOCPWCSBACKLIGHT:
        {
-               ARG_DEF(int, backlight)
-               
-               ARG_IN(backlight)
-               ret = pwc_set_backlight(pdev, ARGR(backlight));
+               int *backlight = arg;
+               ret = pwc_set_backlight(pdev, *backlight);
                break;
        }
 
        case VIDIOCPWCGBACKLIGHT:
        {
-               ARG_DEF(int, backlight)
-               
-               ret = pwc_get_backlight(pdev, ARGA(backlight));
-               ARG_OUT(backlight)
+               int *backlight = arg;
+               ret = pwc_get_backlight(pdev, backlight);
                break;
        }
        
        case VIDIOCPWCSFLICKER:
        {
-               ARG_DEF(int, flicker)
-               
-               ARG_IN(flicker)
-               ret = pwc_set_flicker(pdev, ARGR(flicker));
+               int *flicker = arg;
+               ret = pwc_set_flicker(pdev, *flicker);
                break;
        }
 
        case VIDIOCPWCGFLICKER:
        {
-               ARG_DEF(int, flicker)
-               
-               ret = pwc_get_flicker(pdev, ARGA(flicker));
-               ARG_OUT(flicker)
+               int *flicker = arg;
+               ret = pwc_get_flicker(pdev, flicker);
                break;
        }
        
        case VIDIOCPWCSDYNNOISE:
        {
-               ARG_DEF(int, dynnoise)
-               
-               ARG_IN(dynnoise)
-               ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise));
+               int *dynnoise = arg;
+               ret = pwc_set_dynamic_noise(pdev, *dynnoise);
                break;
        }
        
        case VIDIOCPWCGDYNNOISE:
        {
-               ARG_DEF(int, dynnoise)
-
-               ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise));
-               ARG_OUT(dynnoise);
+               int *dynnoise = arg;
+               ret = pwc_get_dynamic_noise(pdev, dynnoise);
                break;
        }
 
        case VIDIOCPWCGREALSIZE:
        {
-               ARG_DEF(struct pwc_imagesize, size)
-               
-               ARGR(size).width = pdev->image.x;
-               ARGR(size).height = pdev->image.y;
-               ARG_OUT(size)
+               struct pwc_imagesize *size = arg;
+               size->width = pdev->image.x;
+               size->height = pdev->image.y;
                break;
        }
        
@@ -1464,10 +1388,9 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
        {
                if (pdev->features & FEATURE_MOTOR_PANTILT)
                {
-                       ARG_DEF(int, flags)
+                       int *flags = arg;
 
-                       ARG_IN(flags)
-                       ret = pwc_mpt_reset(pdev, ARGR(flags));
+                       ret = pwc_mpt_reset(pdev, *flags);
                        if (ret >= 0)
                        {
                                pdev->pan_angle = 0;
@@ -1485,10 +1408,8 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
        {
                if (pdev->features & FEATURE_MOTOR_PANTILT)
                {
-                       ARG_DEF(struct pwc_mpt_range, range)
-                       
-                       ARGR(range) = pdev->angle_range;
-                       ARG_OUT(range)
+                       struct pwc_mpt_range *range = arg;
+                       *range = pdev->angle_range;
                }
                else
                {       
@@ -1503,21 +1424,19 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
                
                if (pdev->features & FEATURE_MOTOR_PANTILT)
                {
-                       ARG_DEF(struct pwc_mpt_angles, angles)
-
-                       ARG_IN(angles)
+                       struct pwc_mpt_angles *angles = arg;
                        /* The camera can only set relative angles, so
                           do some calculations when getting an absolute angle .
                         */
-                       if (ARGR(angles).absolute)
+                       if (angles->absolute)
                        {
-                               new_pan  = ARGR(angles).pan; 
-                               new_tilt = ARGR(angles).tilt;
+                               new_pan  = angles->pan;
+                               new_tilt = angles->tilt;
                        }
                        else
                        {
-                               new_pan  = pdev->pan_angle  + ARGR(angles).pan;
-                               new_tilt = pdev->tilt_angle + ARGR(angles).tilt;
+                               new_pan  = pdev->pan_angle  + angles->pan;
+                               new_tilt = pdev->tilt_angle + angles->tilt;
                        }
                        /* check absolute ranges */
                        if (new_pan  < pdev->angle_range.pan_min  ||
@@ -1560,12 +1479,11 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
                
                if (pdev->features & FEATURE_MOTOR_PANTILT)
                {
-                       ARG_DEF(struct pwc_mpt_angles, angles)
+                       struct pwc_mpt_angles *angles = arg;
 
-                       ARGR(angles).absolute = 1;
-                       ARGR(angles).pan  = pdev->pan_angle;
-                       ARGR(angles).tilt = pdev->tilt_angle;
-                       ARG_OUT(angles)
+                       angles->absolute = 1;
+                       angles->pan  = pdev->pan_angle;
+                       angles->tilt = pdev->tilt_angle;
                }
                else
                {
@@ -1578,10 +1496,8 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
        {
                if (pdev->features & FEATURE_MOTOR_PANTILT)
                {
-                       ARG_DEF(struct pwc_mpt_status, status)
-                       
-                       ret = pwc_mpt_get_status(pdev, ARGA(status));
-                       ARG_OUT(status)
+                       struct pwc_mpt_status *status = arg;
+                       ret = pwc_mpt_get_status(pdev, status);
                }
                else
                {
@@ -1592,24 +1508,22 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
 
        case VIDIOCPWCGVIDCMD:
        {
-               ARG_DEF(struct pwc_video_command, cmd);
+               struct pwc_video_command *cmd = arg;
                
-                ARGR(cmd).type = pdev->type;
-               ARGR(cmd).release = pdev->release;
-               ARGR(cmd).command_len = pdev->cmd_len;
-               memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len);
-               ARGR(cmd).bandlength = pdev->vbandlength;
-               ARGR(cmd).frame_size = pdev->frame_size;
-               ARG_OUT(cmd)
+                cmd->type = pdev->type;
+               cmd->release = pdev->release;
+               cmd->command_len = pdev->cmd_len;
+               memcpy(&cmd->command_buf, pdev->cmd_buf, pdev->cmd_len);
+               cmd->bandlength = pdev->vbandlength;
+               cmd->frame_size = pdev->frame_size;
                break;
        }
        /*
        case VIDIOCPWCGVIDTABLE:
        {
-               ARG_DEF(struct pwc_table_init_buffer, table);
-               ARGR(table).len = pdev->cmd_len;
-               memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size);
-               ARG_OUT(table)
+               struct pwc_table_init_buffer *table = arg;
+               table->len = pdev->cmd_len;
+               memcpy(&table->buffer, pdev->decompress_data, pdev->decompressor->table_size);
                break;
        }
        */
index e5cea0e2eb5700195d24e858a66404048af9653f..17d60c1eea7eb6e9713b59b20aadf2712d03014a 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************************
  * V4L2 driver for SN9C10x PC Camera Controllers                           *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
@@ -23,7 +23,8 @@
 
 #include <linux/version.h>
 #include <linux/usb.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
 #include <linux/device.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
 
 /*****************************************************************************/
 
-#define SN9C102_MODULE_NAME     "V4L2 driver for SN9C10x PC Camera Controllers"
-#define SN9C102_MODULE_AUTHOR   "(C) 2004-2005 Luca Risolia"
-#define SN9C102_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
-#define SN9C102_MODULE_LICENSE  "GPL"
-#define SN9C102_MODULE_VERSION  "1:1.24a"
-#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 24)
-
 enum sn9c102_bridge {
        BRIDGE_SN9C101 = 0x01,
        BRIDGE_SN9C102 = 0x02,
@@ -102,12 +96,13 @@ enum sn9c102_stream_state {
        STREAM_ON,
 };
 
+typedef char sn9c103_sof_header_t[18];
 typedef char sn9c102_sof_header_t[12];
 typedef char sn9c102_eof_header_t[4];
 
 struct sn9c102_sysfs_attr {
        u8 reg, i2c_reg;
-       sn9c102_sof_header_t frame_header;
+       sn9c103_sof_header_t frame_header;
 };
 
 struct sn9c102_module_param {
@@ -118,8 +113,6 @@ static DECLARE_MUTEX(sn9c102_sysfs_lock);
 static DECLARE_RWSEM(sn9c102_disconnect);
 
 struct sn9c102_device {
-       struct device dev;
-
        struct video_device* v4ldev;
 
        enum sn9c102_bridge bridge;
@@ -140,8 +133,8 @@ struct sn9c102_device {
        struct v4l2_jpegcompression compression;
 
        struct sn9c102_sysfs_attr sysfs;
-       sn9c102_sof_header_t sof_header;
-       u16 reg[32];
+       sn9c103_sof_header_t sof_header;
+       u16 reg[63];
 
        struct sn9c102_module_param module_param;
 
@@ -160,7 +153,6 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
                       struct sn9c102_sensor* sensor)
 {
        cam->sensor = sensor;
-       cam->sensor->dev = &cam->dev;
        cam->sensor->usbdev = cam->usbdev;
 }
 
@@ -170,19 +162,24 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
 #undef KDBG
 #ifdef SN9C102_DEBUG
 #      define DBG(level, fmt, args...)                                       \
-{                                                                             \
+do {                                                                          \
        if (debug >= (level)) {                                               \
                if ((level) == 1)                                             \
-                       dev_err(&cam->dev, fmt "\n", ## args);                \
+                       dev_err(&cam->usbdev->dev, fmt "\n", ## args);        \
                else if ((level) == 2)                                        \
-                       dev_info(&cam->dev, fmt "\n", ## args);               \
+                       dev_info(&cam->usbdev->dev, fmt "\n", ## args);       \
                else if ((level) >= 3)                                        \
-                       dev_info(&cam->dev, "[%s:%d] " fmt "\n",              \
+                       dev_info(&cam->usbdev->dev, "[%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)
 #      define KDBG(level, fmt, args...)                                      \
-{                                                                             \
+do {                                                                          \
        if (debug >= (level)) {                                               \
                if ((level) == 1 || (level) == 2)                             \
                        pr_info("sn9c102: " fmt "\n", ## args);               \
@@ -190,17 +187,18 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
                        pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__,  \
                                 __LINE__ , ## args);                         \
        }                                                                     \
-}
+} while (0)
 #else
-#      define KDBG(level, fmt, args...) do {;} while(0);
-#      define DBG(level, fmt, args...) do {;} while(0);
+#      define DBG(level, fmt, args...) do {;} while(0)
+#      define V4LDBG(level, name, cmd) do {;} while(0)
+#      define KDBG(level, fmt, args...) do {;} while(0)
 #endif
 
 #undef PDBG
 #define PDBG(fmt, args...)                                                    \
-dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
 
 #undef PDBGG
-#define PDBGG(fmt, args...) do {;} while(0); /* placeholder */
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
 
 #endif /* _SN9C102_H_ */
index 8d1a1c357d5a6d7c01b39fc7b53036a5780f218a..c81397e4714ba42791c11dc9642e1b9634106dea 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************************
  * V4L2 driver for SN9C10x PC Camera Controllers                           *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
 
 /*****************************************************************************/
 
+#define SN9C102_MODULE_NAME     "V4L2 driver for SN9C10x PC Camera Controllers"
+#define SN9C102_MODULE_AUTHOR   "(C) 2004-2006 Luca Risolia"
+#define SN9C102_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
+#define SN9C102_MODULE_LICENSE  "GPL"
+#define SN9C102_MODULE_VERSION  "1:1.26"
+#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 26)
+
+/*****************************************************************************/
+
 MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
 
 MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
@@ -70,10 +79,10 @@ static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
                                SN9C102_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 "
+                 "\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)"
@@ -102,6 +111,9 @@ static sn9c102_sof_header_t sn9c102_sof_header[] = {
        {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
 };
 
+static sn9c103_sof_header_t sn9c103_sof_header[] = {
+       {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20},
+};
 
 static sn9c102_eof_header_t sn9c102_eof_header[] = {
        {0x00, 0x00, 0x00, 0x00},
@@ -112,50 +124,6 @@ static sn9c102_eof_header_t sn9c102_eof_header[] = {
 
 /*****************************************************************************/
 
-static void* rvmalloc(size_t size)
-{
-       void* mem;
-       unsigned long adr;
-
-       size = PAGE_ALIGN(size);
-
-       mem = vmalloc_32((unsigned long)size);
-       if (!mem)
-               return NULL;
-
-       memset(mem, 0, size);
-
-       adr = (unsigned long)mem;
-       while (size > 0) {
-               SetPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-
-       return mem;
-}
-
-
-static void rvfree(void* mem, size_t size)
-{
-       unsigned long adr;
-
-       if (!mem)
-               return;
-
-       size = PAGE_ALIGN(size);
-
-       adr = (unsigned long)mem;
-       while (size > 0) {
-               ClearPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-
-       vfree(mem);
-}
-
-
 static u32 
 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, 
                         enum sn9c102_io_method io)
@@ -174,7 +142,7 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
 
        cam->nbuffers = count;
        while (cam->nbuffers > 0) {
-               if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize))))
+               if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
                        break;
                cam->nbuffers--;
        }
@@ -198,10 +166,10 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
 static void sn9c102_release_buffers(struct sn9c102_device* cam)
 {
        if (cam->nbuffers) {
-               rvfree(cam->frame[0].bufmem,
-                      cam->nbuffers * PAGE_ALIGN(cam->frame[0].buf.length));
+               vfree(cam->frame[0].bufmem);
                cam->nbuffers = 0;
        }
+       cam->frame_current = NULL;
 }
 
 
@@ -219,6 +187,19 @@ static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
 }
 
 
+static void sn9c102_requeue_outqueue(struct sn9c102_device* cam)
+{
+       struct sn9c102_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 sn9c102_queue_unusedframes(struct sn9c102_device* cam)
 {
        unsigned long lock_flags;
@@ -235,19 +216,46 @@ static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
 
 /*****************************************************************************/
 
+int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index)
+{
+       struct usb_device* udev = cam->usbdev;
+       int i, res;
+
+       if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg))
+               return -1;
+
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+                             index, 0, buff, sizeof(buff),
+                             SN9C102_CTRL_TIMEOUT*sizeof(buff));
+       if (res < 0) {
+               DBG(3, "Failed to write registers (index 0x%02X, error %d)",
+                   index, res);
+               return -1;
+       }
+
+       for (i = 0; i < sizeof(buff); i++)
+               cam->reg[index+i] = buff[i];
+
+       return 0;
+}
+
+
 int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
 {
        struct usb_device* udev = cam->usbdev;
        u8* buff = cam->control_buffer;
        int res;
 
+       if (index >= ARRAY_SIZE(cam->reg))
+               return -1;
+
        *buff = value;
 
        res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
                              index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
        if (res < 0) {
                DBG(3, "Failed to write a register (value 0x%02X, index "
-                      "0x%02X, error %d)", value, index, res)
+                      "0x%02X, error %d)", value, index, res);
                return -1;
        }
 
@@ -268,7 +276,7 @@ static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
                              index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
        if (res < 0)
                DBG(3, "Failed to read a register (index 0x%02X, error %d)",
-                   index, res)
+                   index, res);
 
        return (res >= 0) ? (int)(*buff) : -1;
 }
@@ -276,8 +284,8 @@ static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
 
 int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
 {
-       if (index > 0x1f)
-               return -EINVAL;
+       if (index >= ARRAY_SIZE(cam->reg))
+               return -1;
 
        return cam->reg[index];
 }
@@ -367,10 +375,10 @@ sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
        err += sn9c102_i2c_detect_read_error(cam, sensor);
 
        PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
-             data[4])
+             data[4]);
 
        if (err) {
-               DBG(3, "I2C read failed for %s image sensor", sensor->name)
+               DBG(3, "I2C read failed for %s image sensor", sensor->name);
                return -1;
        }
 
@@ -410,11 +418,11 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
        err += sn9c102_i2c_detect_write_error(cam, sensor);
 
        if (err)
-               DBG(3, "I2C write failed for %s image sensor", sensor->name)
+               DBG(3, "I2C write failed for %s image sensor", sensor->name);
 
        PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
              "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
-             n, data0, data1, data2, data3, data4, data5)
+             n, data0, data1, data2, data3, data4, data5);
 
        return err ? -1 : 0;
 }
@@ -461,13 +469,27 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
 static void*
 sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
 {
-       size_t soflen = sizeof(sn9c102_sof_header_t), i;
-       u8 j, n = sizeof(sn9c102_sof_header) / soflen;
+       size_t soflen = 0, i;
+       u8 j, n = 0;
+
+       switch (cam->bridge) {
+       case BRIDGE_SN9C101:
+       case BRIDGE_SN9C102:
+               soflen = sizeof(sn9c102_sof_header_t);
+               n = sizeof(sn9c102_sof_header) / soflen;
+               break;
+       case BRIDGE_SN9C103:
+               soflen = sizeof(sn9c103_sof_header_t);
+               n = sizeof(sn9c103_sof_header) / soflen;
+       }
 
-       for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
+       for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
                for (j = 0; j < n; j++)
-                       /* It's enough to compare 7 bytes */
-                       if (!memcmp(mem + i, sn9c102_sof_header[j], 7)) {
+                       /* The invariable part of the header is 6 bytes long */
+                       if ((cam->bridge != BRIDGE_SN9C103 &&
+                           !memcmp(mem + i, sn9c102_sof_header[j], 6)) ||
+                           (cam->bridge == BRIDGE_SN9C103 &&
+                           !memcmp(mem + i, sn9c103_sof_header[j], 6))) {
                                memcpy(cam->sof_header, mem + i, soflen);
                                /* Skip the header */
                                return mem + i + soflen;
@@ -499,8 +521,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
 {
        struct sn9c102_device* cam = urb->context;
        struct sn9c102_frame_t** f;
-       size_t imagesize;
-       unsigned long lock_flags;
+       size_t imagesize, soflen;
        u8 i;
        int err = 0;
 
@@ -513,7 +534,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
                cam->stream = STREAM_OFF;
                if ((*f))
                        (*f)->state = F_QUEUED;
-               DBG(3, "Stream interrupted")
+               DBG(3, "Stream interrupted");
                wake_up_interruptible(&cam->wait_stream);
        }
 
@@ -536,6 +557,10 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
                     cam->sensor->pix_format.height *
                     cam->sensor->pix_format.priv) / 8;
 
+       soflen = (cam->bridge) == BRIDGE_SN9C103 ?
+                                 sizeof(sn9c103_sof_header_t) :
+                                 sizeof(sn9c102_sof_header_t);
+
        for (i = 0; i < urb->number_of_packets; i++) {
                unsigned int img, len, status;
                void *pos, *sof, *eof;
@@ -545,19 +570,12 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
                pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
 
                if (status) {
-                       DBG(3, "Error in isochronous frame")
+                       DBG(3, "Error in isochronous frame");
                        (*f)->state = F_ERROR;
                        continue;
                }
 
-               PDBGG("Isochrnous frame: length %u, #%u i", len, i)
-
-               /*
-                  NOTE: It is probably correct to assume that SOF and EOF
-                        headers do not occur between two consecutive packets,
-                        but who knows..Whatever is the truth, this assumption
-                        doesn't introduce bugs.
-               */
+               PDBGG("Isochrnous frame: length %u, #%u i", len, i);
 
 redo:
                sof = sn9c102_find_sof_header(cam, pos, len);
@@ -575,10 +593,10 @@ end_of_frame:
                                                imagesize;
                                        img = imagesize - (*f)->buf.bytesused;
                                        DBG(3, "Expected EOF not found: "
-                                              "video frame cut")
+                                              "video frame cut");
                                        if (eof)
                                                DBG(3, "Exceeded limit: +%u "
-                                                      "bytes", (unsigned)(b))
+                                                      "bytes", (unsigned)(b));
                                }
 
                                memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
@@ -595,8 +613,7 @@ end_of_frame:
                                        u32 b = (*f)->buf.bytesused;
                                        (*f)->state = F_DONE;
                                        (*f)->buf.sequence= ++cam->frame_count;
-                                       spin_lock_irqsave(&cam->queue_lock,
-                                                         lock_flags);
+                                       spin_lock(&cam->queue_lock);
                                        list_move_tail(&(*f)->frame,
                                                       &cam->outqueue);
                                        if (!list_empty(&cam->inqueue))
@@ -606,13 +623,11 @@ end_of_frame:
                                                        frame );
                                        else
                                                (*f) = NULL;
-                                       spin_unlock_irqrestore(&cam->queue_lock
-                                                              , lock_flags);
+                                       spin_unlock(&cam->queue_lock);
                                        memcpy(cam->sysfs.frame_header,
-                                              cam->sof_header,
-                                              sizeof(sn9c102_sof_header_t));
-                                       DBG(3, "Video frame captured: "
-                                              "%lu bytes", (unsigned long)(b))
+                                              cam->sof_header, soflen);
+                                       DBG(3, "Video frame captured: %lu "
+                                              "bytes", (unsigned long)(b));
 
                                        if (!(*f))
                                                goto resubmit_urb;
@@ -621,18 +636,19 @@ end_of_frame:
                                        (*f)->state = F_ERROR;
                                        DBG(3, "Not expected EOF after %lu "
                                               "bytes of image data", 
-                                         (unsigned long)((*f)->buf.bytesused))
+                                           (unsigned long)
+                                           ((*f)->buf.bytesused));
                                }
 
                                if (sof) /* (1) */
                                        goto start_of_frame;
 
                        } else if (eof) {
-                               DBG(3, "EOF without SOF")
+                               DBG(3, "EOF without SOF");
                                continue;
 
                        } else {
-                               PDBGG("Ignoring pointless isochronous frame")
+                               PDBGG("Ignoring pointless isochronous frame");
                                continue;
                        }
 
@@ -642,7 +658,7 @@ start_of_frame:
                        (*f)->buf.bytesused = 0;
                        len -= (sof - pos);
                        pos = sof;
-                       DBG(3, "SOF detected: new video frame")
+                       DBG(3, "SOF detected: new video frame");
                        if (len)
                                goto redo;
 
@@ -653,12 +669,13 @@ start_of_frame:
                        else {
                                if (cam->sensor->pix_format.pixelformat ==
                                    V4L2_PIX_FMT_SN9C10X) {
-                                       eof = sof-sizeof(sn9c102_sof_header_t);
+                                       eof = sof - soflen;
                                        goto end_of_frame;
                                } else {
                                        DBG(3, "SOF before expected EOF after "
                                               "%lu bytes of image data", 
-                                         (unsigned long)((*f)->buf.bytesused))
+                                           (unsigned long)
+                                           ((*f)->buf.bytesused));
                                        goto start_of_frame;
                                }
                        }
@@ -670,7 +687,7 @@ resubmit_urb:
        err = usb_submit_urb(urb, GFP_ATOMIC);
        if (err < 0 && err != -EPERM) {
                cam->state |= DEV_MISCONFIGURED;
-               DBG(1, "usb_submit_urb() failed")
+               DBG(1, "usb_submit_urb() failed");
        }
 
        wake_up_interruptible(&cam->wait_frame);
@@ -681,18 +698,22 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
 {
        struct usb_device *udev = cam->usbdev;
        struct urb* urb;
-       const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
-                                              680, 800, 900, 1023};
-       const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
+       const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512,
+                                                      680, 800, 900, 1023};
+       const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512,
+                                                      680, 800, 900, 1003};
+       const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ?
+                           sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] :
+                           sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
        s8 i, j;
        int err = 0;
 
        for (i = 0; i < SN9C102_URBS; i++) {
-               cam->transfer_buffer[i] = kmalloc(SN9C102_ISO_PACKETS * psz,
+               cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz,
                                                  GFP_KERNEL);
                if (!cam->transfer_buffer[i]) {
                        err = -ENOMEM;
-                       DBG(1, "Not enough memory")
+                       DBG(1, "Not enough memory");
                        goto free_buffers;
                }
        }
@@ -702,7 +723,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
                cam->urb[i] = urb;
                if (!urb) {
                        err = -ENOMEM;
-                       DBG(1, "usb_alloc_urb() failed")
+                       DBG(1, "usb_alloc_urb() failed");
                        goto free_urbs;
                }
                urb->dev = udev;
@@ -725,14 +746,14 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
                err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
                if (err) {
                        err = -EIO;
-                       DBG(1, "I/O hardware error")
+                       DBG(1, "I/O hardware error");
                        goto free_urbs;
                }
        }
 
        err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
        if (err) {
-               DBG(1, "usb_set_interface() failed")
+               DBG(1, "usb_set_interface() failed");
                goto free_urbs;
        }
 
@@ -743,7 +764,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
                if (err) {
                        for (j = i-1; j >= 0; j--)
                                usb_kill_urb(cam->urb[j]);
-                       DBG(1, "usb_submit_urb() failed, error %d", err)
+                       DBG(1, "usb_submit_urb() failed, error %d", err);
                        goto free_urbs;
                }
        }
@@ -779,7 +800,7 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
 
        err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
        if (err)
-               DBG(3, "usb_set_interface() failed")
+               DBG(3, "usb_set_interface() failed");
 
        return err;
 }
@@ -799,7 +820,7 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
        else if (err) {
                cam->state |= DEV_MISCONFIGURED;
                DBG(1, "The camera is misconfigured. To use it, close and "
-                      "open /dev/video%d again.", cam->v4ldev->minor)
+                      "open /dev/video%d again.", cam->v4ldev->minor);
                return err;
        }
 
@@ -808,6 +829,7 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
 
 /*****************************************************************************/
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
 static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
 {
        char str[5];
@@ -885,8 +907,8 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
 
        cam->sysfs.reg = index;
 
-       DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg)
-       DBG(3, "Written bytes: %zd", count)
+       DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg);
+       DBG(3, "Written bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -916,7 +938,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
 
        count = sprintf(buf, "%d\n", val);
 
-       DBG(3, "Read bytes: %zd", count)
+       DBG(3, "Read bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -954,8 +976,8 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
        }
 
        DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X",
-           cam->sysfs.reg, value)
-       DBG(3, "Written bytes: %zd", count)
+           cam->sysfs.reg, value);
+       DBG(3, "Written bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -979,7 +1001,7 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
 
        count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
 
-       DBG(3, "Read bytes: %zd", count)
+       DBG(3, "Read bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -1011,8 +1033,8 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
 
        cam->sysfs.i2c_reg = index;
 
-       DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg)
-       DBG(3, "Written bytes: %zd", count)
+       DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
+       DBG(3, "Written bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -1047,7 +1069,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
 
        count = sprintf(buf, "%d\n", val);
 
-       DBG(3, "Read bytes: %zd", count)
+       DBG(3, "Read bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -1090,8 +1112,8 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
        }
 
        DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
-           cam->sysfs.i2c_reg, value)
-       DBG(3, "Written bytes: %zd", count)
+           cam->sysfs.i2c_reg, value);
+       DBG(3, "Written bytes: %zd", count);
 
        up(&sn9c102_sysfs_lock);
 
@@ -1193,7 +1215,7 @@ static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf)
        count = sizeof(cam->sysfs.frame_header);
        memcpy(buf, cam->sysfs.frame_header, count);
 
-       DBG(3, "Frame header, read bytes: %zd", count)
+       DBG(3, "Frame header, read bytes: %zd", count);
 
        return count;
 } 
@@ -1227,11 +1249,12 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
                video_device_create_file(v4ldev, &class_device_attr_blue);
                video_device_create_file(v4ldev, &class_device_attr_red);
        }
-       if (cam->sensor->sysfs_ops) {
+       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 */
 
 /*****************************************************************************/
 
@@ -1281,7 +1304,7 @@ static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
        if (err)
                return -EIO;
 
-       PDBGG("Scaling factor: %u", scale)
+       PDBGG("Scaling factor: %u", scale);
 
        return 0;
 }
@@ -1304,7 +1327,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
                return -EIO;
 
        PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
-             "%u %u %u %u", h_start, v_start, h_size, v_size)
+             "%u %u %u %u", h_start, v_start, h_size, v_size);
 
        return 0;
 }
@@ -1336,7 +1359,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
        if (s->init) {
                err = s->init(cam);
                if (err) {
-                       DBG(3, "Sensor initialization failed")
+                       DBG(3, "Sensor initialization failed");
                        return err;
                }
        }
@@ -1353,13 +1376,13 @@ static int sn9c102_init(struct sn9c102_device* cam)
 
        if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
                DBG(3, "Compressed video format is active, quality %d",
-                   cam->compression.quality)
+                   cam->compression.quality);
        else
-               DBG(3, "Uncompressed video format is active")
+               DBG(3, "Uncompressed video format is active");
 
        if (s->set_crop)
                if ((err = s->set_crop(cam, rect))) {
-                       DBG(3, "set_crop() failed")
+                       DBG(3, "set_crop() failed");
                        return err;
                }
 
@@ -1372,11 +1395,11 @@ static int sn9c102_init(struct sn9c102_device* cam)
                                err = s->set_ctrl(cam, &ctrl);
                                if (err) {
                                        DBG(3, "Set %s control failed",
-                                           s->qctrl[i].name)
+                                           s->qctrl[i].name);
                                        return err;
                                }
                                DBG(3, "Image sensor supports '%s' control",
-                                   s->qctrl[i].name)
+                                   s->qctrl[i].name);
                        }
        }
 
@@ -1392,7 +1415,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
                cam->state |= DEV_INITIALIZED;
        }
 
-       DBG(2, "Initialization succeeded")
+       DBG(2, "Initialization succeeded");
        return 0;
 }
 
@@ -1401,7 +1424,7 @@ static void sn9c102_release_resources(struct sn9c102_device* cam)
 {
        down(&sn9c102_sysfs_lock);
 
-       DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor)
+       DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
        video_set_drvdata(cam->v4ldev, NULL);
        video_unregister_device(cam->v4ldev);
 
@@ -1432,7 +1455,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
        }
 
        if (cam->users) {
-               DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor)
+               DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
                if ((filp->f_flags & O_NONBLOCK) ||
                    (filp->f_flags & O_NDELAY)) {
                        err = -EWOULDBLOCK;
@@ -1458,7 +1481,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
                err = sn9c102_init(cam);
                if (err) {
                        DBG(1, "Initialization failed again. "
-                              "I will retry on next open().")
+                              "I will retry on next open().");
                        goto out;
                }
                cam->state &= ~DEV_MISCONFIGURED;
@@ -1475,7 +1498,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
        cam->frame_count = 0;
        sn9c102_empty_framequeues(cam);
 
-       DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor)
+       DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
 
 out:
        up(&cam->dev_sem);
@@ -1504,7 +1527,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp)
        cam->users--;
        wake_up_interruptible_nr(&cam->open, 1);
 
-       DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor)
+       DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
 
        up(&cam->dev_sem);
 
@@ -1524,32 +1547,38 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
                return -ERESTARTSYS;
 
        if (cam->state & DEV_DISCONNECTED) {
-               DBG(1, "Device not present")
+               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.")
+               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")
+                      "the read method");
                up(&cam->fileop_sem);
                return -EINVAL;
        }
 
        if (cam->io == IO_NONE) {
                if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
-                       DBG(1, "read() failed, not enough memory")
+                       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))
+                       sn9c102_empty_framequeues(cam);
                sn9c102_queue_unusedframes(cam);
        }
 
@@ -1584,6 +1613,16 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
 
        f = list_entry(cam->outqueue.prev, struct sn9c102_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;
@@ -1592,16 +1631,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
 
        sn9c102_queue_unusedframes(cam);
 
-       if (count > f->buf.bytesused)
-               count = f->buf.bytesused;
-
-       if (copy_to_user(buf, f->bufmem, count)) {
-               up(&cam->fileop_sem);
-               return -EFAULT;
-       }
-       *f_pos += count;
-
-       PDBGG("Frame #%lu, bytes read: %zu", (unsigned long)f->buf.index,count)
+       PDBGG("Frame #%lu, bytes read: %zu",
+             (unsigned long)f->buf.index, count);
 
        up(&cam->fileop_sem);
 
@@ -1612,33 +1643,42 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
 static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
 {
        struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+       struct sn9c102_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")
+               DBG(1, "Device not present");
                goto error;
        }
 
        if (cam->state & DEV_MISCONFIGURED) {
-               DBG(1, "The camera is misconfigured. Close and open it again.")
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
                goto error;
        }
 
        if (cam->io == IO_NONE) {
                if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
                                             IO_READ)) {
-                       DBG(1, "poll() failed, not enough memory")
+                       DBG(1, "poll() failed, not enough memory");
                        goto error;
                }
                cam->io = IO_READ;
                cam->stream = STREAM_ON;
        }
 
-       if (cam->io == IO_READ)
+       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);
                sn9c102_queue_unusedframes(cam);
+       }
 
        poll_wait(filp, &cam->wait_frame, wait);
 
@@ -1680,22 +1720,22 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
 {
        struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
        unsigned long size = vma->vm_end - vma->vm_start,
-                     start = vma->vm_start,
-                     pos,
-                     page;
+                     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")
+               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.")
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
                up(&cam->fileop_sem);
                return -EIO;
        }
@@ -1715,15 +1755,12 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
                return -EINVAL;
        }
 
-       /* VM_IO is eventually going to replace PageReserved altogether */
        vma->vm_flags |= VM_IO;
-       vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+       vma->vm_flags |= VM_RESERVED;
 
-       pos = (unsigned long)cam->frame[i].bufmem;
+       pos = cam->frame[i].bufmem;
        while (size > 0) { /* size is page-aligned */
-               page = vmalloc_to_pfn((void *)pos);
-               if (remap_pfn_range(vma, start, page, PAGE_SIZE,
-                                   vma->vm_page_prot)) {
+               if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
                        up(&cam->fileop_sem);
                        return -EAGAIN;
                }
@@ -1742,738 +1779,861 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
        return 0;
 }
 
+/*****************************************************************************/
 
-static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
-                              unsigned int cmd, void __user * arg)
+static int
+sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
 {
-       struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
-
-       switch (cmd) {
-
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability cap = {
-                       .driver = "sn9c102",
-                       .version = SN9C102_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->dev.bus_id,
-                               sizeof(cap.bus_info));
-
-               if (copy_to_user(arg, &cap, sizeof(cap)))
-                       return -EFAULT;
-
-               return 0;
-       }
-
-       case VIDIOC_ENUMINPUT:
-       {
-               struct v4l2_input i;
-
-               if (copy_from_user(&i, arg, sizeof(i)))
-                       return -EFAULT;
+       struct v4l2_capability cap = {
+               .driver = "sn9c102",
+               .version = SN9C102_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;
 
-               if (i.index)
-                       return -EINVAL;
+       return 0;
+}
 
-               memset(&i, 0, sizeof(i));
-               strcpy(i.name, "USB");
 
-               if (copy_to_user(arg, &i, sizeof(i)))
-                       return -EFAULT;
+static int
+sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_input i;
 
-               return 0;
-       }
+       if (copy_from_user(&i, arg, sizeof(i)))
+               return -EFAULT;
 
-       case VIDIOC_G_INPUT:
-       case VIDIOC_S_INPUT:
-       {
-               int index;
+       if (i.index)
+               return -EINVAL;
 
-               if (copy_from_user(&index, arg, sizeof(index)))
-                       return -EFAULT;
+       memset(&i, 0, sizeof(i));
+       strcpy(i.name, "Camera");
 
-               if (index != 0)
-                       return -EINVAL;
+       if (copy_to_user(arg, &i, sizeof(i)))
+               return -EFAULT;
 
-               return 0;
-       }
+       return 0;
+}
 
-       case VIDIOC_QUERYCTRL:
-       {
-               struct sn9c102_sensor* s = cam->sensor;
-               struct v4l2_queryctrl qc;
-               u8 i;
 
-               if (copy_from_user(&qc, arg, sizeof(qc)))
-                       return -EFAULT;
+static int
+sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg)
+{
+       int index;
 
-               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;
-                       }
+       if (copy_from_user(&index, arg, sizeof(index)))
+               return -EFAULT;
 
+       if (index != 0)
                return -EINVAL;
-       }
 
-       case VIDIOC_G_CTRL:
-       {
-               struct sn9c102_sensor* s = cam->sensor;
-               struct v4l2_control ctrl;
-               int err = 0;
+       return 0;
+}
 
-               if (!s->get_ctrl)
-                       return -EINVAL;
 
-               if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
-                       return -EFAULT;
+static int
+sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+       struct sn9c102_sensor* s = cam->sensor;
+       struct v4l2_queryctrl qc;
+       u8 i;
 
-               err = s->get_ctrl(cam, &ctrl);
+       if (copy_from_user(&qc, arg, sizeof(qc)))
+               return -EFAULT;
 
-               if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
-                       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 err;
-       }
+       return -EINVAL;
+}
 
-       case VIDIOC_S_CTRL_OLD:
-       case VIDIOC_S_CTRL:
-       {
-               struct sn9c102_sensor* s = cam->sensor;
-               struct v4l2_control ctrl;
-               u8 i;
-               int err = 0;
 
-               if (!s->set_ctrl)
-                       return -EINVAL;
+static int
+sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+       struct sn9c102_sensor* s = cam->sensor;
+       struct v4l2_control ctrl;
+       int err = 0;
+       u8 i;
 
-               if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
-                       return -EFAULT;
+       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) {
-                               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 (ctrl.id && ctrl.id == s->qctrl[i].id) {
+                               ctrl.value = s->_qctrl[i].default_value;
+                               goto exit;
                        }
+               return -EINVAL;
+       } else
+               err = s->get_ctrl(cam, &ctrl);
 
-               if ((err = s->set_ctrl(cam, &ctrl)))
-                       return err;
+exit:
+       if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+               return -EFAULT;
 
-               s->_qctrl[i].default_value = ctrl.value;
+       return err;
+}
 
-               PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
-                     (unsigned long)ctrl.id, (unsigned long)ctrl.value)
 
-               return 0;
-       }
+static int
+sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+       struct sn9c102_sensor* s = cam->sensor;
+       struct v4l2_control ctrl;
+       u8 i;
+       int err = 0;
 
-       case VIDIOC_CROPCAP:
-       {
-               struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+       if (!s->set_ctrl)
+               return -EINVAL;
 
-               cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               cc->pixelaspect.numerator = 1;
-               cc->pixelaspect.denominator = 1;
+       if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+               return -EFAULT;
 
-               if (copy_to_user(arg, cc, sizeof(*cc)))
-                       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;
+               }
 
-               return 0;
-       }
+       if ((err = s->set_ctrl(cam, &ctrl)))
+               return err;
 
-       case VIDIOC_G_CROP:
-       {
-               struct sn9c102_sensor* s = cam->sensor;
-               struct v4l2_crop crop = {
-                       .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
-               };
+       s->_qctrl[i].default_value = ctrl.value;
 
-               memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+       PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
+             (unsigned long)ctrl.id, (unsigned long)ctrl.value);
 
-               if (copy_to_user(arg, &crop, sizeof(crop)))
-                       return -EFAULT;
+       return 0;
+}
 
-               return 0;
-       }
 
-       case VIDIOC_S_CROP:
-       {
-               struct sn9c102_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 sn9c102_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;
+static int
+sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
 
-               rect = &(crop.c);
+       cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       cc->pixelaspect.numerator = 1;
+       cc->pixelaspect.denominator = 1;
 
-               if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
+       if (copy_to_user(arg, cc, sizeof(*cc)))
+               return -EFAULT;
 
-               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;
-                               }
+       return 0;
+}
 
-               /* 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 < 16)
-                       rect->width = 16;
-               if (rect->height < 16)
-                       rect->height = 16;
-               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 &= ~15L;
-               rect->height &= ~15L;
-
-               if (SN9C102_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 :
-                                       ((a / b) < 16 ? 2 : 4)) : 1;
-               } else
-                       scale = 1;
-
-               if (cam->stream == STREAM_ON)
-                       if ((err = sn9c102_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)
-                       sn9c102_release_buffers(cam);
+static int
+sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
+{
+       struct sn9c102_sensor* s = cam->sensor;
+       struct v4l2_crop crop = {
+               .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+       };
 
-               err = sn9c102_set_crop(cam, rect);
-               if (s->set_crop)
-                       err += s->set_crop(cam, rect);
-               err += sn9c102_set_scale(cam, scale);
+       memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
 
-               if (err) { /* atomic, no rollback in ioctl() */
-                       cam->state |= DEV_MISCONFIGURED;
-                       DBG(1, "VIDIOC_S_CROP failed because of hardware "
-                              "problems. To use the camera, close and open "
-                              "/dev/video%d again.", cam->v4ldev->minor)
-                       return -EIO;
-               }
+       if (copy_to_user(arg, &crop, sizeof(crop)))
+               return -EFAULT;
 
-               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 != sn9c102_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;
-               }
+       return 0;
+}
 
-               cam->stream = stream;
 
-               return 0;
-       }
+static int
+sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
+{
+       struct sn9c102_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 sn9c102_stream_state stream = cam->stream;
+       const u32 nbuffers = cam->nbuffers;
+       u32 i;
+       int err = 0;
 
-       case VIDIOC_ENUM_FMT:
-       {
-               struct v4l2_fmtdesc fmtd;
+       if (copy_from_user(&crop, arg, sizeof(crop)))
+               return -EFAULT;
 
-               if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
-                       return -EFAULT;
+       rect = &(crop.c);
 
-               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_SN9C10X;
-                       fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
-               } else
-                       return -EINVAL;
+       if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
 
-               fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+       if (cam->module_param.force_munmap)
+               for (i = 0; i < cam->nbuffers; i++)
+                       if (cam->frame[i].vma_use_count) {
+                               DBG(3, "VIDIOC_S_CROP failed. "
+                                      "Unmap the buffers first.");
+                               return -EINVAL;
+                       }
 
-               if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
-                       return -EFAULT;
+       /* 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 < 16)
+               rect->width = 16;
+       if (rect->height < 16)
+               rect->height = 16;
+       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 &= ~15L;
+       rect->height &= ~15L;
+
+       if (SN9C102_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 : ((a / b) < 16 ? 2 : 4)) : 1;
+       } else
+               scale = 1;
+
+       if (cam->stream == STREAM_ON)
+               if ((err = sn9c102_stream_interrupt(cam)))
+                       return err;
 
-               return 0;
+       if (copy_to_user(arg, &crop, sizeof(crop))) {
+               cam->stream = stream;
+               return -EFAULT;
        }
 
-       case VIDIOC_G_FMT:
-       {
-               struct v4l2_format format;
-               struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
-
-               if (copy_from_user(&format, arg, sizeof(format)))
-                       return -EFAULT;
+       if (cam->module_param.force_munmap || cam->io == IO_READ)
+               sn9c102_release_buffers(cam);
 
-               if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
+       err = sn9c102_set_crop(cam, rect);
+       if (s->set_crop)
+               err += s->set_crop(cam, rect);
+       err += sn9c102_set_scale(cam, scale);
 
-               pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
-                                    ? 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 (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;
+       }
 
-               if (copy_to_user(arg, &format, sizeof(format)))
-                       return -EFAULT;
+       s->pix_format.width = rect->width/scale;
+       s->pix_format.height = rect->height/scale;
+       memcpy(&(s->_rect), rect, sizeof(*rect));
 
-               return 0;
+       if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+           nbuffers != sn9c102_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;
        }
 
-       case VIDIOC_TRY_FMT:
-       case VIDIOC_S_FMT:
-       {
-               struct sn9c102_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 sn9c102_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;
+       if (cam->io == IO_READ)
+               sn9c102_empty_framequeues(cam);
+       else if (cam->module_param.force_munmap)
+               sn9c102_requeue_outqueue(cam);
 
-               pix = &(format.fmt.pix);
+       cam->stream = stream;
 
-               if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-
-               memcpy(&rect, &(s->_rect), sizeof(rect));
+       return 0;
+}
 
-               { /* 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 :
-                                       ((a / b) < 16 ? 2 : 4)) : 1;
-               }
 
-               rect.width = scale * pix->width;
-               rect.height = scale * pix->height;
-
-               if (rect.width < 16)
-                       rect.width = 16;
-               if (rect.height < 16)
-                       rect.height = 16;
-               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 &= ~15L;
-               rect.height &= ~15L;
-
-               { /* adjust the scaling factor */
-                       u32 a, b;
-                       a = rect.width * rect.height;
-                       b = pix->width * pix->height;
-                       scale = b ? (u8)((a / b) < 4 ? 1 :
-                                       ((a / b) < 16 ? 2 : 4)) : 1;
-               }
+static int
+sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_fmtdesc fmtd;
 
-               pix->width = rect.width / scale;
-               pix->height = rect.height / scale;
-
-               if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
-                   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_SN9C10X)
-                                   ? 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 (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+               return -EFAULT;
 
-               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 (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_SN9C10X;
+               fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+       } else
+               return -EINVAL;
 
-               if (cam->stream == STREAM_ON)
-                       if ((err = sn9c102_stream_interrupt(cam)))
-                               return err;
+       fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
 
-               if (copy_to_user(arg, &format, sizeof(format))) {
-                       cam->stream = stream;
-                       return -EFAULT;
-               }
+       if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+               return -EFAULT;
 
-               if (cam->module_param.force_munmap  || cam->io == IO_READ)
-                       sn9c102_release_buffers(cam);
-
-               err += sn9c102_set_pix_format(cam, pix);
-               err += sn9c102_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 += sn9c102_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;
-               }
+       return 0;
+}
 
-               memcpy(pfmt, pix, sizeof(*pix));
-               memcpy(&(s->_rect), &rect, sizeof(rect));
 
-               if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
-                   nbuffers != sn9c102_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;
-               }
+static int
+sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_format format;
+       struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
 
-               cam->stream = stream;
+       if (copy_from_user(&format, arg, sizeof(format)))
+               return -EFAULT;
 
-               return 0;
-       }
+       if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
 
-       case VIDIOC_G_JPEGCOMP:
-       {
-               if (copy_to_user(arg, &cam->compression,
-                                sizeof(cam->compression)))
-                       return -EFAULT;
+       pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
+                            ? 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));
 
-               return 0;
-       }
+       if (copy_to_user(arg, &format, sizeof(format)))
+               return -EFAULT;
 
-       case VIDIOC_S_JPEGCOMP:
-       {
-               struct v4l2_jpegcompression jc;
-               const enum sn9c102_stream_state stream = cam->stream;
-               int err = 0;
+       return 0;
+}
 
-               if (copy_from_user(&jc, arg, sizeof(jc)))
-                       return -EFAULT;
 
-               if (jc.quality != 0 && jc.quality != 1)
-                       return -EINVAL;
+static int
+sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
+                         void __user * arg)
+{
+       struct sn9c102_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 sn9c102_stream_state stream = cam->stream;
+       const u32 nbuffers = cam->nbuffers;
+       u32 i;
+       int err = 0;
 
-               if (cam->stream == STREAM_ON)
-                       if ((err = sn9c102_stream_interrupt(cam)))
-                               return err;
+       if (copy_from_user(&format, arg, sizeof(format)))
+               return -EFAULT;
 
-               err += sn9c102_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;
-               }
+       pix = &(format.fmt.pix);
 
-               cam->compression.quality = jc.quality;
+       if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
 
-               cam->stream = stream;
+       memcpy(&rect, &(s->_rect), sizeof(rect));
 
-               return 0;
+       { /* 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 : ((a / b) < 16 ? 2 : 4)) : 1;
        }
 
-       case VIDIOC_REQBUFS:
-       {
-               struct v4l2_requestbuffers rb;
-               u32 i;
-               int err;
+       rect.width = scale * pix->width;
+       rect.height = scale * pix->height;
 
-               if (copy_from_user(&rb, arg, sizeof(rb)))
-                       return -EFAULT;
+       if (rect.width < 16)
+               rect.width = 16;
+       if (rect.height < 16)
+               rect.height = 16;
+       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;
 
-               if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-                   rb.memory != V4L2_MEMORY_MMAP)
-                       return -EINVAL;
+       rect.width &= ~15L;
+       rect.height &= ~15L;
 
-               if (cam->io == IO_READ) {
-                       DBG(3, "Close and open the device again to choose "
-                              "the mmap I/O method")
-                       return -EINVAL;
-               }
+       { /* adjust the scaling factor */
+               u32 a, b;
+               a = rect.width * rect.height;
+               b = pix->width * pix->height;
+               scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+       }
+
+       pix->width = rect.width / scale;
+       pix->height = rect.height / scale;
+
+       if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+           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_SN9C10X)
+                           ? 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_REQBUFS failed. "
-                                      "Previous buffers are still mapped.")
+                               DBG(3, "VIDIOC_S_FMT failed. Unmap the "
+                                      "buffers first.");
                                return -EINVAL;
                        }
 
-               if (cam->stream == STREAM_ON)
-                       if ((err = sn9c102_stream_interrupt(cam)))
-                               return err;
+       if (cam->stream == STREAM_ON)
+               if ((err = sn9c102_stream_interrupt(cam)))
+                       return err;
 
-               sn9c102_empty_framequeues(cam);
+       if (copy_to_user(arg, &format, sizeof(format))) {
+               cam->stream = stream;
+               return -EFAULT;
+       }
 
+       if (cam->module_param.force_munmap  || cam->io == IO_READ)
                sn9c102_release_buffers(cam);
-               if (rb.count)
-                       rb.count = sn9c102_request_buffers(cam, rb.count,
-                                                          IO_MMAP);
 
-               if (copy_to_user(arg, &rb, sizeof(rb))) {
-                       sn9c102_release_buffers(cam);
-                       cam->io = IO_NONE;
-                       return -EFAULT;
-               }
+       err += sn9c102_set_pix_format(cam, pix);
+       err += sn9c102_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 += sn9c102_set_scale(cam, scale);
 
-               cam->io = rb.count ? IO_MMAP : IO_NONE;
+       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;
+       }
 
-               return 0;
+       memcpy(pfmt, pix, sizeof(*pix));
+       memcpy(&(s->_rect), &rect, sizeof(rect));
+
+       if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
+           nbuffers != sn9c102_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;
        }
 
-       case VIDIOC_QUERYBUF:
-       {
-               struct v4l2_buffer b;
+       if (cam->io == IO_READ)
+               sn9c102_empty_framequeues(cam);
+       else if (cam->module_param.force_munmap)
+               sn9c102_requeue_outqueue(cam);
 
-               if (copy_from_user(&b, arg, sizeof(b)))
-                       return -EFAULT;
+       cam->stream = stream;
 
-               if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-                   b.index >= cam->nbuffers || cam->io != IO_MMAP)
-                       return -EINVAL;
+       return 0;
+}
 
-               memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
 
-               if (cam->frame[b.index].vma_use_count)
-                       b.flags |= V4L2_BUF_FLAG_MAPPED;
+static int
+sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+{
+       if (copy_to_user(arg, &cam->compression,
+                        sizeof(cam->compression)))
+               return -EFAULT;
 
-               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;
+       return 0;
+}
 
-               if (copy_to_user(arg, &b, sizeof(b)))
-                       return -EFAULT;
 
-               return 0;
+static int
+sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_jpegcompression jc;
+       const enum sn9c102_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 = sn9c102_stream_interrupt(cam)))
+                       return err;
+
+       err += sn9c102_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;
        }
 
-       case VIDIOC_QBUF:
-       {
-               struct v4l2_buffer b;
-               unsigned long lock_flags;
+       cam->compression.quality = jc.quality;
 
-               if (copy_from_user(&b, arg, sizeof(b)))
-                       return -EFAULT;
+       cam->stream = stream;
 
-               if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-                   b.index >= cam->nbuffers || cam->io != IO_MMAP)
-                       return -EINVAL;
+       return 0;
+}
+
+
+static int
+sn9c102_vidioc_reqbufs(struct sn9c102_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->frame[b.index].state != F_UNUSED)
+       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;
+               }
 
-               cam->frame[b.index].state = F_QUEUED;
+       if (cam->stream == STREAM_ON)
+               if ((err = sn9c102_stream_interrupt(cam)))
+                       return err;
 
-               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);
+       sn9c102_empty_framequeues(cam);
 
-               PDBGG("Frame #%lu queued", (unsigned long)b.index)
+       sn9c102_release_buffers(cam);
+       if (rb.count)
+               rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP);
 
-               return 0;
+       if (copy_to_user(arg, &rb, sizeof(rb))) {
+               sn9c102_release_buffers(cam);
+               cam->io = IO_NONE;
+               return -EFAULT;
        }
 
-       case VIDIOC_DQBUF:
-       {
-               struct v4l2_buffer b;
-               struct sn9c102_frame_t *f;
-               unsigned long lock_flags;
-               int err = 0;
+       cam->io = rb.count ? IO_MMAP : IO_NONE;
 
-               if (copy_from_user(&b, arg, sizeof(b)))
-                       return -EFAULT;
+       return 0;
+}
 
-               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;
-               }
+static int
+sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_buffer b;
 
-               spin_lock_irqsave(&cam->queue_lock, lock_flags);
-               f = list_entry(cam->outqueue.next, struct sn9c102_frame_t,
-                              frame);
-               list_del(cam->outqueue.next);
-               spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+       if (copy_from_user(&b, arg, sizeof(b)))
+               return -EFAULT;
 
-               f->state = F_UNUSED;
+       if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           b.index >= cam->nbuffers || cam->io != IO_MMAP)
+               return -EINVAL;
 
-               memcpy(&b, &f->buf, sizeof(b));
-               if (f->vma_use_count)
-                       b.flags |= V4L2_BUF_FLAG_MAPPED;
+       memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
 
-               if (copy_to_user(arg, &b, sizeof(b)))
-                       return -EFAULT;
+       if (cam->frame[b.index].vma_use_count)
+               b.flags |= V4L2_BUF_FLAG_MAPPED;
 
-               PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index)
+       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;
 
-               return 0;
-       }
+       if (copy_to_user(arg, &b, sizeof(b)))
+               return -EFAULT;
 
-       case VIDIOC_STREAMON:
-       {
-               int type;
+       return 0;
+}
 
-               if (copy_from_user(&type, arg, sizeof(type)))
-                       return -EFAULT;
 
-               if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
-                       return -EINVAL;
+static int
+sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
+{
+       struct v4l2_buffer b;
+       unsigned long lock_flags;
 
-               if (list_empty(&cam->inqueue))
-                       return -EINVAL;
+       if (copy_from_user(&b, arg, sizeof(b)))
+               return -EFAULT;
 
-               cam->stream = STREAM_ON;
+       if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           b.index >= cam->nbuffers || cam->io != IO_MMAP)
+               return -EINVAL;
 
-               DBG(3, "Stream on")
+       if (cam->frame[b.index].state != F_UNUSED)
+               return -EINVAL;
 
-               return 0;
-       }
+       cam->frame[b.index].state = F_QUEUED;
 
-       case VIDIOC_STREAMOFF:
-       {
-               int type, err;
+       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);
 
-               if (copy_from_user(&type, arg, sizeof(type)))
-                       return -EFAULT;
+       PDBGG("Frame #%lu queued", (unsigned long)b.index);
 
-               if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
-                       return -EINVAL;
+       return 0;
+}
 
-               if (cam->stream == STREAM_ON)
-                       if ((err = sn9c102_stream_interrupt(cam)))
-                               return err;
 
-               sn9c102_empty_framequeues(cam);
+static int
+sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
+                     void __user * arg)
+{
+       struct v4l2_buffer b;
+       struct sn9c102_frame_t *f;
+       unsigned long lock_flags;
+       int err = 0;
+
+       if (copy_from_user(&b, arg, sizeof(b)))
+               return -EFAULT;
 
-               DBG(3, "Stream off")
+       if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+               return -EINVAL;
 
-               return 0;
+       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;
        }
 
-       case VIDIOC_G_PARM:
-       {
-               struct v4l2_streamparm sp;
+       spin_lock_irqsave(&cam->queue_lock, lock_flags);
+       f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame);
+       list_del(cam->outqueue.next);
+       spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
 
-               if (copy_from_user(&sp, arg, sizeof(sp)))
-                       return -EFAULT;
+       f->state = F_UNUSED;
 
-               if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
+       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
+sn9c102_vidioc_streamon(struct sn9c102_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
+sn9c102_vidioc_streamoff(struct sn9c102_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 = sn9c102_stream_interrupt(cam)))
+                       return err;
+
+       sn9c102_empty_framequeues(cam);
+
+       DBG(3, "Stream off");
+
+       return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_parm(struct sn9c102_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;
 
-               sp.parm.capture.extendedmode = 0;
+       return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_parm(struct sn9c102_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 (copy_to_user(arg, &sp, sizeof(sp)))
-                       return -EFAULT;
+       if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
+               sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
 
-               return 0;
-       }
+       if (copy_to_user(arg, &sp, sizeof(sp)))
+               return -EFAULT;
 
-       case VIDIOC_S_PARM_OLD:
-       case VIDIOC_S_PARM:
-       {
-               struct v4l2_streamparm sp;
+       cam->nreadbuffers = sp.parm.capture.readbuffers;
 
-               if (copy_from_user(&sp, arg, sizeof(sp)))
-                       return -EFAULT;
+       return 0;
+}
 
-               if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
 
-               sp.parm.capture.extendedmode = 0;
+static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
+                              unsigned int cmd, void __user * arg)
+{
+       struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+       switch (cmd) {
 
-               if (sp.parm.capture.readbuffers == 0)
-                       sp.parm.capture.readbuffers = cam->nreadbuffers;
+       case VIDIOC_QUERYCAP:
+               return sn9c102_vidioc_querycap(cam, arg);
 
-               if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
-                       sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
+       case VIDIOC_ENUMINPUT:
+               return sn9c102_vidioc_enuminput(cam, arg);
 
-               if (copy_to_user(arg, &sp, sizeof(sp)))
-                       return -EFAULT;
+       case VIDIOC_G_INPUT:
+       case VIDIOC_S_INPUT:
+               return sn9c102_vidioc_gs_input(cam, arg);
 
-               cam->nreadbuffers = sp.parm.capture.readbuffers;
+       case VIDIOC_QUERYCTRL:
+               return sn9c102_vidioc_query_ctrl(cam, arg);
 
-               return 0;
-       }
+       case VIDIOC_G_CTRL:
+               return sn9c102_vidioc_g_ctrl(cam, arg);
+
+       case VIDIOC_S_CTRL_OLD:
+       case VIDIOC_S_CTRL:
+               return sn9c102_vidioc_s_ctrl(cam, arg);
+
+       case VIDIOC_CROPCAP_OLD:
+       case VIDIOC_CROPCAP:
+               return sn9c102_vidioc_cropcap(cam, arg);
+
+       case VIDIOC_G_CROP:
+               return sn9c102_vidioc_g_crop(cam, arg);
+
+       case VIDIOC_S_CROP:
+               return sn9c102_vidioc_s_crop(cam, arg);
+
+       case VIDIOC_ENUM_FMT:
+               return sn9c102_vidioc_enum_fmt(cam, arg);
+
+       case VIDIOC_G_FMT:
+               return sn9c102_vidioc_g_fmt(cam, arg);
+
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_S_FMT:
+               return sn9c102_vidioc_try_s_fmt(cam, cmd, arg);
+
+       case VIDIOC_G_JPEGCOMP:
+               return sn9c102_vidioc_g_jpegcomp(cam, arg);
+
+       case VIDIOC_S_JPEGCOMP:
+               return sn9c102_vidioc_s_jpegcomp(cam, arg);
+
+       case VIDIOC_REQBUFS:
+               return sn9c102_vidioc_reqbufs(cam, arg);
+
+       case VIDIOC_QUERYBUF:
+               return sn9c102_vidioc_querybuf(cam, arg);
+
+       case VIDIOC_QBUF:
+               return sn9c102_vidioc_qbuf(cam, arg);
+
+       case VIDIOC_DQBUF:
+               return sn9c102_vidioc_dqbuf(cam, filp, arg);
+
+       case VIDIOC_STREAMON:
+               return sn9c102_vidioc_streamon(cam, arg);
+
+       case VIDIOC_STREAMOFF:
+               return sn9c102_vidioc_streamoff(cam, arg);
+
+       case VIDIOC_G_PARM:
+               return sn9c102_vidioc_g_parm(cam, arg);
+
+       case VIDIOC_S_PARM_OLD:
+       case VIDIOC_S_PARM:
+               return sn9c102_vidioc_s_parm(cam, arg);
 
        case VIDIOC_G_STD:
        case VIDIOC_S_STD:
@@ -2499,17 +2659,20 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
                return -ERESTARTSYS;
 
        if (cam->state & DEV_DISCONNECTED) {
-               DBG(1, "Device not present")
+               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.")
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
                up(&cam->fileop_sem);
                return -EIO;
        }
 
+       V4LDBG(3, "sn9c102", cmd);
+
        err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
 
        up(&cam->fileop_sem);
@@ -2517,9 +2680,10 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
        return err;
 }
 
+/*****************************************************************************/
 
 static struct file_operations sn9c102_fops = {
-       .owner =   THIS_MODULE,
+       .owner = THIS_MODULE,
        .open =    sn9c102_open,
        .release = sn9c102_release,
        .ioctl =   sn9c102_ioctl,
@@ -2538,36 +2702,22 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
        struct usb_device *udev = interface_to_usbdev(intf);
        struct sn9c102_device* cam;
        static unsigned int dev_nr = 0;
-       unsigned int i, n;
+       unsigned int i;
        int err = 0, r;
 
-       n = ARRAY_SIZE(sn9c102_id_table);
-       for (i = 0; i < n-1; i++)
-               if (le16_to_cpu(udev->descriptor.idVendor) == 
-                   sn9c102_id_table[i].idVendor &&
-                   le16_to_cpu(udev->descriptor.idProduct) ==
-                   sn9c102_id_table[i].idProduct)
-                       break;
-       if (i == n-1)
-               return -ENODEV;
-
-       if (!(cam = kmalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
+       if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
                return -ENOMEM;
-       memset(cam, 0, sizeof(*cam));
 
        cam->usbdev = udev;
 
-       memcpy(&cam->dev, &udev->dev, sizeof(struct device));
-
-       if (!(cam->control_buffer = kmalloc(8, GFP_KERNEL))) {
-               DBG(1, "kmalloc() failed")
+       if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
+               DBG(1, "kmalloc() failed");
                err = -ENOMEM;
                goto fail;
        }
-       memset(cam->control_buffer, 0, 8);
 
        if (!(cam->v4ldev = video_device_alloc())) {
-               DBG(1, "video_device_alloc() failed")
+               DBG(1, "video_device_alloc() failed");
                err = -ENOMEM;
                goto fail;
        }
@@ -2577,25 +2727,22 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
        r = sn9c102_read_reg(cam, 0x00);
        if (r < 0 || r != 0x10) {
                DBG(1, "Sorry, this is not a SN9C10x based camera "
-                      "(vid/pid 0x%04X/0x%04X)",
-                   sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
+                      "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
                err = -ENODEV;
                goto fail;
        }
 
-       cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ?
+       cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ?
                      BRIDGE_SN9C103 : BRIDGE_SN9C102;
        switch (cam->bridge) {
        case BRIDGE_SN9C101:
        case BRIDGE_SN9C102:
                DBG(2, "SN9C10[12] PC Camera Controller detected "
-                      "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
-                   sn9c102_id_table[i].idProduct)
+                      "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
                break;
        case BRIDGE_SN9C103:
                DBG(2, "SN9C103 PC Camera Controller detected "
-                      "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
-                   sn9c102_id_table[i].idProduct)
+                      "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
                break;
        }
 
@@ -2606,24 +2753,24 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
        }
 
        if (!err && cam->sensor) {
-               DBG(2, "%s image sensor detected", cam->sensor->name)
+               DBG(2, "%s image sensor detected", cam->sensor->name);
                DBG(3, "Support for %s maintained by %s",
-                   cam->sensor->name, cam->sensor->maintainer)
+                   cam->sensor->name, cam->sensor->maintainer);
        } else {
-               DBG(1, "No supported image sensor detected")
+               DBG(1, "No supported image sensor detected");
                err = -ENODEV;
                goto fail;
        }
 
        if (sn9c102_init(cam)) {
-               DBG(1, "Initialization failed. I will retry on open().")
+               DBG(1, "Initialization failed. I will retry on open().");
                cam->state |= DEV_MISCONFIGURED;
        }
 
        strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
        cam->v4ldev->owner = THIS_MODULE;
        cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
-       cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
+       cam->v4ldev->hardware = 0;
        cam->v4ldev->fops = &sn9c102_fops;
        cam->v4ldev->minor = video_nr[dev_nr];
        cam->v4ldev->release = video_device_release;
@@ -2634,23 +2781,25 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
        err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
                                    video_nr[dev_nr]);
        if (err) {
-               DBG(1, "V4L2 device registration failed")
+               DBG(1, "V4L2 device registration failed");
                if (err == -ENFILE && video_nr[dev_nr] == -1)
-                       DBG(1, "Free /dev/videoX node not found")
+                       DBG(1, "Free /dev/videoX node not found");
                video_nr[dev_nr] = -1;
                dev_nr = (dev_nr < SN9C102_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)
+       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 < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
        sn9c102_create_sysfs(cam);
-       DBG(2, "Optional device control through 'sysfs' interface ready")
+       DBG(2, "Optional device control through 'sysfs' interface ready");
+#endif
 
        usb_set_intfdata(intf, cam);
 
@@ -2680,14 +2829,14 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf)
 
        down(&cam->dev_sem); 
 
-       DBG(2, "Disconnecting %s...", cam->v4ldev->name)
+       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->v4ldev->minor);
                cam->state |= DEV_MISCONFIGURED;
                sn9c102_stop_transfer(cam);
                cam->state |= DEV_DISCONNECTED;
@@ -2720,11 +2869,11 @@ static int __init sn9c102_module_init(void)
 {
        int err = 0;
 
-       KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION)
-       KDBG(3, SN9C102_MODULE_AUTHOR)
+       KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION);
+       KDBG(3, SN9C102_MODULE_AUTHOR);
 
        if ((err = usb_register(&sn9c102_usb_driver)))
-               KDBG(1, "usb_register() failed")
+               KDBG(1, "usb_register() failed");
 
        return err;
 }
index 18070d5333cfac6df58fe9812704a767be62fa2e..46c12ec3ca6235c729ca9452fe43a11f25e32fa9 100644 (file)
@@ -2,7 +2,7 @@
  * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera     *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
index 86676abf3547819d29813ea9532838c9d5f43c40..d9aa7a61095d3e5b3833dc7fb47f54360c98cb9f 100644 (file)
@@ -2,7 +2,7 @@
  * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera     *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
index d27c5aedeaf89fd08984c85671e36d99b0184bee..4a36519b5af40f067c05d80c3c1310cd01a4d612 100644 (file)
@@ -2,7 +2,7 @@
  * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera      *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2005 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2005-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    *
@@ -375,8 +375,10 @@ int sn9c102_probe_ov7630(struct sn9c102_device* cam)
 
        sn9c102_attach_sensor(cam, &ov7630);
 
-       if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f &&
-           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c)
+       if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c &&
+           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602d &&
+           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f &&
+           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x60b0)
                return -ENODEV;
 
        err += sn9c102_write_reg(cam, 0x01, 0x01);
index 48e3ec39d4e20a8006edcc014674cd63478aa999..b1dee78abe04ba961da5fc846a7ffde87d9d9adf 100644 (file)
@@ -2,7 +2,7 @@
  * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera     *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
index a45166c3488ca3c3177852e92ebd88ef5d308e3c..7d953b24f2f2a168cc52fe30c06e53c090c35bfc 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************************
  * API for image sensors connected to the SN9C10x PC Camera Controllers    *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
@@ -92,7 +92,18 @@ extern void
 sn9c102_attach_sensor(struct sn9c102_device* cam,
                       struct sn9c102_sensor* sensor);
 
-/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/
+/*
+   Each SN9C10x camera has proper PID/VID identifiers.
+   SN9C103 supports multiple interfaces, but we only handle the video class
+   interface.
+*/
+#define SN9C102_USB_DEVICE(vend, prod, intclass)                              \
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |                           \
+                      USB_DEVICE_ID_MATCH_INT_CLASS,                         \
+       .idVendor = (vend),                                                   \
+       .idProduct = (prod),                                                  \
+       .bInterfaceClass = (intclass)
+
 #define SN9C102_ID_TABLE                                                      \
 static const struct usb_device_id sn9c102_id_table[] = {                      \
        { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */                     \
@@ -107,33 +118,34 @@ static const struct usb_device_id sn9c102_id_table[] = {                      \
        { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */                        \
        { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */                         \
        { USB_DEVICE(0x0c45, 0x602d), },                                      \
+       { USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */                         \
        { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */                          \
-       { USB_DEVICE(0x0c45, 0x6080), },                                      \
-       { USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */              \
-       { USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */                   \
-       { USB_DEVICE(0x0c45, 0x6088), },                                      \
-       { USB_DEVICE(0x0c45, 0x608a), },                                      \
-       { USB_DEVICE(0x0c45, 0x608b), },                                      \
-       { USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */                        \
-       { USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */                       \
-       { USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */                         \
-       { USB_DEVICE(0x0c45, 0x60a0), },                                      \
-       { USB_DEVICE(0x0c45, 0x60a2), },                                      \
-       { USB_DEVICE(0x0c45, 0x60a3), },                                      \
-       { USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */                        \
-       { USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */                     \
-       { USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */                     \
-       { USB_DEVICE(0x0c45, 0x60ac), },                                      \
-       { USB_DEVICE(0x0c45, 0x60ae), },                                      \
-       { USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */                      \
-       { USB_DEVICE(0x0c45, 0x60b0), },                                      \
-       { USB_DEVICE(0x0c45, 0x60b2), },                                      \
-       { USB_DEVICE(0x0c45, 0x60b3), },                                      \
-       { USB_DEVICE(0x0c45, 0x60b8), },                                      \
-       { USB_DEVICE(0x0c45, 0x60ba), },                                      \
-       { USB_DEVICE(0x0c45, 0x60bb), },                                      \
-       { USB_DEVICE(0x0c45, 0x60bc), },                                      \
-       { USB_DEVICE(0x0c45, 0x60be), },                                      \
+       { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */  \
+       { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */     \
+       { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131x */          \
+       { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */         \
+       { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */           \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */          \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */       \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */       \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */       \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), },                        \
+       { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), },                        \
        { }                                                                   \
 };
 
@@ -177,16 +189,18 @@ extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
 extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
 
 /* I/O on registers in the bridge. Could be used by the sensor methods too */
+extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index);
 extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
 extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
 
 /*
    NOTE: there are no exported debugging functions. To uniform the output you
    must use the dev_info()/dev_warn()/dev_err() macros defined in device.h,
-   already included here, the argument being the struct device 'dev' of the
-   sensor structure. Do NOT use these macros before the sensor is attached or
-   the kernel will crash! However, you should not need to notify the user about
-   common errors or other messages, since this is done by the master module.
+   already included here, the argument being the struct device '&usbdev->dev'
+   of the sensor structure. Do NOT use these macros before the sensor is
+   attached or the kernel will crash! However, you should not need to notify
+   the user about common errors or other messages, since this is done by the
+   master module.
 */
 
 /*****************************************************************************/
@@ -345,13 +359,6 @@ struct sn9c102_sensor {
           error code without rolling back.
        */
 
-       const struct device* dev;
-       /*
-          This is the argument for dev_err(), dev_info() and dev_warn(). It
-          is used for debugging purposes. You must not access the struct
-          before the sensor is attached.
-       */
-
        const struct usb_device* usbdev;
        /*
           Points to the usb_device struct after the sensor is attached.
index 8775999b5aff7e6410419958ab0c4f6e04bb861a..32ddf236cafed990b242404473cc3dc41f82f102 100644 (file)
@@ -2,7 +2,7 @@
  * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera  *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
index 927eafdd8c73e1541f93381637bcaf479019b32b..a0728f0ae00cdd72ea5890aab6835176f9e192fb 100644 (file)
@@ -2,7 +2,7 @@
  * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera  *
  * Controllers                                                             *
  *                                                                         *
- * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ * Copyright (C) 2004-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    *
index bff9434c8e5578fcf0648dc6c6e09356e61613aa..9937fc64c8bf3c34a3e7a31b1cedaf0ee22065b3 100644 (file)
@@ -62,7 +62,6 @@ MODULE_LICENSE(W9968CF_MODULE_LICENSE);
 MODULE_SUPPORTED_DEVICE("Video");
 
 static int ovmod_load = W9968CF_OVMOD_LOAD;
-static int vppmod_load = W9968CF_VPPMOD_LOAD;
 static unsigned short simcams = W9968CF_SIMCAMS;
 static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
 static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = 
@@ -107,7 +106,6 @@ static unsigned int param_nv[24]; /* number of values per parameter */
 
 #ifdef CONFIG_KMOD
 module_param(ovmod_load, bool, 0644);
-module_param(vppmod_load, bool, 0444);
 #endif
 module_param(simcams, ushort, 0644);
 module_param_array(video_nr, short, &param_nv[0], 0444);
@@ -150,18 +148,6 @@ MODULE_PARM_DESC(ovmod_load,
                  "\ninto memory."
                  "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"."
                  "\n");
-MODULE_PARM_DESC(vppmod_load, 
-                 "\n<0|1> Automatic 'w9968cf-vpp' module loading."
-                 "\n0 disabled, 1 enabled."
-                 "\nIf enabled, every time an application attempts to open a"
-                 "\ncamera, 'insmod' searches for the video post-processing"
-                 "\nmodule in the system and loads it automatically (if"
-                 "\npresent). The optional 'w9968cf-vpp' module adds extra"
-                 "\n image manipulation functions to the 'w9968cf' module,like"
-                 "\nsoftware up-scaling,colour conversions and video decoding"
-                 "\nfor very high frame rates."
-                 "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"."
-                 "\n");
 #endif
 MODULE_PARM_DESC(simcams, 
                  "\n<n> Number of cameras allowed to stream simultaneously."
@@ -492,10 +478,6 @@ static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num);
 static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**);
 static void w9968cf_release_resources(struct w9968cf_device*);
 
-/* Intermodule communication */
-static int w9968cf_vppmod_detect(struct w9968cf_device*);
-static void w9968cf_vppmod_release(struct w9968cf_device*);
-
 
 
 /****************************************************************************
@@ -2737,9 +2719,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
        cam->streaming = 0;
        cam->misconfigured = 0;
 
-       if (!w9968cf_vpp)
-               if ((err = w9968cf_vppmod_detect(cam)))
-                       goto out;
+       w9968cf_adjust_configuration(cam);
 
        if ((err = w9968cf_allocate_memory(cam)))
                goto deallocate_memory;
@@ -2766,7 +2746,6 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
 
 deallocate_memory:
        w9968cf_deallocate_memory(cam);
-out:
        DBG(2, "Failed to open the video device")
        up(&cam->dev_sem);
        up_read(&w9968cf_disconnect);
@@ -2784,8 +2763,6 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
 
        w9968cf_stop_transfer(cam);
 
-       w9968cf_vppmod_release(cam);
-
        if (cam->disconnected) {
                w9968cf_release_resources(cam);
                up(&cam->dev_sem);
@@ -3681,106 +3658,6 @@ static struct usb_driver w9968cf_usb_driver = {
  * Module init, exit and intermodule communication                          *
  ****************************************************************************/
 
-static int w9968cf_vppmod_detect(struct w9968cf_device* cam)
-{
-       if (!w9968cf_vpp)
-               if (vppmod_load)
-                       request_module("w9968cf-vpp");
-
-       down(&w9968cf_vppmod_lock);
-
-       if (!w9968cf_vpp) {
-               DBG(4, "Video post-processing module not detected")
-               w9968cf_adjust_configuration(cam);
-               goto out;
-       }
-
-       if (!try_module_get(w9968cf_vpp->owner)) {
-               DBG(1, "Couldn't increment the reference count of "
-                      "the video post-processing module")
-               up(&w9968cf_vppmod_lock);
-               return -ENOSYS;
-       }
-
-       w9968cf_vpp->busy++;
-
-       DBG(5, "Video post-processing module detected")
-
-out:
-       up(&w9968cf_vppmod_lock);
-       return 0;
-}
-
-
-static void w9968cf_vppmod_release(struct w9968cf_device* cam)
-{
-       down(&w9968cf_vppmod_lock);
-
-       if (w9968cf_vpp && w9968cf_vpp->busy) {
-               module_put(w9968cf_vpp->owner);
-               w9968cf_vpp->busy--;
-               wake_up(&w9968cf_vppmod_wait);
-               DBG(5, "Video post-processing module released")
-       }
-
-       up(&w9968cf_vppmod_lock);
-}
-
-
-int w9968cf_vppmod_register(struct w9968cf_vpp_t* vpp)
-{
-       down(&w9968cf_vppmod_lock);
-
-       if (w9968cf_vpp) {
-               KDBG(1, "Video post-processing module already registered")
-               up(&w9968cf_vppmod_lock);
-               return -EINVAL;
-       }
-
-       w9968cf_vpp = vpp;
-       w9968cf_vpp->busy = 0;
-
-       KDBG(2, "Video post-processing module registered")
-       up(&w9968cf_vppmod_lock);
-       return 0;
-}
-
-
-int w9968cf_vppmod_deregister(struct w9968cf_vpp_t* vpp)
-{
-       down(&w9968cf_vppmod_lock);
-
-       if (!w9968cf_vpp) {
-               up(&w9968cf_vppmod_lock);
-               return -EINVAL;
-       }
-
-       if (w9968cf_vpp != vpp) {
-               KDBG(1, "Only the owner can unregister the video "
-                       "post-processing module")
-               up(&w9968cf_vppmod_lock);
-               return -EINVAL;
-       }
-
-       if (w9968cf_vpp->busy) {
-               KDBG(2, "Video post-processing module busy. Wait for it to be "
-                       "released...")
-               up(&w9968cf_vppmod_lock);
-               wait_event(w9968cf_vppmod_wait, !w9968cf_vpp->busy);
-               w9968cf_vpp = NULL;
-               goto out;
-       }
-
-       w9968cf_vpp = NULL;
-
-       up(&w9968cf_vppmod_lock);
-
-out:
-       KDBG(2, "Video post-processing module unregistered")
-       return 0;
-}
-
-
 static int __init w9968cf_module_init(void)
 {
        int err;
@@ -3810,6 +3687,3 @@ static void __exit w9968cf_module_exit(void)
 module_init(w9968cf_module_init);
 module_exit(w9968cf_module_exit);
 
-
-EXPORT_SYMBOL(w9968cf_vppmod_register);
-EXPORT_SYMBOL(w9968cf_vppmod_deregister);
index 8acbfe205bc760632397e9d1b32ab057129bb951..47a6ff7941712300dd492b2127b1b8243d47aa63 100644 (file)
@@ -195,7 +195,6 @@ enum w9968cf_vpp_flag {
 };
 
 static struct w9968cf_vpp_t* w9968cf_vpp;
-static DECLARE_MUTEX(w9968cf_vppmod_lock);
 static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait);
 
 static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */
index 3f5317dc4c291522fee8ed8fd56e6749f5840bd3..f3b91b782671f4e6d14e12e8c67d80e2812800ea 100644 (file)
@@ -37,7 +37,4 @@ struct w9968cf_vpp_t {
        u8 busy; /* read-only flag: module is/is not in use */
 };
 
-extern int w9968cf_vppmod_register(struct w9968cf_vpp_t*);
-extern int w9968cf_vppmod_deregister(struct w9968cf_vpp_t*);
-
 #endif /* _W9968CF_VPP_H_ */
index 449b2501acf365ac0b105c0bfd4d15b0fa4bf8d5..ad2f4cccd3889d7a6b55dc973600000deb686cd7 100644 (file)
@@ -2093,6 +2093,8 @@ static void auerswald_disconnect (struct usb_interface *intf)
 static struct usb_device_id auerswald_ids [] = {
        { USB_DEVICE (ID_AUERSWALD, 0x00C0) },          /* COMpact 2104 USB */
        { USB_DEVICE (ID_AUERSWALD, 0x00DB) },          /* COMpact 4410/2206 USB */
+       { USB_DEVICE (ID_AUERSWALD, 0x00DC) }, /* COMpact 4406 DSL */
+       { USB_DEVICE (ID_AUERSWALD, 0x00DD) }, /* COMpact 2204 USB */
        { USB_DEVICE (ID_AUERSWALD, 0x00F1) },          /* Comfort 2000 System Telephone */
        { USB_DEVICE (ID_AUERSWALD, 0x00F2) },          /* Comfort 1200 System Telephone */
         { }                                            /* Terminating entry */
index 981d8a5fbfd9e9dd1a361e6939839d1cc629baaa..331d4ae949ed3ae175f39ecec2df72e2e72af125 100644 (file)
@@ -593,7 +593,7 @@ static struct file_operations ld_usb_fops = {
 
 /*
  * usb class driver info in order to get a minor number from the usb core,
- * and to have the device registered with devfs and the driver core
+ * and to have the device registered with the driver core
  */
 static struct usb_class_driver ld_usb_class = {
        .name =         "ldusb%d",
index 541181695040b865ec73fae31b2c8616c0875d38..3094970615cb9c8ce2aefd5013fc0181b2a230f8 100644 (file)
@@ -916,6 +916,10 @@ static const struct usb_device_id  products [] = {
        // Linksys USB200M Rev 2
        USB_DEVICE (0x13b1, 0x0018),
        .driver_info = (unsigned long) &ax88772_info,
+}, {
+       // 0Q0 cable ethernet
+       USB_DEVICE (0x1557, 0x7720),
+       .driver_info = (unsigned long) &ax88772_info,
 },
        { },            // END
 };
index da46b351e188eed017254eb28dad90fb4d0cb4fa..dc7a069503e04528eeb03dfae7b55237e6ead0eb 100644 (file)
@@ -32,7 +32,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.05"
+#define DRIVER_VERSION "v0.06"
 #define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
 
 /*
@@ -55,11 +55,15 @@ static int debug;
 
 static struct usb_device_id id_table [] = {
        { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
-       { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
-       { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
-       { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
        { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
-       { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+       { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+       { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+       { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
+       { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+       { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
+       { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
+       { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
+       { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
        { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
        { } /* Terminating Entry */
 };
index 10bc1bf23b3514cb9bc50acad7c47f3dc3998583..f2b4ca8692d822c65bdc215a164fadd3d8bf9a73 100644 (file)
@@ -476,10 +476,16 @@ static struct usb_device_id id_table_combined [] = {
        { 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) },
+       { 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 */
 };
index 00d45f8600deeccccfa398bd79606f9168ec6296..ca40f16370f1106bb612e0dd87af0b0401d8c3b9 100644 (file)
 #define FTDI_NF_RIC_VID        0x0DCD  /* Vendor Id */
 #define FTDI_NF_RIC_PID        0x0001  /* Product Id */
 
+
 /* www.irtrans.de device */
 #define FTDI_IRTRANS_PID 0xFC60 /* Product Id */
 
+
+/* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */
+#define FTDI_TTUSB_PID 0xFF20 /* Product Id */
+
 /* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */
 /* they use the ftdi chipset for the USB interface and the vendor id is the same */
 #define FTDI_XF_632_PID 0xFC08 /* 632: 16x2 Character Display */
 /* (the VID is the standard ftdi vid (FTDI_VID) */
 #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 )
  * 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
 #define POSIFLEX_VID           0x0d3a  /* Vendor ID */
 #define POSIFLEX_PP7000_PID    0x0300  /* PP-7000II thermal printer */
 
+/*
+ * Westrex International devices submitted by Cory Lee
+ */
+#define FTDI_WESTREX_MODEL_777_PID     0xDC00  /* Model 777 */
+#define FTDI_WESTREX_MODEL_8900F_PID   0xDC01  /* Model 8900F */
+
 /* Commands */
 #define FTDI_SIO_RESET                 0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL    1 /* Set the modem control register */
index 0eb883f44adaee3bfde6c2879ca63a7e98ae668a..e8e575e037c11352b1d1fc65c51192167ec3c74e 100644 (file)
@@ -73,7 +73,9 @@ static struct usb_device_id id_table [] = {
        { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) },
        { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) },
        { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
-       { USB_DEVICE( NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID ) },
+       { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID ) },
+       { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID ) },
+       { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
        { }                                     /* Terminating entry */
 };
 
index 21d434d81813dde3c207d778ecb7067944092e43..1807087a76e3dbe0f218b73c873b172100ab79d4 100644 (file)
 /* Nokia CA-42 Cable */
 #define NOKIA_CA42_VENDOR_ID   0x078b
 #define NOKIA_CA42_PRODUCT_ID  0x1234
+
+/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
+#define CA_42_CA42_VENDOR_ID   0x10b5
+#define CA_42_CA42_PRODUCT_ID  0xac70
+
+#define SAGEM_VENDOR_ID                0x079b
+#define SAGEM_PRODUCT_ID       0x0027
index 5b06f9240d05caa2b348c7cf55cf520ff2ca2240..ab173b30076eb1b7ac2ec3a2fe257a78863918d5 100644 (file)
 #include "debug.h"
 #include "transport.h"
 
+#define RIO_MSC 0x08
+#define RIOP_INIT "RIOP\x00\x01\x08"
+#define RIOP_INIT_LEN 7
+#define RIO_SEND_LEN 40
+#define RIO_RECV_LEN 0x200
+
 /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
  * mode */
 int usb_stor_euscsi_init(struct us_data *us)
@@ -91,3 +97,70 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
 
        return (res ? -1 : 0);
 }
+
+/* Place the Rio Karma into mass storage mode.
+ *
+ * The initialization begins by sending 40 bytes starting
+ * RIOP\x00\x01\x08\x00, which the device will ack with a 512-byte
+ * packet with the high four bits set and everything else null.
+ *
+ * Next, we send RIOP\x80\x00\x08\x00.  Each time, a 512 byte response
+ * must be read, but we must loop until byte 5 in the response is 0x08,
+ * indicating success.  */
+int rio_karma_init(struct us_data *us)
+{
+       int result, partial;
+       char *recv;
+       unsigned long timeout;
+
+       // us->iobuf is big enough to hold cmd but not receive
+       if (!(recv = kmalloc(RIO_RECV_LEN, GFP_KERNEL)))
+               goto die_nomem;
+
+       US_DEBUGP("Initializing Karma...\n");
+
+       memset(us->iobuf, 0, RIO_SEND_LEN);
+       memcpy(us->iobuf, RIOP_INIT, RIOP_INIT_LEN);
+
+       result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+               us->iobuf, RIO_SEND_LEN, &partial);
+       if (result != USB_STOR_XFER_GOOD)
+               goto die;
+
+       result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+               recv, RIO_RECV_LEN, &partial);
+       if (result != USB_STOR_XFER_GOOD)
+               goto die;
+
+       us->iobuf[4] = 0x80;
+       us->iobuf[5] = 0;
+       timeout = jiffies + msecs_to_jiffies(3000);
+       for (;;) {
+               US_DEBUGP("Sending init command\n");
+               result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+                       us->iobuf, RIO_SEND_LEN, &partial);
+               if (result != USB_STOR_XFER_GOOD)
+                       goto die;
+
+               result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+                       recv, RIO_RECV_LEN, &partial);
+               if (result != USB_STOR_XFER_GOOD)
+                       goto die;
+
+               if (recv[5] == RIO_MSC)
+                       break;
+               if (time_after(jiffies, timeout))
+                       goto die;
+               msleep(10);
+       }
+       US_DEBUGP("Karma initialized.\n");
+       kfree(recv);
+       return 0;
+
+die:
+       kfree(recv);
+die_nomem:
+       US_DEBUGP("Could not initialize karma.\n");
+       return USB_STOR_TRANSPORT_FAILED;
+}
+
index 4c1b2bd2e2e41edcc0378b2a8b8aadf90efe7319..f9907a5cf129f42057d590bd7ca3c6551533599d 100644 (file)
@@ -48,3 +48,4 @@ int usb_stor_euscsi_init(struct us_data *us);
 /* This function is required to activate all four slots on the UCR-61S2B
  * flash reader */
 int usb_stor_ucr61s2b_init(struct us_data *us);
+int rio_karma_init(struct us_data *us);
index b28151d1b609b9ba3554c6625f7662a0c16b2808..b1ec4a718547316c62cf39fa4872ffd7651424f9 100644 (file)
@@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(usb_usual_check_type);
 static int usu_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
 {
-       int type;
+       unsigned long type;
        int rc;
        unsigned long flags;
 
index dc301e567cfc6c6ec5037b7ff26246d72276e09b..ee958f986eb8a37c86422b837906f71d4fd1f508 100644 (file)
@@ -145,6 +145,11 @@ UNUSUAL_DEV(  0x0451, 0x5416, 0x0100, 0x0100,
                US_SC_DEVICE, US_PR_BULK, NULL,
                US_FL_NEED_OVERRIDE ),
 
+UNUSUAL_DEV(  0x045a, 0x5210, 0x0101, 0x0101,
+               "Rio",
+               "Rio Karma",
+               US_SC_SCSI, US_PR_BULK, rio_karma_init, 0),
+
 /* Patch submitted by Philipp Friedrich <philipp@void.at> */
 UNUSUAL_DEV(  0x0482, 0x0100, 0x0100, 0x0100,
                "Kyocera",
@@ -424,11 +429,11 @@ UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0450,
                US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE | US_FL_NO_WP_DETECT ),
 
 /* This entry is needed because the device reports Sub=ff */
-UNUSUAL_DEV(  0x054c, 0x0010, 0x0500, 0x0500, 
-               "Sony",
-               "DSC-T1", 
-               US_SC_8070, US_PR_DEVICE, NULL,
-               US_FL_SINGLE_LUN ),
+UNUSUAL_DEV(  0x054c, 0x0010, 0x0500, 0x0600,
+               "Sony",
+               "DSC-T1/T5",
+               US_SC_8070, US_PR_DEVICE, NULL,
+               US_FL_SINGLE_LUN ),
 
 
 /* Reported by wim@geeks.nl */
index 5d02f16b7d0e7b007720486d7f2722b37f58f85e..4de9fb56ebfc02294636e65b1ca92303286e2b4e 100644 (file)
@@ -234,7 +234,7 @@ static struct file_operations skel_fops = {
 
 /* 
  * usb class driver info in order to get a minor number from the usb core,
- * and to have the device registered with devfs and the driver core
+ * and to have the device registered with the driver core
  */
 static struct usb_class_driver skel_class = {
        .name =         "skel%d",
index 2b972461a030b9f2cfa92ee4aea4a62fa2c8b56e..0ae0a97b0fed05c5ea8129b018d2179eecac87bc 100644 (file)
@@ -1665,7 +1665,6 @@ static int __devinit cyblafb_init(void)
                }
 #endif
        output("CyblaFB version %s initializing\n", VERSION);
-       return pci_module_init(&cyblafb_pci_driver);
        return pci_register_driver(&cyblafb_pci_driver);
 }
 
index 0a44072383ec417647194cc505fb5e4d19447302..fe1a2b02fc558d158182642b979d0f6159aceb18 100644 (file)
@@ -406,7 +406,6 @@ struct pci_dev *pci_find_device_reverse (unsigned int vendor, unsigned int devic
 struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
 int pci_find_capability (struct pci_dev *dev, int cap);
 int pci_find_next_capability (struct pci_dev *dev, u8 pos, int cap);
-int pci_find_ext_capability (struct pci_dev *dev, int cap);
 struct pci_bus * pci_find_next_bus(const struct pci_bus *from);
 
 struct pci_dev *pci_get_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
@@ -626,7 +625,6 @@ static inline int pci_register_driver(struct pci_driver *drv) { return 0;}
 static inline void pci_unregister_driver(struct pci_driver *drv) { }
 static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
 static inline int pci_find_next_capability (struct pci_dev *dev, u8 post, int cap) { return 0; }
-static inline int pci_find_ext_capability (struct pci_dev *dev, int cap) {return 0; }
 static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; }
 
 /* Power management related routines */
index 4401a7e06057a2ae369154bedb0ac6a99d9a8b6f..b0b908f583c58a38a614e6e51f30d71ae43d9508 100644 (file)
 #define PCI_DEVICE_ID_NS_SC1100_SMI    0x0511
 #define PCI_DEVICE_ID_NS_SC1100_XBUS   0x0515
 #define PCI_DEVICE_ID_NS_87410         0xd001
-#define PCI_DEVICE_ID_NS_CS5535_IDE    0x002d
 
 #define PCI_DEVICE_ID_NS_CS5535_HOST_BRIDGE  0x0028
 #define PCI_DEVICE_ID_NS_CS5535_ISA_BRIDGE   0x002b
-#define PCI_DEVICE_ID_NS_CS5535_IDE          0x002d
-#define PCI_DEVICE_ID_NS_CS5535_AUDIO        0x002e
-#define PCI_DEVICE_ID_NS_CS5535_USB          0x002f
-#define PCI_DEVICE_ID_NS_CS5535_VIDEO        0x0030
 
 #define PCI_VENDOR_ID_TSENG            0x100c
 #define PCI_DEVICE_ID_TSENG_W32P_2     0x3202
 #define PCI_DEVICE_ID_AMD_CS5536_UOC    0x2097
 #define PCI_DEVICE_ID_AMD_CS5536_IDE    0x209A
 
-#define PCI_DEVICE_ID_AMD_CS5536_IDE   0x209A
-
 #define PCI_DEVICE_ID_AMD_LX_VIDEO  0x2081
 #define PCI_DEVICE_ID_AMD_LX_AES    0x2082
 
 #define PCI_DEVICE_ID_INTEL_82801EB_5  0x24d5
 #define PCI_DEVICE_ID_INTEL_82801EB_6  0x24d6
 #define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db
+#define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd
 #define PCI_DEVICE_ID_INTEL_ESB_1      0x25a1
 #define PCI_DEVICE_ID_INTEL_ESB_2      0x25a2
 #define PCI_DEVICE_ID_INTEL_ESB_4      0x25a4
 #define PCI_DEVICE_ID_INTEL_ICH7_19    0x27dd
 #define PCI_DEVICE_ID_INTEL_ICH7_20    0x27de
 #define PCI_DEVICE_ID_INTEL_ICH7_21    0x27df
+#define PCI_DEVICE_ID_INTEL_ICH8_0     0x2810
+#define PCI_DEVICE_ID_INTEL_ICH8_1     0x2811
+#define PCI_DEVICE_ID_INTEL_ICH8_2     0x2812
+#define PCI_DEVICE_ID_INTEL_ICH8_3     0x2814
+#define PCI_DEVICE_ID_INTEL_ICH8_4     0x2815
+#define PCI_DEVICE_ID_INTEL_ICH8_5     0x283e
+#define PCI_DEVICE_ID_INTEL_ICH8_6     0x2850
 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340
 #define PCI_DEVICE_ID_INTEL_82830_HB   0x3575
 #define PCI_DEVICE_ID_INTEL_82830_CGC  0x3577
 #define PCI_DEVICE_ID_INTEL_82443GX_2  0x71a2
 #define PCI_DEVICE_ID_INTEL_82372FB_1  0x7601
 #define PCI_DEVICE_ID_INTEL_82454GX    0x84c4
+#define PCI_DEVICE_ID_INTEL_82450GX    0x84c5
 #define PCI_DEVICE_ID_INTEL_82451NX    0x84ca
 #define PCI_DEVICE_ID_INTEL_82454NX     0x84cb
 #define PCI_DEVICE_ID_INTEL_84460GX    0x84ea
index ee21e6bf3867a8bff6d2ac30a32ce6f4743a9536..a2aacfc7af2f968ac6b16cb0fc31845f97dcd4d9 100644 (file)
@@ -535,9 +535,11 @@ enum usb_device_state {
         */
        USB_STATE_NOTATTACHED = 0,
 
-       /* the chapter 9 device states */
+       /* chapter 9 and authentication (wireless) device states */
        USB_STATE_ATTACHED,
-       USB_STATE_POWERED,
+       USB_STATE_POWERED,                      /* wired */
+       USB_STATE_UNAUTHENTICATED,              /* auth */
+       USB_STATE_RECONNECTING,                 /* auth */
        USB_STATE_DEFAULT,                      /* limited function */
        USB_STATE_ADDRESS,
        USB_STATE_CONFIGURED,                   /* most functions */
index ce40675324bd5edb738c339bc56f61b01f1212bf..6f6c69777648f0f72f41a55fc1f79092a15092e1 100644 (file)
@@ -315,6 +315,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
index b963c550dae6b50f069ffd52e38ece70b7360ba7..bdee0502f3e232e32cc2e70c90349ae2ae21e8e8 100644 (file)
@@ -462,7 +462,7 @@ stop_dac(struct au1550_state *s)
        /* Wait for Transmit Busy to show disabled.
        */
        do {
-               stat = readl((void *)PSC_AC97STAT);
+               stat = au_readl(PSC_AC97STAT);
                au_sync();
        } while ((stat & PSC_AC97STAT_TB) != 0);
 
@@ -491,7 +491,7 @@ stop_adc(struct au1550_state *s)
        /* Wait for Receive Busy to show disabled.
        */
        do {
-               stat = readl((void *)PSC_AC97STAT);
+               stat = au_readl(PSC_AC97STAT);
                au_sync();
        } while ((stat & PSC_AC97STAT_RB) != 0);
 
@@ -541,7 +541,7 @@ set_xmit_slots(int num_channels)
        /* Wait for Device ready.
        */
        do {
-               stat = readl((void *)PSC_AC97STAT);
+               stat = au_readl(PSC_AC97STAT);
                au_sync();
        } while ((stat & PSC_AC97STAT_DR) == 0);
 }
@@ -573,7 +573,7 @@ set_recv_slots(int num_channels)
        /* Wait for Device ready.
        */
        do {
-               stat = readl((void *)PSC_AC97STAT);
+               stat = au_readl(PSC_AC97STAT);
                au_sync();
        } while ((stat & PSC_AC97STAT_DR) == 0);
 }
@@ -1995,7 +1995,7 @@ au1550_probe(void)
        /* Wait for PSC ready.
        */
        do {
-               val = readl((void *)PSC_AC97STAT);
+               val = au_readl(PSC_AC97STAT);
                au_sync();
        } while ((val & PSC_AC97STAT_SR) == 0);
 
@@ -2018,7 +2018,7 @@ au1550_probe(void)
        /* Wait for Device ready.
        */
        do {
-               val = readl((void *)PSC_AC97STAT);
+               val = au_readl(PSC_AC97STAT);
                au_sync();
        } while ((val & PSC_AC97STAT_DR) == 0);