正点原子【第四期】手把手教你学 Linux之驱动开发篇-01

news2024/11/20 12:40:48

学习目的

了解驱动开发和应用开发的过程,具有一定的基础就行

第一讲:linux驱动开发与裸机开发区别

刚开始听不懂很正常,等之后学了一点你就会知道它说啥了

 第二讲:字符设备驱动开发基础

 字符设备驱动是最简单的,块设备是最难的

 驱动是离不开应用的,但是两个是不同的文件,而我们在裸机单片机开发中,驱动和应用是放在一起的,直接一起编译的,而linux驱动和应用是完全分开的模块

使用应用层的函数调用驱动对应的函数 

 

 

 重点:对于字符设备驱动开发,重点编写应用程序对应的open、close、read、write函数

 

第三讲:我的第一个Linux驱动

字符设备驱动程序的编写其实就是为了实现下面结构体中的函数(选择性实现) 

 

驱动模块的加载与卸载 

针对于驱动程序,我们可以把驱动程序烧进内核或者编译成模块,烧进内核的话会很麻烦因为你需要重新把内核烧录至开发板中,因此我们使用的方法是把驱动程序编译成模块,而把驱动程序编译成模块就需要在驱动程序中编写模块入口函数和出口函数,然后我们在外面执行命令就可以把驱动程序编译成.so模块了 ,然后把.so通过拷贝到nfs的文件夹中通过网络传输.so文件

 模块入口和出口函数编写

当你执行注册模块命令的时候,注册模块内的函数就会被执行(chrdevbase_init),同理删除模块命令,执行出口函数

 内核里的打印函数:printk函数--内核区

而printf是在用户区使用的函数

加载驱动有什么用呢?就是为了注册字符设备,只有注册到了一个字符设备,你才能开始编写open、write、read这些函数

注册与注销字符设备

 register_chrdev的缺点是:它会把主设备号下面的次设备号全部使用,说明空调只能有一种,所以这种很浪费,所以这个函数在后面的话我们不会使用,之所以讲这个函数只是单纯的告诉我们字符设备开发流程而已。

主设备号的赋值需要使用没有使用的主设备号,后面会有一个函数,直接随机分配一个没有用的主设备号


static struct file_operations test_fops;

/* 驱动入口函数 */
static int __init xxx_init(void)
 {
/* 入口函数具体内容 *


int retvalue = 0;

/* 注册字符设备驱动 */
 retvalue = register_chrdev(200, "chrtest", &test_fops);
 if(retvalue < 0){
 /* 字符设备注册失败,自行处理 */
}
 return 0;
 }
16
17 /* 驱动出口函数 */
18 static void __exit xxx_exit(void)
19 {
20 /* 注销字符设备驱动 */
21 unregister_chrdev(200, "chrtest");
22 }
23
24 /* 将上面两个函数指定为驱动的入口和出口函数 */
25 module_init(xxx_init);
26 module_exit(xxx_exit);

主设备号表示一类设备,而次设备就是主设备中的一个设备,比如主设备是空调,那么次设备就是格力空调、美的空调等

完整的驱动代码

驱动加载、注册设备、完成open等函数

#define CHRDEVBASE_MAJOR 200 /* 主设备号 */
19 #define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */
20
21 static char readbuf[100]; /* 读缓冲区 */
22 static char writebuf[100]; /* 写缓冲区 */
23 static char kerneldata[] = {"kernel data!"};
24
25 
32 static int chrdevbase_open(struct inode *inode, struct file *filp)
33 {
34 //printk("chrdevbase open!\r\n");
35 return 0;
36 }
37
38 
46 static ssize_t chrdevbase_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
47 {
48 int retvalue = 0;
49
50 /* 向用户空间发送数据 */
51 memcpy(readbuf, kerneldata, sizeof(kerneldata));
52 retvalue = copy_to_user(buf, readbuf, cnt);
53 if(retvalue == 0){
54 printk("kernel senddata ok!\r\n");
55 }else{
56 printk("kernel senddata failed!\r\n");
57 }
58
59 //printk("chrdevbase read!\r\n");
60 return 0;
61 }
62
63 
71 static ssize_t chrdevbase_write(struct file *filp,
const char __user *buf,
size_t cnt, loff_t *offt)
72 {
73 int retvalue = 0;
74 /* 接收用户空间传递给内核的数据并且打印出来 */
75 retvalue = copy_from_user(writebuf, buf, cnt);
76 if(retvalue == 0){
77 printk("kernel recevdata:%s\r\n", writebuf);
78 }else{
79 printk("kernel recevdata failed!\r\n");
80 }
81
82 //printk("chrdevbase write!\r\n");
83 return 0;
84 }
8
91 static int chrdevbase_release(struct inode *inode,
struct file *filp)
92 {
93 //printk("chrdevbase release! \r\n");
94 return 0;
95 }
96
97 /*
98 * 设备操作函数结构体
99 */
100 static struct file_operations chrdevbase_fops = {
101 .owner = THIS_MODULE,
102 .open = chrdevbase_open,
103 .read = chrdevbase_read,
104 .write = chrdevbase_write,
105 .release = chrdevbase_release,
106 };
107
108 /*
109 * @description : 驱动入口函数
110 * @param : 无
111 * @return : 0 成功;其他 失败
112 */
113 static int __init chrdevbase_init(void)
114 {
115 int retvalue = 0;
116
117 /* 注册字符设备驱动 */
118 retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME,
&chrdevbase_fops);
119 if(retvalue < 0){
120 printk("chrdevbase driver register failed\r\n");
121 }
122 printk("chrdevbase_init()\r\n");
123 return 0;
124 }
125
126 /*
* @description : 驱动出口函数
128 * @param : 无
129 * @return : 无
130 */
131 static void __exit chrdevbase_exit(void)
132 {
133 /* 注销字符设备驱动 */
134 unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
135 printk("chrdevbase_exit()\r\n");
136 }
137
138 /*
139 * 将上面两个函数指定为驱动的入口和出口函数
140 */
141 module_init(chrdevbase_init);
142 module_exit(chrdevbase_exit);
143
144 /*
145 * LICENSE 和作者信息
146 */
147 MODULE_LICENSE("GPL");
148 MODULE_AUTHOR("zuozhongkai");

编写测试 APP--应用开发

static char usrdata[] = {"usr data!"};
22
23 /*
24 * @description : main 主程序
25 * @param - argc : argv 数组元素个数
26 * @param - argv : 具体参数
27 * @return : 0 成功;其他 失败
28 */
29 int main(int argc, char *argv[])
30 {
31 int fd, retvalue;
32 char *filename;
33 char readbuf[100], writebuf[100];
34
35 if(argc != 3){
36 printf("Error Usage!\r\n");
37 return -1;
38 }
39
40 filename = argv[1];
41
42 /* 打开驱动文件 */
43 fd = open(filename, O_RDWR);
44 if(fd < 0){
45 printf("Can't open file %s\r\n", filename);
46 return -1;
47 }
48
49 if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */
50 retvalue = read(fd, readbuf, 50);
51 if(retvalue < 0){
52 printf("read file %s failed!\r\n", filename);
53 }else{
54 /* 读取成功,打印出读取成功的数据 */
55 printf("read data:%s\r\n",readbuf);
56 }
57 }
if(atoi(argv[2]) == 2){
60 /* 向设备驱动写数据 */
61 memcpy(writebuf, usrdata, sizeof(usrdata));
62 retvalue = write(fd, writebuf, 50);
63 if(retvalue < 0){
64 printf("write file %s failed!\r\n", filename);
65 }
66 }
67
68 /* 关闭设备 */
69 retvalue = close(fd);
70 if(retvalue < 0){
71 printf("Can't close file %s\r\n", filename);
72 return -1;
73 }
74
75 return 0;
76 }

创建设备节点文件

驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操作这个设备节点文件来完成对具体设备的操作
然后使用上面的app对chrdevbase 设备操作测试处理
 

开发流程

驱动模块加载、设备注册、函数内部编写、应用程序编写测试

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

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

相关文章

C++ 移动语义

从拷贝说起我们知道&#xff0c;C中有拷贝构造函数和拷贝赋值运算符。那既然是拷贝&#xff0c;听上去就是开销很大的操作。没错&#xff0c;所谓拷贝&#xff0c;就是申请一块新的内存空间&#xff0c;然后将数据复制到新的内存空间中。如果一个对象中都是一些基本类型的数据的…

【Unity3D】绘制物体外框线条盒子

1 需求描述 点选物体、框选物体、绘制外边框 中介绍了物体投影到屏幕上的二维外框绘制方法&#xff0c;本文将介绍物体外框线条盒子绘制方法。 内框&#xff1a;选中物体后&#xff0c;绘制物体的内框&#xff08;紧贴物体、并与物体姿态一致的内框盒子&#xff09;外框&#…

Python与C++语法比较--字符串篇

tags: C Python 写在前面 刷lc从Python转向C, 不只是语法层面, 还要改变很多的API, 这次记录一下C和Python在字符串方面的一些区别, 供参考. 基本区别 Python字符串是不可变类型, 而C的String为容器(本质上是一个类的别名, 说容器有点不合适, 因为容器内的元素类型已经被指…

Kotlin中的Lambda编程

文章目录1.集合的创建与遍历2.集合的函数式API3.Java函数式API的使用1.集合的创建与遍历 传统意义上的集合主要是List和Set&#xff0c;再广泛一点的话&#xff0c;像Map这样的键值对数据结构也可以包含进来。List&#xff0c;Set和Map再Java中都是接口&#xff0c;List主要的…

Java设计模式-命令模式Command

介绍 命令模式&#xff08;Command Pattern&#xff09;&#xff1a;在软件设计中&#xff0c;我们经常需要向某些对象发送请求&#xff0c;但是并不知道请求的接收 者是谁&#xff0c;也不知道被请求的操作是哪个&#xff0c; 我们只需在程序运行时指定具体的请求接收者即可&…

数影周报:TikTok因在线跟踪被罚500万欧,Windows 7退出历史舞台

本周看点&#xff1a;TikTok因在线跟踪被法国罚款500万欧元 &#xff1b;思科已裁员近700 人&#xff1b;Windows 7退出历史舞台&#xff1b;亚马逊向所有卖家开放Buy with Prime服务&#xff1b;“全路程”完成2亿元C轮融资......数据安全那些事TikTok因在线跟踪被法国罚款500…

Android13 wifi无线调试adb连接设置

在进行adb调试的时候&#xff0c;有时候需要使用wifi连接&#xff0c;或者wifi连接较为方便&#xff0c;早些的Android上&#xff0c;需要设置端口等操作&#xff0c;adb tcpip 6666参考android wifi adb 调试 - 简书 (jianshu.com)好几步操作&#xff0c;在Android13上&#x…

Deque 的理解 STL中stack与queue为什么选择使用deque为底层模板容器

目录 一、Deque的引入 二、Deque是什么&#xff1f; 三、deque的遍历方式&#xff1f;deque的缺陷&#xff1f; 四、它为什么能更贴合与stack与queue&#xff1f; 五、STL中vector与list的底层实现 一、Deque的引入 Stack、Queue在之前的博客中我也是分别使用了更容易处理…

【蓝桥杯】历届真题 杨辉三角形 (省赛)Java

【问题描述】 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下数列: 1,1&#xff0c;1&#xff0c;1&#xff0c;2,1&#xff0c;1&#xff0c;3,3&#xff0c;1&#xff0c;1,4&#xff0c;6,4&#xff0c;1&…

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 (六)多功能数据处理器

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 &#xff08;六&#xff09;多功能数据处理器 &#x1f508;声明&#xff1a; &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN博客 &#x1f9e8;未经作者允许&#xff0c;禁止转载 &#x1f511;系列专栏&#xff1a;牛客…

react基础Day02-受控组件评论案例propscontext

React组件 目标 能够知道受控组件是什么能够写出受控组件了解非受控组件 表单处理 受控组件&#xff08;★★★&#xff09; HTML中的表单元素是可输入的&#xff0c;也就是有自己的可变状态而React中可变状态通常保存在state中&#xff0c;并且只能通过setState() 方法来…

[acwing周赛复盘] 第 86 场周赛20230114

[acwing周赛复盘] 第 86 场周赛20230114 一、本周周赛总结二、 4794. 健身1. 题目描述2. 思路分析3. 代码实现三、4795. 安全区域1. 题目描述2. 思路分析3. 代码实现四、4796. 删除序列1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 去吃羊蝎子了&#xff0…

基于汽车知识图谱的汽车问答多轮对话系统 详细教程

结果: 1 技术路线 该技术路线主要将KBQA分为三部分,实体识别与实体链接,关系识别,sparql查询,其中每个部分分为一到多种方法实现。具体的处理流程图如下:

大脑的记忆

AI神经网络中的记忆 当前AI发展进入一个瓶颈,大家都意识到还是要继续在人脑中获取AI方向的指引。当然也有科学家说物理世界与心理世界并非一一对应,人类的智能也没必要与物理世界一一对应,甚至本质上都可以是不同的,所以没必要研究大脑认知和大脑的机制,更不需要分子级别…

IDEA structure窗口各标志及功能

文章目录图标对象类型访问权限其他修饰符工具栏图标 对象类型 class 类 interface 接口 enum 枚举 interface 注解 class initializer 代码块 method 方法 field 字段/属性 anonymous class 匿名类 lambda lambda表达式 propertie 访问器&#xff08;get方法&#xff0…

【Java面试】Queue接口

文章目录BlockingQueue中有哪些方法&#xff0c;为什么这样设计&#xff1f;BlockingQueue是怎么实现的&#xff1f;BlockingQueue中有哪些方法&#xff0c;为什么这样设计&#xff1f; 先看一眼结构&#xff0c;再看具体的分析 为了应对不同的业务场景&#xff0c;Blockin…

拉伯证券|业绩猛增超13倍,主力连续抢筹,这只股收获4连板

成绩陡增股获主力接连抢筹 春节日益接近&#xff0c;A股成交活跃度有所下滑&#xff0c;不过有一些股票节前继续取得主力喜爱。证券时报•数据宝核算&#xff0c;到1月12日收盘&#xff0c;沪深两市共54只个股接连5日或5日以上主力资金净流入。 主力资金净流入继续周期最长的是…

人工智能学习07--pytorch03--tensorboard(下载tensorboard、opencv)

transform 主要是对input图像进行变换&#xff08;统一尺寸、对图像中的数据进行类的转换&#xff09; TensorBoard很有用 如&#xff1a;通过loss的变化过程&#xff0c;来看loss的变化是否复合预想。也可以通过loss来选择模型。 TensorBoard&#xff0c;虽然他是TensorFlo…

排序综合(C++版)

目录 排序综合 一、问题描述 二、运行环境说明 三、代码段 四、效果展示 排序综合 备注&#xff1a;大二&#xff08;上&#xff09;数据结构课程设计B题 一、问题描述 给定N…

Python asyncio异步编程简单实现

今天继续给大家介绍Python相关知识&#xff0c;本文主要内容是Python asyncio异步编程简单实现。 一、asyncio事件循环简介 asyncio引入了事件循环的概念。事件循环是一个死循环&#xff0c;还循环会检测并执行某些代码。在Python中&#xff0c;引入了asyncio模块后&#xff…