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

[PATCH] USB: SN9C10x driver updates and bugfixes

SN9C10x driver updates and bugfixes.

Changes: + new, - removed, * cleanup, @ bugfix:

@ fix poll()
@ Remove bad get_ctrl()'s
* Reduce ioctl stack usage
* Remove final ";" from some macro definitions
* Better support for SN9C103
+ Add sn9c102_write_regs()
+ Add 0x0c45/0x602d to the list of SN9C10x based devices
+ Add support for OV7630 image sensors
+ Provide support for the built-in microphone interface of the SN9C103
+ Documentation updates
+ Add 0x0c45/0x602e to the list of SN9C10x based devices
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ec7dc8d2
...@@ -17,16 +17,15 @@ Index ...@@ -17,16 +17,15 @@ Index
7. Module parameters 7. Module parameters
8. Optional device control through "sysfs" 8. Optional device control through "sysfs"
9. Supported devices 9. Supported devices
10. How to add plug-in's for new image sensors 10. Notes for V4L2 application developers
11. Notes for V4L2 application developers 11. Video frame formats
12. Video frame formats 12. Contact information
13. Contact information 13. Credits
14. Credits
1. Copyright 1. Copyright
============ ============
Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it>
2. Disclaimer 2. Disclaimer
...@@ -54,9 +53,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ...@@ -54,9 +53,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
4. Overview and features 4. Overview and features
======================== ========================
This driver attempts to support the video and audio streaming capabilities of This driver attempts to support the video interface of the devices mounting the
the devices mounting the SONiX SN9C101, SN9C102 and SN9C103 PC Camera SONiX SN9C101, SN9C102 and SN9C103 PC Camera Controllers.
Controllers.
It's worth to note that SONiX has never collaborated with the author during the It's worth to note that SONiX has never collaborated with the author during the
development of this project, despite several requests for enough detailed development of this project, despite several requests for enough detailed
...@@ -78,6 +76,7 @@ Some of the features of the driver are: ...@@ -78,6 +76,7 @@ Some of the features of the driver are:
- available mmap or read/poll methods for video streaming through isochronous - available mmap or read/poll methods for video streaming through isochronous
data transfers; data transfers;
- automatic detection of image sensor; - automatic detection of image sensor;
- support for built-in microphone interface;
- support for any window resolutions and optional panning within the maximum - support for any window resolutions and optional panning within the maximum
pixel area of image sensor; pixel area of image sensor;
- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both - image downscaling with arbitrary scaling factors from 1, 2 and 4 in both
...@@ -96,7 +95,7 @@ Some of the features of the driver are: ...@@ -96,7 +95,7 @@ Some of the features of the driver are:
parameters" paragraph); parameters" paragraph);
- up to 64 cameras can be handled at the same time; they can be connected and - 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 disconnected from the host many times without turning off the computer, if
your system supports hotplugging; the system supports hotplugging;
- no known bugs. - no known bugs.
...@@ -125,6 +124,21 @@ necessary: ...@@ -125,6 +124,21 @@ necessary:
CONFIG_USB_UHCI_HCD=m CONFIG_USB_UHCI_HCD=m
CONFIG_USB_OHCI_HCD=m CONFIG_USB_OHCI_HCD=m
The SN9C103 controller also provides a built-in microphone interface. It is
supported by the USB Audio driver thanks to the ALSA API:
# Sound
#
CONFIG_SOUND=y
# Advanced Linux Sound Architecture
#
CONFIG_SND=m
# USB devices
#
CONFIG_SND_USB_AUDIO=m
And finally: And finally:
# USB Multimedia devices # USB Multimedia devices
...@@ -153,7 +167,7 @@ analyze kernel messages and verify that the loading process has gone well: ...@@ -153,7 +167,7 @@ analyze kernel messages and verify that the loading process has gone well:
Module parameters are listed below: Module parameters are listed below:
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: video_nr Name: video_nr
Type: int array (min = 0, max = 64) Type: short array (min = 0, max = 64)
Syntax: <-1|n[,...]> Syntax: <-1|n[,...]>
Description: Specify V4L2 minor mode number: Description: Specify V4L2 minor mode number:
-1 = use next available -1 = use next available
...@@ -165,19 +179,19 @@ Description: Specify V4L2 minor mode number: ...@@ -165,19 +179,19 @@ Description: Specify V4L2 minor mode number:
other camera. other camera.
Default: -1 Default: -1
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: force_munmap; Name: force_munmap
Type: bool array (min = 0, max = 64) Type: bool array (min = 0, max = 64)
Syntax: <0|1[,...]> Syntax: <0|1[,...]>
Description: Force the application to unmap previously mapped buffer memory Description: Force the application to unmap previously mapped buffer memory
before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not
all the applications support this feature. This parameter is all the applications support this feature. This parameter is
specific for each detected camera. specific for each detected camera.
0 = do not force memory unmapping" 0 = do not force memory unmapping
1 = force memory unmapping (save memory)" 1 = force memory unmapping (save memory)
Default: 0 Default: 0
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: debug Name: debug
Type: int Type: ushort
Syntax: <n> Syntax: <n>
Description: Debugging information level, from 0 to 3: Description: Debugging information level, from 0 to 3:
0 = none (use carefully) 0 = none (use carefully)
...@@ -187,7 +201,7 @@ Description: Debugging information level, from 0 to 3: ...@@ -187,7 +201,7 @@ Description: Debugging information level, from 0 to 3:
Level 3 is useful for testing only, when only one device Level 3 is useful for testing only, when only one device
is used. It also shows some more informations about the is used. It also shows some more informations about the
hardware being detected. This parameter can be changed at hardware being detected. This parameter can be changed at
runtime thanks to the /sys filesystem. runtime thanks to the /sys filesystem interface.
Default: 2 Default: 2
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -236,7 +250,7 @@ serialized. ...@@ -236,7 +250,7 @@ serialized.
The sysfs interface also provides the "frame_header" entry, which exports the The sysfs interface also provides the "frame_header" entry, which exports the
frame header of the most recent requested and captured video frame. The header frame header of the most recent requested and captured video frame. The header
is 12-bytes long and is appended to every video frame by the SN9C10x is always 18-bytes long and is appended to every video frame by the SN9C10x
controllers. As an example, this additional information can be used by the user controllers. As an example, this additional information can be used by the user
application for implementing auto-exposure features via software. application for implementing auto-exposure features via software.
...@@ -250,7 +264,8 @@ Byte # Value Description ...@@ -250,7 +264,8 @@ Byte # Value Description
0x03 0xC4 Frame synchronisation pattern. 0x03 0xC4 Frame synchronisation pattern.
0x04 0xC4 Frame synchronisation pattern. 0x04 0xC4 Frame synchronisation pattern.
0x05 0x96 Frame synchronisation pattern. 0x05 0x96 Frame synchronisation pattern.
0x06 0x00 or 0x01 Unknown meaning. The exact value depends on the chip. 0x06 0xXX Unknown meaning. The exact value depends on the chip;
possible values are 0x00, 0x01 and 0x20.
0x07 0xXX Variable value, whose bits are ff00uzzc, where ff is a 0x07 0xXX Variable value, whose bits are ff00uzzc, where ff is a
frame counter, u is unknown, zz is a size indicator frame counter, u is unknown, zz is a size indicator
(00 = VGA, 01 = SIF, 10 = QSIF) and c stands for (00 = VGA, 01 = SIF, 10 = QSIF) and c stands for
...@@ -267,12 +282,23 @@ Byte # Value Description ...@@ -267,12 +282,23 @@ Byte # Value Description
times the area outside of the specified AE area. For times the area outside of the specified AE area. For
images that are not pure white, the value scales down images that are not pure white, the value scales down
according to relative whiteness. according to relative whiteness.
according to relative whiteness.
The following bytes are used by the SN9C103 bridge only:
0x0C 0xXX Unknown meaning
0x0D 0xXX Unknown meaning
0x0E 0xXX Unknown meaning
0x0F 0xXX Unknown meaning
0x10 0xXX Unknown meaning
0x11 0xXX Unknown meaning
The AE area (sx, sy, ex, ey) in the active window can be set by programming the The AE area (sx, sy, ex, ey) in the active window can be set by programming the
registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C10x controllers, where one unit registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C10x controllers, where one unit
corresponds to 32 pixels. corresponds to 32 pixels.
[1] The frame header has been documented by Bertrik Sikken. [1] Part of the meaning of the frame header has been documented by Bertrik
Sikken.
9. Supported devices 9. Supported devices
...@@ -298,6 +324,7 @@ Vendor ID Product ID ...@@ -298,6 +324,7 @@ Vendor ID Product ID
0x0c45 0x602b 0x0c45 0x602b
0x0c45 0x602c 0x0c45 0x602c
0x0c45 0x602d 0x0c45 0x602d
0x0c45 0x602e
0x0c45 0x6030 0x0c45 0x6030
0x0c45 0x6080 0x0c45 0x6080
0x0c45 0x6082 0x0c45 0x6082
...@@ -348,18 +375,7 @@ appreciated. Non-available hardware will not be supported by the author of this ...@@ -348,18 +375,7 @@ appreciated. Non-available hardware will not be supported by the author of this
driver. driver.
10. How to add plug-in's for new image sensors 10. Notes for V4L2 application developers
==============================================
It should be easy to write plug-in's for new sensors by using the small API
that has been created for this purpose, which is present in "sn9c102_sensor.h"
(documentation is included there). As an example, have a look at the code in
"sn9c102_pas106b.c", which uses the mentioned interface.
At the moment, possible unsupported image sensors are: CIS-VF10 (VGA),
OV7620 (VGA), OV7630 (VGA).
11. Notes for V4L2 application developers
========================================= =========================================
This driver follows the V4L2 API specifications. In particular, it enforces two This driver follows the V4L2 API specifications. In particular, it enforces two
rules: rules:
...@@ -394,7 +410,7 @@ initialized (as described in the documentation of the API for the image sensors ...@@ -394,7 +410,7 @@ initialized (as described in the documentation of the API for the image sensors
supplied by this driver). supplied by this driver).
12. Video frame formats [1] 11. Video frame formats [1]
======================= =======================
The SN9C10x PC Camera Controllers can send images in two possible video The SN9C10x PC Camera Controllers can send images in two possible video
formats over the USB: either native "Sequential RGB Bayer" or Huffman formats over the USB: either native "Sequential RGB Bayer" or Huffman
...@@ -455,7 +471,7 @@ The following Huffman codes have been found: ...@@ -455,7 +471,7 @@ The following Huffman codes have been found:
documented by Bertrik Sikken. documented by Bertrik Sikken.
13. Contact information 12. Contact information
======================= =======================
The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>. The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
...@@ -464,7 +480,7 @@ GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is ...@@ -464,7 +480,7 @@ GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is
the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
14. Credits 13. Credits
=========== ===========
Many thanks to following persons for their contribute (listed in alphabetical Many thanks to following persons for their contribute (listed in alphabetical
order): order):
...@@ -480,5 +496,5 @@ order): ...@@ -480,5 +496,5 @@ order):
- Bertrik Sikken, who reverse-engineered and documented the Huffman compression - Bertrik Sikken, who reverse-engineered and documented the Huffman compression
algorithm used in the SN9C10x controllers and implemented the first decoder; algorithm used in the SN9C10x controllers and implemented the first decoder;
- Mizuno Takafumi for the donation of a webcam; - Mizuno Takafumi for the donation of a webcam;
- An "anonymous" donator (who didn't want his name to be revealed) for the - an "anonymous" donator (who didn't want his name to be revealed) for the
donation of a webcam. donation of a webcam.
/*************************************************************************** /***************************************************************************
* V4L2 driver for SN9C10x PC Camera Controllers * * V4L2 driver for SN9C10x PC Camera Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
...@@ -53,11 +53,11 @@ ...@@ -53,11 +53,11 @@
/*****************************************************************************/ /*****************************************************************************/
#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" #define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers"
#define SN9C102_MODULE_AUTHOR "(C) 2004-2005 Luca Risolia" #define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL" #define SN9C102_MODULE_LICENSE "GPL"
#define SN9C102_MODULE_VERSION "1:1.24a" #define SN9C102_MODULE_VERSION "1:1.25"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 24) #define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 25)
enum sn9c102_bridge { enum sn9c102_bridge {
BRIDGE_SN9C101 = 0x01, BRIDGE_SN9C101 = 0x01,
...@@ -102,12 +102,13 @@ enum sn9c102_stream_state { ...@@ -102,12 +102,13 @@ enum sn9c102_stream_state {
STREAM_ON, STREAM_ON,
}; };
typedef char sn9c103_sof_header_t[18];
typedef char sn9c102_sof_header_t[12]; typedef char sn9c102_sof_header_t[12];
typedef char sn9c102_eof_header_t[4]; typedef char sn9c102_eof_header_t[4];
struct sn9c102_sysfs_attr { struct sn9c102_sysfs_attr {
u8 reg, i2c_reg; u8 reg, i2c_reg;
sn9c102_sof_header_t frame_header; sn9c103_sof_header_t frame_header;
}; };
struct sn9c102_module_param { struct sn9c102_module_param {
...@@ -140,8 +141,8 @@ struct sn9c102_device { ...@@ -140,8 +141,8 @@ struct sn9c102_device {
struct v4l2_jpegcompression compression; struct v4l2_jpegcompression compression;
struct sn9c102_sysfs_attr sysfs; struct sn9c102_sysfs_attr sysfs;
sn9c102_sof_header_t sof_header; sn9c103_sof_header_t sof_header;
u16 reg[32]; u16 reg[63];
struct sn9c102_module_param module_param; struct sn9c102_module_param module_param;
...@@ -170,7 +171,7 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, ...@@ -170,7 +171,7 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
#undef KDBG #undef KDBG
#ifdef SN9C102_DEBUG #ifdef SN9C102_DEBUG
# define DBG(level, fmt, args...) \ # define DBG(level, fmt, args...) \
{ \ do { \
if (debug >= (level)) { \ if (debug >= (level)) { \
if ((level) == 1) \ if ((level) == 1) \
dev_err(&cam->dev, fmt "\n", ## args); \ dev_err(&cam->dev, fmt "\n", ## args); \
...@@ -180,9 +181,9 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, ...@@ -180,9 +181,9 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ dev_info(&cam->dev, "[%s:%d] " fmt "\n", \
__FUNCTION__, __LINE__ , ## args); \ __FUNCTION__, __LINE__ , ## args); \
} \ } \
} } while (0)
# define KDBG(level, fmt, args...) \ # define KDBG(level, fmt, args...) \
{ \ do { \
if (debug >= (level)) { \ if (debug >= (level)) { \
if ((level) == 1 || (level) == 2) \ if ((level) == 1 || (level) == 2) \
pr_info("sn9c102: " fmt "\n", ## args); \ pr_info("sn9c102: " fmt "\n", ## args); \
...@@ -190,17 +191,17 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, ...@@ -190,17 +191,17 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \
__LINE__ , ## args); \ __LINE__ , ## args); \
} \ } \
} } while (0)
#else #else
# define KDBG(level, fmt, args...) do {;} while(0); # define KDBG(level, fmt, args...) do {;} while(0)
# define DBG(level, fmt, args...) do {;} while(0); # define DBG(level, fmt, args...) do {;} while(0)
#endif #endif
#undef PDBG #undef PDBG
#define PDBG(fmt, args...) \ #define PDBG(fmt, args...) \
dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args); dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
#undef PDBGG #undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0); /* placeholder */ #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
#endif /* _SN9C102_H_ */ #endif /* _SN9C102_H_ */
/*************************************************************************** /***************************************************************************
* V4L2 driver for SN9C10x PC Camera Controllers * * V4L2 driver for SN9C10x PC Camera Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
...@@ -70,10 +70,10 @@ static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = ...@@ -70,10 +70,10 @@ static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
SN9C102_FORCE_MUNMAP}; SN9C102_FORCE_MUNMAP};
module_param_array(force_munmap, bool, NULL, 0444); module_param_array(force_munmap, bool, NULL, 0444);
MODULE_PARM_DESC(force_munmap, MODULE_PARM_DESC(force_munmap,
"\n<0|1[,...]> Force the application to unmap previously " "\n<0|1[,...]> Force the application to unmap previously"
"\nmapped buffer memory before calling any VIDIOC_S_CROP or " "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
"\nVIDIOC_S_FMT ioctl's. Not all the applications support " "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
"\nthis feature. This parameter is specific for each " "\nthis feature. This parameter is specific for each"
"\ndetected camera." "\ndetected camera."
"\n 0 = do not force memory unmapping" "\n 0 = do not force memory unmapping"
"\n 1 = force memory unmapping (save memory)" "\n 1 = force memory unmapping (save memory)"
...@@ -102,6 +102,9 @@ static sn9c102_sof_header_t sn9c102_sof_header[] = { ...@@ -102,6 +102,9 @@ static sn9c102_sof_header_t sn9c102_sof_header[] = {
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
}; };
static sn9c103_sof_header_t sn9c103_sof_header[] = {
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20},
};
static sn9c102_eof_header_t sn9c102_eof_header[] = { static sn9c102_eof_header_t sn9c102_eof_header[] = {
{0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00},
...@@ -202,6 +205,7 @@ static void sn9c102_release_buffers(struct sn9c102_device* cam) ...@@ -202,6 +205,7 @@ static void sn9c102_release_buffers(struct sn9c102_device* cam)
cam->nbuffers * PAGE_ALIGN(cam->frame[0].buf.length)); cam->nbuffers * PAGE_ALIGN(cam->frame[0].buf.length));
cam->nbuffers = 0; cam->nbuffers = 0;
} }
cam->frame_current = NULL;
} }
...@@ -219,6 +223,19 @@ static void sn9c102_empty_framequeues(struct sn9c102_device* cam) ...@@ -219,6 +223,19 @@ static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
} }
static void sn9c102_requeue_outqueue(struct sn9c102_device* cam)
{
struct sn9c102_frame_t *i;
list_for_each_entry(i, &cam->outqueue, frame) {
i->state = F_QUEUED;
list_add(&i->frame, &cam->inqueue);
}
INIT_LIST_HEAD(&cam->outqueue);
}
static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
{ {
unsigned long lock_flags; unsigned long lock_flags;
...@@ -235,19 +252,46 @@ static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) ...@@ -235,19 +252,46 @@ static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
/*****************************************************************************/ /*****************************************************************************/
int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index)
{
struct usb_device* udev = cam->usbdev;
int i, res;
if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg))
return -1;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
index, 0, buff, sizeof(buff),
SN9C102_CTRL_TIMEOUT*sizeof(buff));
if (res < 0) {
DBG(3, "Failed to write registers (index 0x%02X, error %d)",
index, res);
return -1;
}
for (i = 0; i < sizeof(buff); i++)
cam->reg[index+i] = buff[i];
return 0;
}
int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
{ {
struct usb_device* udev = cam->usbdev; struct usb_device* udev = cam->usbdev;
u8* buff = cam->control_buffer; u8* buff = cam->control_buffer;
int res; int res;
if (index >= ARRAY_SIZE(cam->reg))
return -1;
*buff = value; *buff = value;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
if (res < 0) { if (res < 0) {
DBG(3, "Failed to write a register (value 0x%02X, index " DBG(3, "Failed to write a register (value 0x%02X, index "
"0x%02X, error %d)", value, index, res) "0x%02X, error %d)", value, index, res);
return -1; return -1;
} }
...@@ -268,7 +312,7 @@ static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) ...@@ -268,7 +312,7 @@ static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
if (res < 0) if (res < 0)
DBG(3, "Failed to read a register (index 0x%02X, error %d)", DBG(3, "Failed to read a register (index 0x%02X, error %d)",
index, res) index, res);
return (res >= 0) ? (int)(*buff) : -1; return (res >= 0) ? (int)(*buff) : -1;
} }
...@@ -276,8 +320,8 @@ static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) ...@@ -276,8 +320,8 @@ static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
{ {
if (index > 0x1f) if (index >= ARRAY_SIZE(cam->reg))
return -EINVAL; return -1;
return cam->reg[index]; return cam->reg[index];
} }
...@@ -367,10 +411,10 @@ sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, ...@@ -367,10 +411,10 @@ sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
err += sn9c102_i2c_detect_read_error(cam, sensor); err += sn9c102_i2c_detect_read_error(cam, sensor);
PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
data[4]) data[4]);
if (err) { if (err) {
DBG(3, "I2C read failed for %s image sensor", sensor->name) DBG(3, "I2C read failed for %s image sensor", sensor->name);
return -1; return -1;
} }
...@@ -410,11 +454,11 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, ...@@ -410,11 +454,11 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
err += sn9c102_i2c_detect_write_error(cam, sensor); err += sn9c102_i2c_detect_write_error(cam, sensor);
if (err) if (err)
DBG(3, "I2C write failed for %s image sensor", sensor->name) DBG(3, "I2C write failed for %s image sensor", sensor->name);
PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
n, data0, data1, data2, data3, data4, data5) n, data0, data1, data2, data3, data4, data5);
return err ? -1 : 0; return err ? -1 : 0;
} }
...@@ -461,13 +505,27 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) ...@@ -461,13 +505,27 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
static void* static void*
sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
{ {
size_t soflen = sizeof(sn9c102_sof_header_t), i; size_t soflen = 0, i;
u8 j, n = sizeof(sn9c102_sof_header) / soflen; u8 j, n = 0;
switch (cam->bridge) {
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
soflen = sizeof(sn9c102_sof_header_t);
n = sizeof(sn9c102_sof_header) / soflen;
break;
case BRIDGE_SN9C103:
soflen = sizeof(sn9c103_sof_header_t);
n = sizeof(sn9c103_sof_header) / soflen;
}
for (i = 0; (len >= soflen) && (i <= len - soflen); i++) for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
for (j = 0; j < n; j++) for (j = 0; j < n; j++)
/* It's enough to compare 7 bytes */ /* The invariable part of the header is 6 bytes long */
if (!memcmp(mem + i, sn9c102_sof_header[j], 7)) { if ((cam->bridge != BRIDGE_SN9C103 &&
!memcmp(mem + i, sn9c102_sof_header[j], 6)) ||
(cam->bridge == BRIDGE_SN9C103 &&
!memcmp(mem + i, sn9c103_sof_header[j], 6))) {
memcpy(cam->sof_header, mem + i, soflen); memcpy(cam->sof_header, mem + i, soflen);
/* Skip the header */ /* Skip the header */
return mem + i + soflen; return mem + i + soflen;
...@@ -499,8 +557,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -499,8 +557,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
{ {
struct sn9c102_device* cam = urb->context; struct sn9c102_device* cam = urb->context;
struct sn9c102_frame_t** f; struct sn9c102_frame_t** f;
size_t imagesize; size_t imagesize, soflen;
unsigned long lock_flags;
u8 i; u8 i;
int err = 0; int err = 0;
...@@ -513,7 +570,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -513,7 +570,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
cam->stream = STREAM_OFF; cam->stream = STREAM_OFF;
if ((*f)) if ((*f))
(*f)->state = F_QUEUED; (*f)->state = F_QUEUED;
DBG(3, "Stream interrupted") DBG(3, "Stream interrupted");
wake_up_interruptible(&cam->wait_stream); wake_up_interruptible(&cam->wait_stream);
} }
...@@ -536,6 +593,10 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -536,6 +593,10 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
cam->sensor->pix_format.height * cam->sensor->pix_format.height *
cam->sensor->pix_format.priv) / 8; cam->sensor->pix_format.priv) / 8;
soflen = (cam->bridge) == BRIDGE_SN9C103 ?
sizeof(sn9c103_sof_header_t) :
sizeof(sn9c102_sof_header_t);
for (i = 0; i < urb->number_of_packets; i++) { for (i = 0; i < urb->number_of_packets; i++) {
unsigned int img, len, status; unsigned int img, len, status;
void *pos, *sof, *eof; void *pos, *sof, *eof;
...@@ -545,19 +606,12 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -545,19 +606,12 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
if (status) { if (status) {
DBG(3, "Error in isochronous frame") DBG(3, "Error in isochronous frame");
(*f)->state = F_ERROR; (*f)->state = F_ERROR;
continue; continue;
} }
PDBGG("Isochrnous frame: length %u, #%u i", len, i) 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: redo:
sof = sn9c102_find_sof_header(cam, pos, len); sof = sn9c102_find_sof_header(cam, pos, len);
...@@ -575,10 +629,10 @@ end_of_frame: ...@@ -575,10 +629,10 @@ end_of_frame:
imagesize; imagesize;
img = imagesize - (*f)->buf.bytesused; img = imagesize - (*f)->buf.bytesused;
DBG(3, "Expected EOF not found: " DBG(3, "Expected EOF not found: "
"video frame cut") "video frame cut");
if (eof) if (eof)
DBG(3, "Exceeded limit: +%u " DBG(3, "Exceeded limit: +%u "
"bytes", (unsigned)(b)) "bytes", (unsigned)(b));
} }
memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
...@@ -595,8 +649,7 @@ end_of_frame: ...@@ -595,8 +649,7 @@ end_of_frame:
u32 b = (*f)->buf.bytesused; u32 b = (*f)->buf.bytesused;
(*f)->state = F_DONE; (*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count; (*f)->buf.sequence= ++cam->frame_count;
spin_lock_irqsave(&cam->queue_lock, spin_lock(&cam->queue_lock);
lock_flags);
list_move_tail(&(*f)->frame, list_move_tail(&(*f)->frame,
&cam->outqueue); &cam->outqueue);
if (!list_empty(&cam->inqueue)) if (!list_empty(&cam->inqueue))
...@@ -606,13 +659,11 @@ end_of_frame: ...@@ -606,13 +659,11 @@ end_of_frame:
frame ); frame );
else else
(*f) = NULL; (*f) = NULL;
spin_unlock_irqrestore(&cam->queue_lock spin_unlock(&cam->queue_lock);
, lock_flags);
memcpy(cam->sysfs.frame_header, memcpy(cam->sysfs.frame_header,
cam->sof_header, cam->sof_header, soflen);
sizeof(sn9c102_sof_header_t)); DBG(3, "Video frame captured: %lu "
DBG(3, "Video frame captured: " "bytes", (unsigned long)(b));
"%lu bytes", (unsigned long)(b))
if (!(*f)) if (!(*f))
goto resubmit_urb; goto resubmit_urb;
...@@ -621,18 +672,19 @@ end_of_frame: ...@@ -621,18 +672,19 @@ end_of_frame:
(*f)->state = F_ERROR; (*f)->state = F_ERROR;
DBG(3, "Not expected EOF after %lu " DBG(3, "Not expected EOF after %lu "
"bytes of image data", "bytes of image data",
(unsigned long)((*f)->buf.bytesused)) (unsigned long)
((*f)->buf.bytesused));
} }
if (sof) /* (1) */ if (sof) /* (1) */
goto start_of_frame; goto start_of_frame;
} else if (eof) { } else if (eof) {
DBG(3, "EOF without SOF") DBG(3, "EOF without SOF");
continue; continue;
} else { } else {
PDBGG("Ignoring pointless isochronous frame") PDBGG("Ignoring pointless isochronous frame");
continue; continue;
} }
...@@ -642,7 +694,7 @@ start_of_frame: ...@@ -642,7 +694,7 @@ start_of_frame:
(*f)->buf.bytesused = 0; (*f)->buf.bytesused = 0;
len -= (sof - pos); len -= (sof - pos);
pos = sof; pos = sof;
DBG(3, "SOF detected: new video frame") DBG(3, "SOF detected: new video frame");
if (len) if (len)
goto redo; goto redo;
...@@ -653,12 +705,13 @@ start_of_frame: ...@@ -653,12 +705,13 @@ start_of_frame:
else { else {
if (cam->sensor->pix_format.pixelformat == if (cam->sensor->pix_format.pixelformat ==
V4L2_PIX_FMT_SN9C10X) { V4L2_PIX_FMT_SN9C10X) {
eof = sof-sizeof(sn9c102_sof_header_t); eof = sof - soflen;
goto end_of_frame; goto end_of_frame;
} else { } else {
DBG(3, "SOF before expected EOF after " DBG(3, "SOF before expected EOF after "
"%lu bytes of image data", "%lu bytes of image data",
(unsigned long)((*f)->buf.bytesused)) (unsigned long)
((*f)->buf.bytesused));
goto start_of_frame; goto start_of_frame;
} }
} }
...@@ -670,7 +723,7 @@ resubmit_urb: ...@@ -670,7 +723,7 @@ resubmit_urb:
err = usb_submit_urb(urb, GFP_ATOMIC); err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0 && err != -EPERM) { if (err < 0 && err != -EPERM) {
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "usb_submit_urb() failed") DBG(1, "usb_submit_urb() failed");
} }
wake_up_interruptible(&cam->wait_frame); wake_up_interruptible(&cam->wait_frame);
...@@ -681,9 +734,13 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) ...@@ -681,9 +734,13 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
{ {
struct usb_device *udev = cam->usbdev; struct usb_device *udev = cam->usbdev;
struct urb* urb; struct urb* urb;
const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512, const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512,
680, 800, 900, 1023}; 680, 800, 900, 1023};
const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512,
680, 800, 900, 1003};
const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ?
sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] :
sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
s8 i, j; s8 i, j;
int err = 0; int err = 0;
...@@ -692,7 +749,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) ...@@ -692,7 +749,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
GFP_KERNEL); GFP_KERNEL);
if (!cam->transfer_buffer[i]) { if (!cam->transfer_buffer[i]) {
err = -ENOMEM; err = -ENOMEM;
DBG(1, "Not enough memory") DBG(1, "Not enough memory");
goto free_buffers; goto free_buffers;
} }
} }
...@@ -702,7 +759,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) ...@@ -702,7 +759,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
cam->urb[i] = urb; cam->urb[i] = urb;
if (!urb) { if (!urb) {
err = -ENOMEM; err = -ENOMEM;
DBG(1, "usb_alloc_urb() failed") DBG(1, "usb_alloc_urb() failed");
goto free_urbs; goto free_urbs;
} }
urb->dev = udev; urb->dev = udev;
...@@ -725,14 +782,14 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) ...@@ -725,14 +782,14 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
if (err) { if (err) {
err = -EIO; err = -EIO;
DBG(1, "I/O hardware error") DBG(1, "I/O hardware error");
goto free_urbs; goto free_urbs;
} }
} }
err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
if (err) { if (err) {
DBG(1, "usb_set_interface() failed") DBG(1, "usb_set_interface() failed");
goto free_urbs; goto free_urbs;
} }
...@@ -743,7 +800,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) ...@@ -743,7 +800,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
if (err) { if (err) {
for (j = i-1; j >= 0; j--) for (j = i-1; j >= 0; j--)
usb_kill_urb(cam->urb[j]); usb_kill_urb(cam->urb[j]);
DBG(1, "usb_submit_urb() failed, error %d", err) DBG(1, "usb_submit_urb() failed, error %d", err);
goto free_urbs; goto free_urbs;
} }
} }
...@@ -779,7 +836,7 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam) ...@@ -779,7 +836,7 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
if (err) if (err)
DBG(3, "usb_set_interface() failed") DBG(3, "usb_set_interface() failed");
return err; return err;
} }
...@@ -799,7 +856,7 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam) ...@@ -799,7 +856,7 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
else if (err) { else if (err) {
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "The camera is misconfigured. To use it, close and " DBG(1, "The camera is misconfigured. To use it, close and "
"open /dev/video%d again.", cam->v4ldev->minor) "open /dev/video%d again.", cam->v4ldev->minor);
return err; return err;
} }
...@@ -885,8 +942,8 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) ...@@ -885,8 +942,8 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
cam->sysfs.reg = index; cam->sysfs.reg = index;
DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg) DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg);
DBG(3, "Written bytes: %zd", count) DBG(3, "Written bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -916,7 +973,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) ...@@ -916,7 +973,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
count = sprintf(buf, "%d\n", val); count = sprintf(buf, "%d\n", val);
DBG(3, "Read bytes: %zd", count) DBG(3, "Read bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -954,8 +1011,8 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) ...@@ -954,8 +1011,8 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
} }
DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X", DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X",
cam->sysfs.reg, value) cam->sysfs.reg, value);
DBG(3, "Written bytes: %zd", count) DBG(3, "Written bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -979,7 +1036,7 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) ...@@ -979,7 +1036,7 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
DBG(3, "Read bytes: %zd", count) DBG(3, "Read bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -1011,8 +1068,8 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) ...@@ -1011,8 +1068,8 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
cam->sysfs.i2c_reg = index; cam->sysfs.i2c_reg = index;
DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg) DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
DBG(3, "Written bytes: %zd", count) DBG(3, "Written bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -1047,7 +1104,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) ...@@ -1047,7 +1104,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
count = sprintf(buf, "%d\n", val); count = sprintf(buf, "%d\n", val);
DBG(3, "Read bytes: %zd", count) DBG(3, "Read bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -1090,8 +1147,8 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) ...@@ -1090,8 +1147,8 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
} }
DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
cam->sysfs.i2c_reg, value) cam->sysfs.i2c_reg, value);
DBG(3, "Written bytes: %zd", count) DBG(3, "Written bytes: %zd", count);
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -1193,7 +1250,7 @@ static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf) ...@@ -1193,7 +1250,7 @@ static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf)
count = sizeof(cam->sysfs.frame_header); count = sizeof(cam->sysfs.frame_header);
memcpy(buf, cam->sysfs.frame_header, count); memcpy(buf, cam->sysfs.frame_header, count);
DBG(3, "Frame header, read bytes: %zd", count) DBG(3, "Frame header, read bytes: %zd", count);
return count; return count;
} }
...@@ -1227,7 +1284,7 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) ...@@ -1227,7 +1284,7 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
video_device_create_file(v4ldev, &class_device_attr_blue); video_device_create_file(v4ldev, &class_device_attr_blue);
video_device_create_file(v4ldev, &class_device_attr_red); video_device_create_file(v4ldev, &class_device_attr_red);
} }
if (cam->sensor->sysfs_ops) { if (cam->sensor && cam->sensor->sysfs_ops) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val); video_device_create_file(v4ldev, &class_device_attr_i2c_val);
} }
...@@ -1281,7 +1338,7 @@ static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) ...@@ -1281,7 +1338,7 @@ static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
if (err) if (err)
return -EIO; return -EIO;
PDBGG("Scaling factor: %u", scale) PDBGG("Scaling factor: %u", scale);
return 0; return 0;
} }
...@@ -1304,7 +1361,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) ...@@ -1304,7 +1361,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
return -EIO; return -EIO;
PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
"%u %u %u %u", h_start, v_start, h_size, v_size) "%u %u %u %u", h_start, v_start, h_size, v_size);
return 0; return 0;
} }
...@@ -1336,7 +1393,7 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1336,7 +1393,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
if (s->init) { if (s->init) {
err = s->init(cam); err = s->init(cam);
if (err) { if (err) {
DBG(3, "Sensor initialization failed") DBG(3, "Sensor initialization failed");
return err; return err;
} }
} }
...@@ -1353,13 +1410,13 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1353,13 +1410,13 @@ static int sn9c102_init(struct sn9c102_device* cam)
if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
DBG(3, "Compressed video format is active, quality %d", DBG(3, "Compressed video format is active, quality %d",
cam->compression.quality) cam->compression.quality);
else else
DBG(3, "Uncompressed video format is active") DBG(3, "Uncompressed video format is active");
if (s->set_crop) if (s->set_crop)
if ((err = s->set_crop(cam, rect))) { if ((err = s->set_crop(cam, rect))) {
DBG(3, "set_crop() failed") DBG(3, "set_crop() failed");
return err; return err;
} }
...@@ -1372,11 +1429,11 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1372,11 +1429,11 @@ static int sn9c102_init(struct sn9c102_device* cam)
err = s->set_ctrl(cam, &ctrl); err = s->set_ctrl(cam, &ctrl);
if (err) { if (err) {
DBG(3, "Set %s control failed", DBG(3, "Set %s control failed",
s->qctrl[i].name) s->qctrl[i].name);
return err; return err;
} }
DBG(3, "Image sensor supports '%s' control", DBG(3, "Image sensor supports '%s' control",
s->qctrl[i].name) s->qctrl[i].name);
} }
} }
...@@ -1392,7 +1449,7 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1392,7 +1449,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
cam->state |= DEV_INITIALIZED; cam->state |= DEV_INITIALIZED;
} }
DBG(2, "Initialization succeeded") DBG(2, "Initialization succeeded");
return 0; return 0;
} }
...@@ -1401,7 +1458,7 @@ static void sn9c102_release_resources(struct sn9c102_device* cam) ...@@ -1401,7 +1458,7 @@ static void sn9c102_release_resources(struct sn9c102_device* cam)
{ {
down(&sn9c102_sysfs_lock); down(&sn9c102_sysfs_lock);
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor) DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL); video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev); video_unregister_device(cam->v4ldev);
...@@ -1432,7 +1489,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) ...@@ -1432,7 +1489,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
} }
if (cam->users) { if (cam->users) {
DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor) DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
if ((filp->f_flags & O_NONBLOCK) || if ((filp->f_flags & O_NONBLOCK) ||
(filp->f_flags & O_NDELAY)) { (filp->f_flags & O_NDELAY)) {
err = -EWOULDBLOCK; err = -EWOULDBLOCK;
...@@ -1458,7 +1515,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) ...@@ -1458,7 +1515,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
err = sn9c102_init(cam); err = sn9c102_init(cam);
if (err) { if (err) {
DBG(1, "Initialization failed again. " DBG(1, "Initialization failed again. "
"I will retry on next open().") "I will retry on next open().");
goto out; goto out;
} }
cam->state &= ~DEV_MISCONFIGURED; cam->state &= ~DEV_MISCONFIGURED;
...@@ -1475,7 +1532,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) ...@@ -1475,7 +1532,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
cam->frame_count = 0; cam->frame_count = 0;
sn9c102_empty_framequeues(cam); sn9c102_empty_framequeues(cam);
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor) DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out: out:
up(&cam->dev_sem); up(&cam->dev_sem);
...@@ -1504,7 +1561,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) ...@@ -1504,7 +1561,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp)
cam->users--; cam->users--;
wake_up_interruptible_nr(&cam->open, 1); wake_up_interruptible_nr(&cam->open, 1);
DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor) DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
up(&cam->dev_sem); up(&cam->dev_sem);
...@@ -1524,32 +1581,38 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1524,32 +1581,38 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
return -ERESTARTSYS; return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) { if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present") DBG(1, "Device not present");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENODEV; return -ENODEV;
} }
if (cam->state & DEV_MISCONFIGURED) { if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it again.") DBG(1, "The camera is misconfigured. Close and open it "
"again.");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -EIO; return -EIO;
} }
if (cam->io == IO_MMAP) { if (cam->io == IO_MMAP) {
DBG(3, "Close and open the device again to choose " DBG(3, "Close and open the device again to choose "
"the read method") "the read method");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -EINVAL; return -EINVAL;
} }
if (cam->io == IO_NONE) { if (cam->io == IO_NONE) {
if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
DBG(1, "read() failed, not enough memory") DBG(1, "read() failed, not enough memory");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENOMEM; return -ENOMEM;
} }
cam->io = IO_READ; cam->io = IO_READ;
cam->stream = STREAM_ON; cam->stream = STREAM_ON;
}
if (list_empty(&cam->inqueue)) {
if (!list_empty(&cam->outqueue))
sn9c102_empty_framequeues(cam);
sn9c102_queue_unusedframes(cam); sn9c102_queue_unusedframes(cam);
} }
...@@ -1584,6 +1647,16 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1584,6 +1647,16 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
if (count > f->buf.bytesused)
count = f->buf.bytesused;
if (copy_to_user(buf, f->bufmem, count)) {
err = -EFAULT;
goto exit;
}
*f_pos += count;
exit:
spin_lock_irqsave(&cam->queue_lock, lock_flags); spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_for_each_entry(i, &cam->outqueue, frame) list_for_each_entry(i, &cam->outqueue, frame)
i->state = F_UNUSED; i->state = F_UNUSED;
...@@ -1592,16 +1665,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1592,16 +1665,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
sn9c102_queue_unusedframes(cam); sn9c102_queue_unusedframes(cam);
if (count > f->buf.bytesused) PDBGG("Frame #%lu, bytes read: %zu",
count = f->buf.bytesused; (unsigned long)f->buf.index, count);
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); up(&cam->fileop_sem);
...@@ -1612,33 +1677,42 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1612,33 +1677,42 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
{ {
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
struct sn9c102_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0; unsigned int mask = 0;
if (down_interruptible(&cam->fileop_sem)) if (down_interruptible(&cam->fileop_sem))
return POLLERR; return POLLERR;
if (cam->state & DEV_DISCONNECTED) { if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present") DBG(1, "Device not present");
goto error; goto error;
} }
if (cam->state & DEV_MISCONFIGURED) { if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it again.") DBG(1, "The camera is misconfigured. Close and open it "
"again.");
goto error; goto error;
} }
if (cam->io == IO_NONE) { if (cam->io == IO_NONE) {
if (!sn9c102_request_buffers(cam, cam->nreadbuffers, if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
IO_READ)) { IO_READ)) {
DBG(1, "poll() failed, not enough memory") DBG(1, "poll() failed, not enough memory");
goto error; goto error;
} }
cam->io = IO_READ; cam->io = IO_READ;
cam->stream = STREAM_ON; cam->stream = STREAM_ON;
} }
if (cam->io == IO_READ) if (cam->io == IO_READ) {
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_for_each_entry(f, &cam->outqueue, frame)
f->state = F_UNUSED;
INIT_LIST_HEAD(&cam->outqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
sn9c102_queue_unusedframes(cam); sn9c102_queue_unusedframes(cam);
}
poll_wait(filp, &cam->wait_frame, wait); poll_wait(filp, &cam->wait_frame, wait);
...@@ -1689,13 +1763,14 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -1689,13 +1763,14 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
return -ERESTARTSYS; return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) { if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present") DBG(1, "Device not present");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENODEV; return -ENODEV;
} }
if (cam->state & DEV_MISCONFIGURED) { if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it again.") DBG(1, "The camera is misconfigured. Close and open it "
"again.");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -EIO; return -EIO;
} }
...@@ -1742,38 +1817,32 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -1742,38 +1817,32 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
return 0; return 0;
} }
/*****************************************************************************/
static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, static int
unsigned int cmd, void __user * arg) sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
{ {
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
switch (cmd) {
case VIDIOC_QUERYCAP:
{
struct v4l2_capability cap = { struct v4l2_capability cap = {
.driver = "sn9c102", .driver = "sn9c102",
.version = SN9C102_MODULE_VERSION_CODE, .version = SN9C102_MODULE_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE | .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING, V4L2_CAP_STREAMING,
}; };
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
sizeof(cap.bus_info)) < 0) strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info));
strlcpy(cap.bus_info, cam->dev.bus_id,
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap))) if (copy_to_user(arg, &cap, sizeof(cap)))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_ENUMINPUT:
{ static int
sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_input i; struct v4l2_input i;
if (copy_from_user(&i, arg, sizeof(i))) if (copy_from_user(&i, arg, sizeof(i)))
...@@ -1789,11 +1858,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1789,11 +1858,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_G_INPUT:
case VIDIOC_S_INPUT: static int
{ sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg)
{
int index; int index;
if (copy_from_user(&index, arg, sizeof(index))) if (copy_from_user(&index, arg, sizeof(index)))
...@@ -1803,10 +1873,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1803,10 +1873,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EINVAL; return -EINVAL;
return 0; return 0;
} }
case VIDIOC_QUERYCTRL:
{ static int
sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
{
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
struct v4l2_queryctrl qc; struct v4l2_queryctrl qc;
u8 i; u8 i;
...@@ -1823,31 +1895,44 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1823,31 +1895,44 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
} }
return -EINVAL; return -EINVAL;
} }
case VIDIOC_G_CTRL:
{ static int
sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
{
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
struct v4l2_control ctrl; struct v4l2_control ctrl;
int err = 0; int err = 0;
u8 i;
if (!s->get_ctrl) if (!s->get_ctrl && !s->set_ctrl)
return -EINVAL; return -EINVAL;
if (copy_from_user(&ctrl, arg, sizeof(ctrl))) if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT; return -EFAULT;
if (!s->get_ctrl) {
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id && ctrl.id == s->qctrl[i].id) {
ctrl.value = s->_qctrl[i].default_value;
goto exit;
}
return -EINVAL;
} else
err = s->get_ctrl(cam, &ctrl); err = s->get_ctrl(cam, &ctrl);
exit:
if (copy_to_user(arg, &ctrl, sizeof(ctrl))) if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
return -EFAULT; return -EFAULT;
return err; return err;
} }
case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL: static int
{ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
{
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
struct v4l2_control ctrl; struct v4l2_control ctrl;
u8 i; u8 i;
...@@ -1874,13 +1959,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1874,13 +1959,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
s->_qctrl[i].default_value = ctrl.value; s->_qctrl[i].default_value = ctrl.value;
PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
(unsigned long)ctrl.id, (unsigned long)ctrl.value) (unsigned long)ctrl.id, (unsigned long)ctrl.value);
return 0; return 0;
} }
case VIDIOC_CROPCAP:
{ static int
sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_cropcap* cc = &(cam->sensor->cropcap); struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
...@@ -1891,10 +1978,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1891,10 +1978,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_G_CROP:
{ static int
sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
{
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
struct v4l2_crop crop = { struct v4l2_crop crop = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
...@@ -1906,10 +1995,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1906,10 +1995,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_S_CROP:
{ static int
sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
{
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
struct v4l2_crop crop; struct v4l2_crop crop;
struct v4l2_rect* rect; struct v4l2_rect* rect;
...@@ -1933,15 +2024,13 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1933,15 +2024,13 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
for (i = 0; i < cam->nbuffers; i++) for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_CROP failed. " DBG(3, "VIDIOC_S_CROP failed. "
"Unmap the buffers first.") "Unmap the buffers first.");
return -EINVAL; return -EINVAL;
} }
/* Preserve R,G or B origin */ /* Preserve R,G or B origin */
rect->left = (s->_rect.left & 1L) ? rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
rect->left | 1L : rect->left & ~1L; rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
rect->top = (s->_rect.top & 1L) ?
rect->top | 1L : rect->top & ~1L;
if (rect->width < 16) if (rect->width < 16)
rect->width = 16; rect->width = 16;
...@@ -1968,8 +2057,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1968,8 +2057,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
u32 a, b; u32 a, b;
a = rect->width * rect->height; a = rect->width * rect->height;
b = pix_format->width * pix_format->height; b = pix_format->width * pix_format->height;
scale = b ? (u8)((a / b) < 4 ? 1 : scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
((a / b) < 16 ? 2 : 4)) : 1;
} else } else
scale = 1; scale = 1;
...@@ -1992,9 +2080,9 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -1992,9 +2080,9 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
if (err) { /* atomic, no rollback in ioctl() */ if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of hardware " DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
"problems. To use the camera, close and open " "use the camera, close and open /dev/video%d again.",
"/dev/video%d again.", cam->v4ldev->minor) cam->v4ldev->minor);
return -EIO; return -EIO;
} }
...@@ -2003,22 +2091,28 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2003,22 +2091,28 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
memcpy(&(s->_rect), rect, sizeof(*rect)); memcpy(&(s->_rect), rect, sizeof(*rect));
if ((cam->module_param.force_munmap || cam->io == IO_READ) && if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
nbuffers != sn9c102_request_buffers(cam, nbuffers, nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
cam->io)) {
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of not enough " DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
"memory. To use the camera, close and open " "use the camera, close and open /dev/video%d again.",
"/dev/video%d again.", cam->v4ldev->minor) cam->v4ldev->minor);
return -ENOMEM; return -ENOMEM;
} }
if (cam->io == IO_READ)
sn9c102_empty_framequeues(cam);
else if (cam->module_param.force_munmap)
sn9c102_requeue_outqueue(cam);
cam->stream = stream; cam->stream = stream;
return 0; return 0;
} }
case VIDIOC_ENUM_FMT:
{ static int
sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_fmtdesc fmtd; struct v4l2_fmtdesc fmtd;
if (copy_from_user(&fmtd, arg, sizeof(fmtd))) if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
...@@ -2041,10 +2135,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2041,10 +2135,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_G_FMT:
{ static int
sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_format format; struct v4l2_format format;
struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format); struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
...@@ -2064,11 +2160,13 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2064,11 +2160,13 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_TRY_FMT:
case VIDIOC_S_FMT: static int
{ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
void __user * arg)
{
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
struct v4l2_format format; struct v4l2_format format;
struct v4l2_pix_format* pix; struct v4l2_pix_format* pix;
...@@ -2095,8 +2193,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2095,8 +2193,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
u32 a, b; u32 a, b;
a = rect.width * rect.height; a = rect.width * rect.height;
b = pix->width * pix->height; b = pix->width * pix->height;
scale = b ? (u8)((a / b) < 4 ? 1 : scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
((a / b) < 16 ? 2 : 4)) : 1;
} }
rect.width = scale * pix->width; rect.width = scale * pix->width;
...@@ -2118,8 +2215,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2118,8 +2215,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
u32 a, b; u32 a, b;
a = rect.width * rect.height; a = rect.width * rect.height;
b = pix->width * pix->height; b = pix->width * pix->height;
scale = b ? (u8)((a / b) < 4 ? 1 : scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
((a / b) < 16 ? 2 : 4)) : 1;
} }
pix->width = rect.width / scale; pix->width = rect.width / scale;
...@@ -2144,8 +2240,8 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2144,8 +2240,8 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
if (cam->module_param.force_munmap) if (cam->module_param.force_munmap)
for (i = 0; i < cam->nbuffers; i++) for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_FMT failed. " DBG(3, "VIDIOC_S_FMT failed. Unmap the "
"Unmap the buffers first.") "buffers first.");
return -EINVAL; return -EINVAL;
} }
...@@ -2171,9 +2267,9 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2171,9 +2267,9 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
if (err) { /* atomic, no rollback in ioctl() */ if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of hardware " DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
"problems. To use the camera, close and open " "use the camera, close and open /dev/video%d again.",
"/dev/video%d again.", cam->v4ldev->minor) cam->v4ldev->minor);
return -EIO; return -EIO;
} }
...@@ -2181,31 +2277,39 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2181,31 +2277,39 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
memcpy(&(s->_rect), &rect, sizeof(rect)); memcpy(&(s->_rect), &rect, sizeof(rect));
if ((cam->module_param.force_munmap || cam->io == IO_READ) && if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
nbuffers != sn9c102_request_buffers(cam, nbuffers, nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
cam->io)) {
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of not enough " DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
"memory. To use the camera, close and open " "use the camera, close and open /dev/video%d again.",
"/dev/video%d again.", cam->v4ldev->minor) cam->v4ldev->minor);
return -ENOMEM; return -ENOMEM;
} }
if (cam->io == IO_READ)
sn9c102_empty_framequeues(cam);
else if (cam->module_param.force_munmap)
sn9c102_requeue_outqueue(cam);
cam->stream = stream; cam->stream = stream;
return 0; return 0;
} }
case VIDIOC_G_JPEGCOMP:
{ static int
sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
{
if (copy_to_user(arg, &cam->compression, if (copy_to_user(arg, &cam->compression,
sizeof(cam->compression))) sizeof(cam->compression)))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_S_JPEGCOMP:
{ static int
sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_jpegcompression jc; struct v4l2_jpegcompression jc;
const enum sn9c102_stream_state stream = cam->stream; const enum sn9c102_stream_state stream = cam->stream;
int err = 0; int err = 0;
...@@ -2225,7 +2329,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2225,7 +2329,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
"problems. To use the camera, close and open " "problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor) "/dev/video%d again.", cam->v4ldev->minor);
return -EIO; return -EIO;
} }
...@@ -2234,10 +2338,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2234,10 +2338,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
cam->stream = stream; cam->stream = stream;
return 0; return 0;
} }
case VIDIOC_REQBUFS:
{ static int
sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_requestbuffers rb; struct v4l2_requestbuffers rb;
u32 i; u32 i;
int err; int err;
...@@ -2250,15 +2356,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2250,15 +2356,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EINVAL; return -EINVAL;
if (cam->io == IO_READ) { if (cam->io == IO_READ) {
DBG(3, "Close and open the device again to choose " DBG(3, "Close and open the device again to choose the mmap "
"the mmap I/O method") "I/O method");
return -EINVAL; return -EINVAL;
} }
for (i = 0; i < cam->nbuffers; i++) for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_REQBUFS failed. " DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are "
"Previous buffers are still mapped.") "still mapped.");
return -EINVAL; return -EINVAL;
} }
...@@ -2270,8 +2376,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2270,8 +2376,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
sn9c102_release_buffers(cam); sn9c102_release_buffers(cam);
if (rb.count) if (rb.count)
rb.count = sn9c102_request_buffers(cam, rb.count, rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP);
IO_MMAP);
if (copy_to_user(arg, &rb, sizeof(rb))) { if (copy_to_user(arg, &rb, sizeof(rb))) {
sn9c102_release_buffers(cam); sn9c102_release_buffers(cam);
...@@ -2282,10 +2387,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2282,10 +2387,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
cam->io = rb.count ? IO_MMAP : IO_NONE; cam->io = rb.count ? IO_MMAP : IO_NONE;
return 0; return 0;
} }
case VIDIOC_QUERYBUF:
{ static int
sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_buffer b; struct v4l2_buffer b;
if (copy_from_user(&b, arg, sizeof(b))) if (copy_from_user(&b, arg, sizeof(b)))
...@@ -2309,10 +2416,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2309,10 +2416,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_QBUF:
{ static int
sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_buffer b; struct v4l2_buffer b;
unsigned long lock_flags; unsigned long lock_flags;
...@@ -2332,13 +2441,16 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2332,13 +2441,16 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags); spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
PDBGG("Frame #%lu queued", (unsigned long)b.index) PDBGG("Frame #%lu queued", (unsigned long)b.index);
return 0; return 0;
} }
case VIDIOC_DQBUF:
{ static int
sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
void __user * arg)
{
struct v4l2_buffer b; struct v4l2_buffer b;
struct sn9c102_frame_t *f; struct sn9c102_frame_t *f;
unsigned long lock_flags; unsigned long lock_flags;
...@@ -2347,7 +2459,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2347,7 +2459,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
if (copy_from_user(&b, arg, sizeof(b))) if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT; return -EFAULT;
if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL; return -EINVAL;
if (list_empty(&cam->outqueue)) { if (list_empty(&cam->outqueue)) {
...@@ -2369,8 +2481,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2369,8 +2481,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
} }
spin_lock_irqsave(&cam->queue_lock, lock_flags); spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame);
frame);
list_del(cam->outqueue.next); list_del(cam->outqueue.next);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags); spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
...@@ -2383,13 +2494,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2383,13 +2494,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
if (copy_to_user(arg, &b, sizeof(b))) if (copy_to_user(arg, &b, sizeof(b)))
return -EFAULT; return -EFAULT;
PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index) PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
return 0; return 0;
} }
case VIDIOC_STREAMON:
{ static int
sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg)
{
int type; int type;
if (copy_from_user(&type, arg, sizeof(type))) if (copy_from_user(&type, arg, sizeof(type)))
...@@ -2403,13 +2516,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2403,13 +2516,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
cam->stream = STREAM_ON; cam->stream = STREAM_ON;
DBG(3, "Stream on") DBG(3, "Stream on");
return 0; return 0;
} }
case VIDIOC_STREAMOFF:
{ static int
sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg)
{
int type, err; int type, err;
if (copy_from_user(&type, arg, sizeof(type))) if (copy_from_user(&type, arg, sizeof(type)))
...@@ -2424,13 +2539,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2424,13 +2539,15 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
sn9c102_empty_framequeues(cam); sn9c102_empty_framequeues(cam);
DBG(3, "Stream off") DBG(3, "Stream off");
return 0; return 0;
} }
case VIDIOC_G_PARM:
{ static int
sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_streamparm sp; struct v4l2_streamparm sp;
if (copy_from_user(&sp, arg, sizeof(sp))) if (copy_from_user(&sp, arg, sizeof(sp)))
...@@ -2446,11 +2563,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2446,11 +2563,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case VIDIOC_S_PARM_OLD:
case VIDIOC_S_PARM: static int
{ sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_streamparm sp; struct v4l2_streamparm sp;
if (copy_from_user(&sp, arg, sizeof(sp))) if (copy_from_user(&sp, arg, sizeof(sp)))
...@@ -2473,7 +2591,86 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, ...@@ -2473,7 +2591,86 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
cam->nreadbuffers = sp.parm.capture.readbuffers; cam->nreadbuffers = sp.parm.capture.readbuffers;
return 0; return 0;
} }
static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
unsigned int cmd, void __user * arg)
{
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
switch (cmd) {
case VIDIOC_QUERYCAP:
return sn9c102_vidioc_querycap(cam, arg);
case VIDIOC_ENUMINPUT:
return sn9c102_vidioc_enuminput(cam, arg);
case VIDIOC_G_INPUT:
case VIDIOC_S_INPUT:
return sn9c102_vidioc_gs_input(cam, arg);
case VIDIOC_QUERYCTRL:
return sn9c102_vidioc_query_ctrl(cam, arg);
case VIDIOC_G_CTRL:
return sn9c102_vidioc_g_ctrl(cam, arg);
case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL:
return sn9c102_vidioc_s_ctrl(cam, arg);
case VIDIOC_CROPCAP_OLD:
case VIDIOC_CROPCAP:
return sn9c102_vidioc_cropcap(cam, arg);
case VIDIOC_G_CROP:
return sn9c102_vidioc_g_crop(cam, arg);
case VIDIOC_S_CROP:
return sn9c102_vidioc_s_crop(cam, arg);
case VIDIOC_ENUM_FMT:
return sn9c102_vidioc_enum_fmt(cam, arg);
case VIDIOC_G_FMT:
return sn9c102_vidioc_g_fmt(cam, arg);
case VIDIOC_TRY_FMT:
case VIDIOC_S_FMT:
return sn9c102_vidioc_try_s_fmt(cam, cmd, arg);
case VIDIOC_G_JPEGCOMP:
return sn9c102_vidioc_g_jpegcomp(cam, arg);
case VIDIOC_S_JPEGCOMP:
return sn9c102_vidioc_s_jpegcomp(cam, arg);
case VIDIOC_REQBUFS:
return sn9c102_vidioc_reqbufs(cam, arg);
case VIDIOC_QUERYBUF:
return sn9c102_vidioc_querybuf(cam, arg);
case VIDIOC_QBUF:
return sn9c102_vidioc_qbuf(cam, arg);
case VIDIOC_DQBUF:
return sn9c102_vidioc_dqbuf(cam, filp, arg);
case VIDIOC_STREAMON:
return sn9c102_vidioc_streamon(cam, arg);
case VIDIOC_STREAMOFF:
return sn9c102_vidioc_streamoff(cam, arg);
case VIDIOC_G_PARM:
return sn9c102_vidioc_g_parm(cam, arg);
case VIDIOC_S_PARM_OLD:
case VIDIOC_S_PARM:
return sn9c102_vidioc_s_parm(cam, arg);
case VIDIOC_G_STD: case VIDIOC_G_STD:
case VIDIOC_S_STD: case VIDIOC_S_STD:
...@@ -2499,13 +2696,14 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp, ...@@ -2499,13 +2696,14 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
return -ERESTARTSYS; return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) { if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present") DBG(1, "Device not present");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENODEV; return -ENODEV;
} }
if (cam->state & DEV_MISCONFIGURED) { if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it again.") DBG(1, "The camera is misconfigured. Close and open it "
"again.");
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -EIO; return -EIO;
} }
...@@ -2517,6 +2715,7 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp, ...@@ -2517,6 +2715,7 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
return err; return err;
} }
/*****************************************************************************/
static struct file_operations sn9c102_fops = { static struct file_operations sn9c102_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -2538,36 +2737,23 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2538,36 +2737,23 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
struct usb_device *udev = interface_to_usbdev(intf); struct usb_device *udev = interface_to_usbdev(intf);
struct sn9c102_device* cam; struct sn9c102_device* cam;
static unsigned int dev_nr = 0; static unsigned int dev_nr = 0;
unsigned int i, n; unsigned int i;
int err = 0, r; int err = 0, r;
n = ARRAY_SIZE(sn9c102_id_table);
for (i = 0; i < n-1; i++)
if (le16_to_cpu(udev->descriptor.idVendor) ==
sn9c102_id_table[i].idVendor &&
le16_to_cpu(udev->descriptor.idProduct) ==
sn9c102_id_table[i].idProduct)
break;
if (i == n-1)
return -ENODEV;
if (!(cam = kmalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) if (!(cam = kmalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
return -ENOMEM; return -ENOMEM;
memset(cam, 0, sizeof(*cam));
cam->usbdev = udev; cam->usbdev = udev;
memcpy(&cam->dev, &udev->dev, sizeof(struct device)); memcpy(&cam->dev, &udev->dev, sizeof(struct device));
if (!(cam->control_buffer = kmalloc(8, GFP_KERNEL))) { if (!(cam->control_buffer = kmalloc(8, GFP_KERNEL))) {
DBG(1, "kmalloc() failed") DBG(1, "kmalloc() failed");
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
memset(cam->control_buffer, 0, 8);
if (!(cam->v4ldev = video_device_alloc())) { if (!(cam->v4ldev = video_device_alloc())) {
DBG(1, "video_device_alloc() failed") DBG(1, "video_device_alloc() failed");
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
...@@ -2577,25 +2763,22 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2577,25 +2763,22 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
r = sn9c102_read_reg(cam, 0x00); r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || r != 0x10) { if (r < 0 || r != 0x10) {
DBG(1, "Sorry, this is not a SN9C10x based camera " DBG(1, "Sorry, this is not a SN9C10x based camera "
"(vid/pid 0x%04X/0x%04X)", "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
err = -ENODEV; err = -ENODEV;
goto fail; goto fail;
} }
cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ? cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ?
BRIDGE_SN9C103 : BRIDGE_SN9C102; BRIDGE_SN9C103 : BRIDGE_SN9C102;
switch (cam->bridge) { switch (cam->bridge) {
case BRIDGE_SN9C101: case BRIDGE_SN9C101:
case BRIDGE_SN9C102: case BRIDGE_SN9C102:
DBG(2, "SN9C10[12] PC Camera Controller detected " DBG(2, "SN9C10[12] PC Camera Controller detected "
"(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
sn9c102_id_table[i].idProduct)
break; break;
case BRIDGE_SN9C103: case BRIDGE_SN9C103:
DBG(2, "SN9C103 PC Camera Controller detected " DBG(2, "SN9C103 PC Camera Controller detected "
"(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
sn9c102_id_table[i].idProduct)
break; break;
} }
...@@ -2606,17 +2789,17 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2606,17 +2789,17 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
} }
if (!err && cam->sensor) { if (!err && cam->sensor) {
DBG(2, "%s image sensor detected", cam->sensor->name) DBG(2, "%s image sensor detected", cam->sensor->name);
DBG(3, "Support for %s maintained by %s", DBG(3, "Support for %s maintained by %s",
cam->sensor->name, cam->sensor->maintainer) cam->sensor->name, cam->sensor->maintainer);
} else { } else {
DBG(1, "No supported image sensor detected") DBG(1, "No supported image sensor detected");
err = -ENODEV; err = -ENODEV;
goto fail; goto fail;
} }
if (sn9c102_init(cam)) { if (sn9c102_init(cam)) {
DBG(1, "Initialization failed. I will retry on open().") DBG(1, "Initialization failed. I will retry on open().");
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
} }
...@@ -2634,23 +2817,23 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2634,23 +2817,23 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
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, "V4L2 device registration failed") DBG(1, "V4L2 device registration failed");
if (err == -ENFILE && video_nr[dev_nr] == -1) if (err == -ENFILE && video_nr[dev_nr] == -1)
DBG(1, "Free /dev/videoX node not found") DBG(1, "Free /dev/videoX node not found");
video_nr[dev_nr] = -1; video_nr[dev_nr] = -1;
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
up(&cam->dev_sem); up(&cam->dev_sem);
goto fail; goto fail;
} }
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor) DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
cam->module_param.force_munmap = force_munmap[dev_nr]; cam->module_param.force_munmap = force_munmap[dev_nr];
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
sn9c102_create_sysfs(cam); sn9c102_create_sysfs(cam);
DBG(2, "Optional device control through 'sysfs' interface ready") DBG(2, "Optional device control through 'sysfs' interface ready");
usb_set_intfdata(intf, cam); usb_set_intfdata(intf, cam);
...@@ -2680,14 +2863,14 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf) ...@@ -2680,14 +2863,14 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf)
down(&cam->dev_sem); down(&cam->dev_sem);
DBG(2, "Disconnecting %s...", cam->v4ldev->name) DBG(2, "Disconnecting %s...", cam->v4ldev->name);
wake_up_interruptible_all(&cam->open); wake_up_interruptible_all(&cam->open);
if (cam->users) { if (cam->users) {
DBG(2, "Device /dev/video%d is open! Deregistration and " DBG(2, "Device /dev/video%d is open! Deregistration and "
"memory deallocation are deferred on close.", "memory deallocation are deferred on close.",
cam->v4ldev->minor) cam->v4ldev->minor);
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
sn9c102_stop_transfer(cam); sn9c102_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED; cam->state |= DEV_DISCONNECTED;
...@@ -2720,11 +2903,11 @@ static int __init sn9c102_module_init(void) ...@@ -2720,11 +2903,11 @@ static int __init sn9c102_module_init(void)
{ {
int err = 0; int err = 0;
KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION) KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION);
KDBG(3, SN9C102_MODULE_AUTHOR) KDBG(3, SN9C102_MODULE_AUTHOR);
if ((err = usb_register(&sn9c102_usb_driver))) if ((err = usb_register(&sn9c102_usb_driver)))
KDBG(1, "usb_register() failed") KDBG(1, "usb_register() failed");
return err; return err;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera * * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera * * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera * * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2005-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
...@@ -375,8 +375,10 @@ int sn9c102_probe_ov7630(struct sn9c102_device* cam) ...@@ -375,8 +375,10 @@ int sn9c102_probe_ov7630(struct sn9c102_device* cam)
sn9c102_attach_sensor(cam, &ov7630); sn9c102_attach_sensor(cam, &ov7630);
if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f && if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c &&
le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c) le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602d &&
le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f &&
le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x60b0)
return -ENODEV; return -ENODEV;
err += sn9c102_write_reg(cam, 0x01, 0x01); err += sn9c102_write_reg(cam, 0x01, 0x01);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera * * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
......
/*************************************************************************** /***************************************************************************
* API for image sensors connected to the SN9C10x PC Camera Controllers * * API for image sensors connected to the SN9C10x PC Camera Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
...@@ -92,7 +92,18 @@ extern void ...@@ -92,7 +92,18 @@ extern void
sn9c102_attach_sensor(struct sn9c102_device* cam, sn9c102_attach_sensor(struct sn9c102_device* cam,
struct sn9c102_sensor* sensor); struct sn9c102_sensor* sensor);
/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/ /*
Each SN9C10x camera has proper PID/VID identifiers.
SN9C103 supports multiple interfaces, but we only handle the video class
interface.
*/
#define SN9C102_USB_DEVICE(vend, prod, intclass) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS, \
.idVendor = (vend), \
.idProduct = (prod), \
.bInterfaceClass = (intclass)
#define SN9C102_ID_TABLE \ #define SN9C102_ID_TABLE \
static const struct usb_device_id sn9c102_id_table[] = { \ static const struct usb_device_id sn9c102_id_table[] = { \
{ USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
...@@ -107,33 +118,34 @@ static const struct usb_device_id sn9c102_id_table[] = { \ ...@@ -107,33 +118,34 @@ static const struct usb_device_id sn9c102_id_table[] = { \
{ USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \ { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \
{ USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \ { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \
{ USB_DEVICE(0x0c45, 0x602d), }, \ { USB_DEVICE(0x0c45, 0x602d), }, \
{ USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */ \
{ USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \
{ USB_DEVICE(0x0c45, 0x6080), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */ \ { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */ \
{ USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */ \ { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */ \
{ USB_DEVICE(0x0c45, 0x6088), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x608a), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x608b), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */ \ { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131x */ \
{ USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */ \ { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \
{ USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */ \ { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \
{ USB_DEVICE(0x0c45, 0x60a0), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60a2), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60a3), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */ \ { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */ \ { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */ \
{ USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */ \ { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x60ac), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60ae), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */ \ { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */ \
{ USB_DEVICE(0x0c45, 0x60b0), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */ \
{ USB_DEVICE(0x0c45, 0x60b2), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60b3), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60b8), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60ba), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60bb), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60bc), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), }, \
{ USB_DEVICE(0x0c45, 0x60be), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), }, \
{ } \ { } \
}; };
...@@ -177,6 +189,7 @@ extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); ...@@ -177,6 +189,7 @@ extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); 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 */ /* I/O on registers in the bridge. Could be used by the sensor methods too */
extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index);
extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera * * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera * * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 *
......
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