• linux驱动之ioctl详解


    何为ioctl?

    在linux对文件IO, 有打开,读写,关闭,定位等命令, 如果需要一些自定义的命令来操作我们写的驱动, ioctl便是传入这些自定义命令的函数, 先看看在应用层的函数原型:

    int ioctl(int fd, unsigned long request, ...);
    
    • 1

    fd: 文件描述符
    request: 请求, 即自定义命令
    可变参数: 一般是一个, 此参数的存在与否却决于request的命令.

    对文件调用ioctl, 在驱动层对应的便是
    file_operations结构体中的:

    
      long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); 
      long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    
    • 1
    • 2
    • 3

    区别在于在 64位系统上,32位的应用程序调用将会使用compat_ioctl函数。在 32位的系统上运行 32位的应用程序调用的是unlocked_ioctl。
    应用层的命令会传值到第二个参数. 至于第三个参数就是应用层的可变参数, 如果arg是一个整数,可以直接使用;
    如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
    内部有检查的,不需要检测的:
    使用以下函数

    copy_from_user
    copy_to_user
    get_user
    put_user
    
    • 1
    • 2
    • 3
    • 4

    自定义命令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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在使用时直接使用:

    _IO:       定义不带参数的 ioctl 命令
    _IOW:      定义带写参数的 ioctl 命令(copy_from_user)
    _IOR:      定义带读参数的ioctl命令(copy_to_user)
    _IOWR:     定义带读写参数的 ioctl 命令
    
    • 1
    • 2
    • 3
    • 4

    看个实例:

    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    编写驱动:
    实现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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    详解基于栈的算法
    【算法速查】一篇文章带你快速入门八大排序(上)
    超全整理——相机标定知识汇总
    基于simulink的微电网虚拟同步发电机vsg控制系统仿真
    Java for each循环语句的功能简介说明
    【递归】汉诺塔问题(Java版)
    【Verilog】画出下列wave信号波形图
    【李沐深度学习笔记】基础优化方法
    一次由 select * 走了索引而引发的思考
    栈和队列知识点+例题
  • 原文地址:https://blog.csdn.net/qq_40684669/article/details/127834337