linux v4l2架构分析之异步注册v4l2_async_subdev_notifier_register、v4l2_async_register_subdev、v4l2_async_notifie

        在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(&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的依赖设备对应的asd
			list_add_tail(&asd->list, &notifier->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;
				/*
					查找&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_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, &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_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(&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_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(&notifier->list, &notifier_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, &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_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;
}

参考:

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

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值