From: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
To: linux-sh@vger.kernel.org
Subject: Re: [PATCH 2/2] media: V4L2: support asynchronous subdevice registration
Date: Sun, 28 Oct 2012 15:30:01 +0000 [thread overview]
Message-ID: <508D4F79.2000204@gmail.com> (raw)
In-Reply-To: <Pine.LNX.4.64.1210200007580.28993@axis700.grange>
Hi,
On 10/24/2012 03:54 PM, Guennadi Liakhovetski wrote:
> On Sat, 20 Oct 2012, Guennadi Liakhovetski wrote:
>
>> Currently bridge device drivers register devices for all subdevices
>> synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
>> is attached to a video bridge device, the bridge driver will create an I2C
>> device and wait for the respective I2C driver to probe. This makes linking
>> of devices straight forward, but this approach cannot be used with
>> intrinsically asynchronous and unordered device registration systems like
>> the Flattened Device Tree. To support such systems this patch adds an
>> asynchronous subdevice registration framework to V4L2. To use it respective
>> (e.g. I2C) subdevice drivers must request deferred probing as long as their
>> bridge driver hasn't probed. The bridge driver during its probing submits a
>> an arbitrary number of subdevice descriptor groups to the framework to
>> manage. After that it can add callbacks to each of those groups to be
>> called at various stages during subdevice probing, e.g. after completion.
>> Then the bridge driver can request single groups to be probed, finish its
>> own probing and continue its video subsystem configuration from its
>> callbacks.
>>
>> Signed-off-by: Guennadi Liakhovetski<g.liakhovetski@gmx.de>
>
> Sorry, I did indeed forget to include you on CC for this patch as promised
> in https://lkml.org/lkml/2012/10/17/306. In the patch below just look for
> the only occurrance of the device_release_driver() / device_attach(). In
> your mail you said, that only bus drivers should do this. In fact this is
> indeed what is happening here. A device is attached to two busses:
> (typically) I2C and "media." And the code below is called when the device
> is detached from the media bus.
>
> Thanks
> Guennadi
>
>> ---
>>
>> One more thing to note about this patch. Subdevice drivers, supporting
>> asynchronous probing, and using this framework, need a unified way to
>> detect, whether their probing should succeed or they should request
>> deferred probing. I implement this using device platform data. This means,
>> that all subdevice drivers, wishing to use this API will have to use the
>> same platform data struct. I don't think this is a major inconvenience,
>> but if we decide against this, we'll have to add a V4L2 function to verify
>> "are you ready for me or not." The latter would be inconvenient, because
>> then we would have to look through all registered subdevice descriptor
>> groups for this specific subdevice.
>>
>> drivers/media/v4l2-core/Makefile | 3 +-
>> drivers/media/v4l2-core/v4l2-async.c | 249 +++++++++++++++++++++++++++++++++
>> drivers/media/v4l2-core/v4l2-device.c | 2 +
>> include/media/v4l2-async.h | 88 ++++++++++++
>> include/media/v4l2-device.h | 6 +
>> include/media/v4l2-subdev.h | 16 ++
>> 6 files changed, 363 insertions(+), 1 deletions(-)
>> create mode 100644 drivers/media/v4l2-core/v4l2-async.c
>> create mode 100644 include/media/v4l2-async.h
>>
>> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
>> index cb5fede..074e01c 100644
>> --- a/drivers/media/v4l2-core/Makefile
>> +++ b/drivers/media/v4l2-core/Makefile
>> @@ -5,7 +5,8 @@
>> tuner-objs := tuner-core.o
>>
>> videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
>> - v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o
>> + v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o \
>> + v4l2-async.o
>> ifeq ($(CONFIG_COMPAT),y)
>> videodev-objs += v4l2-compat-ioctl32.o
>> endif
>> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
>> new file mode 100644
>> index 0000000..871f116
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-async.c
>> @@ -0,0 +1,249 @@
>> +/*
>> + * V4L2 asynchronous subdevice registration API
>> + *
>> + * Copyright (C) 2012, Guennadi Liakhovetski<g.liakhovetski@gmx.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include<linux/device.h>
>> +#include<linux/err.h>
>> +#include<linux/i2c.h>
>> +#include<linux/list.h>
>> +#include<linux/module.h>
>> +#include<linux/mutex.h>
>> +#include<linux/notifier.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/slab.h>
>> +#include<linux/types.h>
>> +
>> +#include<media/v4l2-async.h>
>> +#include<media/v4l2-device.h>
>> +#include<media/v4l2-subdev.h>
>> +
>> +static bool match_i2c(struct device *dev, struct v4l2_async_hw_device *hw_dev)
>> +{
>> + struct i2c_client *client = to_i2c_client(dev);
>> + return hw_dev->bus_type = V4L2_ASYNC_BUS_I2C&&
>> + hw_dev->match.i2c.adapter_id = client->adapter->nr&&
>> + hw_dev->match.i2c.address = client->addr;
>> +}
>> +
>> +static bool match_platform(struct device *dev, struct v4l2_async_hw_device *hw_dev)
>> +{
>> + return hw_dev->bus_type = V4L2_ASYNC_BUS_PLATFORM&&
>> + !strcmp(hw_dev->match.platform.name, dev_name(dev));
>> +}
>> +
>> +/*
>> + * I think, notifiers on different busses can run concurrently, so, we have to
>> + * protect common data, e.g. sub-device lists.
>> + */
>> +static int async_notifier_cb(struct v4l2_async_group *group,
>> + unsigned long action, struct device *dev,
>> + bool (*match)(struct device *, struct v4l2_async_hw_device *))
>> +{
>> + struct v4l2_device *v4l2_dev = group->v4l2_dev;
>> + struct v4l2_async_subdev *asd;
>> + bool done;
>> + int ret;
>> +
>> + if (action != BUS_NOTIFY_BOUND_DRIVER&&
>> + action != BUS_NOTIFY_BIND_DRIVER)
>> + return NOTIFY_DONE;
>> +
>> + /* Asynchronous: have to lock */
>> + mutex_lock(&v4l2_dev->group_lock);
>> +
>> + list_for_each_entry(asd,&group->group, list) {
>> + if (match(dev,&asd->hw))
>> + break;
>> + }
>> +
>> + if (&asd->list =&group->group) {
>> + /* Not our device */
>> + mutex_unlock(&v4l2_dev->group_lock);
>> + return NOTIFY_DONE;
>> + }
>> +
>> + asd->dev = dev;
>> +
>> + if (action = BUS_NOTIFY_BIND_DRIVER) {
>> + /*
>> + * Provide platform data to the driver: it can complete probing
>> + * now.
>> + */
>> + dev->platform_data =&asd->sdpd;
>> + mutex_unlock(&v4l2_dev->group_lock);
>> + if (group->bind_cb)
>> + group->bind_cb(group, asd);
>> + return NOTIFY_OK;
>> + }
>> +
>> + /* BUS_NOTIFY_BOUND_DRIVER */
>> + if (asd->hw.bus_type = V4L2_ASYNC_BUS_I2C)
>> + asd->sdpd.subdev = i2c_get_clientdata(to_i2c_client(dev));
>> + /*
>> + * Non-I2C subdevice drivers should take care to assign their subdevice
>> + * pointers
>> + */
>> + ret = v4l2_device_register_subdev(v4l2_dev,
>> + asd->sdpd.subdev);
>> + if (ret< 0) {
>> + mutex_unlock(&v4l2_dev->group_lock);
>> + /* FIXME: error, clean up world? */
>> + dev_err(dev, "Failed registering a subdev: %d\n", ret);
>> + return NOTIFY_OK;
>> + }
>> + list_move(&asd->list,&group->done);
>> +
>> + /* Client probed& all subdev drivers collected */
>> + done = list_empty(&group->group);
>> +
>> + mutex_unlock(&v4l2_dev->group_lock);
>> +
>> + if (group->bound_cb)
>> + group->bound_cb(group, asd);
>> +
>> + if (done&& group->complete_cb)
>> + group->complete_cb(group);
>> +
>> + return NOTIFY_OK;
>> +}
>> +
>> +static int platform_cb(struct notifier_block *nb,
>> + unsigned long action, void *data)
>> +{
>> + struct device *dev = data;
>> + struct v4l2_async_group *group = container_of(nb, struct v4l2_async_group,
>> + platform_notifier);
>> +
>> + return async_notifier_cb(group, action, dev, match_platform);
>> +}
>> +
>> +static int i2c_cb(struct notifier_block *nb,
>> + unsigned long action, void *data)
>> +{
>> + struct device *dev = data;
>> + struct v4l2_async_group *group = container_of(nb, struct v4l2_async_group,
>> + i2c_notifier);
>> +
>> + return async_notifier_cb(group, action, dev, match_i2c);
>> +}
>> +
>> +/*
>> + * Typically this function will be called during bridge driver probing. It
>> + * installs bus notifiers to handle asynchronously probing subdevice drivers.
>> + * Once the bridge driver probing completes, subdevice drivers, waiting in
>> + * EPROBE_DEFER state are re-probed, at which point they get their platform
>> + * data, which allows them to complete probing.
>> + */
>> +int v4l2_async_group_probe(struct v4l2_async_group *group)
>> +{
>> + struct v4l2_async_subdev *asd, *tmp;
>> + bool i2c_used = false, platform_used = false;
>> + int ret;
>> +
>> + /* This group is inactive so far - no notifiers yet */
>> + list_for_each_entry_safe(asd, tmp,&group->group, list) {
>> + if (asd->sdpd.subdev) {
>> + /* Simulate a BIND event */
>> + if (group->bind_cb)
>> + group->bind_cb(group, asd);
>> +
Still we can't be sure at this moment asd->sdpd.subdev's driver is
valid and not unloaded, can we ?
In the case when a sub-device driver is probed after the host driver
(a caller of this function) I assume doing
asd->sdpd.subdev = i2c_get_clientdata(to_i2c_client(dev));
...
ret = v4l2_device_register_subdev(v4l2_dev, asd->sdpd.subdev);
is safe, because it is done in the i2c bus notifier callback itself,
i.e. under device_lock(dev).
But for these already probed sub-devices, how do we prevent races from
subdev module unloading ? By not setting CONFIG_MODULE_UNLOAD?... ;)
>> + /* Already probed, don't wait for it */
>> + ret = v4l2_device_register_subdev(group->v4l2_dev,
>> + asd->sdpd.subdev);
>> +
>> + if (ret< 0)
>> + return ret;
>> +
>> + list_move(&asd->list,&group->done);
>> + continue;
>> + }
>> +
>> + switch (asd->hw.bus_type) {
>> + case V4L2_ASYNC_BUS_PLATFORM:
>> + platform_used = true;
>> + break;
>> + case V4L2_ASYNC_BUS_I2C:
>> + i2c_used = true;
>> + }
>> + }
>> +
>> + if (list_empty(&group->group)) {
>> + if (group->complete_cb)
>> + group->complete_cb(group);
>> + return 0;
>> + }
>> +
>> + /* TODO: so far bus_register_notifier() never fails */
>> + if (platform_used) {
>> + group->platform_notifier.notifier_call = platform_cb;
>> + bus_register_notifier(&platform_bus_type,
>> + &group->platform_notifier);
>> + }
>> +
>> + if (i2c_used) {
>> + group->i2c_notifier.notifier_call = i2c_cb;
>> + bus_register_notifier(&i2c_bus_type,
>> + &group->i2c_notifier);
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(v4l2_async_group_probe);
>> +
>> +int v4l2_async_group_init(struct v4l2_device *v4l2_dev,
>> + struct v4l2_async_group *group,
>> + struct v4l2_async_subdev *asd, int cnt)
>> +{
>> + int i;
>> +
>> + if (!group)
>> + return -EINVAL;
>> +
>> + INIT_LIST_HEAD(&group->group);
>> + INIT_LIST_HEAD(&group->done);
>> + group->v4l2_dev = v4l2_dev;
>> +
>> + for (i = 0; i< cnt; i++)
>> + list_add_tail(&asd[i].list,&group->group);
>> +
>> + /* Protect the global V4L2 device group list */
>> + mutex_lock(&v4l2_dev->group_lock);
>> + list_add_tail(&group->list,&v4l2_dev->group_head);
>> + mutex_unlock(&v4l2_dev->group_lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(v4l2_async_group_init);
>> +
>> +void v4l2_async_group_release(struct v4l2_async_group *group)
>> +{
>> + struct v4l2_async_subdev *asd, *tmp;
>> +
>> + /* Also no problem, if notifiers haven't been registered */
>> + bus_unregister_notifier(&platform_bus_type,
>> + &group->platform_notifier);
>> + bus_unregister_notifier(&i2c_bus_type,
>> + &group->i2c_notifier);
>> +
>> + mutex_lock(&group->v4l2_dev->group_lock);
>> + list_del(&group->list);
>> + mutex_unlock(&group->v4l2_dev->group_lock);
>> +
>> + list_for_each_entry_safe(asd, tmp,&group->done, list) {
>> + v4l2_device_unregister_subdev(asd->sdpd.subdev);
>> + /* If we handled USB devices, we'd have to lock the parent too */
>> + device_release_driver(asd->dev);
>> + asd->dev->platform_data = NULL;
>> + if (device_attach(asd->dev)<= 0)
>> + dev_dbg(asd->dev, "Failed to re-probe to %s\n", asd->dev->driver ?
>> + asd->dev->driver->name : "(none)");
>> + list_del(&asd->list);
>> + }
>> +}
>> +EXPORT_SYMBOL(v4l2_async_group_release);
>> diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
>> index 513969f..52faf2f 100644
>> --- a/drivers/media/v4l2-core/v4l2-device.c
>> +++ b/drivers/media/v4l2-core/v4l2-device.c
>> @@ -40,6 +40,8 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
>> mutex_init(&v4l2_dev->ioctl_lock);
>> v4l2_prio_init(&v4l2_dev->prio);
>> kref_init(&v4l2_dev->ref);
>> + INIT_LIST_HEAD(&v4l2_dev->group_head);
>> + mutex_init(&v4l2_dev->group_lock);
>> get_device(dev);
>> v4l2_dev->dev = dev;
>> if (dev = NULL) {
>> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
>> new file mode 100644
>> index 0000000..8f7bc06
>> --- /dev/null
>> +++ b/include/media/v4l2-async.h
>> @@ -0,0 +1,88 @@
>> +/*
>> + * V4L2 asynchronous subdevice registration API
>> + *
>> + * Copyright (C) 2012, Guennadi Liakhovetski<g.liakhovetski@gmx.de>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef V4L2_ASYNC_H
>> +#define V4L2_ASYNC_H
>> +
>> +#include<linux/list.h>
>> +#include<linux/mutex.h>
>> +#include<linux/notifier.h>
>> +
>> +#include<media/v4l2-subdev.h>
>> +
>> +struct device;
>> +struct v4l2_device;
>> +
>> +enum v4l2_async_bus_type {
>> + V4L2_ASYNC_BUS_PLATFORM,
>> + V4L2_ASYNC_BUS_I2C,
>> +};
>> +
>> +struct v4l2_async_hw_device {
>> + enum v4l2_async_bus_type bus_type;
>> + union {
>> + struct {
>> + const char *name;
>> + } platform;
>> + struct {
>> + int adapter_id;
>> + unsigned short address;
>> + } i2c;
>> + } match;
>> +};
>> +
>> +/**
>> + * struct v4l2_async_subdev - device descriptor
>> + * @hw: this device descriptor
>> + * @list: member in the group
>> + * @dev: corresponding hardware device (I2C, platform,...)
>> + * @sdpd: embedded subdevice platform data
>> + * @role: this subdevice role in the video pipeline
>> + */
>> +struct v4l2_async_subdev {
>> + struct v4l2_async_hw_device hw;
>> + struct list_head list;
>> + struct device *dev;
>> + struct v4l2_subdev_platform_data sdpd;
>> + enum v4l2_subdev_role role;
>> +};
>> +
>> +/**
>> + * struct v4l2_async_group - list of device descriptors
>> + * @list: member in the v4l2 group list
>> + * @group: head of device list
>> + * @done: head of probed device list
>> + * @platform_notifier: platform bus notifier block
>> + * @i2c_notifier: I2C bus notifier block
>> + * @v4l2_dev: link to the respective struct v4l2_device
>> + * @bind: callback, called upon BUS_NOTIFY_BIND_DRIVER for each
>> + * subdevice
>> + * @complete: callback, called once after all subdevices in the group
>> + * have been bound
>> + */
>> +struct v4l2_async_group {
>> + struct list_head list;
>> + struct list_head group;
>> + struct list_head done;
>> + struct notifier_block platform_notifier;
>> + struct notifier_block i2c_notifier;
>> + struct v4l2_device *v4l2_dev;
>> + int (*bind)(struct v4l2_async_group *group,
>> + struct v4l2_async_subdev *asd);
>> + int (*complete)(struct v4l2_async_group *group);
>> +};
>> +
>> +int v4l2_async_group_init(struct v4l2_device *v4l2_dev,
>> + struct v4l2_async_group *group,
>> + struct v4l2_async_subdev *asd, int cnt);
>> +int v4l2_async_group_probe(struct v4l2_async_group *group);
>> +void v4l2_async_group_release(struct v4l2_async_group *group);
>> +
>> +#endif
>> diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
>> index d61febf..84b18c9 100644
>> --- a/include/media/v4l2-device.h
>> +++ b/include/media/v4l2-device.h
>> @@ -21,6 +21,9 @@
>> #ifndef _V4L2_DEVICE_H
>> #define _V4L2_DEVICE_H
>>
>> +#include<linux/list.h>
>> +#include<linux/mutex.h>
>> +
>> #include<media/media-device.h>
>> #include<media/v4l2-subdev.h>
>> #include<media/v4l2-dev.h>
>> @@ -60,6 +63,9 @@ struct v4l2_device {
>> struct v4l2_prio_state prio;
>> /* BKL replacement mutex. Temporary solution only. */
>> struct mutex ioctl_lock;
>> + /* Subdevice group handling */
>> + struct mutex group_lock;
>> + struct list_head group_head;
>> /* Keep track of the references to this struct. */
>> struct kref ref;
>> /* Release function that is called when the ref count goes to 0. */
>> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
>> index 2ecd737..1756c6c 100644
>> --- a/include/media/v4l2-subdev.h
>> +++ b/include/media/v4l2-subdev.h
>> @@ -574,6 +574,22 @@ struct v4l2_subdev_fh {
>> #endif
>> };
>>
>> +enum v4l2_subdev_role {
>> + V4L2_SUBDEV_DATA_SOURCE = 1,
>> + V4L2_SUBDEV_DATA_SINK,
>> + V4L2_SUBDEV_DATA_PROCESSOR,
>> +};
>> +
>> +/**
>> + * struct v4l2_subdev_platform_data - subdev platform data
>> + * @subdev: subdevice
>> + * @platform_data: subdevice driver platform data
>> + */
>> +struct v4l2_subdev_platform_data {
>> + struct v4l2_subdev *subdev;
>> + void *platform_data;
>> +};
>> +
>> #define to_v4l2_subdev_fh(fh) \
>> container_of(fh, struct v4l2_subdev_fh, vfh)
>>
>> --
>> 1.7.2.5
Thanks,
Sylwester
next prev parent reply other threads:[~2012-10-28 15:30 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-10-19 22:20 [PATCH 0/2] media: V4L2: clock and asynchronous registration Guennadi Liakhovetski
2012-10-19 22:20 ` [PATCH 1/2] media: V4L2: add temporary clock helpers Guennadi Liakhovetski
2012-10-21 18:52 ` Sylwester Nawrocki
2012-10-22 9:14 ` Guennadi Liakhovetski
2012-10-22 10:13 ` Sylwester Nawrocki
2012-10-26 2:05 ` Laurent Pinchart
2012-10-22 12:55 ` Laurent Pinchart
2012-10-22 12:59 ` Laurent Pinchart
2012-10-19 22:20 ` [PATCH 2/2] media: V4L2: support asynchronous subdevice registration Guennadi Liakhovetski
2012-10-22 10:18 ` Hans Verkuil
2012-10-22 11:08 ` Guennadi Liakhovetski
2012-10-22 11:54 ` Hans Verkuil
2012-10-22 12:50 ` Guennadi Liakhovetski
2012-10-22 13:36 ` Hans Verkuil
2012-10-22 14:48 ` Guennadi Liakhovetski
2012-10-22 15:22 ` Hans Verkuil
2012-11-01 14:42 ` Laurent Pinchart
2012-11-01 15:01 ` Guennadi Liakhovetski
2012-11-01 15:22 ` Laurent Pinchart
2012-11-01 15:37 ` Guennadi Liakhovetski
2012-11-01 16:15 ` Hans Verkuil
2012-11-01 16:41 ` Guennadi Liakhovetski
2012-11-01 19:33 ` Sylwester Nawrocki
2012-10-24 12:00 ` Sylwester Nawrocki
2012-11-01 15:13 ` Laurent Pinchart
2012-11-01 16:15 ` Guennadi Liakhovetski
2012-10-24 13:54 ` Guennadi Liakhovetski
2012-10-28 15:30 ` Sylwester Nawrocki [this message]
2012-10-29 7:52 ` Guennadi Liakhovetski
2012-10-31 23:09 ` Sylwester Nawrocki
2012-10-31 23:25 ` Sylwester Nawrocki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=508D4F79.2000204@gmail.com \
--to=sylvester.nawrocki@gmail.com \
--cc=linux-sh@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).