Linux系统驱动(二)字符设备驱动
文章目录
- 一、概念
- (一)相关概念
- (二)字符设备框架结构
- (三)用户空间和内核空间数据传输
- 1. 函数的参数对应关系
- (四)字符设备相关的API
- 1. 字符设备驱动
- (1)注册字符设备驱动
- (2)销毁字符设备驱动
- 2. 用户和内核数据传输
- (1)copy_to_user
- (2)copy_from_user
- 3. 物理地址映射虚拟地址
- (1)地址映射
- (2)取消地址映射
- 二、驱动代码示例
- (一)查看芯片手册
- 1. 寄存器基地址
- (一)驱动代码
- (二)测试
一、概念
(一)相关概念
设备号:内核识别驱动的唯一的编号,设备号由32个bit位组成
设备号(32bit)=主设备号(高12bit)+次设备号(低20bit)
主设备号:代表的是哪一类设备
次设备号:代表同类中的哪一个设备
(二)字符设备框架结构
当用户在应用层面去调用open/read/write/close函数时,操作的是设备文件;
而创建设备文件时,通过设备号把设备文件和内核层面的设备驱动联系起来,又通过file_operations操作方法结构体将函数逐个对应;
在内核层面,驱动通过操作寄存器,最后实现对硬件设备的控制
(三)用户空间和内核空间数据传输
1. 函数的参数对应关系
用户在应用层通过write函数将buff的内容传给内核层中mycdev_write函数中ubuf,
为了保证内核的安全性,需要通过copy_from_user函数,将ubuf的数据拷贝一份到kbuf中,通过操作kbuf来使用用户传入的数据。
read函数同理,用户在应用层通过read函数读取buf,其实在内核层面。是通过copy_to_user函数,将内核空间数据拷贝一份到kbuf,通过kbuf来传递给用户
使用*ubuf会报错:地址不允许访问
(四)字符设备相关的API
1. 字符设备驱动
(1)注册字符设备驱动
#include <linux/fs.h>int register_chrdev(unsigned int major,const char *name,const struct file_operations * fops)
功能:注册字符设备驱动; (register char device)
参数:@major:主设备号(申请到一个主设备号后,0-255的这256个次设备号会全部被分配)major > 0 用户指定主设备号,一般不使用,因为如果与系统已定义的主设备号冲突就会报错major = 0 系统动态分配主设备号@name:设备的名字@fops:操作方法结构体指针struct file_operations{int (*open) (struct inode *, struct file *);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);int (*release) (struct inode *, struct file *);}
返回值:major > 0 ,成功返回0,失败返回错误码major = 0 ,成功返回主设备号,失败返回错误码
- 补:
cat /proc/devices
查看设备名
(2)销毁字符设备驱动
#include <linux/fs.h>int unregister_chrdev(unsigned int major,const char *name)
功能:销毁字符设备驱动
参数:@major:主设备号@name:设备的名字
返回值:无
2. 用户和内核数据传输
(1)copy_to_user
#include <linux/uaccess.h>int copy_to_user(void __user volatile *to, const void *from,unsigned long n)
功能:将数据从内核空间拷贝到用户空间(在驱动的在驱动的read中使用)
参数:@to:用户空间的数据首地址@from:内核空间数据首地址@n:大小,单位是字节
返回值:成功返回0,失败返回未拷贝的字节个数
(2)copy_from_user
int copy_from_user(void *to, const void __user volatile *from,unsigned long n)
功能:将数据从用户空间拷贝到内核空间(在驱动的在驱动的write中使用)
参数:@to:内核空间的数据首地址@from:用户空间数据首地址@n:大小,单位是字节
返回值:成功返回0,失败返回未拷贝的字节个数
- 注:两个函数都是第一个参数是拷贝到哪,第二个参数是从哪里拷贝
3. 物理地址映射虚拟地址
如果要将开发板上的LED点亮,那就必须操作LED灯对应的寄存器,寄存器的地址是物理地址。
驱动运行在3-4G的虚拟地址空间,所以没有办法直接控制LED的亮灭。如果想要在内核空间操作LED那就必须将LED灯的物理地址映射成虚拟地址,以后在内核空间操作这块虚拟地址就相当于在操作LED的物理地址。
(1)地址映射
#include <linux/io.h>void *ioremap(unsigned long phy_addr, unsigned long size)
功能:将物理地址映射成虚拟地址
参数:@phy_addr:物理地址@size:映射的大小,单位是字节
返回值:成功返回内核空间的虚拟地址,失败返回NULL
- 注:
- ioremap一次最多映射4k字节地址
(2)取消地址映射
void iounmap(void *virt_addr)
功能:取消地址映射
参数:@virt_addr:虚拟地址
返回值:无
- 注:地址映射后,如果不取消映射就会造成内存泄漏
二、驱动代码示例
(一)查看芯片手册
1. 寄存器基地址
图片1
(一)驱动代码
功能需求:实现LED1,LED2,LED3交替亮1s
需求分析:
LED1—PE10
LED2—PF10
LED3—PE8
GPIOE基地址—0x50006000
GPIOF基地址—0x50007000
RCC_AHB4基地址—0x50000A28
代码实现:
myled.h
#ifndef __MYLED_H__
#define __MYLED_H__#define GPIOE_BASE 0x50006000
#define GPIOF_BASE 0x50007000
#define RCC 0x50000A28
typedef struct{unsigned int gpiox_0:1;unsigned int gpiox_1:1;unsigned int gpiox_2:1;unsigned int gpiox_3:1;unsigned int gpiox_4:1;unsigned int gpiox_5:1;unsigned int gpiox_6:1;unsigned int gpiox_7:1;unsigned int gpiox_8:1;unsigned int gpiox_9:1;unsigned int gpiox_10:1;unsigned int gpiox_11:1;unsigned int gpiox_12:1;unsigned int gpiox_13:1;unsigned int gpiox_14:1;unsigned int gpiox_15:1;
}bitf1_t;
typedef struct{unsigned int gpiox_0:2;unsigned int gpiox_1:2;unsigned int gpiox_2:2;unsigned int gpiox_3:2;unsigned int gpiox_4:2;unsigned int gpiox_5:2;unsigned int gpiox_6:2;unsigned int gpiox_7:2;unsigned int gpiox_8:2;unsigned int gpiox_9:2;unsigned int gpiox_10:2;unsigned int gpiox_11:2;unsigned int gpiox_12:2;unsigned int gpiox_13:2;unsigned int gpiox_14:2;unsigned int gpiox_15:2;
}bitf2_t;typedef struct{volatile bitf2_t MODER;volatile bitf1_t OTYPER;volatile bitf2_t OSPEEDR;volatile bitf2_t PUPDR;volatile bitf1_t IDR;volatile bitf1_t ODR;
}gpio_t;#define LED1 1
#define LED2 2
#define LED3 3#endif
myled.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include "myled.h"#define CNAME "myled"
int major; //设备号gpio_t *gpioe;
gpio_t *gpiof;
unsigned int *rcc;int kbuf[2];
int mychrdev_open(struct inode *inode, struct file *file){pr_info("%s:%d\n",__func__,__LINE__);//将虚拟地址转换为物理地址rcc=ioremap(RCC,4);if(NULL == rcc){pr_err("RCC ioremap error");return -ENOMEM;}gpioe=ioremap(GPIOE_BASE,sizeof(gpio_t));if(NULL == gpioe){pr_err("GPIOE_BASE ioremap error");return -ENOMEM;}gpiof=ioremap(GPIOF_BASE,sizeof(gpio_t));if(NULL == gpiof){pr_err("GPIOF_BASE ioremap error");return -ENOMEM;}/***初始化LED灯***///使能时钟源*rcc |= 0x3<<4;//RCC使能GPIOE和GPIOF//PE10和PE8输出模式01gpioe->MODER.gpiox_10=1;gpioe->MODER.gpiox_8=1;gpiof->MODER.gpiox_10=1;//输出低电平gpioe->ODR.gpiox_10=0;gpioe->ODR.gpiox_8=0;gpiof->ODR.gpiox_10=0;return 0;
}
ssize_t mychrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *offset){int ret;pr_info("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_to_user(ubuf, kbuf, size);if (ret) {pr_err("copy_to_user error\n");return -EIO;}return 0;
}
ssize_t mychrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offset){int ret;pr_info("%s:%s:%d\n", __FILE__, __func__, __LINE__);memset(kbuf, 0, sizeof(kbuf));if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_from_user(kbuf, ubuf, size);if (ret) {pr_err("copy_from_user error\n");return -EIO;}switch(kbuf[0]){case LED1:kbuf[1]==1?(gpioe->ODR.gpiox_10=1):(gpioe->ODR.gpiox_10=0);break;case LED2:kbuf[1]==1?(gpiof->ODR.gpiox_10=1):(gpiof->ODR.gpiox_10=0);break;case LED3:kbuf[1]==1?(gpioe->ODR.gpiox_8=1):(gpioe->ODR.gpiox_8=0);break;}return 0;
}
int mychrdev_close(struct inode *inode, struct file *file){pr_info("%s:%d\n",__func__,__LINE__);//取消虚拟映射iounmap(rcc);iounmap(gpioe);iounmap(gpiof);return 0;
}const struct file_operations myfops = {.open=mychrdev_open,.read=mychrdev_read,.write=mychrdev_write,.release=mychrdev_close,
};static int __init mychrdev_init(void){//注册设备major = register_chrdev(0,CNAME,&myfops);if(major < 0){pr_err("register error:%d\n",major);return major;//返回错误码}pr_info("major = %d\n",major);//打印设备号return 0;
}
static void __exit mychrdev_exit(void){//注销设备unregister_chrdev(major,CNAME);
}module_init(mychrdev_init);
module_exit(mychrdev_exit);
MODULE_LICENSE("GPL");
- 注:使用的交叉编译器版本比较老,对语法检查要求更高,要求变量只能在函数开头定义,否则报错。
(二)测试
- 补:错误码
/include/uapi/asm0generic/errno-base.h
相关文章:

Linux系统驱动(二)字符设备驱动
文章目录 一、概念(一)相关概念(二)字符设备框架结构(三)用户空间和内核空间数据传输1. 函数的参数对应关系 (四)字符设备相关的API1. 字符设备驱动(1)注册字…...

Day29 | 动态规划 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯
语言 Java 509. 斐波那契数 斐波那契数 题目 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n -…...
【开源移植】MultiButton_小型按键驱动模块移植
MultiButton 简介 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。 使用方法 1.先申请一个…...

【Python系列】Python 字典合并
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

C# 设计模式之装饰器模式
总目录 前言 装饰器模式的主要作用就是扩展一个类的功能,或给一个类添加多个变化的情况。学习面向对象的都知道,如果想单纯的给某个类增加一些功能,可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了,但是…...

【uniapp离线打包】(基于Android studio)
文章目录 uniapp打包官方教程入口一、准备工作(工具三大件)Android Studio版本推荐 二、准备工作(Android壳和uniapp包)导入Android壳生成uniapp包将uniapp包导入android壳降低jdk版本 三、准备工作(证书)准备Android平台离线签名…...

稳稳的年化10%,多任务时序动量策略——基于pytorch的深度学习策略(附python代码)
原创文章第608篇,专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 做因子挖掘这段时间,有一个观感。 传统的因子挖掘,尤其是手工构造因子,到遗传算法因子挖掘。——本身也是一种”拟合“,或者说试图”…...

C++分析AVL树
目录 AVL树介绍 AVL树平衡因子更新分析 AVL树插入时旋转与平衡因子更新 左单旋 右单旋 左右单旋 右左单旋 AVL旋转可行性 AVL树节点删除(待补充) AVL树分析 AVL树介绍 二叉搜索树在某些极端情况下可能会退化,为了解决这个问题&…...

aurora8b10b ip的使用(framing接口下的数据回环测试)
文章目录 一、Aurora8B/10B协议二、时钟、复位与状态指示1、时钟2、复位3、状态指示 三、数据发送、接受接口(1)AXI4-Stream位排序(2)Streaming接口(3)Framing接口(帧传输接口) 四、…...
如何通过OpenCV判断图片是否包含在视频内?
要判断图片是否包含在视频内,可以使用计算机视觉技术和图像处理方法。这通常涉及特征匹配或模板匹配。以下是一个基于OpenCV的解决方案,通过特征匹配的方法来实现这一目标。 步骤概述 读取视频和图片: 使用OpenCV读取视频文件和图片文件。 …...

大数据基础:Spark重要知识汇总
文章目录 Spark重要知识汇总 一、Spark 是什么 二、Spark 四大特点 三、Spark框架模块介绍 3.1、Spark Core的RDD详解 3.1.1、什么是RDD 3.1.2、RDD是怎么理解的 四、Spark 运行模式 4.1、Spark本地模式介绍 4.2、Spark集群模式 Standalone 4.3、Spark集群模式 Stan…...

Executable Code Actions Elicit Better LLM Agents
Executable Code Actions Elicit Better LLM Agents Github: https://github.com/xingyaoww/code-act 一、动机 大语言模型展现出很强的推理能力。但是现如今大模型作为Agent的时候,在执行Action时依然还是通过text-based(文本模态)后者JSO…...

循环结构(三)——do-while语句
目录 🍁引言 🍁一、语句格式 🚀格式1 🚀格式2 🍁二、语句执行过程 🍁三、实例 🚀【例1】 🚀【例2】 🚀【例3】 🍁总结 🍁备注 &am…...
pandas 或筛选
pandas 或筛选 在Pandas中,可以使用DataFrame.loc方法结合逻辑运算符来实现或筛选。这里提供一个简单的例子: import pandas as pd 创建示例DataFrame df pd.DataFrame({ ‘A’: [1, 2, 3, 4], ‘B’: [5, 6, 7, 8], ‘C’: [9, 10, 11, 12] }) 设定…...

工具(1)—截屏和贴图工具snipaste
演示和写代码文档的时候,总是需要用到截图。在之前的流程里面,一般是打开WX或者QQ,找到截图工具。但是尴尬的是,有时候,微信没登录,而你这个时候就在写文档。为了截个图,还需要启动微信…...

【从零开始一步步学习VSOA开发】快速体验SylixOS
快速体验SylixOS 安装完毕RealEvo-IDE 后,同时也安装了RealEvo-Simulator。RealEvo-Simulator 是一个虚拟运行环境,可以模拟各种体系结构并在其上运行 SylixOS。相比于物理板卡,在 RealEvo-Simulator 进行运行调测更加的方便快捷且成本低廉。…...
Ansible自动化:简化IT基础设施管理的艺术
目录 一.前言 二.Ansible简介 2.1什么是Ansible? 2.2Ansible的主要特点 2.3Ansible的应用场景 三.探索Ansible的高级功能 3.1 高级Playbook特性 3.2 Ansible Vault 3.3 动态Inventory 3.4Ansible Tower(AWX) 3.5模块开发 3.6 Ans…...
【Rust光年纪】探索Rust语言中的WebSocket库和框架:优劣一览
Rust语言中的实时通信利器:WebSocket库与框架全面解析 前言 随着Rust语言的不断发展,其在Web开发领域也变得越来越受欢迎。WebSocket作为实现实时通信的重要技术,在Rust的生态系统中也有多个库和框架提供了支持。本文将介绍几个主流的Rust …...

HTML 基础结构
目录 1. 文档声明 2. 根标签 3. 头部元素 4. 主题元素 5. 注释 6. 演示 1. 文档声明 <!DOCTYPE html>:声明文档类型,表示该文档是 html 文档, 2. 根标签 (1)所有的其他标签都要放在一对根标签中&#…...
多页合同怎么盖骑缝章_电子合同怎么盖骑缝章?
多页合同怎么盖骑缝章?电子合同怎么盖骑缝章? 对于纸质多页合同,盖骑缝章是一种常见的做法,用于确保合同的完整性,防止任何页面被替换或篡改。以下是盖骑缝章的基本步骤: 将所有合同页面平铺在桌面上。用…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...