Android系統HAL開發實例


1、前言

 Android系統使用HAL這種設計模式,使得上層服務與底層硬件之間的耦合度降低,在文件:

AOSP/hardware/libhardware/include/hardware/hardware.h

中描述了HAL的編寫規范,並且給出了標准接口,本文將通過一個簡單的實例講解HAL的編寫。

 

2、HAL編寫規范

在之前的文章中講解了兩個很重要的數據結構,struct hw_module_t和struct hw_device_t,在其中hw_module_t代表了整個HAL的實現、功能的封裝,也是外部應用程序看到的唯一視角,而hw_device_t代表了一個實際的硬件設備,是設備的屬性、設備操作的封裝,接下來分析HAL的編寫規范。

(1)定義代表module的數據結構

以LED HAL模塊為例,首先需要定義一個表示LED模塊的數據結構led_moduler_t,並且該數據結構遵循以下准則:

(1.1)該結構體中的第一個成員必須是struct hw_module_t;

(1.2)創建一個該數據結構體的變量,並以HAL_MODULE_INFO_SYM為變量名;

(1.3)結構體hw_module_t中的tag成員固定賦值為HARDWARE_MODULE_TAG;

(1.4)結構體hw_module_t中的module_api_version成員代表了HAL模塊的API版本,當模塊的接口發生改變時,開發人員必須更新該成員;

(1.5)結構體hw_module_t中的id成員是該模塊的標識符,其它程序通過該成員來尋找對應HAL得Stub。

實現如下所示:

#include <hardware/hardware.h>

typedef struct led_module {
    struct hw_module_t common;
    ...
    ...
} led_module_t;

struct led_module HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = LED_MODULE_API_VERSION_1_0,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = LED_HARDWARE_MODULE_ID,
        .name = "Defaule Led HAL",
        .author = "hly",
        .methods = &led_module_methods,
    },
    ...
    ...
};

(2)定義代表device的數據結構

通過module並不能實際操作硬件設備,開發者需要將設備的相關屬性以及操作封裝在代表device的數據結構,該數據結構通常是一系列的函數指針,定義led_device_t結構體,該數據結構遵循以下規則:

(2.1)第一個成員必須是struct hw_device_t;

(2.2)結構體中的hw_device_t的tag成員必須被賦值為HARDWARE_DEVICE_TAG。

實現如下所示:

typedef struct led_device {
    struct hw_device_t common;
    int (*get_led_state)(struct led_device *dev, char **state);
    int (*set_led_state)(struct led_device *dev, char *state);
    ...
    ...
} led_device_t;

(3)實現open函數

當其它的應用程序使用HAL模塊的步驟通常是:

首先通過hw_get_module()函數獲得指定HAL模塊的hw_module_t的引用,然后調用hw_module_t->methods->open函數或得hw_device_t的引用,最后,通過device下的函數指針來操作設備。

open函數指針位於hw_module_methods_t結構體中,該函數的實現方法在不同的HAL模塊中實現也較為類似,大體步驟為:

(3.1)為led_device_t結構分配內存空間;

(3.2)對hw_device_t類型的common成員進行賦值;

(3.3)對封裝的結構體中的函數指針進行賦值;

(3.4)將分配空間的led_device_t的指針通過hw_device_t **device返回。

實現如下所示:

static int led_device_open(const struct hw_module_t *module, 
                           const char *id, struct hw_device_t **device)
{
    struct led_device *dev;
    
    dev = malloc(sizeof(struct led_device));
    if (!dev) {
        ALOGE("Failed to malloc memory");
        return -ENOMEM;
    }
    memset(dev, 0, sizeof(led_device));
    
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = MODULE_API_VERSION_1_0;
    dev->common.module = (struct hw_module_t *)module;
    
    dev->get_led_state = get_led_state;
    dev->set_led_state = set_led_state;
    ...
    ...
    ...
    
    *device = (struct hw_device_t *)dev;
    return 0;
}

(4)實現device的具體操作接口

每個HAL模塊的實現都比較類似,對於device下的操作方法,則要按照具體的設備進行實現,每一類設備都是不盡相同的,需要掌握Linux應用層中文件操作、進程管理、信號處理、多線程編程和網絡編程等相關技術。

 

3、實例講解

 實現一個ledctrl的HAL模塊:

首先在安卓源碼下的hardware/libhardware/include/hardware目錄下創建ledctrl.h文件,代碼如下:

#ifndef ANDROID_LEDCTRL_INTERFACE_H
#define ANDROID_LEDCTRL_INTERFACE_H

#include <hardware/hardware.h>

__BEGIN_DECLS

/* 定義模塊ID */
#define LEDCTRL_HARDWARE_MODULE_ID    "ledctrl"

/* 硬件模塊結構體 */
struct ledctrl_module_t {
    struct hw_module_t common;
};

/* 硬件接口結構體 */
struct ledctrl_device_t {
    struct hw_device_t common;
    
    int fd;
    int (*set_state)(struct ledctrl_device_t *dev, const char *state);
    int (*get_state)(struct ledctrl_device_t *dev, char **state);
};

__END_DECLS

#endif

然后在安卓源碼的hardware/libhardware/modules目錄下,新建ledctrl目錄,並添加ledctrl.c文件,代碼實現如下:

#define LOG_TAG "ledctrl_hw_default"

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <cutils/log.h>

#include <hardware/hardware.h>
#include <hardware/ledctrl.h>

#define DEVICE_NAME "/sys/class/leds/led-red/brightness"
#define MODULE_NAME "ledctrl"
#define MODULE_AUTHOR "HLY"

/* 設備訪問接口 */
static int ledctrl_set_state(struct ledctrl_device_t *dev, const char *state)
{
    int ret = -EINVAL;
    
    if (0 == strcmp(state, "enable")) {
        ret = write(dev->fd, "1", 1);
        if (ret != 1) {
            ALOGE("ledctrl: failed to enable led device");
            goto exit;
        }
        ALOGI("ledctrl: successed to enable led device");
        ret = 0;
    } else if (0 == strcmp(state, "disable")) {
        ret = write(dev->fd, "0", 1);
        if (ret != 1) {
            ALOGE("ledctrl: failed to close led device");
            goto exit;
        }
        ALOGI("ledctrl: successed to disable led device");
        ret = 0;
    } else
        ALOGE("ledctrl: not define the led device state");
    
exit:
    return ret;    
}

static int ledctrl_get_state(struct ledctrl_device_t *dev, char **state)
{
    int ret,value;
    char *buf = malloc(sizeof(char) * 5);
    
    memset(buf, 0, sizeof(char) * 5);
    ret = read(dev->fd, buf, sizeof(int));
    if (ret < 0) {
        ALOGE("ledctrl: failed to read led device");
        goto exit;
    }
    
    value = atoi(buf);
    if (value == 0)
        *state = "disable";
    else
        *state = "enable";
    ALOGD("value = %d, led_state = %s", value, *state);

exit:
    free(buf);
    return ret;
}

/* 設備的打開和關閉接口 */
static int ledctrl_device_close(struct hw_device_t *device)
{
    struct ledctrl_device_t *dev = (struct ledctrl_device_t *)device;
    
    if (dev) {
        close(dev->fd);
        free(dev);
    }
    
    return 0;
}

static int ledctrl_device_open(const struct hw_module_t *module,
            const char *id, struct hw_device_t **device)
{
    int fd;
    struct ledctrl_device_t *dev;
    
    dev = malloc(sizeof(struct ledctrl_device_t));
    if (!dev) {
        ALOGE("ledctrl: failed to malloc memory");
        return -ENOMEM;
    }
    memset(dev, 0, sizeof(struct ledctrl_device_t));
    
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t *)module;
    dev->common.close = ledctrl_device_close;
    
    fd = open(DEVICE_NAME, O_RDWR);
    if (fd == -1) {
        ALOGE("ledctrl: failed to open device file");
        free(dev);
        return fd;
    }
    dev->fd = fd;
    dev->set_state = ledctrl_set_state;
    dev->get_state = ledctrl_get_state;
    
    *device = &dev->common;
    
    return 0;
}

/* 模塊方法表 */
static struct hw_module_methods_t ledctrl_module_methods = {
    .open = ledctrl_device_open,
};

/* 模塊實例變量 */
struct ledctrl_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .version_major = 1,
        .version_minor = 0,
        .id = LEDCTRL_HARDWARE_MODULE_ID,
        .name = MODULE_NAME,
        .author = MODULE_AUTHOR,
        .methods = &ledctrl_module_methods,
    },
};

主要是HAL模塊的實現方法,sysfs中的brightness文件為leds-gpio設備的屬性文件,通過對該屬性文件的讀寫操作,能實現led類設備的點亮與熄滅,另外還需要實現編譯的Android.mk文件,代碼如下:

# Android.mk for ledctrl hal module

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := ledctrl.c
LOCAL_MODULE := ledctrl.default
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)

將該ledctrl模塊編譯為動態庫,因此,最后生成的文件應該是ledctrl.default.so。

 

4、編譯測試

 對上面編寫的HAL模塊進行編譯測試:

$ cd AOSP
$ mmm hardware/libhardware/modules/ledctrl
$ make snod

將Android系統的system.img鏡像重新打包后,使用fastboot命令燒寫到system分區,並使用adb登入到終端,查看對應得動態庫是否已經存在:

# cd /system/lib/hw
# ls

如下:

 

5、小結

本篇文章主要對HAL庫的編寫規則進行了簡單的描述,並簡單介紹了一個簡單的HAL例子實現過程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM