Android板级支持与硬件相关子系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.2 BSP的全局部分

BSP的全局部分与子系统无关,而是在Android开源工程中的整体改动,虽然Android开源工程的大部分公共代码与硬件无关,但还是有一部分内容与硬件相关,这就是BSP的全局部分。

↘2.2.1 源代码工程板级别支持部分

Android系统通过不同的配置来实现同一套源代码(或者区别不大的源代码)对不同板的支持。事实上,支持各种Android设备的源代码都是从Android开源工程衍生而来的。device/*/<board>/和vendor/*/<board>/为Android源代码工程中每个板的配置目录,这里的*表示厂商的名称,例如HTC、SAMSUNG等,其下一级目录则为“板”的名称。

1.板级选择

在Android系统编译之前,首先需要确定的就是名称为TARGET_PRODUCT的环境变量,该变量取值的方法有多种。Android推荐的方法是在envsetup.sh之后通过lunch进行设置。

在编译之前,可以使用envsetup.sh脚本进行编译的配置,配置的过程中,将会调用每一个板的vendersetup.sh文件。

Android 2.3版本的环境设置过程如下所示:

    $ . build/envsetup.sh
    including device/htc/passion/vendorsetup.sh
    including device/samsung/crespo/vendorsetup.sh

Android4.x版本的环境设置过程如下所示(有省略):

    $ . build/envsetup.sh
    including device/samsung/maguro/vendorsetup.sh
    including device/samsung/tuna/vendorsetup.sh
    including device/ti/panda/vendorsetup.sh
    including sdk/bash_completion/adb.bash

以上的passion、crespo、maguro和tuna都是“板”的名称。配置的过程是一个“遍历”的过程,将会调用device///和vendor///目录中每一个板的vendersetup.sh文件。只要板级支持目录中有vendersetup.sh文件,就会被调用。vendersetup.sh被调用后实际上将增加系统可以选择的目标产品。

envsetup.sh脚本执行后,各个板在使用lunch命令进行选择产品时有所体现。

Android2.3可以选择的内容如下所示:

    $ lunch
    You're building on Linux
    Lunch menu... pick a combo:
        l. generic-eng
        2. simulator
        3. full_passion-userdebug
        4. full_crespo-userdebug
    Which would you like? [generic-eng]

Android4.x可以选择的内容如下所示:

    $ lunch
    You're building on Linux
    Lunch menu... pick a combo:
        l. full-eng
        2. full_x86-eng
        3. vbox_x86-eng
        4. full_maguro-userdebug
        5. full_tuna-userdebug
        6. full_panda-eng
    Which would you like? [full-eng]

在lunch时出现的菜单项目中,出现了几个选项,可以使用数字进行选择。从中可见,除了默认的generic-eng等选项之外,Android 2.3的full_passion-userdebug和full_maguro-userdebug、Android4.x的full_maguro-userdebug和full_panda-eng等选项就是来自于自定义板级支持的文件。

用于Nexus S手机的device/samsung/crespo/vendersetup.sh文件如下所示:

    add_lunch_combo full_crespo-userdebug

用于Galaxy Nexus手机的device/samsung/maguro/vendorsetup.sh文件如下所示:

    add_lunch_combo full_maguro-userdebug

其中定义TARGET_PRODUCT宏的名称。执行lunch命令,并选择了数字后,实际上得到的结果也是设置TARGET_PRODUCT等宏。

提示:Nexus One使用passion为板名,另一个名称为mahimahi;Nexus S使用crespo为板名,另一个名称为herring;Galaxy Nexus以maguro为板名,它与panda板都属于tuna。

2.板级配置

BoardConfig.mk是一个平台全局配置的文件,只是进行配置的定义,不能包含实际执行的命令。BoardConfig.mk文件配置的内容可以在各个工程的Android.mk文件中得到。其中主要包括了一些平台配置变量的取值。

几个定义目标平台的配置变量如下所示。

·TARGET_CPU_ABI和TARGET_CPU_ABI2:目标CPU的ABI(Application Binary Interface)。取值有以下几个,armeabi表示ARMv5体系结构;armeabi-v7a表示ARMv7体系结构(Cortex-A);x86表示x86体系结构。

·TARGET_ARCH_VARIANT:目标体系结构的额外设置,取值armv5te表示ARMv5体系结构,armv7-a-neon表示针对支持NEON的ARMv7体系结构,x86-atom表示ATOM的x86体系结构。

·TARGET_CPU_SMP:CPU对称多处理器的支持,取值为true或false。

·ARCH_ARM_HAVE_TLS_REGISTER:ARM处理器的TLS支持,TLS的含义为Thread Local Storage,是一种特殊的寄存器,取值为true或false。

·TARGET_BOARD_PLATFORM :目标板平台的名称,取值为一个字符串。

·TARGET_NO_BOOTLOADER:无BootLoader,取值为true或false。

·TARGET_NO_KERNEL:无内核,取值为true或false。

·TARGET_NO_RADIOIMAGE:无Radio部分的映像,取值为true或false。

如果是编译默认的仿真器支持,就是无BootLoader且无内核的,因为不需要BootLoader,且使用的是预编译的内核映像(相当于zImage)。

与硬件相关子系统的平台配置变量如下所示。

·DEFAULT_FB_NUM:默认framebuffer的数目,取值一般为1、2或4等数值。

·BOARD_USES_GENERIC_AUDIO:是否使用通用音频模块,取值为true或false。

·USE_CAMERA_STUB:是否使用照相机的桩实现,取值为true或false。

·BOARD_EGL_CFG:EGL的配置文件,取值为一个目标机上的路径。

·BOARD_USES_HGL:是否使用硬件的OpenGL,取值为true或false。

·USE_OPENGL_RENDERER:是否使用OpenGL的渲染器,取值为true或false。

·BOARD_USES_OVERLAY:是否使用视频叠加层,取值为true或false。

·BOARD_HAVE_BLUETOOTH等宏:是否具有蓝牙,取值为true或false。

·WIFI_DRIVER_*:WIFI相关的驱动路径,取值为主机的目录。

除此之外,BOARD_NAND_PAGE_SIZE宏表示板上的Nand Flash的页大小,BOARD_FLASH_BLOCK_SIZE表示板上的Flash的块大小。

↘2.2.2 硬件相关的代码改动

1.文件系统配置

Android的源代码经过编译后,将生成目标系统的根文件系统(root)、主文件系统(system)和数据文件系统(userdata)的目录,然后生成几个映像。根据Linux文件系统的特点,每个文件具有自己的用户ID(uid)、组ID(gid)以及3×3的权限,也就是本用户、本组用户、其他用户三者和读(r)、写(w)、执行(x)三者的排列组合。

源代码工程的system/core/include/private目录中的android_filesystem_config.h定义了Android用户ID、组ID和定级目录的属性等。

关于ID定义的内容如下所示:

    #define AID_ROOT           0     // UNIX的根用户
    #define AID_SYSTEM      l000     // 系统用户
    #define AID_RADIO       l00l     // 电话子系统(RIL)
    #define AID_BLUETOOTH   l002     // 蓝牙子系统
    #define AID_GRAPHICS    l003     // 图形设备

此处的id都是整数,在Android系统中用户ID(uid)和组ID(gid)使用取值相同,也就是1000表示用户ID的时候是系统用户,表示组ID(gid)和所属组(GROPUS)的时候也是系统用户。其中AID_ROOT的取值为0,这是遵从UNIX系统的习惯,以0作为根用户的ID。

每个ID为整数类型,都有一个字符串与之相对应,相关的结构如下所示:

    struct android_id_info { const char *name;  unsigned aid; }; // 名称<->id
    static const struct android_id_info android_ids[] = {
        { "root",      AID_ROOT, },
        { "system",    AID_SYSTEM, },
    // 省略部分内容
    };

这些字符串是在Android系统的终端中使用ls、Ps等命令查看时显示的名称。

struct fs_path_config表示了文件系统路径的属性的数据结构,如下所示:

    struct fs_path_config {
        unsigned mode;           // 模式
        unsigned uid;            // 用户ID
        unsigned gid;            // 组ID
        const char *prefix;     // 目录前缀
    };

android_dirs和android_files是fs_path_config类型的两个数组,它们分别表示了目录和具体文件的属性。

fs_path_config类型的android_dirs数组中定义如下所示:

    static struct fs_path_config android_dirs[] = {
        { 00770, AID_SYSTEM, AID_CACHE,   "cache" },
        { 0077l, AID_SYSTEM, AID_SYSTEM,  "data/app" },
        { 0077l, AID_SYSTEM, AID_SYSTEM,  "data/app-private" },
        { 0077l, AID_SYSTEM, AID_SYSTEM,  "data/dalvik-cache" },
        { 0077l, AID_SYSTEM, AID_SYSTEM,  "data/data" },
        { 0077l, AID_SYSTEM, AID_SYSTEM,  "data" },
        { 00750, AID_ROOT,   AID_SHELL,   "sbin" },
        { 00755, AID_ROOT,   AID_SHELL,   "system/bin" },
    // 省略部分内容
        { 00750, AID_ROOT,   AID_SHELL,   "init*" },
        { 00644, AID_ROOT,   AID_ROOT,    0 },
    };
    fs_path_config类型的android_files数组中定义如下所示:
    static struct fs_path_config android_files[] = {
        { 00440, AID_ROOT,     AID_SHELL, "system/etc/init.goldfish.rc" },
        { 00550, AID_ROOT,     AID_SHELL, "system/etc/init.goldfish.sh" },
        { 00440, AID_ROOT,     AID_SHELL, "system/etc/init.trout.rc" },
    // 省略部分内容
    };

例如,根据以上定义,data目录用户和组都是system,访问权限为00771,也就是system用户和属于system组的用户可读、可写也可执行,其他情况只可执行。使用ls-l查看data目录在文件系统中的情况如下所示:

    drwxrwx--x system   system           l970-0l-02 00:58 data

如果需要更改文件系统路径的属性等内容,需要更改android_filesystem_config.h文件即可。mkyaffs2image等工具在生成文件系统映像时,将会根据其中的内容配置文件系统的各个目录和文件的属性。

2.init.rc脚本

init.rc脚本是Android系统的第一个进程init执行的脚本,负责完成初始化的工作。init.rc是一个具有自定义语法的文本文件,主要包括Command(命令)、Action(动作)、Trigger(触发)、Service(服务)、Option(选项)和Property(属性)等几种语法。

init.rc脚本位于根目录中,脚本被解析后将放入队列执行。init.rc脚本通常有两个:第1个是init.rc,第2个是init.<设备>.rc,作为硬件相关的初始化启动内容。两个init.rc脚本的语法和作用完全相同,只是前者用于通用的工作,后者用于设备相关的工作。

默认的init.rc位于system/core/rootdir/目录中,在Android默认的仿真器环境中,使用的第2个init.rc脚本的文件名称为init.goldfish.rc,其源代码的路径为system/core/rootdir/etc/。

init.rc脚本执行的几个阶段如下所示:

    on early-init           # 早于init阶段执行的内容
    on init                 # init阶段执行的内容
    on fs                   # 挂接文件系统执行的内容
    on post-fs              # 挂接文件系统之后执行的内容
    on boot                 # 启动时执行的内容

在执行顺序上,init的几个执行阶段高于脚本。也就是说,执行第一个init.rc的on init之后,再执行第二个init.rc的on init的内容,然后是第一个的on fs的内容,依此类推。

几个常用的命令如下所示:

    export <name> <value>                           # 导出环境变量
    exec <path> [ <argument> ]                      # 执行可执行程序
    write <path> <string> [ <string> ]**          # 写某个文件
    mkdir <path> [mode] [owner] [group]             # 建立目录
    insmod <path>                                   # 插入某个模块

默认init.rc脚本中,挂接文件系统的片段如下所示:

        mount yaffs2 mtd@system    /system
        mount yaffs2 mtd@system    /system ro remount
        mount yaffs2 mtd@userdata  /data nosuid nodev
        mount yaffs2 mtd@cache     /cache nosuid nodev

以上语句用于使用YAFFS2格式的Flash文件系统的情况下,表示将Flash存储器的system分区,挂接到/system目录,并置为只读;将userdata分区,挂接到/data目录;将cache分区,挂接到/cache目录。

两个与设备相关的触发条件如下所示:

    on device-added-<path>               # 设备增加到某个路径的情况
    on device-removed-<path>             # 设备从某个路径删除的情况

根据这个语法可以决定,在某个路径的设备出现或者消失之后,执行某个动作。

服务表示启动一个可执行程序,选项是服务的附加内容,用于配合服务使用。一个硬件相关的服务(蓝牙)的片段如下所示:

    service bluetoothd /system/bin/bluetoothd -n
        socket bluetooth stream 660 bluetooth bluetooth
        socket dbus_bluetooth stream 660 bluetooth bluetooth
        group bluetooth net_bt_admin misc
        disabled

以上内容运行的是/system/bin/bluetoothd可执行程序,作为bluetoothd服务,指定其属于bluetooth、net_bt_admin等几个组(GROUPS)。Socket定义了与之配套的套接字文件,它们被放置到/dev/socket/目录中。

3.ueventd.rc脚本

ueventd.rc脚本是被负责设备管理的ueventd程序使用的。ueventd程序在初始化的较早阶段运行,一般为继init之后的用户空间的第2个进程。对于在系统中的设备,在初始化和热插拔的时候,内核将有uevent消息送到用户空间,ueventd守护进程就将利用这种消息进行设备节点(/dev/中的文件)的建立和删除。其功能类似于桌面Linux的udev。

ueventd.rc脚本作为其中设备管理的列表,与init.rc类似,ueventd实际上也有两个,一个是名称为ueventd.rc的主设备脚本,另一个是名称为ueventd.<设备>.rc的具体硬件的脚本。二者的功能和语法也是相同的。

ueventd脚本的默认路径为system/core/rootdir/ueventd.rc。在仿真器环境中,ueventd.goldfish.rc的内容为空。

ueventd.rc脚本将被安装到目标系统根目录,负责定制/dev/中的设备节点等内容。其内容片段如下所示:

    /dev/null              0666   root       root
    /dev/ashmem            0666   root       root
    /dev/alarm             0664   system     radio
    /dev/eac               0660   root       audio
    /dev/graphics//        0660   root       graphics
    /dev/ttyMSM0           0600   bluetooth  bluetooth

对于设备节点定义的是设备节点名称、权限、用户名和组名等内容。例如,以上的设备节点均为字符设备:/dev/null是标准Linux中一个硬件无关的设备(主设备号为1,次设备号为3);/dev/ashmem是Android的Linux中一个硬件无关的设备(主设备号为10的MISC设备);/dev/eac是Android仿真器中模拟饮品的设备,/dev/ttyMSM0是高通平台中的串口设备;/dev/graphics/则是针对用于显示的帧缓冲设备(主设备号为21),Android对其更改了位置。它们与硬件或者相关,或者无关,但实现部分均为Linux内核中的驱动程序,在这里的配置是为了自动建立其在用户空间的设备节点。