Android 标准的硬件驱动分为两个部分,一个是运行在linux内核里的硬件驱动,而另外一部分是运行在用户空间的硬件抽象层。采用这种方法,就可以使系统具有硬件无关性,也保护了部分厂商的利益。在 Android 从硬件到应用:一步一步向上爬 1 -- 从零编写底层硬件驱动程序 中已经有了编写硬件驱动到linux内核里的步骤,下面就要接着这个工程去看看怎么在硬件抽象层增加硬件模块和我们的内核驱动程序进行交互,完成硬件控制。
进入hardware/libhardware/include/hardware目录,新建gpio.h:
#ifndef ANDROID_GPIO_INTERFACE_H
#define ANDROID_GPIO_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS
/*module ID*/
#define GPIO_HARDWARE_MODULE_ID "gpio"
/*module struct*/
struct gpio_module_t {
struct hw_module_t common;
};
/*interface struct*/
struct gpio_device_t {
struct hw_device_t common;
int fd;
int (*set_val)(struct gpio_device_t* dev, int val);
int (*get_val)(struct gpio_device_t* dev, int* val);
};
__END_DECLS
#endif
其中set_val和get_val是HAL层向上层应用提供的API接口。
cd到hardware/libhardware/modules目录,新建gpio目录,在里面新建gpio.c文件:
#include <hardware/hardware.h>
#include <hardware/gpio.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#define DEVICE_NAME "/dev/AdrIO"
#define MODULE_NAME "Gpio"
//open and close
static int gpio_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int gpio_device_close(struct hw_device_t* device);
//device access
static int gpio_set_val(struct gpio_device_t* dev, int val);
static int gpio_get_val(struct gpio_device_t* dev, int* val);
static struct hw_module_methods_t gpio_module_methods = {
open: gpio_device_open
};
struct gpio_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: GPIO_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: "HAL",
methods: &gpio_module_methods, }
};
static int gpio_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
struct gpio_device_t* dev;
dev = (struct gpio_device_t*)malloc(sizeof(struct gpio_device_t));
memset(dev, 0, sizeof(struct gpio_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = gpio_device_close;
dev->set_val = gpio_set_val;
dev->get_val = gpio_get_val;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
LOGE("gpio: failed to open /dev/AdrIO -- %s.", strerror(errno));
free(dev);
return -EFAULT;
}
*device = &(dev->common);
return 0;
}
static int gpio_device_close(struct hw_device_t* device)
{
struct gpio_device_t* gpio_device = (struct gpio_device_t*)device;
if(gpio_device) {
close(gpio_device->fd);
free(gpio_device);
}
return 0;
}
static int gpio_set_val(struct gpio_device_t* dev, int val)
{
LOGI("gpio: set value %d to device.", val);
write(dev->fd, &val, sizeof(val));
return 0;
}
static int gpio_get_val(struct gpio_device_t* dev, int* val)
{
return 0;
}
为了防止调用时出现 Permission denied的情况:
打开am335xevm文件系统根目录rootfs,打开ueventd.rc添加:
/dev/AdrIO 0666 root root
该文件并不会创建设备节点,而是当有设备节点产生的时候,eventd 会根据这个数据库设置设备的权限。
修改hardware/libhardware/modules目录下Android.mk在harware_modules :=后面加上“gpio”
在gpio目录中继续添加Android.mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := gpio.c
LOCAL_MODULE := gpio.default
include $(BUILD_SHARED_LIBRARY)
编译HAL层:
make TARGET_PRODUCT=am335xevm_sk -j8 OMAPES=4.x
如果出现错误,参考:
如果成功,就可以生成 gpio.default.so
out/target/product/am335xevm_sk/obj/lib/gpio.default.so
这个就是我们需要的硬件抽象层模块,这一步完成之后,还要接着向上走,最终完成硬件调用。