From 723f7d24259c8d985ec13031f7f04ce4e617d5ab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 11 Nov 2007 14:17:17 -0200 Subject: Convert em28xx to video_ioctl2 From: Mauro Carvalho Chehab 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: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 2325 ++++++++++++----------- 1 file changed, 1192 insertions(+), 1133 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index daad789f1..05d88b4d9 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -52,7 +52,7 @@ #define DRIVER_NAME "em28xx" #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 {\ if (video_debug) \ @@ -222,8 +222,6 @@ static void video_mux(struct em28xx *dev, int index) 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->i2s_speed) em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); @@ -284,1374 +282,1443 @@ static void res_free(struct em28xx_fh *fh) } /* - * em28xx_v4l2_open() - * inits the device and starts isoc transfer + * em28xx_vm_open() */ -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); - int errCode = 0; - struct em28xx *h,*dev = NULL; - struct em28xx_fh *fh; + struct em28xx_frame_t *f = vma->vm_private_data; + f->vma_use_count++; +} - list_for_each_entry(h, &em28xx_devlist, devlist) { - if (h->vdev->minor == minor) { - dev = h; - dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - if (h->vbi_dev->minor == minor) { - dev = h; - dev->type = V4L2_BUF_TYPE_VBI_CAPTURE; - } - } - if (NULL == dev) - return -ENODEV; +/* + * em28xx_vm_close() + */ +static void em28xx_vm_close(struct vm_area_struct *vma) +{ + /* NOTE: buffers are not freed here */ + struct em28xx_frame_t *f = vma->vm_private_data; - em28xx_videodbg("open minor=%d type=%s users=%d\n", - minor,v4l2_type_names[dev->type],dev->users); + if (f->vma_use_count) + 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); - 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_stream_interrupt() + * stops streaming + */ +static int em28xx_stream_interrupt(struct em28xx *dev) +{ + int rc = 0; - em28xx_capture_start(dev, 1); - em28xx_resolution_set(dev); + /* stop reading from the device */ -#if 0 - /* device needs to be initialized before isoc transfer */ - video_mux(dev, 0); -#endif + 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 */ - errCode = em28xx_init_isoc(dev); - if (errCode) - goto err; + if (rc) { + dev->state |= DEV_MISCONFIGURED; + em28xx_videodbg("device is misconfigured; close and " + "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: - mutex_unlock(&dev->lock); - return errCode; +static void get_scale(struct em28xx *dev, + unsigned int width, unsigned int height, + 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() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload -*/ -static void em28xx_release_resources(struct em28xx *dev) +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ + +static int vidioc_g_fmt_cap(struct file *file, void *priv, + 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", - dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, - dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); - list_del(&dev->devlist); - video_unregister_device(dev->vdev); - video_unregister_device(dev->vbi_dev); - em28xx_i2c_unregister(dev); - usb_put_dev(dev->udev); + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + f->fmt.pix.bytesperline = dev->bytesperline; + f->fmt.pix.sizeimage = dev->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -#if 0 /* Fixme: disallocating these generates kernel hang */ - kfree (dev->vdev); - kfree (dev->vbi_dev); -#endif + /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ + f->fmt.pix.field = dev->interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; - /* Mark device as unused */ - em28xx_devused&=~(1<devno); + mutex_unlock(&dev->lock); + return 0; } -/* - * 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) +static int vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) { - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int errCode; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + 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)) - res_free(fh); + if (dev->is_em2800) { + /* 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) { - em28xx_uninit_isoc(dev); - em28xx_release_buffers(dev); - dev->io = IO_NONE; + width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + height = (((unsigned long)maxh) << 12) / (vscale + 4096L); - /* 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; - } + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + f->fmt.pix.bytesperline = width * 2; + f->fmt.pix.sizeimage = width * 2 * height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + 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); return 0; } -/* - * 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) +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) { - 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; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc, i; - /* FIXME: read() is not prepared to allow changing the video - resolution while streaming. Seems a bug at em28xx_set_fmt - */ + rc = check_dev(dev); + if (rc < 0) + return rc; - if (unlikely(res_get(fh) < 0)) - return -EBUSY; + vidioc_try_fmt_cap(file, priv, f); mutex_lock(&dev->lock); - if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - em28xx_videodbg("V4l2_Buf_type_videocapture is set\n"); - - if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n"); - em28xx_videodbg("not supported yet! ...\n"); - if (copy_to_user(buf, "", 1)) { - mutex_unlock(&dev->lock); - return -EFAULT; + for (i = 0; i < dev->num_frames; i++) + if (dev->frame[i].vma_use_count) { + em28xx_videodbg("VIDIOC_S_FMT failed. " + "Unmap the buffers first.\n"); + rc = -EINVAL; + goto err; } - mutex_unlock(&dev->lock); - return (1); - } - if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n"); - em28xx_videodbg("not supported yet! ...\n"); - if (copy_to_user(buf, "", 1)) { - mutex_unlock(&dev->lock); - return -EFAULT; - } - mutex_unlock(&dev->lock); - return (1); - } - - if (dev->state & DEV_DISCONNECTED) { - em28xx_videodbg("device not present\n"); - mutex_unlock(&dev->lock); - return -ENODEV; - } - if (dev->state & DEV_MISCONFIGURED) { - em28xx_videodbg("device misconfigured; close and open it again\n"); - mutex_unlock(&dev->lock); - return -EIO; + /* stop io in case it is already in progress */ + if (dev->stream == STREAM_ON) { + em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n"); + rc = em28xx_stream_interrupt(dev); + if (rc < 0) + goto err; } - if (dev->io == IO_MMAP) { - em28xx_videodbg ("IO method is set to mmap; close and open" - " the device again to choose the read method\n"); - mutex_unlock(&dev->lock); - return -EINVAL; - } + em28xx_release_buffers(dev); + dev->io = IO_NONE; - 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); - } + /* 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) { - mutex_unlock(&dev->lock); - return 0; - } + /* FIXME: This is really weird! Why capture is starting with + this ioctl ??? + */ + em28xx_uninit_isoc(dev); + em28xx_set_alternate(dev); + em28xx_capture_start(dev, 1); + em28xx_resolution_set(dev); + em28xx_init_isoc(dev); + rc = 0; - if (list_empty(&dev->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&dev->lock); - return -EAGAIN; - } - 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; - } +err: + mutex_unlock(&dev->lock); + return rc; +} - f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame); +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; - em28xx_queue_unusedframes(dev); + 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; - if (count > f->buf.length) - count = f->buf.length; + *norm = tvnorms[i].id; - if ((dev->video_bytesread + count) > dev->frame_size) - count = dev->frame_size - dev->video_bytesread; + mutex_lock(&dev->lock); + dev->tvnorm = &tvnorms[i]; + mutex_unlock(&dev->lock); - if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) { - em28xx_err("Error while copying to user\n"); - return -EFAULT; - } - dev->video_bytesread += count; + /* Adjusts width/height, if needed */ + f.fmt.pix.width = dev->width; + f.fmt.pix.height = dev->height; + vidioc_try_fmt_cap(file, priv, &f); - if (dev->video_bytesread == dev->frame_size) { - spin_lock_irqsave(&dev->queue_lock, lock_flags); - 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); - dev->video_bytesread = 0; - } + /* 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); - *f_pos += count; + em28xx_resolution_set(dev); + em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id); mutex_unlock(&dev->lock); - - return count; + return 0; } -/* - * 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 = filp->private_data; - struct em28xx *dev = fh->dev; - - if (unlikely(res_get(fh) < 0)) - return POLLERR; +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", +}; - mutex_lock(&dev->lock); +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + unsigned int n; - if (dev->state & DEV_DISCONNECTED) { - 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; - } - } + n = i->index; + if (n >= MAX_EM28XX_INPUT) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; - if (dev->io == IO_READ) { - em28xx_queue_unusedframes(dev); - poll_wait(filp, &dev->wait_frame, wait); + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; - if (!list_empty(&dev->outqueue)) - mask |= POLLIN | POLLRDNORM; + strcpy(i->name, iname[INPUT(n)->type]); - mutex_unlock(&dev->lock); + if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) || + (EM28XX_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; - return mask; - } - } + for (n = 0; n < ARRAY_SIZE(tvnorms); n++) + i->std |= tvnorms[n].id; - mutex_unlock(&dev->lock); - return POLLERR; + return 0; } -/* - * em28xx_vm_open() - */ -static void em28xx_vm_open(struct vm_area_struct *vma) +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct em28xx_frame_t *f = vma->vm_private_data; - f->vma_use_count++; -} + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; -/* - * em28xx_vm_close() - */ -static void em28xx_vm_close(struct vm_area_struct *vma) -{ - /* NOTE: buffers are not freed here */ - struct em28xx_frame_t *f = vma->vm_private_data; + *i = dev->ctl_input; - if (f->vma_use_count) - f->vma_use_count--; + return 0; } -static struct vm_operations_struct em28xx_vm_ops = { - .open = em28xx_vm_open, - .close = em28xx_vm_close, -}; - -/* - * em28xx_v4l2_mmap() - */ -static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - 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; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; - if (unlikely(res_get(fh) < 0)) - return -EBUSY; + 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); - if (dev->state & DEV_DISCONNECTED) { - em28xx_videodbg("mmap: device not present\n"); - mutex_unlock(&dev->lock); - return -ENODEV; - } + video_mux(dev, i); - if (dev->state & DEV_MISCONFIGURED) { - em28xx_videodbg ("mmap: Device is misconfigured; close and " - "open it again\n"); - mutex_unlock(&dev->lock); - return -EIO; - } + mutex_unlock(&dev->lock); + return 0; +} - if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || - size != PAGE_ALIGN(dev->frame[0].buf.length)) { - mutex_unlock(&dev->lock); - return -EINVAL; - } +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + unsigned int index = a->index; - for (i = 0; i < dev->num_frames; i++) { - if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == dev->num_frames) { - em28xx_videodbg("mmap: user supplied mapping address is out of range\n"); - mutex_unlock(&dev->lock); + if (a->index > 1) return -EINVAL; - } - /* VM_IO is eventually going to replace PageReserved altogether */ - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + index = dev->ctl_ainput; - pos = dev->frame[i].bufmem; - 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; + if (index == 0) { + strcpy(a->name, "Television"); + } else { + strcpy(a->name, "Line In"); } + a->capability = V4L2_AUDCAP_STEREO; + a->index = index; - vma->vm_ops = &em28xx_vm_ops; - vma->vm_private_data = &dev->frame[i]; - - em28xx_vm_open(vma); - mutex_unlock(&dev->lock); return 0; } -/* - * em28xx_get_ctrl() - * return the current saturation, brightness or contrast, mute state - */ -static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) { - 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: + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (a->index != dev->ctl_ainput) return -EINVAL; - } + + return 0; } -/* - * em28xx_set_ctrl() - * mute or set new saturation, brightness or contrast - */ -static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) { - 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); + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int id = qc->id; + int i; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + memset(qc, 0, sizeof(*qc)); + + 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; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - return em28xx_audio_analog_set(dev); - default: + else return -EINVAL; - } } -/* - * em28xx_stream_interrupt() - * stops streaming - */ -static int em28xx_stream_interrupt(struct em28xx *dev) +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - 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; + mutex_lock(&dev->lock); - dev->stream = STREAM_INTERRUPT; - ret = wait_event_timeout(dev->wait_stream, - (dev->stream == STREAM_OFF) || - (dev->state & DEV_DISCONNECTED), - EM28XX_URB_TIMEOUT); - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - else if (ret) { - dev->state |= DEV_MISCONFIGURED; - em28xx_videodbg("device is misconfigured; close and " - "open /dev/video%d again\n", - dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN); - return ret; + 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; } - return 0; + mutex_unlock(&dev->lock); + return rc; } -static int em28xx_set_norm(struct em28xx *dev, int width, int height) +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - unsigned int hscale, vscale; - unsigned int maxh, maxw; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + u8 i; + int rc; - maxw = norm_maxw(dev); - maxh = norm_maxh(dev); + rc = check_dev(dev); + if (rc < 0) + return rc; - /* width must even because of the YUYV format */ - /* height must be even because of interlacing */ - height &= 0xfffe; - width &= 0xfffe; + mutex_lock(&dev->lock); - if (height < 32) - height = 32; - if (height > maxh) - height = maxh; - if (width < 48) - width = 48; - if (width > maxw) - width = maxw; + if (dev->has_msp34xx) + em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl); + else { + 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; + } - if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000) - hscale = 0x3fff; - width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + rc = em28xx_set_ctrl(dev, ctrl); + break; + } + } + } - if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000) - vscale = 0x3fff; - height = (((unsigned long)maxh) << 12) / (vscale + 4096L); + /* Control not found - try to send it to the attached devices */ + if (rc == 1) { + em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl); + rc = 0; + } - /* set new image size */ - dev->width = width; - dev->height = height; - 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 = hscale; - dev->vscale = vscale; + mutex_unlock(&dev->lock); + return rc; +} - em28xx_resolution_set(dev); +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; + + strcpy(t->name, "Tuner"); + mutex_lock(&dev->lock); + + em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + + mutex_unlock(&dev->lock); return 0; } -static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format) +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) { - em28xx_videodbg("VIDIOC_G_FMT: type=%s\n", - (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ? - "V4L2_BUF_TYPE_VIDEO_CAPTURE" : - (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; - } + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - { - format->fmt.sliced.service_set=0; + rc = check_dev(dev); + if (rc < 0) + return rc; - em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format); + if (0 != t->index) + return -EINVAL; - if (format->fmt.sliced.service_set==0) - return -EINVAL; + mutex_lock(&dev->lock); - break; - } + em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); -#if 0 - case V4L2_BUF_TYPE_VBI_CAPTURE: - { - format->fmt.vbi.sampling_rate = 6750000 * 4; - format->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; - format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 64 * 4; - format->fmt.vbi.start[0] = norm->vbi_v_start_0; - format->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; - format->fmt.vbi.start[1] = norm->vbi_v_start_1; - format->fmt.vbi.count[1] = format->fmt.vbi.count[0]; - format->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ - - return (0); - } -#endif - default: - return -EINVAL; - } - return (0); + mutex_unlock(&dev->lock); + return 0; } -static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format) +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) { - u32 i; - int ret = 0; - int width = format->fmt.pix.width; - int height = format->fmt.pix.height; - unsigned int hscale, vscale; - unsigned int maxh, maxw; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; - maxw = norm_maxw(dev); - maxh = norm_maxh(dev); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; - em28xx_videodbg("%s: type=%s\n", - cmd == VIDIOC_TRY_FMT ? - "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT", - 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) { - em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format); + return 0; +} - if (format->fmt.sliced.service_set==0) - return -EINVAL; +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; - return 0; - } + rc = check_dev(dev); + if (rc < 0) + return rc; -#if 0 - if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - format->type = V4L2_BUF_TYPE_VBI_CAPTURE; - format->fmt.vbi.sampling_rate = HZ; - format->fmt.vbi.samples_per_line = 2048; - format->fmt.vbi.sample_format = - V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 244; - format->fmt.vbi.flags = 0; - format->fmt.vbi.start[0] = 0; - format->fmt.vbi.start[1] = 0; - return (0); - } -#endif + if (0 != f->tuner) + return -EINVAL; - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; - em28xx_videodbg("%s: requested %dx%d\n", - cmd == VIDIOC_TRY_FMT ? - "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT", - format->fmt.pix.width, format->fmt.pix.height); + mutex_lock(&dev->lock); - /* FIXME: Move some code away from here */ - /* width must even because of the YUYV format */ - /* height must be even because of interlacing */ - height &= 0xfffe; - width &= 0xfffe; + dev->ctl_freq = f->frequency; + em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); - if (height < 32) - height = 32; - if (height > maxh) - height = maxh; - if (width < 48) - width = 48; - if (width > maxw) - width = maxw; + mutex_unlock(&dev->lock); + return 0; +} - if(dev->is_em2800){ - /* 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; - } +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; - if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000) - hscale = 0x3fff; + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + 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; - if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000) - vscale = 0x3fff; + return 0; +} - height = (((unsigned long)maxh) << 12) / (vscale + 4096L); +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; - format->fmt.pix.width = width; - 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; + rc = check_dev(dev); + if (rc < 0) + return rc; - em28xx_videodbg("%s: returned %dx%d (%d, %d)\n", - cmd == VIDIOC_TRY_FMT ? - "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT", - format->fmt.pix.width, format->fmt.pix.height, hscale, vscale); + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP) + return -EINVAL; - if (cmd == VIDIOC_TRY_FMT) - return 0; + if (list_empty(&dev->inqueue)) + return -EINVAL; - for (i = 0; i < dev->num_frames; i++) - if (dev->frame[i].vma_use_count) { - em28xx_videodbg("VIDIOC_S_FMT failed. " - "Unmap the buffers first.\n"); - return -EINVAL; - } + mutex_lock(&dev->lock); - /* stop io in case it is already in progress */ - if (dev->stream == STREAM_ON) { - em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n"); - if ((ret = em28xx_stream_interrupt(dev))) - return ret; + if (unlikely(res_get(fh) < 0)) { + mutex_unlock(&dev->lock); + return -EBUSY; } - em28xx_release_buffers(dev); - dev->io = IO_NONE; - - /* set new image size */ - dev->width = width; - dev->height = height; - dev->frame_size = dev->width * dev->height * 2; - dev->field_size = dev->frame_size >> 1; - 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); + dev->stream = STREAM_ON; /* FIXME: Start video capture here? */ + mutex_unlock(&dev->lock); return 0; } -/* - * em28xx_v4l2_do_ioctl() - * This function is _not_ called directly, but from - * em28xx_v4l2_ioctl. Userspace - * copying is done already, arg is a kernel pointer. - */ -static int em28xx_do_ioctl(struct inode *inode, struct file *filp, - struct em28xx *dev, unsigned int cmd, void *arg, - v4l2_kioctl driver_ioctl) +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) { - struct em28xx_fh *fh = filp->private_data; - int ret; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; - switch (cmd) { - /* ---------- tv norms ---------- */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; + rc = check_dev(dev); + if (rc < 0) + return rc; - i = e->index; - if (i >= TVNORMS) - return -EINVAL; - ret = v4l2_video_std_construct(e, tvnorms[e->index].id, - tvnorms[e->index].name); - e->index = i; - if (ret < 0) - return ret; - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP) + return -EINVAL; - *id = dev->tvnorm->id; - return 0; + mutex_lock(&dev->lock); + + if (dev->stream == STREAM_ON) { + em28xx_videodbg("VIDIOC_STREAMOFF: interrupting stream\n"); + rc = em28xx_stream_interrupt(dev); + if (rc < 0) { + mutex_unlock(&dev->lock); + return rc; + } } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; - for (i = 0; i < TVNORMS; i++) - if (*id == tvnorms[i].id) - break; - if (i == TVNORMS) - for (i = 0; i < TVNORMS; i++) - if (*id & tvnorms[i].id) - break; - if (i == TVNORMS) - return -EINVAL; + em28xx_empty_framequeues(dev); - mutex_lock(&dev->lock); - dev->tvnorm = &tvnorms[i]; + mutex_unlock(&dev->lock); + return 0; +} - em28xx_set_norm(dev, dev->width, dev->height); +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; - em28xx_i2c_call_clients(dev, VIDIOC_S_STD, - &dev->tvnorm->id); + 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)); - mutex_unlock(&dev->lock); + cap->version = EM28XX_VERSION_CODE; - return 0; - } + cap->capabilities = +#if 0 + V4L2_CAP_VBI_CAPTURE | +#endif + V4L2_CAP_SLICED_VBI_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - /* ------ input switching ---------- */ - 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; + if (dev->has_tuner) + cap->capabilities |= V4L2_CAP_TUNER; - return 0; - } - case VIDIOC_S_INPUT: - { - int *index = arg; + return 0; +} - if (*index >= MAX_EM28XX_INPUT) - return -EINVAL; - if (0 == INPUT(*index)->type) - return -EINVAL; +static int vidioc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmtd) +{ + if (fmtd->index != 0) + return -EINVAL; - mutex_lock(&dev->lock); - video_mux(dev, *index); - mutex_unlock(&dev->lock); + 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; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; - unsigned int index = a->index; + return 0; +} - if (a->index > 1) - return -EINVAL; - memset(a, 0, sizeof(*a)); - index = dev->ctl_ainput; +/* Sliced VBI ioctls */ +static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; - if (index == 0) { - strcpy(a->name, "Television"); - } else { - strcpy(a->name, "Line In"); - } - a->capability = V4L2_AUDCAP_STEREO; - a->index = index; - return 0; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *a = arg; + rc = check_dev(dev); + if (rc < 0) + return rc; - if (a->index != dev->ctl_ainput) - return -EINVAL; + mutex_lock(&dev->lock); - return 0; - } + f->fmt.sliced.service_set = 0; - /* --- controls ---------------------------------------------- */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i, id=qc->id; - - memset(qc,0,sizeof(*qc)); - 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,cmd,qc); - mutex_unlock(&dev->lock); - if (qc->type) - return 0; - else - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - int retval=-EINVAL; - - 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); - return 0; - } 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, VIDIOC_G_FMT, f); - em28xx_i2c_call_clients(dev,cmd,arg); - mutex_unlock(&dev->lock); - return 0; + 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 0; +} + +#if 0 +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi(struct file *file, void *priv, + struct v4l2_format *f) +{ + format->fmt.vbi.sampling_rate = 6750000 * 4; + format->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 64 * 4; + format->fmt.vbi.start[0] = norm->vbi_v_start_0; + format->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 + 1; + format->fmt.vbi.start[1] = norm->vbi_v_start_1; + format->fmt.vbi.count[1] = format->fmt.vbi.count[0]; + format->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ + + return 0; +} + +static int vidioc_try_set_vbi(struct file *file, void *priv, + struct v4l2_format *f) +{ + format->type = V4L2_BUF_TYPE_VBI_CAPTURE; + format->fmt.vbi.sampling_rate = HZ; + format->fmt.vbi.samples_per_line = 2048; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 244; + format->fmt.vbi.flags = 0; + format->fmt.vbi.start[0] = 0; + format->fmt.vbi.start[1] = 0; + + return 0; +} +#endif + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + u32 i; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (dev->io == IO_READ) { + em28xx_videodbg("method is set to read;" + " close and open the device again to" + " choose the mmap I/O method\n"); + return -EINVAL; } - /* --- tuner ioctls ------------------------------------------ */ - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - if (0 != t->index) + 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; + } - memset(t, 0, sizeof(*t)); - strcpy(t->name, "Tuner"); - mutex_lock(&dev->lock); - /* let clients fill in the remainder of this struct */ - em28xx_i2c_call_clients(dev, cmd, t); - mutex_unlock(&dev->lock); - em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal, - t->afc); - 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; + } } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; - if (0 != t->index) + 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; +} + +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; + + mutex_lock(&dev->lock); + + memcpy(b, &dev->frame[b->index].buf, sizeof(*b)); + + if (dev->frame[b->index].vma_use_count) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + if (dev->frame[b->index].state == F_DONE) + b->flags |= V4L2_BUF_FLAG_DONE; + else if (dev->frame[b->index].state != F_UNUSED) + b->flags |= V4L2_BUF_FLAG_QUEUED; + + 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; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP || + b->index >= dev->num_frames) + return -EINVAL; + + if (dev->frame[b->index].state != F_UNUSED) + return -EAGAIN; + + dev->frame[b->index].state = F_QUEUED; + + /* 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); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct em28xx_fh *fh = priv; + 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 (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; - mutex_lock(&dev->lock); - /* let clients handle this */ - em28xx_i2c_call_clients(dev, cmd, t); - mutex_unlock(&dev->lock); - return 0; - } - 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; + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; - return 0; + rc = wait_event_interruptible(dev->wait_frame, + (!list_empty(&dev->outqueue)) || + (dev->state & DEV_DISCONNECTED)); + if (rc) + return rc; + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - if (0 != f->tuner) - return -EINVAL; + 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); - if (V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; + f->state = F_UNUSED; + memcpy(b, &f->buf, sizeof(*b)); - mutex_lock(&dev->lock); - dev->ctl_freq = f->frequency; - em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); - mutex_unlock(&dev->lock); - return 0; + if (f->vma_use_count) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + return 0; +} + +/* + * em28xx_v4l2_open() + * inits the device and starts isoc transfer + */ +static int em28xx_v4l2_open(struct inode *inode, struct file *filp) +{ + int minor = iminor(inode); + int errCode = 0; + struct em28xx *h,*dev = NULL; + struct em28xx_fh *fh; + + list_for_each_entry(h, &em28xx_devlist, devlist) { + if (h->vdev->minor == minor) { + dev = h; + dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + if (h->vbi_dev->minor == minor) { + dev = h; + dev->type = V4L2_BUF_TYPE_VBI_CAPTURE; + } } -#if 0 /* ioctl is optional */ - case VIDIOC_G_PARM: - { - struct v4l2_captureparm *parm = arg; - memset(parm, 0, sizeof(*parm)); - return 0; + if (NULL == dev) + return -ENODEV; + + em28xx_videodbg("open minor=%d type=%s users=%d\n", + 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; } + 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); + + dev->width = norm_maxw(dev); + 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_capture_start(dev, 1); + em28xx_resolution_set(dev); + +#if 0 + /* device needs to be initialized before isoc transfer */ + video_mux(dev, 0); #endif - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cc = arg; - if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - 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; + /* start the transfer */ + errCode = em28xx_init_isoc(dev); + if (errCode) + goto err; + + em28xx_empty_framequeues(dev); } - case VIDIOC_STREAMON: - { - int *type = arg; - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || dev->io != IO_MMAP) - return -EINVAL; + dev->users++; - if (list_empty(&dev->inqueue)) - return -EINVAL; +err: + mutex_unlock(&dev->lock); + return errCode; +} - if (unlikely(res_get(fh) < 0)) - return -EBUSY; +/* + * em28xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +static void em28xx_release_resources(struct em28xx *dev) +{ + + /*FIXME: I2C IR should be disconnected */ + + em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n", + dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, + dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); + list_del(&dev->devlist); + video_unregister_device(dev->vdev); + video_unregister_device(dev->vbi_dev); + em28xx_i2c_unregister(dev); + usb_put_dev(dev->udev); - dev->stream = STREAM_ON; /* FIXME: Start video capture here? */ +#if 0 /* Fixme: disallocating these generates kernel hang */ + kfree (dev->vdev); + kfree (dev->vbi_dev); +#endif - em28xx_videodbg("VIDIOC_STREAMON: starting stream\n"); + /* Mark device as unused */ + em28xx_devused&=~(1<devno); +} - return 0; +/* + * 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); + } } - case VIDIOC_STREAMOFF: - { - int *type = arg; - int ret; + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; +} - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || dev->io != IO_MMAP) - 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; - mutex_lock(&dev->lock); - if (dev->stream == STREAM_ON) { - em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n"); - if ((ret = em28xx_stream_interrupt(dev))){ - mutex_unlock(&dev->lock); - return ret; - } + /* FIXME: read() is not prepared to allow changing the video + resolution while streaming. Seems a bug at em28xx_set_fmt + */ + + if (unlikely(res_get(fh) < 0)) + return -EBUSY; + + mutex_lock(&dev->lock); + + if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + em28xx_videodbg("V4l2_Buf_type_videocapture is set\n"); + + if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n"); + em28xx_videodbg("not supported yet! ...\n"); + if (copy_to_user(buf, "", 1)) { + mutex_unlock(&dev->lock); + return -EFAULT; + } + mutex_unlock(&dev->lock); + return (1); + } + if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n"); + em28xx_videodbg("not supported yet! ...\n"); + if (copy_to_user(buf, "", 1)) { + mutex_unlock(&dev->lock); + return -EFAULT; } - - em28xx_empty_framequeues(dev); mutex_unlock(&dev->lock); - - return 0; - } - default: - return v4l_compat_translate_ioctl(inode, filp, cmd, arg, - driver_ioctl); + return (1); } - return 0; -} - -/* - * em28xx_v4l2_do_ioctl() - * This function is _not_ called directly, but from - * 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, - unsigned int cmd, void *arg) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - if (!dev) + if (dev->state & DEV_DISCONNECTED) { + em28xx_videodbg("device not present\n"); + mutex_unlock(&dev->lock); return -ENODEV; - - if (video_debug > 1) - 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 = -#if 0 - V4L2_CAP_VBI_CAPTURE | -#endif - 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) - return -EINVAL; - memset(fmtd, 0, sizeof(*fmtd)); - 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; - } - case VIDIOC_G_FMT: - { - int retval; - mutex_lock(&dev->lock); - retval = em28xx_get_fmt(dev, (struct v4l2_format *) arg); + if (dev->state & DEV_MISCONFIGURED) { + em28xx_videodbg("device misconfigured; close and open it again\n"); mutex_unlock(&dev->lock); - return retval; - + return -EIO; } - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - { - int retval; - mutex_lock(&dev->lock); - retval = em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg); + + if (dev->io == IO_MMAP) { + em28xx_videodbg ("IO method is set to mmap; close and open" + " the device again to choose the read method\n"); mutex_unlock(&dev->lock); - return retval; + return -EINVAL; } -#if 0 - case VIDIOCGMBUF: - { - struct video_mbuf *mbuf = arg; - struct videobuf_queue *q; - struct v4l2_requestbuffers req; - unsigned int i; - - q = get_queue(fh); - memset(&req, 0, sizeof(req)); - req.type = q->type; - req.count = 8; - req.memory = V4L2_MEMORY_MMAP; - err = videobuf_reqbufs(q, &req); - if (err < 0) - return err; - memset(mbuf, 0, sizeof(*mbuf)); - mbuf->frames = req.count; - mbuf->size = 0; - for (i = 0; i < mbuf->frames; i++) { - mbuf->offsets[i] = q->bufs[i]->boff; - mbuf->size += q->bufs[i]->bsize; + 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; } - return 0; + dev->io = IO_READ; + dev->stream = STREAM_ON; + em28xx_queue_unusedframes(dev); } -#endif - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *rb = arg; - u32 i; - int ret; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; + if (!count) { + mutex_unlock(&dev->lock); + return 0; + } - if (dev->io == IO_READ) { - em28xx_videodbg ("method is set to read;" - " close and open the device again to" - " choose the mmap I/O method\n"); - return -EINVAL; + if (list_empty(&dev->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&dev->lock); + return -EAGAIN; } - - 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; - } - - 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; - } + 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; + } - em28xx_empty_framequeues(dev); + f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame); - em28xx_release_buffers(dev); - if (rb->count) - rb->count = - em28xx_request_buffers(dev, rb->count); + em28xx_queue_unusedframes(dev); + + if (count > f->buf.length) + count = f->buf.length; - dev->frame_current = NULL; + if ((dev->video_bytesread + count) > dev->frame_size) + count = dev->frame_size - dev->video_bytesread; - em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n", - rb->count); - dev->io = rb->count ? IO_MMAP : IO_NONE; - mutex_unlock(&dev->lock); - 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_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; + dev->video_bytesread += count; - memcpy(b, &dev->frame[b->index].buf, sizeof(*b)); + if (dev->video_bytesread == dev->frame_size) { + spin_lock_irqsave(&dev->queue_lock, lock_flags); + 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); - if (dev->frame[b->index].vma_use_count) { - b->flags |= V4L2_BUF_FLAG_MAPPED; - } - if (dev->frame[b->index].state == F_DONE) - b->flags |= V4L2_BUF_FLAG_DONE; - else if (dev->frame[b->index].state != F_UNUSED) - b->flags |= V4L2_BUF_FLAG_QUEUED; - return 0; + em28xx_queue_unusedframes(dev); + dev->video_bytesread = 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; - } + *f_pos += count; - if (dev->frame[b->index].state != F_UNUSED) { - return -EAGAIN; - } - dev->frame[b->index].state = F_QUEUED; + mutex_unlock(&dev->lock); - /* 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); + return count; +} - return 0; - } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *b = arg; - struct em28xx_frame_t *f; - unsigned long lock_flags; - int ret = 0; +/* + * 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 = filp->private_data; + struct em28xx *dev = fh->dev; - if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || dev->io != IO_MMAP) - return -EINVAL; + if (unlikely(res_get(fh) < 0)) + return POLLERR; - 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; + mutex_lock(&dev->lock); + + if (dev->state & DEV_DISCONNECTED) { + 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; + } } - 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); + if (dev->io == IO_READ) { + em28xx_queue_unusedframes(dev); + poll_wait(filp, &dev->wait_frame, wait); - f->state = F_UNUSED; - memcpy(b, &f->buf, sizeof(*b)); + if (!list_empty(&dev->outqueue)) + mask |= POLLIN | POLLRDNORM; - if (f->vma_use_count) - b->flags |= V4L2_BUF_FLAG_MAPPED; + mutex_unlock(&dev->lock); - return 0; - } - default: - return em28xx_do_ioctl(inode, filp, dev, cmd, arg, - em28xx_video_do_ioctl); + return mask; + } } - return 0; + + mutex_unlock(&dev->lock); + return POLLERR; } /* - * em28xx_v4l2_ioctl() - * handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl() + * em28xx_v4l2_mmap() */ -static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) { - int ret = 0; - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; + 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; + + if (unlikely(res_get(fh) < 0)) + return -EBUSY; + + mutex_lock(&dev->lock); if (dev->state & DEV_DISCONNECTED) { - em28xx_errdev("v4l2 ioctl: device not present\n"); + em28xx_videodbg("mmap: device not present\n"); + mutex_unlock(&dev->lock); return -ENODEV; } if (dev->state & DEV_MISCONFIGURED) { - em28xx_errdev - ("v4l2 ioctl: device is misconfigured; close and open it again\n"); + em28xx_videodbg ("mmap: Device is misconfigured; close and " + "open it again\n"); + mutex_unlock(&dev->lock); return -EIO; } - ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl); + if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE)) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + + if (size > PAGE_ALIGN(dev->frame[0].buf.length)) + size = PAGE_ALIGN(dev->frame[0].buf.length); + + for (i = 0; i < dev->num_frames; i++) { + if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == dev->num_frames) { + em28xx_videodbg("mmap: user supplied mapping address is out of range\n"); + mutex_unlock(&dev->lock); + return -EINVAL; + } + + /* VM_IO is eventually going to replace PageReserved altogether */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + + pos = dev->frame[i].bufmem; + 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; + 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 = { - .owner = THIS_MODULE, - .open = em28xx_v4l2_open, - .release = em28xx_v4l2_close, - .ioctl = em28xx_v4l2_ioctl, - .read = em28xx_v4l2_read, - .poll = em28xx_v4l2_poll, - .mmap = em28xx_v4l2_mmap, - .llseek = no_llseek, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) - .compat_ioctl = v4l_compat_ioctl32, + .owner = THIS_MODULE, + .open = em28xx_v4l2_open, + .release = em28xx_v4l2_close, + .read = em28xx_v4l2_read, + .poll = em28xx_v4l2_poll, + .mmap = em28xx_v4l2_mmap, + .ioctl = video_ioctl2, + .llseek = no_llseek, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .compat_ioctl = v4l_compat_ioctl32, #endif +}; +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, +#if 0 + .vidioc_g_fmt_vbi = vidioc_g_fmt_vbi, + .vidioc_try_fmt_vbi = vidioc_s_fmt_vbi, + .vidioc_s_fmt_vbi = vidioc_s_fmt_vbi, +#endif + .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 *****************************************/ /* @@ -1736,7 +1803,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, errCode = em28xx_config(dev); - /* allocate and fill v4l2 device struct */ + /* allocate and fill video video_device struct */ dev->vdev = video_device_alloc(); if (NULL == dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); @@ -1744,7 +1811,17 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, kfree(dev); 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(); if (NULL == dev->vbi_dev) { em28xx_errdev("cannot allocate video_device.\n"); @@ -1753,47 +1830,30 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, kfree(dev); return -ENOMEM; } - - /* Fills VBI device info */ + memcpy(dev->vbi_dev, &em28xx_video_template, + sizeof(em28xx_video_template)); 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->release = video_device_release; - snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), "%s#%d %s", - "em28xx",dev->devno,"vbi"); - - /* 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; -#if 0 - dev->vdev->f_op=&em28xx_v4l_fops; -#endif - 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"); + snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), + "%s#%d %s", "em28xx", dev->devno, "vbi"); + dev->vbi_dev->current_norm = dev->tvnorm->id; list_add_tail(&dev->devlist,&em28xx_devlist); #if 0 video_set_drvdata(dev->vbi_dev, dev); #endif - if (dev->has_msp34xx) { /* Send a reset to other chips via gpio */ em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); msleep(3); em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); msleep(3); - } + video_mux(dev, 0); - /* register v4l2 device */ + /* register v4l2 video video_device */ if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]))) { em28xx_errdev("unable to register video device (error=%i).\n", @@ -1806,6 +1866,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return -ENODEV; } + /* register v4l2 vbi video_device */ if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]) < 0) { printk("unable to register vbi device\n"); @@ -1816,8 +1877,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_devused&=~(1<devno); kfree(dev); return -ENODEV; - } else { - printk("registered VBI\n"); } em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", -- cgit v1.2.3