ioctl:
用户空间原型:int
ioctl(int fd, unsigned long cmd, …);
“...”并非可变参数,而是可选参数,防止编译器进行类型检查。
驱动原型:int
(*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long arg);
inode和flip是原来的fd,cmd原封不动,arg是附加参数,被关闭了类型检查。
选择ioctl的命令:
老的约定:
新的约定,应先查看include/asm/ioctl.h和Documentation/ioctl-number.txt这两个文件。
定义号码的新方法使用了4个位字段,下面所介绍的新符号都定义在<linux/ioctl.h>中
type
幻数,选择一个号码(记住先仔细阅读ioctl-number.txt),并在整个驱动程序中使用这个号码,有8位宽(_IOC_TYPEBITS)
number
序数(顺序编号)。8位宽(_IOC_NRBITS)。
direction
定义数据传输方向,_IOC_NONE(没有数据传输)、_IOC_READ、_IOC_WRITE以及_IOC_READ|_IOC_WRITE(双向传输数据)
size
所涉及的用户数据大小。这个字段的宽度与体系结构有关,通常13或14,可通过_IOC_SIZEBITS找到针对特定体系结构的具体数值。内核并不检查这个位字段
<linux/ioctl.h>中包含的<asm/ioctl.h>头文件定义了一些构造命令编号的宏:
_IO(type,nr)用于构造无参数命令
_IOR(type,nr,datatype)
_IOW(type,nr,datatype)
_IOWR(type,nr,datatype)
size字段通过datatype去sizeof得
解开宏:
_IOC_DIR(nr)、_IOC_TYPE(nr)、_IOC_NR(nr)、_IOC_SIZE(nr)
返回值:
不匹配ioctl合法操作时,某些返回-ENVAL(Invalid
argument,非法参数)。POSIX规定应该返回-ENOTTY(Inappropriate
ioctl for device,不适合设备的ioctl)
预定义命令:
有一些命令可由内核识别,这些命令先于我们的操作被调用时解析,所以,如果我们自己的命令选用了与这些命令相同的编号,就永远不会收到该命令的请求,而且命令冲突,应用程序的行为将无法预测。
分三组:
可用于任何文件(普通、设备、FIFO和套接字)的命令
只用于普通文件的命令
特定于文件系统类型的命令
第一组的幻数都是“T”
下列ioctl命令对任何文件(包括设备特定文件)都是预定义的:
FIOCLEX
FIONCLEX
FIOASYNC
FIOQSIZE
FIONBIO
使用ioctl参数:
如果参数是个指针,那么要关心指向的用户空间是否合法的问题。copy_from_user和copy_to_user可以安全地与用户空间交换数据,但ioctl涉及到小的数据项,效率不高。
可以通过函数access_ok验证地址,该函数在<asm/uaccess.h>中声明:
int
access_ok(int type, const void *addr, unsigned long size);
第一个参数VERIFY_READ或VERIFY_WRITE。与其他不同,返回1表示成功,返回0表示失败驱动程序通常要返回-EFAULT给调用者
在调用access_ok后就可以安全的传输数据了。
还可以使用为最常用的数据大小(1,24,8)优化过的函数,这些函数定义在<asm/uaccess.h>中,列在下面:
put_user(datum,
ptr)
__put_user(datum,
ptr)
这些宏定义展开时不进行类型检查,put_user进行检查以确保进程可以写入指定的内存地址,并在成功时返回0,出错时返回-EFAULT。__put_user做的检查少些(它不调用access_ok),但如果地址只想用户不能写入的内存,也会出现操作失败,因此,__put_user应该在已经使用access_ok检验过内存区后再使用。
一般的用法是,实现一个读取方法时,可以调用__put_user来节省几个时钟周期,或者在复制多项数据之前调用一次access_ok。
get_user(local,
ptr)
__get_user(locak,
ptr)
如果试图使用上面列出的函数传递大小不符合任意一个特定值的数值,结果通常是编译器会给出一条奇怪的消息,比如“conversion
to non-scalar type requested(需要转换为非标量类型)”。
权能与受限操作
全部全能操作可以在<linux/capability.h>中找到,其中包含了系统能够理解的所有权能;不修改内核源码,对驱动程序开发者来讲有意义的权能如下所示:
CAP_DAC_OVERRIDE
越过文件或目录的访问权限(数据访问控制或DAC)的能力
CAP_NET_ADMIN
执行网络管理任务的能力,包括那些能影响网络接口的任务
CAP_SYS_MODULE
载入或卸载内核模块的能力
CAP_SYS_RAWIO
执行裸IO的能力,例如访问设备接口或直接与USB设备通讯
CAP_SYS_ADMIN
截获的能力,它提供了访问许多系统管理操作的途径
CAP_SYS_TTY_CONFIG
执行tty配置任务的能力
检查权能的函数定义在<sys/linux.h>
int
capable(int capability);