* 用户空间的系统调用 int ioctl(int fd, ulong cmd, ...)
内核空间 int (*ioctl)(struct inode *inode, struct file *filp, ulong cmd, ulong arg)
* IOCTL的命令号
命令号分为几个位段(Documentation/ioctl-number.txt)(linux/ioctl.h)
type: 魔数 (_IOC_TYPEBITS) type
number: 序号 _IOC_NRBITS nr
direction: 数据传输方向(从应用的角度看待) _IOC_NONE/READ/WRITE/(READ | WRITE)
size: 用户数据大小。 _IOC_SIZEBITS, 一般通过由体系决定的数据类型来决定
* 命令号的定义
#define type魔数 'k" //定义魔数
_IO(type,nr) //定义无参数传递之命令
_IOR(type, nr, datatype) //定义从驱动中读数据的命令号
_IOW(type, nr, datatype) //定义写数据的命令号
_IOWR(type, nr, datatype) //定义双向传输的命令号
解码命令号 _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)
* 命令号定义范例。
/*we use 'k' as magic number*/
#define SCULL_IOC_MAGIC 'k'
/*
* S means "Set" through a ptr,
* G means "get" reply by setting through a ptr,
* T means "Tell" 值参
* Q means "Query" 返回为值参
* X means "eXchange" 自动切换 S & G
* H means "sHift" 自动切换 T & Q
*/
/*reset 没有数据往来*/
#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)
/*通过传递给驱动 指针变量 来设置QUANTNUM */
#define SCULL_IOCSQUANTNUM _IOW(SCULL_IOC_MAGIC, 1, int) //通过指针变量传递
/*通过传递给驱动 指针变量 来设置QSET */
#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) //通过指针变量传递
/*通过传递给驱动 值变量 来设置QUANTNUM */
#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) //通过值传递
/*通过传递给驱动 值变量 来设置QSET */
#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) //通过值传递
/*驱动通过 指针变量int 来返回QUANTNUM 值*/
#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)//通过指针变量传递
/*驱动通过 指针变量int 来返回QSET 值*/
#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)//通过指针变量传递
/*驱动通过 ioctl函数返回值 来返回QUANTNUM 值*/
#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) //通过值传递
/*驱动通过 ioctl函数 来返回QSET 值*/
#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) //通过值传递
#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC, 10, int)
#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)
#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)
#define SCULL_IOC_MAXNR 14
当前列出了通过指针和值参来传递整数参数, 根据惯例在ioctl中应当使用指针方式,即S/G方式。
可以看出上面定义命令号时,通过指针进行参数传递的都是通过 _IOR/W/WR参数进行方向控制。
* ioctl 参数使用
o copy_from_user / copy_to_user 安全的在内核空间和用户空间之间移动数据
o 地址有效性校验
int access_ok(int type, const void *addr, ulong size)
type: VERIFY_READ / VERIFY_WRITE
addr: 用户空间地址
size: 用户空间地址上有效数据的长度
返回值: 1:地址可以正常存取, 0:失败
o 简单数据的存取, 对 ioctl 参数 是地址参数的情况
+ put_user(dataum, ptr) __put_user(datum, ptr)
把datum 写到用户空间 ptr, 这个速度比较快。 传递数据大小依赖ptr参数的类型。 成功时返回0,错误时返回-EFAULT.
一般用来在实现read方法, 可以节省时钟周期。
+ get_user(local, ptr) __get_user(local, ptr)
从用户空间ptr接受数据到local变量。
+ 当编译器出现“converion to non-scalar type requested"错误时,应当是使用copy_to_user 或者 copy_from_user.
* ioctl的返回值
不合适的命令号时应当返回 -ENOTTY 或者 -EINVAL
返回负值则将被在用户空间设置 errno