patch-2.4.19 linux-2.4.19/drivers/media/video/videodev.c

Next file: linux-2.4.19/drivers/media/video/vino.c
Previous file: linux-2.4.19/drivers/media/video/tvmixer.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/media/video/videodev.c linux-2.4.19/drivers/media/video/videodev.c
@@ -25,15 +25,14 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/videodev.h>
 #include <linux/init.h>
-
+#include <linux/kmod.h>
+#include <linux/slab.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/semaphore.h>
 
-#include <linux/kmod.h>
-
+#include <linux/videodev.h>
 
 #define VIDEO_NUM_DEVICES	256 
 
@@ -42,6 +41,7 @@
  */
  
 static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static DECLARE_MUTEX(videodev_lock);
 
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
@@ -70,7 +70,7 @@
 static ssize_t video_read(struct file *file,
 	char *buf, size_t count, loff_t *ppos)
 {
-	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+	struct video_device *vfl = video_devdata(file);
 	if(vfl->read)
 		return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
 	else
@@ -86,13 +86,18 @@
 static ssize_t video_write(struct file *file, const char *buf, 
 	size_t count, loff_t *ppos)
 {
-	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+	struct video_device *vfl = video_devdata(file);
 	if(vfl->write)
 		return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
 	else
 		return 0;
 }
 
+struct video_device* video_devdata(struct file *file)
+{
+	return video_device[minor(file->f_dentry->d_inode->i_rdev)];
+}
+
 /*
  *	Poll to see if we're readable, can probably be used for timing on incoming
  *	frames, etc..
@@ -100,7 +105,7 @@
 
 static unsigned int video_poll(struct file *file, poll_table * wait)
 {
-	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+	struct video_device *vfl = video_devdata(file);
 	if(vfl->poll)
 		return vfl->poll(vfl, file, wait);
 	else
@@ -114,52 +119,64 @@
 
 static int video_open(struct inode *inode, struct file *file)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-	int err, retval = 0;
+	unsigned int minor = minor(inode->i_rdev);
+	int err = 0;
 	struct video_device *vfl;
 	
 	if(minor>=VIDEO_NUM_DEVICES)
 		return -ENODEV;
-	lock_kernel();		
+	down(&videodev_lock);
 	vfl=video_device[minor];
 	if(vfl==NULL) {
 		char modname[20];
 
+		up(&videodev_lock);
 		sprintf (modname, "char-major-%d-%d", VIDEO_MAJOR, minor);
 		request_module(modname);
+		down(&videodev_lock);
 		vfl=video_device[minor];
 		if (vfl==NULL) {
-			retval = -ENODEV;
-			goto error_out;
+			err = -ENODEV;
+			goto unlock_out;
 		}
 	}
-	if(vfl->busy) {
-		retval = -EBUSY;
-		goto error_out;
+	if (vfl->fops) {
+		struct file_operations *old_fops;
+
+		old_fops = file->f_op;
+                file->f_op = fops_get(vfl->fops);
+                if(file->f_op->open)
+                        err = file->f_op->open(inode,file);
+                if (err) {
+                        fops_put(file->f_op);
+                        file->f_op = fops_get(old_fops);
+                }
+                fops_put(old_fops);
+		goto unlock_out;
+	}
+	if(vfl->users) {
+		err = -EBUSY;
+		goto unlock_out;
 	}
-	vfl->busy=1;		/* In case vfl->open sleeps */
+	vfl->users++;		/* In case vfl->open sleeps */
 	
 	if(vfl->owner)
 		__MOD_INC_USE_COUNT(vfl->owner);
 	
-	if(vfl->open)
-	{
+	if (vfl->open) {
 		err=vfl->open(vfl,0);	/* Tell the device it is open */
-		if(err)
-		{
-			vfl->busy=0;
+		if (err) {
+			vfl->users--;
 			if(vfl->owner)
 				__MOD_DEC_USE_COUNT(vfl->owner);
-			
-			unlock_kernel();
-			return err;
+			goto unlock_out;
 		}
 	}
-	unlock_kernel();
-	return 0;
-error_out:
-	unlock_kernel();
-	return retval;
+	err = 0;
+	
+unlock_out:
+	up(&videodev_lock);
+	return err;
 }
 
 /*
@@ -169,21 +186,22 @@
 static int video_release(struct inode *inode, struct file *file)
 {
 	struct video_device *vfl;
-	lock_kernel();
-	vfl=video_device[MINOR(inode->i_rdev)];
+
+	down(&videodev_lock);
+	vfl = video_devdata(file);
 	if(vfl->close)
 		vfl->close(vfl);
-	vfl->busy=0;
+	vfl->users--;
 	if(vfl->owner)
 		__MOD_DEC_USE_COUNT(vfl->owner);
-	unlock_kernel();
+	up(&videodev_lock);
 	return 0;
 }
 
 static int video_ioctl(struct inode *inode, struct file *file,
 	unsigned int cmd, unsigned long arg)
 {
-	struct video_device *vfl=video_device[MINOR(inode->i_rdev)];
+	struct video_device *vfl = video_devdata(file);
 	int err=vfl->ioctl(vfl, cmd, (void *)arg);
 
 	if(err!=-ENOIOCTLCMD)
@@ -203,7 +221,7 @@
 int video_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	int ret = -EINVAL;
-	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+	struct video_device *vfl = video_devdata(file);
 	if(vfl->mmap) {
 		lock_kernel();
 		ret = vfl->mmap(vfl, (char *)vma->vm_start, 
@@ -214,6 +232,93 @@
 }
 
 /*
+ * helper function -- handles userspace copying for ioctl arguments
+ */
+int
+video_usercopy(struct inode *inode, struct file *file,
+	       unsigned int cmd, unsigned long arg,
+	       int (*func)(struct inode *inode, struct file *file,
+			   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 = (void *)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, (void *)arg, _IOC_SIZE(cmd)))
+			goto out;
+		break;
+	}
+
+	/* call driver */
+	err = func(inode, file, 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((void *)arg, parg, _IOC_SIZE(cmd)))
+			err = -EFAULT;
+		break;
+	}
+
+out:
+	if (mbuf)
+		kfree(mbuf);
+	return err;
+}
+
+/*
+ * open/release helper functions -- handle exclusive opens
+ */
+extern int video_exclusive_open(struct inode *inode, struct file *file)
+{
+	struct  video_device *vfl = video_devdata(file);
+	int retval = 0;
+
+	down(&vfl->lock);
+	if (vfl->users) {
+		retval = -EBUSY;
+	} else {
+		vfl->users++;
+	}
+	up(&vfl->lock);
+	return retval;
+}
+
+extern int video_exclusive_release(struct inode *inode, struct file *file)
+{
+	struct  video_device *vfl = video_devdata(file);
+	
+	vfl->users--;
+	return 0;
+}
+
+/*
  *	/proc support
  */
 
@@ -307,7 +412,6 @@
 	video_dev_proc_entry->owner = THIS_MODULE;
 }
 
-#ifdef MODULE
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 static void videodev_proc_destroy(void)
 {
@@ -318,7 +422,6 @@
 		remove_proc_entry("video", &proc_root);
 }
 #endif
-#endif
 
 static void videodev_proc_create_dev (struct video_device *vfd, char *name)
 {
@@ -392,8 +495,6 @@
  *	%VFL_TYPE_RADIO - A radio card	
  */
 
-static DECLARE_MUTEX(videodev_register_lock);
-
 int video_register_device(struct video_device *vfd, int type, int nr)
 {
 	int i=0;
@@ -430,27 +531,27 @@
 	}
 
 	/* pick a minor number */
-	down(&videodev_register_lock);
+	down(&videodev_lock);
 	if (-1 == nr) {
 		/* use first free */
 		for(i=base;i<end;i++)
 			if (NULL == video_device[i])
 				break;
 		if (i == end) {
-			up(&videodev_register_lock);
+			up(&videodev_lock);
 			return -ENFILE;
 		}
 	} else {
 		/* use the one the driver asked for */
 		i = base+nr;
 		if (NULL != video_device[i]) {
-			up(&videodev_register_lock);
+			up(&videodev_lock);
 			return -ENFILE;
 		}
 	}
 	video_device[i]=vfd;
 	vfd->minor=i;
-	up(&videodev_register_lock);
+	up(&videodev_lock);
 
 	/* The init call may sleep so we book the slot out
 	   then call */
@@ -474,6 +575,7 @@
 				S_IFCHR | S_IRUSR | S_IWUSR,
 				&video_fops,
 				NULL);
+	init_MUTEX(&vfd->lock);
 	
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 	sprintf (name, "%s%d", name_base, i - base);
@@ -492,8 +594,10 @@
  
 void video_unregister_device(struct video_device *vfd)
 {
+	down(&videodev_lock);
+
 	if(video_device[vfd->minor]!=vfd)
-		panic("vfd: bad unregister");
+		panic("videodev: bad unregister");
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 	videodev_proc_destroy_dev (vfd);
@@ -502,6 +606,7 @@
 	devfs_unregister (vfd->devfs_handle);
 	video_device[vfd->minor]=NULL;
 	MOD_DEC_USE_COUNT;
+	up(&videodev_lock);
 }
 
 
@@ -540,12 +645,9 @@
 
 static void __exit videodev_exit(void)
 {
-#ifdef MODULE		
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 	videodev_proc_destroy ();
 #endif
-#endif
-	
 	devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
 }
 
@@ -554,6 +656,10 @@
 
 EXPORT_SYMBOL(video_register_device);
 EXPORT_SYMBOL(video_unregister_device);
+EXPORT_SYMBOL(video_devdata);
+EXPORT_SYMBOL(video_usercopy);
+EXPORT_SYMBOL(video_exclusive_open);
+EXPORT_SYMBOL(video_exclusive_release);
 
 MODULE_AUTHOR("Alan Cox");
 MODULE_DESCRIPTION("Device registrar for Video4Linux drivers");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)