在linux对文件IO, 有打开,读写,关闭,定位等命令, 如果需要一些自定义的命令来操作我们写的驱动, ioctl便是传入这些自定义命令的函数, 先看看在应用层的函数原型:
int ioctl(int fd, unsigned long request, ...);
fd: 文件描述符
request: 请求, 即自定义命令
可变参数: 一般是一个, 此参数的存在与否却决于request的命令.
对文件调用ioctl, 在驱动层对应的便是
file_operations结构体中的:
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
区别在于在 64位系统上,32位的应用程序调用将会使用compat_ioctl函数。在 32位的系统上运行 32位的应用程序调用的是unlocked_ioctl。
应用层的命令会传值到第二个参数. 至于第三个参数就是应用层的可变参数, 如果arg是一个整数,可以直接使用;
如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
内部有检查的,不需要检测的:
使用以下函数
copy_from_user
copy_to_user
get_user
put_user
虽然是自定义命令, 以前需要遵守定义命令的规则.
如何定义有效的ioctl命令
从函数参数类型来看 命令类型是int型, 如何用int的整数,来表示命令?
linux中,将 32 位 int 型数据划分为四个位段, 代表不同的涵义:
设备类型 | 序列号 | 方向 | 数据尺寸 |
---|---|---|---|
8 bit | 8 bit | 2 bit | 8~14 bit |
设备类型, 有时候会用一个字母来表示, 数据长度也是8, 这和用一个数字是一样的,只是更加利于记忆和理解
内核中提供了宏用来定义命令:
// include/uapi/asm-generic/ioctl.h
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
dir: ioctl数据传输方向, 占2bit, 取值可以: _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
type : 设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如 ‘a’ , ‘b’, ‘c’.
内核文档Documentation/ioctl/ioctl-number.txt中记录了内核驱动用到的type
nr : 命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;
size: 涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;
为了方便会使用宏 _IOC() 衍生的接口来直接定义 ioctl 命令:
// include/uapi/asm-generic/ioctl.h
/* used to create numbers */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
在使用时直接使用:
_IO: 定义不带参数的 ioctl 命令
_IOW: 定义带写参数的 ioctl 命令(copy_from_user)
_IOR: 定义带读参数的ioctl命令(copy_to_user)
_IOWR: 定义带读写参数的 ioctl 命令
看个实例:
struct data{
int x;
char y;
};
#define IOCTL_BASE 'S'
#define GET_MODEL_NAME _IOR(iOCTL_BASE, 0, char*) //读命令, 带参char*, 自定义0为命令编号不可重复
#define GET_GET_POWER_STATUS _IOR(iOCTL_BASE, 2, int)
#define SET_DELAY_TIME _IOW(IOCTL_BASE, 3, int) //向驱动写数据
#define SET_DATA _IOW(IOCTL_BASE, 4, struct data)
编写驱动:
实现file_operations中的unlocked_ioctl compat_ioctl 函数指针
static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct data tmp;
int status = 1;
switch(cmd){
case GET_MODEL_NAME:
...
break;
case GET_GET_POWER_STATUS:
//数据传给应用层
copy_to_user((unsigned char *)&arg,(unsigned char *)&status ,sizeof(int));
break;
case SET_DELAY_TIME:
...
break;
case SET_DATA:
//这里需要强制转换的类型为unsigned char * 保证数据按最小进行分割
copy_from_user((unsigned char *)&tmp,(unsigned char *)args,sizeof(struct data));//拿到应用层传来的结构体
break;
}
}