linux内核驱动中_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() 的实现,其它的类似:

在 asm-generic/ioctl.h 里可以看到 _IO() 的定义

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

再看 _IOC() 的定义

#define _IOC(dir,type,nr,size) \
                   (((dir)  << _IOC_DIRSHIFT) | \
                   ((type) << _IOC_TYPESHIFT) | \
                   ((nr)   << _IOC_NRSHIFT) | \
                   ((size) << _IOC_SIZESHIFT))

可见,_IO() 的最后结果由 _IOC() 中的 4 个参数移位组合而成。
再看 _IOC_DIRSHIT 的定义

#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)

      _IOC_SIZESHIFT 的定义

#define _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)

      _IOC_TYPESHIF 的定义

#define _IOC_TYPESHIFT    (_IOC_NRSHIFT+_IOC_NRBITS)

      _IOC_NRSHIFT 的定义

#define _IOC_NRSHIFT    0

      _IOC_NRBITS 的定义

#define _IOC_NRBITS    8

      _IOC_TYPEBITS 的定义

#define _IOC_TYPEBITS    8

由上面的定义,往上推得到

引 用

_IOC_TYPESHIFT = 8

_IOC_SIZESHIFT = 16

_IOC_DIRSHIFT = 30

所以,(dir)  << _IOC_DIRSHIFT) 表是 dir 往左移 30 位,即移到 bit31~bit30 两位上,得到方向(读写)的属性;
       (size) << _IOC_SIZESHIFT) 位左移 16 位得到“数据大小”区;
       (type) << _IOC_TYPESHIFT) 左 移 8位得到"魔数区" ;
       (nr)   << _IOC_NRSHIFT)      左移 0 位( bit7~bit0) 。
这样,就得到了 _IO() 的宏值。

这几个宏的使用格式为

  • _IO (魔数, 基数);
  • _IOR (魔数, 基数, 变量型)
  • _IOW  (魔数, 基数, 变量型)
  • _IOWR (魔数, 基数,变量型 )

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

引用

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

而 _IOC_TYPECHECK() 的定义正是:

引用

#define _IOC_TYPECHECK(t) (sizeof(t))

设备驱动程序想要从传送的命令获取相应的值,就要使用下列宏函数:
      _IOC_SIZE(cmd)

_IO 宏

该宏函数没有可传送的变量,只是用于传送命令。例如如下约定:

引用

#define TEST_DRV_RESET _IO (‘Q‘, 0)

此时,省略由应用程序传送的 arg 变量或者代入 0 。在应用程序中使用该宏时,比如:

ioctl (dev, TEST_DEV_RESET, 0)   或者  ioctl (dev, TEST_DRV_RESET) 。
这是因为变量的有效因素是可变因素。只作为命令使用时,没有必要判 断出设备上数据的输出或输入。因此,设备驱动程序没有必要执行设备文件大开选项的相关处理。

_IOR 宏
     该函数用 于创建从设备读取数据的命令,例如可如下约定:

引用

#define TEST_DEV_READ  _IRQ(‘Q‘, 1, int)

这说明应用程序从设备读取数据的大小为 int 。下面宏用于判断传送到设备驱动程序的 cmd 命令的读写状态:
     _IOC_DIR (cmd)
运行该宏时,返回值的类型 如下:

  • _IOC_NONE                             :  无属性
  • _IOC_READ                             :  可读属性
  • _IOC_WRITE                           : 可写属性
  • _IOC_READ | _IOC_WRITE : 可读,可写属性

使用该命令时,应用程序的 ioctl() 的 arg 变量值指定设备驱动程序上读取数据时的缓存(结构体)地址。
_IOW 宏
      用于创建设 备上写入数据的命令,其余内容与 _IOR 相同。通常,使用该命令时,ioctl() 的 arg 变量值指定设备驱动程序上写入数据时的缓存(结构体)地址。
_IOWR 宏
      用于创建设备上读写数据的命令。其余内 容与 _IOR 相同。通常,使用该命令时,ioctl() 的 arg 变量值指定设备驱动程序上写入或读取数据时的缓存 (结构体) 地址。
_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)))

时间: 2024-10-10 13:09:40

linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析的相关文章

_IO, _IOR, _IOW, _IOWR 宏的用法与解析

今天在写字符驱动验证程序的时候要用到ioctl函数,其中有一个cmd参数,搞了半天也不了解是什么意思,那个cmd还有什么命令码了什么的,还好google下,觉得这篇文章写的不错,就转来看看:在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个域: bit31~bit30 2位为 “区别读写” 区,作用是区分是读取命令还是写入命令. bit29~bit

Linux内核中_IO,_IOR,_IOW,_IOWR宏的用法

#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0) #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)  

第02节:Linux 内核驱动中的指定初始化

2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: int a[10] = {0,1,2,3,4,5,6,7,8}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a[9] 赋值,所以编译器会将 a[9] 默认设置为0.当数组长度比较小时,使用这种方式初始化比较方便.当数组比较大,而且数组里的非零元素并不连续时,这时候再按照固定顺序初始化就比较麻烦了. 比如,我们定义一个数组 b[100],其中 b[10].b[30] 需要初始

Linux设备驱动中的阻塞和非阻塞I/O

[基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件后再进行操作.被挂起的进程进入休眠状态(不占用cpu资源),从调度器的运行队列转移到等待队列,直到条件满足. 2.非阻塞 非阻塞操作是指在进行设备操作是,若操作条件不满足并不会挂起,而是直接返回或重新查询(一直占用CPU资源)直到操作条件满足为止. 当用户空间的应用程序调用read(),write()等方法时,若设备的资源不能被获取,而用户又希望以阻塞的方式来访问设备,驱动程序应当在设备驱动层的

linux设备驱动中的并发控制

并发指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源的访问则很容易导致竞态 linux内核中主要竞态1.多对称处理器的多个CPU  2.单CPU内进程与抢占它的进程 3.中断(硬中断.软中断.Tasklet.下半部)与进程之间访问共享内存资源的代码区称为“临界区”,临界区需要被以某种互斥机制加以保护,中断屏蔽.原子操作.自旋锁和信号量等是linux设备驱动中可采用的互斥途径. 这几个互斥的介绍: 1.中断屏蔽,这个主要用于单CPU,中断屏蔽将使得中断和进程之间的并发不再发生.使用方

Linux内核驱动编程

Linux内核驱动编程 2015-02-12 驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处. 下面我们开始写程序: 一.初步helloword程序 首先是来一个简单的hello. hello.c代码: 1 /****************************** 2 3 the first program 4 5 Hello World! 6 7 ******************************/ 8 9 #include <linux/mod

linux 内核驱动--Platform Device和Platform_driver注册过程

linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Platform_driver . Linux 中大部分的设备驱动,都可以使用这套机制 , 设备用 Platform_device 表示,驱动用 Platform_driver 进行注册. Linux platform driver 机制和传统的 device driver 机制 ( 通过 driver_

Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2G 8. 选择网络地址转换 9.设置IO控制器 10. 选择默认磁盘类型 11.创建一个新的虚拟磁盘 12.设置磁盘大小 13.选择路径保存虚拟磁盘 14. 完成虚拟机创建 15.设置虚拟机 16.选择opensuse镜像 17.开启虚拟机 18.虚拟机启动 19.安装opensuse 20.安装程

20150518 Linux设备驱动中的并发控制

20150518 Linux设备驱动中的并发控制 2015-05-18 Lover雪儿 总结一下并发控制的相关知识: 本文参考:华清远见<Linux 设备驱动开发详解>—第7章 Linux 设备驱动中的并发控制,更多详细内容请看原书 一.并发与竞态 并发(concurrency)指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量.静态变量等)的访问则很容易导致竞态(race conditions). 在 Linux 内核中,主要的竞态发生于如下几种情况: