By: 潘云登
Date: 2009-6-2
Email: intrepyd@gmail.com
Homepage: http://blog.youkuaiyun.com/intrepyd
Copyright: 该文章版权由潘云登所有。可在非商业目的下任意传播和复制。
对于商业目的下对本文的任何行为需经作者同意。
写在前面
1. 本文内容对应《linux设备驱动程序》第六章。
2. 参考俞永昌的《设备驱动开发技术及应用》,以及Documentation/ioctl-number.txt和include/asm/ioctl.h两个文件。
3. 希望本文对您有所帮助,也欢迎您给我提意见和建议。
设备控制
对于那种不传送数据而只响应命令的设备,如机器人J,可以通过向设备写入控制序列进行控制。然而,对于具备读写操作的设备,通过打印序列进行控制将给设备增加策略限制。更好的设备控制方式是ioctl方法。在用户空间,ioctl系统调用具有如下原型:
int ioctl(int fd, unsigned long cmd, char *argp);
|
控制命令cmd由用户空间不加修改地传递到驱动程序的ioctl方法。argp为可选参数,它可以是一个整型参数或者是一个指针参数,具体形式依赖于要完成的控制命令cmd。使用指针可以向ioctl调用传递任意数据,从而与用户空间交换任意数量的数据。不论argp是何种类型,它都以unsigned long的形式传递给驱动程序。然后,由驱动程序根据具体情况进行类型转换,如转换为整型指针(int __user *)arg,__user表明指针是一个用户空间地址,不能直接引用。驱动程序的ioctl方法原型如下:
int (*ioctl) (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
|
其返回值,也是系统调用的返回值,可以向用户空间提供设备信息。负的返回值被认为是一个错误,通常为-ENOTTY (不合适的设备ioctl) 或-EINVAL (非法参数),被用来设置用户空间的errno变量。
控制命令
高位 低位 | |||
2位 |
14位 |
8位 |
8位 |
direction |
size |
number |
type |
Ø type:幻数,可以在Documentation/ioctl-number.txt中选择一个未使用的号码。如果整个控制命令能够被内核识别,将无法到达驱动程序。
Ø number:序数,一般从0开始顺序编号。
Ø size:用户数据大小,用于检查参数类型的大小,如sizeof(int) < (1 << size)。
Ø direction:数据传输方向,可以使用的值包括 _IOC_NONE (无数据传输), _IOC_READ (从设备中读数据), _IOC_WRITE (向设备写数据),以及 _IOC_READ|_IOC_WRITE (双向数据传输)。
<linux/ioctl.h>中包含的<asm/ioctl.h>头文件定义了一些构造命令编号的宏:
_IO(type,nr)
_IOR(type,nr,datatype)
_IOW(type,nr,datatype)
_IOWR(type,nr,datatype)
|
以及用于解开位字段的宏:
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)
_IOC_DIR(nr)
|
大多数ioctl的实现中都包含了一个switch语句来根据控制命令选择对应的操作。
ioctl
scull
Ø 控制命令检查
检查设备幻数和命令序数。
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
|
Ø 用户地址空间检查
当用一个指针指向用户空间时,必须确保指向的用户空间是合法的,这通过access_ok函数完成。通常不需要真正调用access_ok,因为数据交换函数copy_to_user, copy_from_user, put_user, get_user 会处理它。
int access_ok(int type, const void *addr, unsigned long size);
/*scull*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
|
Ø 权能检查
对设备的读写访问由设备文件的权限控制,驱动程序通常不进行权限检查。然而,当用户试图修改设备参数时,设备驱动程序应该检查调用进程是否有合适的权能,这通过capable函数完成。
int capable(int capability);
/*scull*/
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
|