Chinaunix首页 | 论坛 | 博客
  • 博客访问: 134253
  • 博文数量: 20
  • 博客积分: 266
  • 博客等级: 二等列兵
  • 技术积分: 317
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-28 14:15
文章存档

2013年(4)

2012年(16)

我的朋友

分类: LINUX

2012-12-08 12:58:06

5 struct v4l2_subdev

v4l2_device下面一个层次是v4l2_subdev,它需要和它的子设备进行通信,如果说camera host是一个v4l2_device设备,那么就可以将camera模组称为一个v4l2_subdev设备,它们之间的通信可以采取多种方式常见的有I2CSPI.

通常这些子设备都属于I2C设备,当然也有其他接口的比如SPI Interface,为了给驱动程序提供一个一致性的接口,Linux内核为这些设备抽象出了一个struct v4l2_subdev结构来描述一个v4l2 sub device,所以每个v4l2 sub-device都需要一个struct v4l2_subdev结构实例,该结构的定义如下:

include/media/v4l2-subdev.h

点击(此处)折叠或打开

  1. /* Each instance of a subdev driver should create this struct, either
  2.    stand-alone or embedded in a larger struct.
  3.  */
  4. struct v4l2_subdev {
  5. #if defined(CONFIG_MEDIA_CONTROLLER)/*这系列文章暂不针对media多媒体设备*/
  6.     struct media_entity entity;
  7. #endif
  8.     struct list_head list;      /*用于管理每个子设备*/
  9.     struct module *owner;       /*指向i2c_lient driver module*/        
  10.     /*该子设备属于那种设备(如I2C),see V4L2_SUBDEV_FL_IS_XX and */
  11.     u32 flags;        /*是否包含设备节点 V4L2_SUBDEV_FL_HAS_DEVNODE 有设备节点*/
  12.     struct v4l2_device *v4l2_dev;
  13.     const struct v4l2_subdev_ops *ops;
  14.     /* Never call these internal ops from within a */
  15.     const struct v4l2_subdev_internal_ops *internal_ops;
  16.     /* The control handler of this subdev. May be NULL. */
  17.     struct v4l2_ctrl_handler *ctrl_handler;
  18.     /* name must be unique */
  19.     char name[V4L2_SUBDEV_NAME_SIZE];/*该模组的名字*/
  20.     /* can be used to group similar subdevs, value is driver-specific */
  21.     u32 grp_id;
  22.     /* pointer to private data */
  23.     void *dev_priv;              /*eg. point i2c_client data struct*/
  24.     void *host_priv;             /*eg. point camera host data struct*/
  25.     /* subdev device node */
  26.     struct video_device devnode;/*指向video device设备实例,后面会详细介绍*/
  27.     /* number of events to be allocated on open */
  28.     unsigned int nevents;
  29. };
  • u32 flagsflags字段用于标志该sub_devs属于那一种设备(I2C,SPI),另外还标致着该子设备是否产生设备节点,它一共有四种取值;V4L2_SUBDEV_FL_IS_I2C,V4L2_SUBDEV_FL_IS_SPI,

V4L2_SUBDEV_FL_HAS_DEVNODE,V4L2_SUBDEV_FL_HAS_EVENTS

  • v4l2_dev:指向struct v4l2_device结构,v4l2_subdev的注册过程会将该指针指向前面注册过的v4l2_device设备结构;

  • ops:指向struct v4l2_subdev_ops *ops 函数结构指针,这个结构指针是需要我们在sub device模组驱动中去实现的;

  • dev_priv:如果该设备为I2C设备,则该字段用于保存i2c_client结构

  • host_priv:用于保存camera host数据结构.

  • devnode:指向video设备结构实例

每一个subdev驱动程序都应该创建一个struct v4l2_subdev结构实例,你可以在你的驱动程序中单独的为该结构申请内存,同样可以将这个结构嵌入到其他的驱动数据结构中去.一般性的我们将该结构嵌入到camera sensor模组驱动的数据结构中去,同样为了该sub devicei2c client之间的相互引用,我们还常把i2c_client结构嵌入到模组驱动数据结构中去系统为我们提供了2API,用于struct v4l2_subdev结构和该子设备所属的设备类型(I2C,SPI)等数据结构之间的相互引用,定义如下:

点击(此处)折叠或打开

  1. static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
  2. {
  3.     sd->dev_priv = p;
  4. }

你可以简单的调用v4l2_set_subdevdata(sd, client)就可以将i2c_client结构保存到struct v4l2_subdev结构中的dev_priv字段中去,这样你下次使用的时候只需要调用下面这个API就可以返回你保存过的i2c_client结构

同样的对于struct v4l2_subdev结构的camera host,内核也为我们提供了两个API用于它和hos驱动结构之间的相互引用

点击(此处)折叠或打开

  1. static inline void v4l2_set_subdev_hostdata(struct v4l2_subdev *sd, void *p)
  2. {
  3.     sd->host_priv = p;
  4. }

这样你只要简单的调用v4l2_set_subdev_hostdata()就可以将camera host那端的数据结构保存到struct v4l2_subdev结构的host_priv字段,在另一地方要引用camera host对应的数据结构的时候之需要调用 v4l2_get_subdev_hostdata就可以返回了

5.1 v4l2_subdev initialized

一个子设备驱动使用如下函数初始化v4l2_subdev结构:

点击(此处)折叠或打开

  1. v4l2_subdev_init(sd, &ops);

点击(此处)折叠或打开

  1. void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
  2. {
  3.     INIT_LIST_HEAD(&sd->list);
  4.     BUG_ON(!ops);
  5.     sd->ops = ops;
  6.     sd->v4l2_dev = NULL;
  7.     sd->flags = 0;
  8.     sd->name[0] = '\0';
  9.     sd->grp_id = 0;
  10.     sd->dev_priv = NULL;
  11.     sd->host_priv = NULL;
  12. #if defined(CONFIG_MEDIA_CONTROLLER)
  13.     sd->entity.name = sd->name;
  14.     sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
  15. #endif
  16. }

其中的subdev->name字段是需要我们去初始化的,并且还需要设置这个module owner,对于I2C设备系统为我们提供了相应的v4l2_i2c_subdev_init接口来初始化一个subdev子模块,这个接口实现如下:drivers/media/video/v4l2-common.c

点击(此处)折叠或打开

  1. void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
  2.         const struct v4l2_subdev_ops *ops)
  3. {
  4.     v4l2_subdev_init(sd, ops);/*初始化struct v4l2_subdev结构,绑定操作函数指针*/
  5.     sd->flags |= V4L2_SUBDEV_FL_IS_I2C;/*设置该标志,指定该模组设备属于I2C设备*/
  6.     /* the owner is the same as the i2c_client's driver owner */
  7.     sd->owner = client->driver->driver.owner;/*init the sd->owner*/
  8.     /* i2c_client and v4l2_subdev point to one another */
  9.     v4l2_set_subdevdata(sd, client);
  10.     i2c_set_clientdata(client, sd);
  11.     /* 初始化subdev结构的name字段*/
  12.     snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
  13.         client->driver->driver.name, i2c_adapter_id(client->adapter),
  14.         client->addr);
  15. }
 5.2 v4l2_subdev register

一个设备v4l2_subdev驱动需要将v4l2_subdev结构注册到v4l2_device当中去(也就是将struct v4l2_subdev struct v4l2_device结构关联起来)使用如下API

点击(此处)折叠或打开

  1. int err = v4l2_device_register_subdev(v4l2_dev, sd);

删除一部分信息,留下一部分我比较关注的信息这个API的实现如下:

点击(此处)折叠或打开

  1. int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
  2.                 struct v4l2_subdev *sd)
  3. {
  4.     int err;

  5.     /* Check for valid input */
  6.     if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
  7.         return -EINVAL;

  8.     /* 检测是否重复注册 */
  9.     WARN_ON(sd->v4l2_dev != NULL);

  10.     /*通过try_module_get去获取sub_dev所属的内核模块是否已经成功被加载
  11.      *如果该sub_dev所属的内核模块还未被成功注册进内核则返回ENODEV
  12.      *try_module_get通过回调module_is_live来判断sd->owner的状态
  13.      */
  14.     if (!try_module_get(sd->owner))
  15.         return -ENODEV;
  16.    
  17.     /*关联v4l2_dev*/
  18.     sd->v4l2_dev = v4l2_dev;
  19.     /*将v4l2_subdev结构中的list链表挂到v4l2_device结构中的subdevs链表中去*/
  20.     spin_lock(&v4l2_dev->lock);
  21.     list_add_tail(&sd->list, &v4l2_dev->subdevs);
  22.     spin_unlock(&v4l2_dev->lock);

  23.     return 0;
  24. }

现在你再返回去看这两个结构体,很明显子设备的注册就是将struct v4l2_subdev结构中的list链表字段追加到struct v4l2_device结构中的subdevs链表尾中.如果子设备模块在它成功注册之前消失了,那这个注册函数肯定会失败的。这个函数成功执行之后, 这样subdev->dev就会指向v4l2_device。于是就关联起来了。


5.3 v4l2_subdev unregister

通过\如下函数注销掉之前注册的子设备:

点击(此处)折叠或打开

  1. v4l2_device_unregister_subdev(sd);

在这个函数执行之后,子设备模块将被卸载并且sd->dev==NULL;

点击(此处)折叠或打开

  1. void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
  2. {
  3.     struct v4l2_device *v4l2_dev;

  4.     /* return if it isn't registered */
  5.     if (sd == NULL || sd->v4l2_dev == NULL)
  6.         return;

  7.     v4l2_dev = sd->v4l2_dev;
  8.     /*将v4l2_subdev结构中的list链表从v4l2_device结构的subdevs链表中删除*/
  9.     spin_lock(&v4l2_dev->lock);
  10.     list_del(&sd->list);
  11.     spin_unlock(&v4l2_dev->lock);
  12.     /*最后注销设备节点*/
  13.     video_unregister_device(&sd->devnode);
  14.     module_put(sd->owner);
  15. }

5.4 v4l2_subdev_xxx_ops

每个v4l2_subdev结构包含了若干函数指针,子设备驱动可以实现这些函数(或者让它为NULL,如果不用到它的话)。这些函数指针已经按照分类排序,并且每个分类具有它自己的ops结构体。顶层的(top-level)ops结构体包含了到分类ops结构体的指针,它们可以是NULL,如果子设备驱动(subdev driver)不支持这个分类的功能。

点击(此处)折叠或打开

  1. struct v4l2_subdev_ops {
  2.     const struct v4l2_subdev_core_ops *core;        /*共用*/
  3.     const struct v4l2_subdev_tuner_ops *tuner;    
  4.     const struct v4l2_subdev_audio_ops *audio;
  5.     const struct v4l2_subdev_video_ops *video;    /*video设备*/
  6. };

下面我们主要来分析v4l2_subdev_ops结构指针中的corevideo结构指针,核心ops(core_ops)是所有子设备(subdevs)公用的,其他分类的ops是否使用取决于子设备。例如,一个视频设备不太可能去支持一个audio ops vice versa;

点击(此处)折叠或打开

  1. struct v4l2_subdev_core_ops {
  2.     int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
  3.     int (*init)(struct v4l2_subdev *sd, u32 val);
  4.     int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
  5.     int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
  6.     int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
  7.     int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
  8.     long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
  9.     ...
  10. };

上面这个结构是我们在摄像头sensor模组驱动中要实现的精华部分,操作摄像头sensor就是通过这些接口来完成的,当然上面这些只是v4l2_subdev_core_ops一部分成员,下面结合v4l2子系统中的一些结构来简单的学习一下这些函数指针的使用

5.5.1 chip identifier

点击(此处)折叠或打开

  1. struct v4l2_dbg_chip_ident {
  2.     struct v4l2_dbg_match match;
  3.     __u32 ident; /* chip identifier as specified in <media/v4l2-chip-ident.h> */
  4.     __u32 revision; /* chip revision, chip specific 0 */
  5. } __attribute__ ((packed));
  • match:用于匹配,如果sensori2c设备那么里面保存着该设备的地址和名字,通过i2c地址以及设备名字来匹配

  • ident:我们每移植一款sensor设备,都需要在media/v4l2-chip-ident.h文件中声明一个唯一的标识符,用来说明你这套代码支持该sensor

  • revision0

通过VIDIOC_DBG_G_CHIP_IDENT命令来枚举该结构int (*g_chip_ident)函数指针的通用实现如下:

点击(此处)折叠或打开

  1. static int sensor_g_chip_ident(struct v4l2_subdev *sd,
  2.                                         struct v4l2_dbg_chip_ident *id)
  3. {
  4.     struct i2c_client *client = v4l2_get_subdevdata(sd);
  5.      /*Match against I2C 7-bit address*/
  6.     if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
  7.         return -EINVAL;
  8.      /*sensor设备从地址*/
  9.     if (id->match.addr != client->addr)
  10.         return -ENODEV;
  11.     id->ident = SENSOR_V4L2_IDENT;/*这个就是我们自己添加的*/
  12.     id->revision = 0;

  13.     return 0;
  14. }

在驱动的初始化阶段,可以通过v4l2_subdev_call(sd, core, g_chip_ident, id)来回调该函数指针来匹配驱动是否支持该sensor


5.5.2 sensor initialized

每个v4l2_subdev设备都需要初始化,调过摄像头的朋友都知道,没个sensor都有N多的初始化代码,这些初始化信息就是由camera host通过v4l2_subdev_call(sd,core, init, 0)来回调init函数指针将初始化代码段写进sensor芯片内,这些初始化信息包括sensor所支持的功能,如亮度调节,白平衡,聚焦,照片的像素等等的一系列信息。当然这部分信息一般由FAE提供。


5.5.3 v4l2 control

对于相机有很多的功能,我们需要对它们进行操作,比如说调焦,白平衡,效果调节等等.用户空间程序中通过VIDIOC_QUERYCTRL命令来枚举下面结构,获取可用的控制操作相应的由驱动程序中的vidioc_queryctrl()方法来实现用户空间的ioctl操作。该函数指针的原型如下:include/media/v4l2-ioctl.h

点击(此处)折叠或打开

  1. int (*vidioc_queryctrl) (struct file *file, void *fh,
  2.                     struct v4l2_queryctrl *a);

v4l2_queryctrl结构的定义如下:include/linux/video2.h

点击(此处)折叠或打开

  1. /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
  2. struct v4l2_queryctrl {
  3.     __u32         id;
  4.     enum v4l2_ctrl_type type;
  5.     __u8         name[32];    /* Whatever */
  6.     __s32         minimum;    /* Note signedness */
  7.     __s32         maximum;
  8.     __s32         step;
  9.     __s32         default_value;
  10.     __u32 flags;
  11.     __u32         reserved[2];
  12. };

这个结构包含了许多字段,下面一个一个分析

  • id:每一个功能都有一个唯一的ID,它们都形如V4L2_CID_XXX,定义在include/linux/video2.h,比较常见的有如下几种

V4L2_CID_DO_WHITE_BALANCE /*白平衡*/

V4L2_CID_EFFECT /*效果*/

V4L2_CID_FLASH /*闪光灯*/

V4L2_CID_BRIGHTNESS /*亮度*/

V4L2_CID_EXPOSURE /*曝光*/

V4L2_CID_SATURATION /*色彩饱和度*/

V4L2_CID_CONTRAST /*对比度*/

V4L2_CID_HFLIP /*水平镜像*/

V4L2_CID_VFLIP /*垂直镜像*/

V4L2_CID_SCENE /*场景,白天黑夜*/

V4L2_CID_FOCUS_AUTO /*自动聚焦*/

V4L2_CID_FOCUS_RELATIVE /*相对聚焦*/

V4L2_CID_FOCUS_ABSOLUTE /*绝对聚焦*/

V4L2_CID_FOCUS_CONTINUOUS /*持续聚焦*/

V4L2_CID_ZOOM_RELATIVE /*数字变焦*/

V4L2_CID_ZOOM_ABSOLUTE

  • name:控制名字如Focus Control只是一个标识,可以随意填\

  • minimum:支持调节的最小值

  • maximum:支持调节的最大值

  • step:每次调节的步进

  • default_value:默认值在minimummaximum之间(对整型、布尔型和菜单控制适用)

  • type:该字段表明了一组固定的选项,针对没一组固定的选项定义了如下枚举结构

点击(此处)折叠或打开

  1. enum v4l2_ctrl_type {
  2.         V4L2_CTRL_TYPE_INTEGER     = 1,
  3.         V4L2_CTRL_TYPE_BOOLEAN     = 2,
  4.         V4L2_CTRL_TYPE_MENU          = 3,    /*菜单型的*/
  5.         V4L2_CTRL_TYPE_BUTTON     = 4,
  6.         V4L2_CTRL_TYPE_INTEGER64 = 5,
  7.         V4L2_CTRL_TYPE_CTRL_CLASS = 6,
  8.         V4L2_CTRL_TYPE_STRING = 7,
  9.     };
  • flags:表示控制的标志,有如下定义

V4L2_CTRL_FLAG_DISABLED /*控制操作不可用,应用应忽略它*/

V4L2_CTRL_FLAG_GRABBED /*控制暂时不可变,可能是因为另一个应用正在使用*/

V4L2_CTRL_FLAG_READ_ONLY /*可以查看,但不可以操作*/

V4L2_CTRL_FLAG_UPDATE /*调整这个参数可以会对其他控指造成影响*/

V4L2_CTRL_FLAG_INACTIVE /*与当前设备配置无关的操作*/

V4L2_CTRL_FLAG_SLIDER /*暗示应用在使用这个操作的时候可以使用类似滚动条的接口*/

V4L2_CTRL_FLAG_WRITE_ONLY

当然该结构可以代表那么多的功能是用id字段来区分的,对于菜单型的诸多控制操作来说(type=V4L2_CTRL_TYPE_MENU)用户空间程序可以使用VIDIOC_QUERYMENU命令通过驱动程序当中的vidioc_querymenu函数来完成相应的ioctl操作

点击(此处)折叠或打开

  1. int (*vidioc_querymenu)(struct file *file, void *fh,
  2.                     struct v4l2_querymenu *a);

struct v4l2_querymenu结构的定义如下:

点击(此处)折叠或打开

  1. /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
  2. struct v4l2_querymenu {
  3.     __u32        id;
  4.     __u32        index;
  5.     __u8        name[32];    /* Whatever */
  6.     __u32        reserved;/*永远都等于0*/
  7. };
  • id:相关控制菜单的ID值和上面v4l2_queryctrl结构中的ID是保持一致的这更进一步表明了可用控制的遍历过程

  • index:菜单ID值的索引(每打开一个菜单是不是有很长的一列?就是这个),0开始一直到上面那个结构定义的maximum字段为止

  • name:由驱动程序填充(并出现在菜单中,比如android系统中的闪光灯中的auto,on,off..)

到这里应用程序已经遍历到驱动中支持的可用操作了,现在我们可以对它进行查询已经操作了,对于这些操作v4l2定义了如下结构:

点击(此处)折叠或打开

  1. /*
  2.  *    include/linux/video2.h
  3.  */
  4. struct v4l2_control {
  5.     __u32         id;
  6.     __s32         value;
  7. };

现在假设我们要查询某一个可用操作,当然这是由用户空间通过VIDIOC_G_CTRL命令发出的,在我们的驱动程序中由vidioc_g_ctrl函数指针来响应这个命令,最后回调v4l2_subdev中的函数指针g_ctrl来实现这个命令(再回首5.1中的那个pos),这两个函数指针的原型分别是:

点击(此处)折叠或打开

  1. int (*vidioc_g_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
  2. int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);

从它们的原型我们可以知道,用户空间通过对设备文件节点的操作,最后转化到实际硬件的操作,linux的朋友一定很清晰,如果查询的操作不存在将返回-EINVAL,如果查询成功则返回当前控制设定的值.

如果我们试图在上层通过按钮试图改变当前的控制,比如说把曝光调大或小一点,则在用户空间通过发送VIDIOC_S_CTRL命令请求来实现,在驱动中对应的vidioc_s_ctrl函数指针首先相应该请求,最后通过会调用v4l2_subdev中对应的s_ctrl函数指针来操作相应的sensor硬件,它们的原型分别是:

点击(此处)折叠或打开

  1. int (*vidioc_s_ctrl)(struct file *file, void *private_data,
  2.                                                  struct v4l2_control *ctrl);
  3.  
  4. int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);

对于具体的操作我们可以查看V4L2应用API官方文档:这些ioctl属于user contrl,除此之外还有其他一些扩展控制,扩展控制可以同时原子的对多个ID进行控制,在用户空间程序中使用ioctl配套的三个命令如下:VIDIOC_G_EXT_CTRLS, VIDIOC_S_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS 如果操作成功则返回0否则返回EINVAL,驱动中对应的v4l2_device函数指针如下:

点击(此处)折叠或打开

  1. int (*vidioc_g_ext_ctrls) (struct file *file, void *fh,
  2.                                             struct v4l2_ext_controls *a);
  3. int (*vidioc_s_ext_ctrls) (struct file *file, void *fh,
  4.                                             struct v4l2_ext_controls *a);
  5. int (*vidioc_try_ext_ctrls) (struct file *file, void *fh,
  6.                                             struct v4l2_ext_controls *a);

驱动中对应的v4l2_subdev中的ops函数指针如下:

点击(此处)折叠或打开

  1. int (*g_ext_ctrls)(struct v4l2_subdev *sd,
  2.                                         struct v4l2_ext_controls *ctrls);
  3. int (*s_ext_ctrls)(struct v4l2_subdev *sd,
  4.                                         struct v4l2_ext_controls *ctrls);
  5. int (*try_ext_ctrls)(struct v4l2_subdev *sd,
  6.                                         struct v4l2_ext_controls *ctrls);

struct v4l2_ext_controls结构的应以如下:include/linux/video2.h

点击(此处)折叠或打开

  1. struct v4l2_ext_controls {
  2.     __u32 ctrl_class;
  3.     __u32 count;
  4.     __u32 error_idx;
  5.     __u32 reserved[2];//0
  6.     struct v4l2_ext_control *controls;
  7. };
  • ctrl_class:控制类,每一种类代表一种特性,CLASS_USER,CLASS_MPEG等等

  • countcontrols数组的个数

  • controls:指向struct v4l2_ext_control结构

对于__u32 ctrl_class字段有如下定义

点击(此处)折叠或打开

  1. /* Values for ctrl_class field */
  2. V4L2_CTRL_CLASS_USER     /*'user' controls VIDIOC_G_CTRL和VIDIOC_S_CTRL属于该范畴*/
  3. V4L2_CTRL_CLASS_MPEG         /*MPEG-compression controls */
  4. V4L2_CTRL_CLASS_CAMERA     /* Camera class controls */
  5. V4L2_CTRL_CLASS_FM_TX     /* FM Modulator control class */
  6. V4L2_CTRL_CLASS_JPEG          /*JPEG compression controls*/
  7. V4L2_CTRL_CLASS_IMAGE_SOURCE     /*Image source controls*/
  8. V4L2_CTRL_CLASS_IMAGE_PROC    /*Image processing controls*/

具体的意思可以通过V4L2提供的官方文档查看:

struct v4l2_ext_control结构的定义如下:

点击(此处)折叠或打开

  1. struct v4l2_ext_control {
  2.     __u32 id;        
  3.     __u32 size;
  4.     __u32 reserved2[1];/*0*/
  5.     union {
  6.         __s32 value;
  7.         __s64 value64;
  8.         char *string;
  9.     };
  10. } __attribute__ ((packed));
  • idIdentifies the control, 和上面结构中的ID一样

  • value:就是要控制值应该和v4l2_querymenu结构中的index字段保持一致

上面这些控制都是一些功能特效的控制,属于基本控制.对于subdev还有许多的内容需要修改,比如流控等,一些数据的格式,这需要和camera host配合控制这将在后面再进行说明

阅读(8946) | 评论(0) | 转发(9) |
给主人留下些什么吧!~~