aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core/v4l2-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-device.c')
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 8ed5da2170b..015f92aab44 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -44,7 +44,8 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
- WARN_ON(!v4l2_dev->name[0]);
+ if (WARN_ON(!v4l2_dev->name[0]))
+ return -EINVAL;
return 0;
}
@@ -105,7 +106,9 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
{
struct v4l2_subdev *sd, *next;
- if (v4l2_dev == NULL)
+ /* Just return if v4l2_dev is NULL or if it was already
+ * unregistered before. */
+ if (v4l2_dev == NULL || !v4l2_dev->name[0])
return;
v4l2_device_disconnect(v4l2_dev);
@@ -135,6 +138,8 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
}
#endif
}
+ /* Mark as unregistered, thus preventing duplicate unregistrations */
+ v4l2_dev->name[0] = '\0';
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);
@@ -153,7 +158,17 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
/* Warn if we apparently re-register a subdev */
WARN_ON(sd->v4l2_dev != NULL);
- if (!try_module_get(sd->owner))
+ /*
+ * The reason to acquire the module here is to avoid unloading
+ * a module of sub-device which is registered to a media
+ * device. To make it possible to unload modules for media
+ * devices that also register sub-devices, do not
+ * try_module_get() such sub-device owners.
+ */
+ sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&
+ sd->owner == v4l2_dev->dev->driver->owner;
+
+ if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))
return -ENODEV;
sd->v4l2_dev = v4l2_dev;
@@ -187,7 +202,8 @@ error_unregister:
if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd);
error_module:
- module_put(sd->owner);
+ if (!sd->owner_v4l2_dev)
+ module_put(sd->owner);
sd->v4l2_dev = NULL;
return err;
}
@@ -269,10 +285,13 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
sd->v4l2_dev = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev->mdev)
+ if (v4l2_dev->mdev) {
+ media_entity_remove_links(&sd->entity);
media_device_unregister_entity(&sd->entity);
+ }
#endif
video_unregister_device(sd->devnode);
- module_put(sd->owner);
+ if (!sd->owner_v4l2_dev)
+ module_put(sd->owner);
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);