Linux 设备驱动概述
计算机系统的运转需要软件和硬件共同参与,硬件是底层基础,软件则实现了具体的应用。硬件和软件之间则通过设备驱动来联系。在没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口。而在有操作系统的情况下,驱动的架构则由相应的操作系统来定义。驱动存在的意义就是给上层应用提供便利。
驱动针对的对象是存储器和外设。Linux将存储器和外设分为 3 个基础大类:字符设备、块设备、网络设备。
字符设备和块设备都被 Linux 映射到文件系统的文件和目录中,通过文件系统的接口(open、read、write、close等)来访问。其中,块设备可以通过类似 dd 命令对应的原始块设备来访问,也可以通过建立文件系统,以文件路径来访问。
学习 Linux 设备驱动,要求非常好的硬件基础、非常好的软件基础、一定的 Linux 内核基础和非常好的多任务并发控制和同步的基础。学习 Linux 设备驱动要将学习的函数、数据结构等放到整体架构中去理解,才能理清驱动中各组成部分之间的关系。
驱动设计的硬件基础
驱动工程师需要掌握 处理器、存储器、接口和总线、可编程门电路、原理图、硬件时序、芯片手册、仪器使用 等方面的内容。
【处理器】
处理器分为冯诺依曼结构和哈佛结构。冯诺依曼结构将指令和数据放在一起存储,二者数据宽度相同。哈佛结构则将数据和指令分开存储,二者各自使用独立的总线。
处理器在不同的领域上也有不同的设计。数字信号处理器(DSP)用于通信、图像、语音、视频处理等算法处理,对于复杂运算进行了优化。DSP 分为定点 DSP 、浮点 DSP 两种。网络处理器用于电信领域上,通常包括微码处理器和若干硬件协处理器。
【存储器】
存储器可分为只读存储器(ROM)、闪存(Flash)、随机存取存储器(RAM)、光/磁介质存储器。
如今常见的 ROM 已经是非常方便的 E2PROM了,完全可以用软件来擦写。
闪存可以分为 NOR FLASH 和 NAND FLASH 两种。NOR FLASH 可在芯片内执行程序,而 NAND FLASH 需要相应的控制电路进行转换。NOR FLASH 和 CPU 是典型的类 SRAM 接口。
FLASH 的擦写只能将 1 写成 0。所以 FLASH 的擦写需要先对块擦除(全写为 1),再在要求的位置写 0 。FLASH 的擦写还需要注意避免反复擦写同一个块,避免出现坏块。
NOR FLASH 可以使用 SPI 进行访问以节省引脚。NAND FLASH 容量大,价格低,但更容易发生数据错误,应该使用错误探测/错误更正算法(EDC/ECC)。
FLASH 可以通过 CFI (Common Flash Interface)或 JEDEC (Joint Electron Device Engineering Council)接口来读出设备信息。
RAM 包括静态RAM(SRAM )和动态RAM(DRAM )两种。DDR SDRAM 也属于 DRAM 的范畴,它同时利用时钟脉冲的上升沿和下降沿传输数据,因此传输速率更高(加倍)。另外,还有 DPRAM、CAM 、FIFO 这几种流行的 RAM。
DPRAM 是双端口的 RAM,它具有两套完全独立的地址总线、数据总线、控制总线,能够利用芯片内部的逻辑电路支持双 CPU 的同时访问。
CAM 使用内容进行寻址,将输入项与 CAM 中所有数据进行比较,输出匹配信息的地址,在数据检索上有非常大的优势。
FIFO 多用于数据串口,但只能串行单字节读取,每次读取后目标地址自增。
【接口和总线】
串口从时间线上来看,依次有 RS-232、RS-422、RS-485等。现用最广泛的是经过修改的 RS-232C 标准。在 RS-232C 和 UART 之间,还需要进行 COMS/TTL 电平转换。
I2C 的特点是支持多主控,IIC 总线上的任何能够发送和接收的设备都能成为主控设备。
USB 1.1 包含全速和低速两种模式。在 USB 2.0 中,增加了高速模式,半双工工作,数据传输率可达 480Mbit/s。 USB 3.0 全双工工作,数据传输率高达 5.0 Gbit/s。
当电路板需要挂载 USB 设备时,需要提供 USB 主机控制器和连接器。若作为 USB 设备,则需要提供 USB 适配器和连接器。USB 总线具备热插拔的能力。
【原理图分析】
原理图的基本分析方法是以主CPU为中心,向存储器和外设辐射。对于 CPU 重点了解片选、中断、集成的外设控制器。
硬件原理图包含的元素:符号(如 PIN 脚符号)、网络(芯片、器件等之间的互联关系)、描述(模块功能辅助描述)。
【硬件时序】
硬件时序并不是非常重点的一项,但如果需要进行电路板调试就有必要掌握。最经典的硬件时序是 SRAM 的读写时序,过程中涉及地址、数据、片选、读/写、字节使能、就绪/忙 等信号。NOR Flash 以及许多外设控制器都采取了类似的时序,所以该时序有必要掌握。
【芯片手册阅读方法】
正确阅读方法:快速准确地定位有用信息,重点阅读这部分,忽略无关内容。
在芯片手册中,OVERVIEW 非常有必要阅读,它会告知产品或芯片的整体信息和结构。
MemoryMap 也比较重要,对工程师了解存储器和外设的基址很有帮助。
对于各种外设接口的信息,应当在编写某个接口驱动时去了解对应部分,一般是分析数据、控制、地址寄存器的访问控制和具体设备的操作流程。
【仪器使用】
常规会接触到的仪器包括万用表、示波器、逻辑仪等。
Linux 内核及内核编程
【Linux 内核的子系统】
Linux 也是一种类 Unix 系统,由 Linus 参照 Minix 系统开发而来。Linux 受 GNU 计划的广大开源程序以及互联网上广大开发者的支持,不断根据 POSIX 标准优化自身设计,已经成为风靡世界的操作系统。驱动编程的本质是内核编程,Linux 内核包括进程调度、内存管理、虚拟文件系统、网络接口、进程间通信这五大子系统。
- 进程调度:使进程在就绪、执行、睡眠、暂停等几个状态之间切换。Linux 内核通过 task_struct 结构体描述进程并管理各种资源。当用户进程有访问底层资源或硬件的需求时,则通过系统调用进入内核空间。对于需要并发支持的进程,还可以启动内核线程。
- 内存管理:将低地址空间作为用户空间,高地址空间作为内核空间。(如 4G 内存则按 3G 用户空间及 1G 内存空间来划分)内存管理主要对页面管理、内核空间 slab、用户空间 C 库二次管理提供支持。
- 虚拟文件系统:对硬件系统进行抽象,对上层应用提供统一的操作接口,由底层文件系统,或者设备驱动中实现的 operations 结构体的操作函数提供支持。
- 网络接口包括网络协议和网络驱动程序,协议规定了通信方式,驱动程序则完成具体的通信工作。
- 进程间通信:Linux 提供了多种 IPC 方式,如信号量、共享内存、消息队列、管道、UNIX 域套接字 等。Android 中则还提供了 Binder 的 IPC 方式。
Linux 内核的配置系统包括 3 个部分:Makefile、Kconfig 配置文件、配置工具。
【Linux 内核的引导】
对于 RAM Linux 来说,Soc 一般都内置了 bootrom ,上电后 CPU0 去执行 bootrom,再由 bootrom 引导 bootloader。其他 CPU 则会进入 WFI 状态等待 CPU0 的唤醒。bootloader 会去引导内核启动,在这个启动阶段 CPU0 使得用户空间的 init 程序被调用,派生出其他进程。启动的过程中,CPU0 还会唤醒其他 CPU 均衡负载。
zImage 内核镜像实际上包括未被压缩的解压算法和被压缩的内核组成。程序从 bootloader 跳入 zImage 后,调用 zImage 中的解压算法,将内核解压出来。
Linux 内核模块
要想将自己的功能包含到内核中,可以有两种方式:直接编入内核、编译为模块导入。编译为模块的方式,能够使内核更加简洁,而且使系统更加灵活。模块通常会包括:模块加载函数、模块卸载函数、模块许可证声明、模块参数(可选)、模块导出符号(可选)、模块作者等信息声明(可选)等。
模块参数可以在 insmod 时传递:insmod
模块导出符号是指将本模块的符号导出到内核符号表中,供其他模块使用。