文章目录
在安卓系统中,会提供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,看看问题出在什么地方。