zoukankan      html  css  js  c++  java
  • V4L2结构体——IOCTL

     

    在应用程序获取视频数据的流程中,都是通过 ioctl 命令与驱动程序进行交互,常见的 ioctl 命令有:

    复制代码
     1 VIDIOC_QUERYCAP     /* 获取设备支持的操作 */
     2 VIDIOC_G_FMT        /* 获取设置支持的视频格式 */
     3 VIDIOC_S_FMT        /* 设置捕获视频的格式 */
     4 VIDIOC_REQBUFS      /* 向驱动提出申请内存的请求 */
     5 VIDIOC_QUERYBUF     /* 向驱动查询申请到的内存 */
     6 VIDIOC_QBUF         /* 将空闲的内存加入可捕获视频的队列 */
     7 VIDIOC_DQBUF        /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
     8 VIDIOC_STREAMON     /* 打开视频流 */
     9 VIDIOC_STREAMOFF    /* 关闭视频流 */
    10 VIDIOC_QUERYCTRL    /* 查询驱动是否支持该命令 */
    11 VIDIOC_G_CTRL       /* 获取当前命令值 */
    12 VIDIOC_S_CTRL       /* 设置新的命令值 */
    13 VIDIOC_G_TUNER      /* 获取调谐器信息 */
    14 VIDIOC_S_TUNER      /* 设置调谐器信息 */
    15 VIDIOC_G_FREQUENCY  /* 获取调谐器频率 */
    16 VIDIOC_S_FREQUENCY  /* 设置调谐器频率 */
    复制代码

    1、struct v4l2_capability 与 VIDIOC_QUERYCAP

    VIDIOC_QUERYCAP 命令通过结构 v4l2_capability 获取设备支持的操作模式:

    复制代码
    1 struct v4l2_capability {
    2     __u8    driver[16];     /* i.e. "bttv" */
    3     __u8    card[32];       /* i.e. "Hauppauge WinTV" */
    4     __u8    bus_info[32];   /* "PCI:" + pci_name(pci_dev) */
    5     __u32   version;        /* should use KERNEL_VERSION() */
    6     __u32    capabilities;   /* Device capabilities */
    7     __u32    reserved[4];
    8 };
    复制代码

    其中域 capabilities 代表设备支持的操作模式,常见的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一个视频捕捉设备并且具有数据流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。

    2、struct v4l2_format 与 VIDIOC_G_FMT、VIDIOC_S_FMT、VIDIOC_TRY_FMT

    通常用 VIDIOC_S_FMT 命令通过结构 v4l2_format 初始化捕获视频的格式,如果要改变格式则用 VIDIOC_TRY_FMT 命令:

    复制代码
     1 struct v4l2_format {
     2     enum v4l2_buf_type type;
     3     union {
     4         struct v4l2_pix_format         pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
     5         struct v4l2_window             win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
     6         struct v4l2_vbi_format         vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
     7         struct v4l2_sliced_vbi_format  sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
     8         __u8   raw_data[200];                   /* user-defined */
     9     } fmt;
    10 };
    11 其中
    12 enum v4l2_buf_type {
    13     V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
    14     V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
    15     V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
    16     ...
    17     V4L2_BUF_TYPE_PRIVATE              = 0x80,
    18 };
    19 
    20 struct v4l2_pix_format {
    21     __u32                   width;
    22     __u32                   height;
    23     __u32                   pixelformat;
    24     enum v4l2_field         field;
    25     __u32                   bytesperline;   /* for padding, zero if unused */
    26     __u32                   sizeimage;
    27     enum v4l2_colorspace    colorspace;
    28     __u32                   priv;           /* private data, depends on pixelformat */
    29 };
    复制代码
     常见的捕获模式为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即视频捕捉模式,在此模式下 fmt 联合体采用域 v4l2_pix_format:其中 width 为视频的宽、height 为视频的高、pixelformat 为视频数据格式(常见的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、bytesperline 为一行图像占用的字节数、sizeimage 则为图像占用的总字节数、colorspace 指定设备的颜色空间。

    3、struct v4l2_requestbuffers 与 VIDIOC_REQBUFS

    VIDIOC_REQBUFS 命令通过结构 v4l2_requestbuffers 请求驱动申请一片连续的内存用于缓存视频信息:

    复制代码
     1 struct v4l2_requestbuffers {
     2     __u32                   count;
     3     enum v4l2_buf_type      type;
     4     enum v4l2_memory        memory;
     5     __u32                   reserved[2];
     6 };
     7 其中
     8 enum v4l2_memory {
     9     V4L2_MEMORY_MMAP             = 1,
    10     V4L2_MEMORY_USERPTR          = 2,
    11     V4L2_MEMORY_OVERLAY          = 3,
    12 };
    复制代码

    count 指定根据图像占用空间大小申请的缓存区个数,type 为视频捕获模式,memory 为内存区的使用方式。

     

    4、struct v4l2_buffer与 VIDIOC_QUERYBUF

    VIDIOC_QUERYBUF 命令通过结构 v4l2_buffer 查询驱动申请的内存区信息:

    复制代码
     1 struct v4l2_buffer {
     2     __u32                   index;
     3     enum v4l2_buf_type      type;
     4     __u32                   bytesused;
     5     __u32                   flags;
     6     enum v4l2_field         field;
     7     struct timeval          timestamp;
     8     struct v4l2_timecode    timecode;
     9     __u32                   sequence;
    10 
    11     /* memory location */
    12     enum v4l2_memory        memory;
    13     union {
    14             __u32           offset;
    15             unsigned long   userptr;
    16     } m;
    17     __u32                   length;
    18     __u32                   input;
    19     __u32                   reserved;
    20 };
    复制代码

    5、enum v4l2_buf_type 与 VIDIOC_STREAMON、VIDIOC_STREAMOFF

     这两个命令使用的只是一个整形数据,即 v4l2_buf_type,一般只要指定其值为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即可。

    6、struct v4l2_queryctrl 与 VIDIOC_QUERYCTRL

    VIDIOC_QUERYCTRL 命令通过结构 v4l2_queryctrl 查询驱动是否支持该 id 代表的命令,并返回该命令的各种参数:

    复制代码
     1 struct v4l2_queryctrl {
     2     __u32                id;            /* 命令编号 */
     3     enum v4l2_ctrl_type  type;          /* 命令值的类型 */
     4     __u8                 name[32];        /* 命令名称*/
     5     __s32                minimum;       /* 最小的命令值 */
     6     __s32                maximum;       /* 最大的命令值 */
     7     __s32                step;          /* 命令值变化的步长 */
     8     __s32                default_value; /* 默认的命令值 */
     9     __u32                flags;         /* 命令的标志 */
    10     __u32                reserved[2];   /* 命令值的位图表示 */
    11 };
    12 其中
    13 enum v4l2_ctrl_type {
    14     V4L2_CTRL_TYPE_INTEGER         = 1,   /* 整形 */
    15     V4L2_CTRL_TYPE_BOOLEAN         = 2,   /* 真值 */
    16     V4L2_CTRL_TYPE_MENU          = 3,   /* 菜单 */
    17     V4L2_CTRL_TYPE_BUTTON         = 4,   /* 无值 */
    18     V4L2_CTRL_TYPE_INTEGER64     = 5,   /* 后面三种不常用 */
    19     V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
    20     V4L2_CTRL_TYPE_STRING        = 7,
    21 };
    22 命令的标志取值如下:
    23 /*  Control flags  */
    24 #define V4L2_CTRL_FLAG_DISABLED        0x0001
    25 #define V4L2_CTRL_FLAG_GRABBED        0x0002
    26 #define V4L2_CTRL_FLAG_READ_ONLY     0x0004
    27 #define V4L2_CTRL_FLAG_UPDATE         0x0008
    28 #define V4L2_CTRL_FLAG_INACTIVE     0x0010
    29 #define V4L2_CTRL_FLAG_SLIDER         0x0020
    30 #define V4L2_CTRL_FLAG_WRITE_ONLY     0x0040
    31 
    32 /*  Query flag, to be ORed with the control ID */
    33 #define V4L2_CTRL_FLAG_NEXT_CTRL    0x80000000
    复制代码

    id 是命令的编号,常见的命令有两种:一种以 V4L2_CID_BASE 为起始值,是公用命令;一种以 V4L2_CID_PRIVATE_BASE 为起始值,是私有命令。在一般的应用中命令值可见如下:

    复制代码
     1 V4L2_CID_CONTRAST               (V4L2_CID_BASE+1)            /* 对比度调节 */
     2 V4L2_CID_SATURATION             (V4L2_CID_BASE+2)            /* 饱和度调节 */
     3 V4L2_CID_AUDIO_VOLUME           (V4L2_CID_BASE+5)            /* 音量调节 */
     4 V4L2_CID_AUDIO_MUTE             (V4L2_CID_BASE+9)            /* 静音设置 */
     5 V4L2_CID_DO_WHITE_BALANCE       (V4L2_CID_BASE+13)           /* 白平衡调节 */
     6 V4L2_CID_GAMMA                  (V4L2_CID_BASE+16)           /* 伽马值调节 */
     7 V4L2_CID_EXPOSURE               (V4L2_CID_BASE+17)           /* 曝光度调节 */
     8 
     9 V4L2_CID_PRIVATE_ATXX_FLASH     (V4L2_CID_PRIVATE_BASE + 2)  /* 闪光灯控制 */
    10 V4L2_CID_PRIVATE_ATXX_FRAME     (V4L2_CID_PRIVATE_BASE + 12) /* 帧率调节 */
    复制代码

     type 为命令值的类型(总共有7中类型的值),name 是命令的名称,reserved 则是命令值的位图表示,驱动会将所有的命令值都以 bit 的形式写到 64 位的域中,上层应用查询时可以根据位图判断命令支持的值。

    7、struct v4l2_control 与 VIDIOC_G_CTRL、VIDIOC_S_CTRL

     VIDIOC_S_CTRL 或 VIDIOC_G_CTRL 命令通过结构 v4l2_control 设置或者获取 id 命令的值:

    1 struct v4l2_control { 2 __u32 id; 3 __s32 value; 4 };

     这个结构只有 2 个域,id 是命令编号,value 则是命令的值。

    8、struct v4l2_tuner 与 VIDIOC_G_TUNER、VIDIOC_S_TUNER

     VIDIOC_S_TUNER 或 VIDIOC_G_TUNER 命令通过结构 v4l2_tuner 设置调谐器的信息:

    复制代码

    1 struct v4l2_tuner {

    2 __u32 index; /* 调谐器编号,由应用程序设置 */

    3 __u8 name[32]; /* 调谐器名称 */

    4 enum v4l2_tuner_type type; /* 调谐器类型 */

    5 __u32 capability; /* 调谐器支持的操作 */

    6 __u32 rangelow; /* 最低频率值,单位为62.5Hz或者62.5KHz */

    7 __u32 rangehigh; /* 最高频率值 */

    8 __u32 rxsubchans; /* 接收的音频信号类型 */

    9 __u32 audmode; /* 当前音频播放形式 */

    10 __s32 signal; /* 信号强度 */

    11 __s32 afc; /* 自动频率控制 */

    12 __u32 reserved[4]; /* 保留备用 */

    13 };

    14 其中

    15 enum v4l2_tuner_type {

    16 V4L2_TUNER_RADIO = 1, /* 调频收音机 */

    17 V4L2_TUNER_ANALOG_TV = 2, /* 模拟电视高频头 */

    18 V4L2_TUNER_DIGITAL_TV = 3, /* 数字电视高频头 */

    19 };

    复制代码

     其中域 type 有三种类型;capability 域一般为 V4L2_TUNER_CAP_LOW,表明频率调节的步长是62.5Hz,如果没有这个标志位则步长为62.5KHz;rangelow 与 rangehigh 是调谐器可以调频率的最高值和最低值,但都以步长为单位表示;rxsubchans 表示调谐器接收的音频信号类型,常见值有 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO  即单声道与立体声;audmode 表示以何种方式播放声音,常见值有 V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO,即以单声道还是立体声的方式播放;signal 为当前信号强度,一般取值范围为 0 - 65535。

    9、struct v4l2_frequency 与 VIDIOC_G_FREQUENCY、VIDIOC_S_FREQUENCY

    VIDIOC_S_FREQUENCY 或 VIDIOC_G_FREQUENCY 命令通过结构 v4l2_frequency 设置或获取当前频率值:

    struct v4l2_frequency {
        __u32                 tuner;          /* 调谐器编号 */
        enum v4l2_tuner_type  type;           /* 调谐器类型 */
        __u32                 frequency;      /* 调谐器频率 */
        __u32                 reserved[8];
    };

     注意:frequency 的值是以62.5Hz 或者 62.5KHZ 为单位的。

    附:_IO、_IOR、_IOW、_IOWR 宏的使用说明

    驱动程序中 ioctl  函数传递的变量 cmd 是应用程序向驱动程序请求处理的命令。cmd 除了用于区别不同命令的数值,还可包含有助于处理的几种信息。cmd 的大小为 32 bit,共分 4 个域:

    bit29 ~ bit31: 3bit  为 “读写” 区,作用是区分是读命令还是写命令。
    bit16 ~ bit28:13bit 为 "数据大小" 区,表示 ioctl 中的 arg 变量传递的数据大小;有时候为 14bit 即将 bit29 覆盖。
    bit8 ~ bit15:   8bit  为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
    bit0 ~ bit7:     8bit  为 "序号" 区,是区分命令的命令顺序序号。

    魔数(magic number)
    魔数范围为 0~255 。通常,用英文字符 'A' ~ 'Z' 或者 'a' ~ 'z' 来表示。设备驱动程序从传递进来的命令获取魔数,然后与自身处理的魔数想比较,如果相同则处理,不同则不处理。魔数是拒绝误使用的初步辅助参数。设备驱动程序可以通过宏 _IOC_TYPE (cmd) 来获取魔数。不同的设备驱动程序最好设置不同的魔数,但并不是要求绝对,也是可以使用其他设备驱动程序已用过的魔数。

    基数(序号)
    基数用于区别各种命令。通常,从 0开始递增,相同设备驱动程序上可以重复使用该值。例如,读和写命令中使用了相同的基数,设备驱动程序也能分辨出来,原因在于设备驱动程序区分命令时使用 switch ,且直接使用命令变量 cmd 值。创建命令的宏生成的值由多个域组合而成,所以即使是相同的基数,也会判断为不同的命令。设备驱动程序想要从命令中获取该基数,就使用宏 _IOC_NR (cmd)。

    下面我们看一下上述宏在内核中的原型:

    下面我们看一下上述宏在内核中的原型

    复制代码
     1 /*
     2  * Our DIR and SIZE overlap in order to simulteneously provide
     3  * a non-zero _IOC_NONE (for binary compatibility) and
     4  * 14 bits of size as on i386. Here's the layout:
     5  *
     6  *   0xE0000000   DIR            3bit
     7  *   0x80000000   DIR = WRITE    bit31
     8  *   0x40000000   DIR = READ     bit30
     9  *   0x20000000   DIR = NONE     bit29
    10  *   0x3FFF0000   SIZE (overlaps NONE bit)  13bit
    11  *   0x0000FF00   TYPE           8bit
    12  *   0x000000FF   NR (CMD)       8bit
    13  */
    14 /* 各个域的长度 */
    15 #define _IOC_NRBITS      8
    16 #define _IOC_TYPEBITS    8
    17 #define _IOC_SIZEBITS   13    /* Actually 14, see below. */
    18 #define _IOC_DIRBITS     3
    19 /* 各个域的掩码 */
    20 #define _IOC_NRMASK      ((1 << _IOC_NRBITS)-1)
    21 #define _IOC_TYPEMASK    ((1 << _IOC_TYPEBITS)-1)
    22 #define _IOC_SIZEMASK    ((1 << _IOC_SIZEBITS)-1)
    23 #define _IOC_XSIZEMASK   ((1 << (_IOC_SIZEBITS+1))-1)
    24 #define _IOC_DIRMASK     ((1 << _IOC_DIRBITS)-1)
    25 /* 各个域的偏移 */
    26 #define _IOC_NRSHIFT     0
    27 #define _IOC_TYPESHIFT   (_IOC_NRSHIFT + _IOC_NRBITS)       /* 8 */
    28 #define _IOC_SIZESHIFT   (_IOC_TYPESHIFT + _IOC_TYPEBITS)   /* 16 */
    29 #define _IOC_DIRSHIFT    (_IOC_SIZESHIFT + _IOC_SIZEBITS)   /* 29 */
    30 /* 读写域的值 */
    31 #define _IOC_NONE        1U
    32 #define _IOC_READ        2U
    33 #define _IOC_WRITE       4U
    34 
    35 #define _IOC(dir,type,nr,size) 
    36         (((dir)  << _IOC_DIRSHIFT) |       /* 读写方向左移 29bit */
    37          ((type) << _IOC_TYPESHIFT) |      /* 幻数左移 8bit */
    38          ((nr)   << _IOC_NRSHIFT) |        /* 命令序号 */
    39          ((size) << _IOC_SIZESHIFT))        /* 参数大小左移 16bit */
    40 /* 宏原型,这里将会根据传递的数据类型取其长度 */
    41 #define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
    42 #define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
    43 #define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
    44 #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
    45 /* 获取各个域的值 */
    46 #define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
    47 #define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
    48 #define _IOC_NR(nr)         (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
    49 #define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
    复制代码

    这里特别说明一下 _IO 宏,该宏没有可传递的变量,只用于发送命令。这是因为变量需要可变数据,只作为命令(比如 reset)使用时,没有必要判断设备上的数据,因此设备驱动程序没有必要执行文件相关的处理。在 v4l2 中使用示例如下:

    #define VIDIOC_QUERYCAP      _IOR('V',  0, struct v4l2_capability)
    #define VIDIOC_RESERVED       _IO('V',  1)
    #define VIDIOC_S_FMT        _IOWR('V',  5, struct v4l2_format)
    #define VIDIOC_STREAMON      _IOW('V', 18, int)

    v4l2 中对上述宏命令的处理在 video_ioctl2 函数中:

    复制代码
     1 static unsigned long cmd_input_size(unsigned int cmd)
     2 {
     3 #define CMDINSIZE(cmd, type, field)                 
     4     case VIDIOC_##cmd:                     
     5         return offsetof(struct v4l2_##type, field) +       /* 域的偏移 */
     6             sizeof(((struct v4l2_##type *)0)->field);      /* 域的长度 */
     7 
     8     switch (cmd) {
     9         CMDINSIZE(ENUM_FMT,        fmtdesc,    type);
    10         CMDINSIZE(G_FMT,        format,        type);
    11         ...
    12         CMDINSIZE(ENUM_FRAMESIZES,    frmsizeenum,    pixel_format);
    13         CMDINSIZE(ENUM_FRAMEINTERVALS,    frmivalenum,    height);
    14     default:
    15         return _IOC_SIZE(cmd);  /* 剩下的是需要全部拷贝的命令 */
    16     }
    17 }
    18 
    19 long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
    20 {
    21     char    sbuf[128];          /* 在栈中分配128个字节空间用来储存命令的参数 */
    22     void    *mbuf = NULL;
    23     void    *parg = NULL;       /* 参数存放的首地址 */
    24     long    err  = -EINVAL;
    25     int     is_ext_ctrl;
    26     size_t  ctrls_size = 0;
    27     void __user *user_ptr = NULL;
    28 
    29     ...
    30     /* 判断是否包含读写命令,如果是则将用户空间的参数值拷贝到内核 */
    31     if (_IOC_DIR(cmd) != _IOC_NONE) {
    32         /* 判断参数大小是否超过128字节 */
    33         if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
    34             parg = sbuf;
    35         } else {
    36             /* 如果超过128字节则从堆中申请 */
    37             mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
    38             if (NULL == mbuf)
    39                 return -ENOMEM;
    40             parg = mbuf;
    41         }
    42 
    43         err = -EFAULT;
    44         /* 如果包含写命令 */
    45         if (_IOC_DIR(cmd) & _IOC_WRITE) {
    46             /* 计算需要拷贝的有效数据长度,有的命令不需要全部拷贝 */
    47             unsigned long n = cmd_input_size(cmd);
    48             /* 从用户空间拷贝参数值 */
    49             if (copy_from_user(parg, (void __user *)arg, n))
    50                 goto out;
    51 
    52             /* 将剩下的空间清零 */
    53             if (n < _IOC_SIZE(cmd))
    54                 memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
    55         } else {
    56             /* 如果是只读命令则将整个buffer清零 */
    57             memset(parg, 0, _IOC_SIZE(cmd));
    58         }
    59     }
    60 
    61     ...
    62     /* 调用 v4l2_ioctl_ops 的成员函数处理命令 */
    63     err = __video_do_ioctl(file, cmd, parg);
    64     if (err == -ENOIOCTLCMD)
    65         err = -EINVAL;
    66     ...
    67     if (err < 0)
    68         goto out;
    69 
    70 out_ext_ctrl:
    71     /* 如果包含读命令则将参数值拷贝到用户空间 */
    72     switch (_IOC_DIR(cmd)) {
    73     case _IOC_READ:
    74     case (_IOC_WRITE | _IOC_READ):
    75         if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
    76             err = -EFAULT;
    77         break;
    78     }
    79 
    80 out:
    81     kfree(mbuf);
    82     return err;
    83 }
    84 EXPORT_SYMBOL(video_ioctl2);
    复制代码

    然后我们在 struct v4l2_file_operations 中将 ioctl 成员设置为 video_ioctl2 即可

  • 相关阅读:
    命令行
    作业三C++
    作业二
    0003---简单的a+b问题
    0002---五层小山
    0001---Hello world
    关于OJ的那些事
    CDQ分治学习笔记
    C++ IO的一些注意点
    Vscode配置C++环境
  • 原文地址:https://www.cnblogs.com/cyyljw/p/12336667.html
Copyright © 2011-2022 走看看