关于构造IOCTL命令的学习心得

来源:互联网 发布:安卓5.1 java模拟器 编辑:程序博客网 时间:2024/05/15 10:25
在编写ioctl代码之前,需要选择对应不同命令的编号。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非串行设备输入流修改波特率,如果每一个ioctl命令都是唯一的,应用程序进行这种操作时就会得到一个EINVAL错误,而不是无意间成功地完成了意想不到的操作。

   要按Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.h和Doucumention/ioctl-number.txt这两个文件。头文件定义了要使用的位字段:类型(幻数)、序数、传送方向以及参数大小等。ioctl-number.txt文件中罗列了内核所使用的幻数,选择自己的幻数要避免和内核冲突。以下是对include/asm/ioctl.h中定义的宏的注释:

#define         _IOC_NRBITS          8                               //序数(number)字段的字位宽度,8bits

#define         _IOC_TYPEBITS      8                               //幻数(type)字段的字位宽度,8bits

#define         _IOC_SIZEBITS       14                              //大小(size)字段的字位宽度,14bits

#define         _IOC_DIRBITS         2                               //方向(direction)字段的字位宽度,2bits

#define         _IOC_NRMASK        ((1 << _IOC_NRBITS)-1)    //序数字段的掩码,0x000000FF

#define         _IOC_TYPEMASK   ((1 << _IOC_TYPEBITS)-1)  //幻数字段的掩码,0x000000FF

#define         _IOC_SIZEMASK     ((1 << _IOC_SIZEBITS)-1)   //大小字段的掩码,0x00003FFF

#define         _IOC_DIRMASK      ((1 << _IOC_DIRBITS)-1)    //方向字段的掩码,0x00000003

#define        _IOC_NRSHIFT       0                                   //序数字段在整个字段中的位移,0

#define        _IOC_TYPESHIFT   (_IOC_NRSHIFT+_IOC_NRBITS)         //幻数字段的位移,8

#define        _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)  //大小字段的位移,16

#define        _IOC_DIRSHIFT      (_IOC_SIZESHIFT+_IOC_SIZEBITS)    //方向字段的位移,30

/*

 * Direction bits.

 */

#define _IOC_NONE     0U     //没有数据传输

#define _IOC_WRITE   1U     //向设备写入数据,驱动程序必须从用户空间读入数据

#define _IOC_READ     2U     //从设备中读取数据,驱动程序必须向用户空间写入数据

/*

*_IOC 宏将dirtypenrsize四个参数组合成一个cmd参数,如下图:

*

*/

#define _IOC(dir,type,nr,size) \

       (((dir)  << _IOC_DIRSHIFT) | \

        ((type) << _IOC_TYPESHIFT) | \

        ((nr)   << _IOC_NRSHIFT) | \

        ((size) << _IOC_SIZESHIFT))

/*

* used to create numbers 

*/

//构造无参数的命令编号

#define _IO(type,nr)             _IOC(_IOC_NONE,(type),(nr),0) 

//构造从驱动程序中读取数据的命令编号

#define _IOR(type,nr,size)     _IOC(_IOC_READ,(type),(nr),sizeof(size)) 

//用于向驱动程序写入数据命令

#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))

//用于双向传输

#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* 

*used to decode ioctl numbers..

 */

//从命令参数中解析出数据方向,即写进还是读出

#define _IOC_DIR(nr)          (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)

//从命令参数中解析出幻数type

#define _IOC_TYPE(nr)              (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)

//从命令参数中解析出序数number

#define _IOC_NR(nr)           (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)

//从命令参数中解析出用户数据大小

#define _IOC_SIZE(nr)         (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

/* ...and for the drivers/sound files... */

#define IOC_IN            (_IOC_WRITE << _IOC_DIRSHIFT)

#define IOC_OUT         (_IOC_READ << _IOC_DIRSHIFT)

#define IOC_INOUT     ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)

#define IOCSIZE_MASK      (_IOC_SIZEMASK << _IOC_SIZESHIFT)

#define IOCSIZE_SHIFT      (_IOC_SIZESHIFT)


 

原文是说type的内容叫幻数(magic number),强调它是一个8位二进制数(number)。

因此不同type的命令就有不同的magic number,因此命令编码自然就不同了。但如果两个命令type相同,它们的magic number就相同,于是就不能仅靠type字段区分了。于是nr字段就起作用了:

number
The ordinal (sequential) number. It’s eight bits (_IOC_NRBITS) wide.

nr(number)即序号,一般地我们从0开始编号。由于nr字段为8位二进制数,所以nr的取值范围为0~255。同一种type的命令每个nr值对应唯一一个命令。