【导语】在Android系统移植中,有很重要的一个部分工作,就是为新平台上的硬件设备移植驱动程序。因为Android系统是基于Linux kernel内核构建,所以这里说的移植驱动程序,其实就是基于Android系统平台开发适应移动设备的Linux内核驱动程序。
一. Android系统中Linux内核与设备驱动
Android系统中使用了Linux内核作为自己的操作系统,除了linux的通用代码之外,主要还包含三个方面的东西:
-
体系结构和处理器
体系结构处理器和标准的设备驱动程序这两个方面是和硬件相关的,但是对于同一种硬件,在Android系统和标准的Linux系统中基本上是一样的。 -
Android专用的驱动程序
Android的专用驱动程序,通常是和硬件无关的驱动程序,仅仅在Android系统中使用。 -
标准的linux设备驱动程序
Android系统中Linux内核与设备驱动的结构如下图示:
Linux内核与设备驱动结构图Android系统通常用于移动设备和其他的嵌入式设备,因此都基于ARM体系结构,在ARM体系结构具有多种处理器。因此,对于不同对的处理器,基于相同的外围设备,驱动程序也可能不一样。
需要说明的是上图中的Goldfish:
Android模拟器通过运行一个Goldfish的虚拟CPU ,Goldfish用来运行arm926t指令集(arm926t属于armv5构架),并且仿真了输入/输出,比如键盘输入和LCD 输出。这个模拟器其实是在qemu之上开发的,输入/输出是基于libSDL的。既然Goldfish是被模拟器运行的虚拟CPU,那么当Android在真实的硬件设备上运行时,我们就需要去掉它。
二. Android系统上的设备驱动
- 基本图形用户界面(GUI)部分:包括显示部分、用户输入部分和硬件相关的加速部分,还包括媒体编解码和OpenGL等。
- 音视频输入输出部分:包括音频、视频输出和摄像头等。
- 连接部分:包括无线局域网、蓝牙、GPS等。
- 电话部分:包括通话、GSM等。
- 附属部件:包括传感器、背光、振动器等。
具体来说是有以下:
- Display显示部分:包括FrameBuffer驱动和Gralloc模块。
- Input用户输入部分:包括Event驱动和EventHub。
- Codec多媒体编解码:包括硬件Codec驱动和Codec插件,例如OpenMax。
- 3DAccelerator(3D加速器)部分:包括硬件OpenGL驱动和OpenGL插件。
- Audio音频部分:包括Audio驱动和Audio硬件抽象层。
- VideoOut视频输出部分:包括视频显示驱动和Overlay硬件抽象层。
- Camera摄像头部分:包括Camera驱动(通常是v4l2)和Camera硬件抽象层。
- Phone电话部分:包括Modem驱动程序和RIL库。
- GPS全球定位系统部分:包括GPS驱动(例如串口)和GPS硬件抽象层。
- Wi-Fi无线局域网部分:包括Wlan驱动和协议和Wi-Fi的适配层。
- BlueTooth蓝牙部分:包括BT驱动和协议及BT的适配层。
- Sensor传感器部分:包括Sensor驱动和Sensor硬件抽象层。
- Vibrator振动器部分:包括Vibrator驱动和Vibrator硬件抽象层。
- Light背光部分:包括Light驱动和Light硬件抽象层。
- Alarm警告器部分:包括Alarm驱动和RTC系统和用户空间调用。
- Battery电池部分:包括电池部分驱动和电池的硬件抽象层。
在实际应用中,通过下面的图,感受下一个量产的平板中用到了哪些硬件,体会下其中涉及到的驱动:
image.png
三.Android系统专用驱动
看一下Google为基于Linux kernel而定制的Android系统专用驱动程序:
(1)Android Binder
Android Binder是基于OpenBinder框架的一个驱动,用于Android平台的进程间通信(InterProcess
Communication,IPC)。原来的Linux系统上层应用的进程间通信主要是D-bus(Desktop bus),采用消息总线的方式来进行IPC。
源代码位于
drivers/staging/android/binder.c
(2)Android电源管理
Android电源管理是基于标准Linux电源管理系统的轻量级Android电源管理驱动,针对嵌入式设备做
了很多优化。利用锁和定时器来切换系统状态,控制设备在不同状态下的功耗,已达到节能的目的。
源代码位于:
kernel/power/earlysuspend.c
kernel/power/consoleearlysuspend.c
kernel/power/fbearlysuspend.c
kernel/power/wakelock.c
kernel/power/userwakelock.c
Android5.0版本引用JobSchedule调度程序,以便增加设备续航时间。
(3)低内存管理器(Low Memory Killer)
比Linux的标准的OOM(Out Of Memory)机制更加灵活,它可以根据需要
杀死进程以释放需要的内存。源代码位于
drivers/staging/ android/lowmemorykiller.c
(4)匿名共享内存(Ashmem)
为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。源代码位于
mm/ashmem.c
(5)Android PMEM(Physical)
PMEM用于向用户空间提供连续的物理内存区域,DSP和某些设备只能工作在连续的物
理内存上。 源代码位于
drivers/misc/pmem.c
(6)Android Logger
一个轻量级的日志设备,用于抓取Android系统的各种日志,是Linux所没有的。
源代码位于
drivers/staging/android/logger.c
(7)Android Alarm
提供了一个定时器,用于把设备从睡眠状态唤醒,同时它还提供了一个即使在设备睡眠时也会
运行的时钟基准。源代码位于
drivers/rtc/alarm.c
(8)USB Gadget驱动
一个基于标准 Linux USB gadget驱动框架的设备驱动,Android的USB驱动是基于gaeget框架的。
源代码位于
drivers/usb/gadget/android.c
drivers/usb/gadget/f_adb.c
drivers/usb/gadget/f_mass_storage.c
(9)Android Ram Console
为了提供调试功能,Android允许将调试日志信息写入一个被称为RAM Console的设备
里,它是一个基于RAM的Buffer。源代码位于
drivers/staging/android / ram_console.c
(10)Android timed device
提供了对设备进行定时控制的功能,目前支持vibrator和LED设备。源代码位于
drivers/staging/android /timed_output.c(timed_gpio.c)
(11)Yaffs2 文件系统
Android采用Yaffs2作为MTD nand flash文件系统,源代码位于fs/yaffs2/目录下。
Yaffs2是一个快速稳定的应用于NAND和NOR Flash的跨平台的嵌入式设备文件系统,同其他Flash文件系统相比,
Yaffs2能使用更小的内存来保存其运行状态,因此它占用内存小。Yaffs2的垃圾回收非常简单而且快速,因此能表
现出更好的性能。Yaffs2在大容量的NAND Flash上的性能表现尤为突出,非常适合大容量的Flash存储。
四.Android驱动开发主要工作
(重点理解一下)
Linux系统将设备驱动分为以下三类:
- (1)字符设备
最常用
指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。 - (2)块设备
指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。 - (3)网络设备
为支持通过文件接口处理网络连接,Linux使用了源于BSD的套接字抽象,套接字keil看作应用程序、文件接口、内核的网络之间的代理。
驱动程序是介于系统和硬件之间的桥梁,实现硬件和系统之间的交互是我们底层开发的主要任务。在Android系统中,我们一般需要编写内核级和用户级的程序来完成具体的任务。
4.1 实现系统和硬件之间交互的几种方式
-
(1).编写自己的系统调用
系统调用是用户级程序访问内核最基本的方法,Linux提供了很多标准的系统调用(参见内核代码树中的include/asm-i386/unistd.h和arch/i386/kernel/entry.S文件),并且允许我们添加自己的系统调用来实现和内核的信息交换; -
(2).编写驱动程序
Linux有一个重要的理念就是“一切皆文件”(everything is a file)。
用户空间的应用程序通过系统提供的统一交互接口open() —— read() —— write() —— ioctl() —— close()
方法,访问文件系统中/dev/目录下的一个文件来访问运行于内核空间的驱动程序,并通过驱动程序中实现的功能达到对硬件的访问。 -
(3). 使用proc 文件系统
proc是Linux提供的一种特殊的文件系统,推出它的目的就是提供一种便捷的用户和内核间的交互方式。proc 文件系统相对是比较简单的,不过proc文件的读写并不统一,读数据的buf指针直接指向的就是用户态的地址,可以用sprintf进行写入;而写方法却是内核态的地址,需要用get_user或者 copy_from_user之类的方法。 -
(4).使用虚拟文件系统
有些内核开发者认为利用ioctl()系统调用往往会似的系统调用意义不明确,而且难控制。而将信息放入到proc文件系统中会使信息组织混乱,因此也不赞成过多使用。他们建议实现一种孤立的虚拟文件系统来代替ioctl()和/proc,因为文件系统接口清楚,而且便于用户空间访问,同时利用虚拟文件系统使得利用脚本执行系统管理任务更家方便、有效。 -
(5).使用内存映像
Linux通过内存映像机制来提供用户程序对内存直接访问的能力。内存映像的意思是把内核中特定部分的内存空间映射到用户级程序的内存空间去。也就是说,用户空间和内核空间共享一块相同的内存。
4.2 内核空间和用户空间的交互
上面讲了系统与硬件交互的几种方式提到了,在具体的交互中,驱动程序驱动具体的硬件模块工作时涉及到一个内核空间和用户空间交互的概念。
重点理解
:现代的计算机体系结构中存储管理通常都包含保护机制。提供保护的目的,是要避免系统中的一个任务访问属于另外的或属于操作系统的存储区域。如在IntelX86体系中,就提供了特权级这种保护机制,通过特权级别的区别来限制对存储区域的访问。基于这种构架,Linux操作系统对自身进行了划分:一部分核心软件独立于普通应用程序,运行在较高的特权级别上,(Linux使用Intel体系的特权级3来运行内核。)
它们驻留在被保护的内存空间上,拥有访问硬件设备的所有权限,Linux将此称为内核空间 。相对的,其它部分被作为应用程序在用户空间 执行。它们只能看到允许它们使用的部分系统资源,并且不能使用某些特定的系统功能,不能直接访问硬件,不能直接访问内核空间,当然还有其他一些具体的使用限制。(Linux使用Intel体系的特权级0来运行用户程序。)
通过下面的图,看一下内核空间与用户空间的交互关系:
内核空间与用户空间的交互关系文章来自:一块钢板
网友评论