android hal文件的编写

一、hal主体框架解析

hal:模板:hardware/libhardware/modules/overlay/
如图所示jni与hal层主要数据结构和函数调用:
android 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
android hal文件的编写
android hal文件的编写
所以即使jni文件引用了Android.mk里的hal动态库名,hal中仍然要规定一样的ID号;

五、代码编写步骤:

android hal文件的编写
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
上一篇:Java双端队列Deque


下一篇:Vivado远程编译并下载程序到本地xilinx开发板