Linux驱动开发学习笔记1《字符设备驱动开发》

目录

一、字符设备驱动简介

二、chrdevbase 字符设备驱动开发实验 

1.创建驱动程序的目录

2.创建vscode工程 

3.编写实验程序

4.编译驱动程序和测试APP代码

(1)加载驱动模块

(2)创建设备节点文件

(3)chrdevbase 设备操作测试

(4)卸载驱动模块


一、字符设备驱动简介

        字符设备是Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

二、chrdevbase 字符设备驱动开发实验 

        本节我们就以chrdevbase 这个虚拟设备为例,完整的编写一个字符设备驱动模块。chrdevbase 不是实际存在的一个设备,是为了方便讲解字符设备的开发而引入的一个虚拟设备。chrdevbase 设备有两个缓冲区,一个为读缓冲区,一个为写缓冲区,这两个缓冲区的大小都为100 字节。在应用程序中可以向chrdevbase 设备的写缓冲区中写入数据,从读缓冲区中读取数据。chrdevbase 这个虚拟设备的功能很简单,但是它包含了字符设备的最基本功能。

1.创建驱动程序的目录

2.创建vscode工程 

 将工作区另存为chrdevbase

使用命令code . 进入vscode

因为是编写Linux 驱动,因此会用到Linux 源码中的函数。我们需要在VSCode 中添加Linux
源码中的头文件路径。打开VSCode,按下“Crtl+Shift+P”打开VSCode 的控制台,然后输入
“C/C++: Edit configurations(UI) ”,打开C/C++编辑配置文件,如下图所示:

 

打开以后会自动在.vscode 目录下生成一个名为c_cpp_properties.json 的文件,此文件修改为如下所示如下所示: 

{"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**","/home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include","/home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include","/home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include/generated"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "c11","cppStandard": "c++17","intelliSenseMode": "clang-x64"}],"version": 4
}

上面includePath 表示头文件路径,需要将Linux 源码里面的头文件路径添加进来,也就是我们前面移植的Linux 内核源码中的头文件路径。

3.编写实验程序

创建chrdevbase.c驱动程序,代码如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>#define CHRDEVBASE_MAJOR 200 //主设备号
#define CHRDEVBASE_NAME "chrdevbase" //设备名static char readbuf[100]; //读缓冲区
static char writebuf[100]; //写缓冲区
static char kerneldata[] = {"kernel data!"};//打开设备
static int chardevbase_open(struct inode *inode, struct file *filp)
{printk("chrdevbase open!\r\n");return 0;
}
//向设备读取数据
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off_t)
{int retvalue = 0;//向用户空间发送数据memcpy(readbuf, kerneldata, sizeof(kerneldata));//内核到用户retvalue = copy_to_user(buf, readbuf, cnt);if(retvalue == 0){printk("kernel sendata ok!\r\n");}else{printk("kernel senddata failed!\r\n");}//printk("chrdevbase read!\r\n");return 0; 
}
//向设备写数据
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;//接收用户空间传递给内核的数据并且打印出来//用户到内核retvalue = copy_from_user(writebuf, buf, cnt);if(retvalue == 0){printk("kernel recevdata:%s\r\n",writebuf);}else{printk("kernel recedata failed!\r\n");}//printk("chrdevbase write!\r\n");return 0;
}
//关闭/释放设备
static int chrdevbase_release(struct inode *inode, struct file *filp)
{//printk("chrdevbase release!\r\n");return 0;
}static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE,.open = chardevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};static int __init chrdevbase_init(void)
{int retvalue = 0;//注册字符设备驱动retvalue = register_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME,&chrdevbase_fops);if(retvalue < 0){//字符设备注册失败,自行处理printk("chrdevbase driver register failed\r\n");}printk("chrdevbase_init()\r\n");return 0;
}
static void __exit chrdevbase_exit(void)
{//注销字符设备驱动unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase_exit()\r\n");
}
/*
驱动模块入口与出口
*/
module_init(chrdevbase_init);//入口
module_exit(chrdevbase_exit);//出口//LICENSE和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ssz");

创建chrdevbaseApp.c测试程序,代码如下:

#include <stdio.h>
#include <unistd.h>
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"static char usrdate[] = {"usr data!"};int main(int argc, char *argv[])
{int fd,retvalue;char *filename;char readbuf[100],writebuf[100];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];//打开驱动文件fd = open(filename, O_RDWR);if(fd < 0){printf("Cant't open file %s\r\n",filename);return -1;}if(atoi(argv[2]) == 1){//从驱动文件读取数据retvalue = read(fd, readbuf, 50);if(retvalue < 0){printf("read file %s failed!\r\n",filename);}else{//读取成功,打印出读取成功的数据// printf("1111\n");printf("read data: %s \r\n",readbuf);}  }if(atoi(argv[2]) == 2){//向设备驱动写数据memcpy(writebuf, usrdate, sizeof(usrdate));retvalue = write(fd, writebuf, 50);if(retvalue < 0){printf("write file %s failed!\r\n",filename);}}retvalue = close(fd);if(retvalue < 0){printf("Can't close file %s\r\n",filename);return -1;}return 0;
}

编译驱动程序,也就是chrdevbase.c 这个文件,我们需要将其编译为.ko 模块,创建
Makefile 文件,然后在其中输入如下内容:

KERNELDIR := /home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.obuild : kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

4.编译驱动程序和测试APP代码

编译驱动程序,输入make命令,会生成chrdevbase.ko文件:

编译测试APP:

使用file命令,chrdevbaseApp这个可执行文件是32 位LSB 格式,ARM 版本的,因此chrdevbaseApp只能在ARM 芯片下运行。

 运行测试:

(1)加载驱动模块

驱动模块chrdevbase.ko 和测试软件chrdevbaseAPP 都已经准备好了,接下来就是运行测试。为了方便测试,Linux 系统选择通过TFTP 从网络启动,并且使用NFS 挂载网络根文件系统,确保uboot 中bootcmd 和bootargs环境变量的值为(确保电脑与开发板通过网线连接):

setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000'
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.1.66:/home/ssz/linux/nfs/
rootfs ip=192.168.1.66:192.168.1.55:192.168.1.1:255.255.255.0::eth0:off'
saveenv

设置好以后启动Linux 系统,检查开发板根文件系统中有没有“/lib/modules/4.1.15”这个目录,如果没有的话自行创建。注意,“/lib/modules/4.1.15”这个目录用来存放驱动模块,使用modprobe 命令加载驱动模块的时候,驱动模块要存放在此目录下。“/lib/modules”是通用的,不管你用的什么板子、什么内核,这部分是一样的。不一样的是后面的“4.1.15”,这里要根据你所使用的Linux 内核版本来设置,比如ALPHA 开发板现在用的是4.1.15 版本的Linux 内核,因此就是“/lib/modules/4.1.15”。如果你使用的其他版本内核,比如5.14.31,那么就应该创建“/lib/modules/5.14.31”目录,否则modprobe 命令无法加载驱动模块。因为是通过NFS 将Ubuntu 中的rootfs(第三十八章制作好的根文件系统)目录挂载为根文件系统,所以可以很方便的将chrdevbase.ko 和chrdevbaseAPP 复制到rootfs/lib/modules/4.1.15 目录中,命令如下:

拷贝完成以后就会在开发板的/lib/modules/4.1.15 目录下存在chrdevbase.ko 和chrdevbaseAPP 这两个文件,如图所示: 

输入如下命令加载chrdevbase.ko 驱动文件: 

insmod chrdevbase.ko
或者
modprobe chrdevbase.ko

从上图 可以看出,modprobe 提示无法打开“modules.dep”这个文件,因此驱动挂载失败了。我们不用手动创建modules.dep 这个文件,直接输入depmod 命令即可自动生成modules.dep,有些根文件系统可能没有depmod 这个命令,如果没有这个命令就只能重新配置busybox,使能此命令,然后重新编译busybox。输入“depmod”命令以后会自动生成modules.alias、modules.symbols 和modules.dep 这三个文件,如下图所示:

驱动加载成功:

输入“lsmod”命令即可查看当前系统中存在的模块,结果如下图所示: 

 

从上图可以看出,当前系统只有“chrdevbase”这一个模块。输入如下命令查看当前系统中有没有chrdevbase 这个设备:

从上图可以看出,当前系统存在chrdevbase 这个设备,主设备号为200,跟我们设置
的主设备号一致。 

(2)创建设备节点文件

驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操作这个设备节点文件来完成对具体设备的操作。输入如下命令创建/dev/chrdevbase 这个设备节点文件: 

其中“mknod”是创建节点命令,“/dev/chrdevbase”是要创建的节点文件,“c”表示这是个字符设备,“200”是设备的主设备号,“0”是设备的次设备号。创建完成以后就会存在/dev/chrdevbase 这个文件,可以使用“ls /dev/chrdevbase -l”命令查看,结果如上图所示:

如果chrdevbaseAPP 想要读写chrdevbase 设备,直接对/dev/chrdevbase 进行读写操作即可。相当于/dev/chrdevbase 这个文件是chrdevbase 设备在用户空间中的实现。前面一直说Linux 下一切皆文件,包括设备也是文件, 

(3)chrdevbase 设备操作测试

读写操作测试:

 

 

(4)卸载驱动模块

如果不再使用某个设备的话可以将其驱动卸载掉,比如输入如下命令卸载掉chrdevbase 这个设备:  

从上图可以看出,此时系统已经没有任何模块了,chrdevbase 这个模块也不存在了,说明模块卸载成功。至此,chrdevbase 这个设备的整个驱动就验证完成了,驱动工作正常。

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

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

相关文章

深度学习 -- 卷积神经网络

1、卷积神经网络的结构 大卫休伯尔( David Hunter Hubel ) 等人研究发现&#xff0c;猫的视皮层上 存在简单细胞( simple cell )和复杂细胞( complex cell )&#xff0c;简单细胞会对 感受野中特定朝向的线段做出反应&#xff0c;而复杂细胞对于特定朝向的钱段移动也能做出反应…

伪原创API,批量创作伪原创文章

内容创作已经成为互联网领域中不可或缺的一环。越来越多的内容创作者和网站管理员开始寻找更高效的伪原创工具&#xff0c;以确保其内容的独特性。 百度文心一言API 我们来了解一下百度文心一言API。作为百度文心推出的一项人工智能服务&#xff0c;通过自然语言处理技术&…

【开源威胁情报挖掘3】开源威胁情报融合评价

基于开源信息平台的威胁情报挖掘综述 写在最前面5. 开源威胁情报关联分析5.1 开源威胁情报网络狩猎&#xff1a;技术、方法和最新研究应用实例和未来方向 5.2 开源威胁情报态势感知关键技术和方法应用实例和未来方向 5.3 开源威胁情报恶意检测关键技术和方法应用实例和未来方向…

【大学英语视听说上】“智力”口语问答练习

题目&#xff1a; book 2, page 9, question 4 回答&#xff1a; 1: What do you think of the view “Intelligence must be bred, not trained”? I think this view is biased. The view suggests that intelligence is primarily determined by genetic factors and inh…

生成对抗网络——研讨会

时隔一年&#xff0c;再跟着李沐大师学习了GAN之后&#xff0c;仍旧没能在离散优化中实现通用的应用&#xff0c;实在惭愧&#xff0c;借着组内研讨会的机会&#xff0c;再队GAN的前世今生做一个简单的综述。 GAN产生的背景 目前与GAN相关的应用 去reddit社区的机器学习板块…

【ArcGIS Pro微课1000例】0041:Pro强大的定位搜索功能、定位窗格、地图上查找地点

一谈到搜索,你是不是还停留在矢量数据的属性表中呢?今天给大家介绍ArcGIS Pro中定位搜索强大功能的使用,可以基于在线地图、矢量数据等多种数据源,进行地址、地名、道路、坐标等的查找。 文章目录 一、定位工具介绍二、在线地图搜索三、本地矢量数据搜索四、无地图搜索五、…

为何全球电商都在拼“质价比”?

远在西雅图的希拉里&#xff0c;在著名的“黑色星期五”大促开始之前&#xff0c;她就已经准备好了一份购物清单。 然而&#xff0c;她发现身边的朋友们总是拉她组团购物。 在朋友和社交媒体的持续轰炸下&#xff0c;希拉里决定尝试一下这个让人贼上头的Temu。 最终&#xf…

学习-java多线程面试题

为什么不建议用Executors启动线程池 *队列LinkedBlockingQueue是没有边界的队列,请求多会造成OOM *建议使用ThreadPoolExecutors 线程池中提交一个任务的流程&#xff1f; 1先判断线程池是否有线程&#xff0c;如果与就直接执行&#xff0c;没有就放队列 2如果队列满了&#…

Linux常用命令——atrm命令

在线Linux命令查询工具 atrm 删除待执行任务队列中的指定任务 补充说明 atrm命令用于删除待执行任务队列中的指定任务。 语法 atrm(选项)(参数)选项 -V&#xff1a;显示版本号。参数 任务号&#xff1a;指定待执行队列中要删除的任务。 实例 删除已经排队的任务 atq…

FPGA纯verilog实现 LZMA 数据压缩,提供工程源码和技术支持

FPGA纯verilog实现 LZMA 数据压缩&#xff0c;提供工程源码和技术支持 目录 1、前言2、我这儿已有的FPGA压缩算法方案3、FPGA LZMA数据压缩功能和性能4、FPGA LZMA 数据压缩设计方案 输入输出接口描述数据处理流程 LZ检索器数据同步LZMA 压缩器为输出LZMA压缩流添加文件头5、…

Debian下载安装教程

目录 一.前言二.下载三.安装 一.前言 这篇文章展示如何使用VMware Workstation Player安装Debian12虚拟机。 二.下载 官网地址&#xff1a;官网 进入官网之后可以直接点击下载Debian选项&#xff0c;这样下载的是最新版的网络安装镜像。 三.安装 使用VMware Workstation P…

PTA校赛算法题十道java、C++详解

目录 7-1 专1 签到 7-2 专2 令人眼花缭乱的字符串 7-3 专3 VALORANT 7-4 专4 吃蛋糕 7-5 专5 Game 7-6 专6 二进制回文串 7-7 专7 度假 7-8 专8 括号匹配Plus 7-9 专9 生成最少叶子树 7-10 专10 禁止超速 这篇文章是基于我们前不久的校赛写的&#xff0c;校赛给的…

麒麟系统添加环境变量

环境变量添加方法 方法一&#xff1a;用户主目录下的.profile或.bashrc文件&#xff08;推荐&#xff09; 登录到你的用户&#xff08;非root&#xff09;&#xff0c;在终端输入&#xff1a; sudo vim ~/.profile 或者 sudo vim ~/.bashrc 翻到该文件最后&#xff0c…

电容和电感

一、电感 1&#xff09;图片 2&#xff09;作用 a&#xff09;储存容量 例如dcdc转换器的原理,将一个电压值转换成另外一个电压值 b&#xff09;选择信号 比如空气中弥漫着很多信号&#xff0c;我们应该怎么选取我们所需要的信号。 电感和电容可以看成一个电阻&#xff0c;当电…

PTA结构体经典编程题

目录 第一题&#xff1a;计算平均成绩 第二题&#xff1a;平面向量加法 第三题&#xff1a;查找书籍 第四题&#xff1a;通讯录排序 第五题&#xff1a;计算职工工资 第一题&#xff1a;计算平均成绩 思路&#xff1a;看到一个学生的基本信息&#xff0c;所以定义一个结构…

神经网络模型预训练

根据神经网络各个层的计算逻辑用程序实现相关的计算&#xff0c;主要是&#xff1a;前向传播计算、反向传播计算、损失计算、精确度计算等&#xff0c;并提供保存超参数到文件中。 # coding: utf-8 import sys, os sys.path.append(os.pardir) # 为了导入父目录的文件而进行的…

【Python百练——第3练】矩形类及操作

&#x1f490;作者&#xff1a;insist-- &#x1f490;个人主页&#xff1a;insist-- 的个人主页 理想主义的花&#xff0c;最终会盛开在浪漫主义的土壤里&#xff0c;我们的热情永远不会熄灭&#xff0c;在现实平凡中&#xff0c;我们终将上岸&#xff0c;阳光万里 ❤️欢迎点…

Golang 原生Rpc Server实现

Golang 原生Rpc Server实现 引言源码解析服务端数据结构服务注册请求处理 客户端数据结构建立连接请求调用 延伸异步调用定制服务名采用TPC协议建立连接自定义编码格式自定义服务器 参考 引言 本文我们来看看golang原生rpc库的实现 , 首先来看一下golang rpc库的demo案例: 服…

python的制图

测试数据示例&#xff1a; day report_user_cnt report_user_cnt_2 label 2023-10-01 3 3 欺诈 2023-10-02 2 4 欺诈 2023-10-03 6 5 欺诈 2023-10-04 2 1 正常 2023-10-05 4 3 正常 2023-10-06 4 4 正常 2023-10-07 2 6 正常 2023-10-08 3 7 正常 2023-10-09 3 12 正常 2023-…

找不到DNS地址的解决方案

找不到DNS地址的解决方案 第一种解决方案&#xff1a;刷新DNS缓存第二种解决方案&#xff1a; 配置Internet协议版本4&#xff08;TCP/IPv4&#xff09;配置IP地址配置DNS地址 如何查看本机IPv4地址、子网掩码与默认网关 第一种解决方案&#xff1a;刷新DNS缓存 WINR输入cmd回…