# HG changeset patch
# User Thierry MERLE <thierry.merle@free.fr>
# Date 1186775270 -7200
# Node ID b7d3470655f06002407771492969b21c4ea8e521
# Parent  ef81571e0c3325930b67ecdd7cf0e6ae580a751c
v4l2_extension module: enable the base driver access

From: Thierry Merle <thierry.merle@free.fr>

The v4l2_extension module calls the base v4l2 driver using a fake file structure

Signed-off-by: Thierry Merle <thierry.merle@free.fr>

diff -r ef81571e0c33 -r b7d3470655f0 linux/drivers/media/video/v4l2_extension/v4l2_extension-video.c
--- a/linux/drivers/media/video/v4l2_extension/v4l2_extension-video.c	Fri Aug 10 21:41:44 2007 +0200
+++ b/linux/drivers/media/video/v4l2_extension/v4l2_extension-video.c	Fri Aug 10 21:47:50 2007 +0200
@@ -63,6 +63,7 @@ static int video_open(struct inode *inod
 	struct v4l2ext *h;
 	struct v4l2ext *dev = NULL;
 	struct list_head *list;
+	struct video_device *base_dev;
 
 	/* Find the right private data corresponding to this inode */
 	list_for_each(list,&v4l2ext_devlist) {
@@ -78,47 +79,63 @@ static int video_open(struct inode *inod
 
 	dprintk(1,"open %d\n",minor);
 
-	/* TODO: call the basic v4l device's open() entry point */
-
-	return 0;
+	/* Now calling the base v4l2 driver's open to lock the device */
+	/* we pass a fake file structure that will be used each time
+	   a base v4l2 driver's entry point will need to be called */
+	base_dev = dev->base_vdev;
+	if(base_dev->fops->open)
+		return base_dev->fops->open(dev->fake_file.f_dentry->d_inode,
+					    &(dev->fake_file));
+	else
+		return -EINVAL;
 }
 
 static ssize_t
 video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
 	struct v4l2ext *dev = file->private_data;
-
-	dprintk(1,"read\n");
-
-	/* TODO: call the basic v4l device's read() entry point */
-
-	return 0;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->fops->read)
+		return base_dev->fops->read(&(dev->fake_file),data,count,ppos);
+	else
+		return -EINVAL;
 }
 
 static unsigned int
 video_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct v4l2ext *dev = file->private_data;
-
-	return 0;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->fops->poll)
+		return base_dev->fops->poll(&(dev->fake_file),wait);
+	else
+		return -EINVAL;
 }
 
 static int video_release(struct inode *inode, struct file *file)
 {
 	struct v4l2ext *dev = file->private_data;
-
-	/* TODO: call the basic v4l device's release() entry point */
-
-	return 0;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->fops->release)
+		return base_dev->fops->release(dev->fake_file.f_dentry->d_inode,
+					       &(dev->fake_file));
+	else
+		return -EINVAL;
 }
 
 static int
 video_mmap(struct file *file, struct vm_area_struct * vma)
 {
 	struct v4l2ext *dev = file->private_data;
-
-	/* TODO */
-	return 0;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->fops->mmap)
+		return base_dev->fops->mmap(&(dev->fake_file),vma);
+	else
+		return -EINVAL;
 }
 
 /* ------------------------------------------------------------------ */
@@ -127,138 +144,333 @@ static int vidioc_querycap (struct file 
 static int vidioc_querycap (struct file *file, void  *priv,
 					struct v4l2_capability *vc)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+	int ret=0;
+
+	/* FIXME: should call the helper daemon to extend capabilities */
+	if(base_dev->vidioc_querycap) {
+		ret = base_dev->vidioc_querycap(&(dev->fake_file),
+						dev->fake_file.private_data,
+						vc);
+		strlcpy(vc->driver,dev->vdev->name,sizeof(vc->driver));
+	}
+	return ret;
 }
 
 static int vidioc_g_input (struct file *file, void *priv, unsigned int *input)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_g_input)
+		return base_dev->vidioc_g_input(&(dev->fake_file),
+						dev->fake_file.private_data,
+						input);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_enum_input (struct file *file, void *priv,
 				struct v4l2_input *vi)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_enum_input)
+		return base_dev->vidioc_enum_input(&(dev->fake_file),
+						   dev->fake_file.private_data,
+						   vi);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_input)
+		return base_dev->vidioc_s_input(&(dev->fake_file),
+						dev->fake_file.private_data,
+						input);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_std)
+		return base_dev->vidioc_s_std(&(dev->fake_file),
+						dev->fake_file.private_data,
+						id);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_g_tuner (struct file *file, void *priv,
 				struct v4l2_tuner *vt)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_g_tuner)
+		return base_dev->vidioc_g_tuner(&(dev->fake_file),
+						dev->fake_file.private_data,
+						vt);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_s_tuner (struct file *file, void *priv,
 				struct v4l2_tuner *vt)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_tuner)
+		return base_dev->vidioc_s_tuner(&(dev->fake_file),
+						dev->fake_file.private_data,
+						vt);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_g_frequency (struct file *file, void *priv,
 				struct v4l2_frequency *freq)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_g_frequency)
+		return base_dev->vidioc_g_frequency(&(dev->fake_file),
+						    dev->fake_file.private_data,
+						    freq);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_s_frequency (struct file *file, void *priv,
 				struct v4l2_frequency *freq)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_frequency)
+		return base_dev->vidioc_s_frequency(&(dev->fake_file),
+						    dev->fake_file.private_data,
+						    freq);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
 {
-	return 0;
-}
-
-static int vidioc_s_audio (struct file *file, void *fh,
-			  struct v4l2_audio *a)
-{
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_g_audio)
+		return base_dev->vidioc_g_audio(&(dev->fake_file),
+						dev->fake_file.private_data,
+						a);
+	else
+		return -EINVAL;
+}
+
+static int vidioc_s_audio (struct file *file, void *priv, struct v4l2_audio *a)
+{
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_audio)
+		return base_dev->vidioc_s_audio(&(dev->fake_file),
+						dev->fake_file.private_data,
+						a);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_queryctrl (struct file *file, void *priv,
 			    struct v4l2_queryctrl *ctrl)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_queryctrl)
+		return base_dev->vidioc_queryctrl(&(dev->fake_file),
+						 dev->fake_file.private_data,
+						 ctrl);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_g_ctrl (struct file *file, void *priv,
 				struct v4l2_control *ctrl)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_g_ctrl)
+		return base_dev->vidioc_g_ctrl(&(dev->fake_file),
+					       dev->fake_file.private_data,
+					       ctrl);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_s_ctrl (struct file *file, void *priv,
 				struct v4l2_control *ctrl)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_ctrl)
+		return base_dev->vidioc_s_ctrl(&(dev->fake_file),
+					       dev->fake_file.private_data,
+					       ctrl);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_reqbufs (struct file *file,
 			   void *priv, struct v4l2_requestbuffers *vr)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_reqbufs)
+		return base_dev->vidioc_reqbufs(&(dev->fake_file),
+						dev->fake_file.private_data,
+						vr);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_querybuf (struct file *file,
 			    void *priv, struct v4l2_buffer *vb)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_querybuf)
+		return base_dev->vidioc_querybuf(&(dev->fake_file),
+						 dev->fake_file.private_data,
+						 vb);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_qbuf)
+		return base_dev->vidioc_qbuf(&(dev->fake_file),
+					     dev->fake_file.private_data,
+					     vb);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
 {
-	return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_dqbuf)
+		return base_dev->vidioc_dqbuf(&(dev->fake_file),
+					      dev->fake_file.private_data,
+					      vb);
+	else
+		return -EINVAL;
+}
+
+static int vidioc_streamon(struct file *file,
+			   void *priv, enum v4l2_buf_type type)
+{
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_streamon)
+		return base_dev->vidioc_streamon(&(dev->fake_file),
+						 dev->fake_file.private_data,
+						 type);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_streamoff(struct file *file,
 			    void *priv, enum v4l2_buf_type type)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_streamoff)
+		return base_dev->vidioc_streamoff(&(dev->fake_file),
+						  dev->fake_file.private_data,
+						  type);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_enum_fmt_cap (struct file *file, void  *priv,
 					struct v4l2_fmtdesc *vfd)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_enum_fmt_cap)
+		return base_dev->vidioc_enum_fmt_cap(&(dev->fake_file),
+						     dev->fake_file.private_data,
+						     vfd);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_g_fmt_cap (struct file *file, void *priv,
 					struct v4l2_format *vf)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_g_fmt_cap)
+		return base_dev->vidioc_g_fmt_cap(&(dev->fake_file),
+						  dev->fake_file.private_data,
+						  vf);
+	else
+	return -EINVAL;
 }
 
 static int vidioc_try_fmt_cap (struct file *file, void *priv,
 			       struct v4l2_format *vf)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_try_fmt_cap)
+		return base_dev->vidioc_try_fmt_cap(&(dev->fake_file),
+						    dev->fake_file.private_data,
+						    vf);
+	else
+		return -EINVAL;
 }
 
 static int vidioc_s_fmt_cap(struct file *file, void *priv,
 			       struct v4l2_format *vf)
 {
-	return 0;
+	struct v4l2ext *dev = (struct v4l2ext *)priv;
+	struct video_device *base_dev = dev->base_vdev;
+
+	if(base_dev->vidioc_s_fmt_cap)
+		return base_dev->vidioc_s_fmt_cap(&(dev->fake_file),
+						  dev->fake_file.private_data,
+						  vf);
+	else
+		return -EINVAL;
 }
 
 
@@ -282,7 +494,7 @@ static struct video_device v4l2ext_video
 	.type		= 0, /* filled dynamically */
 	.hardware	= 0, /* filled dynamically */
 	.fops		= &video_fops,
-	.name           = NULL,
+	.name           = "",
 	.release	= video_device_release,
 	.minor		= -1,
 	.vidioc_querycap      = vidioc_querycap,
@@ -320,8 +532,6 @@ static struct video_device v4l2ext_video
 
 static int v4l2ext_init(void)
 {
-	struct v4l2ext *dev = NULL;
-
 	printk(KERN_INFO "v4l2 extension module  version %d.%d.%d loaded\n",
 	       (V4L2EXT_VERSION_CODE >> 16) & 0xff,
 	       (V4L2EXT_VERSION_CODE >>  8) & 0xff,
@@ -347,6 +557,7 @@ int v4l2ext_register(struct video_device
 int v4l2ext_register(struct video_device *base_vdev)
 {
 	struct v4l2ext *dev = NULL;
+	struct inode *fake_inode = NULL;
 	int nr;
 	int ret=0;
 
@@ -357,8 +568,8 @@ int v4l2ext_register(struct video_device
 	if (nr >= V4L2EXT_MAXDEV) {
 		printk("This driver supports only %d device creations\n",
 		       V4L2EXT_MAXDEV);
-		mutex_unlock(&v4l2ext_lock);
-		return -ENOSPC;
+		ret=-ENOSPC;
+		goto out_unlock;
 	}
 
 	/* reserve this entry */
@@ -370,26 +581,45 @@ int v4l2ext_register(struct video_device
 	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
 	if (NULL == dev) {
 		printk("cannot allocate device.\n");
-		v4l2ext_devused&=~(1<<nr);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out_free;
 	}
 
 	dev->base_vdev = base_vdev;
 	dev->dev_nr = nr;
+
+	/* fill the fake parameters that will be passed to the v4l2 base
+	   driver entry point */
+	fake_inode = kzalloc(sizeof(struct inode),GFP_KERNEL);
+	if(NULL == fake_inode) {
+		printk("cannot allocate fake inode.\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	fake_inode->i_rdev = MKDEV(VIDEO_MAJOR, base_vdev->minor);
+	dev->fake_file.f_dentry = kzalloc(sizeof(struct dentry),
+						 GFP_KERNEL);
+	if(NULL == dev->fake_file.f_dentry) {
+		printk("cannot allocate fake f_dentry.\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	dev->fake_file.f_dentry->d_inode = fake_inode;
 
 	/* allocate and fill v4l2 device struct */
 	dev->vdev = video_device_alloc();
 	if (NULL == dev->vdev) {
 		printk("cannot allocate video_device.\n");
-		v4l2ext_devused&=~(1<<dev->dev_nr);
-		kfree(dev);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out_free;
 	}
 
 	/* almost all entries are filled with the base driver information */
 	*(dev->vdev) = v4l2ext_video_template;
 	dev->vdev->type = base_vdev->type;
 	dev->vdev->hardware = base_vdev->hardware;
+	dev->vdev->tvnorms = base_vdev->tvnorms;
+	dev->vdev->current_norm = base_vdev->current_norm;
 	dev->vdev->fops = &video_fops;
 	dev->vdev->minor = -1;
 	dev->vdev->dev = NULL;
@@ -403,10 +633,8 @@ int v4l2ext_register(struct video_device
 					 video_nr[dev->dev_nr]))) {
 		printk("error on registering video device [%d].\n",
 		       dev->dev_nr);
-		video_device_release(dev->vdev);
-		v4l2ext_devused&=~(1<<dev->dev_nr);
-		kfree(dev);
-		return -ENODEV;
+		ret=-ENODEV;
+		goto out_free;
 	}
 
 	mutex_lock(&v4l2ext_lock);
@@ -417,6 +645,19 @@ int v4l2ext_register(struct video_device
 	       dev->vdev->name);
 
 	return 0;
+
+	/* Error case, cleanup what must be cleaned */
+ out_free:
+	v4l2ext_devused&=~(1<<nr);
+	if(dev) video_device_release(dev->vdev);
+	kfree(dev);
+	kfree(fake_inode);
+	kfree(dev->fake_file.f_dentry);
+
+ out_unlock:
+	mutex_unlock(&v4l2ext_lock);
+
+	return ret;
 }
 
 /* v4l2ext_unregister: removes the base driver operators */
@@ -449,7 +690,8 @@ void v4l2ext_unregister(struct video_dev
 
 		printk("V4l2 extension: Removed (%s) extension\n",
 		       base_vdev->name);
-
+		kfree(dev->fake_file.f_dentry->d_inode);
+		kfree(dev->fake_file.f_dentry);
 		kfree(dev);
 	}
 	else {
diff -r ef81571e0c33 -r b7d3470655f0 linux/drivers/media/video/v4l2_extension/v4l2_extension.h
--- a/linux/drivers/media/video/v4l2_extension/v4l2_extension.h	Fri Aug 10 21:41:44 2007 +0200
+++ b/linux/drivers/media/video/v4l2_extension/v4l2_extension.h	Fri Aug 10 21:47:50 2007 +0200
@@ -58,6 +58,9 @@ struct v4l2ext {
 struct v4l2ext {
 	struct list_head devlist;
 	struct video_device *base_vdev; /* base v4l2 device entry point */
+	/* fake file structure, used to call base v4l2 device */
+	struct file fake_file;
+
 	int dev_nr; /* extended device sequential number */
 	struct video_device *vdev; /* the extended video device entry point */
 };
