2.2.13.4 Inheriting Sub-device Controls

当通过调用v4l2_device_register_subdev()向V4L2驱动程序注册子设备并设置v4l2_subdev和v4l2_device的ctrl_handler字段时,子设备的控件将自动在V4L2驱动程序中可用。如果子设备驱动程序包含已存在于V4L2驱动程序中的控件,则会跳过这些控件(因此V4L2驱动程序始终可以覆盖子设备控件)。
这里发生的是,v4l2_device_register_subdev()调用v4l2_ctrl_add_handler(),将子设备的控件添加到v4l2_device的控件中。

2.2.13.5 Accessing Control Values
以下联合体在控件框架内用于访问控件值:

union v4l2_ctrl_ptr {
s32 *p_s32;
s64 *p_s64;
char *p_char;
void *p;
};

v4l2_ctrl结构包含这些字段,可以用于访问当前值和新值:

s32 val;
struct {
s32 val;
} cur;
union v4l2_ctrl_ptr p_new;
union v4l2_ctrl_ptr p_cur;

If the control has a simple s32 type, then:

&ctrl->val == ctrl->p_new.p_s32
&ctrl->cur.val == ctrl->p_cur.p_s32

对于其他类型,请使用ctrl->p_cur.p<something>。基本上,val和cur.val字段可以视为别名,因为它们经常被使用。
在控制操作中,您可以自由地使用它们。二者都很容易理解。p_char指针指向长度为ctrl-> maximum + 1的字符缓冲区,并且始终以0结尾。
除非控件标记为volatile,否则p_cur字段指向当前缓存的控件值。当您创建新控件时,该值变为与默认值相同。调用v4l2_ctrl_handler_setup()后,将该值传递给硬件。通常最好调用此函数。
每当设置新值时,新值都会自动缓存。这意味着大多数驱动程序不需要实现g_volatile_ctrl()操作。例外情况是对于返回易失寄存器(例如连续变化的信号强度读数)的控件,您需要像这样实现g_volatile_ctrl:

static int foo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->val = read_reg(0x123);
break;
}
}

请注意,在g_volatile_ctrl中也使用了“new value”联合体。通常需要实现g_volatile_ctrl的控件是只读控件。如果它们不是,将不会在控件更改时生成V4L2_EVENT_CTRL_CH_VALUE。
要将控件标记为易失性,必须设置V4L2_CTRL_FLAG_VOLATILE:

ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;

对于try/s_ctrl,新值(即由用户传递的值)会被填充,您可以在try_ctrl中修改这些值或在s_ctrl中设置它们。 “cur”联合体包含当前值,您也可以使用(但无法更改)。
如果s_ctrl返回0(OK),则控件框架将把新的最终值复制到“cur”联合体中。
在g_volatile / s / try_ctrl中,您可以访问由同一处理程序拥有的所有控件的值,因为持有处理程序的锁定。如果您需要访问其他处理程序拥有的控件的值,则必须非常小心,以免引入死锁。
在控制操作之外,您需要通过助手函数来安全地获取或设置驱动程序中单个控件的值:

s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);

这些函数与VIDIOC_G/S_CTRL ioctls一样通过控件框架进行操作。但是,请不要在g_volatile / s / try_ctrl内使用这些函数,否则将导致死锁,因为这些助手程序也会锁定处理程序。
您还可以自己获取处理程序锁:

mutex_lock(&state->ctrl_handler.lock);
pr_info("String value is '%s'\n", ctrl1->p_cur.p_char);
pr_info("Integer value is '%s'\n", ctrl2->cur.val);
mutex_unlock(&state->ctrl_handler.lock);

2.2.13.6 Menu Controls
The v4l2_ctrl struct contains this union:

union {
u32 step;
u32 menu_skip_mask;
};

对于菜单控件,使用menu_skip_mask。它允许您轻松地排除某些菜单项。这在VIDIOC_QUERYMENU实现中使用,您可以返回-EINVAL,如果某个菜单项不存在。请注意,VIDIOC_QUERYCTRL始终为菜单控件返回步骤值1。
一个好的例子是MPEG音频层II比特率菜单控件,其中菜单是标准化可能比特率的列表。但是在实践中,硬件实现只支持其中的一个子集。通过设置skip mask,您可以告诉框架应跳过哪些菜单项。将其设置为0表示支持所有菜单项。
您可以通过v4l2_ctrl_config结构为自定义控件设置此掩码,或者通过调用v4l2_ctrl_new_std_menu()设置掩码。

2.2.13.7 Custom Controls
可以使用v4l2_ctrl_new_custom()创建特定于驱动程序的控件:

static const struct v4l2_ctrl_config ctrl_filter = {
.ops = &ctrl_custom_ops,
.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
.name = "Spatial Filter",
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_SLIDER,
.max = 15,
.step = 1,
};
ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);

最后一个参数是priv指针,可以设置为特定于驱动程序的私有数据。v4l2_ctrl_config结构体还有一个字段用于设置is_private标志。
如果未设置名称字段,则框架将假定这是一个标准控件,并将相应地填充名称、类型和标志字段。

2.2.13.8 Active and Grabbed Controls
如果控件之间存在更复杂的关系,您可能需要激活和停用控件。例如,如果启用了Chroma AGC控件,则Chroma增益控件无效。也就是说,您可以设置它,但只要自动增益控制处于开启状态,硬件将不会使用其值。通常,用户界面可以禁用这样的输入字段。
您可以使用v4l2_ctrl_activate()设置“active”状态。默认情况下,所有控件都是活动的。请注意,框架不会检查此标志。它纯粹是为了GUI而设计的。该函数通常从s_ctrl内调用。
另一个标志是“grabbed”标志。“grabbed”控件意味着您无法更改它,因为它被某些资源使用。典型的示例是MPEG比特率控件,在捕获正在进行时无法更改。
如果使用v4l2_ctrl_grab()将控件设置为“已抓取”,则如果尝试设置此控件,框架将返回-EBUSY。v4l2_ctrl_grab()函数通常从驱动程序在开始或停止流传输时调用。

2.2.13.9 Control Clusters
默认情况下,所有控件都与其他控件无关。但在更复杂的情况下,您可以从一个控件到另一个控件获取依赖关系。在这种情况下,您需要将它们“聚类”:

struct foo {
struct v4l2_ctrl_handler ctrl_handler;
#define AUDIO_CL_VOLUME (0)
#define AUDIO_CL_MUTE (1)
struct v4l2_ctrl *audio_cluster[2];
...
};

state->audio_cluster[AUDIO_CL_VOLUME] =
v4l2_ctrl_new_std(&state->ctrl_handler, ...);
state->audio_cluster[AUDIO_CL_MUTE] =
v4l2_ctrl_new_std(&state->ctrl_handler, ...);
v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);

从现在开始,只要设置(或“获取”或“尝试”)属于同一聚类的一个或多个控件,就只会调用第一个控件(在此示例中为“volume”)的控件操作。实际上,您创建了一个新的复合控件。类似于C语言中的“结构体”的工作方式。
因此,当使用V4L2_CID_AUDIO_VOLUME作为参数调用s_ctrl时,您应设置属于audio_cluster的所有两个控件:

static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME: {
struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
write_reg(0x123, mute->val ? 0 : ctrl->val);
break;
}
case V4L2_CID_CONTRAST:
write_reg(0x456, ctrl->val);
break;
}
return 0;
}

在上面的示例中,以下内容在VOLUME情况下是等效的:

ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]

在实践中,使用此类聚类数组变得非常繁琐。因此,改用以下等效方法:

struct {
/* audio cluster */
struct v4l2_ctrl *volume;
struct v4l2_ctrl *mute;
};

匿名结构用于清楚地“聚类”这两个控件指针,但它没有其他用途。其效果与创建具有两个控件指针的数组相同。因此,您可以只需执行以下操作:

state->volume = v4l2_ctrl_new_std(&state->ctrl_handler, ...);
state->mute = v4l2_ctrl_new_std(&state->ctrl_handler, ...);
v4l2_ctrl_cluster(2, &state->volume);

在foo_s_ctrl中,您可以直接使用这些指针:state->mute->val。
请注意,聚类中的控件可能为NULL。例如,如果由于硬件不支持该特定功能而从未添加静音(mute),则静音(mute)将为NULL。
因此,在这种情况下,我们有一个由2个控件组成的聚类,其中只有1个实例化。唯一的限制是聚类的第一个控件必须始终存在,因为那是该聚类的“主”控件。主控件是标识聚类并提供用于该聚类的v4l2_ctrl_ops结构指针的控件。
显然,聚类数组中的所有控件都必须初始化为有效控件或NULL。
在极少数情况下,您可能想知道集群中实际上是由用户明确设置的哪些控件。为此,可以检查每个控件的“is_new”标志。例如,在音量/静音(mute)聚类的情况下,如果用户仅针对静音(mute)调用VIDIOC_S_CTRL,则静音(mute)控件的“is_new”标志将被设置。如果用户分别调用了静音(mute)和音量控件的VIDIOC_S_EXT_CTRLS,则两个控件的“is_new”标志均为1。
当从v4l2_ctrl_handler_setup()中调用时,“is_new”标记始终为1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值