Add SPI bus framework

User API is compatible to Linux userspace API.  New test libtests/spi01.

Update #2776.
This commit is contained in:
Alexander Krutwig
2016-09-12 15:00:46 +02:00
committed by Sebastian Huber
parent c186de5cd1
commit a42be52bbf
11 changed files with 1199 additions and 0 deletions

View File

@@ -11,11 +11,19 @@ include_dev_i2c_HEADERS += include/dev/i2c/gpio-nxp-pca9535.h
include_dev_i2c_HEADERS += include/dev/i2c/i2c.h
include_dev_i2c_HEADERS += include/dev/i2c/switch-nxp-pca9548a.h
include_dev_spidir = $(includedir)/dev/spi
include_dev_spi_HEADERS =
include_dev_spi_HEADERS += include/dev/spi/spi.h
include_linuxdir = $(includedir)/linux
include_linux_HEADERS =
include_linux_HEADERS += include/linux/i2c.h
include_linux_HEADERS += include/linux/i2c-dev.h
include_linux_spidir = $(includedir)/linux/spi
include_linux_spi_HEADERS =
include_linux_spi_HEADERS += include/linux/spi/spidev.h
noinst_LIBRARIES = libdev.a
libdev_a_SOURCES =
@@ -24,6 +32,7 @@ libdev_a_SOURCES += i2c/gpio-nxp-pca9535.c
libdev_a_SOURCES += i2c/i2c-bus.c
libdev_a_SOURCES += i2c/i2c-dev.c
libdev_a_SOURCES += i2c/switch-nxp-pca9548a.c
libdev_a_SOURCES += spi/spi-bus.c
include $(srcdir)/preinstall.am
include $(top_srcdir)/automake/local.am

View File

@@ -0,0 +1,216 @@
/**
* @file
*
* @brief Serial Peripheral Interface (SPI) Driver API
*
* @ingroup SPI
*/
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef _DEV_SPI_SPI_H
#define _DEV_SPI_SPI_H
#include <linux/spi/spidev.h>
#include <rtems.h>
#include <rtems/seterr.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct spi_ioc_transfer spi_ioc_transfer;
typedef struct spi_bus spi_bus;
/**
* @defgroup SPI Serial Peripheral Interface (SPI) Driver
*
* @brief Serial Peripheral Interface (SPI) bus driver support.
*
* @{
*/
/**
* @brief Obtains the bus.
*
* This command has no argument.
*/
#define SPI_BUS_OBTAIN _IO(SPI_IOC_MAGIC, 13)
/**
* @brief Releases the bus.
*
* This command has no argument.
*/
#define SPI_BUS_RELEASE _IO(SPI_IOC_MAGIC, 23)
/**
* @brief SPI bus control.
*/
struct spi_bus {
/**
* @brief Transfers SPI messages.
*
* @param[in] bus The bus control.
* @param[in] msgs The messages to transfer.
* @param[in] msg_count The count of messages to transfer. It must be
* positive.
*
* @retval 0 Successful operation.
* @retval negative Negative error number in case of an error.
*/
int (*transfer)(spi_bus *bus, const spi_ioc_transfer *msgs, uint32_t msg_count);
/**
* @brief Checks if maximum speed and bits per word are in a valid range
* for the device
*
* @param[in] bus The bus control.
*
* @retval 0 Successful operation.
* @retval negative Negative error number in case of an error.
*/
int (*setup)(spi_bus *bus);
/**
* @brief Destroys the bus.
*
* @param[in] bus The bus control.
*/
void (*destroy)(spi_bus *bus);
/**
* @brief Mutex to protect the bus access.
*/
rtems_id mutex;
/**
* @brief Maximum Speed in Hz
*/
uint32_t max_speed_hz;
/**
* @brief Indicates the speed of the current device message.
*/
uint32_t speed_hz;
/**
* @brief Indicates if chip select must be set high after transfer.
*/
bool cs_change;
/**
* @brief Indicates which device is selected by chip select
*/
uint8_t cs;
/**
* @brief Indicates the bits per word used on the device.
*/
uint8_t bits_per_word;
/**
* @brief Indicates if LSB is supposed to be transmitted first.
*/
bool lsb_first;
/**
* @brief Current mode.
*/
uint32_t mode;
/**
* @brief Indicates the delay between transfers on different chip select
* devices.
*/
uint16_t delay_usecs;
};
/**
* @brief Initializes a bus control.
*
* After a sucessful initialization the bus control must be destroyed via
* spi_bus_destroy(). A registered bus control will be automatically destroyed
* in case the device file is unlinked. Make sure to call spi_bus_destroy() in
* a custom destruction handler.
*
* @param[in] bus The bus control.
*
* @retval 0 Successful operation.
* @retval -1 An error occurred. The errno is set to indicate the error.
*
* @see spi_bus_register()
*/
int spi_bus_init(spi_bus *bus);
/**
* @brief Allocates a bus control from the heap and initializes it.
*
* After a sucessful allocation and initialization the bus control must be
* destroyed via spi_bus_destroy_and_free(). A registered bus control will be
* automatically destroyed in case the device file is unlinked. Make sure to
* call spi_bus_destroy_and_free() in a custom destruction handler.
*
* @param[in] size The size of the bus control. This enables the addition of
* bus controller specific data to the base bus control. The bus control is
* zero initialized.
*
* @retval non-NULL The new bus control.
* @retval NULL An error occurred. The errno is set to indicate the error.
*
* @see spi_bus_register()
*/
spi_bus *spi_bus_alloc_and_init(size_t size);
/**
* @brief Destroys a bus control.
*
* @param[in] bus The bus control.
*/
void spi_bus_destroy(spi_bus *bus);
/**
* @brief Destroys a bus control and frees its memory.
*
* @param[in] bus The bus control.
*/
void spi_bus_destroy_and_free(spi_bus *bus);
/**
* @brief Registers a bus control.
*
* This function claims ownership of the bus control regardless if the
* registration is successful or not.
*
* @param[in] bus The bus control.
* @param[in] bus_path The path to the bus device file.
*
* @retval 0 Successful operation.
* @retval -1 An error occurred. The errno is set to indicate the error.
*/
int spi_bus_register(
spi_bus *bus,
const char *bus_path
);
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _DEV_SPI_SPI_H */

View File

@@ -0,0 +1,268 @@
/**
* @file
*
* @brief RTEMS Port of Linux SPI API
*
* @ingroup SPILinux
*/
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef _UAPI_LINUX_SPI_H
#define _UAPI_LINUX_SPI_H
#include <sys/ioccom.h>
#include <stddef.h>
#include <stdint.h>
/**
* @defgroup SPILinux Linux SPI User-Space API
*
* @ingroup SPI
*
* @brief RTEMS port of Linux SPI user-space API.
*
* Additional documentation is available through the Linux sources, see
*
* /usr/src/linux/include/uapi/linux/spidev.h.
*
* @{
*/
/**
* @name SPI Transfer Flags
*
* @{
*/
/**
* @brief SPI transfer flag which sets the clock phase.
*/
#define SPI_CPHA 0x01
/**
* @brief SPI transfer flag which sets the clock polarity.
*/
#define SPI_CPOL 0x02
/**
* @brief SPI transfer flag which sets SPI Mode 0 (clock starts low, sample on
* leading edge).
*/
#define SPI_MODE_0 0
/**
* @brief SPI transfer flag which sets SPI Mode 0 (clock starts low, sample on
* trailing edge).
*/
#define SPI_MODE_1 SPI_CPHA
/**
* @brief SPI transfer flag which sets SPI Mode 0 (clock starts high, sample on
* leading edge).
*/
#define SPI_MODE_2 SPI_CPOL
/**
* @brief SPI transfer flag which sets SPI Mode 0 (clock starts high, sample on
* trailing edge).
*/
#define SPI_MODE_3 (SPI_CPOL | SPI_CPHA)
/**
* @brief SPI transfer flag which selects the device by setting the chip select
* line.
*/
#define SPI_CS_HIGH 0x04
/**
* @brief SPI transfer flag which triggers data transmission with the LSB being
* sent first.
*/
#define SPI_LSB_FIRST 0x08
/**
* @brief SPI transfer flag which uses a shared wire for master input/slave
* output as well as master output/slave input.
*/
#define SPI_3WIRE 0x10
/**
* @brief SPI transfer flag which initiates the loopback mode.
*/
#define SPI_LOOP 0x20
/**
* @brief SPI transfer flag which indicates that no chip select is needed due to
* only one device on the bus.
*/
#define SPI_NO_CS 0x40
/**
* @brief SPI transfer flag which pulls the slave to low level during pause.
*/
#define SPI_READY 0x80
/**
* @brief SPI transfer flag which sets up dual mode for transmission.
*/
#define SPI_TX_DUAL 0x100
/**
* @brief SPI transfer flag which sets up quad mode for transmission.
*/
#define SPI_TX_QUAD 0x200
/**
* @brief SPI transfer flag which sets up dual mode for reception.
*/
#define SPI_RX_DUAL 0x400
/**
* @brief SPI transfer flag which sets up quad mode for reception.
*/
#define SPI_RX_QUAD 0x800
/** @} */
#define SPI_IOC_MAGIC 's'
/**
* @brief SPI transfer message.
*/
struct spi_ioc_transfer {
/**
* @brief Buffer for receive data.
*/
void *rx_buf;
/**
* @brief Buffer for transmit data.
*/
const void *tx_buf;
/**
* @brief Length of receive and transmit buffers in bytes.
*/
size_t len;
/**
* @brief Sets the bit-rate of the device.
*/
uint32_t speed_hz;
/**
* @brief Sets the delay after a transfer before the chip select status is
* changed and the next transfer is triggered.
*/
uint16_t delay_usecs;
/**
* @brief Sets the device wordsize.
*/
uint8_t bits_per_word;
/**
* @brief If true, device is deselected after transfer ended and before a new
* transfer is started.
*/
uint8_t cs_change;
/**
* @brief Amount of bits that are used for reading.
*/
uint8_t rx_nbits;
/**
* @brief Amount of bits that are used for writing.
*/
uint8_t tx_nbits;
/**
* @brief Sets one of the possible modes that can be used for SPI transfers
* (dependent on clock phase and polarity).
*/
uint32_t mode;
/**
* @brief Indicates which device is currently used.
*/
uint8_t cs;
};
/**
* @brief Calculates the size of the SPI message array.
*/
#define SPI_MSGSIZE(n) \
(((n) * sizeof(struct spi_ioc_transfer) < IOCPARM_MAX) ? \
(n) * sizeof(struct spi_ioc_transfer) : 0)
/**
* @brief Transfers an array with SPI messages.
*/
#define SPI_IOC_MESSAGE(n) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(n)])
/**
* @brief Reads the least-significant 8-bits of the SPI default mode.
*/
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, uint8_t)
/**
* @brief Writes the SPI default mode (the most-significant 24-bits of the mode are
* set to zero).
*/
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, uint8_t)
/**
* @brief Reads the SPI default least-significant bit first setting.
*/
#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, uint8_t)
/**
* @brief Writes the SPI default least-significant-bit first setting.
*/
#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, uint8_t)
/**
* @brief Reads the SPI default bits per word.
*/
#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, uint8_t)
/**
* @brief Writes the SPI default bits per word.
*/
#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, uint8_t)
/**
* @brief Reads the SPI default speed in Hz.
*/
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, uint32_t)
/**
* @brief Writes the SPI default speed in Hz.
*/
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, uint32_t)
/**
* @brief Reads the full 32-bit SPI default mode.
*/
#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, uint32_t)
/**
* @brief Writes the full 32-bit SPI default mode.
*/
#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, uint32_t)
#endif /* _UAPI_LINUX_SPI_H */

View File

@@ -39,6 +39,15 @@ $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h: include/dev/i2c/switch-nxp-pca
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h
$(PROJECT_INCLUDE)/dev/spi/$(dirstamp):
@$(MKDIR_P) $(PROJECT_INCLUDE)/dev/spi
@: > $(PROJECT_INCLUDE)/dev/spi/$(dirstamp)
PREINSTALL_DIRS += $(PROJECT_INCLUDE)/dev/spi/$(dirstamp)
$(PROJECT_INCLUDE)/dev/spi/spi.h: include/dev/spi/spi.h $(PROJECT_INCLUDE)/dev/spi/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/spi/spi.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/spi/spi.h
$(PROJECT_INCLUDE)/linux/$(dirstamp):
@$(MKDIR_P) $(PROJECT_INCLUDE)/linux
@: > $(PROJECT_INCLUDE)/linux/$(dirstamp)
@@ -52,3 +61,12 @@ $(PROJECT_INCLUDE)/linux/i2c-dev.h: include/linux/i2c-dev.h $(PROJECT_INCLUDE)/l
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/linux/i2c-dev.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/linux/i2c-dev.h
$(PROJECT_INCLUDE)/linux/spi/$(dirstamp):
@$(MKDIR_P) $(PROJECT_INCLUDE)/linux/spi
@: > $(PROJECT_INCLUDE)/linux/spi/$(dirstamp)
PREINSTALL_DIRS += $(PROJECT_INCLUDE)/linux/spi/$(dirstamp)
$(PROJECT_INCLUDE)/linux/spi/spidev.h: include/linux/spi/spidev.h $(PROJECT_INCLUDE)/linux/spi/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/linux/spi/spidev.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/linux/spi/spidev.h

366
cpukit/dev/spi/spi-bus.c Normal file
View File

@@ -0,0 +1,366 @@
/**
* @file
*
* @brief Serial Peripheral Interface (SPI) Bus Implementation
*
* @ingroup SPIBus
*/
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <dev/spi/spi.h>
#include <rtems/imfs.h>
#include <stdlib.h>
#include <string.h>
static void spi_bus_obtain(spi_bus *bus)
{
rtems_status_code sc;
sc = rtems_semaphore_obtain(bus->mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
_Assert(sc == RTEMS_SUCCESSFUL);
(void) sc;
}
static void spi_bus_release(spi_bus *bus)
{
rtems_status_code sc;
sc = rtems_semaphore_release(bus->mutex);
_Assert(sc == RTEMS_SUCCESSFUL);
(void) sc;
}
static void spi_bus_set_defaults(spi_bus *bus, spi_ioc_transfer *msg)
{
msg->cs_change = bus->cs_change;
msg->cs = bus->cs;
msg->bits_per_word = bus->bits_per_word;
msg->mode = bus->mode;
msg->speed_hz = bus->speed_hz;
msg->delay_usecs = bus->delay_usecs;
}
static ssize_t spi_bus_read(
rtems_libio_t *iop,
void *buffer,
size_t count
)
{
spi_bus *bus = IMFS_generic_get_context_by_iop(iop);
spi_ioc_transfer msg = {
.len = count,
.rx_buf = buffer
};
int err;
spi_bus_obtain(bus);
spi_bus_set_defaults(bus, &msg);
err = (*bus->transfer)(bus, &msg, 1);
spi_bus_release(bus);
if (err == 0) {
return msg.len;
} else {
rtems_set_errno_and_return_minus_one(-err);
}
}
static ssize_t spi_bus_write(
rtems_libio_t *iop,
const void *buffer,
size_t count
)
{
spi_bus *bus = IMFS_generic_get_context_by_iop(iop);
spi_ioc_transfer msg = {
.len = (uint16_t) count,
.tx_buf = buffer
};
int err;
spi_bus_obtain(bus);
spi_bus_set_defaults(bus, &msg);
err = (*bus->transfer)(bus, &msg, 1);
spi_bus_release(bus);
if (err == 0) {
return msg.len;
} else {
rtems_set_errno_and_return_minus_one(-err);
}
}
static int spi_bus_ioctl(
rtems_libio_t *iop,
ioctl_command_t command,
void *arg
)
{
spi_bus *bus = IMFS_generic_get_context_by_iop(iop);
int err;
uint32_t previous;
spi_bus_obtain(bus);
switch (command) {
case SPI_BUS_OBTAIN:
spi_bus_obtain(bus);
err = 0;
break;
case SPI_BUS_RELEASE:
spi_bus_release(bus);
err = 0;
break;
case SPI_IOC_RD_MODE:
*(uint8_t *) arg = bus->mode;
err = 0;
break;
case SPI_IOC_RD_MODE32:
*(uint32_t *) arg = bus->mode;
err = 0;
break;
case SPI_IOC_RD_LSB_FIRST:
*(uint8_t *) arg = bus->lsb_first;
err = 0;
break;
case SPI_IOC_RD_BITS_PER_WORD:
*(uint8_t *) arg = bus->bits_per_word;
err = 0;
break;
case SPI_IOC_RD_MAX_SPEED_HZ:
*(uint32_t *) arg = bus->speed_hz;
err = 0;
break;
case SPI_IOC_WR_MODE:
previous = bus->mode;
bus->mode = *(uint8_t *) arg;
err = (*bus->setup)(bus);
if (err != 0) {
bus->mode = previous;
}
break;
case SPI_IOC_WR_MODE32:
previous = bus->mode;
bus->mode = *(uint32_t *) arg;
err = (*bus->setup)(bus);
if (err != 0) {
bus->mode = previous;
}
break;
case SPI_IOC_WR_LSB_FIRST:
previous = bus->lsb_first;
bus->lsb_first = (*(uint8_t *) arg) != 0;
err = (*bus->setup)(bus);
if (err != 0) {
bus->lsb_first = (bool) previous;
}
break;
case SPI_IOC_WR_BITS_PER_WORD:
previous = bus->bits_per_word;
bus->bits_per_word = *(uint8_t *) arg;
err = (*bus->setup)(bus);
if (err != 0) {
bus->bits_per_word = (uint8_t) previous;
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ:
previous = bus->speed_hz;
bus->speed_hz = *(uint32_t *) arg;
err = (*bus->setup)(bus);
if (err != 0) {
bus->speed_hz = previous;
}
break;
default:
if (IOCBASECMD(command) == IOCBASECMD(_IOW(SPI_IOC_MAGIC, 0, 0))) {
err = (*bus->transfer)(
bus,
arg,
IOCPARM_LEN(command) / sizeof(struct spi_ioc_transfer)
);
} else {
err = -EINVAL;
}
break;
}
spi_bus_release(bus);
if (err == 0) {
return 0;
} else {
rtems_set_errno_and_return_minus_one(-err);
}
}
static const rtems_filesystem_file_handlers_r spi_bus_handler = {
.open_h = rtems_filesystem_default_open,
.close_h = rtems_filesystem_default_close,
.read_h = spi_bus_read,
.write_h = spi_bus_write,
.ioctl_h = spi_bus_ioctl,
.lseek_h = rtems_filesystem_default_lseek,
.fstat_h = IMFS_stat,
.ftruncate_h = rtems_filesystem_default_ftruncate,
.fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
.fcntl_h = rtems_filesystem_default_fcntl,
.kqfilter_h = rtems_filesystem_default_kqfilter,
.poll_h = rtems_filesystem_default_poll,
.readv_h = rtems_filesystem_default_readv,
.writev_h = rtems_filesystem_default_writev
};
static void spi_bus_node_destroy(IMFS_jnode_t *node)
{
spi_bus *bus;
bus = IMFS_generic_get_context_by_node(node);
(*bus->destroy)(bus);
IMFS_node_destroy_default(node);
}
static const IMFS_node_control spi_bus_node_control = IMFS_GENERIC_INITIALIZER(
&spi_bus_handler,
IMFS_node_initialize_generic,
spi_bus_node_destroy
);
int spi_bus_register(
spi_bus *bus,
const char *bus_path
)
{
int rv;
rv = IMFS_make_generic_node(
bus_path,
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
&spi_bus_node_control,
bus
);
if (rv != 0) {
(*bus->destroy)(bus);
}
return rv;
}
static int spi_bus_transfer_default(
spi_bus *bus,
const spi_ioc_transfer *msgs,
uint32_t msg_count
)
{
(void) bus;
(void) msgs;
(void) msg_count;
return -EIO;
}
static int spi_bus_setup_default(
spi_bus *bus
)
{
(void) bus;
return -EIO;
}
static int spi_bus_do_init(
spi_bus *bus,
void (*destroy)(spi_bus *bus)
)
{
rtems_status_code sc;
sc = rtems_semaphore_create(
rtems_build_name('S', 'P', 'I', ' '),
1,
RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
0,
&bus->mutex
);
if (sc != RTEMS_SUCCESSFUL) {
(*destroy)(bus);
rtems_set_errno_and_return_minus_one(ENOMEM);
}
bus->transfer = spi_bus_transfer_default;
bus->setup = spi_bus_setup_default;
bus->destroy = destroy;
bus->bits_per_word = 8;
return 0;
}
void spi_bus_destroy(spi_bus *bus)
{
rtems_status_code sc;
sc = rtems_semaphore_delete(bus->mutex);
_Assert(sc == RTEMS_SUCCESSFUL);
(void) sc;
}
void spi_bus_destroy_and_free(spi_bus *bus)
{
spi_bus_destroy(bus);
free(bus);
}
int spi_bus_init(spi_bus *bus)
{
memset(bus, 0, sizeof(*bus));
return spi_bus_do_init(bus, spi_bus_destroy);
}
spi_bus *spi_bus_alloc_and_init(size_t size)
{
spi_bus *bus = NULL;
if (size >= sizeof(*bus)) {
bus = calloc(1, size);
if (bus != NULL) {
int rv;
rv = spi_bus_do_init(bus, spi_bus_destroy_and_free);
if (rv != 0) {
return NULL;
}
}
}
return bus;
}

View File

@@ -9,6 +9,7 @@ _SUBDIRS += pwdgrp01
_SUBDIRS += crypt01
_SUBDIRS += sha
_SUBDIRS += i2c01
_SUBDIRS += spi01
_SUBDIRS += newlib01
_SUBDIRS += block17
_SUBDIRS += exit02

View File

@@ -89,6 +89,7 @@ pwdgrp01/Makefile
crypt01/Makefile
sha/Makefile
i2c01/Makefile
spi01/Makefile
newlib01/Makefile
block17/Makefile
exit02/Makefile

View File

@@ -0,0 +1,19 @@
rtems_tests_PROGRAMS = spi01
spi01_SOURCES = init.c
dist_rtems_tests_DATA = spi01.scn spi01.doc
include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
include $(top_srcdir)/../automake/compile.am
include $(top_srcdir)/../automake/leaf.am
AM_CPPFLAGS += -I$(top_srcdir)/../support/include
LINK_OBJS = $(spi01_OBJECTS)
LINK_LIBS = $(spi01_LDLIBS)
spi01$(EXEEXT): $(spi01_OBJECTS) $(spi01_DEPENDENCIES)
@rm -f spi01$(EXEEXT)
$(make-exe)
include $(top_srcdir)/../automake/local.am

View File

@@ -0,0 +1,288 @@
/*
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <dev/spi/spi.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rtems/libcsupport.h>
#include "tmacros.h"
static uint8_t mode_8 = 0xA5;
static uint32_t mode_32 = 0x5A5A5A5A;
static uint32_t speed = 12345678;
static uint8_t bits_per_word = 12;
static uint8_t lsb_first = 1;
const char rtems_test_name[] = "SPI 1";
static const char bus_path[] = "/dev/spi-0";
typedef struct test_device test_device;
struct test_device {
int (*transfer)(
spi_bus *bus,
const spi_ioc_transfer *msgs,
uint32_t msg_count,
test_device *dev
);
};
typedef struct {
test_device base;
char buf[3];
} test_device_simple_read_write;
typedef struct {
spi_bus base;
unsigned long clock;
test_device *device;
uint32_t msg_count;
uint32_t max_speed_hz;
test_device_simple_read_write simple_read_write;
} test_bus;
static int test_simple_read_write_transfer(
spi_bus *bus,
const spi_ioc_transfer *msgs,
uint32_t msg_count,
test_device *base
)
{
(void)bus;
test_device_simple_read_write *dev = (test_device_simple_read_write *) base;
if (msg_count == 1 && msgs[0].len == sizeof(dev->buf)) {
if (msgs[0].rx_buf == 0){
memcpy(&dev->buf[0], msgs[0].tx_buf, sizeof(dev->buf));
} else if (msgs[0].tx_buf == 0){
memcpy(msgs[0].rx_buf, &dev->buf[0], sizeof(dev->buf));
} else {
return -EIO;
}
} else {
return -EIO;
}
return 0;
}
static int test_transfer(
spi_bus *base,
const spi_ioc_transfer *msgs,
uint32_t msg_count
)
{
test_bus *bus = (test_bus *) base;
test_device *dev;
dev = bus->device;
bus->msg_count = msg_count;
return (*dev->transfer)(&bus->base, msgs, msg_count, dev);
}
static int test_setup(spi_bus *base)
{
test_bus *bus = (test_bus *) base;
if ((base->speed_hz > bus->max_speed_hz) ||
((base->bits_per_word < 8) || (base->bits_per_word > 16))) {
return 1;
}
return 0;
}
static void test_destroy(spi_bus *base)
{
spi_bus_destroy_and_free(base);
}
static void test_simple_read_write(test_bus *bus, int fd)
{
static const char zero[] = { 0, 0, 0 };
static const char abc[] = { 'a', 'b', 'c' };
int rv;
char buf[3];
ssize_t n;
errno = 0;
rv = ioctl(fd, 0xb00b);
rtems_test_assert(rv == -1);
rtems_test_assert(errno == EINVAL);
errno = 0;
n = write(fd, &buf[0], 1000);
rtems_test_assert(n == -1);
rtems_test_assert(errno == EIO);
errno = 0;
n = read(fd, &buf[0], 1000);
rtems_test_assert(n == -1);
rtems_test_assert(errno == EIO);
rtems_test_assert(
memcmp(&bus->simple_read_write.buf[0], &zero[0], sizeof(buf)) == 0
);
n = write(fd, &abc[0], sizeof(buf));
rtems_test_assert(n == (ssize_t) sizeof(buf));
rtems_test_assert(
memcmp(&bus->simple_read_write.buf[0], &abc[0], sizeof(buf)) == 0
);
n = read(fd, &buf[0], sizeof(buf));
rtems_test_assert(n == (ssize_t) sizeof(buf));
rtems_test_assert(memcmp(&buf[0], &abc[0], sizeof(buf)) == 0);
}
static void test(void)
{
rtems_resource_snapshot snapshot;
test_bus *bus;
int rv;
int fd;
uint8_t read_mode_8;
uint32_t read_mode_32;
uint32_t read_speed;
uint8_t read_bits_per_word;
uint8_t read_lsb_first;
spi_ioc_transfer msg;
rtems_resource_snapshot_take(&snapshot);
bus = (test_bus *) spi_bus_alloc_and_init(sizeof(*bus));
rtems_test_assert(bus != NULL);
bus->base.transfer = test_transfer;
bus->base.destroy = test_destroy;
bus->base.setup = test_setup;
bus->simple_read_write.base.transfer = test_simple_read_write_transfer;
bus->device = &bus->simple_read_write.base;
bus->max_speed_hz = 50000000;
rv = spi_bus_register(&bus->base, &bus_path[0]);
rtems_test_assert(rv == 0);
fd = open(&bus_path[0], O_RDWR);
rtems_test_assert(fd >= 0);
rv = ioctl(fd, SPI_BUS_OBTAIN);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_BUS_RELEASE);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &read_speed);
rtems_test_assert(rv == 0);
rtems_test_assert(read_speed == speed);
speed = 60000000;
rv = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
rtems_test_assert(rv == -1);
rv = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsb_first);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &read_lsb_first);
rtems_test_assert(rv == 0);
rtems_test_assert(read_lsb_first == lsb_first);
rv = ioctl(fd, SPI_IOC_WR_MODE, &mode_8);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_IOC_RD_MODE, &read_mode_8);
rtems_test_assert(rv == 0);
rtems_test_assert(read_mode_8 == mode_8);
rv = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &read_bits_per_word);
rtems_test_assert(rv == 0);
rtems_test_assert(read_bits_per_word == bits_per_word);
bits_per_word = 7;
rv = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word);
rtems_test_assert(rv == -1);
rv = ioctl(fd, SPI_IOC_WR_MODE32, &mode_32);
rtems_test_assert(rv == 0);
rv = ioctl(fd, SPI_IOC_RD_MODE32, &read_mode_32);
rtems_test_assert(rv == 0);
rtems_test_assert(read_mode_32 == mode_32);
bus->msg_count = 1;
ioctl(fd, SPI_IOC_MESSAGE(8192), &msg);
rtems_test_assert(bus->msg_count == 0);
test_simple_read_write(bus, fd);
rv = close(fd);
rtems_test_assert(rv == 0);
rv = unlink(&bus_path[0]);
rtems_test_assert(rv == 0);
rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
}
static void Init(rtems_task_argument arg)
{
(void)arg;
TEST_BEGIN();
test();
TEST_END();
rtems_test_exit(0);
}
#define CONFIGURE_MICROSECONDS_PER_TICK 2000
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 7
#define CONFIGURE_MAXIMUM_TASKS 1
#define CONFIGURE_MAXIMUM_SEMAPHORES 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>

View File

@@ -0,0 +1,11 @@
This file describes the directives and concepts tested by this test set.
test set name: spi01
directives:
TBD
concepts:
- Ensure that the SPI driver framework works.

View File

@@ -0,0 +1,2 @@
*** BEGIN OF TEST SPI 1 ***
*** END OF TEST SPI 1 ***