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

[PATCH] USB: Add ET61X[12]51 Video4Linux2 driver

This patch adds a Video4Linux2 driver giving support
to ET61X151 and ET61X251 PC Camera Controllers made by
Etoms Electronics.
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent cd6fcc55
ET61X[12]51 PC Camera Controllers
Driver for Linux
=================================
- Documentation -
Index
=====
1. Copyright
2. Disclaimer
3. License
4. Overview and features
5. Module dependencies
6. Module loading
7. Module parameters
8. Optional device control through "sysfs"
9. Supported devices
10. Notes for V4L2 application developers
11. Contact information
1. Copyright
============
Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>
2. Disclaimer
=============
Etoms is a trademark of Etoms Electronics Corp.
This software is not developed or sponsored by Etoms Electronics.
3. License
==========
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
4. Overview and features
========================
This driver supports the video interface of the devices mounting the ET61X151
or ET61X251 PC Camera Controllers.
It's worth to note that Etoms Electronics has never collaborated with the
author during the development of this project; despite several requests,
Etoms Electronics also refused to release enough detailed specifications of
the video compression engine.
The driver relies on the Video4Linux2 and USB core modules. It has been
designed to run properly on SMP systems as well.
The latest version of the ET61X[12]51 driver can be found at the following URL:
http://www.linux-projects.org/
Some of the features of the driver are:
- full compliance with the Video4Linux2 API (see also "Notes for V4L2
application developers" paragraph);
- available mmap or read/poll methods for video streaming through isochronous
data transfers;
- automatic detection of image sensor;
- support for any window resolutions and optional panning within the maximum
pixel area of image sensor;
- image downscaling with arbitrary scaling factors from 1 and 2 in both
directions (see "Notes for V4L2 application developers" paragraph);
- two different video formats for uncompressed or compressed data in low or
high compression quality (see also "Notes for V4L2 application developers"
paragraph);
- full support for the capabilities of every possible image sensors that can
be connected to the ET61X[12]51 bridges, including, for istance, red, green,
blue and global gain adjustments and exposure control (see "Supported
devices" paragraph for details);
- use of default color settings for sunlight conditions;
- dynamic I/O interface for both ET61X[12]51 and image sensor control (see
"Optional device control through 'sysfs'" paragraph);
- dynamic driver control thanks to various module parameters (see "Module
parameters" paragraph);
- up to 64 cameras can be handled at the same time; they can be connected and
disconnected from the host many times without turning off the computer, if
the system supports hotplugging;
- no known bugs.
5. Module dependencies
======================
For it to work properly, the driver needs kernel support for Video4Linux and
USB.
The following options of the kernel configuration file must be enabled and
corresponding modules must be compiled:
# Multimedia devices
#
CONFIG_VIDEO_DEV=m
To enable advanced debugging functionality on the device through /sysfs:
# Multimedia devices
#
CONFIG_VIDEO_ADV_DEBUG=y
# USB support
#
CONFIG_USB=m
In addition, depending on the hardware being used, the modules below are
necessary:
# USB Host Controller Drivers
#
CONFIG_USB_EHCI_HCD=m
CONFIG_USB_UHCI_HCD=m
CONFIG_USB_OHCI_HCD=m
And finally:
# USB Multimedia devices
#
CONFIG_USB_ET61X251=m
6. Module loading
=================
To use the driver, it is necessary to load the "et61x251" module into memory
after every other module required: "videodev", "usbcore" and, depending on
the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd".
Loading can be done as shown below:
[root@localhost home]# modprobe et61x251
At this point the devices should be recognized. You can invoke "dmesg" to
analyze kernel messages and verify that the loading process has gone well:
[user@localhost home]$ dmesg
7. Module parameters
====================
Module parameters are listed below:
-------------------------------------------------------------------------------
Name: video_nr
Type: short array (min = 0, max = 64)
Syntax: <-1|n[,...]>
Description: Specify V4L2 minor mode number:
-1 = use next available
n = use minor number n
You can specify up to 64 cameras this way.
For example:
video_nr=-1,2,-1 would assign minor number 2 to the second
registered camera and use auto for the first one and for every
other camera.
Default: -1
-------------------------------------------------------------------------------
Name: force_munmap
Type: bool array (min = 0, max = 64)
Syntax: <0|1[,...]>
Description: Force the application to unmap previously mapped buffer memory
before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not
all the applications support this feature. This parameter is
specific for each detected camera.
0 = do not force memory unmapping
1 = force memory unmapping (save memory)
Default: 0
-------------------------------------------------------------------------------
Name: debug
Type: ushort
Syntax: <n>
Description: Debugging information level, from 0 to 3:
0 = none (use carefully)
1 = critical errors
2 = significant informations
3 = more verbose messages
Level 3 is useful for testing only, when only one device
is used at the same time. It also shows some more informations
about the hardware being detected. This module parameter can be
changed at runtime thanks to the /sys filesystem interface.
Default: 2
-------------------------------------------------------------------------------
8. Optional device control through "sysfs"
==========================================
If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled,
it is possible to read and write both the ET61X[12]51 and the image sensor
registers by using the "sysfs" filesystem interface.
There are four files in the /sys/class/video4linux/videoX directory for each
registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files
control the ET61X[12]51 bridge, while the other two control the sensor chip.
"reg" and "i2c_reg" hold the values of the current register index where the
following reading/writing operations are addressed at through "val" and
"i2c_val". Their use is not intended for end-users, unless you know what you
are doing. Remember that you must be logged in as root before writing to them.
As an example, suppose we were to want to read the value contained in the
register number 1 of the sensor register table - which is usually the product
identifier - of the camera registered as "/dev/video0":
[root@localhost #] cd /sys/class/video4linux/video0
[root@localhost #] echo 1 > i2c_reg
[root@localhost #] cat i2c_val
Note that if the sensor registers can not be read, "cat" will fail.
To avoid race conditions, all the I/O accesses to the files are serialized.
9. Supported devices
====================
None of the names of the companies as well as their products will be mentioned
here. They have never collaborated with the author, so no advertising.
From the point of view of a driver, what unambiguously identify a device are
its vendor and product USB identifiers. Below is a list of known identifiers of
devices mounting the ET61X[12]51 PC camera controllers:
Vendor ID Product ID
--------- ----------
0x102c 0x6151
0x102c 0x6251
0x102c 0x6253
0x102c 0x6254
0x102c 0x6255
0x102c 0x6256
0x102c 0x6257
0x102c 0x6258
0x102c 0x6259
0x102c 0x625a
0x102c 0x625b
0x102c 0x625c
0x102c 0x625d
0x102c 0x625e
0x102c 0x625f
0x102c 0x6260
0x102c 0x6261
0x102c 0x6262
0x102c 0x6263
0x102c 0x6264
0x102c 0x6265
0x102c 0x6266
0x102c 0x6267
0x102c 0x6268
0x102c 0x6269
The following image sensors are supported:
Model Manufacturer
----- ------------
TAS5130D1B Taiwan Advanced Sensor Corporation
All the available control settings of each image sensor are supported through
the V4L2 interface.
10. Notes for V4L2 application developers
========================================
This driver follows the V4L2 API specifications. In particular, it enforces two
rules:
- exactly one I/O method, either "mmap" or "read", is associated with each
file descriptor. Once it is selected, the application must close and reopen the
device to switch to the other I/O method;
- although it is not mandatory, previously mapped buffer memory should always
be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's.
The same number of buffers as before will be allocated again to match the size
of the new video frames, so you have to map the buffers again before any I/O
attempts on them.
Consistently with the hardware limits, this driver also supports image
downscaling with arbitrary scaling factors from 1 and 2 in both directions.
However, the V4L2 API specifications don't correctly define how the scaling
factor can be chosen arbitrarily by the "negotiation" of the "source" and
"target" rectangles. To work around this flaw, we have added the convention
that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the
scaling factor is restored to 1.
This driver supports two different video formats: the first one is the "8-bit
Sequential Bayer" format and can be used to obtain uncompressed video data
from the device through the current I/O method, while the second one provides
"raw" compressed video data (without frame headers not related to the
compressed data). The current compression quality may vary from 0 to 1 and can
be selected or queried thanks to the VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP
V4L2 ioctl's.
11. Contact information
=======================
The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is
'FCE635A4'; the public 1024-bit key should be available at any keyserver;
the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
...@@ -2673,6 +2673,14 @@ M: dbrownell@users.sourceforge.net ...@@ -2673,6 +2673,14 @@ M: dbrownell@users.sourceforge.net
L: linux-usb-devel@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net
S: Maintained S: Maintained
USB ET61X[12]51 DRIVER
P: Luca Risolia
M: luca.risolia@studio.unibo.it
L: linux-usb-devel@lists.sourceforge.net
L: video4linux-list@redhat.com
W: http://www.linux-projects.org
S: Maintained
USB HID/HIDBP DRIVERS USB HID/HIDBP DRIVERS
P: Vojtech Pavlik P: Vojtech Pavlik
M: vojtech@suse.cz M: vojtech@suse.cz
...@@ -2836,6 +2844,7 @@ USB SN9C10x DRIVER ...@@ -2836,6 +2844,7 @@ USB SN9C10x DRIVER
P: Luca Risolia P: Luca Risolia
M: luca.risolia@studio.unibo.it M: luca.risolia@studio.unibo.it
L: linux-usb-devel@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net
L: video4linux-list@redhat.com
W: http://www.linux-projects.org W: http://www.linux-projects.org
S: Maintained S: Maintained
...@@ -2865,6 +2874,7 @@ USB W996[87]CF DRIVER ...@@ -2865,6 +2874,7 @@ USB W996[87]CF DRIVER
P: Luca Risolia P: Luca Risolia
M: luca.risolia@studio.unibo.it M: luca.risolia@studio.unibo.it
L: linux-usb-devel@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net
L: video4linux-list@redhat.com
W: http://www.linux-projects.org W: http://www.linux-projects.org
S: Maintained S: Maintained
......
...@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_XPAD) += input/ ...@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_XPAD) += input/
obj-$(CONFIG_USB_DABUSB) += media/ obj-$(CONFIG_USB_DABUSB) += media/
obj-$(CONFIG_USB_DSBR) += media/ obj-$(CONFIG_USB_DSBR) += media/
obj-$(CONFIG_USB_ET61X251) += media/
obj-$(CONFIG_USB_IBMCAM) += media/ obj-$(CONFIG_USB_IBMCAM) += media/
obj-$(CONFIG_USB_KONICAWC) += media/ obj-$(CONFIG_USB_KONICAWC) += media/
obj-$(CONFIG_USB_OV511) += media/ obj-$(CONFIG_USB_OV511) += media/
......
...@@ -53,6 +53,21 @@ config USB_DSBR ...@@ -53,6 +53,21 @@ config USB_DSBR
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called dsbr100. module will be called dsbr100.
config USB_ET61X251
tristate "USB ET61X[12]51 PC Camera Controller support"
depends on USB && VIDEO_DEV
---help---
Say Y here if you want support for cameras based on Etoms ET61X151
or ET61X251 PC Camera Controllers.
See <file:Documentation/usb/et61x251.txt> for more informations.
This driver uses the Video For Linux API. You must say Y or M to
"Video For Linux" to use this driver.
To compile this driver as a module, choose M here: the
module will be called et61x251.
config USB_IBMCAM config USB_IBMCAM
tristate "USB IBM (Xirlink) C-it Camera support" tristate "USB IBM (Xirlink) C-it Camera support"
depends on USB && VIDEO_DEV depends on USB && VIDEO_DEV
...@@ -209,5 +224,3 @@ config USB_PWC ...@@ -209,5 +224,3 @@ config USB_PWC
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called pwc. module will be called pwc.
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
# #
sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o
et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_DABUSB) += dabusb.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_USB_ET61X251) += et61x251.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o
obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o
obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_OV511) += ov511.o
......
/***************************************************************************
* V4L2 driver for ET61X[12]51 PC Camera Controllers *
* *
* Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#ifndef _ET61X251_H_
#define _ET61X251_H_
#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/param.h>
#include <linux/rwsem.h>
#include <asm/semaphore.h>
#include "et61x251_sensor.h"
/*****************************************************************************/
#define ET61X251_DEBUG
#define ET61X251_DEBUG_LEVEL 2
#define ET61X251_MAX_DEVICES 64
#define ET61X251_PRESERVE_IMGSCALE 0
#define ET61X251_FORCE_MUNMAP 0
#define ET61X251_MAX_FRAMES 32
#define ET61X251_COMPRESSION_QUALITY 0
#define ET61X251_URBS 2
#define ET61X251_ISO_PACKETS 7
#define ET61X251_ALTERNATE_SETTING 13
#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS)
#define ET61X251_CTRL_TIMEOUT 100
/*****************************************************************************/
static const struct usb_device_id et61x251_id_table[] = {
{ USB_DEVICE(0x102c, 0x6151), },
{ USB_DEVICE(0x102c, 0x6251), },
{ USB_DEVICE(0x102c, 0x6253), },
{ USB_DEVICE(0x102c, 0x6254), },
{ USB_DEVICE(0x102c, 0x6255), },
{ USB_DEVICE(0x102c, 0x6256), },
{ USB_DEVICE(0x102c, 0x6257), },
{ USB_DEVICE(0x102c, 0x6258), },
{ USB_DEVICE(0x102c, 0x6259), },
{ USB_DEVICE(0x102c, 0x625a), },
{ USB_DEVICE(0x102c, 0x625b), },
{ USB_DEVICE(0x102c, 0x625c), },
{ USB_DEVICE(0x102c, 0x625d), },
{ USB_DEVICE(0x102c, 0x625e), },
{ USB_DEVICE(0x102c, 0x625f), },
{ USB_DEVICE(0x102c, 0x6260), },
{ USB_DEVICE(0x102c, 0x6261), },
{ USB_DEVICE(0x102c, 0x6262), },
{ USB_DEVICE(0x102c, 0x6263), },
{ USB_DEVICE(0x102c, 0x6264), },
{ USB_DEVICE(0x102c, 0x6265), },
{ USB_DEVICE(0x102c, 0x6266), },
{ USB_DEVICE(0x102c, 0x6267), },
{ USB_DEVICE(0x102c, 0x6268), },
{ USB_DEVICE(0x102c, 0x6269), },
{ }
};
ET61X251_SENSOR_TABLE
/*****************************************************************************/
enum et61x251_frame_state {
F_UNUSED,
F_QUEUED,
F_GRABBING,
F_DONE,
F_ERROR,
};
struct et61x251_frame_t {
void* bufmem;
struct v4l2_buffer buf;
enum et61x251_frame_state state;
struct list_head frame;
unsigned long vma_use_count;
};
enum et61x251_dev_state {
DEV_INITIALIZED = 0x01,
DEV_DISCONNECTED = 0x02,
DEV_MISCONFIGURED = 0x04,
};
enum et61x251_io_method {
IO_NONE,
IO_READ,
IO_MMAP,
};
enum et61x251_stream_state {
STREAM_OFF,
STREAM_INTERRUPT,
STREAM_ON,
};
struct et61x251_sysfs_attr {
u8 reg, i2c_reg;
};
struct et61x251_module_param {
u8 force_munmap;
};
static DECLARE_MUTEX(et61x251_sysfs_lock);
static DECLARE_RWSEM(et61x251_disconnect);
struct et61x251_device {
struct video_device* v4ldev;
struct et61x251_sensor* sensor;
struct usb_device* usbdev;
struct urb* urb[ET61X251_URBS];
void* transfer_buffer[ET61X251_URBS];
u8* control_buffer;
struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES];
struct list_head inqueue, outqueue;
u32 frame_count, nbuffers, nreadbuffers;
enum et61x251_io_method io;
enum et61x251_stream_state stream;
struct v4l2_jpegcompression compression;
struct et61x251_sysfs_attr sysfs;
struct et61x251_module_param module_param;
enum et61x251_dev_state state;
u8 users;
struct semaphore dev_sem, fileop_sem;
spinlock_t queue_lock;
wait_queue_head_t open, wait_frame, wait_stream;
};
/*****************************************************************************/
void
et61x251_attach_sensor(struct et61x251_device* cam,
struct et61x251_sensor* sensor)
{
cam->sensor = sensor;
cam->sensor->usbdev = cam->usbdev;
}
/*****************************************************************************/
#undef DBG
#undef KDBG
#ifdef ET61X251_DEBUG
# define DBG(level, fmt, args...) \
do { \
if (debug >= (level)) { \
if ((level) == 1) \
dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
else if ((level) == 2) \
dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
else if ((level) >= 3) \
dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
__FUNCTION__, __LINE__ , ## args); \
} \
} while (0)
# define KDBG(level, fmt, args...) \
do { \
if (debug >= (level)) { \
if ((level) == 1 || (level) == 2) \
pr_info("et61x251: " fmt "\n", ## args); \
else if ((level) == 3) \
pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \
__LINE__ , ## args); \
} \
} while (0)
# define V4LDBG(level, name, cmd) \
do { \
if (debug >= (level)) \
v4l_print_ioctl(name, cmd); \
} while (0)
#else
# define DBG(level, fmt, args...) do {;} while(0)
# define KDBG(level, fmt, args...) do {;} while(0)
# define V4LDBG(level, name, cmd) do {;} while(0)
#endif
#undef PDBG
#define PDBG(fmt, args...) \
dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
#undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
#endif /* _ET61X251_H_ */
/***************************************************************************
* V4L2 driver for ET61X[12]51 PC Camera Controllers *
* *
* Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/stddef.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <linux/byteorder/generic.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include "et61x251.h"
/*****************************************************************************/
#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \
"PC Camera Controllers"
#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia"
#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define ET61X251_MODULE_LICENSE "GPL"
#define ET61X251_MODULE_VERSION "1:1.01"
#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1)
/*****************************************************************************/
MODULE_DEVICE_TABLE(usb, et61x251_id_table);
MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL);
MODULE_DESCRIPTION(ET61X251_MODULE_NAME);
MODULE_VERSION(ET61X251_MODULE_VERSION);
MODULE_LICENSE(ET61X251_MODULE_LICENSE);
static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1};
module_param_array(video_nr, short, NULL, 0444);
MODULE_PARM_DESC(video_nr,
"\n<-1|n[,...]> Specify V4L2 minor mode number."
"\n -1 = use next available (default)"
"\n n = use minor number n (integer >= 0)"
"\nYou can specify up to "
__MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way."
"\nFor example:"
"\nvideo_nr=-1,2,-1 would assign minor number 2 to"
"\nthe second registered camera and use auto for the first"
"\none and for every other camera."
"\n");
static short force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] =
ET61X251_FORCE_MUNMAP};
module_param_array(force_munmap, bool, NULL, 0444);
MODULE_PARM_DESC(force_munmap,
"\n<0|1[,...]> Force the application to unmap previously"
"\nmapped buffer memory before calling any VIDIOC_S_CROP or"
"\nVIDIOC_S_FMT ioctl's. Not all the applications support"
"\nthis feature. This parameter is specific for each"
"\ndetected camera."
"\n 0 = do not force memory unmapping"
"\n 1 = force memory unmapping (save memory)"
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
"\n");
#ifdef ET61X251_DEBUG
static unsigned short debug = ET61X251_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
MODULE_PARM_DESC(debug,
"\n<n> Debugging information level, from 0 to 3:"
"\n0 = none (use carefully)"
"\n1 = critical errors"
"\n2 = significant informations"
"\n3 = more verbose messages"
"\nLevel 3 is useful for testing only, when only "
"one device is used."
"\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"."
"\n");
#endif
/*****************************************************************************/
static u32
et61x251_request_buffers(struct et61x251_device* cam, u32 count,
enum et61x251_io_method io)
{
struct v4l2_pix_format* p = &(cam->sensor->pix_format);
struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
const size_t imagesize = cam->module_param.force_munmap ||
io == IO_READ ?
(p->width * p->height * p->priv) / 8 :
(r->width * r->height * p->priv) / 8;
void* buff = NULL;
u32 i;
if (count > ET61X251_MAX_FRAMES)
count = ET61X251_MAX_FRAMES;
cam->nbuffers = count;
while (cam->nbuffers > 0) {
if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.index = i;
cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.length = imagesize;
cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->frame[i].buf.sequence = 0;
cam->frame[i].buf.field = V4L2_FIELD_NONE;
cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
cam->frame[i].buf.flags = 0;
}
return cam->nbuffers;
}
static void et61x251_release_buffers(struct et61x251_device* cam)
{
if (cam->nbuffers) {
vfree(cam->frame[0].bufmem);
cam->nbuffers = 0;
}
cam->frame_current = NULL;
}
static void et61x251_empty_framequeues(struct et61x251_device* cam)
{
u32 i;
INIT_LIST_HEAD(&cam->inqueue);
INIT_LIST_HEAD(&cam->outqueue);
for (i = 0; i < ET61X251_MAX_FRAMES; i++) {
cam->frame[i].state = F_UNUSED;
cam->frame[i].buf.bytesused = 0;
}
}
static void et61x251_requeue_outqueue(struct et61x251_device* cam)
{
struct et61x251_frame_t *i;
list_for_each_entry(i, &cam->outqueue, frame) {
i->state = F_QUEUED;
list_add(&i->frame, &cam->inqueue);
}
INIT_LIST_HEAD(&cam->outqueue);
}
static void et61x251_queue_unusedframes(struct et61x251_device* cam)
{
unsigned long lock_flags;
u32 i;
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].state == F_UNUSED) {
cam->frame[i].state = F_QUEUED;
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_add_tail(&cam->frame[i].frame, &cam->inqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
}
}
/*****************************************************************************/
int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index)
{
struct usb_device* udev = cam->usbdev;
u8* buff = cam->control_buffer;
int res;
*buff = value;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
if (res < 0) {
DBG(3, "Failed to write a register (value 0x%02X, index "
"0x%02X, error %d)", value, index, res);
return -1;
}
return 0;
}
int et61x251_read_reg(struct et61x251_device* cam, u16 index)
{
struct usb_device* udev = cam->usbdev;
u8* buff = cam->control_buffer;
int res;
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
if (res < 0)
DBG(3, "Failed to read a register (index 0x%02X, error %d)",
index, res);
return (res >= 0) ? (int)(*buff) : -1;
}
static int
et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor)
{
int i, r;
for (i = 1; i <= 8; i++) {
if (sensor->interface == ET61X251_I2C_3WIRES) {
r = et61x251_read_reg(cam, 0x8e);
if (!(r & 0x02) && (r >= 0))
return 0;
} else {
r = et61x251_read_reg(cam, 0x8b);
if (!(r & 0x01) && (r >= 0))
return 0;
}
if (r < 0)
return -EIO;
udelay(8*8); /* minimum for sensors at 400kHz */
}
return -EBUSY;
}
int
et61x251_i2c_try_read(struct et61x251_device* cam,
struct et61x251_sensor* sensor, u8 address)
{
struct usb_device* udev = cam->usbdev;
u8* data = cam->control_buffer;
int err = 0, res;
data[0] = address;
data[1] = cam->sensor->i2c_slave_id;
data[2] = cam->sensor->rsta | 0x10;
data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
err += et61x251_i2c_wait(cam, sensor);
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
if (err)
DBG(3, "I2C read failed for %s image sensor", sensor->name);
PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
return err ? -1 : (int)data[0];
}
int
et61x251_i2c_try_write(struct et61x251_device* cam,
struct et61x251_sensor* sensor, u8 address, u8 value)
{
struct usb_device* udev = cam->usbdev;
u8* data = cam->control_buffer;
int err = 0, res;
data[0] = address;
data[1] = cam->sensor->i2c_slave_id;
data[2] = cam->sensor->rsta | 0x12;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
data[0] = value;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
err += et61x251_i2c_wait(cam, sensor);
if (err)
DBG(3, "I2C write failed for %s image sensor", sensor->name);
PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
return err ? -1 : 0;
}
int
et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
u8 data3, u8 data4, u8 data5, u8 data6, u8 data7,
u8 data8, u8 address)
{
struct usb_device* udev = cam->usbdev;
u8* data = cam->control_buffer;
int err = 0, res;
if (!cam->sensor)
return -1;
data[0] = data2;
data[1] = data3;
data[2] = data4;
data[3] = data5;
data[4] = data6;
data[5] = data7;
data[6] = data8;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
data[0] = address;
data[1] = cam->sensor->i2c_slave_id;
data[2] = cam->sensor->rsta | 0x02 | (n << 4);
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
/* Start writing through the serial interface */
data[0] = data1;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
if (res < 0)
err += res;
err += et61x251_i2c_wait(cam, cam->sensor);
if (err)
DBG(3, "I2C raw write failed for %s image sensor",
cam->sensor->name);
PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, "
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X,"
" data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address,
data1, data2, data3, data4, data5, data6, data7, data8);
return err ? -1 : 0;
}
int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
{
if (!cam->sensor)
return -1;
return et61x251_i2c_try_read(cam, cam->sensor, address);
}
int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value)
{
if (!cam->sensor)
return -1;
return et61x251_i2c_try_write(cam, cam->sensor, address, value);
}
/*****************************************************************************/
static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs)
{
struct et61x251_device* cam = urb->context;
struct et61x251_frame_t** f;
size_t imagesize;
u8 i;
int err = 0;
if (urb->status == -ENOENT)
return;
f = &cam->frame_current;
if (cam->stream == STREAM_INTERRUPT) {
cam->stream = STREAM_OFF;
if ((*f))
(*f)->state = F_QUEUED;
DBG(3, "Stream interrupted");
wake_up_interruptible(&cam->wait_stream);
}
if (cam->state & DEV_DISCONNECTED)
return;
if (cam->state & DEV_MISCONFIGURED) {
wake_up_interruptible(&cam->wait_frame);
return;
}
if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
goto resubmit_urb;
if (!(*f))
(*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t,
frame);
imagesize = (cam->sensor->pix_format.width *
cam->sensor->pix_format.height *
cam->sensor->pix_format.priv) / 8;
for (i = 0; i < urb->number_of_packets; i++) {
unsigned int len, status;
void *pos;
u8* b1, * b2, sof;
const u8 VOID_BYTES = 6;
size_t imglen;
len = urb->iso_frame_desc[i].actual_length;
status = urb->iso_frame_desc[i].status;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
if (status) {
DBG(3, "Error in isochronous frame");
(*f)->state = F_ERROR;
continue;
}
b1 = pos++;
b2 = pos++;
sof = ((*b1 & 0x3f) == 63);
imglen = ((*b1 & 0xc0) << 2) | *b2;
PDBGG("Isochrnous frame: length %u, #%u i, image length %zu",
len, i, imglen);
if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
start_of_frame:
if (sof) {
(*f)->state = F_GRABBING;
(*f)->buf.bytesused = 0;
do_gettimeofday(&(*f)->buf.timestamp);
pos += 22;
DBG(3, "SOF detected: new video frame");
}
if ((*f)->state == F_GRABBING) {
if (sof && (*f)->buf.bytesused) {
if (cam->sensor->pix_format.pixelformat ==
V4L2_PIX_FMT_ET61X251)
goto end_of_frame;
else {
DBG(3, "Not expected SOF detected "
"after %lu bytes",
(unsigned long)(*f)->buf.bytesused);
(*f)->state = F_ERROR;
continue;
}
}
if ((*f)->buf.bytesused + imglen > imagesize) {
DBG(3, "Video frame size exceeded");
(*f)->state = F_ERROR;
continue;
}
pos += VOID_BYTES;
memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen);
(*f)->buf.bytesused += imglen;
if ((*f)->buf.bytesused == imagesize) {
u32 b;
end_of_frame:
b = (*f)->buf.bytesused;
(*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count;
spin_lock(&cam->queue_lock);
list_move_tail(&(*f)->frame, &cam->outqueue);
if (!list_empty(&cam->inqueue))
(*f) = list_entry(cam->inqueue.next,
struct et61x251_frame_t,
frame);
else
(*f) = NULL;
spin_unlock(&cam->queue_lock);
DBG(3, "Video frame captured: : %lu bytes",
(unsigned long)(b));
if (!(*f))
goto resubmit_urb;
if (sof &&
cam->sensor->pix_format.pixelformat ==
V4L2_PIX_FMT_ET61X251)
goto start_of_frame;
}
}
}
resubmit_urb:
urb->dev = cam->usbdev;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0 && err != -EPERM) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "usb_submit_urb() failed");
}
wake_up_interruptible(&cam->wait_frame);
}
static int et61x251_start_transfer(struct et61x251_device* cam)
{
struct usb_device *udev = cam->usbdev;
struct urb* urb;
const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832,
864, 896, 920, 956, 980, 1000,
1022};
const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING];
s8 i, j;
int err = 0;
for (i = 0; i < ET61X251_URBS; i++) {
cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz,
GFP_KERNEL);
if (!cam->transfer_buffer[i]) {
err = -ENOMEM;
DBG(1, "Not enough memory");
goto free_buffers;
}
}
for (i = 0; i < ET61X251_URBS; i++) {
urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL);
cam->urb[i] = urb;
if (!urb) {
err = -ENOMEM;
DBG(1, "usb_alloc_urb() failed");
goto free_urbs;
}
urb->dev = udev;
urb->context = cam;
urb->pipe = usb_rcvisocpipe(udev, 1);
urb->transfer_flags = URB_ISO_ASAP;
urb->number_of_packets = ET61X251_ISO_PACKETS;
urb->complete = et61x251_urb_complete;
urb->transfer_buffer = cam->transfer_buffer[i];
urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS;
urb->interval = 1;
for (j = 0; j < ET61X251_ISO_PACKETS; j++) {
urb->iso_frame_desc[j].offset = psz * j;
urb->iso_frame_desc[j].length = psz;
}
}
err = et61x251_write_reg(cam, 0x01, 0x03);
err = et61x251_write_reg(cam, 0x00, 0x03);
err = et61x251_write_reg(cam, 0x08, 0x03);
if (err) {
err = -EIO;
DBG(1, "I/O hardware error");
goto free_urbs;
}
err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING);
if (err) {
DBG(1, "usb_set_interface() failed");
goto free_urbs;
}
cam->frame_current = NULL;
for (i = 0; i < ET61X251_URBS; i++) {
err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
if (err) {
for (j = i-1; j >= 0; j--)
usb_kill_urb(cam->urb[j]);
DBG(1, "usb_submit_urb() failed, error %d", err);
goto free_urbs;
}
}
return 0;
free_urbs:
for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++)
usb_free_urb(cam->urb[i]);
free_buffers:
for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++)
kfree(cam->transfer_buffer[i]);
return err;
}
static int et61x251_stop_transfer(struct et61x251_device* cam)
{
struct usb_device *udev = cam->usbdev;
s8 i;
int err = 0;
if (cam->state & DEV_DISCONNECTED)
return 0;
for (i = ET61X251_URBS-1; i >= 0; i--) {
usb_kill_urb(cam->urb[i]);
usb_free_urb(cam->urb[i]);
kfree(cam->transfer_buffer[i]);
}
err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
if (err)
DBG(3, "usb_set_interface() failed");
return err;
}
static int et61x251_stream_interrupt(struct et61x251_device* cam)
{
int err = 0;
cam->stream = STREAM_INTERRUPT;
err = wait_event_timeout(cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED),
ET61X251_URB_TIMEOUT);
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
else if (err) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "URB timeout reached. The camera is misconfigured. To "
"use it, close and open /dev/video%d again.",
cam->v4ldev->minor);
return err;
}
return 0;
}
/*****************************************************************************/
#ifdef CONFIG_VIDEO_ADV_DEBUG
static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
{
char str[5];
char* endp;
unsigned long val;
if (len < 4) {
strncpy(str, buff, len);
str[len+1] = '\0';
} else {
strncpy(str, buff, 4);
str[4] = '\0';
}
val = simple_strtoul(str, &endp, 0);
*count = 0;
if (val <= 0xff)
*count = (ssize_t)(endp - str);
if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
*count += 1;
return (u8)val;
}
/*
NOTE 1: being inside one of the following methods implies that the v4l
device exists for sure (see kobjects and reference counters)
NOTE 2: buffers are PAGE_SIZE long
*/
static ssize_t et61x251_show_reg(struct class_device* cd, char* buf)
{
struct et61x251_device* cam;
ssize_t count;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
count = sprintf(buf, "%u\n", cam->sysfs.reg);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t
et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
{
struct et61x251_device* cam;
u8 index;
ssize_t count;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
index = et61x251_strtou8(buf, len, &count);
if (index > 0x8e || !count) {
up(&et61x251_sysfs_lock);
return -EINVAL;
}
cam->sysfs.reg = index;
DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg);
DBG(3, "Written bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
{
struct et61x251_device* cam;
ssize_t count;
int val;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) {
up(&et61x251_sysfs_lock);
return -EIO;
}
count = sprintf(buf, "%d\n", val);
DBG(3, "Read bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t
et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
{
struct et61x251_device* cam;
u8 value;
ssize_t count;
int err;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
value = et61x251_strtou8(buf, len, &count);
if (!count) {
up(&et61x251_sysfs_lock);
return -EINVAL;
}
err = et61x251_write_reg(cam, value, cam->sysfs.reg);
if (err) {
up(&et61x251_sysfs_lock);
return -EIO;
}
DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X",
cam->sysfs.reg, value);
DBG(3, "Written bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
{
struct et61x251_device* cam;
ssize_t count;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
DBG(3, "Read bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t
et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
{
struct et61x251_device* cam;
u8 index;
ssize_t count;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
index = et61x251_strtou8(buf, len, &count);
if (!count) {
up(&et61x251_sysfs_lock);
return -EINVAL;
}
cam->sysfs.i2c_reg = index;
DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
DBG(3, "Written bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
{
struct et61x251_device* cam;
ssize_t count;
int val;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) {
up(&et61x251_sysfs_lock);
return -ENOSYS;
}
if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
up(&et61x251_sysfs_lock);
return -EIO;
}
count = sprintf(buf, "%d\n", val);
DBG(3, "Read bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static ssize_t
et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
{
struct et61x251_device* cam;
u8 value;
ssize_t count;
int err;
if (down_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&et61x251_sysfs_lock);
return -ENODEV;
}
if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) {
up(&et61x251_sysfs_lock);
return -ENOSYS;
}
value = et61x251_strtou8(buf, len, &count);
if (!count) {
up(&et61x251_sysfs_lock);
return -EINVAL;
}
err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value);
if (err) {
up(&et61x251_sysfs_lock);
return -EIO;
}
DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
cam->sysfs.i2c_reg, value);
DBG(3, "Written bytes: %zd", count);
up(&et61x251_sysfs_lock);
return count;
}
static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
et61x251_show_reg, et61x251_store_reg);
static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
et61x251_show_val, et61x251_store_val);
static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
et61x251_show_i2c_reg, et61x251_store_i2c_reg);
static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
et61x251_show_i2c_val, et61x251_store_i2c_val);
static void et61x251_create_sysfs(struct et61x251_device* cam)
{
struct video_device *v4ldev = cam->v4ldev;
video_device_create_file(v4ldev, &class_device_attr_reg);
video_device_create_file(v4ldev, &class_device_attr_val);
if (cam->sensor && cam->sensor->sysfs_ops) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
}
#endif /* CONFIG_VIDEO_ADV_DEBUG */
/*****************************************************************************/
static int
et61x251_set_pix_format(struct et61x251_device* cam,
struct v4l2_pix_format* pix)
{
int r, err = 0;
if ((r = et61x251_read_reg(cam, 0x12)) < 0)
err += r;
if (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
err += et61x251_write_reg(cam, r & 0xfd, 0x12);
else
err += et61x251_write_reg(cam, r | 0x02, 0x12);
return err ? -EIO : 0;
}
static int
et61x251_set_compression(struct et61x251_device* cam,
struct v4l2_jpegcompression* compression)
{
int r, err = 0;
if ((r = et61x251_read_reg(cam, 0x12)) < 0)
err += r;
if (compression->quality == 0)
err += et61x251_write_reg(cam, r & 0xfb, 0x12);
else
err += et61x251_write_reg(cam, r | 0x04, 0x12);
return err ? -EIO : 0;
}
static int et61x251_set_scale(struct et61x251_device* cam, u8 scale)
{
int r = 0, err = 0;
r = et61x251_read_reg(cam, 0x12);
if (r < 0)
err += r;
if (scale == 1)
err += et61x251_write_reg(cam, r & ~0x01, 0x12);
else if (scale == 2)
err += et61x251_write_reg(cam, r | 0x01, 0x12);
if (err)
return -EIO;
PDBGG("Scaling factor: %u", scale);
return 0;
}
static int
et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
{
struct et61x251_sensor* s = cam->sensor;
u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left +
s->active_pixel.left),
fmw_sy = (u16)(rect->top - s->cropcap.bounds.top +
s->active_pixel.top),
fmw_length = (u16)(rect->width),
fmw_height = (u16)(rect->height);
int err = 0;
err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69);
err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a);
err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b);
err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c);
err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6)
| ((fmw_length & 0x300) >> 4)
| ((fmw_height & 0x300) >> 2), 0x6d);
if (err)
return -EIO;
PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u",
fmw_sx, fmw_sy, fmw_length, fmw_height);
return 0;
}
static int et61x251_init(struct et61x251_device* cam)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_control ctrl;
struct v4l2_queryctrl *qctrl;
struct v4l2_rect* rect;
u8 i = 0;
int err = 0;
if (!(cam->state & DEV_INITIALIZED)) {
init_waitqueue_head(&cam->open);
qctrl = s->qctrl;
rect = &(s->cropcap.defrect);
cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
} else { /* use current values */
qctrl = s->_qctrl;
rect = &(s->_rect);
}
err += et61x251_set_scale(cam, rect->width / s->pix_format.width);
err += et61x251_set_crop(cam, rect);
if (err)
return err;
if (s->init) {
err = s->init(cam);
if (err) {
DBG(3, "Sensor initialization failed");
return err;
}
}
err += et61x251_set_compression(cam, &cam->compression);
err += et61x251_set_pix_format(cam, &s->pix_format);
if (s->set_pix_format)
err += s->set_pix_format(cam, &s->pix_format);
if (err)
return err;
if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251)
DBG(3, "Compressed video format is active, quality %d",
cam->compression.quality);
else
DBG(3, "Uncompressed video format is active");
if (s->set_crop)
if ((err = s->set_crop(cam, rect))) {
DBG(3, "set_crop() failed");
return err;
}
if (s->set_ctrl) {
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (s->qctrl[i].id != 0 &&
!(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
ctrl.id = s->qctrl[i].id;
ctrl.value = qctrl[i].default_value;
err = s->set_ctrl(cam, &ctrl);
if (err) {
DBG(3, "Set %s control failed",
s->qctrl[i].name);
return err;
}
DBG(3, "Image sensor supports '%s' control",
s->qctrl[i].name);
}
}
if (!(cam->state & DEV_INITIALIZED)) {
init_MUTEX(&cam->fileop_sem);
spin_lock_init(&cam->queue_lock);
init_waitqueue_head(&cam->wait_frame);
init_waitqueue_head(&cam->wait_stream);
cam->nreadbuffers = 2;
memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
memcpy(&(s->_rect), &(s->cropcap.defrect),
sizeof(struct v4l2_rect));
cam->state |= DEV_INITIALIZED;
}
DBG(2, "Initialization succeeded");
return 0;
}
static void et61x251_release_resources(struct et61x251_device* cam)
{
down(&et61x251_sysfs_lock);
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
up(&et61x251_sysfs_lock);
kfree(cam->control_buffer);
}
/*****************************************************************************/
static int et61x251_open(struct inode* inode, struct file* filp)
{
struct et61x251_device* cam;
int err = 0;
/*
This is the only safe way to prevent race conditions with
disconnect
*/
if (!down_read_trylock(&et61x251_disconnect))
return -ERESTARTSYS;
cam = video_get_drvdata(video_devdata(filp));
if (down_interruptible(&cam->dev_sem)) {
up_read(&et61x251_disconnect);
return -ERESTARTSYS;
}
if (cam->users) {
DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
if ((filp->f_flags & O_NONBLOCK) ||
(filp->f_flags & O_NDELAY)) {
err = -EWOULDBLOCK;
goto out;
}
up(&cam->dev_sem);
err = wait_event_interruptible_exclusive(cam->open,
cam->state & DEV_DISCONNECTED
|| !cam->users);
if (err) {
up_read(&et61x251_disconnect);
return err;
}
if (cam->state & DEV_DISCONNECTED) {
up_read(&et61x251_disconnect);
return -ENODEV;
}
down(&cam->dev_sem);
}
if (cam->state & DEV_MISCONFIGURED) {
err = et61x251_init(cam);
if (err) {
DBG(1, "Initialization failed again. "
"I will retry on next open().");
goto out;
}
cam->state &= ~DEV_MISCONFIGURED;
}
if ((err = et61x251_start_transfer(cam)))
goto out;
filp->private_data = cam;
cam->users++;
cam->io = IO_NONE;
cam->stream = STREAM_OFF;
cam->nbuffers = 0;
cam->frame_count = 0;
et61x251_empty_framequeues(cam);
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out:
up(&cam->dev_sem);
up_read(&et61x251_disconnect);
return err;
}
static int et61x251_release(struct inode* inode, struct file* filp)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
down(&cam->dev_sem); /* prevent disconnect() to be called */
et61x251_stop_transfer(cam);
et61x251_release_buffers(cam);
if (cam->state & DEV_DISCONNECTED) {
et61x251_release_resources(cam);
up(&cam->dev_sem);
kfree(cam);
return 0;
}
cam->users--;
wake_up_interruptible_nr(&cam->open, 1);
DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
up(&cam->dev_sem);
return 0;
}
static ssize_t
et61x251_read(struct file* filp, char __user * buf,
size_t count, loff_t* f_pos)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
struct et61x251_frame_t* f, * i;
unsigned long lock_flags;
int err = 0;
if (down_interruptible(&cam->fileop_sem))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
up(&cam->fileop_sem);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
up(&cam->fileop_sem);
return -EIO;
}
if (cam->io == IO_MMAP) {
DBG(3, "Close and open the device again to choose the read "
"method");
up(&cam->fileop_sem);
return -EINVAL;
}
if (cam->io == IO_NONE) {
if (!et61x251_request_buffers(cam, cam->nreadbuffers,
IO_READ)) {
DBG(1, "read() failed, not enough memory");
up(&cam->fileop_sem);
return -ENOMEM;
}
cam->io = IO_READ;
cam->stream = STREAM_ON;
}
if (list_empty(&cam->inqueue)) {
if (!list_empty(&cam->outqueue))
et61x251_empty_framequeues(cam);
et61x251_queue_unusedframes(cam);
}
if (!count) {
up(&cam->fileop_sem);
return 0;
}
if (list_empty(&cam->outqueue)) {
if (filp->f_flags & O_NONBLOCK) {
up(&cam->fileop_sem);
return -EAGAIN;
}
err = wait_event_interruptible
( cam->wait_frame,
(!list_empty(&cam->outqueue)) ||
(cam->state & DEV_DISCONNECTED) ||
(cam->state & DEV_MISCONFIGURED) );
if (err) {
up(&cam->fileop_sem);
return err;
}
if (cam->state & DEV_DISCONNECTED) {
up(&cam->fileop_sem);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
up(&cam->fileop_sem);
return -EIO;
}
}
f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame);
if (count > f->buf.bytesused)
count = f->buf.bytesused;
if (copy_to_user(buf, f->bufmem, count)) {
err = -EFAULT;
goto exit;
}
*f_pos += count;
exit:
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_for_each_entry(i, &cam->outqueue, frame)
i->state = F_UNUSED;
INIT_LIST_HEAD(&cam->outqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
et61x251_queue_unusedframes(cam);
PDBGG("Frame #%lu, bytes read: %zu",
(unsigned long)f->buf.index, count);
up(&cam->fileop_sem);
return err ? err : count;
}
static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
struct et61x251_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0;
if (down_interruptible(&cam->fileop_sem))
return POLLERR;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
goto error;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
goto error;
}
if (cam->io == IO_NONE) {
if (!et61x251_request_buffers(cam, cam->nreadbuffers,
IO_READ)) {
DBG(1, "poll() failed, not enough memory");
goto error;
}
cam->io = IO_READ;
cam->stream = STREAM_ON;
}
if (cam->io == IO_READ) {
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_for_each_entry(f, &cam->outqueue, frame)
f->state = F_UNUSED;
INIT_LIST_HEAD(&cam->outqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
et61x251_queue_unusedframes(cam);
}
poll_wait(filp, &cam->wait_frame, wait);
if (!list_empty(&cam->outqueue))
mask |= POLLIN | POLLRDNORM;
up(&cam->fileop_sem);
return mask;
error:
up(&cam->fileop_sem);
return POLLERR;
}
static void et61x251_vm_open(struct vm_area_struct* vma)
{
struct et61x251_frame_t* f = vma->vm_private_data;
f->vma_use_count++;
}
static void et61x251_vm_close(struct vm_area_struct* vma)
{
/* NOTE: buffers are not freed here */
struct et61x251_frame_t* f = vma->vm_private_data;
f->vma_use_count--;
}
static struct vm_operations_struct et61x251_vm_ops = {
.open = et61x251_vm_open,
.close = et61x251_vm_close,
};
static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
unsigned long size = vma->vm_end - vma->vm_start,
start = vma->vm_start;
void *pos;
u32 i;
if (down_interruptible(&cam->fileop_sem))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
up(&cam->fileop_sem);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
up(&cam->fileop_sem);
return -EIO;
}
if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
size != PAGE_ALIGN(cam->frame[0].buf.length)) {
up(&cam->fileop_sem);
return -EINVAL;
}
for (i = 0; i < cam->nbuffers; i++) {
if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
break;
}
if (i == cam->nbuffers) {
up(&cam->fileop_sem);
return -EINVAL;
}
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
pos = cam->frame[i].bufmem;
while (size > 0) { /* size is page-aligned */
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
up(&cam->fileop_sem);
return -EAGAIN;
}
start += PAGE_SIZE;
pos += PAGE_SIZE;
size -= PAGE_SIZE;
}
vma->vm_ops = &et61x251_vm_ops;
vma->vm_private_data = &cam->frame[i];
et61x251_vm_open(vma);
up(&cam->fileop_sem);
return 0;
}
/*****************************************************************************/
static int
et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_capability cap = {
.driver = "et61x251",
.version = ET61X251_MODULE_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING,
};
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_input i;
if (copy_from_user(&i, arg, sizeof(i)))
return -EFAULT;
if (i.index)
return -EINVAL;
memset(&i, 0, sizeof(i));
strcpy(i.name, "Camera");
if (copy_to_user(arg, &i, sizeof(i)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_gs_input(struct et61x251_device* cam, void __user * arg)
{
int index;
if (copy_from_user(&index, arg, sizeof(index)))
return -EFAULT;
if (index != 0)
return -EINVAL;
return 0;
}
static int
et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_queryctrl qc;
u8 i;
if (copy_from_user(&qc, arg, sizeof(qc)))
return -EFAULT;
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (qc.id && qc.id == s->qctrl[i].id) {
memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
if (copy_to_user(arg, &qc, sizeof(qc)))
return -EFAULT;
return 0;
}
return -EINVAL;
}
static int
et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_control ctrl;
int err = 0;
u8 i;
if (!s->get_ctrl && !s->set_ctrl)
return -EINVAL;
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
if (!s->get_ctrl) {
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
ctrl.value = s->_qctrl[i].default_value;
goto exit;
}
return -EINVAL;
} else
err = s->get_ctrl(cam, &ctrl);
exit:
if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
return -EFAULT;
return err;
}
static int
et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_control ctrl;
u8 i;
int err = 0;
if (!s->set_ctrl)
return -EINVAL;
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
return -ERANGE;
ctrl.value -= ctrl.value % s->qctrl[i].step;
break;
}
if ((err = s->set_ctrl(cam, &ctrl)))
return err;
s->_qctrl[i].default_value = ctrl.value;
return 0;
}
static int
et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cc->pixelaspect.numerator = 1;
cc->pixelaspect.denominator = 1;
if (copy_to_user(arg, cc, sizeof(*cc)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_crop crop = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
if (copy_to_user(arg, &crop, sizeof(crop)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_crop crop;
struct v4l2_rect* rect;
struct v4l2_rect* bounds = &(s->cropcap.bounds);
struct v4l2_pix_format* pix_format = &(s->pix_format);
u8 scale;
const enum et61x251_stream_state stream = cam->stream;
const u32 nbuffers = cam->nbuffers;
u32 i;
int err = 0;
if (copy_from_user(&crop, arg, sizeof(crop)))
return -EFAULT;
rect = &(crop.c);
if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (cam->module_param.force_munmap)
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_CROP failed. "
"Unmap the buffers first.");
return -EINVAL;
}
/* Preserve R,G or B origin */
rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
if (rect->width < 4)
rect->width = 4;
if (rect->height < 4)
rect->height = 4;
if (rect->width > bounds->width)
rect->width = bounds->width;
if (rect->height > bounds->height)
rect->height = bounds->height;
if (rect->left < bounds->left)
rect->left = bounds->left;
if (rect->top < bounds->top)
rect->top = bounds->top;
if (rect->left + rect->width > bounds->left + bounds->width)
rect->left = bounds->left+bounds->width - rect->width;
if (rect->top + rect->height > bounds->top + bounds->height)
rect->top = bounds->top+bounds->height - rect->height;
rect->width &= ~3L;
rect->height &= ~3L;
if (ET61X251_PRESERVE_IMGSCALE) {
/* Calculate the actual scaling factor */
u32 a, b;
a = rect->width * rect->height;
b = pix_format->width * pix_format->height;
scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
} else
scale = 1;
if (cam->stream == STREAM_ON)
if ((err = et61x251_stream_interrupt(cam)))
return err;
if (copy_to_user(arg, &crop, sizeof(crop))) {
cam->stream = stream;
return -EFAULT;
}
if (cam->module_param.force_munmap || cam->io == IO_READ)
et61x251_release_buffers(cam);
err = et61x251_set_crop(cam, rect);
if (s->set_crop)
err += s->set_crop(cam, rect);
err += et61x251_set_scale(cam, scale);
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
"use the camera, close and open /dev/video%d again.",
cam->v4ldev->minor);
return -EIO;
}
s->pix_format.width = rect->width/scale;
s->pix_format.height = rect->height/scale;
memcpy(&(s->_rect), rect, sizeof(*rect));
if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
"use the camera, close and open /dev/video%d again.",
cam->v4ldev->minor);
return -ENOMEM;
}
if (cam->io == IO_READ)
et61x251_empty_framequeues(cam);
else if (cam->module_param.force_munmap)
et61x251_requeue_outqueue(cam);
cam->stream = stream;
return 0;
}
static int
et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_fmtdesc fmtd;
if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
return -EFAULT;
if (fmtd.index == 0) {
strcpy(fmtd.description, "bayer rgb");
fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
} else if (fmtd.index == 1) {
strcpy(fmtd.description, "compressed");
fmtd.pixelformat = V4L2_PIX_FMT_ET61X251;
fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
} else
return -EINVAL;
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_format format;
struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
if (copy_from_user(&format, arg, sizeof(format)))
return -EFAULT;
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
? 0 : (pfmt->width * pfmt->priv) / 8;
pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
pfmt->field = V4L2_FIELD_NONE;
memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
if (copy_to_user(arg, &format, sizeof(format)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
void __user * arg)
{
struct et61x251_sensor* s = cam->sensor;
struct v4l2_format format;
struct v4l2_pix_format* pix;
struct v4l2_pix_format* pfmt = &(s->pix_format);
struct v4l2_rect* bounds = &(s->cropcap.bounds);
struct v4l2_rect rect;
u8 scale;
const enum et61x251_stream_state stream = cam->stream;
const u32 nbuffers = cam->nbuffers;
u32 i;
int err = 0;
if (copy_from_user(&format, arg, sizeof(format)))
return -EFAULT;
pix = &(format.fmt.pix);
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memcpy(&rect, &(s->_rect), sizeof(rect));
{ /* calculate the actual scaling factor */
u32 a, b;
a = rect.width * rect.height;
b = pix->width * pix->height;
scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
}
rect.width = scale * pix->width;
rect.height = scale * pix->height;
if (rect.width < 4)
rect.width = 4;
if (rect.height < 4)
rect.height = 4;
if (rect.width > bounds->left + bounds->width - rect.left)
rect.width = bounds->left + bounds->width - rect.left;
if (rect.height > bounds->top + bounds->height - rect.top)
rect.height = bounds->top + bounds->height - rect.top;
rect.width &= ~3L;
rect.height &= ~3L;
{ /* adjust the scaling factor */
u32 a, b;
a = rect.width * rect.height;
b = pix->width * pix->height;
scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
}
pix->width = rect.width / scale;
pix->height = rect.height / scale;
if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 &&
pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
pix->pixelformat = pfmt->pixelformat;
pix->priv = pfmt->priv; /* bpp */
pix->colorspace = pfmt->colorspace;
pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
? 0 : (pix->width * pix->priv) / 8;
pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
pix->field = V4L2_FIELD_NONE;
if (cmd == VIDIOC_TRY_FMT) {
if (copy_to_user(arg, &format, sizeof(format)))
return -EFAULT;
return 0;
}
if (cam->module_param.force_munmap)
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_FMT failed. "
"Unmap the buffers first.");
return -EINVAL;
}
if (cam->stream == STREAM_ON)
if ((err = et61x251_stream_interrupt(cam)))
return err;
if (copy_to_user(arg, &format, sizeof(format))) {
cam->stream = stream;
return -EFAULT;
}
if (cam->module_param.force_munmap || cam->io == IO_READ)
et61x251_release_buffers(cam);
err += et61x251_set_pix_format(cam, pix);
err += et61x251_set_crop(cam, &rect);
if (s->set_pix_format)
err += s->set_pix_format(cam, pix);
if (s->set_crop)
err += s->set_crop(cam, &rect);
err += et61x251_set_scale(cam, scale);
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
"use the camera, close and open /dev/video%d again.",
cam->v4ldev->minor);
return -EIO;
}
memcpy(pfmt, pix, sizeof(*pix));
memcpy(&(s->_rect), &rect, sizeof(rect));
if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
"use the camera, close and open /dev/video%d again.",
cam->v4ldev->minor);
return -ENOMEM;
}
if (cam->io == IO_READ)
et61x251_empty_framequeues(cam);
else if (cam->module_param.force_munmap)
et61x251_requeue_outqueue(cam);
cam->stream = stream;
return 0;
}
static int
et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg)
{
if (copy_to_user(arg, &cam->compression,
sizeof(cam->compression)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_jpegcompression jc;
const enum et61x251_stream_state stream = cam->stream;
int err = 0;
if (copy_from_user(&jc, arg, sizeof(jc)))
return -EFAULT;
if (jc.quality != 0 && jc.quality != 1)
return -EINVAL;
if (cam->stream == STREAM_ON)
if ((err = et61x251_stream_interrupt(cam)))
return err;
err += et61x251_set_compression(cam, &jc);
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor);
return -EIO;
}
cam->compression.quality = jc.quality;
cam->stream = stream;
return 0;
}
static int
et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_requestbuffers rb;
u32 i;
int err;
if (copy_from_user(&rb, arg, sizeof(rb)))
return -EFAULT;
if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
rb.memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (cam->io == IO_READ) {
DBG(3, "Close and open the device again to choose the mmap "
"I/O method");
return -EINVAL;
}
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_REQBUFS failed. "
"Previous buffers are still mapped.");
return -EINVAL;
}
if (cam->stream == STREAM_ON)
if ((err = et61x251_stream_interrupt(cam)))
return err;
et61x251_empty_framequeues(cam);
et61x251_release_buffers(cam);
if (rb.count)
rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP);
if (copy_to_user(arg, &rb, sizeof(rb))) {
et61x251_release_buffers(cam);
cam->io = IO_NONE;
return -EFAULT;
}
cam->io = rb.count ? IO_MMAP : IO_NONE;
return 0;
}
static int
et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_buffer b;
if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT;
if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b.index >= cam->nbuffers || cam->io != IO_MMAP)
return -EINVAL;
memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
if (cam->frame[b.index].vma_use_count)
b.flags |= V4L2_BUF_FLAG_MAPPED;
if (cam->frame[b.index].state == F_DONE)
b.flags |= V4L2_BUF_FLAG_DONE;
else if (cam->frame[b.index].state != F_UNUSED)
b.flags |= V4L2_BUF_FLAG_QUEUED;
if (copy_to_user(arg, &b, sizeof(b)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_buffer b;
unsigned long lock_flags;
if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT;
if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b.index >= cam->nbuffers || cam->io != IO_MMAP)
return -EINVAL;
if (cam->frame[b.index].state != F_UNUSED)
return -EINVAL;
cam->frame[b.index].state = F_QUEUED;
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
PDBGG("Frame #%lu queued", (unsigned long)b.index);
return 0;
}
static int
et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
void __user * arg)
{
struct v4l2_buffer b;
struct et61x251_frame_t *f;
unsigned long lock_flags;
int err = 0;
if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT;
if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
return -EINVAL;
if (list_empty(&cam->outqueue)) {
if (cam->stream == STREAM_OFF)
return -EINVAL;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
err = wait_event_interruptible
( cam->wait_frame,
(!list_empty(&cam->outqueue)) ||
(cam->state & DEV_DISCONNECTED) ||
(cam->state & DEV_MISCONFIGURED) );
if (err)
return err;
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
if (cam->state & DEV_MISCONFIGURED)
return -EIO;
}
spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame);
list_del(cam->outqueue.next);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
f->state = F_UNUSED;
memcpy(&b, &f->buf, sizeof(b));
if (f->vma_use_count)
b.flags |= V4L2_BUF_FLAG_MAPPED;
if (copy_to_user(arg, &b, sizeof(b)))
return -EFAULT;
PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
return 0;
}
static int
et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
{
int type;
if (copy_from_user(&type, arg, sizeof(type)))
return -EFAULT;
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL;
if (list_empty(&cam->inqueue))
return -EINVAL;
cam->stream = STREAM_ON;
DBG(3, "Stream on");
return 0;
}
static int
et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg)
{
int type, err;
if (copy_from_user(&type, arg, sizeof(type)))
return -EFAULT;
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL;
if (cam->stream == STREAM_ON)
if ((err = et61x251_stream_interrupt(cam)))
return err;
et61x251_empty_framequeues(cam);
DBG(3, "Stream off");
return 0;
}
static int
et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_streamparm sp;
if (copy_from_user(&sp, arg, sizeof(sp)))
return -EFAULT;
if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
sp.parm.capture.extendedmode = 0;
sp.parm.capture.readbuffers = cam->nreadbuffers;
if (copy_to_user(arg, &sp, sizeof(sp)))
return -EFAULT;
return 0;
}
static int
et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_streamparm sp;
if (copy_from_user(&sp, arg, sizeof(sp)))
return -EFAULT;
if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
sp.parm.capture.extendedmode = 0;
if (sp.parm.capture.readbuffers == 0)
sp.parm.capture.readbuffers = cam->nreadbuffers;
if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES)
sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES;
if (copy_to_user(arg, &sp, sizeof(sp)))
return -EFAULT;
cam->nreadbuffers = sp.parm.capture.readbuffers;
return 0;
}
static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
unsigned int cmd, void __user * arg)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
switch (cmd) {
case VIDIOC_QUERYCAP:
return et61x251_vidioc_querycap(cam, arg);
case VIDIOC_ENUMINPUT:
return et61x251_vidioc_enuminput(cam, arg);
case VIDIOC_G_INPUT:
case VIDIOC_S_INPUT:
return et61x251_vidioc_gs_input(cam, arg);
case VIDIOC_QUERYCTRL:
return et61x251_vidioc_query_ctrl(cam, arg);
case VIDIOC_G_CTRL:
return et61x251_vidioc_g_ctrl(cam, arg);
case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL:
return et61x251_vidioc_s_ctrl(cam, arg);
case VIDIOC_CROPCAP_OLD:
case VIDIOC_CROPCAP:
return et61x251_vidioc_cropcap(cam, arg);
case VIDIOC_G_CROP:
return et61x251_vidioc_g_crop(cam, arg);
case VIDIOC_S_CROP:
return et61x251_vidioc_s_crop(cam, arg);
case VIDIOC_ENUM_FMT:
return et61x251_vidioc_enum_fmt(cam, arg);
case VIDIOC_G_FMT:
return et61x251_vidioc_g_fmt(cam, arg);
case VIDIOC_TRY_FMT:
case VIDIOC_S_FMT:
return et61x251_vidioc_try_s_fmt(cam, cmd, arg);
case VIDIOC_G_JPEGCOMP:
return et61x251_vidioc_g_jpegcomp(cam, arg);
case VIDIOC_S_JPEGCOMP:
return et61x251_vidioc_s_jpegcomp(cam, arg);
case VIDIOC_REQBUFS:
return et61x251_vidioc_reqbufs(cam, arg);
case VIDIOC_QUERYBUF:
return et61x251_vidioc_querybuf(cam, arg);
case VIDIOC_QBUF:
return et61x251_vidioc_qbuf(cam, arg);
case VIDIOC_DQBUF:
return et61x251_vidioc_dqbuf(cam, filp, arg);
case VIDIOC_STREAMON:
return et61x251_vidioc_streamon(cam, arg);
case VIDIOC_STREAMOFF:
return et61x251_vidioc_streamoff(cam, arg);
case VIDIOC_G_PARM:
return et61x251_vidioc_g_parm(cam, arg);
case VIDIOC_S_PARM_OLD:
case VIDIOC_S_PARM:
return et61x251_vidioc_s_parm(cam, arg);
case VIDIOC_G_STD:
case VIDIOC_S_STD:
case VIDIOC_QUERYSTD:
case VIDIOC_ENUMSTD:
case VIDIOC_QUERYMENU:
return -EINVAL;
default:
return -EINVAL;
}
}
static int et61x251_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, unsigned long arg)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
int err = 0;
if (down_interruptible(&cam->fileop_sem))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
up(&cam->fileop_sem);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
up(&cam->fileop_sem);
return -EIO;
}
V4LDBG(3, "et61x251", cmd);
err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
up(&cam->fileop_sem);
return err;
}
static struct file_operations et61x251_fops = {
.owner = THIS_MODULE,
.open = et61x251_open,
.release = et61x251_release,
.ioctl = et61x251_ioctl,
.read = et61x251_read,
.poll = et61x251_poll,
.mmap = et61x251_mmap,
.llseek = no_llseek,
};
/*****************************************************************************/
/* It exists a single interface only. We do not need to validate anything. */
static int
et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct et61x251_device* cam;
static unsigned int dev_nr = 0;
unsigned int i;
int err = 0;
if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL)))
return -ENOMEM;
cam->usbdev = udev;
if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
DBG(1, "kmalloc() failed");
err = -ENOMEM;
goto fail;
}
if (!(cam->v4ldev = video_device_alloc())) {
DBG(1, "video_device_alloc() failed");
err = -ENOMEM;
goto fail;
}
init_MUTEX(&cam->dev_sem);
DBG(2, "ET61X[12]51 PC Camera Controller detected "
"(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
for (i = 0; et61x251_sensor_table[i]; i++) {
err = et61x251_sensor_table[i](cam);
if (!err)
break;
}
if (!err && cam->sensor)
DBG(2, "%s image sensor detected", cam->sensor->name);
else {
DBG(1, "No supported image sensor detected");
err = -ENODEV;
goto fail;
}
if (et61x251_init(cam)) {
DBG(1, "Initialization failed. I will retry on open().");
cam->state |= DEV_MISCONFIGURED;
}
strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera");
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = 0;
cam->v4ldev->fops = &et61x251_fops;
cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam);
down(&cam->dev_sem);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]);
if (err) {
DBG(1, "V4L2 device registration failed");
if (err == -ENFILE && video_nr[dev_nr] == -1)
DBG(1, "Free /dev/videoX node not found");
video_nr[dev_nr] = -1;
dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
up(&cam->dev_sem);
goto fail;
}
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
cam->module_param.force_munmap = force_munmap[dev_nr];
dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
#ifdef CONFIG_VIDEO_ADV_DEBUG
et61x251_create_sysfs(cam);
DBG(2, "Optional device control through 'sysfs' interface ready");
#endif
usb_set_intfdata(intf, cam);
up(&cam->dev_sem);
return 0;
fail:
if (cam) {
kfree(cam->control_buffer);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
kfree(cam);
}
return err;
}
static void et61x251_usb_disconnect(struct usb_interface* intf)
{
struct et61x251_device* cam = usb_get_intfdata(intf);
if (!cam)
return;
down_write(&et61x251_disconnect);
down(&cam->dev_sem);
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
wake_up_interruptible_all(&cam->open);
if (cam->users) {
DBG(2, "Device /dev/video%d is open! Deregistration and "
"memory deallocation are deferred on close.",
cam->v4ldev->minor);
cam->state |= DEV_MISCONFIGURED;
et61x251_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
wake_up_interruptible(&cam->wait_frame);
wake_up_interruptible(&cam->wait_stream);
} else {
cam->state |= DEV_DISCONNECTED;
et61x251_release_resources(cam);
}
up(&cam->dev_sem);
if (!cam->users)
kfree(cam);
up_write(&et61x251_disconnect);
}
static struct usb_driver et61x251_usb_driver = {
.name = "et61x251",
.id_table = et61x251_id_table,
.probe = et61x251_usb_probe,
.disconnect = et61x251_usb_disconnect,
};
/*****************************************************************************/
static int __init et61x251_module_init(void)
{
int err = 0;
KDBG(2, ET61X251_MODULE_NAME " v" ET61X251_MODULE_VERSION);
KDBG(3, ET61X251_MODULE_AUTHOR);
if ((err = usb_register(&et61x251_usb_driver)))
KDBG(1, "usb_register() failed");
return err;
}
static void __exit et61x251_module_exit(void)
{
usb_deregister(&et61x251_usb_driver);
}
module_init(et61x251_module_init);
module_exit(et61x251_module_exit);
/***************************************************************************
* API for image sensors connected to ET61X[12]51 PC Camera Controllers *
* *
* Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#ifndef _ET61X251_SENSOR_H_
#define _ET61X251_SENSOR_H_
#include <linux/usb.h>
#include <linux/videodev.h>
#include <linux/device.h>
#include <linux/stddef.h>
#include <linux/errno.h>
#include <asm/types.h>
struct et61x251_device;
struct et61x251_sensor;
/*****************************************************************************/
extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam);
#define ET61X251_SENSOR_TABLE \
/* Weak detections must go at the end of the list */ \
static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \
&et61x251_probe_tas5130d1b, \
NULL, \
};
extern void
et61x251_attach_sensor(struct et61x251_device* cam,
struct et61x251_sensor* sensor);
/*****************************************************************************/
extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index);
extern int et61x251_read_reg(struct et61x251_device*, u16 index);
extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value);
extern int et61x251_i2c_read(struct et61x251_device*, u8 address);
extern int et61x251_i2c_try_write(struct et61x251_device*,
struct et61x251_sensor*, u8 address,
u8 value);
extern int et61x251_i2c_try_read(struct et61x251_device*,
struct et61x251_sensor*, u8 address);
extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
u8 data2, u8 data3, u8 data4, u8 data5,
u8 data6, u8 data7, u8 data8, u8 address);
/*****************************************************************************/
enum et61x251_i2c_sysfs_ops {
ET61X251_I2C_READ = 0x01,
ET61X251_I2C_WRITE = 0x02,
};
enum et61x251_i2c_interface {
ET61X251_I2C_2WIRES,
ET61X251_I2C_3WIRES,
};
/* Repeat start condition when RSTA is high */
enum et61x251_i2c_rsta {
ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */
ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */
};
#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10
struct et61x251_sensor {
char name[32];
enum et61x251_i2c_sysfs_ops sysfs_ops;
enum et61x251_i2c_interface interface;
u8 i2c_slave_id;
enum et61x251_i2c_rsta rsta;
struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */
struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS];
struct v4l2_cropcap cropcap;
struct v4l2_pix_format pix_format;
int (*init)(struct et61x251_device* cam);
int (*get_ctrl)(struct et61x251_device* cam,
struct v4l2_control* ctrl);
int (*set_ctrl)(struct et61x251_device* cam,
const struct v4l2_control* ctrl);
int (*set_crop)(struct et61x251_device* cam,
const struct v4l2_rect* rect);
int (*set_pix_format)(struct et61x251_device* cam,
const struct v4l2_pix_format* pix);
const struct usb_device* usbdev;
/* Private */
struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS];
struct v4l2_rect _rect;
};
#endif /* _ET61X251_SENSOR_H_ */
/***************************************************************************
* Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 *
* PC Camera Controllers *
* *
* Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include "et61x251_sensor.h"
static int tas5130d1b_init(struct et61x251_device* cam)
{
int err = 0;
err += et61x251_write_reg(cam, 0x14, 0x01);
err += et61x251_write_reg(cam, 0x1b, 0x02);
err += et61x251_write_reg(cam, 0x02, 0x12);
err += et61x251_write_reg(cam, 0x0e, 0x60);
err += et61x251_write_reg(cam, 0x80, 0x61);
err += et61x251_write_reg(cam, 0xf0, 0x62);
err += et61x251_write_reg(cam, 0x03, 0x63);
err += et61x251_write_reg(cam, 0x14, 0x64);
err += et61x251_write_reg(cam, 0xf4, 0x65);
err += et61x251_write_reg(cam, 0x01, 0x66);
err += et61x251_write_reg(cam, 0x05, 0x67);
err += et61x251_write_reg(cam, 0x8f, 0x68);
err += et61x251_write_reg(cam, 0x0f, 0x8d);
err += et61x251_write_reg(cam, 0x08, 0x8e);
return err;
}
static int tas5130d1b_set_ctrl(struct et61x251_device* cam,
const struct v4l2_control* ctrl)
{
int err = 0;
switch (ctrl->id) {
case V4L2_CID_GAIN:
err += et61x251_i2c_raw_write(cam, 2, 0x20,
0xf6-ctrl->value, 0, 0, 0,
0, 0, 0, 0);
break;
case V4L2_CID_EXPOSURE:
err += et61x251_i2c_raw_write(cam, 2, 0x40,
0x47-ctrl->value, 0, 0, 0,
0, 0, 0, 0);
break;
default:
return -EINVAL;
}
return err ? -EIO : 0;
}
static struct et61x251_sensor tas5130d1b = {
.name = "TAS5130D1B",
.interface = ET61X251_I2C_3WIRES,
.rsta = ET61X251_I2C_RSTA_STOP,
.active_pixel = {
.left = 106,
.top = 13,
},
.init = &tas5130d1b_init,
.qctrl = {
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
.maximum = 0xf6,
.step = 0x02,
.default_value = 0x0d,
.flags = 0,
},
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = 0x47,
.step = 0x01,
.default_value = 0x23,
.flags = 0,
},
},
.set_ctrl = &tas5130d1b_set_ctrl,
.cropcap = {
.bounds = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},
.defrect = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},
},
.pix_format = {
.width = 640,
.height = 480,
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.priv = 8,
},
};
int et61x251_probe_tas5130d1b(struct et61x251_device* cam)
{
/* This sensor has no identifiers, so let's attach it anyway */
et61x251_attach_sensor(cam, &tas5130d1b);
/* Sensor detection is based on USB pid/vid */
if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6251)
return -ENODEV;
return 0;
}
...@@ -315,6 +315,7 @@ struct v4l2_pix_format ...@@ -315,6 +315,7 @@ struct v4l2_pix_format
#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */
#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */ #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */
#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */
#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */
/* /*
* F O R M A T E N U M E R A T I O N * F O R M A T E N U M E R A T I O N
......
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