ZC0301 driver updates.
Changes: + new, - removed, * cleanup, @ bugfix
@ Need usb_get|put_dev() when disconnecting, if the device is open
* Cleanups and updates in the documentation
+ Use per-device sensor structures
+ Add frame_timeout module parameter
Signed-off-by: Luca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
data transfers;
- automatic detection of image sensor;
- video format is standard JPEG;
-- full support for the capabilities of every possible image sensors that can
- be connected to the ZC0301 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 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
1 = force memory unmapping (save memory)
Default: 0
-------------------------------------------------------------------------------
+Name: frame_timeout
+Type: uint array (min = 0, max = 64)
+Syntax: <n[,...]>
+Description: Timeout for a video frame in seconds. This parameter is
+ specific for each detected camera. This parameter can be
+ changed at runtime thanks to the /sys filesystem interface.
+Default: 2
+-------------------------------------------------------------------------------
Name: debug
Type: ushort
Syntax: <n>
Vendor ID Product ID
--------- ----------
+0x10fd 0x8050
+0x041e 0x0417
+0x041e 0x041e
+0x041e 0x081c
+0x041e 0x0834
+0x041e 0x0835
0x046d 0x08ae
+0x0ac8 0x0301
-The following image sensors are supported:
+The list above does not imply that all those devices work with this driver: up
+until now only the ones that mount the following image sensors are supported;
+kernel messages will always tell you whether this is the case:
Model Manufacturer
----- ------------
PAS202BCB PixArt Imaging, Inc.
-All the available control settings of each image sensor are supported through
-the V4L2 interface.
-
9. Notes for V4L2 application developers
========================================
- Informations about the chip internals needed to enable the I2C protocol have
been taken from the documentation of the ZC030x Video4Linux1 driver written
by Andrew Birkett <andy@nobugs.org>;
-- Initialization values of the ZC0301 controller connected to the PAS202BCB
+- The initialization values of the ZC0301 controller connected to the PAS202BCB
image sensor have been taken from the SPCA5XX driver maintained by
Michel Xhaard <mxhaard@magic.fr>.
#include <linux/param.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
-#include <asm/semaphore.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
#include "zc0301_sensor.h"
#define ZC0301_ALTERNATE_SETTING 7
#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS)
#define ZC0301_CTRL_TIMEOUT 100
-#define ZC0301_FRAME_TIMEOUT 2 * 1000 * msecs_to_jiffies(1)
+#define ZC0301_FRAME_TIMEOUT 2
/*****************************************************************************/
struct zc0301_module_param {
u8 force_munmap;
+ u16 frame_timeout;
};
static DECLARE_RWSEM(zc0301_disconnect);
struct zc0301_device {
struct video_device* v4ldev;
- struct zc0301_sensor* sensor;
+ struct zc0301_sensor sensor;
struct usb_device* usbdev;
struct urb* urb[ZC0301_URBS];
/*****************************************************************************/
+struct zc0301_device*
+zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id)
+{
+ if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id))
+ return cam;
+
+ return NULL;
+}
+
void
zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor)
{
- cam->sensor = sensor;
- cam->sensor->usbdev = cam->usbdev;
+ memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor));
}
/*****************************************************************************/
#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>
#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia"
#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define ZC0301_MODULE_LICENSE "GPL"
-#define ZC0301_MODULE_VERSION "1:1.01"
-#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1)
+#define ZC0301_MODULE_VERSION "1:1.02"
+#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2)
/*****************************************************************************/
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
"\n");
+static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] =
+ ZC0301_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"."
+ "\n");
+
#ifdef ZC0301_DEBUG
static unsigned short debug = ZC0301_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
zc0301_request_buffers(struct zc0301_device* cam, u32 count,
enum zc0301_io_method io)
{
- struct v4l2_pix_format* p = &(cam->sensor->pix_format);
- struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+ 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 :
(*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t,
frame);
- imagesize = (cam->sensor->pix_format.width *
- cam->sensor->pix_format.height *
- cam->sensor->pix_format.priv) / 8;
+ 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;
static int zc0301_init(struct zc0301_device* cam)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
struct v4l2_queryctrl *qctrl;
struct v4l2_rect* rect;
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
+ usb_put_dev(cam->usbdev);
kfree(cam->control_buffer);
}
(!list_empty(&cam->outqueue)) ||
(cam->state & DEV_DISCONNECTED) ||
(cam->state & DEV_MISCONFIGURED),
- ZC0301_FRAME_TIMEOUT );
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
if (timeout < 0) {
mutex_unlock(&cam->fileop_mutex);
return timeout;
static int
zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_queryctrl qc;
u8 i;
static int
zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
int err = 0;
u8 i;
static int
zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
u8 i;
int err = 0;
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
return -ERANGE;
static int
zc0301_vidioc_cropcap(struct zc0301_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->pixelaspect.numerator = 1;
static int
zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_crop crop = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
static int
zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_crop crop;
struct v4l2_rect* rect;
struct v4l2_rect* bounds = &(s->cropcap.bounds);
zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg)
{
struct v4l2_format format;
- struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
if (copy_from_user(&format, arg, sizeof(format)))
return -EFAULT;
zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd,
void __user * arg)
{
- struct zc0301_sensor* s = cam->sensor;
+ struct zc0301_sensor* s = &cam->sensor;
struct v4l2_format format;
struct v4l2_pix_format* pix;
struct v4l2_pix_format* pfmt = &(s->pix_format);
(!list_empty(&cam->outqueue)) ||
(cam->state & DEV_DISCONNECTED) ||
(cam->state & DEV_MISCONFIGURED),
- ZC0301_FRAME_TIMEOUT );
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
if (timeout < 0)
return timeout;
if (cam->state & DEV_DISCONNECTED)
break;
}
- if (!err && cam->sensor)
- DBG(2, "%s image sensor detected", cam->sensor->name);
+ if (!err)
+ DBG(2, "%s image sensor detected", cam->sensor.name);
else {
DBG(1, "No supported image sensor detected");
err = -ENODEV;
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
cam->state |= DEV_DISCONNECTED;
wake_up_interruptible(&cam->wait_frame);
wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
} else {
cam->state |= DEV_DISCONNECTED;
zc0301_release_resources(cam);
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
+/*
+ NOTE: Sensor controls are disabled for now, becouse changing them while
+ streaming sometimes results in out-of-sync video frames. We'll use
+ the default initialization, until we know how to stop and start video
+ in the chip. However, the image quality still looks good under various
+ light conditions.
+*/
+
#include <linux/delay.h>
#include "zc0301_sensor.h"
.maximum = 0x3fff,
.step = 0x0001,
.default_value = 0x01e5,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_GAIN,
.maximum = 0x1f,
.step = 0x01,
.default_value = 0x0c,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = ZC0301_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_RED_BALANCE,
.maximum = 0x0f,
.step = 0x01,
.default_value = 0x01,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_BLUE_BALANCE,
.maximum = 0x0f,
.step = 0x01,
.default_value = 0x05,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = ZC0301_V4L2_CID_GREEN_BALANCE,
.maximum = 0x0f,
.step = 0x01,
.default_value = 0x00,
- .flags = 0,
- },
- {
- .id = ZC0301_V4L2_CID_DAC_MAGNITUDE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "DAC magnitude",
- .minimum = 0x00,
- .maximum = 0xff,
- .step = 0x01,
- .default_value = 0x04,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
},
},
.get_ctrl = &pas202bcb_get_ctrl,
NULL, \
};
+extern struct zc0301_device*
+zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id);
+
extern void
-zc0301_attach_sensor(struct zc0301_device* cam,
- struct zc0301_sensor* sensor);
+zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor);
#define ZC0301_USB_DEVICE(vend, prod, intclass) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
#define ZC0301_ID_TABLE \
static const struct usb_device_id zc0301_id_table[] = { \
+ { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x0417, 0xff), }, \
+ { ZC0301_USB_DEVICE(0x041e, 0x041e, 0xff), }, /* HV7131B */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x081c, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x0834, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x0835, 0xff), }, /* PAS106 */ \
{ ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \
+ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \
{ } \
};
struct v4l2_cropcap cropcap;
struct v4l2_pix_format pix_format;
- int (*init)(struct zc0301_device* cam);
- int (*get_ctrl)(struct zc0301_device* cam,
- struct v4l2_control* ctrl);
- int (*set_ctrl)(struct zc0301_device* cam,
+ int (*init)(struct zc0301_device*);
+ int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct zc0301_device*,
const struct v4l2_control* ctrl);
- int (*set_crop)(struct zc0301_device* cam,
- const struct v4l2_rect* rect);
-
- const struct usb_device* usbdev;
+ int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect);
/* Private */
struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS];