mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
c2c2cc8592
Since https://github.com/RIOT-OS/RIOT/pull/20935 gpio_write() uses a `bool` instead of an `int`. This does the same treatment for `gpio_read()`. This does indeed add an instruction to `gpio_read()` implementations. However, users caring about an instruction more are better served with `gpio_ll_read()` anyway. And `gpio_read() == 1` is often seen in newcomer's code, which would now work as expected.
319 lines
6.1 KiB
C
319 lines
6.1 KiB
C
/*
|
|
* Copyright (C) 2019 Benjamin Valentin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
|
* Public License v2.1. See the file LICENSE in the top level directory for
|
|
* more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_native
|
|
* @ingroup drivers_periph_gpio
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief native GPIO implementation
|
|
*
|
|
* @author Benjamin Valentin <benpicco@googlemail.com>
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/gpio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "native_internal.h"
|
|
#include "periph/gpio.h"
|
|
|
|
typedef struct {
|
|
gpio_cb_t cb;
|
|
void *arg;
|
|
bool enabled;
|
|
} native_gpio_cb_t;
|
|
|
|
typedef struct {
|
|
int fd;
|
|
unsigned num_pins;
|
|
int *pins;
|
|
#ifdef MODULE_PERIPH_GPIO_IRQ
|
|
native_gpio_cb_t **cbs;
|
|
#endif
|
|
} native_port_t;
|
|
|
|
static native_port_t *ports;
|
|
static unsigned port_numof;
|
|
|
|
#define _port(p) (p >> GPIO_PORT_SHIFT)
|
|
#define _pin(p) (p & ((1 << GPIO_PORT_SHIFT) - 1))
|
|
|
|
int gpio_linux_setup(const char* gpiochip)
|
|
{
|
|
struct gpiochip_info info;
|
|
int fd = real_open(gpiochip, O_RDWR);
|
|
|
|
if (fd < 0) {
|
|
real_printf("GPIO: cannot open %s\n", gpiochip);
|
|
return fd;
|
|
}
|
|
|
|
if (real_ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info) < 0) {
|
|
real_close(fd);
|
|
real_printf("GPIO: can not query %s\n", gpiochip);
|
|
return -ENODEV;
|
|
}
|
|
|
|
printf("GPIO: found %d pins on %s [%s]\n", info.lines, info.name, info.label);
|
|
|
|
void *tmp = reallocarray(ports, port_numof + 1, sizeof(*ports));
|
|
|
|
if (tmp == NULL) {
|
|
real_close(fd);
|
|
real_printf("GPIO: out of memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ports = tmp;
|
|
ports[port_numof].fd = fd;
|
|
ports[port_numof].num_pins = info.lines;
|
|
ports[port_numof].pins = calloc(info.lines, sizeof(*ports[0].pins));
|
|
#ifdef MODULE_PERIPH_GPIO_IRQ
|
|
ports[port_numof].cbs = calloc(info.lines, sizeof(*ports[0].cbs));
|
|
#endif
|
|
++port_numof;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gpio_linux_teardown(void)
|
|
{
|
|
for (unsigned i = 0; i < port_numof; ++i) {
|
|
for (unsigned j = 0; j < ports[i].num_pins; ++j) {
|
|
|
|
if (ports[i].pins[j] > 0) {
|
|
real_close(ports[i].pins[j]);
|
|
}
|
|
#ifdef MODULE_PERIPH_GPIO_IRQ
|
|
if (ports[i].cbs[j]) {
|
|
real_free(ports[i].cbs[j]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ports[i].pins) {
|
|
real_free(ports[i].pins);
|
|
}
|
|
|
|
if (ports[i].fd) {
|
|
real_close(ports[i].fd);
|
|
}
|
|
#ifdef MODULE_PERIPH_GPIO_IRQ
|
|
if (ports[i].cbs) {
|
|
real_free(ports[i].cbs);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ports) {
|
|
real_free(ports);
|
|
ports = NULL;
|
|
}
|
|
|
|
port_numof = 0;
|
|
}
|
|
|
|
int gpio_init(gpio_t pin, gpio_mode_t mode)
|
|
{
|
|
int res;
|
|
const unsigned p = _pin(pin);
|
|
struct gpiohandle_request req = {
|
|
.lineoffsets[0] = p,
|
|
.flags = mode,
|
|
.lines = 1
|
|
};
|
|
native_port_t *port;
|
|
|
|
if (mode == 0xFF) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (_port(pin) >= port_numof) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
port = &ports[_port(pin)];
|
|
|
|
if (p >= port->num_pins) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* if the pin is already configured, close it first */
|
|
if (port->pins[p] > 0) {
|
|
real_close(port->pins[p]);
|
|
port->pins[p] = 0;
|
|
}
|
|
|
|
res = real_ioctl(port->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
port->pins[p] = req.fd;
|
|
return 0;
|
|
}
|
|
|
|
bool gpio_read(gpio_t pin)
|
|
{
|
|
struct gpiohandle_data data;
|
|
|
|
if (_port(pin) >= port_numof) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
int fd = ports[_port(pin)].pins[_pin(pin)];
|
|
|
|
if (fd <= 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
real_ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
|
|
|
|
return data.values[0];
|
|
}
|
|
|
|
static void _set(gpio_t pin, uint8_t val)
|
|
{
|
|
int fd = ports[_port(pin)].pins[_pin(pin)];
|
|
|
|
if (fd > 0) {
|
|
real_ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &val);
|
|
}
|
|
}
|
|
|
|
void gpio_set(gpio_t pin)
|
|
{
|
|
_set(pin, 1);
|
|
}
|
|
|
|
void gpio_clear(gpio_t pin)
|
|
{
|
|
_set(pin, 0);
|
|
}
|
|
|
|
void gpio_toggle(gpio_t pin)
|
|
{
|
|
_set(pin, !gpio_read(pin));
|
|
}
|
|
|
|
void gpio_write(gpio_t pin, bool value)
|
|
{
|
|
_set(pin, value);
|
|
}
|
|
|
|
#ifdef MODULE_PERIPH_GPIO_IRQ
|
|
#include "async_read.h"
|
|
|
|
static void _async_read_wrapper(int fd, void *arg) {
|
|
native_gpio_cb_t *cb = arg;
|
|
|
|
struct gpioevent_data event;
|
|
real_read(fd, &event, sizeof(event));
|
|
|
|
if (cb->cb && cb->enabled) {
|
|
cb->cb(cb->arg);
|
|
}
|
|
|
|
native_async_read_continue(fd);
|
|
}
|
|
|
|
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
|
|
gpio_cb_t cb, void *arg)
|
|
{
|
|
int res;
|
|
const unsigned p = _pin(pin);
|
|
struct gpioevent_request req = {
|
|
.lineoffset = p,
|
|
.handleflags = mode,
|
|
.eventflags = flank
|
|
};
|
|
native_port_t *port;
|
|
|
|
if (mode == 0xFF) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (_port(pin) >= port_numof) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
port = &ports[_port(pin)];
|
|
|
|
if (p >= port->num_pins) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* if the pin is already configured, close it first */
|
|
if (port->pins[p] > 0) {
|
|
real_close(port->pins[p]);
|
|
port->pins[p] = 0;
|
|
}
|
|
|
|
res = real_ioctl(port->fd, GPIO_GET_LINEEVENT_IOCTL, &req);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
port->pins[p] = req.fd;
|
|
|
|
if (port->cbs[p] == NULL) {
|
|
port->cbs[p] = malloc(sizeof(native_gpio_cb_t));
|
|
}
|
|
|
|
port->cbs[p]->cb = cb;
|
|
port->cbs[p]->arg = arg;
|
|
port->cbs[p]->enabled = true;
|
|
|
|
native_async_read_setup();
|
|
native_async_read_add_int_handler(req.fd, port->cbs[p], _async_read_wrapper);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _set_irq_enabled(gpio_t pin, bool enabled)
|
|
{
|
|
native_port_t *port;
|
|
const unsigned p = _pin(pin);
|
|
|
|
if (_port(pin) >= port_numof) {
|
|
return;
|
|
}
|
|
|
|
port = &ports[_port(pin)];
|
|
|
|
if (p >= port->num_pins) {
|
|
return;
|
|
}
|
|
|
|
port->cbs[p]->enabled = enabled;
|
|
}
|
|
|
|
void gpio_irq_enable(gpio_t pin)
|
|
{
|
|
_set_irq_enabled(pin, true);
|
|
}
|
|
|
|
void gpio_irq_disable(gpio_t pin)
|
|
{
|
|
_set_irq_enabled(pin, false);
|
|
}
|
|
|
|
#endif /* MODULE_PERIPH_GPIO_IRQ */
|
|
|
|
/** @} */
|