Linux系统驱动(三)ioctl函数

news2025/1/22 16:12:33

文章目录

  • 一、ioctl函数
    • (一)函数格式
    • (二)ioctl命令码的组成
      • 1. 命令码的组成
      • 2. 自己封装命令码
      • 2. 内核提供了封装命令码的宏
    • (三)使用示例
      • 1. 驱动
      • 2. 应用

一、ioctl函数

Linux内核开发者想要将数据的读写和设备的控制分开完成操作,

所以设计了ioctl函数。也就是说数据的读写在read/write中完成,
而设备的控制在ioctl中完成。

(一)函数格式

US:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...)
功能:设备的控制操作
参数:
	@fd:文件描述符
	@request:命令码
	@...:可变参,填写必须填写地址
返回值:成功返回0,失败返回-1,置位错误码
-----------------------------------------------
KS:
#include <linux/uaccess.h>
file_operations:
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
	//request传给了cmd
	//...传递给arg,即arg存放的是参数的地址,接收到后需要强转为指针
  • 注:ioctl是应用层接口,内核层次也有与之对应的接口函数
  • 底层函数的返回值返回给上层应用层函数

(二)ioctl命令码的组成

  • 注:*Documentation目录下存放帮助文档,如果需要了解函数如何使用,就进这个目录下
    在这里插入图片描述

1. 命令码的组成

linux@ubuntu:~/linux-5.10.61/Documentation$ vi ./userspace-api/ioctl/ioctl-decoding.rst
在文档中可以看到如下信息:
在这里插入图片描述

在这里插入图片描述
[31:30] 读写标志位:01 — 写,10 — 读;11 — 读写;此处的方向位是相对用户来定的,用户具有写权限或者用户具有读权限
[29:16] 第三个参数占内存的大小,单位是字节;
[16:8] 每个驱动由唯一的ascii字符来标识;
[7:0] 功能码;

2. 自己封装命令码

#define LED_ON 0b01 00000000000100  01001100 00000001
				|			|			|		|--->功能码,自定义
				|			|			|-->此处以'L'为例0114--0b01001100
				|			|-->此处以int型数据为例,此处定义4字节
				|-->读写标志位
#define LED_OFF 0b01 00000000000100  01001100 00000000

2. 内核提供了封装命令码的宏

通过内核的宏封装命令码:
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
参数:
	@type:类型,即用来标识驱动的字符
	@nr	 :功能码,来区分同一驱动不同的功能
  • 注:此处的size要传数据类型,因为这个宏传入的参数实际在_IOC这个宏中要使用sizeof来确定大小。
#define _IOC(dir,type,nr,size) \
        (((dir)  << 30) | \
         ((type) << 8) | \
         ((nr)   << 0) | \
         ((size) << 16))

使用示例:

#define LED_ON   _IOW('L',0,int)
#define LED_OFF  _IOW('L',1,int)

(三)使用示例

功能需求:
通过ioctl函数实现LED灯的流水

需求分析:
用户层通过ioctl函数,将命令码和

代码实现:

1. 驱动

myioctl.h

#ifndef __MYIOCTL_H__
#define __MYIOCTL_H__

#define CHRNAME "myioctl"
//寄存器基地址---物理地址(使用前需要先转换为虚拟地址)
#define RCC_AHB4_BASE   0x50000A28
#define GPIOE_BASE      0x50006000
#define GPIOF_BASE      0x50007000

/***RCC_AHB4***/
typedef struct{
    unsigned int gpioA:1;
    unsigned int gpioB:1;
    unsigned int gpioC:1;
    unsigned int gpioD:1;
    unsigned int gpioE:1;
    unsigned int gpioF:1;
    unsigned int gpioG:1;
    unsigned int gpioH:1;
    unsigned int gpioI:1;
    unsigned int gpioJ:1;
}rcc_bit_1_t;
typedef struct{
    rcc_bit_1_t rcc_ahb4;
}rcc_ahb4_t;

//使用位域填充寄存器,方便操作
//对应1个bit管理一个引脚的寄存器
//unsigned int是四字节大小,刚好等于一个寄存器的大小,使用位域也不会再对unsigned int类型进行压缩
//即使没有占满四字节,该结构体类型仍然占用四字节
typedef struct{
    unsigned int gpiox0:1;
    unsigned int gpiox1:1;
    unsigned int gpiox2:1;
    unsigned int gpiox3:1;
    unsigned int gpiox4:1;
    unsigned int gpiox5:1;
    unsigned int gpiox6:1;
    unsigned int gpiox7:1;
    unsigned int gpiox8:1;
    unsigned int gpiox9:1;
    unsigned int gpiox10:1;
    unsigned int gpiox11:1;
    unsigned int gpiox12:1;
    unsigned int gpiox13:1;
    unsigned int gpiox14:1;
    unsigned int gpiox15:1;
}gpio_bit_1_t;
//对应两bit管理一个引脚的寄存器
typedef struct{
    unsigned int gpiox0:2;
    unsigned int gpiox1:2;
    unsigned int gpiox2:2;
    unsigned int gpiox3:2;
    unsigned int gpiox4:2;
    unsigned int gpiox5:2;
    unsigned int gpiox6:2;
    unsigned int gpiox7:2;
    unsigned int gpiox8:2;
    unsigned int gpiox9:2;
    unsigned int gpiox10:2;
    unsigned int gpiox11:2;
    unsigned int gpiox12:2;
    unsigned int gpiox13:2;
    unsigned int gpiox14:2;
    unsigned int gpiox15:2;
}gpio_bit_2_t;
//定义gpio的类型
typedef struct{
    gpio_bit_2_t MODER;
    gpio_bit_1_t OTYPER;
    gpio_bit_2_t OSPEEDR;
    gpio_bit_2_t PUPDR;
    gpio_bit_1_t IDR;
    gpio_bit_1_t ODR;
}gpio_t;

/***定义命令码***/
#define LED1_ON _IOW('L',00,int)
#define LED1_OFF _IOW('L',01,int)
#define LED2_ON _IOW('L',10,int)
#define LED2_OFF _IOW('L',11,int)
#define LED3_ON _IOW('L',20,int)
#define LED3_OFF _IOW('L',21,int)

#endif

myioctl.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include "myioctl.h"

int major; //因为该参数不仅要在注册设备的函数中使用,还要再销毁设备的函数中使用,因此必须定义为全局变量
rcc_ahb4_t *mrcc_ahb4;
gpio_t * mgpioe;
gpio_t * mgpiof;
//实现驱动层的打开函数
int myioctl_open(struct inode *inode, struct file *file){
    //1. 将物理地址映射为虚拟地址
    mrcc_ahb4 = ioremap(RCC_AHB4_BASE,sizeof(rcc_ahb4_t));
    //ioremap函数第一个参数是unsigned long类型的参数
    if(NULL == mrcc_ahb4){//失败返回NULL
        pr_err("ioremap error");
        return -ENOMEM;
    }
    mgpioe=ioremap(GPIOE_BASE,sizeof(gpio_t));
    if(NULL == mrcc_ahb4){//失败返回NULL
        pr_err("ioremap error");
        return -ENOMEM;
    }
    mgpiof=ioremap(GPIOF_BASE,sizeof(gpio_t));
    if(NULL == mrcc_ahb4){//失败返回NULL
        pr_err("ioremap error");
        return -ENOMEM;
    }
    //2. 初始化LED灯,并且将其初始值设为熄灭状态
    //时钟使能
    mrcc_ahb4->rcc_ahb4.gpioE=1;
    mrcc_ahb4->rcc_ahb4.gpioF=1;
    //输出模式01
    mgpioe->MODER.gpiox10=1;
    mgpioe->MODER.gpiox8=1;
    mgpiof->MODER.gpiox10=1;
    //输出低电平:熄灭
    mgpioe->ODR.gpiox10=0;
    mgpiof->ODR.gpiox10=0;
    mgpioe->ODR.gpiox8=0;

    return 0; //注意注意!!!!驱动中如果函数返回值不是void,就必须加return
}
//实现驱动层的关闭函数
int myioctl_close(struct inode *inode, struct file *file){
    //取消地址映射
    iounmap(mrcc_ahb4);
    iounmap(mgpioe);
    iounmap(mgpiof);
    return 0;
}
//实现驱动层的ioctol函数
long myioctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
    //arg:第三个参数的首地址,由cmd[29:16]决定大小,此处无需第三个参数
    switch(cmd){
        case LED1_ON:
            //判断是否有写权限
            if(cmd & (0x1<<30)){
                mgpioe->ODR.gpiox10=1;
            }
            else{
                pr_err("NO Write pression\n");
            }
            break;
        case LED1_OFF:
            //判断是否有写权限
            if(cmd & (0x1<<30)){
                mgpioe->ODR.gpiox10=0;
            }
            else{
                pr_err("NO Write pression\n");
            }
            break;
        case LED2_ON:
            //判断是否有写权限
            if(cmd & (0x1<<30)){
                mgpiof->ODR.gpiox10=1;
            }
            else{
                pr_err("NO Write pression\n");
            }
            break;
        case LED2_OFF:
            //判断是否有写权限
            if(cmd & (0x1<<30)){
                mgpiof->ODR.gpiox10=0;
            }
            else{
                pr_err("NO Write pression\n");
            }
            break;
        case LED3_ON:
            //判断是否有写权限
            if(cmd & (0x1<<30)){
                mgpioe->ODR.gpiox8=1;
            }
            else{
                pr_err("NO Write pression\n");
            }
            break;
        case LED3_OFF:
            //判断是否有写权限
            if(cmd & (0x1<<30)){
                mgpioe->ODR.gpiox8=1;
            }
            else{
                pr_err("NO Write pression\n");
            }
            break;
    }

    return 0; //应用层ioctl成功返回0; 失败返回-1
}

//file_operations结构体
const struct file_operations myfops={
    .open=myioctl_open,
    .release=myioctl_close,
    .unlocked_ioctl=myioctl_ioctl,
};

/***驱动三要素***/
static int __init myioctl_init(void){
    //入口注册设备
    major=register_chrdev(0,CHRNAME,&myfops); //第一个参数为0,表示由系统分配主设备号,此时返回值就是系统分配的主设备号
    if(major < 0){//说明出错,返回了错误码,错误码均为负数
        pr_err("register_chrdev error:%d\n",major);
        return major; //失败返回错误码
    }
    printk("major=%d\n",major);
    return 0; //成功返回0
}

static void __exit myioctl_exit(void){
    //出口销毁设备
    unregister_chrdev(major,CHRNAME);
}

module_init(myioctl_init);
module_exit(myioctl_exit);
MODULE_LICENSE("GPL");

2. 应用

test.h

#ifndef __TEST_H__
#define __TEST_H__

#include <stdio.h>
#include <sys/ioctl.h> //yingyong
#include <my_head.h>

/***定义命令码***/
#define LED1_ON _IOW('L',00,int)
#define LED1_OFF _IOW('L',01,int)
#define LED2_ON _IOW('L',10,int)
#define LED2_OFF _IOW('L',11,int)
#define LED3_ON _IOW('L',20,int)
#define LED3_OFF _IOW('L',21,int)

#endif

test.c

#include "test.h"

int main(int argc, char const *argv[])
{
    //入参合法性检查
    if(2!=argc){
        printf("Usage:%s dev_file!\n",argv[0]);
        exit(-1);
    }

    int fd=0;
    fd=open(argv[1],O_RDWR);
    if(0 > fd){
        printf("open error\n");
        exit(-1);
    }
	//使用死循环,实现三个LED灯轮流点亮
    while(1){
        ioctl(fd,LED1_ON);
        sleep(1);
        ioctl(fd,LED1_OFF);
        ioctl(fd,LED2_ON);
        sleep(1);
        ioctl(fd,LED2_OFF);
        ioctl(fd,LED3_ON);
        sleep(1);
        ioctl(fd,LED3_OFF);
    }
    return 0;
}
  • 注:在宏定义中##表示字符串拼接

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++ 21 指针

*像一把钥匙 通过钥匙去找内存空间 间接修改内存空间的值 不停的给指针赋值 等于不停的更改指针的指向 指针也是一种数据类型 指针做函数参数怎么看都不可以 指针也是一个数据类型 是指它指向空间的数据类习惯 作业 野指针 向null空间地址copy数据 不断改变指针指向 …

JVM的组成 -- 字节码文件

类加载器(ClassLoader)&#xff1a;将字节码文件加载到内存中运行时数据区(JVM管理的内存)&#xff1a;负责管理JVM使用的内存&#xff0c;比如创建对象和销毁对象执行引擎&#xff1a;即时编译器、解释器、垃圾回收器。负责本地接口的调用本地接口&#xff1a;native方法&…

高等数学 第八讲 积分学计算_不定积分_定积分_反常积分的计算

高等数学 第八讲 积分学计算 文章目录 高等数学 第八讲 积分学计算1.不定积分的计算1.1 基本积分公式1.2 不定积分的计算方法1.2.1 凑微分法1.2.2 换元法1.2.3 分布积分法1.2.4 有理函数的积分计算(待更新)1.2.5 不定积分的一些计算结论总结 2.定积分的计算2.1 牛顿莱布尼茨公式…

大数据Flink(一百零八):阿里云与开源的功能优势对比

文章目录 阿里云与开源的功能优势对比 阿里云与开源的功能优势对比 下面通过表格介绍阿里云实时计算Flink全托管产品的功能点和价值&#xff0c;以及和开源Flink的对比优势。 类型 功能 描述 价值 性能与成本 资源利用率提升 可以根据业务负载进行弹性扩缩容。 更好的…

手摸手教你前端和后端是如何实现导出 Excel 的?

前言 大家好呀&#xff0c;我是雪荷。在上篇文章&#xff08;EasyExcel 初使用—— Java 实现多种写入 Excel 功能-CSDN博客&#xff09;中给大家介绍了 Java 是如何写入 Excel 的&#xff0c;那么这篇算是对上篇文章的拓展&#xff0c;主要介绍前端和后端分别是如何导出数据至…

一篇了解: MyBatis-Plus 操作数据库的使用

目录 一、MyBatis-Plus介绍 二、基础使用 2.1 准备工作 2.2 编码 2.3 CRUD单元测试 三、MyBatis-Plus复杂操作 3.1 打印日志 3.2 常见注解 3.2.1 TableName 3.2.2 TableField 3.2.3 TableId 3.3 条件构造器 3.3.1 QueryWrapper 3.3.2 UpdateWrapper 3.3.3 Lamb…

网络空间安全专业怎么样,可通过哪些途径自学?

网络空间安全主要研究网络空间的组成、形态、安全、管理等&#xff0c;进行网络空间相关的软硬件开发、系统设计与分析、网络空间安全规划管理等。例如&#xff0c;网络犯罪的预防&#xff0c;国家网络安全的维护&#xff0c;杀毒软件等安全产品的研发&#xff0c;网络世界的监…

计算机常识与NOIP历史-CSP初赛知识点整理

真题练习 [2021-CSP-J-第2题] 以下奖项与计算机领域最相关的是&#xff08; &#xff09;。 A.奥斯卡奖 B.图灵奖 C.诺贝尔奖 D.普利策奖 [2017-NOIP-第7题] 中国计算机学会于( )年创办全国青少年计算机程序设计竞赛。 A. 1983 B. 1984 C. 1985 D. 1986 [2018-NOIP-第5题…

Professional Scrum Master (PSM) 官方认证培训班:掌握Scrum,提升项目管理能力

在快速变化的商业环境中&#xff0c;高效的项目管理和团队协作是企业成功的关键。作为一种广泛认可的敏捷框架&#xff0c;Scrum已成为推动项目成功和提高团队效率的重要工具。为了帮助专业人士掌握Scrum方法和实践&#xff0c;Scrum.org推出了Professional Scrum Master (PSM)…

C++ 初探:不要‘下次一定’,立即开始你的C++之旅

C初识 文章目录 C hello worldnamespace&#xff0c;命名空间命名空间的使用域作用限定符展开命名空间指定展开命名空间成员 C的域 C的输入和输出缺省参数函数重载引用&#xff08;reference&#xff09;引用概念引用的特性引用的使用const引用 inlinenullptr C hello world #…

学习C语言遇到的问题

前言 这是博主在班训班培训期间学习C基础过程中遇到的一些问题&#xff0c;我把遇到的问题以及这些问题的解答链接统一放在这篇文章了。这只是博主遇到的一些问题&#xff0c;可能不会适用于全部人&#xff0c;希望大家把这篇文章当做查漏补缺的内容吧。 问题一:把字符串赋给…

三十种未授权访问漏洞复现 合集( 三)

未授权访问漏洞介绍 未授权访问可以理解为需要安全配置或权限认证的地址、授权页面存在缺陷&#xff0c;导致其他用户可以直接访问&#xff0c;从而引发重要权限可被操作、数据库、网站目录等敏感信息泄露。---->目录遍历 目前主要存在未授权访问漏洞的有:NFS服务&a…

CnosDB 元数据集群 – 分布式时序数据库的大脑

CnosDB 是一个分布式时序数据库系统&#xff0c;其中元数据集群是核心组件之一&#xff0c;负责管理整个集群的元数据信息。 1. 概述 CnosDB 是一个分布式时序数据库系统&#xff0c;其中元数据集群是核心组件之一&#xff0c;负责管理整个集群的元数据信息。元数据包括数据库…

用Ollama 和 Open WebUI本地部署Llama 3.1 8B

说明&#xff1a; 本人运行环境windows11 N卡6G显存。部署Llama3.1 8B 简介 Ollama是一个开源的大型语言模型服务工具&#xff0c;它允许用户在自己的硬件环境中轻松部署和使用大规模预训练模型。Ollama 的主要功能是在Docker容器内部署和管理大型语言模型&#xff08;LLM&…

[CR]厚云填补_GridFormer

GridFormer: Residual Dense Transformer with Grid Structure for Image Restoration in Adverse Weather Conditions Abstract 恶劣天气条件下的图像恢复是计算机视觉中的一个难点。在本文中&#xff0c;我们提出了一种新的基于变压器的框架GridFormer&#xff0c;它可以作为…

【Android】ContentProvider基本概念

ContentProvider Android权限机制详解 <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"com.example.broadcasttest"> <uses-permission android:name"android.permission.RECEIVE_BOOT_COMPLETED" />…

8.2 grafana上导入模板看图并讲解告警

本节重点介绍 : grafana 上导入mysqld-dashboardglobal status 相关源码解读重要指标讲解 连接数内存TPS、QPS 将采集任务添加到prometheus中 - job_name: mysqld_exporterhonor_timestamps: truescrape_interval: 8sscrape_timeout: 8smetrics_path: /metricsscheme: httpf…

七天打造一套量化交易系统:Day7-实盘交易接入方式与注意事项

七天打造一套量化交易系统&#xff1a;Day7-实盘交易接入方式与注意事项 前情回顾证券交易接口XTP 接口头文件列表XTP 接口 demo 示例 期货交易接口CTP-API开发系列专栏 数字货币交易接口实盘接入注意事项 量化交易系统的核心要素包括选择投资标的、资金的分配、何时入场、何时…

Midjourney咒语之装修设计

装修设计 living room with a chinese shanshui painting frame on a wall with a 2 inch frame, colors: blue, white, focus on the picture, 35mm lens, realistic, design, commercial, plants, furniture, centered painting --s 750 --ar 16:9 80 square meter minimalis…

浅谈简单的搜索算法(c++)

目录 DFS思路实现应用场景DFS 的优缺点优点缺点 例题讲解N皇后问题[题目描述]输入输出样例输入样例输出 思路AC代码排列数字[题目描述]输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a;思路AC代码 树的重心[题目描述]输入格式输出格式数据范围输入样例输出样…