测试程序 ioctl , 不同版本下的ioctl存在区别

inux-2.6.36之前的内核:

[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)  
  2. (1)inode和file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改  
  3. 文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针;  
  4. (2)cmd:从用户空间传下来的命令参数;  
  5. (3)arg:可选参数,主要用于定义应用层和驱动层进行命令调用时候是否涉及数据读写;</span></span>  
Linux-2.3.36之后的内核:
[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int (*ioctl)(struct file *filp,unsigned int cmd,unsigned long arg)  
  2. (1)file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改  
  3. 文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针;  
  4. (2)cmd:从用户空间传下来的命令参数;  
  5. (3)arg:可选参数,主要用于定义应用层和驱动层进行命令调用时候是否涉及数据读写;</span>  
   从linux-2.6.36之后,已经由unlocked_ioctl替代原来的ioctl。其中驱动的变化就是函数参数去掉inode参数。这也是我前面提及到的我在项目开发过程中遇到的问题所在。那时候项目开发时,旧平台的内核是Linux-2.6.32的,新平台的内核是Linux-3.6.5的,所以要修改ioctl函数,去掉struct inode *inode这个参数就可以了。在驱动开发中,如果大家遇到像我这样ioctl调用失效的问题,可以查看一下内核版本,然后看看是否是ioctl函数实现不一样导致的!

3 再论Linux内核ioctl函数

3.1 什么是ioctl?

   ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下:
   int ioctl(int fd, ind cmd, …);
   其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。

3.2 ioctl实现原理

    在驱动当中实现的ioctl函数中,其实就是一个switch case的结构,每一个case对应一个命令码,对应一些命令操作。如何实现这些操作是软件工程师根据程序开发的需求来定制,因为每一个设备都是特定的,关键在于如何组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序的路径。
    在Linux内核中是这样定义一个命令码的:
    | 设备类型 | 序列号 | 方向 | 数据尺寸 |
    |----------|--------|------|----------|
    |   8bit   |  8bit  | 2bit | 2~14bit  |
    |----------|--------|------|----------|
    其实,在Linux内核中,命令就是一个整数形式的命令码。可是这样复杂的命令码看起来不够直观,开发人员很难记住,为此,Linux内核专门为这些命令码提供了一些宏,这些宏可以根据便于理解的字符串生成命令码,或者是从,命令码中得到一些用户可以理解的字符串用来标明相应设备的设备类型、设备序列号、数据传输方向、数据传输尺寸。

3.2.1 Linux内核ioctl命令宏定义详解

    在设备驱动开发中,应用层和驱动层进行命令交互和数据传输过程中无非这四种情况:
    1.无任何参数的命令控制;
    2.应用层从驱动中读取数据;
    3.应用层写数据到驱动层;
    4.应用层和驱动层进行双向的命令交互;
    在Linux内核中定义了以下宏用来定义命令码:
[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. //nr为序号,datatype为数据类型,如int  
  2. _IO(type, nr ) //没有参数的命令  
  3. _IOR(type, nr, datatype) //从驱动中读数据  
  4. _IOW(type, nr, datatype) //写数据到驱动  
  5. _IOWR(type,nr, datatype) //双向传送</span>  
    ioctl命令定义实例展示:(液晶驱动ioctl代码截取)
[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #define DISP_IOC_MAGIC 'disp' //定义类型  
  2. #define DISP_IOCSET _IOW(DISP_IOC_MAGIC,0,int)  
  3. #define DISP_IOCGQSET _IOR(DISP_IOC_MAGIC, 1, int)  

3.2.2 ioctl函数命令实现

     实现ioctl函数包含以下三个技术环节:
(1) 返回值:
    ioctl函数根据应用层传入的相应命令到驱动层的ioctl去匹配相应的命令,命令的执行在一个switch语句中,如果命令不匹配,通常返回一个-ENVAL值;
(2) 参数使用:
    用户使用 int ioctl(int fd,unsigned long cmd,....)时候传递参数;
    驱动层调用 int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)中的arg参数进行与应用层的参数传递,如果arg是一个整数,可以直接使用,如果是一个指针,则要判断指针的地址的合法性,因此使用之前需要进行正确的检查。不需要检查的是:
[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. copy_from_user  
  2. copy_to_user  
  3. get_user  
  4. put_user  
    需要检查的是:
[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. _get_user  
  2. __put_user  
    检测函数access_ok():
[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static inline int access_ok(int type, const voidvoid *addr, unsigned long size)  
  2. /* 
  3. type :是VERIFY_READ 或者VERIFY_WRITE用来表明是读用户内存还是写用户内存; 
  4. addr:是要操作的用户内存地址; 
  5. size:是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数就等于sizeof(int); 
  6.  
  7. 返回值:Access_ok返回一个布尔值:1,是成功(存取没问题);0,是失败,ioctl返回-EFAULT; 
  8.  
  9. */  

3.3 驱动ioctl函数代码实现框架

[objc] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static int xxx_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)  
  2. {  
  3.      switch (cmd)   
  4.      {  
  5.     case XXX_IOCTL_CMD_1:  
  6.         {  
  7.         //to do  
  8.         }  
  9.         break;   
  10.         case XXX_IOCTL_CMD_2:  
  11.         {  
  12.         //to do  
  13.         }  
  14.         break;    
  15.         default:  
  16.            break;  
  17.      }  
  18. }    </span>  
     OK,关于ioctl函数的实现,就总结到这里吧,欢迎大家提意见和互相学习!!!

posted on 2016-11-09 14:47  Red_Point  阅读(1548)  评论(0编辑  收藏  举报

导航