Linux驱动开发—ioctl命令构成,设备驱动基础使用ioctl详解

文章目录

    • 1.什么是ioctl?
      • 示例
    • 2.ioctl 与 write,read 有什么区别?
      • 复杂的设备控制
      • 多种数据类型和操作
      • 简化应用层代码
      • 区分数据和控制
    • 3.ioctl命令的构成
      • 宏定义的组成部分
      • 具体的宏定义
      • 举个栗子
    • 4.ioctl命令的分解
      • 宏定义
      • 举个栗子
    • 5.ioctl 设备使用
      • 应用程序构成
      • 驱动程序构成

1.什么是ioctl?

ioctl(输入/输出控制)是一种系统调用,用于设备驱动程序与用户空间应用程序之间进行设备特定的输入/输出操作。它通常用于对设备执行标准文件操作(如读取、写入、打开和关闭)之外的特殊操作。ioctl提供了一种通用的机制,通过文件描述符与设备进行交互,支持各种类型的设备操作和控制。

ioctl的工作原理如下:

  1. 定义命令和参数:设备驱动程序和用户空间应用程序之间的交互是通过命令和参数来实现的。命令通常是一个宏,表示特定的操作,而参数则是传递给驱动程序的数据。

  2. 调用ioctl系统调用:用户空间应用程序通过文件描述符调用ioctl系统调用,传递命令和参数。例如:

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

    这里,fd是文件描述符,request是设备特定的操作命令,后面的省略号表示可选的参数。

  3. 设备驱动处理:驱动程序在内核空间接收到ioctl请求后,根据传入的命令和参数执行相应的操作。驱动程序通常会在其文件操作结构中实现一个ioctl处理函数,用于处理不同的命令。

示例

假设有一个字符设备,需要通过ioctl来设置设备的某个参数:

  1. 定义命令

    #define MY_IOCTL_SET_PARAM _IOW('M', 1, int)
    
  2. 用户空间调用

    int fd = open("/dev/my_device", O_RDWR);
    int param = 42;
    ioctl(fd, MY_IOCTL_SET_PARAM, &param);
    close(fd);
    
  3. 设备驱动实现

    static long my_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {switch (cmd) {case MY_IOCTL_SET_PARAM:int param;if (copy_from_user(&param, (int __user *)arg, sizeof(param)))return -EFAULT;// 设置设备参数break;default:return -EINVAL;}return 0;
    }
    

2.ioctl 与 write,read 有什么区别?

ioctlwriteread 的区别在于用途和灵活性。虽然 writeread 是用于基本的输入/输出操作的常见系统调用,但 ioctl 提供了更灵活的方式来处理设备特定的控制操作。

复杂的设备控制

writeread 主要用于传输数据,但设备通常需要进行更复杂的控制操作,例如:

  • 改变设备的配置参数。
  • 执行特定的硬件操作,如调整音量、设置网络接口参数、获取设备状态等。

例如,假设你有一个音频设备,你可能需要调整音量、均衡器设置等,这些操作并不适合通过 writeread 来实现,而更适合通过 ioctl 进行:

define AUDIO_SET_VOLUME _IOW('A', 1, int)int fd = open("/dev/audio", O_RDWR);
int volume = 10;
ioctl(fd, AUDIO_SET_VOLUME, &volume);
close(fd);

多种数据类型和操作

ioctl 可以处理不同类型的数据和操作,而 writeread 仅限于简单的数据传输。ioctl 可以支持多种数据类型和复杂的控制逻辑,例如:

  • 传递结构体。
  • 执行读写组合操作。
struct device_status {int power;int signal_strength;
};#define DEVICE_GET_STATUS _IOR('D', 1, struct device_status)struct device_status status;
ioctl(fd, DEVICE_GET_STATUS, &status);

简化应用层代码

如果所有控制操作都通过 writeread 实现,应用层代码可能需要处理大量的特定协议逻辑,这增加了代码复杂性。ioctl 使得驱动程序可以集中处理这些复杂性,简化应用层代码。

区分数据和控制

writeread 本质上是用于数据传输的,而 ioctl 专门用于控制操作。将数据传输和控制操作分开,可以使代码更加清晰和模块化。

3.ioctl命令的构成

在这里插入图片描述

设备类型代表这一类设备,一般用一个字母或一个8bit 数字来表示
序列号代表是这个设备的第几个命令
方向表示命令的方向,只读(10) 只写(01) 写读(11) 无数据(00)
数据大小用户数据的大小,传递是数据类型,比如传递四个字节,可以写int

ioctl 命令的构成一般包括一个由四个部分组成的宏定义,这四个部分分别指定了命令的类型、命令的编号、数据的方向以及数据的大小。这种构成方式在 Linux 中非常常见。具体来说,一个 ioctl 命令通常使用如下的宏定义来构建:

_IO(type, nr)          // 无参数的命令
_IOR(type, nr, size)   // 从设备读取数据
_IOW(type, nr, size)   // 向设备写入数据
_IOWR(type, nr, size)  // 读写数据

宏定义的组成部分

  1. type:一个字符,用于表示设备类型。通常使用一个唯一的字符来标识设备类别,例如 ‘T’ 表示终端设备,‘S’ 表示串行设备等。

  2. nr:命令编号。一个整数,用于区分同一类型设备的不同命令。

  3. size:数据的大小。通常是数据结构的大小,用于指定传递给命令的数据大小。

具体的宏定义

这些宏的具体定义在系统头文件 <linux/ioctl.h> 中。以下是每个宏的详细说明:

_IO(type, nr)

定义一个无参数的 ioctl 命令:

#define MY_IOCTL_NO_PARAM _IO('M', 1)

_IOR(type, nr, size)

定义一个从设备读取数据的 ioctl 命令:

#define MY_IOCTL_READ _IOR('M', 2, int)

_IOW(type, nr, size)

定义一个向设备写入数据的 ioctl 命令:

#define MY_IOCTL_WRITE _IOW('M', 3, int)

_IOWR(type, nr, size)

定义一个读写数据的 ioctl 命令:

#define MY_IOCTL_READ_WRITE _IOWR('M', 4, struct my_struct)

举个栗子

假设有一个设备需要以下 ioctl 命令:

  1. 设置参数(无参数)。
  2. 获取状态(读取一个整数)。
  3. 设置配置(写入一个结构体)。
  4. 获取和设置配置(读写一个结构体)。

可以定义如下的 ioctl 命令:

#define MY_IOCTL_SET_PARAM _IO('M', 1)
#define MY_IOCTL_GET_STATUS _IOR('M', 2, int)
#define MY_IOCTL_SET_CONFIG _IOW('M', 3, struct my_config)
#define MY_IOCTL_GET_SET_CONFIG _IOWR('M', 4, struct my_config)struct my_config {int setting1;int setting2;
};

4.ioctl命令的分解

OCTL(输入输出控制)命令由 32 位整数表示,它包含了方向、类型、编号和大小等信息。我们将通过分解一个具体的 IOCTL 命令来理解其构成和解码方法。

宏定义

在 Linux 内核中,IOCTL 宏通常定义在 <linux/ioctl.h> 中。这里是一些关键宏的定义

#define _IOC_DIR(nr)   (((nr) >> _IOC_DIRSHIFT) & ((1 << _IOC_DIRBITS) - 1))
#define _IOC_TYPE(nr)  (((nr) >> _IOC_TYPESHIFT) & ((1 << _IOC_TYPEBITS) - 1))
#define _IOC_NR(nr)    (((nr) >> _IOC_NRSHIFT) & ((1 << _IOC_NRBITS) - 1))
#define _IOC_SIZE(nr)  (((nr) >> _IOC_SIZESHIFT) & ((1 << _IOC_SIZEBITS) - 1))

举个栗子

假设我们有以下 IOCTL 命令定义:

#define DEVICE_CMD3 _IOW('M', 2, int)

我们将分解这个命令,并解码其各个部分。

首先,看看 _IOW 宏的定义:

#define _IOW(type, nr, size)  _IOC(_IOC_WRITE, (type), (nr), sizeof(size))

将参数传入 _IOC 宏:

#define DEVICE_CMD3 _IOC(_IOC_WRITE, 'M', 2, sizeof(int))

让我们计算各个部分:

  • 方向 (_IOC_WRITE):值为 1U
  • 类型 ('M'):ASCII 值为 0x4D
  • 编号 (2):值为 2
  • 大小 (sizeof(int)):在大多数平台上为 4

计算

#define _IOC_WRITE 1U
#define 'M' 0x4D
#define nr 2
#define size 4

使用 _IOC 宏计算值:

#define DEVICE_CMD3 _IOC(_IOC_WRITE, 'M', 2, sizeof(int)) = \(((1U) << _IOC_DIRSHIFT) | ((0x4D) << _IOC_TYPESHIFT) | ((2) << _IOC_NRSHIFT) | ((4) << _IOC_SIZESHIFT))

分解为二进制并位移:

  • 方向 (dir):1U << 30 = 0x40000000
  • 类型 (type):0x4D << 8 = 0x004D00
  • 编号 (nr):2 = 0x02
  • 大小 (size):4 << 16 = 0x00040000

组合这些部分:

0x40000000 | 0x00004D00 | 0x00000002 | 0x00040000 = 0x40044D02

解码

使用解码宏来验证这些值:

#include <stdio.h>
#include <linux/ioctl.h>#define DEVICE_CMD3 _IOW('M', 2, int)int main() {printf("DEVICE_CMD3 value: 0x%x\n", DEVICE_CMD3);printf("Type: %c\n", _IOC_TYPE(DEVICE_CMD3));printf("Number: %d\n", _IOC_NR(DEVICE_CMD3));printf("Direction: %d\n", _IOC_DIR(DEVICE_CMD3));printf("Size: %d\n", _IOC_SIZE(DEVICE_CMD3));return 0;
}

运行代码,输出如下:

DEVICE_CMD3 value: 0x40044d02
Type: M
Number: 2
Direction: 1
Size: 4

其中Write为 01 也就是代表方向为1

5.ioctl 设备使用

应用程序构成

ioctl 是一个用于设备控制的系统调用,允许用户空间应用程序与内核空间的设备驱动程序进行交互。它的主要功能是向设备发送控制命令或从设备读取状态信息。ioctl 的原型定义在 <sys/ioctl.h> 头文件中:

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

参数分析

  1. int fd
    • 表示文件描述符,是一个整数,通过 open 系统调用获取。该文件描述符指向需要控制的设备文件。
  2. unsigned long request
    • 表示控制命令,是一个无符号长整型。控制命令通常是一个通过宏定义的常量,用于指定需要执行的操作。命令值可以是 _IO, _IOR, _IOW, _IOWR 之一。
  3. ...
    • 表示可选参数。这些参数取决于 request 命令的类型,有些命令需要一个指针作为参数,有些则不需要。
    • 如果命令不需要参数,这个位置可以省略;
    • 如果需要参数,通常是一个指向数据结构的指针,用于传递数据或从设备读取数据。

返回值

  • 成功时,ioctl 返回 0
  • 失败时,返回 -1 并设置 errno 以指示错误类型。

无参数版

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>// Define IOCTL commands
#define DEVICE_CMD0 _IO('M', 0)
#define DEVICE_CMD1 _IO('M', 1)
#define DEVICE_CMD2 _IO('L', 0)
#define DEVICE_CMD3 _IOW('M', 2, int)
#define DEVICE_CMD4 _IOR('M', 3, int)
#define DEVICE_CMD5 _IOWR('M', 4, int)#define DEVICE_PATH "/dev/countdown_device"int main()
{int fd;int ret;fd = open(DEVICE_PATH, O_RDWR);if (fd < 0){perror("open");return 1;}// 每秒传递一次while (1){ret = ioctl(fd, DEVICE_CMD0); // 两个参数代表没有传递参数sleep(1);}return 0;
}

驱动程序构成

驱动程序中,也要定义相同的命令 ,否则无法解析命令,可以提取出一个头文件,专门用来放置IOCTL宏命令

同样的文件结构体需要实现IOCTL操作

具体函数原型如下:

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  • file对应的是 应用程序的fd
  • unsigned int cmd 对应的是具体宏命令
  • 第三个参数对应的是参数

具体代码 (无命令参数版)

static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{switch (cmd){case DEVICE_CMD0:printk(" null parmater cmd is invoke...");break;case DEVICE_CMD1:break;case DEVICE_CMD2:break;case DEVICE_CMD3:break;default:return -ENOTTY; // 不支持的命令}return 0;
}
static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,.unlocked_ioctl = device_ioctl};

调用效果:(无参数版本)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394380.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【游戏引擎之路】登神长阶(九)——《3D游戏编程大师技巧》:我想成为游戏之神!

5月20日-6月4日&#xff1a;攻克2D物理引擎。 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 6月13日-6月20日&#xff1a;攻克《3D图形教程》。 6月21日-6月22日&#xff1a;攻克《Raycasting游戏教程》。 6月23日-7月1日&#xff1a;攻克《Windows游戏编程大师技巧》。 7月…

数据库篇--八股文学习第十六天| MySQL的执行引擎有哪些?;MySQL为什么使用B+树来作索引;说一下索引失效的场景?

1、MySQL的执行引擎有哪些&#xff1f; 答&#xff1a; MySQL的执行引擎主要负责查询的执行和数据的存储, 其执行引擎主要有MyISAM、InnoDB、Memery 等。 InnoDB引擎提供了对事务ACID的支持&#xff0c;还提供了行级锁和外键的约束&#xff0c;是目前MySQL的默认存储引擎&…

Codeforces Round 963 (Div. 2)

A题&#xff1a;Question Marks 题目&#xff1a; Tim正在做一个由 4n 个问题组成的测试&#xff0c;每个问题都有 4 个选项&#xff1a;“A”、“B”、“C”和“D”。对于每个选项&#xff0c;有 n 个正确答案对应于该选项&#xff0c;这意味着有 n 个问题的答案为“A”。 n…

个人知识库与RAG的技术

构建个人知识库时&#xff0c;采用RAG结合LangChain的方法极为有效。RAG&#xff0c;即检索增强生成技术&#xff0c;是一种前沿的自然语言处理手段&#xff0c;它融合了信息检索的精确匹配与语言模型的高效文本生成&#xff0c;为处理自然语言相关任务提供了一种既灵活又准确的…

未来已来:人工智能如何重塑Facebook的用户体验?

在数字化时代的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;正成为推动技术进步和用户体验优化的核心力量。Facebook&#xff08;现Meta Platforms&#xff09;作为全球领先的社交媒体平台&#xff0c;正在充分利用人工智能技术&#xff0c;以重塑用户体验&#xff0…

Spring Boot 3.0 热部署

idea开发环境下的spring boot 3.0热部署启用非常简单&#xff0c;并没有网上教程讲的需要对idea做一些设置。 只需引入依赖&#xff1a; developmentOnly org.springframework.boot:spring-boot-devtools其他不需要做任何设置。 服务启动中&#xff0c;改了代码或配置后&…

振动分析-18-基于振动分析进行故障诊断的思路和步骤

参考树立正确的振动诊断思路 参考振动分析相关知识的储备及振动分析仪的局限性 参考如何进行振动分析诊断(译文) 1 正确的故障诊断意识 我们通常在学习班听到的是大学教授以及专家讲解的故障诊断的基础理论,对于刚接触这个专业的人来说,微分方程和复杂的矩阵却有点让人忘…

JeecgBoot低代码平台简单记录

BasicModal弹窗 Usage 由于弹窗内代码一般作为单文件组件存在&#xff0c;也推荐这样做&#xff0c;所以示例都为单文件组件形式 注意v-bind"$attrs"记得写&#xff0c;用于将弹窗组件的attribute传入BasicModal组件 attribute&#xff1a;是属性的意思&#xff0c;…

Spring全家桶(三):Spring AOP

Spring AOP面向切面编程 1.面向切面编程思维&#xff08;AOP&#xff09; 1.1.面向切面编程思想AOP AOP&#xff1a;Aspect Oriented Programming面向切面编程 AOP可以说是OOP&#xff08;Object Oriented Programming&#xff0c;面向对象编程&#xff09;的补充和完善。O…

一键生成专业PPT:2024年AI技术在PPT软件中的应用

不知道你毕业答辩的时候有没有做过PPT&#xff0c;是不是也被这个工具折磨过。没想到现在都有AI生成PPT的工具了吧&#xff1f;这次我就介绍几款可以轻松生成PPT的AI工具吧。 1.笔灵AIPPT 连接直达&#xff1a;​​​​​​​https://ibiling.cn/ppt-zone 这个工具我最早是…

Nature Medicine:基于图像的深度学习和语言模型用于原发性糖尿病护理

首个糖尿病诊疗多模态大模型DeepDR-LLM已成功发表在Nature子刊。 这是全球首个面向糖尿病诊疗的视觉-大语言模型集成系统&#xff0c;结合了语言模型和基于眼底图像的深度学习技术。该系统旨在为基层医生提供个性化的糖尿病管理建议和辅助诊断结果&#xff0c;特别对中低收入国…

【实现100个unity特效之12】Unity中的冲击波 ——如何使用ShaderGraph制作一个冲击波着色器

最终效果 文章目录 最终效果新增LitShaderGraph圆环扭曲效果优化冲击波效果屏幕全屏冲击波圆形冲击波最终连线图代码控制补充源码完结 新增LitShaderGraph 圆环扭曲效果 让我们从一个UV节点开始 创建一个Vector2变量RingSpawnPosition表示冲击波生成位置,在X和Y上将其默认值…

PyTorch基于深度神经网络的语音情绪识别

【图书推荐】《PyTorch语音识别实战》-CSDN博客 《PyTorch语音识别实战&#xff08;人工智能技术丛书&#xff09;》(王晓华)【摘要 书评 试读】- 京东图书 (jd.com) 情绪数据的获取与标签的说明 首先是语音情绪数据集的下载&#xff0c;在这里使用瑞尔森情感语音和歌曲视听数…

sourcemap使用

sourcemap是什么以及怎么生成就不过多阐述了&#xff0c;这是之前看到的一篇文章感觉介绍的很详细&#xff1a;弄懂 SourceMap&#xff0c;前端开发提效 100% 浏览器加载sourcemap 我们线上代码一般不会开启sourcemap&#xff0c;在排查线上的问题时&#xff0c;可以通过浏览器…

DataX PostgreSQL 读写支持Geometry类型

这里写目录标题 简要说明依赖代码 简要说明 通过简单修改源码中关于相关的reader、writer和DBUtil工具类&#xff0c;实现表到表之间的Geometry字段类型数据的输送&#xff0c;目前修改仅测试过在postgresql的postgis插件下的Geometry类型可行。 依赖 1.通过gitclone 或者 到…

用户规模NO.1之后,钉钉还有多大的成长空间?

近日&#xff0c;第三方数据机构QuestMobile发布的《2024半年度中国移动互联网实力价值榜》显示&#xff0c;钉钉与微信、淘宝、抖音等一同入榜 “TOP50赛道用户规模NO.1 App”&#xff0c;蝉联效率办公赛道榜首。 可以看出&#xff0c;现阶段钉钉打开了较大的市场空间&#x…

Qt 跨平台APP单实例运行实现

一.调用方法 支持coreApplication和简单的application两种继承&#xff1a; 二.源码地址&#xff1a; SingleApp: Qt 单实例实现

android13 串口编号修改 串口名修改

总纲 android13 rom 开发总纲说明 目录 1.前言 2.技术分析 别名定义的语法规则 3.修改示例 使用别名 注意事项 4.不生效分析 5.编译查看 6.其他方法 7.彩蛋 1.前言 更改Android设备的串口编号涉及对系统深层次的配置进行修改,通常是为了解决硬件兼容性问题或满足特…

选择文件鼠标右键自定义菜单

注册表路径 计算机\HKEY_CLASSES_ROOT\*\shell 效果 操作 1.定位 winr&#xff0c;输入regedit, 地址栏输入以下路径&#xff0c;并回车。 计算机\HKEY_CLASSES_ROOT\*\shell 2.在shell上右键&#xff0c;新建项 3右键新建字符串值&#xff0c;Icon,Position 4 右键新建c…

LabVIEW压电陶瓷阻抗测试系统

开发了一种基于LabVIEW软件与PXI模块化仪器的压电陶瓷阻抗测试系统。该系统能在高电压工作条件下测量压电陶瓷的阻抗特性&#xff0c;包括阻抗模值与阻抗角的频率特性&#xff0c;为压电陶瓷的进一步分析与应用提供了重要参考。 项目背景 现有的阻抗测试仪大多只能在低电压条件…