From patchwork Thu Nov 25 02:21:49 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 355272 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oAP2MDTk021881 for ; Thu, 25 Nov 2010 02:22:15 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756114Ab0KYCVu (ORCPT ); Wed, 24 Nov 2010 21:21:50 -0500 Received: from perceval.ideasonboard.com ([95.142.166.194]:35112 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755974Ab0KYCVs (ORCPT ); Wed, 24 Nov 2010 21:21:48 -0500 Received: from localhost.localdomain (unknown [91.178.68.191]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BF9A635C9E; Thu, 25 Nov 2010 02:21:46 +0000 (UTC) From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: sakari.ailus@maxwell.research.nokia.com Subject: [RFC/PATCH v4 4/7] v4l: subdev: Add device node support Date: Thu, 25 Nov 2010 03:21:49 +0100 Message-Id: <1290651712-14940-5-git-send-email-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 1.7.2.2 In-Reply-To: <1290651712-14940-1-git-send-email-laurent.pinchart@ideasonboard.com> References: <1290651712-14940-1-git-send-email-laurent.pinchart@ideasonboard.com> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Thu, 25 Nov 2010 02:22:15 +0000 (UTC) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index f22f35c..4c9185a 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -319,6 +319,24 @@ controlled through GPIO pins. This distinction is only relevant when setting up the device, but once the subdev is registered it is completely transparent. +V4L2 sub-device userspace API +----------------------------- + +Beside exposing a kernel API through the v4l2_subdev_ops structure, V4L2 +sub-devices can also be controlled directly by userspace applications. + +When a sub-device is registered, a device node named v4l-subdevX can be created +in /dev. If the sub-device supports direct userspace configuration it must set +the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered. + +For I2C and SPI sub-devices, the v4l2_device driver can disable registration of +the device node if it wants to control the sub-device on its own. In that case +it must set the v4l2_i2c_new_subdev_board or v4l2_spi_new_subdev enable_devnode +argument to 0. Setting the argument to 1 will only enable device node +registration if the sub-device driver has set the V4L2_SUBDEV_FL_HAS_DEVNODE +flag. + + I2C sub-device drivers ---------------------- diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index 6a43578..a4440bf 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c @@ -292,7 +292,7 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev) } sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, NULL, - pdata->subdev_board_info, NULL); + pdata->subdev_board_info, NULL, 0); if (!sd) { dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n"); rval = -ENODEV; diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index af79d47..adc1bd5 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -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-ctrls.o + v4l2-event.o v4l2-ctrls.o v4l2-subdev.o # V4L2 core modules diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index ffbf8af..6a4e61e 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -2073,7 +2073,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, info.platform_data = &sensor_cfg; cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, - &cam->i2c_adapter, info.type, &info, NULL); + &cam->i2c_adapter, info.type, &info, NULL, 0); if (cam->sensor == NULL) { ret = -ENODEV; goto out_smbus; diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index d8e38cc..df26d53 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -1988,7 +1988,7 @@ static __init int vpfe_probe(struct platform_device *pdev) i2c_adap, NULL, &sdinfo->board_info, - NULL); + NULL, 0); if (vpfe_dev->sd[i]) { v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 sub device %s registered\n", diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 6ac6acd..daccafe 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -2015,7 +2015,7 @@ static __init int vpif_probe(struct platform_device *pdev) i2c_adap, NULL, &subdevdata->board_info, - NULL); + NULL, 0); if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 685f6a6..f79ba7e 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -1555,7 +1555,7 @@ static __init int vpif_probe(struct platform_device *pdev) vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, i2c_adap, NULL, &subdevdata[i].board_info, - NULL); + NULL, 0); if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); goto probe_subdev_out; diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 9ec46d3..cdbc766 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -280,7 +280,7 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) info.platform_data = &pdata; sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, NULL, - &info, NULL); + &info, NULL, 0); } else { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, NULL, type, hw_addrs[idx], NULL); diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index e8f13d3..8f8350b 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -44,7 +44,8 @@ static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc, return ERR_PTR(-ENOMEM); sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap, - MODULE_NAME, isp_info->board_info, NULL); + MODULE_NAME, isp_info->board_info, NULL, + 0); if (!sd) { v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n"); return NULL; diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index 0f49061..c71242b 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -1406,7 +1406,7 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) goto ereset; subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap, - NULL, vou_pdata->board_info, NULL); + NULL, vou_pdata->board_info, NULL, 0); if (!subdev) { ret = -ENOMEM; goto ei2cnd; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 43848a7..7570d0d 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -896,7 +896,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, icl->board_info->platform_data = icd; subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - NULL, icl->board_info, NULL); + NULL, icl->board_info, NULL, 0); if (!subdev) goto ei2cnd; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 04ff164..6a55cdf 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -369,7 +369,8 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); /* Load an i2c sub-device. */ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, struct i2c_adapter *adapter, const char *module_name, - struct i2c_board_info *info, const unsigned short *probe_addrs) + struct i2c_board_info *info, const unsigned short *probe_addrs, + int enable_devnode) { struct v4l2_subdev *sd = NULL; struct i2c_client *client; @@ -402,9 +403,12 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, if (!try_module_get(client->driver->driver.owner)) goto error; sd = i2c_get_clientdata(client); + if (!enable_devnode) + sd->flags &= ~V4L2_SUBDEV_FL_HAS_DEVNODE; /* 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. */ @@ -419,6 +423,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; } } @@ -445,7 +451,7 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, info.addr = addr; return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, module_name, - &info, probe_addrs); + &info, probe_addrs, 0); } EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); @@ -515,7 +521,8 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init); struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, - struct spi_master *master, struct spi_board_info *info) + struct spi_master *master, struct spi_board_info *info, + int enable_devnode) { struct v4l2_subdev *sd = NULL; struct spi_device *spi = NULL; @@ -534,6 +541,8 @@ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, goto error; sd = spi_get_drvdata(spi); + if (!enable_devnode) + sd->flags &= ~V4L2_SUBDEV_FL_HAS_DEVNODE; /* Register with the v4l2_device which increases the module's use count as well. */ diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 03f7f46..035db52 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -380,13 +380,14 @@ static int get_index(struct video_device *vdev) } /** - * video_register_device - register video4linux devices + * __video_register_device - register video4linux devices * @vdev: video device structure we want to register * @type: type of device to register * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) * @warn_if_nr_in_use: warn if the desired device node number * was already in use and another number was chosen instead. + * @owner: module that owns the video device node * * The registration code assigns minor numbers and device node numbers * based on the requested type and registers the new device node with @@ -403,9 +404,11 @@ 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) +int __video_register_device(struct video_device *vdev, int type, int nr, + int warn_if_nr_in_use, struct module *owner) { int i = 0; int ret; @@ -438,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 = "v4l-subdev"; + break; default: printk(KERN_ERR "%s called with unknown type: %d\n", __func__, type); @@ -521,7 +527,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, goto cleanup; } vdev->cdev->ops = &v4l2_fops; - vdev->cdev->owner = vdev->fops->owner; + vdev->cdev->owner = owner; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { printk(KERN_ERR "%s: cdev_add failed\n", __func__); @@ -570,18 +576,7 @@ cleanup: vdev->minor = -1; return ret; } - -int video_register_device(struct video_device *vdev, int type, int nr) -{ - return __video_register_device(vdev, type, nr, 1); -} -EXPORT_SYMBOL(video_register_device); - -int video_register_device_no_warn(struct video_device *vdev, int type, int nr) -{ - return __video_register_device(vdev, type, nr, 0); -} -EXPORT_SYMBOL(video_register_device_no_warn); +EXPORT_SYMBOL(__video_register_device); /** * video_unregister_device - unregister a video4linux device diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 0b08f96..318e911 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -116,24 +116,43 @@ 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 err; /* 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; + /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); if (err) return err; + 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; + strlcpy(vdev->name, sd->name, sizeof(vdev->name)); + vdev->parent = v4l2_dev->dev; + vdev->fops = &v4l2_subdev_fops; + vdev->release = video_device_release_empty; + if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) { + err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, + sd->owner); + if (err < 0) + v4l2_device_unregister_subdev(sd); + } + + return err; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); @@ -142,10 +161,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); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 1e01554..4137e4c 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -413,7 +413,7 @@ static unsigned long cmd_input_size(unsigned int cmd) } } -static long +long __video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, v4l2_kioctl func) { diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c new file mode 100644 index 0000000..00bd4b1 --- /dev/null +++ b/drivers/media/video/v4l2-subdev.c @@ -0,0 +1,66 @@ +/* + * V4L2 subdevice support. + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Laurent Pinchart + * + * 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. + * + * 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 +#include +#include + +#include +#include + +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, +}; diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 98e0e35..eae2ef0 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -148,7 +148,8 @@ struct i2c_board_info; struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, struct i2c_adapter *adapter, const char *module_name, - struct i2c_board_info *info, const unsigned short *probe_addrs); + struct i2c_board_info *info, const unsigned short *probe_addrs, + int enable_devnode); /* Initialize an v4l2_subdev with data from an i2c_client struct */ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, @@ -181,7 +182,8 @@ struct spi_device; /* Load an spi module and return an initialized v4l2_subdev struct. The client_type argument is the name of the chip that's on the adapter. */ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, - struct spi_master *master, struct spi_board_info *info); + struct spi_master *master, struct spi_board_info *info, + int enable_devnode); /* Initialize an v4l2_subdev with data from an spi_device struct */ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 15802a0..4fe6831 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -21,7 +21,8 @@ #define VFL_TYPE_GRABBER 0 #define VFL_TYPE_VBI 1 #define VFL_TYPE_RADIO 2 -#define VFL_TYPE_MAX 3 +#define VFL_TYPE_SUBDEV 3 +#define VFL_TYPE_MAX 4 struct v4l2_ioctl_callbacks; struct video_device; @@ -102,15 +103,26 @@ struct video_device /* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) +int __must_check __video_register_device(struct video_device *vdev, int type, + int nr, int warn_if_nr_in_use, struct module *owner); + /* Register video devices. Note that if video_register_device fails, the release() callback of the video_device structure is *not* called, so the caller is responsible for freeing any data. Usually that means that you call video_device_release() on failure. */ -int __must_check video_register_device(struct video_device *vdev, int type, int nr); +static inline int __must_check video_register_device(struct video_device *vdev, + int type, int nr) +{ + return __video_register_device(vdev, type, nr, 1, vdev->fops->owner); +} /* Same as video_register_device, but no warning is issued if the desired device node number was already in use. */ -int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr); +static inline int __must_check video_register_device_no_warn( + struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 0, vdev->fops->owner); +} /* Unregister video devices. Will do nothing if vdev == NULL or video_is_registered() returns false. */ diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 06daa6e..abb64d0 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -316,6 +316,9 @@ extern long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); #endif +extern long __video_usercopy(struct file *file, unsigned int cmd, + unsigned long arg, v4l2_kioctl func); + /* Include support for obsoleted stuff */ extern long video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, v4l2_kioctl func); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b636444..de181db 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -22,6 +22,7 @@ #define _V4L2_SUBDEV_H #include +#include #include /* generic v4l2_device notify callback notification values */ @@ -418,9 +419,11 @@ struct v4l2_subdev_ops { #define V4L2_SUBDEV_NAME_SIZE 32 /* Set this flag if this subdev is a i2c device. */ -#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) +#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) /* Set this flag if this subdev is a spi device. */ -#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) +#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) +/* Set this flag if this subdev needs a device node. */ +#define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) /* Each instance of a subdev driver should create this struct, either stand-alone or embedded in a larger struct. @@ -440,8 +443,16 @@ struct v4l2_subdev { /* pointer to private data */ void *dev_priv; void *host_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->dev_priv = p; @@ -474,6 +485,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, sd->grp_id = 0; sd->dev_priv = NULL; sd->host_priv = NULL; + sd->initialized = 1; } /* Call an ops of a v4l2_subdev, doing the right checks against