在安卓系统中,会提供hal层供上层应用程序访问驱动,下面为一个简单的例子

一.HAL层概述

1.1 主要数据结构

在hal层,会用到以下三个结构体,路径为:/hardware/libhardware/include/hardware/hardware.h

(1)struct hw_module_t:每一个特定的模块需继承这个结构体

typedef struct hw_module_t {
    uint32_t tag; 				/*tag must be initialized to HARDWARE_MODULE_TAG */
    uint16_t module_api_version;/*模块版本号*/
    uint16_t hal_api_version;   /*hal api版本号*/
    const char *id; 			/*Identifier of module */
    const char *name;			/* Name of this module */
    const char *author;         /*Author/owner/implementor of the module */
    struct hw_module_methods_t* methods; /*Modules methods,对应下一个结构体hw_module_methods_t */
    /* 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;

(2)struct hw_module_methods_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;

(3)struct hw_device_t:每一个特定的device都需要继承这个结构体

typedef struct hw_device_t {
    uint32_t tag;                /*tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t version;            /*device版本号*/
    struct hw_module_t* module;  /*reference to the module this device belongs to,指向这个设备的模块 */
#ifdef __LP64__                  /*padding reserved for future use */
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif
    int (*close)(struct hw_device_t* device);/*close the device*/
} hw_device_t;

1.2 上层应用程序访问HAL层的步骤

(1)使用hw_get_module()方法获取hal层对应的module,

(2) 在hw_module_t结构体中的methods属性指向一个hw_module_methods_t结构体,该结构体提供一个open方法来打开模块,在open的参数中会传入一个hw_device_t**的数据结构,将module与device连接,这样子就可使用hw_device_t中的函数

1.3 上层应用程序访问HAL层伪代码示例

具体的说,我们在上层可以这样调用HAL层的module:

(1)获得对应ID的 hw_module_t结构体。

xxx_module_t *module;//通过特定ID获取特定的module
	-->hw_get_module(XXXID,(struct hw_module_t **)&module);

(2)获得hw_device_t结构体,通过hw_device_t结构体访问HAL层对应的module了。

struct xxx_device_t *device;
	-->module->methods->open(module,XXXID,(struct hw_device_t **)&device);/*这里的module为上一步的module*/
		-->device->common.module = (hw_module_t*)module;//device里面的module指向

2.实现HAL层对底层驱动的访问

在asop源码目录下vendor/xxx/hardware/libhardware/modules新建hellotest目录,添加两个文件:hellotest.c 和 Android.mk

2.1 hellotest.c

/******************************************************************************
* @file    	hellotest.c
* @brief   	测试hellotest驱动
* @note    	实现驱动访问相关函数
* @author 	klz
* @version 	1.0.0
* @date    	2019-12-16
* 修改记录:
*******************************************************************************/

/******************************************************************************
								  头文件
*******************************************************************************/
#include <hardware/hardware.h>
#include <hardware/hellotest.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>

/******************************************************************************
                                宏定义
*******************************************************************************/

#define DEVICE_NAME 	"/dev/hello"
#define MODULE_NAME		"HelloTest"
#define	MODULE_AUTHOR   "klz"

/* 定义LOG */
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO  , LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN  , LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , LOG_TAG, __VA_ARGS__)

#define LOG_TAG "klz"

/******************************************************************************
                              变量与数据结构等定义
*******************************************************************************/
static int hellotest_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int hellotest_device_close(struct hw_device_t *device);
static int hellotest_write_string(struct hellotest_device_t *dev,char *str);
static int hellotest_read_string(struct hellotest_device_t *dev,char **str,int len);


//模块方法结构体
static struct hw_module_methods_t hellotest_module_methods = {
	.open = hellotest_device_open,
};

//模块实例变量
struct hellotest_module_t HAL_MODULE_INFO_SYM = {
	  .common = {
        .tag                    = HARDWARE_MODULE_TAG,
        .module_api_version     = HELLOTEST_HARDWARE_MODULE_API_VERSION_1_0,
        .hal_api_version        = HARDWARE_HAL_API_VERSION,
        .id                     = HELLOTEST_HARDWARE_MODULE_ID,
        .name                   = "hello test",
        .author                 = "The Android Open Source Project",
        .methods                = &hellotest_module_methods,
    }
};

/******************************************************************************
                              函数定义
*******************************************************************************/

/*******************************************************************************
* @name   	hellotest_device_open
* @brief  	打开驱动
* @param  	const struct hw_module_t* module  [IN] 打开的模块
* @param  	const char* name     			  [IN] 模块ID
* @param  	struct hw_device_t** device       [IN] 从open函数中获取到的hellotest_device_t
* @return 	int                               [OUT] 函数的返回值,0:EOK,-1:Error
*******************************************************************************/
static int hellotest_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
	struct hellotest_device_t *dev;
	dev = (struct hellotest_device_t*)malloc(sizeof(struct hellotest_device_t));
	
	if(!dev)
	{
		LOGE("%s,failed to alloc space\n",__FUNCTION__);
		return -EFAULT;
	}
	
	memset(dev,0,sizeof(struct hellotest_device_t));
	
	dev->common.tag = HARDWARE_DEVICE_TAG;
	dev->common.version = 0;
	dev->common.module = (hw_module_t*)module;
	dev->common.close = hellotest_device_close;  
	dev->write_string = hellotest_write_string;
	dev->read_string = hellotest_read_string;
	
	if((dev->fd = open(DEVICE_NAME,O_RDWR)) == -1)
	{
		LOGE("%s,open %s failed\n",__func__,DEVICE_NAME);
		return -EFAULT;
	}
	
	*device = &(dev->common);
	 LOGI("HelloTest: open /dev/hello successfully."); 
	
	return 0;
}

/*******************************************************************************
* @name    	hellotest_device_close
* @brief   	关闭驱动
* @param   	struct hw_device_t** device       [IN] 从open函数中获取到的hellotest_device_t
* @return  	int                               [OUT] 函数的返回值,0:EOK,-1:Error
*******************************************************************************/
static int hellotest_device_close(struct hw_device_t *device)
{
	LOGI("%s\n");
	struct hellotest_device_t *hello_device = (struct hellotest_device_t *)device;
	
	if(hello_device)
	{
		close(hello_device->fd);
		free(hello_device);
	}
	
	return 0;
}

/*******************************************************************************
* @name   	 hellotest_write_string
* @brief  	 向驱动中写入字符串
* @param  	 struct hw_device_t* device      [IN] 要写入的hw_device
* @param  	 char *str                       [IN] 写入的字符串
* @return 	 int                             [OUT] 函数的返回值,0:EOK,-1:Error
*******************************************************************************/
static int hellotest_write_string(struct hellotest_device_t *dev,char *str)
{
	LOGI("%s\n",__func__);
	write(dev->fd,str,sizeof(str));
	return 0;
}

/*******************************************************************************
* @name   	 hellotest_read_string
* @brief  	 从驱动中读取字符串
* @param  	 struct hw_device_t*device	 [IN] 要读取的hw_device
* @param  	 char **str                  [IN] 读取的字符串
* @return 	 int                         [OUT] 函数的返回值,0:EOK,-1:Error
*******************************************************************************/
static int hellotest_read_string(struct hellotest_device_t *dev,char **str,int len)
{
	LOGI("%s,sizeof(*str)=%d,len=%d\n",__func__,sizeof(*str),len);
	read(dev->fd,*str,len);
	return 0;
}

经过上面操作我们可以实现hal层中hw_device_t结构体中的方法操作Linux驱动

2.2 Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hellotest.default
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_SRC_FILES := hellotest.c
LOCAL_C_INCLUDES := hardware/libhardware/include
LOCAL_HEADER_LIBRARIES := libhardware_headers
LOCAL_SHARED_LIBRARIES := liblog libcutils libutils
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-implicit-function-declaration
include $(BUILD_SHARED_LIBRARY)

注意:LOCAL_MODULE 的值为hellotest.default,hellotest一定要加上default,不然使用hw_get_module函数找不到我们的HAL模块。

2.3 hellotest.h

vendor/xxx/hardware/libhardware/include/hardware编写头文件hellotest.h

/******************************************************************************
* 文 件 名: hellotest.h  
* 功能描述: 测试hello驱动
* 作    者: klz 
* 版 本 号: 1.0.0 
* 修改日期: 2021-5-19
* 修改记录:
*******************************************************************************/

#ifndef ANDROID_HELLO_TEST_H
#define ANDROID_HELLO_TEST_H
#include <hardware/hardware.h> 

__BEGIN_DECLS

/******************************************************************************
                                宏定义
*******************************************************************************/
#define HELLOTEST_HARDWARE_MODULE_ID "hellotest" //ID值,必须,用于上层获取该模块
#define HELLOTEST_HARDWARE_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1,0) //API版本
  
/******************************************************************************
                              变量与数据结构等定义
*******************************************************************************/

struct hellotest_module_t {
	struct hw_module_t common;
};

struct hellotest_device_t {//硬件接口结构体
	struct hw_device_t common;
	int fd;
	int (*write_string)(struct hellotest_device_t *dev,char *str);
	int (*read_string)(struct hellotest_device_t *dev,char **str,int len);
};

__END_DECLS

#endif

2.4 编译HAL模块

直接进入到vendor/xxx/hardware/libhardware/modules/hellotest目录,执行mm命令进行编译,生成物路径为

out\target\product\xxx\vendor\lib64\hw\

3.编写测试代码

vendor/xxxhardware/libhardware/tests/目录下新建一个hellotest目录,新增test.c和Android.mk文件:

3.1 test.c

#include <hardware/hardware.h>  
#include <hardware/hellotest.h>  
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

const struct hw_module_t * module;
const struct hw_device_t * device;

int main(){
    char *read_str = (char*)malloc(100*sizeof(char));
    char *write_str="nihao";
   
    //1.获取module
    if(hw_get_module(HELLOTEST_HARDWARE_MODULE_ID,&module)==0){
        printf("get module sucess\n");
    } else {
        printf("get module fail\n");
        return -1;
    }

    //module与device关联
    if(module->methods->open(module,HELLOTEST_HARDWARE_MODULE_ID,&device)==0){
        printf("open module sucess\n");
    }else{
        printf("open module error\n");
        return -1;
    }

    struct hellotest_device_t* dev = (struct hellotest_device_t *)device;
    dev->read_string(dev,&read_str,100);
    if(read_str == NULL){
        printf("read error");
    }else{
        printf("read data: %s\n",read_str);
    }

    dev->write_string(dev,write_str);
    printf("write data: %s\n",write_str);
   
    dev->read_string(dev,&read_str,100);
    if(read_str == NULL){
        printf("read error");
    }else{
        printf("read data: %s\n",read_str);
    }
   
return 0;
}

3.2 测试代码调用流程

(1)使用hw_get_module函数获得hw_module_t结构体

(2)使用hw_module_methods_t结构体中的open方法获得hw_device_t结构体,

(3)使用hw_device_t结构体中的read_string和write_string方法与linux驱动交互,驱动的打开是在hw_module_methods_t结构体中的open方法中做的。

3.3 Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := hellotest
LOCAL_LDLIBS:=  -lhardware
LOCAL_C_INCLUDES := hardware/libhardware/include
			
LOCAL_SRC_FILES := $(call all-subdir-c-files)

include $(BUILD_EXECUTABLE)

进入到vendor/xxx/hardware/libhardware/tests/hellotest,所在的目录执行mm命令进行编译,编译完成,生成物的路径为

out/target/product/xxx/system/bin/my_test

4.测试

(1)把生成的hellotest.default.so文件push到android设备的/vendor/lib64/hw目录下

adb push out\target\product\xxx\vendor\lib64\hw\hellotest.default.so vendor/lib64/hw

(2)修改hellotest.default.so文件的的权限为777,执行chmod 777 /system/lib/dw/hellotest.default.so

(3)烧写上一节的boot.img

(4)把生成的my_test可执行文件push到android设备上,建议push到data目录下

adb push my_test /data

(5)给my_test文件添加可执行权限su chmod +x mytest

(6)执行mytest

./mytest

应用程序打印log为:

get module sucess
open module sucess
read data: hello my first driver
write data: nihao
read data: hello my first driver

(7)打印hal层log:logcat | grep klz

12-01 10:16:14.471  4817  4817 E klz     : hellotest_device_open,open /dev/hello failed
12-01 10:21:26.288  4990  4990 I klz     : HelloTest: open /dev/hello successfully.
12-01 10:21:26.288  4990  4990 I klz     : hellotest_read_string,sizeof(*str)=8,len=100
12-01 10:21:26.288  4990  4990 I klz     : hellotest_write_string
12-01 10:21:26.288  4990  4990 I klz     : hellotest_read_string,sizeof(*str)=8,len=100

(8)打印驱动层log:dmesg | grep klz

[  364.015968] klz readbuf=hello my first driver sizeof(kerneldata)=22 cnt=100
[  364.016018] klz readbuf=hello my first driver sizeof(kerneldata)=22 cnt=100

可见,测试成功。如果出现问题,可以分别打印hal层与kernel的log,看看问题出在什么地方。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐