嵌入式(驱动-基础):01---设备驱动入门简介

一、设备驱动介绍

  • 任何一个计算机系统的运转都是系统中软硬件共同努力的结果,没有硬件的软件是空中楼阁,而没有 软件的硬件则只是一堆废铁。硬件是底层基础,是所有软件得以运行的平台,代码最终会落实为硬件上的 组合逻辑与时序逻辑;软件则实现了具体应用,它按照各种不同的业务需求而设计,并完成用户的最终诉 求。硬件较固定,软件则很灵活,可以适应各种复杂多变的应用。因此,计算机系统的软硬件相互成就了 对方
  • 对设备驱动最通俗的解释就是“驱使硬件设备行动” 。驱动与底层硬件直接打交道,按照硬件设备的具 体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA通信,进行物理内存向虚拟内存的 映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据
  • 驱动程序负责硬件和应用软件之间的沟通,而驱动工程师则负责硬件工程师和应用软件工程师之间的 沟通。目前,随着通信、电子行业的迅速发展,全世界每天都会生产大量新芯片,设计大量新电路板,也 因此,会有大量设备驱动需要开发。这些驱动或运行在简单的单任务环境中,或运行在VxWorks、 Linux、Windows等多任务操作系统环境中,它们发挥着不可替代的作用

二、有无操作系统的设备驱动对比

无操作系统的设备驱动

  • 并不是任何一个计算机系统都一定要有操作系统,在许多情况下,操作系统都不必存在。对于功能比较单一、控制并不复杂的系统,譬如ASIC内部、公交车的刷卡机、电冰箱、微波炉、简单的手机和小灵 通等,并不需要多任务调度、文件系统、内存管理等复杂功能,用单任务架构完全可以良好地支持它们的 工作
  • 一个无限循环中夹杂着对设备中断的检测或者对设备的轮询是这种系统中软件的典型架构,如下面所示
  • 在这样的系统中,虽然不存在操作系统,但是设备驱动则无论如何都必须存在。一般情况下,每一种 设备驱动都会定义为一个软件模块
  • 由此可见,在没有操作系统的情况下,设备驱动的接口被直接提交给应用软件工程师,应用软件没有 跨越任何层次就直接访问设备驱动的接口。驱动包含的接口函数也与硬件的功能直接吻合,没有任何附加功能。下图所示为无操作系统情况下硬件、设备驱动与应用软件的关系:

嵌入式(驱动-基础):01---设备驱动入门简介

  • 不合理的设计:
    • 有的工程师把单任务系统设计成了如下图所示的结构,即设备驱动和具体的应用软件模块之间平等,驱动中包含了业务层面上的处理,这显然是不合理的,不符合软件设计中高内聚、低耦合的要求
    • 另一种不合理的设计是直接在应用中操作硬件的寄存器,而不单独设计驱动模块,如下图所示。这 种设计意味着系统中不存在或未能充分利用可重用的驱动代码

嵌入式(驱动-基础):01---设备驱动入门简介

有操作系统的设备驱动

  • 我们还需要将驱动融入内核。为了实现这种融合,必须在所有设备的驱动中设计面向操作系统 内核的接口,这样的接口由操作系统规定,对一类设备而言结构一致,独立于具体的设备
  • 由此可见,当系统中存在操作系统的时候,驱动变成了连接硬件和内核的桥梁。如下图所示,操作 系统的存在势必要求设备驱动附加更多的代码和功能,把单一的“驱使硬件设备行动”变成了操作系统内与 硬件交互的模块,它对外呈现为操作系统的API,不再给应用软件工程师直接提供接口

嵌入式(驱动-基础):01---设备驱动入门简介

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

三、Linux设备驱动

设备的分类及特点

  • 计算机系统的硬件主要由CPU、存储器和外设组成。随着IC制作工艺的发展,目前,芯片的集成度越 来越高,往往在CPU内部就集成了存储器和外设适配器。譬如,相当多的ARM、PowerPC、MIPS等处理 器都集成了UART、I 2C控制器、SPI控制器、USB控制器、SDRAM控制器等,有的处理器还集成了 GPU(图形处理器)、视频编解码器等
  • 驱动针对的对象是存储器和外设(包括CPU内部集成的存储器和外设),而不是针对CPU内核。 Linux将存储器和外设分为3个基础大类:
    • 字符设备:字符设备指那些必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等
    • 块设备:块设备可以 按任意顺序进行访问,以块为单位进行操作,如硬盘、eMMC等
    • 网络设备:在Linux系统中,网络设备面向数据包的接收和发送而设计,它并不倾向于对应于文件系统的节点。内核与网络设备的通信与内核和字符设备、网络设备的通信方式完全不同,前者主要还是使用套接字接 口
  • 字符设备和块设备的驱动设计有出很大的差异,但是对于用户而言,它们都要使用文件系统的操作接口open()、close()、read()、 write()等进行访问

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

嵌入式(驱动-基础):01---设备驱动入门简介

  • 如上图所示,除网络设备外,字符设备与块设备都被映射到Linux文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close()等即可访问字符设备和块设备
  • Linux的块设备有两种访问方法:
    • 一种是类似dd命令对应的原始块设 备,如“/dev/sdb1”等
    • 另一种方法是在块设备上建立FAT、EXT4、BTRFS等文件系统,然后以文件路径 如“/home/barry/hello.txt”的形式进行访问
  • 在Linux中,针对NOR、NAND等提供了独立的内存技术设备 (Memory Technology Device,MTD)子系统,其上运行YAFFS2、JFFS2、UBIFS等具备擦除和负载均衡 能力的文件系统
  • 针对磁盘或者Flash设备的FAT、EXT4、YAFFS2、JFFS2、UBIFS等文件系统定义了文 件和目录在存储介质上的组织。
  • 而Linux的虚拟文件系统则统一对它们进行了抽象
  • 应用程序可以使用Linux的系统调用接口编程,但也可使用C库函数,出于代码可移植性的目的,后者 更值得推荐。C库函数本身也通过系统调用接口而实现,如C库函数fopen()、fwrite()、fread()、 fclose()分别会调用操作系统的API open()、write()、read()、close()

Linux设备驱动的重点、难点

Linux设备驱动的学习是一项浩繁的工程,包含如下重点、难点

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