android访问应用程序第2步:实现hal层

文章目录


在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了。

整个过程可用一张图展示:
android访问应用程序第2步:实现hal层

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层的打印,看看问题出在什么地方。

上一篇:JAVA-HW 07:数组


下一篇:《Accelerated C++中文版》学习配套编程(一)