- 概述
- 目的: 通过设备驱动程序执行各种类型的硬件控制
- 用户空间的调用原型:int ioctl(int fd, unsigned long cmd, ...);
- fd 指的是 文件描述符
- ”…“ 代表可选参数,使用 ”…“ 可以关闭编译时的逻辑检查
- 习惯上使用 char *argp;
- 可选参数可以为空,可为整型,可以是指针;当使用指针时可以交换任意数量的数据。
- 驱动程序的原型实现:int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
- inode 和 filp两个指针对应于用户空间的 文件描述符 fd
- cmd 参数直接传输,不经任何修改
- arg 接收的是可选参数,接收类型永远是 unsigned long,参数类型检查被关闭,如果有错误也不进行报告
- 多数 ioctl 都是通过 switch 语句来完成的
- ioctl 命令选择
- ioctl 命令结构
- 参考文件
- include/asm/ioctl.h
- Documentation/ioctl-number.txt
- 位段结构
- type
- 幻数(类型),主要在Documentation/ioctl-number.txt 中定义
- 位宽:8位 ( _IOC_TYPEBITS )
- number
- 序数(顺序编号)
- 位宽:8位 ( _IOC_NRBITS )
- direction
- 数据传送的标志,定义数据传送的方向,该字段是一个位掩码,可以通过逻辑运算分离
- 可使用值
- 没有数据传输 (_IOC_NONE)
-
#ifndef _IOC_NONE
# define _IOC_NONE 0U
#endif
-
- _IOC_READ
-
#ifndef _IOC_READ
# define _IOC_READ 2U
#endif
-
- _IOC_WRITE
-
#ifndef _IOC_WRITE
# define _IOC_WRITE 1U
#endif
-
- 双向数据传输(_IOC_READ|_IOC_WRITE)
- 没有数据传输 (_IOC_NONE)
- size
- 指示用户数据大小,与体系结构有关,通常是13位或者14位
- 宏定义:_IOC_SIZEBITS
- 系统不对字段进行检查,用户数据大时可以乎略
- type
- 参考文件
- 构造命令编号的宏
- 定义位置: <asm/ioctl.h>;type和number位字段通过参数传入,size位字段通过对 datatype 参数取 sizeof 获得。
- _IO(type, nr)
- 用于构造无参数的命令编号
- _IOR(type, nr, datatype)
- 用于构造从驱动程序中读取数据的命令编号
- _IOW(type, nr, datatype)
- 用于写入数据的命令
- _IOWR(type, nr, datatype)
- 用于双向传输
- 解开位段结构的宏
- 定义位置 <asm/ioctl.h>
- _IOC_DIR(nr)
- _IOC_TYPE(nr)
- _IOC_NR(nr)
- _IOC_SIZE(nr)
- ioctl 命令结构
- ioctl 命令号不能匹配的默认的返回值
- 非法参数:-ENVAL
- POSIX标准规定返回: -ENOTTY
- ioctl 的预定义命令
- 部分预定义命令可由内核识别,这些命令在使用时无法到达设备,由内核运行,并且因为编号冲突,结果不可预期
- 预定义命令分类
- 可用于任何文件(普通、设备、FIFO和套接字)的命令
- 幻数是 ”T“
- 只适用于普通文件的命令
- 特定于文件系统类型的命令
- 只能在宿主文件系统上执行
- 可用于任何文件(普通、设备、FIFO和套接字)的命令
- 实际预定义命令
- FIOCLEX
- 设置执行时关闭标志(File IOctl Close on EXec),设置后当调用进程执行一个新程序时,文件描述符将被关闭
- FIOUNCLEX
- 清除执行时关闭标志(File IOctl Not Close on EXec),恢复通常的文件行为,撤销 FIOCLEX 操作。
- FIOASYNC
- 设置或复位异步通知
- 修改 O_SYNC 标志,同样的工作可通过 fcntl 完成,因此 FIOASYNC 很少使用
- FIOQSIZE
- 返回文件或目录的大小。
- 不可用于设备文件,否则导致 ENOTTY 错误
- FIONBIO
- File IOctl Non-Blocking I/O, 即文件 ioctl 非阻塞型 I/O
- 该调用修改 filp->f_flags 中的 O_NONBLOCK 标志, 系统调用的第三个参数说明了是复位还是设置
- filp->f_flags 中的 O_NONBLOCK 通常由 fcntl 系统调用使用命令 F_SETFL 命令完成
- FIOCLEX
- 使用 ioctl 参数
- 当 ioctl 第三个参数是个指针时,会出现一些问题
- 当指针指向用户空间时,必须保证用户空间合法,否则应返回错误
- 地址验证函数 access_ok
- 函数声明及原型
- <asm/uaccess.h>
- int access_ok(int type, const void *addr, unsigned long size);
- 参数说明
- type
- VERIFY_READ
- 从用户空间读出数据
- VERIFY_WRITE
- 往用户空间写入数据
- 如果既要读取又要写入,则应该使用 VERIFY_WRITE,为超集
- VERIFY_READ
- addr
- 用户空间地址
- size
- 数据的字节数,如果为int,则是 sizeof(int)
- type
- 返回值是 bool 量
- 访问成功返回 1
- 访问失败返回 0,此时驱动程序应该返回 -EFAULT 给调用者
- 使用注意事项
- 没有完成验证内存的全部工作,只对进程对空间的访问权限进行验证,尤其确保指针没有指向内核空间
- 大多数程序不需要直接调用该函数,大多数内存管理函数会处理它
- 函数声明及原型
- 写入/读取数据函数(限于 1,2,4和8个字节)
-
写入用户空间
- put_user(datum, ptr);
- 进行地址检查,成功时返回 0, 失败时返回 -EFAULT
- __put_user(datum, ptr);
- 不进行地址检查,使用时需要自行调用 access_ok
读取用户空间
- get_user(local, ptr)
- __get_user(local, ptr)
当指针类型与指定类型不相符时,编译器返回 “conversion to non-scalar type requested"
此时必须使用 copy_to_user 或者 copy_from_user
- put_user(datum, ptr);
-
- 权能与受限操作
- 对权能检查的说明
- 对设备的访问一般由设备文件的权限控制,驱动程序不进行检查
- 对于一些附加操作,驱动程序需要进行附加检查以确认用户是否有权进行
- 权能(capability)
-
权能把特权操作划分为独立的组,这样某些特定用户或程序可被授权执行指定操作,同时又没有执行其它操作的权限
相关系统调用函数:capget 和 capset,因此可以在用户空间管理权能
-
- 定义文件
- <linux/capability.h>
- 驱动程序关心的权能
- CAP_DAC_OVERRIDE
- 超越文件或目录的访问权限,数据访问控制或DAC)的能力
- CAP_NET_ADMIN
- 执行网络管理任务的能力,包括那些影响网络接口的任务
- CAP_SYS_MODULE
- 载入或删除内核模块的能力
- CAP_SYS_RAMIO
- 执行”裸“ I/O操作的能力,例如访问设备端口或直接与 USB 通信
- CAP_SYS_ADMIN
- 截获的能力,它提供了访问许多系统管理操作的途径
- CAP_SYS_TTY_CONFIG
- 执行 tty 配置任务的能力
- CAP_DAC_OVERRIDE
- 权能检查
- 在执行一项特殊操作时,驱动程序应该检查调用进程是否有相关权能
- 函数实现
- <sys/sched.h>
- int capable(int capability);
- 对权能检查的说明
- ioctl 命令的实现
- 利用 switch 语句实现命令控制,参考 scull 源代码
- 参数传递的一般途径(六种)
-
int quantum;
ioctl(fd,SCULL_IOCSQUANTUM, &quantum); /* Set by pointer */
ioctl(fd,SCULL_IOCTQUANTUM, quantum); /* Set by value */
ioctl(fd,SCULL_IOCGQUANTUM, &quantum); /* Get by pointer */
quantum = ioctl(fd,SCULL_IOCQQUANTUM); /* Get by return value */
ioctl(fd,SCULL_IOCXQUANTUM, &quantum); /* Exchange by pointer */
quantum = ioctl(fd,SCULL_IOCHQUANTUM, quantum); /* Exchange by value *
-
- 非 ioctl 的设备控制——转议序列
- 适用于只执行命令,不需要数据传输的设备
- 如果写入数据种出现控制符时会产生误会