前言:
本章主要讲述Android 应用层app通过jni hal的方式操作硬件节点,以gpio节点为例。
此处特别提醒,jni方法仅适用于不需要过谷歌认证(CTS)的Android设备,因为后续涉及system调用vendor的so,需要修改system/sepolicy,CTS是不允许修改此处的。
所以需要过CTS认证的建议构建一个新的hidl去实现该功能,原理大同小异,可参照 https://blog.csdn.net/weixin_41486477/article/details/110948162
一. 驱动层
这个部分不再展开讲了,根据各自需求,生成自己的节点即可。此处我们以高通的gpio节点为例,假设我们要操作的节点路径为sys/class/gpio/gpio1。
二.hal 层
目的:实现读写节点的方法,编译生成testgpio.default.so
在hardware/libhardware/include/hardware/下新建头文件testgpio.h
#include <hardware/hardware.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include<stdio.h>
#define testgpio_HARDWARE_MODULE_ID "testgpio"
typedef struct testgpio_device {
struct hw_device_t common;
int fd;
int (*set_gpio_up)(struct testgpio_device *dev, int32_t value);
int (*set_gpio_down)(struct testgpio_device *dev, int32_t value);
int (*testgpio_open)(struct testgpio_device *dev);
} testgpio_device_t;
typedef struct testgpio_module {
struct hw_module_t common;
} testgpio_module_t;
在hardware/libhardware/modules下新建文件夹testgpio,该文件夹下包含两个文件,Android.mk控制编译,testgpio.c实现方法
#define LOG_TAG "testgpioHal"
#include <errno.h>
#include <malloc.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <log/log.h>
#include <hardware/testgpio.h>
#include <hardware/hardware.h>
int testgpio_close(struct hw_device_t* device)
{
struct testgpio_device* ctx = (struct testgpio_device*)device;
if (ctx) {
free(ctx);
}
int fd=ctx->fd;
close(fd);
return 0;
}
int hal_set_gpio_up(struct testgpio_device __unused *dev,int32_t gpio_index)
{
int ret;
ret=write(dev->fd, "1", 1);
if(ret<0){
ALOGE("testgpio: write fail\n");
return -1;
}
else{
ALOGI("testgpio: set gpio %d up\n",gpio_index);
return 0;
}
}
int hal_set_gpio_down(struct testgpio_device __unused *dev,int32_t gpio_index)
{
int ret;
ret=write(dev->fd, "0", 1);
if(ret<0){
ALOGE("testgpio: write fail\n");
return -1;
}
else{
ALOGI("testgpio: set gpio %d down\n",gpio_index);
return 0;
}
}
static int hal_testgpio_open(struct testgpio_device *dev)
{
dev->fd = open("sys/class/gpio/gpio1", O_RDWR);
if(dev->fd== -1){
ALOGI("testgpio: testgpiohal_open failed!!!----");
return 0;
}else{
ALOGI("testgpio: testgpiohal_open sucess!!!");
return -1;
}
}
static int testgpio_open(const hw_module_t* module, const char __unused *id,
hw_device_t** device)
{
if (device == NULL) {
ALOGE("NULL device on open");
return -EINVAL;
}
testgpio_device_t *dev = malloc(sizeof(testgpio_device_t));
memset(dev, 0, sizeof(testgpio_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 1.0;
dev->common.module = (struct hw_module_t*) module;
dev->common.close = testgpio_close;
dev->set_gpio_up = hal_set_gpio_up;
dev->set_gpio_down = hal_set_gpio_down;
dev->testgpio_open = hal_testgpio_open;
*device = (hw_device_t*) dev;
return 0;
}
static struct hw_module_methods_t testgpio_module_methods = {
.open = testgpio_open,
};
testgpio_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = 1.0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = testgpio_HARDWARE_MODULE_ID,
.name = "testgpio HAL",
.author = "The Android Open Source Project",
.methods = &testgpio_module_methods,
},
};
Android.mk,此处主要注意加上 libhardware_headers,不然.c调用haraware下的头文件会报错。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := testgpio.default
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_SRC_FILES := \
testgpio.c \
LOCAL_HEADER_LIBRARIES := \
libhardware_headers \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libdl \
liblog \
libutils \
LOCAL_STRIP_MODULE := false
include $(BUILD_SHARED_LIBRARY)
重点提下对节点的操作,此处就是很简单的write,其实还可以利用ioctl,这个根据你驱动里的实现方法去决定,来实现不同的操作方法。