ChangeSet 1.1474.81.36, 2004/01/16 17:22:24-08:00, luca.risolia@studio.unibo.it [PATCH] USB: W996[87]CF driver update This patch contains updates and one bug fix. CREDITS | 6 Documentation/usb/w9968cf.txt | 151 ++--- MAINTAINERS | 2 drivers/usb/media/Kconfig | 19 drivers/usb/media/w9968cf.c | 836 ++++++++++++++++---------------- drivers/usb/media/w9968cf.h | 58 +- drivers/usb/media/w9968cf_decoder.h | 2 drivers/usb/media/w9968cf_externaldef.h | 13 8 files changed, 550 insertions(+), 537 deletions(-) diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Tue Jan 20 17:34:06 2004 +++ b/CREDITS Tue Jan 20 17:34:06 2004 @@ -2670,9 +2670,9 @@ S: Finland N: Luca Risolia -E: luca_ing@libero.it -D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip -S: Via Libertà 41/a +E: luca.risolia@studio.unibo.it +D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips +S: Via Liberta' 41/A S: Osio Sotto, 24046, Bergamo S: Italy diff -Nru a/Documentation/usb/w9968cf.txt b/Documentation/usb/w9968cf.txt --- a/Documentation/usb/w9968cf.txt Tue Jan 20 17:34:06 2004 +++ b/Documentation/usb/w9968cf.txt Tue Jan 20 17:34:06 2004 @@ -1,6 +1,7 @@ - W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.6 - ============================================================== + W996[87]CF JPEG USB Dual Mode Camera Chip + Driver for Linux 2.6 (basic version) + ========================================= - Documentation - @@ -11,15 +12,16 @@ 2. License 3. Overview 4. Supported devices -5. Kernel configuration and third-part module compilation +5. Module dependencies 6. Module loading 7. Module paramaters -8. Credits +8. Contact information +9. Credits 1. Copyright ============ -Copyright (C) 2002 2003 by Luca Risolia +Copyright (C) 2002-2004 by Luca Risolia 2. License @@ -43,23 +45,25 @@ =========== 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. +are being commanded by USB. OV681 based cameras should be supported as well. -The driver relies on the Video4Linux, USB and I2C core modules of the Linux -kernel, version 2.6.0 or greater, and is not compatible in any way with -previous versions. 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 driver is split into two modules: the basic one, "w9968cf", is needed for +The driver is divided into two modules: the basic one, "w9968cf", is needed for the supported devices to work; the second one, "w9968cf-vpp", is an optional module, which provides some useful video post-processing functions like video -decoding, up-scaling and colour conversions. These routines can't be included -into official kernels for performance purposes. Once the driver is installed, +decoding, up-scaling and colour conversions. Once the driver is installed, every time an application tries to open a recognized device, "w9968cf" checks the presence of the "w9968cf-vpp" module and loads it automatically by default. +Please keep in mind that official kernels do NOT include the second module for +performance purposes. However it is always recommended to download and install +the latest and complete release of the driver, replacing the existing one, if +present: it will be still even possible not to load the "w9968cf-vpp" module at +all, if you ever want to. + +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 +that package, if you are going to use it. + Up to 32 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. @@ -67,18 +71,21 @@ To change the default settings for each camera, many paramaters can be passed through command line when the module is loaded into memory. -The latest and full featured version of the W996[87]CF driver can be found at: -http://go.lamarinapunto.com/ +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.25, which can be +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 patch, compile and load it, read the paragraphs below. +To know how to compile it, read the documentation included in the OV511 +package. 4. Supported devices ==================== -At the moment, known W996[87]CF based devices are: +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 @@ -87,27 +94,26 @@ - Ezonics EZ-802 EZMega Cam - OPCOM Digi Pen VGA Dual Mode Pen Camera -If you know any other W996[87]CF based cameras, please contact me. +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" module work. -For a list of supported CMOS sensors, please visit the module author homepage: -http://alpha.dyndns.org/ov511/ +For a list of supported CMOS 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 -means that still images can't be downloaded from the device memory. +means that still images cannot be downloaded from the device memory. Furthermore, it's worth to note that I was only able to run tests on my "Creative Labs Video Blaster WebCam Go". Donations of other models, for additional testing and full support, would be much appreciated. -5. Kernel configuration and third-part module compilation -========================================================= -As noted above, kernel 2.6.0 is the minimum for this driver; for it to work -properly, the driver needs kernel support for Video4Linux, USB and I2C, and a -third-part module for the CMOS sensor. +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. The following options of the kernel configuration file must be enabled and corresponding modules must be compiled: @@ -126,7 +132,7 @@ # CONFIG_USB=m -In addition, depending on the hardware being used, just one of the modules +In addition, depending on the hardware being used, only one of the modules below is necessary: # USB Host Controller Drivers @@ -137,31 +143,16 @@ 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 driver, version 2.25 - don't use other versions - which is available -at http://alpha.dyndns.org/ov511/ . Then you have to download the latest -version of the full featured W996[87]CF driver, which contains a patch for the -"ovcamchip" module; it is available at http://go.lamarinapunto.com . -Once you have obtained the packages, decompress, patch and compile the -"ovcamchip" module. In other words: - - [user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz - [user@localhost home]$ tar xvjf ov511-2.25.tar.bz2 - [user@localhost home]$ cd ov511-2.25 - [user@localhost ov511-2.25]$ patch -p1 < \ - /path/to/w9968cf-x.x/ov511-2.25.patch - [user@localhost ov511-2.25]$ make - -It's worth to note that the full featured version of the W996[87]CF driver -can also be installed overwriting the one in the kernel; in this case, read the -documentation included in the package. - -If everything went well, the W996[87]CF driver can be immediatly used (see next -paragraph). +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 @@ -169,7 +160,7 @@ To use the driver, it is necessary to load the "w9968cf" module into memory after every other module required. -For example, loading can be done this way, as root: +Loading can be done this way, from root: [root@localhost home]# modprobe usbcore [root@localhost home]# modprobe i2c-core @@ -191,11 +182,10 @@ 7. Module paramaters ==================== - Module paramaters are listed below: ------------------------------------------------------------------------------- Name: vppmod_load -Type: int +Type: bool Syntax: <0|1> Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled. If enabled, every time an application attempts to open a @@ -219,7 +209,7 @@ Description: Specify V4L minor mode number. -1 = use next available n = use minor number n - You can specify 32 cameras this way. + 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 @@ -236,22 +226,22 @@ Name: max_buffers Type: int array (min = 0, max = 32) Syntax: -Description: Only for advanced users. +Description: For advanced users. Specify the maximum number of video frame buffers to allocate for each device, from 2 to 32. Default: 2 ------------------------------------------------------------------------------- Name: double_buffer -Type: int array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> 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 at all, or try to + obtain out of sync. video, disable it, or try to decrease the 'clockdiv' module paramater value. Default: 1 for every device. ------------------------------------------------------------------------------- Name: clamping -Type: int array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Video data clamping: 0 disabled, 1 enabled. Default: 0 for every device. @@ -266,13 +256,13 @@ Default: 0 for every device. ------------------------------------------------------------------------------- Name: largeview -Type: int array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Large view: 0 disabled, 1 enabled. Default: 1 for every device. ------------------------------------------------------------------------------- Name: upscaling -Type: int array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Software scaling (for non-compressed video only): 0 disabled, 1 enabled. @@ -319,7 +309,7 @@ Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9. ------------------------------------------------------------------------------- Name: force_rgb -Type: int array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Read RGB video data instead of BGR: 1 = use RGB component ordering. @@ -328,28 +318,28 @@ Default: 0 for every device. ------------------------------------------------------------------------------- Name: autobright -Type: long array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: CMOS sensor automatically changes brightness: 0 = no, 1 = yes Default: 0 for every device. ------------------------------------------------------------------------------- Name: autoexp -Type: long array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: CMOS sensor automatically changes exposure: 0 = no, 1 = yes Default: 1 for every device. ------------------------------------------------------------------------------- Name: lightfreq -Type: long array (min = 0, max = 32) +Type: int array (min = 0, max = 32) Syntax: <50|60[,...]> Description: Light frequency in Hz: 50 for European and Asian lighting, 60 for American lighting. Default: 50 for every device. ------------------------------------------------------------------------------- Name: bandingfilter -Type: long array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Banding filter to reduce effects of fluorescent lighting: @@ -359,7 +349,7 @@ Default: 0 for every device. ------------------------------------------------------------------------------- Name: clockdiv -Type: long array (min = 0, max = 32) +Type: int array (min = 0, max = 32) Syntax: <-1|n[,...]> Description: Force pixel clock divisor to a specific value (for experts): n may vary from 0 to 127. @@ -368,21 +358,21 @@ Default: -1 for every device. ------------------------------------------------------------------------------- Name: backlight -Type: long array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Objects are lit from behind: 0 = no, 1 = yes Default: 0 for every device. ------------------------------------------------------------------------------- Name: mirror -Type: long array (min = 0, max = 32) +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: Reverse image horizontally: 0 = no, 1 = yes Default: 0 for every device. ------------------------------------------------------------------------------- -Name: sensor_mono -Type: long array (min = 0, max = 32) +Name: monochrome +Type: bool array (min = 0, max = 32) Syntax: <0|1[,...]> Description: The CMOS sensor is monochrome: 0 = no, 1 = yes @@ -423,7 +413,7 @@ Type: int Syntax: Description: Debugging information level, from 0 to 6: - 0 = none (be cautious) + 0 = none (use carefully) 1 = critical errors 2 = significant informations 3 = configuration or general messages @@ -435,7 +425,7 @@ Default: 2 ------------------------------------------------------------------------------- Name: specific_debug -Type: int +Type: bool Syntax: <0|1> Description: Enable or disable specific debugging messages: 0 = print messages concerning every level <= 'debug' level. @@ -444,7 +434,16 @@ ------------------------------------------------------------------------------- -8. Credits +8. 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 your keyserver; the fingerprint +is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. + + +9. Credits ========== The development would not have proceed much further without having looked at the source code of other drivers and without the help of several persons; in @@ -456,8 +455,6 @@ - memory management code has been copied from the bttv driver by Ralph Metzler, Marcus Metzler and Gerd Knorr; -- the low-level I2C read function has been written by Frédéric Jouault, who - also gave me commented logs about sniffed USB traffic taken from another - driver for another system; +- the low-level I2C read function has been written by Frederic Jouault; -- the low-level I2C fast write function has been written by Piotr Czerczak; +- the low-level I2C fast write function has been written by Piotr Czerczak. diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Tue Jan 20 17:34:06 2004 +++ b/MAINTAINERS Tue Jan 20 17:34:06 2004 @@ -2223,7 +2223,7 @@ USB W996[87]CF DRIVER P: Luca Risolia -M: luca_ing@libero.it +M: luca.risolia@studio.unibo.it L: linux-usb-devel@lists.sourceforge.net W: http://go.lamarinapunto.com S: Maintained diff -Nru a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig --- a/drivers/usb/media/Kconfig Tue Jan 20 17:34:06 2004 +++ b/drivers/usb/media/Kconfig Tue Jan 20 17:34:06 2004 @@ -182,24 +182,21 @@ tristate "USB W996[87]CF JPEG Dual Mode Camera support" depends on USB && VIDEO_DEV && I2C ---help--- - Say Y here if you want support for cameras based on + Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. This driver has an optional plugin, which is distributed as a - separate module only (released under GPL). It contains code that - allows you to use higher resolutions and framerates, and can't - be included into the official Linux kernel for performance - purposes. - At the moment the driver needs a third-part module for the CMOS + 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. - Information on this API and pointers to "v4l" programs may be found - on the WWW at . + 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). diff -Nru a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c --- a/drivers/usb/media/w9968cf.c Tue Jan 20 17:34:06 2004 +++ b/drivers/usb/media/w9968cf.c Tue Jan 20 17:34:06 2004 @@ -1,14 +1,14 @@ /*************************************************************************** * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * * * - * Copyright (C) 2002 2003 by Luca Risolia * + * Copyright (C) 2002-2004 by Luca Risolia * * * * - 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 * * 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 Frédéric Jouault. * + * - Low-level I2C read function by Frederic Jouault. * * * * 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 * @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -48,81 +49,86 @@ /**************************************************************************** - * Modules paramaters * + * Module macros and paramaters * ****************************************************************************/ -static u8 vppmod_load = W9968CF_VPPMOD_LOAD; -static u8 simcams = W9968CF_SIMCAMS; -static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */ -static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE}; -static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS}; -static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_DOUBLE_BUFFER}; -static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; -static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE}; -static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; -static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_DECOMPRESSION}; -static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; -static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; -static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; -static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; -static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; -static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ}; -static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= - W9968CF_BANDINGFILTER}; -static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; -static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; -static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; -static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO}; -static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS}; -static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; -static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; -static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST}; -static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS}; +MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL); +MODULE_DESCRIPTION(W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION); +MODULE_LICENSE(W9968CF_MODULE_LICENSE); +MODULE_SUPPORTED_DEVICE("Video"); + +static int vppmod_load = W9968CF_VPPMOD_LOAD; +static unsigned short simcams = W9968CF_SIMCAMS; +static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ +static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_PACKET_SIZE}; +static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_BUFFERS}; +static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DOUBLE_BUFFER}; +static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; +static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_FILTER_TYPE}; +static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; +static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DECOMPRESSION}; +static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; +static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; +static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; +static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; +static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; +static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_LIGHTFREQ}; +static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= + W9968CF_BANDINGFILTER}; +static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; +static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; +static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; +static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME}; +static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_BRIGHTNESS}; +static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; +static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; +static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_CONTRAST}; +static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_WHITENESS}; #ifdef W9968CF_DEBUG -static u8 debug = W9968CF_DEBUG_LEVEL; -static u8 specific_debug = W9968CF_SPECIFIC_DEBUG; +static unsigned short debug = W9968CF_DEBUG_LEVEL; +static int specific_debug = W9968CF_SPECIFIC_DEBUG; #endif -MODULE_AUTHOR("Luca Risolia "); - -MODULE_DESCRIPTION("Video4Linux driver for " - "W996[87]CF JPEG USB Dual Mode Camera Chip"); - -MODULE_SUPPORTED_DEVICE("Video"); - -MODULE_LICENSE("GPL"); +static unsigned int param_nv[23]; /* number of values per paramater */ -MODULE_PARM(vppmod_load, "i"); -MODULE_PARM(simcams, "i"); -MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i"); -MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); -MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l"); +module_param(vppmod_load, bool, 0444); +module_param(simcams, ushort, 0444); +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); +module_param_array(double_buffer, bool, param_nv[3], 0444); +module_param_array(clamping, bool, param_nv[4], 0444); +module_param_array(filter_type, ushort, param_nv[5], 0444); +module_param_array(largeview, bool, param_nv[6], 0444); +module_param_array(decompression, ushort, param_nv[7], 0444); +module_param_array(upscaling, bool, param_nv[8], 0444); +module_param_array(force_palette, ushort, param_nv[9], 0444); +module_param_array(force_rgb, ushort, param_nv[10], 0444); +module_param_array(autobright, bool, param_nv[11], 0444); +module_param_array(autoexp, bool, param_nv[12], 0444); +module_param_array(lightfreq, ushort, param_nv[13], 0444); +module_param_array(bandingfilter, bool, param_nv[14], 0444); +module_param_array(clockdiv, short, param_nv[15], 0444); +module_param_array(backlight, bool, param_nv[16], 0444); +module_param_array(mirror, bool, param_nv[17], 0444); +module_param_array(monochrome, bool, param_nv[18], 0444); +module_param_array(brightness, uint, param_nv[19], 0444); +module_param_array(hue, uint, param_nv[20], 0444); +module_param_array(colour, uint, param_nv[21], 0444); +module_param_array(contrast, uint, param_nv[22], 0444); +module_param_array(whiteness, uint, param_nv[23], 0444); #ifdef W9968CF_DEBUG -MODULE_PARM(debug, "i"); -MODULE_PARM(specific_debug, "i"); +module_param(debug, ushort, 0444); +module_param(specific_debug, bool, 0444); #endif MODULE_PARM_DESC(vppmod_load, @@ -146,7 +152,7 @@ "\n<-1|n[,...]> Specify V4L minor mode number." "\n -1 = use next available (default)" "\n n = use minor number n (integer >= 0)" - "\nYou can specify " __MODULE_STRING(W9968CF_MAX_DEVICES) + "\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES) " cameras this way." "\nFor example:" "\nvideo_nr=-1,2,-1 would assign minor number 2 to" @@ -160,7 +166,7 @@ "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")." "\n"); MODULE_PARM_DESC(max_buffers, - "\n Only for advanced users." + "\n For advanced users." "\nSpecify the maximum number of video frame buffers" "\nto allocate for each device, from 2 to " __MODULE_STRING(W9968CF_MAX_BUFFERS) @@ -203,7 +209,8 @@ "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) " for every device." "\nIf 'w9968cf-vpp' is not loaded, this paramater is" - " set to 0."); + " set to 0." + "\n"); MODULE_PARM_DESC(decompression, "\n<0|1|2[,...]> Software video decompression:" "\n- 0 disables decompression (doesn't allow formats needing" @@ -218,7 +225,8 @@ "\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."); + "\nnot allowed; in this case this paramater is set to 2." + "\n"); MODULE_PARM_DESC(force_palette, "\n<0" "|" __MODULE_STRING(VIDEO_PALETTE_UYVY) @@ -252,7 +260,8 @@ "\nInitial palette is " __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." "\nIf 'w9968cf-vpp' is not loaded, this paramater is" - " set to 9 (UYVY)."); + " set to 9 (UYVY)." + "\n"); MODULE_PARM_DESC(force_rgb, "\n<0|1[,...]> Read RGB video data instead of BGR:" "\n 1 = use RGB component ordering." @@ -311,10 +320,11 @@ "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR) " for every device." "\n"); -MODULE_PARM_DESC(sensor_mono, - "\n<0|1[,...]> The OV CMOS sensor is monochrome:" +MODULE_PARM_DESC(monochrome, + "\n<0|1[,...]> Use OV CMOS sensor as monochrome sensor:" "\n 0 = no, 1 = yes" - "\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO) + "\nNot all the sensors support monochrome color." + "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME) " for every device." "\n"); MODULE_PARM_DESC(brightness, @@ -346,7 +356,7 @@ #ifdef W9968CF_DEBUG MODULE_PARM_DESC(debug, "\n Debugging information level, from 0 to 6:" - "\n0 = none (be cautious)" + "\n0 = none (use carefully)" "\n1 = critical errors" "\n2 = significant informations" "\n3 = configuration or general messages" @@ -380,13 +390,12 @@ static int w9968cf_release(struct inode*, struct file*); static ssize_t w9968cf_read(struct file*, char*, size_t, loff_t*); static int w9968cf_mmap(struct file*, struct vm_area_struct*); -static int w9968cf_ioctl(struct inode*, struct file*, - unsigned int, unsigned long); -static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*); +static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long); +static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, void*); /* USB-specific */ -static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs); 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); @@ -402,6 +411,7 @@ static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v); static int w9968cf_smbus_write_ack(struct w9968cf_device*); static int w9968cf_smbus_read_ack(struct w9968cf_device*); +static int w9968cf_smbus_refresh_bus(struct w9968cf_device*); static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, u16 address, u8* value); static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, @@ -436,12 +446,11 @@ static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val); static inline int w9968cf_sensor_cmd(struct w9968cf_device*, unsigned int cmd, void *arg); -static void w9968cf_sensor_configure(struct w9968cf_device*); -static int w9968cf_sensor_change_settings(struct w9968cf_device*); -static int w9968cf_sensor_get_picture(struct w9968cf_device*, - struct video_picture*); -static int w9968cf_sensor_set_picture(struct w9968cf_device*, - struct video_picture pict); +static int w9968cf_sensor_init(struct w9968cf_device*); +static int w9968cf_sensor_update_settings(struct w9968cf_device*); +static int w9968cf_sensor_get_picture(struct w9968cf_device*); +static int w9968cf_sensor_update_picture(struct w9968cf_device*, + struct video_picture pict); /* Other helper functions */ static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, @@ -587,8 +596,6 @@ * Memory management functions * ****************************************************************************/ -/* Shameless copy from bttv-driver.c */ - /* Here we want the physical address of the memory. This is used when initializing the contents of the area. */ static inline unsigned long kvirt_to_pa(unsigned long adr) @@ -639,7 +646,6 @@ } vfree(mem); } -/* End of shameless copy */ /*-------------------------------------------------------------------------- @@ -820,9 +826,9 @@ } for (i = 0; i < urb->number_of_packets; i++) { - len = urb->iso_frame_desc[i].actual_length; + 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; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; if (status && len != 0) { DBG(4, "URB failed, error in data packet " @@ -923,7 +929,6 @@ for (i = 0; i < W9968CF_URBS; i++) { urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); cam->urb[i] = urb; - if (!urb) { for (j = 0; j < i; j++) usb_free_urb(cam->urb[j]); @@ -953,7 +958,7 @@ t_size = (w*h*d)/16; - err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ /* Transfer size */ @@ -1026,11 +1031,12 @@ spin_unlock_irqrestore(&cam->urb_lock, lock_flags); for (i = W9968CF_URBS-1; i >= 0; i--) - if (cam->urb[i]) + if (cam->urb[i]) { if (!usb_unlink_urb(cam->urb[i])) { usb_free_urb(cam->urb[i]); cam->urb[i] = NULL; } + } if (cam->disconnected) goto exit; @@ -1086,8 +1092,7 @@ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, (void*)buff, - 2, W9968CF_USB_CTRL_TIMEOUT); + 0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT); if (res < 0) DBG(4, "Failed to read a register " @@ -1099,7 +1104,7 @@ /*-------------------------------------------------------------------------- - Write data to the fast serial bus registers. + Write 64-bit data to the fast serial bus registers. Return 0 on success, -1 otherwise. --------------------------------------------------------------------------*/ static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) @@ -1112,8 +1117,7 @@ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - value, 0x06, (void*)data, 6, - W9968CF_USB_CTRL_TIMEOUT); + value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT); if (res < 0) DBG(4, "Failed to write the FSB registers " @@ -1275,6 +1279,22 @@ } +/* This seems to refresh the communication through the serial bus */ +static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam) +{ + int err = 0, j; + + for (j = 1; j <= 10; j++) { + err = w9968cf_write_reg(cam, 0x0020, 0x01); + err += w9968cf_write_reg(cam, 0x0000, 0x01); + if (err) + break; + } + + return err; +} + + /* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, @@ -1283,8 +1303,10 @@ u16* data = cam->data_buffer; int err = 0; - /* Enable SBUS outputs */ - err += w9968cf_write_reg(cam, 0x0020, 0x01); + err += w9968cf_smbus_refresh_bus(cam); + + /* Enable SBUS outputs */ + err += w9968cf_write_sb(cam, 0x0020); data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0); data[0] |= (address & 0x40) ? 0x4000 : 0x0; @@ -1328,7 +1350,7 @@ err += w9968cf_write_fsb(cam, data); /* Disable SBUS outputs */ - err += w9968cf_write_reg(cam, 0x0000, 0x01); + err += w9968cf_write_sb(cam, 0x0000); if (!err) DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " @@ -1365,7 +1387,7 @@ err += w9968cf_smbus_read_byte(cam, value); err += w9968cf_smbus_write_ack(cam); err += w9968cf_smbus_stop(cam); - + /* Serial data disable */ err += w9968cf_write_sb(cam, 0x0000); @@ -1434,8 +1456,8 @@ int size, union i2c_smbus_data *data) { struct w9968cf_device* cam = i2c_get_adapdata(adapter); - u8 i, j; - int rc = 0, err = 0; + u8 i; + int err = 0; switch (addr) { case OV6xx0_SID: @@ -1451,31 +1473,26 @@ addr <<= 1; if (read_write == I2C_SMBUS_WRITE) - rc = w9968cf_i2c_adap_write_byte(cam, addr, command); + err = w9968cf_i2c_adap_write_byte(cam, addr, command); else if (read_write == I2C_SMBUS_READ) - rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte); + err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte); } else if (size == I2C_SMBUS_BYTE_DATA) { addr <<= 1; if (read_write == I2C_SMBUS_WRITE) - rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, + err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, command, data->byte); else if (read_write == I2C_SMBUS_READ) { for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { - rc = w9968cf_i2c_adap_read_byte_data(cam, addr, + err = w9968cf_i2c_adap_read_byte_data(cam,addr, command, &data->byte); - if (rc < 0) { - /* Work around: this seems to wake up - the EEPROM from the stall state */ - for (j = 0; j <= 10; j++) { - err += w9968cf_write_sb(cam,0x0020); - err += w9968cf_write_sb(cam,0x0000); - if (err) - break; + if (err) { + if (w9968cf_smbus_refresh_bus(cam)) { + err = -EIO; + break; } - } - else + } else break; } @@ -1487,11 +1504,7 @@ return -EINVAL; } - /* This works around a bug in the I2C core */ - if (rc > 0) - rc = 0; - - return rc; + return err; } @@ -1507,41 +1520,22 @@ { struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); const char* clientname = i2c_clientname(client); - int id = client->driver->id; + int id = client->driver->id, err = 0; if (id == I2C_DRIVERID_OVCAMCHIP) { - int rc = 0; - cam->sensor_client = client; - - rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, - &cam->sensor_mono); - if (rc < 0) { - DBG(1, "CMOS sensor initialization failed (rc=%d)",rc); - cam->sensor_client = NULL; - return rc; - } - - if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, - &cam->sensor) < 0) - rc = -EIO; - else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID) - w9968cf_sensor_configure(cam); - else - rc = -EINVAL; - - if (rc < 0) { + err = w9968cf_sensor_init(cam); + if (err) { cam->sensor_client = NULL; - cam->sensor = CC_UNKNOWN; - return rc; + return err; } - } else { - DBG(4, "Rejected client [%s] with [%s]", + } else { + DBG(4, "Rejected client [%s] with driver [%s]", clientname, client->driver->name) - return -1; + return -EINVAL; } - DBG(2, "I2C attach client [%s] with [%s]", + DBG(5, "I2C attach client [%s] with driver [%s]", clientname, client->driver->name) return 0; @@ -1557,7 +1551,7 @@ cam->sensor_client = NULL; } - DBG(2, "I2C detach [%s]", clientname) + DBG(5, "I2C detach client [%s]", clientname) return 0; } @@ -1573,7 +1567,7 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) { - int rc = 0; + int err = 0; static struct i2c_algorithm algo = { .name = "W996[87]CF algorithm", @@ -1596,15 +1590,15 @@ strcpy(cam->i2c_adapter.name, "w9968cf"); i2c_set_adapdata(&cam->i2c_adapter, cam); - DBG(6, "Registering I2C bus with kernel...") + DBG(6, "Registering I2C adapter with kernel...") - rc = i2c_add_adapter(&cam->i2c_adapter); - if (rc) - DBG(5, "Failed to register the I2C bus.") + err = i2c_add_adapter(&cam->i2c_adapter); + if (err) + DBG(1, "Failed to register the I2C adapter.") else - DBG(5, "I2C bus registered.") + DBG(5, "I2C adapter registered.") - return rc; + return err; } @@ -1644,7 +1638,7 @@ --------------------------------------------------------------------------*/ static int w9968cf_init_chip(struct w9968cf_device* cam) { - int err = 0, rc = 0; + int err = 0; err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */ @@ -1679,22 +1673,11 @@ err += w9968cf_set_window(cam, cam->window); if (err) - goto error; - - rc = w9968cf_sensor_change_settings(cam); - if (rc) - goto error; - - DBG(5, "Chip successfully initialized."); - - return 0; - -error: - DBG(1, "Chip initialization failed.") - if (err) - return err; + DBG(1, "Chip initialization failed.") else - return rc; + DBG(5, "Chip successfully initialized.") + + return err; } @@ -1706,7 +1689,7 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) { u16 fmt, hw_depth, hw_palette, reg_v = 0x0000; - int err = 0, rc = 0; + int err = 0; /* Make sure we are using a valid depth */ pict.depth = w9968cf_valid_depth(pict.palette); @@ -1767,12 +1750,10 @@ else if (cam->filter_type == 2) reg_v |= 0x000c; - err = w9968cf_write_reg(cam, reg_v, 0x16); - if (err) + if ((err = w9968cf_write_reg(cam, reg_v, 0x16))) goto error; - rc = w9968cf_sensor_set_picture(cam, pict); - if (rc) + if ((err = w9968cf_sensor_update_picture(cam, pict))) goto error; /* If all went well, update the device data structure */ @@ -1791,10 +1772,7 @@ error: DBG(1, "Failed to change picture settings.") - if (err) - return err; - else - return rc; + return err; } @@ -1809,7 +1787,7 @@ u16 x, y, w, h, scx, scy, cw, ch, ax, ay; unsigned long fw, fh; struct ovcamchip_window s_win; - int err=0, rc=0; + int err = 0; /* Work around to avoid FP arithmetics */ #define __SC(x) ((x) << 10) @@ -1853,8 +1831,8 @@ ch = h; } - /* Setup the sensor window */ - s_win.format = SENSOR_FORMAT; + /* Setup the window of the sensor */ + s_win.format = VIDEO_PALETTE_UYVY; s_win.width = cam->maxwidth; s_win.height = cam->maxheight; s_win.quarter = 0; /* full progressive video */ @@ -1883,7 +1861,7 @@ s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR; } - /* We have to scale win.x and win.y offsets */ + /* We have to scale win.x and win.y offsets */ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) || (cam->vpp_flag & VPP_UPSCALE) ) { ax = __SC(win.x)/fw; @@ -1914,7 +1892,7 @@ y = ay + s_win.y; /* Go ! */ - if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win))) + if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win))) goto error; err += w9968cf_write_reg(cam, scx + x, 0x10); @@ -1959,10 +1937,7 @@ error: DBG(1, "Failed to change the capture area size.") - if (err) - return err; - else - return rc; + return err; } @@ -2177,173 +2152,192 @@ w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val) { struct ovcamchip_control ctl; - int rc; + int err; ctl.id = cid; ctl.value = val; - rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl); + err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl); - return rc; + return err; } + static int -w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val) +w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val) { struct ovcamchip_control ctl; - int rc; + int err; ctl.id = cid; - rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl); - if (rc >= 0) + err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl); + if (!err) *val = ctl.value; - return rc; + return err; } static inline int -w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg) +w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg) { struct i2c_client* c = cam->sensor_client; + int rc = 0; - DBG(6, "Executing CMOS sensor command...") - - if (c && c->driver->command) - return c->driver->command(cam->sensor_client, cmd, arg); - else + 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; } /*-------------------------------------------------------------------------- - Change some settings of the CMOS sensor. - Returns: 0 for success, a negative number otherwise. + Update some settings of the CMOS sensor. + Returns: 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ -static int w9968cf_sensor_change_settings(struct w9968cf_device* cam) +static int w9968cf_sensor_update_settings(struct w9968cf_device* cam) { - int rc; + int err = 0; /* Auto brightness */ - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, - cam->auto_brt); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, + cam->auto_brt); + if (err) + return err; /* Auto exposure */ - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, - cam->auto_exp); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, + cam->auto_exp); + if (err) + return err; /* Banding filter */ - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, - cam->bandfilt); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, + cam->bandfilt); + if (err) + return err; /* Light frequency */ - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ, - cam->lightfreq); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ, + cam->lightfreq); + if (err) + return err; /* Back light */ - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT, - cam->backlight); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT, + cam->backlight); + if (err) + return err; /* Mirror */ - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR, - cam->mirror); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR, + cam->mirror); + if (err) + return err; return 0; } /*-------------------------------------------------------------------------- - Get some current picture settings from the CMOS sensor. - Returns: 0 for success, a negative number otherwise. + Get some current picture settings from the CMOS sensor and update the + internal 'picture' structure of the camera. + Returns: 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ -static int -w9968cf_sensor_get_picture(struct w9968cf_device* cam, - struct video_picture* pict) +static int w9968cf_sensor_get_picture(struct w9968cf_device* cam) { - int rc, v; + int err, v; + + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v); + if (err) + return err; + cam->picture.contrast = v; - /* Don't return error if a setting is unsupported, or rest of settings - will not be performed */ + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v); + if (err) + return err; + cam->picture.brightness = v; - rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v); - if (SENSOR_FATAL_ERROR(rc)) - return rc; - pict->contrast = v; - - rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v); - if (SENSOR_FATAL_ERROR(rc)) - return rc; - pict->brightness = v; - - rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v); - if (SENSOR_FATAL_ERROR(rc)) - return rc; - pict->colour = v; - - rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v); - if (SENSOR_FATAL_ERROR(rc)) - return rc; - pict->hue = v; + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v); + if (err) + return err; + cam->picture.colour = v; - pict->whiteness = W9968CF_WHITENESS; /* to do! */ + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v); + if (err) + return err; + cam->picture.hue = v; DBG(5, "Got picture settings from the CMOS sensor.") PDBGG("Brightness, contrast, hue, colour, whiteness are " - "%d,%d,%d,%d,%d.", pict->brightness, pict->contrast, - pict->hue, pict->colour, pict->whiteness) + "%d,%d,%d,%d,%d.", cam->picture.brightness,cam->picture.contrast, + cam->picture.hue, cam->picture.colour, cam->picture.whiteness) return 0; } /*-------------------------------------------------------------------------- - Change picture settings of the CMOS sensor. - Returns: 0 for success, a negative number otherwise. + Update picture settings of the CMOS sensor. + Returns: 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int -w9968cf_sensor_set_picture(struct w9968cf_device* cam, - struct video_picture pict) +w9968cf_sensor_update_picture(struct w9968cf_device* cam, + struct video_picture pict) { - int rc; - - rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + int err = 0; - if (!cam->auto_brt) { - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, - pict.brightness); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + if ((!cam->sensor_initialized) + || pict.contrast != cam->picture.contrast) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT, + pict.contrast); + if (err) + goto fail; + DBG(4, "Contrast changed from %d to %d.", + cam->picture.contrast, pict.contrast) + cam->picture.contrast = pict.contrast; } - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + if (((!cam->sensor_initialized) || + pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, + pict.brightness); + if (err) + goto fail; + DBG(4, "Brightness changed from %d to %d.", + cam->picture.brightness, pict.brightness) + cam->picture.brightness = pict.brightness; + } - rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue); - if (SENSOR_FATAL_ERROR(rc)) - return rc; + if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, + pict.colour); + if (err) + goto fail; + DBG(4, "Colour changed from %d to %d.", + cam->picture.colour, pict.colour) + cam->picture.colour = pict.colour; + } - PDBGG("Brightness, contrast, hue, colour, whiteness are " - "%d,%d,%d,%d,%d.", pict.brightness, pict.contrast, - pict.hue, pict.colour, pict.whiteness) + if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, + pict.hue); + if (err) + goto fail; + DBG(4, "Hue changed from %d to %d.", + cam->picture.hue, pict.hue) + cam->picture.hue = pict.hue; + } return 0; + +fail: + DBG(4, "Failed to change sensor picture setting.") + return err; } @@ -2353,12 +2347,22 @@ ****************************************************************************/ /*-------------------------------------------------------------------------- - This function is called when the CMOS sensor is detected. + This function is called when a supported CMOS sensor is detected. + Return 0 if the initialization succeeds, a negative number otherwise. --------------------------------------------------------------------------*/ -static void w9968cf_sensor_configure(struct w9968cf_device* cam) +static int w9968cf_sensor_init(struct w9968cf_device* cam) { - /* NOTE: Make sure width and height are a multiple of 16 */ + int err = 0; + if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, + &cam->monochrome))) + goto error; + + if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, + &cam->sensor))) + goto error; + + /* NOTE: Make sure width and height are a multiple of 16 */ switch (cam->sensor_client->addr) { case OV6xx0_SID: cam->maxwidth = 352; @@ -2372,6 +2376,10 @@ cam->minwidth = 64; cam->minheight = 48; break; + default: + DBG(1, "Not supported CMOS sensor detected for %s.", + symbolic(camlist, cam->id)) + return -EINVAL; } /* These values depend on the ones in the ovxxx0.c sources */ @@ -2390,7 +2398,24 @@ cam->hs_polarity = 0; } - DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor)) + if ((err = w9968cf_sensor_update_settings(cam))) + goto error; + + if ((err = w9968cf_sensor_update_picture(cam, cam->picture))) + goto error; + + cam->sensor_initialized = 1; + + DBG(2, "%s CMOS 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.", + symbolic(camlist, cam->id), cam->v4ldev->minor) + return err; } @@ -2415,13 +2440,7 @@ cam->usbdev = udev; cam->id = mod_id; cam->sensor = CC_UNKNOWN; - - strcpy(cam->v4ldev.name, symbolic(camlist, mod_id)); - cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; - cam->v4ldev.hardware = VID_HARDWARE_W9968CF; - cam->v4ldev.fops = &w9968cf_fops; - cam->v4ldev.priv = (void*)cam; - cam->v4ldev.minor = video_nr[dev_nr]; + cam->sensor_initialized = 0; /* Calculate the alternate setting number (from 1 to 16) according to the 'packet_size' module parameter */ @@ -2433,66 +2452,66 @@ cam->max_buffers = (max_buffers[dev_nr] < 2 || max_buffers[dev_nr] > W9968CF_MAX_BUFFERS) - ? W9968CF_BUFFERS : max_buffers[dev_nr]; + ? W9968CF_BUFFERS : (u8)max_buffers[dev_nr]; cam->double_buffer = (double_buffer[dev_nr] == 0 || double_buffer[dev_nr] == 1) - ? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER; + ? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER; cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1) - ? clamping[dev_nr] : W9968CF_CLAMPING; + ? (u8)clamping[dev_nr] : W9968CF_CLAMPING; cam->filter_type = (filter_type[dev_nr] == 0 || filter_type[dev_nr] == 1 || filter_type[dev_nr] == 2) - ? filter_type[dev_nr] : W9968CF_FILTER_TYPE; + ? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE; cam->capture = 1; cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1) - ? largeview[dev_nr] : W9968CF_LARGEVIEW; + ? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW; cam->decompression = (decompression[dev_nr] == 0 || decompression[dev_nr] == 1 || decompression[dev_nr] == 2) - ? decompression[dev_nr] : W9968CF_DECOMPRESSION; + ? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION; cam->upscaling = (upscaling[dev_nr] == 0 || upscaling[dev_nr] == 1) - ? upscaling[dev_nr] : W9968CF_UPSCALING; + ? (u8)upscaling[dev_nr] : W9968CF_UPSCALING; cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1) - ? autobright[dev_nr] : W9968CF_AUTOBRIGHT; + ? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT; cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1) - ? autoexp[dev_nr] : W9968CF_AUTOEXP; + ? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP; cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60) - ? lightfreq[dev_nr] : W9968CF_LIGHTFREQ; + ? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ; cam->bandfilt = (bandingfilter[dev_nr] == 0 || bandingfilter[dev_nr] == 1) - ? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER; + ? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER; cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1) - ? backlight[dev_nr] : W9968CF_BACKLIGHT; + ? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT; cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0) - ? clockdiv[dev_nr] : W9968CF_CLOCKDIV; + ? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV; cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1) - ? mirror[dev_nr] : W9968CF_MIRROR; + ? (u8)mirror[dev_nr] : W9968CF_MIRROR; - cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1) - ? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO; + cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1) + ? monochrome[dev_nr] : W9968CF_MONOCHROME; - cam->picture.brightness = brightness[dev_nr]; - cam->picture.hue = hue[dev_nr]; - cam->picture.colour = colour[dev_nr]; - cam->picture.contrast = contrast[dev_nr]; - cam->picture.whiteness = whiteness[dev_nr]; - if (w9968cf_valid_palette(force_palette[dev_nr])) { - cam->picture.palette = force_palette[dev_nr]; + cam->picture.brightness = (u16)brightness[dev_nr]; + cam->picture.hue = (u16)hue[dev_nr]; + cam->picture.colour = (u16)colour[dev_nr]; + cam->picture.contrast = (u16)contrast[dev_nr]; + cam->picture.whiteness = (u16)whiteness[dev_nr]; + if (w9968cf_valid_palette((u16)force_palette[dev_nr])) { + cam->picture.palette = (u16)force_palette[dev_nr]; cam->force_palette = 1; } else { cam->force_palette = 0; @@ -2505,7 +2524,7 @@ } cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1) - ? force_rgb[dev_nr] : W9968CF_FORCE_RGB; + ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB; cam->window.x = 0; cam->window.y = 0; @@ -2610,7 +2629,7 @@ else DBG(3, "- Clock divisor: %d", cam->clockdiv) - if (cam->sensor_mono) + if (cam->monochrome) DBG(3, "- CMOS sensor used as monochrome.") else DBG(3, "- CMOS sensor not used as monochrome.") @@ -2626,9 +2645,9 @@ { down(&w9968cf_devlist_sem); - DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor) + DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor) - video_unregister_device(&cam->v4ldev); + video_unregister_device(cam->v4ldev); list_del(&cam->v4llist); i2c_del_adapter(&cam->i2c_adapter); w9968cf_deallocate_memory(cam); @@ -2648,24 +2667,25 @@ static int w9968cf_open(struct inode* inode, struct file* filp) { - struct w9968cf_device* cam = - (struct w9968cf_device*)video_devdata(filp)->priv; + struct w9968cf_device* cam; int err; + 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 " "'ovcamchip' module for the %s (/dev/video%d). Make " "sure it is loaded *before* the 'w9968cf' module.", - symbolic(camlist, cam->id),cam->v4ldev.minor) + symbolic(camlist, cam->id), cam->v4ldev->minor) up(&cam->dev_sem); return -ENODEV; } if (cam->users) { DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.", - symbolic(camlist, cam->id),cam->v4ldev.minor, cam->command) + symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command) if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { up(&cam->dev_sem); return -EWOULDBLOCK; @@ -2680,8 +2700,8 @@ down(&cam->dev_sem); } - DBG(5, "Opening the %s, /dev/video%d ...", - symbolic(camlist, cam->id), cam->v4ldev.minor) + DBG(5, "Opening '%s', /dev/video%d ...", + symbolic(camlist, cam->id), cam->v4ldev->minor) cam->streaming = 0; cam->misconfigured = 0; @@ -2698,7 +2718,7 @@ if ((err = w9968cf_start_transfer(cam))) goto deallocate_memory; - filp->private_data = (void*)cam; + filp->private_data = cam; cam->users++; strcpy(cam->command, current->comm); @@ -2720,8 +2740,9 @@ static int w9968cf_release(struct inode* inode, struct file* filp) { - struct w9968cf_device* cam = - (struct w9968cf_device*)video_devdata(filp)->priv; + struct w9968cf_device* cam; + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); down(&cam->dev_sem); /* prevent disconnect() to be called */ @@ -2749,11 +2770,12 @@ static ssize_t w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos) { - struct w9968cf_device* cam = - (struct w9968cf_device*)video_devdata(filp)->priv; + struct w9968cf_device* cam; struct w9968cf_frame_t* fr; int err = 0; + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + if (filp->f_flags & O_NONBLOCK) return -EWOULDBLOCK; @@ -2817,9 +2839,8 @@ static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) { - struct w9968cf_device* cam = - (struct w9968cf_device*)video_devdata(filp)->priv; - + 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), start = vma->vm_start, @@ -2860,10 +2881,11 @@ w9968cf_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct w9968cf_device* cam = - (struct w9968cf_device*)video_devdata(filp)->priv; + struct w9968cf_device* cam; int err; + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + if (down_interruptible(&cam->fileop_sem)) return -ERESTARTSYS; @@ -2879,7 +2901,7 @@ return -EIO; } - err = w9968cf_do_ioctl(cam, cmd, (void*)arg); + err = w9968cf_v4l_ioctl(inode, filp, cmd, (void* )arg); up(&cam->fileop_sem); return err; @@ -2887,8 +2909,10 @@ static int -w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg) +w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, void* arg) { + struct w9968cf_device* cam; const char* v4l1_ioctls[] = { "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", @@ -2900,7 +2924,9 @@ #define V4L1_IOCTL(cmd) \ ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \ - v4l1_ioctls[_IOC_NR((cmd))] : "???") + v4l1_ioctls[_IOC_NR((cmd))] : "?") + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); switch (cmd) { @@ -2914,7 +2940,7 @@ .minheight = cam->minheight, }; sprintf(cap.name, "W996[87]CF USB Camera #%d", - cam->v4ldev.minor); + cam->v4ldev->minor); cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present) ? W9968CF_MAX_WIDTH : cam->maxwidth; cap.maxheight = (cam->upscaling && w9968cf_vppmod_present) @@ -2965,15 +2991,10 @@ case VIDIOCGPICT: /* get image properties of the picture */ { - struct video_picture pict; - - if (w9968cf_sensor_get_picture(cam, &pict)) + if (w9968cf_sensor_get_picture(cam)) return -EIO; - pict.depth = cam->picture.depth; - pict.palette = cam->picture.palette; - - if (copy_to_user(arg, &pict, sizeof(pict))) + if (copy_to_user(arg, &cam->picture, sizeof(cam->picture))) return -EFAULT; DBG(5, "VIDIOCGPICT successfully called.") @@ -3002,13 +3023,6 @@ return -EINVAL; } - if (pict.depth != w9968cf_valid_depth(pict.palette)) { - DBG(4, "Depth %d bpp is not supported for %s palette. " - "VIDIOCSPICT failed.", - pict.depth, symbolic(v4l1_plist, pict.palette)) - return -EINVAL; - } - if (!cam->force_palette) { if (cam->decompression == 0) { if (w9968cf_need_decompression(pict.palette)) { @@ -3027,9 +3041,15 @@ } } - if (pict.palette != cam->picture.palette || - pict.depth != cam->picture.depth) - { + 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.", + pict.depth, symbolic(v4l1_plist, pict.palette), + w9968cf_valid_depth(pict.palette)) + pict.depth = w9968cf_valid_depth(pict.palette); + } + + if (pict.palette != cam->picture.palette) { if(*cam->requested_frame || cam->frame_current->queued) { err = wait_event_interruptible @@ -3052,15 +3072,9 @@ if (w9968cf_start_transfer(cam)) goto ioctl_fail; - } else if ( ((pict.brightness != cam->picture.brightness) && - (!cam->auto_brt)) || - pict.hue != cam->picture.hue || - pict.colour != cam->picture.colour || - pict.contrast != cam->picture.contrast || - pict.whiteness != cam->picture.whiteness ) { - if (w9968cf_sensor_set_picture(cam, pict)) - return -EIO; - } + } else if (w9968cf_sensor_update_picture(cam, pict)) + return -EIO; + DBG(5, "VIDIOCSPICT successfully called.") return 0; @@ -3092,7 +3106,6 @@ win.y != cam->window.y || win.width != cam->window.width || win.height != cam->window.height) { - if(*cam->requested_frame || cam->frame_current->queued) { err = wait_event_interruptible @@ -3203,12 +3216,12 @@ } } - if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width, - (u16*)&mmap.height)) { + if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, + (u16*)&mmap.height))) { DBG(4, "Resolution not supported (%dx%d). " "VIDIOCMCAPTURE failed.", mmap.width, mmap.height) - return -EINVAL; + return err; } fr = &cam->frame[mmap.frame]; @@ -3276,10 +3289,13 @@ case VIDIOCSYNC: /* wait until the capture of a frame is finished */ { - unsigned int f_num = *((unsigned int *) arg); + unsigned int f_num; struct w9968cf_frame_t* fr; int err = 0; + 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) @@ -3323,7 +3339,7 @@ case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/ { struct video_unit unit = { - .video = cam->v4ldev.minor, + .video = cam->v4ldev->minor, .vbi = VIDEO_NO_UNIT, .radio = VIDEO_NO_UNIT, .audio = VIDEO_NO_UNIT, @@ -3342,9 +3358,8 @@ case VIDIOCGFBUF: { - struct video_buffer* buffer = (struct video_buffer*)arg; - - memset(buffer, 0, sizeof(struct video_buffer)); + if (clear_user(arg, sizeof(struct video_buffer))) + return -EFAULT; DBG(5, "VIDIOCGFBUF successfully called.") return 0; @@ -3470,10 +3485,6 @@ else return -ENODEV; - /* We don't handle multi-config cameras */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - DBG(2, "%s detected.", symbolic(camlist, mod_id)) if (simcams > W9968CF_MAX_DEVICES) @@ -3491,14 +3502,6 @@ return -EPERM; } - err = usb_set_configuration(udev, 1); - err += usb_set_interface(udev, 0, 0); - - if (err) { - DBG(1, "Device configuration failed.") - return -EIO; - } - cam = (struct w9968cf_device*) kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL); @@ -3530,10 +3533,24 @@ } memset(cam->data_buffer, 0, 8); - /* Set some basic constants */ - w9968cf_configure_camera(cam, udev, mod_id, dev_nr); + /* Register the V4L device */ + cam->v4ldev = video_device_alloc(); + if (!cam->v4ldev) { + DBG(1, "Could not allocate memory for a V4L structure.") + err = -ENOMEM; + goto fail; + } - err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, + strcpy(cam->v4ldev->name, symbolic(camlist, mod_id)); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = VID_HARDWARE_W9968CF; + cam->v4ldev->fops = &w9968cf_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); if (err) { DBG(1, "V4L device registration failed.") @@ -3544,13 +3561,15 @@ 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) - /* Ok, add a new entry into the list of V4L registered devices */ + /* Set some basic constants */ + w9968cf_configure_camera(cam, udev, mod_id, dev_nr); + + /* Add a new entry into the list of V4L registered devices */ down(&w9968cf_devlist_sem); list_add(&cam->v4llist, &w9968cf_dev_list); up(&w9968cf_devlist_sem); - dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; w9968cf_turn_on_led(cam); @@ -3559,8 +3578,7 @@ up(&cam->dev_sem); - dev_set_drvdata(&intf->dev, (void*)cam); - + usb_set_intfdata(intf, cam); return 0; fail: /* Free unused memory */ @@ -3569,6 +3587,8 @@ 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); } @@ -3579,9 +3599,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) { struct w9968cf_device* cam = - (struct w9968cf_device*)dev_get_drvdata(&intf->dev); - - dev_set_drvdata(&intf->dev, NULL); + (struct w9968cf_device*)usb_get_intfdata(intf); if (cam) { /* Prevent concurrent accesses to data */ @@ -3599,7 +3617,7 @@ 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->v4ldev->minor, cam->command) cam->misconfigured = 1; @@ -3613,6 +3631,8 @@ if (!cam->users) kfree(cam); } + + usb_set_intfdata(intf, NULL); } diff -Nru a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h --- a/drivers/usb/media/w9968cf.h Tue Jan 20 17:34:06 2004 +++ b/drivers/usb/media/w9968cf.h Tue Jan 20 17:34:06 2004 @@ -1,7 +1,7 @@ /*************************************************************************** * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * * * - * Copyright (C) 2002 2003 by Luca Risolia * + * 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 * @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -105,7 +106,7 @@ #define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */ #define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */ -#define W9968CF_SENSOR_MONO 0 /* 0 not monochrome, 1 monochrome sensor */ +#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */ #define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */ #define W9968CF_HUE 32768 /* from 0 to 65535 */ #define W9968CF_COLOUR 32768 /* from 0 to 65535 */ @@ -129,9 +130,10 @@ #define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ "Dual Mode Camera Chip" -#define W9968CF_MODULE_VERSION "v1.22" -#define W9968CF_MODULE_AUTHOR "(C) 2002 2003 Luca Risolia" -#define W9968CF_AUTHOR_EMAIL "" +#define W9968CF_MODULE_VERSION "v1.25-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 */ @@ -171,6 +173,7 @@ }; struct w9968cf_frame_t { + #define W9968CF_HW_BUF_SIZE 640*480*2 /* buf.size of original frames */ void* buffer; u32 length; enum w9968cf_frame_status status; @@ -194,8 +197,8 @@ struct w9968cf_device { enum w9968cf_model_id id; /* private device identifier */ - struct video_device v4ldev; /* V4L structure */ - struct list_head v4llist; /* entry of the list of V4L cameras */ + struct video_device* v4ldev; /* -> V4L structure */ + struct list_head v4llist; /* entry of the list of V4L cameras */ struct usb_device* usbdev; /* -> main USB structure */ struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */ @@ -207,9 +210,9 @@ struct w9968cf_frame_t frame_tmp; /* temporary frame */ struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */ struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; - void* vpp_buffer; /* -> helper buffer for post-processing routines */ + void* vpp_buffer; /*-> helper buf.for video post-processing routines */ - u8 max_buffers, /* number of requested buffers */ + u8 max_buffers, /* number of requested buffers */ force_palette, /* yes=1/no=0 */ force_rgb, /* read RGB instead of BGR, yes=1, no=0 */ double_buffer, /* hardware double buffering yes=1/no=0 */ @@ -220,15 +223,17 @@ decompression, /* 0=disabled, 1=forced, 2=allowed */ upscaling; /* software image scaling, 0=enabled, 1=disabled */ - struct video_picture picture; /* current window settings */ - struct video_window window; /* current picture settings */ + struct video_picture picture; /* current picture settings */ + struct video_window window; /* current window settings */ u16 hw_depth, /* depth (used by the chip) */ hw_palette, /* palette (used by the chip) */ hw_width, /* width (used by the chip) */ hw_height, /* height (used by the chip) */ hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ - vs_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*/ enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */ @@ -239,16 +244,15 @@ users, /* flag: number of users holding the device */ streaming; /* flag: yes=1, no=0 */ - int sensor; /* type of image CMOS sensor chip (CC_*) */ - - /* Determined by CMOS sensor type */ - u16 maxwidth, - maxheight, - minwidth, - minheight, - start_cropx, - start_cropy; + u8 sensor_initialized; /* flag: yes=1, no=0 */ + /* Determined by CMOS 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 */ u8 auto_brt, /* auto brightness enabled flag */ auto_exp, /* auto exposure enabled flag */ backlight, /* backlight exposure algorithm flag */ @@ -256,7 +260,6 @@ lightfreq, /* power (lighting) frequency */ bandfilt; /* banding filter enabled flag */ s8 clockdiv; /* clock divisor */ - int sensor_mono; /* CMOS sensor is (probably) monochrome */ /* I2C interface to kernel */ struct i2c_adapter i2c_adapter; @@ -271,14 +274,9 @@ wait_queue_head_t open, wait_queue; }; -#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */ - -#define SENSOR_FORMAT VIDEO_PALETTE_UYVY -#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) - /**************************************************************************** - * Macros and other constants * + * Macros for debugging * ****************************************************************************/ #undef DBG @@ -294,7 +292,7 @@ else if ((level) == 4) \ warn(fmt, ## args); \ else if ((level) >= 5) \ - info("[%s,%d] " fmt, \ + info("[%s:%d] " fmt, \ __PRETTY_FUNCTION__, __LINE__ , ## args); \ } \ } @@ -305,7 +303,7 @@ #undef PDBG #undef PDBGG -#define PDBG(fmt, args...) info("[%s, %d] "fmt, \ +#define PDBG(fmt, args...) info("[%s:%d] "fmt, \ __PRETTY_FUNCTION__, __LINE__ , ## args); #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ diff -Nru a/drivers/usb/media/w9968cf_decoder.h b/drivers/usb/media/w9968cf_decoder.h --- a/drivers/usb/media/w9968cf_decoder.h Tue Jan 20 17:34:06 2004 +++ b/drivers/usb/media/w9968cf_decoder.h Tue Jan 20 17:34:06 2004 @@ -1,7 +1,7 @@ /*************************************************************************** * Video decoder for the W996[87]CF driver for Linux. * * * - * Copyright (C) 2003 by Luca Risolia * + * Copyright (C) 2003 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 * diff -Nru a/drivers/usb/media/w9968cf_externaldef.h b/drivers/usb/media/w9968cf_externaldef.h --- a/drivers/usb/media/w9968cf_externaldef.h Tue Jan 20 17:34:06 2004 +++ b/drivers/usb/media/w9968cf_externaldef.h Tue Jan 20 17:34:06 2004 @@ -1,8 +1,9 @@ /*************************************************************************** - * Various definitions for compatibility with external modules. * + * Various definitions for compatibility with OVCAMCHIP external module. * * This file is part of the W996[87]CF driver for Linux. * * * - * Copyright (C) 2002 2003 by Luca Risolia * + * 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 * @@ -27,10 +28,8 @@ #include #include -/* The following values have been copied from the "ovcamchip" module. */ - #ifndef I2C_DRIVERID_OVCAMCHIP -# define I2C_DRIVERID_OVCAMCHIP 0xf00f +# define I2C_DRIVERID_OVCAMCHIP 0xf00f #endif /* Controls */ @@ -77,10 +76,10 @@ int width; int height; int format; - int quarter; /* Scale width and height down 2x */ + int quarter; /* Scale width and height down 2x */ /* This stuff will be removed eventually */ - int clockdiv; /* Clock divisor setting */ + int clockdiv; /* Clock divisor setting */ }; /* Commands.