硬件抽象层(HAL)

上传人:wuy****ng 文档编号:245102572 上传时间:2024-10-07 格式:PPT 页数:33 大小:1.44MB
返回 下载 相关 举报
硬件抽象层(HAL)_第1页
第1页 / 共33页
硬件抽象层(HAL)_第2页
第2页 / 共33页
硬件抽象层(HAL)_第3页
第3页 / 共33页
点击查看更多>>
资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,第,9,讲,硬件抽象层(,HAL,),讲师:李宁,讲师 李宁,主要内容,什么是,HAL,为什么要在,Android,在添加,HAL,编写和测试基于,HAL,的,LED,驱动,讲师 李宁,什么是,HAL,HAL,(,Hardware Abstraction Layer,,硬件抽象层)是建立在,Linux,驱动之上的一套动态库。这套动态库并不属于,Linux,内核,而是属于,Linux,内核层之上的系统运行库层。,Google,为,Android,增加,HAL,的主要目的,除,尽量避免应用程序直接访问,Linux,驱动外,还有一个重要原因,那就是保护“私人财产”。对于那些既想发布基于,Android,的,Linux,驱动程序,又不想将核心业务逻辑公开的企业或个人,,HAL,简直就是福音。,讲师 李宁,为什么要在,Android,中加入,HAL,讲师 李宁,加入,HAL,的,主要,目的,统一硬,件的调用接口。由于,HAL,有标准的调用接口,所以可以利用,HAL,屏蔽,Linux,驱动复杂、不统一的接口。,解决了,GPL,版权问题。由于,Linux,内核基于,GPL,协议,而,Android,基于,Apache Licence 2.0,协议。因此,Google,玩了个“穿越”,将原本位于,Linux,内核中的,Linux,驱动的敏感代码向上移了一个层次。这样这些敏感代码就摆脱了,GPL,协议的束缚。那写不想开源的,Linux,驱动作者也就没必要开源了。,针对一些特殊的要求。对于有些硬件,,,可能,需要访问一些用户空间,的资源,或在内核空间不方便完成的工作以及特殊需求。在这种情况下,可以利用位于用户空间的,HAL,代码来辅助,Linux,驱动完成一些工作。,讲师 李宁,Android HAL,的旧,架构,讲师 李宁,Android HAL,的新,架构,讲师 李宁,Android HAL,程序库的路径,Android HAL,的源代码存储的位置并不固定。一般会存储在,/hardware,目录中。其中,/hardware/libhardware_legacy,用于保存旧的,HAL,架构的源代码。新,HAL,架构的源代码在,/hardware/libhardware,目录中,当然,这些源代码可以放在,/hardware,或其他的目录。最终编译生成的,.so,文件主要放在,Android,系统的,/system/lib/hw,目录,,,也可以放在其他的目录。,讲师 李宁,精简,LED,驱动,基于,HAL,的,LED,驱动,去掉了所有与读写寄存器规则相关的代码,只保留了创建设备文件已经与寄存器交互的代码(不涉及到任何规则,只是将数据简单地写入指定的寄存器,或从指定的寄存器读取数据)。,LED,驱动利用了设备文件的,read,和,write,函数来读写指定的寄存器。基本原理是只从指定寄存器读取或写入,5,个字节。第,1,个字节用于指定读写的动作以及寄存器。后,4,个字节是读写的实际的数据(因为,LED,驱动只涉及到操作一个,int,类型数据的寄存器,因此使用,4,个字节个来表示一个,int,类型的数据)。在与,LED,驱动交互是,只要向设备文件(,/dev/s3c6410_leds_hal,)读取或发送,5,个字节的数据,就可以读写指定的寄存器。,讲师 李宁,测试读写寄存器操作,由于,LED,驱动程序的设备文件接收的不是字符串,而是字节类型的数据(字节数组),因此需要单独做一个程序向设备文件写入字节形式的数据,或从设备文件中读取字节类型的数据。,命令行参数来传递设备文件名、字节数和要传递的字节等信息。命令行语法格式如下:,rwdev byte1 byte2 . byten,编译,rw_dev.c,程序,arm-gcc -static,-o /root/drivers/read_write_dev/rwdev /root/drivers/read_write_dev/rw_dev.c,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,1,),任何被系统自动调用的程序都会有一个标准的接口。这个接口相当与一个约定的规则。不管任何程序,只要遵循这个规则,就可以成功被调用。例如,,C,语言可执行程序都会有一个,main,函数,系统中执行程序是都会寻找,main,函数来执行;,Linux,驱动也有多个接口,最常用的就是,init,和,exit,函数,除此之外,还有与设备文件相关的,read,、,write,、,ioctl,等函数。只要,Linux,驱动程序安装接口的要求定义和实现,就可以成功安装在,Linux,驱动中。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,2,),既然,HAL,程序库也可以被,Android,系统自动调用,,那么,自然也拥有标准的接口。只不过这个接口不是函数,而是一个固定名称的结构体变量,HAL_MODULE_INFO_SYM,。也就是说,所有的,HAL,程序都必须要有一个,HAL_MODULE_INFO_SYM,变量,并且初始化该结构体变量的,common,成员变量。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,3,),第,1,步:,定义结构体和宏,编写,HAL,程序库需要使用到,3,个非常重要的结构体(,hw_module_t,、,hw_device_t,和,hw_module_methods_t,),在第,1,步需要定义两个新的结构体,这两个结构体的第,1,个变量的类型必须是,hw_module_t,和,hw_device_t,。一般还需要为,HAL,模块定义一个,ID,。实际上在这,1,步就是编写,leds_hal.h,头文件的代码。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,4,),typedef struct hw_module_t ,/*,模块的,Tag,,值必须是,HARDWARE_MODULE_TAG */,uint32_t tag;,/*,模块主版本号,*/,uint16_t version_major;,/*,模块从版本号,*/,uint16_t version_minor;,/*,模块的,ID,,通过该,ID,可以找到当前模块,*/,const char *id;,/*,模块名称,*/,const char *name;,/*,模块作者,*/,const char *author;,/*,与模块相关的函数指针,都包含着,hw_module_methods_t,结构体中,*/,struct hw_module_methods_t* methods;,/*,模块的,dso,,,dlopen,函数返回的,HAL,动态库的,handler */,void* dso;,/*,保留的空间,*/,uint32_t reserved32-7;, hw_module_t;,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,5,),描述硬件设备(或称为,HAL,设备)的结构体,hw_device_t,。,typedef struct hw_device_t ,/*,设备的,Tag,,值必须是,HARDWARE_DEVICE_TAG */,uint32_t tag;,/*,硬件设备的版本号,*/,uint32_t version;,/*,指向描述硬件模块的,hw_module_t,结构体指针,*/,struct hw_module_t* module;,/*,保留的内存空间,*/,uint32_t reserved12;,/*,关闭设备的函数指针,*/,int (*close)(struct hw_device_t* device);, hw_device_t;,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,6,),描述模块入口函数的结构体,hw_module_methods_t,。,typedef struct hw_module_methods_t ,/*,打开设备是调用的,open,函数的指针,*/,int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t* device);, hw_module_methods_t;,在这,3,个结构体中,,hw_module_t,是最先使用到的,然后通过,hw_module_t.methods,找到,hw_module_methods_t.open,函数,并调用该函数。这个,open,函数相当与,HAL,程序库的入口函数。一般会在这个函数里打开设备文件,初始化,hw_device_t,结构体,设置,一些控制硬件设备的函数。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,7,),在第,1,步先考虑,hw_module_t,和,hw_device_t,两个结构体。,HAL,规则建议不直接使用,hw_module_t,和,hw_device_t,(直接使用这,而要新定义两个结构体,将,hw_module_t,和,hw_device_t,分别作为新结构体的第,1,个变量的类型。就像,leds_hal.h,文件中的,led_module_t,和,led_control_device_t,。那么,HAL,为什么要这么建议呢,?,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,8,),在说明原因之前,先看一下,led_device_open,函数和,led_control_device_t,结构体。,static int led_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t* device), ,struct led_control_device_t,struct hw_device_t hw_device;,int (*set_on)(struct led_control_device_t *dev, int32_t led);,int (*set_off)(struct led_control_device_t *dev, int32_t led);,;,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,9,),led_device_open,函数将在,NDK,程序中被调用。该函数的最后,1,个参数类型是,hw_device_t*,,不过在调用该函数时,传进来的却是,led_control_device_t*,。从这一点看。,hw_device_t,相当与,led_control_device_t,的父类(,C,语言中并没有类的概念,这样解释只是便于理解,也可以称为其父结构体)。在调用,led_device_open,函数时将,led_control_device_t*,强行转换成了,hw_device_t*,。对于,C,语言来说,这样的转换要满足一个条件,就是做为父结构体(,hw_device_t,)的结构体必须是子结构体(,led_control_device_t,)的第,1,个变量的数据类型。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,10,),那么为什么要这样做强行转换呢?主要是因为扩展的需要。因为在,led_control_device_t,结构体中定义了两个函数指针变量(,set_on,和,set_off,)。这两个变量的名称和参数个数、参数类型是任意指定的。但为了使,HAL,程序库保持独立性(可能会被多个,NDK,模块使用,每个,NDK,模块使用了不同的,hw_device_t,和,hw_module_t,的子结构体),,HAL,程序库在的方法只能使用,hw_device_t,和,hw_module_t,作为参数类型。而如果不使用,hw_device_t,和,hw_module_t,的父类,就意味这无法添加这种设备函数指针(,set_on,和,set_off,)等成员。那么这个,HAL,程序库就成了中看不中用的“东西”了。光有逻辑代码,却无法向外部提供与其交互的接口。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,11,),由于,在,led_module_t,结构体除了,hw_module,变量外,没有任何其他的成员,因此,,HAL_MODULE_INFO_SYM,的类型可以直接定义成,hw_module_t,,代码如下:,struct hw_module_t HAL_MODULE_INFO_SYM =,tag: HARDWARE_MODULE_TAG,version_major: 1,version_minor: 0,id: LED_HARDWARE_MODULE_ID,name: Sample LED HAL Stub,author: Lining,methods: &led_module_methods,;,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,12,),第,2,步:编写打开设备的,open,函数,设备的打开函数是,HAL,模块的入口点。在本例中是,led_device_open,函数。设备打开函数主要做如下,3,项工作。,初始化,hw_device_t,的子结构体。除了设置也写必要的变量外,还需要设置操作硬件的函数指针(本例是,close,、,set_on,和,set_off,)。其中,set_on,和,set_off,在调用,HAL,模块的代码中要使用到。,close,有系统自动调用。在这一步与,close,、,set_on,和,set_off,惯量的函数还没有定义,可以先把函数名写上,或在定义了相关函数后再设置这些函数指针。,打开设备文件。,初始化寄存器。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,13,),第,3,步:定义,hw_module_methods_t,结构体变量,HAL,模块需要,hw_module_methods_t,结构体的,open,函数指针变量指定,open,入口函数。,第,4,步:定义,HAL_MODULE_INFO_SYM,变量,所,有,的,HAL,模块都必须有一个,HAL_MODULE_INFO_SYM,变量。该变量的类型一般为,hw_module_t,或其子结构体。该变量初始化了一些变量,其中,id,和,methods,成员最重要。,id,表示,HAL,模块中,Android,系统中的索引。使用,HAL,模块的程序并不是直接装载,.so,文件,而是通过这个,id,找到并装载,HAL,模块。,methods,变量的值需要设置在第,3,步,定义的,hw_module_methods_,t,结构体变量。当调用者通过,id,找到并装载,HAL,模块后,就会通过,methods,变量调用,open,入口函数来初始化设备。,讲师 李宁,调用,LED,驱动的,HAL,程序库,(,14,),第,5,步:编写卸载设备的,close,函数,当,HAL,模块被卸载后会调用,close,函数。在本例中是,led_device_close,函数。该函数需要在第,2,步的,led_device_open,函数中赋给,hw_device_t,的,close,成员变量。,第,6,步:编写控制设备的函数,根据设备类型和功能的不同,这一步编写的函数也有所不同。在本例中编写了两个控制函数(,led_on,和,led_off,),分别用来控制,LED,的开、关。,led_on,和,led_off,函数需要在第,2,步编写的,led_device_open,函数中赋给,led_control_device_t.set_on,和,led_control_device_t.set_off,变量。,讲师 李宁,编写调用,HAL,程序库的,Service,调用,HAL,程序库涉及到一个非常重要的,hw_get_module,函数。该函数可以通过在,leds_hal.h,中,定义的,LED_HARDWARE_MODULE_ID,宏(,led_hal,)查找,LED HAL,模块,并获得,led_module_t,结构体。然后调用,led_module_t.hw_module.methods.open,函数来初始化,LED,驱动。并通过,open,函数返回,led_control_device_t,结构体。在,led_control_device_t,结构体中包含了在,HAL,模块中定义的控制,LED,驱动的函数指针(,set_on,和,set_off,)。,为,Service,建立符号链接,ln -s /root/drivers/s3c6410_leds_hal/leds_hal_jni,/working/android2.3.4_src/frameworks/base/services/leds_hal_jni,讲师 李宁,HAL,的存放路径和命名规则,(,1,),HAL,程序库(,so,文件)通常存放在,/system/lib/hw,目录。文件名中一般都有一个,default,。例如,,led_hal.default.so,文件是,LED,驱动的,HAL,程序库。,那么,这个存放目录和文件名是我们的唯一选择吗?在回答这个问题之前,需要先查看一下调用,HAL,程序库的核心函数,hw_get_module,的代码,因为正是这个函数利用,HAL,模块的,ID,找到了,HAL,程序库的,.so,文件。所以一切的秘密都会在这个函数的揭开。,hw_get_module,函数位于,hardware.c,文件中。,hardware.c,文件的完整路径如下:,/working/android2.3.4_src/hardware/libhardware/hardware.c,讲师 李宁,HAL,的存放路径和命名规则,(,2,),在阅读完,hardware.c,文件的代码和注释后,可以得出如下结论,这些结论也恰好回到了本节开始提出的问题。,HAL,模块库文件的存放路径有两个:,/system/lib/hw,和,/vendor/lib/hw,。,hw_get_module,函数会先从,/system/lib/hw,目录根据库文件命名规则寻找库文件。如果,/system/lib/hw,目录没有库文件,,hw_get_module,会按同样的规则在,/vendor/lib/hw,目录中寻找。,HAL,模块库文件的命名规则是,ID.suffix.so,。其中,ID,功过,hw_get_module,函数的,id,参数指定。,suffix,(后缀)通过在属性文件中指定。,hw_get_module,会在,Android,系统的属性文件中根据,variant_keys,数组中定义的,4,个,key,依次查找,suffix,。如果未找到,suffix,,使用默认的,suffix,(,default,)。,讲师 李宁,HAL,的存放路径和命名规则,(,3,),在上面的几点多次提到了属性文件,那么这个属性文件到底是什么呢?实际上,,Android,系统的属性文件共有如下,4,个。,/,default.prop,/system/,build.prop,/system/,default.prop,/data/local.prop,讲师 李宁,HAL,的存放路径和命名规则,(,4,),Android,在启动时会自动装载这些属性文件。如果在多个属性文件中都定义了同一个,Key,和,Value,,那么只用第一个,Key,被获取。例如,在,/default.prop,文件中定义了,ro.product.board,的值为,abc,,而在,/system/build.prop,文件中定义了,ro.product.boardd,的值为,xyz,。那么,hw_get_module,函数会把,/default.prop,文件中的,abc,作为,HAL,模块库文件的后缀,而不会再读取,/system/build.prop,文件中的,xyz,。因此,,HAL,模块的库文件名是(假设,ID,是,led_hal,),led_hal.abc.so,。,讲师 李宁,HAL,的存放路径和命名规则,(,5,),4,个,属性文件,名在如下文件定义了,4,个宏。在以后的,Android,版本中有可能增加新的属性文件。,/working/android2.3.4_src/bionic/libc/include/sys/_system_properties.h,打开,_system_properties.h,文件后,就会在后面看到如下,4,个宏。,#define PROP_PATH_RAMDISK_DEFAULT /default.prop,#define PROP_PATH_SYSTEM_BUILD /system/build.prop,#define PROP_PATH_SYSTEM_DEFAULT /system/default.prop,#define PROP_PATH_LOCAL_OVERRIDE /data/local.prop,讲师 李宁,编写调用,Service,的,Java,库,(,1,),LedHalService ledHalService = LedHalService.getInstance();,ledHalService.setOn(0);/,打开第,1,个,LED,ledHalService.setOn(1);/,打开第,2,个,LED,ledHalService.setOff(2); /,关闭第,3,个,LED,ledHalService.setOn(3);/,打开第,4,个,LED,生成,LedHalService.jar,文件,cd /root/drivers/s3c6410_leds_hal/leds_hal_service/bin/classes,jar cvf ././LedHalService.jar ./mobile/android/leds/hal/service/LedHalService.class,讲师 李宁,编写调用,Service,的,Java,库,(,2,),任何,Android,应用程序只要引用,LedHalService.jar,文件就可控制,LED,。不过在运行,Android,应用程序之前需要在开发板上安装如下,3,个程序库。,LED,驱动:,/data/local/,s3c6410_leds_hal.ko,HAL,程序库:,/system/lib/hw/,led_hal.default.so,Service,程序库:,/system/lib/led_hal_jni.so,讲师 李宁,测试,LED,驱动,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!