This is shared access support for em28xx split from Markus Rechberger's tree and backported to 2.6.17. This allows other progams (such as v4l2ctl for example) to manipulate the device while mplayer or vlc or tvtime etc. have it open. This was not submitted as Markus said he will merge his tree with the main v4l tree, which will be merged into the main kernel tree at some stage. [update: a slightly modified version of this was committed on Nov 13th 2007] diff -Naur -Naru linux-2.6.17.i686/drivers/media/video/em28xx/em28xx-video.c linux-2.6.17.i686-shared/drivers/media/video/em28xx/em28xx-video.c --- linux-2.6.17.i686/drivers/media/video/em28xx/em28xx-video.c 2006-08-03 08:43:56.000000000 +0000 +++ linux-2.6.17.i686-shared/drivers/media/video/em28xx/em28xx-video.c 2006-08-03 09:34:31.000000000 +0000 @@ -248,6 +248,7 @@ int minor = iminor(inode); int errCode = 0; struct em28xx *h,*dev = NULL; + struct em28xx_fh *fh; struct list_head *list; list_for_each(list,&em28xx_devlist) { @@ -264,7 +265,17 @@ if (NULL == dev) return -ENODEV; - filp->private_data=dev; + mutex_lock(&dev->lock); + + fh=kzalloc(sizeof(struct em28xx_fh),GFP_KERNEL); + + if(!fh){ + mutex_unlock(&dev->lock); + printk("em28xx-video.c: Out of memory?!\n"); + return -ENOMEM; + } + fh->dev=dev; + filp->private_data=fh; em28xx_videodbg("open minor=%d type=%s users=%d\n", minor,v4l2_type_names[dev->type],dev->users); @@ -272,20 +283,7 @@ if (!down_read_trylock(&em28xx_disconnect)) return -ERESTARTSYS; - if (dev->users) { - em28xx_warn("this driver can be opened only once\n"); - up_read(&em28xx_disconnect); - return -EBUSY; - } - - mutex_init(&dev->fileop_lock); /* to 1 == available */ - spin_lock_init(&dev->queue_lock); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); - - mutex_lock(&dev->lock); - - if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { em28xx_set_alternate(dev); dev->width = norm_maxw(dev); @@ -300,26 +298,21 @@ em28xx_resolution_set(dev); /* device needs to be initialized before isoc transfer */ - video_mux(dev, 0); + //video_mux(dev, 0); /* start the transfer */ errCode = em28xx_init_isoc(dev); + + em28xx_empty_framequeues(dev); if (errCode) goto err; - + dev->io = IO_NONE; + dev->stream = STREAM_OFF; + dev->num_frames = 0; + dev->state |= DEV_INITIALIZED; } dev->users++; - filp->private_data = dev; - dev->io = IO_NONE; - dev->stream = STREAM_OFF; - dev->num_frames = 0; - - /* prepare queues */ - em28xx_empty_framequeues(dev); - - dev->state |= DEV_INITIALIZED; - err: mutex_unlock(&dev->lock); up_read(&em28xx_disconnect); @@ -359,33 +352,42 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) { int errCode; - struct em28xx *dev=filp->private_data; + struct em28xx_fh *fh=filp->private_data; + struct em28xx *dev=fh->dev; em28xx_videodbg("users=%d\n", dev->users); mutex_lock(&dev->lock); + if(fh->reader==1) { + fh->reader=0; + } - em28xx_uninit_isoc(dev); + if(dev->users==1){ + dev->reader=0; - em28xx_release_buffers(dev); + em28xx_uninit_isoc(dev); + em28xx_release_buffers(dev); - /* 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; - } + /* the device is already disconnect, free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + em28xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(fh); + 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); - } + /* 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); @@ -403,7 +405,8 @@ struct em28xx_frame_t *f, *i; unsigned long lock_flags; int ret = 0; - struct em28xx *dev = filp->private_data; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { em28xx_videodbg("V4l2_Buf_type_videocapture is set\n"); @@ -442,11 +445,19 @@ return -EIO; } + if (dev->reader > 0 && fh->reader == 0){ + mutex_unlock(&dev->fileop_lock); + return -EBUSY; + } + 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->fileop_lock); return -EINVAL; + } else { + dev->reader=1; + fh->reader=1; } if (dev->io == IO_NONE) { @@ -515,7 +526,8 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait) { unsigned int mask = 0; - struct em28xx *dev = filp->private_data; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; if (mutex_lock_interruptible(&dev->fileop_lock)) return POLLERR; @@ -587,11 +599,20 @@ void *pos; u32 i; - struct em28xx *dev = filp->private_data; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; if (mutex_lock_interruptible(&dev->fileop_lock)) return -ERESTARTSYS; + if (dev->reader > 0 && fh->reader == 0){ + mutex_unlock(&dev->fileop_lock); + return -EBUSY; + } else { + dev->reader=1; + fh->reader=1; + } + if (dev->state & DEV_DISCONNECTED) { em28xx_videodbg("mmap: device not present\n"); mutex_unlock(&dev->fileop_lock); @@ -943,6 +964,7 @@ v4l2_kioctl driver_ioctl) { int ret; + struct em28xx_fh *fh = filp->private_data; switch (cmd) { /* ---------- tv norms ---------- */ @@ -1239,6 +1261,7 @@ if ((ret = em28xx_stream_interrupt(dev))) return ret; } + fh->reader=0; em28xx_empty_framequeues(dev); return 0; @@ -1259,7 +1282,8 @@ static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg) { - struct em28xx *dev = filp->private_data; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; if (!dev) return -ENODEV; @@ -1558,7 +1582,8 @@ unsigned int cmd, unsigned long arg) { int ret = 0; - struct em28xx *dev = filp->private_data; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; if (mutex_lock_interruptible(&dev->fileop_lock)) return -ERESTARTSYS; @@ -1626,6 +1651,11 @@ dev->tda9887_conf = em28xx_boards[model].tda9887_conf; dev->decoder = em28xx_boards[model].decoder; + mutex_init(&dev->fileop_lock); /* to 1 == available */ + spin_lock_init(&dev->queue_lock); + init_waitqueue_head(&dev->wait_frame); + init_waitqueue_head(&dev->wait_stream); + if (tuner >= 0) dev->tuner_type = tuner; else diff -Naur -Naru linux-2.6.17.i686/drivers/media/video/em28xx/em28xx.h linux-2.6.17.i686-shared/drivers/media/video/em28xx/em28xx.h --- linux-2.6.17.i686/drivers/media/video/em28xx/em28xx.h 2006-08-03 08:43:56.000000000 +0000 +++ linux-2.6.17.i686-shared/drivers/media/video/em28xx/em28xx.h 2006-08-02 15:57:16.000000000 +0000 @@ -256,6 +256,7 @@ int vscale; /* vertical scale factor (see datasheet) */ int interlaced; /* 1=interlace fileds, 0=just top fileds */ int type; + unsigned int reader:1; /* states */ enum em28xx_dev_state state; @@ -289,6 +290,11 @@ int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg); }; +struct em28xx_fh { + struct em28xx *dev; + unsigned int reader:1; +}; + /* Provided by em28xx-i2c.c */ void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg);