All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gerd Knorr <kraxel@bytesex.org>
To: Kernel List <linux-kernel@vger.kernel.org>,
	video4linux list <video4linux-list@redhat.com>
Subject: [PATCH/RFC] videodev.[ch] redesign
Date: Sat, 9 Feb 2002 19:46:02 +0100	[thread overview]
Message-ID: <20020209194602.A23061@bytesex.org> (raw)

  Hi,

In 2.5.x I want to drop the open/read/write/poll/mmap/... function
pointers from struct video_device and all the useless
video_read/write/poll/mmap/...  wrapper functions from videodev.c.

Instead, I'd like to see the video4linux drivers use struct
file_operations directly and handle the device registration like
soundcore does it for the sound drivers: simply swap file->f_op in
video_open().

The patch below does part one of the plan -- for 2.4.x kernels.  It adds
the fops pointer to struct video_device and makes video_open use it if
available, so both old + new style drivers will work.

It also provides a ioctl wrapper function which handles copying the
ioctl args from/to userspace, so we have this at one place can drop all
the copy_from/to_user calls within the v4l device driver ioctl handlers.

Comments?

  Gerd

[mailed to both lkml + video4linux list]

-------------------------- cut here -------------------------
--- linux-2.4.18-pre8/include/linux/videodev.h	Tue Feb  5 11:21:37 2002
+++ linux/include/linux/videodev.h	Tue Feb  5 11:33:04 2002
@@ -4,6 +4,18 @@
 #include <linux/types.h>
 #include <linux/version.h>
 
+#if 0
+/*
+ * v4l2 is still work-in-progress, integration planed for 2.5.x
+ *   v4l2 project homepage:   http://www.thedirks.org/v4l2/
+ *   patches available from:  http://bytesex.org/patches/
+ */ 
+# define HAVE_V4L2 1
+# include <linux/videodev2.h>
+#else
+# undef HAVE_V4L2
+#endif
+
 #ifdef __KERNEL__
 
 #include <linux/poll.h>
@@ -12,22 +24,31 @@
 struct video_device
 {
 	struct module *owner;
-	char name[32];
-	int type;
+    	char name[32];
+	int type;       /* v4l1 */
+	int type2;      /* v4l2 */
 	int hardware;
 
+	/* old, obsolete interface -- to be removed some day (2.5.x ?) */
 	int (*open)(struct video_device *, int mode);
 	void (*close)(struct video_device *);
 	long (*read)(struct video_device *, char *, unsigned long, int noblock);
-	/* Do we need a write method ? */
 	long (*write)(struct video_device *, const char *, unsigned long, int noblock);
-#if LINUX_VERSION_CODE >= 0x020100
 	unsigned int (*poll)(struct video_device *, struct file *, poll_table *);
-#endif
 	int (*ioctl)(struct video_device *, unsigned int , void *);
 	int (*mmap)(struct video_device *, const char *, unsigned long);
-	int (*initialize)(struct video_device *);	
-	void *priv;		/* Used to be 'private' but that upsets C++ */
+	int (*initialize)(struct video_device *);       
+
+	/* new interface -- we will use file_operations directly
+	 * like soundcore does.
+	 * kernel_ioctl() will be called by video_generic_ioctl.
+	 * video_generic_ioctl() does the userspace copying of the
+	 * ioctl arguments */
+	struct file_operations *fops;
+	int (*kernel_ioctl)(struct inode *inode, struct file *file,
+			    unsigned int cmd, void *arg);
+
+	void *priv; /* Used to be 'private' but that upsets C++ */
 	int busy;
 	int minor;
 	devfs_handle_t devfs_handle;
@@ -42,8 +63,11 @@
 #define VFL_TYPE_VTX		3
 
 extern void video_unregister_device(struct video_device *);
-#endif
+extern struct video_device* video_devdata(struct file*);
 
+extern int video_generic_ioctl(struct inode *inode, struct file *file,
+			       unsigned int cmd, unsigned long arg);
+#endif /* __KERNEL__ */
 
 #define VID_TYPE_CAPTURE	1	/* Can capture */
 #define VID_TYPE_TUNER		2	/* Can tune */
@@ -149,6 +173,7 @@
 #define VIDEO_AUDIO_VOLUME	4
 #define VIDEO_AUDIO_BASS	8
 #define VIDEO_AUDIO_TREBLE	16	
+#define VIDEO_AUDIO_BALANCE	32
 	char    name[16];
 #define VIDEO_SOUND_MONO	1
 #define VIDEO_SOUND_STEREO	2
@@ -378,4 +403,10 @@
 #define VID_HARDWARE_MEYE	32	/* Sony Vaio MotionEye cameras */
 #define VID_HARDWARE_CPIA2	33
 
-#endif
+#endif /* __LINUX_VIDEODEV_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- linux-2.4.18-pre8/drivers/media/video/Config.in	Tue Feb  5 11:21:40 2002
+++ linux/drivers/media/video/Config.in	Tue Feb  5 11:33:04 2002
@@ -8,6 +8,7 @@
 dep_tristate '  I2C on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT $CONFIG_I2C
 
 comment 'Video Adapters'
+dep_tristate '  Skeleton Driver Video For Linux' CONFIG_VIDEO_SKELETON $CONFIG_VIDEO_DEV
 if [ "$CONFIG_I2C_ALGOBIT" = "y" -o "$CONFIG_I2C_ALGOBIT" = "m" ]; then
    dep_tristate '  BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C_ALGOBIT
 fi
--- linux-2.4.18-pre8/drivers/media/video/Makefile	Tue Feb  5 11:21:32 2002
+++ linux/drivers/media/video/Makefile	Tue Feb  5 11:33:04 2002
@@ -33,6 +33,7 @@
 
 obj-$(CONFIG_VIDEO_DEV) += videodev.o
 
+obj-$(CONFIG_VIDEO_SKELETON) += skeleton.o
 obj-$(CONFIG_BUS_I2C) += i2c-old.o
 obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \
 	tda7432.o tda9875.o tuner.o
--- linux-2.4.18-pre8/drivers/media/video/skeleton.c	Tue Feb  5 11:33:04 2002
+++ linux/drivers/media/video/skeleton.c	Tue Feb  5 12:27:18 2002
@@ -0,0 +1,249 @@
+/*
+ * video4linux driver skeleton
+ *
+ *   You can build and insmod it.  It doesn't do anything fancy, just
+ *   prints some messages when the file_operations functions are called.
+ *
+ *   written by Gerd Knorr <kraxel@bytesex.org>
+ *   this source file is public domain, you can do with it
+ *   whatever you like ...
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/videodev.h>
+
+#define DEVNAME "skeleton"
+
+/* ------------------------------------------------------------------ */
+
+struct device_data {
+	struct list_head     devlist;
+	struct video_device  vdev;
+	struct semaphore     lock;
+	int                  users;
+	/* more device specific stuff */
+};
+
+struct file_data {
+	struct device_data   *dev;
+
+	/* just some random value to show how to keep multiple file
+	 * handles apart and decorate the debug printk's a bit */
+	int                  cookie; 
+
+	/* more filehandle specific data */
+};
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int exclusive = 0;
+static unsigned int debug = 1;
+static unsigned int video_nr = -1;
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"enable debug messages (default: on)");
+MODULE_PARM(exclusive,"i");
+MODULE_PARM_DESC(exclusive,"only one process can open the device (default: off)");
+MODULE_PARM(video_nr,"i");
+MODULE_PARM_DESC(video_nr,"device number");
+
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_DESCRIPTION("video4linux driver skeleton");
+MODULE_LICENSE("GPL and additional rights");
+
+#define dprintk(fmt, arg...)	if (debug) \
+	printk(KERN_DEBUG DEVNAME ": " fmt, ## arg)
+
+static struct list_head skeldev;
+
+/* ------------------------------------------------------------------ */
+
+static int skeleton_open(struct inode *inode, struct file *file)
+{
+	static int cookie = 42;
+	unsigned int minor = MINOR(inode->i_rdev);
+	struct device_data *h,*dev = NULL;
+	struct file_data *fh;
+	struct list_head *list;
+	
+	dprintk("open: looking for my device [minor=%d]\n",minor);
+
+	list_for_each(list,&skeldev) {
+		h = list_entry(list, struct device_data, devlist);
+		if (h->vdev.minor == minor)
+			dev = h;
+	}
+	if (NULL == dev)
+		return -ENODEV;
+	
+	down(&dev->lock);
+	dprintk("open: device found (%d users)\n",dev->users);
+	if (exclusive && dev->users > 0) {
+		dprintk("open: device busy\n");
+		up(&dev->lock);
+		return -EBUSY;
+	}
+	
+	/* allocate + initialize per filehandle data */
+	fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+	file->private_data = fh;
+	memset(fh,0,sizeof(*fh));
+	fh->dev    = dev;
+	fh->cookie = cookie++;
+
+	dprintk("open successful [minor=%d,cookie=%d]\n",
+		dev->vdev.minor,fh->cookie);
+	dev->users++;
+	up(&dev->lock);
+        return 0;
+}
+
+static int skeleton_release(struct inode *inode, struct file *file)
+{
+	struct file_data *fh    = file->private_data;
+	struct device_data *dev = fh->dev;
+
+	dprintk("release called [minor=%d,cookie=%d]\n",
+		dev->vdev.minor,fh->cookie);
+	file->private_data = NULL;
+	kfree(fh);
+	dev->users--;
+	return 0;
+}
+
+static ssize_t
+skeleton_read(struct file *file, char *data, size_t count, loff_t *ppos)
+{
+	struct file_data *fh    = file->private_data;
+	struct device_data *dev = fh->dev;
+
+	dprintk("read called [minor=%d,cookie=%d]\n",
+		dev->vdev.minor,fh->cookie);
+	return 0; /* EOF */
+}
+
+static int
+skeleton_mmap(struct file *file, struct vm_area_struct * vma)
+{
+	struct file_data *fh    = file->private_data;
+	struct device_data *dev = fh->dev;
+
+	dprintk("mmap called [minor=%d,cookie=%d]\n",
+		dev->vdev.minor,fh->cookie);
+	return -EINVAL;
+}
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others).  userspace
+ * copying is done already, arg is a kernel pointer.
+ */
+static int skeleton_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, void *arg)
+{
+	struct file_data *fh    = file->private_data;
+	struct device_data *dev = fh->dev;
+
+	dprintk("ioctl called [minor=%d,cookie=%d,cmd=0x%x,arg=%p]\n",
+		dev->vdev.minor,fh->cookie,cmd,arg);
+	switch (cmd) {
+	case VIDIOCGCAP:
+	{
+                struct video_capability *cap = arg;
+		memset(cap,0,sizeof(*cap));
+		strcpy(cap->name,dev->vdev.name);
+                return 0;
+	}
+	/* all the ioctls go here */
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations skeleton_fops =
+{
+	owner:	  THIS_MODULE,
+	open:	  skeleton_open,
+	release:  skeleton_release,
+	read:	  skeleton_read,
+	mmap:	  skeleton_mmap,
+	ioctl:	  video_generic_ioctl,
+	llseek:   no_llseek,
+};
+
+static struct video_device skeleton_template =
+{
+	name:          "undef",
+	type:          0,
+	hardware:      0,
+	fops:          &skeleton_fops,
+	kernel_ioctl:  skeleton_ioctl,
+	minor:         -1,
+};
+
+/* ------------------------------------------------------------------ */
+
+int skeleton_initdev(char *name)
+{
+	struct device_data *dev;
+	int err;
+
+	dev = kmalloc(sizeof(*dev),GFP_KERNEL);
+	if (NULL == dev)
+		return -ENOMEM;
+	memset(dev,0,sizeof(*dev));
+        init_MUTEX(&dev->lock);
+
+	/* initialize device here */
+
+	dev->vdev = skeleton_template;
+	strcpy(dev->vdev.name, name);
+	dev->vdev.priv = dev;
+	err = video_register_device(&dev->vdev,VFL_TYPE_GRABBER,video_nr);
+	if (err < 0) {
+		kfree(dev);
+		return err;
+	}
+	list_add_tail(&dev->devlist,&skeldev);
+	dprintk("registered device %s [minor=%d]\n",name,dev->vdev.minor);
+	return 0;
+}
+
+int skeleton_init(void)
+{
+	dprintk("video4linux skeleton driver loaded\n");
+	INIT_LIST_HEAD(&skeldev);
+	skeleton_initdev("skeleton-1");
+	skeleton_initdev("skeleton-2");
+	return 0;
+}
+
+void skeleton_fini(void)
+{
+	struct device_data *dev;
+
+	while (!list_empty(&skeldev)) {
+		dev = list_entry(skeldev.next, struct device_data, devlist);
+		video_unregister_device(&dev->vdev);
+		kfree(dev);
+		list_del(&dev->devlist);
+	}
+	dprintk("unloaded\n");
+}
+
+module_init(skeleton_init);
+module_exit(skeleton_fini);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- linux-2.4.18-pre8/drivers/media/video/videodev.c	Tue Feb  5 11:21:16 2002
+++ linux/drivers/media/video/videodev.c	Tue Feb  5 11:33:04 2002
@@ -25,15 +25,13 @@
 #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 <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/semaphore.h>
 
-#include <linux/kmod.h>
-
+#include <linux/videodev.h>
 
 #define VIDEO_NUM_DEVICES	256 
 
@@ -70,7 +68,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 +84,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 +103,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
@@ -133,6 +136,22 @@
 			goto error_out;
 		}
 	}
+	if (vfl->fops) {
+		int err = 0;
+		struct file_operations *old_fops;
+
+		unlock_kernel();
+		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);
+                return err;
+	}
 	if(vfl->busy) {
 		retval = -EBUSY;
 		goto error_out;
@@ -170,7 +189,7 @@
 {
 	struct video_device *vfl;
 	lock_kernel();
-	vfl=video_device[MINOR(inode->i_rdev)];
+	vfl = video_devdata(file);
 	if(vfl->close)
 		vfl->close(vfl);
 	vfl->busy=0;
@@ -183,7 +202,7 @@
 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 +222,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 +233,69 @@
 }
 
 /*
+ * ioctl helper function -- handles userspace copying
+ */
+int
+video_generic_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct  video_device *vfl = video_devdata(file);
+	char	sbuf[128];
+	void    *mbuf = NULL;
+	void	*parg = NULL;
+	int	err  = -EINVAL;
+	
+	if (vfl->kernel_ioctl == NULL)
+		return -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 = vfl->kernel_ioctl(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;
+}
+
+/*
  *	/proc support
  */
 
@@ -554,6 +636,8 @@
 
 EXPORT_SYMBOL(video_register_device);
 EXPORT_SYMBOL(video_unregister_device);
+EXPORT_SYMBOL(video_devdata);
+EXPORT_SYMBOL(video_generic_ioctl);
 
 MODULE_AUTHOR("Alan Cox");
 MODULE_DESCRIPTION("Device registrar for Video4Linux drivers");
--- linux-2.4.18-pre8/Documentation/Configure.help	Tue Feb  5 11:21:49 2002
+++ linux/Documentation/Configure.help	Tue Feb  5 11:33:04 2002
@@ -21269,6 +21269,19 @@
   To use this option, you have to check, that the "/proc file system
   support" (CONFIG_PROC_FS) is enabled too.
 
+Skeleton Video Capture Driver
+CONFIG_VIDEO_SKELETON
+  This code is a skeleton driver that only illustrates how V4L
+  drivers register and communicate with the Video for Linux subsystem.
+  It does nothing.
+
+  This driver is also available as a module called skeleton.o ( = code
+  which can be inserted in and removed from the running kernel 
+  whenever you want). If you want to compile it as a module, say M 
+  here and read Documentation/modules.txt.
+
+  If unsure, say N, otherwise say N anyway.
+
 AIMSlab RadioTrack (aka RadioReveal) support
 CONFIG_RADIO_RTRACK
   Choose Y here if you have one of these FM radio cards, and then fill

             reply	other threads:[~2002-02-09 18:45 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-02-09 18:46 Gerd Knorr [this message]
2002-02-09 20:53 ` [PATCH/RFC] videodev.[ch] redesign Oliver Neukum
2002-02-09 20:53 ` Oliver Neukum
2002-02-09 20:44   ` Gerd Knorr
2002-02-10  0:32     ` Oliver Neukum
2002-02-10  8:34       ` Gerd Knorr
2002-02-10  2:03 ` [V4L] " Alan Cox
2002-02-10  8:59   ` Gerd Knorr
2002-02-11 21:10     ` [PATCH/RFC] videodev.[ch] redesign -- take #2 Gerd Knorr
2002-02-10  3:58 ` [V4L] [PATCH/RFC] videodev.[ch] redesign Mark McClelland
2002-02-10  9:11   ` Gerd Knorr
2002-02-10 12:54     ` Mark McClelland
2002-02-11  9:55       ` Gerd Knorr
2002-02-11 11:58         ` Mark McClelland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20020209194602.A23061@bytesex.org \
    --to=kraxel@bytesex.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=video4linux-list@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.