首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

ioctl的命令cmd构造详解

ioctl的命令cmd构造详解

经常碰到魔数,比如:
#define PLATDRV_MAGIC 0X60
#define LED_OFF    _IO(PLATDRV_MAGIC,0X18)
#define LED_ON      _IO(PLATDRV_MAGIC,0X19)
然后我们会调用ioctl(fd,LED_OFF)或者ioctl(fd,LED_ON)
网上资料:_IO(魔数,基数)
                  _IOR(魔数,基数,变量型)
                  _IOW(魔数,基数,变量型)
                  _IOWR(魔数,基数,变量型)

魔数 (magicnumber):
魔数范围为 0~255 。通常,用英文字符 "A" ~ "Z" 或者 "a" ~"z" 来表示。设备驱动程序从传递进来的命令获取魔数,然后与自身处理的魔数想比较,如果相同则处理,不同则不处理。魔数是拒绝误使用的初步辅助状态。设备驱动程序可以通过 _IOC_TYPE(cmd)来获取魔数。不同的设备驱动程序最好设置不同的魔数,但并不是要求绝对,也是可以使用其他设备驱动程序已用过的魔数。
基(序列号)数:
基数用于区别各种命令。通常,从 0开始递增,相同设备驱动程序上可以重复使用该值。例如,读取和写入命令中使用了相同的基数,设备驱动程序也能分辨出来,原因在于设备驱动程序区分命令时使用 switch ,且直接使用命令变量 cmd值。创建命令的宏生成的值由多个域组合而成,所以即使是相同的基数,也会判断为不同的命令。设备驱动程序想要从命令中获取该基数,就使用下面的宏:
_IOC_NR (cmd)
变量型:
变量型使用 arg 变量指定传送的数据大小,但是不直接代入输入,而是代入变量或者是变量的类型,原因是在使用宏创建命令,已经包含了 sizeof() 编译命令

#define _IO(type,nr)    _IOC(_IOC_NONE,(type),(nr),0)
再看 _IOC() 的定义://include/asm/ioctl.h
#define _IOC(dir,type,nr,size)   
(((dir)<< _IOC_DIRSHIFT)|((type)<<_IOC_TYPESHIFT)|((nr)<<_IOC_NRSHIFT)|((size) << _IOC_SIZESHIFT))
#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)    //16+14=30
#define _IOC_TYPESHIFT    (_IOC_NRSHIFT+_IOC_NRBITS)      //0+8=8
#define _IOC_NRSHIFT    0
#define _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)//8+8=16
#define _IOC_NRBITS    8
#define _IOC_TYPEBITS    8  //
#define _IOC_SIZEBITS     14 //大小(size)字段的字位宽度,14bits
由上面的定义,往上推得到:
_IOC_TYPESHIFT = 8

_IOC_SIZESHIFT = 16
_IOC_DIRSHIFT = 30
所以#define _IOC(dir,type,nr,size)   (((dir)<<30)|((type)<<8)|((nr)<<0)((size)<<16))
cmd的大小为 32位,共分 4 个域:
bit31~bit30 2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。//dir
bit29~bit16 14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。//size
bit15~bit08  8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。//type
bit07~bit00   8位为 "区别序号" 区,是区分命令的命令顺序序号。//nr
所以#define LED_OFF    _IO(PLATDRV_MAGIC,0X18)就是_IOC(_IOC_NONE,PLATDRV_MAGIC,0X18,0)
其中_IOC_NONE为0 ,也即是:
((0<<30)|(0x60<<8)|(0x18<<0)|(0<<16))
现在我们来看ioctl(fd,LED_OFF)
ioctl的原型:int ioctl(int fd, int cmd, …)
其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是
一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。
在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。
怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl
中命令码是唯一联系用户程序命令和驱动程序支持的途径。
核心中是这样定义一个命令码的:


| 设备类型 | 数据尺寸 | 魔数   |  序列号 |
|      2 bit    |    14 bit   | 8 bit    |     8bit   |
这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。
_IOR(),_IOW(),IORW()的定义
#define _IOR(type,nr,size)    _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))

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

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

再看 _IOC() 的定义:
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
所以,
(dir)  << _IOC_DIRSHIFT) 表是dir往左移30位,即移到bit31~bit30 两位上,得到方向(读写)的属性;
(size) << _IOC_SIZESHIFT) 位左移16位得到“数据大小”区;
(type) << _IOC_TYPESHIFT) 左移8位得到"魔数区" ;
(nr)   << _IOC_NRSHIFT) 左移0位( bit7~bit0) 。
返回列表