V4L/DVB: c-qcam: convert to V4L2

Note: due to lack of hardware this conversion is untested.
However, it is pretty straightforward so I do not expect any problems.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Hans Verkuil 2010-05-10 03:55:25 -03:00 committed by Mauro Carvalho Chehab
parent 483d67ff0a
commit d71964fb97
2 changed files with 345 additions and 319 deletions

View File

@ -656,7 +656,7 @@ config VIDEO_BWQCAM
config VIDEO_CQCAM config VIDEO_CQCAM
tristate "QuickCam Colour Video For Linux (EXPERIMENTAL)" tristate "QuickCam Colour Video For Linux (EXPERIMENTAL)"
depends on EXPERIMENTAL && PARPORT && VIDEO_V4L1 depends on EXPERIMENTAL && PARPORT && VIDEO_V4L2
help help
This is the video4linux driver for the colour version of the This is the video4linux driver for the colour version of the
Connectix QuickCam. If you have one of these cameras, say Y here, Connectix QuickCam. If you have one of these cameras, say Y here,

View File

@ -33,15 +33,17 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/parport.h> #include <linux/parport.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/version.h>
#include <linux/videodev2.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
struct qcam_device { struct qcam {
struct v4l2_device v4l2_dev;
struct video_device vdev; struct video_device vdev;
struct pardevice *pdev; struct pardevice *pdev;
struct parport *pport; struct parport *pport;
@ -51,7 +53,6 @@ struct qcam_device {
int contrast, brightness, whitebal; int contrast, brightness, whitebal;
int top, left; int top, left;
unsigned int bidirectional; unsigned int bidirectional;
unsigned long in_use;
struct mutex lock; struct mutex lock;
}; };
@ -68,33 +69,45 @@ struct qcam_device {
#define QC_DECIMATION_2 2 #define QC_DECIMATION_2 2
#define QC_DECIMATION_4 4 #define QC_DECIMATION_4 4
#define BANNER "Colour QuickCam for Video4Linux v0.05" #define BANNER "Colour QuickCam for Video4Linux v0.06"
static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
static int probe = 2; static int probe = 2;
static int force_rgb; static int force_rgb;
static int video_nr = -1; static int video_nr = -1;
static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i) /* FIXME: parport=auto would never have worked, surely? --RR */
MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
"probe=<0|1|2> for camera detection method\n"
"force_rgb=<0|1> for RGB data format (default BGR)");
module_param_array(parport, int, NULL, 0);
module_param(probe, int, 0);
module_param(force_rgb, bool, 0);
module_param(video_nr, int, 0);
static struct qcam *qcams[MAX_CAMS];
static unsigned int num_cams;
static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
{ {
/* note: the QC specs refer to the PCAck pin by voltage, not /* note: the QC specs refer to the PCAck pin by voltage, not
software level. PC ports have builtin inverters. */ software level. PC ports have builtin inverters. */
parport_frob_control(qcam->pport, 8, i ? 8 : 0); parport_frob_control(qcam->pport, 8, i ? 8 : 0);
} }
static inline unsigned int qcam_ready1(struct qcam_device *qcam) static inline unsigned int qcam_ready1(struct qcam *qcam)
{ {
return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0; return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
} }
static inline unsigned int qcam_ready2(struct qcam_device *qcam) static inline unsigned int qcam_ready2(struct qcam *qcam)
{ {
return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0; return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
} }
static unsigned int qcam_await_ready1(struct qcam_device *qcam, static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
int value)
{ {
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
unsigned long oldjiffies = jiffies; unsigned long oldjiffies = jiffies;
unsigned int i; unsigned int i;
@ -112,14 +125,15 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam,
} }
/* Probably somebody pulled the plug out. Not much we can do. */ /* Probably somebody pulled the plug out. Not much we can do. */
printk(KERN_ERR "c-qcam: ready1 timeout (%d) %x %x\n", value, v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
parport_read_status(qcam->pport), parport_read_status(qcam->pport),
parport_read_control(qcam->pport)); parport_read_control(qcam->pport));
return 1; return 1;
} }
static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
{ {
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
unsigned long oldjiffies = jiffies; unsigned long oldjiffies = jiffies;
unsigned int i; unsigned int i;
@ -137,14 +151,14 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value)
} }
/* Probably somebody pulled the plug out. Not much we can do. */ /* Probably somebody pulled the plug out. Not much we can do. */
printk(KERN_ERR "c-qcam: ready2 timeout (%d) %x %x %x\n", value, v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
parport_read_status(qcam->pport), parport_read_status(qcam->pport),
parport_read_control(qcam->pport), parport_read_control(qcam->pport),
parport_read_data(qcam->pport)); parport_read_data(qcam->pport));
return 1; return 1;
} }
static int qcam_read_data(struct qcam_device *qcam) static int qcam_read_data(struct qcam *qcam)
{ {
unsigned int idata; unsigned int idata;
@ -159,21 +173,22 @@ static int qcam_read_data(struct qcam_device *qcam)
return idata; return idata;
} }
static int qcam_write_data(struct qcam_device *qcam, unsigned int data) static int qcam_write_data(struct qcam *qcam, unsigned int data)
{ {
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
unsigned int idata; unsigned int idata;
parport_write_data(qcam->pport, data); parport_write_data(qcam->pport, data);
idata = qcam_read_data(qcam); idata = qcam_read_data(qcam);
if (data != idata) { if (data != idata) {
printk(KERN_WARNING "cqcam: sent %x but received %x\n", data, v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
idata); idata);
return 1; return 1;
} }
return 0; return 0;
} }
static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned int data) static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
{ {
if (qcam_write_data(qcam, cmd)) if (qcam_write_data(qcam, cmd))
return -1; return -1;
@ -182,14 +197,14 @@ static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned
return 0; return 0;
} }
static inline int qcam_get(struct qcam_device *qcam, unsigned int cmd) static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
{ {
if (qcam_write_data(qcam, cmd)) if (qcam_write_data(qcam, cmd))
return -1; return -1;
return qcam_read_data(qcam); return qcam_read_data(qcam);
} }
static int qc_detect(struct qcam_device *qcam) static int qc_detect(struct qcam *qcam)
{ {
unsigned int stat, ostat, i, count = 0; unsigned int stat, ostat, i, count = 0;
@ -246,7 +261,7 @@ static int qc_detect(struct qcam_device *qcam)
return 0; return 0;
} }
static void qc_reset(struct qcam_device *qcam) static void qc_reset(struct qcam *qcam)
{ {
parport_write_control(qcam->pport, 0xc); parport_write_control(qcam->pport, 0xc);
parport_write_control(qcam->pport, 0x8); parport_write_control(qcam->pport, 0x8);
@ -258,55 +273,55 @@ static void qc_reset(struct qcam_device *qcam)
/* Reset the QuickCam and program for brightness, contrast, /* Reset the QuickCam and program for brightness, contrast,
* white-balance, and resolution. */ * white-balance, and resolution. */
static void qc_setup(struct qcam_device *q) static void qc_setup(struct qcam *qcam)
{ {
qc_reset(q); qc_reset(qcam);
/* Set the brightness. */ /* Set the brightness. */
qcam_set(q, 11, q->brightness); qcam_set(qcam, 11, qcam->brightness);
/* Set the height and width. These refer to the actual /* Set the height and width. These refer to the actual
CCD area *before* applying the selected decimation. */ CCD area *before* applying the selected decimation. */
qcam_set(q, 17, q->ccd_height); qcam_set(qcam, 17, qcam->ccd_height);
qcam_set(q, 19, q->ccd_width / 2); qcam_set(qcam, 19, qcam->ccd_width / 2);
/* Set top and left. */ /* Set top and left. */
qcam_set(q, 0xd, q->top); qcam_set(qcam, 0xd, qcam->top);
qcam_set(q, 0xf, q->left); qcam_set(qcam, 0xf, qcam->left);
/* Set contrast and white balance. */ /* Set contrast and white balance. */
qcam_set(q, 0x19, q->contrast); qcam_set(qcam, 0x19, qcam->contrast);
qcam_set(q, 0x1f, q->whitebal); qcam_set(qcam, 0x1f, qcam->whitebal);
/* Set the speed. */ /* Set the speed. */
qcam_set(q, 45, 2); qcam_set(qcam, 45, 2);
} }
/* Read some bytes from the camera and put them in the buffer. /* Read some bytes from the camera and put them in the buffer.
nbytes should be a multiple of 3, because bidirectional mode gives nbytes should be a multiple of 3, because bidirectional mode gives
us three bytes at a time. */ us three bytes at a time. */
static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, unsigned int nbytes) static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
{ {
unsigned int bytes = 0; unsigned int bytes = 0;
qcam_set_ack(q, 0); qcam_set_ack(qcam, 0);
if (q->bidirectional) { if (qcam->bidirectional) {
/* It's a bidirectional port */ /* It's a bidirectional port */
while (bytes < nbytes) { while (bytes < nbytes) {
unsigned int lo1, hi1, lo2, hi2; unsigned int lo1, hi1, lo2, hi2;
unsigned char r, g, b; unsigned char r, g, b;
if (qcam_await_ready2(q, 1)) if (qcam_await_ready2(qcam, 1))
return bytes; return bytes;
lo1 = parport_read_data(q->pport) >> 1; lo1 = parport_read_data(qcam->pport) >> 1;
hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
qcam_set_ack(q, 1); qcam_set_ack(qcam, 1);
if (qcam_await_ready2(q, 0)) if (qcam_await_ready2(qcam, 0))
return bytes; return bytes;
lo2 = parport_read_data(q->pport) >> 1; lo2 = parport_read_data(qcam->pport) >> 1;
hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
qcam_set_ack(q, 0); qcam_set_ack(qcam, 0);
r = lo1 | ((hi1 & 1) << 7); r = lo1 | ((hi1 & 1) << 7);
g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1); g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
b = lo2 | ((hi2 & 1) << 7); b = lo2 | ((hi2 & 1) << 7);
@ -328,14 +343,14 @@ static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, u
while (bytes < nbytes) { while (bytes < nbytes) {
unsigned int hi, lo; unsigned int hi, lo;
if (qcam_await_ready1(q, 1)) if (qcam_await_ready1(qcam, 1))
return bytes; return bytes;
hi = (parport_read_status(q->pport) & 0xf0); hi = (parport_read_status(qcam->pport) & 0xf0);
qcam_set_ack(q, 1); qcam_set_ack(qcam, 1);
if (qcam_await_ready1(q, 0)) if (qcam_await_ready1(qcam, 0))
return bytes; return bytes;
lo = (parport_read_status(q->pport) & 0xf0); lo = (parport_read_status(qcam->pport) & 0xf0);
qcam_set_ack(q, 0); qcam_set_ack(qcam, 0);
/* flip some bits */ /* flip some bits */
rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88; rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
if (i >= 2) { if (i >= 2) {
@ -361,10 +376,11 @@ get_fragment:
#define BUFSZ 150 #define BUFSZ 150
static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long len) static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
{ {
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
unsigned lines, pixelsperline, bitsperxfer; unsigned lines, pixelsperline, bitsperxfer;
unsigned int is_bi_dir = q->bidirectional; unsigned int is_bi_dir = qcam->bidirectional;
size_t wantlen, outptr = 0; size_t wantlen, outptr = 0;
char tmpbuf[BUFSZ]; char tmpbuf[BUFSZ];
@ -373,10 +389,10 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
/* Wait for camera to become ready */ /* Wait for camera to become ready */
for (;;) { for (;;) {
int i = qcam_get(q, 41); int i = qcam_get(qcam, 41);
if (i == -1) { if (i == -1) {
qc_setup(q); qc_setup(qcam);
return -EIO; return -EIO;
} }
if ((i & 0x80) == 0) if ((i & 0x80) == 0)
@ -384,25 +400,25 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
schedule(); schedule();
} }
if (qcam_set(q, 7, (q->mode | (is_bi_dir ? 1 : 0)) + 1)) if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
return -EIO; return -EIO;
lines = q->height; lines = qcam->height;
pixelsperline = q->width; pixelsperline = qcam->width;
bitsperxfer = (is_bi_dir) ? 24 : 8; bitsperxfer = (is_bi_dir) ? 24 : 8;
if (is_bi_dir) { if (is_bi_dir) {
/* Turn the port around */ /* Turn the port around */
parport_data_reverse(q->pport); parport_data_reverse(qcam->pport);
mdelay(3); mdelay(3);
qcam_set_ack(q, 0); qcam_set_ack(qcam, 0);
if (qcam_await_ready1(q, 1)) { if (qcam_await_ready1(qcam, 1)) {
qc_setup(q); qc_setup(qcam);
return -EIO; return -EIO;
} }
qcam_set_ack(q, 1); qcam_set_ack(qcam, 1);
if (qcam_await_ready1(q, 0)) { if (qcam_await_ready1(qcam, 0)) {
qc_setup(q); qc_setup(qcam);
return -EIO; return -EIO;
} }
} }
@ -413,7 +429,7 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
size_t t, s; size_t t, s;
s = (wantlen > BUFSZ) ? BUFSZ : wantlen; s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
t = qcam_read_bytes(q, tmpbuf, s); t = qcam_read_bytes(qcam, tmpbuf, s);
if (outptr < len) { if (outptr < len) {
size_t sz = len - outptr; size_t sz = len - outptr;
@ -432,10 +448,10 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
len = outptr; len = outptr;
if (wantlen) { if (wantlen) {
printk(KERN_ERR "qcam: short read.\n"); v4l2_err(v4l2_dev, "short read.\n");
if (is_bi_dir) if (is_bi_dir)
parport_data_forward(q->pport); parport_data_forward(qcam->pport);
qc_setup(q); qc_setup(qcam);
return len; return len;
} }
@ -443,49 +459,49 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
int l; int l;
do { do {
l = qcam_read_bytes(q, tmpbuf, 3); l = qcam_read_bytes(qcam, tmpbuf, 3);
cond_resched(); cond_resched();
} while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
if (force_rgb) { if (force_rgb) {
if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
printk(KERN_ERR "qcam: bad EOF\n"); v4l2_err(v4l2_dev, "bad EOF\n");
} else { } else {
if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
printk(KERN_ERR "qcam: bad EOF\n"); v4l2_err(v4l2_dev, "bad EOF\n");
} }
qcam_set_ack(q, 0); qcam_set_ack(qcam, 0);
if (qcam_await_ready1(q, 1)) { if (qcam_await_ready1(qcam, 1)) {
printk(KERN_ERR "qcam: no ack after EOF\n"); v4l2_err(v4l2_dev, "no ack after EOF\n");
parport_data_forward(q->pport); parport_data_forward(qcam->pport);
qc_setup(q); qc_setup(qcam);
return len; return len;
} }
parport_data_forward(q->pport); parport_data_forward(qcam->pport);
mdelay(3); mdelay(3);
qcam_set_ack(q, 1); qcam_set_ack(qcam, 1);
if (qcam_await_ready1(q, 0)) { if (qcam_await_ready1(qcam, 0)) {
printk(KERN_ERR "qcam: no ack to port turnaround\n"); v4l2_err(v4l2_dev, "no ack to port turnaround\n");
qc_setup(q); qc_setup(qcam);
return len; return len;
} }
} else { } else {
int l; int l;
do { do {
l = qcam_read_bytes(q, tmpbuf, 1); l = qcam_read_bytes(qcam, tmpbuf, 1);
cond_resched(); cond_resched();
} while (l && tmpbuf[0] == 0x7e); } while (l && tmpbuf[0] == 0x7e);
l = qcam_read_bytes(q, tmpbuf + 1, 2); l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
if (force_rgb) { if (force_rgb) {
if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
printk(KERN_ERR "qcam: bad EOF\n"); v4l2_err(v4l2_dev, "bad EOF\n");
} else { } else {
if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
printk(KERN_ERR "qcam: bad EOF\n"); v4l2_err(v4l2_dev, "bad EOF\n");
} }
} }
qcam_write_data(q, 0); qcam_write_data(qcam, 0);
return len; return len;
} }
@ -493,184 +509,202 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
* Video4linux interfacing * Video4linux interfacing
*/ */
static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) static int qcam_querycap(struct file *file, void *priv,
struct v4l2_capability *vcap)
{ {
struct video_device *dev = video_devdata(file); struct qcam *qcam = video_drvdata(file);
struct qcam_device *qcam = (struct qcam_device *)dev;
switch (cmd) { strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
case VIDIOCGCAP: strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
{ strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
struct video_capability *b = arg; vcap->version = KERNEL_VERSION(0, 0, 3);
vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
strcpy(b->name, "Quickcam");
b->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
b->channels = 1;
b->audios = 0;
b->maxwidth = 320;
b->maxheight = 240;
b->minwidth = 80;
b->minheight = 60;
return 0;
}
case VIDIOCGCHAN:
{
struct video_channel *v = arg;
if (v->channel != 0)
return -EINVAL;
v->flags = 0;
v->tuners = 0;
/* Good question.. its composite or SVHS so.. */
v->type = VIDEO_TYPE_CAMERA;
strcpy(v->name, "Camera");
return 0;
}
case VIDIOCSCHAN:
{
struct video_channel *v = arg;
if (v->channel != 0)
return -EINVAL;
return 0;
}
case VIDIOCGTUNER:
{
struct video_tuner *v = arg;
if (v->tuner)
return -EINVAL;
memset(v, 0, sizeof(*v));
strcpy(v->name, "Format");
v->mode = VIDEO_MODE_AUTO;
return 0;
}
case VIDIOCSTUNER:
{
struct video_tuner *v = arg;
if (v->tuner)
return -EINVAL;
if (v->mode != VIDEO_MODE_AUTO)
return -EINVAL;
return 0;
}
case VIDIOCGPICT:
{
struct video_picture *p = arg;
p->colour = 0x8000;
p->hue = 0x8000;
p->brightness = qcam->brightness << 8;
p->contrast = qcam->contrast << 8;
p->whiteness = qcam->whitebal << 8;
p->depth = 24;
p->palette = VIDEO_PALETTE_RGB24;
return 0;
}
case VIDIOCSPICT:
{
struct video_picture *p = arg;
/*
* Sanity check args
*/
if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24)
return -EINVAL;
/*
* Now load the camera.
*/
qcam->brightness = p->brightness >> 8;
qcam->contrast = p->contrast >> 8;
qcam->whitebal = p->whiteness >> 8;
mutex_lock(&qcam->lock);
parport_claim_or_block(qcam->pdev);
qc_setup(qcam);
parport_release(qcam->pdev);
mutex_unlock(&qcam->lock);
return 0;
}
case VIDIOCSWIN:
{
struct video_window *vw = arg;
if (vw->flags)
return -EINVAL;
if (vw->clipcount)
return -EINVAL;
if (vw->height < 60 || vw->height > 240)
return -EINVAL;
if (vw->width < 80 || vw->width > 320)
return -EINVAL;
qcam->width = 80;
qcam->height = 60;
qcam->mode = QC_DECIMATION_4;
if (vw->width >= 160 && vw->height >= 120) {
qcam->width = 160;
qcam->height = 120;
qcam->mode = QC_DECIMATION_2;
}
if (vw->width >= 320 && vw->height >= 240) {
qcam->width = 320;
qcam->height = 240;
qcam->mode = QC_DECIMATION_1;
}
qcam->mode |= QC_MILLIONS;
#if 0
if (vw->width >= 640 && vw->height >= 480) {
qcam->width = 640;
qcam->height = 480;
qcam->mode = QC_BILLIONS | QC_DECIMATION_1;
}
#endif
/* Ok we figured out what to use from our
wide choice */
mutex_lock(&qcam->lock);
parport_claim_or_block(qcam->pdev);
qc_setup(qcam);
parport_release(qcam->pdev);
mutex_unlock(&qcam->lock);
return 0;
}
case VIDIOCGWIN:
{
struct video_window *vw = arg;
memset(vw, 0, sizeof(*vw));
vw->width = qcam->width;
vw->height = qcam->height;
return 0;
}
case VIDIOCKEY:
return 0;
case VIDIOCCAPTURE:
case VIDIOCGFBUF:
case VIDIOCSFBUF:
case VIDIOCGFREQ:
case VIDIOCSFREQ:
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
return -EINVAL;
default:
return -ENOIOCTLCMD;
}
return 0; return 0;
} }
static long qcam_ioctl(struct file *file, static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
unsigned int cmd, unsigned long arg)
{ {
return video_usercopy(file, cmd, arg, qcam_do_ioctl); if (vin->index > 0)
return -EINVAL;
strlcpy(vin->name, "Camera", sizeof(vin->name));
vin->type = V4L2_INPUT_TYPE_CAMERA;
vin->audioset = 0;
vin->tuner = 0;
vin->std = 0;
vin->status = 0;
return 0;
}
static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
{
*inp = 0;
return 0;
}
static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
{
return (inp > 0) ? -EINVAL : 0;
}
static int qcam_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
switch (qc->id) {
case V4L2_CID_BRIGHTNESS:
return v4l2_ctrl_query_fill(qc, 0, 255, 1, 240);
case V4L2_CID_CONTRAST:
return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192);
case V4L2_CID_GAMMA:
return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
}
return -EINVAL;
}
static int qcam_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct qcam *qcam = video_drvdata(file);
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = qcam->brightness;
break;
case V4L2_CID_CONTRAST:
ctrl->value = qcam->contrast;
break;
case V4L2_CID_GAMMA:
ctrl->value = qcam->whitebal;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int qcam_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct qcam *qcam = video_drvdata(file);
int ret = 0;
mutex_lock(&qcam->lock);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
qcam->brightness = ctrl->value;
break;
case V4L2_CID_CONTRAST:
qcam->contrast = ctrl->value;
break;
case V4L2_CID_GAMMA:
qcam->whitebal = ctrl->value;
break;
default:
ret = -EINVAL;
break;
}
if (ret == 0) {
parport_claim_or_block(qcam->pdev);
qc_setup(qcam);
parport_release(qcam->pdev);
}
mutex_unlock(&qcam->lock);
return ret;
}
static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct qcam *qcam = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
pix->width = qcam->width;
pix->height = qcam->height;
pix->pixelformat = V4L2_PIX_FMT_RGB24;
pix->field = V4L2_FIELD_NONE;
pix->bytesperline = 3 * qcam->width;
pix->sizeimage = 3 * qcam->width * qcam->height;
/* Just a guess */
pix->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct v4l2_pix_format *pix = &fmt->fmt.pix;
if (pix->height < 60 || pix->width < 80) {
pix->height = 60;
pix->width = 80;
} else if (pix->height < 120 || pix->width < 160) {
pix->height = 120;
pix->width = 160;
} else {
pix->height = 240;
pix->width = 320;
}
pix->pixelformat = V4L2_PIX_FMT_RGB24;
pix->field = V4L2_FIELD_NONE;
pix->bytesperline = 3 * pix->width;
pix->sizeimage = 3 * pix->width * pix->height;
/* Just a guess */
pix->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct qcam *qcam = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
if (ret)
return ret;
switch (pix->height) {
case 60:
qcam->mode = QC_DECIMATION_4;
break;
case 120:
qcam->mode = QC_DECIMATION_2;
break;
default:
qcam->mode = QC_DECIMATION_1;
break;
}
mutex_lock(&qcam->lock);
qcam->mode |= QC_MILLIONS;
qcam->height = pix->height;
qcam->width = pix->width;
parport_claim_or_block(qcam->pdev);
qc_setup(qcam);
parport_release(qcam->pdev);
mutex_unlock(&qcam->lock);
return 0;
}
static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
{
static struct v4l2_fmtdesc formats[] = {
{ 0, 0, 0,
"RGB 8:8:8", V4L2_PIX_FMT_RGB24,
{ 0, 0, 0, 0 }
},
};
enum v4l2_buf_type type = fmt->type;
if (fmt->index > 0)
return -EINVAL;
*fmt = formats[fmt->index];
fmt->type = type;
return 0;
} }
static ssize_t qcam_read(struct file *file, char __user *buf, static ssize_t qcam_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct video_device *v = video_devdata(file); struct qcam *qcam = video_drvdata(file);
struct qcam_device *qcam = (struct qcam_device *)v;
int len; int len;
mutex_lock(&qcam->lock); mutex_lock(&qcam->lock);
@ -682,81 +716,80 @@ static ssize_t qcam_read(struct file *file, char __user *buf,
return len; return len;
} }
static int qcam_exclusive_open(struct file *file)
{
struct video_device *dev = video_devdata(file);
struct qcam_device *qcam = (struct qcam_device *)dev;
return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0;
}
static int qcam_exclusive_release(struct file *file)
{
struct video_device *dev = video_devdata(file);
struct qcam_device *qcam = (struct qcam_device *)dev;
clear_bit(0, &qcam->in_use);
return 0;
}
/* video device template */
static const struct v4l2_file_operations qcam_fops = { static const struct v4l2_file_operations qcam_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = qcam_exclusive_open, .ioctl = video_ioctl2,
.release = qcam_exclusive_release,
.ioctl = qcam_ioctl,
.read = qcam_read, .read = qcam_read,
}; };
static struct video_device qcam_template = { static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
.name = "Colour QuickCam", .vidioc_querycap = qcam_querycap,
.fops = &qcam_fops, .vidioc_g_input = qcam_g_input,
.release = video_device_release_empty, .vidioc_s_input = qcam_s_input,
.vidioc_enum_input = qcam_enum_input,
.vidioc_queryctrl = qcam_queryctrl,
.vidioc_g_ctrl = qcam_g_ctrl,
.vidioc_s_ctrl = qcam_s_ctrl,
.vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
}; };
/* Initialize the QuickCam driver control structure. */ /* Initialize the QuickCam driver control structure. */
static struct qcam_device *qcam_init(struct parport *port) static struct qcam *qcam_init(struct parport *port)
{ {
struct qcam_device *q; struct qcam *qcam;
struct v4l2_device *v4l2_dev;
q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
if (q == NULL) if (qcam == NULL)
return NULL; return NULL;
q->pport = port; v4l2_dev = &qcam->v4l2_dev;
q->pdev = parport_register_device(port, "c-qcam", NULL, NULL, strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
NULL, 0, NULL);
q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0; if (v4l2_device_register(NULL, v4l2_dev) < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
if (q->pdev == NULL) {
printk(KERN_ERR "c-qcam: couldn't register for %s.\n",
port->name);
kfree(q);
return NULL; return NULL;
} }
memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); qcam->pport = port;
qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
NULL, 0, NULL);
mutex_init(&q->lock); qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
q->width = q->ccd_width = 320;
q->height = q->ccd_height = 240; if (qcam->pdev == NULL) {
q->mode = QC_MILLIONS | QC_DECIMATION_1; v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
q->contrast = 192; kfree(qcam);
q->brightness = 240; return NULL;
q->whitebal = 128; }
q->top = 1;
q->left = 14; strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
return q; qcam->vdev.v4l2_dev = v4l2_dev;
qcam->vdev.fops = &qcam_fops;
qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
qcam->vdev.release = video_device_release_empty;
video_set_drvdata(&qcam->vdev, qcam);
mutex_init(&qcam->lock);
qcam->width = qcam->ccd_width = 320;
qcam->height = qcam->ccd_height = 240;
qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
qcam->contrast = 192;
qcam->brightness = 240;
qcam->whitebal = 128;
qcam->top = 1;
qcam->left = 14;
return qcam;
} }
static struct qcam_device *qcams[MAX_CAMS];
static unsigned int num_cams;
static int init_cqcam(struct parport *port) static int init_cqcam(struct parport *port)
{ {
struct qcam_device *qcam; struct qcam *qcam;
struct v4l2_device *v4l2_dev;
if (parport[0] != -1) { if (parport[0] != -1) {
/* The user gave specific instructions */ /* The user gave specific instructions */
@ -777,6 +810,8 @@ static int init_cqcam(struct parport *port)
if (qcam == NULL) if (qcam == NULL)
return -ENODEV; return -ENODEV;
v4l2_dev = &qcam->v4l2_dev;
parport_claim_or_block(qcam->pdev); parport_claim_or_block(qcam->pdev);
qc_reset(qcam); qc_reset(qcam);
@ -793,14 +828,14 @@ static int init_cqcam(struct parport *port)
parport_release(qcam->pdev); parport_release(qcam->pdev);
if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
printk(KERN_ERR "Unable to register Colour QuickCam on %s\n", v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
qcam->pport->name); qcam->pport->name);
parport_unregister_device(qcam->pdev); parport_unregister_device(qcam->pdev);
kfree(qcam); kfree(qcam);
return -ENODEV; return -ENODEV;
} }
printk(KERN_INFO "%s: Colour QuickCam found on %s\n", v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
video_device_node_name(&qcam->vdev), qcam->pport->name); video_device_node_name(&qcam->vdev), qcam->pport->name);
qcams[num_cams++] = qcam; qcams[num_cams++] = qcam;
@ -808,7 +843,7 @@ static int init_cqcam(struct parport *port)
return 0; return 0;
} }
static void close_cqcam(struct qcam_device *qcam) static void close_cqcam(struct qcam *qcam)
{ {
video_unregister_device(&qcam->vdev); video_unregister_device(&qcam->vdev);
parport_unregister_device(qcam->pdev); parport_unregister_device(qcam->pdev);
@ -833,7 +868,7 @@ static struct parport_driver cqcam_driver = {
static int __init cqcam_init(void) static int __init cqcam_init(void)
{ {
printk(BANNER "\n"); printk(KERN_INFO BANNER "\n");
return parport_register_driver(&cqcam_driver); return parport_register_driver(&cqcam_driver);
} }
@ -852,14 +887,5 @@ MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
MODULE_DESCRIPTION(BANNER); MODULE_DESCRIPTION(BANNER);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* FIXME: parport=auto would never have worked, surely? --RR */
MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
"probe=<0|1|2> for camera detection method\n"
"force_rgb=<0|1> for RGB data format (default BGR)");
module_param_array(parport, int, NULL, 0);
module_param(probe, int, 0);
module_param(force_rgb, bool, 0);
module_param(video_nr, int, 0);
module_init(cqcam_init); module_init(cqcam_init);
module_exit(cqcam_cleanup); module_exit(cqcam_cleanup);