一、hal主体框架解析
hal:模板:hardware/libhardware/modules/overlay/
如图所示jni与hal层主要数据结构和函数调用:
首先jni层通过hw_get_module函数传入LED_MODULE_ID 即hal库的字符串名称,来找到hal库并加载它,加载后为pmodule赋值拿到led_hw_module_t 结构体实现。
然后再通过pmodule->common.methods->open函数给pdevice赋值,拿到led_hw_device_t的结构体实现。
通过拿到对应的库再调用库内部函数来拿到关键结构device。然后就可以对device进行操作了。
hal的开发主要是为了保护厂家私有代码而制定的一个架构。如果不考虑是否保护则直接用jni就可以了。
二、关键结构体源码:
hal:模板:hardware/libhardware/modules/overlay/
// 模块操作方法
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag; // 必须初始化成HARDWARE_MODULE_TAG
/** major version number for the module */
uint16_t version_major; // 主版本号: 1
/** minor version number of the module */
uint16_t version_minor; // 此版本号: 0
/** Identifier of module */
const char *id; // 动态库的文件名字
/** Name of this module */
const char *name; // 动态库的简单描述:自定义
/** Author/owner/implementor of the module */
const char *author; // 作者: 自定义
/** Modules methods */
struct hw_module_methods_t* methods; // 方法
/** module's dso */
void* dso; // 一般不用
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7]; // 保留,一般不用
} hw_module_t;
// device对象
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag; //必须初始化成:HARDWARE_DEVICE_TAG
/** version number for hw_device_t */
uint32_t version; // 主版本号:1
/** reference to the module this device belongs to */
struct hw_module_t* module; //指向device所在的module对象
/** padding reserved for future use */
uint32_t reserved[12];
/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;
分别对应上面的
1、device结构体包含device操作方式
struct led_hw_device_t{
struct hw_device_t common;// 必须是这样的
// 以下为扩展部分
int (*led_on)(void);
int (*led_off)(void);
};
2、给module结构体赋值;
// 必须有一个HMI变量
struct led_hw_module_t HMI ={
common : {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : LED_MODULE_ID,
name : "led hal sample",
author : "wukong",
3、给定module结构体中method的方法结构体
methods : &my_methods,
}
};
4、实现method方法结构体中的open方法;
struct hw_module_methods_t my_methods ={
open : led_hal_open
};
三、对应的android.mk的编写
hal : Android.mk 的编写;
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils
#指定目标文件应该安装到哪个路径上
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SRC_FILES := led_hal.c
#指定目标动态库文件名,要和代码中的id保持一致,加个.default后缀
LOCAL_MODULE := myled_hal.default
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
jni文件的编写:
jni调用hal的接口:
1, int hw_get_module(const char * id,const struct hw_module_t * * module)
参数1: 动态库文件名,LED_MODULE_ID
参数2: 执行模块的指针
返回值: 正确为0
2,调用module中的方法
pModule->common.methods->open();
编译jni文件的Android.mk的编写;
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#jni调用hal的时候,需要连接libhardware==>(hw_get_module)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_SRC_FILES:= led_jni.cpp
LOCAL_MODULE:= libled_jni
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
四、三个问题
1、为什么hal.so --> /system/lib/hw是放在这个目录下?
2、为什么必须有要将moudle定义为HMI变量
3、指定目标动态库文件名,要和代码中的id保持一致,为什么要加个.default后缀
LOCAL_MODULE := myled_hal.default
hw_get_module(LED_MODULE_ID, (const struct hw_module_t * * )&pModule);
|
snprintf(path, sizeof(path), "%s/%s.default.so","/system/lib/hw", "myled_hal");
==> "/system/lib/hw/myled_hal.default.so"
if (access(path, R_OK)) // 测试文件是否存在
status = load(id, path, module);
|// load(const char *id,const char *path, const struct hw_module_t **pHmi)
handle = dlopen(path, RTLD_NOW);
const char *sym = "HMI";
hmi = (struct hw_module_t *)dlsym(handle, sym);
*pHmi = hmi;
// 必须有一个HMI变量
struct led_hw_module_t HMI ={
common : {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : LED_MODULE_ID,
name : "led hal sample",
author : "wukong",
methods : &my_methods,
}
在hw_get_module中传ID;LED_MODULE_ID
在hw_get_module中传ID;LED_MODULE_ID
所以即使jni文件引用了Android.mk里的hal动态库名,hal中仍然要规定一样的ID号;
五、代码编写步骤:
1、定义两个结构体分别为module和device结构体
struct led_hw_module_t{
struct hw_module_t common; // 必须是这样的
// 以下为扩展部分
};
struct led_hw_device_t{
struct hw_device_t common;// 必须是这样的
// 以下为扩展部分
int (*led_on)(void);
int (*led_off)(void);
};
2、给module结构体赋值;
// 必须有一个HMI变量
struct led_hw_module_t HMI ={
common : {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : LED_MODULE_ID,
name : "led hal sample",
author : "wukong",
3、给定module结构体中method的方法结构体
methods : &my_methods,
}
};
4、实现method方法结构体中的open方法;
struct hw_module_methods_t my_methods ={
open : led_hal_open
};
5、匹配module结构体指针
int led_hal_open(const struct hw_module_t* module, const char* id,
struct hw_device_t** device)
{
LOGD("---^_^ ---%s--\n", __FUNCTION__);
6、 构建mydevice对象
struct led_hw_device_t *mydev = NULL;
mydev = (struct led_hw_device_t *)malloc(sizeof(struct led_hw_device_t));
mydev->common.tag = HARDWARE_DEVICE_TAG;
mydev->common.version = 1;
mydev->common.module = module; ==>这一步有什么作用?可将module结构体中的信息传递给device对象;
mydev->common.close = led_hal_close;
mydev->led_on = hal_led_on;
mydev->led_off = hal_led_off;
7、打开设备文件操作底层驱动
fd = open("/dev/led", O_RDWR);
if(fd < 0)
{
LOGE("open : %s\n", strerror(errno));
return -1;
}
8、将构建好的mydevice对象返回给device对象则device可以调用device中定义的方法;
*device = (struct hw_device_t*)mydev;
return 0;
}
9、具体构建device对象定义的方法;
int led_hal_close(struct hw_device_t* device)
{
由于open方法中malloc的是led_hw_device_t结构体指针所以要释放也是对应的指针
struct led_hw_device_t *tmp;
if(device != NULL)
{
在这里将传进来的device还原;
tmp = (struct led_hw_device_t *)device;
后free掉
free(tmp);
}
if(fd > 0)
close(fd);
return 0;
}
int hal_led_on(void)
{
LOGD("---^_^ ---%s--\n", __FUNCTION__);
int ret ;
ret = ioctl(fd,LED_ON,NULL);
if(ret < 0)
{
LOGE("ioctl : %s\n", strerror(errno));
return -1;
}
return 0;
}
int hal_led_off(void)
{
LOGD("---^_^ ---%s--\n", __FUNCTION__);
int ret ;
ret = ioctl(fd,LED_OFF,NULL);
if(ret < 0)
{
LOGE("ioctl : %s\n", strerror(errno));
return -1;
}
return 0;
}
在jni层通过实现hw_get_module方法
包含相同的头文件则调用的方法也相同;这也就成了对应的关系了
ret = hw_get_module(LED_MODULE_ID, (const struct hw_module_t * * )&pModule);
if(ret == 0)
{
LOGD("hw_get_module ok");
if(pModule != NULL)
{
可以理解为open之后device对象就有了匹配方法了
pModule->common.methods->open((const struct hw_module_t*)pModule, NULL, (struct hw_device_t**)&pDevice);
}
}
即pDevice->led_off(); pDevice->led_off(); pDevice->common.close((struct hw_device_t *)pDevice);
jni中通过module的LED_MODULE_ID定义的字符串匹配hal层的模块匹配到后再调用hal层中的open方法调用设备方法;
其中的null可传入具体的设备号,如果有多个设备的话,即一个模块中集成了多个设备;
有多个id是否有多个open函数;
jni 中的Adroid.mk文件的编写;
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware ==>#jni调用hal的时候,需要连接libhardware==>(hw_get_module)
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_SRC_FILES:= led_jni.cpp
LOCAL_MODULE:= libled_jni
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
hal 文件的Android.mk的编写;
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SRC_FILES := led_hal.c
LOCAL_MODULE := myled_hal.default
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
库在文件系统中的摆放;
apk--> /system/app
jni.so --> /system/lib
cp -raf out/target/product/fs100/system/lib/libled_jni.so /opt/fs100_root/system/lib
hal.so --> /system/lib/hw
cp -raf out/target/product/fs100/system/lib/hw/myled_hal.default.so /opt/fs100_root/system/lib/hw