|
@@ -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;
|
|
|
|