转自(39条消息) linux驱动---ioctl函数解析_那可真是太开心了的博客-CSDN博客_linux驱动ioctl

参考:ioctl,unlocked_ioctl 处理方法-阿里云开发者社区 (aliyun.com)

一个字符设备驱动会实现常规的打开、关闭、读、写等功能,但是在一些细分的情景下,如果需要扩展新功能,通常以增设ioctl()命令的方式实现,其作用类似于“拾遗补漏”。在文件I/O中,ioctl扮演着重要角色,本文将以驱动开发为侧重点,从用户空间到内核空间纵向分析ioctl函数。

1. 用户空间的ioctl()

#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...) ;

在man手册中描述ioctl函数的作用是:操作特殊文件的底层设备参数。

2. 驱动中的ioctl()

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

3.ioctl命令,用户与驱动之间的协议

在驱动程序中,ioctl()函数上传送的变量cmd是应用程序用于区别设备驱动程序请求处理内容的值,cmd除了可区别数字外,还包含有助于处理的几种相应信息。cmd的大小为32位,共分为4个域

  • bit31 ~ bit30 2位为“区别读写”区,作用是区分是读取命令还是写入命令
  • bit29 ~ bit15 14位为 “数据大小” 区,表示 ioctl() 中的 arg 变量传送的内存大小。
  • bit20 ~ bit08 8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
  • bit07 ~ bit00 8位为 “区别序号” 区,是区分命令的命令顺序序号。

在编写ioctl代码之前,需要选择对应不同命令的编号。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内为唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非串行设备输入流修改波特率,如果每一个ioctl命令都是唯一的,应用程序进行这种操作时就会得到一个EINVAL错误,而不是无意间成功完成了意想不到的操作。

要按Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.h和Doucumention/ioctl-number.txt这两个文件。头文件定义了要使用的位字段:类型(幻数)、序数、传送方向以及参数大小等。ioctl-number.txt文件中罗列了内核所使用的幻数,选择自己的幻数要避免和内核冲突

在asm-generic/ioctl.h里有如下定义

#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)

再看__IOC()的定义:

 #define _IOC(dir,type,nr,size) \(((dir)  << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr)   << _IOC_NRSHIFT) | \((size) << _IOC_SIZESHIFT))

可见,__IO()的最后结果由__IOC()中的4个参数移位组合而成。

#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)#define _IOC_SIZESHIFT   (_IOC_TYPESHIFT+_IOC_TYPEBITS)#define _IOC_SIZEBITS    14#define _IOC_TYPESHIFT    (_IOC_NRSHIFT+_IOC_NRBITS)#define _IOC_TYPEBITS    8#define _IOC_NRSHIFT   0#define _IOC_NRBITS    8
所以可以得到
_IOC_DIRSHIFT = _IOC_SIZESHIFT + 14 = (_IOC_TYPESHIFT+_IOC_TYPEBITS) + 14= (_IOC_NRSHIFT+_IOC_NRBITS + 8) +14= 0 + 8 + 8 +14= 30_IOC_TYPESHIFT = (_IOC_NRSHIFT+_IOC_NRBITS) = 0 + 8 = 8_IOC_NRSHIFT = 0_IOC_SIZESHIFT = (_IOC_TYPESHIFT+_IOC_TYPEBITS) = (_IOC_NRSHIFT+_IOC_NRBITS) + 8= 0 + 8 + 8= 16所以,(dir) << _IOC_DIRSHIFT表示dir左移30位,得到bit31 ~ bit30两位上,得到方向(读写)的属性
(size) << _IOC_SIZESHIFT) 位左移 16 位得到“数据大小”区;
(type) << _IOC_TYPESHIFT) 左移 8位得到"魔数区" ;
(nr)   << _IOC_NRSHIFT)   左移 0 位( bit7~bit0)在asm-generic/ioctl.h中还有如下宏定义
//构造无参数的命令编号
#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、dir(direction),ioctl命令访问模式(数据传输方向),占据2bit,可以为_IOC_NONE、_IOC_READ、_IOC_WRITE _IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据

2、type(device type),设备类型,占据8bit,在一些文献中翻译为“幻数”或者“魔数”,可以为任意char型字符,例如‘a’、‘b’、‘c’等等,其主要作用是使ioctl命令有唯一的设备标识;

tips:Documentions/ioctl-number.txt记录了在内核中已经使用的“魔数”字符,为避免冲突,在自定义ioctl命令之前应该先查阅该文档

3、nr(number),命令编号/序数,占据8bit,可以为任意unsigned char型数据,取值范围0~255,如果定义了多个ioctl命令,通常从0开始编号递增;

4、size,涉及到ioctl第三个参数arg,占据13bit或者14bit(体系相关,arm架构一般为14位),指定了arg的数据类型及长度,如果在驱动的ioctl实现中不检查,通常可以忽略该参数。

4.举例分析

内核空间

#define CLOSE_CMD       (_IO(0XEF,0X1))     /*关闭定时器*/
#define OPEN_CMD        (_IO(0XEF,0X2))     /*打开定时器*/
#define SETPERIOD_CMD   (_IO(0XEF,0X3))     /*设置定时器周期命令*/......static long timer_unlocked_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{struct timer_dev *dev = (struct timer_dev *)filp->private_data;int timerperiod;unsigned long flags;switch (cmd) {case CLOSE_CMD: /* 关闭定时器 */del_timer_sync(&dev->timer);break;case OPEN_CMD: /* 打开定时器 */spin_lock_irqsave(&dev->lock, flags);timerperiod = dev->timeperiod;spin_unlock_irqrestore(&dev->lock, flags);mod_timer(&dev->timer, jiffies +msecs_to_jiffies(timerperiod));break;case SETPERIOD_CMD: /* 设置定时器周期 */spin_lock_irqsave(&dev->lock, flags);dev->timeperiod = arg;spin_unlock_irqrestore(&dev->lock, flags);mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));break;default:break;}return 0;
}//设备操作函数
static struct file_operations timer_fops = {.owner = THIS_MODULE,.open = timer_open,.unlocked_ioctl = timer_unlocked_ioctl,
};

用户空间

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"/* 命令值 */
#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */
#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */int main(int argc,char *argv[])
{int fd,ret;char *filename;unsigned int cmd;unsigned int arg;unsigned char str[100];if(argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename,O_RDWR);if(fd < 0){printf("cant opem file %s\r\n",filename);return -1;}while (1) {printf("Input CMD:");ret = scanf("%d", &cmd);if (ret != 1) /* 参数输入错误 */{ gets(str); /* 防止卡死 */}if(cmd == 1) /* 关闭 LED 灯 */cmd = CLOSE_CMD;else if(cmd == 2) /* 打开 LED 灯 */cmd = OPEN_CMD;else if(cmd == 3) {cmd = SETPERIOD_CMD; /* 设置周期值 */printf("Input Timer Period:");ret = scanf("%d", &arg);if (ret != 1) /* 参数输入错误 */{ gets(str); /* 防止卡死 */}}ioctl(fd, cmd, arg); /* 控制定时器的打开和关闭 */}close(fd);
}

linux驱动---ioctl函数解析相关推荐

  1. Linux内核驱动 --ioctl函数解析

    1.前言 当我们在讨论linux内核驱动开发时,就不得不提到ioctl这个及其重要的函数.它是字符类设备驱动程序中实现对设备控制的接口之一. ioctl是设备驱动程序中对设备的I/O通道进行管理的函数 ...

  2. linux网络编程函数解析之——setsockopt / getsockopt用法

    linux网络编程函数解析之--setsockopt / getsockopt用法 工程中无线传输方面的东西用到了setsockopt(),getsockopt().网上相关博客很多,而且类似,原文出 ...

  3. Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析

    解析完 open.close.read.write 四个函数后,终于到我们的 ioctl() 函数了 一. 什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通 ...

  4. Linux下ioctl函数理解

    一. 什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率.马达的转速等等.它的调用个数如下: i ...

  5. linux驱动程序ioctl函数用法

    一. 什么是ioctl     ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率.马达的转速等等.它的调用个数如 ...

  6. linux 内核 - ioctl 函数详解

    1. 概念 ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开.关闭.读.写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实 ...

  7. linux驱动 ioctl 小结

    依赖版本: linux4.14 依赖头文件: sys/ioctl.h -> asm/ioctl.h -> asm-generic/ioctl.h 简介 ioctl是设备驱动程序中对设备的I ...

  8. linux驱动入口函数执行了,probe函数没有执行排查

    写了个spi1的驱动函数,insmod后发现没有反应. 添加打印信息后,发现执行了驱动入口函数(在入口函数中执行了spi_register_driver),但是probe函数没有执行,检查compat ...

  9. linux asm函数,Linux 字符设备驱动—— ioremap() 函数解析

    一. ioremap() 函数基础概念 几乎每一种外设都是通过读写设备上的相关寄存器来进行的,通常包括控制寄存器.状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址.根据CPU体系结构的不同, ...

最新文章

  1. 深度丨解密Python为什么能够风靡全宇宙
  2. postgre sql 括字段_SQL-约束(cnostraints)
  3. python零基础怎么学-零基础python入门分析,如何做到一个月学会(深思极恐)
  4. 安卓个人信息界面_LOL手游上架,安卓+IOS安装教程
  5. c语言ifft,用于ARM上的FFT与IFFT源代码-C语言
  6. 如何成为呼叫中心客服老司机
  7. 消费者驱动的契约测试_告诉我们您想要什么,我们将做到:消费者驱动的合同测试消息传递...
  8. mongodb python 存文件_Python保存MongoDB上的文件到本地的方法介绍
  9. 为什么谈及硬件,必言软件?软硬件协同让开源世界“阴阳调和”
  10. emmet工具使用和技巧
  11. matlab瑞利衰落信道仿真
  12. Linux笔记本电源管理指南
  13. BackupPC - 恢复选项Restore options
  14. oracle 查看指标 tps(Transactions Per Second)
  15. Linux Polkit本地权限提升漏洞(CVE-2021-4034)
  16. 如何搭建一套个性化推荐系统?
  17. 中南大学信息与通信工程专业研究生入学考试计算机网络试题2001答案,中南大学信息与通信工程专业研究生入学考试计算机网络试题.doc...
  18. Go GUI---lxn/walk 自带demo学习---7.文件浏览器
  19. Android,通讯录导入,contacts,联系人
  20. 爬取虎牙游戏主播人气分析实战

热门文章

  1. 中国大学MOOC视频字幕下载2.0(2020.08.05更新)
  2. ANTLR4权威参考手册(一)
  3. AD怎么输入坐标_测量员必须掌握的——全站仪坐标放样
  4. SDIO、SDMMC接口
  5. Bootstrap栅格系统概述
  6. JSP企业快信系统的设计与实现(论文+源码+PPT)
  7. 支付平台--账户体系之作用和分类
  8. 常用阿拉伯字母书写以及读法
  9. SWOT分析法/KPI/SMART原则/STAR原则/6W1H原则
  10. 第四十五章 龙牙现身(老顽童们2 )