Commit 195a4ef6 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

V4L/DVB (6585): Convert em28xx to video_ioctl2

Uses the newer ioctl handler at videodev. This patch also cleans up some
bad logic at the driver and do CodingStyle and other cleanups at the
resulting driver.

Also, since VIDIOCMBUF were not working, the V4L1 compat code were removed.
The compat code will eventually be re-inserted, if we find a clean way for
implementing compatibility with the old API.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 9e31ced8
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
#define DRIVER_NAME "em28xx" #define DRIVER_NAME "em28xx"
#define DRIVER_DESC "Empia em28xx based USB video device driver" #define DRIVER_DESC "Empia em28xx based USB video device driver"
#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 0, 1) #define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
#define em28xx_videodbg(fmt, arg...) do {\ #define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \ if (video_debug) \
...@@ -202,8 +202,6 @@ static void video_mux(struct em28xx *dev, int index) ...@@ -202,8 +202,6 @@ static void video_mux(struct em28xx *dev, int index)
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,route.input,dev->ctl_ainput);
if (dev->has_msp34xx) { if (dev->has_msp34xx) {
if (dev->i2s_speed) if (dev->i2s_speed)
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
...@@ -264,1297 +262,1391 @@ static void res_free(struct em28xx_fh *fh) ...@@ -264,1297 +262,1391 @@ static void res_free(struct em28xx_fh *fh)
} }
/* /*
* em28xx_v4l2_open() * em28xx_vm_open()
* inits the device and starts isoc transfer
*/ */
static int em28xx_v4l2_open(struct inode *inode, struct file *filp) static void em28xx_vm_open(struct vm_area_struct *vma)
{ {
int minor = iminor(inode); struct em28xx_frame_t *f = vma->vm_private_data;
int errCode = 0; f->vma_use_count++;
struct em28xx *h,*dev = NULL; }
struct em28xx_fh *fh;
list_for_each_entry(h, &em28xx_devlist, devlist) { /*
if (h->vdev->minor == minor) { * em28xx_vm_close()
dev = h; */
dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; static void em28xx_vm_close(struct vm_area_struct *vma)
} {
if (h->vbi_dev->minor == minor) { /* NOTE: buffers are not freed here */
dev = h; struct em28xx_frame_t *f = vma->vm_private_data;
dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
}
}
if (NULL == dev)
return -ENODEV;
em28xx_videodbg("open minor=%d type=%s users=%d\n", if (f->vma_use_count)
minor,v4l2_type_names[dev->type],dev->users); f->vma_use_count--;
}
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); static struct vm_operations_struct em28xx_vm_ops = {
.open = em28xx_vm_open,
.close = em28xx_vm_close,
};
if (!fh) {
em28xx_errdev("em28xx-video.c: Out of memory?!\n"); /*
return -ENOMEM; * em28xx_get_ctrl()
* return the current saturation, brightness or contrast, mute state
*/
static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = dev->mute;
return 0;
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = dev->volume;
return 0;
default:
return -EINVAL;
} }
mutex_lock(&dev->lock); }
fh->dev = dev;
filp->private_data = fh;
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { /*
em28xx_set_alternate(dev); * em28xx_set_ctrl()
* mute or set new saturation, brightness or contrast
*/
static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value != dev->mute) {
dev->mute = ctrl->value;
em28xx_audio_usb_mute(dev, ctrl->value);
return em28xx_audio_analog_set(dev);
}
return 0;
case V4L2_CID_AUDIO_VOLUME:
dev->volume = ctrl->value;
return em28xx_audio_analog_set(dev);
default:
return -EINVAL;
}
}
dev->width = norm_maxw(dev); /*
dev->height = norm_maxh(dev); * em28xx_stream_interrupt()
dev->frame_size = dev->width * dev->height * 2; * stops streaming
dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */ */
dev->bytesperline = dev->width * 2; static int em28xx_stream_interrupt(struct em28xx *dev)
dev->hscale = 0; {
dev->vscale = 0; int rc = 0;
em28xx_capture_start(dev, 1); /* stop reading from the device */
em28xx_resolution_set(dev);
dev->stream = STREAM_INTERRUPT;
rc = wait_event_timeout(dev->wait_stream,
(dev->stream == STREAM_OFF) ||
(dev->state & DEV_DISCONNECTED),
EM28XX_URB_TIMEOUT);
/* start the transfer */ if (rc) {
errCode = em28xx_init_isoc(dev); dev->state |= DEV_MISCONFIGURED;
if (errCode) em28xx_videodbg("device is misconfigured; close and "
goto err; "open /dev/video%d again\n",
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
return rc;
}
em28xx_empty_framequeues(dev); return 0;
}
static int check_dev(struct em28xx *dev)
{
if (dev->state & DEV_DISCONNECTED) {
em28xx_errdev("v4l2 ioctl: device not present\n");
return -ENODEV;
} }
dev->users++; if (dev->state & DEV_MISCONFIGURED) {
em28xx_errdev("v4l2 ioctl: device is misconfigured; "
"close and open it again\n");
return -EIO;
}
return 0;
}
err: static void get_scale(struct em28xx *dev,
mutex_unlock(&dev->lock); unsigned int width, unsigned int height,
return errCode; unsigned int *hscale, unsigned int *vscale)
{
unsigned int maxw = norm_maxw(dev);
unsigned int maxh = norm_maxh(dev);
*hscale = (((unsigned long)maxw) << 12) / width - 4096L;
if (*hscale >= 0x4000)
*hscale = 0x3fff;
*vscale = (((unsigned long)maxh) << 12) / height - 4096L;
if (*vscale >= 0x4000)
*vscale = 0x3fff;
} }
/* /* ------------------------------------------------------------------
* em28xx_realease_resources() IOCTL vidioc handling
* unregisters the v4l2,i2c and usb devices ------------------------------------------------------------------*/
* called when the device gets disconected or at module unload
*/ static int vidioc_g_fmt_cap(struct file *file, void *priv,
static void em28xx_release_resources(struct em28xx *dev) struct v4l2_format *f)
{ {
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
/*FIXME: I2C IR should be disconnected */ mutex_lock(&dev->lock);
em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n", f->fmt.pix.width = dev->width;
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, f->fmt.pix.height = dev->height;
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
list_del(&dev->devlist); f->fmt.pix.bytesperline = dev->bytesperline;
video_unregister_device(dev->vdev); f->fmt.pix.sizeimage = dev->frame_size;
video_unregister_device(dev->vbi_dev); f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
em28xx_i2c_unregister(dev);
usb_put_dev(dev->udev);
/* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
f->fmt.pix.field = dev->interlaced ?
V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
/* Mark device as unused */ mutex_unlock(&dev->lock);
em28xx_devused&=~(1<<dev->devno); return 0;
} }
/* static int vidioc_try_fmt_cap(struct file *file, void *priv,
* em28xx_v4l2_close() struct v4l2_format *f)
* stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
*/
static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
{ {
struct em28xx_fh *fh = filp->private_data; struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev; struct em28xx *dev = fh->dev;
int errCode; int width = f->fmt.pix.width;
int height = f->fmt.pix.height;
unsigned int maxw = norm_maxw(dev);
unsigned int maxh = norm_maxh(dev);
unsigned int hscale, vscale;
/* width must even because of the YUYV format
height must be even because of interlacing */
height &= 0xfffe;
width &= 0xfffe;
em28xx_videodbg("users=%d\n", dev->users); if (height < 32)
height = 32;
if (height > maxh)
height = maxh;
if (width < 48)
width = 48;
if (width > maxw)
width = maxw;
mutex_lock(&dev->lock);
if (res_check(fh)) if (dev->is_em2800) {
res_free(fh); /* the em2800 can only scale down to 50% */
if (height % (maxh / 2))
height = maxh;
if (width % (maxw / 2))
width = maxw;
/* according to empiatech support */
/* the MaxPacketSize is to small to support */
/* framesizes larger than 640x480 @ 30 fps */
/* or 640x576 @ 25 fps. As this would cut */
/* of a part of the image we prefer */
/* 360x576 or 360x480 for now */
if (width == maxw && height == maxh)
width /= 2;
}
mutex_lock(&dev->lock); get_scale(dev, width, height, &hscale, &vscale);
if (dev->users == 1) { width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
em28xx_uninit_isoc(dev); height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
em28xx_release_buffers(dev);
dev->io = IO_NONE;
/* the device is already disconnect, f->fmt.pix.width = width;
free the remaining resources */ f->fmt.pix.height = height;
if (dev->state & DEV_DISCONNECTED) { f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
em28xx_release_resources(dev); f->fmt.pix.bytesperline = width * 2;
mutex_unlock(&dev->lock); f->fmt.pix.sizeimage = width * 2 * height;
kfree(dev); f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0; f->fmt.pix.field = V4L2_FIELD_INTERLACED;
}
/* set alternate 0 */
dev->alt = 0;
em28xx_videodbg("setting alternate 0\n");
errCode = usb_set_interface(dev->udev, 0, 0);
if (errCode < 0) {
em28xx_errdev("cannot change alternate number to "
"0 (error=%i)\n", errCode);
}
}
kfree(fh);
dev->users--;
wake_up_interruptible_nr(&dev->open, 1);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return 0;
} }
/* static int vidioc_s_fmt_cap(struct file *file, void *priv,
* em28xx_v4l2_read() struct v4l2_format *f)
* will allocate buffers when called for the first time
*/
static ssize_t
em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
loff_t * f_pos)
{ {
struct em28xx_frame_t *f, *i; struct em28xx_fh *fh = priv;
unsigned long lock_flags; struct em28xx *dev = fh->dev;
int ret = 0; int rc, i;
struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
/* FIXME: read() is not prepared to allow changing the video rc = check_dev(dev);
resolution while streaming. Seems a bug at em28xx_set_fmt if (rc < 0)
*/ return rc;
if (unlikely(res_get(fh) < 0)) vidioc_try_fmt_cap(file, priv, f);
return -EBUSY;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) for (i = 0; i < dev->num_frames; i++)
em28xx_videodbg("V4l2_Buf_type_videocapture is set\n"); if (dev->frame[i].vma_use_count) {
em28xx_videodbg("VIDIOC_S_FMT failed. "
if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) { "Unmap the buffers first.\n");
em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n"); rc = -EINVAL;
em28xx_videodbg("not supported yet! ...\n"); goto err;
if (copy_to_user(buf, "", 1)) {
mutex_unlock(&dev->lock);
return -EFAULT;
} }
mutex_unlock(&dev->lock);
return (1); /* stop io in case it is already in progress */
} if (dev->stream == STREAM_ON) {
if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n"); rc = em28xx_stream_interrupt(dev);
em28xx_videodbg("not supported yet! ...\n"); if (rc < 0)
if (copy_to_user(buf, "", 1)) { goto err;
mutex_unlock(&dev->lock);
return -EFAULT;
}
mutex_unlock(&dev->lock);
return (1);
} }
if (dev->state & DEV_DISCONNECTED) { em28xx_release_buffers(dev);
em28xx_videodbg("device not present\n"); dev->io = IO_NONE;
mutex_unlock(&dev->lock);
return -ENODEV;
}
if (dev->state & DEV_MISCONFIGURED) { /* set new image size */
em28xx_videodbg("device misconfigured; close and open it again\n"); dev->width = f->fmt.pix.width;
mutex_unlock(&dev->lock); dev->height = f->fmt.pix.height;
return -EIO; dev->frame_size = dev->width * dev->height * 2;
} dev->field_size = dev->frame_size >> 1;
dev->bytesperline = dev->width * 2;
get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
if (dev->io == IO_MMAP) { /* FIXME: This is really weird! Why capture is starting with
em28xx_videodbg ("IO method is set to mmap; close and open" this ioctl ???
" the device again to choose the read method\n"); */
mutex_unlock(&dev->lock); em28xx_uninit_isoc(dev);
em28xx_set_alternate(dev);
em28xx_capture_start(dev, 1);
em28xx_resolution_set(dev);
em28xx_init_isoc(dev);
rc = 0;
err:
mutex_unlock(&dev->lock);
return rc;
}
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
struct v4l2_format f;
unsigned int i;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
for (i = 0; i < TVNORMS; i++)
if (*norm == tvnorms[i].id)
break;
if (i == TVNORMS)
for (i = 0; i < TVNORMS; i++)
if (*norm & tvnorms[i].id)
break;
if (i == TVNORMS)
return -EINVAL; return -EINVAL;
}
if (dev->io == IO_NONE) { *norm = tvnorms[i].id;
if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
em28xx_errdev("read failed, not enough memory\n");
mutex_unlock(&dev->lock);
return -ENOMEM;
}
dev->io = IO_READ;
dev->stream = STREAM_ON;
em28xx_queue_unusedframes(dev);
}
if (!count) { mutex_lock(&dev->lock);
mutex_unlock(&dev->lock); dev->tvnorm = &tvnorms[i];
return 0; mutex_unlock(&dev->lock);
}
if (list_empty(&dev->outqueue)) { /* Adjusts width/height, if needed */
if (filp->f_flags & O_NONBLOCK) { f.fmt.pix.width = dev->width;
mutex_unlock(&dev->lock); f.fmt.pix.height = dev->height;
return -EAGAIN; vidioc_try_fmt_cap(file, priv, &f);
}
ret = wait_event_interruptible
(dev->wait_frame,
(!list_empty(&dev->outqueue)) ||
(dev->state & DEV_DISCONNECTED));
if (ret) {
mutex_unlock(&dev->lock);
return ret;
}
if (dev->state & DEV_DISCONNECTED) {
mutex_unlock(&dev->lock);
return -ENODEV;
}
dev->video_bytesread = 0;
}
f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame); mutex_lock(&dev->lock);
em28xx_queue_unusedframes(dev); /* set new image size */
dev->width = f.fmt.pix.width;
dev->height = f.fmt.pix.height;
dev->frame_size = dev->width * dev->height * 2;
dev->field_size = dev->frame_size >> 1;
dev->bytesperline = dev->width * 2;
get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
if (count > f->buf.length) em28xx_resolution_set(dev);
count = f->buf.length; em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id);
if ((dev->video_bytesread + count) > dev->frame_size) mutex_unlock(&dev->lock);
count = dev->frame_size - dev->video_bytesread; return 0;
}
if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) { static const char *iname[] = {
em28xx_err("Error while copying to user\n"); [EM28XX_VMUX_COMPOSITE1] = "Composite1",
return -EFAULT; [EM28XX_VMUX_COMPOSITE2] = "Composite2",
} [EM28XX_VMUX_COMPOSITE3] = "Composite3",
dev->video_bytesread += count; [EM28XX_VMUX_COMPOSITE4] = "Composite4",
[EM28XX_VMUX_SVIDEO] = "S-Video",
[EM28XX_VMUX_TELEVISION] = "Television",
[EM28XX_VMUX_CABLE] = "Cable TV",
[EM28XX_VMUX_DVB] = "DVB",
[EM28XX_VMUX_DEBUG] = "for debug only",
};
if (dev->video_bytesread == dev->frame_size) { static int vidioc_enum_input(struct file *file, void *priv,
spin_lock_irqsave(&dev->queue_lock, lock_flags); struct v4l2_input *i)
list_for_each_entry(i, &dev->outqueue, frame) {
i->state = F_UNUSED; struct em28xx_fh *fh = priv;
INIT_LIST_HEAD(&dev->outqueue); struct em28xx *dev = fh->dev;
spin_unlock_irqrestore(&dev->queue_lock, lock_flags); unsigned int n;
em28xx_queue_unusedframes(dev); n = i->index;
dev->video_bytesread = 0; if (n >= MAX_EM28XX_INPUT)
} return -EINVAL;
if (0 == INPUT(n)->type)
return -EINVAL;
*f_pos += count; i->index = n;
i->type = V4L2_INPUT_TYPE_CAMERA;
mutex_unlock(&dev->lock); strcpy(i->name, iname[INPUT(n)->type]);
return count; if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
(EM28XX_VMUX_CABLE == INPUT(n)->type))
i->type = V4L2_INPUT_TYPE_TUNER;
for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
i->std |= tvnorms[n].id;
return 0;
} }
/* static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
* em28xx_v4l2_poll()
* will allocate buffers when called for the first time
*/
static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
{ {
unsigned int mask = 0; struct em28xx_fh *fh = priv;
struct em28xx_fh *fh = filp->private_data; struct em28xx *dev = fh->dev;
struct em28xx *dev = fh->dev;
if (unlikely(res_get(fh) < 0)) *i = dev->ctl_input;
return POLLERR;
return 0;
}
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (i >= MAX_EM28XX_INPUT)
return -EINVAL;
if (0 == INPUT(i)->type)
return -EINVAL;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (dev->state & DEV_DISCONNECTED) { video_mux(dev, i);
em28xx_videodbg("device not present\n");
} else if (dev->state & DEV_MISCONFIGURED) {
em28xx_videodbg("device is misconfigured; close and open it again\n");
} else {
if (dev->io == IO_NONE) {
if (!em28xx_request_buffers
(dev, EM28XX_NUM_READ_FRAMES)) {
em28xx_warn
("poll() failed, not enough memory\n");
} else {
dev->io = IO_READ;
dev->stream = STREAM_ON;
}
}
if (dev->io == IO_READ) { mutex_unlock(&dev->lock);
em28xx_queue_unusedframes(dev); return 0;
poll_wait(filp, &dev->wait_frame, wait); }
if (!list_empty(&dev->outqueue)) static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
mask |= POLLIN | POLLRDNORM; {
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
unsigned int index = a->index;
mutex_unlock(&dev->lock); if (a->index > 1)
return -EINVAL;
return mask; index = dev->ctl_ainput;
}
if (index == 0) {
strcpy(a->name, "Television");
} else {
strcpy(a->name, "Line In");
} }
a->capability = V4L2_AUDCAP_STEREO;
a->index = index;
mutex_unlock(&dev->lock); return 0;
return POLLERR;
} }
/* static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
* em28xx_vm_open()
*/
static void em28xx_vm_open(struct vm_area_struct *vma)
{ {
struct em28xx_frame_t *f = vma->vm_private_data; struct em28xx_fh *fh = priv;
f->vma_use_count++; struct em28xx *dev = fh->dev;
if (a->index != dev->ctl_ainput)
return -EINVAL;
return 0;
} }
/* static int vidioc_queryctrl(struct file *file, void *priv,
* em28xx_vm_close() struct v4l2_queryctrl *qc)
*/
static void em28xx_vm_close(struct vm_area_struct *vma)
{ {
/* NOTE: buffers are not freed here */ struct em28xx_fh *fh = priv;
struct em28xx_frame_t *f = vma->vm_private_data; struct em28xx *dev = fh->dev;
int id = qc->id;
int i;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (f->vma_use_count) memset(qc, 0, sizeof(*qc));
f->vma_use_count--;
qc->id = id;
if (!dev->has_msp34xx) {
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (qc->id && qc->id == em28xx_qctrl[i].id) {
memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
return 0;
}
}
}
mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_QUERYCTRL, qc);
mutex_unlock(&dev->lock);
if (qc->type)
return 0;
else
return -EINVAL;
} }
static struct vm_operations_struct em28xx_vm_ops = { static int vidioc_g_ctrl(struct file *file, void *priv,
.open = em28xx_vm_open, struct v4l2_control *ctrl)
.close = em28xx_vm_close, {
}; struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
/* rc = check_dev(dev);
* em28xx_v4l2_mmap() if (rc < 0)
*/ return rc;
static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) mutex_lock(&dev->lock);
if (!dev->has_msp34xx)
rc = em28xx_get_ctrl(dev, ctrl);
else
rc = -EINVAL;
if (rc == -EINVAL) {
em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
rc = 0;
}
mutex_unlock(&dev->lock);
return rc;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{ {
struct em28xx_fh *fh = filp->private_data; struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev; struct em28xx *dev = fh->dev;
unsigned long size = vma->vm_end - vma->vm_start; u8 i;
unsigned long start = vma->vm_start; int rc;
void *pos;
u32 i;
if (unlikely(res_get(fh) < 0)) rc = check_dev(dev);
return -EBUSY; if (rc < 0)
return rc;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (dev->state & DEV_DISCONNECTED) { if (dev->has_msp34xx)
em28xx_videodbg("mmap: device not present\n"); em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
mutex_unlock(&dev->lock); else {
return -ENODEV; rc = 1;
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (ctrl->id == em28xx_qctrl[i].id) {
if (ctrl->value < em28xx_qctrl[i].minimum ||
ctrl->value > em28xx_qctrl[i].maximum) {
rc = -ERANGE;
break;
}
rc = em28xx_set_ctrl(dev, ctrl);
break;
}
}
} }
if (dev->state & DEV_MISCONFIGURED) { /* Control not found - try to send it to the attached devices */
em28xx_videodbg ("mmap: Device is misconfigured; close and " if (rc == 1) {
"open it again\n"); em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
mutex_unlock(&dev->lock); rc = 0;
return -EIO;
} }
if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || mutex_unlock(&dev->lock);
size != PAGE_ALIGN(dev->frame[0].buf.length)) { return rc;
mutex_unlock(&dev->lock); }
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (0 != t->index)
return -EINVAL; return -EINVAL;
}
for (i = 0; i < dev->num_frames; i++) { strcpy(t->name, "Tuner");
if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
break; mutex_lock(&dev->lock);
}
if (i == dev->num_frames) { em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0;
}
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (0 != t->index)
return -EINVAL; return -EINVAL;
}
/* VM_IO is eventually going to replace PageReserved altogether */ mutex_lock(&dev->lock);
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
pos = dev->frame[i].bufmem; em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
while (size > 0) { /* size is page-aligned */
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
em28xx_videodbg("mmap: vm_insert_page failed\n");
mutex_unlock(&dev->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
pos += PAGE_SIZE;
size -= PAGE_SIZE;
}
vma->vm_ops = &em28xx_vm_ops; mutex_unlock(&dev->lock);
vma->vm_private_data = &dev->frame[i]; return 0;
}
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
f->type = V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
return 0;
}
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (0 != f->tuner)
return -EINVAL;
if (V4L2_TUNER_ANALOG_TV != f->type)
return -EINVAL;
mutex_lock(&dev->lock);
dev->ctl_freq = f->frequency;
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
em28xx_vm_open(vma);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return 0;
} }
/* static int vidioc_cropcap(struct file *file, void *priv,
* em28xx_get_ctrl() struct v4l2_cropcap *cc)
* return the current saturation, brightness or contrast, mute state
*/
static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
{ {
switch (ctrl->id) { struct em28xx_fh *fh = priv;
case V4L2_CID_AUDIO_MUTE: struct em28xx *dev = fh->dev;
ctrl->value = dev->mute;
return 0; if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = dev->volume;
return 0;
default:
return -EINVAL; return -EINVAL;
}
cc->bounds.left = 0;
cc->bounds.top = 0;
cc->bounds.width = dev->width;
cc->bounds.height = dev->height;
cc->defrect = cc->bounds;
cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
cc->pixelaspect.denominator = 59;
return 0;
} }
/* static int vidioc_streamon(struct file *file, void *priv,
* em28xx_set_ctrl() enum v4l2_buf_type type)
* mute or set new saturation, brightness or contrast
*/
static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
{ {
switch (ctrl->id) { struct em28xx_fh *fh = priv;
case V4L2_CID_AUDIO_MUTE: struct em28xx *dev = fh->dev;
if (ctrl->value != dev->mute) { int rc;
dev->mute = ctrl->value;
em28xx_audio_usb_mute(dev, ctrl->value); rc = check_dev(dev);
return em28xx_audio_analog_set(dev); if (rc < 0)
} return rc;
return 0;
case V4L2_CID_AUDIO_VOLUME: if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
dev->volume = ctrl->value; return -EINVAL;
return em28xx_audio_analog_set(dev);
default: if (list_empty(&dev->inqueue))
return -EINVAL; return -EINVAL;
mutex_lock(&dev->lock);
if (unlikely(res_get(fh) < 0)) {
mutex_unlock(&dev->lock);
return -EBUSY;
} }
dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
mutex_unlock(&dev->lock);
return 0;
} }
/* static int vidioc_streamoff(struct file *file, void *priv,
* em28xx_stream_interrupt() enum v4l2_buf_type type)
* stops streaming
*/
static int em28xx_stream_interrupt(struct em28xx *dev)
{ {
int ret = 0; struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
/* stop reading from the device */ rc = check_dev(dev);
if (rc < 0)
return rc;
dev->stream = STREAM_INTERRUPT; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
ret = wait_event_timeout(dev->wait_stream, return -EINVAL;
(dev->stream == STREAM_OFF) ||
(dev->state & DEV_DISCONNECTED), mutex_lock(&dev->lock);
EM28XX_URB_TIMEOUT);
if (dev->state & DEV_DISCONNECTED) if (dev->stream == STREAM_ON) {
return -ENODEV; em28xx_videodbg("VIDIOC_STREAMOFF: interrupting stream\n");
else if (ret) { rc = em28xx_stream_interrupt(dev);
dev->state |= DEV_MISCONFIGURED; if (rc < 0) {
em28xx_videodbg("device is misconfigured; close and " mutex_unlock(&dev->lock);
"open /dev/video%d again\n", return rc;
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN); }
return ret;
} }
em28xx_empty_framequeues(dev);
mutex_unlock(&dev->lock);
return 0; return 0;
} }
static int em28xx_set_norm(struct em28xx *dev, int width, int height) static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{ {
unsigned int hscale, vscale; struct em28xx_fh *fh = priv;
unsigned int maxh, maxw; struct em28xx *dev = fh->dev;
maxw = norm_maxw(dev); strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
maxh = norm_maxh(dev); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
/* width must even because of the YUYV format */ cap->version = EM28XX_VERSION_CODE;
/* height must be even because of interlacing */
height &= 0xfffe;
width &= 0xfffe;
if (height < 32) cap->capabilities =
height = 32; V4L2_CAP_SLICED_VBI_CAPTURE |
if (height > maxh) V4L2_CAP_VIDEO_CAPTURE |
height = maxh; V4L2_CAP_AUDIO |
if (width < 48) V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
width = 48;
if (width > maxw)
width = maxw;
if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000) if (dev->has_tuner)
hscale = 0x3fff; cap->capabilities |= V4L2_CAP_TUNER;
width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000) return 0;
vscale = 0x3fff; }
height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
/* set new image size */ static int vidioc_enum_fmt_cap(struct file *file, void *priv,
dev->width = width; struct v4l2_fmtdesc *fmtd)
dev->height = height; {
dev->frame_size = dev->width * dev->height * 2; if (fmtd->index != 0)
dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */ return -EINVAL;
dev->bytesperline = dev->width * 2;
dev->hscale = hscale;
dev->vscale = vscale;
em28xx_resolution_set(dev); fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
strcpy(fmtd->description, "Packed YUY2");
fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
return 0; return 0;
} }
static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format) /* Sliced VBI ioctls */
static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
struct v4l2_format *f)
{ {
em28xx_videodbg("VIDIOC_G_FMT: type=%s\n", struct em28xx_fh *fh = priv;
(format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ? struct em28xx *dev = fh->dev;
"V4L2_BUF_TYPE_VIDEO_CAPTURE" : int rc;
(format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ?
"V4L2_BUF_TYPE_VBI_CAPTURE" :
(format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ?
"V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " :
"not supported");
switch (format->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
{
format->fmt.pix.width = dev->width;
format->fmt.pix.height = dev->height;
format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format->fmt.pix.bytesperline = dev->bytesperline;
format->fmt.pix.sizeimage = dev->frame_size;
format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width,
dev->height);
break;
}
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: rc = check_dev(dev);
{ if (rc < 0)
format->fmt.sliced.service_set=0; return rc;
em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format); mutex_lock(&dev->lock);
if (format->fmt.sliced.service_set==0) f->fmt.sliced.service_set = 0;
return -EINVAL;
break; em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
}
default: if (f->fmt.sliced.service_set == 0)
rc = -EINVAL;
mutex_unlock(&dev->lock);
return rc;
}
static int vidioc_try_set_vbi_capture(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
mutex_unlock(&dev->lock);
if (f->fmt.sliced.service_set == 0)
return -EINVAL; return -EINVAL;
}
return (0); return 0;
} }
static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format)
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb)
{ {
u32 i; struct em28xx_fh *fh = priv;
int ret = 0; struct em28xx *dev = fh->dev;
int width = format->fmt.pix.width; u32 i;
int height = format->fmt.pix.height; int rc;
unsigned int hscale, vscale;
unsigned int maxh, maxw;
maxw = norm_maxw(dev); rc = check_dev(dev);
maxh = norm_maxh(dev); if (rc < 0)
return rc;
em28xx_videodbg("%s: type=%s\n", if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
cmd == VIDIOC_TRY_FMT ? rb->memory != V4L2_MEMORY_MMAP)
"VIDIOC_TRY_FMT" : "VIDIOC_S_FMT", return -EINVAL;
format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
"V4L2_BUF_TYPE_VIDEO_CAPTURE" :
format->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
"V4L2_BUF_TYPE_VBI_CAPTURE " :
"not supported");
if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { if (dev->io == IO_READ) {
em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format); em28xx_videodbg("method is set to read;"
" close and open the device again to"
" choose the mmap I/O method\n");
return -EINVAL;
}
if (format->fmt.sliced.service_set==0) for (i = 0; i < dev->num_frames; i++)
if (dev->frame[i].vma_use_count) {
em28xx_videodbg("VIDIOC_REQBUFS failed; "
"previous buffers are still mapped\n");
return -EINVAL; return -EINVAL;
}
return 0; mutex_lock(&dev->lock);
if (dev->stream == STREAM_ON) {
em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
rc = em28xx_stream_interrupt(dev);
if (rc < 0) {
mutex_unlock(&dev->lock);
return rc;
}
} }
em28xx_empty_framequeues(dev);
em28xx_release_buffers(dev);
if (rb->count)
rb->count = em28xx_request_buffers(dev, rb->count);
dev->frame_current = NULL;
dev->io = rb->count ? IO_MMAP : IO_NONE;
mutex_unlock(&dev->lock);
return 0;
}
if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) static int vidioc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *b)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= dev->num_frames || dev->io != IO_MMAP)
return -EINVAL; return -EINVAL;
em28xx_videodbg("%s: requested %dx%d\n", mutex_lock(&dev->lock);
cmd == VIDIOC_TRY_FMT ?
"VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
format->fmt.pix.width, format->fmt.pix.height);
/* FIXME: Move some code away from here */ memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
/* width must even because of the YUYV format */
/* height must be even because of interlacing */
height &= 0xfffe;
width &= 0xfffe;
if (height < 32) if (dev->frame[b->index].vma_use_count)
height = 32; b->flags |= V4L2_BUF_FLAG_MAPPED;
if (height > maxh)
height = maxh; if (dev->frame[b->index].state == F_DONE)
if (width < 48) b->flags |= V4L2_BUF_FLAG_DONE;
width = 48; else if (dev->frame[b->index].state != F_UNUSED)
if (width > maxw) b->flags |= V4L2_BUF_FLAG_QUEUED;
width = maxw;
mutex_unlock(&dev->lock);
return 0;
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
unsigned long lock_flags;
int rc;
if(dev->is_em2800){ rc = check_dev(dev);
/* the em2800 can only scale down to 50% */ if (rc < 0)
if(height % (maxh / 2)) return rc;
height=maxh;
if(width % (maxw / 2))
width=maxw;
/* according to empiatech support */
/* the MaxPacketSize is to small to support */
/* framesizes larger than 640x480 @ 30 fps */
/* or 640x576 @ 25 fps. As this would cut */
/* of a part of the image we prefer */
/* 360x576 or 360x480 for now */
if(width == maxw && height == maxh)
width /= 2;
}
if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000) if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP ||
hscale = 0x3fff; b->index >= dev->num_frames)
return -EINVAL;
width = (((unsigned long)maxw) << 12) / (hscale + 4096L); if (dev->frame[b->index].state != F_UNUSED)
return -EAGAIN;
if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000) dev->frame[b->index].state = F_QUEUED;
vscale = 0x3fff;
height = (((unsigned long)maxh) << 12) / (vscale + 4096L); /* add frame to fifo */
spin_lock_irqsave(&dev->queue_lock, lock_flags);
list_add_tail(&dev->frame[b->index].frame, &dev->inqueue);
spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
format->fmt.pix.width = width; return 0;
format->fmt.pix.height = height; }
format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format->fmt.pix.bytesperline = width * 2;
format->fmt.pix.sizeimage = width * 2 * height;
format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
format->fmt.pix.field = V4L2_FIELD_INTERLACED;
em28xx_videodbg("%s: returned %dx%d (%d, %d)\n", static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
cmd == VIDIOC_TRY_FMT ? {
"VIDIOC_TRY_FMT" :"VIDIOC_S_FMT", struct em28xx_fh *fh = priv;
format->fmt.pix.width, format->fmt.pix.height, hscale, vscale); struct em28xx *dev = fh->dev;
int rc;
struct em28xx_frame_t *f;
unsigned long lock_flags;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (cmd == VIDIOC_TRY_FMT) if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
return 0; return -EINVAL;
for (i = 0; i < dev->num_frames; i++) if (list_empty(&dev->outqueue)) {
if (dev->frame[i].vma_use_count) { if (dev->stream == STREAM_OFF)
em28xx_videodbg("VIDIOC_S_FMT failed. "
"Unmap the buffers first.\n");
return -EINVAL; return -EINVAL;
}
/* stop io in case it is already in progress */ if (file->f_flags & O_NONBLOCK)
if (dev->stream == STREAM_ON) { return -EAGAIN;
em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
if ((ret = em28xx_stream_interrupt(dev))) rc = wait_event_interruptible(dev->wait_frame,
return ret; (!list_empty(&dev->outqueue)) ||
(dev->state & DEV_DISCONNECTED));
if (rc)
return rc;
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
} }
em28xx_release_buffers(dev); spin_lock_irqsave(&dev->queue_lock, lock_flags);
dev->io = IO_NONE; f = list_entry(dev->outqueue.next, struct em28xx_frame_t, frame);
list_del(dev->outqueue.next);
spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
/* set new image size */ f->state = F_UNUSED;
dev->width = width; memcpy(b, &f->buf, sizeof(*b));
dev->height = height;
dev->frame_size = dev->width * dev->height * 2; if (f->vma_use_count)
dev->field_size = dev->frame_size >> 1; b->flags |= V4L2_BUF_FLAG_MAPPED;
dev->bytesperline = dev->width * 2;
dev->hscale = hscale;
dev->vscale = vscale;
em28xx_uninit_isoc(dev);
em28xx_set_alternate(dev);
em28xx_capture_start(dev, 1);
em28xx_resolution_set(dev);
em28xx_init_isoc(dev);
return 0; return 0;
} }
/* /*
* em28xx_v4l2_do_ioctl() * em28xx_v4l2_open()
* This function is _not_ called directly, but from * inits the device and starts isoc transfer
* em28xx_v4l2_ioctl. Userspace
* copying is done already, arg is a kernel pointer.
*/ */
static int em28xx_do_ioctl(struct inode *inode, struct file *filp, static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
struct em28xx *dev, unsigned int cmd, void *arg,
v4l2_kioctl driver_ioctl)
{ {
struct em28xx_fh *fh = filp->private_data; int minor = iminor(inode);
int ret; int errCode = 0;
struct em28xx *h,*dev = NULL;
switch (cmd) { struct em28xx_fh *fh;
/* ---------- tv norms ---------- */
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *e = arg;
unsigned int i;
i = e->index; list_for_each_entry(h, &em28xx_devlist, devlist) {
if (i >= TVNORMS) if (h->vdev->minor == minor) {
return -EINVAL; dev = h;
ret = v4l2_video_std_construct(e, tvnorms[e->index].id, dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tvnorms[e->index].name); }
e->index = i; if (h->vbi_dev->minor == minor) {
if (ret < 0) dev = h;
return ret; dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
return 0; }
} }
case VIDIOC_G_STD: if (NULL == dev)
{ return -ENODEV;
v4l2_std_id *id = arg;
*id = dev->tvnorm->id; em28xx_videodbg("open minor=%d type=%s users=%d\n",
return 0; minor,v4l2_type_names[dev->type],dev->users);
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
if (!fh) {
em28xx_errdev("em28xx-video.c: Out of memory?!\n");
return -ENOMEM;
} }
case VIDIOC_S_STD: mutex_lock(&dev->lock);
{ fh->dev = dev;
v4l2_std_id *id = arg; filp->private_data = fh;
unsigned int i;
for (i = 0; i < TVNORMS; i++) if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
if (*id == tvnorms[i].id) em28xx_set_alternate(dev);
break;
if (i == TVNORMS)
for (i = 0; i < TVNORMS; i++)
if (*id & tvnorms[i].id)
break;
if (i == TVNORMS)
return -EINVAL;
mutex_lock(&dev->lock); dev->width = norm_maxw(dev);
dev->tvnorm = &tvnorms[i]; dev->height = norm_maxh(dev);
dev->frame_size = dev->width * dev->height * 2;
dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
dev->bytesperline = dev->width * 2;
dev->hscale = 0;
dev->vscale = 0;
em28xx_set_norm(dev, dev->width, dev->height); em28xx_capture_start(dev, 1);
em28xx_resolution_set(dev);
em28xx_i2c_call_clients(dev, VIDIOC_S_STD,
&dev->tvnorm->id);
mutex_unlock(&dev->lock); /* start the transfer */
errCode = em28xx_init_isoc(dev);
if (errCode)
goto err;
return 0; em28xx_empty_framequeues(dev);
} }
/* ------ input switching ---------- */ dev->users++;
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
unsigned int n;
static const char *iname[] = {
[EM28XX_VMUX_COMPOSITE1] = "Composite1",
[EM28XX_VMUX_COMPOSITE2] = "Composite2",
[EM28XX_VMUX_COMPOSITE3] = "Composite3",
[EM28XX_VMUX_COMPOSITE4] = "Composite4",
[EM28XX_VMUX_SVIDEO] = "S-Video",
[EM28XX_VMUX_TELEVISION] = "Television",
[EM28XX_VMUX_CABLE] = "Cable TV",
[EM28XX_VMUX_DVB] = "DVB",
[EM28XX_VMUX_DEBUG] = "for debug only",
};
n = i->index;
if (n >= MAX_EM28XX_INPUT)
return -EINVAL;
if (0 == INPUT(n)->type)
return -EINVAL;
memset(i, 0, sizeof(*i));
i->index = n;
i->type = V4L2_INPUT_TYPE_CAMERA;
strcpy(i->name, iname[INPUT(n)->type]);
if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
(EM28XX_VMUX_CABLE == INPUT(n)->type))
i->type = V4L2_INPUT_TYPE_TUNER;
for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
i->std |= tvnorms[n].id;
return 0;
}
case VIDIOC_G_INPUT:
{
int *i = arg;
*i = dev->ctl_input;
return 0; err:
} mutex_unlock(&dev->lock);
case VIDIOC_S_INPUT: return errCode;
{ }
int *index = arg;
if (*index >= MAX_EM28XX_INPUT) /*
return -EINVAL; * em28xx_realease_resources()
if (0 == INPUT(*index)->type) * unregisters the v4l2,i2c and usb devices
return -EINVAL; * called when the device gets disconected or at module unload
*/
static void em28xx_release_resources(struct em28xx *dev)
{
mutex_lock(&dev->lock); /*FIXME: I2C IR should be disconnected */
video_mux(dev, *index);
mutex_unlock(&dev->lock);
return 0; em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
} dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
case VIDIOC_G_AUDIO: dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
{ list_del(&dev->devlist);
struct v4l2_audio *a = arg; video_unregister_device(dev->vdev);
unsigned int index = a->index; video_unregister_device(dev->vbi_dev);
em28xx_i2c_unregister(dev);
usb_put_dev(dev->udev);
if (a->index > 1)
return -EINVAL;
memset(a, 0, sizeof(*a));
index = dev->ctl_ainput;
if (index == 0) { /* Mark device as unused */
strcpy(a->name, "Television"); em28xx_devused&=~(1<<dev->devno);
} else { }
strcpy(a->name, "Line In");
/*
* em28xx_v4l2_close()
* stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
*/
static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
{
struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
int errCode;
em28xx_videodbg("users=%d\n", dev->users);
if (res_check(fh))
res_free(fh);
mutex_lock(&dev->lock);
if (dev->users == 1) {
em28xx_uninit_isoc(dev);
em28xx_release_buffers(dev);
dev->io = IO_NONE;
/* the device is already disconnect,
free the remaining resources */
if (dev->state & DEV_DISCONNECTED) {
em28xx_release_resources(dev);
mutex_unlock(&dev->lock);
kfree(dev);
return 0;
}
/* set alternate 0 */
dev->alt = 0;
em28xx_videodbg("setting alternate 0\n");
errCode = usb_set_interface(dev->udev, 0, 0);
if (errCode < 0) {
em28xx_errdev("cannot change alternate number to "
"0 (error=%i)\n", errCode);
} }
a->capability = V4L2_AUDCAP_STEREO;
a->index = index;
return 0;
} }
case VIDIOC_S_AUDIO: kfree(fh);
{ dev->users--;
struct v4l2_audio *a = arg; wake_up_interruptible_nr(&dev->open, 1);
mutex_unlock(&dev->lock);
return 0;
}
if (a->index != dev->ctl_ainput) /*
return -EINVAL; * em28xx_v4l2_read()
* will allocate buffers when called for the first time
*/
static ssize_t
em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
loff_t * f_pos)
{
struct em28xx_frame_t *f, *i;
unsigned long lock_flags;
int ret = 0;
struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
return 0; /* FIXME: read() is not prepared to allow changing the video
} resolution while streaming. Seems a bug at em28xx_set_fmt
*/
/* --- controls ---------------------------------------------- */ if (unlikely(res_get(fh) < 0))
case VIDIOC_QUERYCTRL: return -EBUSY;
{
struct v4l2_queryctrl *qc = arg; mutex_lock(&dev->lock);
int i, id=qc->id;
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
memset(qc,0,sizeof(*qc)); em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
qc->id=id;
if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
if (!dev->has_msp34xx) { em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { em28xx_videodbg("not supported yet! ...\n");
if (qc->id && qc->id == em28xx_qctrl[i].id) { if (copy_to_user(buf, "", 1)) {
memcpy(qc, &(em28xx_qctrl[i]), mutex_unlock(&dev->lock);
sizeof(*qc)); return -EFAULT;
return 0;
}
}
} }
mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev,cmd,qc);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
if (qc->type) return (1);
return 0;
else
return -EINVAL;
} }
case VIDIOC_G_CTRL: if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
{ em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
struct v4l2_control *ctrl = arg; em28xx_videodbg("not supported yet! ...\n");
int retval=-EINVAL; if (copy_to_user(buf, "", 1)) {
if (!dev->has_msp34xx)
retval=em28xx_get_ctrl(dev, ctrl);
if (retval==-EINVAL) {
mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev,cmd,arg);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return -EFAULT;
} else return retval;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = arg;
u8 i;
mutex_lock(&dev->lock);
if (!dev->has_msp34xx){
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (ctrl->id == em28xx_qctrl[i].id) {
int retval=-EINVAL;
if (ctrl->value <
em28xx_qctrl[i].minimum
|| ctrl->value >
em28xx_qctrl[i].maximum)
return -ERANGE;
retval = em28xx_set_ctrl(dev, ctrl);
mutex_unlock(&dev->lock);
return retval;
}
}
} }
em28xx_i2c_call_clients(dev,cmd,arg);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return (1);
} }
/* --- tuner ioctls ------------------------------------------ */
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *t = arg;
if (0 != t->index) if (dev->state & DEV_DISCONNECTED) {
return -EINVAL; em28xx_videodbg("device not present\n");
mutex_unlock(&dev->lock);
return -ENODEV;
}
memset(t, 0, sizeof(*t)); if (dev->state & DEV_MISCONFIGURED) {
strcpy(t->name, "Tuner"); em28xx_videodbg("device misconfigured; close and open it again\n");
mutex_lock(&dev->lock);
/* let clients fill in the remainder of this struct */
em28xx_i2c_call_clients(dev, cmd, t);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal, return -EIO;
t->afc);
return 0;
} }
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *t = arg;
if (0 != t->index) if (dev->io == IO_MMAP) {
return -EINVAL; em28xx_videodbg ("IO method is set to mmap; close and open"
mutex_lock(&dev->lock); " the device again to choose the read method\n");
/* let clients handle this */
em28xx_i2c_call_clients(dev, cmd, t);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return -EINVAL;
} }
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
memset(f, 0, sizeof(*f));
f->type = V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
return 0; if (dev->io == IO_NONE) {
if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
em28xx_errdev("read failed, not enough memory\n");
mutex_unlock(&dev->lock);
return -ENOMEM;
}
dev->io = IO_READ;
dev->stream = STREAM_ON;
em28xx_queue_unusedframes(dev);
} }
case VIDIOC_S_FREQUENCY:
{
struct v4l2_frequency *f = arg;
if (0 != f->tuner)
return -EINVAL;
if (V4L2_TUNER_ANALOG_TV != f->type)
return -EINVAL;
mutex_lock(&dev->lock); if (!count) {
dev->ctl_freq = f->frequency;
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return 0;
} }
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *cc = arg;
if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (list_empty(&dev->outqueue)) {
return -EINVAL; if (filp->f_flags & O_NONBLOCK) {
cc->bounds.left = 0; mutex_unlock(&dev->lock);
cc->bounds.top = 0; return -EAGAIN;
cc->bounds.width = dev->width; }
cc->bounds.height = dev->height; ret = wait_event_interruptible
cc->defrect = cc->bounds; (dev->wait_frame,
cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ (!list_empty(&dev->outqueue)) ||
cc->pixelaspect.denominator = 59; (dev->state & DEV_DISCONNECTED));
return 0; if (ret) {
mutex_unlock(&dev->lock);
return ret;
}
if (dev->state & DEV_DISCONNECTED) {
mutex_unlock(&dev->lock);
return -ENODEV;
}
dev->video_bytesread = 0;
} }
case VIDIOC_STREAMON:
{
int *type = arg;
if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
|| dev->io != IO_MMAP)
return -EINVAL;
if (list_empty(&dev->inqueue))
return -EINVAL;
if (unlikely(res_get(fh) < 0)) em28xx_queue_unusedframes(dev);
return -EBUSY;
dev->stream = STREAM_ON; /* FIXME: Start video capture here? */ if (count > f->buf.length)
count = f->buf.length;
em28xx_videodbg("VIDIOC_STREAMON: starting stream\n"); if ((dev->video_bytesread + count) > dev->frame_size)
count = dev->frame_size - dev->video_bytesread;
return 0; if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) {
em28xx_err("Error while copying to user\n");
return -EFAULT;
} }
case VIDIOC_STREAMOFF: dev->video_bytesread += count;
{
int *type = arg;
int ret;
if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE if (dev->video_bytesread == dev->frame_size) {
|| dev->io != IO_MMAP) spin_lock_irqsave(&dev->queue_lock, lock_flags);
return -EINVAL; list_for_each_entry(i, &dev->outqueue, frame)
i->state = F_UNUSED;
INIT_LIST_HEAD(&dev->outqueue);
spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
mutex_lock(&dev->lock); em28xx_queue_unusedframes(dev);
if (dev->stream == STREAM_ON) { dev->video_bytesread = 0;
em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n"); }
if ((ret = em28xx_stream_interrupt(dev))){
mutex_unlock(&dev->lock);
return ret;
}
}
em28xx_empty_framequeues(dev); *f_pos += count;
mutex_unlock(&dev->lock);
return 0; mutex_unlock(&dev->lock);
}
default: return count;
return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
driver_ioctl);
}
return 0;
} }
/* /*
* em28xx_v4l2_do_ioctl() * em28xx_v4l2_poll()
* This function is _not_ called directly, but from * will allocate buffers when called for the first time
* em28xx_v4l2_ioctl. Userspace
* copying is done already, arg is a kernel pointer.
*/ */
static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp, static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
unsigned int cmd, void *arg)
{ {
unsigned int mask = 0;
struct em28xx_fh *fh = filp->private_data; struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev; struct em28xx *dev = fh->dev;
if (!dev) if (unlikely(res_get(fh) < 0))
return -ENODEV; return POLLERR;
if (video_debug > 1) mutex_lock(&dev->lock);
v4l_print_ioctl(dev->name,cmd);
switch (cmd) {
/* --- capabilities ------------------------------------------ */
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = arg;
memset(cap, 0, sizeof(*cap));
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name,
sizeof(cap->card));
strlcpy(cap->bus_info, dev->udev->dev.bus_id,
sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
cap->capabilities =
V4L2_CAP_SLICED_VBI_CAPTURE |
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_AUDIO |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
if (dev->has_tuner)
cap->capabilities |= V4L2_CAP_TUNER;
return 0;
}
/* --- capture ioctls ---------------------------------------- */
case VIDIOC_ENUM_FMT:
{
struct v4l2_fmtdesc *fmtd = arg;
if (fmtd->index != 0) if (dev->state & DEV_DISCONNECTED) {
return -EINVAL; em28xx_videodbg("device not present\n");
memset(fmtd, 0, sizeof(*fmtd)); } else if (dev->state & DEV_MISCONFIGURED) {
fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; em28xx_videodbg("device is misconfigured; close and open it again\n");
strcpy(fmtd->description, "Packed YUY2"); } else {
fmtd->pixelformat = V4L2_PIX_FMT_YUYV; if (dev->io == IO_NONE) {
memset(fmtd->reserved, 0, sizeof(fmtd->reserved)); if (!em28xx_request_buffers
return 0; (dev, EM28XX_NUM_READ_FRAMES)) {
} em28xx_warn
case VIDIOC_G_FMT: ("poll() failed, not enough memory\n");
{ } else {
int retval; dev->io = IO_READ;
mutex_lock(&dev->lock); dev->stream = STREAM_ON;
retval = em28xx_get_fmt(dev, (struct v4l2_format *) arg); }
mutex_unlock(&dev->lock); }
return retval;
} if (dev->io == IO_READ) {
case VIDIOC_TRY_FMT: em28xx_queue_unusedframes(dev);
case VIDIOC_S_FMT: poll_wait(filp, &dev->wait_frame, wait);
{
int retval;
mutex_lock(&dev->lock);
retval = em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg);
mutex_unlock(&dev->lock);
return retval;
}
case VIDIOC_REQBUFS: if (!list_empty(&dev->outqueue))
{ mask |= POLLIN | POLLRDNORM;
struct v4l2_requestbuffers *rb = arg;
u32 i;
int ret;
if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || mutex_unlock(&dev->lock);
rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (dev->io == IO_READ) { return mask;
em28xx_videodbg ("method is set to read;"
" close and open the device again to"
" choose the mmap I/O method\n");
return -EINVAL;
} }
}
for (i = 0; i < dev->num_frames; i++) mutex_unlock(&dev->lock);
if (dev->frame[i].vma_use_count) { return POLLERR;
em28xx_videodbg ("VIDIOC_REQBUFS failed; previous buffers are still mapped\n"); }
return -EINVAL;
}
mutex_lock(&dev->lock);
if (dev->stream == STREAM_ON) {
em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
if ((ret = em28xx_stream_interrupt(dev))){
mutex_unlock(&dev->lock);
return ret;
}
}
em28xx_empty_framequeues(dev); /*
* em28xx_v4l2_mmap()
*/
static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long start = vma->vm_start;
void *pos;
u32 i;
em28xx_release_buffers(dev); if (unlikely(res_get(fh) < 0))
if (rb->count) return -EBUSY;
rb->count =
em28xx_request_buffers(dev, rb->count);
dev->frame_current = NULL; mutex_lock(&dev->lock);
em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n", if (dev->state & DEV_DISCONNECTED) {
rb->count); em28xx_videodbg("mmap: device not present\n");
dev->io = rb->count ? IO_MMAP : IO_NONE;
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return 0; return -ENODEV;
} }
case VIDIOC_QUERYBUF:
{
struct v4l2_buffer *b = arg;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= dev->num_frames || dev->io != IO_MMAP)
return -EINVAL;
memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
if (dev->frame[b->index].vma_use_count) { if (dev->state & DEV_MISCONFIGURED) {
b->flags |= V4L2_BUF_FLAG_MAPPED; em28xx_videodbg ("mmap: Device is misconfigured; close and "
} "open it again\n");
if (dev->frame[b->index].state == F_DONE) mutex_unlock(&dev->lock);
b->flags |= V4L2_BUF_FLAG_DONE; return -EIO;
else if (dev->frame[b->index].state != F_UNUSED)
b->flags |= V4L2_BUF_FLAG_QUEUED;
return 0;
} }
case VIDIOC_QBUF:
{
struct v4l2_buffer *b = arg;
unsigned long lock_flags;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= dev->num_frames || dev->io != IO_MMAP) {
return -EINVAL;
}
if (dev->frame[b->index].state != F_UNUSED) {
return -EAGAIN;
}
dev->frame[b->index].state = F_QUEUED;
/* add frame to fifo */ if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE)) {
spin_lock_irqsave(&dev->queue_lock, lock_flags); mutex_unlock(&dev->lock);
list_add_tail(&dev->frame[b->index].frame, return -EINVAL;
&dev->inqueue);
spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
return 0;
} }
case VIDIOC_DQBUF:
{
struct v4l2_buffer *b = arg;
struct em28xx_frame_t *f;
unsigned long lock_flags;
int ret = 0;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
|| dev->io != IO_MMAP)
return -EINVAL;
if (list_empty(&dev->outqueue)) {
if (dev->stream == STREAM_OFF)
return -EINVAL;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible
(dev->wait_frame,
(!list_empty(&dev->outqueue)) ||
(dev->state & DEV_DISCONNECTED));
if (ret)
return ret;
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
}
spin_lock_irqsave(&dev->queue_lock, lock_flags);
f = list_entry(dev->outqueue.next,
struct em28xx_frame_t, frame);
list_del(dev->outqueue.next);
spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
f->state = F_UNUSED; if (size > PAGE_ALIGN(dev->frame[0].buf.length))
memcpy(b, &f->buf, sizeof(*b)); size = PAGE_ALIGN(dev->frame[0].buf.length);
if (f->vma_use_count) for (i = 0; i < dev->num_frames; i++) {
b->flags |= V4L2_BUF_FLAG_MAPPED; if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
break;
return 0;
} }
default: if (i == dev->num_frames) {
return em28xx_do_ioctl(inode, filp, dev, cmd, arg, em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
em28xx_video_do_ioctl); mutex_unlock(&dev->lock);
return -EINVAL;
} }
return 0;
}
/*
* em28xx_v4l2_ioctl()
* handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl()
*/
static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
if (dev->state & DEV_DISCONNECTED) { /* VM_IO is eventually going to replace PageReserved altogether */
em28xx_errdev("v4l2 ioctl: device not present\n"); vma->vm_flags |= VM_IO;
return -ENODEV; vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
}
if (dev->state & DEV_MISCONFIGURED) { pos = dev->frame[i].bufmem;
em28xx_errdev while (size > 0) { /* size is page-aligned */
("v4l2 ioctl: device is misconfigured; close and open it again\n"); if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
return -EIO; em28xx_videodbg("mmap: vm_insert_page failed\n");
mutex_unlock(&dev->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
pos += PAGE_SIZE;
size -= PAGE_SIZE;
} }
ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl); vma->vm_ops = &em28xx_vm_ops;
vma->vm_private_data = &dev->frame[i];
return ret; em28xx_vm_open(vma);
mutex_unlock(&dev->lock);
return 0;
} }
static const struct file_operations em28xx_v4l_fops = { static const struct file_operations em28xx_v4l_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = em28xx_v4l2_open, .open = em28xx_v4l2_open,
.release = em28xx_v4l2_close, .release = em28xx_v4l2_close,
.ioctl = em28xx_v4l2_ioctl, .read = em28xx_v4l2_read,
.read = em28xx_v4l2_read, .poll = em28xx_v4l2_poll,
.poll = em28xx_v4l2_poll, .mmap = em28xx_v4l2_mmap,
.mmap = em28xx_v4l2_mmap, .ioctl = video_ioctl2,
.llseek = no_llseek, .llseek = no_llseek,
.compat_ioctl = v4l_compat_ioctl32, .compat_ioctl = v4l_compat_ioctl32,
};
static const struct video_device em28xx_video_template = {
.fops = &em28xx_v4l_fops,
.release = video_device_release,
.minor = -1,
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
.vidioc_g_fmt_cap = vidioc_g_fmt_cap,
.vidioc_try_fmt_cap = vidioc_try_fmt_cap,
.vidioc_s_fmt_cap = vidioc_s_fmt_cap,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_cropcap = vidioc_cropcap,
.vidioc_g_fmt_vbi_capture = vidioc_g_fmt_vbi_capture,
.vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture,
.vidioc_s_fmt_vbi_capture = vidioc_try_set_vbi_capture,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.tvnorms = V4L2_STD_ALL,
.current_norm = V4L2_STD_NTSC_M,
}; };
/******************************** usb interface *****************************************/ /******************************** usb interface *****************************************/
/* /*
...@@ -1639,7 +1731,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1639,7 +1731,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
errCode = em28xx_config(dev); errCode = em28xx_config(dev);
/* allocate and fill v4l2 device struct */ /* allocate and fill video video_device struct */
dev->vdev = video_device_alloc(); dev->vdev = video_device_alloc();
if (NULL == dev->vdev) { if (NULL == dev->vdev) {
em28xx_errdev("cannot allocate video_device.\n"); em28xx_errdev("cannot allocate video_device.\n");
...@@ -1647,7 +1739,17 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1647,7 +1739,17 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
kfree(dev); kfree(dev);
return -ENOMEM; return -ENOMEM;
} }
memcpy(dev->vdev, &em28xx_video_template,
sizeof(em28xx_video_template));
dev->vdev->type = VID_TYPE_CAPTURE;
if (dev->has_tuner)
dev->vdev->type |= VID_TYPE_TUNER;
dev->vdev->dev = &dev->udev->dev;
snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name),
"%s#%d %s", "em28xx", dev->devno, "video");
dev->vdev->current_norm = dev->tvnorm->id;
/* Allocate and fill vbi video_device struct */
dev->vbi_dev = video_device_alloc(); dev->vbi_dev = video_device_alloc();
if (NULL == dev->vbi_dev) { if (NULL == dev->vbi_dev) {
em28xx_errdev("cannot allocate video_device.\n"); em28xx_errdev("cannot allocate video_device.\n");
...@@ -1656,41 +1758,27 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1656,41 +1758,27 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
kfree(dev); kfree(dev);
return -ENOMEM; return -ENOMEM;
} }
memcpy(dev->vbi_dev, &em28xx_video_template,
/* Fills VBI device info */ sizeof(em28xx_video_template));
dev->vbi_dev->type = VFL_TYPE_VBI; dev->vbi_dev->type = VFL_TYPE_VBI;
dev->vbi_dev->fops = &em28xx_v4l_fops;
dev->vbi_dev->minor = -1;
dev->vbi_dev->dev = &dev->udev->dev; dev->vbi_dev->dev = &dev->udev->dev;
dev->vbi_dev->release = video_device_release; snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name),
snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), "%s#%d %s", "%s#%d %s", "em28xx", dev->devno, "vbi");
"em28xx",dev->devno,"vbi"); dev->vbi_dev->current_norm = dev->tvnorm->id;
/* Fills CAPTURE device info */
dev->vdev->type = VID_TYPE_CAPTURE;
if (dev->has_tuner)
dev->vdev->type |= VID_TYPE_TUNER;
dev->vdev->fops = &em28xx_v4l_fops;
dev->vdev->minor = -1;
dev->vdev->dev = &dev->udev->dev;
dev->vdev->release = video_device_release;
snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
"em28xx",dev->devno,"video");
list_add_tail(&dev->devlist,&em28xx_devlist); list_add_tail(&dev->devlist,&em28xx_devlist);
if (dev->has_msp34xx) { if (dev->has_msp34xx) {
/* Send a reset to other chips via gpio */ /* Send a reset to other chips via gpio */
em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
msleep(3); msleep(3);
em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
msleep(3); msleep(3);
} }
video_mux(dev, 0); video_mux(dev, 0);
/* register v4l2 device */ /* register v4l2 video video_device */
if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
video_nr[dev->devno]))) { video_nr[dev->devno]))) {
em28xx_errdev("unable to register video device (error=%i).\n", em28xx_errdev("unable to register video device (error=%i).\n",
...@@ -1703,6 +1791,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1703,6 +1791,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
return -ENODEV; return -ENODEV;
} }
/* register v4l2 vbi video_device */
if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
vbi_nr[dev->devno]) < 0) { vbi_nr[dev->devno]) < 0) {
printk("unable to register vbi device\n"); printk("unable to register vbi device\n");
...@@ -1713,8 +1802,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1713,8 +1802,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
em28xx_devused&=~(1<<dev->devno); em28xx_devused&=~(1<<dev->devno);
kfree(dev); kfree(dev);
return -ENODEV; return -ENODEV;
} else {
printk("registered VBI\n");
} }
em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment