V4l2 async框架介绍
v4l2 async框架是Linux V4L2框架中的一个子框架,用于实现v4l2 device的异步注册。Linux V4L2框架中对设备定义分为:v4l2_device和v4l2_subdev, 设备是有从属概念的,v4l2 device之间是可以存在依赖关系的。v4l2 async框架主要就是为了解决这种乱序异步注册问题,保证一个v4l2 device的上一层的被依赖设备注册后注册这个v4l2 device。按依赖链自上而下完成注册。
v4l2 async框架定义两个角色,3个概念:
v4l2_async_subdev //async的桥。是异步通知到对端的桥。notifier通过这个桥知道异步操作的对端是谁。notifier持有桥(其实是怎么match)。
v4l2_async_notifier //v4l2 async框架中的重要角色,是绑定动作的发起者(该角色需要实现v4l2_async框架定义接口:bound, complete, unbind)。
v4l2_subdev //async通知给谁。是通知桥连接的对端。
v4l2 async框架是围绕两个链表来实现异步处理的:
static LIST_HEAD(notifier_list) //注册到v4l2 async框架的没有完成bind操作的v4l2_async_notifier.
static LIST_HEAD(subdev_list) //注册到v4l2 async框架的没有bind的对端。
对端向v4l2 async框架注册的两种情况:
情况1:桥连接的对端先注册,notifier还没注册到v4l2 async框架。这时找不到匹配的桥,不能触发notifier bind流程,这个对端(v4l2_subdev)会被记录到subdev_list。
情况2:桥连接的对端注册之前notifier已经注册到v4l2 async框架。这时能找到匹配的桥,将触发notifier bind流程。如果异步处理完成,这个对端其实是不会进入subdev_list。
如何使用v4l2 async框架实现一个camera系统(基于Linux V4L2框架实现)异步处理:
首先,确定camera系统中哪些模块充当v4l2_async_notifier, 哪些模块充当v4l2_subdev,即确定模块的主从关系。
其次,对于充当v4l2_async_notifier角色的模块需要实现相应的异步处理回调接口(struct v4l2_async_notifier_operations数据结构定义框架接口,自己根据实际情况实现需要的接口就可以)
然后,根据实际情况定义n个notifier match桥。(桥有x种,调用v4l2_async_notifier_add_xx_remote_subdev接口将一个桥添加到notifier)
{BANNED}{BANNED}最佳佳后,调用将async notifier和v4l2_subdev向v4l2_async框架注册。此时v4l2_async框架会根据v4l2 device设备的注册情况完成异步bind。
v4l2 async框架定义接口
主要是3个数据结构:v4l2_async_notifier, v4l2_async_subdev和v4l2_async_notifier_operations
/**
* struct v4l2_async_notifier - v4l2_device notifier data
*
* @ops: notifier operations
* @v4l2_dev: v4l2_device of the root notifier, NULL otherwise
* @sd: sub-device that registered the notifier, NULL otherwise
* @parent: parent notifier
* @asd_list: master list of struct v4l2_async_subdev
* @waiting: list of struct v4l2_async_subdev, waiting for their drivers
* @done: list of struct v4l2_subdev, already probed
* @list: member in a global list of notifiers
*/
struct v4l2_async_notifier {
const struct v4l2_async_notifir_operations *ops;
struct v4l2_device *v4l2_dev; //root notifier的v4l2_device
struct v4l2_subdev *sd; //这个notifier注册到sub-device
struct v4l2_async_notifier *parent;
struct list_head asd_list;//
struct list_head waiting;//等待他们的drivers的v4l2_async_subdev链表
struct list_head done; //已经probed的v4l2_subdev链表
struct list_head list; //用于将这个notifier串到v4l2_async框架的全局链表notifier中。
};
/**
* struct v4l2_async_subdev - sub-device descriptor, as known to a bridge
*
* @match_type: type of match that will be used
* @match: union of per-bus type matching data sets
* @match.fwnode:
* pointer to &struct fwnode_handle to be matched.
* Used if @match_type is %V4L2_ASYNC_MATCH_FWNODE.
* @match.device_name:
* string containing the device name to be matched.
* Used if @match_type is %V4L2_ASYNC_MATCH_DEVNAME.
* @match.i2c: embedded struct with I2C parameters to be matched.
* Both @match.i2c.adapter_id and @match.i2c.address
* should be matched.
* Used if @match_type is %V4L2_ASYNC_MATCH_I2C.
* @match.i2c.adapter_id:
* I2C adapter ID to be matched.
* Used if @match_type is %V4L2_ASYNC_MATCH_I2C.
* @match.i2c.address:
* I2C address to be matched.
* Used if @match_type is %V4L2_ASYNC_MATCH_I2C.
* @match.custom:
* Driver-specific match criteria.
* Used if @match_type is %V4L2_ASYNC_MATCH_CUSTOM.
* @match.custom.match:
* Driver-specific match function to be used if
* %V4L2_ASYNC_MATCH_CUSTOM.
* @match.custom.priv:
* Driver-specific private struct with match parameters
* to be used if %V4L2_ASYNC_MATCH_CUSTOM.
* @asd_list: used to add struct v4l2_async_subdev objects to the
* master notifier @asd_list
* @list: used to link struct v4l2_async_subdev objects, waiting to be
* probed, to a notifier->waiting list
*
* When this struct is used as a member in a driver specific struct,
* the driver specific struct shall contain the &struct
* v4l2_async_subdev as its first member.
*/
struct v4l2_async_subdev {
enum v4l2_async_match_type match_type;
union {
struct fwnode_handle *fwnode;
const char *device_name;
struct {
int adapter_id;
unsigned short address;
}i2c;
struct {
bool (*match)(struct device *dev,
struct v4l2_async_subdev *sd);
void *priv;
}custom;
}match;
/* v4l2-async core private: not to be used by drivers */
struct list_head list;
struct list_head asd_list;
};
/**
* @bound: a subdevice driver has successfully probed one of the subdevices
* @complete: All subdevices have been probed successfully. The complete callback is only executed for the root notifier.
* @unbind: a subdevice is leaving
*/
struct v4l2_async_notifier_operations {
int (*bound)(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd);
int (*complete)(struct v4l2_async_notifier *notifier);
int (*unbind)(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd)
}
v4l2_async_notifier_register和v4l2_async_subdev_notifier_register都是向v4l2 async框架注册一个异步通知器,不同的是后边这个接口注册的notifier是子设备(v4l2_subdev),{BANNED}中国{BANNED}中国第一个接口时v4l2_device作通知器。v4l2 async框架中notifier是有root的概念,root notifier是v4l2_device。
示例
notifier_list链表上有3个notifier,分别是imx291的, mipi csi phy的及 mipi csi 的
subdev_list链表上有3个subdev,分别是imx291的, mipi csi phy的及 mipi csi 的
mipi csi phy的notifier->waitng上有个asd,asd指向imx291 dts的节点
mipi csi 的notifier->waiting上有个asd,asd指向mipi csi phy dts的节点
rkcif_mipi 的notifier->waiting上有个asd,asd指向mipi csi dts的节点
异步通知机制允许驱动程序在子设备可用时通知V4L2核心进行注册,而不需要在驱动程序初始化阶段进行静态注册。这种机制非常适用于需要动态添加或移除子设备的情况,例如热插拔设备或根据特定条件动态注册设备的应用场景。
需要补充下:v4l2_async_notifier_register()和v4l2_async_register_subdev实际上并真正注册设备,只有在它们两个注册的设备信息匹配后,才会调用v4l2_device_register_subdev()函数向系统真正注册子设备。
小结:
视频异步子设备注册可以分为两个部分,一部分为notifier管理的v4l2_async_subdev,另外一部分为子设备驱动的v4l2_subdev。notifier中的异步子设备都是根据设备树信息注册的。然后子设备驱动再注册自己对应的子设备。上面所涉及到的主要区别在于v4l2_async_notifier_register()和v4l2_async_register_subdev()函数。这两个函数需要仔细去分析下,原理和linux驱动中的其它总线和设备匹配非常相似。一般来说视频驱动会先解析设备树获取子设备信息后,再通过v4l2_async_notifier_register()来注册到notifier,将还没有匹配的子设备添加到waiting链表上。然后调用notifier->match()函数来进行匹配,如果匹配成功,将调用v4l2_device_register_subdev()将subdev添加到V4L2的子设备链表上。{BANNED}最佳后调用notifier->complete()回调函数。如果匹配不成功,则将添加到v4l2自己维护的通知链表notifier_list。另一方面,在驱动中会调用v4l2_async_register_subdev()来注册子设备,将调用notifier->match()函数来进行匹配,如果匹配成功,将调用v4l2_device_register_subdev()将subdev添加到V4L2的子设备链表上。{BANNED}最佳后调用notifier->complete()回调函数。如果匹配不成功,会将设备添加到v4l2自己维护的子设备链表subdev_list是上。