HAL层代码编写注意事项

基本架构

#include <hardware/hardware.h>
#include <fcntl.h>
#include <cutils/log.h>

//HAL规定不能直接使用hw_module_t结构体
//因此需要在hw_module_t外再套一层结构体,这也是HAL要求的
struct my_module_t
{
    //hw_module_t结构体表示HAL模块的基本信息,成员变量可以任意起,
    //但hw_module_t结构体必须是led_module_t结构体的第一个成员,因为可以直接拿地址进行类型转换
    struct hw_module_t hw_module;
}
//自定义的结构体,该结构体的第1个成员变量的类型必须是hw_ device_t
//在该结构体中还需要定义控制设备的函数指针

struct my_device_t{
    struct hw_device_t hw_device;
    int (*自己的A函数指针成员)(my_device_t dev,int32_t i);

}

//为HAL模块定义一个ID,需要通过这个ID来查找HAL模块
#define XXX_HARDWARE_MODULE_ID "xxx_hal"


int A函数(my_device_t dev,int32_t i){
    //……
}

static int 自己的open函数(const struct hw_module_t* module,const char* name,struct hw_device_t** device){
    struct my_device_t *dev;
    dev=(struct my_control_device_t*)malloc(sizeof(*dev));
    memset(dev,0,sizeof(*dev));
    dev->hw_device.tag=HARDWARE_DEVICE_TAG;
    //
    dev->hw_device.version=0;
    dev->hw_device.module=0;
    dev->hw_device.close=自己的close函数;
    dev->自己的A函数指针成员=A函数;
    *device=(hw_device_t*)dev;
    //一些初始化操作……
    return 0;
}


static struct hw_module_methods_t module_methods={
    .open= 自己的open函数,
}


struct my_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .module_api_version = 1,
    .hal_api_version = HARDWARE_HAL_API_VERSION,
    .id = XXX_HARDWARE_MODULE_ID,
    .name = "Example Hardware Module",
    .author = "Example Author",
    .methods = &module_methods,
};

hw_module_t是最先使用到的,然后通过hw_module_t.methods找到hw_module_methods_t.open函数,并调用该
函数。这个open函数相当于HAL模块的入口。一般会在这个函数里打开设备文件、初始化hw_device_t结构体以及一些控制硬件设备的函数

系统如何找到HAL_MODULE_INFO_SYM?

https://blog.csdn.net/badbayyj/article/details/115826477

Android.mk

编写完成后,还需要个Android.mk文件。
模板如下。

LOCAL PATH :$(call my-dir)
include $(SCLEAR VARS)

LOCAL_PRELINK_MODULE := faise
LOCAL_MODULE_PATH :=$(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES :=XXX_hal.c
LOCAL_MODULE :=XXX_hal.default
LOCAL_MODULE_TAGS :=eng
include $(BUILD_SHARED_LIBRARY)

使用mm编译后,将生成的.default.so文件push到板子的/system/lib/hw文件夹下即可使用,或者将其release到对应文件夹,打包生成image,烧录进去。

HAL Service

单单有.so库是不行的,还需要一个服务端来调用这个.default.so文件。
如果该Service是由JAVA调用的话,这个Service的对外接口要写成JNI函数。在安卓中的audio hal的server是采用一些IPC手段来编写的,并不是直接被JAVA调用。所以以下仅仅是个人笔记,可以不看。后续有机会会补充audio hal的结构。
JNI的模板如下:


#include<对应的hal.h>

//my_device_t结构体会通过open方法返回
struct my_device_t* my_hal_device=NULL;
//JNI函数
static jboolen 函数A(JNIEnv* env,jboject this,jint i)
{
    //如果未成功获取my_device_t,直接返回
    if(my_hal_device==Null)
    {
        return -1;
    }else
    {
        //调用HAL层函数
        return my_hal_device->自己的A函数指针成员(my_hal_device,i);
    }
}

static inline int my_hal_open(const struct hw_module_t* module,
                                struct my_device_t** device)
{
    //调用my_module_t.hw_module.mothods.open函数进行一些初始化工作
    //open函数会获取my_device_t结构体
    return module->methods->open(module,XXX_HARDWARE_MODULE_ID,
                                (struct hw_device_t**)device);
}
//JNI,在该方法中,会通过XXX_HARDWARE_MODULE_ID来找到对应的HAL模块。
//并通过my_hal_open来获取my_device_t结构体
static jboolean my_init(JNIEnv *env,jclass class)
{
    my_module_t* module;
    if(hw_get_module_open(&module->hw_module,&my_hal_device)==0)
    {
        return 0;
    }
}
//定义JNI函数的映射
static const JNINativeMethod methods[]=
{
    {"_init","()Z",(void*)my_init},
    {"_set_on"}//todo
    
}

//将JNI程序库与java类绑定
int register_my_hal_jni(JNIEnv* env)
{
    //必须由该类调用当前的JNI程序库
    static const char* const kClassName="mobile/";//todo
    jclass clazz;
    //获取HalService类的jclass对象
    clazz=env->FindClass(kClassName);
    if(clazz==NULL)
    {
        return -1;
    }
    //调用RegisterNatives方法将函数(到JAVA类中就成为方法了)注册到MyHalService类中
    //其中sizeof(methods)/sizeof(methods[0])是计算methods数组长度
    //RegisterNatives方法的定义:jint RegisterNatives(jclass clazz,const JNINativemethod*)
    //methods,jint nMethods)
    //最后一个参数nMethods表示要映射的函数(方法)数量,也就是methods数组长度
    if(env->RegisterNatives(clazz,methods,sizeof(methods)/sizeof(methods[0]))!=JNI_OK)
    {
        return -1;
    }
    return 0;//成功
}


//系统在成功装载JNI共享库后,会自动调用JNI_OnLoad函数,改函数一般用于初始化JNI模块
jint JNI_OnLoad(JavaVM* vm,void* reserved)
{
    JNIEnv *env=NULL;
    jint reselt=-1;
    //获取JNIEnv结构体的指针,(还可以在这里判断一下J2SE版本)
    //来说明当前模块对J2SE版本的要求。
    if(vm->GetEnv((void**)&env,JNI_VERSION_1_4)!=JNI_OK)
    {
        return -1;
    }
    //将java类与JNI函数绑定
    register_my_hal_jni(env);
    //返回JNI_VERSION_1_4,表明只能运行在对应版本以上java
    return JNI_VERSION_1_4;
}

HAL模块的存放路径和命名规则

HAL模块的存放路径和命名规则在Android系统中是经过精心设计的,以确保系统能够正确加载和使用这些模块。

存放路径

HAL模块的库文件(通常是.so文件)存放在以下两个路径之一:

  • /system/lib/hw
  • /vendor/lib/hw

hw_get_module函数会首先在/system/lib/hw目录中查找HAL模块的库文件。如果在该目录中未找到,则会继续在/vendor/lib/hw目录中查找。

命名规则

HAL模块库文件的命名规则是:ID.suffix.so

  • ID:通过hw_get_module函数的id参数指定。例如,led_hal。
  • suffix:后缀,通过属性文件指定。如果没有在属性文件中找到suffix,则使用默认的suffix,即default。
    例如,假设ID是led_hal,那么库文件名可能是:led_hal.default.so(默认情况)、led_hal.abc.so(如果属性文件中定义了abc作为suffix)

属性文件

Android系统的属性文件用于定义各种系统属性和配置。HAL模块的suffix就是通过这些属性文件来确定的。以下是Android系统中用于定义属性的四个主要文件:

  • /default.prop
  • /system/build.prop
  • /system/default.prop
  • /data/local.prop

Android在启动时会自动加载这些属性文件。如果在多个属性文件中定义了相同的Key和Value,那么只会使用第一个被读取的Key。例如:如果在/default.prop文件中定义了ro.product.board的值为abc,而在/system/build.prop文件中定义了ro.product.board的值为xyz,那么hw_get_module函数会将/default.prop文件中的abc作为HAL模块库文件的后缀,而不会再读取/system/build.prop文件中的xyz。因此,HAL模块的库文件名将是led_hal.abc.so。

属性文件的定义

上述四个属性文件的定义可以在以下文件中找到:/working/android2.3.4_src/bionic/libc/include/sys/_system_properties.h
打开_system_properties.h文件后,可以看到定义了四个宏,分别对应上述四个属性文件。

posted @   任侠平生愿  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示