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

应用程序中的ioctl(系统IO的内容):

int ioctl(int fd, int cmd, ...)/*
fd对应于应用程序传递的文件描述符fd
cmd 由用户空间直接不经修改的传递给驱动程序
... 可选,就是要传递到kernel的参数,可以是结构体指针等内容。
*/

驱动程序中,对应的ioctl

struct file_operations {
struct module *owner;
int (*ioctl) (struct inode *inode, struct file *filep, unsigned int cmd, unsigned long args);
long (*unlocked_ioctl) (struct file *filep, unsigned int cmd, unsigned long args);
......
}
/*
在驱动程序中,ioctl和unlocked_ioctl的区别:
在2.6.36以后linux的内核中,只支持unlocked_ioctl(),不支持ioctl()。
2.6.35.7内核中,两个函数都可以使用。
*/

在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。

在Linux核心中是这样定义一个命令码的:

| 设备类型  | 序列号 |  方向 | 数据尺寸 ||----------|--------|------|-------- || 8 bit   |  8 bit   | 2 bit |8~14 bit||----------|--------|------|-------- |

这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。

1、定义命令:
  内核提供了一些宏来帮助定义命令:

//nr为序号,datatype为数据类型,如int
_IO(type, nr ) //没有参数的命令
_IOR(type, nr, datatype) //从驱动中读数据
_IOW(type, nr, datatype) //写数据到驱动
_IOWR(type,nr, datatype) //双向传送

以上几个宏的使用格式为:
_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() 编译命令。

2、实现命令:
  定义好了命令,下一步就是要实现ioctl函数了,ioctl的实现包括三个技术环节:

1)返回值;
  ioctl函数的实现是根据命令执行的一个switch语句,但是,当命令不能匹配任何一个设备所支持的 命令时,通常返回-EINVAL(非法参数);
2)参数使用;
  用户空间使用  int ioctl(int fd,unsinged long cmd,…)  时,…就是要传递的参数;
  再通过内核空间使用  int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)或者long (*unlocked_ioctl) (struct file *filep, unsigned int cmd, unsigned long args);  中的arg传递;
  如果arg是一个整数,可以直接使用;
  如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。

内部有检查的,不需要检测的函数接口:

copy_from_user
copy_to_user
get_user
put_user

需要检测的函数接口:

__get_user
__put_user

检测函数access_ok():

static inline int access_ok(int type, const void *addr, unsigned long size)/*
type :是VERIFY_READ 或者VERIFY_WRITE用来表明是读用户内存还是写用户内存;
addr:是要操作的用户内存地址;
size:是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数就等于sizeof(int);返回值:Access_ok返回一个布尔值:1,是成功(存取没问题);0,是失败,ioctl返回-EFAULT;
*/

3)命令操作;

switch(cmd)
{case:... ...
}

如果不用ioctl的话,也可以实现对设备I/O通道的控制,例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数 据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员 自己也会头昏眼花的。所以,我们就使用ioctl来实现控制的功能。要记住,用户程序所作的只是通过命令码(cmd)告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。

ioctl其实没有什么很难的东西需要理解,关键是理解cmd命令码是怎么在用户程序里生成并在驱动程序里解析的,程序员最主要的工作量在switch{case}结构中,因为对设备的I/O控制都是通过这一部分的代码实现的。

Linux驱动的ioctl函数简要说明相关推荐

  1. Linux驱动设计ioctl函数的cmd参数不能为2

    Linux驱动程序设计的时候偶然发现的ioctl()函数的cmd参数不能为2,如果为2,ioctl()函数返回-1,网上说就是这样的,正常,不知道为什么,stack overflow上有一个外国学友的 ...

  2. linux ioctl命令,关于LINUX下的ioctl函数

    驱动程序中ioctl函数的函数原型如下: int (*ioctl)(struct inode *inode, struct file *filp,unsigned int cmd, unsigned ...

  3. linux驱动之ioctl

    大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力. 一.在用户空间,使用ioctl系统调用来控制设备,原型如下: int ioctl(int fd,unsigned long cm ...

  4. (八)linux驱动之ioctl的使用

    这篇文章给大家讲解一下ioctl的简单使用,关于ioctl更详细的教程后面有机会单独写出来 (一)什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通道进行管理 ...

  5. linux驱动的入口函数module_init的加载和释放

    就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件: #include <linux/init.h> # ...

  6. linux驱动的入口函数module_init的加载和释放(转)

    像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件: #include <linux/init.h> #i ...

  7. linux驱动中probe函数是怎么调用的

    linux驱动的三个概念:设备.驱动.总线 probe何时被调用:在总线上驱动和设备的名字匹配,就会调用驱动的probe函数 probe函数被调用后,系统就调用platform设备的probe函数完成 ...

  8. linux驱动的中断函数,嵌入式Linux驱动开发(四)——字符设备驱动之中断方式以及中断方式获取按键值...

    之前我们完成了关于通过查询的方式获取按键键值的驱动程序,可以参考:嵌入式Linux开发--裸板程序之中断控制器. 虽然读取键值没有什么问题,但是测试程序占用CPU过高,一直在不断的查询,资源消耗过大, ...

  9. linux probe函数调用,Linux驱动中probe函数何时被调用?

    关于struct device_driver结构中的probe探测函数的调用 用SourceInsight跟踪: 从driver_register看起,此处我的这里是: int driver_regi ...

  10. 嵌入式linux应用层中断函数,嵌入式LINUX驱动开发(中断处理函数)

    嵌入式LINUX驱动开发(中断处理函数) 2020年08月11日 | 萬仟网网络运营 | 我要评论 嵌入式LINUX驱动学习之7中断相关(一)中断处理函数一.函数.头文件及说明二.编译举例:一.函数. ...

最新文章

  1. android框架连接mysql_Android:ROOM数据库框架
  2. kali linux 安装java_kali linux安装java
  3. DTCC 2020 | 阿里云吉剑南:在线分析进入Fast Data时代的关键技术解读
  4. Context结构图
  5. .NET Core HttpClient请求异常思考
  6. mysql current_timestamp 不自动更新_MySQL ON UPDATE CURRENT_TIMESTAMP不更新
  7. 看雪KSSD-windows驱动
  8. JavaScript试题练习题
  9. 云原生时代,开发者如何构筑容器安全?
  10. 求链表的倒数第m个元素
  11. 国家一级计算机考试选择题题库,计算机一级考试选择题题库与答案2016
  12. (转载)图文推荐给开发人员非常实用的站点
  13. winform中导入excel表格
  14. 损失函数、代价函数、目标函数、适应度函数的区别与联系
  15. pdf文件如何生成目录 wps_怎样快速为WPS文档增加目录
  16. matlab 空集判定,在使用matlab 符号运算中的solve函数时,为啥计算的结果是空集?该怎么办?...
  17. G盘格式化了,要怎样恢复文件
  18. 严格模式和普通模式之间的区别
  19. Raspberry Pi 3安装配置Raspbian过程
  20. android beam小米,小米手机自定义空白卡模拟加密卡门禁卡教程

热门文章

  1. C#利用Process关闭所有的IE窗口
  2. UserCF、 KNN 和 TopK
  3. AACL2022会议征稿
  4. 基于Transformers+CNN/LSTM/GRU的文本分类
  5. 【顶会论文解析】罪行预测
  6. 机器学习—XGboost的原理、工程实现与优缺点
  7. pandas—显示行索引与列索引(数组或者列表)
  8. 机器学习基础算法29-EM实践
  9. LeetCode学习记录(4-6)
  10. [论文翻译]Sequence to Sequence Learning with Neural Networks