-
ioctl 是设备驱动程序中设备控制接口函数,通过指定的命令来实现对应的操作。
用户空间
int ioctl(int fd, int cmd, ...) ;
-
cmd: 交互协议,设备驱动将根据 cmd 执行对应操作
-
...: 可变参数 arg
驱动程序
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
在linux内核2.6.36以后版本中使用上面两个函数取代了ioctl()。
-
unlocked_ioctl: 在无大内核锁的情况下调用,一般使用这个。
-
compat_ioctl:为 64 位系统提供 32 位 ioctl 的兼容方法,也是在无大内核锁的情况下调用。
协议cmd
第二个参数 cmd 为用户与驱动的 “协议”,可以是任意 int 型数据,但是为了确保该 “协议” 的唯一性,ioctl 命令应该使用更科学严谨的方法赋值,在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段。
内核提供了宏来生成ioctl命令:
#define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | ((type) << _IOC_TYPESHIFT) | ((nr) << _IOC_NRSHIFT) | ((size) << _IOC_SIZESHIFT))
-
dir(direction,ioctl 命令访问模式(数据传输方向),占据 2 bit,可以为
_IOC_NONE
、_IOC_READ
、_IOC_WRITE
、_IOC_READ | _IOC_WRITE
,分别指示了四种访问模式:无数据、读数据、写数据、读写数据; -
type(device type),设备类型,占据 8 bit,又称为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;
-
nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;
-
size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度;
通常,为了方便会使用宏 _IOC() 衍生的接口来直接定义 ioctl 命令:
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) //定义不带参数的 ioctl 命令 #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) //定义带写参数的 ioctl 命令(copy_from_user) #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) //定义带读参数的ioctl命令(copy_to_user) #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) //定义带读写参数的 ioctl 命令
同时,内核还提供了反向解析ioctl命令的宏接口:
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
-