Merge pull request #12451 from benpicco/native-gpio
cpu/native: Allow Access to Hardware GPIO Pins on Linux
This commit is contained in:
commit
9f28e1deee
@ -51,6 +51,8 @@ config NATIVE_OS_DARWIN
|
||||
|
||||
config NATIVE_OS_LINUX
|
||||
bool
|
||||
select HAS_PERIPH_GPIO
|
||||
select HAS_PERIPH_GPIO_IRQ
|
||||
select HAS_PERIPH_SPI
|
||||
|
||||
config NATIVE_OS_FREEBSD
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
ifneq (,$(filter periph_spi,$(USEMODULE)))
|
||||
USEMODULE += periph_spidev_linux
|
||||
ifeq ($(OS),Linux)
|
||||
ifneq (,$(filter periph_gpio,$(USEMODULE)))
|
||||
ifeq (,$(filter periph_gpio_mock,$(USEMODULE)))
|
||||
USEMODULE += periph_gpio_linux
|
||||
endif
|
||||
endif
|
||||
ifneq (,$(filter periph_spi,$(USEMODULE)))
|
||||
USEMODULE += periph_spidev_linux
|
||||
endif
|
||||
else
|
||||
ifneq (,$(filter periph_gpio,$(USEMODULE)))
|
||||
USEMODULE += periph_gpio_mock
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (,$(filter stdio_%,$(USEMODULE)))
|
||||
USEMODULE += stdio_native
|
||||
endif
|
||||
|
||||
@ -17,7 +17,9 @@ FEATURES_PROVIDED += periph_pm
|
||||
FEATURES_PROVIDED += periph_pwm
|
||||
FEATURES_PROVIDED += ssp
|
||||
|
||||
# Access to hardware SPI bus is only supported on Linux hosts
|
||||
ifeq ($(OS),Linux)
|
||||
# Access to hardware SPI bus is only supported on Linux hosts
|
||||
FEATURES_PROVIDED += periph_spi
|
||||
# Hardware GPIO access is only available on Linux hosts
|
||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||
endif
|
||||
|
||||
74
cpu/native/include/gpiodev_linux.h
Normal file
74
cpu/native/include/gpiodev_linux.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup drivers_gpio_linux Linux User Mode GPIO Driver
|
||||
* @ingroup cpu_native
|
||||
* @brief Implementation of GPIO access from Linux User Space
|
||||
*
|
||||
* This module allows to connect a RIOT application that runs on a Linux host to
|
||||
* the physical GPIO pins of that host. To do so, the application has to be
|
||||
* compiled for the native board in a Linux environment.
|
||||
*
|
||||
* GPIO support is automatically included if either a module requiring the
|
||||
* `periph_gpio` feature is added to the application or if it is explicitly
|
||||
* listed as `FEATURES_REQUIRED` in the application's Makefile.
|
||||
*
|
||||
* At runtime, the process has to be connected to a specific GPIO bank on the host
|
||||
* machine. GPIO banks are exposed as `/dev/gpiochipN` character files, where N
|
||||
* is the bank ID to which several GPIO pins are connected.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* $ ./riot_native_app --gpio=/dev/gpiochip0 --gpio=/dev/gpiochip1
|
||||
* ```
|
||||
*
|
||||
* This will add `/dev/gpiochip0` and `/dev/gpiochip1` as PORT(0) and PORT(1) in RIOT.
|
||||
* The first pin can be used with PIN(0,0) as `gpio_t`, the second one on the first
|
||||
* port would be PIN(0,1) and so on.
|
||||
*
|
||||
* Please refer to your board's documentation for the mapping of the pins.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of GPIO access from Linux User Space
|
||||
*
|
||||
* @author Benjamin Valentin <benpicco@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GPIODEV_LINUX_H
|
||||
#define GPIODEV_LINUX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief register `/dev/gpiochip*` device to be used for GPIO
|
||||
*
|
||||
* @param[in] device The gpiochip device to open.
|
||||
*
|
||||
* @return 0 on success, error otherwise
|
||||
*/
|
||||
int gpio_linux_setup(const char* device);
|
||||
|
||||
/**
|
||||
* @brief shutdown GPIO subsystem
|
||||
*
|
||||
* This closes all GPIO fds.
|
||||
*/
|
||||
void gpio_linux_teardown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GPIODEV_LINUX_H */
|
||||
/** @} */
|
||||
@ -111,7 +111,7 @@ extern "C" {
|
||||
* `--spi` startup parameter with the corresponding SPI device and HWCS-line
|
||||
* parameter has been given.
|
||||
*/
|
||||
#define SPI_HWCS(x) (x)
|
||||
#define SPI_HWCS(x) (UINT_MAX - SPI_MAXCS + x)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -32,6 +32,61 @@ extern "C" {
|
||||
#define CPUID_LEN (4U)
|
||||
#endif
|
||||
|
||||
/* GPIO configuration only if the module is available (=Linux) */
|
||||
#if defined(MODULE_PERIPH_GPIO_LINUX) || defined(DOXYGEN)
|
||||
#include <linux/gpio.h>
|
||||
|
||||
/**
|
||||
* @name GPIO Configuration
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief The offset between Port and Pin
|
||||
*/
|
||||
#define GPIO_PORT_SHIFT (24)
|
||||
|
||||
/**
|
||||
* @brief Define a custom GPIO_PIN macro for native
|
||||
*/
|
||||
#define GPIO_PIN(port, pin) (gpio_t)((port << GPIO_PORT_SHIFT) | pin)
|
||||
|
||||
#define HAVE_GPIO_MODE_T
|
||||
#ifndef GPIOHANDLE_REQUEST_PULL_DOWN
|
||||
#define GPIOHANDLE_REQUEST_PULL_DOWN (0xFF)
|
||||
#endif
|
||||
#ifndef GPIOHANDLE_REQUEST_PULL_UP
|
||||
#define GPIOHANDLE_REQUEST_PULL_UP (0xFF)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Available pin modes
|
||||
*
|
||||
* Generally, a pin can be configured to be input or output. In output mode, a
|
||||
* pin can further be put into push-pull or open drain configuration. Though
|
||||
* this is supported by most platforms, this is not always the case, so driver
|
||||
* implementations may return an error code if a mode is not supported.
|
||||
*/
|
||||
typedef enum {
|
||||
GPIO_IN = GPIOHANDLE_REQUEST_INPUT,
|
||||
GPIO_IN_PD = GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_PULL_DOWN,
|
||||
GPIO_IN_PU = GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_PULL_UP,
|
||||
GPIO_OUT = GPIOHANDLE_REQUEST_OUTPUT,
|
||||
GPIO_OD = GPIOHANDLE_REQUEST_OPEN_DRAIN,
|
||||
GPIO_OD_PU = GPIOHANDLE_REQUEST_OPEN_DRAIN | GPIOHANDLE_REQUEST_PULL_UP
|
||||
} gpio_mode_t;
|
||||
|
||||
#define HAVE_GPIO_FLANK_T
|
||||
typedef enum {
|
||||
GPIO_FALLING = GPIOEVENT_EVENT_FALLING_EDGE, /**< emit interrupt on falling flank */
|
||||
GPIO_RISING = GPIOEVENT_EVENT_RISING_EDGE, /**< emit interrupt on rising flank */
|
||||
GPIO_BOTH = GPIO_FALLING | GPIO_RISING /**< emit interrupt on both flanks */
|
||||
} gpio_flank_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* MODULE_PERIPH_GPIO_LINUX | DOXYGEN */
|
||||
|
||||
/**
|
||||
* @brief Prevent shared timer functions from being used
|
||||
*/
|
||||
|
||||
@ -107,12 +107,12 @@ typedef struct spidev_linux_state {
|
||||
* @brief register `/dev/spidev*` device to be used for SPI
|
||||
*
|
||||
* @param[in] bus SPI bus id of the device
|
||||
* @param[in] cs CS line to configure
|
||||
* @param[in] cs_id ID of CS line to configure
|
||||
* @param[in] name path name for `/dev/spidev*` device
|
||||
* @return SPI_SETUP_OK On success
|
||||
* @return SPI_SETUP_INVALID On invalid parameters
|
||||
*/
|
||||
int spidev_linux_setup(spi_t bus, spi_cs_t cs, const char *name);
|
||||
int spidev_linux_setup(spi_t bus, unsigned cs_id, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Close open SPI file descriptors
|
||||
|
||||
318
cpu/native/periph/gpio_linux.c
Normal file
318
cpu/native/periph/gpio_linux.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
int 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, int 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 */
|
||||
|
||||
/** @} */
|
||||
@ -29,6 +29,28 @@ int gpio_init(gpio_t pin, gpio_mode_t mode) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
|
||||
gpio_cb_t cb, void *arg)
|
||||
{
|
||||
(void) pin;
|
||||
(void) mode;
|
||||
(void) flank;
|
||||
(void) cb;
|
||||
(void) arg;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void gpio_irq_enable(gpio_t pin)
|
||||
{
|
||||
(void) pin;
|
||||
}
|
||||
|
||||
void gpio_irq_disable(gpio_t pin)
|
||||
{
|
||||
(void) pin;
|
||||
}
|
||||
|
||||
int gpio_read(gpio_t pin) {
|
||||
(void) pin;
|
||||
|
||||
@ -51,4 +73,5 @@ void gpio_write(gpio_t pin, int value) {
|
||||
(void) pin;
|
||||
(void) value;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@ -27,9 +27,11 @@
|
||||
#include "tty_uart.h"
|
||||
|
||||
#ifdef MODULE_PERIPH_SPIDEV_LINUX
|
||||
/* Only manage SPI if it is part of the build */
|
||||
#include "spidev_linux.h"
|
||||
#endif
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
#include "gpiodev_linux.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
@ -51,6 +53,9 @@ void pm_off(void)
|
||||
puts("\nnative: exiting");
|
||||
#ifdef MODULE_PERIPH_SPIDEV_LINUX
|
||||
spidev_linux_teardown();
|
||||
#endif
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
gpio_linux_teardown();
|
||||
#endif
|
||||
real_exit(EXIT_SUCCESS);
|
||||
}
|
||||
@ -63,6 +68,9 @@ void pm_reboot(void)
|
||||
#ifdef MODULE_PERIPH_SPIDEV_LINUX
|
||||
spidev_linux_teardown();
|
||||
#endif
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
gpio_linux_teardown();
|
||||
#endif
|
||||
|
||||
if (real_execve(_native_argv[0], _native_argv, NULL) == -1) {
|
||||
err(EXIT_FAILURE, "reboot: execve");
|
||||
|
||||
@ -35,10 +35,38 @@
|
||||
#include "assert.h"
|
||||
#include "native_internal.h"
|
||||
#include "spidev_linux.h"
|
||||
#ifdef MODULE_PERIPH_GPIO
|
||||
#include "periph/gpio.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief true, if x is a hardware-based chip select line
|
||||
*/
|
||||
#define IS_HW_CS(x) (x < UINT_MAX && x >= UINT_MAX - SPI_MAXCS )
|
||||
|
||||
/**
|
||||
* @brief true, if x is a gpio-based chip select line
|
||||
*/
|
||||
#ifdef MODULE_PERIPH_GPIO
|
||||
#define IS_GPIO_CS(x) (x < UINT_MAX - SPI_MAXCS)
|
||||
#else
|
||||
#define IS_GPIO_CS(x) (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief true, if x is a valid chip select line (either GPIO or HW)
|
||||
*/
|
||||
#define IS_VALID_CS(x) (IS_HW_CS(x) || IS_GPIO_CS(x))
|
||||
|
||||
/**
|
||||
* @brief If IS_HW_CS(x), this converts x to the corresponding CS ID for file
|
||||
* descriptor selection, basically a reverse SPI_HWCS(x)
|
||||
*/
|
||||
#define CS_TO_CSID(x) (x - (UINT_MAX - SPI_MAXCS))
|
||||
|
||||
/**
|
||||
* @brief Holds the configuration for each SPI device (pathnames)
|
||||
*/
|
||||
@ -91,16 +119,16 @@ static int spidev_get_first_fd(spidev_linux_state_t *state)
|
||||
return fd;
|
||||
}
|
||||
|
||||
int spidev_linux_setup(spi_t bus, spi_cs_t cs, const char *name)
|
||||
int spidev_linux_setup(spi_t bus, unsigned cs_id, const char *name)
|
||||
{
|
||||
if (bus >= SPI_NUMOF || cs >= SPI_MAXCS) {
|
||||
if (bus >= SPI_NUMOF || cs_id >= SPI_MAXCS) {
|
||||
return SPI_SETUP_INVALID;
|
||||
}
|
||||
spidev_linux_conf_t *conf = &(device_conf[bus]);
|
||||
if (conf->device_filename[cs] != NULL) {
|
||||
if (conf->device_filename[cs_id] != NULL) {
|
||||
return SPI_SETUP_INVALID;
|
||||
}
|
||||
device_conf[bus].device_filename[cs] = strndup(name, PATH_MAX - 1);
|
||||
device_conf[bus].device_filename[cs_id] = strndup(name, PATH_MAX - 1);
|
||||
return SPI_SETUP_OK;
|
||||
}
|
||||
|
||||
@ -119,39 +147,52 @@ void spidev_linux_teardown(void)
|
||||
|
||||
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
|
||||
{
|
||||
DEBUG("spi_acquire(%d, %d, 0x%02x, %d)\n", bus, cs, mode, clk);
|
||||
DEBUG("spi_acquire(%u, %u, 0x%02x, %d)\n", bus, cs, mode, clk);
|
||||
if (bus >= SPI_NUMOF) {
|
||||
return SPI_NODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&(device_state[bus].lock));
|
||||
|
||||
/* If true, the spidev API controls the chip select line. We need this here
|
||||
* if an SPI_HWCS parameter is used. If cs==SPI_CS_UNDEF, the driver should
|
||||
* not care about CS at all (use_hwcs=true), if cs is a GPIO_PIN and the
|
||||
* GPIO module is enabled, the driver controls the pin through that module.
|
||||
*/
|
||||
bool use_hwcs = false;
|
||||
int fd = -1;
|
||||
if (cs != SPI_CS_UNDEF) {
|
||||
if (IS_HW_CS(cs)) {
|
||||
use_hwcs = true;
|
||||
if (cs > SPI_MAXCS || device_state[bus].fd[cs] < 0) {
|
||||
DEBUG("spi_acquire: No fd for %d:%d\n", bus, cs);
|
||||
unsigned csid = CS_TO_CSID(cs);
|
||||
if (device_state[bus].fd[csid] < 0) {
|
||||
DEBUG("spi_acquire: No fd for %u:%u\n", bus, csid);
|
||||
mutex_unlock(&(device_state[bus].lock));
|
||||
return SPI_NOCS;
|
||||
}
|
||||
fd = device_state[bus].fd[cs];
|
||||
DEBUG("spi_acquire: Using %d:%d with HWCS (-> fd 0x%x)\n", bus, cs, fd);
|
||||
fd = device_state[bus].fd[csid];
|
||||
DEBUG("spi_acquire: Using %u:%u with HWCS (-> fd 0x%x)\n", bus, csid, fd);
|
||||
}
|
||||
else {
|
||||
else if (IS_GPIO_CS(cs) || cs == SPI_CS_UNDEF) {
|
||||
fd = spidev_get_first_fd(&(device_state[bus]));
|
||||
if (fd < 0) {
|
||||
mutex_unlock(&(device_state[bus].lock));
|
||||
return SPI_NOCS;
|
||||
}
|
||||
DEBUG("spi_acquire: Using SPI_NO_CS (-> fd 0x%x)\n", fd);
|
||||
DEBUG("spi_acquire: Using SPI_CS_UNDEF (-> fd 0x%x)\n", fd);
|
||||
}
|
||||
else {
|
||||
DEBUG("spi_acquire: Invalid CS parameter\n");
|
||||
mutex_unlock(&(device_state[bus].lock));
|
||||
return SPI_NOCS;
|
||||
}
|
||||
|
||||
int res = spi_set_params(fd, use_hwcs, mode, clk);
|
||||
if (res < 0) {
|
||||
DEBUG("spi_acquire: set_params failed for %d:%d\n", bus, cs);
|
||||
DEBUG("spi_acquire: set_params failed\n");
|
||||
mutex_unlock(&(device_state[bus].lock));
|
||||
}
|
||||
|
||||
DEBUG("spi_acquire: %d:%d acquired\n", bus, cs);
|
||||
DEBUG("spi_acquire: bus %u acquired\n", bus);
|
||||
return SPI_OK;
|
||||
}
|
||||
|
||||
@ -162,26 +203,26 @@ void spi_init(spi_t bus)
|
||||
spidev_linux_conf_t *conf = &(device_conf[bus]);
|
||||
|
||||
spidev_init_device_state(state);
|
||||
DEBUG("spi_init: init bus %d\n", bus);
|
||||
DEBUG("spi_init: init bus %u\n", bus);
|
||||
for (spi_cs_t cs = 0; cs < SPI_MAXCS; cs++) {
|
||||
if (conf->device_filename[cs] != NULL) {
|
||||
int fd = real_open(conf->device_filename[cs], O_RDWR);
|
||||
if (fd < 0) {
|
||||
/* Add a printf instead of only asserting to show invalid bus */
|
||||
real_printf(
|
||||
"Cannot acquire %s for spidev%d:%d\n",
|
||||
"Cannot acquire %s for spidev%u:%u\n",
|
||||
conf->device_filename[cs],
|
||||
bus,
|
||||
cs
|
||||
);
|
||||
assert(false);
|
||||
}
|
||||
DEBUG("spi_init: %d:%d %s (fd 0x%x)\n", bus, cs,
|
||||
DEBUG("spi_init: %u:%u %s (fd 0x%x)\n", bus, cs,
|
||||
conf->device_filename[cs], fd);
|
||||
state->fd[cs] = fd;
|
||||
}
|
||||
else {
|
||||
DEBUG("spi_init: %d:%d Unused\n", bus, cs);
|
||||
DEBUG("spi_init: %u:%u Unused\n", bus, cs);
|
||||
}
|
||||
}
|
||||
DEBUG("spi_init: done\n");
|
||||
@ -192,12 +233,20 @@ int spi_init_cs(spi_t bus, spi_cs_t cs)
|
||||
if (bus >= SPI_NUMOF) {
|
||||
return SPI_NODEV;
|
||||
}
|
||||
else if (cs != SPI_CS_UNDEF && cs >= SPI_MAXCS) {
|
||||
else if (!IS_VALID_CS(cs) && cs != SPI_CS_UNDEF) {
|
||||
return SPI_NOCS;
|
||||
}
|
||||
else if (device_state[bus].fd[cs] < 0) {
|
||||
else if (IS_HW_CS(cs) && device_state[bus].fd[CS_TO_CSID(cs)] < 0) {
|
||||
return SPI_NOCS;
|
||||
}
|
||||
else if (IS_GPIO_CS(cs)) {
|
||||
#ifdef MODULE_PERIPH_GPIO
|
||||
if (gpio_init(cs, GPIO_OUT) < 0) {
|
||||
return SPI_NOCS;
|
||||
}
|
||||
gpio_set(cs);
|
||||
#endif
|
||||
}
|
||||
return SPI_OK;
|
||||
}
|
||||
|
||||
@ -209,7 +258,7 @@ void spi_init_pins(spi_t bus)
|
||||
|
||||
void spi_release(spi_t bus)
|
||||
{
|
||||
DEBUG("spi_release(%d)\n", bus);
|
||||
DEBUG("spi_release(%u)\n", bus);
|
||||
if (bus < SPI_NUMOF) {
|
||||
mutex_unlock(&(device_state[bus].lock));
|
||||
}
|
||||
@ -232,15 +281,17 @@ static int spi_set_params(int fd, bool hwcs, spi_mode_t mode, spi_clk_t clk)
|
||||
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
||||
const void *out, void *in, size_t len)
|
||||
{
|
||||
if (bus >= SPI_NUMOF || (cs != SPI_CS_UNDEF && cs >= SPI_MAXCS)) {
|
||||
if (bus >= SPI_NUMOF || (!IS_VALID_CS(cs) && cs != SPI_CS_UNDEF)) {
|
||||
DEBUG("spi_transfer_bytes: invalid bus/cs. Skipping transfer.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = (cs == SPI_CS_UNDEF) ?
|
||||
spidev_get_first_fd(&(device_state[bus])) :
|
||||
device_state[bus].fd[cs];
|
||||
int fd = IS_HW_CS(cs) ?
|
||||
device_state[bus].fd[CS_TO_CSID(cs)] :
|
||||
spidev_get_first_fd(&(device_state[bus]));
|
||||
|
||||
if (fd < 0) {
|
||||
DEBUG("spi_transfer_bytes: no suitable fd. Skipping transfer.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -263,12 +314,25 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
||||
.speed_hz = 0,
|
||||
};
|
||||
|
||||
#ifdef MODULE_PERIPH_GPIO
|
||||
if (IS_GPIO_CS(cs)) {
|
||||
DEBUG("spi_transfer_bytes: using GPIO-based CS\n");
|
||||
gpio_clear(cs);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (real_ioctl(fd, SPI_IOC_MESSAGE(1), &spi_tf) < 0) {
|
||||
DEBUG("spi_transfer_bytes: ioctl failed\n");
|
||||
}
|
||||
else {
|
||||
DEBUG("\nspi_transfer_bytes: transferred %d bytes\n", len);
|
||||
DEBUG("spi_transfer_bytes: transferred %u bytes\n", len);
|
||||
}
|
||||
|
||||
#ifdef MODULE_PERIPH_GPIO
|
||||
if (IS_GPIO_CS(cs) && !cont) {
|
||||
gpio_set(cs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* MODULE_PERIPH_SPIDEV_LINUX */
|
||||
|
||||
@ -81,6 +81,9 @@ netdev_tap_params_t netdev_tap_params[NETDEV_TAP_MAX];
|
||||
#ifdef MODULE_PERIPH_SPIDEV_LINUX
|
||||
#include "spidev_linux.h"
|
||||
#endif
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
#include "gpiodev_linux.h"
|
||||
#endif
|
||||
#ifdef MODULE_SOCKET_ZEP
|
||||
#include "socket_zep_params.h"
|
||||
|
||||
@ -92,6 +95,9 @@ extern char eeprom_file[EEPROM_FILEPATH_MAX_LEN];
|
||||
#endif
|
||||
|
||||
static const char short_opts[] = ":hi:s:deEoc:"
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
"g:"
|
||||
#endif
|
||||
#ifdef MODULE_MTD_NATIVE
|
||||
"m:"
|
||||
#endif
|
||||
@ -115,6 +121,9 @@ static const struct option long_opts[] = {
|
||||
{ "stderr-noredirect", no_argument, NULL, 'E' },
|
||||
{ "stdout-pipe", no_argument, NULL, 'o' },
|
||||
{ "uart-tty", required_argument, NULL, 'c' },
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
{ "gpio", required_argument, NULL, 'g' },
|
||||
#endif
|
||||
#ifdef MODULE_MTD_NATIVE
|
||||
{ "mtd", required_argument, NULL, 'm' },
|
||||
#endif
|
||||
@ -260,6 +269,9 @@ void usage_exit(int status)
|
||||
}
|
||||
#endif
|
||||
real_printf(" [-i <id>] [-d] [-e|-E] [-o] [-c <tty>]\n");
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
real_printf(" [-g <gpiochip>]\n");
|
||||
#endif
|
||||
#if defined(MODULE_SOCKET_ZEP) && (SOCKET_ZEP_MAX > 0)
|
||||
real_printf(" -z [[<laddr>:<lport>,]<raddr>:<rport>]\n");
|
||||
for (int i = 0; i < SOCKET_ZEP_MAX - 1; i++) {
|
||||
@ -295,6 +307,11 @@ void usage_exit(int status)
|
||||
" -c <tty>, --uart-tty=<tty>\n"
|
||||
" specify TTY device for UART. This argument can be used multiple\n"
|
||||
" times (up to UART_NUMOF)\n"
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
" -g <gpio>, --gpio=<gpio>\n"
|
||||
" specify gpiochip device for GPIO access. This argument can be used multiple times.\n"
|
||||
" Example: --gpio=/dev/gpiochip0 uses gpiochip0 for port 0\n"
|
||||
#endif
|
||||
#if defined(MODULE_SOCKET_ZEP) && (SOCKET_ZEP_MAX > 0)
|
||||
" -z [<laddr>:<lport>,]<raddr>:<rport> --zep=[<laddr>:<lport>,]<raddr>:<rport>\n"
|
||||
" provide a ZEP interface with local address and port (<laddr>, <lport>)\n"
|
||||
@ -463,6 +480,13 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e
|
||||
}
|
||||
force_stderr = true;
|
||||
break;
|
||||
#ifdef MODULE_PERIPH_GPIO_LINUX
|
||||
case 'g':
|
||||
if (gpio_linux_setup(optarg) < 0) {
|
||||
usage_exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 'o':
|
||||
stdouttype = _STDIOTYPE_FILE;
|
||||
break;
|
||||
|
||||
@ -25,8 +25,8 @@
|
||||
|
||||
#define AES_DEBUG(...) DEBUG("[at86rf2xx_aes_spi]: "__VA_ARGS__)
|
||||
|
||||
#define AT86RF2XX_CMD_SRAM_READ (0b00000000)
|
||||
#define AT86RF2XX_CMD_SRAM_WRITE (0b01000000)
|
||||
#define AT86RF2XX_CMD_SRAM_READ (0x00)
|
||||
#define AT86RF2XX_CMD_SRAM_WRITE (0x40)
|
||||
|
||||
static inline
|
||||
void at86rf2xx_spi_get_bus(const at86rf2xx_t *dev)
|
||||
|
||||
@ -117,10 +117,10 @@ static void _ecb(at86rf2xx_t *dev,
|
||||
uint8_t nblocks)
|
||||
{
|
||||
at86rf2xx_aes_key_write_encrypt(dev, key);
|
||||
at86rf2xx_aes_ecb_encrypt(dev, cipher, key, plain, nblocks);
|
||||
at86rf2xx_aes_ecb_encrypt(dev, cipher, key, (void*)plain, nblocks);
|
||||
memset(plain, 0, AT86RF2XX_AES_BLOCK_SIZE * nblocks);
|
||||
at86rf2xx_aes_key_write_decrypt(dev, key);
|
||||
at86rf2xx_aes_ecb_decrypt(dev, plain, key, cipher, nblocks);
|
||||
at86rf2xx_aes_ecb_decrypt(dev, plain, key, (void*)cipher, nblocks);
|
||||
}
|
||||
|
||||
static void _cbc(at86rf2xx_t *dev,
|
||||
@ -131,10 +131,10 @@ static void _cbc(at86rf2xx_t *dev,
|
||||
uint8_t nblocks)
|
||||
{
|
||||
at86rf2xx_aes_key_write_encrypt(dev, key);
|
||||
at86rf2xx_aes_cbc_encrypt(dev, cipher, key, iv, plain, nblocks);
|
||||
at86rf2xx_aes_cbc_encrypt(dev, cipher, key, iv, (void*)plain, nblocks);
|
||||
memset(plain, 0, AT86RF2XX_AES_BLOCK_SIZE * nblocks);
|
||||
at86rf2xx_aes_key_write_decrypt(dev, key);
|
||||
at86rf2xx_aes_cbc_decrypt(dev, plain, key, iv, cipher, nblocks);
|
||||
at86rf2xx_aes_cbc_decrypt(dev, plain, key, iv, (void*)cipher, nblocks);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
||||
@ -2,6 +2,11 @@ include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += grove_ledbar
|
||||
|
||||
# disable native GPIOs for automatic test
|
||||
ifeq (native,$(BOARD))
|
||||
USEMODULE += periph_gpio_mock
|
||||
endif
|
||||
|
||||
# set default device parameters in case they are undefined
|
||||
# the following params are for board pba-d-01-kw2x and pins PA01 and PA02
|
||||
TEST_GROVE_LEDBAR_CLK ?= GPIO_PIN\(0,1\)
|
||||
|
||||
@ -2,6 +2,11 @@ include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += hd44780
|
||||
|
||||
# disable native GPIOs for automatic test
|
||||
ifeq (native,$(BOARD))
|
||||
USEMODULE += periph_gpio_mock
|
||||
endif
|
||||
|
||||
# Fails on esp32 because the driver defines default GPIOs that are used for the
|
||||
# SPI flash interface.
|
||||
TEST_ON_CI_BLACKLIST += esp32-wroom-32
|
||||
|
||||
@ -2,6 +2,11 @@ include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += my9221
|
||||
|
||||
# disable native GPIOs for automatic test
|
||||
ifeq (native,$(BOARD))
|
||||
USEMODULE += periph_gpio_mock
|
||||
endif
|
||||
|
||||
# set default device parameters in case they are undefined
|
||||
# the following params are for board pba-d-01-kw2x and pins PA01 and PA02
|
||||
TEST_MY9221_CLK ?= GPIO_PIN\(0,1\)
|
||||
|
||||
@ -7,6 +7,11 @@ USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += benchmark
|
||||
|
||||
# disable native GPIOs for automatic test
|
||||
ifeq (native,$(BOARD))
|
||||
USEMODULE += periph_gpio_mock
|
||||
endif
|
||||
|
||||
BOARDS_BENCH_PORT_1 = \
|
||||
slstk3402a \
|
||||
z1 \
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user