ChangeSet 1.1757.66.26, 2004/07/14 15:01:10-07:00, luca.risolia@studio.unibo.it [PATCH] Updates for W99[87]CF and new SN9C10[12] driver This single patch contains some updates and cleanups for the W996[87]CF driver and a new experimental V4L2 driver for SONiX SN9C10[12] PC Camera Controllers connected to various image sensors. I have not divided the patch in two logical sub-patches becouse of two independent changes in one common file, KConfigure. More informations about the SN9C10[12] can be found below in the documentation. The driver is marked as "EXPERIMENTAL", meaning that there are no known bugs, but further testing is necessary before considering it stable. This the first driver using the new SBGGR8 video format, which has been recently added to the mainline kernel, so there are no available user application at the moment: this is one more reason why it should be in the kernel now. Changes in W996[87]CF: - remove w9968cf_externaldef.h now that ovcamchip.h is in the kernel; - mark user pointers with __user in a cleaner way to avoid sparse warnings; - use appropriate exclusive wait macro during open(); - replace info(), err(), warn() with dev_info(), dev_err(), dev_warn(), pr_debug(), pr_info(); - replace usb_unlink_urb() + wait_for_completion() with usb_kill_urb(); - fix memory offsets for buffers in the chip to be used with generic image sensors; - 'vppmod_load', 'debug', 'specific_debug' and 'simcams' module parameters are now writeable by default; - fix possible race conditions between disconnect() and open(); - add automatic 'ovcamchip' module loading option with 'ovmod_load' module parameter; - get rid of deprecated intermodule communication routines and use the correct module registration/unregistration approach; - remove period at the end of kernel messages; - fix several typos; - use MODULE_VERSION() macro; - other small internal cleanups; - documentation updates. Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman drivers/usb/media/w9968cf_externaldef.h | 94 - CREDITS | 2 Documentation/usb/sn9c102.txt | 276 +++ Documentation/usb/w9968cf.txt | 114 - drivers/usb/media/Kconfig | 40 drivers/usb/media/Makefile | 2 drivers/usb/media/sn9c102.h | 182 ++ drivers/usb/media/sn9c102_core.c | 2439 ++++++++++++++++++++++++++++++++ drivers/usb/media/sn9c102_pas106b.c | 209 ++ drivers/usb/media/sn9c102_sensor.h | 270 +++ drivers/usb/media/sn9c102_tas5110c1b.c | 98 + drivers/usb/media/sn9c102_tas5130d1b.c | 120 + drivers/usb/media/w9968cf.c | 974 +++++++----- drivers/usb/media/w9968cf.h | 115 - drivers/usb/media/w9968cf_vpp.h | 43 include/linux/videodev.h | 3 16 files changed, 4349 insertions(+), 632 deletions(-) diff -Nru a/CREDITS b/CREDITS --- a/CREDITS 2004-07-14 16:44:05 -07:00 +++ b/CREDITS 2004-07-14 16:44:05 -07:00 @@ -2702,7 +2702,9 @@ N: Luca Risolia E: luca.risolia@studio.unibo.it +P: 1024D/FCE635A4 88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4 D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips +D: V4L2 driver for SN9C10[12] PC Camera Controllers S: Via Liberta' 41/A S: Osio Sotto, 24046, Bergamo S: Italy diff -Nru a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/usb/sn9c102.txt 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,276 @@ + + SN9C10[12] PC Camera Controllers + Driver for Linux + ================================ + + - Documentation - + + +Index +===== +1. Copyright +2. License +3. Overview +4. Module dependencies +5. Module loading +6. Module parameters +7. Device control through "sysfs" +8. Supported devices +9. How to add support for new image sensors +10. Note for V4L2 developers +11. Contact information +12. Credits + + +1. Copyright +============ +Copyright (C) 2004 by Luca Risolia + +SONiX is a trademark of SONiX Technology Company Limited, inc. +This driver is not sponsored or developed by SONiX. + + +2. License +========== +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +3. Overview +=========== +This driver attempts to support the video streaming capabilities of the devices +mounting the SONiX SN9C101 or SONiX SN9C102 PC Camera Controllers. + +- It's worth to note that SONiX has never collaborated with me during the +development of this project, despite of several requests for enough detailed +specifications of the register tables, compression engine and video data format +of the above chips - + +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 the hotplug facility. + +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 SN9C10[12] driver can be found at the following URL: +http://go.lamarinapunto.com/ + + +4. Module dependencies +====================== +For it to work properly, the driver needs kernel support for Video4Linux and +USB. + +The following options of the kernel configuration file must be enabled and +corresponding modules must be compiled: + + # Multimedia devices + # + CONFIG_VIDEO_DEV=m + + # USB support + # + CONFIG_USB=m + +In addition, depending on the hardware being used, the modules below are +necessary: + + # USB Host Controller Drivers + # + CONFIG_USB_EHCI_HCD=m + CONFIG_USB_UHCI_HCD=m + CONFIG_USB_OHCI_HCD=m + +And finally: + + # USB Multimedia devices + # + CONFIG_USB_SN9C102=m + + +5. Module loading +================= +To use the driver, it is necessary to load the "sn9c102" 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 usbcore + [root@localhost home]# modprobe sn9c102 + +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 + + +6. Module parameters +==================== +Module parameters are listed below: +------------------------------------------------------------------------------- +Name: video_nr +Type: int array (min = 0, max = 32) +Syntax: <-1|n[,...]> +Description: Specify V4L2 minor mode number: + -1 = use next available + n = use minor number n + You can specify up to 32 cameras this way. + For example: + video_nr=-1,2,-1 would assign minor number 2 to the second + recognized camera and use auto for the first one and for every + other camera. +Default: -1 +------------------------------------------------------------------------------- +Name: debug +Type: int +Syntax: +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 just one device + is used. +Default: 2 +------------------------------------------------------------------------------- + + +7. Device control through "sysfs" +================================= +It is possible to read and write both the SN9C10[12] and the image sensor +registers by using the "sysfs" filesystem interface. + +Every time a supported device is recognized, read-only files named "redblue" +and "green" are created in the /sys/class/video4linux/videoX directory. You can +set the red, blue and green channel's gain by writing the desired value to +them. The value may range from 0 to 15 for each channel; this means that +"redblue" accepts 8-bit values, where the low 4 bits are reserved for red and +the others for blue. + +There are other four entries in the directory above for each registered camera: +"reg", "val", "i2c_reg" and "i2c_val". The first two files control the +SN9C10[12] 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. Note +that "i2c_reg" and "i2c_val" won't be created if the sensor does not actually +support the standard I2C protocol. Also, 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 usually is 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 + +Now let's set the green gain's register of the SN9C10[12] chip to 2: + + [root@localhost #] echo 0x11 > reg + [root@localhost #] echo 2 > val + +Note that the SN9C10[12] always returns 0 when some of its registers are read. +To avoid race conditions, all the I/O accesses to the files are serialized. + + +8. Supported devices +==================== +- I won't mention any of the names of the companies as well as their products +here. They have never collaborated with me, 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 SN9C10[12] PC camera controllers: + +Vendor ID Product ID +--------- ---------- +0xc45 0x6001 +0xc45 0x6005 +0xc45 0x6009 +0xc45 0x600d +0xc45 0x6024 +0xc45 0x6025 +0xc45 0x6028 +0xc45 0x6029 +0xc45 0x602a +0xc45 0x602c +0xc45 0x8001 + +The list above does NOT imply that all those devices work with this driver: up +until now only the ones that mount the following image sensors are supported. +Kernel messages will always tell you whether this is the case: + +Model Manufacturer +----- ------------ +PAS106B PixArt Imaging Inc. +TAS5110C1B Taiwan Advanced Sensor Corporation +TAS5130D1B Taiwan Advanced Sensor Corporation + +If you think your camera is based on the above hardware and is not actually +listed in the above table, you may try to add the specific USB VendorID and +ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h"; +then compile, load the module again and look at the kernel output. +If this works, please send an email to me reporting the kernel messages, so +that I will add a new entry in the list of supported devices. + +Donations of new models for further testing and support would be much +appreciated. I won't add official support for hardware that I don't actually +have. + + +9. How to add support for new image sensors +=========================================== +It should be easy to write code for new sensors by using the small API that I +have 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, not yet supported image sensors are: PAS202B (VGA), +HV7131[D|E1] (VGA), MI03 (VGA), OV7620 (VGA). + + +10. Note for V4L2 developers +============================ +This driver follows the V4L2 API specifications. In particular, it enforces two +rules: + +1) 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. + +2) Previously mapped buffer memory must always be unmapped before calling any +of the "VIDIOC_S_CROP", "VIDIOC_TRY_FMT" and "VIDIOC_S_FMT" ioctl's. In case, +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 them again before any I/O attempts. + + +11. Contact information +======================= +I may be contacted by e-mail at . + +I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'. +My public 1024-bit key should be available at any keyserver; the fingerprint +is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. + + +12. Credits +=========== +I would thank the following persons: + +- Stefano Mozzi, who donated 45 EU; +- Luca Capello for the donation of a webcam; +- Mizuno Takafumi for the donation of a webcam. diff -Nru a/Documentation/usb/w9968cf.txt b/Documentation/usb/w9968cf.txt --- a/Documentation/usb/w9968cf.txt 2004-07-14 16:44:05 -07:00 +++ b/Documentation/usb/w9968cf.txt 2004-07-14 16:44:05 -07:00 @@ -23,6 +23,9 @@ ============ Copyright (C) 2002-2004 by Luca Risolia +Winbond is a trademark of Winbond Electronics Corporation. +This driver is not sponsored or developed by Winbond. + 2. License ========== @@ -44,8 +47,8 @@ 3. Overview =========== This driver supports the video streaming capabilities of the devices mounting -Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they -are being commanded by USB. OV681 based cameras should be supported as well. +Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips. OV681 +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 @@ -58,7 +61,8 @@ 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. +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. The latest and full-featured version of the W996[87]CF driver can be found at: http://go.lamarinapunto.com/ . Please refer to the documentation included in @@ -68,38 +72,37 @@ disconnected from the host many times without turning off the computer, if your system supports the hotplug facility. -To change the default settings for each camera, many paramaters can be passed +To change the default settings for each camera, many parameters can be passed through command line when the module is loaded into memory. -The driver relies on the Video4Linux, USB and I2C core modules of the official -Linux kernels. It has been designed to run properly on SMP systems as well. -At the moment, an additional module, "ovcamchip", is mandatory; it provides -support for some OmniVision CMOS sensors connected to the W996[87]CF chips. - -The "ovcamchip" module is part of the OV511 driver, version 2.27, which can be -downloaded from internet: -http://alpha.dyndns.org/ov511/ -To know how to compile it, read the documentation included in the OV511 -package. +The driver relies on the Video4Linux, USB and I2C core modules. It has been +designed to run properly on SMP systems as well. An additional module, +"ovcamchip", is mandatory; it provides support for some OmniVision image +sensors connected to the W996[87]CF chips; if found in the system, the module +will be automatically loaded by default (provided that the kernel has been +compiled with the automatic module loading option). 4. Supported devices ==================== At the moment, known W996[87]CF and OV681 based devices are: -- Aroma Digi Pen ADG-5000 Refurbished -- AVerTV USB -- Creative Labs Video Blaster WebCam Go -- Creative Labs Video Blaster WebCam Go Plus -- Die Lebon LDC-D35A Digital Kamera -- Ezonics EZ-802 EZMega Cam -- OPCOM Digi Pen VGA Dual Mode Pen Camera +- Aroma Digi Pen VGA Dual Mode ADG-5000 (unknown image sensor) +- AVerMedia AVerTV USB (SAA7111A, Philips FI1216Mk2 tuner, PT2313L audio chip) +- Creative Labs Video Blaster WebCam Go (OmniVision OV7610 sensor) +- Creative Labs Video Blaster WebCam Go Plus (OmniVision OV7620 sensor) +- Lebon LDC-035A (unknown image sensor) +- Ezonics EZ-802 EZMega Cam (OmniVision OV8610C sensor) +- OmniVision OV8610-EDE (OmniVision OV8610 sensor) +- OPCOM Digi Pen VGA Dual Mode Pen Camera (unknown image sensor) +- Pretec Digi Pen-II (OmniVision OV7620 sensor) +- Pretec DigiPen-480 (OmniVision OV8610 sensor) If you know any other W996[87]CF or OV681 based cameras, please contact me. The list above does NOT imply that all those devices work with this driver: up -until now only webcams that have a CMOS sensor supported by the "ovcamchip" +until now only webcams that have an image sensor supported by the "ovcamchip" module work. -For a list of supported CMOS sensors, please visit the author's homepage on +For a list of supported image sensors, please visit the author's homepage on this module: http://alpha.dyndns.org/ov511/ Possible external microcontrollers of those webcams are not supported: this @@ -112,8 +115,10 @@ 5. Module dependencies ====================== -For it to work properly, the driver needs kernel support for Video4Linux, -USB and I2C, and a third-party module for the CMOS sensor. +For it to work properly, the driver needs kernel support for Video4Linux, USB +and I2C, and the "ovcamchip" module for the image sensor. Make sure you are not +actually using any external "ovcamchip" module, given that the W996[87]CF +driver depends on the version of the module present in the official kernels. The following options of the kernel configuration file must be enabled and corresponding modules must be compiled: @@ -128,6 +133,10 @@ The I2C core module can be compiled statically in the kernel as well. + # OmniVision Camera Chip support + # + CONFIG_VIDEO_OVCAMCHIP=m + # USB support # CONFIG_USB=m @@ -141,19 +150,12 @@ CONFIG_USB_UHCI_HCD=m CONFIG_USB_OHCI_HCD=m -Also, make sure "Enforce bandwidth allocation" is NOT enabled. - And finally: # USB Multimedia devices # CONFIG_USB_W9968CF=m -The last module we need is "ovcamchip.o". To obtain it, you have to download -the OV511 package, version 2.27 - don't use other versions - and compile it -according to its documentation. -The package is available at http://alpha.dyndns.org/ov511/ . - 6. Module loading ================= @@ -164,11 +166,10 @@ [root@localhost home]# modprobe usbcore [root@localhost home]# modprobe i2c-core - [root@localhost ov511-x.xx]# insmod ./ovcamchip.ko [root@localhost home]# modprobe w9968cf -At this point the devices should be recognized: "dmesg" can be used to analyze -kernel messages: +At this point the pertinent devices should be recognized: "dmesg" can be used +to analyze kernel messages: [user@localhost home]$ dmesg @@ -180,9 +181,22 @@ [root@locahost home]# modinfo w9968cf -7. Module paramaters +7. Module parameters ==================== -Module paramaters are listed below: +Module parameters are listed below: +------------------------------------------------------------------------------- +Name: ovmod_load +Type: bool +Syntax: <0|1> +Description: Automatic 'ovcamchip' module loading: 0 disabled, 1 enabled. + If enabled, 'insmod' searches for the required 'ovcamchip' + module in the system, according to its configuration, and + loads that module automatically. This action is performed as + once soon as the 'w9968cf' module is loaded into memory. +Default: 1 +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 @@ -191,10 +205,14 @@ If enabled, every time an application attempts to open a camera, 'insmod' searches for the video post-processing module in the system and loads it automatically (if present). - The 'w9968cf-vpp' module adds extra image manipulation + The optional 'w9968cf-vpp' module adds extra image manipulation capabilities to the 'w9968cf' module,like software up-scaling, - colour conversions and video decoding. + 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 @@ -237,7 +255,7 @@ Description: Hardware double buffering: 0 disabled, 1 enabled. It should be enabled if you want smooth video output: if you obtain out of sync. video, disable it, or try to - decrease the 'clockdiv' module paramater value. + decrease the 'clockdiv' module parameter value. Default: 1 for every device. ------------------------------------------------------------------------------- Name: clamping @@ -252,7 +270,7 @@ Description: Video filter type. 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter. The filter is used to reduce noise and aliasing artifacts - produced by the CCD or CMOS sensor. + produced by the CCD or CMOS image sensor. Default: 0 for every device. ------------------------------------------------------------------------------- Name: largeview @@ -269,7 +287,7 @@ Disable it if you have a slow CPU or you don't have enough memory. Default: 0 for every device. -Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 0. +Note: If 'w9968cf-vpp' is not present, this parameter is set to 0. ------------------------------------------------------------------------------- Name: decompression Type: int array (min = 0, max = 32) @@ -284,8 +302,8 @@ YUV420P/YUV420 in any resolutions where width and height are multiples of 16. Default: 2 for every device. -Note: If 'w9968cf-vpp' is not loaded, forcing decompression is not - allowed; in this case this paramater is set to 2. +Note: If 'w9968cf-vpp' is not present, forcing decompression is not + allowed; in this case this parameter is set to 2. ------------------------------------------------------------------------------- Name: force_palette Type: int array (min = 0, max = 32) @@ -304,9 +322,9 @@ 3 = RGB565 16 bpp - Software conversion from UYVY 4 = RGB24 24 bpp - Software conversion from UYVY 5 = RGB32 32 bpp - Software conversion from UYVY - When not 0, this paramater will override 'decompression'. + When not 0, this parameter will override 'decompression'. Default: 0 for every device. Initial palette is 9 (UYVY). -Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9. +Note: If 'w9968cf-vpp' is not present, this parameter is set to 9. ------------------------------------------------------------------------------- Name: force_rgb Type: bool array (min = 0, max = 32) @@ -320,14 +338,14 @@ Name: autobright Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> -Description: CMOS sensor automatically changes brightness: +Description: Image sensor automatically changes brightness: 0 = no, 1 = yes Default: 0 for every device. ------------------------------------------------------------------------------- Name: autoexp Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> -Description: CMOS sensor automatically changes exposure: +Description: Image sensor automatically changes exposure: 0 = no, 1 = yes Default: 1 for every device. ------------------------------------------------------------------------------- @@ -354,7 +372,7 @@ Description: Force pixel clock divisor to a specific value (for experts): n may vary from 0 to 127. -1 for automatic value. - See also the 'double_buffer' module paramater. + See also the 'double_buffer' module parameter. Default: -1 for every device. ------------------------------------------------------------------------------- Name: backlight @@ -374,7 +392,7 @@ Name: monochrome Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> -Description: The CMOS sensor is monochrome: +Description: The image sensor is monochrome: 0 = no, 1 = yes Default: 0 for every device. ------------------------------------------------------------------------------- @@ -420,7 +438,7 @@ 4 = warnings 5 = called functions 6 = function internals - Level 5 and 6 are useful for testing only, when just one + Level 5 and 6 are useful for testing only, when only one device is used. Default: 2 ------------------------------------------------------------------------------- @@ -449,7 +467,7 @@ the source code of other drivers and without the help of several persons; in particular: -- the I2C interface to kernel and high-level CMOS sensor control routines have +- the I2C interface to kernel and high-level image sensor control routines have been taken from the OV511 driver by Mark McClelland; - memory management code has been copied from the bttv driver by Ralph Metzler, diff -Nru a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig --- a/drivers/usb/media/Kconfig 2004-07-14 16:44:05 -07:00 +++ b/drivers/usb/media/Kconfig 2004-07-14 16:44:05 -07:00 @@ -162,6 +162,21 @@ To compile this driver as a module, choose M here: the module will be called se401. +config USB_SN9C102 + tristate "USB SN9C10[12] PC Camera Controller support (EXPERIMENTAL)" + depends on USB && VIDEO_DEV && EXPERIMENTAL + ---help--- + Say Y here if you want support for cameras based on SN9C101 and + SN9C102 PC Camera Controllers. + + See 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 sn9c102. + config USB_STV680 tristate "USB STV680 (Pencam) Camera support" depends on USB && VIDEO_DEV @@ -181,7 +196,7 @@ config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support" - depends on USB && VIDEO_DEV && I2C + depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP ---help--- Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. @@ -190,16 +205,13 @@ separate module only (released under GPL). It allows to use higher resolutions and framerates, but cannot be included in the official Linux kernel for performance purposes. - At the moment the driver needs a third-party module for the CMOS - sensors, which is available on internet: it is recommended to read - for more informations and for - a list of supported cameras. - - This driver uses the Video For Linux and the I2C APIs. You must say - Y or M to both "Video For Linux" and "I2C Support" to use this - driver. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called w9968cf.o. If you want to compile it as a - module, say M here and read . + + See for more informations. + + This driver uses the Video For Linux and the I2C APIs. It needs the + OmniVision Camera Chip support as well. You must say Y or M to + "Video For Linux", "I2C Support" and "OmniVision Camera Chip + support" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called w9968cf. diff -Nru a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile --- a/drivers/usb/media/Makefile 2004-07-14 16:44:05 -07:00 +++ b/drivers/usb/media/Makefile 2004-07-14 16:44:05 -07:00 @@ -3,6 +3,7 @@ # pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o +sn9c102-objs := sn9c102_core.o sn9c102_pas106b.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_DSBR) += dsbr100.o @@ -11,6 +12,7 @@ obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_PWC) += pwc.o obj-$(CONFIG_USB_SE401) += se401.o +obj-$(CONFIG_USB_SN9C102) += sn9c102.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o obj-$(CONFIG_USB_W9968CF) += w9968cf.o diff -Nru a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/sn9c102.h 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,182 @@ +/*************************************************************************** + * V4L2 driver for SN9C10[12] PC Camera Controllers * + * * + * Copyright (C) 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_H_ +#define _SN9C102_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102_sensor.h" + +/*****************************************************************************/ + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_CTRL_TIMEOUT 10*HZ + +/*****************************************************************************/ + +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers" +#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" +#define SN9C102_AUTHOR_EMAIL "" +#define SN9C102_MODULE_LICENSE "GPL" +#define SN9C102_MODULE_VERSION "1:1.01-beta" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) + +SN9C102_ID_TABLE; +SN9C102_SENSOR_TABLE; + +enum sn9c102_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct sn9c102_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum sn9c102_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum sn9c102_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum sn9c102_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum sn9c102_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct sn9c102_sysfs_attr { + u8 reg, val, i2c_reg, i2c_val; +}; + +static DECLARE_MUTEX(sn9c102_sysfs_lock); +static DECLARE_RWSEM(sn9c102_disconnect); + +struct sn9c102_device { + struct device dev; + + struct video_device* v4ldev; + + struct sn9c102_sensor* sensor; + + struct usb_device* usbdev; + struct urb* urb[SN9C102_URBS]; + void* transfer_buffer[SN9C102_URBS]; + u8* control_buffer; + + struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers; + + enum sn9c102_io_method io; + enum sn9c102_stream_state stream; + + struct sn9c102_sysfs_attr sysfs; + u16 reg[32]; + + enum sn9c102_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 +sn9c102_attach_sensor(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + cam->sensor = sensor; + cam->sensor->dev = &cam->dev; + cam->sensor->usbdev = cam->usbdev; +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef SN9C102_DEBUG +# define DBG(level, fmt, args...) \ +{ \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} +# define KDBG(level, fmt, args...) \ +{ \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("sn9c102: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} +#else +# define KDBG(level, fmt, args...) do {;} while(0); +# define DBG(level, fmt, args...) 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 /* _SN9C102_H_ */ diff -Nru a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/sn9c102_core.c 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,2439 @@ +/*************************************************************************** + * V4L2 driver for SN9C10[12] PC Camera Controllers * + * * + * Copyright (C) 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102.h" + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, sn9c102_id_table); + +MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); +MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_VERSION(SN9C102_MODULE_VERSION); +MODULE_LICENSE(SN9C102_MODULE_LICENSE); + +static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; +static unsigned int nv; +module_param_array(video_nr, short, nv, 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(SN9C102_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); + +#ifdef SN9C102_DEBUG +static unsigned short debug = SN9C102_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\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(SN9C102_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +typedef char sn9c102_sof_header_t[7]; +typedef char sn9c102_eof_header_t[4]; + +static sn9c102_sof_header_t sn9c102_sof_header[] = { + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00}, + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, +}; + +/* Number of random bytes that complete the SOF above headers */ +#define SN9C102_SOFLEN 5 + +static sn9c102_eof_header_t sn9c102_eof_header[] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, +}; + +/*****************************************************************************/ + +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); + ret = __pa(kva); + return ret; +} + + +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) +{ + struct v4l2_pix_format* p = &(cam->sensor->pix_format); + const size_t imagesize = (p->width * p->height * p->priv)/8; + void* buff = NULL; + u32 i; + + if (count > SN9C102_MAX_FRAMES) + count = SN9C102_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = rvmalloc(cam->nbuffers * imagesize))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*imagesize; + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*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 sn9c102_release_buffers(struct sn9c102_device* cam) +{ + if (cam->nbuffers) { + rvfree(cam->frame[0].bufmem, + cam->nbuffers * cam->frame[0].buf.length); + cam->nbuffers = 0; + } +} + + +static void sn9c102_empty_framequeues(struct sn9c102_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < SN9C102_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void sn9c102_queue_unusedframes(struct sn9c102_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 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 == 0x18) + value = (value & 0xcf) | (cam->reg[0x18] & 0x30); + + *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) + return -1; + } + + cam->reg[index] = value; + + return 0; +} + + +/* NOTE: reading some registers always returns 0 */ +static int sn9c102_read_reg(struct sn9c102_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, + index, 0, buff, 1, SN9C102_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; +} + + +int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) +{ + if (index > 0x1f) + return -EINVAL; + + return cam->reg[index]; +} + + +static int +sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 5; i++) { + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + return -EIO; + if (r & 0x04) + return 0; + if (sensor->frequency & SN9C102_I2C_400KHZ) + udelay(5*8); + else + udelay(16*8); + } + return -EBUSY; +} + + +static int +sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0; +} + + +static int +sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; +} + + +int +sn9c102_i2c_try_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 address) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle - address */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; + data[1] = sensor->slave_write_id; + data[2] = address; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* Read cycle - 1 byte */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | + 0x10 | 0x02; + data[1] = sensor->slave_read_id; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* The read byte will be placed in data[4] */ + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_detect_read_error(cam, sensor); + + if (err) + DBG(3, "I2C read failed for %s image sensor", sensor->name) + + PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[4]) + + return err ? -1 : (int)data[4]; +} + + +int +sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 n, u8 data0, + u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle. It usually is address + value */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) + | ((n - 1) << 4); + data[1] = data0; + data[2] = data1; + data[3] = data2; + data[4] = data3; + data[5] = data4; + data[6] = data5; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + err += sn9c102_i2c_detect_write_error(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name) + + PDBGG("I2C 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) + + return err ? -1 : 0; +} + + +int +sn9c102_i2c_try_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 address, u8 value) +{ + return sn9c102_i2c_try_raw_write(cam, sensor, 3, + sensor->slave_write_id, address, + value, 0, 0, 0); +} + + +int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) +{ + if (!cam->sensor) + return -1; + + return sn9c102_i2c_try_read(cam, cam->sensor, address); +} + + +int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) +{ + if (!cam->sensor) + return -1; + + return sn9c102_i2c_try_write(cam, cam->sensor, address, value); +} + +/*****************************************************************************/ + +static void* sn9c102_find_sof_header(void* mem, size_t len) +{ + size_t soflen=sizeof(sn9c102_sof_header_t), SOFLEN=SN9C102_SOFLEN, i; + u8 j, n = sizeof(sn9c102_sof_header) / soflen; + + for (i = 0; (len >= soflen+SOFLEN) && (i <= len-soflen-SOFLEN); i++) + for (j = 0; j < n; j++) + if (!memcmp(mem + i, sn9c102_sof_header[j], soflen)) + /* Skips the header */ + return mem + i + soflen + SOFLEN; + + return NULL; +} + + +static void* sn9c102_find_eof_header(void* mem, size_t len) +{ + size_t eoflen = sizeof(sn9c102_eof_header_t), i; + unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; + + for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++) + for (j = 0; j < n; j++) + if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen)) + return mem + i; + + return NULL; +} + + +static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct sn9c102_device* cam = urb->context; + struct sn9c102_frame_t** f; + unsigned long lock_flags; + 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)||(cam->state & DEV_MISCONFIGURED)) + return; + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, + frame); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int img, len, status; + void *pos, *sof, *eof; + + 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; + } + + 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. */ + +redo: + sof = sn9c102_find_sof_header(pos, len); + if (!sof) { + eof = sn9c102_find_eof_header(pos, len); + if ((*f)->state == F_GRABBING) { +end_of_frame: + img = len; + + if (eof) + img = (eof > pos) ? eof - pos - 1 : 0; + + if ((*f)->buf.bytesused+img>(*f)->buf.length) { + u32 b = (*f)->buf.bytesused + img - + (*f)->buf.length; + img = (*f)->buf.length - + (*f)->buf.bytesused; + DBG(3, "Expected EOF not found: " + "video frame cut") + if (eof) + DBG(3, "Exceeded limit: +%u " + "bytes", (unsigned)(b)) + } + + memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, + img); + + if ((*f)->buf.bytesused == 0) + do_gettimeofday(&(*f)->buf.timestamp); + + (*f)->buf.bytesused += img; + + if ((*f)->buf.bytesused == (*f)->buf.length) { + u32 b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock_irqsave(&cam->queue_lock, + lock_flags); + list_move_tail(&(*f)->frame, + &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry( + cam->inqueue.next, + struct sn9c102_frame_t, + frame ); + else + (*f) = NULL; + spin_unlock_irqrestore(&cam->queue_lock + , lock_flags); + DBG(3, "Video frame captured: " + "%lu bytes", (unsigned long)(b)) + + if (!(*f)) + goto resubmit_urb; + + } else if (eof) { + (*f)->state = F_ERROR; + DBG(3, "Not expected EOF after %lu " + "bytes of image data", + (unsigned long)((*f)->buf.bytesused)) + } + + if (sof) /* (1) */ + goto start_of_frame; + + } else if (eof) { + DBG(3, "EOF without SOF") + continue; + + } else { + PDBGG("Ignoring pointless isochronous frame") + continue; + } + + } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { +start_of_frame: + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + len -= (sof - pos); + pos = sof; + DBG(3, "SOF detected: new video frame") + if (len) + goto redo; + + } else if ((*f)->state == F_GRABBING) { + eof = sn9c102_find_eof_header(pos, len); + if (eof && eof < sof) + goto end_of_frame; /* (1) */ + else { + DBG(3, "SOF before expected EOF after %lu " + "bytes of image data", + (unsigned long)((*f)->buf.bytesused)) + 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 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]; + s8 i, j; + int err = 0; + + for (i = 0; i < SN9C102_URBS; i++) { + cam->transfer_buffer[i] = kmalloc(SN9C102_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory") + goto free_buffers; + } + } + + for (i = 0; i < SN9C102_URBS; i++) { + urb = usb_alloc_urb(SN9C102_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 = SN9C102_ISO_PACKETS; + urb->complete = sn9c102_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < SN9C102_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + /* Enable video */ + if (!(cam->reg[0x01] & 0x04)) { + err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); + if (err) { + err = -EIO; + 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") + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < SN9C102_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 < SN9C102_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int sn9c102_stop_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = SN9C102_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 u8 sn9c102_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 sn9c102_show_reg(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + up(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 index; + ssize_t count; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou8(buf, len, &count); + if (index > 0x1f || !count) { + up(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg) + DBG(3, "Written bytes: %zd", count) + + up(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { + up(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count) + + up(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 value; + ssize_t count; + int err; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + value = sn9c102_strtou8(buf, len, &count); + if (!count) { + up(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_write_reg(cam, value, cam->sysfs.reg); + if (err) { + up(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value) + DBG(3, "Written bytes: %zd", count) + + up(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count) + + up(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 index; + ssize_t count; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou8(buf, len, &count); + if (!count) { + up(&sn9c102_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(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + up(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count) + + up(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 value; + ssize_t count; + int err; + + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + value = sn9c102_strtou8(buf, len, &count); + if (!count) { + up(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + up(&sn9c102_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(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x10", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x0f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, + sn9c102_show_reg, sn9c102_store_reg); +static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR, + sn9c102_show_val, sn9c102_store_val); +static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); +static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_val, sn9c102_store_i2c_val); +static CLASS_DEVICE_ATTR(redblue, S_IWUGO, NULL, sn9c102_store_redblue); +static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); + + +static void sn9c102_create_sysfs(struct sn9c102_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); + video_device_create_file(v4ldev, &class_device_attr_redblue); + video_device_create_file(v4ldev, &class_device_attr_green); + if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) { + video_device_create_file(v4ldev, &class_device_attr_i2c_reg); + video_device_create_file(v4ldev, &class_device_attr_i2c_val); + } +} + +/*****************************************************************************/ + +static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) +{ + u8 r = 0; + int err = 0; + + if (scale == 1) + r = cam->reg[0x18] & 0xcf; + else if (scale == 2) { + r = cam->reg[0x18] & 0xcf; + r |= 0x10; + } else if (scale == 4) + r = cam->reg[0x18] | 0x20; + + err += sn9c102_write_reg(cam, r, 0x18); + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale) + + return 0; +} + + +static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = cam->sensor; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), + v_start = (u8)(rect->top - s->cropcap.bounds.top), + h_size = (u8)(rect->width / 16), + v_size = (u8)(rect->height / 16), + ae_strx = 0x00, + ae_stry = 0x00, + ae_endx = h_size / 2, + ae_endy = v_size / 2; + int err = 0; + + /* These are a sort of stroboscopic signal for some sensors */ + err += sn9c102_write_reg(cam, h_size, 0x1a); + err += sn9c102_write_reg(cam, v_size, 0x1b); + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + err += sn9c102_write_reg(cam, h_size, 0x15); + err += sn9c102_write_reg(cam, v_size, 0x16); + err += sn9c102_write_reg(cam, ae_strx, 0x1c); + err += sn9c102_write_reg(cam, ae_stry, 0x1d); + err += sn9c102_write_reg(cam, ae_endx, 0x1e); + err += sn9c102_write_reg(cam, ae_endy, 0x1f); + if (err) + return -EIO; + + PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " + "%u %u %u %u %u %u", h_start, v_start, h_size, v_size, ho_size, + vo_size) + + return 0; +} + + +static int sn9c102_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0, n = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); + err += sn9c102_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed") + return err; + } + } + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed") + return err; + } + + if (s->set_ctrl) { + n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); + for (i = 0; i < n; 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 control failed") + return err; + } + } + } + + 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); + 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 sn9c102_release_resources(struct sn9c102_device* cam) +{ + down(&sn9c102_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(&sn9c102_sysfs_lock); + + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int sn9c102_open(struct inode* inode, struct file* filp) +{ + struct sn9c102_device* cam; + int err = 0; + + /* This the only safe way to prevent race conditions with disconnect */ + if (!down_read_trylock(&sn9c102_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (down_interruptible(&cam->dev_sem)) { + up_read(&sn9c102_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(&sn9c102_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&sn9c102_disconnect); + return -ENODEV; + } + down(&cam->dev_sem); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = sn9c102_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open().") + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = sn9c102_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; + sn9c102_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor) + +out: + up(&cam->dev_sem); + up_read(&sn9c102_disconnect); + return err; +} + + +static int sn9c102_release(struct inode* inode, struct file* filp) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + + down(&cam->dev_sem); /* prevent disconnect() to be called */ + + sn9c102_stop_transfer(cam); + + sn9c102_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + sn9c102_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 +sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_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 (!sn9c102_request_buffers(cam, 2)) { + DBG(1, "read() failed, not enough memory") + up(&cam->fileop_sem); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + sn9c102_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) ); + if (err) { + up(&cam->fileop_sem); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up(&cam->fileop_sem); + return -ENODEV; + } + } + + f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); + + 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); + + sn9c102_queue_unusedframes(cam); + + if (count > f->buf.length) + count = f->buf.length; + + 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) + + up(&cam->fileop_sem); + + return count; +} + + +static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + 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 (!sn9c102_request_buffers(cam, 2)) { + DBG(1, "poll() failed, not enough memory") + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) + sn9c102_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 sn9c102_vm_open(struct vm_area_struct* vma) +{ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void sn9c102_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct sn9c102_vm_ops = { + .open = sn9c102_vm_open, + .close = sn9c102_vm_close, +}; + + +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; + 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; + } + + pos = (unsigned long)cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + page = kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, + vma->vm_page_prot)) { + up(&cam->fileop_sem); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &sn9c102_vm_ops; + vma->vm_flags &= ~VM_IO; /* not I/O memory */ + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + vma->vm_private_data = &cam->frame[i]; + + sn9c102_vm_open(vma); + + up(&cam->fileop_sem); + + return 0; +} + + +static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, 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)); + 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; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "USB"); + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; + } + + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + { + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; + } + + case VIDIOC_QUERYCTRL: + { + struct sn9c102_sensor* s = cam->sensor; + struct v4l2_queryctrl qc; + u8 i, n; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); + for (i = 0; i < n; 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; + } + + case VIDIOC_G_CTRL: + { + struct sn9c102_sensor* s = cam->sensor; + struct v4l2_control ctrl; + int err = 0; + + if (!s->get_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + err = s->get_ctrl(cam, &ctrl); + + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; + } + + case VIDIOC_S_CTRL: + { + struct sn9c102_sensor* s = cam->sensor; + struct v4l2_control ctrl; + u8 i, n; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); + for (i = 0; i < n; i++) + if (ctrl.id == s->qctrl[i].id) { + s->_qctrl[i].default_value = ctrl.value; + break; + } + + return 0; + } + + case VIDIOC_CROPCAP: + { + 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; + } + + case VIDIOC_G_CROP: + { + struct sn9c102_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; + } + + 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; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + 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 (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; + + { /* calculate the scaling factor */ + u32 a, b; + a = rect->width * rect->height; + b = pix_format->width * pix_format->height; + scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : + ((a / b) > 4 ? 4 : (a / b)))) : 1; + } + + if (cam->stream == STREAM_ON) { + cam->stream = STREAM_INTERRUPT; + err = wait_event_interruptible + ( cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED) ); + 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) + return err; + } + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + } + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + sn9c102_release_buffers(cam); + + err = sn9c102_set_crop(cam, rect); + 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_CROP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor) + return err; + } + + s->pix_format.width = rect->width/scale; + s->pix_format.height = rect->height/scale; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) { + 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; + } + + cam->stream = stream; + + return 0; + } + + case VIDIOC_ENUM_FMT: + { + struct sn9c102_sensor* s = cam->sensor; + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index != 0) + return -EINVAL; + + memset(&fmtd, 0, sizeof(fmtd)); + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(fmtd.description, "bayer rgb"); + fmtd.pixelformat = s->pix_format.pixelformat; + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; + } + + 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 (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = (pfmt->width * pfmt->priv) / 8; + pfmt->sizeimage = pfmt->height * pfmt->bytesperline; + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; + } + + 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; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + { /* calculate the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : + ((a / b) > 4 ? 4 : (a / b)))) : 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; + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = pfmt->colorspace; + pix->bytesperline = (pix->width * pix->priv) / 8; + pix->sizeimage = pix->height * pix->bytesperline; + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) + return 0; + + 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) { + cam->stream = STREAM_INTERRUPT; + err = wait_event_interruptible + ( cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED) ); + 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) + return err; + } + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + } + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + sn9c102_release_buffers(cam); + + err = sn9c102_set_crop(cam, &rect); + 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 err; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) { + 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; + } + + cam->stream = stream; + + return 0; + } + + case VIDIOC_REQBUFS: + { + 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) { + cam->stream = STREAM_INTERRUPT; + err = wait_event_interruptible + ( cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED) ); + 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) + return err; + } + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + } + + sn9c102_empty_framequeues(cam); + + sn9c102_release_buffers(cam); + if (rb.count) + rb.count = sn9c102_request_buffers(cam, rb.count); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + sn9c102_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; + } + + case VIDIOC_QUERYBUF: + { + 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; + } + + case VIDIOC_QBUF: + { + 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; + } + + case VIDIOC_DQBUF: + { + 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; + + 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) ); + if (err) + return err; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, + frame); + list_del(&cam->outqueue); + 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; + } + + case VIDIOC_STREAMON: + { + 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; + } + + case VIDIOC_STREAMOFF: + { + 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) { + cam->stream = STREAM_INTERRUPT; + err = wait_event_interruptible + ( cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED) ); + 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) + return err; + } + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + } + + sn9c102_empty_framequeues(cam); + + DBG(3, "Stream off") + + return 0; + } + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + case VIDIOC_G_PARM: + case VIDIOC_S_PARM: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int sn9c102_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct sn9c102_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; + } + + err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg); + + up(&cam->fileop_sem); + + return err; +} + + +static struct file_operations sn9c102_fops = { + .owner = THIS_MODULE, + .open = sn9c102_open, + .release = sn9c102_release, + .ioctl = sn9c102_ioctl, + .read = sn9c102_read, + .poll = sn9c102_poll, + .mmap = sn9c102_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +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; + int err = 0, r; + + n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]); + for (i = 0; i < n-1; i++) + if (udev->descriptor.idVendor==sn9c102_id_table[i].idVendor && + udev->descriptor.idProduct==sn9c102_id_table[i].idProduct) + break; + if (i == n-1) + return -ENODEV; + + if (!(cam = kmalloc(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") + err = -ENOMEM; + goto fail; + } + memset(cam->control_buffer, 0, 8); + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed") + err = -ENOMEM; + goto fail; + } + + init_MUTEX(&cam->dev_sem); + + r = sn9c102_read_reg(cam, 0x00); + if (r < 0 || r != 0x10) { + DBG(1, "Sorry, this is not a SN9C10[12] based camera " + "(vid/pid 0x%04X/0x%04X)", + sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct) + err = -ENODEV; + goto fail; + } + + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", + sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct) + + for (i = 0; sn9c102_sensor_table[i]; i++) { + err = sn9c102_sensor_table[i](cam); + if (!err) + break; + } + + if (!err && cam->sensor) { + DBG(2, "%s image sensor detected", cam->sensor->name) + DBG(3, "Support for %s maintained by %s", + cam->sensor->name, cam->sensor->maintainer) + } else { + DBG(1, "No supported image sensor detected") + err = -ENODEV; + goto fail; + } + + if (sn9c102_init(cam)) { + DBG(1, "Initialization failed. I will retry on open().") + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = VID_HARDWARE_SN9C102; + cam->v4ldev->fops = &sn9c102_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 < 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) + + sn9c102_create_sysfs(cam); + + 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 sn9c102_usb_disconnect(struct usb_interface* intf) +{ + struct sn9c102_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&sn9c102_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; + sn9c102_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up_interruptible(&cam->wait_stream); + } else { + cam->state |= DEV_DISCONNECTED; + sn9c102_release_resources(cam); + } + + up(&cam->dev_sem); + + if (!cam->users) + kfree(cam); + + up_write(&sn9c102_disconnect); +} + + +static struct usb_driver sn9c102_usb_driver = { + .owner = THIS_MODULE, + .name = "sn9c102", + .id_table = sn9c102_id_table, + .probe = sn9c102_usb_probe, + .disconnect = sn9c102_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init sn9c102_module_init(void) +{ + int err = 0; + + 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") + + return err; +} + + +static void __exit sn9c102_module_exit(void) +{ + usb_deregister(&sn9c102_usb_driver); +} + + +module_init(sn9c102_module_init); +module_exit(sn9c102_module_exit); diff -Nru a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/sn9c102_pas106b.c 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,209 @@ +/*************************************************************************** + * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas106b; + + +static int pas106b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x20, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x03, 0x12); + err += sn9c102_i2c_write(cam, 0x04, 0x05); + err += sn9c102_i2c_write(cam, 0x05, 0x22); + err += sn9c102_i2c_write(cam, 0x06, 0xac); + err += sn9c102_i2c_write(cam, 0x07, 0x00); + err += sn9c102_i2c_write(cam, 0x08, 0x01); + err += sn9c102_i2c_write(cam, 0x0a, 0x00); + err += sn9c102_i2c_write(cam, 0x0b, 0x00); + err += sn9c102_i2c_write(cam, 0x0d, 0x00); + err += sn9c102_i2c_write(cam, 0x10, 0x06); + err += sn9c102_i2c_write(cam, 0x11, 0x06); + err += sn9c102_i2c_write(cam, 0x12, 0x00); + err += sn9c102_i2c_write(cam, 0x14, 0x02); + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + msleep(400); + + return err; +} + + +static int pas106b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_RED_BALANCE: + return (ctrl->value = sn9c102_i2c_read(cam, 0x0c))<0 ? -EIO:0; + case V4L2_CID_BLUE_BALANCE: + return (ctrl->value = sn9c102_i2c_read(cam, 0x09))<0 ? -EIO:0; + case V4L2_CID_BRIGHTNESS: + return (ctrl->value = sn9c102_i2c_read(cam, 0x0e))<0 ? -EIO:0; + default: + return -EINVAL; + } +} + + +static int pas106b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f); + break; + case V4L2_CID_BRIGHTNESS: + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + return err; +} + + +static int pas106b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas106b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas106b = { + .name = "PAS106B", + .maintainer = "Luca Risolia ", + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .slave_read_id = 0x40, + .slave_write_id = 0x40, + .init = &pas106b_init, + .qctrl = { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x03, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x02, + .flags = 0, + }, + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + }, + .get_ctrl = &pas106b_get_ctrl, + .set_ctrl = &pas106b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &pas106b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, /* we use this field as 'bits per pixel' */ + } +}; + + +int sn9c102_probe_pas106b(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* Minimal initialization to enable the I2C communication + NOTE: do NOT change the values! */ + err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ + err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 48 MHz */ + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); + + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 11) | ((r1 & 0xf0) >> 4); + if (pid != 0x007) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas106b); + + return 0; +} diff -Nru a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/sn9c102_sensor.h 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,270 @@ +/*************************************************************************** + * API for image sensors connected to the SN9C10[12] PC Camera Controllers * + * * + * Copyright (C) 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_SENSOR_H_ +#define _SN9C102_SENSOR_H_ + +#include +#include +#include +#include +#include +#include + +struct sn9c102_device; +struct sn9c102_sensor; + +/*****************************************************************************/ + +/* OVERVIEW. + This is a small interface that allows you to add support for any CCD/CMOS + image sensors connected to the SN9C10X bridges. The entire API is documented + below. In the most general case, to support a sensor there are three steps + you have to follow: + 1) define the main "sn9c102_sensor" structure by setting the basic fields; + 2) write a probing function to be called by the core module when the USB + camera is recognized, then add both the USB ids and the name of that + function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see + below); + 3) implement the methods that you want/need (and fill the rest of the main + structure accordingly). + "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do + NOT need to touch the source code of the core module for the things to work + properly, unless you find bugs or flaws in it. Finally, do not forget to + read the V4L2 API for completeness. */ + +/*****************************************************************************/ + +/* Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor() provided below. + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C10X chip by using the write function declared + ahead. + Functions must return 0 on success, the appropriate error otherwise. */ +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +/* Add the above entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom */ +#define SN9C102_SENSOR_TABLE \ +static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR vid/pid */\ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ + NULL, \ +}; + +/* Attach a probed sensor to the camera. */ +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.*/ +#define SN9C102_ID_TABLE \ +static const struct usb_device_id sn9c102_id_table[] = { \ + { USB_DEVICE(0xc45, 0x6001), }, \ + { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \ + { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \ + { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \ + { USB_DEVICE(0xc45, 0x6024), }, \ + { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B Maybe also TAS5110C1B */\ + { USB_DEVICE(0xc45, 0x6028), }, /* Maybe PAS202B */ \ + { USB_DEVICE(0xc45, 0x6029), }, \ + { USB_DEVICE(0xc45, 0x602a), }, /* Maybe HV7131[D|E1] */ \ + { USB_DEVICE(0xc45, 0x602c), }, /* Maybe OV7620 */ \ + { USB_DEVICE(0xc45, 0x6030), }, /* Maybe MI03 */ \ + { USB_DEVICE(0xc45, 0x8001), }, \ + { } \ +}; + +/*****************************************************************************/ + +/* Read/write routines: they always return -1 on error, 0 or the read value + otherwise. NOTE that a real read operation is not supported by the SN9C10X + chip for some of its registers. To work around this problem, a pseudo-read + call is provided instead: it returns the last successfully written value + on the register (0 if it has never been written), the usual -1 on error. */ + +/* The "try" I2C I/O versions are used when probing the sensor */ +extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*, + u8 address, u8 value); +extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*, + u8 address); + +/* This must be used if and only if the sensor doesn't implement the standard + I2C protocol, like the TASC sensors. There a number of good reasons why you + must use the single-byte versions of this function: do not abuse. It writes + n bytes, from data0 to datan, (registers 0x09 - 0x09+n of SN9C10X chip) */ +extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 n, + u8 data0, u8 data1, u8 data2, u8 data3, + u8 data4, u8 data5); + +/* To be used after the sensor struct has been attached to the camera struct */ +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_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); + +/* NOTE: there are no debugging functions here. 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. */ + +/*****************************************************************************/ + +enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ + SN9C102_I2C_100KHZ = 0x01, + SN9C102_I2C_400KHZ = 0x02, +}; + +enum sn9c102_i2c_interface { + SN9C102_I2C_2WIRES, + SN9C102_I2C_3WIRES, +}; + +struct sn9c102_sensor { + char name[32], /* sensor name */ + maintainer[64]; /* name of the mantainer */ + + /* These sensor capabilities must be provided if the SN9C10X controller + needs to communicate through the sensor serial interface by using + at least one of the i2c functions available */ + enum sn9c102_i2c_frequency frequency; + enum sn9c102_i2c_interface interface; + + /* These identifiers must be provided if the image sensor implements + the standard I2C protocol. TASC sensors don't, although they have a + serial interface: so this is a case where the "raw" I2C version + could be helpful. */ + u8 slave_read_id, slave_write_id; /* reg. 0x09 */ + + /* NOTE: Where not noted,most of the functions below are not mandatory. + Set to null if you do not implement them. If implemented, + they must return 0 on success, the proper error otherwise. */ + + int (*init)(struct sn9c102_device* cam); + /* This function is called after the sensor has been attached. + It should be used to initialize the sensor only, but may also + configure part of the SN9C10X chip if necessary. You don't need to + setup picture settings like brightness, contrast, etc.. here, if + the corrisponding controls are implemented (see below), since + they are adjusted in the core driver by calling the set_ctrl() + method after init(), where the arguments are the default values + specified in the v4l2_queryctrl list of supported controls; + Same suggestions apply for other settings, _if_ the corresponding + methods are present; if not, the initialization must configure the + sensor according to the default configuration structures below. */ + + struct v4l2_queryctrl qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE]; + /* Optional list of default controls, defined as indicated in the + V4L2 API. Menu type controls are not handled by this interface. */ + + int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); + int (*set_ctrl)(struct sn9c102_device* cam, + const struct v4l2_control* ctrl); + /* You must implement at least the set_ctrl method if you have defined + the list above. The returned value must follow the V4L2 + specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER + are not supported by this driver, so do not implement them. Also, + passed values are NOT checked to see if they are out of bounds. */ + + struct v4l2_cropcap cropcap; + /* Think the image sensor as a grid of R,G,B monochromatic pixels + disposed according to a particular Bayer pattern, which describes + the complete array of pixels, from (0,0) to (xmax, ymax). We will + use this coordinate system from now on. It is assumed the sensor + chip can be programmed to capture/transmit a subsection of that + array of pixels: we will call this subsection "active window". + It is not always true that the largest achievable active window can + cover the whole array of pixels. The V4L2 API defines another + area called "source rectangle", which, in turn, is a subrectangle of + the active window. The SN9C10X chip is always programmed to read the + source rectangle. + The bounds of both the active window and the source rectangle are + specified in the cropcap substructures 'bounds' and 'defrect'. + By default, the source rectangle should cover the largest possible + area. Again, it is not always true that the largest source rectangle + can cover the entire active window, although it is a rare case for + the hardware we have. The bounds of the source rectangle _must_ be + multiple of 16 and must use the same coordinate system as indicated + before; their centers shall align initially. + If necessary, the sensor chip must be initialized during init() to + set the bounds of the active sensor window; however, by default, it + usually covers the largest achievable area (maxwidth x maxheight) + of pixels, so no particular initialization is needed, if you have + defined the correct default bounds in the structures. + See the V4L2 API for further details. + NOTE: once you have defined the bounds of the active window + (struct cropcap.bounds) you must not change them.anymore. + Only 'bounds' and 'defrect' fields are mandatory, other fields + will be ignored. */ + + int (*set_crop)(struct sn9c102_device* cam, + const struct v4l2_rect* rect); + /* To be called on VIDIOC_C_SETCROP. The core module always calls a + default routine which configures the appropriate SN9C10X regs (also + scaling), but you may need to override/adjust specific stuff. + 'rect' contains width and height values that are multiple of 16: in + case you override the default function, you always have to program + the chip to match those values; on error return the corresponding + error code without rolling back. + NOTE: in case, you must program the SN9C10X chip to get rid of + blank pixels or blank lines at the _start_ of each line or + frame after each HSYNC or VSYNC, so that the image starts with + real RGB data (see regs 0x12,0x13) (having set H_SIZE and, + V_SIZE you don't have to care about blank pixels or blank + lines at the end of each line or frame). */ + + struct v4l2_pix_format pix_format; + /* What you have to define here are: initial 'width' and 'height' of + the target rectangle, the bayer 'pixelformat' and 'priv' which we'll + be used to indicate the number of bits per pixel, 8 or 9. + Nothing more. + NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 + of cropcap.defrect.width and cropcap.defrect.height. I + suggest 1/1. + NOTE 2: as said above, you have to program the SN9C10X chip to get + rid of any blank pixels, so that the output of the sensor + matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). */ + + 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. + Do not touch unless you know what you are doing. */ + + /* Do NOT write to the data below, it's READ ONLY. It is used by the + core module to store successfully updated values of the above + settings, for rollbacks..etc..in case of errors during atomic I/O */ + struct v4l2_queryctrl _qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE]; + struct v4l2_rect _rect; +}; + +#endif /* _SN9C102_SENSOR_H_ */ diff -Nru a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/sn9c102_tas5110c1b.c 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,98 @@ +/*************************************************************************** + * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC * + * Camera Controllers * + * * + * Copyright (C) 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor tas5110c1b; + + +static int tas5110c1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x44, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x06, 0x18); + err += sn9c102_write_reg(cam, 0xcb, 0x19); + + return err; +} + + +static int tas5110c1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &tas5110c1b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor tas5110c1b = { + .name = "TAS5110C1B", + .maintainer = "Luca Risolia ", + .init = &tas5110c1b_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110c1b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + } +}; + + +int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) +{ + /* This sensor has no identifiers, so let's attach it anyway */ + sn9c102_attach_sensor(cam, &tas5110c1b); + + /* At the moment, only devices whose PID is 0x6005 have this sensor */ + if (tas5110c1b.usbdev->descriptor.idProduct != 0x6005) + return -ENODEV; + + return 0; +} diff -Nru a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/sn9c102_tas5130d1b.c 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,120 @@ +/*************************************************************************** + * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC * + * Camera Controllers * + * * + * Copyright (C) 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor tas5130d1b; + + +static int tas5130d1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x04, 0x01); + err += sn9c102_write_reg(cam, 0x01, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x07, 0x18); + err += sn9c102_write_reg(cam, 0x33, 0x19); + + err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40, + 0x47, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20, + 0xa9, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0, + 0x49, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20, + 0x6c, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0, + 0x08, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x20, + 0x00, 0, 0); + + err += sn9c102_write_reg(cam, 0x63, 0x19); + + return err; +} + + +static int tas5130d1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &tas5130d1b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor tas5130d1b = { + .name = "TAS5130D1B", + .maintainer = "Luca Risolia ", + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5130d1b_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &tas5130d1b_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + } +}; + + +int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) +{ + /* This sensor has no identifiers, so let's attach it anyway */ + sn9c102_attach_sensor(cam, &tas5130d1b); + + /* At the moment, only devices whose PID is 0x6025 have this sensor */ + if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025) + return -ENODEV; + + dev_info(tas5130d1b.dev, "TAS5130D1B detected, but the support for it " + "is disabled at the moment - needs further " + "testing -\n"); + + return -ENODEV; +} diff -Nru a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c --- a/drivers/usb/media/w9968cf.c 2004-07-14 16:44:05 -07:00 +++ b/drivers/usb/media/w9968cf.c 2004-07-14 16:44:05 -07:00 @@ -5,7 +5,7 @@ * * * - Memory management code from bttv driver by Ralph Metzler, * * Marcus Metzler and Gerd Knorr. * - * - I2C interface to kernel, high-level CMOS sensor control routines and * + * - I2C interface to kernel, high-level image sensor control routines and * * some symbolic names from OV511 driver by Mark W. McClelland. * * - Low-level I2C fast write function by Piotr Czerczak. * * - Low-level I2C read function by Frederic Jouault. * @@ -27,21 +27,23 @@ #include #include -#include #include +#include #include #include #include #include #include #include -#include #include #include #include #include +#include #include #include +#include +#include #include "w9968cf.h" #include "w9968cf_decoder.h" @@ -49,14 +51,18 @@ /**************************************************************************** - * Module macros and paramaters * + * Module macros and parameters * ****************************************************************************/ +MODULE_DEVICE_TABLE(usb, winbond_id_table); + MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL); -MODULE_DESCRIPTION(W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION); +MODULE_DESCRIPTION(W9968CF_MODULE_NAME); +MODULE_VERSION(W9968CF_MODULE_VERSION); 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*/ @@ -100,8 +106,11 @@ 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); -module_param(simcams, ushort, 0444); +#endif +module_param(simcams, ushort, 0644); module_param_array(video_nr, short, param_nv[0], 0444); module_param_array(packet_size, uint, param_nv[1], 0444); module_param_array(max_buffers, ushort, param_nv[2], 0444); @@ -127,21 +136,34 @@ module_param_array(contrast, uint, param_nv[22], 0444); module_param_array(whiteness, uint, param_nv[23], 0444); #ifdef W9968CF_DEBUG -module_param(debug, ushort, 0444); -module_param(specific_debug, bool, 0444); +module_param(debug, ushort, 0644); +module_param(specific_debug, bool, 0644); #endif +#ifdef CONFIG_KMOD +MODULE_PARM_DESC(ovmod_load, + "\n<0|1> Automatic 'ovcamchip' module loading." + "\n0 disabled, 1 enabled." + "\nIf enabled,'insmod' searches for the required 'ovcamchip'" + "\nmodule in the system, according to its configuration, and" + "\nattempts to load that module automatically. This action is" + "\nperformed once as soon as the 'w9968cf' module is loaded" + "\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 disable, 1 enable." + "\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 'w9968cf-vpp' module adds extra image" - "\nmanipulation functions to the 'w9968cf' module, like" - "\nsoftware up-scaling,colour conversions and video decoding." + "\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 Number of cameras allowed to stream simultaneously." "\nn may vary from 0 to " @@ -176,8 +198,8 @@ "\n<0|1[,...]> " "Hardware double buffering: 0 disabled, 1 enabled." "\nIt should be enabled if you want smooth video output: if" - "\nyou obtain out of sync. video, disable it at all, or try" - "\nto decrease the 'clockdiv' module paramater value." + "\nyou obtain out of sync. video, disable it, or try to" + "\ndecrease the 'clockdiv' module parameter value." "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) " for every device." "\n"); @@ -193,7 +215,7 @@ "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE) " for every device." "\nThe filter is used to reduce noise and aliasing artifacts" - "\nproduced by the CCD or CMOS sensor, and the scaling" + "\nproduced by the CCD or CMOS image sensor, and the scaling" " process." "\n"); MODULE_PARM_DESC(largeview, @@ -208,7 +230,7 @@ " enough memory." "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) " for every device." - "\nIf 'w9968cf-vpp' is not loaded, this paramater is" + "\nIf 'w9968cf-vpp' is not present, this parameter is" " set to 0." "\n"); MODULE_PARM_DESC(decompression, @@ -224,8 +246,8 @@ "a multiple of 16." "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION) " for every device." - "\nIf 'w9968cf-vpp' is not loaded, forcing decompression is " - "\nnot allowed; in this case this paramater is set to 2." + "\nIf 'w9968cf-vpp' is not present, forcing decompression is " + "\nnot allowed; in this case this parameter is set to 2." "\n"); MODULE_PARM_DESC(force_palette, "\n<0" @@ -255,11 +277,11 @@ "\n- RGB565 16 bpp - Software conversion from UYVY" "\n- RGB24 24 bpp - Software conversion from UYVY" "\n- RGB32 32 bpp - Software conversion from UYVY" - "\nWhen not 0, this paramater will override 'decompression'." + "\nWhen not 0, this parameter will override 'decompression'." "\nDefault value is 0 for every device." "\nInitial palette is " __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." - "\nIf 'w9968cf-vpp' is not loaded, this paramater is" + "\nIf 'w9968cf-vpp' is not present, this parameter is" " set to 9 (UYVY)." "\n"); MODULE_PARM_DESC(force_rgb, @@ -271,13 +293,13 @@ " for every device." "\n"); MODULE_PARM_DESC(autobright, - "\n<0|1[,...]> CMOS sensor automatically changes brightness:" + "\n<0|1[,...]> Image sensor automatically changes brightness:" "\n 0 = no, 1 = yes" "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT) " for every device." "\n"); MODULE_PARM_DESC(autoexp, - "\n<0|1[,...]> CMOS sensor automatically changes exposure:" + "\n<0|1[,...]> Image sensor automatically changes exposure:" "\n 0 = no, 1 = yes" "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) " for every device." @@ -304,7 +326,7 @@ "Force pixel clock divisor to a specific value (for experts):" "\n n may vary from 0 to 127." "\n -1 for automatic value." - "\nSee also the 'double_buffer' module paramater." + "\nSee also the 'double_buffer' module parameter." "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV) " for every device." "\n"); @@ -321,7 +343,7 @@ " for every device." "\n"); MODULE_PARM_DESC(monochrome, - "\n<0|1[,...]> Use OV CMOS sensor as monochrome sensor:" + "\n<0|1[,...]> Use image sensor as monochrome sensor:" "\n 0 = no, 1 = yes" "\nNot all the sensors support monochrome color." "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME) @@ -363,7 +385,7 @@ "\n4 = warnings" "\n5 = called functions" "\n6 = function internals" - "\nLevel 5 and 6 are useful for testing only, when just " + "\nLevel 5 and 6 are useful for testing only, when only " "one device is used." "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." "\n"); @@ -388,14 +410,14 @@ static struct file_operations w9968cf_fops; static int w9968cf_open(struct inode*, struct file*); static int w9968cf_release(struct inode*, struct file*); -static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*); static int w9968cf_mmap(struct file*, struct vm_area_struct*); static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long); -static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, void*); +static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*); +static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, + void __user *); /* USB-specific */ static int w9968cf_start_transfer(struct w9968cf_device*); -static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs); static int w9968cf_stop_transfer(struct w9968cf_device*); static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index); static int w9968cf_read_reg(struct w9968cf_device*, u16 index); @@ -403,6 +425,7 @@ static int w9968cf_write_sb(struct w9968cf_device*, u16 value); static int w9968cf_read_sb(struct w9968cf_device*); static int w9968cf_upload_quantizationtables(struct w9968cf_device*); +static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs); /* Low-level I2C (SMBus) I/O */ static int w9968cf_smbus_start(struct w9968cf_device*); @@ -439,12 +462,11 @@ static void rvfree(void *mem, unsigned long size); static void w9968cf_deallocate_memory(struct w9968cf_device*); static int w9968cf_allocate_memory(struct w9968cf_device*); -static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device*); -/* High-level CMOS sensor control functions */ +/* High-level image sensor control functions */ static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val); static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val); -static int w9968cf_sensor_cmd(struct w9968cf_device*, +static int w9968cf_sensor_cmd(struct w9968cf_device*, unsigned int cmd, void *arg); static int w9968cf_sensor_init(struct w9968cf_device*); static int w9968cf_sensor_update_settings(struct w9968cf_device*); @@ -456,12 +478,13 @@ static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, enum w9968cf_model_id, const unsigned short dev_nr); +static void w9968cf_adjust_configuration(struct w9968cf_device*); static int w9968cf_turn_on_led(struct w9968cf_device*); static int w9968cf_init_chip(struct w9968cf_device*); static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); static int w9968cf_set_window(struct w9968cf_device*, struct video_window); static inline u16 w9968cf_valid_palette(u16 palette); -static u16 w9968cf_valid_depth(u16 palette); +static inline u16 w9968cf_valid_depth(u16 palette); static inline u8 w9968cf_need_decompression(u16 palette); static int w9968cf_postprocess_frame(struct w9968cf_device*, struct w9968cf_frame_t*); @@ -472,18 +495,8 @@ static void w9968cf_release_resources(struct w9968cf_device*); /* Intermodule communication */ -static int w9968cf_vppmod_detect(void); -static void w9968cf_vppmod_release(void); - -/* Pointers to registered video post-processing functions */ -static void (*w9968cf_vpp_init_decoder)(void); -static int (*w9968cf_vpp_check_headers)(const unsigned char*, - const unsigned long); -static int (*w9968cf_vpp_decode)(const char*, const unsigned, - const unsigned, const unsigned, char*); -static void (*w9968cf_vpp_swap_yuvbytes)(void*, unsigned long); -static void (*w9968cf_vpp_uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8); -static void (*w9968cf_vpp_scale_up)(u8*, u8*, u16, u16, u16, u16, u16); +static int w9968cf_vppmod_detect(struct w9968cf_device*); +static void w9968cf_vppmod_release(struct w9968cf_device*); @@ -518,12 +531,15 @@ { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" }, /* Other cameras (having the same descriptors as Generic W996[87]CF) */ - { W9968CF_MOD_ADPA5R, "Aroma Digi Pen ADG-5000 Refurbished" }, - { W9986CF_MOD_AU, "AVerTV USB" }, + { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" }, + { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" }, { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, - { W9968CF_MOD_DLLDK, "Die Lebon LDC-D35A Digital Kamera" }, + { W9968CF_MOD_LL, "Lebon LDC-035A" }, { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" }, + { W9968CF_MOD_OOE, "OmniVision OV8610-EDE" }, { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" }, + { W9968CF_MOD_PDPII, "Pretec Digi Pen-II" }, + { W9968CF_MOD_PDP480, "Pretec DigiPen-480" }, { -1, NULL } }; @@ -649,17 +665,6 @@ /*-------------------------------------------------------------------------- - Return the maximum size (in bytes) of a frame buffer. - --------------------------------------------------------------------------*/ -static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device* cam) -{ - u8 bpp = (w9968cf_vppmod_present) ? 4 : 2; - return (cam->upscaling) ? W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp : - cam->maxwidth*cam->maxheight*bpp; -} - - -/*-------------------------------------------------------------------------- Deallocate previously allocated memory. --------------------------------------------------------------------------*/ static void w9968cf_deallocate_memory(struct w9968cf_device* cam) @@ -674,26 +679,25 @@ /* Free temporary frame buffer */ if (cam->frame_tmp.buffer) { - rvfree(cam->frame_tmp.buffer, W9968CF_HW_BUF_SIZE); + rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size); cam->frame_tmp.buffer = NULL; } /* Free helper buffer */ - if (cam->vpp_buffer) { - rvfree(cam->vpp_buffer, w9968cf_get_max_bufsize(cam)); - cam->vpp_buffer = NULL; + if (cam->frame_vpp.buffer) { + rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size); + cam->frame_vpp.buffer = NULL; } - + /* Free video frame buffers */ if (cam->frame[0].buffer) { - rvfree(cam->frame[0].buffer, - cam->nbuffers * w9968cf_get_max_bufsize(cam)); + rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size); cam->frame[0].buffer = NULL; } cam->nbuffers = 0; - DBG(5, "Memory successfully deallocated.") + DBG(5, "Memory successfully deallocated") } @@ -704,19 +708,30 @@ --------------------------------------------------------------------------*/ static int w9968cf_allocate_memory(struct w9968cf_device* cam) { - const unsigned long bufsize = w9968cf_get_max_bufsize(cam); const u16 p_size = wMaxPacketSize[cam->altsetting-1]; void* buff = NULL; - u8 i; + unsigned long hw_bufsize, vpp_bufsize; + u8 i, bpp; /* NOTE: Deallocation is done elsewhere in case of error */ + /* Calculate the max amount of raw data per frame from the device */ + hw_bufsize = cam->maxwidth*cam->maxheight*2; + + /* Calculate the max buf. size needed for post-processing routines */ + bpp = (w9968cf_vpp) ? 4 : 2; + if (cam->upscaling) + vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp, + cam->maxwidth*cam->maxheight*bpp); + else + vpp_bufsize = cam->maxwidth*cam->maxheight*bpp; + /* Allocate memory for the isochronous transfer buffers */ for (i = 0; i < W9968CF_URBS; i++) { if (!(cam->transfer_buffer[i] = kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { DBG(1, "Couldn't allocate memory for the isochronous " - "transfer buffers (%d bytes).", + "transfer buffers (%u bytes)", p_size * W9968CF_ISO_PACKETS) return -ENOMEM; } @@ -724,44 +739,49 @@ } /* Allocate memory for the temporary frame buffer */ - if (!(cam->frame_tmp.buffer = rvmalloc(W9968CF_HW_BUF_SIZE))) { + if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) { DBG(1, "Couldn't allocate memory for the temporary " - "video frame buffer (%i bytes).", W9968CF_HW_BUF_SIZE) + "video frame buffer (%lu bytes)", hw_bufsize) return -ENOMEM; } + cam->frame_tmp.size = hw_bufsize; + cam->frame_tmp.number = -1; /* Allocate memory for the helper buffer */ - if (w9968cf_vppmod_present) { - if (!(cam->vpp_buffer = rvmalloc(bufsize))) { + if (w9968cf_vpp) { + if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) { DBG(1, "Couldn't allocate memory for the helper buffer" - " (%li bytes).", bufsize) + " (%lu bytes)", vpp_bufsize) return -ENOMEM; } + cam->frame_vpp.size = vpp_bufsize; } else - cam->vpp_buffer = NULL; + cam->frame_vpp.buffer = NULL; - /* Allocate memory for video frame buffers */ + /* Allocate memory for video frame buffers */ cam->nbuffers = cam->max_buffers; while (cam->nbuffers >= 2) { - if ((buff = rvmalloc(cam->nbuffers * bufsize))) + if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize))) break; else cam->nbuffers--; } if (!buff) { - DBG(1, "Couldn't allocate memory for the video frame buffers.") + DBG(1, "Couldn't allocate memory for the video frame buffers") cam->nbuffers = 0; return -ENOMEM; } if (cam->nbuffers != cam->max_buffers) - DBG(2, "Couldn't allocate memory for %d video frame buffers. " - "Only memory for %d buffers has been allocated.", + DBG(2, "Couldn't allocate memory for %u video frame buffers. " + "Only memory for %u buffers has been allocated", cam->max_buffers, cam->nbuffers) for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].buffer = buff + i*bufsize; + cam->frame[i].buffer = buff + i*vpp_bufsize; + cam->frame[i].size = vpp_bufsize; + cam->frame[i].number = i; /* Circular list */ if (i != cam->nbuffers-1) cam->frame[i].next = &cam->frame[i+1]; @@ -770,7 +790,7 @@ cam->frame[i].status = F_UNUSED; } - DBG(5, "Memory successfully allocated.") + DBG(5, "Memory successfully allocated") return 0; } @@ -794,20 +814,16 @@ { struct w9968cf_device* cam = (struct w9968cf_device*)urb->context; struct w9968cf_frame_t** f; - unsigned long maxbufsize; unsigned int len, status; void* pos; u8 i; int err = 0; if ((!cam->streaming) || cam->disconnected) { - DBG(4, "Got interrupt, but not streaming.") + DBG(4, "Got interrupt, but not streaming") return; } - maxbufsize = min( (unsigned long)W9968CF_HW_BUF_SIZE, - w9968cf_get_max_bufsize(cam) ); - /* "(*f)" will be used instead of "cam->frame_current" */ f = &cam->frame_current; @@ -820,8 +836,8 @@ (*f)->length = cam->frame_tmp.length; memcpy((*f)->buffer, cam->frame_tmp.buffer, (*f)->length); - DBG(6, "Switched from temp. frame to frame #%zd", - (*f) - &cam->frame[0]) + DBG(6, "Switched from temp. frame to frame #%d", + (*f)->number) } } @@ -832,7 +848,7 @@ if (status && len != 0) { DBG(4, "URB failed, error in data packet " - "(error #%d, %s).", + "(error #%u, %s)", status, symbolic(urb_errlist, status)) (*f)->status = F_ERROR; continue; @@ -846,8 +862,8 @@ } /* Buffer overflows shouldn't happen, however...*/ - if ((*f)->length + len > maxbufsize) { - DBG(4, "Buffer overflow: bad data packets.") + if ((*f)->length + len > (*f)->size) { + DBG(4, "Buffer overflow: bad data packets") (*f)->status = F_ERROR; } @@ -858,11 +874,10 @@ } else if ((*f)->status == F_GRABBING) { /* end of frame */ - DBG(6, "Frame #%zd successfully grabbed.", - ((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0])) + DBG(6, "Frame #%d successfully grabbed", (*f)->number) if (cam->vpp_flag & VPP_DECOMPRESSION) { - err=(*w9968cf_vpp_check_headers)((*f)->buffer, + err = w9968cf_vpp->check_headers((*f)->buffer, (*f)->length); if (err) { DBG(4, "Skip corrupted frame: %s", @@ -887,7 +902,7 @@ } else if ((*f)->status == F_ERROR) (*f)->status = F_UNUSED; /* grab it again */ - PDBGG("Frame length %li | pack.#%d | pack.len. %d | state %d", + PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d", (unsigned long)(*f)->length, i, len, (*f)->status) } /* end for */ @@ -900,7 +915,7 @@ if ((err = usb_submit_urb(urb, GFP_ATOMIC))) { cam->misconfigured = 1; DBG(1, "Couldn't resubmit the URB: error %d, %s", - err, symbolic(urb_errlist, err)); + err, symbolic(urb_errlist, err)) } spin_unlock(&cam->urb_lock); @@ -931,7 +946,7 @@ if (!urb) { for (j = 0; j < i; j++) usb_free_urb(cam->urb[j]); - DBG(1, "Couldn't allocate the URB structures.") + DBG(1, "Couldn't allocate the URB structures") return -ENOMEM; } @@ -976,7 +991,7 @@ if (err || (vidcapt < 0)) { for (i = 0; i < W9968CF_URBS; i++) usb_free_urb(cam->urb[i]); - DBG(1, "Couldn't tell the camera to start the data transfer.") + DBG(1, "Couldn't tell the camera to start the data transfer") return err; } @@ -988,26 +1003,29 @@ cam->frame_current = &cam->frame_tmp; if (!(cam->vpp_flag & VPP_DECOMPRESSION)) - DBG(5, "Isochronous transfer size: %li bytes/frame.", + DBG(5, "Isochronous transfer size: %lu bytes/frame", (unsigned long)t_size*2) DBG(5, "Starting the isochronous transfer...") + cam->streaming = 1; + /* Submit the URBs */ for (i = 0; i < W9968CF_URBS; i++) { err = usb_submit_urb(cam->urb[i], GFP_KERNEL); if (err) { - for (j = i-1; j >= 0; j--) - if (!usb_unlink_urb(cam->urb[j])) - usb_free_urb(cam->urb[j]); + cam->streaming = 0; + for (j = i-1; j >= 0; j--) { + usb_kill_urb(cam->urb[j]); + usb_free_urb(cam->urb[j]); + } DBG(1, "Couldn't send a transfer request to the " - "USB core (error #%d, %s).", err, + "USB core (error #%d, %s)", err, symbolic(urb_errlist, err)) + return err; } } - cam->streaming = 1; - return 0; } @@ -1023,6 +1041,9 @@ int err = 0; s8 i; + if (!cam->streaming) + return 0; + /* This avoids race conditions with usb_submit_urb() in the URB completition handler */ spin_lock_irqsave(&cam->urb_lock, lock_flags); @@ -1031,10 +1052,9 @@ for (i = W9968CF_URBS-1; i >= 0; i--) if (cam->urb[i]) { - if (!usb_unlink_urb(cam->urb[i])) { - usb_free_urb(cam->urb[i]); - cam->urb[i] = NULL; - } + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + cam->urb[i] = NULL; } if (cam->disconnected) @@ -1052,7 +1072,7 @@ } exit: - DBG(5, "Isochronous transfer stopped.") + DBG(5, "Isochronous transfer stopped") return 0; } @@ -1072,7 +1092,7 @@ if (res < 0) DBG(4, "Failed to write a register " - "(value 0x%04X, index 0x%02X, error #%d, %s).", + "(value 0x%04X, index 0x%02X, error #%d, %s)", value, index, res, symbolic(urb_errlist, res)) return (res >= 0) ? 0 : -1; @@ -1095,7 +1115,7 @@ if (res < 0) DBG(4, "Failed to read a register " - "(index 0x%02X, error #%d, %s).", + "(index 0x%02X, error #%d, %s)", index, res, symbolic(urb_errlist, res)) return (res >= 0) ? (int)(*buff) : -1; @@ -1120,7 +1140,7 @@ if (res < 0) DBG(4, "Failed to write the FSB registers " - "(error #%d, %s).", res, symbolic(urb_errlist, res)) + "(error #%d, %s)", res, symbolic(urb_errlist, res)) return (res >= 0) ? 0 : -1; } @@ -1270,7 +1290,7 @@ if (sda < 0) err += sda; if (sda == 1) { - DBG(6, "Couldn't receive the ACK.") + DBG(6, "Couldn't receive the ACK") err += -1; } @@ -1353,11 +1373,11 @@ if (!err) DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " - "value 0x%02X.", address, subaddress, value) + "value 0x%02X", address, subaddress, value) else DBG(5, "I2C write byte data failed, addr.0x%04X, " - "subaddr.0x%02X, value 0x%02X.", - address, subaddress, value) + "subaddr.0x%02X, value 0x%02X", + address, subaddress, value) return err; } @@ -1392,11 +1412,11 @@ if (!err) DBG(5, "I2C read byte data done, addr.0x%04X, " - "subaddr.0x%02X, value 0x%02X.", + "subaddr.0x%02X, value 0x%02X", address, subaddress, *value) else DBG(5, "I2C read byte data failed, addr.0x%04X, " - "subaddr.0x%02X, wrong value 0x%02X.", + "subaddr.0x%02X, wrong value 0x%02X", address, subaddress, *value) return err; @@ -1424,11 +1444,11 @@ err += w9968cf_write_sb(cam, 0x0000); if (!err) - DBG(5, "I2C read byte done, addr.0x%04X." - "value 0x%02X.", address, *value) + DBG(5, "I2C read byte done, addr.0x%04X, " + "value 0x%02X", address, *value) else - DBG(5, "I2C read byte failed, addr.0x%04X." - "wrong value 0x%02X.", address, *value) + DBG(5, "I2C read byte failed, addr.0x%04X, " + "wrong value 0x%02X", address, *value) return err; } @@ -1439,7 +1459,7 @@ w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, u16 address, u8 value) { - DBG(4, "i2c_write_byte() is an unsupported transfer mode.") + DBG(4, "i2c_write_byte() is an unsupported transfer mode") return -EINVAL; } @@ -1546,9 +1566,8 @@ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); const char* clientname = i2c_clientname(client); - if (cam->sensor_client == client) { + if (cam->sensor_client == client) cam->sensor_client = NULL; - } DBG(5, "I2C detach client [%s]", clientname) @@ -1593,9 +1612,9 @@ err = i2c_add_adapter(&cam->i2c_adapter); if (err) - DBG(1, "Failed to register the I2C adapter.") + DBG(1, "Failed to register the I2C adapter") else - DBG(5, "I2C adapter registered.") + DBG(5, "I2C adapter registered") return err; } @@ -1622,9 +1641,9 @@ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ if (err) - DBG(2, "Couldn't turn on the LED.") + DBG(2, "Couldn't turn on the LED") - DBG(5, "LED turned on.") + DBG(5, "LED turned on") return err; } @@ -1637,6 +1656,13 @@ --------------------------------------------------------------------------*/ static int w9968cf_init_chip(struct w9968cf_device* cam) { + unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2, + y0 = 0x0000, + u0 = y0 + hw_bufsize/2, + v0 = u0 + hw_bufsize/4, + y1 = v0 + hw_bufsize/4, + u1 = y1 + hw_bufsize/2, + v1 = u1 + hw_bufsize/4; int err = 0; err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ @@ -1645,23 +1671,25 @@ err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */ err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */ - err += w9968cf_write_reg(cam, 0x0000, 0x20); /* Y frame buf.0, low */ - err += w9968cf_write_reg(cam, 0x0000, 0x21); /* Y frame buf.0, high */ - err += w9968cf_write_reg(cam, 0xb000, 0x22); /* Y frame buf.1, low */ - err += w9968cf_write_reg(cam, 0x0004, 0x23); /* Y frame buf.1, high */ - err += w9968cf_write_reg(cam, 0x5800, 0x24); /* U frame buf.0, low */ - err += w9968cf_write_reg(cam, 0x0002, 0x25); /* U frame buf.0, high */ - err += w9968cf_write_reg(cam, 0x0800, 0x26); /* U frame buf.1, low */ - err += w9968cf_write_reg(cam, 0x0007, 0x27); /* U frame buf.1, high */ - err += w9968cf_write_reg(cam, 0x8400, 0x28); /* V frame buf.0, low */ - err += w9968cf_write_reg(cam, 0x0003, 0x29); /* V frame buf.0, high */ - err += w9968cf_write_reg(cam, 0x3400, 0x2a); /* V frame buf.1, low */ - err += w9968cf_write_reg(cam, 0x0008, 0x2b); /* V frame buf.1, high */ - - err += w9968cf_write_reg(cam, 0x6000, 0x32); /* JPEG bitstream buf 0 */ - err += w9968cf_write_reg(cam, 0x0009, 0x33); /* JPEG bitstream buf 0 */ - err += w9968cf_write_reg(cam, 0x2000, 0x34); /* JPEG bitstream buf 1 */ - err += w9968cf_write_reg(cam, 0x000d, 0x35); /* JPEG bitstream buf 1 */ + err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */ + err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */ + err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */ + err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */ + err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */ + err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V buf.0, high */ + + err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */ + err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */ + err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */ + err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */ + err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */ + err += w9968cf_write_reg(cam, v1 >> 16, 0x2b); /* V buf.1, high */ + + err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */ + err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */ + + err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */ + err += w9968cf_write_reg(cam, y1 >> 16, 0x35); /* JPEG bug 1 high */ err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */ err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ @@ -1672,9 +1700,9 @@ err += w9968cf_set_window(cam, cam->window); if (err) - DBG(1, "Chip initialization failed.") + DBG(1, "Chip initialization failed") else - DBG(5, "Chip successfully initialized.") + DBG(5, "Chip successfully initialized") return err; } @@ -1736,8 +1764,8 @@ break; } - /* FIXME: 'hardware double buffer' doesn't work when compressed video - is enabled (corrupted frames). */ + /* NOTE: due to memory issues, it is better to disable the hardware + double buffering during compression */ if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) reg_v |= 0x0080; @@ -1761,16 +1789,15 @@ cam->hw_palette = hw_palette; /* Settings changed, so we clear the frame buffers */ - memset(cam->frame[0].buffer, 0, - cam->nbuffers*w9968cf_get_max_bufsize(cam)); + memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size); - DBG(4, "Palette is %s, depth is %d bpp.", + DBG(4, "Palette is %s, depth is %u bpp", symbolic(v4l1_plist, pict.palette), pict.depth) return 0; error: - DBG(1, "Failed to change picture settings.") + DBG(1, "Failed to change picture settings") return err; } @@ -1921,21 +1948,20 @@ cam->hw_height = h; /* Settings changed, so we clear the frame buffers */ - memset(cam->frame[0].buffer, 0, - cam->nbuffers*w9968cf_get_max_bufsize(cam)); + memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size); - DBG(4, "The capture area is %dx%d, Offset (x,y)=(%d,%d).", + DBG(4, "The capture area is %dx%d, Offset (x,y)=(%u,%u)", win.width, win.height, win.x, win.y) - PDBGG("x=%d ,y=%d, w=%d, h=%d, ax=%d, ay=%d, s_win.x=%d, s_win.y=%d, " - "cw=%d, ch=%d, win.x=%d ,win.y=%d, win.width=%d, win.height=%d", + PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, " + "cw=%u, ch=%u, win.x=%u, win.y=%u, win.width=%u, win.height=%u", x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y, win.width, win.height) return 0; error: - DBG(1, "Failed to change the capture area size.") + DBG(1, "Failed to change the capture area size") return err; } @@ -1959,7 +1985,7 @@ Return the depth corresponding to the given palette. Palette _must_ be supported ! --------------------------------------------------------------------------*/ -static u16 w9968cf_valid_depth(u16 palette) +static inline u16 w9968cf_valid_depth(u16 palette) { u8 i=0; while (w9968cf_formatlist[i].palette != palette) @@ -1996,10 +2022,12 @@ if ((*width < cam->minwidth) || (*height < cam->minheight)) return -ERANGE; - maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) - && w9968cf_vppmod_present ? W9968CF_MAX_WIDTH : cam->maxwidth; - maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) - && w9968cf_vppmod_present ? W9968CF_MAX_HEIGHT : cam->maxheight; + maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && + w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) + : cam->maxwidth; + maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && + w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) + : cam->maxheight; if (*width > maxw) *width = maxw; @@ -2011,7 +2039,7 @@ *height &= ~15L; } - PDBGG("Window size adjusted w=%d, h=%d ", *width, *height) + PDBGG("Window size adjusted w=%u, h=%u ", *width, *height) return 0; } @@ -2050,7 +2078,7 @@ spin_unlock_irqrestore(&cam->flist_lock, lock_flags); - DBG(6, "Frame #%d pushed into the FIFO list. Position %d.", f_num, f) + DBG(6, "Frame #%u pushed into the FIFO list. Position %u", f_num, f) } @@ -2074,7 +2102,7 @@ spin_unlock(&cam->flist_lock); - DBG(6,"Popped frame #%zd from the list.",*framep-&cam->frame[0]) + DBG(6,"Popped frame #%d from the list", (*framep)->number) } @@ -2086,7 +2114,7 @@ w9968cf_postprocess_frame(struct w9968cf_device* cam, struct w9968cf_frame_t* fr) { - void *pIn = fr->buffer, *pOut = cam->vpp_buffer, *tmp; + void *pIn = fr->buffer, *pOut = cam->frame_vpp.buffer, *tmp; u16 w = cam->window.width, h = cam->window.height, d = cam->picture.depth, @@ -2102,41 +2130,41 @@ if (cam->vpp_flag & VPP_DECOMPRESSION) { memcpy(pOut, pIn, fr->length); _PSWAP(pIn, pOut) - err = (*w9968cf_vpp_decode)(pIn, fr->length, hw_w, hw_h, pOut); - PDBGG("Compressed frame length: %li",(unsigned long)fr->length) + err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut); + PDBGG("Compressed frame length: %lu",(unsigned long)fr->length) fr->length = (hw_w*hw_h*hw_d)/8; _PSWAP(pIn, pOut) if (err) { DBG(4, "An error occurred while decoding the frame: " - "%s.", symbolic(decoder_errlist, err)) + "%s", symbolic(decoder_errlist, err)) return err; } else DBG(6, "Frame decoded") } if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) { - (*w9968cf_vpp_swap_yuvbytes)(pIn, fr->length); - DBG(6, "Original UYVY component ordering changed.") + w9968cf_vpp->swap_yuvbytes(pIn, fr->length); + DBG(6, "Original UYVY component ordering changed") } if (cam->vpp_flag & VPP_UPSCALE) { - (*w9968cf_vpp_scale_up)(pIn, pOut, hw_w, hw_h, hw_d, w, h); + w9968cf_vpp->scale_up(pIn, pOut, hw_w, hw_h, hw_d, w, h); fr->length = (w*h*hw_d)/8; _PSWAP(pIn, pOut) - DBG(6, "Vertical up-scaling done: %d,%d,%dbpp->%d,%d", + DBG(6, "Vertical up-scaling done: %u,%u,%ubpp->%u,%u", hw_w, hw_h, hw_d, w, h) } if (cam->vpp_flag & VPP_UYVY_TO_RGBX) { - (*w9968cf_vpp_uyvy_to_rgbx)(pIn, fr->length, pOut, fmt, rgb); + w9968cf_vpp->uyvy_to_rgbx(pIn, fr->length, pOut, fmt, rgb); fr->length = (w*h*d)/8; _PSWAP(pIn, pOut) - DBG(6, "UYVY-16bit to %s conversion done.", + DBG(6, "UYVY-16bit to %s conversion done", symbolic(v4l1_plist, fmt)) } if (pOut == fr->buffer) - memcpy(fr->buffer, cam->vpp_buffer, fr->length); + memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length); return 0; } @@ -2144,7 +2172,7 @@ /**************************************************************************** - * CMOS sensor control routines * + * Image sensor control routines * ****************************************************************************/ static int @@ -2184,17 +2212,17 @@ struct i2c_client* c = cam->sensor_client; int rc = 0; - if (c->driver->command) { - rc = c->driver->command(cam->sensor_client, cmd, arg); - /* The I2C driver returns -EPERM on non-supported controls */ - return (rc < 0 && rc != -EPERM) ? rc : 0; - } else - return -ENODEV; + if (!c || !c->driver || !c->driver->command) + return -EINVAL; + + rc = c->driver->command(c, cmd, arg); + /* The I2C driver returns -EPERM on non-supported controls */ + return (rc < 0 && rc != -EPERM) ? rc : 0; } /*-------------------------------------------------------------------------- - Update some settings of the CMOS sensor. + Update some settings of the image sensor. Returns: 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_sensor_update_settings(struct w9968cf_device* cam) @@ -2242,7 +2270,7 @@ /*-------------------------------------------------------------------------- - Get some current picture settings from the CMOS sensor and update the + Get some current picture settings from the image sensor and update the internal 'picture' structure of the camera. Returns: 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ @@ -2270,10 +2298,10 @@ return err; cam->picture.hue = v; - DBG(5, "Got picture settings from the CMOS sensor.") + DBG(5, "Got picture settings from the image sensor") PDBGG("Brightness, contrast, hue, colour, whiteness are " - "%d,%d,%d,%d,%d.", cam->picture.brightness,cam->picture.contrast, + "%u,%u,%u,%u,%u", cam->picture.brightness,cam->picture.contrast, cam->picture.hue, cam->picture.colour, cam->picture.whiteness) return 0; @@ -2281,7 +2309,7 @@ /*-------------------------------------------------------------------------- - Update picture settings of the CMOS sensor. + Update picture settings of the image sensor. Returns: 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int @@ -2296,7 +2324,7 @@ pict.contrast); if (err) goto fail; - DBG(4, "Contrast changed from %d to %d.", + DBG(4, "Contrast changed from %u to %u", cam->picture.contrast, pict.contrast) cam->picture.contrast = pict.contrast; } @@ -2307,7 +2335,7 @@ pict.brightness); if (err) goto fail; - DBG(4, "Brightness changed from %d to %d.", + DBG(4, "Brightness changed from %u to %u", cam->picture.brightness, pict.brightness) cam->picture.brightness = pict.brightness; } @@ -2317,7 +2345,7 @@ pict.colour); if (err) goto fail; - DBG(4, "Colour changed from %d to %d.", + DBG(4, "Colour changed from %u to %u", cam->picture.colour, pict.colour) cam->picture.colour = pict.colour; } @@ -2327,7 +2355,7 @@ pict.hue); if (err) goto fail; - DBG(4, "Hue changed from %d to %d.", + DBG(4, "Hue changed from %u to %u", cam->picture.hue, pict.hue) cam->picture.hue = pict.hue; } @@ -2335,7 +2363,7 @@ return 0; fail: - DBG(4, "Failed to change sensor picture setting.") + DBG(4, "Failed to change sensor picture setting") return err; } @@ -2346,7 +2374,7 @@ ****************************************************************************/ /*-------------------------------------------------------------------------- - This function is called when a supported CMOS sensor is detected. + This function is called when a supported image sensor is detected. Return 0 if the initialization succeeds, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_sensor_init(struct w9968cf_device* cam) @@ -2376,7 +2404,7 @@ cam->minheight = 48; break; default: - DBG(1, "Not supported CMOS sensor detected for %s.", + DBG(1, "Not supported image sensor detected for %s", symbolic(camlist, cam->id)) return -EINVAL; } @@ -2386,7 +2414,7 @@ case CC_OV7620: cam->start_cropx = 287; cam->start_cropy = 35; - /* Seems to work around a bug in the CMOS sensor */ + /* Seems to work around a bug in the image sensor */ cam->vs_polarity = 1; cam->hs_polarity = 1; break; @@ -2405,14 +2433,14 @@ cam->sensor_initialized = 1; - DBG(2, "%s CMOS sensor initialized.", symbolic(senlist, cam->sensor)) + DBG(2, "%s image sensor initialized", symbolic(senlist, cam->sensor)) return 0; error: cam->sensor_initialized = 0; cam->sensor = CC_UNKNOWN; - DBG(1, "CMOS sensor initialization failed for %s (/dev/video%d). " - "Try to detach and attach this device again.", + DBG(1, "Image sensor initialization failed for %s (/dev/video%d). " + "Try to detach and attach this device again", symbolic(camlist, cam->id), cam->v4ldev->minor) return err; } @@ -2436,7 +2464,6 @@ cam->users = 0; cam->disconnected = 0; - cam->usbdev = udev; cam->id = mod_id; cam->sensor = CC_UNKNOWN; cam->sensor_initialized = 0; @@ -2521,6 +2548,7 @@ else cam->picture.palette = W9968CF_PALETTE_DECOMP_ON; } + cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1) ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB; @@ -2533,105 +2561,120 @@ cam->window.clipcount = 0; cam->window.flags = 0; - /* If the video post-processing module is not present, some paramaters - must be overridden: */ - if (!w9968cf_vppmod_present) { - if (cam->decompression == 1) - cam->decompression = 2; - cam->upscaling = 0; - if (cam->picture.palette != VIDEO_PALETTE_UYVY) - cam->force_palette = 0; - cam->picture.palette = VIDEO_PALETTE_UYVY; - } - - cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); - - DBG(3, "%s configured with settings #%d:", + DBG(3, "%s configured with settings #%u:", symbolic(camlist, cam->id), dev_nr) - DBG(3, "- Data packet size for USB isochrnous transfer: %d bytes.", + DBG(3, "- Data packet size for USB isochrnous transfer: %u bytes", wMaxPacketSize[cam->altsetting-1]) - DBG(3, "- Number of requested video frame buffers: %d", + DBG(3, "- Number of requested video frame buffers: %u", cam->max_buffers) if (cam->double_buffer) - DBG(3, "- Hardware double buffering enabled.") + DBG(3, "- Hardware double buffering enabled") else - DBG(3, "- Hardware double buffering disabled.") + DBG(3, "- Hardware double buffering disabled") if (cam->filter_type == 0) - DBG(3, "- Video filtering disabled.") + DBG(3, "- Video filtering disabled") else if (cam->filter_type == 1) - DBG(3, "- Video filtering enabled: type 1-2-1.") + DBG(3, "- Video filtering enabled: type 1-2-1") else if (cam->filter_type == 2) - DBG(3, "- Video filtering enabled: type 2-3-6-3-2.") + DBG(3, "- Video filtering enabled: type 2-3-6-3-2") if (cam->clamping) - DBG(3, "- Video data clamping (CCIR-601 format) enabled.") + DBG(3, "- Video data clamping (CCIR-601 format) enabled") else - DBG(3, "- Video data clamping (CCIR-601 format) disabled.") + DBG(3, "- Video data clamping (CCIR-601 format) disabled") if (cam->largeview) - DBG(3, "- Large view enabled.") + DBG(3, "- Large view enabled") else - DBG(3, "- Large view disabled.") + DBG(3, "- Large view disabled") if ((cam->decompression) == 0 && (!cam->force_palette)) - DBG(3, "- Decompression disabled.") + DBG(3, "- Decompression disabled") else if ((cam->decompression) == 1 && (!cam->force_palette)) - DBG(3, "- Decompression forced.") + DBG(3, "- Decompression forced") else if ((cam->decompression) == 2 && (!cam->force_palette)) - DBG(3, "- Decompression allowed.") + DBG(3, "- Decompression allowed") if (cam->upscaling) - DBG(3, "- Software image scaling enabled.") + DBG(3, "- Software image scaling enabled") else - DBG(3, "- Software image scaling disabled.") + DBG(3, "- Software image scaling disabled") if (cam->force_palette) - DBG(3, "- Image palette forced to %s.", + DBG(3, "- Image palette forced to %s", symbolic(v4l1_plist, cam->picture.palette)) if (cam->force_rgb) - DBG(3, "- RGB component ordering will be used instead of BGR.") + DBG(3, "- RGB component ordering will be used instead of BGR") if (cam->auto_brt) - DBG(3, "- Auto brightness enabled.") + DBG(3, "- Auto brightness enabled") else - DBG(3, "- Auto brightness disabled.") + DBG(3, "- Auto brightness disabled") if (cam->auto_exp) - DBG(3, "- Auto exposure enabled.") + DBG(3, "- Auto exposure enabled") else - DBG(3, "- Auto exposure disabled.") + DBG(3, "- Auto exposure disabled") if (cam->backlight) - DBG(3, "- Backlight exposure algorithm enabled.") + DBG(3, "- Backlight exposure algorithm enabled") else - DBG(3, "- Backlight exposure algorithm disabled.") + DBG(3, "- Backlight exposure algorithm disabled") if (cam->mirror) - DBG(3, "- Mirror enabled.") + DBG(3, "- Mirror enabled") else - DBG(3, "- Mirror disabled.") + DBG(3, "- Mirror disabled") if (cam->bandfilt) - DBG(3, "- Banding filter enabled.") + DBG(3, "- Banding filter enabled") else - DBG(3, "- Banding filter disabled.") + DBG(3, "- Banding filter disabled") - DBG(3, "- Power lighting frequency: %d", cam->lightfreq) + DBG(3, "- Power lighting frequency: %u", cam->lightfreq) if (cam->clockdiv == -1) - DBG(3, "- Automatic clock divisor enabled.") + DBG(3, "- Automatic clock divisor enabled") else DBG(3, "- Clock divisor: %d", cam->clockdiv) if (cam->monochrome) - DBG(3, "- CMOS sensor used as monochrome.") + DBG(3, "- Image sensor used as monochrome") else - DBG(3, "- CMOS sensor not used as monochrome.") + DBG(3, "- Image sensor not used as monochrome") +} + + +/*-------------------------------------------------------------------------- + If the video post-processing module is not loaded, some parameters + must be overridden. + --------------------------------------------------------------------------*/ +static void w9968cf_adjust_configuration(struct w9968cf_device* cam) +{ + if (!w9968cf_vpp) { + if (cam->decompression == 1) { + cam->decompression = 2; + DBG(2, "Video post-processing module not found: " + "'decompression' parameter forced to 2") + } + if (cam->upscaling) { + cam->upscaling = 0; + DBG(2, "Video post-processing module not found: " + "'upscaling' parameter forced to 0") + } + if (cam->picture.palette != VIDEO_PALETTE_UYVY) { + cam->force_palette = 0; + DBG(2, "Video post-processing module not found: " + "'force_palette' parameter forced to 0") + } + cam->picture.palette = VIDEO_PALETTE_UYVY; + cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); + } } @@ -2654,8 +2697,6 @@ kfree(cam->data_buffer); up(&w9968cf_devlist_sem); - - DBG(5, "Resources released.") } @@ -2669,38 +2710,45 @@ struct w9968cf_device* cam; int err; + /* This the only safe way to prevent race conditions with disconnect */ + if (!down_read_trylock(&w9968cf_disconnect)) + return -ERESTARTSYS; + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); down(&cam->dev_sem); if (cam->sensor == CC_UNKNOWN) { - DBG(2, "No supported CMOS sensor has been detected by the " + DBG(2, "No supported image sensor has been detected by the " "'ovcamchip' module for the %s (/dev/video%d). Make " - "sure it is loaded *before* the 'w9968cf' module.", + "sure it is loaded *before* (re)connecting the camera.", symbolic(camlist, cam->id), cam->v4ldev->minor) up(&cam->dev_sem); + up_read(&w9968cf_disconnect); return -ENODEV; } if (cam->users) { - DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.", + DBG(2, "%s (/dev/video%d) has been already occupied by '%s'", symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command) if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { up(&cam->dev_sem); + up_read(&w9968cf_disconnect); return -EWOULDBLOCK; } -retry: up(&cam->dev_sem); - err = wait_event_interruptible(cam->open, cam->disconnected || - (cam->users == 0)); - if (err) + err = wait_event_interruptible_exclusive(cam->open, + cam->disconnected || + !cam->users); + if (err) { + up_read(&w9968cf_disconnect); return err; - if (cam->disconnected) + } + if (cam->disconnected) { + up_read(&w9968cf_disconnect); return -ENODEV; + } down(&cam->dev_sem); - /*recheck - there may be several waiters */ - if (cam->users) - goto retry; } DBG(5, "Opening '%s', /dev/video%d ...", @@ -2709,8 +2757,9 @@ cam->streaming = 0; cam->misconfigured = 0; - if (!w9968cf_vppmod_present) - w9968cf_vppmod_detect(); + if (!w9968cf_vpp) + if ((err = w9968cf_vppmod_detect(cam))) + goto out; if ((err = w9968cf_allocate_memory(cam))) goto deallocate_memory; @@ -2728,15 +2777,19 @@ init_waitqueue_head(&cam->wait_queue); + DBG(5, "Video device is open") + up(&cam->dev_sem); + up_read(&w9968cf_disconnect); - DBG(5, "Video device is open.") return 0; deallocate_memory: w9968cf_deallocate_memory(cam); - DBG(2, "Failed to open the video device.") +out: + DBG(2, "Failed to open the video device") up(&cam->dev_sem); + up_read(&w9968cf_disconnect); return err; } @@ -2751,6 +2804,8 @@ w9968cf_stop_transfer(cam); + w9968cf_vppmod_release(cam); + if (cam->disconnected) { w9968cf_release_resources(cam); up(&cam->dev_sem); @@ -2760,10 +2815,9 @@ cam->users--; w9968cf_deallocate_memory(cam); + wake_up_interruptible_nr(&cam->open, 1); - wake_up_interruptible(&cam->open); - - DBG(5, "Video device closed.") + DBG(5, "Video device closed") up(&cam->dev_sem); return 0; } @@ -2785,7 +2839,7 @@ return -ERESTARTSYS; if (cam->disconnected) { - DBG(2, "Device not present.") + DBG(2, "Device not present") up(&cam->fileop_sem); return -ENODEV; } @@ -2817,7 +2871,7 @@ fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1]; - if (w9968cf_vppmod_present) + if (w9968cf_vpp) w9968cf_postprocess_frame(cam, fr); if (count > fr->length) @@ -2832,7 +2886,7 @@ fr->status = F_UNUSED; - DBG(5, "%zd bytes read.", count) + DBG(5, "%zu bytes read", count) up(&cam->fileop_sem); return count; @@ -2844,25 +2898,25 @@ struct w9968cf_device* cam = (struct w9968cf_device*) video_get_drvdata(video_devdata(filp)); unsigned long vsize = vma->vm_end - vma->vm_start, - psize = cam->nbuffers * w9968cf_get_max_bufsize(cam), + psize = cam->nbuffers * cam->frame[0].size, start = vma->vm_start, pos = (unsigned long)cam->frame[0].buffer, page; if (cam->disconnected) { - DBG(2, "Device not present.") + DBG(2, "Device not present") return -ENODEV; } if (cam->misconfigured) { - DBG(2, "The camera is misconfigured. Close and open it again.") + DBG(2, "The camera is misconfigured. Close and open it again") return -EIO; } - PDBGG("mmapping %li bytes...", vsize) + PDBGG("mmapping %lu bytes...", vsize) - if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) - return -EAGAIN; + if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) + return -EINVAL; while (vsize > 0) { page = kvirt_to_pa(pos) + vma->vm_pgoff; @@ -2871,10 +2925,10 @@ return -EAGAIN; start += PAGE_SIZE; pos += PAGE_SIZE; - vsize = (vsize > PAGE_SIZE) ? vsize-PAGE_SIZE : 0; + vsize -= PAGE_SIZE; } - DBG(5, "mmap method successfully called.") + DBG(5, "mmap method successfully called") return 0; } @@ -2892,7 +2946,7 @@ return -ERESTARTSYS; if (cam->disconnected) { - DBG(2, "Device not present.") + DBG(2, "Device not present") up(&cam->fileop_sem); return -ENODEV; } @@ -2903,19 +2957,17 @@ return -EIO; } - err = w9968cf_v4l_ioctl(inode, filp, cmd, (void* )arg); + err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg); up(&cam->fileop_sem); return err; } -static int -w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, - unsigned int cmd, void* arg) +static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) { struct w9968cf_device* cam; - void __user *user_arg = (void __user *)arg; const char* v4l1_ioctls[] = { "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", @@ -2944,22 +2996,24 @@ }; sprintf(cap.name, "W996[87]CF USB Camera #%d", cam->v4ldev->minor); - cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present) - ? W9968CF_MAX_WIDTH : cam->maxwidth; - cap.maxheight = (cam->upscaling && w9968cf_vppmod_present) - ? W9968CF_MAX_HEIGHT : cam->maxheight; + cap.maxwidth = (cam->upscaling && w9968cf_vpp) + ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) + : cam->maxwidth; + cap.maxheight = (cam->upscaling && w9968cf_vpp) + ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) + : cam->maxheight; - if (copy_to_user(user_arg, &cap, sizeof(cap))) + if (copy_to_user(arg, &cap, sizeof(cap))) return -EFAULT; - DBG(5, "VIDIOCGCAP successfully called.") + DBG(5, "VIDIOCGCAP successfully called") return 0; } case VIDIOCGCHAN: /* get video channel informations */ { struct video_channel chan; - if (copy_from_user(&chan, user_arg, sizeof(chan))) + if (copy_from_user(&chan, arg, sizeof(chan))) return -EFAULT; if (chan.channel != 0) @@ -2971,10 +3025,10 @@ chan.type = VIDEO_TYPE_CAMERA; chan.norm = VIDEO_MODE_AUTO; - if (copy_to_user(user_arg, &chan, sizeof(chan))) + if (copy_to_user(arg, &chan, sizeof(chan))) return -EFAULT; - DBG(5, "VIDIOCGCHAN successfully called.") + DBG(5, "VIDIOCGCHAN successfully called") return 0; } @@ -2982,13 +3036,13 @@ { struct video_channel chan; - if (copy_from_user(&chan, user_arg, sizeof(chan))) + if (copy_from_user(&chan, arg, sizeof(chan))) return -EFAULT; if (chan.channel != 0) return -EINVAL; - DBG(5, "VIDIOCSCHAN successfully called.") + DBG(5, "VIDIOCSCHAN successfully called") return 0; } @@ -2997,10 +3051,10 @@ if (w9968cf_sensor_get_picture(cam)) return -EIO; - if (copy_to_user(user_arg, &cam->picture, sizeof(cam->picture))) + if (copy_to_user(arg, &cam->picture, sizeof(cam->picture))) return -EFAULT; - DBG(5, "VIDIOCGPICT successfully called.") + DBG(5, "VIDIOCGPICT successfully called") return 0; } @@ -3009,19 +3063,19 @@ struct video_picture pict; int err = 0; - if (copy_from_user(&pict, user_arg, sizeof(pict))) + if (copy_from_user(&pict, arg, sizeof(pict))) return -EFAULT; - if ( (cam->force_palette || !w9968cf_vppmod_present) + if ( (cam->force_palette || !w9968cf_vpp) && pict.palette != cam->picture.palette ) { - DBG(4, "Palette %s rejected. Only %s is allowed.", + DBG(4, "Palette %s rejected: only %s is allowed", symbolic(v4l1_plist, pict.palette), symbolic(v4l1_plist, cam->picture.palette)) return -EINVAL; } if (!w9968cf_valid_palette(pict.palette)) { - DBG(4, "Palette %s not supported. VIDIOCSPICT failed.", + DBG(4, "Palette %s not supported. VIDIOCSPICT failed", symbolic(v4l1_plist, pict.palette)) return -EINVAL; } @@ -3030,14 +3084,14 @@ if (cam->decompression == 0) { if (w9968cf_need_decompression(pict.palette)) { DBG(4, "Decompression disabled: palette %s is not " - "allowed. VIDIOCSPICT failed.", + "allowed. VIDIOCSPICT failed", symbolic(v4l1_plist, pict.palette)) return -EINVAL; } } else if (cam->decompression == 1) { if (!w9968cf_need_decompression(pict.palette)) { DBG(4, "Decompression forced: palette %s is not " - "allowed. VIDIOCSPICT failed.", + "allowed. VIDIOCSPICT failed", symbolic(v4l1_plist, pict.palette)) return -EINVAL; } @@ -3045,8 +3099,8 @@ } if (pict.depth != w9968cf_valid_depth(pict.palette)) { - DBG(4, "Requested depth %d bpp is not valid for %s " - "palette: ignored and changed to %d bpp.", + DBG(4, "Requested depth %u bpp is not valid for %s " + "palette: ignored and changed to %u bpp", pict.depth, symbolic(v4l1_plist, pict.palette), w9968cf_valid_depth(pict.palette)) pict.depth = w9968cf_valid_depth(pict.palette); @@ -3079,7 +3133,7 @@ return -EIO; - DBG(5, "VIDIOCSPICT successfully called.") + DBG(5, "VIDIOCSPICT successfully called") return 0; } @@ -3088,11 +3142,11 @@ struct video_window win; int err = 0; - if (copy_from_user(&win, user_arg, sizeof(win))) + if (copy_from_user(&win, arg, sizeof(win))) return -EFAULT; - DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%d, " - "x=%d, y=%d, %dx%d", win.clipcount, win.flags, + DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, " + "x=%u, y=%u, %ux%u", win.clipcount, win.flags, win.x, win.y, win.width, win.height) if (win.clipcount != 0 || win.flags != 0) @@ -3100,8 +3154,8 @@ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, (u16*)&win.height))) { - DBG(4, "Resolution not supported (%dx%d)." - "VIDIOCSWIN failed.", win.width, win.height) + DBG(4, "Resolution not supported (%ux%u). " + "VIDIOCSWIN failed", win.width, win.height) return err; } @@ -3142,10 +3196,10 @@ case VIDIOCGWIN: /* get current window properties */ { - if (copy_to_user(user_arg, &cam->window, sizeof(struct video_window))) + if (copy_to_user(arg,&cam->window,sizeof(struct video_window))) return -EFAULT; - DBG(5, "VIDIOCGWIN successfully called.") + DBG(5, "VIDIOCGWIN successfully called") return 0; } @@ -3154,16 +3208,16 @@ struct video_mbuf mbuf; u8 i; - mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam); + mbuf.size = cam->nbuffers * cam->frame[0].size; mbuf.frames = cam->nbuffers; for (i = 0; i < cam->nbuffers; i++) mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer - (unsigned long)cam->frame[0].buffer; - if (copy_to_user(user_arg, &mbuf, sizeof(mbuf))) + if (copy_to_user(arg, &mbuf, sizeof(mbuf))) return -EFAULT; - DBG(5, "VIDIOCGMBUF successfully called.") + DBG(5, "VIDIOCGMBUF successfully called") return 0; } @@ -3173,22 +3227,22 @@ struct w9968cf_frame_t* fr; int err = 0; - if (copy_from_user(&mmap, user_arg, sizeof(mmap))) + if (copy_from_user(&mmap, arg, sizeof(mmap))) return -EFAULT; - DBG(6, "VIDIOCMCAPTURE called: frame #%d, format=%s, %dx%d", + DBG(6, "VIDIOCMCAPTURE called: frame #%u, format=%s, %dx%d", mmap.frame, symbolic(v4l1_plist, mmap.format), mmap.width, mmap.height) if (mmap.frame >= cam->nbuffers) { - DBG(4, "Invalid frame number (%d). " - "VIDIOCMCAPTURE failed.", mmap.frame) + DBG(4, "Invalid frame number (%u). " + "VIDIOCMCAPTURE failed", mmap.frame) return -EINVAL; } if (mmap.format!=cam->picture.palette && - (cam->force_palette || !w9968cf_vppmod_present)) { - DBG(4, "Palette %s rejected. Only %s is allowed.", + (cam->force_palette || !w9968cf_vpp)) { + DBG(4, "Palette %s rejected: only %s is allowed", symbolic(v4l1_plist, mmap.format), symbolic(v4l1_plist, cam->picture.palette)) return -EINVAL; @@ -3196,7 +3250,7 @@ if (!w9968cf_valid_palette(mmap.format)) { DBG(4, "Palette %s not supported. " - "VIDIOCMCAPTURE failed.", + "VIDIOCMCAPTURE failed", symbolic(v4l1_plist, mmap.format)) return -EINVAL; } @@ -3205,14 +3259,14 @@ if (cam->decompression == 0) { if (w9968cf_need_decompression(mmap.format)) { DBG(4, "Decompression disabled: palette %s is not " - "allowed. VIDIOCSPICT failed.", + "allowed. VIDIOCSPICT failed", symbolic(v4l1_plist, mmap.format)) return -EINVAL; } } else if (cam->decompression == 1) { if (!w9968cf_need_decompression(mmap.format)) { DBG(4, "Decompression forced: palette %s is not " - "allowed. VIDIOCSPICT failed.", + "allowed. VIDIOCSPICT failed", symbolic(v4l1_plist, mmap.format)) return -EINVAL; } @@ -3222,7 +3276,7 @@ if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, (u16*)&mmap.height))) { DBG(4, "Resolution not supported (%dx%d). " - "VIDIOCMCAPTURE failed.", + "VIDIOCMCAPTURE failed", mmap.width, mmap.height) return err; } @@ -3239,7 +3293,7 @@ if(*cam->requested_frame || cam->frame_current->queued) { DBG(6, "VIDIOCMCAPTURE. Change settings for " - "frame #%d: %dx%d, format %s. Wait...", + "frame #%u: %dx%d, format %s. Wait...", mmap.frame, mmap.width, mmap.height, symbolic(v4l1_plist, mmap.format)) err = wait_event_interruptible @@ -3274,7 +3328,7 @@ } else if (fr->queued) { - DBG(6, "Wait until frame #%d is free.", mmap.frame) + DBG(6, "Wait until frame #%u is free", mmap.frame) err = wait_event_interruptible(cam->wait_queue, cam->disconnected || @@ -3286,7 +3340,7 @@ } w9968cf_push_frame(cam, mmap.frame); - DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame) + DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame) return 0; } @@ -3296,23 +3350,23 @@ struct w9968cf_frame_t* fr; int err = 0; - if (copy_from_user(&f_num, user_arg, sizeof(f_num))) + if (copy_from_user(&f_num, arg, sizeof(f_num))) return -EFAULT; if (f_num >= cam->nbuffers) { - DBG(4, "Invalid frame number (%d). " - "VIDIOCMCAPTURE failed.", f_num) + DBG(4, "Invalid frame number (%u). " + "VIDIOCMCAPTURE failed", f_num) return -EINVAL; } - DBG(6, "VIDIOCSYNC called for frame #%d", f_num) + DBG(6, "VIDIOCSYNC called for frame #%u", f_num) fr = &cam->frame[f_num]; switch (fr->status) { case F_UNUSED: if (!fr->queued) { - DBG(4, "VIDIOSYNC: Frame #%d not requested!", + DBG(4, "VIDIOSYNC: Frame #%u not requested!", f_num) return -EFAULT; } @@ -3330,12 +3384,12 @@ break; } - if (w9968cf_vppmod_present) + if (w9968cf_vpp) w9968cf_postprocess_frame(cam, fr); fr->status = F_UNUSED; - DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num) + DBG(5, "VIDIOCSYNC(%u) successfully called", f_num) return 0; } @@ -3349,10 +3403,10 @@ .teletext = VIDEO_NO_UNIT, }; - if (copy_to_user(user_arg, &unit, sizeof(unit))) + if (copy_to_user(arg, &unit, sizeof(unit))) return -EFAULT; - DBG(5, "VIDIOCGUNIT successfully called.") + DBG(5, "VIDIOCGUNIT successfully called") return 0; } @@ -3361,17 +3415,17 @@ case VIDIOCGFBUF: { - if (clear_user(user_arg, sizeof(struct video_buffer))) + if (clear_user(arg, sizeof(struct video_buffer))) return -EFAULT; - DBG(5, "VIDIOCGFBUF successfully called.") + DBG(5, "VIDIOCGFBUF successfully called") return 0; } case VIDIOCGTUNER: { struct video_tuner tuner; - if (copy_from_user(&tuner, user_arg, sizeof(tuner))) + if (copy_from_user(&tuner, arg, sizeof(tuner))) return -EFAULT; if (tuner.tuner != 0) @@ -3384,17 +3438,17 @@ tuner.mode = VIDEO_MODE_AUTO; tuner.signal = 0xffff; - if (copy_to_user(user_arg, &tuner, sizeof(tuner))) + if (copy_to_user(arg, &tuner, sizeof(tuner))) return -EFAULT; - DBG(5, "VIDIOCGTUNER successfully called.") + DBG(5, "VIDIOCGTUNER successfully called") return 0; } case VIDIOCSTUNER: { struct video_tuner tuner; - if (copy_from_user(&tuner, user_arg, sizeof(tuner))) + if (copy_from_user(&tuner, arg, sizeof(tuner))) return -EFAULT; if (tuner.tuner != 0) @@ -3403,7 +3457,7 @@ if (tuner.mode != VIDEO_MODE_AUTO) return -EINVAL; - DBG(5, "VIDIOCSTUNER successfully called.") + DBG(5, "VIDIOCSTUNER successfully called") return 0; } @@ -3423,7 +3477,7 @@ "(type 0x%01X, " "n. 0x%01X, " "dir. 0x%01X, " - "size 0x%02X).", + "size 0x%02X)", V4L1_IOCTL(cmd), _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) @@ -3434,7 +3488,7 @@ "type 0x%01X, " "n. 0x%01X, " "dir. 0x%01X, " - "size 0x%02X.", + "size 0x%02X", V4L1_IOCTL(cmd), _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) @@ -3480,15 +3534,27 @@ if (udev->descriptor.idVendor == winbond_id_table[0].idVendor && udev->descriptor.idProduct == winbond_id_table[0].idProduct) mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */ - else if (udev->descriptor.idVendor == winbond_id_table[1].idVendor && udev->descriptor.idProduct == winbond_id_table[1].idProduct) mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */ - else return -ENODEV; - DBG(2, "%s detected.", symbolic(camlist, mod_id)) + cam = (struct w9968cf_device*) + kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + memset(cam, 0, sizeof(*cam)); + + init_MUTEX(&cam->dev_sem); + down(&cam->dev_sem); + + cam->usbdev = udev; + /* NOTE: a local copy is used to avoid possible race conditions */ + memcpy(&cam->dev, &udev->dev, sizeof(struct device)); + + DBG(2, "%s detected", symbolic(camlist, mod_id)) if (simcams > W9968CF_MAX_DEVICES) simcams = W9968CF_SIMCAMS; @@ -3501,27 +3567,15 @@ if (sc >= simcams) { DBG(2, "Device rejected: too many connected cameras " - "(max. %d)", simcams) - return -EPERM; - } - - cam = (struct w9968cf_device*) - kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL); - - if (!cam) { - DBG(1, "Couldn't allocate %zd bytes of kernel memory.", - sizeof(struct w9968cf_device)) - err = -ENOMEM; + "(max. %u)", simcams) + err = -EPERM; goto fail; } - memset(cam, 0, sizeof(*cam)); - init_MUTEX(&cam->dev_sem); - down(&cam->dev_sem); /* Allocate 2 bytes of memory for camera control USB transfers */ if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) { - DBG(1,"Couldn't allocate memory for camera control transfers.") + DBG(1,"Couldn't allocate memory for camera control transfers") err = -ENOMEM; goto fail; } @@ -3530,7 +3584,7 @@ /* Allocate 8 bytes of memory for USB data transfers to the FSB */ if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) { DBG(1, "Couldn't allocate memory for data " - "transfers to the FSB.") + "transfers to the FSB") err = -ENOMEM; goto fail; } @@ -3539,7 +3593,7 @@ /* Register the V4L device */ cam->v4ldev = video_device_alloc(); if (!cam->v4ldev) { - DBG(1, "Could not allocate memory for a V4L structure.") + DBG(1, "Could not allocate memory for a V4L structure") err = -ENOMEM; goto fail; } @@ -3552,19 +3606,20 @@ cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); + cam->v4ldev->dev = &cam->dev; err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); if (err) { - DBG(1, "V4L device registration failed.") + DBG(1, "V4L device registration failed") if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(2, "Couldn't find a free /dev/videoX node.") + DBG(2, "Couldn't find a free /dev/videoX node") video_nr[dev_nr] = -1; dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; goto fail; } - DBG(2, "V4L device registered as /dev/video%d.", cam->v4ldev->minor) + DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev->minor) /* Set some basic constants */ w9968cf_configure_camera(cam, udev, mod_id, dev_nr); @@ -3579,22 +3634,19 @@ w9968cf_i2c_init(cam); - up(&cam->dev_sem); - usb_set_intfdata(intf, cam); + up(&cam->dev_sem); return 0; fail: /* Free unused memory */ - if (cam) { - if (cam->control_buffer) - kfree(cam->control_buffer); - if (cam->data_buffer) - kfree(cam->data_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - up(&cam->dev_sem); - kfree(cam); - } + if (cam->control_buffer) + kfree(cam->control_buffer); + if (cam->data_buffer) + kfree(cam->data_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + up(&cam->dev_sem); + kfree(cam); return err; } @@ -3604,28 +3656,26 @@ struct w9968cf_device* cam = (struct w9968cf_device*)usb_get_intfdata(intf); + down_write(&w9968cf_disconnect); + if (cam) { /* Prevent concurrent accesses to data */ down(&cam->dev_sem); - cam->streaming = 0; cam->disconnected = 1; DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)) - if (waitqueue_active(&cam->open)) - wake_up_interruptible(&cam->open); + wake_up_interruptible_all(&cam->open); if (cam->users) { DBG(2, "The device is open (/dev/video%d)! " "Process name: %s. Deregistration and memory " "deallocation are deferred on close.", cam->v4ldev->minor, cam->command) - cam->misconfigured = 1; - - if (waitqueue_active(&cam->wait_queue)) - wake_up_interruptible(&cam->wait_queue); + w9968cf_stop_transfer(cam); + wake_up_interruptible(&cam->wait_queue); } else w9968cf_release_resources(cam); @@ -3635,7 +3685,7 @@ kfree(cam); } - usb_set_intfdata(intf, NULL); + up_write(&w9968cf_disconnect); } @@ -3653,48 +3703,103 @@ * Module init, exit and intermodule communication * ****************************************************************************/ -static int w9968cf_vppmod_detect(void) +static int w9968cf_vppmod_detect(struct w9968cf_device* cam) { - w9968cf_vpp_init_decoder = inter_module_get("w9968cf_init_decoder"); - - if (!w9968cf_vpp_init_decoder) { + if (!w9968cf_vpp) if (vppmod_load) - w9968cf_vpp_init_decoder = inter_module_get_request - ( "w9968cf_init_decoder", - "w9968cf-vpp" ); - if (!w9968cf_vpp_init_decoder) { - w9968cf_vppmod_present = 0; - DBG(4, "Video post-processing module not detected.") - return -ENODEV; - } + 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_check_headers = inter_module_get("w9968cf_check_headers"); - w9968cf_vpp_decode = inter_module_get("w9968cf_decode"); - w9968cf_vpp_swap_yuvbytes = inter_module_get("w9968cf_swap_yuvbytes"); - w9968cf_vpp_uyvy_to_rgbx = inter_module_get("w9968cf_uyvy_to_rgbx"); - w9968cf_vpp_scale_up = inter_module_get("w9968cf_scale_up"); + w9968cf_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); - w9968cf_vppmod_present = 1; + if (w9968cf_vpp) { + KDBG(1, "Video post-processing module already registered") + up(&w9968cf_vppmod_lock); + return -EINVAL; + } - /* Initialization */ - (*w9968cf_vpp_init_decoder)(); + w9968cf_vpp = vpp; + w9968cf_vpp->busy = 0; - DBG(2, "Video post-processing module detected.") + KDBG(2, "Video post-processing module registered") + up(&w9968cf_vppmod_lock); return 0; } -static void w9968cf_vppmod_release(void) +int w9968cf_vppmod_deregister(struct w9968cf_vpp_t* vpp) { - inter_module_put("w9968cf_init_decoder"); - inter_module_put("w9968cf_check_headers"); - inter_module_put("w9968cf_decode"); - inter_module_put("w9968cf_swap_yuvbytes"); - inter_module_put("w9968cf_uyvy_to_rgbx"); - inter_module_put("w9968cf_scale_up"); + down(&w9968cf_vppmod_lock); + + if (!w9968cf_vpp) { + up(&w9968cf_vppmod_lock); + return -EINVAL; + } - DBG(2, "Video post-processing module released.") + 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; } @@ -3702,18 +3807,14 @@ { int err; - DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) - DBG(3, W9968CF_MODULE_AUTHOR) - - init_MUTEX(&w9968cf_devlist_sem); + KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) + KDBG(3, W9968CF_MODULE_AUTHOR) - w9968cf_vppmod_detect(); + if (ovmod_load) + request_module("ovcamchip"); - if ((err = usb_register(&w9968cf_usb_driver))) { - if (w9968cf_vppmod_present) - w9968cf_vppmod_release(); + if ((err = usb_register(&w9968cf_usb_driver))) return err; - } return 0; } @@ -3724,12 +3825,13 @@ /* w9968cf_usb_disconnect() will be called */ usb_deregister(&w9968cf_usb_driver); - if (w9968cf_vppmod_present) - w9968cf_vppmod_release(); - - DBG(2, W9968CF_MODULE_NAME" deregistered.") + KDBG(2, W9968CF_MODULE_NAME" deregistered") } module_init(w9968cf_module_init); module_exit(w9968cf_module_exit); + + +EXPORT_SYMBOL(w9968cf_vppmod_register); +EXPORT_SYMBOL(w9968cf_vppmod_deregister); diff -Nru a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h --- a/drivers/usb/media/w9968cf.h 2004-07-14 16:44:05 -07:00 +++ b/drivers/usb/media/w9968cf.h 2004-07-14 16:44:05 -07:00 @@ -24,21 +24,26 @@ #include #include #include +#include #include #include #include #include #include +#include #include -#include +#include -#include "w9968cf_externaldef.h" +#include + +#include "w9968cf_vpp.h" /**************************************************************************** * Default values * ****************************************************************************/ +#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */ #define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ /* Comment/uncomment the following line to enable/disable debugging messages */ @@ -95,8 +100,8 @@ #define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */ -#define W9968CF_MAX_WIDTH 800 /* should be >= 640 */ -#define W9968CF_MAX_HEIGHT 600 /* should be >= 480 */ +#define W9968CF_MAX_WIDTH 800 /* Has effect if up-scaling is on */ +#define W9968CF_MAX_HEIGHT 600 /* Has effect if up-scaling is on */ #define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */ #define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */ @@ -130,13 +135,11 @@ #define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ "Dual Mode Camera Chip" -#define W9968CF_MODULE_VERSION "v1.25-basic" +#define W9968CF_MODULE_VERSION "1:1.32-basic" #define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia" #define W9968CF_AUTHOR_EMAIL "" #define W9968CF_MODULE_LICENSE "GPL" -static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */ - static const struct usb_device_id winbond_id_table[] = { { /* Creative Labs Video Blaster WebCam Go Plus */ @@ -151,18 +154,19 @@ { } /* terminating entry */ }; -MODULE_DEVICE_TABLE(usb, winbond_id_table); - /* W996[87]CF camera models, internal ids: */ enum w9968cf_model_id { W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */ W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/ - W9968CF_MOD_ADPA5R = 21, /* Aroma Digi Pen ADG-5000 Refurbished */ - W9986CF_MOD_AU = 31, /* AVerTV USB */ - W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */ - W9968CF_MOD_DLLDK = 37, /* Die Lebon LDC-D35A Digital Kamera */ + W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */ + W9986CF_MOD_AAU = 31, /* AVerMedia AVerTV USB */ + W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */ + W9968CF_MOD_LL = 37, /* Lebon LDC-035A */ W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */ + W9968CF_MOD_OOE = 42, /* OmniVision OV8610-EDE */ W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */ + W9968CF_MOD_PDPII = 46, /* Pretec Digi Pen-II */ + W9968CF_MOD_PDP480 = 49, /* Pretec DigiPen-480 */ }; enum w9968cf_frame_status { @@ -173,9 +177,10 @@ }; struct w9968cf_frame_t { - #define W9968CF_HW_BUF_SIZE 640*480*2 /* buf.size of original frames */ void* buffer; + unsigned long size; u32 length; + int number; enum w9968cf_frame_status status; struct w9968cf_frame_t* next; u8 queued; @@ -189,12 +194,19 @@ VPP_UYVY_TO_RGBX = 0x08, }; -static struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */ -static LIST_HEAD(w9968cf_dev_list); -struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */ +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 */ +static DECLARE_MUTEX(w9968cf_devlist_sem); /* semaphore for list traversal */ + +static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */ /* Main device driver structure */ struct w9968cf_device { + struct device dev; /* device structure */ + enum w9968cf_model_id id; /* private device identifier */ struct video_device* v4ldev; /* -> V4L structure */ @@ -207,10 +219,10 @@ u16* data_buffer; /* -> data to send to the FSB */ struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS]; - struct w9968cf_frame_t frame_tmp; /* temporary frame */ + struct w9968cf_frame_t frame_tmp; /* temporary frame */ + struct w9968cf_frame_t frame_vpp; /* helper frame.*/ struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */ struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; - void* vpp_buffer; /*-> helper buf.for video post-processing routines */ u8 max_buffers, /* number of requested buffers */ force_palette, /* yes=1/no=0 */ @@ -233,7 +245,7 @@ hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/ - start_cropy; /* pixels from VS incative edge to 1st cropped pixel*/ + start_cropy; /* pixels from VS inactive edge to 1st cropped pixel*/ enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */ @@ -246,13 +258,13 @@ u8 sensor_initialized; /* flag: yes=1, no=0 */ - /* Determined by CMOS sensor type: */ + /* Determined by the image sensor type: */ int sensor, /* type of image sensor chip (CC_*) */ - monochrome; /* CMOS sensor is (probably) monochrome */ - u16 maxwidth, /* maximum width supported by the CMOS sensor */ - maxheight, /* maximum height supported by the CMOS sensor */ - minwidth, /* minimum width supported by the CMOS sensor */ - minheight; /* minimum height supported by the CMOS sensor */ + monochrome; /* image sensor is (probably) monochrome */ + u16 maxwidth, /* maximum width supported by the image sensor */ + maxheight, /* maximum height supported by the image sensor */ + minwidth, /* minimum width supported by the image sensor */ + minheight; /* minimum height supported by the image sensor */ u8 auto_brt, /* auto brightness enabled flag */ auto_exp, /* auto exposure enabled flag */ backlight, /* backlight exposure algorithm flag */ @@ -270,8 +282,9 @@ fileop_sem; /* for read and ioctl */ spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */ flist_lock; /* for requested frame list accesses */ - char command[16]; /* name of the program holding the device */ wait_queue_head_t open, wait_queue; + + char command[16]; /* name of the program holding the device */ }; @@ -280,31 +293,47 @@ ****************************************************************************/ #undef DBG +#undef KDBG #ifdef W9968CF_DEBUG -# define DBG(level, fmt, args...) \ -{ \ -if ( ((specific_debug) && (debug == (level))) || \ - ((!specific_debug) && (debug >= (level))) ) { \ - if ((level) == 1) \ - err(fmt, ## args); \ - else if ((level) == 2 || (level) == 3) \ - info(fmt, ## args); \ - else if ((level) == 4) \ - warn(fmt, ## args); \ - else if ((level) >= 5) \ - info("[%s:%d] " fmt, \ - __FUNCTION__, __LINE__ , ## args); \ -} \ +/* For device specific debugging messages */ +# define DBG(level, fmt, args...) \ +{ \ + if ( ((specific_debug) && (debug == (level))) || \ + ((!specific_debug) && (debug >= (level))) ) { \ + if ((level) == 1) \ + dev_err(&cam->dev, fmt "\n", ## args); \ + else if ((level) == 2 || (level) == 3) \ + dev_info(&cam->dev, fmt "\n", ## args); \ + else if ((level) == 4) \ + dev_warn(&cam->dev, fmt "\n", ## args); \ + else if ((level) >= 5) \ + dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} +/* For generic kernel (not device specific) messages */ +# define KDBG(level, fmt, args...) \ +{ \ + if ( ((specific_debug) && (debug == (level))) || \ + ((!specific_debug) && (debug >= (level))) ) { \ + if ((level) >= 1 && (level) <= 4) \ + pr_info("w9968cf: " fmt "\n", ## args); \ + else if ((level) >= 5) \ + pr_debug("w9968cf: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ } #else /* Not debugging: nothing */ # define DBG(level, fmt, args...) 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); + #undef PDBGG -#define PDBG(fmt, args...) info("[%s:%d] "fmt, \ - __PRETTY_FUNCTION__, __LINE__ , ## args); #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ #endif /* _W9968CF_H_ */ diff -Nru a/drivers/usb/media/w9968cf_externaldef.h b/drivers/usb/media/w9968cf_externaldef.h --- a/drivers/usb/media/w9968cf_externaldef.h 2004-07-14 16:44:05 -07:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,94 +0,0 @@ -/*************************************************************************** - * Various definitions for compatibility with OVCAMCHIP external module. * - * This file is part of the W996[87]CF driver for Linux. * - * * - * The definitions have been taken from the OVCAMCHIP module written by * - * Mark McClelland. * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _W9968CF_EXTERNALDEF_H_ -#define _W9968CF_EXTERNALDEF_H_ - -#include -#include -#include -#include - -#ifndef I2C_DRIVERID_OVCAMCHIP -# define I2C_DRIVERID_OVCAMCHIP 0xf00f -#endif - -/* Controls */ -enum { - OVCAMCHIP_CID_CONT, /* Contrast */ - OVCAMCHIP_CID_BRIGHT, /* Brightness */ - OVCAMCHIP_CID_SAT, /* Saturation */ - OVCAMCHIP_CID_HUE, /* Hue */ - OVCAMCHIP_CID_EXP, /* Exposure */ - OVCAMCHIP_CID_FREQ, /* Light frequency */ - OVCAMCHIP_CID_BANDFILT, /* Banding filter */ - OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */ - OVCAMCHIP_CID_AUTOEXP, /* Auto exposure */ - OVCAMCHIP_CID_BACKLIGHT, /* Back light compensation */ - OVCAMCHIP_CID_MIRROR, /* Mirror horizontally */ -}; - -/* I2C addresses */ -#define OV7xx0_SID (0x42 >> 1) -#define OV6xx0_SID (0xC0 >> 1) - -/* Sensor types */ -enum { - CC_UNKNOWN, - CC_OV76BE, - CC_OV7610, - CC_OV7620, - CC_OV7620AE, - CC_OV6620, - CC_OV6630, - CC_OV6630AE, - CC_OV6630AF, -}; - -/* API */ -struct ovcamchip_control { - __u32 id; - __s32 value; -}; - -struct ovcamchip_window { - int x; - int y; - int width; - int height; - int format; - int quarter; /* Scale width and height down 2x */ - - /* This stuff will be removed eventually */ - int clockdiv; /* Clock divisor setting */ -}; - -/* Commands. - You must call OVCAMCHIP_CMD_INITIALIZE before any of other commands */ -#define OVCAMCHIP_CMD_Q_SUBTYPE _IOR (0x88, 0x00, int) -#define OVCAMCHIP_CMD_INITIALIZE _IOW (0x88, 0x01, int) -#define OVCAMCHIP_CMD_S_CTRL _IOW (0x88, 0x02, struct ovcamchip_control) -#define OVCAMCHIP_CMD_G_CTRL _IOWR (0x88, 0x03, struct ovcamchip_control) -#define OVCAMCHIP_CMD_S_MODE _IOW (0x88, 0x04, struct ovcamchip_window) -#define OVCAMCHIP_MAX_CMD _IO (0x88, 0x3f) - -#endif /* _W9968CF_EXTERNALDEF_H_ */ diff -Nru a/drivers/usb/media/w9968cf_vpp.h b/drivers/usb/media/w9968cf_vpp.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/media/w9968cf_vpp.h 2004-07-14 16:44:05 -07:00 @@ -0,0 +1,43 @@ +/*************************************************************************** + * Interface for video post-processing functions for the W996[87]CF driver * + * for Linux. * + * * + * Copyright (C) 2002-2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _W9968CF_VPP_H_ +#define _W9968CF_VPP_H_ + +#include +#include + +struct w9968cf_vpp_t { + struct module* owner; + int (*check_headers)(const unsigned char*, const unsigned long); + int (*decode)(const char*, const unsigned long, const unsigned, + const unsigned, char*); + void (*swap_yuvbytes)(void*, unsigned long); + void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8); + void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16); + + 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_ */ diff -Nru a/include/linux/videodev.h b/include/linux/videodev.h --- a/include/linux/videodev.h 2004-07-14 16:44:05 -07:00 +++ b/include/linux/videodev.h 2004-07-14 16:44:05 -07:00 @@ -429,8 +429,9 @@ #define VID_HARDWARE_CPIA2 33 #define VID_HARDWARE_VICAM 34 #define VID_HARDWARE_SF16FMR2 35 -#define VID_HARDWARE_W9968CF 36 +#define VID_HARDWARE_W9968CF 36 #define VID_HARDWARE_SAA7114H 37 +#define VID_HARDWARE_SN9C102 38 #endif /* __LINUX_VIDEODEV_H */ /*