一、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); }无论是 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; }
文章详细介绍了V4L2应用层调用流程,包括设备注册、video设备初始化、控制结构框架、ioctl接口调用等步骤。通过v4l2_ctrl_handler和v4l2_ctrl结构体的初始化及管理,展示了如何添加和组织控制,并利用v4l2_ctrl_new_std等函数创建标准控制。最后,讨论了控制的设置与操作过程。



1758

被折叠的 条评论
为什么被折叠?



