下面我们看一下上述宏在内核中的原型:
[cpp]
/*
* 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_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_XSIZEMASK ((1 << (_IOC_SIZEBITS+1))-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
/* 各个域的偏移 */
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS) /* 8 */
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS) /* 16 */
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS) /* 29 */
/* 读写域的值 */
#define _IOC_NONE 1U
#define _IOC_READ 2U
#define _IOC_WRITE 4U
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \ /* 读写方向左移 29bit */
((type) << _IOC_TYPESHIFT) | \ /* 幻数左移 8bit */
((nr) << _IOC_NRSHIFT) | \ /* 命令序号 */
((size) << _IOC_SIZESHIFT)) /* 参数大小左移 16bit */
/* 宏原型,这里将会根据传递的数据类型取其长度 */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
/* 获取各个域的值 */
#define _IOC_DIR(nr) (((nr) >> _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]
#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 plaincopy
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;