一、 预备知识及说明:
1、硬件抽象层模块向上层提供接口,以便用户空间的程序通过该模块提供的接口访问内核空间的驱动程序。
2、硬件抽象层模块是动态链接库文件(.so),它不属于内核层的,它是属于android系统Linux内核层的上层,因此它的源文件不能放在goldfish里。
3、硬件抽象层模块的头文件源代码放在 /android/hardware/libhardware/include/hardware目录下
实现文件源代码放在/android/hardware/libhardware/module/目录下
生成的动态链接库.so文件,放在/android/out/target/product/gerneric/system/lib/hw/目录下
4、本例的硬件抽象层模块,是作为前例中的 /dev/testdev这个字符设备驱动的硬件抽象层模块来实现的
二、在/android/hardware/libhardware/include/hardware目录下,添加test.h头文件
#ifndef ANDROID_TEST_INTERFACE_H #define ANDROID_TEST_INTERFACE_H #include <hardware/hardware.h> //扩充C语言在编译的时候按照C++编译器进行统一处理 __BEGIN_DECLS //按照硬件抽象层模块规范要求,定义模块ID, #define TEST_HARDWARE_MODULE_ID "test" //按照规范要求,定义硬件抽象层模块结构体,代表硬件抽象层模块 struct test_module_t { struct hw_module_t common; //硬件抽象层标准硬件模块结构体 }; //按照规范要求,定义硬件接口结构体,代表硬件设备 struct test_device_t { struct hw_device_t common; //标准的硬件接口 int fd; //设备文件描述符 //接口函数,供上层调用 int (*set_val)(struct test_device_t* dev, int val); int (*get_val)(struct test_device_t* dev, int* val); }; __END_DECLS #endif
说明:硬件抽象层模块头文件编写规范要求必须包含:模块ID、模块本身、模块接口三部分,具体如下:
1、硬件模块ID: XXX_HARDWARE_MODULE_ID
2、硬件模块结构体:xxx_module_t,结构体中必须包含硬件抽象层标准模块结构:hw_module_t
3、硬件设备结构体:xxx_device_t ,结构体中包含标准的硬件设备结构(hw_device_t )、硬件设备接口、硬件设备文件描述符
三、在/android/hardware/libhardware/module/目录下,创建test目录,添加test.c文件
test.c文件如下:
//hardware.h是HAL模块必须包含的头文件 #include <hardware/hardware.h> #include <fcntl.h> #include <errno.h> #include <cutils/log.h> #include <cutils/atomic.h> #include <hardware/test.h> //定义该模块在android系统日志中显示的标签为TestStub #define LOG_TAG "TestStub" //对应的硬件设备文件名 #define DEVICE_NAME "/dev/testdev" //本模块名 #define MODULE_NAME "Test" //模块开发者 #define MODULE_AUTHOR "tongruan" //设备打开和关闭接口 static int test_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device); static int test_device_close(struct hw_device_t* device); //设备访问接口 static int test_set_val(struct test_device_t* dev, int val); static int test_get_val(struct test_device_t* dev, int* val); //模块方法表: 列表中只列出了打开函数 static struct hw_module_methods_t test_module_methods = { open: test_device_open }; //模块实例变量:实例化模块,并赋初值 //变量名必须为HAL_MODULE_INFO_SYM,这是强制要求的,你要写Android的HAL就得遵循这个游戏规则 struct test_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, //硬件模块标签 version_major: 1, //版本号 version_minor: 0, id: TEST_HARDWARE_MODULE_ID, //模块ID,在头文件中定义 name: MODULE_NAME, //模块名 author: MODULE_AUTHOR, //模块作者 methods: &test_module_methods, //模块方法表 } };
且看下面模块方法表中唯一的一个函数test_device_open实现:
//设备打开初始化函数实现:添加向上层提供的接口函数 static int test_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { //硬件设备定义、分配内存空间、初始化 struct test_device_t* dev; dev = (struct test_device_t*)malloc(sizeof(struct test_device_t)); if(!dev){LOGE("TEST Stub: failed to alloc space"); return -EFAULT;} memset(dev, 0, sizeof(struct test_device_t)); //给硬件设备的成员变量(hw_device_t common)赋值 dev->common.tag = HARDWARE_DEVICE_TAG; //硬件设备标签 dev->common.version = 0; //版本号 dev->common.module = (hw_module_t*)module; //该硬件设备对应的模块 dev->common.close = test_device_close; //设备的关闭函数 //******原来硬件的接口函数在此指明*****// dev->set_val = test_set_val; dev->get_val = test_get_val; //硬件设备的文件描述符在此获取,同设备文件/dev/testdev在此关联在一起 if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { LOGE("Test Stub: failed to open /dev/testdev-- %s.", strerror(errno));free(dev); return -EFAULT; } //自定义设备test_device_t里的hw_device_t common的指针传给参数 *device = &(dev->common); LOGI("Test Stub: open /dev/testdev successfully."); return 0; }
回答:是经过模块初始化函数test_device_open!!!
那么下面,我们只要实现test_device_close、test_set_val、test_set_val,就完成了硬件抽象层模块的开发!
//关闭设备函数 static int test_device_close(struct hw_device_t* device) { struct test_device_t* test_device = (struct test_device_t*)device; if(test_device) { close(test_device->fd);//关闭设备文件,对应打开设备文件 free(test_device); //释放给硬件设备分配的空间 } return 0; } //写入接口函数 static int test_set_val(struct test_device_t* dev, int val) { LOGI("Test Stub: set value %d to device.", val); write(dev->fd, &val, sizeof(val)); return 0; } //读取接口函数 static int test_get_val(struct test_device_t* dev, int* val) { if(!val) { LOGE("Test Stub: error val pointer"); return -EFAULT; } read(dev->fd, val, sizeof(*val)); LOGI("Test Stub: get value %d from device", *val); return 0; }
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_PRELINK_MODULE := false LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := test.c LOCAL_MODULE := test.default include $(BUILD_SHARED_LIBRARY)
~/Android$. ./build/envsetup.sh
//用mmm编译工具编译