android HAL 硬件抽象层

1. 开发 Android 硬件抽象层

1.1 HAL 层模块编写规范

Android 系统的硬件抽象层以模块的形式来管理各个硬件访问接口。每一个硬件模块都对应有一个动态链接库文件。

在系统内部,每一个硬件抽象层模块都使用结构体 hw_moudle_t 描述,而硬件设备则使用结构体 hw_device_t 来描述。下面分别描述硬件抽象层模块文件的命令规范以及结构体 hw_moudle_t 和 hw_device_t 的含义。

1.1.1 硬件抽象层模块编写规范

硬件抽象层的模块文件的命令规范定义在 hardware/libhardware/hardware.c 文件中

/**
 * There are a set of variant filename for modules. The form of the filename
 * is "<MODULE_ID>.variant.so" so for the led module the Dream variants 
 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
 *
 * led.trout.so
 * led.msm7k.so
 * led.ARMV6.so
 * led.default.so
 */

static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different
                       file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};
  • 硬件抽象层模块文件的命令规范:<MOUDLE_ID>.variant.so
    • MOUDLE_ID 表示模块的ID
    • variant 表示variant_keys四个系统属性值的其中一个,顺序依次
    • variant 如果四个属性值都不存在,则将 variant 的值设为 default

variant变量取值过程:

ro.hardware: /prop/cpuinfo 的 Hardware 字段

其他三个在:/system/build.prop 中查找

1.1.2 硬件抽象层模块结构体定义规范

结构体 hw_device_t 和 hw_moudle_t 及其它相关结构体定义在文件 hardware/hardware.h 中

/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;

    uint16_t module_api_version;
    
    uint16_t hal_api_version;
#define version_minor hal_api_version

    /** Identifier of module */
    const char *id;

    /** Name of this module */
    const char *name;

    /** Author/owner/implementor of the module */
    const char *author;

    /** Modules methods */
    struct hw_module_methods_t* methods;

    /** module's dso */
    void* dso;

#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
#endif
}
  1. 硬件抽象层中的每一个模块都必须自定义一个硬件抽象层模块结构体,而且它的第一个成员变量的类型必须是 hw_moudle_t
  2. 硬件抽象层的每一个模块都必须存在一个导出符号 HAL_MODULE_INFO_SYM,它指向一个自定义的硬件抽象层模块结构体。
  3. 结构体 hw_moudle_t 的成员变量 tag 值必须设置为 HARDWARE_MODULE_TAG,用来标识这是一个硬件抽象层模块结构体
  4. 结构体 hw_moudle_t 的成员变量 dso 用来保存加载硬件设备层模块后得到的句柄值。我们知道每一个硬件抽象层模块都对应有一个动态链接库文件。加载硬件抽象层模块的过程实际上就是调用 dlopen 函数来加载与其对应的动态链接库文件的过程。在调用 dlclose 函数来卸载这个硬件抽象层模块时,需要用到这个句柄值。
  5. hw_module_methods_t 变量定义了硬件抽象层模块的操作方法列表。
typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;
  1. 结构体 hw_device_t 用来描述一个已经打开的硬件设备。
typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;

    uint32_t version;

    /** reference to the module this device belongs to */
    struct hw_module_t* module;

    /** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif

    /** Close this device */
    int (*close)(struct hw_device_t* device);

} hw_device_t;

###################################
(1) 硬件抽象层模块中的每一个硬件设备必须自定义一个硬件设备结构体
    而且它的第一个成员变量的类型必须为 hw_device_t
(2) 结构体 hw_device_t 的成员变量 tag 的值必须设置为 HARDWARE_DEVICE_TAG
    用来标识这是一个硬件抽象层中的硬件设备结构体
(3) close 是一个函数指针,用来关闭一个硬件设备

1.2 编写硬件抽象层模块接口

每一个硬件抽象层模块在内核中都对应一个驱动程序,硬件抽象层模块就是通过这些驱动程序来访问硬件设备的,他们通过读写设备文件来进行通信

硬件抽象层的模块接口源文件一般保存在 harware/libhardware 中,以 freg模块 寄存器操作为例,进行说明,它的目录结构如下:

~ Android/hardware/libhardware
---- include
    ----- hardware
        ---- freg.h
        
---- moudles
    ---- power
        ---- freg.c
        ---- Android.mk

其中 freg.h 和 freg.c 是源文件,Android.mk 是模块的编译脚本文件

—> frag.h
#ifndef ANDROID_INCLUDE_HARDWARE_FRAG_H
#define ANDROID_INCLUDE_HARDWARE_FRAG_H

#include <hardware/hardware.h>

__BEGIN_DECLS

// 硬件模块ID
#define FRAG_HARDWARE_MODULE_ID "freg"

// 硬件设备ID
#define FRAG_HARDWARE_DEVICE_ID "freg"

// 自定义硬件模块结构体
typedef struct freg_moudle_t {
    struct hw_module_t common;
}

// 自定义设备结构体
typedef struct freg_device_t {
    struct hw_device_t common;
    int fd;
    int (*set_val)(struct freg_device_t* dev, int val);
    int (*get_val)(struct freg_device_t* dev, int* val);
}

__END_DECLS

#endif  // ANDROID_INCLUDE_HARDWARE_FRAG_H

这个文件中的常量结构体都是按照硬件抽象层模块编写规范来定义的。

  1. 宏 FRAG_HARDWARE_MODULE_ID 描述模块ID
  2. 宏 FRAG_HARDWARE_DEVICE_ID 描述设备ID
  3. 结构体 freg_moudle_t 描述自定义的模块结构体
  4. 结构体 freg_device_t 描述虚拟硬件设备,其中 fd 用来描述打开的文件设备 /dev/frag,成员变量 set_val 和 get_val 是函数指针,用来写和读虚拟硬件设备freg的寄存器地址。
—> frag.c
...
#define DEVICE_NAME "/dev/frag"
#define DMOUDLE_NAME "frag"
#define MOUDLE_AUTHOR "kevin"

/* 设备打开与关闭接口 */
static int freg_device_open(const struct hw_moudle_t* moudle, const char* id, struct hw_device_t** device);
static int freg_device_open( struct hw_device_t** device);

/* 设备寄存器读写接口 */
static int freg_get_val(struct freg_device_t* dev, int* val);
static int freg_set_val(struct freg_device_t* dev, int val);

/* 定义模块操作方法结构体变量  */
static struct hw_moudle_methonds_t freg_moudle_methods = {
    open: freg_device_open
}

/* 定义模块结构体变量 */
struct freg_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = 1,
        .hal_api_version = 1,
        .id = POWER_HARDWARE_MODULE_ID,
        .name = MOUDLE_NAME,
        .author = MOUDLE_AUTHOR,
        .methods = &freg_moudle_methods,
    },

    .init = power_init,
    .setInteractive = power_set_interactive,
    .powerHint = power_hint,
};
...

在这段代码中,最值得关注的是模块变量 HAL_MODULE_INFO_SYM 的定义,按照硬件抽象层模块编写规范,每一个硬件抽象层模块必须导出一个名称为 HAL_MODULE_INFO_SYM 的符号,它指向一个自定义的硬件抽象层模块结构体。

—> Android.mk
1. LOCAL_PATH := $(call my-dir)
2. include $(CLEAR_VARS)
3. LOCAL_MODULE_TAGS := optional
4. LOCAL_PROPRIETARY_MODULE := true
5. LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
6. LOCAL_SHARED_LIBRARIES := liblog
7. LOCAL_SRC_FILES := freg.cpp
8. LOCAL_MODULE := freg.default
9. include $(BUILD_SHARED_LIBRARY)
  1. 这是硬件抽象层的编译脚本文件,第九行指定将模块编译成一个动态链接库文件
  2. LOCAL_MODULE_PATH 指定了库保存的路径(8.1: /vendor/lib/hw)
  3. 将硬件抽象层模块 freg 对应的文件命令为 freg.default,编译成功后在指定的位置成功 freg.default.so,当我们要加载 freg 抽象层模块 freg 时,只需要指定它的ID值,就能找到对应的 so 文件

1.3 硬件抽象层模块的加载过程

在 Android 硬件抽象层,负责加载硬件抽象模块的函数时 hw_get_moudle,它的原型如下:

# Android/hardware/libhardware/include/hardware/hardware.h

/**
 * Get the module info associated with a module by id.
 *
 * @return: 0 == success, <0 == error and *module == NULL
 */
int hw_get_module(const char *id, const struct hw_module_t **module);

===================================================
id:输入参数,表示要加载的硬件抽象层模块ID
moudle:输出参数,如果加载成功,它指向一个自定义的硬件抽象层模块结构体

函数返回值等于0代表加载成功,小于0则表示加载失败

简单阐述一下 hw_get_module 的实现过程,代码在 hardware.c 中

  1. 从 variant_keys 中查找对应注册的模块,获取动态链接库路径
  2. 调用 dlopen 函数将动态连接库加载到内存中,成功则返回 handler 句柄
  3. 使用 dlsym 函数获取名称为 HAL_MODULE_INFO_SYM_AS_STR 的符号,这个 HAL_MODULE_INFO_SYM_AS_STR 符号指向一个自定义的抽象层模块结构体,它包含了模块的所有信息。
  4. HAL_MODULE_INFO_SYM_AS_STR 自定义抽象层模块结构体的第一个成员变量为 hw_moudle_t,将 hw_moudle_t.id 与所要加载的模块ID做比较,返回结果

2. Android上层访问硬件抽象层

应用层使用 JNI 访问硬件模块接口

因为上层应用框架使用java语言开发,而硬件抽象层是使用C++语言进行开发,所以必须通过Java本地接口(JNI)来调用硬件抽象层模块的接口

应用层使用 binder进程间通信机制 实现跨进程通信

Android 系统的硬件访问服务通常运行在系统进程 System 中,应用程序所在的进程要想访问硬件访问服务就需要通过一种进程间通信机制。

Android 提供了一种高效的进程间通信机制 —— Binder进程间通信,应用程序通过它来访问运行在系统进程System中的硬件访问服务。因此实现硬件硬件访问服务之前,首先要定义它的服务接口。

从应用层往下分析,以PoweManager为例:

1. PoweManager.java

该文件中为用户提供了很多关于电源管理方法的操作,这些操作都是通过 Android Binder 进程间通信实现,简单来说就是通过 PowerManagerService 服务提供的接口来实现的

// mService = IPowerManager.Stub.asInterface("---");
final IPowerManager mService;


public boolean isInteractive() {
    try {
        return mService.isInteractive();
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

===========================================
- 1. mService 是PoweManagerService服务的Binder代理对象
- 2. 使用 IPowerManager.Stub.asInterface 获取电源管理服务的 Binder 代理对象
- 3. 使用 mService 代理对象进行电源管理相关的操作,所以使用远程电源管理服务提供的接口
2. IPowerMnager.aidl

该文件定义了电源硬件访问的服务接口

Android 接口定义语言(AIDL)是 Android 系统提供的一种描述语言来定义具体跨进程访问能力的服务接口。在编译时,ADIL文件会被转换成 java 语言,这个转换 java 语言中就包含 Binder 本地对象类 Stub,它实现了 IPowerManager 接口。

前面说到服务接口是用来进程间通信的,提供服务的进程称为Service进程,而使用服务的进程称为 Client 进程(APP),在Service进程中,每一个服务都对应有一个本地 Binder 对象,它通过一个桩(Stub)来等待客户端连接。Client 客户端进程在访问服务之前,首先获取它的一个 Binder 代理对象(Proxy),然后通过这个代理对象接口访问服务接口。

3. 应用层 java 服务接口实现文件 —— PowerManagerService.java

前面的 AIDL 文件提供了服务接口,但是还没有一个具体的地方去实现这些接口,在 PowerManagerService.java 中从 IPowerManager.Stub 类继承下来,并且实现了这一些接口

private final class BinderService extends IPowerManager.Stub {
    ...
}
4. 实现硬件访问服务接口 —— com_android_server_power_PowerManagerService.cpp
#include <android/hardware/power/1.1/IPower.h>
#include <hardware/power.h>

// Check validity of current handle to the power HAL service, and call getService() if necessary.
// The caller must be holding gPowerHalMutex.
bool getPowerHal() {
    if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
        if (gPowerHalV1_0 != nullptr) {
            gPowerHalV1_1 =  android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
            ALOGI("Loaded power HAL service");
        } else {
            ALOGI("Couldn't load power HAL service");
            gPowerHalExists = false;
        }
    }
    return gPowerHalV1_0 != nullptr;
}

int register_android_server_PowerManagerService(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService",
            gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods));
    (void) res;  // Faked use when LOG_NDEBUG.
    LOG_FATAL_IF(res < 0, "Unable to register native methods.");

    // Callbacks

    jclass clazz;
    FIND_CLASS(clazz, "com/android/server/power/PowerManagerService");

    GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz,
            "userActivityFromNative", "(JII)V");

    // Initialize
    for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) {
        gLastEventTime[i] = LLONG_MIN;
    }
    gPowerManagerServiceObj = NULL;
    return 0;
}
  • getPowerHal 方法获取硬件抽象层的 hw_device_t 对象 gPowerHalV1_1
  • 获取到硬件设备 gPowerHalV1_1之后,使用这个对象访问硬件
  • register_android_server_PowerManagerService 方法中调用 jniRegisterNativeMethods 方法将所有实现的方法注册到Java虚拟机中
5. onload.java —— 增加电源管理函数的声明和调用

register_android_server_PowerManagerService 函数时上一小节中编写为了注册到 Java 虚拟机中去的。

onload.cpp 文件实现在 libandroid_service 模块中,当系统加载 libandroid_service 模块时,就会调用 onload.cpp 中的 JNI_Load 函数,添加 native 方法的 Java 虚拟机注册

extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    ...
    register_android_server_PowerManagerService(env);
    ...
}
6. SystemService.java —— 启动电源管理硬件访问服务

前面说到,System 系统进程中会启动各种硬件访问服务,可以在该文件中添加电源模块服务的启动,这样系统一启动,服务就开启了

private void startOtherServices() {
    ....
    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
    ....
    
    private void startOtherServices() {
        ...
        try {
            // TODO: use boot phase
            mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
        } catch (Throwable e) {
            reportWtf("making Power Manager Service ready", e);
        }
        ...
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值