本文根据原代码分析v4l2的handler初始化以及添加ctrl的过程,会涉及v4l2_ctrl_handler、v4l2_ctrl、v4l2_ctrl_ref结构体的分析,以及介绍v4l2_ctrl_handler_init、v4l2_ctrl_new_std、v4l2_ctrl_new、handler_new_ref、v4l2_ctrl_handler_setup相关函数的在驱动实例中的使用过程,并且对这些函数进行详细的分析。
v4l2的io ctrl的管理与v4l2_ctrl_handler、v4l2_ctrl、v4l2_ctrl_ref这三个结构体密切相关,在具体分析程序之前,需要介绍一下这几个结构体的内容以及相互之间的关系。
v4l2_ctrl_handler:
struct v4l2_ctrl_handler {
struct mutex _lock;
struct mutex *lock;
struct list_head ctrls;//v4l2_ctrl链表,挂接着属于本handler的v4l2_ctrl
struct list_head ctrl_refs;//v4l2_ctrl_ref链表,挂接着属于本handler的v4l2_ctrl的v4l2_ctrl_ref
struct v4l2_ctrl_ref *cached;//上一次查询的v4l2_ctrl_ref
struct v4l2_ctrl_ref **buckets;//v4l2_ctrl_ref的哈希表
v4l2_ctrl_notify_fnc notify;
void *notify_priv;
u16 nr_of_buckets;//buckets数组中的元素总数
int error;
};
v4l2_ctrl:
struct v4l2_ctrl {
/* Administrative fields */
struct list_head node;//被挂接到所属的@v4l2_ctrl_hander.ctrls
struct list_head ev_subs;
struct v4l2_ctrl_handler *handler;//所属的v4l2_ctrl_handler
struct v4l2_ctrl **cluster;//在handler_new_ref函数中,但传入handler等于自身的handler时,被设置为v4l2_ctrl_ref.v4l2_ctrl的地址。更多情况看handler_new_ref函数
unsigned int ncontrols;//cluster数组的元素总数
unsigned int done:1;//Internal flag: set for each processed control.
//ctrl的一些属性
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;//ctrl对应的id
const char *name;//crtl对应的名字
enum v4l2_ctrl_type type;//ctrl类型
s64 minimum, maximum, default_value;
u32 elems;//dims数组中表示的n维数组的元素总数,表示ctrl相关的参数总数?
u32 elem_size;//ctrl的参数字节长度
u32 dims[V4L2_CTRL_MAX_DIMS];//数组中的元素为每个维度大小,表示的是每一维的参数总数?
u32 nr_of_dims;//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;//ctrl的新值的指针,当前值是一个int类型的数据时,指向val的地址
union v4l2_ctrl_ptr p_cur;//ctrl的当前值的指针,当前值是一个int类型的数据时,指向curr.val的地址
};
v4l2_ctrl_ref:
struct v4l2_ctrl_ref {
struct list_head node;//被挂载到v4l2_ctrl_handler.ctrl_refs
struct v4l2_ctrl_ref *next;//用于v4l2_ctrl_handler.buckets[]中的元素为表头的链表节点,以处理哈希表映射冲突。
struct v4l2_ctrl *ctrl;
struct v4l2_ctrl_helper *helper;
};
三者之间的关系:每一个v4l2_ctrl在添加到v4l2_ctrl_handler时会创建一个v4l2_ctrl_ref(v4l2_ctrl的指针被保存在v4l2_ctrl_ref中),v4l2_ctrl和v4l2_ctrl_ref都被分别挂接到v4l2_ctrl_handler中的两个链表 v4l2_ctrl和ctrl_refs,由于从链表中查找 v4l2_ctrl和v4l2_ctrl_ref需要进行遍历,为了提高查找效率,通过v4l2_ctrl的id映射,将v4l2_ctrl_ref映射到v4l2_ctrl_handler中的buckets列表中的链表进行保存,提高查找的效率。
之前直接干看handler和ctrl的底层源码,从底层的代码操作去思考和理解上层驱动中调用的需求,虽然认真思考后大概能够理解各种参数含义和上下层的整体代码逻辑,这种从下而上阅读源码的方式还是有一定局限性,比如在源码中看到对v4l2_ctrl结构体的成员cluster的相关代码时,虽然不难,但仍无法完整理解其真正意义,通过搜索代码引用找到cx25840-core.c的实例进行分析整体理解才比较清晰,所以在分析需要结合一些实例比较分析效率和效果更好。以下用cx25840-core.c中驱动程序的handle代码部分分析。
cx25840驱动代码:
//驱动相关的数据结构
struct cx25840_state {
struct i2c_client *c;
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;//关键,用来管理所有的v4l2_ctrl
struct {
/* volume cluster */
//这两个v4l2_ctrl属于同个簇
struct v4l2_ctrl *volume;//关键,音量相关的控件
struct v4l2_ctrl *mute;//关键,静音相关的控件
};
...
}
static int cx25840_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct cx25840_state *state;
...
v4l2_ctrl_handler_init(&state->hdl, 9);//看后面展开函数的具体实现
/*
v4l2_ctrl_new_std返回添加的v4l2_ctrl,为什么不用存储,不需在再访问?
如果v4l2_ctrl不被v4l2_ctrl的ops外访问,就不存储,因为ops通过传入参数就是v4l2_ctrl
*/
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);//看后面展开函数的具体实现
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_CONTRAST, 0, 127, 1, 64);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_SATURATION, 0, 127, 1, 64);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
...
/*
state->volume和state->mute在下面v4l2_ctrl_cluster函数中作为一个集合,保存在v4l2_ctrl.cluster中
在调用v4l2_ctrl_handler_setup时只执行集合中最先插入handler链表中的ctrl的ops,
volume mute分别是声量和静音控制,可见v4l2_ctrl.cluster中保存的是功能互斥的ctrl,
所以在v4l2_ctrl_handler_setup函数中只执行头一个的ctrl的s_ctrl。
*/
state->volume = v4l2_ctrl_new_std(&state->hdl,
&cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
0, 65535, 65535 / 100, default_volume);
state->mute = v4l2_ctrl_new_std(&state->hdl,
&cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
0, 1, 1, 0);
//相对上面新创建的ctrl,这里的ctrl是独立的,在下面调用v4l2_ctrl_handler_setup时,都会调用ctrl的ops.s_ctrl
v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
V4L2_CID_AUDIO_BALANCE,
0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
V4L2_CID_AUDIO_BASS,
0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
V4L2_CID_AUDIO_TREBLE,
0, 65535, 65535 / 100, 32768);
...
v4l2_ctrl_cluster(2, &state->volume);//看后面展开函数的具体实现
v4l2_ctrl_handler_setup(&state->hdl);//看后面展开函数的具体实现
下面单独分析上面程序中的重要语句中的函数执行过程:
v4l2_ctrl_handler_init(&state->hdl, 9)
#define v4l2_ctrl_handler_init(hdl, nr_of_controls_hint) \
v4l2_ctrl_handler_init_class(hdl, nr_of_controls_hint, NULL, NULL)
/* Initialize the handler */
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;
/*
主要初始化hdl->lock.dep_map的key数组,key每个元素对应class_cache的每个元素,
class_cache通过key映射到哈希表中,class_cache的本体来源于lock_classes数组。
这里跟锁相关的可以不用深究。
*/
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;
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;
}
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,V4L2_CID_BRIGHTNESS, 0, 255, 1, 128)
/* Helper function for standard non-menu controls*/
/*
非菜单控件添加,s64 min, s64 max, u64 step, s64 def这四个参数为驱动程序传入,
特定于驱动程序,而属性如类型名称标志是全局的。
v4l2_ctrl_new_std返回添加的v4l2_ctrl,如果v4l2_ctrl不被v4l2_ctrl的ops外访问,
就不存储,因为ops通过传入参数就是v4l2_ctrl
*/
/*
形参:hdl, ops, id, min, max, step, def
实参:&state->hdl, &cx25840_ctrl_ops,V4L2_CID_BRIGHTNESS, 0, 255, 1, 128
*/
struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s64 min, s64 max, u64 step, s64 def)
{
const char *name;
enum v4l2_ctrl_type type;//默认为V4L2_CTRL_TYPE_INTEGER,整数型参数的控件
u32 flags;
v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
if (type == V4L2_CTRL_TYPE_MENU ||
type == V4L2_CTRL_TYPE_INTEGER_MENU ||
type >= V4L2_CTRL_COMPOUND_TYPES) {
handler_set_err(hdl, -EINVAL);
return NULL;
}
return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
min, max, step, def, NULL, 0,
flags, NULL, NULL, NULL);
}
const char *v4l2_ctrl_get_name(u32 id)
{
switch (id) {
/* USER controls */
...
case V4L2_CID_BRIGHTNESS: return "Brightness";
...
}
}
/*
形参:id,
实参:V4L2_CID_BRIGHTNESS
*/
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_BRIGHTNESS:
...
/*
#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900)
#define V4L2_CID_BRIGHTNESS (V4L2_CID_BASE+0)
*/
*flags |= V4L2_CTRL_FLAG_SLIDER;//只设置控制标志位,#define V4L2_CTRL_FLAG_SLIDER 0x0020
}
}
/* Add a new control */
/*
实际输入参数:
hdl = &state->hdl
ops = &cx25840_ctrl_ops
type_ops = NULL
name = "Brightness"
type = V4L2_CTRL_TYPE_INTEGER
min = 0
max = 255
step = 1
def = 128
dims = NULL
elem_size = 0
flags = V4L2_CTRL_FLAG_SLIDER
qmenu = NULL
qmenu_int = NULL
priv = NULL
*/
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;
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;
}
tot_ctrl_size = elem_size * elems;
/* 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;
}
err = check_range(type, min, max, step, def);
if (err) {//不满足,跳过
handler_set_err(hdl, err);
return NULL;
}
if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {//不满足,跳过
handler_set_err(hdl, -ERANGE);
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后面多申请了sz_extra,只有在V4L2_CTRL_TYPE_INTEGER64
V4L2_CTRL_TYPE_STRING V4L2_CTRL_COMPOUND_TYPES类型时才会申请这
些空间用于存放较多的控件相关的参数
*/
ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
if (ctrl == NULL) {
handler_set_err(hdl, -ENOMEM);
return NULL;
}
//初始化ctrl的成员,如控件相关参数和操作集
INIT_LIST_HEAD(&ctrl->node);
INIT_LIST_HEAD(&ctrl->ev_subs);
ctrl->handler = hdl;
ctrl->ops = 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;//控件默认参数
ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING;//不是数组且为字符类型,说明字符串类型不是数组参数
ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string;//参数是数组或组合控件类型或字符串控件类型,控件会使用下面的data空间存储控件相关参数
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 = qmenu_int;
ctrl->priv = priv;
ctrl->cur.val = ctrl->val = def;
data = &ctrl[1];
/*
1、参数为非整形的控件:
即前面提到的V4L2_CTRL_TYPE_INTEGER64
V4L2_CTRL_TYPE_STRING V4L2_CTRL_COMPOUND_TYPES控件,会将
data作为参数的空间,并将新参数的地址和当前值参数的地址保
存在ctrl->p_new.p,ctrl->p_cur.p
2、参数为整形的控件:
直接使用ctrl->val和ctrl->cur.val作为参数空间,同样将新参数
的地址和当前值参数的地址保存在ctrl->p_new.p,ctrl->p_cur.p
通过将新参数的空间地址和当前参数的空间地址统一保存在ctrl->p_new.p,
ctrl->p_cur.p,这样能通过统一接口访问参数。
*/
if (!ctrl->is_int) {
ctrl->p_new.p = data;
ctrl->p_cur.p = data + tot_ctrl_size;
} else {
ctrl->p_new.p = &ctrl->val;
ctrl->p_cur.p = &ctrl->cur.val;
}
/*
根据数据类型,用ctrl->default_value填充ctrl->p_cur和ctrl->p_new地址处的所有elems元素
*/
for (idx = 0; idx < elems; idx++) {
ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
}
/*
为ctrl创建v4l2_ctrl_ref并添加到hdl,这个函数也很重要,下面继续分析这个函数
*/
if (handler_new_ref(hdl, ctrl)) {
kvfree(ctrl);
return NULL;
}
mutex_lock(hdl->lock);
list_add_tail(&ctrl->node, &hdl->ctrls);//将ctrl添加到hdl
mutex_unlock(hdl->lock);
return ctrl;
}
#define V4L2_CTRL_ID2WHICH(id) ((id) & 0x0fff0000UL)
#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900)
#define V4L2_CID_USER_BASE V4L2_CID_BASE
#define V4L2_CID_USER_CLASS (V4L2_CTRL_CLASS_USER | 1)
#define V4L2_CID_BRIGHTNESS (V4L2_CID_BASE+0)
/*
向hdl添加ctrl的引用。如果ctrl不是复合控件且id不是class并且在hdl的引用ctrl的哈希表中找不到
class的ctrl的引用,则将在添加引用前需要创建并添加class ctrl,这个class ctrl将添加到hdl的
ctrls表,其引用将添加到ctrl_refs所以ctrl_refs的引用表第一个为class的引用(而哈希表中则是id
大小排序且存在冲突情况,不一定第一个)
*/
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;
u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1;
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.
ctrl->type大于等于V4L2_CTRL_COMPOUND_TYPES为复合控件,
id == class_ctrl的情况,看上面的宏定义,V4L2_CID_USER_CLASS时满足,为类控件。
find_ref_lock查找handler之前是否注册过相同id的ctrl。
handler_new_ref函数,里面会递归调用v4l2_ctrl_new_std函数,进而
调用本函数形成递归,handler_new_ref和v4l2_ctrl_new_std的递归调用终止于id != class_ctrl这个条件,可回到前面的v4l2_ctrl_new_std函数看看,这里主要是为class_ctrl添加一个控件。
下面会分析find_ref_lock函数
*/
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)//前面v4l2_ctrl_new_std出错应该在前面返回了,这里什么情况下会出错?留意handler_set_err这些地方的影响
return hdl->error;
new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
if (!new_ref)
return handler_set_err(hdl, -ENOMEM);
new_ref->ctrl = ctrl;
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;//如果ctrl->handler != hdl不用处理,那ctrl->cluster的作用是什么?或者在其他地方处理了?
} //这里通过查找在本文件中查找cluster的赋值操作,找到v4l2_ctrl_cluster函数,可以到这个函数看说明情况。
//上面解释的是初始化的问题,关于这个如果使用,可以查考cx25840-core.c文件中的cx25840_probe函数中调用v4l2_ctrl_handler_setup函数
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.
按照id升序添加到链表
node2id(hdl->ctrl_refs.prev)是链表最后一个>ctrl_refs的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);//new_ref插入hdl->ctrl_refs链表
break;
}
insert_in_hash:
/* Insert the control node in the hash
new_ref添加到哈希表相应的冲突链表,可以看出这个冲突链表没有按d排序
*/
new_ref->next = hdl->buckets[bucket];
hdl->buckets[bucket] = new_ref;
unlock:
mutex_unlock(hdl->lock);
return 0;
}
/* Find a control with the given ID. Take the handler's lock first. */
static struct v4l2_ctrl_ref *find_ref_lock(
struct v4l2_ctrl_handler *hdl, u32 id)
{
struct v4l2_ctrl_ref *ref = NULL;
if (hdl) {
mutex_lock(hdl->lock);
ref = find_ref(hdl, id);
mutex_unlock(hdl->lock);
}
return ref;
}
/* Find a control with the given ID. */
static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
{
struct v4l2_ctrl_ref *ref;
int bucket;
id &= V4L2_CTRL_ID_MASK;
/* Old-style private controls need special handling */
if (id >= V4L2_CID_PRIVATE_BASE)
return find_private_ref(hdl, id);//这个可以不看
bucket = id % hdl->nr_of_buckets;//取余计算在哈希表的哪个链表中
/* Simple optimization: cache the last control found
上一次的查找结果保存在hdl->cached
*/
if (hdl->cached && hdl->cached->ctrl->id == id)
return hdl->cached;
/* Not in cache, search the hash */
ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
while (ref && ref->ctrl->id != id)//遍历链表查找
ref = ref->next;
if (ref)
hdl->cached = ref; /* cache it! */
return ref;
}
v4l2_ctrl_cluster(2, &state->volume)
/* Cluster controls */
/*在应用实例中了解函数的作用和参数的来源及如何使用
可以通过案例dtcs033.c dtcs033_init_controls函数中了解到,通过v4l2_ctrl_new_std
构建多个v4l2_ctrl保存在驱动数据结构体中的v4l2_ctrl *类型表中,表中的所有v4l2_ctrl属于同一簇
在将表地址作为本函数中的传入参数controls
*/
void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
{
bool has_volatiles = false;
int i;
/* The first control is the master control and it must not be NULL */
if (WARN_ON(ncontrols == 0 || controls[0] == NULL))
return;
for (i = 0; i < ncontrols; i++) {
//遍历表中的v4l2_ctrl,将v4l2_ctrl的簇表controls保存在v4l2_ctrl的cluster
if (controls[i]) {
controls[i]->cluster = controls;
controls[i]->ncontrols = ncontrols;
if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
has_volatiles = true;
}
}
controls[0]->has_volatiles = has_volatiles;
}
v4l2_ctrl_handler_setup(&state->hdl)
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;
}
/* Call s_ctrl for all controls owned by the handler */
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;
/* Skip if this control was already handled by a cluster. */
/* Skip button controls and read-only controls. */
if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
(ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
continue;
for (i = 0; i < master->ncontrols; i++) {
if (master->cluster[i]) {
cur_to_new(master->cluster[i]);//把控件输入的当前值作为控制的新值
master->cluster[i]->is_new = 1;
master->cluster[i]->done = true;//hdl中存在的所有的ctrl,有些地址会存于->cluster数组中,
} //数组的集中在这里处理后需要做标记,免得在外面循环重复处理
}
/*
调用ctrl->l2_ctrl_ops->s_ctrl,一个cluster中的ctrl只调用一次ctrl,根据上面遍历顺序和&hdl->ctrls插入顺序,那这个ctrl是cluster最先插入&hdl->ctrls,
那与cluster中后插入&hdl->ctrls中的ctrl与头一个插入的ctrl是什么关系?从属或父子关系?
先回到代码本身进行思考,代码中只执行cluster中的一个ctrl的s_ctrl,而s_ctrl是设置物理设备的参数,那为什么只能设置一个ctrl的参数,
是不是说明这个ctrl的参数会影响到同一个cluster的其他ctrl的参数设置?
关于这个问题,找到cx25840-core.c实例分析,被保存到cluster的ctrl,为音量ctrl和静音ctrl,功能和参数上具有互斥性,所以只执行一个ctrl的s_ctrl,而且
这个ctrl是最先插入hdl->ctrls的ctrl,所以可以把默认功能的ctrl最先插入。
再进一步看s_ctrl的功能,应该是把ctrl里面的参数设置到实际物理设备中,这样就保持了数据结构和物理设备的数据一致性,
这个在v4l2_ctrl_handler_setup函数的头文件中也有说明。
*/
ret = call_op(master, s_ctrl);//调用注册的osp的s_ctrl,如下cx25840_ctrl_ops
if (ret)
break;
}
return ret;
}
static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
.s_ctrl = cx25840_s_ctrl,
};
static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
cx25840_write(client, 0x414, ctrl->val - 128);//最终写i2c寄存器
break;
case V4L2_CID_CONTRAST:
cx25840_write(client, 0x415, ctrl->val << 1);
break;
case V4L2_CID_SATURATION:
if (is_cx23888(state)) {
cx25840_write(client, 0x418, ctrl->val << 1);
cx25840_write(client, 0x419, ctrl->val << 1);
} else {
cx25840_write(client, 0x420, ctrl->val << 1);
cx25840_write(client, 0x421, ctrl->val << 1);
}
break;
case V4L2_CID_HUE:
if (is_cx23888(state))
cx25840_write(client, 0x41a, ctrl->val);
else
cx25840_write(client, 0x422, ctrl->val);
break;
default:
return -EINVAL;
}
return 0;
}
int cx25840_write(struct i2c_client *client, u16 addr, u8 value)
{
u8 buffer[3];
buffer[0] = addr >> 8;
buffer[1] = addr & 0xff;
buffer[2] = value;
return i2c_master_send(client, buffer, 3);
}