linux驱动之ioctl详解

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

何为ioctl?

在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

自定义命令cmd参数

虽然是自定义命令, 以前需要遵守定义命令的规则.
如何定义有效的ioctl命令
从函数参数类型来看 命令类型是int型, 如何用int的整数,来表示命令?
linux中将 32 位 int 型数据划分为四个位段, 代表不同的涵义:

设备类型序列号方向数据尺寸
8 bit8 bit2 bit8~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;
	}
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux