2005-04-16 22:20:36 +00:00
|
|
|
/***************************************************************************
|
2007-01-08 13:43:56 +00:00
|
|
|
* V4L2 driver for SN9C1xx PC Camera Controllers *
|
2005-04-16 22:20:36 +00:00
|
|
|
* *
|
2007-01-08 13:43:56 +00:00
|
|
|
* Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
|
2005-04-16 22:20:36 +00:00
|
|
|
* *
|
|
|
|
* 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/errno.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/delay.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>
|
2011-06-25 16:43:22 +00:00
|
|
|
#include <linux/version.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/page-flags.h>
|
2008-05-16 18:20:25 +00:00
|
|
|
#include <asm/byteorder.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
#include "sn9c102.h"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers"
|
2007-03-26 19:12:04 +00:00
|
|
|
#define SN9C102_MODULE_ALIAS "sn9c1xx"
|
|
|
|
#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia"
|
2006-01-13 17:19:43 +00:00
|
|
|
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
|
|
|
|
#define SN9C102_MODULE_LICENSE "GPL"
|
2011-06-25 16:43:22 +00:00
|
|
|
#define SN9C102_MODULE_VERSION "1:1.48"
|
2006-01-13 17:19:43 +00:00
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
|
|
|
|
|
|
|
|
MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
|
|
|
|
MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
|
2007-03-26 19:12:04 +00:00
|
|
|
MODULE_ALIAS(SN9C102_MODULE_ALIAS);
|
2005-04-16 22:20:36 +00:00
|
|
|
MODULE_VERSION(SN9C102_MODULE_VERSION);
|
|
|
|
MODULE_LICENSE(SN9C102_MODULE_LICENSE);
|
|
|
|
|
|
|
|
static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
|
|
|
|
module_param_array(video_nr, short, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(video_nr,
|
2007-06-13 17:37:50 +00:00
|
|
|
" <-1|n[,...]>"
|
|
|
|
"\nSpecify V4L2 minor mode number."
|
|
|
|
"\n-1 = use next available (default)"
|
|
|
|
"\n n = use minor number n (integer >= 0)"
|
2006-03-25 12:19:53 +00:00
|
|
|
"\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES)
|
|
|
|
" cameras this way."
|
|
|
|
"\nFor example:"
|
|
|
|
"\nvideo_nr=-1,2,-1 would assign minor number 2 to"
|
|
|
|
"\nthe second camera and use auto for the first"
|
|
|
|
"\none and for every other camera."
|
|
|
|
"\n");
|
|
|
|
|
2012-01-12 23:02:16 +00:00
|
|
|
static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
|
|
|
|
SN9C102_FORCE_MUNMAP};
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param_array(force_munmap, bool, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(force_munmap,
|
2007-06-13 17:37:50 +00:00
|
|
|
" <0|1[,...]>"
|
|
|
|
"\nForce the application to unmap previously"
|
2006-03-25 12:19:53 +00:00
|
|
|
"\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."
|
2007-06-13 17:37:50 +00:00
|
|
|
"\n0 = do not force memory unmapping"
|
|
|
|
"\n1 = force memory unmapping (save memory)"
|
2006-03-25 12:19:53 +00:00
|
|
|
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
|
|
|
|
"\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] =
|
2006-03-25 12:19:53 +00:00
|
|
|
SN9C102_FRAME_TIMEOUT};
|
2006-02-25 06:50:47 +00:00
|
|
|
module_param_array(frame_timeout, uint, NULL, 0644);
|
|
|
|
MODULE_PARM_DESC(frame_timeout,
|
2007-06-13 17:37:50 +00:00
|
|
|
" <0|n[,...]>"
|
|
|
|
"\nTimeout for a video frame in seconds before"
|
2007-01-08 13:43:56 +00:00
|
|
|
"\nreturning an I/O error; 0 for infinity."
|
2006-03-25 12:19:53 +00:00
|
|
|
"\nThis parameter is specific for each detected camera."
|
|
|
|
"\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"."
|
|
|
|
"\n");
|
2006-02-25 06:50:47 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef SN9C102_DEBUG
|
|
|
|
static unsigned short debug = SN9C102_DEBUG_LEVEL;
|
|
|
|
module_param(debug, ushort, 0644);
|
|
|
|
MODULE_PARM_DESC(debug,
|
2007-06-13 17:37:50 +00:00
|
|
|
" <n>"
|
|
|
|
"\nDebugging information level, from 0 to 3:"
|
2006-03-25 12:19:53 +00:00
|
|
|
"\n0 = none (use carefully)"
|
|
|
|
"\n1 = critical errors"
|
|
|
|
"\n2 = significant informations"
|
|
|
|
"\n3 = more verbose messages"
|
2007-03-26 19:12:04 +00:00
|
|
|
"\nLevel 3 is useful for testing only."
|
2006-03-25 12:19:53 +00:00
|
|
|
"\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
|
|
|
|
"\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
|
|
|
|
2008-09-04 06:33:43 +00:00
|
|
|
/*
|
|
|
|
Add the probe entries to this table. Be sure to add the entry in the right
|
|
|
|
place, since, on failure, the next probing routine is called according to
|
|
|
|
the order of the list below, from top to bottom.
|
|
|
|
*/
|
|
|
|
static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = {
|
|
|
|
&sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */
|
|
|
|
&sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */
|
|
|
|
&sn9c102_probe_tas5110d, /* detection based on USB pid/vid */
|
|
|
|
&sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */
|
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
2006-03-25 12:19:53 +00:00
|
|
|
static u32
|
|
|
|
sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
|
|
|
|
enum sn9c102_io_method io)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct v4l2_pix_format* p = &(cam->sensor.pix_format);
|
|
|
|
struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
|
2007-01-08 13:43:56 +00:00
|
|
|
size_t imagesize = cam->module_param.force_munmap || io == IO_READ ?
|
2007-03-26 19:12:04 +00:00
|
|
|
(p->width * p->height * p->priv) / 8 :
|
|
|
|
(r->width * r->height * p->priv) / 8;
|
2005-04-16 22:20:36 +00:00
|
|
|
void* buff = NULL;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
if (count > SN9C102_MAX_FRAMES)
|
|
|
|
count = SN9C102_MAX_FRAMES;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120)
|
|
|
|
imagesize += 589 + 2; /* length of JPEG header + EOI marker */
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->nbuffers = count;
|
|
|
|
while (cam->nbuffers > 0) {
|
2007-01-08 13:43:56 +00:00
|
|
|
if ((buff = vmalloc_32_user(cam->nbuffers *
|
|
|
|
PAGE_ALIGN(imagesize))))
|
2005-04-16 22:20:36 +00:00
|
|
|
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 sn9c102_release_buffers(struct sn9c102_device* cam)
|
|
|
|
{
|
|
|
|
if (cam->nbuffers) {
|
2006-01-13 17:19:43 +00:00
|
|
|
vfree(cam->frame[0].bufmem);
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->nbuffers = 0;
|
|
|
|
}
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->frame_current = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&cam->inqueue);
|
|
|
|
INIT_LIST_HEAD(&cam->outqueue);
|
|
|
|
|
|
|
|
for (i = 0; i < SN9C102_MAX_FRAMES; i++) {
|
|
|
|
cam->frame[i].state = F_UNUSED;
|
|
|
|
cam->frame[i].buf.bytesused = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static void sn9c102_requeue_outqueue(struct sn9c102_device* cam)
|
|
|
|
{
|
|
|
|
struct sn9c102_frame_t *i;
|
|
|
|
|
|
|
|
list_for_each_entry(i, &cam->outqueue, frame) {
|
|
|
|
i->state = F_QUEUED;
|
|
|
|
list_add(&i->frame, &cam->inqueue);
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&cam->outqueue);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
|
|
|
|
{
|
|
|
|
unsigned long lock_flags;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
for (i = 0; i < cam->nbuffers; i++)
|
|
|
|
if (cam->frame[i].state == F_UNUSED) {
|
|
|
|
cam->frame[i].state = F_QUEUED;
|
|
|
|
spin_lock_irqsave(&cam->queue_lock, lock_flags);
|
|
|
|
list_add_tail(&cam->frame[i].frame, &cam->inqueue);
|
|
|
|
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2007-05-02 13:04:03 +00:00
|
|
|
|
2007-04-04 20:11:04 +00:00
|
|
|
/*
|
2007-05-02 13:04:03 +00:00
|
|
|
Write a sequence of count value/register pairs. Returns -1 after the first
|
|
|
|
failed write, or 0 for no errors.
|
|
|
|
*/
|
2007-04-04 20:11:04 +00:00
|
|
|
int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2],
|
|
|
|
int count)
|
2006-01-05 18:14:04 +00:00
|
|
|
{
|
|
|
|
struct usb_device* udev = cam->usbdev;
|
2007-05-02 13:04:03 +00:00
|
|
|
u8* buff = cam->control_buffer;
|
2006-01-05 18:14:04 +00:00
|
|
|
int i, res;
|
|
|
|
|
2007-04-04 20:11:04 +00:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
u8 index = valreg[i][1];
|
|
|
|
|
|
|
|
/*
|
2007-05-02 13:04:03 +00:00
|
|
|
index is a u8, so it must be <256 and can't be out of range.
|
|
|
|
If we put in a check anyway, gcc annoys us with a warning
|
|
|
|
hat our check is useless. People get all uppity when they
|
|
|
|
see warnings in the kernel compile.
|
|
|
|
*/
|
|
|
|
|
|
|
|
*buff = valreg[i][0];
|
|
|
|
|
|
|
|
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08,
|
|
|
|
0x41, index, 0, buff, 1,
|
|
|
|
SN9C102_CTRL_TIMEOUT);
|
|
|
|
|
2007-04-04 20:11:04 +00:00
|
|
|
if (res < 0) {
|
|
|
|
DBG(3, "Failed to write a register (value 0x%02X, "
|
2007-05-02 13:04:03 +00:00
|
|
|
"index 0x%02X, error %d)", *buff, index, res);
|
2007-04-04 20:11:04 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2006-01-05 18:14:04 +00:00
|
|
|
|
2007-05-02 13:04:03 +00:00
|
|
|
cam->reg[index] = *buff;
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
|
|
|
|
{
|
|
|
|
struct usb_device* udev = cam->usbdev;
|
|
|
|
u8* buff = cam->control_buffer;
|
|
|
|
int res;
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (index >= ARRAY_SIZE(cam->reg))
|
|
|
|
return -1;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
*buff = value;
|
|
|
|
|
|
|
|
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
|
2006-03-25 12:19:53 +00:00
|
|
|
index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (res < 0) {
|
|
|
|
DBG(3, "Failed to write a register (value 0x%02X, index "
|
2006-01-05 18:14:04 +00:00
|
|
|
"0x%02X, error %d)", value, index, res);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->reg[index] = value;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-02 13:04:03 +00:00
|
|
|
/* NOTE: with the SN9C10[123] reading some registers always returns 0 */
|
|
|
|
int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct usb_device* udev = cam->usbdev;
|
|
|
|
u8* buff = cam->control_buffer;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
|
2006-03-25 12:19:53 +00:00
|
|
|
index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (res < 0)
|
|
|
|
DBG(3, "Failed to read a register (index 0x%02X, error %d)",
|
2006-01-05 18:14:04 +00:00
|
|
|
index, res);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return (res >= 0) ? (int)(*buff) : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
|
|
|
|
{
|
2006-01-05 18:14:04 +00:00
|
|
|
if (index >= ARRAY_SIZE(cam->reg))
|
|
|
|
return -1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return cam->reg[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2007-05-02 13:04:03 +00:00
|
|
|
sn9c102_i2c_wait(struct sn9c102_device* cam,
|
|
|
|
const struct sn9c102_sensor* sensor)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int i, r;
|
|
|
|
|
|
|
|
for (i = 1; i <= 5; i++) {
|
|
|
|
r = sn9c102_read_reg(cam, 0x08);
|
|
|
|
if (r < 0)
|
|
|
|
return -EIO;
|
|
|
|
if (r & 0x04)
|
|
|
|
return 0;
|
|
|
|
if (sensor->frequency & SN9C102_I2C_400KHZ)
|
|
|
|
udelay(5*16);
|
|
|
|
else
|
|
|
|
udelay(16*16);
|
|
|
|
}
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2006-03-25 12:19:53 +00:00
|
|
|
sn9c102_i2c_detect_read_error(struct sn9c102_device* cam,
|
2007-05-02 13:04:03 +00:00
|
|
|
const struct sn9c102_sensor* sensor)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-01-08 13:43:56 +00:00
|
|
|
int r , err = 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
r = sn9c102_read_reg(cam, 0x08);
|
2007-01-08 13:43:56 +00:00
|
|
|
if (r < 0)
|
|
|
|
err += r;
|
|
|
|
|
|
|
|
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) {
|
|
|
|
if (!(r & 0x08))
|
|
|
|
err += -1;
|
|
|
|
} else {
|
|
|
|
if (r & 0x08)
|
|
|
|
err += -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err ? -EIO : 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2006-03-25 12:19:53 +00:00
|
|
|
sn9c102_i2c_detect_write_error(struct sn9c102_device* cam,
|
2007-05-02 13:04:03 +00:00
|
|
|
const struct sn9c102_sensor* sensor)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
r = sn9c102_read_reg(cam, 0x08);
|
|
|
|
return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-25 12:19:53 +00:00
|
|
|
int
|
2005-04-16 22:20:36 +00:00
|
|
|
sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
|
2007-05-02 13:04:03 +00:00
|
|
|
const struct sn9c102_sensor* sensor, u8 data0,
|
|
|
|
u8 data1, u8 n, u8 buffer[])
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct usb_device* udev = cam->usbdev;
|
|
|
|
u8* data = cam->control_buffer;
|
2007-05-02 13:04:03 +00:00
|
|
|
int i = 0, err = 0, res;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Write cycle */
|
|
|
|
data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
|
2006-03-25 12:19:53 +00:00
|
|
|
((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
|
2005-04-16 22:20:36 +00:00
|
|
|
data[1] = data0; /* I2C slave id */
|
|
|
|
data[2] = data1; /* address */
|
|
|
|
data[7] = 0x10;
|
|
|
|
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
|
2006-03-25 12:19:53 +00:00
|
|
|
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (res < 0)
|
|
|
|
err += res;
|
|
|
|
|
|
|
|
err += sn9c102_i2c_wait(cam, sensor);
|
|
|
|
|
|
|
|
/* Read cycle - n bytes */
|
|
|
|
data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
|
2006-03-25 12:19:53 +00:00
|
|
|
((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
|
|
|
|
(n << 4) | 0x02;
|
2005-04-16 22:20:36 +00:00
|
|
|
data[1] = data0;
|
|
|
|
data[7] = 0x10;
|
|
|
|
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
|
2006-03-25 12:19:53 +00:00
|
|
|
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (res < 0)
|
|
|
|
err += res;
|
|
|
|
|
|
|
|
err += sn9c102_i2c_wait(cam, sensor);
|
|
|
|
|
|
|
|
/* The first read byte will be placed in data[4] */
|
|
|
|
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
|
2006-03-25 12:19:53 +00:00
|
|
|
0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (res < 0)
|
|
|
|
err += res;
|
|
|
|
|
|
|
|
err += sn9c102_i2c_detect_read_error(cam, sensor);
|
|
|
|
|
|
|
|
PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
|
2006-01-05 18:14:04 +00:00
|
|
|
data[4]);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (err) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "I2C read failed for %s image sensor", sensor->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer)
|
2007-05-02 13:04:03 +00:00
|
|
|
for (i = 0; i < n && i < 5; i++)
|
|
|
|
buffer[n-i-1] = data[4-i];
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return (int)data[4];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-25 12:19:53 +00:00
|
|
|
int
|
2005-04-16 22:20:36 +00:00
|
|
|
sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
|
2007-05-02 13:04:03 +00:00
|
|
|
const struct sn9c102_sensor* sensor, u8 n, u8 data0,
|
2006-03-25 12:19:53 +00:00
|
|
|
u8 data1, u8 data2, u8 data3, u8 data4, u8 data5)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct usb_device* udev = cam->usbdev;
|
|
|
|
u8* data = cam->control_buffer;
|
|
|
|
int err = 0, res;
|
|
|
|
|
|
|
|
/* Write cycle. It usually is address + value */
|
|
|
|
data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
|
2006-03-25 12:19:53 +00:00
|
|
|
((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0)
|
|
|
|
| ((n - 1) << 4);
|
2005-04-16 22:20:36 +00:00
|
|
|
data[1] = data0;
|
|
|
|
data[2] = data1;
|
|
|
|
data[3] = data2;
|
|
|
|
data[4] = data3;
|
|
|
|
data[5] = data4;
|
|
|
|
data[6] = data5;
|
2007-01-08 13:43:56 +00:00
|
|
|
data[7] = 0x17;
|
2005-04-16 22:20:36 +00:00
|
|
|
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
|
2006-03-25 12:19:53 +00:00
|
|
|
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (res < 0)
|
|
|
|
err += res;
|
|
|
|
|
|
|
|
err += sn9c102_i2c_wait(cam, sensor);
|
|
|
|
err += sn9c102_i2c_detect_write_error(cam, sensor);
|
|
|
|
|
|
|
|
if (err)
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "I2C write failed for %s image sensor", sensor->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
|
|
|
|
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
|
2006-01-05 18:14:04 +00:00
|
|
|
n, data0, data1, data2, data3, data4, data5);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return err ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
sn9c102_i2c_try_read(struct sn9c102_device* cam,
|
2007-05-02 13:04:03 +00:00
|
|
|
const struct sn9c102_sensor* sensor, u8 address)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id,
|
2006-03-25 12:19:53 +00:00
|
|
|
address, 1, NULL);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-31 02:29:02 +00:00
|
|
|
static int sn9c102_i2c_try_write(struct sn9c102_device* cam,
|
|
|
|
const struct sn9c102_sensor* sensor,
|
|
|
|
u8 address, u8 value)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-03-25 12:19:53 +00:00
|
|
|
return sn9c102_i2c_try_raw_write(cam, sensor, 3,
|
|
|
|
sensor->i2c_slave_id, address,
|
|
|
|
value, 0, 0, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
return sn9c102_i2c_try_read(cam, &cam->sensor, address);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
return sn9c102_i2c_try_write(cam, &cam->sensor, address, value);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
static size_t sn9c102_sof_length(struct sn9c102_device* cam)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-01-05 18:14:04 +00:00
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
2007-01-08 13:43:56 +00:00
|
|
|
return 12;
|
2006-01-05 18:14:04 +00:00
|
|
|
case BRIDGE_SN9C103:
|
2007-01-08 13:43:56 +00:00
|
|
|
return 18;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
return 62;
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void*
|
|
|
|
sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
|
|
|
|
{
|
2007-04-04 20:11:05 +00:00
|
|
|
static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
|
2007-03-26 19:12:04 +00:00
|
|
|
const char *m = mem;
|
|
|
|
size_t soflen = 0, i, j;
|
2007-01-08 13:43:56 +00:00
|
|
|
|
|
|
|
soflen = sn9c102_sof_length(cam);
|
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
size_t b;
|
|
|
|
|
|
|
|
/* Read the variable part of the header */
|
|
|
|
if (unlikely(cam->sof.bytesread >= sizeof(marker))) {
|
|
|
|
cam->sof.header[cam->sof.bytesread] = *(m+i);
|
|
|
|
if (++cam->sof.bytesread == soflen) {
|
|
|
|
cam->sof.bytesread = 0;
|
|
|
|
return mem + i;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search for the SOF marker (fixed part) in the header */
|
|
|
|
for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) {
|
2008-04-22 17:42:08 +00:00
|
|
|
if (unlikely(i+j == len))
|
2007-03-26 19:12:04 +00:00
|
|
|
return NULL;
|
|
|
|
if (*(m+i+j) == marker[cam->sof.bytesread]) {
|
|
|
|
cam->sof.header[cam->sof.bytesread] = *(m+i+j);
|
|
|
|
if (++cam->sof.bytesread == sizeof(marker)) {
|
|
|
|
PDBGG("Bytes to analyze: %zd. SOF "
|
|
|
|
"starts at byte #%zd", len, i);
|
|
|
|
i += j+1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cam->sof.bytesread = 0;
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2007-03-26 19:12:04 +00:00
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void*
|
|
|
|
sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
|
|
|
|
{
|
2007-04-04 20:11:05 +00:00
|
|
|
static const u8 eof_header[4][4] = {
|
2007-01-08 13:43:56 +00:00
|
|
|
{0x00, 0x00, 0x00, 0x00},
|
|
|
|
{0x40, 0x00, 0x00, 0x00},
|
|
|
|
{0x80, 0x00, 0x00, 0x00},
|
|
|
|
{0xc0, 0x00, 0x00, 0x00},
|
|
|
|
};
|
|
|
|
size_t i, j;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
/* The EOF header does not exist in compressed data */
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
|
2007-03-26 19:12:04 +00:00
|
|
|
return NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
/*
|
|
|
|
The EOF header might cross the packet boundary, but this is not a
|
|
|
|
problem, since the end of a frame is determined by checking its size
|
|
|
|
in the first place.
|
|
|
|
*/
|
2007-01-08 13:43:56 +00:00
|
|
|
for (i = 0; (len >= 4) && (i <= len - 4); i++)
|
|
|
|
for (j = 0; j < ARRAY_SIZE(eof_header); j++)
|
|
|
|
if (!memcmp(mem + i, eof_header[j], 4))
|
2005-04-16 22:20:36 +00:00
|
|
|
return mem + i;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
static void
|
|
|
|
sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f)
|
|
|
|
{
|
2007-04-04 20:11:05 +00:00
|
|
|
static const u8 jpeg_header[589] = {
|
2007-01-08 13:43:56 +00:00
|
|
|
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
|
|
|
|
0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
|
|
|
|
0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
|
|
|
|
0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16,
|
|
|
|
0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16,
|
|
|
|
0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29,
|
|
|
|
0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29,
|
|
|
|
0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
|
|
|
|
0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28,
|
|
|
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
|
|
|
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
|
|
|
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
|
|
|
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
|
|
|
|
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2,
|
|
|
|
0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
|
|
|
|
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01,
|
|
|
|
0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
|
|
|
|
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00,
|
|
|
|
0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
|
|
|
|
0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
|
|
|
|
0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
|
|
|
|
0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
|
|
|
|
0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
|
|
|
|
0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
|
|
|
|
0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
|
|
|
|
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
|
|
|
|
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
|
|
|
|
0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
|
|
|
|
0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
|
|
|
|
0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
|
|
|
|
0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
|
|
|
|
0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
|
|
|
|
0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
|
|
|
|
0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
|
|
|
|
0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
|
|
|
|
0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
|
|
|
|
0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
|
|
|
|
0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
|
|
|
|
0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
|
|
|
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
|
|
|
|
0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
|
|
|
|
0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
|
|
|
|
0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
|
|
|
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
|
|
|
|
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
|
|
|
|
0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
|
|
|
|
0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
|
|
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
|
|
|
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
|
|
|
|
0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
|
|
|
|
0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
|
|
|
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
|
|
|
|
0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
|
|
|
|
0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11,
|
|
|
|
0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02,
|
|
|
|
0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03,
|
|
|
|
0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
|
|
|
|
};
|
|
|
|
u8 *pos = f->bufmem;
|
|
|
|
|
|
|
|
memcpy(pos, jpeg_header, sizeof(jpeg_header));
|
|
|
|
*(pos + 6) = 0x00;
|
|
|
|
*(pos + 7 + 64) = 0x01;
|
|
|
|
if (cam->compression.quality == 0) {
|
|
|
|
memcpy(pos + 7, SN9C102_Y_QTABLE0, 64);
|
|
|
|
memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64);
|
|
|
|
} else if (cam->compression.quality == 1) {
|
|
|
|
memcpy(pos + 7, SN9C102_Y_QTABLE1, 64);
|
|
|
|
memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64);
|
|
|
|
}
|
|
|
|
*(pos + 564) = cam->sensor.pix_format.width & 0xFF;
|
|
|
|
*(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF;
|
|
|
|
*(pos + 562) = cam->sensor.pix_format.height & 0xFF;
|
|
|
|
*(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF;
|
|
|
|
*(pos + 567) = 0x21;
|
|
|
|
|
|
|
|
f->buf.bytesused += sizeof(jpeg_header);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 13:55:46 +00:00
|
|
|
static void sn9c102_urb_complete(struct urb *urb)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam = urb->context;
|
|
|
|
struct sn9c102_frame_t** f;
|
2006-01-05 18:14:04 +00:00
|
|
|
size_t imagesize, soflen;
|
2005-04-16 22:20:36 +00:00
|
|
|
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;
|
2007-03-26 19:12:04 +00:00
|
|
|
cam->sof.bytesread = 0;
|
2007-01-08 13:43:56 +00:00
|
|
|
DBG(3, "Stream interrupted by application");
|
2006-02-25 06:50:47 +00:00
|
|
|
wake_up(&cam->wait_stream);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 sn9c102_frame_t,
|
2006-03-25 12:19:53 +00:00
|
|
|
frame);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
imagesize = (cam->sensor.pix_format.width *
|
2006-03-25 12:19:53 +00:00
|
|
|
cam->sensor.pix_format.height *
|
|
|
|
cam->sensor.pix_format.priv) / 8;
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
|
|
|
|
imagesize += 589; /* length of jpeg header */
|
|
|
|
soflen = sn9c102_sof_length(cam);
|
2006-01-05 18:14:04 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
unsigned int img, len, status;
|
|
|
|
void *pos, *sof, *eof;
|
|
|
|
|
|
|
|
len = urb->iso_frame_desc[i].actual_length;
|
|
|
|
status = urb->iso_frame_desc[i].status;
|
|
|
|
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
|
|
|
|
|
|
|
|
if (status) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Error in isochronous frame");
|
2005-04-16 22:20:36 +00:00
|
|
|
(*f)->state = F_ERROR;
|
2007-03-26 19:12:04 +00:00
|
|
|
cam->sof.bytesread = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
PDBGG("Isochrnous frame: length %u, #%u i", len, i);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
redo:
|
|
|
|
sof = sn9c102_find_sof_header(cam, pos, len);
|
2006-02-25 06:50:47 +00:00
|
|
|
if (likely(!sof)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
eof = sn9c102_find_eof_header(cam, pos, len);
|
|
|
|
if ((*f)->state == F_GRABBING) {
|
|
|
|
end_of_frame:
|
|
|
|
img = len;
|
|
|
|
|
|
|
|
if (eof)
|
|
|
|
img = (eof > pos) ? eof - pos - 1 : 0;
|
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
if ((*f)->buf.bytesused + img > imagesize) {
|
2006-02-25 06:50:47 +00:00
|
|
|
u32 b;
|
|
|
|
b = (*f)->buf.bytesused + img -
|
|
|
|
imagesize;
|
2005-04-16 22:20:36 +00:00
|
|
|
img = imagesize - (*f)->buf.bytesused;
|
2007-03-26 19:12:04 +00:00
|
|
|
PDBGG("Expected EOF not found: video "
|
|
|
|
"frame cut");
|
2005-04-16 22:20:36 +00:00
|
|
|
if (eof)
|
|
|
|
DBG(3, "Exceeded limit: +%u "
|
2006-01-05 18:14:04 +00:00
|
|
|
"bytes", (unsigned)(b));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
|
|
|
|
img);
|
|
|
|
|
|
|
|
if ((*f)->buf.bytesused == 0)
|
|
|
|
do_gettimeofday(&(*f)->buf.timestamp);
|
|
|
|
|
|
|
|
(*f)->buf.bytesused += img;
|
|
|
|
|
|
|
|
if ((*f)->buf.bytesused == imagesize ||
|
2007-01-08 13:43:56 +00:00
|
|
|
((cam->sensor.pix_format.pixelformat ==
|
|
|
|
V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
cam->sensor.pix_format.pixelformat ==
|
|
|
|
V4L2_PIX_FMT_JPEG) && eof)) {
|
2006-02-25 06:50:47 +00:00
|
|
|
u32 b;
|
2007-01-08 13:43:56 +00:00
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
b = (*f)->buf.bytesused;
|
2005-04-16 22:20:36 +00:00
|
|
|
(*f)->state = F_DONE;
|
|
|
|
(*f)->buf.sequence= ++cam->frame_count;
|
2007-01-08 13:43:56 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
spin_lock(&cam->queue_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
list_move_tail(&(*f)->frame,
|
2006-03-25 12:19:53 +00:00
|
|
|
&cam->outqueue);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!list_empty(&cam->inqueue))
|
|
|
|
(*f) = list_entry(
|
2006-03-25 12:19:53 +00:00
|
|
|
cam->inqueue.next,
|
|
|
|
struct sn9c102_frame_t,
|
|
|
|
frame );
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
|
|
|
(*f) = NULL;
|
2006-01-05 18:14:04 +00:00
|
|
|
spin_unlock(&cam->queue_lock);
|
2007-01-08 13:43:56 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(cam->sysfs.frame_header,
|
2007-03-26 19:12:04 +00:00
|
|
|
cam->sof.header, soflen);
|
2007-01-08 13:43:56 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Video frame captured: %lu "
|
|
|
|
"bytes", (unsigned long)(b));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!(*f))
|
|
|
|
goto resubmit_urb;
|
|
|
|
|
|
|
|
} else if (eof) {
|
|
|
|
(*f)->state = F_ERROR;
|
|
|
|
DBG(3, "Not expected EOF after %lu "
|
2006-03-25 12:19:53 +00:00
|
|
|
"bytes of image data",
|
2006-01-05 18:14:04 +00:00
|
|
|
(unsigned long)
|
|
|
|
((*f)->buf.bytesused));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sof) /* (1) */
|
|
|
|
goto start_of_frame;
|
|
|
|
|
|
|
|
} else if (eof) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "EOF without SOF");
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
} else {
|
2006-01-05 18:14:04 +00:00
|
|
|
PDBGG("Ignoring pointless isochronous frame");
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) {
|
|
|
|
start_of_frame:
|
|
|
|
(*f)->state = F_GRABBING;
|
|
|
|
(*f)->buf.bytesused = 0;
|
|
|
|
len -= (sof - pos);
|
|
|
|
pos = sof;
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->sensor.pix_format.pixelformat ==
|
|
|
|
V4L2_PIX_FMT_JPEG)
|
|
|
|
sn9c102_write_jpegheader(cam, (*f));
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "SOF detected: new video frame");
|
2005-04-16 22:20:36 +00:00
|
|
|
if (len)
|
|
|
|
goto redo;
|
|
|
|
|
|
|
|
} else if ((*f)->state == F_GRABBING) {
|
|
|
|
eof = sn9c102_find_eof_header(cam, pos, len);
|
|
|
|
if (eof && eof < sof)
|
|
|
|
goto end_of_frame; /* (1) */
|
|
|
|
else {
|
2006-02-25 06:50:47 +00:00
|
|
|
if (cam->sensor.pix_format.pixelformat ==
|
2007-01-08 13:43:56 +00:00
|
|
|
V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
cam->sensor.pix_format.pixelformat ==
|
|
|
|
V4L2_PIX_FMT_JPEG) {
|
2007-03-26 19:12:04 +00:00
|
|
|
if (sof - pos >= soflen) {
|
|
|
|
eof = sof - soflen;
|
|
|
|
} else { /* remove header */
|
|
|
|
eof = pos;
|
|
|
|
(*f)->buf.bytesused -=
|
|
|
|
(soflen - (sof - pos));
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
goto end_of_frame;
|
|
|
|
} else {
|
|
|
|
DBG(3, "SOF before expected EOF after "
|
2006-03-25 12:19:53 +00:00
|
|
|
"%lu bytes of image data",
|
2006-01-05 18:14:04 +00:00
|
|
|
(unsigned long)
|
|
|
|
((*f)->buf.bytesused));
|
2005-04-16 22:20:36 +00:00
|
|
|
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;
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "usb_submit_urb() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wake_up_interruptible(&cam->wait_frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int sn9c102_start_transfer(struct sn9c102_device* cam)
|
|
|
|
{
|
|
|
|
struct usb_device *udev = cam->usbdev;
|
|
|
|
struct urb* urb;
|
2007-01-08 13:43:56 +00:00
|
|
|
struct usb_host_interface* altsetting = usb_altnum_to_altsetting(
|
|
|
|
usb_ifnum_to_if(udev, 0),
|
|
|
|
SN9C102_ALTERNATE_SETTING);
|
|
|
|
const unsigned int psz = le16_to_cpu(altsetting->
|
|
|
|
endpoint[0].desc.wMaxPacketSize);
|
2005-04-16 22:20:36 +00:00
|
|
|
s8 i, j;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < SN9C102_URBS; i++) {
|
2006-01-13 17:19:43 +00:00
|
|
|
cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz,
|
2006-03-25 12:19:53 +00:00
|
|
|
GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam->transfer_buffer[i]) {
|
|
|
|
err = -ENOMEM;
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Not enough memory");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto free_buffers;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < SN9C102_URBS; i++) {
|
|
|
|
urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL);
|
|
|
|
cam->urb[i] = urb;
|
|
|
|
if (!urb) {
|
|
|
|
err = -ENOMEM;
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "usb_alloc_urb() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto free_urbs;
|
|
|
|
}
|
|
|
|
urb->dev = udev;
|
|
|
|
urb->context = cam;
|
|
|
|
urb->pipe = usb_rcvisocpipe(udev, 1);
|
|
|
|
urb->transfer_flags = URB_ISO_ASAP;
|
|
|
|
urb->number_of_packets = SN9C102_ISO_PACKETS;
|
|
|
|
urb->complete = sn9c102_urb_complete;
|
|
|
|
urb->transfer_buffer = cam->transfer_buffer[i];
|
|
|
|
urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS;
|
|
|
|
urb->interval = 1;
|
|
|
|
for (j = 0; j < SN9C102_ISO_PACKETS; j++) {
|
|
|
|
urb->iso_frame_desc[j].offset = psz * j;
|
|
|
|
urb->iso_frame_desc[j].length = psz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable video */
|
|
|
|
if (!(cam->reg[0x01] & 0x04)) {
|
|
|
|
err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
|
|
|
|
if (err) {
|
|
|
|
err = -EIO;
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "I/O hardware error");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto free_urbs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
|
|
|
|
if (err) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "usb_set_interface() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto free_urbs;
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->frame_current = NULL;
|
2007-03-26 19:12:04 +00:00
|
|
|
cam->sof.bytesread = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
for (i = 0; i < SN9C102_URBS; i++) {
|
|
|
|
err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
|
|
|
|
if (err) {
|
|
|
|
for (j = i-1; j >= 0; j--)
|
|
|
|
usb_kill_urb(cam->urb[j]);
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "usb_submit_urb() failed, error %d", err);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto free_urbs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_urbs:
|
2007-01-08 13:43:56 +00:00
|
|
|
for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++)
|
2005-04-16 22:20:36 +00:00
|
|
|
usb_free_urb(cam->urb[i]);
|
|
|
|
|
|
|
|
free_buffers:
|
|
|
|
for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++)
|
|
|
|
kfree(cam->transfer_buffer[i]);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int sn9c102_stop_transfer(struct sn9c102_device* cam)
|
|
|
|
{
|
|
|
|
struct usb_device *udev = cam->usbdev;
|
|
|
|
s8 i;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (cam->state & DEV_DISCONNECTED)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = SN9C102_URBS-1; i >= 0; i--) {
|
|
|
|
usb_kill_urb(cam->urb[i]);
|
|
|
|
usb_free_urb(cam->urb[i]);
|
|
|
|
kfree(cam->transfer_buffer[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
|
|
|
|
if (err)
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "usb_set_interface() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-22 22:07:00 +00:00
|
|
|
static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
cam->stream = STREAM_INTERRUPT;
|
2012-05-14 13:17:35 +00:00
|
|
|
wait_event_timeout(cam->wait_stream,
|
2006-03-25 12:19:53 +00:00
|
|
|
(cam->stream == STREAM_OFF) ||
|
|
|
|
(cam->state & DEV_DISCONNECTED),
|
|
|
|
SN9C102_URB_TIMEOUT);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (cam->state & DEV_DISCONNECTED)
|
|
|
|
return -ENODEV;
|
2006-02-25 06:50:47 +00:00
|
|
|
else if (cam->stream != STREAM_OFF) {
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
2006-02-25 06:50:47 +00:00
|
|
|
DBG(1, "URB timeout reached. The camera is misconfigured. "
|
2009-11-27 16:57:15 +00:00
|
|
|
"To use it, close and open %s again.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2006-02-25 06:50:47 +00:00
|
|
|
return -EIO;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
2006-01-13 17:19:43 +00:00
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
2007-01-08 13:43:56 +00:00
|
|
|
static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-01-08 13:43:56 +00:00
|
|
|
char str[7];
|
2005-04-16 22:20:36 +00:00
|
|
|
char* endp;
|
|
|
|
unsigned long val;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
if (len < 6) {
|
2005-04-16 22:20:36 +00:00
|
|
|
strncpy(str, buff, len);
|
2007-03-26 19:12:04 +00:00
|
|
|
str[len] = '\0';
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
2007-03-26 19:12:04 +00:00
|
|
|
strncpy(str, buff, 6);
|
2007-01-08 13:43:56 +00:00
|
|
|
str[6] = '\0';
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
val = simple_strtoul(str, &endp, 0);
|
|
|
|
|
|
|
|
*count = 0;
|
2007-01-08 13:43:56 +00:00
|
|
|
if (val <= 0xffff)
|
2005-04-16 22:20:36 +00:00
|
|
|
*count = (ssize_t)(endp - str);
|
|
|
|
if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
|
|
|
|
*count += 1;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
return (u16)val;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
NOTE 1: being inside one of the following methods implies that the v4l
|
2006-03-25 12:19:53 +00:00
|
|
|
device exists for sure (see kobjects and reference counters)
|
2005-04-16 22:20:36 +00:00
|
|
|
NOTE 2: buffers are PAGE_SIZE long
|
|
|
|
*/
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
static ssize_t sn9c102_show_reg(struct device* cd,
|
|
|
|
struct device_attribute *attr, char* buf)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
ssize_t count;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = sprintf(buf, "%u\n", cam->sysfs.reg);
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
2006-03-25 12:19:53 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-03-25 12:19:53 +00:00
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_reg(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 index;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
index = sn9c102_strtou16(buf, len, &count);
|
|
|
|
if (index >= ARRAY_SIZE(cam->reg) || !count) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->sysfs.reg = index;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg);
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Written bytes: %zd", count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
static ssize_t sn9c102_show_val(struct device* cd,
|
|
|
|
struct device_attribute *attr, char* buf)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
ssize_t count;
|
|
|
|
int val;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = sprintf(buf, "%d\n", val);
|
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
DBG(3, "Read bytes: %zd, value: %d", count, val);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
2006-03-25 12:19:53 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_val(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 value;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
int err;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
value = sn9c102_strtou16(buf, len, &count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!count) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = sn9c102_write_reg(cam, value, cam->sysfs.reg);
|
|
|
|
if (err) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X",
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->sysfs.reg, value);
|
|
|
|
DBG(3, "Written bytes: %zd", count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
static ssize_t sn9c102_show_i2c_reg(struct device* cd,
|
|
|
|
struct device_attribute *attr, char* buf)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
ssize_t count;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Read bytes: %zd", count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-25 12:19:53 +00:00
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 index;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
index = sn9c102_strtou16(buf, len, &count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!count) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->sysfs.i2c_reg = index;
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
|
|
|
|
DBG(3, "Written bytes: %zd", count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
static ssize_t sn9c102_show_i2c_val(struct device* cd,
|
|
|
|
struct device_attribute *attr, char* buf)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
ssize_t count;
|
|
|
|
int val;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = sprintf(buf, "%d\n", val);
|
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
DBG(3, "Read bytes: %zd, value: %d", count, val);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
2006-03-25 12:19:53 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 value;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
int err;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
value = sn9c102_strtou16(buf, len, &count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!count) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value);
|
|
|
|
if (err) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->sysfs.i2c_reg, value);
|
|
|
|
DBG(3, "Written bytes: %zd", count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_green(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
enum sn9c102_bridge bridge;
|
|
|
|
ssize_t res = 0;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 value;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
bridge = cam->bridge;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
value = sn9c102_strtou16(buf, len, &count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!count)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
if (value > 0x0f)
|
|
|
|
return -EINVAL;
|
2007-10-08 19:26:13 +00:00
|
|
|
if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0)
|
|
|
|
res = sn9c102_store_val(cd, attr, buf, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C103:
|
2007-01-08 13:43:56 +00:00
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
2005-04-16 22:20:36 +00:00
|
|
|
if (value > 0x7f)
|
|
|
|
return -EINVAL;
|
2007-10-08 19:26:13 +00:00
|
|
|
if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0)
|
|
|
|
res = sn9c102_store_val(cd, attr, buf, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_blue(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
ssize_t res = 0;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 value;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
value = sn9c102_strtou16(buf, len, &count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!count || value > 0x7f)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0)
|
|
|
|
res = sn9c102_store_val(cd, attr, buf, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2007-10-08 19:26:13 +00:00
|
|
|
sn9c102_store_red(struct device* cd, struct device_attribute *attr,
|
|
|
|
const char* buf, size_t len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
ssize_t res = 0;
|
2007-01-08 13:43:56 +00:00
|
|
|
u16 value;
|
2005-04-16 22:20:36 +00:00
|
|
|
ssize_t count;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
value = sn9c102_strtou16(buf, len, &count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!count || value > 0x7f)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0)
|
|
|
|
res = sn9c102_store_val(cd, attr, buf, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
static ssize_t sn9c102_show_frame_header(struct device* cd,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char* buf)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
ssize_t count;
|
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
cam = video_get_drvdata(container_of(cd, struct video_device, dev));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!cam)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
count = sizeof(cam->sysfs.frame_header);
|
|
|
|
memcpy(buf, cam->sysfs.frame_header, count);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Frame header, read bytes: %zd", count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
2006-03-25 12:19:53 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2007-10-08 19:26:13 +00:00
|
|
|
static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg);
|
|
|
|
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val);
|
|
|
|
static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
|
|
|
|
sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
|
|
|
|
static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
|
|
|
|
sn9c102_show_i2c_val, sn9c102_store_i2c_val);
|
2011-02-04 12:23:33 +00:00
|
|
|
static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green);
|
|
|
|
static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue);
|
|
|
|
static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red);
|
2007-10-08 19:26:13 +00:00
|
|
|
static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-10-13 10:17:32 +00:00
|
|
|
static int sn9c102_create_sysfs(struct sn9c102_device* cam)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-07-20 09:35:02 +00:00
|
|
|
struct device *dev = &(cam->v4ldev->dev);
|
2007-01-08 13:43:56 +00:00
|
|
|
int err = 0;
|
2006-10-13 10:17:32 +00:00
|
|
|
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_reg)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_out;
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_val)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_reg;
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_frame_header)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_val;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
if (cam->sensor.sysfs_ops) {
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_i2c_reg)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_frame_header;
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_i2c_val)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_i2c_reg;
|
2006-10-13 10:17:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) {
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_green)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_i2c_val;
|
|
|
|
} else {
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_blue)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_i2c_val;
|
2008-07-20 09:35:02 +00:00
|
|
|
if ((err = device_create_file(dev, &dev_attr_red)))
|
2007-01-08 13:43:56 +00:00
|
|
|
goto err_blue;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2006-10-13 10:17:32 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_blue:
|
2008-07-20 09:35:02 +00:00
|
|
|
device_remove_file(dev, &dev_attr_blue);
|
2006-10-13 10:17:32 +00:00
|
|
|
err_i2c_val:
|
|
|
|
if (cam->sensor.sysfs_ops)
|
2008-07-20 09:35:02 +00:00
|
|
|
device_remove_file(dev, &dev_attr_i2c_val);
|
2006-10-13 10:17:32 +00:00
|
|
|
err_i2c_reg:
|
|
|
|
if (cam->sensor.sysfs_ops)
|
2008-07-20 09:35:02 +00:00
|
|
|
device_remove_file(dev, &dev_attr_i2c_reg);
|
2007-01-08 13:43:56 +00:00
|
|
|
err_frame_header:
|
2008-07-20 09:35:02 +00:00
|
|
|
device_remove_file(dev, &dev_attr_frame_header);
|
2006-10-13 10:17:32 +00:00
|
|
|
err_val:
|
2008-07-20 09:35:02 +00:00
|
|
|
device_remove_file(dev, &dev_attr_val);
|
2006-10-13 10:17:32 +00:00
|
|
|
err_reg:
|
2008-07-20 09:35:02 +00:00
|
|
|
device_remove_file(dev, &dev_attr_reg);
|
2007-01-08 13:43:56 +00:00
|
|
|
err_out:
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2006-01-13 17:19:43 +00:00
|
|
|
#endif /* CONFIG_VIDEO_ADV_DEBUG */
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
pix->pixelformat == V4L2_PIX_FMT_JPEG) {
|
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
case BRIDGE_SN9C103:
|
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80,
|
|
|
|
0x18);
|
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f,
|
|
|
|
0x18);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
case BRIDGE_SN9C103:
|
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f,
|
|
|
|
0x18);
|
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80,
|
|
|
|
0x18);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return err ? -EIO : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_set_compression(struct sn9c102_device* cam,
|
2006-03-25 12:19:53 +00:00
|
|
|
struct v4l2_jpegcompression* compression)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-01-08 13:43:56 +00:00
|
|
|
int i, err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
case BRIDGE_SN9C103:
|
2007-03-26 19:12:04 +00:00
|
|
|
if (compression->quality == 0)
|
2007-01-08 13:43:56 +00:00
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01,
|
|
|
|
0x17);
|
2007-03-26 19:12:04 +00:00
|
|
|
else if (compression->quality == 1)
|
2007-01-08 13:43:56 +00:00
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe,
|
|
|
|
0x17);
|
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
if (compression->quality == 0) {
|
|
|
|
for (i = 0; i <= 63; i++) {
|
|
|
|
err += sn9c102_write_reg(cam,
|
2007-03-26 19:12:04 +00:00
|
|
|
SN9C102_Y_QTABLE1[i],
|
2007-01-08 13:43:56 +00:00
|
|
|
0x100 + i);
|
|
|
|
err += sn9c102_write_reg(cam,
|
2007-03-26 19:12:04 +00:00
|
|
|
SN9C102_UV_QTABLE1[i],
|
2007-01-08 13:43:56 +00:00
|
|
|
0x140 + i);
|
|
|
|
}
|
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf,
|
|
|
|
0x18);
|
|
|
|
} else if (compression->quality == 1) {
|
|
|
|
for (i = 0; i <= 63; i++) {
|
|
|
|
err += sn9c102_write_reg(cam,
|
|
|
|
SN9C102_Y_QTABLE1[i],
|
|
|
|
0x100 + i);
|
|
|
|
err += sn9c102_write_reg(cam,
|
|
|
|
SN9C102_UV_QTABLE1[i],
|
|
|
|
0x140 + i);
|
|
|
|
}
|
|
|
|
err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40,
|
|
|
|
0x18);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return err ? -EIO : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
|
|
|
|
{
|
|
|
|
u8 r = 0;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (scale == 1)
|
|
|
|
r = cam->reg[0x18] & 0xcf;
|
|
|
|
else if (scale == 2) {
|
|
|
|
r = cam->reg[0x18] & 0xcf;
|
|
|
|
r |= 0x10;
|
|
|
|
} else if (scale == 4)
|
|
|
|
r = cam->reg[0x18] | 0x20;
|
|
|
|
|
|
|
|
err += sn9c102_write_reg(cam, r, 0x18);
|
|
|
|
if (err)
|
|
|
|
return -EIO;
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
PDBGG("Scaling factor: %u", scale);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2005-04-16 22:20:36 +00:00
|
|
|
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);
|
|
|
|
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);
|
|
|
|
if (err)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
|
2006-01-05 18:14:04 +00:00
|
|
|
"%u %u %u %u", h_start, v_start, h_size, v_size);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int sn9c102_init(struct sn9c102_device* cam)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct v4l2_control ctrl;
|
|
|
|
struct v4l2_queryctrl *qctrl;
|
|
|
|
struct v4l2_rect* rect;
|
2005-12-11 15:20:08 +00:00
|
|
|
u8 i = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (!(cam->state & DEV_INITIALIZED)) {
|
2007-06-13 17:37:50 +00:00
|
|
|
mutex_init(&cam->open_mutex);
|
|
|
|
init_waitqueue_head(&cam->wait_open);
|
2005-04-16 22:20:36 +00:00
|
|
|
qctrl = s->qctrl;
|
|
|
|
rect = &(s->cropcap.defrect);
|
|
|
|
} else { /* use current values */
|
|
|
|
qctrl = s->_qctrl;
|
|
|
|
rect = &(s->_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
err += sn9c102_set_scale(cam, rect->width / s->pix_format.width);
|
|
|
|
err += sn9c102_set_crop(cam, rect);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (s->init) {
|
|
|
|
err = s->init(cam);
|
|
|
|
if (err) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Sensor initialization failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cam->state & DEV_INITIALIZED))
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->bridge == BRIDGE_SN9C101 ||
|
|
|
|
cam->bridge == BRIDGE_SN9C102 ||
|
|
|
|
cam->bridge == BRIDGE_SN9C103) {
|
2007-03-26 19:12:04 +00:00
|
|
|
if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
|
|
|
|
s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8;
|
2007-01-08 13:43:56 +00:00
|
|
|
cam->compression.quality = cam->reg[0x17] & 0x01 ?
|
|
|
|
0 : 1;
|
|
|
|
} else {
|
2007-03-26 19:12:04 +00:00
|
|
|
if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
|
|
|
|
s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG;
|
2007-01-08 13:43:56 +00:00
|
|
|
cam->compression.quality = cam->reg[0x18] & 0x40 ?
|
|
|
|
0 : 1;
|
|
|
|
err += sn9c102_set_compression(cam, &cam->compression);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
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;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
|
2005-12-11 15:20:08 +00:00
|
|
|
DBG(3, "Compressed video format is active, quality %d",
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->compression.quality);
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "Uncompressed video format is active");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (s->set_crop)
|
|
|
|
if ((err = s->set_crop(cam, rect))) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "set_crop() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->set_ctrl) {
|
2005-12-11 15:20:08 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
|
|
|
|
if (s->qctrl[i].id != 0 &&
|
2005-04-16 22:20:36 +00:00
|
|
|
!(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",
|
2006-01-05 18:14:04 +00:00
|
|
|
s->qctrl[i].name);
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
DBG(3, "Image sensor supports '%s' control",
|
2006-01-05 18:14:04 +00:00
|
|
|
s->qctrl[i].name);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cam->state & DEV_INITIALIZED)) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_init(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
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));
|
2005-12-11 15:20:08 +00:00
|
|
|
memcpy(&(s->_rect), &(s->cropcap.defrect),
|
2005-04-16 22:20:36 +00:00
|
|
|
sizeof(struct v4l2_rect));
|
|
|
|
cam->state |= DEV_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(2, "Initialization succeeded");
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
/*****************************************************************************/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
static void sn9c102_release_resources(struct kref *kref)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-06-13 17:37:50 +00:00
|
|
|
struct sn9c102_device *cam;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_lock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
cam = container_of(kref, struct sn9c102_device, kref);
|
|
|
|
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(2, "V4L2 device %s deregistered",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2005-04-16 22:20:36 +00:00
|
|
|
video_set_drvdata(cam->v4ldev, NULL);
|
|
|
|
video_unregister_device(cam->v4ldev);
|
2007-06-13 17:37:50 +00:00
|
|
|
usb_put_dev(cam->usbdev);
|
|
|
|
kfree(cam->control_buffer);
|
|
|
|
kfree(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&sn9c102_sysfs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 09:58:20 +00:00
|
|
|
static int sn9c102_open(struct file *filp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct sn9c102_device* cam;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
/*
|
2007-06-13 17:37:50 +00:00
|
|
|
A read_trylock() in open() is the only safe way to prevent race
|
|
|
|
conditions with disconnect(), one close() and multiple (not
|
|
|
|
necessarily simultaneous) attempts to open(). For example, it
|
|
|
|
prevents from waiting for a second access, while the device
|
|
|
|
structure is being deallocated, after a possible disconnect() and
|
|
|
|
during a following close() holding the write lock: given that, after
|
|
|
|
this deallocation, no access will be possible anymore, using the
|
|
|
|
non-trylock version would have let open() gain the access to the
|
|
|
|
device structure improperly.
|
|
|
|
For this reason the lock must also not be per-device.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2007-06-13 17:37:50 +00:00
|
|
|
if (!down_read_trylock(&sn9c102_dev_lock))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2008-08-23 11:32:09 +00:00
|
|
|
cam = video_drvdata(filp);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
if (wait_for_completion_interruptible(&cam->probe)) {
|
|
|
|
up_read(&sn9c102_dev_lock);
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
kref_get(&cam->kref);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Make sure to isolate all the simultaneous opens.
|
|
|
|
*/
|
|
|
|
if (mutex_lock_interruptible(&cam->open_mutex)) {
|
|
|
|
kref_put(&cam->kref, sn9c102_release_resources);
|
|
|
|
up_read(&sn9c102_dev_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
|
|
|
DBG(1, "Device not present");
|
|
|
|
err = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (cam->users) {
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(2, "Device %s is already in use",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2007-01-08 13:43:56 +00:00
|
|
|
DBG(3, "Simultaneous opens are not supported");
|
2007-06-13 17:37:50 +00:00
|
|
|
/*
|
|
|
|
open() must follow the open flags and should block
|
|
|
|
eventually while the device is in use.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
if ((filp->f_flags & O_NONBLOCK) ||
|
|
|
|
(filp->f_flags & O_NDELAY)) {
|
|
|
|
err = -EWOULDBLOCK;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-06-13 17:37:50 +00:00
|
|
|
DBG(2, "A blocking open() has been requested. Wait for the "
|
|
|
|
"device to be released...");
|
|
|
|
up_read(&sn9c102_dev_lock);
|
|
|
|
/*
|
|
|
|
We will not release the "open_mutex" lock, so that only one
|
|
|
|
process can be in the wait queue below. This way the process
|
2011-03-31 01:57:33 +00:00
|
|
|
will be sleeping while holding the lock, without losing its
|
2007-06-13 17:37:50 +00:00
|
|
|
priority after any wake_up().
|
|
|
|
*/
|
|
|
|
err = wait_event_interruptible_exclusive(cam->wait_open,
|
|
|
|
(cam->state & DEV_DISCONNECTED)
|
2006-03-25 12:19:53 +00:00
|
|
|
|| !cam->users);
|
2007-06-13 17:37:50 +00:00
|
|
|
down_read(&sn9c102_dev_lock);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
2007-06-13 17:37:50 +00:00
|
|
|
err = -ENODEV;
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->state & DEV_MISCONFIGURED) {
|
|
|
|
err = sn9c102_init(cam);
|
|
|
|
if (err) {
|
|
|
|
DBG(1, "Initialization failed again. "
|
2006-01-05 18:14:04 +00:00
|
|
|
"I will retry on next open().");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
cam->state &= ~DEV_MISCONFIGURED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = sn9c102_start_transfer(cam)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
filp->private_data = cam;
|
|
|
|
cam->users++;
|
|
|
|
cam->io = IO_NONE;
|
|
|
|
cam->stream = STREAM_OFF;
|
|
|
|
cam->nbuffers = 0;
|
|
|
|
cam->frame_count = 0;
|
|
|
|
sn9c102_empty_framequeues(cam);
|
|
|
|
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
out:
|
2007-06-13 17:37:50 +00:00
|
|
|
mutex_unlock(&cam->open_mutex);
|
|
|
|
if (err)
|
|
|
|
kref_put(&cam->kref, sn9c102_release_resources);
|
|
|
|
|
|
|
|
up_read(&sn9c102_dev_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 09:58:20 +00:00
|
|
|
static int sn9c102_release(struct file *filp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-06-13 17:37:50 +00:00
|
|
|
struct sn9c102_device* cam;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
down_write(&sn9c102_dev_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-08-23 11:32:09 +00:00
|
|
|
cam = video_drvdata(filp);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
sn9c102_stop_transfer(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
sn9c102_release_buffers(cam);
|
|
|
|
cam->users--;
|
2007-06-13 17:37:50 +00:00
|
|
|
wake_up_interruptible_nr(&cam->wait_open, 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
kref_put(&cam->kref, sn9c102_release_resources);
|
|
|
|
|
|
|
|
up_write(&sn9c102_dev_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
|
|
|
|
{
|
2008-08-23 11:32:09 +00:00
|
|
|
struct sn9c102_device *cam = video_drvdata(filp);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct sn9c102_frame_t* f, * i;
|
|
|
|
unsigned long lock_flags;
|
2006-02-25 06:50:47 +00:00
|
|
|
long timeout;
|
2005-04-16 22:20:36 +00:00
|
|
|
int err = 0;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&cam->fileop_mutex))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Device not present");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->state & DEV_MISCONFIGURED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "The camera is misconfigured. Close and open it "
|
|
|
|
"again.");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->io == IO_MMAP) {
|
|
|
|
DBG(3, "Close and open the device again to choose "
|
2006-01-05 18:14:04 +00:00
|
|
|
"the read method");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2007-03-26 19:12:04 +00:00
|
|
|
return -EBUSY;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->io == IO_NONE) {
|
|
|
|
if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "read() failed, not enough memory");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
cam->io = IO_READ;
|
|
|
|
cam->stream = STREAM_ON;
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (list_empty(&cam->inqueue)) {
|
|
|
|
if (!list_empty(&cam->outqueue))
|
|
|
|
sn9c102_empty_framequeues(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
sn9c102_queue_unusedframes(cam);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!count) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_empty(&cam->outqueue)) {
|
|
|
|
if (filp->f_flags & O_NONBLOCK) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
2007-01-08 13:43:56 +00:00
|
|
|
if (!cam->module_param.frame_timeout) {
|
|
|
|
err = wait_event_interruptible
|
|
|
|
( cam->wait_frame,
|
|
|
|
(!list_empty(&cam->outqueue)) ||
|
|
|
|
(cam->state & DEV_DISCONNECTED) ||
|
|
|
|
(cam->state & DEV_MISCONFIGURED) );
|
|
|
|
if (err) {
|
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
} else {
|
2007-03-26 19:12:04 +00:00
|
|
|
timeout = wait_event_interruptible_timeout
|
|
|
|
( cam->wait_frame,
|
|
|
|
(!list_empty(&cam->outqueue)) ||
|
|
|
|
(cam->state & DEV_DISCONNECTED) ||
|
|
|
|
(cam->state & DEV_MISCONFIGURED),
|
2009-09-14 22:13:29 +00:00
|
|
|
msecs_to_jiffies(
|
|
|
|
cam->module_param.frame_timeout * 1000
|
|
|
|
)
|
|
|
|
);
|
2007-03-26 19:12:04 +00:00
|
|
|
if (timeout < 0) {
|
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
|
|
|
return timeout;
|
2007-01-08 13:43:56 +00:00
|
|
|
} else if (timeout == 0 &&
|
|
|
|
!(cam->state & DEV_DISCONNECTED)) {
|
|
|
|
DBG(1, "Video frame timeout elapsed");
|
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
|
|
|
return -EIO;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->state & DEV_MISCONFIGURED) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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:
|
2005-04-16 22:20:36 +00:00
|
|
|
spin_lock_irqsave(&cam->queue_lock, lock_flags);
|
|
|
|
list_for_each_entry(i, &cam->outqueue, frame)
|
|
|
|
i->state = F_UNUSED;
|
|
|
|
INIT_LIST_HEAD(&cam->outqueue);
|
|
|
|
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
|
|
|
|
|
|
|
|
sn9c102_queue_unusedframes(cam);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
PDBGG("Frame #%lu, bytes read: %zu",
|
|
|
|
(unsigned long)f->buf.index, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
|
|
|
|
{
|
2008-08-23 11:32:09 +00:00
|
|
|
struct sn9c102_device *cam = video_drvdata(filp);
|
2006-01-05 18:14:04 +00:00
|
|
|
struct sn9c102_frame_t* f;
|
|
|
|
unsigned long lock_flags;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int mask = 0;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&cam->fileop_mutex))
|
2005-04-16 22:20:36 +00:00
|
|
|
return POLLERR;
|
|
|
|
|
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Device not present");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->state & DEV_MISCONFIGURED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "The camera is misconfigured. Close and open it "
|
|
|
|
"again.");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->io == IO_NONE) {
|
|
|
|
if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
|
2006-03-25 12:19:53 +00:00
|
|
|
IO_READ)) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "poll() failed, not enough memory");
|
2005-04-16 22:20:36 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
cam->io = IO_READ;
|
|
|
|
cam->stream = STREAM_ON;
|
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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);
|
2005-04-16 22:20:36 +00:00
|
|
|
sn9c102_queue_unusedframes(cam);
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
poll_wait(filp, &cam->wait_frame, wait);
|
|
|
|
|
|
|
|
if (!list_empty(&cam->outqueue))
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
error:
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return POLLERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void sn9c102_vm_open(struct vm_area_struct* vma)
|
|
|
|
{
|
|
|
|
struct sn9c102_frame_t* f = vma->vm_private_data;
|
|
|
|
f->vma_use_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void sn9c102_vm_close(struct vm_area_struct* vma)
|
|
|
|
{
|
|
|
|
/* NOTE: buffers are not freed here */
|
|
|
|
struct sn9c102_frame_t* f = vma->vm_private_data;
|
|
|
|
f->vma_use_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-27 18:29:37 +00:00
|
|
|
static const struct vm_operations_struct sn9c102_vm_ops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.open = sn9c102_vm_open,
|
|
|
|
.close = sn9c102_vm_close,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
|
|
|
|
{
|
2008-08-23 11:32:09 +00:00
|
|
|
struct sn9c102_device *cam = video_drvdata(filp);
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned long size = vma->vm_end - vma->vm_start,
|
2006-03-25 12:19:53 +00:00
|
|
|
start = vma->vm_start;
|
2006-01-13 17:19:43 +00:00
|
|
|
void *pos;
|
2005-04-16 22:20:36 +00:00
|
|
|
u32 i;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&cam->fileop_mutex))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Device not present");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->state & DEV_MISCONFIGURED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "The camera is misconfigured. Close and open it "
|
|
|
|
"again.");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
|
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->io != IO_MMAP ||
|
2005-04-16 22:20:36 +00:00
|
|
|
size != PAGE_ALIGN(cam->frame[0].buf.length)) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
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) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
mm: kill vma flag VM_RESERVED and mm->reserved_vm counter
A long time ago, in v2.4, VM_RESERVED kept swapout process off VMA,
currently it lost original meaning but still has some effects:
| effect | alternative flags
-+------------------------+---------------------------------------------
1| account as reserved_vm | VM_IO
2| skip in core dump | VM_IO, VM_DONTDUMP
3| do not merge or expand | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
4| do not mlock | VM_IO, VM_DONTEXPAND, VM_HUGETLB, VM_PFNMAP
This patch removes reserved_vm counter from mm_struct. Seems like nobody
cares about it, it does not exported into userspace directly, it only
reduces total_vm showed in proc.
Thus VM_RESERVED can be replaced with VM_IO or pair VM_DONTEXPAND | VM_DONTDUMP.
remap_pfn_range() and io_remap_pfn_range() set VM_IO|VM_DONTEXPAND|VM_DONTDUMP.
remap_vmalloc_range() set VM_DONTEXPAND | VM_DONTDUMP.
[akpm@linux-foundation.org: drivers/vfio/pci/vfio_pci.c fixup]
Signed-off-by: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Carsten Otte <cotte@de.ibm.com>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Eric Paris <eparis@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Kentaro Takeda <takedakn@nttdata.co.jp>
Cc: Matt Helsley <matthltc@us.ibm.com>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: Venkatesh Pallipadi <venki@google.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-08 23:29:02 +00:00
|
|
|
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-13 17:19:43 +00:00
|
|
|
pos = cam->frame[i].bufmem;
|
2005-04-16 22:20:36 +00:00
|
|
|
while (size > 0) { /* size is page-aligned */
|
2006-01-13 17:19:43 +00:00
|
|
|
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
start += PAGE_SIZE;
|
|
|
|
pos += PAGE_SIZE;
|
|
|
|
size -= PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
vma->vm_ops = &sn9c102_vm_ops;
|
|
|
|
vma->vm_private_data = &cam->frame[i];
|
|
|
|
sn9c102_vm_open(vma);
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
/*****************************************************************************/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_capability cap = {
|
|
|
|
.driver = "sn9c102",
|
2011-06-25 16:43:22 +00:00
|
|
|
.version = LINUX_VERSION_CODE,
|
2006-01-05 18:14:04 +00:00
|
|
|
.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
|
2006-03-25 12:19:53 +00:00
|
|
|
V4L2_CAP_STREAMING,
|
2006-01-05 18:14:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
|
|
|
|
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
|
V4L/DVB (9521): V4L: struct device - replace bus_id with dev_name(), dev_set_name()
This patch is part of a larger patch series which will remove
the "char bus_id[20]" name string from struct device. The device
name is managed in the kobject anyway, and without any size
limitation, and just needlessly copied into "struct device".
To set and read the device name dev_name(dev) and dev_set_name(dev)
must be used. If your code uses static kobjects, which it shouldn't
do, "const char *init_name" can be used to statically provide the
name the registered device should have. At registration time, the
init_name field is cleared, to enforce the use of dev_name(dev) to
access the device name at a later time.
We need to get rid of all occurrences of bus_id in the entire tree
to be able to enable the new interface. Please apply this patch,
and possibly convert any remaining remaining occurrences of bus_id.
We want to submit a patch to -next, which will remove bus_id from
"struct device", to find the remaining pieces to convert, and finally
switch over to the new api, which will remove the 20 bytes array
and does no longer have a size limitation.
Thanks,
Kay
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2008-10-30 03:51:46 +00:00
|
|
|
strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
|
2006-03-25 12:19:53 +00:00
|
|
|
sizeof(cap.bus_info));
|
2006-01-05 18:14:04 +00:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &cap, sizeof(cap)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_input i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&i, arg, sizeof(i)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (i.index)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
memset(&i, 0, sizeof(i));
|
2006-01-13 17:19:43 +00:00
|
|
|
strcpy(i.name, "Camera");
|
2006-02-25 06:50:47 +00:00
|
|
|
i.type = V4L2_INPUT_TYPE_CAMERA;
|
2010-12-29 17:29:55 +00:00
|
|
|
i.capabilities = V4L2_IN_CAP_STD;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &i, sizeof(i)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
2006-02-25 06:50:47 +00:00
|
|
|
sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &index, sizeof(index)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
|
2006-01-05 18:14:04 +00:00
|
|
|
{
|
|
|
|
int index;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&index, arg, sizeof(index)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (index != 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_queryctrl qc;
|
|
|
|
u8 i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&qc, arg, sizeof(qc)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_control ctrl;
|
|
|
|
int err = 0;
|
|
|
|
u8 i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (!s->get_ctrl && !s->set_ctrl)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (!s->get_ctrl) {
|
2005-12-11 15:20:08 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
|
2006-01-05 18:14:04 +00:00
|
|
|
if (ctrl.id && ctrl.id == s->qctrl[i].id) {
|
|
|
|
ctrl.value = s->_qctrl[i].default_value;
|
|
|
|
goto exit;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EINVAL;
|
|
|
|
} else
|
|
|
|
err = s->get_ctrl(cam, &ctrl);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
exit:
|
|
|
|
if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
PDBGG("VIDIOC_G_CTRL: id %lu, value %lu",
|
|
|
|
(unsigned long)ctrl.id, (unsigned long)ctrl.value);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return err;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_control ctrl;
|
|
|
|
u8 i;
|
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (!s->set_ctrl)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-03-28 11:50:05 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) {
|
2006-01-05 18:14:04 +00:00
|
|
|
if (ctrl.id == s->qctrl[i].id) {
|
2006-02-25 06:50:47 +00:00
|
|
|
if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
|
|
|
|
return -EINVAL;
|
2006-01-05 18:14:04 +00:00
|
|
|
if (ctrl.value < s->qctrl[i].minimum ||
|
|
|
|
ctrl.value > s->qctrl[i].maximum)
|
|
|
|
return -ERANGE;
|
|
|
|
ctrl.value -= ctrl.value % s->qctrl[i].step;
|
|
|
|
break;
|
|
|
|
}
|
2010-03-28 11:50:05 +00:00
|
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(s->qctrl))
|
|
|
|
return -EINVAL;
|
2006-01-05 18:14:04 +00:00
|
|
|
if ((err = s->set_ctrl(cam, &ctrl)))
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
s->_qctrl[i].default_value = ctrl.value;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
|
|
|
|
(unsigned long)ctrl.id, (unsigned long)ctrl.value);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
cc->pixelaspect.numerator = 1;
|
|
|
|
cc->pixelaspect.denominator = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, cc, sizeof(*cc)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_crop crop = {
|
|
|
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &crop, sizeof(crop)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_crop crop;
|
|
|
|
struct v4l2_rect* rect;
|
|
|
|
struct v4l2_rect* bounds = &(s->cropcap.bounds);
|
|
|
|
struct v4l2_pix_format* pix_format = &(s->pix_format);
|
|
|
|
u8 scale;
|
|
|
|
const enum sn9c102_stream_state stream = cam->stream;
|
|
|
|
const u32 nbuffers = cam->nbuffers;
|
|
|
|
u32 i;
|
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&crop, arg, sizeof(crop)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
rect = &(crop.c);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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.");
|
2007-03-26 19:12:04 +00:00
|
|
|
return -EBUSY;
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
/* 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 < 16)
|
|
|
|
rect->width = 16;
|
|
|
|
if (rect->height < 16)
|
|
|
|
rect->height = 16;
|
|
|
|
if (rect->width > bounds->width)
|
|
|
|
rect->width = bounds->width;
|
|
|
|
if (rect->height > bounds->height)
|
|
|
|
rect->height = bounds->height;
|
|
|
|
if (rect->left < bounds->left)
|
|
|
|
rect->left = bounds->left;
|
|
|
|
if (rect->top < bounds->top)
|
|
|
|
rect->top = bounds->top;
|
|
|
|
if (rect->left + rect->width > bounds->left + bounds->width)
|
|
|
|
rect->left = bounds->left+bounds->width - rect->width;
|
|
|
|
if (rect->top + rect->height > bounds->top + bounds->height)
|
|
|
|
rect->top = bounds->top+bounds->height - rect->height;
|
|
|
|
|
|
|
|
rect->width &= ~15L;
|
|
|
|
rect->height &= ~15L;
|
|
|
|
|
|
|
|
if (SN9C102_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 : ((a / b) < 16 ? 2 : 4)) : 1;
|
|
|
|
} else
|
|
|
|
scale = 1;
|
|
|
|
|
|
|
|
if (cam->stream == STREAM_ON)
|
|
|
|
if ((err = sn9c102_stream_interrupt(cam)))
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &crop, sizeof(crop))) {
|
|
|
|
cam->stream = stream;
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->module_param.force_munmap || cam->io == IO_READ)
|
|
|
|
sn9c102_release_buffers(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
err = sn9c102_set_crop(cam, rect);
|
|
|
|
if (s->set_crop)
|
|
|
|
err += s->set_crop(cam, rect);
|
|
|
|
err += sn9c102_set_scale(cam, scale);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (err) { /* atomic, no rollback in ioctl() */
|
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
|
|
|
DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
|
2009-11-27 16:57:15 +00:00
|
|
|
"use the camera, close and open %s again.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
s->pix_format.width = rect->width/scale;
|
|
|
|
s->pix_format.height = rect->height/scale;
|
|
|
|
memcpy(&(s->_rect), rect, sizeof(*rect));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
|
|
|
|
nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
|
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
|
|
|
DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
|
2009-11-27 16:57:15 +00:00
|
|
|
"use the camera, close and open %s again.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2006-01-05 18:14:04 +00:00
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->io == IO_READ)
|
|
|
|
sn9c102_empty_framequeues(cam);
|
|
|
|
else if (cam->module_param.force_munmap)
|
|
|
|
sn9c102_requeue_outqueue(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->stream = stream;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_frmsizeenum frmsize;
|
|
|
|
|
|
|
|
if (copy_from_user(&frmsize, arg, sizeof(frmsize)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (frmsize.index != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
case BRIDGE_SN9C103:
|
|
|
|
if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X &&
|
|
|
|
frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
|
|
|
|
return -EINVAL;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG &&
|
|
|
|
frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE;
|
|
|
|
frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16;
|
|
|
|
frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16;
|
|
|
|
frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width;
|
|
|
|
frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height;
|
|
|
|
memset(&frmsize.reserved, 0, sizeof(frmsize.reserved));
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &frmsize, sizeof(frmsize)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_fmtdesc fmtd;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (fmtd.index == 0) {
|
|
|
|
strcpy(fmtd.description, "bayer rgb");
|
|
|
|
fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
|
|
|
|
} else if (fmtd.index == 1) {
|
2007-01-08 13:43:56 +00:00
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
case BRIDGE_SN9C103:
|
2007-03-26 19:12:04 +00:00
|
|
|
strcpy(fmtd.description, "compressed");
|
|
|
|
fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
|
2007-01-08 13:43:56 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
strcpy(fmtd.description, "JPEG");
|
|
|
|
fmtd.pixelformat = V4L2_PIX_FMT_JPEG;
|
|
|
|
break;
|
|
|
|
}
|
2006-01-05 18:14:04 +00:00
|
|
|
fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
|
|
|
|
} else
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_format format;
|
2006-02-25 06:50:47 +00:00
|
|
|
struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&format, arg, sizeof(format)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-03-26 19:12:04 +00:00
|
|
|
pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ?
|
|
|
|
V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
|
|
|
|
pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
pfmt->pixelformat == V4L2_PIX_FMT_JPEG)
|
2006-03-25 12:19:53 +00:00
|
|
|
? 0 : (pfmt->width * pfmt->priv) / 8;
|
2006-01-05 18:14:04 +00:00
|
|
|
pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
|
|
|
|
pfmt->field = V4L2_FIELD_NONE;
|
|
|
|
memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &format, sizeof(format)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
|
2006-03-25 12:19:53 +00:00
|
|
|
void __user * arg)
|
2006-01-05 18:14:04 +00:00
|
|
|
{
|
2006-02-25 06:50:47 +00:00
|
|
|
struct sn9c102_sensor* s = &cam->sensor;
|
2006-01-05 18:14:04 +00:00
|
|
|
struct v4l2_format format;
|
|
|
|
struct v4l2_pix_format* pix;
|
|
|
|
struct v4l2_pix_format* pfmt = &(s->pix_format);
|
|
|
|
struct v4l2_rect* bounds = &(s->cropcap.bounds);
|
|
|
|
struct v4l2_rect rect;
|
|
|
|
u8 scale;
|
|
|
|
const enum sn9c102_stream_state stream = cam->stream;
|
|
|
|
const u32 nbuffers = cam->nbuffers;
|
|
|
|
u32 i;
|
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&format, arg, sizeof(format)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
pix = &(format.fmt.pix);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
memcpy(&rect, &(s->_rect), sizeof(rect));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
{ /* 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 : ((a / b) < 16 ? 2 : 4)) : 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
rect.width = scale * pix->width;
|
|
|
|
rect.height = scale * pix->height;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (rect.width < 16)
|
|
|
|
rect.width = 16;
|
|
|
|
if (rect.height < 16)
|
|
|
|
rect.height = 16;
|
|
|
|
if (rect.width > bounds->left + bounds->width - rect.left)
|
|
|
|
rect.width = bounds->left + bounds->width - rect.left;
|
|
|
|
if (rect.height > bounds->top + bounds->height - rect.top)
|
|
|
|
rect.height = bounds->top + bounds->height - rect.top;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
rect.width &= ~15L;
|
|
|
|
rect.height &= ~15L;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
{ /* adjust the scaling factor */
|
|
|
|
u32 a, b;
|
|
|
|
a = rect.width * rect.height;
|
|
|
|
b = pix->width * pix->height;
|
|
|
|
scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pix->width = rect.width / scale;
|
|
|
|
pix->height = rect.height / scale;
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
case BRIDGE_SN9C103:
|
2007-03-26 19:12:04 +00:00
|
|
|
if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
|
|
|
|
pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
|
|
|
|
pix->pixelformat = pfmt->pixelformat;
|
2007-01-08 13:43:56 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
if (pix->pixelformat != V4L2_PIX_FMT_JPEG &&
|
|
|
|
pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
|
|
|
|
pix->pixelformat = pfmt->pixelformat;
|
|
|
|
break;
|
|
|
|
}
|
2006-01-05 18:14:04 +00:00
|
|
|
pix->priv = pfmt->priv; /* bpp */
|
2007-03-26 19:12:04 +00:00
|
|
|
pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ?
|
|
|
|
V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
|
2007-01-08 13:43:56 +00:00
|
|
|
pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X ||
|
|
|
|
pix->pixelformat == V4L2_PIX_FMT_JPEG)
|
2006-03-25 12:19:53 +00:00
|
|
|
? 0 : (pix->width * pix->priv) / 8;
|
2006-01-05 18:14:04 +00:00
|
|
|
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;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->module_param.force_munmap)
|
2005-04-16 22:20:36 +00:00
|
|
|
for (i = 0; i < cam->nbuffers; i++)
|
|
|
|
if (cam->frame[i].vma_use_count) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(3, "VIDIOC_S_FMT failed. Unmap the "
|
|
|
|
"buffers first.");
|
2007-03-26 19:12:04 +00:00
|
|
|
return -EBUSY;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->stream == STREAM_ON)
|
|
|
|
if ((err = sn9c102_stream_interrupt(cam)))
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &format, sizeof(format))) {
|
|
|
|
cam->stream = stream;
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->module_param.force_munmap || cam->io == IO_READ)
|
2005-04-16 22:20:36 +00:00
|
|
|
sn9c102_release_buffers(cam);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (err) { /* atomic, no rollback in ioctl() */
|
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
|
|
|
DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
|
2009-11-27 16:57:15 +00:00
|
|
|
"use the camera, close and open %s again.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
memcpy(pfmt, pix, sizeof(*pix));
|
|
|
|
memcpy(&(s->_rect), &rect, sizeof(rect));
|
|
|
|
|
|
|
|
if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
|
|
|
|
nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
|
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
|
|
|
DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
|
2009-11-27 16:57:15 +00:00
|
|
|
"use the camera, close and open %s again.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2006-01-05 18:14:04 +00:00
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->io == IO_READ)
|
|
|
|
sn9c102_empty_framequeues(cam);
|
|
|
|
else if (cam->module_param.force_munmap)
|
|
|
|
sn9c102_requeue_outqueue(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->stream = stream;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
2007-01-08 13:43:56 +00:00
|
|
|
if (copy_to_user(arg, &cam->compression, sizeof(cam->compression)))
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_jpegcompression jc;
|
|
|
|
const enum sn9c102_stream_state stream = cam->stream;
|
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&jc, arg, sizeof(jc)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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;
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. "
|
|
|
|
"To use the camera, close and open %s again.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EIO;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->compression.quality = jc.quality;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->stream = stream;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_reqbufs(struct sn9c102_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");
|
2007-03-26 19:12:04 +00:00
|
|
|
return -EBUSY;
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < cam->nbuffers; i++)
|
|
|
|
if (cam->frame[i].vma_use_count) {
|
|
|
|
DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are "
|
|
|
|
"still mapped.");
|
2007-03-26 19:12:04 +00:00
|
|
|
return -EBUSY;
|
2006-01-05 18:14:04 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->stream == STREAM_ON)
|
|
|
|
if ((err = sn9c102_stream_interrupt(cam)))
|
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
sn9c102_empty_framequeues(cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
sn9c102_release_buffers(cam);
|
|
|
|
if (rb.count)
|
|
|
|
rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &rb, sizeof(rb))) {
|
|
|
|
sn9c102_release_buffers(cam);
|
|
|
|
cam->io = IO_NONE;
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->io = rb.count ? IO_MMAP : IO_NONE;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_buffer b;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&b, arg, sizeof(b)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
|
|
|
|
b.index >= cam->nbuffers || cam->io != IO_MMAP)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->frame[b.index].vma_use_count)
|
|
|
|
b.flags |= V4L2_BUF_FLAG_MAPPED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &b, sizeof(b)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_buffer b;
|
|
|
|
unsigned long lock_flags;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&b, arg, sizeof(b)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
|
|
|
|
b.index >= cam->nbuffers || cam->io != IO_MMAP)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->frame[b.index].state != F_UNUSED)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->frame[b.index].state = F_QUEUED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
PDBGG("Frame #%lu queued", (unsigned long)b.index);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
|
2006-03-25 12:19:53 +00:00
|
|
|
void __user * arg)
|
2006-01-05 18:14:04 +00:00
|
|
|
{
|
|
|
|
struct v4l2_buffer b;
|
|
|
|
struct sn9c102_frame_t *f;
|
|
|
|
unsigned long lock_flags;
|
2006-02-25 06:50:47 +00:00
|
|
|
long timeout;
|
2007-01-08 13:43:56 +00:00
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_from_user(&b, arg, sizeof(b)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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;
|
2007-01-08 13:43:56 +00:00
|
|
|
if (!cam->module_param.frame_timeout) {
|
|
|
|
err = wait_event_interruptible
|
|
|
|
( cam->wait_frame,
|
|
|
|
(!list_empty(&cam->outqueue)) ||
|
|
|
|
(cam->state & DEV_DISCONNECTED) ||
|
|
|
|
(cam->state & DEV_MISCONFIGURED) );
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
} else {
|
2007-03-26 19:12:04 +00:00
|
|
|
timeout = wait_event_interruptible_timeout
|
|
|
|
( cam->wait_frame,
|
|
|
|
(!list_empty(&cam->outqueue)) ||
|
|
|
|
(cam->state & DEV_DISCONNECTED) ||
|
|
|
|
(cam->state & DEV_MISCONFIGURED),
|
|
|
|
cam->module_param.frame_timeout *
|
|
|
|
1000 * msecs_to_jiffies(1) );
|
|
|
|
if (timeout < 0)
|
|
|
|
return timeout;
|
2007-01-08 13:43:56 +00:00
|
|
|
else if (timeout == 0 &&
|
|
|
|
!(cam->state & DEV_DISCONNECTED)) {
|
|
|
|
DBG(1, "Video frame timeout elapsed");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
2006-01-05 18:14:04 +00:00
|
|
|
if (cam->state & DEV_DISCONNECTED)
|
|
|
|
return -ENODEV;
|
2007-01-08 13:43:56 +00:00
|
|
|
if (cam->state & DEV_MISCONFIGURED)
|
2006-01-05 18:14:04 +00:00
|
|
|
return -EIO;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
spin_lock_irqsave(&cam->queue_lock, lock_flags);
|
|
|
|
f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame);
|
|
|
|
list_del(cam->outqueue.next);
|
|
|
|
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
f->state = F_UNUSED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
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
|
|
|
|
sn9c102_vidioc_streamon(struct sn9c102_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;
|
|
|
|
|
|
|
|
cam->stream = STREAM_ON;
|
|
|
|
|
|
|
|
DBG(3, "Stream on");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_streamoff(struct sn9c102_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 = sn9c102_stream_interrupt(cam)))
|
|
|
|
return err;
|
|
|
|
|
|
|
|
sn9c102_empty_framequeues(cam);
|
|
|
|
|
|
|
|
DBG(3, "Stream off");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_g_parm(struct sn9c102_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
|
|
|
|
sn9c102_vidioc_s_parm(struct sn9c102_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;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (sp.parm.capture.readbuffers == 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
sp.parm.capture.readbuffers = cam->nreadbuffers;
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
|
|
|
|
sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
if (copy_to_user(arg, &sp, sizeof(sp)))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
cam->nreadbuffers = sp.parm.capture.readbuffers;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
static int
|
|
|
|
sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_audio audio;
|
|
|
|
|
|
|
|
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (copy_from_user(&audio, arg, sizeof(audio)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (audio.index != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
strcpy(audio.name, "Microphone");
|
|
|
|
audio.capability = 0;
|
|
|
|
audio.mode = 0;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &audio, sizeof(audio)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_audio audio;
|
|
|
|
|
|
|
|
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (copy_from_user(&audio, arg, sizeof(audio)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
memset(&audio, 0, sizeof(audio));
|
|
|
|
strcpy(audio.name, "Microphone");
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &audio, sizeof(audio)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg)
|
|
|
|
{
|
|
|
|
struct v4l2_audio audio;
|
|
|
|
|
|
|
|
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (copy_from_user(&audio, arg, sizeof(audio)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (audio.index != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 10:04:34 +00:00
|
|
|
static long sn9c102_ioctl_v4l2(struct file *filp,
|
2008-12-30 09:58:20 +00:00
|
|
|
unsigned int cmd, void __user *arg)
|
2006-01-05 18:14:04 +00:00
|
|
|
{
|
2008-08-23 11:32:09 +00:00
|
|
|
struct sn9c102_device *cam = video_drvdata(filp);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
switch (cmd) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
case VIDIOC_QUERYCAP:
|
|
|
|
return sn9c102_vidioc_querycap(cam, arg);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
case VIDIOC_ENUMINPUT:
|
|
|
|
return sn9c102_vidioc_enuminput(cam, arg);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
case VIDIOC_G_INPUT:
|
2006-02-25 06:50:47 +00:00
|
|
|
return sn9c102_vidioc_g_input(cam, arg);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
case VIDIOC_S_INPUT:
|
2006-02-25 06:50:47 +00:00
|
|
|
return sn9c102_vidioc_s_input(cam, arg);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
case VIDIOC_QUERYCTRL:
|
|
|
|
return sn9c102_vidioc_query_ctrl(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_G_CTRL:
|
|
|
|
return sn9c102_vidioc_g_ctrl(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_S_CTRL:
|
|
|
|
return sn9c102_vidioc_s_ctrl(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_CROPCAP:
|
|
|
|
return sn9c102_vidioc_cropcap(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_G_CROP:
|
|
|
|
return sn9c102_vidioc_g_crop(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_S_CROP:
|
|
|
|
return sn9c102_vidioc_s_crop(cam, arg);
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
case VIDIOC_ENUM_FRAMESIZES:
|
|
|
|
return sn9c102_vidioc_enum_framesizes(cam, arg);
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
case VIDIOC_ENUM_FMT:
|
|
|
|
return sn9c102_vidioc_enum_fmt(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_G_FMT:
|
|
|
|
return sn9c102_vidioc_g_fmt(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_TRY_FMT:
|
|
|
|
case VIDIOC_S_FMT:
|
|
|
|
return sn9c102_vidioc_try_s_fmt(cam, cmd, arg);
|
|
|
|
|
|
|
|
case VIDIOC_G_JPEGCOMP:
|
|
|
|
return sn9c102_vidioc_g_jpegcomp(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_S_JPEGCOMP:
|
|
|
|
return sn9c102_vidioc_s_jpegcomp(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_REQBUFS:
|
|
|
|
return sn9c102_vidioc_reqbufs(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_QUERYBUF:
|
|
|
|
return sn9c102_vidioc_querybuf(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_QBUF:
|
|
|
|
return sn9c102_vidioc_qbuf(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_DQBUF:
|
|
|
|
return sn9c102_vidioc_dqbuf(cam, filp, arg);
|
|
|
|
|
|
|
|
case VIDIOC_STREAMON:
|
|
|
|
return sn9c102_vidioc_streamon(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_STREAMOFF:
|
|
|
|
return sn9c102_vidioc_streamoff(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_G_PARM:
|
|
|
|
return sn9c102_vidioc_g_parm(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_S_PARM:
|
|
|
|
return sn9c102_vidioc_s_parm(cam, arg);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
case VIDIOC_ENUMAUDIO:
|
|
|
|
return sn9c102_vidioc_enumaudio(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_G_AUDIO:
|
|
|
|
return sn9c102_vidioc_g_audio(cam, arg);
|
|
|
|
|
|
|
|
case VIDIOC_S_AUDIO:
|
|
|
|
return sn9c102_vidioc_s_audio(cam, arg);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
default:
|
2011-06-26 13:18:03 +00:00
|
|
|
return -ENOTTY;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 10:04:34 +00:00
|
|
|
static long sn9c102_ioctl(struct file *filp,
|
2006-03-25 12:19:53 +00:00
|
|
|
unsigned int cmd, unsigned long arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-08-23 11:32:09 +00:00
|
|
|
struct sn9c102_device *cam = video_drvdata(filp);
|
2005-04-16 22:20:36 +00:00
|
|
|
int err = 0;
|
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
if (mutex_lock_interruptible(&cam->fileop_mutex))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (cam->state & DEV_DISCONNECTED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Device not present");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->state & DEV_MISCONFIGURED) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "The camera is misconfigured. Close and open it "
|
|
|
|
"again.");
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2006-01-13 17:19:43 +00:00
|
|
|
V4LDBG(3, "sn9c102", cmd);
|
|
|
|
|
2008-12-30 09:58:20 +00:00
|
|
|
err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-11 14:55:29 +00:00
|
|
|
mutex_unlock(&cam->fileop_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
/*****************************************************************************/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-30 09:58:20 +00:00
|
|
|
static const struct v4l2_file_operations sn9c102_fops = {
|
2006-01-05 18:14:04 +00:00
|
|
|
.owner = THIS_MODULE,
|
2007-05-02 13:04:03 +00:00
|
|
|
.open = sn9c102_open,
|
2005-04-16 22:20:36 +00:00
|
|
|
.release = sn9c102_release,
|
2010-11-14 14:13:39 +00:00
|
|
|
.unlocked_ioctl = sn9c102_ioctl,
|
2007-05-02 13:04:03 +00:00
|
|
|
.read = sn9c102_read,
|
|
|
|
.poll = sn9c102_poll,
|
|
|
|
.mmap = sn9c102_mmap,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
/* It exists a single interface only. We do not need to validate anything. */
|
|
|
|
static int
|
|
|
|
sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
|
|
|
|
{
|
|
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
|
|
struct sn9c102_device* cam;
|
2008-04-22 17:41:48 +00:00
|
|
|
static unsigned int dev_nr;
|
2006-01-05 18:14:04 +00:00
|
|
|
unsigned int i;
|
2005-04-16 22:20:36 +00:00
|
|
|
int err = 0, r;
|
|
|
|
|
2006-01-13 17:19:43 +00:00
|
|
|
if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
cam->usbdev = udev;
|
|
|
|
|
2006-01-13 17:19:43 +00:00
|
|
|
if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
|
2007-01-08 13:43:56 +00:00
|
|
|
DBG(1, "kzalloc() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cam->v4ldev = video_device_alloc())) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "video_device_alloc() failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sn9c102_read_reg(cam, 0x00);
|
2007-01-08 13:43:56 +00:00
|
|
|
if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) {
|
2007-03-26 19:12:04 +00:00
|
|
|
DBG(1, "Sorry, this is not a SN9C1xx-based camera "
|
|
|
|
"(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
|
2005-04-16 22:20:36 +00:00
|
|
|
err = -ENODEV;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
cam->bridge = id->driver_info;
|
2005-04-16 22:20:36 +00:00
|
|
|
switch (cam->bridge) {
|
|
|
|
case BRIDGE_SN9C101:
|
|
|
|
case BRIDGE_SN9C102:
|
|
|
|
DBG(2, "SN9C10[12] PC Camera Controller detected "
|
2007-03-26 19:12:04 +00:00
|
|
|
"(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C103:
|
|
|
|
DBG(2, "SN9C103 PC Camera Controller detected "
|
2007-03-26 19:12:04 +00:00
|
|
|
"(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
|
2007-01-08 13:43:56 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C105:
|
|
|
|
DBG(2, "SN9C105 PC Camera Controller detected "
|
2007-03-26 19:12:04 +00:00
|
|
|
"(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
|
2007-01-08 13:43:56 +00:00
|
|
|
break;
|
|
|
|
case BRIDGE_SN9C120:
|
|
|
|
DBG(2, "SN9C120 PC Camera Controller detected "
|
2007-03-26 19:12:04 +00:00
|
|
|
"(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-05-02 13:04:03 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) {
|
2005-04-16 22:20:36 +00:00
|
|
|
err = sn9c102_sensor_table[i](cam);
|
|
|
|
if (!err)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-02-25 06:50:47 +00:00
|
|
|
if (!err) {
|
|
|
|
DBG(2, "%s image sensor detected", cam->sensor.name);
|
2005-04-16 22:20:36 +00:00
|
|
|
DBG(3, "Support for %s maintained by %s",
|
2006-02-25 06:50:47 +00:00
|
|
|
cam->sensor.name, cam->sensor.maintainer);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
2007-05-02 13:04:03 +00:00
|
|
|
DBG(1, "No supported image sensor detected for this bridge");
|
2005-04-16 22:20:36 +00:00
|
|
|
err = -ENODEV;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
if (!(cam->bridge & cam->sensor.supported_bridge)) {
|
|
|
|
DBG(1, "Bridge not supported");
|
|
|
|
err = -ENODEV;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (sn9c102_init(cam)) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Initialization failed. I will retry on open().");
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
|
|
|
}
|
|
|
|
|
2007-01-08 13:43:56 +00:00
|
|
|
strcpy(cam->v4ldev->name, "SN9C1xx PC Camera");
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->v4ldev->fops = &sn9c102_fops;
|
|
|
|
cam->v4ldev->release = video_device_release;
|
2008-09-01 18:28:23 +00:00
|
|
|
cam->v4ldev->parent = &udev->dev;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
init_completion(&cam->probe);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
|
2006-03-25 12:19:53 +00:00
|
|
|
video_nr[dev_nr]);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (err) {
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "V4L2 device registration failed");
|
2005-04-16 22:20:36 +00:00
|
|
|
if (err == -ENFILE && video_nr[dev_nr] == -1)
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(1, "Free /dev/videoX node not found");
|
2007-01-08 13:43:56 +00:00
|
|
|
video_nr[dev_nr] = -1;
|
|
|
|
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
|
2007-06-13 17:37:50 +00:00
|
|
|
complete_all(&cam->probe);
|
2007-01-08 13:43:56 +00:00
|
|
|
goto fail;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(2, "V4L2 device registered as %s",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-11-12 14:42:54 +00:00
|
|
|
video_set_drvdata(cam->v4ldev, cam);
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->module_param.force_munmap = force_munmap[dev_nr];
|
2006-02-25 06:50:47 +00:00
|
|
|
cam->module_param.frame_timeout = frame_timeout[dev_nr];
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
|
|
|
|
|
2006-01-13 17:19:43 +00:00
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
2006-10-13 10:17:32 +00:00
|
|
|
err = sn9c102_create_sysfs(cam);
|
2007-01-08 13:43:56 +00:00
|
|
|
if (!err)
|
|
|
|
DBG(2, "Optional device control through 'sysfs' "
|
|
|
|
"interface ready");
|
|
|
|
else
|
|
|
|
DBG(2, "Failed to create optional 'sysfs' interface for "
|
|
|
|
"device controlling. Error #%d", err);
|
|
|
|
#else
|
|
|
|
DBG(2, "Optional device control through 'sysfs' interface disabled");
|
2007-03-26 19:12:04 +00:00
|
|
|
DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
|
|
|
|
"configuration option to enable it.");
|
2006-01-13 17:19:43 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
usb_set_intfdata(intf, cam);
|
2007-06-13 17:37:50 +00:00
|
|
|
kref_init(&cam->kref);
|
|
|
|
usb_get_dev(cam->usbdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
complete_all(&cam->probe);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (cam) {
|
|
|
|
kfree(cam->control_buffer);
|
|
|
|
if (cam->v4ldev)
|
|
|
|
video_device_release(cam->v4ldev);
|
|
|
|
kfree(cam);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void sn9c102_usb_disconnect(struct usb_interface* intf)
|
|
|
|
{
|
2007-06-13 17:37:50 +00:00
|
|
|
struct sn9c102_device* cam;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
down_write(&sn9c102_dev_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
cam = usb_get_intfdata(intf);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-05 18:14:04 +00:00
|
|
|
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (cam->users) {
|
2009-11-27 16:57:15 +00:00
|
|
|
DBG(2, "Device %s is open! Deregistration and memory "
|
|
|
|
"deallocation are deferred.",
|
|
|
|
video_device_node_name(cam->v4ldev));
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->state |= DEV_MISCONFIGURED;
|
|
|
|
sn9c102_stop_transfer(cam);
|
|
|
|
cam->state |= DEV_DISCONNECTED;
|
|
|
|
wake_up_interruptible(&cam->wait_frame);
|
2006-02-25 06:50:47 +00:00
|
|
|
wake_up(&cam->wait_stream);
|
2007-06-13 17:37:50 +00:00
|
|
|
} else
|
2005-04-16 22:20:36 +00:00
|
|
|
cam->state |= DEV_DISCONNECTED;
|
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
wake_up_interruptible_all(&cam->wait_open);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
kref_put(&cam->kref, sn9c102_release_resources);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-06-13 17:37:50 +00:00
|
|
|
up_write(&sn9c102_dev_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct usb_driver sn9c102_usb_driver = {
|
|
|
|
.name = "sn9c102",
|
|
|
|
.id_table = sn9c102_id_table,
|
|
|
|
.probe = sn9c102_usb_probe,
|
|
|
|
.disconnect = sn9c102_usb_disconnect,
|
|
|
|
};
|
|
|
|
|
2011-11-18 17:46:12 +00:00
|
|
|
module_usb_driver(sn9c102_usb_driver);
|