我是靠谱客的博主 寂寞篮球,最近开发中收集的这篇文章主要介绍Linux V4L2驱动框架分析之(四):sensor驱动,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

系列文章
Linux V4L2驱动框架分析之(一):架构介绍
Linux V4L2驱动框架分析之(二):平台v4l2设备驱动
Linux V4L2驱动框架分析之(三):v4l2设备的缓存管理
Linux V4L2驱动框架分析之(四):sensor驱动

为了实现代码的重用,sensor驱动只需实现各种设备控制方法供上层调用并注册v4l2_subdev,而无需关心video_device和v4l2_dev。

struct v4l2_subdev结构体定义如下:

struct v4l2_subdev {
	......

	struct list_head list;   //用于挂入v4l2_dev的subdevs链表
	struct module *owner;
	bool owner_v4l2_dev;
	u32 flags;
	struct v4l2_device *v4l2_dev;

	
	const struct v4l2_subdev_ops *ops;  //操作集
	......
	char name[V4L2_SUBDEV_NAME_SIZE];
	......
	struct video_device *devnode;
	struct device *dev;
	struct device_node *of_node;
	struct list_head async_list;
	struct v4l2_async_subdev *asd;
	struct v4l2_async_notifier *notifier;
	struct v4l2_subdev_platform_data *pdata;
};

如果说v4l2_subdev是个i2c设备,可调用v4l2_i2c_subdev_init函数初始化v4l2_subdev:

void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
		const struct v4l2_subdev_ops *ops)
{
	//初始化v4l2_subdev里的ops等
	v4l2_subdev_init(sd, ops);

	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
	
	sd->owner = client->dev.driver->owner;
	sd->dev = &client->dev;
	
	v4l2_set_subdevdata(sd, client);
	i2c_set_clientdata(client, sd);

	/* initialize name */
	snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
		client->dev.driver->name, i2c_adapter_id(client->adapter),
		client->addr);
}

struct v4l2_subdev_ops结构体定义如下:

struct v4l2_subdev_ops {
	const struct v4l2_subdev_core_ops	*core;
	const struct v4l2_subdev_video_ops	*video;
	const struct v4l2_subdev_pad_ops	*pad;
	......
};

对于sensor,一般要实现struct v4l2_subdev_ops的core、video和pad成员。

struct v4l2_subdev_video_ops结构体定义如下:

struct v4l2_subdev_video_ops {
	......

	//获取当前正在使用的标准
	int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);

	//设置视频标准
	int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);

	int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
	int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
	int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
	int (*g_frame_interval)(struct v4l2_subdev *sd,
				struct v4l2_subdev_frame_interval *interval);
	int (*s_frame_interval)(struct v4l2_subdev *sd,
				struct v4l2_subdev_frame_interval *interval);
	......
};

如应用层要获取v4l2设备当前正在使用的视频标准,会"ioctl /dev/video0",ioctl传入到平台驱动层,那么平台驱动可以调用v4l2_subdev->ops->video->g_std回调函数获取sensor的视频标准。

设置好v4l2_subdev后,调用v4l2_async_register_subdev进行注册:

int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
	struct v4l2_async_notifier *notifier;

	
	if (!sd->of_node && sd->dev)
		sd->of_node = sd->dev->of_node;

	mutex_lock(&list_lock);

	INIT_LIST_HEAD(&sd->async_list);

	//匹配v4l2_device,具体怎么匹配后面在分析
	list_for_each_entry(notifier, &notifier_list, list) {
		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
		if (asd) {
			int ret = v4l2_async_test_notify(notifier, sd, asd);
			mutex_unlock(&list_lock);
			return ret;
		}
	}

	/* 未匹配则挂入全局链表subdev_list */
	list_add(&sd->async_list, &subdev_list);

	mutex_unlock(&list_lock);

	return 0;
}

对于未匹配的v4l2_subdev会挂入全局链表subdev_list:
在这里插入图片描述

为了能让平台v4l2设备驱动能匹配到sensor驱动注册的v4l2_subdev,平台驱动需要注册一个struct v4l2_async_notifier:

struct v4l2_async_notifier {
	unsigned int num_subdevs;            //需要匹配v4l2_subdev的数目 
	struct v4l2_async_subdev **subdevs;  //v4l2_async_subdev数组
	struct v4l2_device *v4l2_dev;
	struct list_head waiting;  
	struct list_head done;
	struct list_head list;

	//与v4l2_subdev匹配后,调用bound回调
	int (*bound)(struct v4l2_async_notifier *notifier,
		     struct v4l2_subdev *subdev,
		     struct v4l2_async_subdev *asd);
	int (*complete)(struct v4l2_async_notifier *notifier);
	void (*unbind)(struct v4l2_async_notifier *notifier,
		       struct v4l2_subdev *subdev,
		       struct v4l2_async_subdev *asd);
};



struct v4l2_async_subdev {
	//匹配类型
	enum v4l2_async_match_type match_type; 
	union {
		struct {
			const struct device_node *node; //设备节点匹配
		} of;
		struct {
			const char *name;  //名称匹配
		} device_name;
		struct {
			int adapter_id;
			unsigned short address;  //i2c设备地址匹配
		} i2c;
		struct {
			bool (*match)(struct device *,
				      struct v4l2_async_subdev *);
			void *priv;
		} custom;
	} match;

	/* v4l2-async core private: not to be used by drivers */
	struct list_head list;
};

//匹配类型
enum v4l2_async_match_type {
	V4L2_ASYNC_MATCH_CUSTOM,
	V4L2_ASYNC_MATCH_DEVNAME,
	V4L2_ASYNC_MATCH_I2C,
	V4L2_ASYNC_MATCH_OF,
};

平台v4l2设备驱动设置好struct v4l2_async_notifier后,调用v4l2_async_notifier_register进行注册:

int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
				 struct v4l2_async_notifier *notifier)
{
	struct v4l2_subdev *sd, *tmp;
	struct v4l2_async_subdev *asd;
	int i;

	......

	notifier->v4l2_dev = v4l2_dev;
	INIT_LIST_HEAD(&notifier->waiting);
	INIT_LIST_HEAD(&notifier->done);

	for (i = 0; i < notifier->num_subdevs; i++) {
		asd = notifier->subdevs[i];

		switch (asd->match_type) {
		case V4L2_ASYNC_MATCH_CUSTOM:
		case V4L2_ASYNC_MATCH_DEVNAME:
		case V4L2_ASYNC_MATCH_I2C:
		case V4L2_ASYNC_MATCH_OF:
			break;
		default:
			......
			return -EINVAL;
		}
		//把v4l2_async_subdev都挂入v4l2_async_notifier的waiting链表
		list_add_tail(&asd->list, &notifier->waiting);
	}

	mutex_lock(&list_lock);

	/* 把v4l2_async_notifier挂入全局链表notifier_list */
	list_add(&notifier->list, &notifier_list);

	//遍历subdev_list链表
	list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
		int ret;

		//进行匹配
		asd = v4l2_async_belongs(notifier, sd); 
		if (!asd)
			continue;

		
		ret = v4l2_async_test_notify(notifier, sd, asd);
		if (ret < 0) {
			mutex_unlock(&list_lock);
			return ret;
		}
	}

	mutex_unlock(&list_lock);

	return 0;
}

调用v4l2_async_belongs函数进行匹配:

static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
						    struct v4l2_subdev *sd)
{
	bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
	struct v4l2_async_subdev *asd;

	//从v4l2_async_notifier的waiting链表取出每项v4l2_async_subdev 
	list_for_each_entry(asd, &notifier->waiting, list) {
		/* bus_type has been verified valid before */
		switch (asd->match_type) {
		case V4L2_ASYNC_MATCH_CUSTOM:
			match = match_custom;
			break;
		case V4L2_ASYNC_MATCH_DEVNAME:
			match = match_devname;
			break;
		case V4L2_ASYNC_MATCH_I2C:
			match = match_i2c;
			break;
		case V4L2_ASYNC_MATCH_OF:
			match = match_of;
			break;
		default:
			/* Cannot happen, unless someone breaks us */
			WARN_ON(true);
			return NULL;
		}

		/* 调用match进行v4l2_async_subdev与v4l2_subdev的匹配,不同的匹配类型 
       match回调函数不一样
     */
		if (match(sd, asd))
			return asd;
	}

	return NULL;
}


//对于V4L2_ASYNC_MATCH_OF类型,match为match_of
static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
	return sd->of_node == asd->match.of.node; //判断设备节点是否一致
}

匹配成功后调用v4l2_async_test_notify:

static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
				  struct v4l2_subdev *sd,
				  struct v4l2_async_subdev *asd)
{
	int ret;

	/* Remove from the waiting list */
	list_del(&asd->list);

	//设置v4l2_subdev的asd、notifier 
	sd->asd = asd;
	sd->notifier = notifier;

	if (notifier->bound) {
		ret = notifier->bound(notifier, sd, asd); //回调v4l2_async_notifier的bound
		if (ret < 0)
			return ret;
	}

	/* Move from the global subdevice list to notifier's done */
	list_move(&sd->async_list, &notifier->done);


	//把v4l2_subdev挂入v4l2_device的subdevs链表
	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
	if (ret < 0) {
		if (notifier->unbind)
			notifier->unbind(notifier, sd, asd);
		return ret;
	}

	if (list_empty(&notifier->waiting) && notifier->complete)
		return notifier->complete(notifier);

	return 0;
}

匹配前:
在这里插入图片描述

匹配后:
在这里插入图片描述

最后

以上就是寂寞篮球为你收集整理的Linux V4L2驱动框架分析之(四):sensor驱动的全部内容,希望文章能够帮你解决Linux V4L2驱动框架分析之(四):sensor驱动所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部