Linux支持几乎所有市面上硬件设备,导致Linux内核一半以上的代码是设备驱动,而且随着硬件快速提升换代设备驱动代码量还会增长。最为宏内核,为了降低设备的多样性带来驱动开发的复杂度,同时支持设备的热插拔和电源管理,Linux引入一整套硬件设备驱动管理的机制。Linux驱动管理机制是一套软件管理概念,硬件部分内容可以查看逻辑部分学习详细的硬件使用逻辑。主要涉及的概念:
- Linux设备驱动模型
- Linux驱动编写基本套路
- Linux驱动同步和异步操作处理和中断处理
- Linux系统调用流程
1. Linux设备驱动模型
inux设备模型的核心是使用Bus、Class、Device、Driver四个核心数据结构,将大量的、不同功能的硬件设备(以及驱动该硬件设备的方法),以树状结构的形式,进行归纳、抽象,从而方便Kernel的统一管理。
- Bus(总线):Linux认为(可以参考include/linux/device.h中struct bus_type的注释),总线是CPU和一个或多个设备之间信息交互的通道。而为了方便设备模型的抽象,所有的设备都应连接到总线上(无论是CPU内部总线、还是虚拟的总线“platform Bus”)。
- Class(分类):在Linux设备模型中,Class的概念非常类似面向对象程序设计中的Class(类),它主要是集合具有相似功能或属性的设备,这样就可以抽象出一套可以在多个设备之间共用的数据结构和接口函数。因而从属于相同Class的设备的驱动程序,就不再需要重复定义这些公共资源,直接从Class中继承即可。
- Device(设备):抽象系统中所有的硬件设备,描述它的名字、属性、从属的Bus、从属的Class等信息。
- Device Driver(驱动):Linux设备模型用Driver抽象硬件设备的驱动程序,它包含设备初始化、电源管理相关的接口实现。而Linux内核中的驱动开发,基本都围绕该抽象进行(实现所规定的接口函数)。
Linux设备模型的核心思想是(通过xxx手段,实现xxx目的):
1. 用Device(struct device)和Device Driver(struct device_driver)两个数据结构,分别从“有什么用”和“怎么用”两个角度描述硬件设备。这样就统一了编写设备驱动的格式,使驱动开发从论述题变为填空体,从而简化了设备驱动的开发。
2. 同样使用Device和Device Driver两个数据结构,实现硬件设备的即插即用(热拔插)。
在Linux内核中,只要任何Device和Device Driver具有相同的名字,内核就会执行Device Driver结构中的初始化函数(probe),该函数会初始化设备,使其为可用状态。
而对大多数热拔插设备而言,它们的Device Driver一直存在内核中。当设备没有插入时,其Device结构不存在,因而其Driver也就不执行初始化操作。当设备插入时,内核会创建一个Device结构(名称和Driver相同),此时就会触发Driver的执行。这就是即插即用的概念。
3. 通过"Bus-->Device”类型的树状结构解决设备之间的依赖,而这种依赖在开关机、电源管理等过程中尤为重要。
试想,一个设备挂载在一条总线上,要启动这个设备,必须先启动它所挂载的总线。很显然,如果系统中设备非常多、依赖关系非常复杂的时候,无论是内核还是驱动的开发人员,都无力维护这种关系。
而设备模型中的这种树状结构,可以自动处理这种依赖关系。启动某一个设备前,内核会检查该设备是否依赖其它设备或者总线,如果依赖,则检查所依赖的对象是否已经启动,如果没有,则会先启动它们,直到启动该设备的条件具备为止。而驱动开发人员需要做的,就是在编写设备驱动时,告知内核该设备的依赖关系即可。
4. 使用Class结构,在设备模型中引入面向对象的概念,这样可以最大限度地抽象共性,减少驱动开发过程中的重复劳动,降低工作量。
而硬件设备的数量、种类是非常多的,这就决定了Kernel中将会有大量的有关设备模型的数据结构。这些数据结构一定有一些共同的功能,需要抽象出来统一实现,否则就会不可避免的产生冗余代码。这就是Kobject诞生的背景。
通过parent指针,可以将所有Kobject以层次结构的形式组合起来。使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把它释放(这是Kobject诞生时的唯一功能)。
和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间(有关sysfs,会在其它文章中专门描述,本文不会涉及太多内容)。
2. Linux驱动编写流程
Linux设备驱动模型将开发工作就可以简化为数据结构与接口的填充与实现,驱动开发主要包含一下7个步骤:
- 建立Linux驱动框架(装载、卸载Linux驱动)。Linux内核在使用驱动时首先要装载驱动,在装载过程中进行一些初始化动作(建立设备文件、分配内存等),在驱动程序中需提供相应函数来处理驱动初始化工作,该函数须使用module_init宏指定;Linux系统在退出是需卸载Linux驱动,卸载过程中进行一些退出工作(删除设备文件、释放内存等),在驱动程序中需提供相应函数来处理退出工作,该函数须使用module_exit宏指定。
- 注册和注销设备文件。任何Linux驱动都需要有一个设备文件来与应用程序进行交互,建立设备文件的工作一般在probe函数或者是上一步module_init宏指定的函数中完成的。
- 驱动源码文件的结尾指定驱动相关信息。驱动程序是自描述的,通过MODULE_AUTHOR(作者姓名)、MODULE_LICENSE(使用的开源协议)、MODULE_ALIAS(别名)、MODULE_DESCRIPTION(驱动描述)等宏来指定与驱动相关的信息。
- file_ops实现读写等回调函数业务逻辑并注册回调函数。Linux驱动包含了很多动作,也称为事件,如“读”“写”事件,触发相应事件时Linux系统会自动调用驱动程序的相应回调函数。
- 编写Makefile文件
- 编译Linux驱动程序
- 安装和卸载Linux驱动
3. Linux驱动同步和异步操作处理
3.1 Linux同步与异步
waitqueue工作队列,complete完成量
4. Linux系统调用流程
5. Linux共享资源与多进程多线程
锁和多进程多线程协作查看博客专题。
6. 其他
6.1 环境搭建
这个专题研究基于Linux2.6,整个环境配套搭建过程大概如下
- uboot编译,具体见之前的专题
- linux内核编译,具体见之前专题
- 文件系统制作,具体见之前专题
文件系统制作中已经具体讲解了如何在进行系统SND搭建,搭建以后可以直接通过网络方式将开发测试的文件通过网络传递到测试单板而不需要反复的从新编译内核。正常启动了文件系统以后将需要测试的驱动文件拷贝到共享目录中,在开发版中一般共享在/mnt目录,在这个目录中查看相关的文件然后拷贝到开发板根目录,之后就可以insmod安装和测试了。需要注意的是如果文件系统是最简答的文件系统,可能驱动代码中写了相关的设备节点创建方式,但系统安装以后却没有办法真正常见节点,这个时候需要mknod方式来手动创建设备节点进行测试。
6.2优秀博客:
https://www.cnblogs.com/xinghuo123/category/1767980.html?page=3