diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/v4l2-core/v4l2-async.c | 191 |
1 files changed, 149 insertions, 42 deletions
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index f09d354b96a0..7925875d09b7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -365,16 +365,26 @@ v4l2_async_notifier_has_async_subdev(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd, unsigned int this_index) { + struct v4l2_async_subdev *asd_y; unsigned int j; lockdep_assert_held(&list_lock); /* Check that an asd is not being added more than once. */ - for (j = 0; j < this_index; j++) { - struct v4l2_async_subdev *asd_y = notifier->subdevs[j]; - - if (asd_equal(asd, asd_y)) - return true; + if (notifier->subdevs) { + for (j = 0; j < this_index; j++) { + asd_y = notifier->subdevs[j]; + if (asd_equal(asd, asd_y)) + return true; + } + } else { + j = 0; + list_for_each_entry(asd_y, ¬ifier->asd_list, asd_list) { + if (j++ >= this_index) + break; + if (asd_equal(asd, asd_y)) + return true; + } } /* Check that an asd does not exist in other notifiers. */ @@ -385,10 +395,48 @@ v4l2_async_notifier_has_async_subdev(struct v4l2_async_notifier *notifier, return false; } -static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) +static int v4l2_async_notifier_asd_valid(struct v4l2_async_notifier *notifier, + struct v4l2_async_subdev *asd, + unsigned int this_index) { struct device *dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL; + + if (!asd) + return -EINVAL; + + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: + case V4L2_ASYNC_MATCH_DEVNAME: + case V4L2_ASYNC_MATCH_I2C: + case V4L2_ASYNC_MATCH_FWNODE: + if (v4l2_async_notifier_has_async_subdev(notifier, asd, + this_index)) { + dev_dbg(dev, "subdev descriptor already listed in this or other notifiers\n"); + return -EEXIST; + } + break; + default: + dev_err(dev, "Invalid match type %u on %p\n", + asd->match_type, asd); + return -EINVAL; + } + + return 0; +} + +void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier) +{ + mutex_lock(&list_lock); + + INIT_LIST_HEAD(¬ifier->asd_list); + + mutex_unlock(&list_lock); +} +EXPORT_SYMBOL(v4l2_async_notifier_init); + +static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) +{ struct v4l2_async_subdev *asd; int ret; int i; @@ -401,29 +449,25 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) mutex_lock(&list_lock); - for (i = 0; i < notifier->num_subdevs; i++) { - asd = notifier->subdevs[i]; + if (notifier->subdevs) { + for (i = 0; i < notifier->num_subdevs; i++) { + asd = notifier->subdevs[i]; - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_CUSTOM: - case V4L2_ASYNC_MATCH_DEVNAME: - case V4L2_ASYNC_MATCH_I2C: - case V4L2_ASYNC_MATCH_FWNODE: - if (v4l2_async_notifier_has_async_subdev(notifier, - asd, i)) { - dev_err(dev, - "subdev descriptor already listed in this or other notifiers\n"); - ret = -EEXIST; + ret = v4l2_async_notifier_asd_valid(notifier, asd, i); + if (ret) goto err_unlock; - } - break; - default: - dev_err(dev, "Invalid match type %u on %p\n", - asd->match_type, asd); - ret = -EINVAL; - goto err_unlock; + + list_add_tail(&asd->list, ¬ifier->waiting); + } + } else { + i = 0; + list_for_each_entry(asd, ¬ifier->asd_list, asd_list) { + ret = v4l2_async_notifier_asd_valid(notifier, asd, i++); + if (ret) + goto err_unlock; + + list_add_tail(&asd->list, ¬ifier->waiting); } - list_add_tail(&asd->list, ¬ifier->waiting); } ret = v4l2_async_notifier_try_all_subdevs(notifier); @@ -513,36 +557,99 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) } EXPORT_SYMBOL(v4l2_async_notifier_unregister); -void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) +static void __v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) { + struct v4l2_async_subdev *asd, *tmp; unsigned int i; - if (!notifier || !notifier->max_subdevs) + if (!notifier) return; - for (i = 0; i < notifier->num_subdevs; i++) { - struct v4l2_async_subdev *asd = notifier->subdevs[i]; + if (notifier->subdevs) { + if (!notifier->max_subdevs) + return; - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_FWNODE: - fwnode_handle_put(asd->match.fwnode); - break; - default: - WARN_ON_ONCE(true); - break; + for (i = 0; i < notifier->num_subdevs; i++) { + asd = notifier->subdevs[i]; + + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_FWNODE: + fwnode_handle_put(asd->match.fwnode); + break; + default: + break; + } + + kfree(asd); } - kfree(asd); + notifier->max_subdevs = 0; + kvfree(notifier->subdevs); + notifier->subdevs = NULL; + } else { + list_for_each_entry_safe(asd, tmp, + ¬ifier->asd_list, asd_list) { + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_FWNODE: + fwnode_handle_put(asd->match.fwnode); + break; + default: + break; + } + + list_del(&asd->asd_list); + kfree(asd); + } } - notifier->max_subdevs = 0; notifier->num_subdevs = 0; +} + +void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) +{ + mutex_lock(&list_lock); + + __v4l2_async_notifier_cleanup(notifier); - kvfree(notifier->subdevs); - notifier->subdevs = NULL; + mutex_unlock(&list_lock); } EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); +int v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, + struct v4l2_async_subdev *asd) +{ + int ret; + + mutex_lock(&list_lock); + + if (notifier->num_subdevs >= V4L2_MAX_SUBDEVS) { + ret = -EINVAL; + goto unlock; + } + + /* + * If caller uses this function, it cannot also allocate and + * place asd's in the notifier->subdevs array. + */ + if (WARN_ON(notifier->subdevs)) { + ret = -EINVAL; + goto unlock; + } + + ret = v4l2_async_notifier_asd_valid(notifier, asd, + notifier->num_subdevs); + if (ret) + goto unlock; + + list_add_tail(&asd->asd_list, ¬ifier->asd_list); + notifier->num_subdevs++; + +unlock: + mutex_unlock(&list_lock); + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_subdev); + int v4l2_async_register_subdev(struct v4l2_subdev *sd) { struct v4l2_async_notifier *subdev_notifier; @@ -616,7 +723,7 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) mutex_lock(&list_lock); __v4l2_async_notifier_unregister(sd->subdev_notifier); - v4l2_async_notifier_cleanup(sd->subdev_notifier); + __v4l2_async_notifier_cleanup(sd->subdev_notifier); kfree(sd->subdev_notifier); sd->subdev_notifier = NULL; |