您的位置:首页 > 移动开发 > IOS开发

controls for advanced topics and scenarios

2018-03-19 15:02 429 查看

Controls继承

当sub-device通过调用v4l2_device_register_subdev()注册时,v4l2_device 和v4l2_subdev的ctrl_handler字段都会设置,然后V4L2驱动程序中subdev的controls 也会有效。如果subdev驱动

包含在V4L2驱动已存在的控制,那这些会跳过(所以V4L2驱动也可以重写一个subdev控制)。

v4l2_device_register_subdev()调用v4l2_ctrl_add_handler()添加subdev的controls到v4l2_device。

访问Control 值

v4l2_ctrl结构包含这两个联合体:

/* The current control value. */
union {
s32 val;
s64 val64;
char *string;
} cur;

/* The new control value. */
union {
s32 val;
s64 val64;
char *string;
};


你可以自由使用这些control ops。字符串指针指向长度为ctrl->maximum + 1的字符缓冲区,总是’\0’为结束符。

在大多数情况下,’cur’包含当前缓存的控制值。当你创建一个新control 该值与default 值相同。在调用v4l2_ctrl_handler_setup()后这个值提交到硬件。这是一般调用这个函数的方法。

每当设置新值时,新值将自动缓存。这意味着,大多数驱动不需要实现的g_volatile_ctrl()。

controls的异常是返回一个volatile register,例如连续变化的信号强度读数。在这种情况下,你将需要实现这样的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 controls是只读controls。

标志control为volatile,你要设置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),然后control framework将复制新的最终值的’cur’联合体。

而在g_volatile / S / try_ctrl可以访问相同handler 的所有控件,一旦handler的锁生效 。如果您需要访问其他handlers拥有的controls 的值,那么您必须非常小心以免引入死锁。

在control ops外,您必须通过帮助函数在驱动程序中安全地获取或设置一个单独的control 值:

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一样的control framework执行。不要使用这些内部control ops g_volatile/s/try_ctrl,因为helpers锁住handler将导致死锁。

还可以自己处理handler :

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


菜单Controls

v4l2_ctrl结构体包含这个联合体:

union {
u32 step;
u32 menu_skip_mask;
};


菜单Controls经常使用menu_skip_mask。它允许你轻松排除某些菜单项。这是用在

VIDIOC_QUERYMENU 实现,可以返回-EINVAL如果某菜单项是不存在的。注意,对于菜单controls VIDIOC_QUERYCTRL 总是返回一个1。

一个很好的例子是在MPEG音频第二层码率菜单control, 菜单是一个标准化的可能的比特率。但实际上是硬件实现只支持其中的一个子集。通过设置跳过掩码,您可以告诉框架应该跳过哪些菜单项。将其设置为0意味着支持所有菜单项。

也可以通过custom control的v4l2_ctrl_config结构体掩盖,或者调用v4l2_ctrl_new_std_menu()。

Custom Controls

驱动程序特定的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);


最后一个参数是一个可以设置为驱动程序特定的私有数据的指针。

v4l2_ctrl_config结构体也有一个字段用来设置is_private标志。

如果未设置名称字段,则框架将假定这是一个标准控件,并将相应地填入名称、类型和标志字段。

主动抓取Controls

如果Controls之间的关系更复杂,则可能需要激活和停用Controls。例如,如果色度AGC控制开启,则色度增益控制处于非活动状态。也就是说,您可以设置它,但只要自动增益控制在,硬件就不会使用该值。通常,用户界面可以禁用此类输入字段。

你可以使用v4l2_ctrl_activate()设置“主动”状态。默认情况下,所有Controls都是活动的。注意,框架不检查此标志。它是纯粹的图形用户界面。该功能通常称为内s_ctrl。

另一面标志是’grabbed’标志。一个被抓住的Controls意味着你不能更改它,因为某些资源正在使用它。典型的例子是MPEG在捕获过程中不能更改比特率控制。

如果Controls使用v4l2_ctrl_grab()设置为 ‘grabbed’ ,如果尝试设置该控制那么框架将返回

-EBUSY。当开始或停止streaming,v4l2_ctrl_grab()函数通常被驱动调用。

Control集群

默认情况下,所有controls 都与其他controls 无关。但在更复杂的场景中,一个controls 依赖另一个controls 。

在这种情况下,您需要“集群”它们:

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


从现在开始,当一个或多个属于同一个群集的controls 被设置 (or ‘gotten’, or ‘tried’),只有第一个control ops(’volume’在这个例子中)被调用。这样有效地创建一个新的复合control。类似于

‘struct’在C的工作方式

所以当s_ctrl使用v4l2_cid_audio_volume作为参数,你应该设置两个属于audio_cluster的controls :

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


在上面的例子等价于下面的内容:

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


匿名结构体用于清楚地’cluster’这两个control 指针,但它没有其他用途。效果与创建带有两个控制指针的数组相同。所以你可以做:

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。

注意,集群中的controls 可能为null。例如,如果由于某种原因没有添加静音(因为硬件不支持该特定功能),那么静音将为null。因此,在这种情况下,我们有2个controls 的集群,其中只有1个是实际实例化的。唯一的限制是必须始终存在集群的第一个controls ,因为这是集群的“主”controls 。主controls 识别集群,对v4l2_ctrl_ops结构体用于集群提供了指针。

显然,集群数组中的所有control必须初始化为有效control或null。

在少数情况下,您可能想知道用户实际上明确地确定了集群的哪些controls 。这个你可以检查每个control的’is_new’标志。例如,在一个音量/静音集群的情况下,如果用户调用VIDIOC_S_CTRL 静音,静音control ‘is_new’标志 将设置。如果用户为静音和音量controls调用VIDIOC_S_EXT_CTRLS ,那么两者’is_new’ 标志都为1。

当调用v4l2_ctrl_handler_setup()时,’is_new’标志始终是1。

处理自动增益/增益自动集群式Controls

一种常见的control 集群类型是处理“自动”类型的control 集群控制。典型的例子是自动增益、增益、自动曝光、曝光,自动白色 /红色/蓝色平衡平衡。在所有情况下,都有一个control 。它决定了另一个control 是由硬件自动处理的,还是由用户手动控制的。

如果群集处于自动模式,则手动control 应标记为inactive和volatile。当volatile controls读取g_volatile_ctrl操作应该返回到硬件的自动模式自动设置的值。

如果集群处于手动模式,则手动control 应该成为active ,volatile 标志被清除(所g_volatile_ctrl不再称为在手动模式)。另外,在切换到手动模式之前,自动模式所确定的当前值被复制为新的手动值。

最后,V4L2_CTRL_FLAG_UPDATE 应设置为自动控制由于改变控制影响手动控制的控制标志。

为了简化,引进了一种v4l2_ctrl_cluster的变体:

void v4l2_ctrl_auto_cluster(unsigned ncontrols,
struct v4l2_ctrl **controls,u8 manual_val, bool set_volatile);


前两个参数跟v4l2_ctrl_cluster相同。第三个参数告诉framework 将集群切换为手动模式的值。最后一个参数可以为非自动控制设置V4L2_CTRL_FLAG_VOLATILE 。如果它是错的,那么手动控制永远不会生效。如果硬件不给你通过自动方式的(如自动增益是,硬件不允许你获得的电流增益值)写回值选项,你通常就会使用这个选项。

集群的第一个control 被认为是“自动”control。

使用这个函数可以确保不需要处理所有复杂的标志和volatile 处理。

vidioc_log_status支持

这允许你dump ioctl驱动程序当前状态的内核日志。

v4l2_ctrl_handler_log_status(ctrl_handler, prefix) 可以用来dump

给定处理程序对controls 的值到日志。你可以提供一个

前缀。如果前缀没有以空格结尾,则会为你添加“:”。

不同的视频节点的不同处理程序

通常,V4L2驱动只有一个对所有视频节点的全局control handler。但也可以为不同的视频节点指定不同的控件处理程序。你可以通过手动设置video_device ctrl_handler场的结构。

如果没有subdevs是没有问题的。但如果有,那么你需要块自动合并subdev controls到control handler。只需要简单的设置v4l2_device结构体中ctrl_handler字段为NULL。现在v4l2_device_register_subdev()不再合并subdev controls。

每一次添加subdev后,你将不得不调用v4l2_ctrl_add_handler手动添加subdev control handler(SD—> ctrl_handler)所需的control handler。control handler可以具体到video_device或video_device的子集。例如:无线电设备节点只有音频的controls,而视频和VBI设备节点共享相同的对音频和视频controls的control handler。

如果一个handler(例如对于无线电设备节点)有另一个handler(例如对于视频设备节点)的子集,那么您应该首先添加controls到第一个handler,将第二个handler的其他controls,最后将第一个handler添加到第二个中。例如:

v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);


或者您可以向handler添加特定的controls :

volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);


您不应该为两个handlers生成两个相同的controls 。

例如:

v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);


一旦静音收音机不会改变视频静音control,就要有一个control 每个硬件可以旋转的旋钮。

查找Controls

通常,自己创建控件后,并且可以存储v4l2_ctrl结构体指针到你自己的结构。

但有时需要从不属于自己的另一个handler 中找到一个control 。例如,如果你要从一个subdev找到音量control 。

你可以通过调用v4l2_ctrl_find来查找:

struct v4l2_ctrl *volume;

volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);

Since v4l2_ctrl_find will lock the handler you have to be careful where you use it. For example, this is not a good idea:

struct v4l2_ctrl_handler ctrl_handler;

v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);


在video_ops.s_ctrl:

case V4L2_CID_BRIGHTNESS:
contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
...


当s_ctrl被framework调用,ctrl_handler.lock已经被设置,因此试图从相同的handler 序,找到另一个control 将死锁。

建议不要在control ops内使用此功能。

继承的Controls

当一个control handler添加到另一个使用v4l2_ctrl_add_handler时,然后从所有control默认都合并到另一个。但subdev可能有低层次的controls ,特别但当它被用在消费级硬件,例如一些先进的嵌入式系统。在这种情况下保持subdev中的那些低级controls ,你可以通过设置’is_private’标志为1:

static const struct v4l2_ctrl_config ctrl_private = {
.ops = &ctrl_custom_ops,
.id = V4L2_CID_...,
.name = "Some Private Control",
.type = V4L2_CTRL_TYPE_INTEGER,
.max = 15,
.step = 1,
.is_private = 1,
};

ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_private, NULL);


v4l2_ctrl_add_handler被调用时,这些controls 将被跳过。

V4L2_CTRL_TYPE_CTRL_CLASS Controls

这种类型的Controls 可以使用图形用户界面来获得control class的名称。

功能齐全的GUI有一个对话框,每个选项卡包含属于特定control class的多个control 。

每个选项卡的名称可以通过查询一个带有ID < control class | 1 >的特殊control 找到。

驱动不必关心这件事。每当添加属于新control class的第一个控件时,framework 将自动添加一个control 。

扩展建议

对将来规范的扩展的一些想法:

1)增加V4L2_CTRL_FLAG_HEX 使用十六进制而不是十进制值显示。例如有用video_mute_yuv。

2)增加control ID的以为,使得在controls 数组中可以标记成功写入和失败的controls 。

尽管如此,我不确定这是否值得。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: