linux驱动程序开发中,ioctl命令编号问题,应该遵循include/asm/ioctl.h和Documentation/ioctl-number.txt这两个文件。一般命令使用四个字段来组成32位的值来编码:类型(幻数)、序数、传送方向以及参数大小。 Documentation/ioctl-number.txt已经罗列出了内核中使用的幻数,我们的驱动程序中应该避免与其冲突。
定义号码的四个字段如下所示:
type 幻数。选择一个号码,在整个程序中使用,该字段占8位。(_IOC_TYPEBITS)
number 序数。依次按顺序增长,也占8位。(_IOC_NRBITS)
direction。 数据的传输方向。这个方向是从用户的角度来看的,_IOC_NONE(没有数据传输),_IOC_READ(从驱动读出数据到用户)、_IOC_WRITE(从用户写数据到驱动)以及_IOC_READ|_IOC_WRITE(双向传输数据)。
size 所涉及的用户数据的大小。与具体的体系结构相关,通常占13位或14位。_IOC_SIZEBITS定义其具体位数。
文件中定义了一些常用的宏来构造命令编号。我们拿2.6.14版本来举例。其中我们需要的有如下几个宏定义,其他属于从已定义的命令中解析各个字段的值,我们暂不考虑。
/* 定义了每个字段的长度:*/
**************************************************
_IO, _IOR, _IOW, _IOWR 宏的用法与解析
在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值。cmd除了可区别数字外,还包含有助于处理的几种相应信息。 cmd的大小为 32位,共分 4 个域:
bit31~bit30 2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。
bit29~bit15 14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。
bit20~bit08 8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
bit07~bit00 8位为 "区别序号" 区,是区分命令的命令顺序序号。
像 命令码中的 “区分读写区” 里的值可能是 _IOC_NONE (0值)表示无数据传输,_IOC_READ (读), _IOC_WRITE (写) , _IOC_READ|_IOC_WRITE (双向)。
内核定义了 _IO() , _IOR() , IOW() 和 _IOWR() 这 4 个宏来辅助生成上面的 cmd 。下面分析 _IO() 的实现,其它的类似。
*********************************************************************************************
*****************************************************8
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
/*定义了每个字段在32位整数中的偏移量*/
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
/*定义了数据传输方向,从用户角度来看 */
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
#define _IOC_READ 2U
/*根据四个字段值生成一个命令编号,一般不直接使用*/
#define _IOC(dir,type,nr,size) /
(((dir) << _IOC_DIRSHIFT) | /
((type) << _IOC_TYPESHIFT) | /
((nr) << _IOC_NRSHIFT) | /
((size) << _IOC_SIZESHIFT))
/* 真正的根据类型、序号、方向和大小四个参数生成命令编号的宏,根据具体情况选择以下四个之一*/
#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)) /*双向传输数据*/
使用例子:
使用k作为幻数:
#define MY_MAGIC_DATA 'k'
#define MY_IOC_PRINT_DATA _IO(MY_MAGIC_DATA, 1)
#define MY_IOC_GET_DATA _IOR(MY_MAGIC_DATA, 2, int)
#define MY_IOC_SET_DATA _IOW(MY_MAGIC_DATA, 3, int)
#define MY_IOC_EXCHANGE_DATA _IOWR(MY_MAGIC_DATA,4,int)