---
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index a593ea0598b5..a81ea5b01f12 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -873,10 +873,9 @@ static int video_register_media_controller(struct video_device *vdev)
return 0;
}
-int __video_register_device(struct video_device *vdev,
- enum vfl_devnode_type type,
- int nr, int warn_if_nr_in_use,
- struct module *owner)
+int v4l2_vdev_init(struct video_device *vdev,
+ enum vfl_devnode_type type,
+ int nr, int warn_if_nr_in_use)
{
int i = 0;
int ret;
@@ -1009,7 +1008,68 @@ int __video_register_device(struct video_device *vdev,
if (vdev->ioctl_ops)
determine_valid_ioctls(vdev);
- /* Part 3: Initialize the character device */
+ /* Part 3: initialize vdev->dev */
+ vdev->dev.class = &video_class;
+ vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
+ vdev->dev.parent = vdev->dev_parent;
+ dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
+
+ if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
+ pr_warn("%s: requested %s%d, got %s\n", __func__,
+ name_base, nr, video_device_node_name(vdev));
+
+ /* Part 4: Register the entity. */
+ ret = video_register_media_controller(vdev);
+ if (ret)
+ goto cleanup;
+
+ /* Part 5: mark as initialized. */
+ set_bit(V4L2_FL_INITIALIZED, &vdev->flags);
+
+ return 0;
+
+cleanup:
+ mutex_lock(&videodev_lock);
+ video_devices[vdev->minor] = NULL;
+ devnode_clear(vdev);
+ mutex_unlock(&videodev_lock);
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_vdev_init);
+
+void v4l2_vdev_uninit(struct video_device *vdev)
+{
+ mutex_lock(&videodev_lock);
+ clear_bit(V4L2_FL_INITIALIZED, &vdev->flags);
+ video_devices[vdev->minor] = NULL;
+ devnode_clear(vdev);
+ mutex_unlock(&videodev_lock);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (vdev->v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
+ /* Remove interfaces and interface links */
+ media_devnode_remove(vdev->intf_devnode);
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
+ media_device_unregister_entity(&vdev->entity);
+ }
+#endif
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
+}
+EXPORT_SYMBOL(v4l2_vdev_uninit);
+
+int v4l2_vdev_register(struct video_device *vdev, struct module *owner)
+{
+ int ret;
+
+ if (WARN_ON(!test_bit(V4L2_FL_INITIALIZED, &vdev->flags)))
+ return -EINVAL;
+
+ /* Part 1: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
@@ -1025,11 +1085,7 @@ int __video_register_device(struct video_device *vdev,
goto cleanup;
}
- /* Part 4: register the device with sysfs */
- vdev->dev.class = &video_class;
- vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
- vdev->dev.parent = vdev->dev_parent;
- dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ /* Part 2: register the device with sysfs */
ret = device_register(&vdev->dev);
if (ret < 0) {
pr_err("%s: device_register failed\n", __func__);
@@ -1039,17 +1095,10 @@ int __video_register_device(struct video_device *vdev,
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;
- if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
- pr_warn("%s: requested %s%d, got %s\n", __func__,
- name_base, nr, video_device_node_name(vdev));
-
/* Increase v4l2_device refcount */
v4l2_device_get(vdev->v4l2_dev);
- /* Part 5: Register the entity. */
- ret = video_register_media_controller(vdev);
-
- /* Part 6: Activate this minor. The char device can now be used. */
+ /* Part 3: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
return 0;
@@ -1058,11 +1107,24 @@ int __video_register_device(struct video_device *vdev,
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
- video_devices[vdev->minor] = NULL;
- devnode_clear(vdev);
mutex_unlock(&videodev_lock);
- /* Mark this video device as never having been registered. */
- vdev->minor = -1;
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_vdev_register);
+
+int __video_register_device(struct video_device *vdev,
+ enum vfl_devnode_type type,
+ int nr, int warn_if_nr_in_use,
+ struct module *owner)
+{
+ int ret;
+
+ ret = v4l2_vdev_init(vdev, type, nr, warn_if_nr_in_use);
+ if (ret)
+ return ret;
+ ret = v4l2_vdev_register(vdev, owner);
+ if (ret)
+ v4l2_vdev_uninit(vdev);
return ret;
}
EXPORT_SYMBOL(__video_register_device);
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index ad2d41952442..0a8e22e92e6a 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -66,6 +66,9 @@ struct v4l2_ctrl_handler;
/**
* enum v4l2_video_device_flags - Flags used by &struct video_device
*
+ * @V4L2_FL_INITIALIZED:
+ * indicates that a &struct video_device is initialized.
+ * It is cleared by video_unregister_device.
* @V4L2_FL_REGISTERED:
* indicates that a &struct video_device is registered.
* Drivers can clear this flag if they want to block all future
@@ -90,10 +93,11 @@ struct v4l2_ctrl_handler;
* handler to restrict access to some ioctl calls.
*/
enum v4l2_video_device_flags {
- V4L2_FL_REGISTERED = 0,
- V4L2_FL_USES_V4L2_FH = 1,
- V4L2_FL_QUIRK_INVERTED_CROP = 2,
- V4L2_FL_SUBDEV_RO_DEVNODE = 3,
+ V4L2_FL_INITIALIZED,
+ V4L2_FL_REGISTERED,
+ V4L2_FL_USES_V4L2_FH,
+ V4L2_FL_QUIRK_INVERTED_CROP,
+ V4L2_FL_SUBDEV_RO_DEVNODE,
};
/* Priority helper functions */
@@ -326,6 +330,13 @@ struct video_device
*/
#define to_video_device(cd) container_of(cd, struct video_device, dev)
+int v4l2_vdev_init(struct video_device *vdev,
+ enum vfl_devnode_type type,
+ int nr, int warn_if_nr_in_use);
+void v4l2_vdev_uninit(struct video_device *vdev);
+
+int v4l2_vdev_register(struct video_device *vdev, struct module *owner);
+
/**
* __video_register_device - register video4linux devices
*