目的
似乎作为软件开发人员来说,一旦涉及硬件,不管是从心里还是从实际操作上都总有一层厚厚的隔阂,但是要认识Android清晰层次化的认识Android系统,也得咬牙去接触,我终究在等待这么一天:其实它没有想象的那么难,它层次相当分明,有助于们整体上去认识Android系统,更便于我们对源代码的分析。
探究的位置:
学Android 的同学都看过下面一个图:
在上图中,Application层就是一个个应用程序。Framework提供一个java的运行环境以及对功能实现的封装,Runtime/ART是一个java虚拟机,通过它编译java。从Libraries那些名字也可以看出来,这里有很多高端大气库,它是功能实现区,多媒体编解码,浏览器渲染,数据库实现,and so on。Kernel部分负责交互硬件。
再看看站在 HAL的角度 重视这个架构:
内核空间中可以有特权操作硬件设备,而用户空间不行,用户空间包含了上层的一些应用层的功能模块,以及面向用户的Application。这样的分层结构实际上在保护移动设备厂商。
Hardware Abstract Layer (HAl) 硬件抽象层运行在用户空间中。
开发Android硬件驱动程序
实现内核驱动模块
目录结构
- ~/Android/kernel/goldfish-android-goldfish-3.4
- drivers
- freg
- freg.h
- freg.c
- Kconfig
- Makefile
关于命名 freg:
按照老罗的最高指示:我开发的是一个虚拟的字符硬件设备驱动程序,手里没有任何单片机或者寄存器,当然是开发虚拟的了…实体的玩意儿我也搞不了呀,这个字符硬件虚拟设备只有一个大小为4个字节的可读可写寄存器,叫做fake register,驱动程序命令为freg。
- freg
- drivers
在其目录下创建上述四个文件:
1 | coffee@ubuntu:~/Android/kernel/goldfish-android-goldfish-3.4/drivers/freg$ ls |
freg.h
1 |
|
freg.c 为驱动程序的实现文件,该程序主要实现了 向用户空间提供了三个接口来访问该虚拟设备中的寄存器val
- 接口1 proc文件系统接口
- 接口2 设备文件系统接口
- 接口3 devfs文件系统接口
freg.c
1 |
|
Kconfig
在编译freg之前,我们需要通过 make menuconfig 命令来设置这些选项,指定驱动程序freg的编译方式。
下面的配置文件有三种方式来编译:
- 建立在内核中
- 编译成内核模块
- 不编译到内核中
1 | config FREG |
Makefile
1 | obj-$(CONFIG_FREG) += freg.o |
修改Kconfig文件
我们需要修改内核的根Kconfig文件,让直行make menuconfig 的时候,让编译系统找到驱动程序freg的Kconfig文件
一般来说,各个CPU体系架构目录下的Kconifg文件都会通过source “drivers/Kconfig” 命令将drivers 目录下的Kconfig文件包含进去。
打开drivers/Kcofig文件 添加 source “drivers/freg/Kconfig”
修改内核Makefile文件
不仅是Kconfig需要让编译器找到,新增的驱动的Makefile也要让编译器找到,所以需要修改内核的Makefile文件。
drivers/Makefile下添加
1 | obj-$(CONFIG_FREG) += freg/ |
编译内核驱动程序模块
设置编译选项 make menuconfig
执行 make menuconfig 命令来配置编译方式:
Fake Register Driver 选项
- ❤ 号 表示建立在内核中
- M 表示 编译成内核模块
exit后 执行 make
error11
2In function 'freg_create_proc':
error: 'struct proc_dir_entry' has no member named 'owner'
原因:
由错误信息可以看出struct proc_dir_entry结构体中没有找到owner的成员。
看到引用的proc_fs.h头文件,发现里面的struct proc_dir_entry结构体中,是有owner成员的,Linux API 里说在2.6版本,struct proc_dir_entry:owner就已经被移除了
我也尝试注释掉owner使用的代码,跟踪到 freg.c 的create文件:
error2 :
1 | implicit declaration of function 'init_MUTEX' [-Werror=implicit-function-declaration] |
原因:
在新版本的linux内核中,init_mutex已经被废除了,新版本使用sema_init函数。查了一下早期版本的定义:
新内核中的定义:
1 | /* |
老内核中的定义
1 | static inline void sema_init(struct semaphore *sem, int val) |
解决办法:绕过函数 init_MUTEX ,在freg.c中直接调用:sema_init(),不过要添加一个整形参数1
1 | semaphore.h: |
freg.c 修改 init_MUTEX()调用
1 | //初始化设备 |
编译成功:
内核镜像文件zImage 保存在arch/arm/boot目录下,可以用它来启动Android模拟器
验证内核驱动程序模块
分别验证 proc文件系统接口, devfs 文件系统, dev文件系统来访问虚拟寄存器
首先打开Android模拟器:
1 | emulator -kernel -3.4 arm zImage & goldfish-android-goldfish |
proc文件系统接口访问虚拟寄存器验证
1 | cd dev //验证驱动程序freg是否成功注册虚拟硬件设备到设备文件系统中 |
devfs 文件系统接口访问虚拟寄存器验证
1 | root@generic://sys/class/freg/freg # cat val |
开发C程序来验证dev文件系统访问虚拟寄存器的正确性
通过c程序来对寄存器进行读写操作:
编写源文件:
- Android/external
- freg
- freg.c
- Android.mk
- freg
freg.c:
1 |
|
freg.c 源文件的编译脚本文件:Android.mk
1 | LOCAL_PATH := $(call my-dir) |
对于C程序来说,include后面的参数为BUILD_EXECUTABLE 可执行应用模块程序。编译结果在:
out/target/product/generic/system/bin中
编译:
1 | source ./build/envsetup.sh |
进入Android系统执行
1 | emulator -kerner /Kernel/goldfish-android-goldfish-3.4/arch/arm/boot/zImage & |
当我们看到了输出为5 ,说明C程序可以执行,虚拟寄存器正常使用。
开发Android硬件抽象层模块
开发Android硬件访问服务
开发Android应用程序来使用硬件访问服务
声明: 相关内容及源代码参考老罗的《Android系统源代码情景分析》感谢老罗!