/***************************************************************************
- * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * V4L2 driver for SN9C10x PC Camera Controllers *
* *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * Copyright (C) 2004-2005 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 *
#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>
-#include <linux/time.h>
#include <linux/delay.h>
#include <linux/stddef.h>
#include <linux/compiler.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>
MODULE_LICENSE(SN9C102_MODULE_LICENSE);
static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
-static unsigned int nv;
-module_param_array(video_nr, short, nv, 0444);
+module_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)"
"\none and for every other camera."
"\n");
+static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_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 SN9C102_DEBUG
static unsigned short debug = SN9C102_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
/*****************************************************************************/
-typedef char sn9c102_sof_header_t[12];
-typedef char sn9c102_eof_header_t[4];
-
static sn9c102_sof_header_t sn9c102_sof_header[] = {
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00},
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
/*****************************************************************************/
-static inline unsigned long kvirt_to_pa(unsigned long adr)
-{
- unsigned long kva, ret;
-
- kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
- kva |= adr & (PAGE_SIZE-1);
- ret = __pa(kva);
- return ret;
-}
-
-
static void* rvmalloc(size_t size)
{
void* mem;
}
-static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
+static u32
+sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
+ enum sn9c102_io_method io)
{
struct v4l2_pix_format* p = &(cam->sensor->pix_format);
- const size_t imagesize = (p->width * p->height * p->priv)/8;
+ 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;
cam->nbuffers = count;
while (cam->nbuffers > 0) {
- if ((buff = rvmalloc(cam->nbuffers * imagesize)))
+ if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
- cam->frame[i].bufmem = buff + i*imagesize;
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.index = i;
- cam->frame[i].buf.m.offset = i*imagesize;
+ 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;
if (r & 0x04)
return 0;
if (sensor->frequency & SN9C102_I2C_400KHZ)
- udelay(5*8);
+ udelay(5*16);
else
- udelay(16*8);
+ udelay(16*16);
}
return -EBUSY;
}
int
-sn9c102_i2c_try_read(struct sn9c102_device* cam,
- struct sn9c102_sensor* sensor, u8 address)
+sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 data0, u8 data1,
+ u8 n, u8 buffer[])
{
struct usb_device* udev = cam->usbdev;
u8* data = cam->control_buffer;
int err = 0, res;
- /* Write cycle - address */
+ /* Write cycle */
data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
- data[1] = sensor->slave_write_id;
- data[2] = address;
+ data[1] = data0; /* I2C slave id */
+ data[2] = data1; /* address */
data[7] = 0x10;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
err += sn9c102_i2c_wait(cam, sensor);
- /* Read cycle - 1 byte */
+ /* Read cycle - n bytes */
data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
- 0x10 | 0x02;
- data[1] = sensor->slave_read_id;
+ (n << 4) | 0x02;
+ data[1] = data0;
data[7] = 0x10;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
err += sn9c102_i2c_wait(cam, sensor);
- /* The read byte will be placed in data[4] */
+ /* The first read byte will be placed in data[4] */
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
if (res < 0)
err += sn9c102_i2c_detect_read_error(cam, sensor);
- if (err)
+ PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
+ data[4])
+
+ if (err) {
DBG(3, "I2C read failed for %s image sensor", sensor->name)
+ return -1;
+ }
- PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[4])
+ if (buffer)
+ memcpy(buffer, data, sizeof(buffer));
- return err ? -1 : (int)data[4];
+ return (int)data[4];
}
data[4] = data3;
data[5] = data4;
data[6] = data5;
- data[7] = 0x10;
+ data[7] = 0x14;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
if (res < 0)
if (err)
DBG(3, "I2C write failed for %s image sensor", sensor->name)
- PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+ PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
n, data0, data1, data2, data3, data4, data5)
}
+int
+sn9c102_i2c_try_read(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 address)
+{
+ return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id,
+ address, 1, NULL);
+}
+
+
int
sn9c102_i2c_try_write(struct sn9c102_device* cam,
struct sn9c102_sensor* sensor, u8 address, u8 value)
{
return sn9c102_i2c_try_raw_write(cam, sensor, 3,
- sensor->slave_write_id, address,
+ sensor->i2c_slave_id, address,
value, 0, 0, 0);
}
/*****************************************************************************/
-static void* sn9c102_find_sof_header(void* mem, size_t len)
+static void*
+sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
{
size_t soflen = sizeof(sn9c102_sof_header_t), i;
u8 j, n = sizeof(sn9c102_sof_header) / soflen;
for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
for (j = 0; j < n; j++)
/* It's enough to compare 7 bytes */
- if (!memcmp(mem + i, sn9c102_sof_header[j], 7))
- /* Skips the header */
+ if (!memcmp(mem + i, sn9c102_sof_header[j], 7)) {
+ memcpy(cam->sof_header, mem + i, soflen);
+ /* Skip the header */
return mem + i + soflen;
+ }
return NULL;
}
-static void* sn9c102_find_eof_header(void* mem, size_t len)
+static void*
+sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
{
size_t eoflen = sizeof(sn9c102_eof_header_t), i;
unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
+ if (cam->sensor->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ return NULL; /* EOF header does not exist in compressed data */
+
for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
for (j = 0; j < n; j++)
if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen))
*/
redo:
- sof = sn9c102_find_sof_header(pos, len);
+ sof = sn9c102_find_sof_header(cam, pos, len);
if (!sof) {
- eof = sn9c102_find_eof_header(pos, len);
+ eof = sn9c102_find_eof_header(cam, pos, len);
if ((*f)->state == F_GRABBING) {
end_of_frame:
img = len;
(*f)->buf.bytesused += img;
- if ((*f)->buf.bytesused == (*f)->buf.length) {
+ if ((*f)->buf.bytesused == (*f)->buf.length ||
+ (cam->sensor->pix_format.pixelformat ==
+ V4L2_PIX_FMT_SN9C10X && eof)) {
u32 b = (*f)->buf.bytesused;
(*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count;
(*f) = NULL;
spin_unlock_irqrestore(&cam->queue_lock
, lock_flags);
+ memcpy(cam->sysfs.frame_header,
+ cam->sof_header,
+ sizeof(sn9c102_sof_header_t));
DBG(3, "Video frame captured: "
"%lu bytes", (unsigned long)(b))
goto redo;
} else if ((*f)->state == F_GRABBING) {
- eof = sn9c102_find_eof_header(pos, len);
+ eof = sn9c102_find_eof_header(cam, pos, len);
if (eof && eof < sof)
goto end_of_frame; /* (1) */
else {
- DBG(3, "SOF before expected EOF after %lu "
- "bytes of image data",
- (unsigned long)((*f)->buf.bytesused))
- goto start_of_frame;
+ if (cam->sensor->pix_format.pixelformat ==
+ V4L2_PIX_FMT_SN9C10X) {
+ eof = sof-sizeof(sn9c102_sof_header_t);
+ goto end_of_frame;
+ } else {
+ DBG(3, "SOF before expected EOF after "
+ "%lu bytes of image data",
+ (unsigned long)((*f)->buf.bytesused))
+ goto start_of_frame;
+ }
}
}
}
struct usb_device *udev = cam->usbdev;
struct urb* urb;
const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
- 680, 800, 900, 1023};
+ 680, 800, 900, 1023};
const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
s8 i, j;
int err = 0;
return err;
}
+
+int sn9c102_stream_interrupt(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ cam->stream = STREAM_INTERRUPT;
+ err = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ SN9C102_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (err) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "The camera is misconfigured. To use it, close and "
+ "open /dev/video%d again.", cam->v4ldev->minor)
+ return err;
+ }
+
+ return 0;
+}
+
/*****************************************************************************/
static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
up(&sn9c102_sysfs_lock);
return count;
-}
+}
static ssize_t
return -ENODEV;
}
+ if (!(cam->sensor->sysfs_ops & SN9C102_I2C_READ)) {
+ up(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
up(&sn9c102_sysfs_lock);
return -EIO;
return -ENODEV;
}
+ if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
+ up(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
value = sn9c102_strtou8(buf, len, &count);
if (!count) {
up(&sn9c102_sysfs_lock);
static ssize_t
sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ enum sn9c102_bridge bridge;
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ bridge = cam->bridge;
+
+ up(&sn9c102_sysfs_lock);
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count)
+ return -EINVAL;
+
+ switch (bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ if (value > 0x0f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ case BRIDGE_SN9C103:
+ if (value > 0x7f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ }
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_red(struct class_device* cd, const char* buf, size_t len)
{
ssize_t res = 0;
u8 value;
ssize_t count;
value = sn9c102_strtou8(buf, len, &count);
- if (!count || value > 0x0f)
+ if (!count || value > 0x7f)
return -EINVAL;
- if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
return res;
}
+static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam)
+ return -ENODEV;
+
+ count = sizeof(cam->sysfs.frame_header);
+ memcpy(buf, cam->sysfs.frame_header, count);
+
+ DBG(3, "Frame header, read bytes: %zd", count)
+
+ return count;
+}
+
+
static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
sn9c102_show_reg, sn9c102_store_reg);
static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_val, sn9c102_store_i2c_val);
static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
+static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
+static CLASS_DEVICE_ATTR(frame_header, S_IRUGO,
+ sn9c102_show_frame_header, NULL);
static void sn9c102_create_sysfs(struct sn9c102_device* cam)
video_device_create_file(v4ldev, &class_device_attr_reg);
video_device_create_file(v4ldev, &class_device_attr_val);
- video_device_create_file(v4ldev, &class_device_attr_green);
- if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
+ video_device_create_file(v4ldev, &class_device_attr_frame_header);
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ video_device_create_file(v4ldev, &class_device_attr_green);
+ else if (cam->bridge == BRIDGE_SN9C103) {
+ video_device_create_file(v4ldev, &class_device_attr_blue);
+ video_device_create_file(v4ldev, &class_device_attr_red);
+ }
+ if (cam->sensor->sysfs_ops) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
/*****************************************************************************/
+static int
+sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18);
+ else
+ err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18);
+
+ return err ? -EIO : 0;
+}
+
+
+static int
+sn9c102_set_compression(struct sn9c102_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int err = 0;
+
+ if (compression->quality == 0)
+ err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17);
+ else if (compression->quality == 1)
+ err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17);
+
+ return err ? -EIO : 0;
+}
+
+
static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
{
u8 r = 0;
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
v_start = (u8)(rect->top - s->cropcap.bounds.top),
h_size = (u8)(rect->width / 16),
- v_size = (u8)(rect->height / 16),
- ae_strx = 0x00,
- ae_stry = 0x00,
- ae_endx = h_size / 2,
- ae_endy = v_size / 2;
+ v_size = (u8)(rect->height / 16);
int err = 0;
err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13);
err += sn9c102_write_reg(cam, h_size, 0x15);
err += sn9c102_write_reg(cam, v_size, 0x16);
- err += sn9c102_write_reg(cam, ae_strx, 0x1c);
- err += sn9c102_write_reg(cam, ae_stry, 0x1d);
- err += sn9c102_write_reg(cam, ae_endx, 0x1e);
- err += sn9c102_write_reg(cam, ae_endy, 0x1f);
if (err)
return -EIO;
}
}
+ if (!(cam->state & DEV_INITIALIZED))
+ cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1;
+ else
+ err += sn9c102_set_compression(cam, &cam->compression);
+ err += sn9c102_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_SN9C10X)
+ 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")
ctrl.value = qctrl[i].default_value;
err = s->set_ctrl(cam, &ctrl);
if (err) {
- DBG(3, "Set control failed")
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name)
return err;
}
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name)
}
}
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));
}
if (cam->io == IO_NONE) {
- if (!sn9c102_request_buffers(cam, 2)) {
+ if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
DBG(1, "read() failed, not enough memory")
up(&cam->fileop_sem);
return -ENOMEM;
sn9c102_queue_unusedframes(cam);
- if (count > f->buf.length)
- count = f->buf.length;
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
if (copy_to_user(buf, f->bufmem, count)) {
up(&cam->fileop_sem);
}
if (cam->io == IO_NONE) {
- if (!sn9c102_request_buffers(cam, 2)) {
+ if (!sn9c102_request_buffers(cam, 2, IO_READ)) {
DBG(1, "poll() failed, not enough memory")
goto error;
}
return -EINVAL;
}
+ /* VM_IO is eventually going to replace PageReserved altogether */
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+
pos = (unsigned long)cam->frame[i].bufmem;
while (size > 0) { /* size is page-aligned */
- page = kvirt_to_pa(pos);
- if (remap_page_range(vma, start, page, PAGE_SIZE,
- vma->vm_page_prot)) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE,
+ vma->vm_page_prot)) {
up(&cam->fileop_sem);
return -EAGAIN;
}
}
vma->vm_ops = &sn9c102_vm_ops;
- vma->vm_flags &= ~VM_IO; /* not I/O memory */
- vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
vma->vm_private_data = &cam->frame[i];
sn9c102_vm_open(vma);
.version = SN9C102_MODULE_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING,
+ V4L2_CAP_STREAMING,
};
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
- strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info));
+ if (usb_make_path(cam->usbdev, cap.bus_info,
+ sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, cam->dev.bus_id,
+ sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
return -EFAULT;
return err;
}
+ case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL:
{
struct sn9c102_sensor* s = cam->sensor;
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
- if ((err = s->set_ctrl(cam, &ctrl)))
- return err;
-
n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
for (i = 0; i < n; i++)
if (ctrl.id == s->qctrl[i].id) {
- s->_qctrl[i].default_value = ctrl.value;
+ 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;
+
+ PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
+ (unsigned long)ctrl.id, (unsigned long)ctrl.value)
+
return 0;
}
if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- for (i = 0; i < cam->nbuffers; i++)
- if (cam->frame[i].vma_use_count) {
- DBG(3, "VIDIOC_S_CROP failed. "
- "Unmap the buffers first.")
- return -EINVAL;
- }
+ if (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) ?
} else
scale = 1;
- if (cam->stream == STREAM_ON) {
- cam->stream = STREAM_INTERRUPT;
- err = wait_event_interruptible
- ( cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED) );
- if (err) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "The camera is misconfigured. To use "
- "it, close and open /dev/video%d "
- "again.", cam->v4ldev->minor)
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
return err;
- }
- if (cam->state & DEV_DISCONNECTED)
- return -ENODEV;
- }
if (copy_to_user(arg, &crop, sizeof(crop))) {
cam->stream = stream;
return -EFAULT;
}
- sn9c102_release_buffers(cam);
+ if (cam->module_param.force_munmap)
+ sn9c102_release_buffers(cam);
err = sn9c102_set_crop(cam, rect);
if (s->set_crop)
DBG(1, "VIDIOC_S_CROP failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
- return err;
+ return -EIO;
}
s->pix_format.width = rect->width/scale;
s->pix_format.height = rect->height/scale;
memcpy(&(s->_rect), rect, sizeof(*rect));
- if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+ if (cam->module_param.force_munmap &&
+ nbuffers != sn9c102_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 "
case VIDIOC_ENUM_FMT:
{
- struct sn9c102_sensor* s = cam->sensor;
struct v4l2_fmtdesc fmtd;
if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
return -EFAULT;
- if (fmtd.index != 0)
+ 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_SN9C10X;
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
return -EINVAL;
- memset(&fmtd, 0, sizeof(fmtd));
-
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmtd.description, "bayer rgb");
- fmtd.pixelformat = s->pix_format.pixelformat;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
return -EFAULT;
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- pfmt->bytesperline = (pfmt->width * pfmt->priv) / 8;
- pfmt->sizeimage = pfmt->height * pfmt->bytesperline;
+ pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
+ ? 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));
pix->width = rect.width / scale;
pix->height = rect.height / scale;
- pix->pixelformat = pfmt->pixelformat;
+ if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
pix->priv = pfmt->priv; /* bpp */
pix->colorspace = pfmt->colorspace;
- pix->bytesperline = (pix->width * pix->priv) / 8;
- pix->sizeimage = pix->height * pix->bytesperline;
+ pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ ? 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 (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
return 0;
+ }
- for (i = 0; i < cam->nbuffers; i++)
- if (cam->frame[i].vma_use_count) {
- DBG(3, "VIDIOC_S_FMT failed. "
- "Unmap the buffers first.")
- return -EINVAL;
- }
+ if (cam->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) {
- cam->stream = STREAM_INTERRUPT;
- err = wait_event_interruptible
- ( cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED) );
- if (err) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "The camera is misconfigured. To use "
- "it, close and open /dev/video%d "
- "again.", cam->v4ldev->minor)
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
return err;
- }
- if (cam->state & DEV_DISCONNECTED)
- return -ENODEV;
- }
if (copy_to_user(arg, &format, sizeof(format))) {
cam->stream = stream;
return -EFAULT;
}
- sn9c102_release_buffers(cam);
+ if (cam->module_param.force_munmap)
+ sn9c102_release_buffers(cam);
- err = sn9c102_set_crop(cam, &rect);
+ err += sn9c102_set_pix_format(cam, pix);
+ err += sn9c102_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 += sn9c102_set_scale(cam, scale);
DBG(1, "VIDIOC_S_FMT failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
- return err;
+ return -EIO;
}
memcpy(pfmt, pix, sizeof(*pix));
memcpy(&(s->_rect), &rect, sizeof(rect));
- if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+ if (cam->module_param.force_munmap &&
+ nbuffers != sn9c102_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 "
return 0;
}
+ case VIDIOC_G_JPEGCOMP:
+ {
+ if (copy_to_user(arg, &cam->compression,
+ sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_S_JPEGCOMP:
+ {
+ struct v4l2_jpegcompression jc;
+ const enum sn9c102_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 = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ err += sn9c102_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;
+ }
+
case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers rb;
return -EINVAL;
}
- if (cam->stream == STREAM_ON) {
- cam->stream = STREAM_INTERRUPT;
- err = wait_event_interruptible
- ( cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED) );
- if (err) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "The camera is misconfigured. To use "
- "it, close and open /dev/video%d "
- "again.", cam->v4ldev->minor)
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
return err;
- }
- if (cam->state & DEV_DISCONNECTED)
- return -ENODEV;
- }
sn9c102_empty_framequeues(cam);
sn9c102_release_buffers(cam);
if (rb.count)
- rb.count = sn9c102_request_buffers(cam, rb.count);
+ rb.count = sn9c102_request_buffers(cam, rb.count,
+ IO_MMAP);
if (copy_to_user(arg, &rb, sizeof(rb))) {
sn9c102_release_buffers(cam);
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL;
- if (cam->stream == STREAM_ON) {
- cam->stream = STREAM_INTERRUPT;
- err = wait_event_interruptible
- ( cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED) );
- if (err) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "The camera is misconfigured. To use "
- "it, close and open /dev/video%d "
- "again.", cam->v4ldev->minor)
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
return err;
- }
- if (cam->state & DEV_DISCONNECTED)
- return -ENODEV;
- }
sn9c102_empty_framequeues(cam);
return 0;
}
+ case VIDIOC_G_PARM:
+ {
+ 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;
+ }
+
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_PARM:
+ {
+ 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 > SN9C102_MAX_FRAMES)
+ sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+ }
+
case VIDIOC_G_STD:
case VIDIOC_S_STD:
case VIDIOC_QUERYSTD:
case VIDIOC_ENUMSTD:
case VIDIOC_QUERYMENU:
- case VIDIOC_G_PARM:
- case VIDIOC_S_PARM:
return -EINVAL;
default:
n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
for (i = 0; i < n-1; i++)
- if (udev->descriptor.idVendor==sn9c102_id_table[i].idVendor &&
- udev->descriptor.idProduct==sn9c102_id_table[i].idProduct)
+ if (le16_to_cpu(udev->descriptor.idVendor) ==
+ sn9c102_id_table[i].idVendor &&
+ le16_to_cpu(udev->descriptor.idProduct) ==
+ sn9c102_id_table[i].idProduct)
break;
if (i == n-1)
return -ENODEV;
r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || r != 0x10) {
- DBG(1, "Sorry, this is not a SN9C10[12] based camera "
+ DBG(1, "Sorry, this is not a SN9C10x based camera "
"(vid/pid 0x%04X/0x%04X)",
sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
err = -ENODEV;
goto fail;
}
- DBG(2, "SN9C10[12] PC Camera Controller detected "
- "(vid/pid 0x%04X/0x%04X)",
- sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
+ cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ?
+ BRIDGE_SN9C103 : BRIDGE_SN9C102;
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ DBG(2, "SN9C10[12] PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+ sn9c102_id_table[i].idProduct)
+ break;
+ case BRIDGE_SN9C103:
+ DBG(2, "SN9C103 PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+ sn9c102_id_table[i].idProduct)
+ break;
+ }
for (i = 0; sn9c102_sensor_table[i]; i++) {
err = sn9c102_sensor_table[i](cam);
cam->state |= DEV_MISCONFIGURED;
}
- strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
+ strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
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 < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
sn9c102_create_sysfs(cam);
+ DBG(2, "Optional device control through 'sysfs' interface ready")
usb_set_intfdata(intf, cam);