一种常见类型的控件集群是处理“自动foo/foo”类型的控件。典型的例子是自动增益(autogain)/增益(gain),自动曝光(autoexposure)/曝光(exposure),自动白平衡(autowhitebalance)/红色平衡(red balance)/蓝色平衡(blue balance)。在所有情况下,您都有一个控件,该控件确定另一个控件是由硬件自动处理还是由用户手动控制。
如果聚类处于自动模式,则应将手动控件标记为非活动和易失性。当读取易失性控件时,g_volatile_ctrl操作应返回硬件自动模式设置的值。如果将聚类置于手动模式,则手动控件应再次变为活动状态,并清除易失性标志(因此在手动模式下不再调用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相同。第三个参数告诉框架哪个值将聚类切换到手动模式。最后一个参数将可选地为非自动控件设置V4L2_CTRL_FLAG_VOLATILE。如果为假,则手动控件永远不是易失性的。如果硬件不允许您读取由自动模式确定的当前值(例如,如果autogain已开启,则硬件不允许您获取当前增益值),则通常会使用它。
聚类的第一个控件被认为是“自动”控件。
使用此函数将确保您无需处理所有复杂的标志和易失性处理。
2.2.13.11 VIDIOC_LOG_STATUS Support
这个ioctl允许你将驱动程序的当前状态转储到内核日志中。v4l2_ctrl_handler_log_status(ctrl_handler,prefix)可用于将给定处理程序所拥有的控件的值转储到日志中。你也可以提供一个前缀。如果前缀没有以空格结尾,则会为你添加': '。
2.2.13.12 Different Handlers for Different Video Nodes
通常,V4L2驱动程序只有一个控制处理程序,它是所有视频节点的全局控制处理程序。但是,你也可以为不同的视频节点指定不同的控制处理程序。你可以通过手动设置结构体video_device的ctrl_handler字段来实现。如果没有涉及到子设备,则不会有问题,但是如果有子设备,则需要阻止子设备控制自动合并到全局控制处理程序。你可以通过简单地在struct v4l2_device中设置ctrl_handler字段为NULL来实现此操作。此后,v4l2_device_register_subdev()将不再合并子设备控件。
添加每个子设备后,你需要调用v4l2_ctrl_add_handler手动将子设备的控制处理程序(sd->ctrl_handler)添加到所需的控制处理程序中。该控制处理程序可以是特定于video_device或video_device子集的。例如:收音机设备节点仅具有音频控件,而视频和vbi设备节点共享用于音频和视频控件的相同控制处理程序。
如果你想让一个处理程序(例如一个收音机设备节点)拥有另一个处理程序(例如一个视频设备节点)的子集,那么你应该先将控件添加到第一个处理程序中,将其他控件添加到第二个处理程序中,最后将第一个处理程序添加到第二个处理程序中。例如:
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, NULL);
v4l2_ctrl_add_handler()的最后一个参数是一个筛选函数,允许你筛选将被添加的控件。如果你想添加所有控件,请将其设置为NULL。或者,你可以将特定的控件添加到处理程序中:
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_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
这是不好的,因为静音收音机不会改变视频静音控制。规则是每个硬件旋钮都应该有一个控件。
2.2.13.13 Finding Controls
通常,你自己创建了控件,并可以将struct v4l2_ctrl指针存储到自己的结构中。但有时你需要从另一个未拥有的处理程序中查找控件。例如,如果你需要从子设备中查找音量控制。
你可以通过调用v4l2_ctrl_find来实现:
struct v4l2_ctrl *volume;
volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
由于v4l2_ctrl_find将锁定处理程序,因此你必须小心在哪里使用它。例如,下面这种做法是不好的:
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, ...);
…and in video_ops.s_ctrl:
case V4L2_CID_BRIGHTNESS:
contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
...
当框架调用s_ctrl时,ctrl_handler.lock已经被占用,因此尝试从相同的处理程序中查找另一个控件将导致死锁。
因此,不建议在控制操作内部使用此函数。
2.2.13.14 Preventing Controls inheritance
使用v4l2_ctrl_add_handler将一个控制处理程序添加到另一个处理程序时,默认情况下,一个处理程序的所有控件会合并到另一个处理程序中。但是,一个子设备可能具有在某些高级嵌入式系统中有用,但在消费级硬件中使用时并不合适的低级控件。在这种情况下,你想将这些低级别的控件保留在子设备本地。你可以通过简单地将控件的“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时,这些控件将被跳过。
2.2.13.15 V4L2_CTRL_TYPE_CTRL_CLASS Controls
这种类型的控件可以被GUI用于获取控制类的名称。一个完整功能的GUI可以创建一个具有多个选项卡的对话框,每个选项卡包含属于特定控制类的控件。每个选项卡的名称可以通过查询特殊ID <control class | 1> 的控件来获得。
驱动程序不必关心此事。只要添加属于新控制类的第一个控件,框架将自动添加此类型的控件。
2.2.13.16 Adding Notify Callbacks
有时,平台或桥接驱动程序需要在子设备驱动程序的控件发生更改时进行通知。你可以通过调用此函数来设置一个通知回调函数:
void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl,
void (*notify)(struct v4l2_ctrl *ctrl, void *priv), void *priv);
每当给定控件更改值时,通知回调函数将被调用,并提供指向控件和通过v4l2_ctrl_notify传递的私有指针。请注意,在调用通知函数时,控制处理程序锁被持有。
每个控制处理程序只能有一个通知函数。任何尝试设置另一个通知函数都将导致WARN_ON警告。