文章目录
在android系统中会提供hal层,即hardware层来封装对Linux的驱动访问,同时会为上层提供一个统一的硬件接口
1.hardware层概述
1.1 主要数据结构
在hal层,会有以下三个结构体
- struct hw_module_t
- struct hw_module_methods_t
- struct hw_device_t
1.2 上层访问驱动流程
原文:参考Android应用程序访问linux驱动第二步:实现并测试hardware层
使用hw_get_module()方法获取hal层对应的module,函数通过给定模块的ID来寻找硬件模块的动态链接库,找到后使用load()函数打开这个库,并通过一个固定的符号-HAL_MODULE_INFO_SYM寻找hw_module_t结构体,我们会在hw_module_t结构体中会有一个methods属性,指向hw_module_methods_t结构体,这个结构体中会提供一个open方法用来打开模块,在open的参数中会传入一个hw_device_t**的数据结构,这样我们就可以把对模块的操作函数等数据保存在这个hw_device_t结构中,这样,这样用户可以通过hw_device_t来和模块交互。
1.3 Hal伪代码示例
具体的说,我们在上层可以这样调用HAL层的module:
xxx_module_t *module;
hw_get_module(XXXID,(struct hw_module_t **)&module);
以上两步我们就获得了对应ID的 hw_module_t结构体。
struct xxx_device_t *device;
module->methods->open(module,XXXID,(struct hw_device_t **)&device);
这样我们就获得了hw_device_t结构体,通过hw_device_t结构体我们就可以访问HAL层对应的module了。
整个过程可用一张图展示:
2.实现hal对于驱动的访问
在asop源码目录下hardware/libhardware/modules新建hellotest目录,添加两个文件:hellotest.c 和 Android.mk
2.1 hellotest.c
/******************************************************************************
* @file hellotest.c
* @brief 测试hellotest驱动
* @note 实现驱动访问相关函数
* @author 无
* @version 1.0.0
* @date 2020-6-05
* 修改记录:
*******************************************************************************/
/******************************************************************************
头文件
*******************************************************************************/
#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__)
/******************************************************************************
变量与数据结构,函数等定义
*******************************************************************************/
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);
//模块方法结构体
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)
{
LOGI("%s\n",__func__);
read(dev->fd,*str,sizeof(*str));
return 0;
}
经过上面操作我们可以实现hal层中hw_device_t结构体中的方法操作Linux驱动
2.2 Android.mk
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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
在hardware/libhardware/include/hardware编写头文件hellotest.h
/******************************************************************************
* 文 件 名: hellotest.h
* 功能描述: 测试hello驱动
* 作 者: 无
* 版 本 号: 1.0.0
* 修改日期: 2021-6-05
* 修改记录:
*******************************************************************************/
#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);
};
__END_DECLS
#endif
2.4 编译hal模块
直接进入到hardware/libhardware/modules/hellotest目录,执行mm命令进行编译
3.编写测试代码
在hardware/libhardware/tests/目录下新建一个hellotest目录,新增hellotest.c和Android.mk文件:
3.1 hellotest.c
#include <hardware/hardware.h>
#include <hardware/hellotest.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
struct hw_module_t * module;
struct hw_device_t * device;
int main(){
char *read_str;
char *write_str="hellotest";
read_str = malloc(100);
if(hw_get_module(HELLOTEST_HARDWARE_MODULE_ID,(struct hw_module_t const **)&module)==0)
{
printf("get module sucess\n");
}else{
printf("get module fail\n");
return -1;
}
if(module->methods->open(module,HELLOTEST_HARDWARE_MODULE_ID,(struct hw_device_t const**)&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);
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);
if(read_str == NULL)
{
printf("read error");
}
else
{
printf("read data: %s\n",read_str);
}
return 0;
}
3.2 测试代码调用流程
先使用hw_get_module函数获得hw_module_t结构体,再调用hw_module_methods_t结构体中的open方法获得hw_device_t结构体,然后使用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)
然后进入到该目录执行mm命令进行编译
4.测试
- 把生成的hellotest.default.so文件拷贝到android设备的/system/lib/hw目录下,这需要root权限,没有root权限请自行解决。
- 获得root权限后会提示/system是一个只读文件系统,所以需要重新挂载为可读可写的文件系统,执行mount -o remount,rw /system 即可。
- 修改hellotest.default.so文件的的权限为644,执行chmod 644 /system/lib/dw/hellotest.default.so
- 装载上一节实现的hello.ko驱动:insmod hello.ko
链接Android应用程序访问linux驱动第一步:实现并测试Linux驱动
- 把生成的my_test可执行文件拷贝的android设备上,建议push hellotest/data
- 给my_test文件添加可执行权限. chmod +x hellotest
- 执行hellotest。 ./hellotest
打印如下:
get module sucess
open module sucess
read data: hello
write data: hellotest
read data: hellotest
可见,测试成功。
如果有问题,可以使用logcat看下HAL层的打印,看看问题出在什么地方。