Android系统HAL开发实例

摘要:
其中hw_ module_T表示整个HAL的实现和功能封装,(1.3)结构hw_ module_T中的标记成员固定到HARDWARE_MULE_tag(1.4)t_api_版本成员表示HAL模块的api版本。(1.5)结构hw_模块_ t中的id成员是模块的标识符。

1、前言

 Android系统使用HAL这种设计模式,使得上层服务与底层硬件之间的耦合度降低,在文件:

AOSP/hardware/libhardware/include/hardware/hardware.h

中描述了HAL的编写规范,并且给出了标准接口,本文将通过一个简单的实例讲解HAL的编写。

2、HAL编写规范

在之前的文章中讲解了两个很重要的数据结构,struct hw_module_t和struct hw_device_t,在其中hw_module_t代表了整个HAL的实现、功能的封装,也是外部应用程序看到的唯一视角,而hw_device_t代表了一个实际的硬件设备,是设备的属性、设备操作的封装,接下来分析HAL的编写规范。

(1)定义代表module的数据结构

以LED HAL模块为例,首先需要定义一个表示LED模块的数据结构led_moduler_t,并且该数据结构遵循以下准则:

(1.1)该结构体中的第一个成员必须是struct hw_module_t;

(1.2)创建一个该数据结构体的变量,并以HAL_MODULE_INFO_SYM为变量名;

(1.3)结构体hw_module_t中的tag成员固定赋值为HARDWARE_MODULE_TAG;

(1.4)结构体hw_module_t中的module_api_version成员代表了HAL模块的API版本,当模块的接口发生改变时,开发人员必须更新该成员;

(1.5)结构体hw_module_t中的id成员是该模块的标识符,其它程序通过该成员来寻找对应HAL得Stub。

实现如下所示:

#include <hardware/hardware.h>

typedef struct led_module {
    struct hw_module_t common;
    ...
    ...
} led_module_t;

struct led_module HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = LED_MODULE_API_VERSION_1_0,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = LED_HARDWARE_MODULE_ID,
        .name = "Defaule Led HAL",
        .author = "hly",
        .methods = &led_module_methods,
    },
    ...
    ...
};

(2)定义代表device的数据结构

通过module并不能实际操作硬件设备,开发者需要将设备的相关属性以及操作封装在代表device的数据结构,该数据结构通常是一系列的函数指针,定义led_device_t结构体,该数据结构遵循以下规则:

(2.1)第一个成员必须是struct hw_device_t;

(2.2)结构体中的hw_device_t的tag成员必须被赋值为HARDWARE_DEVICE_TAG。

实现如下所示:

typedef struct led_device {
    struct hw_device_t common;
    int (*get_led_state)(struct led_device *dev, char **state);
    int (*set_led_state)(struct led_device *dev, char *state);
    ...
    ...
} led_device_t;

(3)实现open函数

当其它的应用程序使用HAL模块的步骤通常是:

首先通过hw_get_module()函数获得指定HAL模块的hw_module_t的引用,然后调用hw_module_t->methods->open函数或得hw_device_t的引用,最后,通过device下的函数指针来操作设备。

open函数指针位于hw_module_methods_t结构体中,该函数的实现方法在不同的HAL模块中实现也较为类似,大体步骤为:

(3.1)为led_device_t结构分配内存空间;

(3.2)对hw_device_t类型的common成员进行赋值;

(3.3)对封装的结构体中的函数指针进行赋值;

(3.4)将分配空间的led_device_t的指针通过hw_device_t **device返回。

实现如下所示:

static int led_device_open(const struct hw_module_t *module, 
                           const char *id, struct hw_device_t **device)
{
    struct led_device *dev;
    
    dev = malloc(sizeof(struct led_device));
    if (!dev) {
        ALOGE("Failed to malloc memory");
        return -ENOMEM;
    }
    memset(dev, 0, sizeof(led_device));
    
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = MODULE_API_VERSION_1_0;
    dev->common.module = (struct hw_module_t *)module;
    
    dev->get_led_state = get_led_state;
    dev->set_led_state = set_led_state;
    ...
    ...
    ...
    
    *device = (struct hw_device_t *)dev;
    return 0;
}

(4)实现device的具体操作接口

每个HAL模块的实现都比较类似,对于device下的操作方法,则要按照具体的设备进行实现,每一类设备都是不尽相同的,需要掌握Linux应用层中文件操作、进程管理、信号处理、多线程编程和网络编程等相关技术。

3、实例讲解

 实现一个ledctrl的HAL模块:

首先在安卓源码下的hardware/libhardware/include/hardware目录下创建ledctrl.h文件,代码如下:

#ifndef ANDROID_LEDCTRL_INTERFACE_H
#define ANDROID_LEDCTRL_INTERFACE_H

#include <hardware/hardware.h>

__BEGIN_DECLS

/* 定义模块ID */
#define LEDCTRL_HARDWARE_MODULE_ID    "ledctrl"

/* 硬件模块结构体 */
struct ledctrl_module_t {
    struct hw_module_t common;
};

/* 硬件接口结构体 */
struct ledctrl_device_t {
    struct hw_device_t common;
    
    int fd;
    int (*set_state)(struct ledctrl_device_t *dev, const char *state);
    int (*get_state)(struct ledctrl_device_t *dev, char **state);
};

__END_DECLS

#endif

然后在安卓源码的hardware/libhardware/modules目录下,新建ledctrl目录,并添加ledctrl.c文件,代码实现如下:

#define LOG_TAG "ledctrl_hw_default"

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <cutils/log.h>

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

#define DEVICE_NAME "/sys/class/leds/led-red/brightness"
#define MODULE_NAME "ledctrl"
#define MODULE_AUTHOR "HLY"

/* 设备访问接口 */
static int ledctrl_set_state(struct ledctrl_device_t *dev, const char *state)
{
    int ret = -EINVAL;
    
    if (0 == strcmp(state, "enable")) {
        ret = write(dev->fd, "1", 1);
        if (ret != 1) {
            ALOGE("ledctrl: failed to enable led device");
            goto exit;
        }
        ALOGI("ledctrl: successed to enable led device");
        ret = 0;
    } else if (0 == strcmp(state, "disable")) {
        ret = write(dev->fd, "0", 1);
        if (ret != 1) {
            ALOGE("ledctrl: failed to close led device");
            goto exit;
        }
        ALOGI("ledctrl: successed to disable led device");
        ret = 0;
    } else
        ALOGE("ledctrl: not define the led device state");
    
exit:
    return ret;    
}

static int ledctrl_get_state(struct ledctrl_device_t *dev, char **state)
{
    int ret,value;
    char *buf = malloc(sizeof(char) * 5);
    
    memset(buf, 0, sizeof(char) * 5);
    ret = read(dev->fd, buf, sizeof(int));
    if (ret < 0) {
        ALOGE("ledctrl: failed to read led device");
        goto exit;
    }
    
    value = atoi(buf);
    if (value == 0)
        *state = "disable";
    else
        *state = "enable";
    ALOGD("value = %d, led_state = %s", value, *state);

exit:
    free(buf);
    return ret;
}

/* 设备的打开和关闭接口 */
static int ledctrl_device_close(struct hw_device_t *device)
{
    struct ledctrl_device_t *dev = (struct ledctrl_device_t *)device;
    
    if (dev) {
        close(dev->fd);
        free(dev);
    }
    
    return 0;
}

static int ledctrl_device_open(const struct hw_module_t *module,
            const char *id, struct hw_device_t **device)
{
    int fd;
    struct ledctrl_device_t *dev;
    
    dev = malloc(sizeof(struct ledctrl_device_t));
    if (!dev) {
        ALOGE("ledctrl: failed to malloc memory");
        return -ENOMEM;
    }
    memset(dev, 0, sizeof(struct ledctrl_device_t));
    
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t *)module;
    dev->common.close = ledctrl_device_close;
    
    fd = open(DEVICE_NAME, O_RDWR);
    if (fd == -1) {
        ALOGE("ledctrl: failed to open device file");
        free(dev);
        return fd;
    }
    dev->fd = fd;
    dev->set_state = ledctrl_set_state;
    dev->get_state = ledctrl_get_state;
    
    *device = &dev->common;
    
    return 0;
}

/* 模块方法表 */
static struct hw_module_methods_t ledctrl_module_methods = {
    .open = ledctrl_device_open,
};

/* 模块实例变量 */
struct ledctrl_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .version_major = 1,
        .version_minor = 0,
        .id = LEDCTRL_HARDWARE_MODULE_ID,
        .name = MODULE_NAME,
        .author = MODULE_AUTHOR,
        .methods = &ledctrl_module_methods,
    },
};

主要是HAL模块的实现方法,sysfs中的brightness文件为leds-gpio设备的属性文件,通过对该属性文件的读写操作,能实现led类设备的点亮与熄灭,另外还需要实现编译的Android.mk文件,代码如下:

# Android.mk for ledctrl hal module

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := ledctrl.c
LOCAL_MODULE := ledctrl.default
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)

将该ledctrl模块编译为动态库,因此,最后生成的文件应该是ledctrl.default.so。

4、编译测试

 对上面编写的HAL模块进行编译测试:

$ cd AOSP
$ mmm hardware/libhardware/modules/ledctrl
$ make snod

将Android系统的system.img镜像重新打包后,使用fastboot命令烧写到system分区,并使用adb登入到终端,查看对应得动态库是否已经存在:

# cd /system/lib/hw
# ls

如下:

Android系统HAL开发实例第1张

5、小结

本篇文章主要对HAL库的编写规则进行了简单的描述,并简单介绍了一个简单的HAL例子实现过程。

免责声明:文章转载自《Android系统HAL开发实例》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇kickstart详解Idea中使用Redis的Java客户端和Jedis下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

SqlServer性能急剧下降,查看所有会话的状态及等待类型---Latch_Ex

      当某个数据库文件空间用尽,做自动增长的时候,同一时间点只能有一个用户人员可以做文件自动增长动作,其他任务必须等待,此时会出现Latch资源的等待。使用sp_helpdb查看业务数据库时发现:该数据库设置不合理,数据文件每次增长2M,日志文件每次增长10%,且分别设置了最大限制。修改为每次增长200M,不限制大小,系统恢复正常,问题解决。 Sql...

Qt 事件总结(鼠标、键盘) QMouseEvent、QKeyEvent

Qt 程序需要在main()函数创建一个QCoreApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行 exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于 QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject...

硬件抽象层:HAL

本章主要讲硬件抽象层:HAL,它是建立在Linux驱动之上的一套程序库。刚开始介绍了为什么要在Android中加入HAL,目的有三个,一,统一硬件的调用接口。二,解决了GPL版权问题。三,针对一些特殊的要求。可以利用位于用户空间的HAL代码来辅助Linux驱动完成一些工作。 下一个小节讲了Android HAL 架构。接下来讲了为led驱动增加HAL,主要...

[Matlab] 短时傅里叶变换spectrogram函数

Matlab 文档:https://ww2.mathworks.cn/help/signal/ref/spectrogram.html#bultmx7-x 调用:[~,f,t,ps] = spectrogram(data,opt.window,opt.noverlap,freqRange(1):freqRange(2),sample_freq,'reass...

从零开始学习GDI+ (二) 基本概念与基本操作

       从零开始学习GDI+ (一)我的第一个GDI+程序        上文给新手学习GDI+讲述了vs环境等的准备工作,并且可以直接用GDI+绘图了。本文开始,讲述的可能偏理论,建议学习的过程中大胆尝试,多使用API。        首先上官方文档https://docs.microsoft.com/en-us/windows/win32/gdi...

CSC 命令编译cs文件

CSC 命令编译cs文件 编译 File.cs 以产生 File.exe: csc File.cs 编译 File.cs 以产生 File.dll: csc /target:library File.cs  可缩写成 csc /t:library File.cs  编译 File.cs 并创建 My.exe: csc /out:My.exe Fil...