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

    HAL(Hardware Abstraction Layer,硬件抽象层)是建立在Linux驱动上的一套程序库,这套程序库并不属于Linux内核,而属于Linux内核层之上的应用层。但是,HAL却可以看作是Android真正意义上的驱动层,Google增加HAL的主要目的是为了保护“私人财产”,编写于HAL层的代码并不用遵守Linux内核的GPL协议,也就避免了被公开。实际上,如果不考虑商业因素,仅仅对于开发者来说,HAL层的东西基本上都可以写入驱动层的。

    Android HAL的源代码存储位置并不固定,一般会存储在<Android源码根目录>/hardware目录中。其中hardware/libhardware_legacy目录存储的是旧HAL框架的源代码文件,新HAL框架的源代码放在hardware/libhardware目录中。而MTK的HAL层代码,依旧放在其自建的mediatek目录下面,为了体现大众性,这里我们还是加在hardware/libhardware/目录下。HAL层最终编译生成的.so文件主要放在Android系统的/system/lib/hw目录下。

    这里先介绍下HAL模块的编写步骤,HAL模块一般由一个.c文件和一个.h文件组成,本实验为hw_breath_leds.c和hw_breath_leds.h。

    第一步:定义结构体和代表HAL ID的宏

        编写HAL模块需要使用到3个非常重要的结构体(hw_module_t,hw_device_t和hw_module_methods_t,必须要用到)。首先要定义两个新的结构体,而且这两个结构体的第1个变量类型分别必须是hw_module_t和hw_device_t,以满足两个新结构体与hw_module_t和hw_device_t进行强制转换的条件。除此之外,还需要为HAL模块定义一个ID,上层会通过该ID与该HAL模块建立联系。实际上第一步就是编写.h文件的内容,本实验hw_breath_leds.h完整源码如下:


 
 
  1. #ifndef ANDROID_INCLUDE_HW_BREATH_LEDS_H
  2. #define ANDROID_INCLUDE_HW_BREATH_LEDS_H
  3. #include <hardware/hardware.h>
  4. __BEGIN_DECLS
  5. //定义模块id,jni层通过该id查找该模块
  6. #define BREATH_LEDS_HW_MODULE_ID "breath_leds"
  7. //定义硬件模块结构体,HAL规定不能直接使用hw_module_t结构体,需要在该结构体外再套一层结构体,但hw_module_t结构体
  8. //必须是该结构体的第一个成员变量数据类型,以方便两者之间的强制转换
  9. struct breath_leds_module_t
  10. {
  11. struct hw_module_t breath_module; //表示HAL模块的相关信息
  12. };
  13. //硬件接口结构体,同上,该结构体第一个成员变量数据类型必须是struct hw_device_t
  14. struct breath_leds_device_t
  15. {
  16. struct hw_device_t breath_device;
  17. int (*set_breath_value)( struct breath_leds_device_t *dev, int val); //对外接口
  18. };
  19. __END_DECLS
  20. #endif
为什么直接使用hw_module_t和hw_device_t结构体呢?这是HAL规范建议的,为了拓展的需要,需要向外提供交互的接口,如breath_leds_device_t结构体中的set_breath_value函数指针。

        下面看一看这3个重要结构体的定义代码。

        1  描述HAL模块的hw_module_t结构体


 
 
  1. typedef struct hw_module_t {
  2. /** 模块Tag,值必须是HARDWARE_MODULE_TAG */
  3. uint32_t tag;
  4. /** 模块主版本号 */
  5. uint16_t version_major;
  6. /** 模块从版本号 */
  7. uint16_t version_minor;
  8. /** 模块ID,通过该ID找到当前模块 */
  9. const char * id;
  10. /** 模块名称 */
  11. const char *name;
  12. /** 模块作者 */
  13. const char *author;
  14. /** 与模块相关的函数指针,都包含在hw_module_methods_t结构体中 */
  15. struct hw_module_methods_t* methods;
  16. /** 模块的dso */
  17. void *dso;
  18. /** 保留的空间 */
  19. uint32_t reserved[ 32 -7];
  20. } hw_module_t;

        2  描述HAL设备的hw_device_t结构体


 
 
  1. typedef struct hw_device_t {
  2. /** 设备Tag,值必须是HARDWARE_DEVICE_TAG */
  3. uint32_t tag;
  4. /** HAL设备版本号 */
  5. uint32_t version;
  6. /** 指向描述HAL模块的hw_module_t结构体指针 */
  7. struct hw_module_t* module;
  8. /** 保留的内存空间 */
  9. uint32_t reserved[ 12];
  10. /** 关闭设备的函数指针 */
  11. int (*close)( struct hw_device_t* device);
  12. } hw_device_t;
        3  描述模块入口函数的hw_module_methods_t结构体


 
 
  1. typedef struct hw_module_methods_t {
  2. /** 打开设备时调用的open函数指针 */
  3. int (*open)( const struct hw_module_t* module, const char* id, struct hw_device_t** device);
  4. } hw_module_methods_t;

    在这三个结构体中,hw_module_t是最先使用的,然后通过hw_module_t.methods找到hw_module_methods_t结构体,并调用hw_module_methods_t.open函数。这个open函数相当于HAL模块入口,一般在其中打开设备文件,初始化hw_device_t结构体等。

    第二步:定义HAL_MODULE_INFO_SYM变量

    所有的HAL模块都必须有一个HAL_MODULE_INFO_SYM变量,该变量类型为hw_module_t或包含hw_module_t且第一个变量类型是hw_module_t的结构体(如本实验的brearh_leds_module_t)。在定义HAL_MODULE_INFO_SYM变量的代码中一般会初始化hw_module_t或brearh_leds_module_t结构体的一些成员变量,其中id和methods特别重要。id表示HAL模块在Android系统中的标识,使用HAL模块的程序会通过该id找到并装载HAL模块,methods变量需要指向一个hw_module_methods_t结构体变量地址,当调用者通过id找到并装载HAL模块后,就会通过methods变量找到hw_module_methods_t结构体,并调用其open函数。如本例:


 
 
  1. struct breath_leds_module_t HAL_MODULE_INFO_SYM =
  2. {
  3. breath_module:
  4. {
  5. tag: HARDWARE_MODULE_TAG, //HAL初始化标志,必须为该值
  6. version_major: 1, //初始化HAL模块的主版本号
  7. version_minor: 0, //初始化HAL模块的此版本号
  8. id: BREATH_LEDS_HW_MODULE_ID, //jni通过匹配该id找到该HAL模块,该id在hw_breath_leds.h中定义
  9. name: HAL_NAME,
  10. author: HAL_AUTHOR,
  11. methods: &breath_module_methods, //初始化HAL模块open函数指针,hw_module_methods_t结构体变量
  12. }
  13. };
    第三步:定义上步中指定的hw_module_methods_t结构体变量

    HAL模块需要hw_module_methods_t结构体的open函数指针变量指定入口函数。如本例:


 
 
  1. static struct hw_module_methods_t breath_module_methods =
  2. {
  3. open: hw_breath_leds_open
  4. };
    第四步:编写HAL模块的open函数

    即编写上步指定的hw_breath_leds_open函数。在该函数中初始化hw_device_t或包含hw_device_t变量的结构体,打开设备文件等一些初始化工作。

    第五步:编写HAL模块的close函数

    即hw_device_t.close指针指向的函数,该函数需要在上步的open函数中通过hw_device_t.close变量指定才能调用。

    如上五步即为HAL模块的一般编写步骤,下面是hw_breath_leds.c的完整代码:


 
 
  1. #include <hardware/hw_breath_leds.h>
  2. #include <cutils/log.h>
  3. #include <fcntl.h>
  4. #define DEV_NAME "/dev/breath_leds"
  5. #define HAL_NAME "Breath Leds HAL Stub" //HAL模块名称
  6. #define HAL_AUTHOR "vip-wming"
  7. //设备文件句柄,open函数返回值,映射/dev/breath_leds设备文件
  8. static int fd = 0;
  9. //相当于对外接口的回调的函数
  10. static int hw_set_breath_value( struct breath_leds_device_t *dev, int val)
  11. {
  12. unsigned char buf[ 2] = { 0};
  13. buf[ 0] = val & 0xff; //bit0~7
  14. buf[ 1] = (val >> 8) & 0xff; //bit8~15
  15. write(fd, buf, 2); //将数据写入驱动设备
  16. return 0;
  17. }
  18. //关闭HAL设备
  19. static int hw_breath_device_close( struct hw_device_t* device)
  20. {
  21. //将hw_device_t类型强制转换成breath_leds_device_t类型,hw_device_t类型变量必须作为breath_leds_device_t第一个参数的原因
  22. struct breath_leds_device_t* dev = ( struct breath_leds_device_t*) device;
  23. if (dev)
  24. {
  25. free(dev); //释放设备
  26. }
  27. close(dev); //关闭设备
  28. return 0;
  29. }
  30. //打开设备入口函数
  31. static int hw_breath_leds_open( const struct hw_module_t* module, const char* name,
  32. struct hw_device_t** device)
  33. {
  34. struct breath_leds_device_t *dev;
  35. dev = ( struct breath_leds_device_t *)malloc( sizeof(*dev)); //分配空间
  36. memset(dev, 0, sizeof(*dev)); //为分配的空间清零
  37. dev->breath_device.tag = HARDWARE_DEVICE_TAG; //设置HAL设备标志
  38. dev->breath_device.version = 0; //设置HAL设备版本号
  39. dev->breath_device.module = ( struct hw_module_t*) module;
  40. dev->breath_device.close = hw_breath_device_close; //设值关闭HAL设备的函数指针,自定义该函数的实现
  41. dev->set_breath_value = hw_set_breath_value;
  42. *device = &(dev->breath_device); //将设置好的hw_device_t结构体传递给device参数指向的地址空间
  43. //打开breath_leds设备驱动文件,与相应驱动关联起来
  44. if((fd = open(DEV_NAME, O_RDWR)) < 0)
  45. {
  46. // LOGE("BREATH LEDS Stub: open /dev/breath_leds fail.\n");
  47. }
  48. return 0;
  49. }
  50. //描述模块入口函数的hw_module_methods_t结构体
  51. static struct hw_module_methods_t breath_module_methods =
  52. {
  53. open: hw_breath_leds_open
  54. };
  55. //初始化HAL模块信息,该结构体变量名必须为HAL_MODULE_INFO_SYM,HAL能被Android自动
  56. //调用,靠的就是该名,类似于每个C执行程序都有一个main函数一样
  57. struct breath_leds_module_t HAL_MODULE_INFO_SYM =
  58. {
  59. breath_module:
  60. {
  61. tag: HARDWARE_MODULE_TAG, //HAL初始化标志,必须为该值
  62. version_major: 1, //初始化HAL模块的主版本号
  63. version_minor: 0, //初始化HAL模块的此版本号
  64. id: BREATH_LEDS_HW_MODULE_ID, //jni通过匹配该id找到该HAL模块,该id在hw_breath_leds.h中定义
  65. name: HAL_NAME,
  66. author: HAL_AUTHOR,
  67. methods: &breath_module_methods, //初始化HAL模块open函数指针,即上面定义的hw_module_methods_t结构体变量
  68. }
  69. };
    下面记录下本实验HAL层详细开发过程:

    1)编写hw_breath_leds.h头文件

        进入hardware/libhardware/include/hardware/目录,新建hw_breath_leds.h文件,内容上面已展现。

    2)编写hw_breath_leds.c源代码

        进入hardware/libhardware/modules/目录,新建hw_breath_leds目录,进入该目录,新建hw_breath_leds.c,内容上面已展现。

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

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

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

/dev/breath_leds          0666   root       root
    4)编写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
    5)将该HAL模块添加进Android编译系统

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关于呼吸灯的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、付费专栏及课程。

余额充值