🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
每一个硬件抽象层模块在内核中都对应有一个驱动程序,硬件抽象层模块就是通过这些驱动程序来访问硬件设备的,它们是通过读写设备文件来进行通信的。 硬件抽象层中的模块接口源文件一般保存在hardware/libhardware目录中。为了方便起见,我们将虚拟硬件设备freg在硬件抽象层中的模块名称定义为freg,它的目录结构如下: ~~~ ~/Android/hardware/libhardware ----include ----hardware ----freg.h ----modules ----freg ----freg.cpp ----Android.mk ~~~ 它由三个文件组成,其中,freg.h和freg.cpp是源代码文件,而Android.mk是模块的编译脚本文件。下面我们就分别介绍这三个文件的内容。 **freg.h** ~~~ #ifndef ANDROID_FREG_INTERFACE_H #define ANDROID_FREG_INTERFACE_H #include <hardware/hardware.h> __BEGIN_DECLS /*定义模块ID*/ #define FREG_HARDWARE_MODULE_ID "freg" /*定义设备ID*/ #define FREG_HARDWARE_DEVICE_ID "freg" /*自定义模块结构体*/ struct freg_module_t { struct hw_module_t common; }; /*自定义设备结构体*/ 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 ~~~ 这个文件中的常量和结构体都是按照硬件抽象层模块编写规范来定义的。宏FREG_HARDWARE_MODULE_ID和FREG_HARDWARE_DEVICE_ID分别用来描述模块ID和设备ID。结构体freg_module_t用来描述自定义的模块结构体,它的第一个成员变量的类型为hw_module_t。结构体freg_device_t用来描述虚拟硬件设备freg,它的第一个成员变量的类型为freg_device_t。此外,结构体freg_device_t还定义了其他三个成员变量,其中,成员变量fd是一个文件描述符,用来描述打开的设备文件/dev/freg,成员变量set_val和get_val是函数指针,它们分别用来写和读虚拟硬件设备freg的寄存器val的内容。 **freg.cpp** 这是硬件抽象层模块freg的实现文件,我们分段来阅读。 文件首先包含相关头文件并且定义相关结构体变量。 ~~~ #define LOG_TAG "FregHALStub" #include <hardware/hardware.h> #include <hardware/freg.h> #include <fcntl.h> #include <errno.h> #include <cutils/log.h> #include <cutils/atomic.h> #define DEVICE_NAME "/dev/freg" #define MODULE_NAME "Freg" #define MODULE_AUTHOR "shyluo@gmail.com" /*设备打开和关闭接口*/ static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device); static int freg_device_close(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_module_methods_t freg_module_methods = { open: freg_device_open }; /*定义模块结构体变量*/ struct freg_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: FREG_HARDWARE_MODULE_ID, name: MODULE_NAME, author: MODULE_AUTHOR, methods: &freg_module_methods, } }; ~~~ 在这段代码中,最值得关注的就是模块变量HAL_MODULE_INFO_SYM的定义。按照硬件抽象层模块编写规范,每一个硬件抽象层模块必须导出一个名称为HAL_MODULE_INFO_SYM的符号,它指向一个自定义的硬件抽象层模块结构体,而且它的第一个类型为hw_module_t的成员变量的tag值必须设置为HARDWARE_MODULE_TAG。除此之外,还初始化了这个硬件抽象层模块结构体的版本号、ID、名称、作者和操作方法列表等。 虚拟硬件设备freg的打开和关闭分别由函数freg_device_open和freg_device_close来实现,如下所示。 ~~~ static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) { if(!strcmp(id, FREG_HARDWARE_DEVICE_ID)) { struct freg_device_t* dev; dev = (struct freg_device_t*)malloc(sizeof(struct freg_device_t)); if(!dev) { LOGE("Failed to alloc space for freg_device_t."); return -EFAULT; } memset(dev, 0, sizeof(struct freg_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (hw_module_t*)module; dev->common.close = freg_device_close; dev->set_val = freg_set_val; dev->get_val = freg_get_val; if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { LOGE("Failed to open device file /dev/freg -- %s.", strerror(errno)); free(dev); return -EFAULT; } *device = &(dev->common); LOGI("Open device file /dev/freg successfully."); return 0; } return -EFAULT; } static int freg_device_close(struct hw_device_t* device) { struct freg_device_t* freg_device = (struct freg_device_t*)device; if(freg_device) { close(freg_device->fd); free(freg_device); } return 0; } ~~~ 前面提到,一个硬件抽象层模块可能会包含多个硬件设备,而这些硬件设备的打开操作都是由函数freg_device_open来完成的,因此,函数freg_device_open会根据传进来的参数id来判断要打开哪一个硬件设备。 在硬件抽象层模块freg中,只有一个虚拟硬件设备freg,它使用结构体freg_device_t来描述。因此,函数freg_device_open发现参数id与虚拟硬件设备freg的ID值匹配以后,就会分配一个freg_device_t结构体,并且对它的成员变量进行初始化。按照硬件抽象层模块编写规范,硬件抽象层中的硬件设备标签(dev->common.tag)必须设置为HARDWARE_DEVICE_TAG。除此之外,我们还将虚拟硬件设备freg的关闭函数设置为freg_device_close,并且将它的读写函数设置为freg_get_val和freg_set_val。 初始化完成用来描述虚拟硬件设备freg的结构体freg_device_t之后,我们就可以调用open函数来打开虚拟硬件设备文件/dev/freg了,并且将得到的文件描述符保存在结构体freg_device_t的成员变量fd中。 虚拟硬件设备freg的关闭函数freg_device_close的实现比较简单,它主要是关闭设备文件/dev/freg,以及释放设备在打开时所分配的资源。 虚拟硬件设备freg的读写函数freg_get_val和freg_set_val的实现如下所示。 ~~~ static int freg_get_val(struct freg_device_t* dev, int* val) { if(!dev) { LOGE("Null dev pointer."); return -EFAULT; } if(!val) { LOGE("Null val pointer."); return -EFAULT; } read(dev->fd, val, sizeof(*val)); LOGI("Get value %d from device file /dev/freg.", *val); return 0; } static int freg_set_val(struct freg_device_t* dev, int val) { if(!dev) { LOGE("Null dev pointer."); return -EFAULT; } LOGI("Set value %d to device file /dev/freg.", val); write(dev->fd, &val, sizeof(val)); return 0; } ~~~ 这两个函数分别通过调用read和write函数来实现读写虚拟硬件设备freg的寄存器val的内容。 **Android.mk** ~~~ 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 := freg.cpp LOCAL_MODULE := freg.default include $(BUILD_SHARED_LIBRARY) ~~~ 这是硬件抽象层模块freg的编译脚本文件。第9行指定include命令的参数为$(BUILD_SHARED_LIBRARY),表示要将该硬件抽象层模块编译成一个动态链接库文件,名称为freg.default,并且保存在$(TARGET_OUT_SHARED_LIBRARIES)/hw目录下,即out/target/product/generic/system/lib/hw目录下。 > 注意:我们将硬件抽象层模块freg对应的文件名称定义为freg.default,编译成功后,系统就会自动在后 面加后缀.so,于是就得到了一个freg.default.so文件。根据硬件抽象层模块文件的命名规范,当我们要加载硬件抽象层模块freg时,只需要指定它的ID值,即“freg”,系统就会根据一定的规则成功地找到要加载的freg.default.so文件。 硬件抽象层模块freg的所有文件都准备好之后,我们就可以执行mmm和make snod命令对它进行编译和打包了。 ~~~ USER@MACHINE:~/Android$ mmm ./hardware/libhardware/modules/freg USER@MACHINE:~/Android$ make snod ~~~ 最终就可以在out/target/product/generic/system/lib/hw目录下得到一个freg.default.so文件。