Android系统级深入开发
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2.2 硬件抽象层接口方式

1.hardware模块的方式

Android的libhardware库提供一种不依赖编译时绑定,可以动态加载硬件抽象层,硬件模块方式的硬件抽象层架构如图3-7所示。

图3-7 硬件模块方式的硬件抽象层

在libhardware的接口中定义了通用的内容和各个硬件模块的id,每一个硬件模块需要被实现成所要求的接口形式,并生成单独的动态库文件(*.so)。

在使用硬件抽象层的过程中,Android系统的框架层将调用libhardware的接口,根据每一个模块的id,将在指定路径动态打开(dlopen)各个硬件模块,然后找到符号(dlsym),调用硬件模块中的各个接口。

接口的调用方式是一致的,只是在不同系统所打开的硬件模块是不相同的。

Android仿真器的环境中,包含的硬件模块如下所示:

    # ls /system/lib/hw
    sensors.goldfish.so
    gralloc.default.so

其中,gralloc.default.so表示默认的Gralloc模块(用于显示),sensors.goldfish.so表示默认的传感器模块。

libhardware的接口在以下目录中定义:

    hardware/libhardware/include/hardware/hardware.h

struct hw_module_t结构体用于定义了硬件模块的格式,如下所示:

    typedef struct hw_module_t {
        uint32_t tag;                       /* tag,需要被初始化为HARDWARE_MODULE_TAG */
        uint16_t version_major;           /* 主版本号 */
        uint16_t version_minor;           /* 次版本号*/
        const char *id;                    /* 模块标识 */
        const char *name;                  /* 模块的名称 */
        const char *author;                /* 模块作者 */
        struct hw_module_methods_t* methods;      /* 模块方法 */
        void* dso;                                     /* 模块的dso */
        uint32_t reserved[32-7];                    /* 填充字节,为以后使用 */
    } hw_module_t;

struct hw_module_t结构体定义了一个硬件模块的信息。在各个具体硬件模块中,需要以这个结构体为第一个成员,即表示“继承”了这个结构体。

struct hw_module_methods_t是一个表示模块方法的结构体,如下所示:

    typedef struct hw_module_methods_t {
        /** 打开设备的方法 */
        int (*open)(const struct hw_module_t* module, const char* id,
              struct hw_device_t** device);
    } hw_module_methods_t;

struct hw_module_methods_t结构体只包含了一个打开模块的函数指针,这个结构体也作为struct hw_module_t结构体的一个成员。

struct hw_device_t表示一个硬件设备,如下所示

    typedef struct hw_device_t {
        uint32_t tag;                       /* tag 需要被初始化为HARDWARE_DEVICE_TAG */
        uint32_t version;                  /* hw_device_t的版本号 */
        struct hw_module_t* module;                      /* 引用这个设备属于的硬件模块 */
        uint32_t reserved[12];                            /* 填充保留字节 */
        int (*close)(struct hw_device_t* device);     /* 关闭设备 */
    } hw_device_t;

struct hw_device_t也是需要被具体实现的结构体包含使用的,一个硬件模块可以包含多个硬件设备。

硬件的具体的调用流程如下所示:

(1)通过id得到硬件模块。

(2)从硬件模块中得到hw_module_methods_t,打开得到硬件设备hw_device_t。

(3)调用hw_device_t中的各个方法(不同模块所实现的)。

(4)调用方程,通过hw_device_t的close关闭设备。

模块打开到使用的流程,如图3-8所示。

图3-8 硬件模块方式的调用方式

在以上的流程中,还需要libhareware提供一个得到模块的函数,各个函数就是hw_get_module,如下所示:

    int hw_get_module(const char *id, const struct hw_module_t **module);

hw_get_module()函数的实现在hardware/libhardware/目录的hardware.c文件中,其内容如下所示:

    int hw_get_module(const char *id, const struct hw_module_t **module)
    {
        int status;
        int i;
        const struct hw_module_t *hmi = NULL;
        char prop[PATH_MAX];
        char path[PATH_MAX];
        for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
            if (i < HAL_VARIANT_KEYS_COUNT) {
              if (property_get(variant_keys[i], prop, NULL) == 0) {
                  continue;
              }
              snprintf(path, sizeof(path), "%s/%s.%s.so",           // 得到模块的名称
                      HAL_LIBRARY_PATH, id, prop);
            } else {
              snprintf(path, sizeof(path), "%s/%s.default.so",     // 得到默认模块的名称
                      HAL_LIBRARY_PATH, id);
            }
            if (access(path, R_OK)) {
              continue;
            }
            break;                                                           // 找到模块,然后退出
        }
        status = -ENOENT;
        if (i < HAL_VARIANT_KEYS_COUNT+1) {
            status = load(id, path, module);
        }
        return status;
    }

hw_get_module()函数的内容可见,执行的是一个动态查找的过程,找到硬件动态库(*.so)打开,当没有动态库的时候,将打开默认的库文件(*.default.so)。

在hw_get_module()函数中调用的load()函数,其主要内容如下所示:

    static int load(const char *id,
            const char *path,
            const struct hw_module_t **pHmi)
    {
        int status;
        void *handle;
        struct hw_module_t *hmi;
        handle = dlopen(path, RTLD_NOW);       /* 进行动态库的打开 */
        const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
        hmi = (struct hw_module_t *)dlsym(handle, sym);
        /*  ……省略部分内容 */
    }

load()函数实际上执行了一个动态的打开(dlopen)和动态取出符号(dlsym)的过程。这个过程解除了在编译阶段的Android本地框架对特有的硬件模块依赖。

硬件模块的调用方式如下所示:

    xxx_module_t * gModule;
    xxx_device_t * gDevice;
    {
        xxx_module_t const* module;
        err = hw_get_module(XXXX_HARDWARE_MODULE_ID, (const hw_module_t **)&module);
        if (err == 0)
            gModule = (xxxx_module_t*)module;
        gModule->ModuleFunction();          /* 调用模块中的函数 */
        gDevice->DeviceFunction();          /* 调用设备中的函数 */
    }

通常情况下,硬件模块的调用者是Android中的本地框架层。

libhardware的接口头文件中,除了hardware.h之外,其他各个头文件是相互并列的,每一个文件表示了一种硬件抽象层。

lights.h(背光和指示灯模块)

copybit.h(位复制模块)

overlay.h(叠加视频抽象层模块)

qemud.h(QEMU的守护进程模块)

sensors.h(传感器模块)

gralloc.h(gralloc模块,用于显示,Eclair版本新增)

2.直接接口方式

hardware_legacy库中提供了一些各自独立的接口,由用户实现后形成库,被直接连接到系统中。这是实现硬件抽象层最简单也是最直接的方式。hardware_legacy的头文件路径为:

hardware/libhardware_legacy/include/hardware_legacy

Android中的蓝牙库bluedroid与之类似,也是采用同样的方式,其头文件的路径为:

system/bluetooth/bluedroid/include/bluedroid/bluetooth.h

hardware_legacy库中包含了几个C接口的文件,如GPS ,power,wifi,vibrator等,同时在hardware_legacy库中也包含了power,wifi,vibrator这几个系统的实现和GPS在仿真器上的实现。在开发一个新的硬件系统时,可以根据需要去实现这几个库,也可以使用系统默认的实现方式。

这种做法实际上并没有完全将硬件抽象层和Android的本地框架分开,其好处是接口的定义和实现比较简单。

3.C++的继承实现方式

使用C++类的继承方式实现硬件抽象层,也是Android中的一种方式。为了使用这种方式,Android平台定义了C++的接口。由具体的实现者继承实现这些接口,同时在Android系统中,通常也有通用的实现方式,可以作为一个简易的实现或者“桩(stub)”的作用。

使用C++类的继承方式的硬件抽象层结构如图3-9所示。

图3-9 使用C++类的继承方式的硬件抽象层结构

在这种实现方式中,具体的硬件抽象层通常要求被编译成为指定的名称的动态库,由本地框架库连接它;通用的实现被编译成静态库(*.a),本地框架库连接这些静态库的时候,其实就是包含了它们在其中。使用特定硬件抽象层还是通用的硬件抽象层,通常需要根据宏来指定。

Camera和Audio系统使用的是C++类的继承的方式。

4.直接调用驱动

在Android中有一些比较简单的子系统,并没有在“物理”上存在的硬件抽象层,也就是说,实现其硬件抽象功能的部分不在单独的代码中。例如,由JNI部分代码直接调用的驱动程序的设备节点或者使用sys文件系统。

Alarm子系统就是采用的这种方式,在JNI中直接调用驱动程序。此外,在Android的JNI代码中,还包含一些对sys文件系统的访问,例如switch等。