MT6572平台加入呼吸灯功能——编写HAL模块

    HAL即硬件抽象层,是Google为满足那些不想开源的Linux驱动开发者的要求在Android系统加入的一个层次结构。HAL的源代码存储位置并不固定,一般会存储在hardware/libhardware/目录中,最终编译生成的.so文件主要放在system/lib/hw目录。当然MTK自己的hal模块都放在mediatek/目录下,为了体现大众性,这里我们还是加在hardware/libhardware/目录下。下面为详细添加方法:

一)编写hw_breath_leds.h头文件

    进入hardware/libhardware/include/hardware/目录,新建hw_breath_leds.h文件:

#ifndef ANDROID_INCLUDE_HW_BREATH_LEDS_H
#define ANDROID_INCLUDE_HW_BREATH_LEDS_H

#include <hardware/hardware.h>

__BEGIN_DECLS

//定义模块id,jni层通过该id查找该模块
#define BREATH_LEDS_HW_MODULE_ID  "breath_leds"

//定义硬件模块结构体,HAL规定不能直接使用hw_module_t结构体,需要在该结构体外再套一层结构体,但hw_module_t结构体
//必须是该结构体的第一个成员变量数据类型,以方便两者之间的强制转换
struct breath_leds_module_t
{
    struct hw_module_t breath_module;  //表示HAL模块的相关信息
};

//硬件接口结构体,同上,该结构体第一个成员变量数据类型必须是struct hw_device_t
struct breath_leds_device_t
{
    struct hw_device_t breath_device;

    int (*set_breath_value)(struct breath_leds_device_t *dev, int val);  //对外接口
};

__END_DECLS

#endif
如上为编写HAL模块.h文件的标准格式。编写HAL模块需要使用到3个非常重要的结构体(hw_module_t, hw_device_t和hw_module_methods_t),首先需要定义两个新的结构体,这两个结构体的第一个变量的数据类型必须是hw_module_t和hw_device_t,除此之外,还要为HAL模块定义一个ID,JNI层可通过该ID找到该HAL模块,基本上这就是.h文件的内容。具体为什么要这样定义结构体,上述代码中已有注释。

    既然提到这三个重要的结构体,我们先说一下调用这三个结构体的先后顺序:hw_modult_t是最先使用的,然后通过hw_modult_t.methods找到hw_module_methods_t.open函数,并调用该函数,这个open函数相当于HAL模块的入口,一般在该函数中打开设备文件(如/dev/breath_leds),初始化hw_device_t结构体以及一些接口控制函数。对外接口控制函数一般包含hw_device_t结构体成员变量的结构体中声明(如上breath_leds_device_t)。


二)编写hw_breath_leds.c源代码

    进入hardware/libhardware/modules/目录,新建hw_breath_leds目录,进入该目录,新建hw_breath_leds.c:

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

#define DEV_NAME "/dev/breath_leds"
#define HAL_NAME "Breath Leds HAL Stub"   //HAL模块名称
#define HAL_AUTHOR  "vip-wming"

//设备文件句柄,open函数返回值,映射/dev/breath_leds设备文件
static int fd = 0;

//相当于对外接口的回调的函数
static int hw_set_breath_value(struct breath_leds_device_t *dev, int val)
{
    unsigned char buf[2] = {0};
    
    buf[0] = val & 0xff;   //bit0~7
    buf[1] = (val >> 8) & 0xff;  //bit8~15
    write(fd, buf, 2);  //将数据写入驱动设备

    return 0;
}

//关闭HAL设备
static int hw_breath_device_close(struct hw_device_t* device)
{
    //将hw_device_t类型强制转换成breath_leds_device_t类型,hw_device_t类型变量必须作为breath_leds_device_t第一个参数的原因
    struct breath_leds_device_t* dev = (struct breath_leds_device_t*) device;
    if (dev)
    {
        free(dev); //释放设备
    }
    close(dev);  //关闭设备
    return 0;
}

//打开设备入口函数
static int hw_breath_leds_open(const struct hw_module_t* module, const char* name,
                    struct hw_device_t** device)
{
    struct breath_leds_device_t *dev;
    dev = (struct breath_leds_device_t *)malloc(sizeof(*dev)); //分配空间
    memset(dev, 0, sizeof(*dev));  //为分配的空间清零

    dev->breath_device.tag = HARDWARE_DEVICE_TAG; //设置HAL设备标志
    dev->breath_device.version = 0;     //设置HAL设备版本号
    dev->breath_device.module = (struct hw_module_t*) module;
    dev->breath_device.close = hw_breath_device_close;  //设值关闭HAL设备的函数指针,自定义该函数的实现
    dev->set_breath_value = hw_set_breath_value;
    
    *device = &(dev->breath_device); //将设置好的hw_device_t结构体传递给device参数指向的地址空间

    //打开breath_leds设备驱动文件,与相应驱动关联起来
    if((fd = open(DEV_NAME, O_RDWR)) < 0)
    {
       // LOGE("BREATH LEDS Stub: open /dev/breath_leds fail.\n");
    }

    return 0;
}

//描述模块入口函数的hw_module_methods_t结构体
static struct hw_module_methods_t breath_module_methods =
{
    open: hw_breath_leds_open
};

//初始化HAL模块信息,该结构体变量名必须为HAL_MODULE_INFO_SYM,HAL能被Android自动
//调用,靠的就是该名,类似于每个C执行程序都有一个main函数一样
struct breath_leds_module_t HAL_MODULE_INFO_SYM =
{
    breath_module:
    {
        tag: HARDWARE_MODULE_TAG,  //HAL初始化标志,必须为该值
        version_major: 1,          //初始化HAL模块的主版本号
        version_minor: 0,          //初始化HAL模块的此版本号
        id: BREATH_LEDS_HW_MODULE_ID, //jni通过匹配该id找到该HAL模块,该id在hw_breath_leds.h中定义
        name: HAL_NAME,
        author: HAL_AUTHOR,
        methods: &breath_module_methods,  //初始化HAL模块open函数指针,即上面定义的hw_module_methods_t结构体变量
    }
};
总结一下编写HAL模块代码的步骤:

  1,定义结构体和代表HAL ID的宏,即第一步.h文件的内容;

  2,编写HAL模块的open函数,在里面初始化hw_device_t,打开设备文件;

  3,定义hw_module_methods_t结构体变量,指定open函数指针;

  4,定义HAL_MODULE_INFO_SYM变量,所有的HAL模块必须有一个HAL_MODULE_INFO_SYM变量,该变量的类型一般为.h中定义的包含hw_method_t成员变量的结构体(或者直接定义成hw_method_t结构体变量也可以),在定义HAL_MODULE_INFO_SYM变量的代码中一般会初始化一些所含结构体的成员变量,其中id和methods最重要;

  5,编写HAL模块的close函数;

  6,编写对外接口控制函数实体。

所以,编写HAL模块代码框架已经形成一套规范,往框架里面填内容即可。


三)打开设备文件读写权限

    由于设备文件“/dev/breath_leds”是在内核驱动里面通过device_create创建的,该函数创建的设备文件默认只有root用户可读写,上层APP一般不具备root权限,调用open函数打开时就会导致打开设备文件失败。

    解决办法,进入system/core/rootdir目录,打开ueventd.rc,添加:

/dev/breath_leds          0666   root       root


四)编写Android.mk
    在hardware/libhardware/modules/hw_breath_leds目录下新建Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := hw_breath_leds.c
#注意LOCAL_MODULE的定义规则,后面跟有default能保证我们的模块总能被硬件抽象层加载到
LOCAL_MODULE := breath_leds.default
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false

include $(BUILD_SHARED_LIBRARY)
然后打开hardware/libhardware/modules/Android.mk,将新建的目录名添加进hardware_modules变量:

hardware_modules := gralloc hwcomposer audio nfc local_time power usbaudio audio_remote_submix hw_breath_leds


五)将该HAL模块添加进Android编译系统

    完成上述操作后,可以进行模块编译并手动打包进system.img,但是new时却不能被自动编译进去,打开build/target/product/common.mk,在PRODUCT_PACKAGES中加入breath_leds.default(模块Android.mk中的LOCAL_MODULE值)即可。


六)编译。

你可能感兴趣的:(MT6572平台加入呼吸灯功能——编写HAL模块)