【Linux】ioctl分析
简介
一个字符设备驱动通常会实现常规的open
、release
、read
和write
接口,但是如果需要扩展新的功能,通常以ioctl
接口的方式实现。
用户空间的ioctl
#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...) ;
参数 | 描述 |
---|---|
fd | 文件描述符 |
cmd | 交互协议,设备驱动将根据cmd执行对应操作 |
… | 可变参数arg,依赖cmd指定的长度以及类型 |
返回值 | 执行成功时返回0,失败则返回-1 |
驱动中的ioctl
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
在新版内核中,unlocked_ioctl()与compat_ioctl()取代了ioctl()。unlocked_ioctl(),顾名思义,应该在无大内核锁(BKL)的情况下调用;compat_ioctl(),compat全称compatible(兼容的),主要目的是为64位系统提供32位ioctl的兼容方法,也是在无大内核锁的情况下调用。
在字符设备驱动开发中,一般情况下只要实现unlocked_ioctl()即可,因为在vfs层的代码是直接调用unlocked_ioctl()。
ioctl命令,用户与驱动之间的协议
前文提到ioctl方法第二个参数cmd为用户与驱动的“协议”,理论上可以为任意int型数据,可以为0、1、2、3……,但是为了确保该“协议”的唯一性,ioctl命令应该使用更科学严谨的方法赋值,在linux中,提供了一种ioctl命令的统一格式,将32位int型数据划分为四个位段,如下图所示:
在内核中,提供了宏接口以生成上述格式的ioctl命令:
// 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(direction),ioctl命令访问模式(数据传输方向),占据2bit,可以为_IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
- type(device type),设备类型,占据8bit,在一些文献中翻译为“幻数”或者“魔数”,可以为任意char型字符,例如‘a’、‘b’、‘c’等等,其主要作用是使ioctl命令有唯一的设备标识。**tips:**Documentions/ioctl-number.txt记录了在内核中已经使用的“魔数”字符,为避免冲突,在自定义ioctl命令之前应该先查阅该文档。
- nr(number),命令编号/序数,占据8bit,可以为任意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)))
同时,内核还提供了反向解析ioctl命令的宏接口:
// include/uapi/asm-generic/ioctl.h/* used to decode ioctl numbers */
#define _IOC_DIR(cmd) (((cmd) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(cmd) (((cmd) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(cmd) (((cmd) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(cmd) (((cmd) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
示例
#ifndef __HAPTCIS_IOCTL_H__
#define __HAPTCIS_IOCTL_H__/** @Date: 2024-10-12 15:53:37* @LastEditors: zdk* @LastEditTime: 2024-10-14 11:47:49* @FilePath: \kernel\drivers\haptics\haptics_ioctl.h*/#include <linux/ioctl.h>
// #include <sys/ioctl.h> // 用户空间/*这里使用ioctl定义两个协议,读寄存器和写寄存器
*用户空间和内核空间共用的头文件,包含ioctl命令及相关宏定义,可以理解为一份“协议”文件
*/
//cmd中的type
#define DEVICE_TYPE 'H'
#define HAPTICS_READ_REG_NR (0)
#define HAPTICS_WRITE_REG_NR (1)
#define HAPTICS_READ_REG _IOR(DEVICE_TYPE,HAPTICS_READ_REG_NR,ioctl_protocol_t *)
#define HAPTCIS_WRITE_REG _IOW(DEVICE_TYPE,HAPTICS_WRITE_REG_NR,ioctl_protocol_t *)typedef struct
{uint8_t reg_addr;uint8_t reg_value;
}ioctl_protocol_t;#endif
这个头文件是共用的,因为他定义了协议的具体内容。
/** Silicon Integrated Co., Ltd haptic sih688x haptic driver file** Copyright (c) 2021 heater <daokuan.zhu@si-in.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include "haptics_ioctl.h"#define HAPTICS_MISC_DEV_NAME "haptics"//打开设备
static int haptics_open(struct inode* inode,struct file * filp)
{printk("%s\n",__FUNCTION__);return 0;
}//关闭设备
static int haptics_release(struct inode* inode ,struct file* filp)
{printk("%s\n",__FUNCTION__);return 0;
}//ioctl
static long haptics_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{int ret = 0;ioctl_protocol_t msg;//反解cmd中的字段int type = _IOC_TYPE(cmd);int dir = _IOC_DIR(cmd);int nr = _IOC_NR(cmd);int size = _IOC_SIZE(cmd);printk("dir=%d size=%d\n",dir,size);//检验cmd是否正确//1.校验cmd_typeif(DEVICE_TYPE != type){printk(KERN_ERR "cmd type error\n");ret =-1;return ret;}if(HAPTICS_READ_REG == cmd)//读寄存器{//校验nrif(HAPTICS_READ_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("read_reg:%#02x\n",msg.reg_addr);msg.reg_value=0xff;//这里模拟读寄存器的值//将读取到的值传给用户空间ret = copy_to_user((ioctl_protocol_t __user *)arg, &msg, sizeof(ioctl_protocol_t));}}else if(HAPTCIS_WRITE_REG == cmd)//写寄存器{//校验nrif(HAPTICS_WRITE_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("write_reg:%#02x=%#02x\n",msg.reg_addr,msg.reg_value);//模拟写寄存器的值}}else//{printk(KERN_ERR "unknown cmd\n");}return ret;
}static struct file_operations haptics_fops=
{.owner = THIS_MODULE,.open = haptics_open,.release = haptics_release,.unlocked_ioctl = haptics_ioctl,
};struct miscdevice mdev =
{.minor = MISC_DYNAMIC_MINOR,.name = HAPTICS_MISC_DEV_NAME,.fops = &haptics_fops,
};//定义一个杂项设备结构体static int __init haptics_init(void)
{int ret = 0;//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "%s\n",__FUNCTION__); //输出等级为0ret = misc_register(&mdev);if(0 == ret){printk(KERN_EMERG "misc_register ok minor=%d\n",mdev.minor);}return 0;
}static void __exit haptics_exit(void)
{misc_deregister(&mdev);printk(KERN_EMERG "%s\n",__FUNCTION__); //输出等级为0
}module_init(haptics_init);//驱动入口
module_exit(haptics_exit);//驱动出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptics Driver V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本
上面是驱动代码,负责解析ioctl
命令。
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "haptics_ioctl.h"
/*
argc:应用程序参数个数,包括应用程序本身
argv[]:具体的参数内容,字符串形式
./main <filename> <r:w> r表示读 w表示写
*/int main(int argc,char * argv[])
{ioctl_protocol_t msg;char* filename;int fd=0;if(argc!=3){printf("error usage\n");return -1;}filename=argv[1];fd = open(filename,O_RDWR);if(fd<0){printf("can not open file %s\n",filename);return -2;}if(0 == strcmp(argv[2],"r")){msg.reg_addr = 0x01;ioctl(fd,HAPTICS_READ_REG,&msg);printf("read %#02x=%#02x\n", msg.reg_addr,msg.reg_value);}else if(0 == strcmp(argv[2],"w")){msg.reg_addr = 0x01;msg.reg_value = 0xff;ioctl(fd,HAPTCIS_WRITE_REG,&msg);}else{printf("error usage\n");}close(fd);
}
上面是应用层代码,负责发送ioctl
消息。
注意:
_IOR
中第三个参数size是参数的大小,用于驱动中解析使用,但是如果是固定长度,这个参数也没有用到。理论上使用_IOR
、_IOW
或_IO
是没有严格区别的,所以我们上述代码中可以改为\#define HAPTICS_READ_REG _IO(DEVICE_TYPE,HAPTICS_READ_REG_NR)
ioctl
函数中第三个参数,表示传入驱动的实际参数,可以是一个整型值,也可以是一个结构体指针。我们示例中是传入的结构体指针。
相关文章:

【Linux】ioctl分析
简介 一个字符设备驱动通常会实现常规的open、release、read和write接口,但是如果需要扩展新的功能,通常以ioctl接口的方式实现。 #mermaid-svg-uY8EyPklf5e4ZMQo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill…...

物联网通信会给人们的生活带来什么样的变化
物联网(IoT)通信的崛起正以前所未有的速度改变着人们的生活方式。从智能家居、智能交通到远程医疗、工业自动化,物联网技术的应用已经渗透到我们日常生活的方方面面。以下是对物联网通信如何具体影响并改变人们生活的详细探讨。 一、智能家居…...

Android 中获取当前 CPU 频率和占用率
最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下: 目前没有标准的 API 来获取 CPU 的使用频率,只能通过读取指定 CPU 文件获取当前 CPU 频率,在某些机器或者特定版本中…...

pymobiledevice3使用介绍(安装、常用命令、访问iOS沙盒目录)
项目地址:https://github.com/doronz88/pymobiledevice3 首先先介绍一下pymobiledevice3, pymobiledevice3是用Python3 实现的,用于处理 iDevices(iPhone 等)。它可以跨平台使用,支持:windows…...

python 爬虫模拟登录
在使用 Python 编写爬虫时,模拟登录是一个非常常见的需求,尤其是当你需要爬取需要身份验证的数据时。模拟登录通常需要以下步骤: 分析登录页面:确定提交登录请求的 URL 和相关参数。发送登录请求:模拟用户发送登录表单…...

AOP基础、快速入门、进阶
一、概述 AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程 那什么又是面向方法编程呢,为什么又需要面向方法编程呢?来我们举个例子做一个说明: 比如…...

哪款宠物空净运行吸毛好、噪音小?希喂、霍尼韦尔、安德迈测评!
作为宠物领域目前最火热的产品,宠物空气净化器的讨论度一直很高。身为铲屎官的我在产品刚出的时候就购入了一台,结果让我非常失望! 抛开产品效果不提,它运行起来的声音实在太大了!我家猫根本不愿意靠近,每…...

新兴的安全职业挑战
我们经常与安全专业人士交谈,他们希望在努力提升职业发展的同时提高自己的价值并克服组织内部的挑战。在这些谈话中,花费大量时间讨论公司未来将面临的安全问题并不罕见。 安全领导者希望为问题制定计划并获得领导层对其计划的支持。这通常意味着实施修…...

代码随想录算法训练营Day32 | 122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组和
目录 122.买卖股票的最佳时机Ⅱ 55.跳跃游戏 45.跳跃游戏Ⅱ 1005.K次取反后最大化的数组和 122.买卖股票的最佳时机Ⅱ 题目 122. 买卖股票的最佳时机 II - 力扣(LeetCode) 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i…...

3D Slicer 教程一
先了解一下什么是3D Slicer,这个是做什么,然后一步步了解功能,一起看看源码 一.初识 这块软件用来处理医学影像,是一款开源的软件. 里面涉及到一些 2d 常见的操作,图像处理,调窗,测量; 涉及到3d的一些常见重建,mpr,vr,cpr等, 还包括一些分割,变换等(越高级精确的一些通过插件…...

github pages + hugo 搭建静态博客网站
体验地址 1. 起因, 目的: 其实6年前,我就写过这个。 项目代码 博客地址 最近想改写一下。 github 推荐的主题是 Jekyll, 我当时用的就是这个,感觉很麻烦。尤其是文章命名。 新的主题 hugo 用起来还行。 2.过程: 过程记录&am…...

Python爬虫如何爬取并解析JSON数据
前言 Python爬虫是一种用于从互联网上获取数据的程序,而JSON(JavaScript Object Notation)是一种常用的数据交换格式。本文将介绍如何使用Python爬虫来爬取并解析JSON数据,同时还会讲解如何使用代理IP来提高爬取效率。 1. 什么是…...

【C++】精妙的哈希算法
🚀个人主页:小羊 🚀所属专栏:C 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 一、哈希结构1、哈希概念2、哈希函数3、哈希冲突3.1 闭散列3.2 开散列 4、完整代码 一、哈希结构 1、哈希概念 A…...

智慧链动青春:国家区块链中心接待北京市十一学校青少年访学探索
以生动科学的方法点燃青少年科学探索欲望是构建未来科技人才梯队的基石。近期国家区块链技术创新中心接待北京市十一学校新生访学,以科普讲座、实操互动的方式让学生在深度思考中感受科学魅力、接触前沿科技,激发学生对区块链、隐私计算和芯片设计制造的…...

利用C++封装鼠标轨迹算法为DLL:游戏行为检测的利器
在现代软件开发中,鼠标轨迹模拟技术因其在自动化测试、游戏脚本编写等领域的广泛应用而备受青睐。本文将介绍如何使用C语言将鼠标轨迹算法封装为DLL(动态链接库),以便在多种编程环境中实现高效调用,同时探讨其在游戏行…...

Qt- QSS风格选择器常用属性选择器样式表盒子
1. 风格设置 Qt 提供了 3 种整体风格,使用 QStyleFactory::keys() 来获取 (windowsvista 、Windows 、Fusion) 可以在 main.cpp 中调用 setStyle 方法对应用程序进行全局风格的设置 int main(int argc, char *argv[]) {QApplication a(arg…...

粤智助自助一体机大厂浮出水面 OBOO鸥柏已成服务终端中坚力量
自助服务查询一体机作为操作自主化便民的重要窗口,OBOO鸥柏自助服务终端机以其显著的技术优化,通过触摸屏或其他交互界面,使用户能够自助服务完成各种操作,如支付、查询信息终端、办理业务,自助查档答应一体化等。为交…...

SpringBoot-application.properties配置
默认配置最终都是映射/关联到某个类 #SPRING CONFIG(ConfigFileApplicationListener) spring.config.name #配置文件名(默认 为 application ) spring.config.location #配置文件的位置 …...

STM32-ADC模数转换
一、概述 ADC(Analog-Digital Converter)模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁12位逐次逼近型ADC,1us转换时间输入电压范围:0~3.3Vÿ…...

lspci | grep VGA
执行lspci | grep VGA后如下,解释含义 00:0f.0 VGA compatible controller: VMware SVGA II Adapter 0b:00.0 VGA compatible controller: NVIDIA Corporation GA104 [GeForce RTX 3070] (rev a1) 执行 lspci | grep VGA 命令后,您得到了两条输出&#…...

智慧厂区车辆导航解决方案;智慧工厂电子地图应用解决方案;大型工厂内部导航解决方案;智慧工厂可视化地图应用方案
智慧厂区车辆导航解决方案;智慧工厂电子地图应用解决方案 在智慧工业的蓬勃发展背景下,上海懒图科技凭借其室内电子地图技术的深厚积淀,正为智慧工厂物流管理领域注入革新力量。其创新的车辆导航与可视化管理系统,凭借高精度定位…...

决策树C4.5算法详解及实现
C4.5决策树是一种广泛使用的机器学习算法,它用于分类任务。它是在ID3算法的基础上改进的,主要通过生成决策树来构建分类模型。C4.5通过以下步骤工作: 1. 数据集分裂 C4.5通过选择具有最高信息增益率的特征来分裂数据集。信息增益率…...

prompt learning
prompt learning 对于CLIP(如上图所示)而言,对其prompt构造的更改就是在zero shot应用到下游任务的时候对其输入的label text进行一定的更改,比如将“A photo of a{obj}”改为“[V1][V2]…[Vn][Class]”这样可学习的V1-Vn的token…...

适用于 Windows 11 的 5 大数据恢复软件 [免费和付费]
为什么我们需要Windows 11数据恢复软件? 计算机用户经常遇到的一件事就是数据丢失,这种情况随时可能发生。错误地删除重要文件和文件夹可能会非常令人担忧,但幸运的是,有一种方法可以恢复 PC 上丢失的数据。本文将向您展示可用于…...

vue实现获取当前时间并实时显示
以下代码可以实现获取当前时间并实时显示,朋友们直接copy使用即可,希望可以帮助到有需要的朋友们! <template><div class"time">{{ datetimeStr }}</div> </template> <script>export default {data…...
【论文阅读】SRCNN
学习资料 论文题目:Learning a Deep Convolutional Network for Image Super-Resolution(学习深度卷积网络用于图像超分辨率)论文地址:link.springer.com/content/pdf/10.1007/978-3-319-10593-2_13.pdf代码:作者提出的…...

数据结构与算法——Java实现 32.堆
目录 堆 大顶堆 威廉姆斯建堆算法 Floyd建堆算法 Floyd建堆算法复杂度 大顶堆代码实现 人的想法和感受是会随着时间的认知改变而改变, 原来你笃定不会变的事,也会在最后一刻变得释然 —— 24.10.10 堆 堆是基于二叉树实现的数据结构 大顶堆任意一个父节…...

深度学习 .dot()
在 MXNet 中,.dot() 是用于计算两个数组的点积(矩阵乘法)的方法。这个方法适用于一维和二维数组,并返回它们的点积结果。 语法 ndarray1.dot(ndarray2) 参数 ndarray1: 第一个输入数组。ndarray2: 第二个输入数组,…...

idea2024 git merge 时丢失 Merge remote-tracking branch问题
idea2024 git merge 时丢失 Merge remote-tracking branch问题 处理建议 直接修改本地git的配置 git config --global merge.ff false 分析 在 IntelliJ IDEA 中进行 Git merge 操作时,有时你可能会遇到提交历史中丢失 Merge remote-tracking branch 的信息&#…...

pdf怎么删除多余不想要的页面?删除pdf多余页面的多个方法
pdf怎么删除多余不想要的页面?在日常办公或学习中,我们经常会遇到需要处理PDF文件的情况。PDF文件因其格式稳定、不易被篡改的特点而广受青睐,但在编辑方面却相对不如Word等文档灵活。有时,在接收或创建的PDF文件中,可…...