Linux设备驱动系列(七)——真实的设备驱动程序

本文重点关注构建一个真实完整的Linux设备驱动程序,涵盖用户空间驱动程序和内核驱动程序的实现。
通过结合之前给予设备驱动程序的知识,将理论转化为实践,实现了能够存储和返回用户数据的设备驱动程序。
实现设备驱动程序涉及几个基本步骤:首先,必须执行内核空间软件(设备驱动程序)。
该驱动程序允许用户程序存储数据并在读取设备文件时返回先前保存的数据。
为了保证内核空间程序的运行,本文介绍了几个实用的函数。
第一个是kmalloc()/kfree()。
这两个函数实现了内核空间的动态内存管理。
kmalloc()分配内存,而kfree()释放内存但不清除以前写入的数据。
用户可以使用flags参数自定义内存管理行为。
为了解决用户态和内核态之间复制内存数据的问题,本文介绍了copy_to_user()和copy_from_user()函数。
这两个函数允许用户空间和内核空间之间交换数据。
一般来说,Copy_to_user()用于将数据从用户空间传输到内核空间,而Copy_from_user()用于相反方向传输数据。
在设备驱动交互中,常见的系统调用包括open()、write()、read()和close()。
open()用于打开设备文件,write()用于写入数据,read()用于读取数据,close()用于关闭设备文件。
本文详细介绍了设备驱动程序中执行这些系统调用的过程,以及如何通过这些调用与用户程序进行交互。
为了实现上述功能,会编写设备驱动代码和用户空间软件代码。
设备驱动程序代码主要关注数据存储和检索机制,而用户空间软件通过系统调用与设备驱动程序交互。
最后,通过编译并运行这些代码,可以达到预期的演示效果,验证设备驱动的正确性和有效性。
完成这一系列的实际操作,不仅可以加深对Linux设备驱动原理的理解,还可以提高处理实际问题的能力,为后续的系统编程和设备开发工作打下坚实的基础。

linux设备驱动程序——bus

总线在Linux内核中起到了统一管理所有设备的作用。
它将硬件总线或虚拟总线抽象为设备模型,使系统能够统一地识别和控制各种设备。
在Linux系统中,设备通常挂载在总线上,形成抽象的设备树结构。
这种设计有助于简化设备管理和驱动程序开发。
总线工作流程主要由两部分组成:驱动程序(driver)和设备(device)。
驱动程序实现特定类型设备的驱动程序实现,设备向系统注册所需的资源。
当总线上添加新的驱动程序时,系统会调用总线的匹配函数,尝试匹配相应的设备(驱动程序)。
如果匹配成功,则会调用probe函数来实现设备初始化、配置以及用户空间文件接口的生成。
以AT24CXX(常用的存储设备)为例。
同系列的器件(如AT24C01、AT24C02),其操作方法类似,但容量不同。
因此,无需为每个模型编写单独的驱动程序。
通过编写兼容所有AT24CXX设备的驱动程序并根据型号参数进行调整,可以实现对这些设备的统一管理,大大提高了复用性并节省了内存空间。
在Linux驱动管理模型中,设备是在总线上注册的。
当用户需要使用特定型号的硬件时,只需构建一个与该型号对应的设备,并将其注册到总线上即可。
总线的match函数匹配完成后,调用probe函数完成设备初始化和用户空间文件接口的注册。
Linux中的总线是通过structbus_type结构体来描述的,该结构体包括总线名称、设备名称、设备结构体、匹配回调函数、事件回调函数、初始化和卸载函数等。
该结构体使得总线能够管理注册的设备和驱动程序,并通过match功能实现设备和驱动程序的自动匹配。
总线注册过程主要涉及物理总线(如SPI、I2C)和虚拟总线(如platform)的初始化。
物理总线通过postcore_initcall()将init函数注册到系统中,而虚拟总线在系统初始化时直接调用init函数。
物理总线和虚拟总线各自包含特定的structbus_type描述结构来实现各自的注册和初始化。
当新的设备或驱动程序在总线上注册时,bus_register()接口负责初始化相关资源。
以I2C为例,通过i2c_new_device接口添加设备,调用device_register和bus_add_device函数将设备添加到总线上。
同时,I2C驱动程序的注册和匹配过程是通过i2c_driver_register函数实现的。
设备和驱动的匹配主要是通过设备和驱动中的属性来进行,比如设备名、ID表、设备树转换过程中的兼容性属性等。
总线的match函数负责实现这个匹配过程,以保证可以自动关联正确的设备和驱动程序。
Linux的总线机制为设备管理和驱动开发提供了强大的支持,简化了系统对多个硬件设备的管理。
通过上面的介绍,我们对Linux内核中的总线机制有了更深入的了解。

我在学习写linux驱动程序,以前没接触linux,看《linux设备驱动》看不懂,请高手指点,应该怎样入门?

我也在学习,大家一起学习吧,老师推荐了一本中国电力的书,叫《Linux设备驱动》。
看看吧。
我也看不懂哈哈——Linux驱动简介Linux中的大部分驱动都是以模块的形式编写的。
.模块内核的概念是一个整体结构,隐藏某些功能是非常困难的。
为了解决这个问题,引入了内核机制,通过控制模块的大小,内核可以动态地添加或删除模块。
kernel但是,该模块进入kernel后,就和kernel的其他部分一样了。
.1.1模块实现机制:模块一开始是通过函数voidinti_modules来初始化的(因为启动时往往没有模块,如果系统需要的话)。
以sys开头的一系列函数对模块进行操作,例如:sys_create_modules()、sys_inti_modules()、sys_deldte_modules()等。
这里会用到一些模块的数据结构,有兴趣的朋友可以在/usr/scr/linux/include/linux/module.h中找到。
我们来看看两种添加块的方式:一种是手动添加:eg:insmodmodulename。
HDD/MNT/D。
系统自动加载FAT模块以支持MSDOS文件系统。
1.2模块编程编写模块必须有一定的多进程编程背景因为你的程序不是作为独立程序运行空间和用户空间交换数据的问题使用特殊函数来完成内核空间和用户空间的交换空间数据。
这些函数包括:voidput_user(typevalue,type*u_addr)memcpy_tofs()等需要说明的是,模块代码成和内核版本有极好的关系,如果版本不一致,可能会导致内核模块无法编译,或者该模块执行过程中可能会出现不可预测的结果,例如系统故障等当你明白了这些之后。
你可以尝试一下编写内核模块对于每个内核模块,它必须包含两个函数intinit_module(.)这个函数在内核加载时启动,在内核中注册一些功能函数,或者用它的代码来替换内河的一些功能(假设这些功能为空,因此内河可以安全卸载)。
(个人猜测)intcleanup_module()在内核模块加载时被调用。
举个例子helloworld/*hello.camoduleprogramm*//*theprogramrunningunderkernelmodanditisamodule*/#include"linux/kernerl.h"#ininclude"llinux/module.h"/*prosstheCONFIG_MODVERSIONS*/#ifCONFIG_MODVERSIONS==1#defineMODVERSIONS#include"“linux/mod版本ns.h"#endif/*theinitfunction*/intinit_module(){printk("helloworld!\n');printd("Ihaveruninginakernermod@!!\n");return1;}/*thedistoryfunction*/intcleanup_module(){printk("Iwillshutdownmyselfinkernerlmod/n)";retutn0;}这样的例子也就完成了例子,适合我们在大型程序中的重度应用下面是文件内容makefile#amakefileforamoduleCC=gccMODCFLAGS:=-Wall_DMODULE-D_KERNEL_-Dlinuxhello.ohello.c/usr/。
inculde?linux/version.hCC$(MODCFLAGS)0chello.cechothemoduleiscompliecompletely然后运行​​make命令来获取模块hello.o运行$insmodhello.ohelloworld!自由插入和删除。