2019-06-04 10:11:33 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2013-01-08 07:06:31 -03:00
|
|
|
/*
|
|
|
|
* V4L2 asynchronous subdevice registration API
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
|
|
|
*/
|
|
|
|
|
2021-01-08 18:17:28 +01:00
|
|
|
#include <linux/debugfs.h>
|
2013-01-08 07:06:31 -03:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/list.h>
|
[media] v4l2-core: Use kvmalloc() for potentially big allocations
There are multiple places where arrays or otherwise variable sized
buffer are allocated through V4L2 core code, including things like
controls, memory pages, staging buffers for ioctls and so on. Such
allocations can potentially require an order > 0 allocation from the
page allocator, which is not guaranteed to be fulfilled and is likely to
fail on a system with severe memory fragmentation (e.g. a system with
very long uptime).
Since the memory being allocated is intended to be used by the CPU
exclusively, we can consider using vmalloc() as a fallback and this is
exactly what the recently merged kvmalloc() helpers do. A kmalloc() call
is still attempted, even for order > 0 allocations, but it is done
with __GFP_NORETRY and __GFP_NOWARN, with expectation of failing if
requested memory is not available instantly. Only then the vmalloc()
fallback is used. This should give us fast and more reliable allocations
even on systems with higher memory pressure and/or more fragmentation,
while still retaining the same performance level on systems not
suffering from such conditions.
While at it, replace explicit array size calculations on changed
allocations with kvmalloc_array().
Purposedly not touching videobuf1, as it is deprecated, has only few
users remaining and would rather be seen removed instead.
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-06-19 00:53:43 -03:00
|
|
|
#include <linux/mm.h>
|
2013-01-08 07:06:31 -03:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mutex.h>
|
2016-08-16 06:54:59 -03:00
|
|
|
#include <linux/of.h>
|
2013-01-08 07:06:31 -03:00
|
|
|
#include <linux/platform_device.h>
|
2021-01-08 18:17:28 +01:00
|
|
|
#include <linux/seq_file.h>
|
2013-01-08 07:06:31 -03:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
|
|
|
|
#include <media/v4l2-async.h>
|
|
|
|
#include <media/v4l2-device.h>
|
2017-08-17 11:28:21 -04:00
|
|
|
#include <media/v4l2-fwnode.h>
|
2013-01-08 07:06:31 -03:00
|
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
|
2023-01-27 21:37:25 +01:00
|
|
|
#include "v4l2-subdev-priv.h"
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
|
|
|
|
struct v4l2_subdev *subdev,
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc)
|
2017-09-01 08:27:32 -04:00
|
|
|
{
|
|
|
|
if (!n->ops || !n->ops->bound)
|
|
|
|
return 0;
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
return n->ops->bound(n, subdev, asc);
|
2017-09-01 08:27:32 -04:00
|
|
|
}
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
static void v4l2_async_nf_call_unbind(struct v4l2_async_notifier *n,
|
|
|
|
struct v4l2_subdev *subdev,
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc)
|
2017-09-01 08:27:32 -04:00
|
|
|
{
|
|
|
|
if (!n->ops || !n->ops->unbind)
|
|
|
|
return;
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
n->ops->unbind(n, subdev, asc);
|
2017-09-01 08:27:32 -04:00
|
|
|
}
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n)
|
2017-09-01 08:27:32 -04:00
|
|
|
{
|
|
|
|
if (!n->ops || !n->ops->complete)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return n->ops->complete(n);
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:10:48 +01:00
|
|
|
static void v4l2_async_nf_call_destroy(struct v4l2_async_notifier *n,
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc)
|
2022-06-14 20:10:48 +01:00
|
|
|
{
|
|
|
|
if (!n->ops || !n->ops->destroy)
|
|
|
|
return;
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
n->ops->destroy(asc);
|
2022-06-14 20:10:48 +01:00
|
|
|
}
|
|
|
|
|
2020-07-01 08:21:39 +02:00
|
|
|
static bool match_i2c(struct v4l2_async_notifier *notifier,
|
2023-02-19 13:24:41 +01:00
|
|
|
struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_async_match_desc *match)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2013-06-24 05:13:51 -03:00
|
|
|
#if IS_ENABLED(CONFIG_I2C)
|
2015-06-11 12:18:01 -07:00
|
|
|
struct i2c_client *client = i2c_verify_client(sd->dev);
|
2018-10-04 17:10:46 -04:00
|
|
|
|
2013-01-08 07:06:31 -03:00
|
|
|
return client &&
|
2023-02-19 13:24:41 +01:00
|
|
|
match->i2c.adapter_id == client->adapter->nr &&
|
|
|
|
match->i2c.address == client->addr;
|
2013-06-24 05:13:51 -03:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
static struct device *notifier_dev(struct v4l2_async_notifier *notifier)
|
|
|
|
{
|
|
|
|
if (notifier->sd)
|
|
|
|
return notifier->sd->dev;
|
|
|
|
|
|
|
|
if (notifier->v4l2_dev)
|
|
|
|
return notifier->v4l2_dev->dev;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-07-05 18:40:54 +01:00
|
|
|
static bool
|
|
|
|
match_fwnode_one(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_subdev *sd, struct fwnode_handle *sd_fwnode,
|
2023-02-19 13:24:41 +01:00
|
|
|
struct v4l2_async_match_desc *match)
|
2016-08-16 06:54:59 -03:00
|
|
|
{
|
2023-02-19 00:21:02 +01:00
|
|
|
struct fwnode_handle *asd_dev_fwnode;
|
|
|
|
bool ret;
|
2020-07-01 08:21:38 +02:00
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: fwnode match: need %pfw, trying %pfw\n",
|
2023-02-19 13:24:41 +01:00
|
|
|
sd_fwnode, match->fwnode);
|
2023-02-09 23:10:04 +01:00
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
if (sd_fwnode == match->fwnode) {
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: direct match found\n");
|
2021-01-07 14:28:35 +01:00
|
|
|
return true;
|
2023-02-09 23:10:04 +01:00
|
|
|
}
|
2021-01-07 14:28:35 +01:00
|
|
|
|
2023-02-19 00:21:02 +01:00
|
|
|
if (!fwnode_graph_is_endpoint(match->fwnode)) {
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: direct match not found\n");
|
2020-07-01 08:21:38 +02:00
|
|
|
return false;
|
2023-02-09 23:10:04 +01:00
|
|
|
}
|
2020-07-01 08:21:38 +02:00
|
|
|
|
2023-02-19 00:21:02 +01:00
|
|
|
asd_dev_fwnode = fwnode_graph_get_port_parent(match->fwnode);
|
2020-07-01 08:21:38 +02:00
|
|
|
|
2023-02-19 00:21:02 +01:00
|
|
|
ret = sd_fwnode == asd_dev_fwnode;
|
2020-07-01 08:21:40 +02:00
|
|
|
|
2023-02-19 00:21:02 +01:00
|
|
|
fwnode_handle_put(asd_dev_fwnode);
|
2020-07-01 08:21:40 +02:00
|
|
|
|
2023-02-19 00:21:02 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: device--endpoint match %sfound\n",
|
|
|
|
ret ? "" : "not ");
|
2023-02-09 23:10:04 +01:00
|
|
|
|
2023-02-19 00:21:02 +01:00
|
|
|
return ret;
|
2016-08-16 06:54:59 -03:00
|
|
|
}
|
|
|
|
|
2022-07-05 18:40:54 +01:00
|
|
|
static bool match_fwnode(struct v4l2_async_notifier *notifier,
|
2023-02-19 13:24:41 +01:00
|
|
|
struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_async_match_desc *match)
|
2022-07-05 18:40:54 +01:00
|
|
|
{
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: matching for notifier %pfw, sd fwnode %pfw\n",
|
|
|
|
dev_fwnode(notifier_dev(notifier)), sd->fwnode);
|
|
|
|
|
2023-04-28 15:58:42 +02:00
|
|
|
if (!list_empty(&sd->async_subdev_endpoint_list)) {
|
|
|
|
struct v4l2_async_subdev_endpoint *ase;
|
|
|
|
|
|
|
|
dev_dbg(sd->dev,
|
|
|
|
"v4l2-async: endpoint fwnode list available, looking for %pfw\n",
|
|
|
|
match->fwnode);
|
|
|
|
|
|
|
|
list_for_each_entry(ase, &sd->async_subdev_endpoint_list,
|
|
|
|
async_subdev_endpoint_entry) {
|
|
|
|
bool matched = ase->endpoint == match->fwnode;
|
|
|
|
|
|
|
|
dev_dbg(sd->dev,
|
|
|
|
"v4l2-async: endpoint-endpoint match %sfound with %pfw\n",
|
|
|
|
matched ? "" : "not ", ase->endpoint);
|
|
|
|
|
|
|
|
if (matched)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(sd->dev, "async: no endpoint matched\n");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
if (match_fwnode_one(notifier, sd, sd->fwnode, match))
|
2022-07-05 18:40:54 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Also check the secondary fwnode. */
|
|
|
|
if (IS_ERR_OR_NULL(sd->fwnode->secondary))
|
|
|
|
return false;
|
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: trying secondary fwnode match\n");
|
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
return match_fwnode_one(notifier, sd, sd->fwnode->secondary, match);
|
2022-07-05 18:40:54 +01:00
|
|
|
}
|
|
|
|
|
2013-01-08 07:06:31 -03:00
|
|
|
static LIST_HEAD(subdev_list);
|
|
|
|
static LIST_HEAD(notifier_list);
|
|
|
|
static DEFINE_MUTEX(list_lock);
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
static struct v4l2_async_connection *
|
2018-10-04 17:10:46 -04:00
|
|
|
v4l2_async_find_match(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_subdev *sd)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2020-07-01 08:21:39 +02:00
|
|
|
bool (*match)(struct v4l2_async_notifier *notifier,
|
2023-02-19 13:24:41 +01:00
|
|
|
struct v4l2_subdev *sd,
|
|
|
|
struct v4l2_async_match_desc *match);
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) {
|
2013-01-08 07:06:31 -03:00
|
|
|
/* bus_type has been verified valid before */
|
2023-02-16 14:54:53 +01:00
|
|
|
switch (asc->match.type) {
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_I2C:
|
2013-01-08 07:06:31 -03:00
|
|
|
match = match_i2c;
|
|
|
|
break;
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_FWNODE:
|
2016-08-16 06:54:59 -03:00
|
|
|
match = match_fwnode;
|
|
|
|
break;
|
2013-01-08 07:06:31 -03:00
|
|
|
default:
|
|
|
|
/* Cannot happen, unless someone breaks us */
|
|
|
|
WARN_ON(true);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* match cannot be NULL here */
|
2023-02-16 14:54:53 +01:00
|
|
|
if (match(notifier, sd, &asc->match))
|
|
|
|
return asc;
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
/* Compare two async match descriptors for equivalence */
|
|
|
|
static bool v4l2_async_match_equal(struct v4l2_async_match_desc *match1,
|
|
|
|
struct v4l2_async_match_desc *match2)
|
2018-09-29 15:54:05 -04:00
|
|
|
{
|
2023-02-19 13:24:41 +01:00
|
|
|
if (match1->type != match2->type)
|
2018-09-29 15:54:05 -04:00
|
|
|
return false;
|
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
switch (match1->type) {
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_I2C:
|
2023-02-19 13:24:41 +01:00
|
|
|
return match1->i2c.adapter_id == match2->i2c.adapter_id &&
|
|
|
|
match1->i2c.address == match2->i2c.address;
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_FWNODE:
|
2023-02-19 13:24:41 +01:00
|
|
|
return match1->fwnode == match2->fwnode;
|
2018-09-29 15:54:05 -04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
/* Find the sub-device notifier registered by a sub-device driver. */
|
2018-10-04 17:10:46 -04:00
|
|
|
static struct v4l2_async_notifier *
|
|
|
|
v4l2_async_find_subdev_notifier(struct v4l2_subdev *sd)
|
2017-09-24 20:54:31 -04:00
|
|
|
{
|
|
|
|
struct v4l2_async_notifier *n;
|
|
|
|
|
2023-02-22 12:02:39 +01:00
|
|
|
list_for_each_entry(n, ¬ifier_list, notifier_entry)
|
2017-09-24 20:54:31 -04:00
|
|
|
if (n->sd == sd)
|
|
|
|
return n;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get v4l2_device related to the notifier if one can be found. */
|
2018-10-04 17:10:46 -04:00
|
|
|
static struct v4l2_device *
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_find_v4l2_dev(struct v4l2_async_notifier *notifier)
|
2017-09-24 20:54:31 -04:00
|
|
|
{
|
|
|
|
while (notifier->parent)
|
|
|
|
notifier = notifier->parent;
|
|
|
|
|
|
|
|
return notifier->v4l2_dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if all child sub-device notifiers are complete, false otherwise.
|
|
|
|
*/
|
2018-10-04 17:10:46 -04:00
|
|
|
static bool
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_can_complete(struct v4l2_async_notifier *notifier)
|
2017-09-24 20:54:31 -04:00
|
|
|
{
|
2023-04-18 14:00:54 +02:00
|
|
|
struct v4l2_async_connection *asc;
|
2017-09-24 20:54:31 -04:00
|
|
|
|
2023-02-22 12:02:39 +01:00
|
|
|
if (!list_empty(¬ifier->waiting_list))
|
2017-09-24 20:54:31 -04:00
|
|
|
return false;
|
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
list_for_each_entry(asc, ¬ifier->done_list, asc_entry) {
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_async_notifier *subdev_notifier =
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_find_subdev_notifier(asc->sd);
|
2017-09-24 20:54:31 -04:00
|
|
|
|
|
|
|
if (subdev_notifier &&
|
2021-03-05 18:13:12 +01:00
|
|
|
!v4l2_async_nf_can_complete(subdev_notifier))
|
2017-09-24 20:54:31 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Complete the master notifier if possible. This is done when all async
|
|
|
|
* sub-devices have been bound; v4l2_device is also available then.
|
|
|
|
*/
|
2018-10-04 17:10:46 -04:00
|
|
|
static int
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_try_complete(struct v4l2_async_notifier *notifier)
|
2017-09-24 20:54:31 -04:00
|
|
|
{
|
2023-02-09 23:10:04 +01:00
|
|
|
struct v4l2_async_notifier *__notifier = notifier;
|
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
/* Quick check whether there are still more sub-devices here. */
|
2023-02-22 12:02:39 +01:00
|
|
|
if (!list_empty(¬ifier->waiting_list))
|
2017-09-24 20:54:31 -04:00
|
|
|
return 0;
|
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
if (notifier->sd)
|
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: trying to complete\n");
|
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
/* Check the entire notifier tree; find the root notifier first. */
|
|
|
|
while (notifier->parent)
|
|
|
|
notifier = notifier->parent;
|
|
|
|
|
|
|
|
/* This is root if it has v4l2_dev. */
|
2023-02-09 23:10:04 +01:00
|
|
|
if (!notifier->v4l2_dev) {
|
|
|
|
dev_dbg(notifier_dev(__notifier),
|
|
|
|
"v4l2-async: V4L2 device not available\n");
|
2017-09-24 20:54:31 -04:00
|
|
|
return 0;
|
2023-02-09 23:10:04 +01:00
|
|
|
}
|
2017-09-24 20:54:31 -04:00
|
|
|
|
|
|
|
/* Is everything ready? */
|
2021-03-05 18:13:12 +01:00
|
|
|
if (!v4l2_async_nf_can_complete(notifier))
|
2017-09-24 20:54:31 -04:00
|
|
|
return 0;
|
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(__notifier), "v4l2-async: complete\n");
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
return v4l2_async_nf_call_complete(notifier);
|
2017-09-24 20:54:31 -04:00
|
|
|
}
|
|
|
|
|
2018-10-04 17:10:46 -04:00
|
|
|
static int
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier);
|
2017-09-24 20:54:31 -04:00
|
|
|
|
2022-03-02 22:03:04 +00:00
|
|
|
static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n,
|
|
|
|
struct v4l2_subdev *sd)
|
|
|
|
{
|
|
|
|
#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
|
2024-04-15 19:34:26 +00:00
|
|
|
struct media_link *link;
|
2022-03-02 22:03:04 +00:00
|
|
|
|
|
|
|
if (sd->entity.function != MEDIA_ENT_F_LENS &&
|
|
|
|
sd->entity.function != MEDIA_ENT_F_FLASH)
|
|
|
|
return 0;
|
|
|
|
|
2024-05-20 10:28:20 +03:00
|
|
|
if (!n->sd) {
|
|
|
|
dev_warn(notifier_dev(n),
|
|
|
|
"not a sub-device notifier, not creating an ancillary link for %s!\n",
|
|
|
|
dev_name(sd->dev));
|
2024-05-08 10:51:49 +08:00
|
|
|
return 0;
|
2024-05-20 10:28:20 +03:00
|
|
|
}
|
2024-05-08 10:51:49 +08:00
|
|
|
|
2022-03-02 22:03:04 +00:00
|
|
|
link = media_create_ancillary_link(&n->sd->entity, &sd->entity);
|
|
|
|
|
|
|
|
return IS_ERR(link) ? PTR_ERR(link) : 0;
|
2024-04-15 19:34:26 +00:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
2022-03-02 22:03:04 +00:00
|
|
|
}
|
|
|
|
|
2017-09-04 12:44:39 -04:00
|
|
|
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
|
2017-09-24 20:48:08 -04:00
|
|
|
struct v4l2_device *v4l2_dev,
|
2017-09-04 12:44:39 -04:00
|
|
|
struct v4l2_subdev *sd,
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_async_notifier *subdev_notifier;
|
2023-05-19 13:44:03 +02:00
|
|
|
bool registered = false;
|
2013-01-08 07:06:31 -03:00
|
|
|
int ret;
|
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
if (list_empty(&sd->asc_list)) {
|
2024-03-13 14:03:19 +00:00
|
|
|
ret = __v4l2_device_register_subdev(v4l2_dev, sd, sd->owner);
|
2023-05-19 13:44:03 +02:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
registered = true;
|
|
|
|
}
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
ret = v4l2_async_nf_call_bound(notifier, sd, asc);
|
2023-05-19 13:44:03 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE)
|
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"failed binding %pfw (%d)\n",
|
|
|
|
asc->match.fwnode, ret);
|
2023-04-17 16:09:27 +02:00
|
|
|
goto err_unregister_subdev;
|
2023-05-19 13:44:03 +02:00
|
|
|
}
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
if (registered) {
|
|
|
|
/*
|
|
|
|
* Depending of the function of the entities involved, we may
|
|
|
|
* want to create links between them (for example between a
|
|
|
|
* sensor and its lens or between a sensor's source pad and the
|
|
|
|
* connected device's sink pad).
|
|
|
|
*/
|
|
|
|
ret = v4l2_async_create_ancillary_links(notifier, sd);
|
|
|
|
if (ret) {
|
|
|
|
if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE)
|
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"failed creating links for %pfw (%d)\n",
|
|
|
|
asc->match.fwnode, ret);
|
|
|
|
goto err_call_unbind;
|
|
|
|
}
|
|
|
|
}
|
[media] v4l2-async: failing functions shouldn't have side effects
v4l2-async had several functions doing some operations and then
not undoing the operations in a failure situation. For example,
v4l2_async_test_notify() moved a subdev into notifier's done list
even if registering the subdev (v4l2_device_register_subdev) failed.
If the subdev was allocated and v4l2_async_register_subdev() called
from the driver's probe() function, as usually, the probe()
function freed the allocated subdev and returned a failure.
Nevertheless, the subdev was still left into the notifier's done
list, causing an access to already freed memory when the notifier
was later unregistered.
A hand-edited call trace leaving freed subdevs into the notifier:
v4l2_async_register_notifier(notifier, asd)
cameradrv_probe
sd = devm_kzalloc()
v4l2_async_register_subdev(sd)
v4l2_async_test_notify(notifier, sd, asd)
list_move(sd, ¬ifier->done)
v4l2_device_register_subdev(notifier->v4l2_dev, sd)
cameradrv_registered(sd) -> fails
->v4l2_async_register_subdev returns failure
->cameradrv_probe returns failure
->devres frees the allocated sd
->sd was freed but it still remains in the notifier's list.
This patch fixes this and several other cases where a failing
function could leave nodes into a linked list while the caller
might free the node due to a failure.
Signed-off-by: Tuukka Toivonen <tuukka.toivonen@intel.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-01-27 08:32:56 -02:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
list_add(&asc->asc_subdev_entry, &sd->asc_list);
|
2023-04-18 14:00:54 +02:00
|
|
|
asc->sd = sd;
|
|
|
|
|
|
|
|
/* Move from the waiting list to notifier's done */
|
|
|
|
list_move(&asc->asc_entry, ¬ifier->done_list);
|
[media] v4l2-async: failing functions shouldn't have side effects
v4l2-async had several functions doing some operations and then
not undoing the operations in a failure situation. For example,
v4l2_async_test_notify() moved a subdev into notifier's done list
even if registering the subdev (v4l2_device_register_subdev) failed.
If the subdev was allocated and v4l2_async_register_subdev() called
from the driver's probe() function, as usually, the probe()
function freed the allocated subdev and returned a failure.
Nevertheless, the subdev was still left into the notifier's done
list, causing an access to already freed memory when the notifier
was later unregistered.
A hand-edited call trace leaving freed subdevs into the notifier:
v4l2_async_register_notifier(notifier, asd)
cameradrv_probe
sd = devm_kzalloc()
v4l2_async_register_subdev(sd)
v4l2_async_test_notify(notifier, sd, asd)
list_move(sd, ¬ifier->done)
v4l2_device_register_subdev(notifier->v4l2_dev, sd)
cameradrv_registered(sd) -> fails
->v4l2_async_register_subdev returns failure
->cameradrv_probe returns failure
->devres frees the allocated sd
->sd was freed but it still remains in the notifier's list.
This patch fixes this and several other cases where a failing
function could leave nodes into a linked list while the caller
might free the node due to a failure.
Signed-off-by: Tuukka Toivonen <tuukka.toivonen@intel.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-01-27 08:32:56 -02:00
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n",
|
|
|
|
dev_name(sd->dev), ret);
|
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
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;
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
return v4l2_async_nf_try_all_subdevs(subdev_notifier);
|
2023-04-17 16:09:27 +02:00
|
|
|
|
|
|
|
err_call_unbind:
|
|
|
|
v4l2_async_nf_call_unbind(notifier, sd, asc);
|
2023-05-19 13:44:03 +02:00
|
|
|
list_del(&asc->asc_subdev_entry);
|
2023-04-17 16:09:27 +02:00
|
|
|
|
|
|
|
err_unregister_subdev:
|
2023-05-19 13:44:03 +02:00
|
|
|
if (registered)
|
|
|
|
v4l2_device_unregister_subdev(sd);
|
2023-04-17 16:09:27 +02:00
|
|
|
|
|
|
|
return ret;
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
|
|
|
|
2017-09-24 20:48:08 -04:00
|
|
|
/* Test all async sub-devices in a notifier for a match. */
|
2018-10-04 17:10:46 -04:00
|
|
|
static int
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier)
|
2017-09-24 20:48:08 -04:00
|
|
|
{
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_device *v4l2_dev =
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_find_v4l2_dev(notifier);
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_subdev *sd;
|
|
|
|
|
|
|
|
if (!v4l2_dev)
|
|
|
|
return 0;
|
2017-09-24 20:48:08 -04:00
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier), "v4l2-async: trying all sub-devices\n");
|
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
again:
|
|
|
|
list_for_each_entry(sd, &subdev_list, async_list) {
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2017-09-24 20:48:08 -04:00
|
|
|
int ret;
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
asc = v4l2_async_find_match(notifier, sd);
|
|
|
|
if (!asc)
|
2017-09-24 20:48:08 -04:00
|
|
|
continue;
|
|
|
|
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_dbg(notifier_dev(notifier),
|
|
|
|
"v4l2-async: match found, subdev %s\n", sd->name);
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asc);
|
2017-09-24 20:48:08 -04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2017-09-24 20:54:31 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* v4l2_async_match_notify() may lead to registering a
|
|
|
|
* new notifier and thus changing the async subdevs
|
|
|
|
* list. In order to proceed safely from here, restart
|
|
|
|
* parsing the list from the beginning.
|
|
|
|
*/
|
|
|
|
goto again;
|
2017-09-24 20:48:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
static void v4l2_async_unbind_subdev_one(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_async_connection *asc)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2023-05-19 13:44:03 +02:00
|
|
|
list_move_tail(&asc->asc_entry, ¬ifier->waiting_list);
|
|
|
|
if (list_is_singular(&asc->asc_subdev_entry)) {
|
|
|
|
v4l2_async_nf_call_unbind(notifier, asc->sd, asc);
|
|
|
|
v4l2_device_unregister_subdev(asc->sd);
|
|
|
|
asc->sd = NULL;
|
|
|
|
}
|
|
|
|
list_del(&asc->asc_subdev_entry);
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
/* Unbind all sub-devices in the notifier tree. */
|
2018-10-04 17:10:46 -04:00
|
|
|
static void
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier)
|
2017-10-02 06:24:54 -04:00
|
|
|
{
|
2023-04-18 14:00:54 +02:00
|
|
|
struct v4l2_async_connection *asc, *asc_tmp;
|
2017-10-02 06:24:54 -04:00
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
list_for_each_entry_safe(asc, asc_tmp, ¬ifier->done_list,
|
|
|
|
asc_entry) {
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_async_notifier *subdev_notifier =
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_find_subdev_notifier(asc->sd);
|
2017-09-24 20:54:31 -04:00
|
|
|
|
|
|
|
if (subdev_notifier)
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_nf_unbind_all_subdevs(subdev_notifier);
|
2017-09-24 20:54:31 -04:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
v4l2_async_unbind_subdev_one(notifier, asc);
|
2017-10-02 06:24:54 -04:00
|
|
|
}
|
2017-09-24 20:54:31 -04:00
|
|
|
|
|
|
|
notifier->parent = NULL;
|
2017-10-02 06:24:54 -04:00
|
|
|
}
|
|
|
|
|
2018-09-29 15:54:05 -04:00
|
|
|
/* See if an async sub-device can be found in a notifier's lists. */
|
|
|
|
static bool
|
2023-02-19 13:24:41 +01:00
|
|
|
v4l2_async_nf_has_async_match_entry(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_async_match_desc *match)
|
2017-09-20 03:51:54 -04:00
|
|
|
{
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2017-09-20 03:51:54 -04:00
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry)
|
2023-02-16 14:54:53 +01:00
|
|
|
if (v4l2_async_match_equal(&asc->match, match))
|
2017-09-20 03:51:54 -04:00
|
|
|
return true;
|
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
list_for_each_entry(asc, ¬ifier->done_list, asc_entry)
|
2023-04-18 14:00:54 +02:00
|
|
|
if (v4l2_async_match_equal(&asc->match, match))
|
2017-09-20 03:51:54 -04:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2023-02-20 12:14:19 +01:00
|
|
|
* Find out whether an async sub-device was set up already or whether it exists
|
2023-04-18 11:54:32 +02:00
|
|
|
* in a given notifier.
|
2017-09-20 03:51:54 -04:00
|
|
|
*/
|
2018-09-29 15:54:05 -04:00
|
|
|
static bool
|
2023-02-19 13:24:41 +01:00
|
|
|
v4l2_async_nf_has_async_match(struct v4l2_async_notifier *notifier,
|
2023-04-18 11:54:32 +02:00
|
|
|
struct v4l2_async_match_desc *match)
|
2017-09-20 03:51:54 -04:00
|
|
|
{
|
2023-04-18 14:00:54 +02:00
|
|
|
struct list_head *heads[] = {
|
|
|
|
¬ifier->waiting_list,
|
|
|
|
¬ifier->done_list,
|
|
|
|
};
|
|
|
|
unsigned int i;
|
2017-09-20 03:51:54 -04:00
|
|
|
|
|
|
|
lockdep_assert_held(&list_lock);
|
|
|
|
|
2018-09-29 15:54:05 -04:00
|
|
|
/* Check that an asd is not being added more than once. */
|
2023-04-18 14:00:54 +02:00
|
|
|
for (i = 0; i < ARRAY_SIZE(heads); i++) {
|
|
|
|
struct v4l2_async_connection *asc;
|
|
|
|
|
|
|
|
list_for_each_entry(asc, heads[i], asc_entry) {
|
|
|
|
if (&asc->match == match)
|
|
|
|
continue;
|
|
|
|
if (v4l2_async_match_equal(&asc->match, match))
|
|
|
|
return true;
|
|
|
|
}
|
2017-09-20 03:51:54 -04:00
|
|
|
}
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
/* Check that an asc does not exist in other notifiers. */
|
2023-02-22 12:02:39 +01:00
|
|
|
list_for_each_entry(notifier, ¬ifier_list, notifier_entry)
|
2023-02-19 13:24:41 +01:00
|
|
|
if (v4l2_async_nf_has_async_match_entry(notifier, match))
|
2017-09-20 03:51:54 -04:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
static int v4l2_async_nf_match_valid(struct v4l2_async_notifier *notifier,
|
2023-04-18 11:54:32 +02:00
|
|
|
struct v4l2_async_match_desc *match)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2023-02-09 23:10:04 +01:00
|
|
|
struct device *dev = notifier_dev(notifier);
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
switch (match->type) {
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_I2C:
|
|
|
|
case V4L2_ASYNC_MATCH_TYPE_FWNODE:
|
2023-04-18 11:54:32 +02:00
|
|
|
if (v4l2_async_nf_has_async_match(notifier, match)) {
|
2023-02-19 13:24:41 +01:00
|
|
|
dev_dbg(dev, "v4l2-async: match descriptor already listed in a notifier\n");
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2023-02-09 23:10:04 +01:00
|
|
|
dev_err(dev, "v4l2-async: Invalid match type %u on %p\n",
|
2023-02-19 13:24:41 +01:00
|
|
|
match->type, match);
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-02-23 16:24:48 +01:00
|
|
|
void v4l2_async_nf_init(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_device *v4l2_dev)
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
{
|
2023-04-18 14:00:54 +02:00
|
|
|
INIT_LIST_HEAD(¬ifier->waiting_list);
|
|
|
|
INIT_LIST_HEAD(¬ifier->done_list);
|
2024-03-07 15:24:51 +01:00
|
|
|
INIT_LIST_HEAD(¬ifier->notifier_entry);
|
2023-02-23 16:24:48 +01:00
|
|
|
notifier->v4l2_dev = v4l2_dev;
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL(v4l2_async_nf_init);
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
|
2023-02-23 16:24:48 +01:00
|
|
|
void v4l2_async_subdev_nf_init(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_subdev *sd)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(¬ifier->waiting_list);
|
|
|
|
INIT_LIST_HEAD(¬ifier->done_list);
|
2024-03-07 15:24:51 +01:00
|
|
|
INIT_LIST_HEAD(¬ifier->notifier_entry);
|
2023-02-23 16:24:48 +01:00
|
|
|
notifier->sd = sd;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(v4l2_async_subdev_nf_init);
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
static int __v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
{
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2023-02-20 12:14:19 +01:00
|
|
|
int ret;
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2017-09-20 03:51:54 -04:00
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) {
|
2023-04-18 11:54:32 +02:00
|
|
|
ret = v4l2_async_nf_match_valid(notifier, &asc->match);
|
2018-09-29 15:54:19 -04:00
|
|
|
if (ret)
|
|
|
|
goto err_unlock;
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
ret = v4l2_async_nf_try_all_subdevs(notifier);
|
2017-09-20 03:51:54 -04:00
|
|
|
if (ret < 0)
|
2017-09-24 20:54:31 -04:00
|
|
|
goto err_unbind;
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
ret = v4l2_async_nf_try_complete(notifier);
|
2017-09-20 03:51:54 -04:00
|
|
|
if (ret < 0)
|
2017-09-24 20:54:31 -04:00
|
|
|
goto err_unbind;
|
2017-10-02 06:24:54 -04:00
|
|
|
|
[media] v4l2-async: failing functions shouldn't have side effects
v4l2-async had several functions doing some operations and then
not undoing the operations in a failure situation. For example,
v4l2_async_test_notify() moved a subdev into notifier's done list
even if registering the subdev (v4l2_device_register_subdev) failed.
If the subdev was allocated and v4l2_async_register_subdev() called
from the driver's probe() function, as usually, the probe()
function freed the allocated subdev and returned a failure.
Nevertheless, the subdev was still left into the notifier's done
list, causing an access to already freed memory when the notifier
was later unregistered.
A hand-edited call trace leaving freed subdevs into the notifier:
v4l2_async_register_notifier(notifier, asd)
cameradrv_probe
sd = devm_kzalloc()
v4l2_async_register_subdev(sd)
v4l2_async_test_notify(notifier, sd, asd)
list_move(sd, ¬ifier->done)
v4l2_device_register_subdev(notifier->v4l2_dev, sd)
cameradrv_registered(sd) -> fails
->v4l2_async_register_subdev returns failure
->cameradrv_probe returns failure
->devres frees the allocated sd
->sd was freed but it still remains in the notifier's list.
This patch fixes this and several other cases where a failing
function could leave nodes into a linked list while the caller
might free the node due to a failure.
Signed-off-by: Tuukka Toivonen <tuukka.toivonen@intel.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-01-27 08:32:56 -02:00
|
|
|
/* Keep also completed notifiers on the list */
|
2023-02-22 12:02:39 +01:00
|
|
|
list_add(¬ifier->notifier_entry, ¬ifier_list);
|
[media] v4l2-async: failing functions shouldn't have side effects
v4l2-async had several functions doing some operations and then
not undoing the operations in a failure situation. For example,
v4l2_async_test_notify() moved a subdev into notifier's done list
even if registering the subdev (v4l2_device_register_subdev) failed.
If the subdev was allocated and v4l2_async_register_subdev() called
from the driver's probe() function, as usually, the probe()
function freed the allocated subdev and returned a failure.
Nevertheless, the subdev was still left into the notifier's done
list, causing an access to already freed memory when the notifier
was later unregistered.
A hand-edited call trace leaving freed subdevs into the notifier:
v4l2_async_register_notifier(notifier, asd)
cameradrv_probe
sd = devm_kzalloc()
v4l2_async_register_subdev(sd)
v4l2_async_test_notify(notifier, sd, asd)
list_move(sd, ¬ifier->done)
v4l2_device_register_subdev(notifier->v4l2_dev, sd)
cameradrv_registered(sd) -> fails
->v4l2_async_register_subdev returns failure
->cameradrv_probe returns failure
->devres frees the allocated sd
->sd was freed but it still remains in the notifier's list.
This patch fixes this and several other cases where a failing
function could leave nodes into a linked list while the caller
might free the node due to a failure.
Signed-off-by: Tuukka Toivonen <tuukka.toivonen@intel.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-01-27 08:32:56 -02:00
|
|
|
|
2013-01-08 07:06:31 -03:00
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
|
|
|
|
return 0;
|
2017-10-02 06:24:54 -04:00
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
err_unbind:
|
|
|
|
/*
|
|
|
|
* On failure, unbind all sub-devices registered through this notifier.
|
|
|
|
*/
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_nf_unbind_all_subdevs(notifier);
|
2017-10-02 06:24:54 -04:00
|
|
|
|
2017-09-20 03:51:54 -04:00
|
|
|
err_unlock:
|
2017-10-02 06:24:54 -04:00
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
|
|
|
|
return ret;
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
2017-09-24 20:48:08 -04:00
|
|
|
|
2023-02-23 16:24:48 +01:00
|
|
|
int v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
|
2017-09-24 20:48:08 -04:00
|
|
|
{
|
2023-02-23 16:24:48 +01:00
|
|
|
if (WARN_ON(!notifier->v4l2_dev == !notifier->sd))
|
2017-09-24 20:48:08 -04:00
|
|
|
return -EINVAL;
|
|
|
|
|
2024-03-08 15:07:45 +02:00
|
|
|
return __v4l2_async_nf_register(notifier);
|
2017-09-24 20:48:08 -04:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL(v4l2_async_nf_register);
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2018-10-04 17:10:46 -04:00
|
|
|
static void
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2017-09-24 18:47:44 -04:00
|
|
|
if (!notifier || (!notifier->v4l2_dev && !notifier->sd))
|
2013-07-03 07:49:06 -03:00
|
|
|
return;
|
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_nf_unbind_all_subdevs(notifier);
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2024-03-08 15:06:13 +02:00
|
|
|
list_del_init(¬ifier->notifier_entry);
|
2017-09-24 18:47:44 -04:00
|
|
|
}
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
|
2017-09-24 18:47:44 -04:00
|
|
|
{
|
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_unregister(notifier);
|
2017-09-24 20:54:31 -04:00
|
|
|
|
|
|
|
mutex_unlock(&list_lock);
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL(v4l2_async_nf_unregister);
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
static void __v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier)
|
2017-08-17 11:28:21 -04:00
|
|
|
{
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc, *tmp;
|
2017-08-17 11:28:21 -04:00
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
if (!notifier || !notifier->waiting_list.next)
|
2017-08-17 11:28:21 -04:00
|
|
|
return;
|
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
WARN_ON(!list_empty(¬ifier->done_list));
|
|
|
|
|
|
|
|
list_for_each_entry_safe(asc, tmp, ¬ifier->waiting_list, asc_entry) {
|
2023-02-16 14:54:53 +01:00
|
|
|
list_del(&asc->asc_entry);
|
|
|
|
v4l2_async_nf_call_destroy(notifier, asc);
|
2023-05-19 13:44:03 +02:00
|
|
|
|
|
|
|
if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE)
|
|
|
|
fwnode_handle_put(asc->match.fwnode);
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
kfree(asc);
|
2017-08-17 11:28:21 -04:00
|
|
|
}
|
2023-02-23 16:24:48 +01:00
|
|
|
|
|
|
|
notifier->sd = NULL;
|
|
|
|
notifier->v4l2_dev = NULL;
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
}
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier)
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
{
|
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_cleanup(notifier);
|
2017-08-17 11:28:21 -04:00
|
|
|
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
mutex_unlock(&list_lock);
|
2017-08-17 11:28:21 -04:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL_GPL(v4l2_async_nf_cleanup);
|
2017-08-17 11:28:21 -04:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
static void __v4l2_async_nf_add_connection(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_async_connection *asc)
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
{
|
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2023-04-18 14:00:54 +02:00
|
|
|
list_add_tail(&asc->asc_entry, ¬ifier->waiting_list);
|
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>
2018-09-29 15:54:06 -04:00
|
|
|
|
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
}
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_add_fwnode(struct v4l2_async_notifier *notifier,
|
|
|
|
struct fwnode_handle *fwnode,
|
2023-02-16 14:54:53 +01:00
|
|
|
unsigned int asc_struct_size)
|
2018-09-29 15:54:07 -04:00
|
|
|
{
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
asc = kzalloc(asc_struct_size, GFP_KERNEL);
|
|
|
|
if (!asc)
|
2018-09-29 15:54:07 -04:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
asc->notifier = notifier;
|
2023-02-16 14:54:53 +01:00
|
|
|
asc->match.type = V4L2_ASYNC_MATCH_TYPE_FWNODE;
|
|
|
|
asc->match.fwnode = fwnode_handle_get(fwnode);
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
__v4l2_async_nf_add_connection(notifier, asc);
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
return asc;
|
2018-09-29 15:54:07 -04:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_fwnode);
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_add_fwnode_remote(struct v4l2_async_notifier *notif,
|
|
|
|
struct fwnode_handle *endpoint,
|
2023-02-16 14:54:53 +01:00
|
|
|
unsigned int asc_struct_size)
|
2019-02-28 08:25:28 -05:00
|
|
|
{
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2019-02-28 08:25:28 -05:00
|
|
|
struct fwnode_handle *remote;
|
|
|
|
|
2022-03-21 14:51:34 +00:00
|
|
|
remote = fwnode_graph_get_remote_endpoint(endpoint);
|
2019-02-28 08:25:28 -05:00
|
|
|
if (!remote)
|
2021-01-18 02:52:45 +01:00
|
|
|
return ERR_PTR(-ENOTCONN);
|
2019-02-28 08:25:28 -05:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
asc = __v4l2_async_nf_add_fwnode(notif, remote, asc_struct_size);
|
2021-01-18 02:52:45 +01:00
|
|
|
/*
|
2021-03-05 18:13:12 +01:00
|
|
|
* Calling __v4l2_async_nf_add_fwnode grabs a refcount,
|
2021-01-18 02:52:45 +01:00
|
|
|
* so drop the one we got in fwnode_graph_get_remote_port_parent.
|
|
|
|
*/
|
|
|
|
fwnode_handle_put(remote);
|
2023-02-16 14:54:53 +01:00
|
|
|
return asc;
|
2019-02-28 08:25:28 -05:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_fwnode_remote);
|
2019-02-28 08:25:28 -05:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_add_i2c(struct v4l2_async_notifier *notifier, int adapter_id,
|
2023-02-16 14:54:53 +01:00
|
|
|
unsigned short address, unsigned int asc_struct_size)
|
2018-09-29 15:54:07 -04:00
|
|
|
{
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
asc = kzalloc(asc_struct_size, GFP_KERNEL);
|
|
|
|
if (!asc)
|
2018-09-29 15:54:07 -04:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
asc->notifier = notifier;
|
2023-02-16 14:54:53 +01:00
|
|
|
asc->match.type = V4L2_ASYNC_MATCH_TYPE_I2C;
|
|
|
|
asc->match.i2c.adapter_id = adapter_id;
|
|
|
|
asc->match.i2c.address = address;
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
__v4l2_async_nf_add_connection(notifier, asc);
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
return asc;
|
2018-09-29 15:54:07 -04:00
|
|
|
}
|
2021-03-05 18:13:12 +01:00
|
|
|
EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_i2c);
|
2018-09-29 15:54:07 -04:00
|
|
|
|
2023-04-28 15:58:42 +02:00
|
|
|
int v4l2_async_subdev_endpoint_add(struct v4l2_subdev *sd,
|
|
|
|
struct fwnode_handle *fwnode)
|
|
|
|
{
|
|
|
|
struct v4l2_async_subdev_endpoint *ase;
|
|
|
|
|
|
|
|
ase = kmalloc(sizeof(*ase), GFP_KERNEL);
|
|
|
|
if (!ase)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ase->endpoint = fwnode;
|
|
|
|
list_add(&ase->async_subdev_endpoint_entry,
|
|
|
|
&sd->async_subdev_endpoint_list);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(v4l2_async_subdev_endpoint_add);
|
|
|
|
|
2023-05-15 11:06:50 +02:00
|
|
|
struct v4l2_async_connection *
|
|
|
|
v4l2_async_connection_unique(struct v4l2_subdev *sd)
|
|
|
|
{
|
2023-05-19 13:44:03 +02:00
|
|
|
if (!list_is_singular(&sd->asc_list))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return list_first_entry(&sd->asc_list,
|
|
|
|
struct v4l2_async_connection, asc_subdev_entry);
|
2023-05-15 11:06:50 +02:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(v4l2_async_connection_unique);
|
|
|
|
|
2024-03-13 13:57:50 +00:00
|
|
|
int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
|
2013-01-08 07:06:31 -03:00
|
|
|
{
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_async_notifier *subdev_notifier;
|
2013-01-08 07:06:31 -03:00
|
|
|
struct v4l2_async_notifier *notifier;
|
2023-05-19 13:44:03 +02:00
|
|
|
struct v4l2_async_connection *asc;
|
2017-10-02 06:24:54 -04:00
|
|
|
int ret;
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
INIT_LIST_HEAD(&sd->asc_list);
|
|
|
|
|
2015-06-11 12:18:01 -07:00
|
|
|
/*
|
2023-02-19 00:21:02 +01:00
|
|
|
* No reference taken. The reference is held by the device (struct
|
|
|
|
* v4l2_subdev.dev), and async sub-device does not exist independently
|
|
|
|
* of the device at any point of time.
|
|
|
|
*
|
|
|
|
* The async sub-device shall always be registered for its device node,
|
|
|
|
* not the endpoint node.
|
2015-06-11 12:18:01 -07:00
|
|
|
*/
|
2023-02-19 00:21:02 +01:00
|
|
|
if (!sd->fwnode && sd->dev) {
|
2016-08-26 20:17:25 -03:00
|
|
|
sd->fwnode = dev_fwnode(sd->dev);
|
2023-02-19 00:21:02 +01:00
|
|
|
} else if (fwnode_graph_is_endpoint(sd->fwnode)) {
|
|
|
|
dev_warn(sd->dev, "sub-device fwnode is an endpoint!\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-06-11 12:18:01 -07:00
|
|
|
|
2024-03-13 13:57:50 +00:00
|
|
|
sd->owner = module;
|
|
|
|
|
2013-01-08 07:06:31 -03:00
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2023-02-22 12:02:39 +01:00
|
|
|
list_for_each_entry(notifier, ¬ifier_list, notifier_entry) {
|
2017-09-24 20:54:31 -04:00
|
|
|
struct v4l2_device *v4l2_dev =
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_find_v4l2_dev(notifier);
|
2017-10-02 06:24:54 -04:00
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
if (!v4l2_dev)
|
|
|
|
continue;
|
|
|
|
|
2023-07-29 13:52:14 +02:00
|
|
|
while ((asc = v4l2_async_find_match(notifier, sd))) {
|
|
|
|
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd,
|
|
|
|
asc);
|
|
|
|
if (ret)
|
|
|
|
goto err_unbind;
|
|
|
|
|
|
|
|
ret = v4l2_async_nf_try_complete(notifier);
|
|
|
|
if (ret)
|
|
|
|
goto err_unbind;
|
|
|
|
}
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* None matched, wait for hot-plugging */
|
2013-07-22 08:01:33 -03:00
|
|
|
list_add(&sd->async_list, &subdev_list);
|
2013-01-08 07:06:31 -03:00
|
|
|
|
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
|
|
|
|
return 0;
|
2017-10-02 06:24:54 -04:00
|
|
|
|
2017-09-24 20:54:31 -04:00
|
|
|
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)
|
2023-04-18 14:00:54 +02:00
|
|
|
v4l2_async_nf_unbind_all_subdevs(subdev_notifier);
|
2017-09-24 20:54:31 -04:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
if (asc)
|
|
|
|
v4l2_async_unbind_subdev_one(notifier, asc);
|
2017-10-02 06:24:54 -04:00
|
|
|
|
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
|
2024-03-13 13:57:50 +00:00
|
|
|
sd->owner = NULL;
|
|
|
|
|
2017-10-02 06:24:54 -04:00
|
|
|
return ret;
|
2013-01-08 07:06:31 -03:00
|
|
|
}
|
2024-03-13 13:57:50 +00:00
|
|
|
EXPORT_SYMBOL(__v4l2_async_register_subdev);
|
2013-01-08 07:06:31 -03:00
|
|
|
|
|
|
|
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
|
|
|
|
{
|
2023-05-19 13:44:03 +02:00
|
|
|
struct v4l2_async_connection *asc, *asc_tmp;
|
|
|
|
|
2021-01-07 23:54:58 +01:00
|
|
|
if (!sd->async_list.next)
|
|
|
|
return;
|
|
|
|
|
2023-01-27 21:37:25 +01:00
|
|
|
v4l2_subdev_put_privacy_led(sd);
|
|
|
|
|
2013-01-08 07:06:31 -03:00
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
__v4l2_async_nf_unregister(sd->subdev_notifier);
|
|
|
|
__v4l2_async_nf_cleanup(sd->subdev_notifier);
|
2017-09-24 18:47:44 -04:00
|
|
|
kfree(sd->subdev_notifier);
|
|
|
|
sd->subdev_notifier = NULL;
|
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
if (sd->asc_list.next) {
|
|
|
|
list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list,
|
|
|
|
asc_subdev_entry) {
|
|
|
|
v4l2_async_unbind_subdev_one(asc->notifier, asc);
|
|
|
|
}
|
2017-10-03 02:26:32 -04:00
|
|
|
}
|
2013-01-08 07:06:31 -03:00
|
|
|
|
2023-05-19 13:44:03 +02:00
|
|
|
list_del(&sd->async_list);
|
|
|
|
sd->async_list.next = NULL;
|
2017-10-02 16:16:52 -04:00
|
|
|
|
2013-01-08 07:06:31 -03:00
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(v4l2_async_unregister_subdev);
|
2021-01-08 18:17:28 +01:00
|
|
|
|
2023-02-19 13:24:41 +01:00
|
|
|
static void print_waiting_match(struct seq_file *s,
|
|
|
|
struct v4l2_async_match_desc *match)
|
2021-01-08 18:17:28 +01:00
|
|
|
{
|
2023-02-19 13:24:41 +01:00
|
|
|
switch (match->type) {
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_I2C:
|
2023-02-19 13:24:41 +01:00
|
|
|
seq_printf(s, " [i2c] dev=%d-%04x\n", match->i2c.adapter_id,
|
|
|
|
match->i2c.address);
|
2021-01-08 18:17:28 +01:00
|
|
|
break;
|
2023-05-05 09:09:10 +02:00
|
|
|
case V4L2_ASYNC_MATCH_TYPE_FWNODE: {
|
2023-02-19 13:24:41 +01:00
|
|
|
struct fwnode_handle *devnode, *fwnode = match->fwnode;
|
2021-01-08 18:17:28 +01:00
|
|
|
|
|
|
|
devnode = fwnode_graph_is_endpoint(fwnode) ?
|
|
|
|
fwnode_graph_get_port_parent(fwnode) :
|
|
|
|
fwnode_handle_get(fwnode);
|
|
|
|
|
|
|
|
seq_printf(s, " [fwnode] dev=%s, node=%pfw\n",
|
|
|
|
devnode->dev ? dev_name(devnode->dev) : "nil",
|
|
|
|
fwnode);
|
|
|
|
|
|
|
|
fwnode_handle_put(devnode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_name(struct v4l2_async_notifier *notifier)
|
2021-01-08 18:17:28 +01:00
|
|
|
{
|
|
|
|
if (notifier->v4l2_dev)
|
|
|
|
return notifier->v4l2_dev->name;
|
|
|
|
else if (notifier->sd)
|
|
|
|
return notifier->sd->name;
|
|
|
|
else
|
|
|
|
return "nil";
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pending_subdevs_show(struct seq_file *s, void *data)
|
|
|
|
{
|
|
|
|
struct v4l2_async_notifier *notif;
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc;
|
2021-01-08 18:17:28 +01:00
|
|
|
|
|
|
|
mutex_lock(&list_lock);
|
|
|
|
|
2023-02-22 12:02:39 +01:00
|
|
|
list_for_each_entry(notif, ¬ifier_list, notifier_entry) {
|
2021-03-05 18:13:12 +01:00
|
|
|
seq_printf(s, "%s:\n", v4l2_async_nf_name(notif));
|
2023-04-18 14:00:54 +02:00
|
|
|
list_for_each_entry(asc, ¬if->waiting_list, asc_entry)
|
2023-02-16 14:54:53 +01:00
|
|
|
print_waiting_match(s, &asc->match);
|
2021-01-08 18:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&list_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(pending_subdevs);
|
|
|
|
|
2021-03-05 18:38:39 +01:00
|
|
|
static struct dentry *v4l2_async_debugfs_dir;
|
|
|
|
|
|
|
|
static int __init v4l2_async_init(void)
|
2021-01-08 18:17:28 +01:00
|
|
|
{
|
2021-03-05 18:38:39 +01:00
|
|
|
v4l2_async_debugfs_dir = debugfs_create_dir("v4l2-async", NULL);
|
|
|
|
debugfs_create_file("pending_async_subdevices", 0444,
|
|
|
|
v4l2_async_debugfs_dir, NULL,
|
2021-01-08 18:17:28 +01:00
|
|
|
&pending_subdevs_fops);
|
2021-03-05 18:38:39 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit v4l2_async_exit(void)
|
|
|
|
{
|
|
|
|
debugfs_remove_recursive(v4l2_async_debugfs_dir);
|
2021-01-08 18:17:28 +01:00
|
|
|
}
|
2021-03-05 18:38:39 +01:00
|
|
|
|
|
|
|
subsys_initcall(v4l2_async_init);
|
|
|
|
module_exit(v4l2_async_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
|
|
|
|
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
|
|
|
|
MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
|
2024-06-10 11:33:38 -07:00
|
|
|
MODULE_DESCRIPTION("V4L2 asynchronous subdevice registration API");
|
2021-03-05 18:38:39 +01:00
|
|
|
MODULE_LICENSE("GPL");
|