@@ -248,18 +248,20 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
list_move(&sd->async_list, ¬ifier->done);
/*
- * See if the sub-device has a notifier. If it does, proceed
- * with checking for its async sub-devices.
+ * See if the sub-device has a notifier. If not, return here.
*/
subdev_notifier = v4l2_async_find_subdev_notifier(sd);
- if (subdev_notifier && !subdev_notifier->parent) {
- subdev_notifier->parent = notifier;
- ret = v4l2_async_notifier_try_all_subdevs(subdev_notifier);
- if (ret)
- return ret;
- }
+ if (!subdev_notifier || subdev_notifier->parent)
+ return 0;
- return 0;
+ /*
+ * Proceed with checking for the sub-device notifier's async
+ * sub-devices, and return the result. The error will be handled by the
+ * caller.
+ */
+ subdev_notifier->parent = notifier;
+
+ return v4l2_async_notifier_try_all_subdevs(subdev_notifier);
}
/* Test all async sub-devices in a notifier for a match. */
@@ -304,7 +306,28 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd)
/* Subdevice driver will reprobe and put the subdev back onto the list */
list_del_init(&sd->async_list);
sd->asd = NULL;
- sd->dev = NULL;
+}
+
+/* Unbind all sub-devices in the notifier tree. */
+static void v4l2_async_notifier_unbind_all_subdevs(
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_subdev *sd, *tmp;
+
+ list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
+ struct v4l2_async_notifier *subdev_notifier =
+ v4l2_async_find_subdev_notifier(sd);
+
+ if (subdev_notifier)
+ v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
+
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
+ v4l2_async_cleanup(sd);
+
+ list_move(&sd->async_list, &subdev_list);
+ }
+
+ notifier->parent = NULL;
}
/* See if an fwnode can be found in a notifier's lists. */
@@ -412,9 +435,11 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
ret = v4l2_async_notifier_try_all_subdevs(notifier);
if (ret)
- goto out_unlock;
+ goto err_unbind;
ret = v4l2_async_notifier_try_complete(notifier);
+ if (ret)
+ goto err_unbind;
/* Keep also completed notifiers on the list */
list_add(¬ifier->list, ¬ifier_list);
@@ -422,69 +447,74 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
out_unlock:
mutex_unlock(&list_lock);
+ return 0;
+
+err_unbind:
+ /*
+ * On failure, unbind all sub-devices registered through this notifier.
+ */
+ v4l2_async_notifier_unbind_all_subdevs(notifier);
+
+ mutex_unlock(&list_lock);
+
return ret;
}
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
+ int ret;
+
if (WARN_ON(!v4l2_dev || notifier->sd))
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
- return __v4l2_async_notifier_register(notifier);
+ ret = __v4l2_async_notifier_register(notifier);
+ if (ret)
+ notifier->v4l2_dev = NULL;
+
+ return ret;
}
EXPORT_SYMBOL(v4l2_async_notifier_register);
int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
struct v4l2_async_notifier *notifier)
{
+ int ret;
+
if (WARN_ON(!sd || notifier->v4l2_dev))
return -EINVAL;
notifier->sd = sd;
- return __v4l2_async_notifier_register(notifier);
+ ret = __v4l2_async_notifier_register(notifier);
+ if (ret)
+ notifier->sd = NULL;
+
+ return ret;
}
EXPORT_SYMBOL(v4l2_async_subdev_notifier_register);
-/* Unbind all sub-devices in the notifier tree. */
-static void v4l2_async_notifier_unbind_all_subdevs(
+static void __v4l2_async_notifier_unregister(
struct v4l2_async_notifier *notifier)
{
- struct v4l2_subdev *sd, *tmp;
-
- list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
- struct v4l2_async_notifier *subdev_notifier =
- v4l2_async_find_subdev_notifier(sd);
-
- if (subdev_notifier)
- v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
-
- v4l2_async_cleanup(sd);
+ if (!notifier || (!notifier->v4l2_dev && !notifier->sd))
+ return;
- v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
+ v4l2_async_notifier_unbind_all_subdevs(notifier);
- list_move(&sd->async_list, &subdev_list);
- }
+ notifier->sd = NULL;
+ notifier->v4l2_dev = NULL;
- notifier->parent = NULL;
+ list_del(¬ifier->list);
}
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{
- if (!notifier->v4l2_dev && !notifier->sd)
- return;
-
mutex_lock(&list_lock);
- v4l2_async_notifier_unbind_all_subdevs(notifier);
-
- notifier->sd = NULL;
- notifier->v4l2_dev = NULL;
-
- list_del(¬ifier->list);
+ __v4l2_async_notifier_unregister(notifier);
mutex_unlock(&list_lock);
}
@@ -522,7 +552,9 @@ EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup);
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
+ struct v4l2_async_notifier *subdev_notifier;
struct v4l2_async_notifier *notifier;
+ int ret;
/*
* No reference taken. The reference is held by the device
@@ -549,47 +581,64 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
if (!asd)
continue;
- ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
+ ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd,
+ asd);
+ if (ret)
+ goto err_unbind;
- if (!ret)
- ret = v4l2_async_notifier_try_complete(notifier);
+ ret = v4l2_async_notifier_try_complete(notifier);
+ if (ret)
+ goto err_unbind;
- mutex_unlock(&list_lock);
- return ret;
+ goto out_unlock;
}
/* None matched, wait for hot-plugging */
list_add(&sd->async_list, &subdev_list);
+out_unlock:
mutex_unlock(&list_lock);
return 0;
+
+err_unbind:
+ /*
+ * Complete failed. Unbind the sub-devices bound through registering
+ * this async sub-device.
+ */
+ subdev_notifier = v4l2_async_find_subdev_notifier(sd);
+ if (subdev_notifier)
+ v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
+
+ if (sd->asd)
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
+ v4l2_async_cleanup(sd);
+
+ mutex_unlock(&list_lock);
+
+ return ret;
}
EXPORT_SYMBOL(v4l2_async_register_subdev);
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
{
- struct v4l2_async_notifier *notifier = sd->notifier;
+ mutex_lock(&list_lock);
- if (sd->subdev_notifier)
- v4l2_async_notifier_unregister(sd->subdev_notifier);
+ __v4l2_async_notifier_unregister(sd->subdev_notifier);
v4l2_async_notifier_cleanup(sd->subdev_notifier);
kfree(sd->subdev_notifier);
+ sd->subdev_notifier = NULL;
- if (!sd->asd) {
- if (!list_empty(&sd->async_list))
- v4l2_async_cleanup(sd);
- return;
- }
+ if (sd->asd) {
+ struct v4l2_async_notifier *notifier = sd->notifier;
- mutex_lock(&list_lock);
+ list_add(&sd->asd->list, ¬ifier->waiting);
- list_add(&sd->asd->list, ¬ifier->waiting);
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
+ }
v4l2_async_cleanup(sd);
- v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
-
mutex_unlock(&list_lock);
}
EXPORT_SYMBOL(v4l2_async_unregister_subdev);
@@ -201,5 +201,4 @@ int __must_check v4l2_async_register_subdev_sensor_common(
* @sd: pointer to &struct v4l2_subdev
*/
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
-
#endif
@@ -137,7 +137,7 @@ struct v4l2_fwnode_link {
int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
struct v4l2_fwnode_endpoint *vep);
-/*
+/**
* v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by
* v4l2_fwnode_endpoint_alloc_parse()
* @vep - the V4L2 fwnode the resources of which are to be released