Commit be3a4fef authored by Luca Risolia's avatar Luca Risolia Committed by Greg Kroah-Hartman

[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: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 75509570
...@@ -2702,7 +2702,9 @@ S: Finland ...@@ -2702,7 +2702,9 @@ S: Finland
N: Luca Risolia N: Luca Risolia
E: luca.risolia@studio.unibo.it 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: 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: Via Liberta' 41/A
S: Osio Sotto, 24046, Bergamo S: Osio Sotto, 24046, Bergamo
S: Italy S: Italy
......
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 <luca.risolia@studio.unibo.it>
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: <n>
Description: Debugging information level, from 0 to 3:
0 = none (use carefully)
1 = critical errors
2 = significant informations
3 = more verbose messages
Level 3 is useful for testing only, when 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 <luca.risolia@studio.unibo.it>.
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.
...@@ -23,6 +23,9 @@ Index ...@@ -23,6 +23,9 @@ Index
============ ============
Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
Winbond is a trademark of Winbond Electronics Corporation.
This driver is not sponsored or developed by Winbond.
2. License 2. License
========== ==========
...@@ -44,8 +47,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ...@@ -44,8 +47,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
3. Overview 3. Overview
=========== ===========
This driver supports the video streaming capabilities of the devices mounting This driver supports the video streaming capabilities of the devices mounting
Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips. OV681
are being commanded by USB. OV681 based cameras should be supported as well. based cameras should be supported as well.
The driver is divided 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 the supported devices to work; the second one, "w9968cf-vpp", is an optional
...@@ -58,7 +61,8 @@ Please keep in mind that official kernels do NOT include the second module for ...@@ -58,7 +61,8 @@ 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 performance purposes. However it is always recommended to download and install
the latest and complete release of the driver, replacing the existing one, if 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 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: 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 http://go.lamarinapunto.com/ . Please refer to the documentation included in
...@@ -68,38 +72,37 @@ Up to 32 cameras can be handled at the same time. They can be connected and ...@@ -68,38 +72,37 @@ 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 disconnected from the host many times without turning off the computer, if
your system supports the hotplug facility. 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. through command line when the module is loaded into memory.
The driver relies on the Video4Linux, USB and I2C core modules of the official The driver relies on the Video4Linux, USB and I2C core modules. It has been
Linux kernels. It has been designed to run properly on SMP systems as well. designed to run properly on SMP systems as well. An additional module,
At the moment, an additional module, "ovcamchip", is mandatory; it provides "ovcamchip", is mandatory; it provides support for some OmniVision image
support for some OmniVision CMOS sensors connected to the W996[87]CF chips. 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
The "ovcamchip" module is part of the OV511 driver, version 2.27, which can be compiled with the automatic module loading option).
downloaded from internet:
http://alpha.dyndns.org/ov511/
To know how to compile it, read the documentation included in the OV511
package.
4. Supported devices 4. Supported devices
==================== ====================
At the moment, known W996[87]CF and OV681 based devices are: At the moment, known W996[87]CF and OV681 based devices are:
- Aroma Digi Pen ADG-5000 Refurbished - Aroma Digi Pen VGA Dual Mode ADG-5000 (unknown image sensor)
- AVerTV USB - AVerMedia AVerTV USB (SAA7111A, Philips FI1216Mk2 tuner, PT2313L audio chip)
- Creative Labs Video Blaster WebCam Go - Creative Labs Video Blaster WebCam Go (OmniVision OV7610 sensor)
- Creative Labs Video Blaster WebCam Go Plus - Creative Labs Video Blaster WebCam Go Plus (OmniVision OV7620 sensor)
- Die Lebon LDC-D35A Digital Kamera - Lebon LDC-035A (unknown image sensor)
- Ezonics EZ-802 EZMega Cam - Ezonics EZ-802 EZMega Cam (OmniVision OV8610C sensor)
- OPCOM Digi Pen VGA Dual Mode Pen Camera - 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. 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 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. 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/ this module: http://alpha.dyndns.org/ov511/
Possible external microcontrollers of those webcams are not supported: this Possible external microcontrollers of those webcams are not supported: this
...@@ -112,8 +115,10 @@ additional testing and full support, would be much appreciated. ...@@ -112,8 +115,10 @@ additional testing and full support, would be much appreciated.
5. Module dependencies 5. Module dependencies
====================== ======================
For it to work properly, the driver needs kernel support for Video4Linux, For it to work properly, the driver needs kernel support for Video4Linux, USB
USB and I2C, and a third-party module for the CMOS sensor. 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 The following options of the kernel configuration file must be enabled and
corresponding modules must be compiled: corresponding modules must be compiled:
...@@ -128,6 +133,10 @@ corresponding modules must be compiled: ...@@ -128,6 +133,10 @@ corresponding modules must be compiled:
The I2C core module can be compiled statically in the kernel as well. The I2C core module can be compiled statically in the kernel as well.
# OmniVision Camera Chip support
#
CONFIG_VIDEO_OVCAMCHIP=m
# USB support # USB support
# #
CONFIG_USB=m CONFIG_USB=m
...@@ -141,19 +150,12 @@ below is necessary: ...@@ -141,19 +150,12 @@ below is necessary:
CONFIG_USB_UHCI_HCD=m CONFIG_USB_UHCI_HCD=m
CONFIG_USB_OHCI_HCD=m CONFIG_USB_OHCI_HCD=m
Also, make sure "Enforce bandwidth allocation" is NOT enabled.
And finally: And finally:
# USB Multimedia devices # USB Multimedia devices
# #
CONFIG_USB_W9968CF=m 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 6. Module loading
================= =================
...@@ -164,11 +166,10 @@ Loading can be done this way, from root: ...@@ -164,11 +166,10 @@ Loading can be done this way, from root:
[root@localhost home]# modprobe usbcore [root@localhost home]# modprobe usbcore
[root@localhost home]# modprobe i2c-core [root@localhost home]# modprobe i2c-core
[root@localhost ov511-x.xx]# insmod ./ovcamchip.ko
[root@localhost home]# modprobe w9968cf [root@localhost home]# modprobe w9968cf
At this point the devices should be recognized: "dmesg" can be used to analyze At this point the pertinent devices should be recognized: "dmesg" can be used
kernel messages: to analyze kernel messages:
[user@localhost home]$ dmesg [user@localhost home]$ dmesg
...@@ -180,9 +181,22 @@ explanation about them and which syntax to use, it is recommended to run the ...@@ -180,9 +181,22 @@ explanation about them and which syntax to use, it is recommended to run the
[root@locahost home]# modinfo w9968cf [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 Name: vppmod_load
Type: bool Type: bool
...@@ -191,10 +205,14 @@ Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled. ...@@ -191,10 +205,14 @@ Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled.
If enabled, every time an application attempts to open a If enabled, every time an application attempts to open a
camera, 'insmod' searches for the video post-processing module camera, 'insmod' searches for the video post-processing module
in the system and loads it automatically (if present). 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, 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 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 Name: simcams
Type: int Type: int
...@@ -237,7 +255,7 @@ Syntax: <0|1[,...]> ...@@ -237,7 +255,7 @@ Syntax: <0|1[,...]>
Description: Hardware double buffering: 0 disabled, 1 enabled. Description: Hardware double buffering: 0 disabled, 1 enabled.
It should be enabled if you want smooth video output: if you It should be enabled if you want smooth video output: if you
obtain out of sync. video, disable it, or try to 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. Default: 1 for every device.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: clamping Name: clamping
...@@ -252,7 +270,7 @@ Syntax: <0|1|2[,...]> ...@@ -252,7 +270,7 @@ Syntax: <0|1|2[,...]>
Description: Video filter type. Description: Video filter type.
0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter. 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 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. Default: 0 for every device.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: largeview Name: largeview
...@@ -269,7 +287,7 @@ Description: Software scaling (for non-compressed video only): ...@@ -269,7 +287,7 @@ Description: Software scaling (for non-compressed video only):
Disable it if you have a slow CPU or you don't have enough Disable it if you have a slow CPU or you don't have enough
memory. memory.
Default: 0 for every device. 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 Name: decompression
Type: int array (min = 0, max = 32) Type: int array (min = 0, max = 32)
...@@ -284,8 +302,8 @@ Description: Software video decompression: ...@@ -284,8 +302,8 @@ Description: Software video decompression:
YUV420P/YUV420 in any resolutions where width and height are YUV420P/YUV420 in any resolutions where width and height are
multiples of 16. multiples of 16.
Default: 2 for every device. Default: 2 for every device.
Note: If 'w9968cf-vpp' is not loaded, forcing decompression is not Note: If 'w9968cf-vpp' is not present, forcing decompression is not
allowed; in this case this paramater is set to 2. allowed; in this case this parameter is set to 2.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: force_palette Name: force_palette
Type: int array (min = 0, max = 32) Type: int array (min = 0, max = 32)
...@@ -304,9 +322,9 @@ Description: Force picture palette. ...@@ -304,9 +322,9 @@ Description: Force picture palette.
3 = RGB565 16 bpp - Software conversion from UYVY 3 = RGB565 16 bpp - Software conversion from UYVY
4 = RGB24 24 bpp - Software conversion from UYVY 4 = RGB24 24 bpp - Software conversion from UYVY
5 = RGB32 32 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). 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 Name: force_rgb
Type: bool array (min = 0, max = 32) Type: bool array (min = 0, max = 32)
...@@ -320,14 +338,14 @@ Default: 0 for every device. ...@@ -320,14 +338,14 @@ Default: 0 for every device.
Name: autobright Name: autobright
Type: bool array (min = 0, max = 32) Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]> Syntax: <0|1[,...]>
Description: CMOS sensor automatically changes brightness: Description: Image sensor automatically changes brightness:
0 = no, 1 = yes 0 = no, 1 = yes
Default: 0 for every device. Default: 0 for every device.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: autoexp Name: autoexp
Type: bool array (min = 0, max = 32) Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]> Syntax: <0|1[,...]>
Description: CMOS sensor automatically changes exposure: Description: Image sensor automatically changes exposure:
0 = no, 1 = yes 0 = no, 1 = yes
Default: 1 for every device. Default: 1 for every device.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -354,7 +372,7 @@ Syntax: <-1|n[,...]> ...@@ -354,7 +372,7 @@ Syntax: <-1|n[,...]>
Description: Force pixel clock divisor to a specific value (for experts): Description: Force pixel clock divisor to a specific value (for experts):
n may vary from 0 to 127. n may vary from 0 to 127.
-1 for automatic value. -1 for automatic value.
See also the 'double_buffer' module paramater. See also the 'double_buffer' module parameter.
Default: -1 for every device. Default: -1 for every device.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: backlight Name: backlight
...@@ -374,7 +392,7 @@ Default: 0 for every device. ...@@ -374,7 +392,7 @@ Default: 0 for every device.
Name: monochrome Name: monochrome
Type: bool array (min = 0, max = 32) Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]> Syntax: <0|1[,...]>
Description: The CMOS sensor is monochrome: Description: The image sensor is monochrome:
0 = no, 1 = yes 0 = no, 1 = yes
Default: 0 for every device. Default: 0 for every device.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -420,7 +438,7 @@ Description: Debugging information level, from 0 to 6: ...@@ -420,7 +438,7 @@ Description: Debugging information level, from 0 to 6:
4 = warnings 4 = warnings
5 = called functions 5 = called functions
6 = function internals 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. device is used.
Default: 2 Default: 2
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -449,7 +467,7 @@ The development would not have proceed much further without having looked at ...@@ -449,7 +467,7 @@ 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 the source code of other drivers and without the help of several persons; in
particular: 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; been taken from the OV511 driver by Mark McClelland;
- memory management code has been copied from the bttv driver by Ralph Metzler, - memory management code has been copied from the bttv driver by Ralph Metzler,
......
...@@ -162,6 +162,21 @@ config USB_SE401 ...@@ -162,6 +162,21 @@ config USB_SE401
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called se401. 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 <file:Documentation/usb/sn9c102.txt> for more informations.
This driver uses the Video For Linux API. You must say Y or M to
"Video For Linux" to use this driver.
To compile this driver as a module, choose M here: the
module will be called sn9c102.
config USB_STV680 config USB_STV680
tristate "USB STV680 (Pencam) Camera support" tristate "USB STV680 (Pencam) Camera support"
depends on USB && VIDEO_DEV depends on USB && VIDEO_DEV
...@@ -181,7 +196,7 @@ config USB_STV680 ...@@ -181,7 +196,7 @@ config USB_STV680
config USB_W9968CF config USB_W9968CF
tristate "USB W996[87]CF JPEG Dual Mode Camera support" 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--- ---help---
Say Y here if you want support for cameras based on OV681 or Say Y here if you want support for cameras based on OV681 or
Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
...@@ -190,16 +205,13 @@ config USB_W9968CF ...@@ -190,16 +205,13 @@ config USB_W9968CF
separate module only (released under GPL). It allows to use higher separate module only (released under GPL). It allows to use higher
resolutions and framerates, but cannot be included in the official resolutions and framerates, but cannot be included in the official
Linux kernel for performance purposes. 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 See <file:Documentation/usb/w9968cf.txt> for more informations.
<file:Documentation/usb/w9968cf.txt> for more informations and for
a list of supported cameras. 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
This driver uses the Video For Linux and the I2C APIs. You must say "Video For Linux", "I2C Support" and "OmniVision Camera Chip
Y or M to both "Video For Linux" and "I2C Support" to use this support" to use this driver.
driver.
To compile this driver as a module, choose M here: the
This code is also available as a module ( = code which can be module will be called w9968cf.
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 <file:Documentation/kbuild/modules.txt>.
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o 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_DABUSB) += dabusb.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_DSBR) += dsbr100.o
...@@ -11,6 +12,7 @@ obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o ...@@ -11,6 +12,7 @@ obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o
obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_PWC) += pwc.o obj-$(CONFIG_USB_PWC) += pwc.o
obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_SE401) += se401.o
obj-$(CONFIG_USB_SN9C102) += sn9c102.o
obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o
obj-$(CONFIG_USB_W9968CF) += w9968cf.o obj-$(CONFIG_USB_W9968CF) += w9968cf.o
/***************************************************************************
* V4L2 driver for SN9C10[12] PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#ifndef _SN9C102_H_
#define _SN9C102_H_
#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/param.h>
#include <asm/semaphore.h>
#include <asm/rwsem.h>
#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 "<luca.risolia@studio.unibo.it>"
#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_ */
/***************************************************************************
* V4L2 driver for SN9C10[12] PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/stddef.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#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<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);
/***************************************************************************
* Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/delay.h>
#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 <luca.risolia@studio.unibo.it>",
.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;
}
/***************************************************************************
* API for image sensors connected to the SN9C10[12] PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#ifndef _SN9C102_SENSOR_H_
#define _SN9C102_SENSOR_H_
#include <linux/usb.h>
#include <linux/videodev.h>
#include <linux/device.h>
#include <linux/stddef.h>
#include <linux/errno.h>
#include <asm/types.h>
struct 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 <email> */
/* 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_ */
/***************************************************************************
* Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC *
* Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include "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 <luca.risolia@studio.unibo.it>",
.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;
}
/***************************************************************************
* Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC *
* Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include "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 <luca.risolia@studio.unibo.it>",
.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;
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* * * *
* - Memory management code from bttv driver by Ralph Metzler, * * - Memory management code from bttv driver by Ralph Metzler, *
* Marcus Metzler and Gerd Knorr. * * 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. * * some symbolic names from OV511 driver by Mark W. McClelland. *
* - Low-level I2C fast write function by Piotr Czerczak. * * - Low-level I2C fast write function by Piotr Czerczak. *
* - Low-level I2C read function by Frederic Jouault. * * - Low-level I2C read function by Frederic Jouault. *
...@@ -27,21 +27,23 @@ ...@@ -27,21 +27,23 @@
#include <linux/version.h> #include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/ctype.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/stddef.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/page-flags.h>
#include <linux/moduleparam.h>
#include "w9968cf.h" #include "w9968cf.h"
#include "w9968cf_decoder.h" #include "w9968cf_decoder.h"
...@@ -49,14 +51,18 @@ ...@@ -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_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_LICENSE(W9968CF_MODULE_LICENSE);
MODULE_SUPPORTED_DEVICE("Video"); MODULE_SUPPORTED_DEVICE("Video");
static int ovmod_load = W9968CF_OVMOD_LOAD;
static int vppmod_load = W9968CF_VPPMOD_LOAD; static int vppmod_load = W9968CF_VPPMOD_LOAD;
static unsigned short simcams = W9968CF_SIMCAMS; static unsigned short simcams = W9968CF_SIMCAMS;
static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
...@@ -100,8 +106,11 @@ static int specific_debug = W9968CF_SPECIFIC_DEBUG; ...@@ -100,8 +106,11 @@ static int specific_debug = W9968CF_SPECIFIC_DEBUG;
static unsigned int param_nv[24]; /* number of values per parameter */ 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(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(video_nr, short, param_nv[0], 0444);
module_param_array(packet_size, uint, param_nv[1], 0444); module_param_array(packet_size, uint, param_nv[1], 0444);
module_param_array(max_buffers, ushort, param_nv[2], 0444); module_param_array(max_buffers, ushort, param_nv[2], 0444);
...@@ -127,21 +136,34 @@ module_param_array(colour, uint, param_nv[21], 0444); ...@@ -127,21 +136,34 @@ module_param_array(colour, uint, param_nv[21], 0444);
module_param_array(contrast, uint, param_nv[22], 0444); module_param_array(contrast, uint, param_nv[22], 0444);
module_param_array(whiteness, uint, param_nv[23], 0444); module_param_array(whiteness, uint, param_nv[23], 0444);
#ifdef W9968CF_DEBUG #ifdef W9968CF_DEBUG
module_param(debug, ushort, 0444); module_param(debug, ushort, 0644);
module_param(specific_debug, bool, 0444); module_param(specific_debug, bool, 0644);
#endif #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, MODULE_PARM_DESC(vppmod_load,
"\n<0|1> Automatic 'w9968cf-vpp' module loading." "\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" "\nIf enabled, every time an application attempts to open a"
"\ncamera, 'insmod' searches for the video post-processing" "\ncamera, 'insmod' searches for the video post-processing"
"\nmodule in the system and loads it automatically (if" "\nmodule in the system and loads it automatically (if"
"\npresent). The 'w9968cf-vpp' module adds extra image" "\npresent). The optional 'w9968cf-vpp' module adds extra"
"\nmanipulation functions to the 'w9968cf' module, like" "\n image manipulation functions to the 'w9968cf' module,like"
"\nsoftware up-scaling,colour conversions and video decoding." "\nsoftware up-scaling,colour conversions and video decoding"
"\nfor very high frame rates."
"\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"." "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"."
"\n"); "\n");
#endif
MODULE_PARM_DESC(simcams, MODULE_PARM_DESC(simcams,
"\n<n> Number of cameras allowed to stream simultaneously." "\n<n> Number of cameras allowed to stream simultaneously."
"\nn may vary from 0 to " "\nn may vary from 0 to "
...@@ -176,8 +198,8 @@ MODULE_PARM_DESC(double_buffer, ...@@ -176,8 +198,8 @@ MODULE_PARM_DESC(double_buffer,
"\n<0|1[,...]> " "\n<0|1[,...]> "
"Hardware double buffering: 0 disabled, 1 enabled." "Hardware double buffering: 0 disabled, 1 enabled."
"\nIt should be enabled if you want smooth video output: if" "\nIt should be enabled if you want smooth video output: if"
"\nyou obtain out of sync. video, disable it at all, or try" "\nyou obtain out of sync. video, disable it, or try to"
"\nto decrease the 'clockdiv' module paramater value." "\ndecrease the 'clockdiv' module parameter value."
"\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER)
" for every device." " for every device."
"\n"); "\n");
...@@ -193,7 +215,7 @@ MODULE_PARM_DESC(filter_type, ...@@ -193,7 +215,7 @@ MODULE_PARM_DESC(filter_type,
"\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE) "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE)
" for every device." " for every device."
"\nThe filter is used to reduce noise and aliasing artifacts" "\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." " process."
"\n"); "\n");
MODULE_PARM_DESC(largeview, MODULE_PARM_DESC(largeview,
...@@ -208,7 +230,7 @@ MODULE_PARM_DESC(upscaling, ...@@ -208,7 +230,7 @@ MODULE_PARM_DESC(upscaling,
" enough memory." " enough memory."
"\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
" for every device." " for every device."
"\nIf 'w9968cf-vpp' is not loaded, this paramater is" "\nIf 'w9968cf-vpp' is not present, this parameter is"
" set to 0." " set to 0."
"\n"); "\n");
MODULE_PARM_DESC(decompression, MODULE_PARM_DESC(decompression,
...@@ -224,8 +246,8 @@ MODULE_PARM_DESC(decompression, ...@@ -224,8 +246,8 @@ MODULE_PARM_DESC(decompression,
"a multiple of 16." "a multiple of 16."
"\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION) "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
" for every device." " for every device."
"\nIf 'w9968cf-vpp' is not loaded, forcing decompression is " "\nIf 'w9968cf-vpp' is not present, forcing decompression is "
"\nnot allowed; in this case this paramater is set to 2." "\nnot allowed; in this case this parameter is set to 2."
"\n"); "\n");
MODULE_PARM_DESC(force_palette, MODULE_PARM_DESC(force_palette,
"\n<0" "\n<0"
...@@ -255,11 +277,11 @@ MODULE_PARM_DESC(force_palette, ...@@ -255,11 +277,11 @@ MODULE_PARM_DESC(force_palette,
"\n- RGB565 16 bpp - Software conversion from UYVY" "\n- RGB565 16 bpp - Software conversion from UYVY"
"\n- RGB24 24 bpp - Software conversion from UYVY" "\n- RGB24 24 bpp - Software conversion from UYVY"
"\n- RGB32 32 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." "\nDefault value is 0 for every device."
"\nInitial palette is " "\nInitial palette is "
__MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." __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)." " set to 9 (UYVY)."
"\n"); "\n");
MODULE_PARM_DESC(force_rgb, MODULE_PARM_DESC(force_rgb,
...@@ -271,13 +293,13 @@ MODULE_PARM_DESC(force_rgb, ...@@ -271,13 +293,13 @@ MODULE_PARM_DESC(force_rgb,
" for every device." " for every device."
"\n"); "\n");
MODULE_PARM_DESC(autobright, 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" "\n 0 = no, 1 = yes"
"\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT) "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT)
" for every device." " for every device."
"\n"); "\n");
MODULE_PARM_DESC(autoexp, 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" "\n 0 = no, 1 = yes"
"\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP)
" for every device." " for every device."
...@@ -304,7 +326,7 @@ MODULE_PARM_DESC(clockdiv, ...@@ -304,7 +326,7 @@ MODULE_PARM_DESC(clockdiv,
"Force pixel clock divisor to a specific value (for experts):" "Force pixel clock divisor to a specific value (for experts):"
"\n n may vary from 0 to 127." "\n n may vary from 0 to 127."
"\n -1 for automatic value." "\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) "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV)
" for every device." " for every device."
"\n"); "\n");
...@@ -321,7 +343,7 @@ MODULE_PARM_DESC(mirror, ...@@ -321,7 +343,7 @@ MODULE_PARM_DESC(mirror,
" for every device." " for every device."
"\n"); "\n");
MODULE_PARM_DESC(monochrome, 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" "\n 0 = no, 1 = yes"
"\nNot all the sensors support monochrome color." "\nNot all the sensors support monochrome color."
"\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME) "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME)
...@@ -363,7 +385,7 @@ MODULE_PARM_DESC(debug, ...@@ -363,7 +385,7 @@ MODULE_PARM_DESC(debug,
"\n4 = warnings" "\n4 = warnings"
"\n5 = called functions" "\n5 = called functions"
"\n6 = function internals" "\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." "one device is used."
"\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"."
"\n"); "\n");
...@@ -388,14 +410,14 @@ MODULE_PARM_DESC(specific_debug, ...@@ -388,14 +410,14 @@ MODULE_PARM_DESC(specific_debug,
static struct file_operations w9968cf_fops; static struct file_operations w9968cf_fops;
static int w9968cf_open(struct inode*, struct file*); static int w9968cf_open(struct inode*, struct file*);
static int w9968cf_release(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_mmap(struct file*, struct vm_area_struct*);
static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long); 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 */ /* USB-specific */
static int w9968cf_start_transfer(struct w9968cf_device*); 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_stop_transfer(struct w9968cf_device*);
static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index); static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
static int w9968cf_read_reg(struct w9968cf_device*, u16 index); static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
...@@ -403,6 +425,7 @@ static int w9968cf_write_fsb(struct w9968cf_device*, u16* data); ...@@ -403,6 +425,7 @@ static int w9968cf_write_fsb(struct w9968cf_device*, u16* data);
static int w9968cf_write_sb(struct w9968cf_device*, u16 value); static int w9968cf_write_sb(struct w9968cf_device*, u16 value);
static int w9968cf_read_sb(struct w9968cf_device*); static int w9968cf_read_sb(struct w9968cf_device*);
static int w9968cf_upload_quantizationtables(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 */ /* Low-level I2C (SMBus) I/O */
static int w9968cf_smbus_start(struct w9968cf_device*); static int w9968cf_smbus_start(struct w9968cf_device*);
...@@ -439,12 +462,11 @@ static void* rvmalloc(unsigned long size); ...@@ -439,12 +462,11 @@ static void* rvmalloc(unsigned long size);
static void rvfree(void *mem, unsigned long size); static void rvfree(void *mem, unsigned long size);
static void w9968cf_deallocate_memory(struct w9968cf_device*); static void w9968cf_deallocate_memory(struct w9968cf_device*);
static int w9968cf_allocate_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_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_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); unsigned int cmd, void *arg);
static int w9968cf_sensor_init(struct w9968cf_device*); static int w9968cf_sensor_init(struct w9968cf_device*);
static int w9968cf_sensor_update_settings(struct w9968cf_device*); static int w9968cf_sensor_update_settings(struct w9968cf_device*);
...@@ -456,12 +478,13 @@ static int w9968cf_sensor_update_picture(struct w9968cf_device*, ...@@ -456,12 +478,13 @@ static int w9968cf_sensor_update_picture(struct w9968cf_device*,
static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
enum w9968cf_model_id, enum w9968cf_model_id,
const unsigned short dev_nr); 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_turn_on_led(struct w9968cf_device*);
static int w9968cf_init_chip(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_picture(struct w9968cf_device*, struct video_picture);
static int w9968cf_set_window(struct w9968cf_device*, struct video_window); static int w9968cf_set_window(struct w9968cf_device*, struct video_window);
static inline u16 w9968cf_valid_palette(u16 palette); 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 inline u8 w9968cf_need_decompression(u16 palette);
static int w9968cf_postprocess_frame(struct w9968cf_device*, static int w9968cf_postprocess_frame(struct w9968cf_device*,
struct w9968cf_frame_t*); struct w9968cf_frame_t*);
...@@ -472,18 +495,8 @@ static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); ...@@ -472,18 +495,8 @@ static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**);
static void w9968cf_release_resources(struct w9968cf_device*); static void w9968cf_release_resources(struct w9968cf_device*);
/* Intermodule communication */ /* Intermodule communication */
static int w9968cf_vppmod_detect(void); static int w9968cf_vppmod_detect(struct w9968cf_device*);
static void w9968cf_vppmod_release(void); static void w9968cf_vppmod_release(struct w9968cf_device*);
/* 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);
...@@ -518,12 +531,15 @@ static struct w9968cf_symbolic_list camlist[] = { ...@@ -518,12 +531,15 @@ static struct w9968cf_symbolic_list camlist[] = {
{ W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" }, { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" },
/* Other cameras (having the same descriptors as Generic W996[87]CF) */ /* Other cameras (having the same descriptors as Generic W996[87]CF) */
{ W9968CF_MOD_ADPA5R, "Aroma Digi Pen ADG-5000 Refurbished" }, { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" },
{ W9986CF_MOD_AU, "AVerTV USB" }, { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" },
{ W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, { 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_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_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" },
{ W9968CF_MOD_PDPII, "Pretec Digi Pen-II" },
{ W9968CF_MOD_PDP480, "Pretec DigiPen-480" },
{ -1, NULL } { -1, NULL }
}; };
...@@ -648,17 +664,6 @@ static void rvfree(void* mem, unsigned long size) ...@@ -648,17 +664,6 @@ static void rvfree(void* mem, unsigned long size)
} }
/*--------------------------------------------------------------------------
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. Deallocate previously allocated memory.
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
...@@ -674,26 +679,25 @@ static void w9968cf_deallocate_memory(struct w9968cf_device* cam) ...@@ -674,26 +679,25 @@ static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
/* Free temporary frame buffer */ /* Free temporary frame buffer */
if (cam->frame_tmp.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; cam->frame_tmp.buffer = NULL;
} }
/* Free helper buffer */ /* Free helper buffer */
if (cam->vpp_buffer) { if (cam->frame_vpp.buffer) {
rvfree(cam->vpp_buffer, w9968cf_get_max_bufsize(cam)); rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size);
cam->vpp_buffer = NULL; cam->frame_vpp.buffer = NULL;
} }
/* Free video frame buffers */ /* Free video frame buffers */
if (cam->frame[0].buffer) { if (cam->frame[0].buffer) {
rvfree(cam->frame[0].buffer, rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size);
cam->nbuffers * w9968cf_get_max_bufsize(cam));
cam->frame[0].buffer = NULL; cam->frame[0].buffer = NULL;
} }
cam->nbuffers = 0; cam->nbuffers = 0;
DBG(5, "Memory successfully deallocated.") DBG(5, "Memory successfully deallocated")
} }
...@@ -704,19 +708,30 @@ static void w9968cf_deallocate_memory(struct w9968cf_device* cam) ...@@ -704,19 +708,30 @@ static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
static int w9968cf_allocate_memory(struct w9968cf_device* cam) 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]; const u16 p_size = wMaxPacketSize[cam->altsetting-1];
void* buff = NULL; void* buff = NULL;
u8 i; unsigned long hw_bufsize, vpp_bufsize;
u8 i, bpp;
/* NOTE: Deallocation is done elsewhere in case of error */ /* 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 */ /* Allocate memory for the isochronous transfer buffers */
for (i = 0; i < W9968CF_URBS; i++) { for (i = 0; i < W9968CF_URBS; i++) {
if (!(cam->transfer_buffer[i] = if (!(cam->transfer_buffer[i] =
kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
DBG(1, "Couldn't allocate memory for the isochronous " DBG(1, "Couldn't allocate memory for the isochronous "
"transfer buffers (%d bytes).", "transfer buffers (%u bytes)",
p_size * W9968CF_ISO_PACKETS) p_size * W9968CF_ISO_PACKETS)
return -ENOMEM; return -ENOMEM;
} }
...@@ -724,44 +739,49 @@ static int w9968cf_allocate_memory(struct w9968cf_device* cam) ...@@ -724,44 +739,49 @@ static int w9968cf_allocate_memory(struct w9968cf_device* cam)
} }
/* Allocate memory for the temporary frame buffer */ /* 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 " 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; return -ENOMEM;
} }
cam->frame_tmp.size = hw_bufsize;
cam->frame_tmp.number = -1;
/* Allocate memory for the helper buffer */ /* Allocate memory for the helper buffer */
if (w9968cf_vppmod_present) { if (w9968cf_vpp) {
if (!(cam->vpp_buffer = rvmalloc(bufsize))) { if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) {
DBG(1, "Couldn't allocate memory for the helper buffer" DBG(1, "Couldn't allocate memory for the helper buffer"
" (%li bytes).", bufsize) " (%lu bytes)", vpp_bufsize)
return -ENOMEM; return -ENOMEM;
} }
cam->frame_vpp.size = vpp_bufsize;
} else } 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; cam->nbuffers = cam->max_buffers;
while (cam->nbuffers >= 2) { while (cam->nbuffers >= 2) {
if ((buff = rvmalloc(cam->nbuffers * bufsize))) if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize)))
break; break;
else else
cam->nbuffers--; cam->nbuffers--;
} }
if (!buff) { 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; cam->nbuffers = 0;
return -ENOMEM; return -ENOMEM;
} }
if (cam->nbuffers != cam->max_buffers) if (cam->nbuffers != cam->max_buffers)
DBG(2, "Couldn't allocate memory for %d video frame buffers. " DBG(2, "Couldn't allocate memory for %u video frame buffers. "
"Only memory for %d buffers has been allocated.", "Only memory for %u buffers has been allocated",
cam->max_buffers, cam->nbuffers) cam->max_buffers, cam->nbuffers)
for (i = 0; i < cam->nbuffers; i++) { 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 */ /* Circular list */
if (i != cam->nbuffers-1) if (i != cam->nbuffers-1)
cam->frame[i].next = &cam->frame[i+1]; cam->frame[i].next = &cam->frame[i+1];
...@@ -770,7 +790,7 @@ static int w9968cf_allocate_memory(struct w9968cf_device* cam) ...@@ -770,7 +790,7 @@ static int w9968cf_allocate_memory(struct w9968cf_device* cam)
cam->frame[i].status = F_UNUSED; cam->frame[i].status = F_UNUSED;
} }
DBG(5, "Memory successfully allocated.") DBG(5, "Memory successfully allocated")
return 0; return 0;
} }
...@@ -794,20 +814,16 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -794,20 +814,16 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
{ {
struct w9968cf_device* cam = (struct w9968cf_device*)urb->context; struct w9968cf_device* cam = (struct w9968cf_device*)urb->context;
struct w9968cf_frame_t** f; struct w9968cf_frame_t** f;
unsigned long maxbufsize;
unsigned int len, status; unsigned int len, status;
void* pos; void* pos;
u8 i; u8 i;
int err = 0; int err = 0;
if ((!cam->streaming) || cam->disconnected) { if ((!cam->streaming) || cam->disconnected) {
DBG(4, "Got interrupt, but not streaming.") DBG(4, "Got interrupt, but not streaming")
return; return;
} }
maxbufsize = min( (unsigned long)W9968CF_HW_BUF_SIZE,
w9968cf_get_max_bufsize(cam) );
/* "(*f)" will be used instead of "cam->frame_current" */ /* "(*f)" will be used instead of "cam->frame_current" */
f = &cam->frame_current; f = &cam->frame_current;
...@@ -820,8 +836,8 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -820,8 +836,8 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
(*f)->length = cam->frame_tmp.length; (*f)->length = cam->frame_tmp.length;
memcpy((*f)->buffer, cam->frame_tmp.buffer, memcpy((*f)->buffer, cam->frame_tmp.buffer,
(*f)->length); (*f)->length);
DBG(6, "Switched from temp. frame to frame #%zd", DBG(6, "Switched from temp. frame to frame #%d",
(*f) - &cam->frame[0]) (*f)->number)
} }
} }
...@@ -832,7 +848,7 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -832,7 +848,7 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
if (status && len != 0) { if (status && len != 0) {
DBG(4, "URB failed, error in data packet " DBG(4, "URB failed, error in data packet "
"(error #%d, %s).", "(error #%u, %s)",
status, symbolic(urb_errlist, status)) status, symbolic(urb_errlist, status))
(*f)->status = F_ERROR; (*f)->status = F_ERROR;
continue; continue;
...@@ -846,8 +862,8 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -846,8 +862,8 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
} }
/* Buffer overflows shouldn't happen, however...*/ /* Buffer overflows shouldn't happen, however...*/
if ((*f)->length + len > maxbufsize) { if ((*f)->length + len > (*f)->size) {
DBG(4, "Buffer overflow: bad data packets.") DBG(4, "Buffer overflow: bad data packets")
(*f)->status = F_ERROR; (*f)->status = F_ERROR;
} }
...@@ -858,11 +874,10 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -858,11 +874,10 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
} else if ((*f)->status == F_GRABBING) { /* end of frame */ } else if ((*f)->status == F_GRABBING) { /* end of frame */
DBG(6, "Frame #%zd successfully grabbed.", DBG(6, "Frame #%d successfully grabbed", (*f)->number)
((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0]))
if (cam->vpp_flag & VPP_DECOMPRESSION) { if (cam->vpp_flag & VPP_DECOMPRESSION) {
err=(*w9968cf_vpp_check_headers)((*f)->buffer, err = w9968cf_vpp->check_headers((*f)->buffer,
(*f)->length); (*f)->length);
if (err) { if (err) {
DBG(4, "Skip corrupted frame: %s", DBG(4, "Skip corrupted frame: %s",
...@@ -887,7 +902,7 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -887,7 +902,7 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
} else if ((*f)->status == F_ERROR) } else if ((*f)->status == F_ERROR)
(*f)->status = F_UNUSED; /* grab it again */ (*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) (unsigned long)(*f)->length, i, len, (*f)->status)
} /* end for */ } /* end for */
...@@ -900,7 +915,7 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) ...@@ -900,7 +915,7 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
if ((err = usb_submit_urb(urb, GFP_ATOMIC))) { if ((err = usb_submit_urb(urb, GFP_ATOMIC))) {
cam->misconfigured = 1; cam->misconfigured = 1;
DBG(1, "Couldn't resubmit the URB: error %d, %s", 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); spin_unlock(&cam->urb_lock);
...@@ -931,7 +946,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) ...@@ -931,7 +946,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
if (!urb) { if (!urb) {
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
usb_free_urb(cam->urb[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; return -ENOMEM;
} }
...@@ -976,7 +991,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) ...@@ -976,7 +991,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
if (err || (vidcapt < 0)) { if (err || (vidcapt < 0)) {
for (i = 0; i < W9968CF_URBS; i++) for (i = 0; i < W9968CF_URBS; i++)
usb_free_urb(cam->urb[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; return err;
} }
...@@ -988,26 +1003,29 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) ...@@ -988,26 +1003,29 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
cam->frame_current = &cam->frame_tmp; cam->frame_current = &cam->frame_tmp;
if (!(cam->vpp_flag & VPP_DECOMPRESSION)) 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) (unsigned long)t_size*2)
DBG(5, "Starting the isochronous transfer...") DBG(5, "Starting the isochronous transfer...")
cam->streaming = 1;
/* Submit the URBs */ /* Submit the URBs */
for (i = 0; i < W9968CF_URBS; i++) { for (i = 0; i < W9968CF_URBS; i++) {
err = usb_submit_urb(cam->urb[i], GFP_KERNEL); err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
if (err) { if (err) {
for (j = i-1; j >= 0; j--) cam->streaming = 0;
if (!usb_unlink_urb(cam->urb[j])) for (j = i-1; j >= 0; j--) {
usb_free_urb(cam->urb[j]); usb_kill_urb(cam->urb[j]);
usb_free_urb(cam->urb[j]);
}
DBG(1, "Couldn't send a transfer request to the " 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)) symbolic(urb_errlist, err))
return err;
} }
} }
cam->streaming = 1;
return 0; return 0;
} }
...@@ -1023,6 +1041,9 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam) ...@@ -1023,6 +1041,9 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam)
int err = 0; int err = 0;
s8 i; s8 i;
if (!cam->streaming)
return 0;
/* This avoids race conditions with usb_submit_urb() /* This avoids race conditions with usb_submit_urb()
in the URB completition handler */ in the URB completition handler */
spin_lock_irqsave(&cam->urb_lock, lock_flags); spin_lock_irqsave(&cam->urb_lock, lock_flags);
...@@ -1031,10 +1052,9 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam) ...@@ -1031,10 +1052,9 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam)
for (i = W9968CF_URBS-1; i >= 0; i--) for (i = W9968CF_URBS-1; i >= 0; i--)
if (cam->urb[i]) { if (cam->urb[i]) {
if (!usb_unlink_urb(cam->urb[i])) { usb_kill_urb(cam->urb[i]);
usb_free_urb(cam->urb[i]); usb_free_urb(cam->urb[i]);
cam->urb[i] = NULL; cam->urb[i] = NULL;
}
} }
if (cam->disconnected) if (cam->disconnected)
...@@ -1052,7 +1072,7 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam) ...@@ -1052,7 +1072,7 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam)
} }
exit: exit:
DBG(5, "Isochronous transfer stopped.") DBG(5, "Isochronous transfer stopped")
return 0; return 0;
} }
...@@ -1072,7 +1092,7 @@ static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index) ...@@ -1072,7 +1092,7 @@ static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index)
if (res < 0) if (res < 0)
DBG(4, "Failed to write a register " 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)) value, index, res, symbolic(urb_errlist, res))
return (res >= 0) ? 0 : -1; return (res >= 0) ? 0 : -1;
...@@ -1095,7 +1115,7 @@ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index) ...@@ -1095,7 +1115,7 @@ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
if (res < 0) if (res < 0)
DBG(4, "Failed to read a register " 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)) index, res, symbolic(urb_errlist, res))
return (res >= 0) ? (int)(*buff) : -1; return (res >= 0) ? (int)(*buff) : -1;
...@@ -1120,7 +1140,7 @@ static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) ...@@ -1120,7 +1140,7 @@ static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
if (res < 0) if (res < 0)
DBG(4, "Failed to write the FSB registers " 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; return (res >= 0) ? 0 : -1;
} }
...@@ -1270,7 +1290,7 @@ static int w9968cf_smbus_read_ack(struct w9968cf_device* cam) ...@@ -1270,7 +1290,7 @@ static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
if (sda < 0) if (sda < 0)
err += sda; err += sda;
if (sda == 1) { if (sda == 1) {
DBG(6, "Couldn't receive the ACK.") DBG(6, "Couldn't receive the ACK")
err += -1; err += -1;
} }
...@@ -1353,11 +1373,11 @@ w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, ...@@ -1353,11 +1373,11 @@ w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
if (!err) if (!err)
DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " 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 else
DBG(5, "I2C write byte data failed, addr.0x%04X, " DBG(5, "I2C write byte data failed, addr.0x%04X, "
"subaddr.0x%02X, value 0x%02X.", "subaddr.0x%02X, value 0x%02X",
address, subaddress, value) address, subaddress, value)
return err; return err;
} }
...@@ -1392,11 +1412,11 @@ w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, ...@@ -1392,11 +1412,11 @@ w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam,
if (!err) if (!err)
DBG(5, "I2C read byte data done, addr.0x%04X, " 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) address, subaddress, *value)
else else
DBG(5, "I2C read byte data failed, addr.0x%04X, " 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) address, subaddress, *value)
return err; return err;
...@@ -1424,11 +1444,11 @@ w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, ...@@ -1424,11 +1444,11 @@ w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
err += w9968cf_write_sb(cam, 0x0000); err += w9968cf_write_sb(cam, 0x0000);
if (!err) if (!err)
DBG(5, "I2C read byte done, addr.0x%04X." DBG(5, "I2C read byte done, addr.0x%04X, "
"value 0x%02X.", address, *value) "value 0x%02X", address, *value)
else else
DBG(5, "I2C read byte failed, addr.0x%04X." DBG(5, "I2C read byte failed, addr.0x%04X, "
"wrong value 0x%02X.", address, *value) "wrong value 0x%02X", address, *value)
return err; return err;
} }
...@@ -1439,7 +1459,7 @@ static int ...@@ -1439,7 +1459,7 @@ static int
w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam,
u16 address, u8 value) 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; return -EINVAL;
} }
...@@ -1546,9 +1566,8 @@ static int w9968cf_i2c_detach_inform(struct i2c_client* client) ...@@ -1546,9 +1566,8 @@ static int w9968cf_i2c_detach_inform(struct i2c_client* client)
struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
const char* clientname = i2c_clientname(client); const char* clientname = i2c_clientname(client);
if (cam->sensor_client == client) { if (cam->sensor_client == client)
cam->sensor_client = NULL; cam->sensor_client = NULL;
}
DBG(5, "I2C detach client [%s]", clientname) DBG(5, "I2C detach client [%s]", clientname)
...@@ -1593,9 +1612,9 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) ...@@ -1593,9 +1612,9 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam)
err = i2c_add_adapter(&cam->i2c_adapter); err = i2c_add_adapter(&cam->i2c_adapter);
if (err) if (err)
DBG(1, "Failed to register the I2C adapter.") DBG(1, "Failed to register the I2C adapter")
else else
DBG(5, "I2C adapter registered.") DBG(5, "I2C adapter registered")
return err; return err;
} }
...@@ -1622,9 +1641,9 @@ static int w9968cf_turn_on_led(struct w9968cf_device* cam) ...@@ -1622,9 +1641,9 @@ static int w9968cf_turn_on_led(struct w9968cf_device* cam)
err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */
if (err) 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; return err;
} }
...@@ -1637,6 +1656,13 @@ static int w9968cf_turn_on_led(struct w9968cf_device* cam) ...@@ -1637,6 +1656,13 @@ static int w9968cf_turn_on_led(struct w9968cf_device* cam)
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
static int w9968cf_init_chip(struct w9968cf_device* cam) 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; int err = 0;
err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
...@@ -1645,23 +1671,25 @@ static int w9968cf_init_chip(struct w9968cf_device* cam) ...@@ -1645,23 +1671,25 @@ static int w9968cf_init_chip(struct w9968cf_device* cam)
err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */ err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */
err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM 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, y0 & 0xffff, 0x20); /* Y buf.0, low */
err += w9968cf_write_reg(cam, 0x0000, 0x21); /* Y frame buf.0, high */ err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */
err += w9968cf_write_reg(cam, 0xb000, 0x22); /* Y frame buf.1, low */ err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */
err += w9968cf_write_reg(cam, 0x0004, 0x23); /* Y frame buf.1, high */ err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */
err += w9968cf_write_reg(cam, 0x5800, 0x24); /* U frame buf.0, low */ err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */
err += w9968cf_write_reg(cam, 0x0002, 0x25); /* U frame buf.0, high */ err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V 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, y1 & 0xffff, 0x22); /* Y buf.1, low */
err += w9968cf_write_reg(cam, 0x8400, 0x28); /* V frame buf.0, low */ err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */
err += w9968cf_write_reg(cam, 0x0003, 0x29); /* V frame buf.0, high */ err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */
err += w9968cf_write_reg(cam, 0x3400, 0x2a); /* V frame buf.1, low */ err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */
err += w9968cf_write_reg(cam, 0x0008, 0x2b); /* V frame 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, 0x6000, 0x32); /* JPEG bitstream buf 0 */
err += w9968cf_write_reg(cam, 0x0009, 0x33); /* JPEG bitstream buf 0 */ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */
err += w9968cf_write_reg(cam, 0x2000, 0x34); /* JPEG bitstream buf 1 */ err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */
err += w9968cf_write_reg(cam, 0x000d, 0x35); /* JPEG bitstream buf 1 */
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, 0x0000, 0x36);/* JPEG restart interval */
err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/
...@@ -1672,9 +1700,9 @@ static int w9968cf_init_chip(struct w9968cf_device* cam) ...@@ -1672,9 +1700,9 @@ static int w9968cf_init_chip(struct w9968cf_device* cam)
err += w9968cf_set_window(cam, cam->window); err += w9968cf_set_window(cam, cam->window);
if (err) if (err)
DBG(1, "Chip initialization failed.") DBG(1, "Chip initialization failed")
else else
DBG(5, "Chip successfully initialized.") DBG(5, "Chip successfully initialized")
return err; return err;
} }
...@@ -1736,8 +1764,8 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) ...@@ -1736,8 +1764,8 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
break; break;
} }
/* FIXME: 'hardware double buffer' doesn't work when compressed video /* NOTE: due to memory issues, it is better to disable the hardware
is enabled (corrupted frames). */ double buffering during compression */
if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION))
reg_v |= 0x0080; reg_v |= 0x0080;
...@@ -1761,16 +1789,15 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) ...@@ -1761,16 +1789,15 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
cam->hw_palette = hw_palette; cam->hw_palette = hw_palette;
/* Settings changed, so we clear the frame buffers */ /* Settings changed, so we clear the frame buffers */
memset(cam->frame[0].buffer, 0, memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
cam->nbuffers*w9968cf_get_max_bufsize(cam));
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) symbolic(v4l1_plist, pict.palette), pict.depth)
return 0; return 0;
error: error:
DBG(1, "Failed to change picture settings.") DBG(1, "Failed to change picture settings")
return err; return err;
} }
...@@ -1921,21 +1948,20 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) ...@@ -1921,21 +1948,20 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
cam->hw_height = h; cam->hw_height = h;
/* Settings changed, so we clear the frame buffers */ /* Settings changed, so we clear the frame buffers */
memset(cam->frame[0].buffer, 0, memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
cam->nbuffers*w9968cf_get_max_bufsize(cam));
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) 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, " PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, "
"cw=%d, ch=%d, win.x=%d ,win.y=%d, win.width=%d, win.height=%d", "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, x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y,
win.width, win.height) win.width, win.height)
return 0; return 0;
error: error:
DBG(1, "Failed to change the capture area size.") DBG(1, "Failed to change the capture area size")
return err; return err;
} }
...@@ -1959,7 +1985,7 @@ static inline u16 w9968cf_valid_palette(u16 palette) ...@@ -1959,7 +1985,7 @@ static inline u16 w9968cf_valid_palette(u16 palette)
Return the depth corresponding to the given palette. Return the depth corresponding to the given palette.
Palette _must_ be supported ! Palette _must_ be supported !
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
static u16 w9968cf_valid_depth(u16 palette) static inline u16 w9968cf_valid_depth(u16 palette)
{ {
u8 i=0; u8 i=0;
while (w9968cf_formatlist[i].palette != palette) while (w9968cf_formatlist[i].palette != palette)
...@@ -1996,10 +2022,12 @@ w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) ...@@ -1996,10 +2022,12 @@ w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height)
if ((*width < cam->minwidth) || (*height < cam->minheight)) if ((*width < cam->minwidth) || (*height < cam->minheight))
return -ERANGE; return -ERANGE;
maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
&& w9968cf_vppmod_present ? W9968CF_MAX_WIDTH : cam->maxwidth; w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) : cam->maxwidth;
&& w9968cf_vppmod_present ? W9968CF_MAX_HEIGHT : cam->maxheight; maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
: cam->maxheight;
if (*width > maxw) if (*width > maxw)
*width = maxw; *width = maxw;
...@@ -2011,7 +2039,7 @@ w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) ...@@ -2011,7 +2039,7 @@ w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height)
*height &= ~15L; *height &= ~15L;
} }
PDBGG("Window size adjusted w=%d, h=%d ", *width, *height) PDBGG("Window size adjusted w=%u, h=%u ", *width, *height)
return 0; return 0;
} }
...@@ -2050,7 +2078,7 @@ static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num) ...@@ -2050,7 +2078,7 @@ static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num)
spin_unlock_irqrestore(&cam->flist_lock, lock_flags); 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 @@ w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep) ...@@ -2074,7 +2102,7 @@ w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep)
spin_unlock(&cam->flist_lock); 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 @@ static int ...@@ -2086,7 +2114,7 @@ static int
w9968cf_postprocess_frame(struct w9968cf_device* cam, w9968cf_postprocess_frame(struct w9968cf_device* cam,
struct w9968cf_frame_t* fr) 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, u16 w = cam->window.width,
h = cam->window.height, h = cam->window.height,
d = cam->picture.depth, d = cam->picture.depth,
...@@ -2102,41 +2130,41 @@ w9968cf_postprocess_frame(struct w9968cf_device* cam, ...@@ -2102,41 +2130,41 @@ w9968cf_postprocess_frame(struct w9968cf_device* cam,
if (cam->vpp_flag & VPP_DECOMPRESSION) { if (cam->vpp_flag & VPP_DECOMPRESSION) {
memcpy(pOut, pIn, fr->length); memcpy(pOut, pIn, fr->length);
_PSWAP(pIn, pOut) _PSWAP(pIn, pOut)
err = (*w9968cf_vpp_decode)(pIn, fr->length, hw_w, hw_h, pOut); err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut);
PDBGG("Compressed frame length: %li",(unsigned long)fr->length) PDBGG("Compressed frame length: %lu",(unsigned long)fr->length)
fr->length = (hw_w*hw_h*hw_d)/8; fr->length = (hw_w*hw_h*hw_d)/8;
_PSWAP(pIn, pOut) _PSWAP(pIn, pOut)
if (err) { if (err) {
DBG(4, "An error occurred while decoding the frame: " DBG(4, "An error occurred while decoding the frame: "
"%s.", symbolic(decoder_errlist, err)) "%s", symbolic(decoder_errlist, err))
return err; return err;
} else } else
DBG(6, "Frame decoded") DBG(6, "Frame decoded")
} }
if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) { if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) {
(*w9968cf_vpp_swap_yuvbytes)(pIn, fr->length); w9968cf_vpp->swap_yuvbytes(pIn, fr->length);
DBG(6, "Original UYVY component ordering changed.") DBG(6, "Original UYVY component ordering changed")
} }
if (cam->vpp_flag & VPP_UPSCALE) { 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; fr->length = (w*h*hw_d)/8;
_PSWAP(pIn, pOut) _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) hw_w, hw_h, hw_d, w, h)
} }
if (cam->vpp_flag & VPP_UYVY_TO_RGBX) { 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; fr->length = (w*h*d)/8;
_PSWAP(pIn, pOut) _PSWAP(pIn, pOut)
DBG(6, "UYVY-16bit to %s conversion done.", DBG(6, "UYVY-16bit to %s conversion done",
symbolic(v4l1_plist, fmt)) symbolic(v4l1_plist, fmt))
} }
if (pOut == fr->buffer) if (pOut == fr->buffer)
memcpy(fr->buffer, cam->vpp_buffer, fr->length); memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length);
return 0; return 0;
} }
...@@ -2144,7 +2172,7 @@ w9968cf_postprocess_frame(struct w9968cf_device* cam, ...@@ -2144,7 +2172,7 @@ w9968cf_postprocess_frame(struct w9968cf_device* cam,
/**************************************************************************** /****************************************************************************
* CMOS sensor control routines * * Image sensor control routines *
****************************************************************************/ ****************************************************************************/
static int static int
...@@ -2184,17 +2212,17 @@ w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg) ...@@ -2184,17 +2212,17 @@ w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg)
struct i2c_client* c = cam->sensor_client; struct i2c_client* c = cam->sensor_client;
int rc = 0; int rc = 0;
if (c->driver->command) { if (!c || !c->driver || !c->driver->command)
rc = c->driver->command(cam->sensor_client, cmd, arg); return -EINVAL;
/* The I2C driver returns -EPERM on non-supported controls */
return (rc < 0 && rc != -EPERM) ? rc : 0; rc = c->driver->command(c, cmd, arg);
} else /* The I2C driver returns -EPERM on non-supported controls */
return -ENODEV; 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. Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
static int w9968cf_sensor_update_settings(struct w9968cf_device* cam) static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
...@@ -2242,7 +2270,7 @@ static int w9968cf_sensor_update_settings(struct w9968cf_device* cam) ...@@ -2242,7 +2270,7 @@ static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
/*-------------------------------------------------------------------------- /*--------------------------------------------------------------------------
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. internal 'picture' structure of the camera.
Returns: 0 on success, a negative number otherwise. Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
...@@ -2270,10 +2298,10 @@ static int w9968cf_sensor_get_picture(struct w9968cf_device* cam) ...@@ -2270,10 +2298,10 @@ static int w9968cf_sensor_get_picture(struct w9968cf_device* cam)
return err; return err;
cam->picture.hue = v; 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 " 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) cam->picture.hue, cam->picture.colour, cam->picture.whiteness)
return 0; return 0;
...@@ -2281,7 +2309,7 @@ static int w9968cf_sensor_get_picture(struct w9968cf_device* cam) ...@@ -2281,7 +2309,7 @@ static int w9968cf_sensor_get_picture(struct w9968cf_device* cam)
/*-------------------------------------------------------------------------- /*--------------------------------------------------------------------------
Update picture settings of the CMOS sensor. Update picture settings of the image sensor.
Returns: 0 on success, a negative number otherwise. Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
static int static int
...@@ -2296,7 +2324,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam, ...@@ -2296,7 +2324,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam,
pict.contrast); pict.contrast);
if (err) if (err)
goto fail; 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)
cam->picture.contrast = pict.contrast; cam->picture.contrast = pict.contrast;
} }
...@@ -2307,7 +2335,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam, ...@@ -2307,7 +2335,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam,
pict.brightness); pict.brightness);
if (err) if (err)
goto fail; 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)
cam->picture.brightness = pict.brightness; cam->picture.brightness = pict.brightness;
} }
...@@ -2317,7 +2345,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam, ...@@ -2317,7 +2345,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam,
pict.colour); pict.colour);
if (err) if (err)
goto fail; 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)
cam->picture.colour = pict.colour; cam->picture.colour = pict.colour;
} }
...@@ -2327,7 +2355,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam, ...@@ -2327,7 +2355,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam,
pict.hue); pict.hue);
if (err) if (err)
goto fail; 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)
cam->picture.hue = pict.hue; cam->picture.hue = pict.hue;
} }
...@@ -2335,7 +2363,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam, ...@@ -2335,7 +2363,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam,
return 0; return 0;
fail: fail:
DBG(4, "Failed to change sensor picture setting.") DBG(4, "Failed to change sensor picture setting")
return err; return err;
} }
...@@ -2346,7 +2374,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam, ...@@ -2346,7 +2374,7 @@ w9968cf_sensor_update_picture(struct w9968cf_device* cam,
****************************************************************************/ ****************************************************************************/
/*-------------------------------------------------------------------------- /*--------------------------------------------------------------------------
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. Return 0 if the initialization succeeds, a negative number otherwise.
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
static int w9968cf_sensor_init(struct w9968cf_device* cam) static int w9968cf_sensor_init(struct w9968cf_device* cam)
...@@ -2376,7 +2404,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam) ...@@ -2376,7 +2404,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam)
cam->minheight = 48; cam->minheight = 48;
break; break;
default: default:
DBG(1, "Not supported CMOS sensor detected for %s.", DBG(1, "Not supported image sensor detected for %s",
symbolic(camlist, cam->id)) symbolic(camlist, cam->id))
return -EINVAL; return -EINVAL;
} }
...@@ -2386,7 +2414,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam) ...@@ -2386,7 +2414,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam)
case CC_OV7620: case CC_OV7620:
cam->start_cropx = 287; cam->start_cropx = 287;
cam->start_cropy = 35; 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->vs_polarity = 1;
cam->hs_polarity = 1; cam->hs_polarity = 1;
break; break;
...@@ -2405,14 +2433,14 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam) ...@@ -2405,14 +2433,14 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam)
cam->sensor_initialized = 1; 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; return 0;
error: error:
cam->sensor_initialized = 0; cam->sensor_initialized = 0;
cam->sensor = CC_UNKNOWN; cam->sensor = CC_UNKNOWN;
DBG(1, "CMOS sensor initialization failed for %s (/dev/video%d). " DBG(1, "Image sensor initialization failed for %s (/dev/video%d). "
"Try to detach and attach this device again.", "Try to detach and attach this device again",
symbolic(camlist, cam->id), cam->v4ldev->minor) symbolic(camlist, cam->id), cam->v4ldev->minor)
return err; return err;
} }
...@@ -2436,7 +2464,6 @@ w9968cf_configure_camera(struct w9968cf_device* cam, ...@@ -2436,7 +2464,6 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
cam->users = 0; cam->users = 0;
cam->disconnected = 0; cam->disconnected = 0;
cam->usbdev = udev;
cam->id = mod_id; cam->id = mod_id;
cam->sensor = CC_UNKNOWN; cam->sensor = CC_UNKNOWN;
cam->sensor_initialized = 0; cam->sensor_initialized = 0;
...@@ -2521,6 +2548,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam, ...@@ -2521,6 +2548,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
else else
cam->picture.palette = W9968CF_PALETTE_DECOMP_ON; 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) cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB; ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB;
...@@ -2533,105 +2561,120 @@ w9968cf_configure_camera(struct w9968cf_device* cam, ...@@ -2533,105 +2561,120 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
cam->window.clipcount = 0; cam->window.clipcount = 0;
cam->window.flags = 0; cam->window.flags = 0;
/* If the video post-processing module is not present, some paramaters DBG(3, "%s configured with settings #%u:",
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:",
symbolic(camlist, cam->id), dev_nr) 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]) 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) cam->max_buffers)
if (cam->double_buffer) if (cam->double_buffer)
DBG(3, "- Hardware double buffering enabled.") DBG(3, "- Hardware double buffering enabled")
else else
DBG(3, "- Hardware double buffering disabled.") DBG(3, "- Hardware double buffering disabled")
if (cam->filter_type == 0) if (cam->filter_type == 0)
DBG(3, "- Video filtering disabled.") DBG(3, "- Video filtering disabled")
else if (cam->filter_type == 1) 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) 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) if (cam->clamping)
DBG(3, "- Video data clamping (CCIR-601 format) enabled.") DBG(3, "- Video data clamping (CCIR-601 format) enabled")
else else
DBG(3, "- Video data clamping (CCIR-601 format) disabled.") DBG(3, "- Video data clamping (CCIR-601 format) disabled")
if (cam->largeview) if (cam->largeview)
DBG(3, "- Large view enabled.") DBG(3, "- Large view enabled")
else else
DBG(3, "- Large view disabled.") DBG(3, "- Large view disabled")
if ((cam->decompression) == 0 && (!cam->force_palette)) if ((cam->decompression) == 0 && (!cam->force_palette))
DBG(3, "- Decompression disabled.") DBG(3, "- Decompression disabled")
else if ((cam->decompression) == 1 && (!cam->force_palette)) else if ((cam->decompression) == 1 && (!cam->force_palette))
DBG(3, "- Decompression forced.") DBG(3, "- Decompression forced")
else if ((cam->decompression) == 2 && (!cam->force_palette)) else if ((cam->decompression) == 2 && (!cam->force_palette))
DBG(3, "- Decompression allowed.") DBG(3, "- Decompression allowed")
if (cam->upscaling) if (cam->upscaling)
DBG(3, "- Software image scaling enabled.") DBG(3, "- Software image scaling enabled")
else else
DBG(3, "- Software image scaling disabled.") DBG(3, "- Software image scaling disabled")
if (cam->force_palette) if (cam->force_palette)
DBG(3, "- Image palette forced to %s.", DBG(3, "- Image palette forced to %s",
symbolic(v4l1_plist, cam->picture.palette)) symbolic(v4l1_plist, cam->picture.palette))
if (cam->force_rgb) 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) if (cam->auto_brt)
DBG(3, "- Auto brightness enabled.") DBG(3, "- Auto brightness enabled")
else else
DBG(3, "- Auto brightness disabled.") DBG(3, "- Auto brightness disabled")
if (cam->auto_exp) if (cam->auto_exp)
DBG(3, "- Auto exposure enabled.") DBG(3, "- Auto exposure enabled")
else else
DBG(3, "- Auto exposure disabled.") DBG(3, "- Auto exposure disabled")
if (cam->backlight) if (cam->backlight)
DBG(3, "- Backlight exposure algorithm enabled.") DBG(3, "- Backlight exposure algorithm enabled")
else else
DBG(3, "- Backlight exposure algorithm disabled.") DBG(3, "- Backlight exposure algorithm disabled")
if (cam->mirror) if (cam->mirror)
DBG(3, "- Mirror enabled.") DBG(3, "- Mirror enabled")
else else
DBG(3, "- Mirror disabled.") DBG(3, "- Mirror disabled")
if (cam->bandfilt) if (cam->bandfilt)
DBG(3, "- Banding filter enabled.") DBG(3, "- Banding filter enabled")
else 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) if (cam->clockdiv == -1)
DBG(3, "- Automatic clock divisor enabled.") DBG(3, "- Automatic clock divisor enabled")
else else
DBG(3, "- Clock divisor: %d", cam->clockdiv) DBG(3, "- Clock divisor: %d", cam->clockdiv)
if (cam->monochrome) if (cam->monochrome)
DBG(3, "- CMOS sensor used as monochrome.") DBG(3, "- Image sensor used as monochrome")
else 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 @@ static void w9968cf_release_resources(struct w9968cf_device* cam) ...@@ -2654,8 +2697,6 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
kfree(cam->data_buffer); kfree(cam->data_buffer);
up(&w9968cf_devlist_sem); up(&w9968cf_devlist_sem);
DBG(5, "Resources released.")
} }
...@@ -2669,38 +2710,45 @@ static int w9968cf_open(struct inode* inode, struct file* filp) ...@@ -2669,38 +2710,45 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
struct w9968cf_device* cam; struct w9968cf_device* cam;
int err; 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)); cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
down(&cam->dev_sem); down(&cam->dev_sem);
if (cam->sensor == CC_UNKNOWN) { 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 " "'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) symbolic(camlist, cam->id), cam->v4ldev->minor)
up(&cam->dev_sem); up(&cam->dev_sem);
up_read(&w9968cf_disconnect);
return -ENODEV; return -ENODEV;
} }
if (cam->users) { 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) symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
up(&cam->dev_sem); up(&cam->dev_sem);
up_read(&w9968cf_disconnect);
return -EWOULDBLOCK; return -EWOULDBLOCK;
} }
retry:
up(&cam->dev_sem); up(&cam->dev_sem);
err = wait_event_interruptible(cam->open, cam->disconnected || err = wait_event_interruptible_exclusive(cam->open,
(cam->users == 0)); cam->disconnected ||
if (err) !cam->users);
if (err) {
up_read(&w9968cf_disconnect);
return err; return err;
if (cam->disconnected) }
if (cam->disconnected) {
up_read(&w9968cf_disconnect);
return -ENODEV; return -ENODEV;
}
down(&cam->dev_sem); down(&cam->dev_sem);
/*recheck - there may be several waiters */
if (cam->users)
goto retry;
} }
DBG(5, "Opening '%s', /dev/video%d ...", DBG(5, "Opening '%s', /dev/video%d ...",
...@@ -2709,8 +2757,9 @@ static int w9968cf_open(struct inode* inode, struct file* filp) ...@@ -2709,8 +2757,9 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
cam->streaming = 0; cam->streaming = 0;
cam->misconfigured = 0; cam->misconfigured = 0;
if (!w9968cf_vppmod_present) if (!w9968cf_vpp)
w9968cf_vppmod_detect(); if ((err = w9968cf_vppmod_detect(cam)))
goto out;
if ((err = w9968cf_allocate_memory(cam))) if ((err = w9968cf_allocate_memory(cam)))
goto deallocate_memory; goto deallocate_memory;
...@@ -2728,15 +2777,19 @@ static int w9968cf_open(struct inode* inode, struct file* filp) ...@@ -2728,15 +2777,19 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
init_waitqueue_head(&cam->wait_queue); init_waitqueue_head(&cam->wait_queue);
DBG(5, "Video device is open")
up(&cam->dev_sem); up(&cam->dev_sem);
up_read(&w9968cf_disconnect);
DBG(5, "Video device is open.")
return 0; return 0;
deallocate_memory: deallocate_memory:
w9968cf_deallocate_memory(cam); 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(&cam->dev_sem);
up_read(&w9968cf_disconnect);
return err; return err;
} }
...@@ -2751,6 +2804,8 @@ static int w9968cf_release(struct inode* inode, struct file* filp) ...@@ -2751,6 +2804,8 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
w9968cf_stop_transfer(cam); w9968cf_stop_transfer(cam);
w9968cf_vppmod_release(cam);
if (cam->disconnected) { if (cam->disconnected) {
w9968cf_release_resources(cam); w9968cf_release_resources(cam);
up(&cam->dev_sem); up(&cam->dev_sem);
...@@ -2760,10 +2815,9 @@ static int w9968cf_release(struct inode* inode, struct file* filp) ...@@ -2760,10 +2815,9 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
cam->users--; cam->users--;
w9968cf_deallocate_memory(cam); 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); up(&cam->dev_sem);
return 0; return 0;
} }
...@@ -2785,7 +2839,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -2785,7 +2839,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
return -ERESTARTSYS; return -ERESTARTSYS;
if (cam->disconnected) { if (cam->disconnected) {
DBG(2, "Device not present.") DBG(2, "Device not present")
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENODEV; return -ENODEV;
} }
...@@ -2817,7 +2871,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -2817,7 +2871,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1]; 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); w9968cf_postprocess_frame(cam, fr);
if (count > fr->length) if (count > fr->length)
...@@ -2832,7 +2886,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -2832,7 +2886,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
fr->status = F_UNUSED; fr->status = F_UNUSED;
DBG(5, "%zd bytes read.", count) DBG(5, "%zu bytes read", count)
up(&cam->fileop_sem); up(&cam->fileop_sem);
return count; return count;
...@@ -2844,25 +2898,25 @@ static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -2844,25 +2898,25 @@ static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
struct w9968cf_device* cam = (struct w9968cf_device*) struct w9968cf_device* cam = (struct w9968cf_device*)
video_get_drvdata(video_devdata(filp)); video_get_drvdata(video_devdata(filp));
unsigned long vsize = vma->vm_end - vma->vm_start, 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, start = vma->vm_start,
pos = (unsigned long)cam->frame[0].buffer, pos = (unsigned long)cam->frame[0].buffer,
page; page;
if (cam->disconnected) { if (cam->disconnected) {
DBG(2, "Device not present.") DBG(2, "Device not present")
return -ENODEV; return -ENODEV;
} }
if (cam->misconfigured) { 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; return -EIO;
} }
PDBGG("mmapping %li bytes...", vsize) PDBGG("mmapping %lu bytes...", vsize)
if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
return -EAGAIN; return -EINVAL;
while (vsize > 0) { while (vsize > 0) {
page = kvirt_to_pa(pos) + vma->vm_pgoff; page = kvirt_to_pa(pos) + vma->vm_pgoff;
...@@ -2871,10 +2925,10 @@ static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -2871,10 +2925,10 @@ static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
return -EAGAIN; return -EAGAIN;
start += PAGE_SIZE; start += PAGE_SIZE;
pos += 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; return 0;
} }
...@@ -2892,7 +2946,7 @@ w9968cf_ioctl(struct inode* inode, struct file* filp, ...@@ -2892,7 +2946,7 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
return -ERESTARTSYS; return -ERESTARTSYS;
if (cam->disconnected) { if (cam->disconnected) {
DBG(2, "Device not present.") DBG(2, "Device not present")
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENODEV; return -ENODEV;
} }
...@@ -2903,19 +2957,17 @@ w9968cf_ioctl(struct inode* inode, struct file* filp, ...@@ -2903,19 +2957,17 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
return -EIO; 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); up(&cam->fileop_sem);
return err; return err;
} }
static int static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg)
unsigned int cmd, void* arg)
{ {
struct w9968cf_device* cam; struct w9968cf_device* cam;
void __user *user_arg = (void __user *)arg;
const char* v4l1_ioctls[] = { const char* v4l1_ioctls[] = {
"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER",
"GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
...@@ -2944,22 +2996,24 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -2944,22 +2996,24 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
}; };
sprintf(cap.name, "W996[87]CF USB Camera #%d", sprintf(cap.name, "W996[87]CF USB Camera #%d",
cam->v4ldev->minor); cam->v4ldev->minor);
cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present) cap.maxwidth = (cam->upscaling && w9968cf_vpp)
? W9968CF_MAX_WIDTH : cam->maxwidth; ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
cap.maxheight = (cam->upscaling && w9968cf_vppmod_present) : cam->maxwidth;
? W9968CF_MAX_HEIGHT : cam->maxheight; cap.maxheight = (cam->upscaling && w9968cf_vpp)
? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
if (copy_to_user(user_arg, &cap, sizeof(cap))) : cam->maxheight;
if (copy_to_user(arg, &cap, sizeof(cap)))
return -EFAULT; return -EFAULT;
DBG(5, "VIDIOCGCAP successfully called.") DBG(5, "VIDIOCGCAP successfully called")
return 0; return 0;
} }
case VIDIOCGCHAN: /* get video channel informations */ case VIDIOCGCHAN: /* get video channel informations */
{ {
struct video_channel chan; struct video_channel chan;
if (copy_from_user(&chan, user_arg, sizeof(chan))) if (copy_from_user(&chan, arg, sizeof(chan)))
return -EFAULT; return -EFAULT;
if (chan.channel != 0) if (chan.channel != 0)
...@@ -2971,10 +3025,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -2971,10 +3025,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
chan.type = VIDEO_TYPE_CAMERA; chan.type = VIDEO_TYPE_CAMERA;
chan.norm = VIDEO_MODE_AUTO; chan.norm = VIDEO_MODE_AUTO;
if (copy_to_user(user_arg, &chan, sizeof(chan))) if (copy_to_user(arg, &chan, sizeof(chan)))
return -EFAULT; return -EFAULT;
DBG(5, "VIDIOCGCHAN successfully called.") DBG(5, "VIDIOCGCHAN successfully called")
return 0; return 0;
} }
...@@ -2982,13 +3036,13 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -2982,13 +3036,13 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
{ {
struct video_channel chan; struct video_channel chan;
if (copy_from_user(&chan, user_arg, sizeof(chan))) if (copy_from_user(&chan, arg, sizeof(chan)))
return -EFAULT; return -EFAULT;
if (chan.channel != 0) if (chan.channel != 0)
return -EINVAL; return -EINVAL;
DBG(5, "VIDIOCSCHAN successfully called.") DBG(5, "VIDIOCSCHAN successfully called")
return 0; return 0;
} }
...@@ -2997,10 +3051,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -2997,10 +3051,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if (w9968cf_sensor_get_picture(cam)) if (w9968cf_sensor_get_picture(cam))
return -EIO; 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; return -EFAULT;
DBG(5, "VIDIOCGPICT successfully called.") DBG(5, "VIDIOCGPICT successfully called")
return 0; return 0;
} }
...@@ -3009,19 +3063,19 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3009,19 +3063,19 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
struct video_picture pict; struct video_picture pict;
int err = 0; int err = 0;
if (copy_from_user(&pict, user_arg, sizeof(pict))) if (copy_from_user(&pict, arg, sizeof(pict)))
return -EFAULT; return -EFAULT;
if ( (cam->force_palette || !w9968cf_vppmod_present) if ( (cam->force_palette || !w9968cf_vpp)
&& pict.palette != cam->picture.palette ) { && 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, pict.palette),
symbolic(v4l1_plist, cam->picture.palette)) symbolic(v4l1_plist, cam->picture.palette))
return -EINVAL; return -EINVAL;
} }
if (!w9968cf_valid_palette(pict.palette)) { 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)) symbolic(v4l1_plist, pict.palette))
return -EINVAL; return -EINVAL;
} }
...@@ -3030,14 +3084,14 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3030,14 +3084,14 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if (cam->decompression == 0) { if (cam->decompression == 0) {
if (w9968cf_need_decompression(pict.palette)) { if (w9968cf_need_decompression(pict.palette)) {
DBG(4, "Decompression disabled: palette %s is not " DBG(4, "Decompression disabled: palette %s is not "
"allowed. VIDIOCSPICT failed.", "allowed. VIDIOCSPICT failed",
symbolic(v4l1_plist, pict.palette)) symbolic(v4l1_plist, pict.palette))
return -EINVAL; return -EINVAL;
} }
} else if (cam->decompression == 1) { } else if (cam->decompression == 1) {
if (!w9968cf_need_decompression(pict.palette)) { if (!w9968cf_need_decompression(pict.palette)) {
DBG(4, "Decompression forced: palette %s is not " DBG(4, "Decompression forced: palette %s is not "
"allowed. VIDIOCSPICT failed.", "allowed. VIDIOCSPICT failed",
symbolic(v4l1_plist, pict.palette)) symbolic(v4l1_plist, pict.palette))
return -EINVAL; return -EINVAL;
} }
...@@ -3045,8 +3099,8 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3045,8 +3099,8 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
} }
if (pict.depth != w9968cf_valid_depth(pict.palette)) { if (pict.depth != w9968cf_valid_depth(pict.palette)) {
DBG(4, "Requested depth %d bpp is not valid for %s " DBG(4, "Requested depth %u bpp is not valid for %s "
"palette: ignored and changed to %d bpp.", "palette: ignored and changed to %u bpp",
pict.depth, symbolic(v4l1_plist, pict.palette), pict.depth, symbolic(v4l1_plist, pict.palette),
w9968cf_valid_depth(pict.palette)) w9968cf_valid_depth(pict.palette))
pict.depth = w9968cf_valid_depth(pict.palette); pict.depth = w9968cf_valid_depth(pict.palette);
...@@ -3079,7 +3133,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3079,7 +3133,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
return -EIO; return -EIO;
DBG(5, "VIDIOCSPICT successfully called.") DBG(5, "VIDIOCSPICT successfully called")
return 0; return 0;
} }
...@@ -3088,11 +3142,11 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3088,11 +3142,11 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
struct video_window win; struct video_window win;
int err = 0; int err = 0;
if (copy_from_user(&win, user_arg, sizeof(win))) if (copy_from_user(&win, arg, sizeof(win)))
return -EFAULT; return -EFAULT;
DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%d, " DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, "
"x=%d, y=%d, %dx%d", win.clipcount, win.flags, "x=%u, y=%u, %ux%u", win.clipcount, win.flags,
win.x, win.y, win.width, win.height) win.x, win.y, win.width, win.height)
if (win.clipcount != 0 || win.flags != 0) if (win.clipcount != 0 || win.flags != 0)
...@@ -3100,8 +3154,8 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3100,8 +3154,8 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
(u16*)&win.height))) { (u16*)&win.height))) {
DBG(4, "Resolution not supported (%dx%d)." DBG(4, "Resolution not supported (%ux%u). "
"VIDIOCSWIN failed.", win.width, win.height) "VIDIOCSWIN failed", win.width, win.height)
return err; return err;
} }
...@@ -3142,10 +3196,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3142,10 +3196,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
case VIDIOCGWIN: /* get current window properties */ 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; return -EFAULT;
DBG(5, "VIDIOCGWIN successfully called.") DBG(5, "VIDIOCGWIN successfully called")
return 0; return 0;
} }
...@@ -3154,16 +3208,16 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3154,16 +3208,16 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
struct video_mbuf mbuf; struct video_mbuf mbuf;
u8 i; u8 i;
mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam); mbuf.size = cam->nbuffers * cam->frame[0].size;
mbuf.frames = cam->nbuffers; mbuf.frames = cam->nbuffers;
for (i = 0; i < cam->nbuffers; i++) for (i = 0; i < cam->nbuffers; i++)
mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer - mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer -
(unsigned long)cam->frame[0].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; return -EFAULT;
DBG(5, "VIDIOCGMBUF successfully called.") DBG(5, "VIDIOCGMBUF successfully called")
return 0; return 0;
} }
...@@ -3173,22 +3227,22 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3173,22 +3227,22 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
struct w9968cf_frame_t* fr; struct w9968cf_frame_t* fr;
int err = 0; int err = 0;
if (copy_from_user(&mmap, user_arg, sizeof(mmap))) if (copy_from_user(&mmap, arg, sizeof(mmap)))
return -EFAULT; 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.frame, symbolic(v4l1_plist, mmap.format),
mmap.width, mmap.height) mmap.width, mmap.height)
if (mmap.frame >= cam->nbuffers) { if (mmap.frame >= cam->nbuffers) {
DBG(4, "Invalid frame number (%d). " DBG(4, "Invalid frame number (%u). "
"VIDIOCMCAPTURE failed.", mmap.frame) "VIDIOCMCAPTURE failed", mmap.frame)
return -EINVAL; return -EINVAL;
} }
if (mmap.format!=cam->picture.palette && if (mmap.format!=cam->picture.palette &&
(cam->force_palette || !w9968cf_vppmod_present)) { (cam->force_palette || !w9968cf_vpp)) {
DBG(4, "Palette %s rejected. Only %s is allowed.", DBG(4, "Palette %s rejected: only %s is allowed",
symbolic(v4l1_plist, mmap.format), symbolic(v4l1_plist, mmap.format),
symbolic(v4l1_plist, cam->picture.palette)) symbolic(v4l1_plist, cam->picture.palette))
return -EINVAL; return -EINVAL;
...@@ -3196,7 +3250,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3196,7 +3250,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if (!w9968cf_valid_palette(mmap.format)) { if (!w9968cf_valid_palette(mmap.format)) {
DBG(4, "Palette %s not supported. " DBG(4, "Palette %s not supported. "
"VIDIOCMCAPTURE failed.", "VIDIOCMCAPTURE failed",
symbolic(v4l1_plist, mmap.format)) symbolic(v4l1_plist, mmap.format))
return -EINVAL; return -EINVAL;
} }
...@@ -3205,14 +3259,14 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3205,14 +3259,14 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if (cam->decompression == 0) { if (cam->decompression == 0) {
if (w9968cf_need_decompression(mmap.format)) { if (w9968cf_need_decompression(mmap.format)) {
DBG(4, "Decompression disabled: palette %s is not " DBG(4, "Decompression disabled: palette %s is not "
"allowed. VIDIOCSPICT failed.", "allowed. VIDIOCSPICT failed",
symbolic(v4l1_plist, mmap.format)) symbolic(v4l1_plist, mmap.format))
return -EINVAL; return -EINVAL;
} }
} else if (cam->decompression == 1) { } else if (cam->decompression == 1) {
if (!w9968cf_need_decompression(mmap.format)) { if (!w9968cf_need_decompression(mmap.format)) {
DBG(4, "Decompression forced: palette %s is not " DBG(4, "Decompression forced: palette %s is not "
"allowed. VIDIOCSPICT failed.", "allowed. VIDIOCSPICT failed",
symbolic(v4l1_plist, mmap.format)) symbolic(v4l1_plist, mmap.format))
return -EINVAL; return -EINVAL;
} }
...@@ -3222,7 +3276,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3222,7 +3276,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
(u16*)&mmap.height))) { (u16*)&mmap.height))) {
DBG(4, "Resolution not supported (%dx%d). " DBG(4, "Resolution not supported (%dx%d). "
"VIDIOCMCAPTURE failed.", "VIDIOCMCAPTURE failed",
mmap.width, mmap.height) mmap.width, mmap.height)
return err; return err;
} }
...@@ -3239,7 +3293,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3239,7 +3293,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if(*cam->requested_frame if(*cam->requested_frame
|| cam->frame_current->queued) { || cam->frame_current->queued) {
DBG(6, "VIDIOCMCAPTURE. Change settings for " 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, mmap.frame, mmap.width, mmap.height,
symbolic(v4l1_plist, mmap.format)) symbolic(v4l1_plist, mmap.format))
err = wait_event_interruptible err = wait_event_interruptible
...@@ -3274,7 +3328,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3274,7 +3328,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
} else if (fr->queued) { } 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, err = wait_event_interruptible(cam->wait_queue,
cam->disconnected || cam->disconnected ||
...@@ -3286,7 +3340,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3286,7 +3340,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
} }
w9968cf_push_frame(cam, mmap.frame); w9968cf_push_frame(cam, mmap.frame);
DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame) DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame)
return 0; return 0;
} }
...@@ -3296,23 +3350,23 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3296,23 +3350,23 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
struct w9968cf_frame_t* fr; struct w9968cf_frame_t* fr;
int err = 0; 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; return -EFAULT;
if (f_num >= cam->nbuffers) { if (f_num >= cam->nbuffers) {
DBG(4, "Invalid frame number (%d). " DBG(4, "Invalid frame number (%u). "
"VIDIOCMCAPTURE failed.", f_num) "VIDIOCMCAPTURE failed", f_num)
return -EINVAL; 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]; fr = &cam->frame[f_num];
switch (fr->status) { switch (fr->status) {
case F_UNUSED: case F_UNUSED:
if (!fr->queued) { if (!fr->queued) {
DBG(4, "VIDIOSYNC: Frame #%d not requested!", DBG(4, "VIDIOSYNC: Frame #%u not requested!",
f_num) f_num)
return -EFAULT; return -EFAULT;
} }
...@@ -3330,12 +3384,12 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3330,12 +3384,12 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
break; break;
} }
if (w9968cf_vppmod_present) if (w9968cf_vpp)
w9968cf_postprocess_frame(cam, fr); w9968cf_postprocess_frame(cam, fr);
fr->status = F_UNUSED; fr->status = F_UNUSED;
DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num) DBG(5, "VIDIOCSYNC(%u) successfully called", f_num)
return 0; return 0;
} }
...@@ -3349,10 +3403,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3349,10 +3403,10 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
.teletext = VIDEO_NO_UNIT, .teletext = VIDEO_NO_UNIT,
}; };
if (copy_to_user(user_arg, &unit, sizeof(unit))) if (copy_to_user(arg, &unit, sizeof(unit)))
return -EFAULT; return -EFAULT;
DBG(5, "VIDIOCGUNIT successfully called.") DBG(5, "VIDIOCGUNIT successfully called")
return 0; return 0;
} }
...@@ -3361,17 +3415,17 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3361,17 +3415,17 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
case VIDIOCGFBUF: case VIDIOCGFBUF:
{ {
if (clear_user(user_arg, sizeof(struct video_buffer))) if (clear_user(arg, sizeof(struct video_buffer)))
return -EFAULT; return -EFAULT;
DBG(5, "VIDIOCGFBUF successfully called.") DBG(5, "VIDIOCGFBUF successfully called")
return 0; return 0;
} }
case VIDIOCGTUNER: case VIDIOCGTUNER:
{ {
struct video_tuner tuner; struct video_tuner tuner;
if (copy_from_user(&tuner, user_arg, sizeof(tuner))) if (copy_from_user(&tuner, arg, sizeof(tuner)))
return -EFAULT; return -EFAULT;
if (tuner.tuner != 0) if (tuner.tuner != 0)
...@@ -3384,17 +3438,17 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3384,17 +3438,17 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
tuner.mode = VIDEO_MODE_AUTO; tuner.mode = VIDEO_MODE_AUTO;
tuner.signal = 0xffff; tuner.signal = 0xffff;
if (copy_to_user(user_arg, &tuner, sizeof(tuner))) if (copy_to_user(arg, &tuner, sizeof(tuner)))
return -EFAULT; return -EFAULT;
DBG(5, "VIDIOCGTUNER successfully called.") DBG(5, "VIDIOCGTUNER successfully called")
return 0; return 0;
} }
case VIDIOCSTUNER: case VIDIOCSTUNER:
{ {
struct video_tuner tuner; struct video_tuner tuner;
if (copy_from_user(&tuner, user_arg, sizeof(tuner))) if (copy_from_user(&tuner, arg, sizeof(tuner)))
return -EFAULT; return -EFAULT;
if (tuner.tuner != 0) if (tuner.tuner != 0)
...@@ -3403,7 +3457,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3403,7 +3457,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
if (tuner.mode != VIDEO_MODE_AUTO) if (tuner.mode != VIDEO_MODE_AUTO)
return -EINVAL; return -EINVAL;
DBG(5, "VIDIOCSTUNER successfully called.") DBG(5, "VIDIOCSTUNER successfully called")
return 0; return 0;
} }
...@@ -3423,7 +3477,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3423,7 +3477,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
"(type 0x%01X, " "(type 0x%01X, "
"n. 0x%01X, " "n. 0x%01X, "
"dir. 0x%01X, " "dir. 0x%01X, "
"size 0x%02X).", "size 0x%02X)",
V4L1_IOCTL(cmd), V4L1_IOCTL(cmd),
_IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
...@@ -3434,7 +3488,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, ...@@ -3434,7 +3488,7 @@ w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
"type 0x%01X, " "type 0x%01X, "
"n. 0x%01X, " "n. 0x%01X, "
"dir. 0x%01X, " "dir. 0x%01X, "
"size 0x%02X.", "size 0x%02X",
V4L1_IOCTL(cmd), V4L1_IOCTL(cmd),
_IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
...@@ -3480,15 +3534,27 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -3480,15 +3534,27 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
if (udev->descriptor.idVendor == winbond_id_table[0].idVendor && if (udev->descriptor.idVendor == winbond_id_table[0].idVendor &&
udev->descriptor.idProduct == winbond_id_table[0].idProduct) udev->descriptor.idProduct == winbond_id_table[0].idProduct)
mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */ mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */
else if (udev->descriptor.idVendor == winbond_id_table[1].idVendor && else if (udev->descriptor.idVendor == winbond_id_table[1].idVendor &&
udev->descriptor.idProduct == winbond_id_table[1].idProduct) udev->descriptor.idProduct == winbond_id_table[1].idProduct)
mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */ mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */
else else
return -ENODEV; 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) if (simcams > W9968CF_MAX_DEVICES)
simcams = W9968CF_SIMCAMS; simcams = W9968CF_SIMCAMS;
...@@ -3501,27 +3567,15 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -3501,27 +3567,15 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
if (sc >= simcams) { if (sc >= simcams) {
DBG(2, "Device rejected: too many connected cameras " DBG(2, "Device rejected: too many connected cameras "
"(max. %d)", simcams) "(max. %u)", simcams)
return -EPERM; err = -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;
goto fail; 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 */ /* Allocate 2 bytes of memory for camera control USB transfers */
if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) { 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; err = -ENOMEM;
goto fail; goto fail;
} }
...@@ -3530,7 +3584,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -3530,7 +3584,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
/* Allocate 8 bytes of memory for USB data transfers to the FSB */ /* Allocate 8 bytes of memory for USB data transfers to the FSB */
if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) { if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) {
DBG(1, "Couldn't allocate memory for data " DBG(1, "Couldn't allocate memory for data "
"transfers to the FSB.") "transfers to the FSB")
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
...@@ -3539,7 +3593,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -3539,7 +3593,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
/* Register the V4L device */ /* Register the V4L device */
cam->v4ldev = video_device_alloc(); cam->v4ldev = video_device_alloc();
if (!cam->v4ldev) { 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; err = -ENOMEM;
goto fail; goto fail;
} }
...@@ -3552,19 +3606,20 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -3552,19 +3606,20 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release; cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam); video_set_drvdata(cam->v4ldev, cam);
cam->v4ldev->dev = &cam->dev;
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]); video_nr[dev_nr]);
if (err) { if (err) {
DBG(1, "V4L device registration failed.") DBG(1, "V4L device registration failed")
if (err == -ENFILE && video_nr[dev_nr] == -1) 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; video_nr[dev_nr] = -1;
dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
goto fail; 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 */ /* Set some basic constants */
w9968cf_configure_camera(cam, udev, mod_id, dev_nr); w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
...@@ -3579,22 +3634,19 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -3579,22 +3634,19 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
w9968cf_i2c_init(cam); w9968cf_i2c_init(cam);
up(&cam->dev_sem);
usb_set_intfdata(intf, cam); usb_set_intfdata(intf, cam);
up(&cam->dev_sem);
return 0; return 0;
fail: /* Free unused memory */ fail: /* Free unused memory */
if (cam) { if (cam->control_buffer)
if (cam->control_buffer) kfree(cam->control_buffer);
kfree(cam->control_buffer); if (cam->data_buffer)
if (cam->data_buffer) kfree(cam->data_buffer);
kfree(cam->data_buffer); if (cam->v4ldev)
if (cam->v4ldev) video_device_release(cam->v4ldev);
video_device_release(cam->v4ldev); up(&cam->dev_sem);
up(&cam->dev_sem); kfree(cam);
kfree(cam);
}
return err; return err;
} }
...@@ -3604,28 +3656,26 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) ...@@ -3604,28 +3656,26 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
struct w9968cf_device* cam = struct w9968cf_device* cam =
(struct w9968cf_device*)usb_get_intfdata(intf); (struct w9968cf_device*)usb_get_intfdata(intf);
down_write(&w9968cf_disconnect);
if (cam) { if (cam) {
/* Prevent concurrent accesses to data */ /* Prevent concurrent accesses to data */
down(&cam->dev_sem); down(&cam->dev_sem);
cam->streaming = 0;
cam->disconnected = 1; cam->disconnected = 1;
DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)) DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
if (waitqueue_active(&cam->open)) wake_up_interruptible_all(&cam->open);
wake_up_interruptible(&cam->open);
if (cam->users) { if (cam->users) {
DBG(2, "The device is open (/dev/video%d)! " DBG(2, "The device is open (/dev/video%d)! "
"Process name: %s. Deregistration and memory " "Process name: %s. Deregistration and memory "
"deallocation are deferred on close.", "deallocation are deferred on close.",
cam->v4ldev->minor, cam->command) cam->v4ldev->minor, cam->command)
cam->misconfigured = 1; cam->misconfigured = 1;
w9968cf_stop_transfer(cam);
if (waitqueue_active(&cam->wait_queue)) wake_up_interruptible(&cam->wait_queue);
wake_up_interruptible(&cam->wait_queue);
} else } else
w9968cf_release_resources(cam); w9968cf_release_resources(cam);
...@@ -3635,7 +3685,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) ...@@ -3635,7 +3685,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
kfree(cam); kfree(cam);
} }
usb_set_intfdata(intf, NULL); up_write(&w9968cf_disconnect);
} }
...@@ -3653,48 +3703,103 @@ static struct usb_driver w9968cf_usb_driver = { ...@@ -3653,48 +3703,103 @@ static struct usb_driver w9968cf_usb_driver = {
* Module init, exit and intermodule communication * * 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)
if (!w9968cf_vpp_init_decoder) {
if (vppmod_load) if (vppmod_load)
w9968cf_vpp_init_decoder = inter_module_get_request request_module("w9968cf-vpp");
( "w9968cf_init_decoder",
"w9968cf-vpp" ); down(&w9968cf_vppmod_lock);
if (!w9968cf_vpp_init_decoder) {
w9968cf_vppmod_present = 0; if (!w9968cf_vpp) {
DBG(4, "Video post-processing module not detected.") DBG(4, "Video post-processing module not detected")
return -ENODEV; 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->busy++;
w9968cf_vpp_decode = inter_module_get("w9968cf_decode");
w9968cf_vpp_swap_yuvbytes = inter_module_get("w9968cf_swap_yuvbytes"); DBG(5, "Video post-processing module detected")
w9968cf_vpp_uyvy_to_rgbx = inter_module_get("w9968cf_uyvy_to_rgbx");
w9968cf_vpp_scale_up = inter_module_get("w9968cf_scale_up"); 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 = vpp;
(*w9968cf_vpp_init_decoder)(); w9968cf_vpp->busy = 0;
DBG(2, "Video post-processing module detected.") KDBG(2, "Video post-processing module registered")
up(&w9968cf_vppmod_lock);
return 0; return 0;
} }
static void w9968cf_vppmod_release(void) int w9968cf_vppmod_deregister(struct w9968cf_vpp_t* vpp)
{ {
inter_module_put("w9968cf_init_decoder"); down(&w9968cf_vppmod_lock);
inter_module_put("w9968cf_check_headers");
inter_module_put("w9968cf_decode"); if (!w9968cf_vpp) {
inter_module_put("w9968cf_swap_yuvbytes"); up(&w9968cf_vppmod_lock);
inter_module_put("w9968cf_uyvy_to_rgbx"); return -EINVAL;
inter_module_put("w9968cf_scale_up"); }
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 @@ static int __init w9968cf_module_init(void) ...@@ -3702,18 +3807,14 @@ static int __init w9968cf_module_init(void)
{ {
int err; int err;
DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
DBG(3, W9968CF_MODULE_AUTHOR) KDBG(3, W9968CF_MODULE_AUTHOR)
init_MUTEX(&w9968cf_devlist_sem);
w9968cf_vppmod_detect(); if (ovmod_load)
request_module("ovcamchip");
if ((err = usb_register(&w9968cf_usb_driver))) { if ((err = usb_register(&w9968cf_usb_driver)))
if (w9968cf_vppmod_present)
w9968cf_vppmod_release();
return err; return err;
}
return 0; return 0;
} }
...@@ -3724,12 +3825,13 @@ static void __exit w9968cf_module_exit(void) ...@@ -3724,12 +3825,13 @@ static void __exit w9968cf_module_exit(void)
/* w9968cf_usb_disconnect() will be called */ /* w9968cf_usb_disconnect() will be called */
usb_deregister(&w9968cf_usb_driver); usb_deregister(&w9968cf_usb_driver);
if (w9968cf_vppmod_present) KDBG(2, W9968CF_MODULE_NAME" deregistered")
w9968cf_vppmod_release();
DBG(2, W9968CF_MODULE_NAME" deregistered.")
} }
module_init(w9968cf_module_init); module_init(w9968cf_module_init);
module_exit(w9968cf_module_exit); module_exit(w9968cf_module_exit);
EXPORT_SYMBOL(w9968cf_vppmod_register);
EXPORT_SYMBOL(w9968cf_vppmod_deregister);
...@@ -24,21 +24,26 @@ ...@@ -24,21 +24,26 @@
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/types.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/types.h> #include <asm/rwsem.h>
#include "w9968cf_externaldef.h" #include <media/ovcamchip.h>
#include "w9968cf_vpp.h"
/**************************************************************************** /****************************************************************************
* Default values * * Default values *
****************************************************************************/ ****************************************************************************/
#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */
#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ #define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */
/* Comment/uncomment the following line to enable/disable debugging messages */ /* Comment/uncomment the following line to enable/disable debugging messages */
...@@ -95,8 +100,8 @@ static const struct w9968cf_format w9968cf_formatlist[] = { ...@@ -95,8 +100,8 @@ static const struct w9968cf_format w9968cf_formatlist[] = {
#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */ #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_WIDTH 800 /* Has effect if up-scaling is on */
#define W9968CF_MAX_HEIGHT 600 /* should be >= 480 */ #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_WIDTH 320 /* from 128 to 352, multiple of 16 */
#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */ #define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */
...@@ -130,13 +135,11 @@ static const struct w9968cf_format w9968cf_formatlist[] = { ...@@ -130,13 +135,11 @@ static const struct w9968cf_format w9968cf_formatlist[] = {
#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ #define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \
"Dual Mode Camera Chip" "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_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia"
#define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define W9968CF_MODULE_LICENSE "GPL" #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[] = { static const struct usb_device_id winbond_id_table[] = {
{ {
/* Creative Labs Video Blaster WebCam Go Plus */ /* Creative Labs Video Blaster WebCam Go Plus */
...@@ -151,18 +154,19 @@ static const struct usb_device_id winbond_id_table[] = { ...@@ -151,18 +154,19 @@ static const struct usb_device_id winbond_id_table[] = {
{ } /* terminating entry */ { } /* terminating entry */
}; };
MODULE_DEVICE_TABLE(usb, winbond_id_table);
/* W996[87]CF camera models, internal ids: */ /* W996[87]CF camera models, internal ids: */
enum w9968cf_model_id { enum w9968cf_model_id {
W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */ W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */
W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/ W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/
W9968CF_MOD_ADPA5R = 21, /* Aroma Digi Pen ADG-5000 Refurbished */ W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */
W9986CF_MOD_AU = 31, /* AVerTV USB */ W9986CF_MOD_AAU = 31, /* AVerMedia AVerTV USB */
W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */ W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */
W9968CF_MOD_DLLDK = 37, /* Die Lebon LDC-D35A Digital Kamera */ W9968CF_MOD_LL = 37, /* Lebon LDC-035A */
W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */ 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_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 { enum w9968cf_frame_status {
...@@ -173,9 +177,10 @@ enum w9968cf_frame_status { ...@@ -173,9 +177,10 @@ enum w9968cf_frame_status {
}; };
struct w9968cf_frame_t { struct w9968cf_frame_t {
#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf.size of original frames */
void* buffer; void* buffer;
unsigned long size;
u32 length; u32 length;
int number;
enum w9968cf_frame_status status; enum w9968cf_frame_status status;
struct w9968cf_frame_t* next; struct w9968cf_frame_t* next;
u8 queued; u8 queued;
...@@ -189,12 +194,19 @@ enum w9968cf_vpp_flag { ...@@ -189,12 +194,19 @@ enum w9968cf_vpp_flag {
VPP_UYVY_TO_RGBX = 0x08, VPP_UYVY_TO_RGBX = 0x08,
}; };
static struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */ static struct w9968cf_vpp_t* w9968cf_vpp;
static LIST_HEAD(w9968cf_dev_list); static DECLARE_MUTEX(w9968cf_vppmod_lock);
struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */ 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 */ /* Main device driver structure */
struct w9968cf_device { struct w9968cf_device {
struct device dev; /* device structure */
enum w9968cf_model_id id; /* private device identifier */ enum w9968cf_model_id id; /* private device identifier */
struct video_device* v4ldev; /* -> V4L structure */ struct video_device* v4ldev; /* -> V4L structure */
...@@ -207,10 +219,10 @@ struct w9968cf_device { ...@@ -207,10 +219,10 @@ struct w9968cf_device {
u16* data_buffer; /* -> data to send to the FSB */ u16* data_buffer; /* -> data to send to the FSB */
struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS]; 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* frame_current; /* -> frame being grabbed */
struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; 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 */ u8 max_buffers, /* number of requested buffers */
force_palette, /* yes=1/no=0 */ force_palette, /* yes=1/no=0 */
...@@ -233,7 +245,7 @@ struct w9968cf_device { ...@@ -233,7 +245,7 @@ struct w9968cf_device {
hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ 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_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 */ enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
...@@ -246,13 +258,13 @@ struct w9968cf_device { ...@@ -246,13 +258,13 @@ struct w9968cf_device {
u8 sensor_initialized; /* flag: yes=1, no=0 */ 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_*) */ int sensor, /* type of image sensor chip (CC_*) */
monochrome; /* CMOS sensor is (probably) monochrome */ monochrome; /* image sensor is (probably) monochrome */
u16 maxwidth, /* maximum width supported by the CMOS sensor */ u16 maxwidth, /* maximum width supported by the image sensor */
maxheight, /* maximum height supported by the CMOS sensor */ maxheight, /* maximum height supported by the image sensor */
minwidth, /* minimum width supported by the CMOS sensor */ minwidth, /* minimum width supported by the image sensor */
minheight; /* minimum height supported by the CMOS sensor */ minheight; /* minimum height supported by the image sensor */
u8 auto_brt, /* auto brightness enabled flag */ u8 auto_brt, /* auto brightness enabled flag */
auto_exp, /* auto exposure enabled flag */ auto_exp, /* auto exposure enabled flag */
backlight, /* backlight exposure algorithm flag */ backlight, /* backlight exposure algorithm flag */
...@@ -270,8 +282,9 @@ struct w9968cf_device { ...@@ -270,8 +282,9 @@ struct w9968cf_device {
fileop_sem; /* for read and ioctl */ fileop_sem; /* for read and ioctl */
spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */ spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */
flist_lock; /* for requested frame list accesses */ flist_lock; /* for requested frame list accesses */
char command[16]; /* name of the program holding the device */
wait_queue_head_t open, wait_queue; wait_queue_head_t open, wait_queue;
char command[16]; /* name of the program holding the device */
}; };
...@@ -280,31 +293,47 @@ struct w9968cf_device { ...@@ -280,31 +293,47 @@ struct w9968cf_device {
****************************************************************************/ ****************************************************************************/
#undef DBG #undef DBG
#undef KDBG
#ifdef W9968CF_DEBUG #ifdef W9968CF_DEBUG
# define DBG(level, fmt, args...) \ /* For device specific debugging messages */
{ \ # define DBG(level, fmt, args...) \
if ( ((specific_debug) && (debug == (level))) || \ { \
((!specific_debug) && (debug >= (level))) ) { \ if ( ((specific_debug) && (debug == (level))) || \
if ((level) == 1) \ ((!specific_debug) && (debug >= (level))) ) { \
err(fmt, ## args); \ if ((level) == 1) \
else if ((level) == 2 || (level) == 3) \ dev_err(&cam->dev, fmt "\n", ## args); \
info(fmt, ## args); \ else if ((level) == 2 || (level) == 3) \
else if ((level) == 4) \ dev_info(&cam->dev, fmt "\n", ## args); \
warn(fmt, ## args); \ else if ((level) == 4) \
else if ((level) >= 5) \ dev_warn(&cam->dev, fmt "\n", ## args); \
info("[%s:%d] " fmt, \ else if ((level) >= 5) \
__FUNCTION__, __LINE__ , ## args); \ 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 #else
/* Not debugging: nothing */ /* Not debugging: nothing */
# define DBG(level, fmt, args...) do {;} while(0); # define DBG(level, fmt, args...) do {;} while(0);
# define KDBG(level, fmt, args...) do {;} while(0);
#endif #endif
#undef PDBG #undef PDBG
#define PDBG(fmt, args...) \
dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
#undef PDBGG #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 */ #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
#endif /* _W9968CF_H_ */ #endif /* _W9968CF_H_ */
/*************************************************************************** /***************************************************************************
* Various definitions for compatibility with OVCAMCHIP external module. * * Interface for video post-processing functions for the W996[87]CF driver *
* This file is part of the W996[87]CF driver for Linux. * * for Linux. *
* * * *
* The definitions have been taken from the OVCAMCHIP module written by * * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* Mark McClelland. *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 * * it under the terms of the GNU General Public License as published by *
...@@ -20,75 +19,25 @@ ...@@ -20,75 +19,25 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/ ***************************************************************************/
#ifndef _W9968CF_EXTERNALDEF_H_ #ifndef _W9968CF_VPP_H_
#define _W9968CF_EXTERNALDEF_H_ #define _W9968CF_VPP_H_
#include <linux/videodev.h> #include <linux/module.h>
#include <linux/i2c.h>
#include <asm/ioctl.h>
#include <asm/types.h> #include <asm/types.h>
#ifndef I2C_DRIVERID_OVCAMCHIP struct w9968cf_vpp_t {
# define I2C_DRIVERID_OVCAMCHIP 0xf00f struct module* owner;
#endif 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);
/* Controls */ u8 busy; /* read-only flag: module is/is not in use */
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 */ extern int w9968cf_vppmod_register(struct w9968cf_vpp_t*);
#define OV7xx0_SID (0x42 >> 1) extern int w9968cf_vppmod_deregister(struct w9968cf_vpp_t*);
#define OV6xx0_SID (0xC0 >> 1)
/* Sensor types */ #endif /* _W9968CF_VPP_H_ */
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_ */
...@@ -429,8 +429,9 @@ struct video_code ...@@ -429,8 +429,9 @@ struct video_code
#define VID_HARDWARE_CPIA2 33 #define VID_HARDWARE_CPIA2 33
#define VID_HARDWARE_VICAM 34 #define VID_HARDWARE_VICAM 34
#define VID_HARDWARE_SF16FMR2 35 #define VID_HARDWARE_SF16FMR2 35
#define VID_HARDWARE_W9968CF 36 #define VID_HARDWARE_W9968CF 36
#define VID_HARDWARE_SAA7114H 37 #define VID_HARDWARE_SAA7114H 37
#define VID_HARDWARE_SN9C102 38
#endif /* __LINUX_VIDEODEV_H */ #endif /* __LINUX_VIDEODEV_H */
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment