ChangeSet 1.893.2.27, 2003/01/06 11:07:07-08:00, mark@alpha.dyndns.org [PATCH] USB ov511: Convert to new V4L 1 interface Here's a patch to switch ov511 over to the new V4L 1 interface introduced in 2.4.19. Rather than storing the function pointers for open(), ioctl(), etc in struct video_device, I just hand the V4L layer a struct file_operations. The advantages of this are: - The driver more closely resembles its 2.5 counterpart. - Multiple simultaneous opens will eventually be possible. - The old interfaces required calling video_unregister_device() from close() if the device was unplugged while open, causing a deadlock in the V4L layer. Now we just call video_unregister_device() unconditionally from disconnect(). (Credit goes to Duncan Haldane for tracking that bug down) This is just a backport of the changes made to the driver in ~2.5.10; nothing new here. I've been using this version of the code under 2.4 for months and it works well. diff -Nru a/drivers/usb/ov511.c b/drivers/usb/ov511.c --- a/drivers/usb/ov511.c Mon Jan 6 11:29:19 2003 +++ b/drivers/usb/ov511.c Mon Jan 6 11:29:19 2003 @@ -1,7 +1,7 @@ /* * OmniVision OV511 Camera-to-USB Bridge Driver * - * Copyright (c) 1999-2002 Mark W. McClelland + * Copyright (c) 1999-2003 Mark W. McClelland * Original decompression code Copyright 1998-2000 OmniVision Technologies * Many improvements by Bret Wallach * Color fixes by by Orion Sky Lawlor (2/26/2000) @@ -4529,8 +4529,9 @@ ***************************************************************************/ static int -ov51x_v4l1_open(struct video_device *vdev, int flags) +ov51x_v4l1_open(struct inode *inode, struct file *file) { + struct video_device *vdev = video_devdata(file); struct usb_ov511 *ov = vdev->priv; int err, i; @@ -4574,6 +4575,7 @@ } ov->user++; + file->private_data = vdev; if (ov->led_policy == LED_AUTO) ov51x_led_control(ov, 1); @@ -4583,9 +4585,10 @@ return err; } -static void -ov51x_v4l1_close(struct video_device *vdev) +static int +ov51x_v4l1_close(struct inode *inode, struct file *file) { + struct video_device *vdev = file->private_data; struct usb_ov511 *ov = vdev->priv; PDEBUG(4, "ov511_close"); @@ -4614,19 +4617,22 @@ up(&ov->cbuf_lock); ov51x_dealloc(ov, 1); - video_unregister_device(&ov->vdev); kfree(ov); ov = NULL; } - return; + file->private_data = NULL; + return 0; } /* Do not call this function directly! */ static int -ov51x_v4l1_ioctl_internal(struct usb_ov511 *ov, unsigned int cmd, - void *arg) +ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) { + struct video_device *vdev = file->private_data; + struct usb_ov511 *ov = vdev->priv; + PDEBUG(5, "IOCtl: 0x%X", cmd); if (!ov->dev) @@ -5066,80 +5072,29 @@ return 0; } -/* This is implemented as video_generic_ioctl() in the new V4L's videodev.c */ static int -ov51x_v4l1_generic_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) -{ - char sbuf[128]; - void *mbuf = NULL; - void *parg = NULL; - int err = -EINVAL; - - /* Copy arguments into temp kernel buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: - parg = arg; - break; - case _IOC_READ: /* some v4l ioctls are marked wrong ... */ - case _IOC_WRITE: - case (_IOC_WRITE | _IOC_READ): - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { - parg = sbuf; - } else { - /* too big to allocate from stack */ - mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (NULL == mbuf) - return -ENOMEM; - parg = mbuf; - } - - err = -EFAULT; - if (copy_from_user(parg, arg, _IOC_SIZE(cmd))) - goto out; - break; - } - - err = ov51x_v4l1_ioctl_internal(vdev->priv, cmd, parg); - if (err == -ENOIOCTLCMD) - err = -EINVAL; - if (err < 0) - goto out; - - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) - { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user(arg, parg, _IOC_SIZE(cmd))) - err = -EFAULT; - break; - } - -out: - if (mbuf) - kfree(mbuf); - return err; -} - -static int -ov51x_v4l1_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +ov51x_v4l1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { + struct video_device *vdev = file->private_data; struct usb_ov511 *ov = vdev->priv; int rc; if (down_interruptible(&ov->lock)) return -EINTR; - rc = ov51x_v4l1_generic_ioctl(vdev, cmd, arg); + rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal); up(&ov->lock); return rc; } -static inline long -ov51x_v4l1_read(struct video_device *vdev, char *buf, unsigned long count, - int noblock) +static int +ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos) { + struct video_device *vdev = file->private_data; + int noblock = file->f_flags&O_NONBLOCK; + unsigned long count = cnt; struct usb_ov511 *ov = vdev->priv; int i, rc = 0, frmx = -1; struct ov511_frame *frame; @@ -5289,9 +5244,11 @@ } static int -ov51x_v4l1_mmap(struct video_device *vdev, const char *adr, unsigned long size) +ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) { - unsigned long start = (unsigned long)adr; + struct video_device *vdev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; struct usb_ov511 *ov = vdev->priv; unsigned long page, pos; @@ -5327,16 +5284,22 @@ return 0; } +static struct file_operations ov511_fops = { + .owner = THIS_MODULE, + .open = ov51x_v4l1_open, + .release = ov51x_v4l1_close, + .read = ov51x_v4l1_read, + .mmap = ov51x_v4l1_mmap, + .ioctl = ov51x_v4l1_ioctl, + .llseek = no_llseek, +}; + static struct video_device vdev_template = { - owner: THIS_MODULE, - name: "OV511 USB Camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_OV511, - open: ov51x_v4l1_open, - close: ov51x_v4l1_close, - read: ov51x_v4l1_read, - ioctl: ov51x_v4l1_ioctl, - mmap: ov51x_v4l1_mmap, + .owner = THIS_MODULE, + .name = "OV511 USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_OV511, + .fops = &ov511_fops, }; #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) @@ -6621,10 +6584,8 @@ PDEBUG(3, ""); - /* We don't want people trying to open up the device */ - if (!ov->user) - video_unregister_device(&ov->vdev); - else + video_unregister_device(&ov->vdev); + if (ov->user) PDEBUG(3, "Device open...deferring video_unregister_device"); for (n = 0; n < OV511_NUMFRAMES; n++)