@[TOC]应用层ioctl的理解(这里写自定义目录标题)
关于应用层ioctl的理解
刚接触ioctl时候不理解它的具体作用,下面将我理解的记录一下。写这些时候也是复习一遍,也给别人提供一个理解的方案。
IOCTL的出现原因
首先需要明确的是,驱动的存在是服务于应用层,并且文件的操作集结构体没有提供满足应用层需求的接口从而得到如下几点原因:
1:操作集结构体struct file_operations动作函数有限,比如应用层想要改变外设SPI的速率,改变SPI极性等等关于SPI的各种属性的修改更新,但是不能都用write接口去完成,而且操作集结构体里面也没有提供SPI改变速率等这些功能的函数接口。
2:除了1中提到的改变属性需求外,还会有控制外设硬件的需求,虽然可以通过write写入,但是全在write内部实现代码显得驱动结构杂乱。
3:最后我们写驱动的对应用层是完全封闭的,应用层如何能用简单的命令去控制外设硬件呢?
总结以上三点(也只想到了三点,还参考了大佬们的思想),所以需要有一个控制接口,接口参数包含一个命令代码,这样所有关于控制相关的都可以用命令在这个接口中实现,并且在头文件中将使用的命令描述清楚,提供给应用层人员,这样应用层也不必关心驱动层。这样ioctl就应运而生啦。
应用层原型
int ioctrl(int fd,unsigned long cmd,…);
fd:文件描述符
cmd:命令代码(unsigned long类型下面会描述cmd的组成)
返回值:0:成功,-1:失败
Tip:关于不定参数相关后期更新
驱动层原型旧版本
int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
inode:当前文件描述符对应的相关结构。
filp:当前要操作的文件相关结构体。同open,write,read参数一样
cmd:从应用层原封不动传递过来.
驱动层原型新版本
int (*unlocked_ioctl)(struct file *file,unsigned int cmd,unsigned long arg);
file:当前要操作的文件相关结构体。同open,write,read参数一样
cmd:从应用层原封不动传递过来.
arg:不定参数部分,如果应用层没有提供这个参数,忽略这个参数即可无意义.
关于ioctl命令
- 命令的使用和提供方式
一般情况应用层调用ioctl发送控制命令,在驱动层ioctl内部会使用
// An highlighted block
switch(cmd)
{
case CLOSE_ALL_LED:break;
case OPEN_RED_LED:break;
case OPEN_BLUE_LED:break;
default : break;
}
那么这里的命令就需要写在头文件内提供给应用层,应用层就必须包含我们写的头文件
#include "my_leds_ctlcmd.h"
- 命令的选择和选取
在选取命令unsigned long类型的数字时候,需要考虑这个数字在整个linux系统中应该是唯一的,避免向当前不是我们想要控制的设备中发送正确的数字(注册了命令数字就算是正确的).例如我们现在想要改变SPI速率,但是我们发送这个命令给音频设备(音频驱动也有这个命令),这种情况需要返回一个EINVAL错误代码,而不是继续去做我们不希望的事情.
所以需要创建唯一命令代码,内核提供了方法—宏定义去帮助我们创建.
关联的相关文件路径:
include/asm-generic/ioctl.h:检查目的
Documentation/ioctl/ioctl-number.txt:查看已经注册的数据序号防止冲突
1.基本格式
魔数 | 序号 | 传输方向 | 参数大小 |
---|---|---|---|
8-bit | 8-bit | 2-bit | 14-bit |
_IOC_TYPEBITS | _IOC_NRBITS | _IOC_READ _IOC_WRITE _IOC_NONE | _IOC_SIZEBITS |
魔数(type):在文件ioctl.h中查找,避免和其他设备冲突。目的找一个当前没有占用的8bit数.---- _IOC_TYPEBITS
序号(number):自己命令的序号,当前这个命令是第几个自定义.
---- _IOC_NRBITS
数据方向(direction):这一部分如果当前命令是涉及数据的传输目的,可能的值会是(数据传送是从应用层角度来讲的)可以使用位运算 & |
_IOC_NONE无传输
_IOC_READ读数据:从设备读数据.
_IOC_WRITE写数据:向设备写数据.
_IOC_READ | _IOC_WRITE数据在两个方向传输.
数据大小(size):指的是数据结构的大小 ,如sizeof(double)和体系有关,在arm下基本使用14bits.— _IOC_SIZEBITS
tip:其实这么描述和后面使用会让人混淆。只要记住在传入参数传递一个数据类型就可以了.不需要我们去传递sizeof(xxx)。
2.使用的宏列表
/*****************************/
//type:魔数
//nr:序号
//datatype:传递一个数据类型比如double,int等,宏内部已经包含了sizeof.
_IO(type,nr)
_IOR(type,nr,datatype)从设备(驱动)中读数据
_IOW(type,nr,datatype)向设备(驱动)中写数据
_IOWR(type,nr,datatype)双向传输
/*****************************/
//解析命令有关的宏
//内核源码中其实表示的是nr并非cmd,这里方便大家理解使用才这么写的。具体为什么写nr原因希望大佬了解的话也帮我解惑.
_IOC_DIR(cmd)
_IOC_TYPE(cmd)
_IOC_NR(cmd)
_IOC_SIZE(cmd)
***************************************
app中ioctl调用传递带有参数的命令,参数为一个内存地址
***************************************
==> app.c
#include <sys/types.h>
#include <sys/stat.h