正点原子嵌入式linux驱动开发——新字符设备驱动实验

news2024/11/25 1:02:51

经过之前两篇笔记的实战操作,已经掌握了Linux字符设备驱动开发的基本步骤,字符设备驱动开发重点是使用register_chrdev函数注册字符设备,当不再使用设备的时候就使用unregister_chrdev函数注销字符设备,驱动模块加载成功以后还需要手动使用mknod命令创建设备节点。register_chrdev和unregister_chrdev这两个函数是老版本驱动使用的函数,现在新的字符设备驱动已经不再使用这两个函数,而是使用Linux内核推荐的新字符设备驱动 API函数。本节就学习一下如何编写新字符设备驱动,并且在驱动模块加载的时候自动创建设备节点文件

新字符设备驱动原理

使用register_chrdev函数注册字符设备的时候只需要给定一个主设备号即可,但是这样会带来问题:需要实现确定主设备号的使用情况,且会将该主设备号下的所有此设备号都使用掉。

解决这两个问题最好的方法就是在使用设备号的时候向Linux内核申请,需要几个就申请几个,由Linux内核分配设备可以使用的设备号。如果没有指定设备号就可以这样申请:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

如果给定了主设备号和次设备号可以这样申请:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

from就是申请的其实设备号,即给定设备号;count就是申请数量,name即设备名。

注销字符设备后要释放设备号,可统一使用如下函数:

void unregister_chrdev_region(dev_t from, unsigned count)

可以如此来分配设备号:

1 int major; /* 主设备号 */ 
2 int minor; /* 次设备号 */ 
3 dev_t devid; /* 设备号 */ 
4 
5 if (major) { /* 定义了主设备号 */ 
6     devid = MKDEV(major, 0); /* 大部分驱动次设备号都选择0 */ 
7     register_chrdev_region(devid, 1, "test"); 
8 } else { /* 没有定义设备号 */ 
9     alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */ 
10     major = MAJOR(devid); /* 获取分配号的主设备号 */ 
11     minor = MINOR(devid); /* 获取分配号的次设备号 */ 
12 }
13 
14 unregister_chrdev_region(devid, 1); /* 注销设备号 */

新的字符设备注册方法

字符设备结构

可以在Linux中使用cdev结构体表示字符设备,定义在include/linux/cdev.h中:

1 struct cdev { 
2     struct kobject kobj; 
3     struct module *owner; 
4     const struct file_operations *ops; 
5     struct list_head list; 
6     dev_t dev; 
7     unsigned int count;
8 } __randomize_layout

这其中,重要的是ops和dev,即字符设备文件操作函数集合file_operations以及设备号dev_t。使用如下:

struct cdev test_cdev

cdev_init函数

定义好cdev变量之后要调用cdev_init函数来初始化:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

cdev就是要初始化的cdev结构体变量,fops就是字符设备文件操作函数集合。

cdev_add函数

用于像Linux系统添加字符设备。首先cdev初始化,之后使用cdev_add来向Linux系统添加,原型如下:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

p指向要添加的字符设备(cdev结构体变量),dev就是设备号,count是设备数量。

cdev_del函数

卸载驱动就是调用这个函数从Linux内核删除相应字符设备,原型如下:

void cdev_del(struct cdev *p)

p就是要删除的设备。

cdev_del和unregister_chedev_region合起来的功能相当于unregister_chedev函数。

自动创建设备节点

在前面的Linux驱动实验中,使用modprobe加载驱动程序以后还需要使用命令“mknod”手动创建设备节点。本节讲解一下如何实现自动创建设备节点,在驱动中实现自动创建设备节点的功能以后,使用modprobe加载驱动模块成功的话就会自动在/dev目录下创建对应的设备文件

mdev机制

udev是一个用户程序,在Linux下通过udev来实现设备文件的创建与删除,udev可以检测系统中硬件设备状态,可以根据系统中硬件设备状态来创建或者删除设备文件。在使用buildroot构建根文件系统的时候选择了udev的简化版本mdev,所以在嵌入式Linux中用mdev来实现设备节点文件的自动创建与删除,Linux系统中的热插拔事件也由mdev管理,如果使用busybox构建根文件系统,会在/etc/init.d/rcS文件中如下语句:

echo /sbin/mdev > /proc/sys/kernel/hotplug

上述命令设置热插拔事件由mdev来管理。 buildroot构建的根文件系统已经全部处理好mdev了,不需要在修改什么文件。

创建和删除类

自动创建设备节点的工作是在驱动程序的入口函数中完成的,一般在cdev_add函数后面添加自动创建设备节点相关代码。首先要创建一个 class类,class是个结构体,定义在文件include/linux/device.h里面。class_create是类创建函数,class_create是个宏定义。最后将宏class_create展开后如下:

struct class *class_create (struct module *owner, const char *name)

owner一般为THIS_MODULE,name是类名字,返回值是指向结构体class的指针。

卸载驱动程序也需要删除类,函数为class_destroy,原型如下:

void class_destroy(struct class *cls);

cls就是要删除的类。

创建设备

创建好类之后,还需要在这个类下创建一个设备,使用函数device_create,原型如下:

struct device *device_create(struct class *cls,
                             struct device *parent, 
                             dev_t devt, 
                             void *drvdata, 
                             const char *fmt, ...)

device_create是个可变参数函数,cls就是设备要创建哪个类下面;parent是父设备,一般为NULL,也就是没有父设备;devt是设备号;drvdata是设备可能会使用的一些数据,一般为NULL;fmt是设备名字,如果设置fmt=xxx的话,就会生成 /dev/xxx这个设备文件,返回值就是创建好的设备。

同样,卸载驱动的时候需要删除创建的设备,函数为device_destroy,原型如下:

void device_destroy(struct class *cls, dev_t devt)

class是要删除设备所处类,devt是删除的设备号。

参考示例

struct class *class; /* 类 */ 
struct device *device; /* 设备 */ 
dev_t devid; /* 设备号 */ 

/* 驱动入口函数 */ 
static int __init xxx_init(void) 
{ 
	/* 创建类 */ 
	class = class_create(THIS_MODULE, "xxx"); 
	/* 创建设备 */ 
	device = device_create(class, NULL, devid, NULL, "xxx"); 
	return 0; 1
} 

/* 驱动出口函数 */ 
static void __exit led_exit(void) 
{
	/* 删除设备 */
	device_destroy(newchrled.class, newchrled.devid); 
	/* 删除类 */ 
	class_destroy(newchrled.class); 
} 
	
module_init(led_init); 
module_exit(led_exit);

设备私有数据

每个硬件设备都有一些属性,比如主设备号(dev_t),类(class)、设备(device)、开关状(state)等等,在编写驱动的时候你可以将这些属性全部写成变量的形式,但最好的做法试讲所有属性信息做成结构体。编写驱动open的时候将设备结构体作为私有数据添加到设备文件中。举例如下:

/* 设备结构体 */ 
struct test_dev{ 
	dev_t devid; /* 设备号 */ 
	struct cdev cdev; /* cdev */ 
	struct class *class; /* 类 */ 
	struct device *device; /* 设备 */ 
	int major; /* 主设备号 */ 
	int minor; /* 次设备号 */ 
}; 

struct test_dev testdev; 

/* open函数 */ 
static int test_open(struct inode *inode, struct file *filp) 
{ 
	filp->private_data = &testdev; /* 设置私有数据 */ 
	return 0; 
}

设置好后,在write、read、close函数中直接读取private_data即可获得设备结构体。

实验程序编写

与之前LED的实验相比,重点就是使用了新的字符设备驱动,设置了文件四有数据,添加了自动创建设备节点相关内容。

LED灯驱动程序编写

区别点在于,申请好__iomem*的映射后虚拟地址指针,就申请一个newchrled_dev的设备结构体newchrdev;然后在led_open中设置私有数据private_data指向newchrdev;最后在led_init中申请设备号、添加字符设备、创建类和设备,并在led_exit中注销字符新设备、删除类和设备。

编写测试APP

直接使用LED实验的APP即可。

运行测试

编译驱动程序和测试APP

把Makefile中obj-m的值改为newchrled.o即可,“make”之后就会有“newchrled.ko”驱动模块文件。

ledAPP.c则通过下述命令编译:

arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp

运行测试

将两个程序拷贝到rootfs/lib/modules/5.4.31目录中,重启开发板并进入到目录lib/modules/5.4.31中,输入如下命令加载newchrled.ko:

depmod //第一次加载驱动的时候需要运行此命令
modprobe newchrled //加载驱动

加载成功后会输出申请到的主设备号和次设备号,如下图所示:
申请到的设备号
驱动加载成功后会自动在/dev目录下创建设备节点文件/dev/newchrdev,输入如下命令查看:

ls /dev/newchrled -l

驱动节点创建成功后就可以使用ledApp软件来测试,测试命令是一样的:

./ledApp /dev/newchrled 1 //打开 LED灯
./ledApp /dev/newchrled 0 //关闭 LED灯

卸载也是一样:

rmmod newchrled

总结

本篇还是在LED驱动的基础上,完成了自动创建驱动节点的代码编写,在led_open将私有数据private_data指向事先声明的结构体来管理设备文件;驱动open函数中添加类和设备。如此在加载好驱动之后,可以直接通过测试APP来测试,不需要自己设定驱动节点了

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

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

相关文章

广义回归神经网络预测程序

欢迎关注“电击小子程高兴的MATLAB小屋” %% 学习目标:广义回归神经网络 %% 训练速度快 非线性映射能力强 常用于函数逼近 clear all; close all; P1:30; T3*sin(P); netnewgrnn(P,T,0.3); %径向基函数的分布密度是0.3 ysim(net,P); figure; plot(P,T,:,P,T-y,-o);

GitHub下载量从19暴涨到5W,这份架构师学习路线只用了一晚

技术人P8什么概念? 正常本科校招进阿里,6年以下别想P8,普通社招进入阿里,30岁以下P8几乎没有,P8不仅仅是个人技术能力强,还有要有撑起整个团队的能力; 这位大佬在阿里已经工作了8年&#xff0…

zabbix部署与监控

目录 一、什么是zabbix? 二、zabbix 监控原理 三、Zabbix 新特性 三、Zabbix 功能组件 四、部署 zabbix zabbix的服务端部署 zabbix的客户端部署 zabbix的服务端部署 一、什么是zabbix? zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络…

51单片机点阵

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、点阵是什么?1.点阵的原理2. 3*3 点阵显示原理3. 8*8点阵实物图4. 8*8点阵内部原理图5. 16*16点阵实物图,显示原理 二、使用步骤1.先…

【C++从0到王者】第三十八站:位图和布隆过滤器

文章目录 一、哈希桶的改进1.链表与树结构的结合2.扩容使用质数 二、位图1.位图的概念2.位图的实现3.位图的其他应用 三、布隆过滤器1.布隆过滤器的提出2.布隆过滤器的实现3.布隆过滤器的应用 一、哈希桶的改进 1.链表与树结构的结合 有时候,在极端场景下&#xf…

一本由红帽专家亲作的Quarkus实战型入门书籍——《Kubernetes原生微服务开发》

Kubernetes原生微服务开发 出版社: 清华大学出版社 作者:[美] 约翰克林甘(John Clingan)、肯芬尼根(Ken Finnigan) 出版时间:2023年6月 微服务开发并不容易。其中涉及大量的概念与复杂的技术,令很多开发者…

ARP协议(地址解析协议) 的作用和操作过程

目录 1.问题: (在同一个LAN局域网内)如何在已知目的接口的IP地址前提下确定其MAC地址?2.问题:现在假设主机A要向目的主机B发送一个数据报,怎么发送呢?2.1在一个局域网内时2.1.1情况一:2.1.2情况…

从基础到卷积神经网络(第15天)

1. PyTorch 神经网络基础 1.1 模型构造 1. 块和层 首先,回顾一下多层感知机 import torch from torch import nn from torch.nn import functional as Fnet = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))X = torch.rand(2, 20) # 生成随机输入(批…

年龄越大,越要小心逢九年

老话有云:年龄逢九,灾祸频有。在我国的许多农村地区,至今还流传着这么句话,这句话的大概意思是说:每个人命理年龄逢九,就会有个坎,年龄越大,坎就越厉害,所以,…

【真题T1】[NOIP2022] 种花

一.题目 P8865 [NOIP2022] 种花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 二.思路(80pts) (1)"C"型 则我们可以计算出每一行的前缀和,然后枚举每一列再每枚举每一行,定义为x1&#xff1b…

算法leetcode|84. 柱状图中最大的矩形(rust重拳出击)

文章目录 84. 柱状图中最大的矩形:样例 1:样例 2:提示: 分析:题解:rust:go:c:python:java: 84. 柱状图中最大的矩形: 给定 n 个非负整…

配置文件-依赖注入

文章目录 前言一、如何解决注入依赖顺序?二、不生效的到问题三 解决办法总结 前言 spring注入bean的时候,可以指定某些注入顺序 例如:A B两个bean注入,B依赖A ,所以要先注入A 然后才能注入B,此时如何处理呢? 一、如何解决注入依赖顺序? 通…

pdf怎么压缩?pdf文件过大这样压缩准没错

在日常生活中,我们常常需要处理大量的PDF文件,这些文件不仅占用存储空间,而且在传输时也可能会受阻,为了解决这些问题,我们需要对PDF文件进行压缩,下面就给大家分享几个PDF压缩方法,一起来看看吧…

【Python数据挖掘】自动售货机销售数据分析与应用【送书活动】

目录 前言01 案例背景02 分析目标03 分析过程04数据预处理1.清洗数据1.1 合并订单表并处理缺失值1.2 增加“市”属性1.3 处理订单表中的“商品详情”属性1.4 处理“总金额(元)”属性 2.属性选择3.属性规约 05销售数据可视化分析1.销售额和自动售货机数量…

【软考-中级】系统集成项目管理工程师-质量管理历年案例

持续更新。。。。。。。。。。。。。。。 目录 2020 下 试题一(18分) 2020 下 试题一(18分) 某公司刚承接了某市政府的办公系统集成项目,急需一名质量管理人员。因公司有类似项目经验,资料比较齐全。项目经理考虑到配置管理员小张工作积极负责&#xff0…

记一次 .NET某新能源检测系统 崩溃分析

一:背景 1. 讲故事 前几天有位朋友微信上找到我,说他的程序会偶发性崩溃,一直找不到原因,让我帮忙看一下怎么回事,对于这种崩溃类的程序,最好的办法就是丢dump过来看一下便知,话不多说&#x…

【CFD小工坊】浅水模型的边界条件

【CFD小工坊】浅水模型的边界条件 前言处理边界条件的原则边界处水力要素的计算水位边界条件单宽流量边界条件流量边界条件固壁边界条件 参考文献 前言 在浅水方程的离散及求解方法一篇中,我们学习了三角形网格各边通量值及源项的求解。但仍有一个问题没有解决&…

nginx报错

故障1:nginx 502错误 故障描述 一个 post 的请求,直接调接口服务数据正常返回,但是通过 nginx 代理后, 什么都没有返回 nginx错误日志 upstream sent invalid chunked response while reading upstream 原因 http协议版本不一致…

掌握 Scikit-Learn: Python 中的机器学习库入门

机器学习 第二课 Sklearn 入门 概述机器学习与 Python 的完美结合Scikit-Learn 的核心组件与结构安装与配置验证安装 数据表示与预处理特征矩阵和目标向量数据处理 估计器模型的选择思考问题的本质研究数据的分布判断任务的复杂性分类问题回归问题 监督学习分类算法回归算法 无…

微信小程序引入阿里巴巴iconfont图标并使用

介绍 在小程序里,使用阿里巴巴的图标,如下所示: 使用方式 搜索自己需要的图标,然后将需要用到的图标加入购物车,如下图所示: 去右上角,点击购物车按钮;这里第一次使用,会有三个提…