linux驱动ioctl()cmd定义中 _IO, _IOR, _IOW, _IOWR 宏介绍

一、宏介绍

1. 在驱动中 ioctl() 参数 cmd 是应用发给驱动的命令代码,cmd 除了可区别的普通数字外,还可以使用包含有助于处理的几种相应信息的数字作为cmd,cmd为int型,32位,共分 4 个域:

bit31~bit30 2位为 "区别读写" 区,作用是区分是读取命令还是写入命令。
bit29~bit15 14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。
bit20~bit08 8位为 "魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
bit07~bit00 8位为 "区别序号" 区,是区分命令的命令顺序序号。

2. 宏定义为:

#define _IOC(dir, type, nr, size) ((dir  << 30) |(type << 8) | (nr   << 0) | (size << 16))

其它宏都是由此宏衍生出来的:

///include/uapi/asm-generic/ioctl.h
#define _IO(type, nr)              _IOC(0, type, nr ,0)
#define _IOR(type, nr, size)       _IOC(2, type, nr, sizeof(size)) //有对参数大小的check,注意宏中的size是参数的类型
#define _IOW(type, nr, size)       _IOC(1, type, nr, sizeof(size)) //有对参数大小的check
#define _IOWR(type, nr, size)      _IOC(3, type, nr, sizeof(size)) //有对参数大小的check
#define _IOR_BAD(type, nr, size)   _IOC(2, type, nr, sizeof(size)) //没有对参数大小的check
#define _IOW_BAD(type, nr, size)   _IOC(1, type, nr, sizeof(size)) //有对参数大小的check
#define _IOWR_BAD(type, nr, size)  _IOC(3, type, nr, sizeof(size)) //有对参数大小的check

下面还定义了一些帮助宏:

///include/uapi/asm-generic/ioctl.h
#define _IOC_DIR(nr)    (nr >> 30) & (1<<2 -1)
#define _IOC_TYPE(nr)   (nr >> 8) & (1<<8 -1)
#define _IOC_NR(nr)     (nr >> 0) & (1<<8 -1)
#define _IOC_SIZE(nr)   (nr >> 16) & (1<<14 -1)

#define IOC_IN         1 << 30
#define IOC_OUT        2 << 30
#define IOC_INOUT      3 << 30
#define IOCSIZE_MASK  (1<<14 - 1) << 16
#define IOCSIZE_SHIFT 16

注:为了阅读方便,这里把出于使用安全考虑的括号给去掉了。比如 _IOC_NR(CMD1) 就可以知道CMD1这个命令是哪个命令了,_IOC_NR(CMD_LAST) 就可以知道一共有多少个命令了。

 

二、说明

1. 使用 _IO、_IOR、_IOW、_IOWR 定义的cmd命令,只要nr不重复,就不会出现重复的命令。

2. 驱动可以使用这些宏,也可以不使用,可理解为这个是属于驱动自己定义的协议自己遵守。

3. 使用这些宏可以增加代码的可读性,当当看到驱动自己定义的cmd命令宏的时候,就可以知道是读还是写,以及cmd跟的arg参数的大小。

4. 对于负载的驱动,建议使用,比如Android中的binder驱动,ioctl()的二级cmd(BC_XXX/BR_XXX)是和数据都放在buffer中的,使用这些宏后,读取到命令后,就可以知道接下来的args要读取多少字节。

 

三、举例

enum ctrl_cmd_id {
    SET_FPS = 1,
    GET_FPS,
    ...
    CMD_ID_MAX
};

//SET_FPS 命令的参数结构
struct ctrl_data {
    pid_t pid;
    int level;
};

#define DEMO_MAGIC 0xee
#define CMD_ID_SET_FPS \
    _IOWR(DEMO_MAGIC, SET_FPS, struct ctrl_data)
#define CMD_ID_GET_FPS \
    _IOWR(DEMO_MAGIC, GET_FPS, unsigned int)
...


static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    void __user *uarg = (void __user *)arg;

    /* 通过这两个成员来判断cmd是否合法 */
    if (_IOC_TYPE(cmd) != DEMO_MAGIC)
        return -EINVAL;

    if (_IOC_NR(cmd) >= CMD_ID_MAX)
        return -EINVAL;

    ...
}

 

四、补充

1. 可参考binder启动中的使用。

 

posted on 2018-01-18 19:45  Hello-World3  阅读(1387)  评论(0编辑  收藏  举报

导航