首页 数据库 mysql教程 v4l2 编程接口(一) — ioctl

v4l2 编程接口(一) — ioctl

Jun 07, 2016 pm 03:32 PM
应用程序 接口 数据 编程 获取 视频

在应用程序获取视频数据的流程中,都是通过 ioctl 命令与驱动程序进行交互,常见的 ioctl 命令有: [cpp] view plaincopyprint? VIDIOC_QUERYCAP /*获取设备支持的操作*/ VIDIOC_G_FMT /*获取设置支持的视频式*/ VIDIOC_S_FMT /*设置捕获视频的式*/ VIDIOC_R

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

[cpp] view plaincopyprint?

  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  /* 设置调谐器频率 */  
VIDIOC_QUERYCAP     /* 获取设备支持的操作 */
VIDIOC_G_FMT        /* 获取设置支持的视频格式 */
VIDIOC_S_FMT        /* 设置捕获视频的格式 */
VIDIOC_REQBUFS      /* 向驱动提出申请内存的请求 */
VIDIOC_QUERYBUF     /* 向驱动查询申请到的内存 */
VIDIOC_QBUF         /* 将空闲的内存加入可捕获视频的队列 */
VIDIOC_DQBUF        /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
VIDIOC_STREAMON     /* 打开视频流 */
VIDIOC_STREAMOFF    /* 关闭视频流 */
VIDIOC_QUERYCTRL    /* 查询驱动是否支持该命令 */
VIDIOC_G_CTRL       /* 获取当前命令值 */
VIDIOC_S_CTRL       /* 设置新的命令值 */
VIDIOC_G_TUNER      /* 获取调谐器信息 */
VIDIOC_S_TUNER      /* 设置调谐器信息 */
VIDIOC_G_FREQUENCY  /* 获取调谐器频率 */
VIDIOC_S_FREQUENCY  /* 设置调谐器频率 */
登录后复制
1、struct v4l2_capability 与 VIDIOC_QUERYCAP
VIDIOC_QUERYCAP 命令通过结构 v4l2_capability 获取设备支持的操作模式:

[cpp] view plaincopyprint?

  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. };  
struct v4l2_capability {
	__u8	driver[16];     /* i.e. "bttv" */
	__u8	card[32];       /* i.e. "Hauppauge WinTV" */
	__u8	bus_info[32];   /* "PCI:" + pci_name(pci_dev) */
	__u32   version;        /* should use KERNEL_VERSION() */
	__u32	capabilities;   /* Device capabilities */
	__u32	reserved[4];
};
登录后复制
其中域 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 命令:

[cpp] view plaincopyprint?

  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. };  
struct v4l2_format {
	enum v4l2_buf_type type;
	union {
		struct v4l2_pix_format         pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
		struct v4l2_window             win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
		struct v4l2_vbi_format         vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
		struct v4l2_sliced_vbi_format  sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
		__u8   raw_data[200];                   /* user-defined */
	} fmt;
};
其中
enum v4l2_buf_type {
	V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
	V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
	V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
	...
	V4L2_BUF_TYPE_PRIVATE              = 0x80,
};

struct v4l2_pix_format {
	__u32                   width;
	__u32                   height;
	__u32                   pixelformat;
	enum v4l2_field         field;
	__u32                   bytesperline;   /* for padding, zero if unused */
	__u32                   sizeimage;
	enum v4l2_colorspace    colorspace;
	__u32                   priv;           /* private data, depends on pixelformat */
};
登录后复制
常见的捕获模式为 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 请求驱动申请一片连续的内存用于缓存视频信息:

[cpp] view plaincopyprint?

  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. };  
struct v4l2_requestbuffers {
	__u32                   count;
	enum v4l2_buf_type      type;
	enum v4l2_memory        memory;
	__u32                   reserved[2];
};
其中
enum v4l2_memory {
	V4L2_MEMORY_MMAP             = 1,
	V4L2_MEMORY_USERPTR          = 2,
	V4L2_MEMORY_OVERLAY          = 3,
};
登录后复制
count 指定根据图像占用空间大小申请的缓存区个数,type 为视频捕获模式,memory 为内存区的使用方式。

4、struct v4l2_buffer与 VIDIOC_QUERYBUF

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

[cpp] view plaincopyprint?

  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. };  
struct v4l2_buffer {
	__u32                   index;
	enum v4l2_buf_type      type;
	__u32                   bytesused;
	__u32                   flags;
	enum v4l2_field         field;
	struct timeval          timestamp;
	struct v4l2_timecode    timecode;
	__u32                   sequence;

	/* memory location */
	enum v4l2_memory        memory;
	union {
	        __u32           offset;
	        unsigned long   userptr;
	} m;
	__u32                   length;
	__u32                   input;
	__u32                   reserved;
};
登录后复制
index 为缓存编号,type 为视频捕获模式,bytesused 为缓存已使用空间大小,flags 为缓存当前状态(常见值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分别代表当前缓存已经映射、缓存可以采集数据、缓存可以提取数据),timestamp 为时间戳,sequence为缓存序号,memory 为缓存使用方式,offset 为当前缓存与内存区起始地址的偏移,length 为缓存大小,reserved 一般用于传递物理地址值。
另外 VIDIOC_QBUF 和 VIDIOC_DQBUF 命令都采用结构 v4l2_buffer 与驱动通信:VIDIOC_QBUF 命令向驱动传递应用程序已经处理完的缓存,即将缓存加入空闲可捕获视频的队列,传递的主要参数为 index;VIDIOC_DQBUF 命令向驱动获取已经存放有视频数据的缓存,v4l2_buffer 的各个域几乎都会被更新,但主要的参数也是 index,应用程序会根据 index 确定可用数据的起始地址和范围。

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 代表的命令,并返回该命令的各种参数:

[cpp] view plaincopyprint?

  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  
struct v4l2_queryctrl {
	__u32                id;            /* 命令编号 */
	enum v4l2_ctrl_type  type;          /* 命令值的类型 */
	__u8                 name[32];	    /* 命令名称*/
	__s32                minimum;       /* 最小的命令值 */
	__s32                maximum;       /* 最大的命令值 */
	__s32                step;          /* 命令值变化的步长 */
	__s32                default_value; /* 默认的命令值 */
	__u32                flags;         /* 命令的标志 */
	__u32                reserved[2];   /* 命令值的位图表示 */
};
其中
enum v4l2_ctrl_type {
	V4L2_CTRL_TYPE_INTEGER	     = 1,   /* 整形 */
	V4L2_CTRL_TYPE_BOOLEAN	     = 2,   /* 真值 */
	V4L2_CTRL_TYPE_MENU          = 3,   /* 菜单 */
	V4L2_CTRL_TYPE_BUTTON	     = 4,   /* 无值 */
	V4L2_CTRL_TYPE_INTEGER64     = 5,   /* 后面三种不常用 */
	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
	V4L2_CTRL_TYPE_STRING        = 7,
};
命令的标志取值如下:
/*  Control flags  */
#define V4L2_CTRL_FLAG_DISABLED		0x0001
#define V4L2_CTRL_FLAG_GRABBED		0x0002
#define V4L2_CTRL_FLAG_READ_ONLY 	0x0004
#define V4L2_CTRL_FLAG_UPDATE 		0x0008
#define V4L2_CTRL_FLAG_INACTIVE 	0x0010
#define V4L2_CTRL_FLAG_SLIDER 		0x0020
#define V4L2_CTRL_FLAG_WRITE_ONLY 	0x0040

/*  Query flag, to be ORed with the control ID */
#define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000
登录后复制

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

[cpp] view plaincopyprint?

  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) /* 帧率调节 */  
V4L2_CID_CONTRAST               (V4L2_CID_BASE+1)            /* 对比度调节 */
V4L2_CID_SATURATION             (V4L2_CID_BASE+2)            /* 饱和度调节 */
V4L2_CID_AUDIO_VOLUME           (V4L2_CID_BASE+5)            /* 音量调节 */
V4L2_CID_AUDIO_MUTE             (V4L2_CID_BASE+9)            /* 静音设置 */
V4L2_CID_DO_WHITE_BALANCE       (V4L2_CID_BASE+13)           /* 白平衡调节 */
V4L2_CID_GAMMA                  (V4L2_CID_BASE+16)           /* 伽马值调节 */
V4L2_CID_EXPOSURE               (V4L2_CID_BASE+17)           /* 曝光度调节 */

V4L2_CID_PRIVATE_ATXX_FLASH     (V4L2_CID_PRIVATE_BASE + 2)  /* 闪光灯控制 */
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 命令的值:

[cpp] view plaincopyprint?

  1. struct v4l2_control {  
  2.     __u32            id;  
  3.     __s32            value;  
  4. };  
struct v4l2_control {
	__u32		     id;
	__s32		     value;
};
登录后复制

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

8、struct v4l2_tuner 与 VIDIOC_G_TUNER、VIDIOC_S_TUNER

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

[cpp] view plaincopyprint?

  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. };  
struct v4l2_tuner {
	__u32                   index;           /* 调谐器编号,由应用程序设置 */
	__u8                    name[32];        /* 调谐器名称 */
	enum v4l2_tuner_type    type;            /* 调谐器类型 */
	__u32                   capability;      /* 调谐器支持的操作 */
	__u32                   rangelow;        /* 最低频率值,单位为62.5Hz或者62.5KHz */
	__u32                   rangehigh;       /* 最高频率值 */
	__u32                   rxsubchans;      /* 接收的音频信号类型 */
	__u32                   audmode;         /* 当前音频播放形式 */
	__s32                   signal;          /* 信号强度 */
	__s32                   afc;             /* 自动频率控制 */
	__u32                   reserved[4];     /* 保留备用 */
};
其中
enum v4l2_tuner_type {
	V4L2_TUNER_RADIO             = 1,        /* 调频收音机 */
	V4L2_TUNER_ANALOG_TV         = 2,        /* 模拟电视高频头 */
	V4L2_TUNER_DIGITAL_TV        = 3,        /* 数字电视高频头 */
};
登录后复制
其中域 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 设置或获取当前频率值:

[cpp] view plaincopyprint?

  1. struct v4l2_frequency {  
  2.     __u32                 tuner;          /* 调谐器编号 */  
  3.     enum v4l2_tuner_type  type;           /* 调谐器类型 */  
  4.     __u32                 frequency;      /* 调谐器频率 */  
  5.     __u32                 reserved[8];  
  6. };  
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)。

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

[cpp] view plaincopyprint?

  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   
  21. #define _IOC_TYPEMASK    ((1   
  22. #define _IOC_SIZEMASK    ((1   
  23. #define _IOC_XSIZEMASK   ((1   
  24. #define _IOC_DIRMASK     ((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)  /* 读写方向左移 29bit */  
  37.          ((type) /* 幻数左移 8bit */  
  38.          ((nr)   /* 命令序号 */  
  39.          ((size) /* 参数大小左移 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)  
/*
 * Our DIR and SIZE overlap in order to simulteneously provide
 * a non-zero _IOC_NONE (for binary compatibility) and
 * 14 bits of size as on i386. Here's the layout:
 *
 *   0xE0000000   DIR            3bit
 *   0x80000000   DIR = WRITE    bit31
 *   0x40000000   DIR = READ     bit30
 *   0x20000000   DIR = NONE     bit29
 *   0x3FFF0000   SIZE (overlaps NONE bit)  13bit
 *   0x0000FF00   TYPE           8bit
 *   0x000000FF   NR (CMD)       8bit
 */
/* 各个域的长度 */
#define _IOC_NRBITS      8
#define _IOC_TYPEBITS    8
#define _IOC_SIZEBITS   13	/* Actually 14, see below. */
#define _IOC_DIRBITS     3
/* 各个域的掩码 */
#define _IOC_NRMASK      ((1 > _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)         (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
登录后复制
这里特别说明一下 _IO 宏,该宏没有可传递的变量,只用于发送命令。这是因为变量需要可变数据,只作为命令(比如 reset)使用时,没有必要判断设备上的数据,因此设备驱动程序没有必要执行文件相关的处理。在 v4l2 中使用示例如下:

[cpp] view plaincopyprint?

  1. #define VIDIOC_QUERYCAP      _IOR('V',  0, struct v4l2_capability)   
  2. #define VIDIOC_RESERVED       _IO('V',  1)   
  3. #define VIDIOC_S_FMT        _IOWR('V',  5, struct v4l2_format)   
  4. #define VIDIOC_STREAMON      _IOW('V', 18, int)  
#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 函数中:

[cpp] view plaincopyprint?

  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 
  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 
  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);  
static unsigned long cmd_input_size(unsigned int cmd)
{
#define CMDINSIZE(cmd, type, field) 				\
	case VIDIOC_##cmd: 					\
		return offsetof(struct v4l2_##type, field) + 	\  /* 域的偏移 */
			sizeof(((struct v4l2_##type *)0)->field);      /* 域的长度 */

	switch (cmd) {
		CMDINSIZE(ENUM_FMT,		fmtdesc,	type);
		CMDINSIZE(G_FMT,		format,		type);
		...
		CMDINSIZE(ENUM_FRAMESIZES,	frmsizeenum,	pixel_format);
		CMDINSIZE(ENUM_FRAMEINTERVALS,	frmivalenum,	height);
	default:
		return _IOC_SIZE(cmd);  /* 剩下的是需要全部拷贝的命令 */
	}
}

long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
{
	char	sbuf[128];          /* 在栈中分配128个字节空间用来储存命令的参数 */
	void    *mbuf = NULL;
	void	*parg = NULL;       /* 参数存放的首地址 */
	long	err  = -EINVAL;
	int     is_ext_ctrl;
	size_t  ctrls_size = 0;
	void __user *user_ptr = NULL;

	...
	/* 判断是否包含读写命令,如果是则将用户空间的参数值拷贝到内核 */
	if (_IOC_DIR(cmd) != _IOC_NONE) {
		/* 判断参数大小是否超过128字节 */
		if (_IOC_SIZE(cmd) 
登录后复制
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Shazam应用程序在iPhone中无法运行:修复 Shazam应用程序在iPhone中无法运行:修复 Jun 08, 2024 pm 12:36 PM

iPhone上的Shazam应用程序有问题?Shazam可帮助您通过聆听歌曲找到歌曲。但是,如果Shazam无法正常工作或无法识别歌曲,则必须手动对其进行故障排除。修复Shazam应用程序不会花费很长时间。因此,无需再浪费时间,请按照以下步骤解决Shazam应用程序的问题。修复1–禁用粗体文本功能iPhone上的粗体文本可能是Shazam无法正常运行的原因。步骤1–您只能从iPhone设置中执行此操作。所以,打开它。步骤2–接下来,打开其中的“显示和亮度”设置。步骤3–如果您发现启用了“粗体文本

iPhone上的蜂窝数据互联网速度慢:修复 iPhone上的蜂窝数据互联网速度慢:修复 May 03, 2024 pm 09:01 PM

在iPhone上面临滞后,缓慢的移动数据连接?通常,手机上蜂窝互联网的强度取决于几个因素,例如区域、蜂窝网络类型、漫游类型等。您可以采取一些措施来获得更快、更可靠的蜂窝互联网连接。修复1–强制重启iPhone有时,强制重启设备只会重置许多内容,包括蜂窝网络连接。步骤1–只需按一次音量调高键并松开即可。接下来,按降低音量键并再次释放它。步骤2–该过程的下一部分是按住右侧的按钮。让iPhone完成重启。启用蜂窝数据并检查网络速度。再次检查修复2–更改数据模式虽然5G提供了更好的网络速度,但在信号较弱

美国空军高调展示首个AI战斗机!部长亲自试驾全程未干预,10万行代码试飞21次 美国空军高调展示首个AI战斗机!部长亲自试驾全程未干预,10万行代码试飞21次 May 07, 2024 pm 05:00 PM

最近,军事圈被这个消息刷屏了:美军的战斗机,已经能由AI完成全自动空战了。是的,就在最近,美军的AI战斗机首次公开,揭开了神秘面纱。这架战斗机的全名是可变稳定性飞行模拟器测试飞机(VISTA),由美空军部长亲自搭乘,模拟了一对一的空战。5月2日,美国空军部长FrankKendall在Edwards空军基地驾驶X-62AVISTA升空注意,在一小时的飞行中,所有飞行动作都由AI自主完成!Kendall表示——在过去的几十年中,我们一直在思考自主空对空作战的无限潜力,但它始终显得遥不可及。然而如今,

特斯拉机器人进厂打工,马斯克:手的自由度今年将达到22个! 特斯拉机器人进厂打工,马斯克:手的自由度今年将达到22个! May 06, 2024 pm 04:13 PM

特斯拉机器人Optimus最新视频出炉,已经可以在厂子里打工了。正常速度下,它分拣电池(特斯拉的4680电池)是这样的:官方还放出了20倍速下的样子——在小小的“工位”上,拣啊拣啊拣:这次放出的视频亮点之一在于Optimus在厂子里完成这项工作,是完全自主的,全程没有人为的干预。并且在Optimus的视角之下,它还可以把放歪了的电池重新捡起来放置,主打一个自动纠错:对于Optimus的手,英伟达科学家JimFan给出了高度的评价:Optimus的手是全球五指机器人里最灵巧的之一。它的手不仅有触觉

70B模型秒出1000token,代码重写超越GPT-4o,来自OpenAI投资的代码神器Cursor团队 70B模型秒出1000token,代码重写超越GPT-4o,来自OpenAI投资的代码神器Cursor团队 Jun 13, 2024 pm 03:47 PM

70B模型,秒出1000token,换算成字符接近4000!研究人员将Llama3进行了微调并引入加速算法,和原生版本相比,速度足足快出了快了13倍!不仅是快,在代码重写任务上的表现甚至超越了GPT-4o。这项成果,来自爆火的AI编程神器Cursor背后团队anysphere,OpenAI也参与过投资。要知道在以快着称的推理加速框架Groq上,70BLlama3的推理速度也不过每秒300多token。 Cursor这样的速度,可以说是实现了近乎即时的完整代码文件编辑。有人直呼好家伙,如果把Curs

AI初创集体跳槽OpenAI,Ilya出走后安全团队重整旗鼓! AI初创集体跳槽OpenAI,Ilya出走后安全团队重整旗鼓! Jun 08, 2024 pm 01:00 PM

上周,在内部的离职潮和外部的口诛笔伐之下,OpenAI可谓是内忧外患:-侵权寡姐引发全球热议-员工签署「霸王条款」被接连曝出-网友细数奥特曼「七宗罪」辟谣:根据Vox获取的泄露信息和文件,OpenAI的高级领导层,包括Altman在内,非常了解这些股权回收条款,并且签署了它们。除此之外,还有一个严峻而紧迫的问题摆在OpenAI面前——AI安全。最近,五名与安全相关的员工离职,其中包括两名最著名的员工,“超级对齐”团队的解散让OpenAI的安全问题再次被置于聚光灯下。《财富》杂志报道称,OpenA

58行代码把Llama 3扩展到100万上下文,任何微调版都适用 58行代码把Llama 3扩展到100万上下文,任何微调版都适用 May 06, 2024 pm 06:10 PM

堂堂开源之王Llama3,原版上下文窗口居然只有……8k,让到嘴边的一句“真香”又咽回去了。在32k起步,100k寻常的今天,这是故意要给开源社区留做贡献的空间吗?开源社区当然不会放过这个机会:现在只需58行代码,任何Llama370b的微调版本都能自动扩展到1048k(一百万)上下文。背后是一个LoRA,从扩展好上下文的Llama370BInstruct微调版本中提取出来,文件只有800mb。接下来使用Mergekit,就可以与其他同架构模型一起运行或直接合并到模型中。所使用的1048k上下文

iPhone相机在室内灯光下闪烁/闪烁:修复 iPhone相机在室内灯光下闪烁/闪烁:修复 Jun 06, 2024 pm 02:28 PM

您的iPhone相机在室内灯光下闪烁或闪烁太多吗?您用iPhone拍摄的视频看起来不太好,因为灯光会亮起。您在视频上看到的闪烁是因为刷新率的差异。这是不正常的,有一些方法可以解决这个问题。此修复程序将帮助您解决iPhone视频中的连续闪烁或闪烁效果。修复–更改视频输出格式这种现象背后的原因是物体和相机镜头的刷新率之间存在明显差异。通常,如果您使用iPhone录制屏幕,视频会闪烁或闪烁很多。但是,如果您录制普通视频,它不应该闪烁。这种现象主要是因为屏幕的刷新率和相机的帧率不匹配。屏幕的刷新率通常

See all articles