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值)即可。


六)编译。

关于呼吸灯的C语言编程Options 1,0,0 // Target 'Target 1' Device (AT89C51) Vendor (Atmel) Cpu (IRAM(0-0x7F) IROM(0-0xFFF) CLOCK(24000000)) FlashUt () StupF ("LIB\STARTUP.A51" ("Standard 8051 Startup Code")) FlashDR () DevID (2976) Rgf (REGX51.H) Mem () C () A () RL () OH () DBC_IFX () DBC_CMS () DBC_AMS () DBC_LMS () UseEnv=0 EnvBin () EnvInc () EnvLib () EnvReg (Atmel\) OrgReg (Atmel\) TgStat=16 OutDir (.\) OutName (a9) GenApp=1 GenLib=0 GenHex=1 Debug=1 Browse=1 LstDir (.\) HexSel=0 MG32K=0 TGMORE=0 RunUsr 0 0 RunUsr 1 0 BrunUsr 0 0 BrunUsr 1 0 SVCSID MODEL5=0 RTOS5=0 ROMSZ5=2 DHOLD5=0 XHOLD5=0 T51FL=80 XT51FL=0 CBANKS5=0 XBANKS5=0 RCB51 { 0,0,0,0,0,0,0,1,0 } RXB51 { 0,0,0,0,0,0,0,0,0 } OCM51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } OCR51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } IRO51 { 1,0,0,0,0,0,16,0,0 } IRA51 { 0,0,0,0,0,128,0,0,0 } XRA51 { 0,0,0,0,0,0,0,0,0 } XRA512 { 0,0,0,0,0,0,0,0,0 } IROM512 { 0,0,0,0,0,0,0,0,0 } C51FL=21630224 C51VA=0 C51MSC () C51DEF () C51UDF () INCC5 () AX51FL=4 AX51MSC () AX51SET () AX51RST () INCA5 () PropFld { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } IncBld=1 AlwaysBuild=0 GenAsm=0 AsmAsm=0 PublicsOnly=0 StopCode=3 CustArgs () LibMods () BankNo=65535 LX51FL=292 LX51OVL () LX51MSC () LX51DWN () LX51LFI () LX51ASN () LX51RES () LX51CCL () LX51UCL () LX51CSC () LX51UCS () LX51COB () LX51XDB () LX51PDB () LX51BIB () LX51DAB () LX51IDB () LX51PRC () LX51STK () LX51COS () LX51XDS () LX51BIS () LX51DAS () LX51IDS () OPTDL (S8051.DLL)()(DP51.DLL)(-p51)(S8051.DLL)()(TP51.DLL)(-p51) OPTDBG 48125,-1,()()()()()()()()()() ()()()() FLASH1 { 0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0 } FLASH2 () FLASH3 () FLASH4 () EndOpt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值