在camera驱动注册中,v4l2_async_subdev_notifier_register、v4l2_async_register_subdev、v4l2_async_notifier_register这几个函数都会被使用到,三者在异步注册的实现中是紧密关联的,所以本文将三者放在一起进行分析。本文主要介绍异步注册的功能的整体实现框架,为了更好把握整体思路,会忽略中间的非关键性代码。文中将会先分析v4l2异步注册的实现逻辑思路,后面会结合代码进行具体的分析。
一、v4l2异步注册的实现逻辑思路
以下为异步注册过程中涉及到的关键数据结构。
struct v4l2_device {
struct device *dev;
struct list_head subdevs;//子设备被注册到v4l2_dev后,挂接在这个链表
};
struct v4l2_async_notifier { const struct v4l2_async_notifier_operations *ops; struct v4l2_device *v4l2_dev;//v4l2_async_notifier所属的v4l2_device struct v4l2_subdev *sd;//被异步注册的子设备 struct v4l2_async_notifier *parent;//父v4l2_async_notifier,父子关系依赖于v4l2_async_notifier 对应的sd之间父子关系 struct list_head asd_list;//该链表用于挂接所依赖的v4l2_async_subdev->list struct list_head waiting;//该链表用于挂接待注册的v4l2_async_subdev->list struct list_head done;//该链表用于挂接已经被注册的v4l2_subdev->async_list struct list_head list;//被挂接到全局notifiers_list };
struct v4l2_async_subdev {
enum v4l2_async_match_type match_type;
union {
struct fwnode_handle *fwnode;//匹配相关的fwnode_handle
const char *device_name;//设备名
} match;//匹配子设备的时候使用/* v4l2-async core private: not to be used by drivers */
struct list_head list;//在子设备被异步注册后,真正注册前,被添加到notifier->waiting
struct list_head asd_list;//被挂接到v4l2_async_notifier->asd_list,说明本v4l2_async_subdev为v4l2_async_notifier的所依赖的子设备的v4l2_async_subdev};
struct v4l2_subdev {
struct list_head list;//本子设备被注册到v4l2_dev后,挂接到v4l2_dev->subdevs链表
struct v4l2_device *v4l2_dev;//本子设备所属的v4l2_device
struct fwnode_handle *fwnode;//与设备树节点相关
struct list_head async_list;// 被挂接到全局subdev_list链表或v4l2_async_notifier ->done链表
struct v4l2_async_subdev *asd;//指向所代表的v4l2_async_notifier
struct v4l2_async_notifier *notifier;//指向子设备的v4l2_async_notifier
};
v4l2_device管理着所有v4l2_subdev子设备,所有子设备从属于v4l2_device,所以v4l2_device的真正注册的时机要先于所有子设备真正注册的时机,v4l2_device的注册完成后才存在一个v4l2_device用于注册子设备v4l2_subdev。
设备与设备之间存在依赖关系,所有设备形成一条依赖链(其实是树,这里简化考虑),依赖链最顶层的设备是管理全部设备的v4l2_dev,最底层的设备是产生原图像数据的sensor。对依赖链上的多个设备进行乱序异步注册时,因未达到注册条件(条件就是设备的上一层的被依赖设备没被注册,可见这是依赖链自上而下的注册,如果顶层的v4l2_dev未被注册,整个链条上的设备都无法注册)而没有被真正注册的设备,设备本身以及其对应的notifier会被添加到全局列表,其依赖的设备被保存在其waiting列表,等待本设备的上一层的被依赖设备被真正注册后,会来注册本设备,本设备再通过waiting列表查找并真正注册下一层的设备,可见真正的设备注册过程是一个从依赖链上到下的注册过程,因为前面所说的原因,所以无论v4l2_device是在在任何时候被异步注册,它都会是第一个被真正注册,可见这是一个从依赖链顶端设备v4l2_device开始的注册过程,并且被注册的设备的notifier后会形成一条父子关系的依赖链以供查询本设备是否被真正注册(链表的顶端的notifier的v4l2_dev是存在的说明已经被被注册)。
上面的过程是v4l2_async_subdev_notifier_register的实现思路,不过有个问题,如果只能从依赖链的上层往下层注册,那么当中间出现设备未被异步注册过,依赖链的注册将到此为止,所以当新设备被注册时,还需要具备向上查找上一层的被依赖设备的能力,如果上一层的被依赖设备waiting列表的成员,说明新设备是其依赖设备,如果达到注册条件(如上说的条件)则进行上一段内容中描述的相同的注册过程。这一部分代码在v4l2_async_register_subdev中实现。
异步注册的过程:
1、异步注册前,从获取设备树中本设备的依赖设备信息
执行v4l2_async_subdev_notifier_register、v4l2_async_register_subdev进行子设备的异步注册前,需要使用v4l2_async_notifier_parse_fwnode_endpoints_by_port类似的函数先读取子设备在设备树中endpoints数据,从而获取子设备依赖的子设备的信息于v4l2_async_subdev并挂接到v4l2_async_notifier->asd_list中,这样asd_list中就存放着依赖设备的信息。
2、执行v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, struct v4l2_async_notifier *notifier)
2.1 检测重复性和合法性后,将notifier->asd_list上的v4l2_async_subdev添加到notifier->waitting。进入等待注册状态。
2.2 如果notifier在已注册设备依赖链上(说明notifier的设备及上层的设备都被注册,v4l2_dev是第一个被注册的),遍历全局subdev_list,找到notifier->waitting上的v4l2_async_subdev,通过这个v4l2_async_subdev获得依赖设备v4l2_subdev并进行注册。把v4l2_subdev添加到v4l2_dev->subdevs,从notifier->waiting删除子设备对应的v4l2_async_subdev,从subdev_list链表删除v4l2_subdev,并添加到notifier->done。
2.3 找到v4l2_subdev的subdev_notifier,subdev_notifier->parent = notifier,建立已注册设备依赖链,以subdev_notifier为参数notifier,继续递归调用步骤2.2。
2.4 检测是否所有依赖链上的设备注册都完成,回调notifier->ops->complete函数,该函数在v4l2_dev的驱动程序中实现。
3、执行v4l2_async_register_subdev(struct v4l2_subdev *sd)
v4l2_async_notifier_try_all_subdev是根据notifier->waitting中的依赖设备去寻找全局subdev_list找可以注册的设备进行注册,如果subdev_list中没有,则需要等待设备异步注册时自己注册,而v4l2_async_register_subdev就是这个设备自己向上层依赖寻找到这个notifier并实现注册的过程。
3.1 遍历全局notifier_list,找到在已注册设备依赖链上的notifier,在notifier->waiting中找到v4l2_subdev对应的v4l2_async_subdev,这里是从v4l2_subdev找到上一层被依赖的v4l2_subdev的4l2_async_subdev。
3.2 在notifier中完成v4l2_subdev的注册,步骤如2.2。
二、代码分析
关键的代码其实不多,但耦合性比较强,需要多思考各种条件发生的情况和前提,尽量理清数据结构的作用和数据结构之间的关系。函数的调用比较复杂,直接把调用的函数代码也都整合到一起罗列出来逻辑会比较清晰。
这里的实现为v4.20.17内核版本,在线阅读链接v4l2-async.c - drivers/media/v4l2-core/v4l2-async.c - Linux source code (v4.20.17) - Bootlin
v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,struct v4l2_async_notifier *notifier)
if (WARN_ON(!sd || notifier->v4l2_dev))//注意这里,notifier->v4l2_dev存在则返回,最后的v4l2_dev注册使用4l2_async_notifier_register
return -EINVAL;
notifier->sd = sd;//与v4l2_async_find_subdev_notifier()有关
ret = __v4l2_async_notifier_register(notifier);
"start" __v4l2_async_notifier_register
INIT_LIST_HEAD(¬ifier->waiting);//初始化链表
INIT_LIST_HEAD(¬ifier->done);
//遍历notifier->asd_list列表,里面存着从设备树读取的依赖设备节点,可以代表依赖设备
list_for_each_entry(asd, ¬ifier->asd_list, asd_list) {
/*
判断asd是否已经存在或被处理过,通过检测asd->match.fwnode是否存在于notifier->asd_list的
其他元素中以及notifier_list链表中的元素notifier->waiting和notifier->done进行判断
*/
ret = v4l2_async_notifier_asd_valid(notifier, asd, i++);
if (ret)
goto err_unlock;
//没问题的asd添加到notifier->waiting,waiting列表中就是notifier的依赖设备对应的asd
list_add_tail(&asd->list, ¬ifier->waiting);
}
ret = v4l2_async_notifier_try_all_subdevs(notifier);
"start" v4l2_async_notifier_try_all_subdevs
struct v4l2_device *v4l2_dev =v4l2_async_notifier_find_v4l2_dev(notifier);//循环查找notifier->parent,直到notifier->parent == NULL,返回notifier->v4l2_dev
if (!v4l2_dev)
return 0;//顶层的设备的notifier才有v4l2_dev
/*
从后面的代码看,能到这里,说明notifier已经在依靠notifier->parent形成的已注册设备依赖链上,
notifier对应的设备已经在notifier的父notifier调用v4l2_async_notifier_try_all_subdevs时完成了。
(猜测notifier->asd_list可能挂多个依赖设备,可读取设备树的代码验证,waiting可能挂多个设备的asd,
所以依赖关系形成了依赖树,但parent却只能形成的依赖链表,可能是数据流只能有一个流通路径)
第一次能运行到这里的是v4l2_async_notifier_register函数中调用__v4l2_async_notifier_register
(因为其notifier->v4l2_dev被提前幅值),即依赖链顶层设备v4l2_dev的注册。
*/
again:
/*
之前调用异步注册的设备在未满足注册条件时,会被挂接在subdev_list上,
等待它们的上一层设备异步注册时查找并注册它们,以下是这个过程。
*/
list_for_each_entry(sd, &subdev_list, async_list) {
struct v4l2_async_subdev *asd;
int ret;
/*
查找¬ifier->waiting中匹配sd的asd,这里sd和asd并没有相互包含从而能够直接找到对方的关系,需要遍历比较查找
*/
asd = v4l2_async_find_match(notifier, sd);
if (!asd)
continue;
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
"start" v4l2_async_match_notify
/*
在这里,之前的父设备(上一层的被依赖设备)异步注册时通过v4l2_async_subdev_notifier_register挂在notifier->waiting的子设备
匹配那些在子设备异步注册时通过v4l2_async_register_subdev挂在subdev_list的子设备本身后,
在这里将子设备的注册到v4l2_dev
*/
ret = v4l2_device_register_subdev(v4l2_dev, sd);//真正注册设备
"start" v4l2_device_register_subdev
sd->v4l2_dev = v4l2_dev;
v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL, true);//控制相关的句柄注册,先不用管
if (v4l2_dev->mdev)
err = media_device_register_entity(v4l2_dev->mdev, entity);//也在这里注册媒体设备
if (sd->internal_ops && sd->internal_ops->registered)//先不用管
sd->internal_ops->registered(sd);
NULL, true);
list_add_tail(&sd->list, &v4l2_dev->subdevs);//v4l2_device_register_subdev注册,把sd添加到v4l2_dev->subdevs
"end" v4l2_device_register_subdev
ret = v4l2_async_notifier_call_bound(notifier, sd, asd);//notifier->ops->bound(notifier, sd, asd),跟驱动相关,具体看驱动的实现
/* Remove from the waiting list */
list_del(&asd->list);//完成子设备注册到v4l2_dev后,从notifier->waiting删除子设备对应的asd
sd->asd = asd;//把从notifier->waiting删除的asd添加到sd->asd
sd->notifier = notifier;//notifier为sd的父notifier
/* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, ¬ifier->done);//从subdev_list链表删除sd,并添加到notifier->done
/*
* See if the sub-device has a notifier. If not, return here.
遍历notifier_list找到与sd相等的notifier,即notifier的子notifier
之所以能够通过比较notifier->sd == sd找到对应的notifier,是因为之前的子设备调用
v4l2_async_subdev_notifier_register时,设置notifier->sd = sd;
*/
subdev_notifier = v4l2_async_find_subdev_notifier(sd);
list_for_each_entry(n, ¬ifier_list, list)
if (n->sd == sd)
return n;
/*
能运行到这里,说明sd是在本次运行时才进行第一次注册,之前是没有注册过的,因为之前注册的sd,早就从subdev_list移除,其对应的v4l2_async_subdev也早从notifier->waiting移除,
所以运行到前面的v4l2_async_find_match时,就从continue跳过了。
同时因为后面subdev_notifier->parent = notifier,说明首次注册后subdev_notifier->parent有值。
有个疑问?那什么情况下既能能运行到这里,subdev_notifier->parent为不为NULL呢?是存在两个设备依赖同一个子设备sd?不过这种情况在前面v4l2_async_notifier_fwnode_has_async_subdev
中会有判断,但这是在v4l2_async_subdev_notifier_register中调用v4l2_async_notifier_try_all_subdevs的情况,
而在v4l2_async_register_subdev中调用v4l2_async_match_notify则没有类似v4l2_async_notifier_fwnode_has_async_subdev的判断
*/
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.
建立sd的notifier与其parent的关系,也说明subdev_notifier->parent存在时,subdev_notifier对应的sd已经被注册了。
*/
subdev_notifier->parent = notifier;
return v4l2_async_notifier_try_all_subdevs(subdev_notifier);//递归注册subdev_notifier->waitting的驱动,如果满足注册条件的话
"end" v4l2_async_match_notify
if (ret < 0)
return ret;
/*
* 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;
}
return 0
"end" v4l2_async_notifier_try_all_subdevs
ret = v4l2_async_notifier_try_complete(notifier);//最顶层且notifier->waiting为空的subdev才会执行
"start" v4l2_async_notifier_try_complete
/* Quick check whether there are still more sub-devices here.
非空时,存在未处理的notifier->waiting,什么情况会出现,比如前面v4l2_async_notifier_try_all_subdevs中,
notifier不是v4l2_dev或者从subdev_list存在的subdev不足够与notifier->waiting匹配(需要进一步看什么时候subdev会被添加到subdev_list,
subdev会被添加到subdev_list是在驱动注册的probe()中调用v4l2_async_register_subdev时进行的,所以只有执行驱动模块后的驱动设备才会
添加到subdev_list,subdev_list就是已有驱动的设备列表)
*/
if (!list_empty(¬ifier->waiting))
return 0;
/* Check the entire notifier tree; find the root notifier first. */
while (notifier->parent)
notifier = notifier->parent;//前面v4l2_async_notifier_try_all_subdevs中已注册的设备会建立的父子依赖链的关系
/* This is root if it has v4l2_dev. */
if (!notifier->v4l2_dev)
return 0;//只有依赖链最顶层的v4l2_dev的notifier具备v4l2_dev
/* Is everything ready?
Return true if all child sub-device notifiers are complete, false otherwise.
通过检测当前notifier上的notifier->waiting是否为空,并递归检测notifier->done上所有subdev的notifier
*/
if (!v4l2_async_notifier_can_complete(notifier))
return 0;
return v4l2_async_notifier_call_complete(notifier);
"start" v4l2_async_notifier_call_complete
return notifier->ops->complete(n);//这个complete是关键,顶层的v4l2_dev的驱动程序中在这个函数中进行子设备的video_device的注册,生成设备节点文件
"end" v4l2_async_notifier_call_complete
"end" v4l2_async_notifier_try_complete
list_add(¬ifier->list, ¬ifier_list);//一般设备
"end" __v4l2_async_notifier_register
if (ret)
notifier->sd = NULL;//__v4l2_async_notifier_register失败,notifier->sd清空
return ret;
经过上面的v4l2_async_subdev_notifier_register的分析,需要结合v4l2_async_register_subdev分析才能理解清楚子设备和v4l2_dev的异步注册过程
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
"start" v4l2_async_register_subdev
if (!sd->fwnode && sd->dev)
sd->fwnode = dev_fwnode(sd->dev);
INIT_LIST_HEAD(&sd->async_list);//驱动中初次调用v4l2_async_register_subdev异步注册
/*
下面代码比较关键,其实现跟v4l2_async_notifier_try_all_subdev类似,
只不过v4l2_async_notifier_try_all_subdev是根据notifier->waitting中的依赖设备去寻找全局subdev_list找可以注册的设备进行注册,
如果subdev_list中没有,则需要等待设备异步注册时自己注册,而下面则是这个设备自己向上层依赖寻找到这个notifier并实现注册的过程。
*/
list_for_each_entry(notifier, ¬ifier_list, list) {
struct v4l2_device *v4l2_dev =
v4l2_async_notifier_find_v4l2_dev(notifier);//这里是获取v4l2_dev,通过递归查找notifier->parent直到为NULL,返回notifier->v4l2_dev
/*
v4l2_async_subdev_notifier_register中v4l2_async_notifier_try_all_subdevs调用中的条件跟这里类似,结合起来分析,
异步注册只通过调用v4l2_async_subdev_notifier_register或v4l2_async_register_subdev实现真正注册,
而要实现真正的注册,需要满足条件v4l2_dev != NULL,所以多个有依赖关系(或者说父子关系)子设备在乱序注册时,必须等到最顶层的v4l2_dev
进行注册后,才会真正注册设备,而真正注册的时机是,与顶层设备的notifier通过parent关系构建的已注册设备依赖链条能够连接到本设备时。
*/
if (!v4l2_dev)//遍历,直到找到含有v4l2_dev的notifier,说明v4l2_dev在之前在驱动中异步注册到notifier的链表
continue;
/*
注意,根据上面的分析并结合v4l2_async_notifier_try_all_subdevs中的分析,
如果notifier的sd注册过,那notifier的parent及以上层的notifier是注册过的
,且最顶层的notifier对应v4l2_dev,那该notifier能够走到这一步
*/
asd = v4l2_async_find_match(notifier, sd);//notifier->waiting中找到sd对应的v4l2_async_subdev,这里是从sd找到上一层被依赖的sd的asd,在notifier中完成sd的注册
if (!asd)
continue;
/*
这里有些疑问,如果notifier->waiting中有多个设备,其中一个设备已经之前注册了,
并且这个设备的依赖设备也都完成真正的注册,已经通过notifier->parent形成已注册的依赖链,
此时的sd是notifier->waiting中另一个未注册的设备,代码也能运行到这里,
这时进入v4l2_async_match_notify后会注册sd,在赋值notifier->parent时岂不是会破坏上面形成的依赖链?
*/
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);//这里分析参考v4l2_async_subdev_notifier_register中v4l2_async_notifier_try_all_subdevs的分析
if (ret)
goto err_unbind;
ret = v4l2_async_notifier_try_complete(notifier);
if (ret)
goto err_unbind;
goto out_unlock;
}
list_add(&sd->async_list, &subdev_list);//进行过异步注册但为被真正注册的待设备被挂在这个全局列表
out_unlock:
mutex_unlock(&list_lock);
return 0;
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)
v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
if (sd->asd)
v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd);
mutex_unlock(&list_lock);
return ret;
"end" v4l2_async_register_subdev
v4l2_async_subdev_notifier_register,异步注册的子设备只是被挂在notifier->waitting, subdev_list,
要真正注册时需要设备的notifier的父notifier与v4l2_dev的notifier的父子依赖链条已经构建好,
而一开始依赖链了只能从v4l2_dev的注册开始,但依赖链条只能向下构建,如果中间刚好有设备还没被异步注册就断了,
之后那个设备在v4l2_async_register_subdev中,再从下往上寻找依赖关系并注册。
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
int ret;
if (WARN_ON(!v4l2_dev || notifier->sd))
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
ret = __v4l2_async_notifier_register(notifier);
if (ret)
notifier->v4l2_dev = NULL;
return ret;
}
参考:文章来源:https://www.toymoban.com/news/detail-623057.html
v4l2_async_subdev_notifier_register 分析_dianlong_lee的博客-CSDN博客_v4l2_async_subdev_notifier_register文章来源地址https://www.toymoban.com/news/detail-623057.html
到了这里,关于linux v4l2架构分析之异步注册v4l2_async_subdev_notifier_register、v4l2_async_register_subdev、v4l2_async_notifie的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!