五、v4l2 ctrl 函数初始化---增加标准接口v4l2_ctrl_new_std

一、V4L2应用层调用流程
二、V4L2设备注册
三、video设备初始化
四、V4L2 control结构框架图
五、v4l2 ctrl 函数初始化—增加标准接口v4l2_ctrl_new_std
六、v4l2 ctrl 函数初始化—增加自定义接口v4l2_ctrl_new_custom
七、V4L2 ioctl 标准接口 调用流程
八、V4L2 ioctl 控制接口 调用流程

xxx_v4l2_ctrl结构体定义

struct xxx_v4l2_ctrl {
	struct v4l2_device *v4l2_dev;
	struct video_device *video_dev;
	uint32_t ctx_id;
	struct v4l2_ctrl_handler ctrl_hdl_std_ctrl;
	struct v4l2_ctrl_handler ctrl_hdl_cst_ctrl;
};

v4l2_ctrl_handler结构体定义

struct v4l2_ctrl_handler {
  	struct mutex _lock;
    struct mutex *lock;
    struct list_head ctrls;
    struct list_head ctrl_refs;
    struct v4l2_ctrl_ref *cached;
    struct v4l2_ctrl_ref **buckets;
    v4l2_ctrl_notify_fnc notify;
    void *notify_priva;
    u16 nr_of_buckets;
    int error;
};

v4l2_ctrl结构体定义

struct v4l2_ctrl {
    struct list_head node;
    struct list_head ev_subs;
    struct v4l2_ctrl_handler *handler;
    struct v4l2_ctrl **cluster;
    unsigned int ncontrols;
    unsigned int done:1;
    unsigned int is_new:1;
    unsigned int has_changed:1;
    unsigned int is_private:1;
    unsigned int is_auto:1;
    unsigned int is_int:1;
    unsigned int is_string:1;
    unsigned int is_ptr:1;
    unsigned int is_array:1;
    unsigned int has_volatiles:1;
    unsigned int call_notify:1;
    unsigned int manual_mode_value:8;
    
    const struct v4l2_ctrl_ops *ops;
    const struct v4l2_ctrl_type_ops *type_ops;
    u32 id;
    const char *name;
    enum v4l2_ctrl_type type;
    s64 minimum, maximum, default_value;
    u32 elems;
    u32 elem_size;
    u32 dims[V4L2_CTRL_MAX_DIMS];
    u32 nr_of_dims;
    
    union {
                u64 step;
                u64 menu_skip_mask;
        };
        union {
                const char * const *qmenu;
                const s64 *qmenu_int;
        };
        unsigned long flags;
        void *priv;
        s32 val;
        struct {
                s32 val;
        } cur;

        union v4l2_ctrl_ptr p_new;
        union v4l2_ctrl_ptr p_cur;
}
//dev->xxx_v4l2_ctrl.v4l2_dev = dev->v4l2_dev;
//dev->xxx_v4l2_ctrl.video_dev = &dev->video_dev;
//v4l2_ctrl_init(ctx_id, &dev->xxx_v4l2_ctrl);
int v4l2_ctrl_init( uint32_t ctx_id, xxx_v4l2_ctrl *ctrl )
{
    struct v4l2_ctrl_handler *hdl_std_ctrl = &ctrl->ctrl_hdl_std_ctrl;//标准控制
    struct v4l2_ctrl_handler *hdl_cst_ctrl = &ctrl->ctrl_hdl_cst_ctrl;//自定义控制
    struct v4l2_ctrl *tmp_ctrl;
    struct v4l2_ctrl_config tmp_ctrl_cfg;
    struct v4l2_ctrl_config tmp_ctrl_cfg;

	/* Init and add standard controls */
	/*
		hdl_std_ctrl
		1、初始化handler集合的ctrl头节点
		2、初始化handler集合的refs头节点
		3、初始化buckets的个数
		hdl->nr_of_buckets表明桶的数量,一个桶最多可以存放8个control,buckets表明用来存放的地址,假如有24个control,那就要用到3个桶,hdl->nr_of_buckets = 3
		buckets[0]存放8个v4l2_ref指针
		在struct v4l2_ctrl_handler结构体类型内部还有一个cache成员,其他类型是struct
	*/
	v4l2_ctrl_handler_init(hdl_std_ctrl, 10);//10个控制
	//新增一个control
	ADD_CTRL_STD(V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
}

v4l2_ctrl_handler结构体

struct v4l2_ctrl_handler {
  	struct mutex _lock;
    struct mutex *lock;
    struct list_head ctrls;
    struct list_head ctrl_refs;
    struct v4l2_ctrl_ref *cached;
    struct v4l2_ctrl_ref **buckets;
    v4l2_ctrl_notify_fnc notify;
    void *notify_priva;
    u16 nr_of_buckets;
    int error;
};

v4l2_ctrl_handler_init初始化

#define v4l2_ctrl_handler_init(hdl, nr_of_controls_hint) \
		v4l2_ctrl_handler_init_class(hdl, nr_of_controls_hint, NULL, NULL);

int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint,...)
{
	hdl->lock = &hdl->_lock;
	mutex_init(hdl->lock);
	lockdep_set_class_and_name(hdl->lock, key, name);
	INIT_LIST_HEAD(&hdl->ctrls);
	INIT_LIST_HEAD(&hdl->ctrl_refs);
	hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;//10个控制需要两个桶
	...
	        /*
         * 这里注意 buckets的类型是 struct v4l2_ctrl_ref **buckets;
         * 所以sizeof(hdl->buckets[0]) 对应的大小是 struct v4l2_ctl_ref *
         * 也就是指针的大小,32位系统这个值为4,64位系统这个值为8
         * 这里也可以知道 hdl->buckets[0] 用于存储buckets的地址
*/
	hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
				      sizeof(hdl->buckets[0]),
				      GFP_KERNEL | __GFP_ZERO);
				      
	hdl->error = hdl->buckets ? 0 : -ENOMEM;
	return hdl->error;
}

hdl->nr_of_buckets = 1 + nr_of_controls_hint/8,这表明桶的数量,这里可以得出一个桶能放置8个v4l2_ctrl_ref指针,
再看下buckets成员类型,是struct v4l2_ctrl_ref**类型的,表明这个参数用来存放桶的地址,一共有nr_of_buckets个桶会被记录到buckets[n]数组内
在这里插入图片描述

v4l2_ctrl_new_std

当v4l2_ctrl_handler_init初始化结束后,基本的ctrl链表和ctrl_ref指针链表以及buckets大小分配完毕
新增一个control v4l2_ctrl_new_std( handler_ctrl, &isp_v4l2_ctrl_ops,V4L2_CID_BRIGHTNESS, … );

struct v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id,....)
{
	const char *name;
	enum v4l2_ctrl_type type;
	u32 flags;
	...
	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, min, max, step, def, NULL, 0....);
}

v4l2_ctrl_new

在这个函数中,初始化一个v4l2_ctrl,把v4l2_ctrl的id、ops、handler、name都初始化了

static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,const struct v4l2_ctrl_ops *ops,,,u32 id)
{
	struct v4l2_ctrl *ctrl;
	...
	INIT_LIST_HEAD(&ctrl->node);
	ctrl->handler = hdl;
	ctrl->ops = ops;
	ctrl->type_ops = type_ops ? type_ops : &std_type_ops;
	ctrl->id = id;
	ctrl->name = name;
	...
	handler_new_ref(hdl,ctrl);

}

handler_new_ref

无论是 v4l2_ctrl_new_std、v4l2_ctrl_new_std_menu、v4l2_ctrl_new_custom最终都会调到一个地方,那就是 handler_new_ref 函数

static int handler_new_ref(struct v4l2_ctrl_handler *hdl, struct *ctrl)
{
	struct v4l2_ctrl_ref *refs;
	struct v4l2_ctrl_ref new_ref;
	u32 id = ctrl->id;
	u32 class_ctrl = V4L2_CTRL_ID2_WHICH(id) | 1;
	int bucket = id % hdl->nr_of_buckets;
	...
	new_ref = kzalloc(sizeof(*new_ref),GFP_KERNEL);
	if (!new_ref)
		return handler_set_err(hdl, -ENOMEM);
	new_ref->ctrl = ctrl;//新建的v4l2_ctrl_ref指向了新添加的v4l2_ctrl
	if (ctrl->handler == hdl) {
		//在后面拿control值时,直接从cluster中取值
		ctrl->cluster = &new_ref->ctrl;
		ctrl->ncontrols = 1;
	}
	...
	INIT_LIST_HEAD(&new_ref->node);
	/* 如果为空,或者大于尾端的id,在尾端插入ref节点 */
	    if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
        list_add_tail(&new_ref->node, &hdl->ctrl_refs);
        goto insert_in_hash;
    }
/* 否则遍历 [ctrl_refs] 链表,直到找到第一个 id 值比将要插入的 contorl 的 id 大的
* [v4l2_ctrl_ref] 实例化对象 [ref],然后把新的 [new_ref] 插入到这个 [ref] 前面 */
	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
		if (ref->ctrl->id < id)
			continue;
		if (ref->ctrl->id == id) {
			kfree(new_ref);
			goto unlock;
		}
		list_add(&new_ref->node, ref->node.prev);
		break;
	}
/* 由此可见,ctrl_ref链表中的实例new_ref(指向一个control) 都是按照id值升序排列的,完成了v4l2_ctrl_handler->ctrl_refs链表的插入动作之后,还有最后一步,那就是把新的ctrl_ref放入到一个哈希表中,也就是前面说的桶*/	
insert_in_hash:
	new_ref->next = hdl->buckets[bucket];	
	hdl->buckets[bucket] = new_ref;
unlock: mutex_unlock(hdl->lock);
	return 	0;
}

这里的bucket根据id找到对应的桶,假如一共有2个桶 buckets[0]、buckets[1],假设有8个control的id分别是1-8,通过取模运算,2、4、6、8放在了buckets[0]中,1、3、5、7放到了buckets[1]中,查找的时候也是按照桶排序查找

insert_in_hash:
/* Insert the control node in the hash */
new_ref->next = hdl->buckets[bucket];
hdl->buckets[bucket] = new_ref;

这个函数的作用是把新的ctrl_ref放入到哈希表中,也就是前面的桶
他的作用是把开头使用的id % hdl->nr_of_buckets索引到的桶里面的第一项v4l2_ctrl_ref地址赋值给新的new_ref的next成员,然后把新的new_ref地址赋值给索引到的桶
现在以buckets[1]为例
在这里插入图片描述

v4l2_ctrl_add_handler

假设我们已经创建了两个 v4l2_ctrl_handler
一个标准的hdl_std_ctrl、一个自定义的hdl_cst_ctrl

//把自定义的ctrl也绑定到hdl_std_ctrl上面

int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,struct v4l2_ctrl_handler *add,bool (*filter)(const struct v4l2_ctrl *ctrl), bool from_other_dev)
{
	struct v4l2_ctrl_ref *ref;
	int ret = 0;
	if (!hdl, || !add || hdl == add)
		return 0;
	if (hdl->error)
		return hdl->error;
	mutex_lock(add->lock);
	list_for_each_entry(ref, &add->ctrl_refs, node) {
		struct v4l2_ctrl *ctrl = ref->ctrl;
		if (ctrl->is_private)
			continue;
		if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
			continue;
		if (filter && !filter(ctrl))
			continue;
		ret = handler_new_ref(hdl, ctrl, NULL, from_other_dev, false);
		if (ret)
			break;			
	}
	mutex_unlock(add->lock);
	return ret;
}

//将ctrl_handler 绑定到 video设备的v4l2_handler_ctrl
video_dev->ctrl_handler = hdl_std_ctrl;

使能v4l2_ctrl_handler

int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
{
	int ret;
	if (hdl == NULL)
		return 0;
	mutex_lock(hdl->lock);
	ret = __v4l2_ctrl_handler_setup(hdl);
	mutex_unlock(hdl->lock);	
	return ret;
}
int __v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
{
	struct v4l2_ctrl *ctrl;
	int ret = 0;
	if (hdl == NULL)
		return 0;
	lockdep_assert_held(hdl->lock);
	
    list_for_each_entry(ctrl, &hdl->ctrls, node)
        ctrl->done = false;

	list_for_each_entry(ctrl, &hdl->ctrls, node) {
		struct v4l2_ctrl *master = ctrl->cluster[0];
		int i;
		if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON || (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
			continue;
		for (i = 0; i < master->nconyrols; i++) {
			if (master->cluster[i]) {
				//std的handler->ctrl
				// ctrl->p_cur copy to ctrl->p_new
				cur_to_new(master->cluster[i]);
				master->cluster[i]->is_new = 1;
				master->cluster[i]->done = true;
			}
		}
		ret = call_op(master, s_ctrl);
		if (ret)
			break;
	}
	return ret;
}

cur_to_new

//这里的v4l2_ctrl 
static void cur_to_new(struct v4l2_ctrl *ctrl)
{
	if (ctrl == NULL)
		return NULL;
		//把ctrl->p_cur copy 到 ctrl->p_new
	ptr_to_prt(ctrl, ctrl->p_cur, ctrl->p_new);	
}

call_op

/*
	struct v4l2_ctrl *ctrl;
	...
	INIT_LIST_HEAD(&ctrl->node);
	ctrl->handler = hdl;
	ctrl->ops = ops;
	ctrl->type_ops = type_ops ? type_ops : &std_type_ops;
	ctrl->id = id;
	ctrl->name = name;
*/
#define call_op(master, op) \
		(has_op(master, op) ? master->ops->op(master) : 0)

#define has_op(master, op) \
		(master->ops && master->ops->op)

所以最终会调用到当初ADD一个 v4l2_ctrl的地方来

v4l2_ctrl_new_std( hdl_std_ctrl, &xxx_v4l2_ctrl_ops, \
                               id, min, max, step, def );
   假如id = V4L2_CID_BRIGHTNESS;
   xxx_v4l2_ctrl_ops
   static const struct v4l2_ctrl_ops xxx_v4l2_ctrl_ops = {
    .s_ctrl = xxx_v4l2_ctrl_s_ctrl_standard,
};

那这个call_op(master, op)最终的调用地方就是到
static int xxx_v4l2_ctrl_s_ctrl_standard( struct v4l2_ctrl *ctrl )
{
    switch ( ctrl->id ) {
    	case V4L2_CID_BRIGHTNESS:
        	xxx
        break;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值