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)所有的其他标签都要放在一对根标签中&#…...
多页合同怎么盖骑缝章_电子合同怎么盖骑缝章?
多页合同怎么盖骑缝章?电子合同怎么盖骑缝章? 对于纸质多页合同,盖骑缝章是一种常见的做法,用于确保合同的完整性,防止任何页面被替换或篡改。以下是盖骑缝章的基本步骤: 将所有合同页面平铺在桌面上。用…...
GD 32 IIC通信协议
前言: ... 通信方式 通信方式分为串行通信和并行通信。常见的串口就是串行通信的方式 常用的串行通信接口 常用的串行通信方式有USART,IIC,USB,CAN总线 同步与异步 同步通信:IIC是同步通信,有两个线一个是时钟信号线,一个数数据…...
Spring Task初学
介绍 Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑 为什么要在Java程序中使用Spring Task? 运行效果 cron表达式:一般日和周不会同时出现 入门案例 启动类添加注解EnableScheduling开始任务调度 创建MyTask类…...
决策树可解释性分析
决策树可解释性分析 决策树是一种广泛使用的机器学习算法,以其直观的结构和可解释性而闻名。在许多应用场景中,尤其是金融、医疗等领域,模型的可解释性至关重要。本文将从决策路径、节点信息、特征重要性等多个方面分析决策树的可解释性&…...
BUGKU-WEB never_give_up
解题思路 F12查看请求和响应,查找线索 相关工具 base64解码URL解码Burp Suit抓包 页面源码提示 <!--1p.html--> 2. 去访问这个文件,发现直接跳转到BUGKU首页,有猫腻那就下载看看这个文件内容吧 爬虫下载这个文件 import requests …...
hive自动安装脚本
使用该脚本注意事项 安装hive之前确定机子有网络。或者yum 更改为本地源,因为会使用epel仓库下载一个pv的软件使用该脚本前提是自行安装好mysql数据库准备好tomcat软件包,该脚本使用tomcat9.x版本测试过能正常执行安装成功,其他版本没有测试…...
unix 用户态 内核态
在UNIX操作系统中,"用户态"和"内核态"是两种不同的运行模式,它们定义了程序在执行时的权限级别: 用户态(User Mode): 用户态是程序运行的常规状态,大多数应用程序在执行时…...
GD32 IAP升级——boot和app相互切换
GD32 IAP升级——boot和app相互切换 目录 GD32 IAP升级——boot和app相互切换1 Keil工程设置1.1 修改ROM1.2 Keil烧录配置 2 代码编写2.1 app跳转2.2 软件重启2.3 app中断向量表偏移 结束语 1 Keil工程设置 1.1 修改ROM GD32内部Flash是一整块连续的内存,但是因为…...
C++11革新之旅:探索C++编程的无限可能
C11革新之旅:探索C编程的无限可能 C11,作为C语言的一个重要标准,为C编程带来了革命性的变革。它不仅引入了众多新特性和改进,还极大地增强了C的表达能力、提高了程序的性能和资源利用率。本文将从多个方面深入探讨C11的新特性&am…...
免费自动化AI视频剪辑工具
下载地址:https://pan.quark.cn/s/3c5995da512e FunClip是一款完全开源、本地部署的自动化视频剪辑工具,通过调用阿里巴巴通义实验室开源的FunASR Paraformer系列模型进行视频的语音识别,随后用户可以自由选择识别结果中的文本片段或说话人&a…...
Linux中安装C#的.net,创建运行后端或控制台项目
安装脚本命令: 创建一个sh文件并将该文件更改权限运行 sudo apt update wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt-get upd…...
网站设计建设代理机构/seo网站优化培训公司
在window平台,文件的默认编码是gbk, 此时如果写入的字符串的编码是utf-8就会引发这种错误,打开文件的编码必须与字符串的编码一致 with open(‘content.txt’,w,encodingutf-8) as f: r.write() 这样就不会报错 原创链接:https:/…...
香港美女做旅游视频网站/郑州高端网站建设哪家好
作者: sudami 嘿嘿,今天终于有时间学习自己喜欢的东西了,在kanxue里看到一篇关于U盘小偷的文章:http://bbs.pediy.com/showthread.php?p381656#post381656读了其源码,学到一些东西: (1) 判断移动设备的插入/拔出 嘿嘿, 以前一直觉得很神秘的"判断是否有U盘插入"其实…...
wordpress 主頁html/南宁网站建设
注:本文所指的YUV均为YUV420中的I420格式(最常见的一种),其他格式不能用以下的代码。位深为8bit时,每个像素占用1字节,对应文件指针的fp.read(1);位深为10bit时,每个像素占用2字节,对应文件指针…...
可以做软文的网站/湖南关键词优化排名推广
简介 本文将介绍如何利用阿里云的sls插件功能和emr来进行mysql binlog的准实时传输 基本架构 rds -> sls -> spark streaming -> spark hdfs 主要包含3个链路: 1. 怎么把rds的binlog收集到sls 2.怎么通过spark streaming将sls中的日志读取出来,…...
中国建筑装修网/优化大师win10下载
python下载安装好了之后,在cmd窗口里面,我们输入python,提示找不到该命令,这时候我们需要配置环境变量 我们找到我们安装python的目录,至于怎么找到python的安装目录?我们在win10里面可以搜索python&#x…...
腾讯风铃wordpress/关键词首页排名优化
I/O 设备模型 绝大部分的嵌入式系统都包括一些 I/O(Input/Output,输入 / 输出)设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的 Flash 或 SD 卡,以及网络设备的以太网接口等&#…...