Linux驱动(驱动程序开发驱动框架代码编译和测试)

驱动就是对底层硬件设备的操作进行封装,并向上层提供函数接口。

linux系统将设备分为3类:字符设备、块设备、网络设备。

先看一张图,图中描述了流程,有助了解驱动。

用户态:

内核态:

驱动链表:管理所有设备的驱动,添加或查找,添加是发生在我们编写完驱动程序,加载到内核。查找是在调用驱动程序,由应用层用户空间去查找使用open函数。驱动插入链表的顺序由设备号检索,就是说主设备号和次设备号除了能区分不同种类的设备和不同类型的设备,还能起到将驱动程序加载到链表的某个位置,在下面介绍的驱动代码的开发无非就是添加驱动(添加设备号、设备名和设备驱动函数)和调用驱动。

补充:

字符设备驱动工作原理在linux的世界里一切皆文件,所有的硬件设备操作到应用层都会被抽象成文件的操作。我们知道如果应用层要访问硬件设备,它必定要调用到硬件对应的驱动程序。Linux内核有那么多驱动程序,应用怎么才能精确的调用到底层的驱动程序呢?

(1)当open函数打开设备文件时,可以根据设备文件对应的structinode结构体描述的信息,可以知道接下来要操作的设备类型(字符设备还是块设备),还会分配一个structfile结构体。

(2)根据structinode结构体里面记录的设备号,可以找到对应的驱动程序。这里以字符设备为例。在Linux操作系统中每个字符设备都有一个structcdev结构体。此结构体描述了字符设备所有信息,其中最重要的一项就是字符设备的操作函数接口。

(3)找到structcdev结构体后,linux内核就会将structcdev结构体所在的内存空间首地址记录在structinode结构体i_cdev成员中,将structcdev结构体中的记录的函数操作接口地址记录在structfile结构体的f_ops成员中。

(4)任务完成,VFS层会给应用返回一个文件描述符(fd)。这个fd是和structfile结构体对应的。接下来上层应用程序就可以通过fd找到structfile,然后在structfile找到操作字符设备的函数接口file_operation了。

其中,cdev_init和cdev_add在驱动程序的入口函数中就已经被调用,分别完成字符设备与file_operation函数操作接口的绑定,和将字符驱动注册到内核的工作。

如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性。通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等。

在Linux字符设备驱动中,模块加载函数通过register_chrdev_region()或alloc_chrdev_region()来静态或者动态获取设备号,通过cdev_init()建立cdev与file_operations之间的连接,通过cdev_add()向系统添加一个cdev以完成注册。模块卸载函数通过cdev_del()来注销cdev,通过unregister_chrdev_region()来释放设备号。

用户空间访问该设备的程序通过Linux系统调用,如open()、read()、write(),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数。

Linux内核就是由各种驱动组成的,内核源码中有大约85%是各种驱动程序的代码。内核中驱动程序种类齐全,可以在同类驱动的基础上进行修改以符合具体单板。

编写驱动程序的难点并不是硬件的具体操作,而是弄清楚现有驱动程序的框架,在这个框架中加入这个硬件。

一般来说,编写一个linux设备驱动程序的大致流程如下:

下面就以一个简单的字符设备驱动框架代码来进行驱动程序的开发、编译等。

上层调用代码

驱动框架代码

驱动框架设计流程

1.确定主设备号

2.定义结构体类型file_operations

3.实现对应的drv_open/drv_read/drv_write等函数,填入file_operations结构体

4.实现驱动入口:安装驱动程序时,就会去调用这个入口函数,执行工作:

①把file_operations结构体告诉内核:注册驱动程序register_chrdev.

②创建类class_create.

③创建设备device_create.

5.实现出口:卸载驱动程序时,就会去调用这个出口函数,执行工作:

①把file_operations结构体从内核注销:unregister_chrdev.

②销毁类class_create.

③销毁设备结点device_destroy.

6.其他完善:GPL协议、入口加载

1、确定主设备、变量定义

2、定义file_operations结构体,加载到内核驱动链表中

这是Linux内核中的file_operations结构体

根据上层调用函数定义结构体成员

staticstructfile_operationspin4_fops={.owner=THIS_MODULE,.open=pin4_open,.write=pin4_write,.read=pin4_read,};3、实现结构体成员pin4_read等函数

4、驱动入口

int__initpin4_drv_init(void)//真实驱动入口{intret;devno=MKDEV(major,minor);//创建设备号ret=register_chrdev(major,module_name,&pin4_fops);//注册驱动告诉内核,把这个驱动加入到内核驱动的链表中pin4_class=class_create(THIS_MODULE,'myfirstdemo');//由代码在dev自动生成设备pin4_class_dev=device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件return0;}其中pin4_class=class_create(THIS_MODULE,'myfirstdemo');//由代码在dev自动生成设备,除此之外还可以手动生成设备,在dev目录下sudomknod+设备名字+设备类型(c表示字符设备驱动)+主设备号+次设备号。

5、出口

6、GPI协议,入口加载,出口加载

module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使用module_exit(pin4_drv_exit);MODULE_LICENSE('GPLv2');驱动模块代码编译和测试编译阶段

驱动模块代码编译(模块的编译需要配置过的内核源码,编译、连接后生成的内核模块后缀为.ko,编译过程首先会到内核源码目录下,读取顶层的Makefile文件,然后再返回模块源码所在目录。)

将该驱动代码拷贝到linux-rpi-4.14.y/drivers/char目录下文件中(也可选择设备目录下其它文件)

修改该文件夹下Makefile(驱动代码放到哪个目录,就修改该目录下的Makefile),将上面的代码编译生成模块,文件内容如下图所示:(-y表示编译进内核,-m表示生成驱动模块,CONFIG_表示是根据config生成的),所以只需要将obj-m+=pin4drive.o添加到Makefile中即可。

回到linux-rpi-4.14.y/编译驱动文件

使用指令:ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-KERNEL=kernel7makemodules进行编译生成驱动模块。

编译生成驱动模块会生成以下几个文件:

.o的文件是object文件,.ko是kernelobject,与.o的区别在于其多了一些sections,比如.modinfo。.modinfosection是由kernelsource里的modpost工具生成的,包括MODULE_AUTHOR,MODULE_DESCRIPTION,MODULE_LICENSE,deviceIDtable以及模块依赖关系等等。depmod工具根据.modinfosection生成modules.dep,modules.*map等文件,以便modprobe更方便的加载模块。

编译过程中,经历了这样的步骤:先进入Linux内核所在的目录,并编译出pin4drive.o文件,运行MODPOST会生成临时的pin4drive.mod.c文件,而后根据此文件编译出pin4drive.mod.o,之后连接pin4drive.o和pin4drive.mod.o文件得到模块目标文件pin4drive.ko,最后离开Linux内核所在的目录。

将生成的.ko文件发送给树莓派:scppin4drive.kopi@192.168.1.106:/home/pi

将pin4test.c(上层调用代码)进行交叉编译后发送给树莓派,就可以看到pi目录下存在发送过来的.ko文件和pin4test这两个文件,

加载内核驱动

lsmod查看系统的驱动模块,执行上层代码,赋予权限

查看内核打印的信息,

如下图所示:表示驱动调用成功

在装完驱动后可以使用指令:sudormmod+驱动名(不需要写ko)将驱动卸载。

调用流程:

我们上层空间的open去查找dev下的驱动(文件名),文件名背后包含了驱动的主设备号和次设备号,此时用户open触发一个系统调用,系统调用经过vfs(虚拟文件系统),vfs根据文件名背后的设备号去调用sys_open去判断,找到内核中驱动链表的驱动位置,再去调用驱动里面自己的dev_open函数

为什么生成驱动模块需要在虚拟机上生成?树莓派不行吗?

生成驱动模块需要编译环境(linux源码并且编译,需要下载和系统版本相同的Linux内核源代码),也可以在树莓派上面编译,但在树莓派里编译,效率会很低,要非常久。

THE END
1.使用图书馆2024-12-11讲座 缥帙留香——2024年图书馆文化嘉年华系列活动 一场穿越时空的文化之旅,一场关于古籍的盛宴,一次心灵的触动,一段历史的对话。在这里,我们将遇见古籍,不仅触摸它们的历史脉络,更感受它们的温度和生命力。让我们一同走进古籍的世界,探索那些被岁月雕琢的智慧与美。 活动一、“一本书的诞生”——古籍https://lib.intl.zju.edu.cn/zh-hans
2.zlibirary电子图书馆官网最新版下载zlibirary中文官网20zlibirary电子图书馆是一款很专业的阅读服务软件,zlibirary电子图书馆帮助用户可以更好的进行阅读,在这里有丰富的书籍资源在这里推荐给你,帮助用户可以更好的进行选择,在这里优质的书籍作品在这里轻松进行查找,满足不同用户的阅读需求,为你带来优质的服务,用户可以更好的进行书籍资源选择,为你带来优质的服务,更好的进https://www.doyo.cn/app/423442.html
3.图书详情限定所在馆: 限定所在馆藏地点: 限定馆藏状态: >> >> 索书号条码号馆藏状态应还时间文献所属馆所在馆所在馆位置流通类型卷册信息借阅次数续借次数 I267.1/z7772 00000190990685 借出 2025-01-22 杭州图书馆 杭州图书馆 文献借阅中心 中文图书 3 1 I267.1/z7772 00000191004197 借出 2024-12-28 杭州图书馆 http://my2.hzlib.net/opac/book/2007038477
4.国家图书馆“亘古巨制 煌煌文脉——中华优秀传统文化典籍展”开展2024-09-12 国家图书馆召开第九次古籍数字资源联合发布会2024-09-12 国家图书馆迎来建馆115周年多项服务举措及活动惠及广大读者2024-09-11 古籍保护与利用公益项目二期启动仪式举行2024-08-29 “四季童读·跟着童书去旅行”突破传统阅读模式2024-08-07 https://www.mct.gov.cn/whzx/zsdw/zggjtsg/
5.c语言图书借出归还系统,c语言图书馆管理系统(codeblocks版).docxbook; //两个全局指针 会导致安全性不高 但是操作方便book *pbook;user *puser = NULL,*paccount; //四个全局变量int user_sum = 0, book_sum = 0, publictime = 0;char choice; //程序入口void main(){system("color 8b");system("mode con cols=100 lines=50");printf("\t\t\t图书馆系统https://blog.csdn.net/weixin_35348635/article/details/117185361
6.CollegeEssays,ResearchPapers,BookReportsInternetNeed writing help for your college essays and research papers? Your search ends here. IPL provides suitable samples to enhance your essay writing, so you can produce top-notch papers every time.https://www.ipl.org/
7.Linux驱动(驱动程序开发驱动框架代码编译和测试)C库(是linux标准库一定有):就是Clibary,提供了程序支配内核干活的接口,调用的open,read,write,fork,pthread,socket由此处封装实现,由写的应用程序调用,C库中的各种API调用的是内核态,支配内核干活。 内核态: 用户要使用某个硬件设备时,需要内核态的设备驱动程序,进而驱动硬件干活,就比如之前文章里面所提到的wiringhttps://zhuanlan.zhihu.com/p/684172553
8.Windows运行时组件Windows 运行库和 c + +-移植到 Windows 运行时的桌面应用程序 使用英语阅读 保存 添加到集合 添加到计划 通过 Facebookx.com 共享LinkedIn电子邮件 打印 项目 2015/07/14 本文内容 Windows 运行时组件 投影和映射 游戏规则 缩略图生成器 显示另外 8 个 https://msdn.microsoft.com/zh-cn/magazine/jj651570.aspx
9.c++基础复习整合未名亚柳一、编译和链接装载库2https://github.com/huihut/interview#%EF%B8%8F-%E9%93%BE%E6%8E%A5%E8%A3%85%E8%BD%BD%E5%BA%9334'''51、c++内存类型(5+1)6'''7堆:由程序员维护分配和释放,如果没有释放,程序结束时由操作系统回收。容纳程序动态分配的内存。8栈:由操作系统自动分配和释放,包含:函数的https://www.cnblogs.com/dzzy/p/13716846.html