Android硬件访问服务-HAL

本文详细介绍了Android系统中硬件抽象层(HAL)的工作原理和技术细节,包括HAL如何与JNI配合实现对硬件的操作,以及如何通过特定函数加载和利用HAL文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般情况下,APP不直接访问硬件,而是由SystemServer通过HAL和JNI来访问硬件,实质即java访问C,建议把对硬件的操作写在一个HAL文件里面,对上一章写的代码com_android_server_LedService.cpp这个JNI文件做进一步的修改,除了向上注册本地函数之外,还要去加载HAL文件,然后调用HAL里面的函数。

使用HAL有两个好处:

1.	容易修改,只需直接修改HAL文件然后编译成so文件放入系统即可
2.	保密作用,linux基于GPL协议,一旦使用linux内核就必须公开linux内核的源代码,如果我们在linux内核里面实现这些硬件操作的话就需要把这些操作全部公开出去,很多人厂家不愿这么做,所以他们只在内核写一个最简单的驱动程序来操作寄存器,而硬件内部的复杂操作放在HAL文件里面,厂家只提供HAL文件的so文件给客户,这样就避开了GPL协议,android基于Apache协议,你可以去修改它的代码,而不需要去公开你的修改。

对于HAL层,摘取老罗博客中的一段来进行简述:
Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而linux内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。我们知道,Linux内核源代码版权遵循GNU License,而android源代码版权遵循Apache License,前者在发布产品时,必须公布源代码,而后者无须发布源代码。如果把对硬件支持的所有代码都放在Linux驱动层,那就意味着发布时要公开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来说,损害是非常大的。因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android被踢出了Linux内核主线代码树中。大家想想,Android放在内核空间的驱动程序对硬件的支持是不完整的,把Linux内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android是开放系统而不是开源系统的原因。
博客原文:http://blog.csdn.net/luoshengyang/article/details/6567257


所以android里面对硬件的操作我们一般分为两个文件,一个是JNI文件(向上提供(注册)本地函数,向下加载HAL文件并调用HAL文件的函数),一个是HAL文件(负责访问驱动程序执行具体的硬件操作),这两个文件都是C/C++语言写的,所以JNI来加载HAL的实质就是怎么使用dl_open来加载动态库,android里面对dlopen又做了一层封装,我们使用的是“hw_get_module”这个函数(位于Hardware.c),函数实现过程如下

hw_get_module(“led”);//假设模块名为led,最终由这个模块名转为文件名
代码路径:external\chromium_org\third_party\hwcplus\src\Hardware.c

调用过程:

  1. 模块名(“led”)===>文件名(filename)
    hw_get_module
    ----hw_get_module_by_class(“led”, NULL)
    ------name = “led”
    ---------property_get(根据名字获得某个属性值)
    ---------hw_module_exists(去目录下判断某个模块是否存在)
  2. 加载
    dlopen(filename)------>(android源目录使用man dlopen查看使用方法)

上面最后两个函数的具体解析如下:

hw_module_exists(char *path, size_t path_len, const char *name, const char *subname)
它是用来判断”name”.”subname”.so文件是否存在,总共在三个目录下查找

a. HAL_LIBRARY_PATH
b. /vendor/lib/hw
c. /system/lib/hw

对应源码及注解如下:
getenv(“HAL_LIBRARY_PATH”); //获得环境变量,在这个目录(“HAL_LIBRARY_PATH”)所指示的那个目录下查到是否有那个so文件

snprintf(path, path_len, “%s/%s.%s.so”,HAL_LIBRARY_PATH2, name, subname);//在目录(HAL_LIBRARY_PATH2)下查找是否有那个文件存在

snprintf(path,path_len,"%s/%s.%s.so", HAL_LIBRARY_PATH1, name, subname); //在目录(HAL_LIBRARY_PATH1)下查找是否有那个文件存在

上面的宏对应如下:
#ifndef HAL_LIBRARY_PATH1
#define HAL_LIBRARY_PATH1 “/system/lib” _64 “/hw”
#endif
#ifndef HAL_LIBRARY_PATH2
#define HAL_LIBRARY_PATH2 “/vendor/lib” _64 “/hw”
#endif

这里写图片描述

直接到android下进行搜索,我们只能搜索到存在一个 system/lib/hw 这个文件,所以最终会去这个文件里面搜索so文件

另一个函数的解析如下:

property_get
这个函数涉及到android的一个重要系统:属性系统(可以从name里面获得一个value),对应代码如下:
属性< 键,值 >< name, value > 可以从名字获得一个值

/* First try a property specific to the class and possibly instance */
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    if (property_get(prop_name, prop, NULL) > 0) {
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }

传入 prop_name 获得某个 prop(这个属性就是它的值), 这个prop会传入hw_module_exists里面的subname作为参数
比如传入的prop_name是led,则他会去查找以下文件:
Led.XXX.so(XXX来源于prop这个属性,查找"ro.hardware.led"属性是否存在),查找结果如下:

这里写图片描述

没有找到

接着去数组中定义的属性中查找

static const char variant_keys[] = {
“ro.hardware”, /
This goes first so that it can pick up a different
file on the emulator. */
“ro.product.board”,
“ro.board.platform”,
“ro.arch”
};

依次查找到属性值的结果如下:
这里写图片描述

找到这个属性则会去判断文件是否存在, 在这三个目录(HAL_LIBRARY_PATH、/vendor/lib/hw、/system/lib/hw)下面依次查找这两个led文件
led.nanopi3.so
led.slsiap.so

如果这三个目录都不存在这两个so文件,依据源码
hw_module_exists(path, sizeof(path), name, “default”)
会去搜索
led.defualt.so
如果找到了某个so文件,则会把这个文件的绝对路径就会保存在path数组里面

整个过程可以总结如下:

总结:
hw_module_exists
property_get
这两个函数用来判断是否存在led.xxx.so是否存在(xxx是属性),一旦查找到就用dlopen(filename)加载进来
load(class_id, path, module)
-----dlopen(filename)—>加so文件
-----dlsym(“HMI”)—>从so文件中获得名为HMI的hw_module_t结构体
-----strcmp(id, hmi->id)—>判断这个hw_module_t结构体跟我们找的文件的名字是否一样,如比较(“led”, hmi->id)是否一致


下面是HAL代码实现:

JNI 怎么使用 HAL
参考com_android_lights_LightsService.cpp

  1. hw_get_module -> get_device获得一个hw_module_t结构体
  2. 调用 module->methods->open(module, device_name, &device)
    根据device_name的名字获得一个hw_device_t(设备)结构体,并且把hw_device_t结构体转换为设备自定义的结构体
    (light_device_t*)device;
    为什么能直接这样转换的原因是light_device_t这个结构体第一个成员就是hw_device_t类型的参数。

HAL 怎么写

  1. 实现一个名为HMI的hw_module_t结构体
  2. 实现一个open函数, 它会根据name返回一个设备自定义的结构体
    这个设备自定义的结构体的第1个成员是 hw_device_t结构体
    还可以定义设备相关的成员

编写代码实现过程:

  1. 首先是JNI文件的实现:
ine LOG_TAG "LedService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>

#include <stdio.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>


namespace android
{

static led_device_t* led_device;

jint ledOpen(JNIEnv *env, jobject cls)
{
	jint err;
    hw_module_t* module;
	hw_device_t* device;

	ALOGI("native ledOpen ...");

	/* 1. hw_get_module */
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
		/* 2. get device : module->methods->open */
	    err = module->methods->open(module, NULL, &device);
	    if (err == 0) {
			/* 3. call led_open */
	        led_device = (led_device_t *)device;
			return led_device->led_open(led_device);
	    } else {
	        return -1;
    	}
    }
	
    return -1;	
}

void ledClose(JNIEnv *env, jobject cls)
{
	//ALOGI("native ledClose ...");
	//close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
	ALOGI("native ledCtrl %d, %d", which, status);
	return led_device->led_ctrl(led_device, which, status);
}


static const JNINativeMethod methods[] = {
	{"native_ledOpen", "()I", (void *)ledOpen},
	{"native_ledClose", "()V", (void *)ledClose},
	{"native_ledCtrl", "(II)I", (void *)ledCtrl},
};
	

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

}

2.编写HAL文件,需要实现 led_hal.c 和 led_hal.h 两个文件

led_hal.c代码如下:

#define LOG_TAG "LedHal"


/* 1. 实现一个名为HMI的hw_module_t结构体 */

/* 2. 实现一个open函数, 它返回led_device_t结构体 */

/* 3. 实现led_device_t结构体 */

/* 参考 hardware\libhardware\modules\vibrator\vibrator.c
 */

#include <hardware/vibrator.h>
#include <hardware/hardware.h>

#include <cutils/log.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <hardware/led_hal.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>


static int fd;


/** Close this device */
static int led_close(struct hw_device_t* device)
{
	close(fd);
	return 0;
}

static int led_open(struct led_device_t* dev)
{
	fd = open("/dev/zf_led", O_RDWR);
	ALOGI("led_open : %d", fd);
	if (fd >= 0)
		return 0;
	else
		return -1;
}

static int led_ctrl(struct led_device_t* dev, int which, int status)
{
	int ret = ioctl(fd, which, status);
	ALOGI("led_ctrl : %d, %d, %d", which, status, ret);
	return ret;
}




static struct led_device_t led_dev = {
	.common = {
		.tag   = HARDWARE_DEVICE_TAG,
		.close = led_close,
	},
	.led_open  = led_open,
	.led_ctrl  = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module, const char* id,
        struct hw_device_t** device)
{
	*device = &led_dev;
	return 0;
}


static struct hw_module_methods_t led_module_methods = {
    .open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
	.tag = HARDWARE_MODULE_TAG,
    .id = "led",
    .methods = &led_module_methods,
};

led_hal.h代码如下:

#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H

#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>

__BEGIN_DECLS

 /* 结构体定义 */
struct led_device_t {
    struct hw_device_t common;

    int (*led_open)(struct led_device_t* dev);
    int (*led_ctrl)(struct led_device_t* dev, int which, int status);
};

__END_DECLS

#endif  // ANDROID_LIGHTS_INTERFACE_H

重新上传以上文件进行编译即可:

JNI: frameworks/base/services/core/jni/com_android_server_LedService.cpp
hal.c:hardware/libhardware/modules/led/led_hal.c
hal.h:hardware/libhardware/include/hardware/led_hal.h

修改 hardware/libhardware/modules/led/Android.mk
Android.mk内容如下:`

# Copyright (C) 2012 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 := led.default

# HAL module implementation stored in
# hw/<VIBRATOR_HARDWARE_MODULE_ID>.default.so
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := optional

include $(BUILD_SHARED_LIBRARY)


编译:
mmm frameworks/base/services
mmm hardware/libhardware/modules/led
make snod

验证(在android系统命令行中输入搜索):
Logcat | grep “LedHal”

Android打印信息简介:

	1.有三类打印信息: app, system, radio
	程序里使用 ALOGx, SLOGx, RLOGx来打印
	>
	2. x表示6种打印级别,有:
	  V    Verbose
	  D    Debug
	  I    Info
	  W    Warn
	  E    Error
	  F    Fatal
	比如: 
	#define LOG_TAG "LedHal"
	ALOGI("led_open : %d", fd);
	>
	3. 打印出来的格式为:
      I/LedHal  ( 1987): led_open : 65
      (级别) LOG_TAG  进程号   打印信息  
    >
    4. 使用 logcat 命令查看  
	    logcat LedHal:I *:S
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值