随笔录:Android—HAL层(一)

一、什么是HAL层? 为什么需要用到HAL层?可以参考一下连接
答:https://blog.csdn.net/shift_wwx/article/details/49000305

2、由内核底层思考,当我们写好了驱动怎么向上提供给HAL层?
首先当我写好了对应的驱动模块,生成驱动文件和设备节点文件。之后怎么去使用上安卓的HAL?
开始之前我们看看 android/hardware/libhardware/include/hardware/hardware.h 文件 谷歌给出的定义和里面所包含的说明,看看里面具体说些什么?
摘要:


/ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM * and the fields of this data structure must begin with hw_module_t * followed by module specific information. */

typedef struct hw_module_t {

}hw_module_t;


/** * Every device data structure must begin with hw_device_t * followed by module specific public methods and attributes. */

typedef struct hw_device_t {

} hw_device_t;


typedef struct hw_module_methods_t {
/** Open a specific device /
int (open)(const struct hw_module_t module, const char
id,
struct hw_device_t** device);

} hw_module_methods_t;

目前只关注里面的三个结构体

hardware.h里面包含了三个结构体。这三个结构体就是传递设备节点操作的关键。
在写HAL层的代码关键的点是,一定要按照对应的HAL指定格式。
表明三个结构体:
	struct hw_module_t 				               //模块类型 
	struct hw_module_methods_t 		  //模块方法  	
	struct hw_device_t			                    //向上提供设备接口  

原型-模块结构

typedef struct hw_module_t {

/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;

/**
 * The API version of the implemented module. The module owner is
 * responsible for updating the version when a module interface has
 * changed.
 *
 * The derived modules such as gralloc and audio own and manage this field.
 * The module user must interpret the version field to decide whether or
 * not to inter-operate with the supplied module implementation.
 * For example, SurfaceFlinger is responsible for making sure that
 * it knows how to manage different versions of the gralloc-module API,
 * and AudioFlinger must know how to do the same for audio-module API.
 *
 * The module API version should include a major and a minor component.
 * For example, version 1.0 could be represented as 0x0100. This format
 * implies that versions 0x0100-0x01ff are all API-compatible.
 *
 * In the future, libhardware will expose a hw_get_module_version()
 * (or equivalent) function that will take minimum/maximum supported
 * versions as arguments and would be able to reject modules with
 * versions outside of the supplied range.
 */
uint16_t module_api_version;
#define version_major module_api_version
/**
 * version_major/version_minor defines are supplied here for temporary
 * source code compatibility. They will be removed in the next version.
 * ALL clients must convert to the new version format.
 */

/**
 * The API version of the HAL module interface. This is meant to
 * version the hw_module_t, hw_module_methods_t, and hw_device_t
 * structures and definitions.
 *
 * The HAL interface owns this field. Module users/implementations
 * must NOT rely on this value for version information.
 *
 * Presently, 0 is the only valid value.
 */
uint16_t hal_api_version;
#define version_minor hal_api_version

/** Identifier of module */
const char *id;

/** Name of this module */
const char *name;

/** Author/owner/implementor of the module */
const char *author;

/** Modules methods */
struct hw_module_methods_t* methods;

/** module's dso */
void* dso;

#ifdef __LP64__
uint64_t reserved[32-7];
#else
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
#endif

} hw_module_t;

原型 -模块方法结构

typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;

原型-设备模块(上报的设备模块)

typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;

/**
 * Version of the module-specific device API. This value is used by
 * the derived-module user to manage different device implementations.
 *
 * The module user is responsible for checking the module_api_version
 * and device version fields to ensure that the user is capable of
 * communicating with the specific module implementation.
 *
 * One module can support multiple devices with different versions. This
 * can be useful when a device interface changes in an incompatible way
 * but it is still necessary to support older implementations at the same
 * time. One such example is the Camera 2.0 API.
 *
 * This field is interpreted by the module user and is ignored by the
 * HAL interface itself.
 */
uint32_t version;

/** reference to the module this device belongs to */
struct hw_module_t* module;

/** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif

/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;

实现HAL层的大致步骤:

	a、首先定义一个模块名字必须是 HAL_MODULE_INFO_SYM,它的第一个成员必须是	struct hw_module_t 类型。
	b、填充	  struct hw_module_t 自身的成员,特别关注其中的成员 struct hw_module_methods_t* methods;  
	c. 实例化 struct hw_module_methods_t* methods ,初始化里面的函数指针(服务函数)
	d、自定义上层硬件接口结构,其中这个结构的第一个成员必须是struct hw_device_t 类型,在结构里面定义其他成员
	e、在第c步实例的服务函数里面。 将我们d步自定义上层硬件接口的结构进行实例化,获取对应的句柄
	f. 最后获取到的硬件接口结构句柄地址,给服务函数的形参hw_device_t**

①/硬件模块结构体/
在编写HAL硬件模块的时候,在hardware.h说明中要求每一个硬件模块的名字必须是 HAL_MODULE_INFO_SYM
并且这个结构体第一个成员必须是 struct hw_module_t 结构体成员,如:
/**
*** 按照规定实例化出来一个名字必须为 HAL_MODULE_INFO_SYM 的模块
*** 并且这个结构体第一个成员必须是 struct hw_module_t 结构体成员
*** struct hw_module_t commen具体成员说明在hardware.h 有说明
**/

typedef struct  Test_module_t {
			struct hw_module_t commen
		}
		
typedef struct Test_module_t HAL_MODULE_INFO_SYM {
	
				common  : {
				tag     : HARDWARE_MODULE_TAG,
				version_major : 1,
				version_minor : 0,
				id      : "test2",
				name    : "test2",
				author  : "javalong",
				methods : &Test_method          /* 重点关注这个对象 */ 
			  }
			}

**

②实现上面 hw_module_t commen结构里面的 methods 对象,为(struct hw_module_methods_t )类型的。 在实现methods对象的过程中发现,他还是一个结构体,里面成员为函数指针 (open)(const struct hw_module_t module, const char* id, struct hw_device_t** device),那么先将对应的函数 指针实现,在回充到methods对象,如:

**

static int Test_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
		/* .... */
		/* ...  */					
}  
 
struct hw_module_methods_t  Test_method = {  
											 open: Test_device_open 
										   };

③上面的服务函数内容还没有编写,单看函数名字知道是一个模块方法。难道我们期待已久的HAL上向层提供函数的地方?失望的是并没有那么简单,
上面的 Test_device_open()函数仅仅是一个模块初始化的作用,真正要向上层提供的是一个自定义结硬件接口结构体,但是要求该硬件接口结构体
的第一个成员必须是 struct hw_device_t ,其他成员可以自定义,如:

	/*硬件接口结构体*/  
		struct Text_device_t {  
			struct hw_device_t common;  
									       //以下成员是HAL对上层提供的接口或一些属性自行定义
			int fd;   
			int (*set_val)(struct Text_device_t* dev, int val);  
			int (*get_val)(struct Text_device_t* dev, int* val);  
		};  
	 int Test_set_val(struct Text_device_t* dev, int val)  {LOGI("hgello set_val\n"); return 0};
	 int Test_get_val(struct Text_device_t* dev, int* val) {LOGI("hgello get_val\n"); return 0};

④所以将上面的服务函数内容进行编写,在Test_device_open()进行模块初始化的时候将自定义硬件接口结构体实现,真正向HAL层提供一个操作
接口,让上层的函数可以真正地访问到内核驱动。

int Test_device_close(struct he_device_t * device) {LOGI("hgello Test_device_close \n"); close(dev->fd) ;return 0; }***


static int Test_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
		struct Test_device_t * dev;
		dev = (struct Test_device_t*)malloc(sizeof(struct Test_device_t)); 	
		if(!dev) { LOGI("request memory founder \n");   return -1;  } 
		
		memset(dev, 0, sizeof(struct Test_device_t));
		dev->commen.tag     = HARDWARE_DEVICE_TAG; 
		dev->commen.version = 0;  
		dev->commen.module  = (hw_module_t*)module;  
		dev->commen.close   = Test_device_close;
		//用户自定义的属性      
		dev->set_val = Test_set_val;  
		dev->get_val = Test_get_val; 
		dev->fd      = open("/dev/DevName","O_RDWR");
		if(dev->fd < 0) {  LOGI("open deviceFile founder :exit \n"); free(dev); return -1;} 
		*device = &(dev->commen);                                   
		return 0
}  	

问题一:在①中为什么实例出来的模块名字必须是 HAL_MODULE_INFO_SYM ?


	 回到hardware.h文件下查看到这是一个宏
	 #define HAL_MODULE_INFO_SYM  HMI 其中
	 
	 hmi = (structhw_module_t *)dlsym(handle, sym)
	 
	 这里是查找“HMI”这个导出符号,并获取其地址。不禁要问,为什么根据“HMI”这个
	 导出符号, 就可以从动态链接库中找到结构体hw_module_t呢?在 UNIX下 
	 
	 ELF = Executable and Linkable Format,
	 
	 可执行连接格式, 是UNIX系统实验室(USL)作为应用程序二进制接口	而开发和
	 发 布的, 扩展名为elf。一个ELF头在文件的开始,保存了路线图(road  map),
	 描 述 了该文件的组织情况。sections 保存着object 文件的信息,从连接 角
	 度看:包括指令,数据,符号表,重定位信息等等。随意找到一个  xxx.default
	 .so 的elf文件。
	    可使用 < readelf  xxx.default.so -s >查看随意的xxx.default.so文
     件 会发现21行的符号信息是HMI。通过查找可以找到我们所做好的动态库 所以这
     里HAL_MODULE_INFO_SYM变量必须为这个名字这样编译器才会将 这个结构体的导
     出符号变为“HMI”。
	    这样这个结构体才能被dlsym 函数找到!知道了andriod HAL模块也有一个通
     用的入口地址,这个入口地址就 是HAL_MODULE_INFO_SYM变量,通过它,我们可
     以访问到HAL模块中的所有想要外部访问到的方法。

问题二、在③为什么第一个成员必须是hw_device_t 类型的?
在hardware.h头文件中,还有明确要求定义xxx_module_t类型时,明确要求第一个成员变量类型必须
为hw_module_t,在参考:

	作者:罗升阳  
	原文:https://blog.csdn.net/luoshengyang/article/details/6575988 

	 /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
static inline int hello_device_open(const hw_module_t* module, \
struct hello_device_t** device) {

		return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, 
		(struct hw_device_t**)device);
		
	}
	
    /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
	static jboolean hello_init(JNIEnv* env, jclass clazz) {
		hello_module_t* module;
		LOGI("Hello JNI: initializing......");
		if(hw_get_module(HELLO_HARDWARE_MODULE_ID, \
		  (const struct hw_module_t**)&module) == 0) {
		
			LOGI("Hello JNI: hello Stub found.");
			if(hello_device_open(&(module->common), &hello_device) \
				 == 0)  {
								LOGI("Hello JNI: hello device is open.");
								return 0;
				
			}
			LOGE("Hello JNI: failed to open hello device.");
			return -1;
		}
		LOGE("Hello JNI: failed to get hello stub module.");
		return -1;		
	}
	里面写了,在.h文件里面可以获取对应的自定义结构,在自定义结构里面填充的只
	有&(dev->commen),明显看见在static inline int hello_device_open()
	这个函数中,返回值打开第三个参数是用自定义结构体强转成struct hw_device_t
	这就要求(struct hw_device_t **)必须是第一个成员,否则在上层进行强转
	是找不到(dev->commen),这个强转是没有意义的,
	。

问题三:在①中为什么第一个成员必须是 hw_module_t?
上面代码有这一段

		if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) 
		
	主要关注第三个形参,HAL_MODULE_INFO_SYM 成员 hw_module_t为什么强调第一
	个成员是 hw_module_t ,那么在使用hw_get_module获取的时候就没有意义了。
	hw_get_module是有HAL层提供给安卓框架层获取整个模块函数,通过JNI调用。
	想一下在我们的④里面一个上报问题,为什么我仅仅上报
	
		*device = &(dev->commen);   
		
	他是怎么得到我整个自定义硬件接口结构体(Text_device_t),
	这里就是答案,因为他将整个模块加载了,里面自然包含了自定义硬件结构体
   (Text_device_t)。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值