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
- Lines: 392
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/media/video/videodev.c
- Orig date:
Thu Oct 11 09:14:32 2001
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)