0-ARM Linux驱动开发-字符设备
一、字符设备概述
Linux 系统中,设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输,数据的访问是按顺序的,一个字节一个字节地进行读取和写入操作,没有缓冲区。例如,终端(/dev/tty)、鼠标、键盘等设备都是典型的字符设备。
字符设备通过特殊的设备文件来表示。这些设备文件通常位于/dev
目录下。设备文件有主设备号(major number)和次设备号(minor number)。主设备号用于标识设备驱动程序,内核通过主设备号来查找对应的驱动程序;次设备号用于标识同一类型设备中的不同个体。例如,系统中可能有多个串口设备,它们的主设备号相同(表示使用相同的驱动程序),但次设备号不同,用于区分不同的串口。
二、字符设备驱动开发
1、驱动文件
//添加头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ide.h>static char readbuf[100]; // 读缓冲区
static char writebuf[100]; // 写缓冲区
static char message[] = {"This message comes from kernel."};static int drive_major; //设备号
static struct class *KernelPrint_cls;//1.5.5 驱动程序的打开,读取,写入,关闭。
static int KernelPrint_open(struct inode *inode, struct file *filp) //打开函数
{//本DEMO无需申请资源,此处留白printk("-KernelPrint open-\n");return 0;
}
static ssize_t KernelPrint_read(struct file *filp, char __user *buf, size_t count, loff_t *fops) //用户读取,内核发送信息
{int flag = 0;memcpy(readbuf, message, sizeof(message)); //使用memcpy将内核中要发送的内容写入读缓冲区flag = copy_to_user(buf, readbuf, count); //使用copy_to_user函数将读缓冲区的内容发送到用户态if (flag == 0) //返回0成功,否则失败{printk("Kernel send data success!\n");}else{printk("Kernel send data failed!\n");}printk("-KernelPrint read-\n");return 0;
}
static ssize_t KernelPrint_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops) //用户发送,内核读取信息并打印
{int flag = 0;flag = copy_from_user(writebuf, buf, count); //使用copy_from_user读取用户态发送过来的数据if (flag == 0){printk(KERN_CRIT "Kernel receive data: %s\n", writebuf);}else{printk("Kernel receive data failed!\n");}printk("-KernelPrint write-\n");return 0;
}
static int KernelPrint_release(struct inode *inode, struct file *filp) //释放设备
{//由于open函数并没有占用什么资源,因此无需释放printk("-KernelPrint release-\n");return 0;
}//1.5.1 驱动文件描述集合
static struct file_operations drive_fops = {.owner = THIS_MODULE,.open = KernelPrint_open,.read = KernelPrint_read,.write = KernelPrint_write,.release = KernelPrint_release,
};//1.5.2 装载入口函数
static __init int KernelPrint_init(void)
{printk("-------^v^-------\n");printk("-KernelPrint init-\n");//1.5.3 设备的申请//申请主设备号//参数1----需要的主设备号,>0静态分配, ==0自动分配//参数2----设备的描述 信息,体现在cat /proc/devices, 一般自定义//参数3----文件描述集合//返回值,小于0报错drive_major = register_chrdev(0, "KernelPrint", &drive_fops);if (drive_major < 0) //判断是否申请成功{printk("register chrdev faile!\n");return drive_major;}else{printk("register chrdev ok!\n");}//1.5.4 //自动创建设备节点//创建设备的类别//参数1----设备的拥有者,当前模块,直接填THIS_MODULE//参数2----设备类别的名字,自定义//返回值:类别结构体指针,其实就是分配了一个结构体空间KernelPrint_cls = class_create(THIS_MODULE, "KernelPrint_class");printk("class create ok!\n");//创建设备//参数1----设备对应的类别//参数2----当前设备的父类,直接填NULL//参数3----设备节点关联的设备号//参数4----私有数据直接填NULL//参数5----设备节点的名字device_create(KernelPrint_cls, NULL, MKDEV(drive_major, 0), NULL, "KernelPrint_%d", 0);printk("device create ok!\n");return 0;
}//1.5.2 卸载入口函数
static __exit void KernelPrint_exit(void)
{//1.5.3 设备的注销device_destroy(KernelPrint_cls, MKDEV(drive_major, 0)); //删除设备class_destroy(KernelPrint_cls); //删除类unregister_chrdev(drive_major, "KernelPrint"); //注销主设备号printk("-------^v^-------\n");printk("-KernelPrint exit-\n");
}//申明装载入口函数和卸载入口函数
module_init(KernelPrint_init);
module_exit(KernelPrint_exit);//添加各类信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Popeye");
2、应用文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];filename = argv[1];fd = open(filename, O_RDWR); //打开设备if (fd < 0){printf("Can't open file %s\n", filename);return -1;}switch (*argv[2]) //对操作数进行解析{case 'r':if (argc != 3) //进行鲁棒性检查{printf("Unknow operation, use the formate: ./APPNAME /dev/DRIVENAME r to read date from kernel.\n");return -1;}retvalue = read(fd, readbuf, 100);if (retvalue < 0) //检查是否读取成功{printf("Read file %s failed!\n", filename);}else{printf("User receive data: %s\n", readbuf);}break;case 'w':if (argc != 4) //进行鲁棒性检查{printf("Unknow operation, use the formate: ./APPNAME /dev/DRIVENAME w \"USERDATE\" to write date to kernel.\n");return -2;}memcpy(writebuf, argv[3], strlen(argv[3])); //将内容拷贝到缓冲区retvalue = write(fd, writebuf, 50); //写数据if (retvalue < 0){printf("Write file %s failed!\n", filename);}else{printf("Write file success!\n");}break;default:printf("Unknow Operation: %d\n", *argv[2]);break;}retvalue = close(fd); //关闭设备if (retvalue < 0){printf("Can't close file %s\n", filename);return -1;}return 0;
}
三、上板验证
安装驱动insmod kernel_print.ko
,查看驱动安装是否成功ls /dev/
,kernel_print内核打印测试./kernel_print_app /dev/KernelPrint_0 w "Popeye"
相关文章:

0-ARM Linux驱动开发-字符设备
一、字符设备概述 Linux 系统中,设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输,数据的访问是按顺序的,一个字节一个字节地进行读取和写入操作,没有缓冲区。例如,终端(/dev…...

使用 Faster Whisper 和 Gradio 实现实时语音转文字
随着人工智能技术的进步,语音识别已经成为最热门的研究领域之一。如何实现高效、准确的实时语音转文字功能,是许多开发者关注的重点。本文将介绍如何使用 Faster Whisper 和 Gradio 这两个强大工具,快速构建一个实时语音转文字应用。 Faster…...

redis v6.0.16 安装 基于Ubuntu 22.04
redis安装 基于Ubuntu 22.04 本文演示如何在ubuntu22.04下,安装redis v6.0.16,并配置测试远程访问。 Step1 更新环境 sudo apt updateStep2 安装redis sudo apt install redis-server -yStep3 启动 sudo systemctl restart redissudo systemctl sta…...

Milvus - 内存索引类型详解
1. 背景概述 在大规模数据处理和向量相似性搜索场景中,内存索引的使用显著提升了查询速度和效率。Milvus 提供了多种内存索引类型,以满足不同场景下的性能需求。本文将介绍 Milvus 支持的各种内存索引类型及其适用场景、配置参数和使用方法。 2. 为什么…...

【STM32】按键控制LED 光敏传感器控制蜂鸣器
文章目录 前置知识按键介绍传感器模块硬件电路按键硬件电路传感器模块硬件电路 C语言数据类型在Keil中的对应写法C语言枚举 按键控制LED接线图Hardware文件夹(模块化编程)LED驱动程序封装Key(按键)驱动程序封装 main.c源文件 光敏传感器控制蜂鸣器接线图…...

flutter-防抖
在Flutter中实现输入框的防抖功能,通常是为了减少用户输入时触发的事件数量,特别是在进行网络请求时。防抖(Debounce)意味着在用户停止输入一段时间后才触发事件。以下是实现输入框防抖的一种方法: 1、使用Debounce类…...

什么是贪心算法
贪心算法(Greedy Algorithm)是一种逐步构建解决方案的方法,在每一步选择中都作出局部最优的选择,希望最终能够获得全局最优解。贪心算法的核心思想是贪心选择性质,即每次选择当前看来最好的解,不考虑未来可…...

YOLOv6-4.0部分代码阅读笔记-effidehead_lite.py
effidehead_lite.py yolov6\models\heads\effidehead_lite.py 目录 effidehead_lite.py 1.所需的库和模块 2.class Detect(nn.Module): 3.def build_effidehead_layer(channels_list, num_anchors, num_classes, num_layers): 1.所需的库和模块 import torch import t…...

重学SpringBoot3-整合 Elasticsearch 8.x (一)客户端方式
更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 这里写目录标题 1. 为什么选择 Elasticsearch?2. Spring Boot 3 和 Elasticsearch 8.x 的集成概述2.1 准备工作2.2 添加依赖 3. Elasticsearch 客户端配置方式…...

极简实现酷炫动效:Flutter隐式动画指南第三篇自定义Flutter隐式动画
目录 前言 一、TweenAnimationBuilder 二、使用TweenAnimationBuilder实现的一些动画效果 1.调整透明度的动画 2.稍微复杂点的组合动画 3.数字跳动的动画效果 前言 上两节博客分别介绍了Flutter中的隐式动画的基础知识以及使用隐式动画实现的一些动画效果。当系统提供的隐…...

无人机维护保养、部件修理更换技术详解
无人机作为一种精密的航空设备,其维护保养和部件修理更换是确保飞行安全、延长使用寿命的重要环节。以下是对无人机维护保养、部件修理更换技术的详细解析: 一、无人机维护保养技术 1. 基础构造理解: 熟悉无人机的基本构造,包括…...

xilinx vitis 更换硬件平台——ZYNQ学习笔记5
1、重新生成硬件信息 2、选择带有bit信息 3、设施路径和名字 4、打开更新硬件选项 5、选择新的硬件信息 6、打开系统工程界面 7、复位硬件信息 更新完毕...

vscode makfile编译c程序
编译工具安装 为了在 Windows 上安装 GCC,您需要安装 MinGW-w64。 MinGW-w64 是一个开源项目,它为 Windows 系统提供了一个完整的 GCC 工具链,支持编译生成 32 位和 64 位的 Windows 应用程序。 1. 下载MinGW-w64源代码,如图点…...

【学术论文投稿】探索嵌入式硬件设计:揭秘智能设备的心脏
【IEEE出版】第六届国际科技创新学术交流大会暨通信、信息系统与软件工程学术会议(CISSE 2024)_艾思科蓝_学术一站式服务平台 更多学术会议论文投稿请看:https://ais.cn/u/nuyAF3 目录 引言 嵌入式系统简介 嵌入式硬件设计的组成部分 设…...

JavaScript 概述
### JavaScript 概述 JavaScript 是一种广泛使用的编程语言,它最初由 Netscape 公司的 Brendan Eich 在1995年创建,目的是为网页添加交互性。随着时间的发展,JavaScript 已经从一个简单的脚本语言演变成了一种功能强大的编程语言,…...

2024年10月个人工作生活总结
本文为 2024年10月工作生活总结。 研发编码 一个证书过期问题记录 某天,现场反馈某服务无法使用问题,经同事排查,是因为服务证书过期导致的。原来,证书的有效期设置为5年,这个月刚好到期。 虽然这个问题与自己无直接…...

uniapp ,微信小程序,滚动(下滑,上拉)到底部加载下一页内容
前言 小程序的内容基本都是滑动到底部加载下一页,这个一般都没有什么好用的组件来用,我看vant和uniapp的插件里最多只有个分页,没有滚动到底部加载下一页。再次做个记录。 效果预览 下滑到底部若是有下一页,则会自动加载下一页&…...

MySQL中的日志类型有哪些?binlog、redolog和undolog的作用和区别是什么?
简介: MySQL中有六种日志文件,分别是:重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、慢查询日志࿰…...

【uni-app】创建自定义模板
1. 步骤 打开自定义模板文件夹 在此文件夹下创建模板文件(注意后缀名) 重新点击“新建页面” 即可看到新建的模板 2. 注意事项 创建的模板必须文件类型对应(vue模板就创建*.vue文件, uvue模板就创建*.uvue文件)...

Cesium移动Primitive位置
与传统的Entity实体不同,Primitive作为一种自定义基本图元,几何形状、材质和其他属性均由使用者定义,在需要绘制大量静态几何图形的高效渲染场景中更为适用。 Primitive的移动涉及到矩阵变换,并不像Entity那样给它替换一个新的坐…...

安卓13默认连接wifi热点 android13默认连接wifi
总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 有时候我们需要让固件里面内置好,相关的wifi的ssid和密码,让固件起来就可以连接wifi,不用在手动操作。 2.问题分析 这个功能,使用普通的安卓代码就可以实现了。 3.代…...

parted 磁盘分区
目录 磁盘格式磁盘分区文件系统挂载使用扩展 - parted、fdisk、gdisk 区别 磁盘格式 parted /dev/vdcmklabel gpt # 设置磁盘格式为GPT p # 打印磁盘信息此时磁盘格式设置完成! 磁盘分区 开始分区: mkpart data_mysql # 分区名&…...

第三百零八节 Log4j教程 - Log4j日志到数据库
Log4j教程 - Log4j日志到数据库 我们可以使用log4j API通过使用org.apache.log4j.jdbc.JDBCAppender对象将信息记录到数据库中。 下表列出了JDBCAppender的配置属性。 属性描述bufferSize设置缓冲区大小。默认大小为1。driverJDBC驱动程序类。默认为sun.jdbc.odbc.JdbcOdbcDr…...

ai智能语音电销机器人可以做哪些事情?
AI智能语音电销机器人是结合人工智能技术进行自动化电话销售和客户互动的工具,能够完成一系列任务,有助于提升销售效果、优化客户体验和提高工作效率。以下是AI智能语音电销机器人可以做的一些主要事情: 1. 自动拨号 AI语音电销机器人可以自…...

CleanShot X - Mac(苹果电脑)专业截图录屏软件
CleanShot X 不仅提供了基础的截图功能,更内置了强大的图片编辑器,让你能轻松添加标注、形状、文本……以及将多个截图进行合并。 无论是为社交媒体制作图文,还是制作专业的产品 / 教程演示,CleanShot X 都能满足你的需求。 软件…...

Kafka 客户端工具使用分享【offsetexplorer】
前言: 前面我们使用 Spring Boot 继承 Kafka 完成了消息发送,有朋友会问 Kafka 有没有好用的客户端工具,RabbitMQ、RocketMQ 都有自己的管理端,那 Kafka 如何去查看发送出去的消息呢? 本篇我们就来分享一个好用的工具…...

uni-app 下拉刷新、 上拉触底(列表信息)、 上滑加载(短视频) 一键搞定
一、下拉刷新 1. 首先找到pages.json中 给需要进行下拉刷新的页面设置可以下拉刷新 2. 然后在需要实现下拉刷新的script标签内添加 导入onPullDownRefresh import {onPullDownRefresh} from dcloudio/uni-app 下拉刷新触发的事件 onPullDownRefresh(()> {console.log(正…...

基于Spring Boot+Vue的助农销售平台(协同过滤算法、限流算法、支付宝沙盒支付、实时聊天、图形化分析)
🎈系统亮点:协同过滤算法、节流算法、支付宝沙盒支付、图形化分析、实时聊天; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构:B/S架构 运行环境:win10/win11、jdk1…...

如何在Linux环境中的Qt项目中使用ActiveMQ-CPP
文章目录 代码1:消费者代码2:生成者 之前在Linux下的qt程序中使用activeMQ的时候也是用了很多时间去研究,本来想的是好好记录一下,但是当时顾着写代码。很多细节也不想再去走一遍了。大概写一下怎么使用就行了。注意:一…...

HTML字符实体详解
HTML 字符实体是在 HTML 文档中用来表示特定字符的特殊编码。这些字符可能因为直接输入而引发解析错误,或某些字符在 HTML 中具有特殊含义(例如,< 和 > 用于标签)。因此,使用字符实体可以确保文本的准确呈现。 1. 什么是字符实体? 字符实体由一个 & 符号开始,…...