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内核中的驱动程序,在这里的配置是为了自动建立其在用户空间的设备节点。