~ ioctl方法
概述 目的: 通过设备驱动程序执行各种类型的硬件控制 用户空间的调用原型: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.hDocumentation/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) size 指示用户数据大小,与体系结构有关,通常是13位或者14位 宏定义:_IOC_SIZEBITS 系统不对字段进行检查,用户数据大时可以乎略 构造命令编号的宏 定义位置: <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 命令号不能匹配的默认的返回值 非法参数:-ENVAL POSIX标准规定返回: -ENOTTY ioctl 的预定义命令 部分预定义命令可由内核识别,这些命令在使用时无法到达设备,由内核运行,并且因为编号冲突,结果不可预期 预定义命令分类 可用于任何文件(普通、设备、FIFO和套接字)的命令 幻数是 ”T“ 只适用于普通文件的命令 特定于文件系统类型的命令 只能在宿主文件系统上执行 实际预定义命令 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 命令完成 使用 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,为超集 addr 用户空间地址 size 数据的字节数,如果为int,则是 sizeof(int) 返回值是 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
权能与受限操作 对权能检查的说明 对设备的访问一般由设备文件的权限控制,驱动程序不进行检查 对于一些附加操作,驱动程序需要进行附加检查以确认用户是否有权进行 权能(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 配置任务的能力 权能检查 在执行一项特殊操作时,驱动程序应该检查调用进程是否有相关权能 函数实现 <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 的设备控制——转议序列 适用于只执行命令,不需要数据传输的设备 如果写入数据种出现控制符时会产生误会