在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_devicestruct 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_registerreturn -EINVAL; notifier->sd = sd;//与v4l2_async_find_subdev_notifier()有关 ret = __v4l2_async_notifier_register(notifier);"start" __v4l2_async_notifier_registerINIT_LIST_HEAD(&notifier->waiting);//初始化链表INIT_LIST_HEAD(&notifier->done);//遍历notifier->asd_list列表,里面存着从设备树读取的依赖设备节点,可以代表依赖设备list_for_each_entry(asd, &notifier->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的依赖设备对应的asdlist_add_tail(&asd->list, &notifier->waiting);}ret = v4l2_async_notifier_try_all_subdevs(notifier);"start" v4l2_async_notifier_try_all_subdevsstruct v4l2_device *v4l2_dev =v4l2_async_notifier_find_v4l2_dev(notifier);//循环查找notifier->parent,直到notifier->parent == NULL,返回notifier->v4l2_devif (!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;/*查找&notifier->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_subdevsd->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_subdevret = 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删除子设备对应的asdsd->asd = asd;//把从notifier->waiting删除的asd添加到sd->asdsd->notifier = notifier;//notifier为sd的父notifier/* Move from the global subdevice list to notifier's done */list_move(&sd->async_list, &notifier->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, &notifier_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_notifyif (ret 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(&notifier->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_completereturn notifier->ops->complete(n);//这个complete是关键,顶层的v4l2_dev的驱动程序中在这个函数中进行子设备的video_device的注册,生成设备节点文件"end" v4l2_async_notifier_call_complete"end" v4l2_async_notifier_try_completelist_add(&notifier->list, &notifier_list);//一般设备"end" __v4l2_async_notifier_registerif (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_subdevif (!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, &notifier_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_subdevv4l2_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;}

参考:

v4l2_async_subdev_notifier_register 分析_dianlong_lee的博客-CSDN博客_v4l2_async_subdev_notifier_register