第10章 注册字符设备实验(iTOP-RK3568开发板驱动开发指南 )

news2024/12/25 1:06:23

在上一小节中已经对设备号的相关知识进行了讲解,并成功申请到了设备号,那在Linux系统中,设备号是怎样与字符设备进行关联的呢?字符设备又是怎样注册的呢?带着疑问,让我们开始本章节的学习吧。

10.1 注册字符设备

注册字符设备可以分为两个步骤:

1.字符设备初始化

2.字符设备的添加

在本小节将对上述两个步骤所用到的函数和结构体进行讲解。

10.1.1 字符设备初始化

字符设备初始化所用到的函数为cdev_init(…),在对该函数讲解之前,首先对cdev结构体进行介绍。

Linux 内核中将字符设备抽象成一个具体的数据结构 (struct cdev), 我们可以理解为字符设备对象,cdev 记录了字符设备号、内核对象、文件操作file_operations结构体(设备的打开、读写、关闭等操作接口)等信息,struct cdev 结构体定义在“内核源码/include/linux/cdev.h”文件中(在编写驱动程序的时候要加入该文件的引用),如下(图10-1)所示:

struct cdev { 
	struct kobject kobj;                  //内嵌的内核对象.
	struct module *owner;                 //该字符设备所在的内核模块的对象指针.
	const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
	struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.
	dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.
	unsigned int count;                   //隶属于同一主设备号的次设备号的个数.
};

图 10-1

关于该结构体参数的注释在上图已经添加,设备初始化所用到的函数为cdev_init(),该函数同样在“内核源码/include/linux/cdev.h”文件中所引用如下(图10-2)所示:

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

图10-2

该函数的详细内容在“内核源码/include/fs/char_dev.c”文件中定义,如下(图10-3)所示:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev);//将整个结构体清零;
    INIT_LIST_HEAD(&cdev->list);//初始化list成员使其指向自身;
    kobject_init(&cdev->kobj, &ktype_cdev_default);//初始化kobj成员;
    cdev->ops = fops;//初始化ops成员,建立cdev 和 file_operations之间的连接
}

图 10-3

函数作用:

​ 初始化传入的cdev 类型的结构体,并与自定义的file_operations * 类型的结构体进行链接。

参数含义:

​ cdev: 要传入的cdev类型结构体,为要初始化的字符设备。

​ fops:要传入的file_operations * 类型结构体,关于file_operations结构体的相关的知识会在下一章节进行讲解。

**函数返回值:**无返回值。

10.1.2 字符设备的注册

字符设备的注册:

字符设备添加所用到的函数为cdev_add(),该函数在“内核源码/include/linux/cdev.h”文件中所引用,如下(图10-4)所示:

int cdev_add(struct cdev *, dev_t, unsigned);

图 10-4

函数原型:

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

函数作用:

​ 该函数向内核注册一个struct cdev结构体

参数含义:

​ (1)第一个参数为要添加的struct cdev 类型的结构体

​ (2)第二个参数为申请的字符设备号

​ (3)第三个参数为和该设备关联的设备编号的数量。

​ 这两个参数直接赋值给struct cdev 的dev成员和count成员。

**函数返回值:**添加成功返回0,添加失败返回负数。

字符设备的注销:

字符设备删除所用到的函数为cdev_del(),该函数同样在“内核源码/include/linux/cdev.h”文件中所引用,如下(图10-5)所示:

void cdev_del(struct cdev *);

图 10-5

函数原型:

​ void cdev_del(struct cdev *p)

函数作用:

​ 该函数会向内核删除一个struct cdev 类型结构体

参数含义:

​ 该函数只有一个参数,为要删除的struct cdev 类型的结构体

**函数返回值:**无返回值

​ 至此,关于注册字符设备实验所用到的函数就讲解完成了,在下一小节中将编写注册字符设备代码。

10.2 实验程序的编写

本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\05。

本实验采用动态申请设备号的方式进行设备号的申请,然后对设备进行注册,并将申请到的主设备号和次设备号以及设备注册情况打印到终端上。

编写完成的cdev.c代码如下(图10-6)所示

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>

static dev_t dev_num;//定义dev_t类型(32位大小)的变量dev_num,用来存放设备号
struct cdev cdev_test;//定义cdev结构体类型的变量cdev_test
struct file_operations cdev_test_ops{
	.owner=THIS_MODULE//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
};//定义file_operations结构体类型的变量cdev_test_ops

static int __init module_cdev_init(void)//驱动入口函数
{
    int ret;//定义int类型变量ret,进行函数返回值判断
    int major,minor;//定义int类型的主设备号major和次设备号minor
    ret = alloc_chrdev_region(&dev_num,0,1,"chrdev_name");//自动获取设备号,设备名chrdev_name
    if (ret < 0){
        printk("alloc_chrdev_region is error\n");
    }
    printk("alloc_register_region is ok\n");
    major = MAJOR(dev_num);//使用MAJOR()函数获取主设备号
    minor = MINOR(dev_num);//使用MINOR()函数获取次设备号
    printk("major is %d\n",major);
    printk("minor is %d\n",minor);                                         
cdev_init(&cdev_test,&cdev_test_ops);//使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构体
cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 
    ret = cdev_add(&cdev_test,dev_num,1);//使用cdev_add()函数进行字符设备的添加
    if(ret < 0 ){
        printk("cdev_add is error\n");
    }
    printk("cdev_add is ok\n");
    return 0;
}

static void __exit module_cdev_exit(void)//驱动出口函数
{
    cdev_del(&cdev_test);//使用cdev_del()函数进行字符设备的删除
    unregister_chrdev_region(dev_num,1);//释放字符驱动设备号 
    printk("module exit \n");
}

module_init(module_cdev_init);//注册入口函数
module_exit(module_cdev_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("topeet");  //作者信息  

编写完成的cdev.c代码如下(图10-6)所示

图 10-6

相较于上一章节实验,本章节的代码去掉了静态申请设备号部分代码,并在申请设备号完成之后注册了相应的字符设备,并在驱动出口函数中添加了相应的字符设备删除代码(相关代码已加粗)。

需要注意的是,字符设备的注册要放在申请字符设备号之后,字符设备的删除要放在释放字符驱动设备号之前。

10.3 运行测试

10.3.1 编译驱动程序

在上一小节中的cdev.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下(图10-7)所示:

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += cdev.o    #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:
    make -C $(KDIR) M=$(PWD) modules    #make操作
clean:
    make -C $(KDIR) M=$(PWD) clean    #make clean操作

图 10-7

对于Makefile的内容注释已在上图添加,保存退出之后,来到存放parameter.c和Makefile文件目录下,如下图(图10-8)所示:

img

图 10-8

然后使用命令“make”进行驱动的编译,编译完成如下图(图10-9)所示:

img

图 10-9

编译完会生成 cdev.ko目标文件,如下图(图10-10)所示:

img

图 10-10

至此我们的驱动模块就编译成功了,下面进行驱动的运行测试。

10.3.2 运行测试

开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图10-11)所示:

insmod cdev.ko

img

图 10-11

可以看到动态申请设备号成功了,主设备号为236,次设备号为0,然后使用以下命令进行注册设备号的查看,如下图(图10-12)所示:

cat /proc/devices

img

图 10-12

可以看到主设备号236的设备名为chrdev_name,和驱动程序中设置的设备名称相同,证明字符设备注册成功了,最后可以使用以下命令对驱动进行卸载,卸载完成如下图(10-13)所示:

rmmod cdev.ko

img

图 10-13

【最新驱动资料(文档+例程)】

链接 https://pan.baidu.com/s/1M4smUG2vw_hnn0Hye-tkog

提取码:hbh6

【B 站配套视频】

https://b23.tv/XqYa6Hm

【RK3568 购买链接】

https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-2245

ev.ko

[外链图片转存中…(img-zvsdnSuT-1694140423531)]

图 10-13

【最新驱动资料(文档+例程)】

链接 https://pan.baidu.com/s/1M4smUG2vw_hnn0Hye-tkog

提取码:hbh6

【B 站配套视频】

https://b23.tv/XqYa6Hm

【RK3568 购买链接】

https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-2245

2452613.11.2fec74a6elWNeA&id=669939423234

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

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

相关文章

PostgreSQL 数据库使用 psql 导入 SQL

最近我们有一个 SQL 需要导入到 PostgreSQL &#xff0c;但数据格式使用的是用&#xff1a; -- -- TOC entry 7877 (class 0 OID 21961) -- Dependencies: 904 -- Data for Name: upload_references; Type: TABLE DATA; Schema: public; Owner: - --COPY public.upload_refere…

电脑数据恢复软件都有哪些,哪个好用

当涉及到机房使用的系统还原软件时&#xff0c;选择一个强大且稳定的工具对于确保计算机系统的安全性和可靠性至关重要。以下是五款常见的系统还原软件&#xff0c;它们具有出色的功能和性能&#xff0c;以满足不同用户的需求&#xff0c;大家可根据自己的需求来选择。 云顷还…

ARM接口编程—GPIO(exynox 4412平台)

GPIO简介 GPIO&#xff08;General-purpose input/output&#xff09;即通用型输入输出&#xff0c;GPIO可以控制连接在其之上的引脚实现信号的输入和输出 芯片的引脚与外部设备相连&#xff0c;从而实现与外部硬件设备的通讯、控制及信号采集等功能 GPIO寄存器配置 查看LED…

【运维 Pro】时序场景实践与原理 - 1. 分布与分区

【运维 Pro】: 是由 YMatrix 售前和售后团队负责的栏目。除了介绍日常的数据库运维和使用知识&#xff0c;我们更希望能够通过介绍这些知识背后的原理&#xff0c;让大家和我们一起感知数据库的美妙。 摘要 有别于其它场景&#xff0c;时序场景中的数据、查询都有着更为明显的…

即拼七人拼团系统开发模式具体应该怎么玩?

随着电商市场竞争力的不断加剧&#xff0c;如何提高顾客复购率&#xff0c;成为了每个电商平台都在纠结的难题。针对这种情况&#xff0c;即拼七人拼团模式应运而生&#xff0c;下面就来具体说一下这个模式具体应该怎么玩&#xff1f; 即拼七人拼团其实就是在电商平台上推出的一…

探究SpringWeb对于请求的处理过程

探究目的 在路径归一化被提出后&#xff0c;越来越多的未授权漏洞被爆出&#xff0c;而这些未授权多半跟spring自身对路由分发的处理机制有关。今天就来探究一下到底spring处理了什么导致了才导致鉴权被绕过这样严重的问题。 DispatcherServlet介绍 首先在分析spring对请求处…

【C++】怎么接受未知数量的参数?

2023年9月8日&#xff0c;周五下午 目录 第一种方式&#xff1a;可变参数函数(Variadic Function)头文件使用方法详解va_start宏详解va_arg宏示例程序 第一种方式&#xff1a;可变参数函数(Variadic Function) 可变参数函数(Variadic Function)是一种可以接受不定数量参数的函…

gpt-author v2:一款自动创作小说的AI工具

基本介绍 gpt-author v2 是一个自动创作小说的AI&#xff0c;它可以在几分钟内根据用户提供的初始提示和章节数生成一整本奇幻小说&#xff0c;并自动打包为电子书格式。 该项目利用 GPT-4、Stable Diffusion API 和 Anthropic API 等一系列大模型调用组成的链来生成原创奇幻小…

Python 递归、迷宫问题、八皇后问题

递归应用场景 各种数学问题&#xff0c;如八皇后问题、汉诺塔、阶乘问题、迷宫问题、球和篮子问题等各种算法中也会使用到递归&#xff0c;比如快排、归并排序、二分查找、分治算法等能够用栈解决的问题递归的优点就是代码比较简洁 迷宫问题&#xff08;Python版&#xff09;…

武警三维数字沙盘电子沙盘虚拟现实模拟推演大数据人工智能开发教程第15课

部队三维数字沙盘电子沙盘虚拟现实模拟推演大数据人工智能开发教程第15课 现在不管什么GIS平台首先要解决的就是数据来源问题&#xff0c;因为没有数据的GIS就是一个空壳&#xff0c;下面我就目前一些主流的数据获取 方式了解做如下之我见&#xff08;主要针对互联网上的一些…

阿里云APP备案步骤_完成工信部APP备案

完成工信部APP备案工作&#xff0c;阿小云分享阿里云App备案详细流程&#xff0c;阿里云APP备案流程分为6步&#xff0c;APP备案成功后应用可以上架&#xff0c;登录阿里云账号填写APP信息&#xff0c;等待阿里云初审&#xff0c;初审通过后进行工信部短信核验&#xff0c;管局…

如何修改VS2017、VS2019、VS2022中C++默认版本

首先明确VS各个版本对C标准的支持情况&#xff1a; IC版本 VS版本 支持情况 C11 2015完全支持 &#xff0c;13基本支持&#xff0c;12部分支持&#xff0c;10以及以下不支持 完全支持 C14 2017完全支持 &#xff0c;15基本支持&#xff0c;13部分支持 完全支…

leetcode刷题--数组类

文章目录 1. 485 最大连续1的个数2. 495 提莫攻击3. 414 第三大的数4. 628 三个数的最大乘积5. 645 错误的集合6. 697 数组的度7. 448 找到所有数组中消失的数字9. 41 缺失的第一个正数10. 274 H指数11. 453 最小操作次数使得数组元素相等12. 665 非递减数列13. 283 移动零14. …

Win10下使用vim9

作为一个经常与文字打交道的Writer&#xff0c;你在学会Vim的基本操作之后&#xff0c;就一定会爱上Vim的。 以下是Windows10_64位&#xff08;专业版&#xff09;环境中安装、使用Vim9的全过程&#xff0c;分享一下&#xff1a; 一、下载、安装Vim9 去Vim官网去下载最新的Vi…

后端开发进阶之路:后端开发核心竞争力之一抽象建模能力

0.引言 在互联网行业&#xff0c;软件工程师面对的产品需求大都是以具象的现实世界事物概念来描述的&#xff0c;遵循的是人类世界的自然语言&#xff0c;而软件世界里通行的则是机器语言&#xff0c;两者间跨度太大&#xff0c;需要一座桥梁来联通&#xff0c;抽象建模便是打…

Vue 报错error:0308010C:digital envelope routines::unsupported 解决方案(三种)

新换的电脑&#xff0c;系统装的win11&#xff0c;node也是18的版本。 跑了一下老项目&#xff0c;我用的是HbuilderX&#xff0c;点击运行和发行时&#xff0c;都会报错&#xff1a; Error: error:0308010C:digital envelope routines::unsupported 出现这个错误是因为 node.j…

024 - STM32学习笔记 - 液晶屏控制(一) - LTDC与DMA2D初始

024- STM32学习笔记 - LTDC控制液晶屏 在学习如何控制液晶屏之前&#xff0c;先了解一下显示屏的分类&#xff0c;按照目前市场上存在的各种屏幕材质&#xff0c;主要分为CRT阴极射线管显示屏、LCD液晶显示屏、LED显示屏、OLED显示屏&#xff0c;在F429的开发板上&#xff0c;…

工作流-flowable

1. 工作流概述 1.1 概念 工作流(Workflow)&#xff0c;就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程&#xff0c;从而实现某个预期的业务目标&#xff0c;或者促使此目标的实现”。 …

图解系列 图解直播推拉流流程

文章目录 流程推流流程扩展 拉流流程 文件加密流程 常用开源流媒体服务器为SRS和MTX 流程 涉及到的组件 主播&#xff08;推流端&#xff09;观众&#xff08;播放器&#xff09;业务服务【持有一些私有Key&#xff0c;如rtmpKey等】流媒体服务器【SRS/MTX】CDN【持有公钥】 …

2023-大数据应用开发-电商可视化结果汇总

电商可视化 任务一&#xff1a;用柱状图展示消费额最高的省份 编写Vue工程代码&#xff0c;根据接口&#xff0c;用柱状图展示2020年消费额最高的5个省份&#xff0c;同时将用于图表展示的数据结构在浏览器的console中进行打印输出&#xff0c;将图表可视化结果和浏览器conso…