v4l2架构专题模块handler分析 --- handler的初始化及handler ctrl注册

Linux v4l2架构学习总链接

handler初始化代码调用如下:

v4l2_ctrl_handler_init(handler, 9);

对应源码:

#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,
				 struct lock_class_key *key, const char *name)
{
	mutex_init(&hdl->_lock);
	hdl->lock = &hdl->_lock;
	lockdep_set_class_and_name(hdl->lock, key, name);
	INIT_LIST_HEAD(&hdl->ctrls);
	INIT_LIST_HEAD(&hdl->ctrl_refs);

        /*
         * 这里有个对8求商的操作
         * 传入的nr_of_controls_hint值为9
         * 所以这里的nr_of_buckets = 1 + 9/8 = 2
         */

	hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;

        /*
         * 这里注意 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;
}

初始化比较简单,接着分析注册相关的代码
 

static const s64 link_freq_menu_items[] = {
    MIPI_FREQ_360M,
    MIPI_FREQ_648M,
};

link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
            V4L2_CID_LINK_FREQ,
            1, 0, link_freq_menu_items);

v4l2_ctrl_new_initn_menu() 代码如下

struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
			const struct v4l2_ctrl_ops *ops,
			u32 id, u8 _max, u8 _def, const s64 *qmenu_int)
{
	const char *name;
	enum v4l2_ctrl_type type;
	s64 min;
	u64 step;
	s64 max = _max;
	s64 def = _def;
	u32 flags;

    
        /*
         * 根据下面的分析可以知道
         * name : "Link Frequenncy"
         * type : V4L2_CTRL_TYPE_INTEGER_MENU
         * flags : 0
         * min  : 局部变量没有赋值,值未知
         * max  : 参数传入 1
         * step : 局部变量没有赋值,值未知
         * def  : 参数传入 0
         */


	v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
	if (type != V4L2_CTRL_TYPE_INTEGER_MENU) {
		handler_set_err(hdl, -EINVAL);
		return NULL;
	}
	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
			     0, max, 0, def, NULL, 0,
			     flags, NULL, qmenu_int, NULL);
}

v4l2_ctrl_new_initn_menu() -> v4l2_ctrl_fill()

const char *v4l2_ctrl_get_name(u32 id)
{
	switch (id) {
            ...
            case V4L2_CID_LINK_FREQ:		return "Link Frequency";
            ...
        }
}

void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
		    s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags)
{
	*name = v4l2_ctrl_get_name(id);
	*flags = 0;

        switch (id) {
            ...
            case V4L2_CID_LINK_FREQ:
		*type = V4L2_CTRL_TYPE_INTEGER_MENU;
		break;
            ...
        }
}

v4l2_ctrl_new_initn_menu() -> v4l2_ctrl_new()

static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
			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 min, s64 max, u64 step, s64 def,
			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
			u32 flags, const char * const *qmenu,
			const s64 *qmenu_int, void *priv)
{
	struct v4l2_ctrl *ctrl;
	unsigned sz_extra;
	unsigned nr_of_dims = 0;
	unsigned elems = 1;
	bool is_array;
	unsigned tot_ctrl_size;
	unsigned idx;
	void *data;
	int err;

	if (hdl->error)
		return NULL;

        /*
         * 根据传入的参数分析
         * ops       : NULL
         * type_ops  : NULL
         * id        : V4L2_CID_LINK_FREQ
         * name      : Link Frequency
         * type      : V4l2_CTRL_TYPE_INTEGER_MENU
         * min       : 0
         * max       : 1
         * step      : 0
         * def       : 0
         * dims      : NULL
         * elem_size : 0
         * flags      : 0
         * qmenu     : NULL
         * qmenu_int : link_freq_menu_items
         * priv      : NULL
         */

	while (dims && dims[nr_of_dims]) {
		elems *= dims[nr_of_dims];
		nr_of_dims++;
		if (nr_of_dims == V4L2_CTRL_MAX_DIMS)
			break;
	}
	is_array = nr_of_dims > 0;

	/* Prefill elem_size for all types handled by std_type_ops */
	switch (type) {
	case V4L2_CTRL_TYPE_INTEGER64:
		elem_size = sizeof(s64);
		break;
	case V4L2_CTRL_TYPE_STRING:
		elem_size = max + 1;
		break;
	case V4L2_CTRL_TYPE_U8:
		elem_size = sizeof(u8);
		break;
	case V4L2_CTRL_TYPE_U16:
		elem_size = sizeof(u16);
		break;
	case V4L2_CTRL_TYPE_U32:
		elem_size = sizeof(u32);
		break;
	default:
		if (type < V4L2_CTRL_COMPOUND_TYPES)
			elem_size = sizeof(s32);
		break;
	}

        /*
         * 对于不同的类型,elem_size值不一样
         */

	tot_ctrl_size = elem_size * elems;

        /*
         * 这里分析的是V4L2_CTRL_TYPE_INTEGER_MENU
         * 需要检查qmenu_init是否为NULL
         */

	/* Sanity checks */
	if (id == 0 || name == NULL || !elem_size ||
	    id >= V4L2_CID_PRIVATE_BASE ||
	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
	    (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) {
		handler_set_err(hdl, -ERANGE);
		return NULL;
	}

        /*
         * check_range检查如下
         * 对于 V4L2_CTRL_TYPE_INTEGER_MENU
         * if (min > max || def < min || def > max)
	 *	return -ERANGE;
         * if (step && ((1 << def) & step))
	 *      return -EINVAL;
         */

	err = check_range(type, min, max, step, def);
	if (err) {
		handler_set_err(hdl, err);
		return NULL;
	}
	if (is_array &&
	    (type == V4L2_CTRL_TYPE_BUTTON ||
	     type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
		handler_set_err(hdl, -EINVAL);
		return NULL;
	}

	sz_extra = 0;
	if (type == V4L2_CTRL_TYPE_BUTTON)
		flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
			V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
	else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
		flags |= V4L2_CTRL_FLAG_READ_ONLY;
	else if (type == V4L2_CTRL_TYPE_INTEGER64 ||
		 type == V4L2_CTRL_TYPE_STRING ||
		 type >= V4L2_CTRL_COMPOUND_TYPES ||
		 is_array)
		sz_extra += 2 * tot_ctrl_size;

        /*
         * 创建一个ctrl
         */

	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
	if (ctrl == NULL) {
		handler_set_err(hdl, -ENOMEM);
		return NULL;
	}

	INIT_LIST_HEAD(&ctrl->node);
	INIT_LIST_HEAD(&ctrl->ev_subs);
        
        /*
         * 填充ctrl成员
         */
    
	ctrl->handler = hdl;
	ctrl->ops = ops;

        /*
         * 注意type_ops为NULL的话,这里会赋值std_type_ops
         */
	ctrl->type_ops = type_ops ? type_ops : &std_type_ops;
	ctrl->id = id;
	ctrl->name = name;
	ctrl->type = type;
	ctrl->flags = flags;
	ctrl->minimum = min;
	ctrl->maximum = max;
	ctrl->step = step;

        /* 默认值 */
	ctrl->default_value = def;

        /* 
         * !is_array == 1
         * type != V4L2_CTRL_TYPE_STRING
         * is_string == 0
         */
	ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING;

        /*
         * is_ptr == 0
         */
	ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string;

        /*
         * !ctrl->is_ptr == 1
         * type != V4L2_CTRL_TYPE_INTEGER64
         * is_int == 1
         */
	ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64;

	ctrl->is_array = is_array;
	ctrl->elems = elems;
	ctrl->nr_of_dims = nr_of_dims;
	if (nr_of_dims)
		memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0]));
	ctrl->elem_size = elem_size;
	if (type == V4L2_CTRL_TYPE_MENU)
		ctrl->qmenu = qmenu;
	else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
        /*
         * ctrl->qmenu_int = link_freq_menu_items
         */
		ctrl->qmenu_int = qmenu_int;

	ctrl->priv = priv;

        /*
         * ctrl->cur.val = 0
         * ctrl->val = 0
         */

	ctrl->cur.val = ctrl->val = def;

        /*
         * 因为sz_extra = 0
         * 所以data没有指向的地址 为NULL
         */
	data = &ctrl[1];

	if (!ctrl->is_int) {
		ctrl->p_new.p = data;
		ctrl->p_cur.p = data + tot_ctrl_size;
	} else {

        /*
         * @p_cur:通过联合表示的控件的当前值,该联合提供了一种通过指针访问控件类型的标准方法。
         * @p_new:通过联合表示控件的新值,该联合提供了一种通过指针访问控件类型的标准方法。
         */
		ctrl->p_new.p = &ctrl->val;
		ctrl->p_cur.p = &ctrl->cur.val;
	}

        /* 
         * elems值为1 
         * type_ops->init
         *     std_init()
         * 对于type为V4L2_CTRL_TYPE_INTEGER_MENU
         * ptr.p_s32[idx] = ctrl->default_value
         */
	for (idx = 0; idx < elems; idx++) {
		ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
		ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
	}

	if (handler_new_ref(hdl, ctrl)) {
		kvfree(ctrl);
		return NULL;
	}
	mutex_lock(hdl->lock);

        /*
         * 将ctrl通过node挂载到hdl底ctrls链表上
         */

	list_add_tail(&ctrl->node, &hdl->ctrls);
	mutex_unlock(hdl->lock);
	return ctrl;
}

v4l2_ctrl_new_initn_menu() -> v4l2_ctrl_new() -> handler_new_ref()

static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
			   struct v4l2_ctrl *ctrl)
{
	struct v4l2_ctrl_ref *ref;
	struct v4l2_ctrl_ref *new_ref;
	u32 id = ctrl->id;

        /*
         * 这里获取ctrl所属的类
         * 比如当前分析的V4L2_CID_LINK_FREQ
         * 所属的类为 V4L2_CTRL_CLASS_IMAGE_PROC
         */

	u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1;

        /*
         * 这里将不同的id放入多个buckets中,方便快速查找
         */

	int bucket = id % hdl->nr_of_buckets;	/* which bucket to use */

	/*
	 * Automatically add the control class if it is not yet present and
	 * the new control is not a compound control.
	 */

        /*
         * 如果尚不存在控件类并且新控件不是复合控件,则自动添加该控件类。 
         * 对于控件类这里先不分析,等分析完V4L2_CID_LINK_FREQ
         * 再回来看这个
         */
	if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES &&
	    id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
		if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
			return hdl->error;

	if (hdl->error)
		return hdl->error;

        /*
         * ref是reference的缩写,参考的意思
         */

	new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
	if (!new_ref)
		return handler_set_err(hdl, -ENOMEM);

        /*
         * 填充ctrl
         */

	new_ref->ctrl = ctrl;

        /*
         * 对于v4l2_ctrl_add_handler调用当前函数的时候
         * ctrl->handler != hdl
         * 所以这里加上判断
         * 但是if中的代码暂时没有看明白....
         */

	if (ctrl->handler == hdl) {
		/* By default each control starts in a cluster of its own.
		   new_ref->ctrl is basically a cluster array with one
		   element, so that's perfect to use as the cluster pointer.
		   But only do this for the handler that owns the control. */
		ctrl->cluster = &new_ref->ctrl;
		ctrl->ncontrols = 1;
	}

	INIT_LIST_HEAD(&new_ref->node);

	mutex_lock(hdl->lock);

	/* Add immediately at the end of the list if the list is empty, or if
	   the last element in the list has a lower ID.
	   This ensures that when elements are added in ascending order the
	   insertion is an O(1) operation. */

        /*
         * new_ref是根据id排列挂载到hdl->ctrl_refs上的
         * 1. hdl->ctrl_refs链表是空的,直接添加就可以
         * 2. 链表上最后一个成员ID比当前的ID小
         *    链表上有数据,说明已经排列过了,所以这里也可以直接添加
         */

	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;
	}

        /*
         * 上面的条件都不符合,这里要找到合适的位置添加
         */

	/* Find insert position in sorted list */
	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
		if (ref->ctrl->id < id)
			continue;
		/* Don't add duplicates */
		if (ref->ctrl->id == id) {
			kfree(new_ref);
			goto unlock;
		}

                /*
                 * 找到合适的位置,添加
                 */

		list_add(&new_ref->node, ref->node.prev);
		break;
	}

insert_in_hash:

        /*
         * 之前说过buckets是为了实现快速查找
         * 这里直接通过hdl->buckets[bucket]指向new_ref
         */

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

unlock:
	mutex_unlock(hdl->lock);
	return 0;
}

以上就完成了handler ctrl的注册

假设11个ctrl,ID为1~11,这里先不考虑ctrl class,注册顺序 1 3 4 6 7 8 10 11 9 2 5

 

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值