0-ARM Linux驱动开发-字符设备

news2024/11/25 20:23:51

一、字符设备概述

Linux 系统中,设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输,数据的访问是按顺序的,一个字节一个字节地进行读取和写入操作,没有缓冲区。例如,终端(/dev/tty)、鼠标、键盘等设备都是典型的字符设备。

字符设备通过特殊的设备文件来表示。这些设备文件通常位于/dev​目录下。设备文件有主设备号(major number)和次设备号(minor number)。主设备号用于标识设备驱动程序,内核通过主设备号来查找对应的驱动程序;次设备号用于标识同一类型设备中的不同个体。例如,系统中可能有多个串口设备,它们的主设备号相同(表示使用相同的驱动程序),但次设备号不同,用于区分不同的串口。

image

image

二、字符设备驱动开发

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"

image

image

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

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

相关文章

HTML 基础标签——表格标签<table>

文章目录 1. `<table>` 标签:定义表格2. `<tr>` 标签:定义表格行3. `<th>` 标签:定义表头单元格4. `<td>` 标签:定义表格单元格5. `<caption>` 标签:为表格添加标题6. `<thead>` 标签:定义表格头部7. `<tbody>` 标签:定义表格…

【优选算法】——二分查找!

目录 1、二分查找 2、在排序数组中查找元素的第一个和最后一个位置 3、搜索插入位置 4、x的平方根 5、山脉数组的封顶索引 6、寻找峰值 7、寻找旋转排序数组中的最小值 8、点名 9、完结散花 1、二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组…

东北虎豹国家公园shp格式范围

东北虎豹国家公园地处中国吉林、黑龙江两省交界的老爷岭南部&#xff08;珲春—汪清—东宁—绥阳&#xff09;区域&#xff0c;东起吉林省珲春林业局青龙台林场&#xff0c;与俄罗斯滨海边疆区接壤&#xff0c;西至吉林省大兴沟林业局岭东林场&#xff0c;南自吉林省珲春林业局…

Spring 中的 Environment 对象

可参考官网&#xff1a;Environment Abstraction 核心概念 Environment 对象对两个关键方面进行建模&#xff1a;profiles 和 属性&#xff08;properties&#xff09;。 Profile 简单来说&#xff1a;profile 机制在 IOC 容器中提供了一种机制&#xff1a;允许在不同的环境…

Puppeteer点击系统:解锁百度流量点击率提升的解决案例

在数字营销领域&#xff0c;流量和搜索引擎优化&#xff08;SEO&#xff09;是提升网站可见性的关键。我开发了一个基于Puppeteer的点击系统&#xff0c;旨在自动化地提升百度流量点击率。本文将介绍这个系统如何通过模拟真实用户行为&#xff0c;优化关键词排名&#xff0c;并…

浅谈UI自动化

⭐️前言⭐️ 本篇文章围绕UI自动化来展开&#xff0c;主要内容包括什么是UI自动化&#xff0c;常用的UI自动化框架&#xff0c;UI自动化原理等。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f349;博主将持续更新学习记录收获&#xff0c;友友们有任何问题…

[Android]从FLAG_SECURE禁止截屏看surface

在应用中&#xff0c;设置activity的flag为FLAG_SECURE就可以禁止截屏&#xff0c;截屏出来是黑色的&#xff0c; 试验一下&#xff0c; 注意事项 影响&#xff1a; 设置 FLAG_SECURE 标志后&#xff0c;用户将无法对该Activity进行截屏或录制屏幕。这个标志会影响所有屏幕录…

设计模式之模块方法

定义 模板与方法应该是最常使用的设计模式&#xff0c;在GOF&#xff08;设计模式&#xff09;中的定义&#xff1a;定义一个操作中的算法的骨架 &#xff0c;而将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 …

深度学习之降维和聚类

1 降维和聚类 1.1 图解为什么会产生维数灾难 ​ 假如数据集包含10张照片&#xff0c;照片中包含三角形和圆两种形状。现在来设计一个分类器进行训练&#xff0c;让这个分类器对其他的照片进行正确分类&#xff08;假设三角形和圆的总数是无限大&#xff09;&#xff0c;简单的…

uni-app 下拉刷新、 上拉触底(列表信息)、 上滑加载(短视频) 一键搞定

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

AprilTag在相机标定中的应用简介

1. AprilTag简介 相机标定用的标靶类型多样,常见的形式有棋盘格标靶和圆形标靶。今天要介绍的AprilTag比较特别,它是一种编码形式的标靶。其官网为AprilTag,它是一套视觉基准系统,包含标靶编解码方法(Tag生成)和检测算法(Tag检测),可用于AR、机器人、相机标定等领域。…

stm32入门教程--USART外设 超详细!!!

目录 简介 什么是UART&#xff1f; 什么是USART&#xff1f; 简介 USART&#xff08;Universal Synchron /Asynchronous Receiver /Transmitter&#xff09;通用同步/异步收发器 1、USART是STM32内部集成的硬件外设&#xff0c;可根据数据寄存器的一个字节数据自动生成数据帧…

ubuntu20.04 加固方案-设置重复登录失败后锁定时间限制

一、编辑PAM配置文件 打开终端。 使用文本编辑器&#xff08;如vim&#xff09;编辑/etc/pam.d/common-auth文件。 sudo vim /etc/pam.d/common-auth 二、添加配置参数 在打开的配置文件中&#xff0c;添加或修改以下参数&#xff1a; auth required pam_tally2.so deny5 un…

Linux操作系统指令(部分)

Linux操作系统 要求如下&#xff1a; 1、查看/etc/passwd文件的第18-20行内容&#xff0c;并将找到的内容存储至/home/passwd文件中 2、查找/etc/passwd文件中包含root字符的行并将找到的行存储至/root/passwd文件中 3、将/home目录复制到/root目录 4、将/root/home目录&a…

钉子户绷不住了,Win 10正式改为「付费续命」模式

众所周知&#xff0c;「终止支持」是所有 Windows 版本早已既定又让人非常操蛋的最终归宿。 当微软认为一代操作系统已完成其生命周期使命的那一刻&#xff0c;便会毫不犹豫地宣告它的死期。 Windows 系统一旦结束支持&#xff0c;微软将不会再提供任何技术维护和安全更新。 …

C++ | Leetcode C++题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; class Solution { private:mt19937 gen;uniform_int_distribution<int> dis;vector<int> pre;public:Solution(vector<int>& w): gen(random_device{}()), dis(1, accumulate(w.begin(), w.end(), 0)) {partial_sum(…

Grafana+Prometheus监控篇-Nginx

一、监控exporter安装 ①、下载地址 nginx-exporter 这里是Windows下监控&#xff0c;选择amd64. ②、nginx-exporter配置 打开nginx的配置文件nginx.conf,启用nginx的基本状态. server {listen 8088;location /status {stub_status;allow 127.0.0.1; deny all;}} ③…

git 入门作业

任务1: 破冰活动&#xff1a;自我介绍任务2: 实践项目&#xff1a;构建个人项目 git使用流程&#xff1a; 1.将本项目直接fork到自己的账号下&#xff0c;这样就可以直接在自己的账号下进行修改和提交。 这里插一条我遇到的问题&#xff0c;在fork的时候没有将那个only camp4的…

RGA DEMO 下部

#加载llm模型通过ollama最好别用ollama我是没经济条件 from langchain_community.llms import Ollama llm Ollama(model"qwen1_5-4b-chat-q2_k")#pip install langchain_ollama -i https://pypi.tuna.tsinghua.edu.cn/simple #OllamaEmbeddings 要写地址本地也要写&…

Linux 常用安装软件

1、安装JDK 1.1、查看系统自带JDK yum search java|grep jdk 1.2、安装JDK yum install java-1.8.0-openjdk 输入Y 1.3、编辑环境变量配置 vim /etc/profile 添加一下配置 export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk export PATH$JAVA_HOME/bin:$PATH export CLAS…