v4l2-controls

介绍

V4L2 control API似乎很简单,然而正确实现地驱动程序很快就变得很难。因为大部分需要处理control的代码实际上是不特定的驱动和可移动的V4L核心框架。

毕竟,驱动程序开发人员感兴趣的惟一部分是:

1)如何添加控件?
2)如何设置控件的值?(即s_ctrl)

偶尔:

3)如何获得控件的值?(即g_volatile_ctrl)
4)我如何验证用户建议的控制值?(即try_ctrl)

其余的都是可以集中完成的。

控制框架的产生是为了实现在一个中央位置相对于控件的V4L2规范所有的规则。让驱动程序开发者尽可能简单。

注意控制框架依赖于代表v4l2驱动的v4l2_device结构体和代表子设备驱动的v4l2_subdev结构体。


框架中的对象

有两个主要对象:

v4l2_ctrl对象描述的控制特性和跟踪控制的值(包括最近值和新值)。

v4l2_ctrl_handler是跟踪控制的对象。它拥有一个v4l2_ctrl列表。这些v4l2_ctrl有一个可能被其他程序使用的控制列表。


在V4L2和子设备驱动程序的基本用法

1)准备:

1.1)将处理程序添加到驱动程序的顶层结构中:


struct foo_dev {
    ...
    struct v4l2_ctrl_handler ctrl_handler;
    ...
};

struct foo_dev *foo;

1.2)初始化处理程序:

v4l2_ctrl_handler_init(& foo -> ctrl_handler,nr_of_controls);

第二个参数是一个提示,告诉函数这个处理程序需要处理多少控件。基于此信息它将分配一个哈希表。这只是一个暗示。

1.3)将控制处理程序钩子连接到驱动程序中:

1.3.1)对于V4L2驱动这样做:

struct foo_dev {
    ...
    struct v4l2_device v4l2_dev;
    ...
    struct v4l2_ctrl_handler ctrl_handler;
    ...
};

foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;

foo -> v4l2_dev是v4l2_device结构体类型。

最后,从你的v4l2_ioctl_ops移除所有控制功能(如果有):
vidioc_queryctrl,vidioc_query_ext_ctrl,vidioc_querymenu,vidioc_g_ctrl,
vidioc_s_ctrl,vidioc_g_ext_ctrls,vidioc_try_ext_ctrls和vidioc_s_ext_ctrls。
现在不再需要这些了。

1.3.2)sub-device驱动这样处理:

struct foo_dev {
    ...
    struct v4l2_subdev sd;
    ...
    struct v4l2_ctrl_handler ctrl_handler;
    ...
};

foo->sd.ctrl_handler = &foo->ctrl_handler;

foo -> SD是v4l2_subdev结构体类型。

v4l2_subdev_core_ops中所有的核心控制行动如下:

       .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
        .g_ctrl = v4l2_subdev_g_ctrl,
        .s_ctrl = v4l2_subdev_s_ctrl,
        .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
        .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
        .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,

注意:这只是一个临时解决方案。一旦所有依靠subdev的V4L2驱动变为控制框架,这些将不再需要。

1.4)在结束时清理处理程序:

v4l2_ctrl_handler_free(& foo -> ctrl_handler);

2) 添加controls:

通过调用v4l2_ctrl_new_std添加非菜单控件:

struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
    const struct v4l2_ctrl_ops *ops,
    u32 id, s32 min, s32 max, u32 step, s32 def);

通过调用v4l2_ctrl_new_std_menu添加菜单控件:

struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
    const struct v4l2_ctrl_ops *ops,
    u32 id, s32 max, s32 skip_mask, s32 def);

这些功能通常是在v4l2_ctrl_handler_init执行后调用:


v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
        V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
        V4L2_CID_CONTRAST, 0, 255, 1, 128);
v4l2_ctrl_new_std_menu(&foo->ctrl_handler, &foo_ctrl_ops,
        V4L2_CID_POWER_LINE_FREQUENCY,
        V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
        V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
...
if (foo->ctrl_handler.error) {
    int err = foo->ctrl_handler.error;

    v4l2_ctrl_handler_free(&foo->ctrl_handler);
    return err;
}

v4l2_ctrl_new_std函数返回指向新的控制v4l2_ctrl指针,但是如果你不需在control ops外访问这个指针,那么就没有必要存储。

v4l2_ctrl_new_std功能将在大多数领域基于control ID,除了min,max,步骤和默认值。这些在过去四个参数中传递。这些值是特定于驱动程序的,而控制属性(如类型、名称、标志)都是全局的。控件的当前值将设置为默认值。

v4l2_ctrl_new_std_menu函数很相似但它是用于menu controls.由于menu controls件总是0,所以没有最小参数。相反,有一个掩码位参数:如果位是1,然后菜单跳过该项。

注意,如果某个函数失败,函数将返回null或错误并把ctrl_handler->error设置为错误代码。如果ctrl_handler->error已经设置,然后它只会返回,什么也不做。这同样适用于v4l2_ctrl_handler_init,如果它不能分配的内部数据结构。

这使得初始化处理程序很容易,只需添加所有控件并只检查结束时的错误代码。可以节省大量重复性错误检查。

建议在升序control ID顺序中添加控件:它那样快一点。

3)可选地强制初始control 设置:

v4l2_ctrl_handler_setup(& foo -> ctrl_handler);

这将会为所有的controls 无条件调用s_ctrl。实际上这将硬件初始化为默认的控制值。建议这样做,确保内部数据结构和硬件同步。

4)最后:实现v4l2_ctrl_ops

static const struct v4l2_ctrl_ops foo_ctrl_ops = {
    .s_ctrl = foo_s_ctrl,
};

通常,所有你需要的是s_ctrl:

静态变量foo_s_ctrl(struct v4l2_ctrl * Ctrl)
{
struct foo *状态= container_of(Ctrl ->处理器,struct foo,ctrl_handler);

开关(Ctrl - id){
案例v4l2_cid_brightness:

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_BRIGHTNESS:
        write_reg(0x123, ctrl->val);
            break;
    case V4L2_CID_CONTRAST:
        write_reg(0x456, ctrl->val);
            break;
    }
    return 0;
}

control ops被v4l2_ctrl指针作为参数调用。新的控制值已经被生效,所以您需要做的是
实际更新硬件寄存器。

完成!这对我们大多数的司机来说已经足够了。不需要做任何控制值的确认,或实现QUERYCTRL/QUERYMENU。G/S_CTRL、G/TRY/S_EXT_CTRLS 会自动支持。

你可能感兴趣的:(Linux,Driver,V4L2)