在上一篇博客中,介绍了Rockchip系列的GPIO接口如何在Driver中 注册和使用GPIO设备。在这篇博客中,将继续探讨如何在Android HAL层中实现GPIO接口的驱动,然后以及后面文章再讨论 如何在应用层中调用GPIO接口的函数。
Android HAL层的GPIO驱动
Android HAL层是Android系统和硬件之间的抽象层,它提供了一组标准的接口和结构体,用于封装不同硬件平台的具体实现。HAL层的主要作用是屏蔽硬件的差异性,使得应用层可以使用统一的API来访问不同的硬件设备。
为了在Android HAL层中实现GPIO接口的驱动,需要遵循以下几个步骤:
- 定义GPIO设备结构体和函数指针
- 实现GPIO设备的打开、关闭、读写、方向设置、按键事件注册和取消注册、获取GPIO数量等函数
- 注册GPIO设备到HAL模块中
下面来具体看一下每个步骤的细节。
定义GPIO设备结构体和函数指针
首先,我们需要在hardware/libhardware/include/hardware/gpio_hal.h
文件中定义一个GPIO设备结构体gpio_device_t
,它继承自hw_device_t
结构体,并包含了一些GPIO相关的函数指针。这些函数指针分别对应了要实现的GPIO设备的操作函数,例如打开、关闭、读写、方向设置等。代码如下:
#ifndef ANDROID_GPIO_INTERFACE_H #define ANDROID_GPIO_INTERFACE_H #include <hardware/hardware.h> __BEGIN_DECLS #define GPIO_HARDWARE_MODULE_ID "gpio" struct gpio_device_t { struct hw_device_t common; int (*gpio_open)(struct gpio_device_t* dev); int (*gpio_write)(struct gpio_device_t* dev, int gpio, int value); int (*gpio_read)(struct gpio_device_t* dev, int gpio); int (*gpio_direction)(struct gpio_device_t* dev, int gpio, int direction, int value); int (*gpio_reg_key_event)(struct gpio_device_t* dev, int gpio); int (*gpio_unreg_key_event)(struct gpio_device_t* dev, int gpio); int (*gpio_get_number)(struct gpio_device_t* dev); }; __END_DECLS #endif // ANDROID_GPIO_INTERFACE_H
实现GPIO设备的操作函数
然后需要在hardware/libhardware/modules/gpio/gpio.c
文件中实现上述定义的GPIO设备结构体中的函数指针所对应的操作函数。这些操作函数主要是通过调用Linux内核提供的ioctl接口来与底层的GPIO驱动进行通信。ioctl接口是一种通用的设备控制接口,它可以根据不同的命令码(cmd)和参数(arg)来执行不同的操作。在我们的例子中,我们定义了以下几个ioctl命令码:
#define GPIO_IOC_MAGIC 'f' #define GPIO_IOC_SET_VALUE _IOW(GPIO_IOC_MAGIC, 1, int) #define GPIO_IOC_GET_VALUE _IOR(GPIO_IOC_MAGIC, 2, int) #define GPIO_IOC_SET_DIRECTION _IOW(GPIO_IOC_MAGIC, 3, int) #define GPIO_IOC_REG_KEY_EVENT _IOW(GPIO_IOC_MAGIC, 4, int) #define GPIO_IOC_UNREG_KEY_EVENT _IOW(GPIO_IOC_MAGIC, 5, int) #define GPIO_IOC_GET_NUMBER _IOW(GPIO_IOC_MAGIC, 6, int)
这些命令码的含义如下:
- GPIO_IOC_SET_VALUE:设置GPIO的输出值,参数是一个整数,高4位表示GPIO的编号,低4位表示GPIO的值(0或1)
- GPIO_IOC_GET_VALUE:获取GPIO的输入值,参数是一个整数,高4位表示GPIO的编号,低4位返回GPIO的值(0或1)
- GPIO_IOC_SET_DIRECTION:设置GPIO的方向,参数是一个整数,高4位表示GPIO的编号,低4位表示GPIO的方向(0为输入,1为输出)和初始值(0或1)
- GPIO_IOC_REG_KEY_EVENT:注册GPIO的按键事件,参数是一个整数,高4位表示GPIO的编号,低4位无意义。注册后,当GPIO的输入值发生变化时,会产生一个按键事件,并通过/dev/input/eventX设备报告给应用层。
- GPIO_IOC_UNREG_KEY_EVENT:取消注册GPIO的按键事件,参数是一个整数,高4位表示GPIO的编号,低4位无意义。
- GPIO_IOC_GET_NUMBER:获取GPIO的数量,参数是一个整数,返回值为GPIO的数量。
基于这些命令码,可以实现以下几个操作函数:
#include <hardware/hardware.h> #include <cutils/log.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <hardware/gpio_hal.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <utils/Log.h> #define DEVICE "/dev/custom_gpio" #define GPIO_IOC_MAGIC 'f' #define GPIO_IOC_SET_VALUE _IOW(GPIO_IOC_MAGIC, 1, int) #define GPIO_IOC_GET_VALUE _IOR(GPIO_IOC_MAGIC, 2, int) #define GPIO_IOC_SET_DIRECTION _IOW(GPIO_IOC_MAGIC, 3, int) #define GPIO_IOC_REG_KEY_EVENT _IOW(GPIO_IOC_MAGIC, 4, int) #define GPIO_IOC_UNREG_KEY_EVENT _IOW(GPIO_IOC_MAGIC, 5, int) #define GPIO_IOC_GET_NUMBER _IOW(GPIO_IOC_MAGIC, 6, int) #define GPIO_DIRECTION_MASK 0x01 #define GPIO_VALUE_MASK 0x02 static int fd; static int gpio_close(struct hw_device_t* device) { close(fd); return 0; } static int gpio_open(struct gpio_device_t* dev) { fd = open(DEVICE, O_RDWR); ALOGI("gpio_open : %d", fd); // add error checking and debugging print if (fd == -1) { ALOGE("gpio_open: failed to open %s: %s", DEVICE, strerror(errno)); return -1; } else { ALOGD("gpio_open: successfully opened %s", DEVICE); return 0; } } static int gpio_write(struct gpio_device_t* dev, int gpio, int value) { int ret = 0; int data = 0; if(fd < 0) { ret = gpio_open(dev); if (ret < 0) { return -1; } } data = ((gpio & 0x0f) << 4) | (value & 0x0f); ret = ioctl(fd, GPIO_IOC_SET_VALUE, &data); ALOGI("gpio_write: gpio=%d, value=%d, data=%d, ret=%d", gpio, value, data, ret); // add error checking and debugging print if (ret == -1) { ALOGE("gpio_write: failed to ioctl %s: %s", DEVICE, strerror(errno)); return -1; } else { ALOGD("gpio_write: successfully ioctl %s", DEVICE); return 0; } } static int gpio_read(struct gpio_device_t* dev, int gpio) { int ret = 0; int data = 0; if(fd < 0) { ret = gpio_open(dev); if (ret < 0) { return -1; } } data = (gpio & 0x0f) << 4; ret = ioctl(fd, GPIO_IOC_GET_VALUE, &data); ALOGI("gpio_read: gpio=%d, data=%d, ret=%d", gpio, data, ret); // add error checking and debugging print if (ret == -1) { ALOGE("gpio_read: failed to ioctl %s: %s", DEVICE, strerror(errno )); return -1; } else { ALOGD("gpio_read: successfully ioctl %s", DEVICE); return data; } } static int gpio_direction(struct gpio_device_t* dev, int gpio, int direction, int value) { int ret = 0; int data = 0; if(fd < 0) { ret = gpio_open(dev); if (ret < 0) { return -1; } } data = ((gpio & 0x0f) << 4) | (direction > 0 ? GPIO_DIRECTION_MASK : 0x00) | (value > 0 ? GPIO_VALUE_MASK : 0x00); ret = ioctl(fd, GPIO_IOC_SET_DIRECTION, &data); ALOGI("gpio_direction: gpio=%d, direction=%d, value=%d, data=%d, ret=%d", gpio, direction, value, data, ret); // add error checking and debugging print if (ret == -1) { ALOGE("gpio_direction: failed to ioctl %s: %s", DEVICE, strerror(errno)); return -1; } else { ALOGD("gpio_direction: successfully ioctl %s", DEVICE); return 0; } } static int gpio_reg_key_event(struct gpio_device_t* dev, int gpio) { int ret = 0; int data = 0; if(fd < 0) { ret = gpio_open(dev); if (ret < 0) { return -1; } } data = (gpio & 0x0f) << 4; ret = ioctl(fd, GPIO_IOC_REG_KEY_EVENT, &data); ALOGI("gpio_reg_key_event: gpio=%d, data=%d, ret=%d", gpio, data, ret); // add error checking and debugging print if (ret == -1) { ALOGE("gpio_reg_key_event: failed to ioctl %s: %s", DEVICE, strerror(errno)); return -1; } else { ALOGD("gpio_reg_key_event: successfully ioctl %s", DEVICE); return 0; } } static int gpio_unreg_key_event(struct gpio_device_t* dev, int gpio) { int ret = 0; int data = 0; if(fd < 0) { ret = gpio_open(dev); if (ret < 0) { return -1; } } data = (gpio & 0x0f) << 4; ret = ioctl(fd, GPIO_IOC_UNREG_KEY_EVENT, &data); ALOGI("gpio_unreg_key_event: gpio=%d, data=%d, ret=%d", gpio, data, ret); // add error checking and debugging print if (ret == -1) { ALOGE("gpio_unreg_key_event: failed to ioctl %s: %s", DEVICE, strerror(errno)); return -1; } else { ALOGD("gpio_unreg_key_event: successfully ioctl %s", DEVICE); return 0; } } static int gpio_get_number(struct gpio_device_t* dev) { int ret = 0; int data = 0; if(fd < 0) { ret = gpio_open(dev); if (ret < 0) { return -1; } } ret = ioctl(fd, GPIO_IOC_GET_NUMBER, &data); ALOGI("gpio_get_number: data=%d, ret=%d", data, ret); // add error checking and debugging print if (ret == -1) { ALOGE("gpio_get_number: failed to ioctl %s: %s", DEVICE, strerror(errno)); return -1; } else { ALOGD("gpio_get_number: successfully ioctl %s", DEVICE); return data; } }
注册GPIO设备到HAL模块中
最后,需要在hardware/libhardware/modules/gpio/gpio.c
文件中注册GPIO设备到HAL模块中,以便应用层可以通过HAL接口来获取GPIO设备的句柄。这一步主要是通过定义一个hw_module_t
结构体,并实现一个open
函数,用于打开GPIO设备并返回一个hw_device_t
结构体的指针。代码如下:
static struct gpio_device_t gpio_dev = { .common = { .tag = HARDWARE_DEVICE_TAG, .close = gpio_close, }, .gpio_open = gpio_open, .gpio_write = gpio_write, .gpio_read = gpio_read, .gpio_direction = gpio_direction, .gpio_reg_key_event = gpio_reg_key_event, .gpio_unreg_key_event = gpio_unreg_key_event, .gpio_get_number = gpio_get_number, }; static int gpio_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) { *device = &gpio_dev; return 0; } static struct hw_module_methods_t gpio_module_methods = { .open = gpio_device_open, }; struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .id = "gpio", .methods = &gpio_module_methods, };
添加GPIO编译
gpio.default 是我们在前面定义的 GPIO HAL 模块。通过将 gpio.default 添加到 PRODUCT_PACKAGES,我们可以确保在构建 Android 系统映像时,这个模块会被编译并包含在最终的系统映像中。
当 Android 系统运行时,就可以加载并使用这个 GPIO HAL 模块,从而实现对 GPIO 的操作。
PRODUCT_PACKAGES += \ gpio.default \
必须在out目录或者系统运行起来后查看是否有以下文件 , 如果没有则编译应用编译到系统中。
./vendor/lib64/hw/gpio.default.so
./vendor/lib/hw/gpio.default.so
总结
到这就完成了Android HAL层的GPIO驱动的实现。下一篇博客,我将介绍如何在应用层中调用GPIO接口的函数,以及如何处理GPIO的按键事件。敬请期待!