Rockchip系列之客制化GPIO接口Hardware部分(3)

简介: Rockchip系列之客制化GPIO接口Hardware部分(3)

在上一篇博客中,介绍了Rockchip系列的GPIO接口如何在Driver中 注册和使用GPIO设备。在这篇博客中,将继续探讨如何在Android HAL层中实现GPIO接口的驱动,然后以及后面文章再讨论 如何在应用层中调用GPIO接口的函数。

Android HAL层的GPIO驱动

Android HAL层是Android系统和硬件之间的抽象层,它提供了一组标准的接口和结构体,用于封装不同硬件平台的具体实现。HAL层的主要作用是屏蔽硬件的差异性,使得应用层可以使用统一的API来访问不同的硬件设备。

为了在Android HAL层中实现GPIO接口的驱动,需要遵循以下几个步骤:

  1. 定义GPIO设备结构体和函数指针
  2. 实现GPIO设备的打开、关闭、读写、方向设置、按键事件注册和取消注册、获取GPIO数量等函数
  3. 注册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的按键事件。敬请期待!

相关文章
|
7月前
|
存储 Linux 开发工具
Rockchip系列之浅度分析UART接口系列(1)
Rockchip系列之浅度分析UART接口系列(1)
420 1
|
7月前
|
Java Android开发
Rockchip系列之客制化GPIO接口应用部分(5)
Rockchip系列之客制化GPIO接口应用部分(5)
72 0
|
7月前
|
存储 Android开发
Rockchip系列之客制化GPIO接口Driver部分(2)
Rockchip系列之客制化GPIO接口Driver部分(2)
121 0
|
7月前
|
存储 数据安全/隐私保护
Rockchip系列之VendorStorage 浅浅的介绍(1)
Rockchip系列之VendorStorage 浅浅的介绍(1)
365 0
|
7月前
|
内存技术
【HARDWARE】 --- SPI接口协议介绍与应用说明
【HARDWARE】 --- SPI接口协议介绍与应用说明
196 3
|
7月前
HARDWARE --- IIC协议
HARDWARE --- IIC协议
104 1
|
Perl
PYNQ-关于PYNQ的GPIO的使用(RPI接口和arduino接口)或者常用的IO设备(如UART SPI IIC TIMER)
PYNQ-关于PYNQ的GPIO的使用(RPI接口和arduino接口)或者常用的IO设备(如UART SPI IIC TIMER)
658 0
PYNQ-关于PYNQ的GPIO的使用(RPI接口和arduino接口)或者常用的IO设备(如UART SPI IIC TIMER)
|
4月前
|
编解码 数据格式
IMX6ULL开发板spi OLED驱动
【8月更文挑战第24天】本文档介绍在IMX6ULL开发板上实现SPI接口OLED显示器驱动的步骤。首先需正确连接OLED至开发板的SPI接口,包括时钟(SCLK)、数据(MOSI)及片选(CS)等线路。理解SPI协议与OLED规格也很关键:SPI为同步串行通信,涉及主从设备交互;OLED参数如分辨率、颜色深度等须明确。接下来配置IMX6ULL的SPI控制器,通过设备树设定时钟频率、数据宽度等参数,并加载内核驱动。最后编写驱动程序,初始化SPI设备、发送控制命令与数据以完成OLED初始化,并实现文本或图像的显示功能。
|
7月前
|
芯片
MTK平台驱动调试指南 GPIO设置篇
MTK平台驱动调试指南 GPIO设置篇
503 1
|
存储 传感器 Linux
PYNQ-关于PYNQ的UART的使用(RPI接口)
PYNQ-关于PYNQ的UART的使用(RPI接口)
556 0
PYNQ-关于PYNQ的UART的使用(RPI接口)