V4L/DVB (10643): v4l2-device: allow a NULL parent device when registering.
authorHans Verkuil <hverkuil@xs4all.nl>
Sat, 14 Feb 2009 14:54:23 +0000 (11:54 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Mar 2009 15:42:50 +0000 (12:42 -0300)
Some drivers (e.g. for ISA devices) have no parent device because there
is no associated bus driver. Allow the parent device to be NULL in
those cases when registering v4l2_device.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Documentation/video4linux/v4l2-framework.txt
drivers/media/video/v4l2-device.c
include/media/v4l2-device.h

index 48cdf86..e1620e2 100644 (file)
@@ -84,12 +84,14 @@ You must register the device instance:
        v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
 
 Registration will initialize the v4l2_device struct and link dev->driver_data
-to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from
-dev (driver name followed by the bus_id, to be precise). You may change the
-name after registration if you want.
+to v4l2_dev. If v4l2_dev->name is empty then it will be set to a value derived
+from dev (driver name followed by the bus_id, to be precise). If you set it
+up before calling v4l2_device_register then it will be untouched. If dev is
+NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register.
 
 The first 'dev' argument is normally the struct device pointer of a pci_dev,
-usb_device or platform_device.
+usb_device or platform_device. It is rare for dev to be NULL, but it happens
+with ISA devices, for example.
 
 You unregister with:
 
index 8a4b74f..3330ffb 100644 (file)
 
 int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
 {
-       if (dev == NULL || v4l2_dev == NULL)
+       if (v4l2_dev == NULL)
                return -EINVAL;
-       /* Warn if we apparently re-register a device */
-       WARN_ON(dev_get_drvdata(dev) != NULL);
+
        INIT_LIST_HEAD(&v4l2_dev->subdevs);
        spin_lock_init(&v4l2_dev->lock);
        v4l2_dev->dev = dev;
-       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
+       if (dev == NULL) {
+               /* If dev == NULL, then name must be filled in by the caller */
+               WARN_ON(!v4l2_dev->name[0]);
+               return 0;
+       }
+
+       /* Set name to driver name + device name if it is empty. */
+       if (!v4l2_dev->name[0])
+               snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
                        dev->driver->name, dev_name(dev));
+       if (dev_get_drvdata(dev))
+               v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n");
        dev_set_drvdata(dev, v4l2_dev);
        return 0;
 }
@@ -44,10 +53,11 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
 {
        struct v4l2_subdev *sd, *next;
 
-       if (v4l2_dev == NULL || v4l2_dev->dev == NULL)
+       if (v4l2_dev == NULL)
                return;
-       dev_set_drvdata(v4l2_dev->dev, NULL);
-       /* unregister subdevs */
+       if (v4l2_dev->dev)
+               dev_set_drvdata(v4l2_dev->dev, NULL);
+       /* Unregister subdevs */
        list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list)
                v4l2_device_unregister_subdev(sd);
 
@@ -55,19 +65,20 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
 }
 EXPORT_SYMBOL_GPL(v4l2_device_unregister);
 
-int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd)
+int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+                                               struct v4l2_subdev *sd)
 {
        /* Check for valid input */
-       if (dev == NULL || sd == NULL || !sd->name[0])
+       if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
                return -EINVAL;
        /* Warn if we apparently re-register a subdev */
        WARN_ON(sd->dev != NULL);
        if (!try_module_get(sd->owner))
                return -ENODEV;
-       sd->dev = dev;
-       spin_lock(&dev->lock);
-       list_add_tail(&sd->list, &dev->subdevs);
-       spin_unlock(&dev->lock);
+       sd->dev = v4l2_dev;
+       spin_lock(&v4l2_dev->lock);
+       list_add_tail(&sd->list, &v4l2_dev->subdevs);
+       spin_unlock(&v4l2_dev->lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
index 55e41af..5d7146d 100644 (file)
@@ -33,7 +33,9 @@
 #define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16)
 
 struct v4l2_device {
-       /* dev->driver_data points to this struct */
+       /* dev->driver_data points to this struct.
+          Note: dev might be NULL if there is no parent device
+          as is the case with e.g. ISA devices. */
        struct device *dev;
        /* used to keep track of the registered subdevs */
        struct list_head subdevs;
@@ -44,7 +46,9 @@ struct v4l2_device {
        char name[V4L2_DEVICE_NAME_SIZE];
 };
 
-/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */
+/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
+   dev may be NULL in rare cases (ISA devices). In that case you
+   must fill in the v4l2_dev->name field before calling this function. */
 int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
 /* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */
 void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
@@ -52,23 +56,24 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
 /* Register a subdev with a v4l2 device. While registered the subdev module
    is marked as in-use. An error is returned if the module is no longer
    loaded when you attempt to register it. */
-int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd);
+int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+                                               struct v4l2_subdev *sd);
 /* Unregister a subdev with a v4l2 device. Can also be called if the subdev
    wasn't registered. In that case it will do nothing. */
 void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
 
 /* Iterate over all subdevs. */
-#define v4l2_device_for_each_subdev(sd, dev)                           \
-       list_for_each_entry(sd, &(dev)->subdevs, list)
+#define v4l2_device_for_each_subdev(sd, v4l2_dev)                      \
+       list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)
 
 /* Call the specified callback for all subdevs matching the condition.
    Ignore any errors. Note that you cannot add or delete a subdev
    while walking the subdevs list. */
-#define __v4l2_device_call_subdevs(dev, cond, o, f, args...)           \
+#define __v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args...)      \
        do {                                                            \
                struct v4l2_subdev *sd;                                 \
                                                                        \
-               list_for_each_entry(sd, &(dev)->subdevs, list)          \
+               list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)     \
                        if ((cond) && sd->ops->o && sd->ops->o->f)      \
                                sd->ops->o->f(sd , ##args);             \
        } while (0)
@@ -77,12 +82,12 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
    If the callback returns an error other than 0 or -ENOIOCTLCMD, then
    return with that error code. Note that you cannot add or delete a
    subdev while walking the subdevs list. */
-#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...)  \
+#define __v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args...) \
 ({                                                                     \
        struct v4l2_subdev *sd;                                         \
        long err = 0;                                                   \
                                                                        \
-       list_for_each_entry(sd, &(dev)->subdevs, list) {                \
+       list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) {           \
                if ((cond) && sd->ops->o && sd->ops->o->f)              \
                        err = sd->ops->o->f(sd , ##args);               \
                if (err && err != -ENOIOCTLCMD)                         \
@@ -94,16 +99,16 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
 /* Call the specified callback for all subdevs matching grp_id (if 0, then
    match them all). Ignore any errors. Note that you cannot add or delete
    a subdev while walking the subdevs list. */
-#define v4l2_device_call_all(dev, grpid, o, f, args...)                \
-       __v4l2_device_call_subdevs(dev,                                 \
+#define v4l2_device_call_all(v4l2_dev, grpid, o, f, args...)           \
+       __v4l2_device_call_subdevs(v4l2_dev,                            \
                        !(grpid) || sd->grp_id == (grpid), o, f , ##args)
 
 /* Call the specified callback for all subdevs matching grp_id (if 0, then
    match them all). If the callback returns an error other than 0 or
    -ENOIOCTLCMD, then return with that error code. Note that you cannot
    add or delete a subdev while walking the subdevs list. */
-#define v4l2_device_call_until_err(dev, grpid, o, f, args...)          \
-       __v4l2_device_call_subdevs_until_err(dev,                       \
+#define v4l2_device_call_until_err(v4l2_dev, grpid, o, f, args...)     \
+       __v4l2_device_call_subdevs_until_err(v4l2_dev,                  \
                       !(grpid) || sd->grp_id == (grpid), o, f , ##args)
 
 #endif