Linux设备驱动基础篇

一、芯片手册阅读方法

芯片手册往往长达数百页甚至上千页,而且全部都是英文,从头到尾不加区分地阅读需要花费非常长的时间,而且不一定能获取对设计设备驱动有帮助的信息。芯片手册的正确阅读方法是快速而准确地定位到有用的信息,重点阅读这些信息,忽略无关的内容。

下面以S3C6410A的datasheet为例来分析阅读方法,为了直观地反应阅读过程,本节的图都直接从手册中抓图而得。

打开S3C6410A的datasheet,发现页数为1378页,阅读这样的数据手册所花费的时间足够完成整个驱动的设计工作了。S3C6410A datasheet的第一章“PRODUCT OVERVIES”即“产品综述”是必读的,通过阅读这一部分可以获知整个芯片的组成。这一章往往会给出一个芯片的整体结构图,并对芯片内的主要模块进行一个简洁的描述。如图1.1所示,S3C6410A的整体结构图在第61页出现。

图1.1 S3C6410A的整体结构图


Linux设备驱动基础篇_第1张图片
image

接下来的第2~43章每一章都对应S3C6410A整体结构图中的一个模块,这可以从该芯片手册的目录结构图中看出来。

第2章的“MemoryMap”即“内存映射”比较关键,对应定位存储器和外设所对应的基地址有直接指导意义,这一部分应该细看。

第3~34章对应于CPU内部集成的外设或总线控制器,当具体编写某一接口的驱动时,应该详细阅读,主要是分析数据、控制、地址寄存器(datasheet中一般会以表格列出)的访问控制和具体设备的操作流程(datasheet中会给出步骤,有的还会给出流程图)。譬如为了编写S3C6410A的IIC控制器驱动,我们可以详细阅读类似图1.2的寄存器定义表格和图1.3的操作流程图。

图1.2 芯片手册中以表格形式列出的寄存器定义


Linux设备驱动基础篇_第2张图片
image

图1.3 芯片手册中给出外设控制器的操作流程


Linux设备驱动基础篇_第3张图片
image

第44章“ELECTRICAL DATA”即“电气数据”,描述芯片的电气特性,如电压、电流和各种工作模式下的时序及建立时间和保持时间的要求。所有的datasheet都会包含类似章节,这一章对于硬件工程师比较关键,但是一般来说,驱动工程师不需要阅读。

第45章“MECHANICAL DATA”即“机械数据”,描述芯片的物理特性,尺寸和封装,硬件工程师会依据这一章绘制芯片的封装(footprint),驱动工程师无需阅读。

二、Linux设备驱动基础

2.1 设备驱动的作用

任何一个计算机系统的运转都是系统中软硬件共同努力的结果,没有硬件的软件是空中楼阁,而没有软件的硬件则只是一堆废铁。硬件是底层基础,是所有软件得以运行的平台,代码最终会落实为硬件上的组合逻辑与时序逻辑。软件则实现了具体的应用,它按照各种不同的业务需求而设计,完成了用户的最终诉求。硬件较固定,软件则很灵活可以适应各种复杂多变的应用。

对设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据等。

由此可见,设备驱动充当了硬件和应用软件之间的纽带,它使得应用软件只需要调用系统软件的应用编程接口(API)就可以让硬件去完成要求的工作。驱动程序沟通着硬件和应用软件,而驱动工程师则沟通着硬件工程师和应用软件工程师。目前,随着通信、电子行业的迅速发展,全世界每天都会有大量的新芯片被生产,大量的新电路板被设计,也因此,会有大量设备驱动需要开发。这些驱动,或运行在简单的单任务环境,或运行在VxWorks、Linux、Windows等多任务操作系统环境,发挥着不可替代的作用。

大多数编程可以分为两个部分:==机制==,++需要提供什么功能++;==策略==:++如何使用这些功能++。++Linux应用程序则是实现策略,而Linux驱动程序的目的就是实现机制,而且要完成应用程序到驱动再到具体硬件的映射任务++。

在Linux中,驱动程序是内核的一部分,它屏蔽了硬件细节,是整个操作系统的基础。

2.2 设备的分类与特点

驱动针对的对象是存储器和外设(包括CPU内部集成的存储器和外设),而不是针对CPU核。Linux将存储器和外设分为3个基础大类:++字符设备、块设备、网络设备++。

字符设备是指必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。块设备可以任意顺序进行访问,以块为单位进行操作,如磁盘、软驱等。字符设备不需要经过系统的快速缓冲,而块设备需经过系统的快速缓冲。但是,字符设备和块设备并没有严格的界限,有些设备(如Flash设备)既可看做字符设备,也可作为块设备来访问。

字符设备和块设备的驱动设计呈现出很大的差异,但对于用户而言,他们都使用文件系统操作接口open()、close()、read()、write()、ioctl()等进行访问。

在Linux系统中,网络设备是面向数据包的接收和发送而设计,并不对应文件系统节点。内核与网络设备的通信与内核和字符设备、块设备的通信方式完全不同。

另外一些设备分类方法中所称的I2C驱动、USB驱动、PCI驱动、LCD驱动等本身可归纳入3个基础大类,但是对于这些复杂的设备,Linux也定义了独特的驱动体系结构。

2.3 设备驱动的重点与难点

Linux设备驱动学习是一项好饭的工作,包含如下的重点和难点:

  • 编写Linux设备驱动要求工程师有非常好的硬件基础,懂得SRAM、Flash、SDRAM、磁盘的读写方式、UART、IIC、USB、LCD等设备的接口及轮询、中断、DMA的原理,PCI总线的工作方式以及CPU的内存管理单元MMU等;
  • 编写Linux设备驱动要求工程师有非常好的C语言基础,能灵活地运用C语言的结构体、指针、函数指针及内存动态申请和释放等;
  • 编写Linux设备驱动要求工程师有一定的Linux内核基础,虽然并不要求工程师对内核各个部分有深入的研究,但至少要明白驱动与内核的接口。尤其是对于块设备、网络设备、Flash设备、串口设备等复杂设备,内核定义的驱动体系架构本身就非常复杂;
  • 编写Linux设备驱动要求工程师有非常好的多任务并发控制和同步的基础,因为在驱动中会大量使用自旋锁、互斥、信号量、等待队列等并发与同步机制。

上述经验值的获取并非朝夕之事,因此需要我们有足够的学习恒心和毅力。

动手实践永远是学习任何软件开发的最好方法,学习Linux设备驱动的一个注意事项是要避免管中窥豹、只见树木不见森林,因为各类Linux设备驱动都从属于一个Linux设备驱动的架构,单纯而片面地学习几个函数、几个数据结构是不可能理清驱动中各组成部分之间的关系的。因此,Linux驱动的分析方法是点面结合,将对函数和数据结构的理解放在整体架构的背景之中。

2.4 应用-内核-驱动-硬件关系

2.4.1 无操作系统设备驱动

在没有操作系统的情况下,设备驱动的接口被直接提交给了应用软件工程师,应用软件没有跨越任何层次就直接访问了设备驱动的接口。驱动包含的接口函数也与硬件的功能直接吻合,没有任何附加功能。图2.1所示为无操作系统情况下硬件、驱动与应用软件的关系。

图2.1 无操作系统时正确的硬件、驱动和应用软件的关系


Linux设备驱动基础篇_第4张图片
image

下面为两种不合理的设计。图2.2中将设备驱动和具体的应用软件模块平等对待,驱动中包含了业务层面上的处理,这显然不符合软件设计中高内聚、低耦合的要求。图2.3中直接在应用中操作硬件的寄存器,而不是单独设计驱动模块,这意味着系统中不存在或未能充分利用可被重用的驱动代码。

图2.2 驱动与应用高耦合的不合理设计


Linux设备驱动基础篇_第5张图片
image

图2.3 应用直接访问硬件的不合理设计
[图片上传失败...(image-10a194-1551320965105)]

2.4.2 有操作系统设备驱动

当系统中存在操作系统时,驱动变成了连接硬件和内核的桥梁。如图2.4所示,操作系统的存在势必要求设备驱动附加更多的代码和功能,把单一的“驱使硬件设备行动”变成操作系统内与硬件交互的模块,它对外呈现为操作系统API,不再给应用软件工程师直接提供接口。

图2.4 硬件、驱动、操作系统和应用程序的关系


Linux设备驱动基础篇_第6张图片
image

2.4.3 Linux设备驱动与整个软硬件系统的关系

在Linux系统中,如图2.5和2.6所示,除网络设备外,字符设备与块设备都被映射到Linux文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close()等即可访问字符设备和块设备。所有的字符设备和块设备都统一地呈现给用户。块设备比字符设备负责,在它上面会首先建立一个磁盘/Flash文件系统,如FAT、EXT2、EXT3、YAFFS2、JFFS2、UBIFS等,它们定义了文件和目录在存储介质上的组织。

图2.5 Linux设备驱动与整个软硬件系统的关系


Linux设备驱动基础篇_第7张图片
image

图2.6 Linux设备驱动与整个软硬件系统的关系


Linux设备驱动基础篇_第8张图片
image

应用程序可以使用Linux的系统调用接口编程,也可以使用C库函数,出于代码可移植性的目的,推荐使用C库。C库函数本身也通过系统调用接口而实现,如C库函数fopen()、fwrite()、fread()、fclose()分别会调用操作系统的API:open()、write()、read()、close()。

2.4.4 有系统后驱动更复杂,那要操作系统干什么?

首先,一个复杂的软件系统需要处理多个并发的任务,没有操作系统,想完成多任务并发是很困难的;

其次,操作系统给我们提供内存管理机制;一个典型的例子是,对于多数含MMU的处理器而言,Windows、Linux等操作系统可以让每个进程都可以独立地访问4GB的内存空间;

简而言之,操作系统通过给驱动制造麻烦来达到给上层应用提供便利的目的。当驱动都按照操作系统给出的独立于设备的接口而设计,那么应用程序将可使用统一的系统调用接口来访问各种设备。对于类UNIX的VxWorks、Linux等操作系统而言,当应用程序通过write()、read()等函数读写文件就可访问各种字符设备和块设备,而不论设备的具体类型和工作方式,这将是怎样的便利?

你可能感兴趣的:(Linux设备驱动基础篇)