summaryrefslogtreecommitdiffstats
path: root/drivers/media/v4l2-core/v4l2-async.c
diff options
context:
space:
mode:
authorSteve Longerbeam <slongerbeam@gmail.com>2018-09-29 15:54:06 -0400
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>2018-10-04 15:17:33 -0400
commitb47d7ff1ae8d40eef92abbe718316c8a56558744 (patch)
treefc9ab32839a16e72aa9773c01704c6ffc762d042 /drivers/media/v4l2-core/v4l2-async.c
parenta6e7003c35e185b0fe4245bdfd086021de9ec305 (diff)
downloadlinux-b47d7ff1ae8d40eef92abbe718316c8a56558744.tar.bz2
media: v4l2: async: Add v4l2_async_notifier_add_subdev
v4l2_async_notifier_add_subdev() adds an asd to the notifier. It checks that no other equivalent asd's have already been added to this notifier's asd list, or to other registered notifier's waiting or done lists, and increments num_subdevs. v4l2_async_notifier_add_subdev() does not make use of the notifier subdevs array, otherwise it would have to re-allocate the array every time the function was called. In place of the subdevs array, the function adds the newly allocated asd to a new master asd_list. The function will return error with a WARN() if it is ever called with the subdevs array allocated. Drivers are now required to call a v4l2_async_notifier_init(), before the first call to v4l2_async_notifier_add_subdev(), in order to initialize the asd_list. In v4l2_async_notifier_has_async_subdev(), __v4l2_async_notifier_register(), and v4l2_async_notifier_cleanup(), maintain backward compatibility with the subdevs array, by alternatively operate on the subdevs array or a non-empty notifier->asd_list. Signed-off-by: Steve Longerbeam <slongerbeam@gmail.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-async.c')
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c191
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, &notifier->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(&notifier->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, &notifier->waiting);
+ }
+ } else {
+ i = 0;
+ list_for_each_entry(asd, &notifier->asd_list, asd_list) {
+ ret = v4l2_async_notifier_asd_valid(notifier, asd, i++);
+ if (ret)
+ goto err_unlock;
+
+ list_add_tail(&asd->list, &notifier->waiting);
}
- list_add_tail(&asd->list, &notifier->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,
+ &notifier->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, &notifier->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;