Skip to content

Commit

Permalink
cpu/native: Implement hardware SPI access (Linux)
Browse files Browse the repository at this point in the history
  • Loading branch information
fhessel committed Oct 7, 2019
1 parent 255ce7d commit 7fc3662
Show file tree
Hide file tree
Showing 7 changed files with 541 additions and 3 deletions.
4 changes: 4 additions & 0 deletions cpu/native/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += periph_pwm
# Hardware SPI access is only available on Linux hosts
ifeq ($(OS),Linux)
FEATURES_PROVIDED += periph_spi
endif
41 changes: 39 additions & 2 deletions cpu/native/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define PERIPH_CONF_H

#ifdef __cplusplus
extern "C" {
extern "C" {
#endif

/**
Expand Down Expand Up @@ -50,7 +50,6 @@

/**
* @brief xtimer configuration
* @{
*/
#define XTIMER_OVERHEAD 14

Expand Down Expand Up @@ -86,6 +85,44 @@
#define QDEC_NUMOF (8U)
#endif

/**
* @name SPI configuration (Linux host only)
* @{
*/
#if !defined(SPI_NUMOF) || defined(DOXYGEN)
/**
* @brief Amount of SPI devices
*
* Allows up to SPI_NUMOF SPI devices with each having up to SPI_MAXCS hardware
* cable select lines. Assignment to hardware devices can be configured at
* runtime using the `--spi` startup parameter.
*
* Can be overriden during compile time with a `-DSPI_NUMOF=n` flag.
*/
#define SPI_NUMOF (1U)
#endif

#if !defined(SPI_MAXCS) || defined(DOXYGEN)
/**
* @brief Maximum amount of chip select lines per bus
*
* Allows up to SPI_MAXCS hardware cable select lines per SPI device. The n-th
* hardware select line can be used with the SPI_HWCS macro.
*/
#define SPI_MAXCS (4U)
#endif

/**
* @brief Hardware chip select access macro.
*
* The amount of available hardware chip select lines depends on the SPI_MAXCS
* parameter. If the line is actually available at runtime depends of whether a
* `--spi` startup parameter with the corresponding SPI device and HWCS-line
* parameter has been given.
*/
#define SPI_HWCS(x) (x)
/** @} */

#ifdef __cplusplus
}
#endif
Expand Down
42 changes: 42 additions & 0 deletions cpu/native/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,48 @@ extern "C" {
#define PROVIDES_PM_SET_LOWEST
/** @} */

/* SPI configuration only if the module is available (=Linux) */
#if defined(MODULE_PERIPH_SPI) || defined(DOXYGEN)

/**
* @name SPI Configuration
*/

/**
* @brief Use the common `transfer_byte` SPI function
*/
#define PERIPH_SPI_NEEDS_TRANSFER_BYTE
/**
* @brief Use the common `transfer_reg` SPI function
*/
#define PERIPH_SPI_NEEDS_TRANSFER_REG
/**
* @brief Use the common `transfer_regs` SPI function
*/
#define PERIPH_SPI_NEEDS_TRANSFER_REGS

/**
* @brief Use a custom clock speed type
*/
#define HAVE_SPI_CLK_T
/**
* @brief SPI clock speed values
*
* The Linux userspace driver takes values in Hertz, which values are available
* can only be determined at runtime.
* @{
*/
typedef enum {
SPI_CLK_100KHZ = (100000U),
SPI_CLK_400KHZ = (400000U),
SPI_CLK_1MHZ = (1000000U),
SPI_CLK_5MHZ = (5000000U),
SPI_CLK_10MHZ = (10000000U)
} spi_clk_t;
/** @} */

#endif /* MODULE_PERIPH_SPI | DOXYGEN */

#ifdef __cplusplus
}
#endif
Expand Down
134 changes: 134 additions & 0 deletions cpu/native/include/spidev_linux.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright (C) 2019 Frank Hessel <frank@fhessel.de>
*
* 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_spidev_linux Linux User Mode SPI Driver
* @ingroup cpu_native
* @brief Implementation of SPI access from Linux User Space
*
* This module allows to connect a RIOT application that runs on a Linux host to
* the physical SPI bus(ses) of that host. To do so, the application has to be
* compiled for the native board in a Linux environment.
*
* SPI support is automatically included if either a module requiring the
* `PERIPH_SPI` 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 bus on the host
* machine. SPI busses are exposed as `/dev/spidevB.D` character files, where B
* is the Bus ID (MISO, MOSI and SCLK lines) and D denotes the connected device
* or hardware chip select line. Ideally, this structure should be reflected
* when mapping the device files to RIOT SPI busses.
*
* Example:
*
* ```
* $ ./riot_native_app --spi=0:0:/dev/spidev0.0 --spi=0:1:/dev/spidev0.1
* ```
*
* This will add `/dev/spidev0.0` and `/dev/spidev0.1` as SPI_DEV(0) in RIOT.
* The first device can be used with SPI_HWCS(0) as CS parameter, the second one
* with SPI_HWCS(1) as CS parameter.
*
* Multiple SPI busses can be added by increasing SPI_NUMOF in the Makefile:
* ```
* CFLAGS += -DSPI_NUMOF=n
* ```
*
* The sames goes for the SPI_MAXCS parameter that defines the maximum number of
* SPI_HWCS values per bus.
*
* Busses that aren't assigned during startup will return either SPI_NODEV or
* SPI_NOCS when accessed.
*
* If the SPI API is called with SPI_CS_UNDEF as CS parameter, the driver will
* select the file descriptor with the lowest HWCS id for that bus, but the
* actual CS line will not be pulled low (if the hardware supports this). This
* would (in principle) allow to control CS manually.
*
* @{
*
* @file
* @brief Implementation of SPI access from Linux User Space
*
* @author Frank Hessel <frank@fhessel.de>
*/

#ifndef SPIDEV_LINUX_H
#define SPIDEV_LINUX_H

#if defined(__linux__) || defined(DOXYGEN) /* Linux-only */

#include "periph/spi.h"

#include "mutex.h"
#include "periph_conf.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Status codes for SPI device setup
*/
enum {
SPI_SETUP_OK = 0, /**< parameters are sound */
SPI_SETUP_INVALID = -1, /**< invalid params or duplicate definition */
};

/**
* @brief Static runtime configuration for SPI port + CS line
*
* Contains the information that is passed by command line on startup
*/
typedef struct spidev_linux_conf {
/** Filename for a specific SPI device + CS line (like /dev/spidev0.0) */
char *device_filename[SPI_MAXCS];
} spidev_linux_conf_t;

/**
* @brief Dynamic runtime state for SPI port + CS line
*
* Contains state of the line (whether if it's opened, in use, available, ...)
*/
typedef struct spidev_linux_state {
/** Mutex for the whole bus (all CS lines) */
mutex_t lock;
/** File descriptors for each CS line on the bus */
int fd[SPI_MAXCS];
} spidev_linux_state_t;

/**
* @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] 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);

/**
* @brief Close open SPI file descriptors
*/
void spidev_linux_teardown(void);

#ifdef __cplusplus
}
#endif

#else
/* Create the error in the header file as spi.c will be compiled to late to show it */
#ifdef MODULE_PERIPH_SPI
#error "MODULE periph_spi is only available on Linux"
#endif
#endif /* defined(__linux__) || defined(DOXYGEN) */

#endif /* SPIDEV_LINUX_H */
/** @} */
11 changes: 11 additions & 0 deletions cpu/native/periph/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
#include "async_read.h"
#include "tty_uart.h"

#ifdef MODULE_PERIPH_SPI
/* Only manage SPI if it is part of the build */
#include "spidev_linux.h"
#endif

#define ENABLE_DEBUG (0)
#include "debug.h"

Expand All @@ -44,6 +49,9 @@ void pm_set_lowest(void)
void pm_off(void)
{
puts("\nnative: exiting");
#ifdef MODULE_PERIPH_SPI
spidev_linux_teardown();
#endif
real_exit(EXIT_SUCCESS);
}

Expand All @@ -52,6 +60,9 @@ void pm_reboot(void)
printf("\n\n\t\t!! REBOOT !!\n\n");

native_async_read_cleanup();
#ifdef MODULE_PERIPH_SPI
spidev_linux_teardown();
#endif

if (real_execve(_native_argv[0], _native_argv, NULL) == -1) {
err(EXIT_FAILURE, "reboot: execve");
Expand Down
Loading

0 comments on commit 7fc3662

Please sign in to comment.