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例子實現過程。