@@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
- v4l2-event.o
+ v4l2-event.o v4l2-subdev.o
# V4L2 core modules
@@ -871,6 +871,7 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
/* Register with the v4l2_device which increases the module's
use count as well. */
+ sd->initialized = 0;
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
@@ -885,6 +886,8 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
if (err && err != -ENOIOCTLCMD) {
v4l2_device_unregister_subdev(sd);
sd = NULL;
+ } else {
+ sd->initialized = 1;
}
}
@@ -401,6 +401,8 @@ static int get_index(struct video_device *vdev)
* %VFL_TYPE_VBI - Vertical blank data (undecoded)
*
* %VFL_TYPE_RADIO - A radio card
+ *
+ * %VFL_TYPE_SUBDEV - A subdevice
*/
static int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use)
@@ -439,6 +441,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
case VFL_TYPE_RADIO:
name_base = "radio";
break;
+ case VFL_TYPE_SUBDEV:
+ name_base = "subdev";
+ break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
@@ -115,18 +115,40 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister);
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
+ struct video_device *vdev;
+ int ret;
+
/* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL;
+
/* Warn if we apparently re-register a subdev */
WARN_ON(sd->v4l2_dev != NULL);
+
if (!try_module_get(sd->owner))
return -ENODEV;
+
sd->v4l2_dev = v4l2_dev;
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock);
- return 0;
+
+ /* Register the device node. */
+ vdev = &sd->devnode;
+ snprintf(vdev->name, sizeof(vdev->name), "subdev");
+ vdev->parent = v4l2_dev->dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = video_device_release_empty;
+ ret = video_register_device(vdev, VFL_TYPE_SUBDEV, -1);
+ if (ret < 0) {
+ spin_lock(&v4l2_dev->lock);
+ list_del(&sd->list);
+ spin_unlock(&v4l2_dev->lock);
+ sd->v4l2_dev = NULL;
+ module_put(sd->owner);
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
@@ -135,10 +157,13 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
/* return if it isn't registered */
if (sd == NULL || sd->v4l2_dev == NULL)
return;
+
spin_lock(&sd->v4l2_dev->lock);
list_del(&sd->list);
spin_unlock(&sd->v4l2_dev->lock);
sd->v4l2_dev = NULL;
+
module_put(sd->owner);
+ video_unregister_device(&sd->devnode);
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
new file mode 100644
@@ -0,0 +1,65 @@
+/*
+ * V4L2 subdevice support.
+ *
+ * Copyright (C) 2009 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+static int subdev_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ if (!sd->initialized)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int subdev_close(struct file *file)
+{
+ return 0;
+}
+
+static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static long subdev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, subdev_do_ioctl);
+}
+
+const struct v4l2_file_operations v4l2_subdev_fops = {
+ .owner = THIS_MODULE,
+ .open = subdev_open,
+ .unlocked_ioctl = subdev_ioctl,
+ .release = subdev_close,
+};
@@ -22,7 +22,8 @@
#define VFL_TYPE_VBI 1
#define VFL_TYPE_RADIO 2
#define VFL_TYPE_VTX 3
-#define VFL_TYPE_MAX 4
+#define VFL_TYPE_SUBDEV 4
+#define VFL_TYPE_MAX 5
struct v4l2_ioctl_callbacks;
struct video_device;
@@ -22,6 +22,7 @@
#define _V4L2_SUBDEV_H
#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
#include <media/v4l2-mediabus.h>
/* generic v4l2_device notify callback notification values */
@@ -421,8 +422,16 @@ struct v4l2_subdev {
u32 grp_id;
/* pointer to private data */
void *priv;
+ /* subdev device node */
+ struct video_device devnode;
+ unsigned int initialized;
};
+#define vdev_to_v4l2_subdev(vdev) \
+ container_of(vdev, struct v4l2_subdev, devnode)
+
+extern const struct v4l2_file_operations v4l2_subdev_fops;
+
static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
{
sd->priv = p;
@@ -444,6 +453,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
sd->name[0] = '\0';
sd->grp_id = 0;
sd->priv = NULL;
+ sd->initialized = 0;
}
/* Call an ops of a v4l2_subdev, doing the right checks against