Good. We'll need some user-friendly (BSP developer) documentation eventually.
On Mon, Sep 12, 2016 at 9:10 AM, Alexander Krutwig <alexander.krut...@embedded-brains.de> wrote: > Linux Userspace API was taken as a template > Test libtests/spi01 is included into testsuite > --- > cpukit/dev/Makefile.am | 10 + > cpukit/dev/include/dev/spi/spi.h | 452 > +++++++++++++++++++++++++++++ > cpukit/dev/include/linux/spi/spidev.h | 242 +++++++++++++++ > cpukit/dev/spi/spi-bus.c | 362 +++++++++++++++++++++++ > cpukit/dev/spi/spi-dev.c | 291 +++++++++++++++++++ > testsuites/libtests/Makefile.am | 1 + > testsuites/libtests/spi01/buffer_test_io.h | 219 ++++++++++++++ > testsuites/libtests/spi01/init.c | 256 ++++++++++++++++ > testsuites/libtests/spi01/spi01.doc | 11 + > testsuites/libtests/spi01/spi01.scn | 2 + > testsuites/libtests/spi01/tmacros.h | 372 ++++++++++++++++++++++++ > 11 files changed, 2218 insertions(+) > create mode 100644 cpukit/dev/include/dev/spi/spi.h > create mode 100644 cpukit/dev/include/linux/spi/spidev.h > create mode 100644 cpukit/dev/spi/spi-bus.c > create mode 100644 cpukit/dev/spi/spi-dev.c > create mode 100644 testsuites/libtests/spi01/buffer_test_io.h > create mode 100644 testsuites/libtests/spi01/init.c > create mode 100644 testsuites/libtests/spi01/spi01.doc > create mode 100644 testsuites/libtests/spi01/spi01.scn > create mode 100644 testsuites/libtests/spi01/tmacros.h > > diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am > index 47a1585..3d39e38 100644 > --- a/cpukit/dev/Makefile.am > +++ b/cpukit/dev/Makefile.am > @@ -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,8 @@ 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-dev.c > +libdev_a_SOURCES += spi/spi-bus.c > > include $(srcdir)/preinstall.am > include $(top_srcdir)/automake/local.am > diff --git a/cpukit/dev/include/dev/spi/spi.h > b/cpukit/dev/include/dev/spi/spi.h > new file mode 100644 > index 0000000..ce3fd7c > --- /dev/null > +++ b/cpukit/dev/include/dev/spi/spi.h > @@ -0,0 +1,452 @@ > +/** > + * @file > + * > + * @brief Inter-Integrated Circuit (SPI) Driver API > + * > + * @ingroup SPI > + */ > + > +/* > + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. > + * > + * embedded brains GmbH > + * Dornierstr. 4 > + * 82178 Puchheim > + * Germany > + * <rt...@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> > + > +#include <sys/ioctl.h> > +#include <sys/stat.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif /* __cplusplus */ > + > +typedef struct spi_ioc_transfer spi_ioc_transfer; > + > +typedef struct spi_bus spi_bus; > + > +typedef struct spi_dev spi_dev; > + > +typedef struct spi_rdwr_ioctl_data spi_rdwr_ioctl_data; > + > +/** > + * @defgroup SPI Inter-Integrated Circuit (SPI) Driver > + * > + * @brief Inter-Integrated Circuit (SPI) bus and device driver support. > + * > + * @{ > + */ > + > +/** > + * @defgroup SPIBus SPI Bus Driver > + * > + * @ingroup SPI > + * > + * @{ > + */ > + > +/** > + * @name SPI IO Control Commands > + * > + * @{ > + */ > + > +/** > + * @brief Obtains the bus. > + * > + * This command has no argument. > + */ > +#define SPI_BUS_OBTAIN 0x800 > + > +/** > + * @brief Releases the bus. > + * > + * This command has no argument. > + */ > +#define SPI_BUS_RELEASE 0x801 > + > +/** > + * @brief Gets the bus control. > + * > + * The argument type is a pointer to spi_bus pointer. > + */ > +#define SPI_BUS_GET_CONTROL 0x802 > + > +/** > + * @brief Sets the bus clock in Hz. > + * > + * The argument type is unsigned long. > + */ > +#define SPI_BUS_SET_CLOCK 0x803 > + > +/** @} */ > + > +/** > + * @brief Default SPI bus clock in Hz. > + */ > +#define SPI_BUS_CLOCK_DEFAULT 100000 > + > +/** > + * @brief Data Field of the SPI bus control > + */ > +typedef struct { > + /** > + * @brief Current mode. > + */ > + uint32_t mode; > + > + /** > + * @brief Indicates the bits per word used on the device. > + */ > + uint8_t bits_per_word; > + > + /** > + * @brief Indicates the speed of the current device message. > + */ > + uint32_t speed_hz; > + > + /** > + * @brief Indicates the delay between transfers on different chip select > + * devices. > + */ > + uint16_t delay_usecs; > + > + /** > + * @brief Indicates which device is selected by chip select > + */ > + uint8_t cs; > +} spi_bus_data; > + > +/** > + * @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, spi_ioc_transfer *msgs, uint32_t msg_count); > + > + /** > + * @brief Sets the bus clock. > + * > + * @param[in] bus The bus control. > + * @param[in] clock The desired bus clock in Hz. > + * > + * @retval 0 Successful operation. > + * @retval negative Negative error number in case of an error. > + */ > + int (*set_clock)(spi_bus *bus, unsigned long clock); > + > + /** > + * @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; > + > + /** > + * @brief Indicates if LSB is supposed to be transmitted first. > + */ > + uint8_t lsb_first; > + > + /** > + * @brief Indicates if chip select must be set high after transfer. > + */ > + uint8_t cs_change; > + > + /** > + * @brief Data field of the bus control. > + */ > + spi_bus_data data; > +}; > + > +/** > + * @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 > +); > + > +/** > + * @brief Obtains the bus. > + * > + * @param[in] bus The bus control. > + */ > +void spi_bus_obtain(spi_bus *bus); > + > +/** > + * @brief Releases the bus. > + * > + * @param[in] bus The bus control. > + */ > +void spi_bus_release(spi_bus *bus); > + > +/** > + * @brief Transfers SPI messages. > + * > + * The bus is obtained before the transfer and released afterwards. > + * > + * @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 spi_bus_transfer(spi_bus *bus, spi_ioc_transfer *msgs, uint32_t > msg_count); > + > +/** @} */ > + > +/** > + * @defgroup SPIDevice SPI Device Driver > + * > + * @ingroup SPI > + * > + * @{ > + */ > + > +/** > + * @brief Base number for device IO control commands. > + */ > +#define SPI_DEV_IO_CONTROL 0x900 > + > +/** > + * @brief SPI slave device control. > + */ > +struct spi_dev { > + /** > + * @brief Reads from the device. > + * > + * @retval non-negative Bytes transferred from device. > + * @retval negative Negative error number in case of an error. > + */ > + ssize_t (*read)(spi_dev *dev, void *buf, size_t n, off_t offset); > + > + /** > + * @brief Writes to the device. > + * > + * @retval non-negative Bytes transferred to device. > + * @retval negative Negative error number in case of an error. > + */ > + ssize_t (*write)(spi_dev *dev, const void *buf, size_t n, off_t offset); > + > + /** > + * @brief Device IO control. > + * > + * @retval 0 Successful operation. > + * @retval negative Negative error number in case of an error. > + */ > + int (*ioctl)(spi_dev *dev, ioctl_command_t command, void *arg); > + > + /** > + * @brief Gets the file size. > + */ > + off_t (*get_size)(spi_dev *dev); > + > + /** > + * @brief Gets the file block size. > + */ > + blksize_t (*get_block_size)(spi_dev *dev); > + > + /** > + * @brief Destroys the device. > + */ > + void (*destroy)(spi_dev *dev); > + > + /** > + * @brief The bus control. > + */ > + spi_bus *bus; > + > + /** > + * @brief The device address. > + */ > + uint16_t address; > + > + /** > + * @brief File descriptor of the bus. > + * > + * This prevents destruction of the bus since we hold a reference to it > with > + * this. > + */ > + int bus_fd; > +}; > + > + > +/** > + * @brief Initializes a device control. > + * > + * After a sucessful initialization the device control must be destroyed via > + * spi_dev_destroy(). A registered device control will be automatically > + * destroyed in case the device file is unlinked. Make sure to call > + * spi_dev_destroy_and_free() in a custom destruction handler. > + * > + * @param[in] device The device control. > + * @param[in] bus_path The path to the bus device file. > + * @param[in] address The address of the device. > + * > + * @retval 0 Successful operation. > + * @retval -1 An error occurred. The errno is set to indicate the error. > + * > + * @see spi_dev_register() > + */ > +int spi_dev_init(spi_dev *dev, const char *bus_path, uint16_t address); > + > +/** > + * @brief Allocates a device control from the heap and initializes it. > + * > + * After a sucessful allocation and initialization the device control must be > + * destroyed via spi_dev_destroy_and_free(). A registered device control > will > + * be automatically destroyed in case the device file is unlinked. Make sure > + * to call spi_dev_destroy_and_free() in a custom destruction handler. > + * > + * @param[in] size The size of the device control. This enables the addition > + * of device specific data to the base device control. The device control is > + * zero initialized. > + * @param[in] bus_path The path to the bus device file. > + * @param[in] address The address of the device. > + * > + * @retval non-NULL The new device control. > + * @retval NULL An error occurred. The errno is set to indicate the error. > + * > + * @see spi_dev_register() > + */ > +spi_dev *spi_dev_alloc_and_init( > + size_t size, > + const char *bus_path, > + uint16_t address > +); > + > +/** > + * @brief Destroys a device control. > + * > + * @param[in] dev The device control. > + */ > +void spi_dev_destroy(spi_dev *dev); > + > +/** > + * @brief Destroys a device control and frees its memory. > + * > + * @param[in] dev The device control. > + */ > +void spi_dev_destroy_and_free(spi_dev *dev); > + > +/** > + * @brief Registers a device control. > + * > + * This function claims ownership of the device control regardless if the > + * registration is successful or not. > + * > + * @param[in] dev The dev control. > + * @param[in] dev_path The path to the device file of the device. > + * > + * @retval 0 Successful operation. > + * @retval -1 An error occurred. The errno is set to indicate the error. > + */ > +int spi_dev_register( > + spi_dev *dev, > + const char *dev_path > +); > + > +/** @} */ /* end of spi device driver */ > + > +/** @} */ > + > +#ifdef __cplusplus > +} > +#endif /* __cplusplus */ > + > +#endif /* _DEV_SPI_SPI_H */ > diff --git a/cpukit/dev/include/linux/spi/spidev.h > b/cpukit/dev/include/linux/spi/spidev.h > new file mode 100644 > index 0000000..a5b7df3 > --- /dev/null > +++ b/cpukit/dev/include/linux/spi/spidev.h > @@ -0,0 +1,242 @@ > +/** > + * @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 > + * <rt...@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 <stdint.h> > +#include <stdlib.h> > +#include <sys/ioccom.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, and > + * - /usr/src/linux/Documentation/spi. > + * > + * @{ > + */ > + > +#define SPI_MESSAGE_SIZE_POS 16 > +#define SPI_MESSAGE_SIZE_BIT_LENGTH 14 > + > +/** > + * @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|0) > +/** > + * @brief SPI transfer flag which sets SPI Mode 0 (clock starts low, sample > on > + * trailing edge). > + */ > +#define SPI_MODE_1 (0|SPI_CPHA) > +/** > + * @brief SPI transfer flag which sets SPI Mode 0 (clock starts high, sample > on > + * leading edge). > + */ > +#define SPI_MODE_2 (SPI_CPOL|0) > +/** > + * @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' > + > +#define SPI_IOC_TRANSFER_MAGIC 'p' > + > +/** > + * @brief SPI Transfer Structure. > + */ > +struct spi_ioc_transfer { > + /** > + * @brief Buffer for receive data. > + */ > + void *rx_buf; > + > + /** > + * @brief Buffer for transmit data. > + */ > + const void *tx_buf; > + > + /** > + * @brief Length of rx_buf and tx_buf in bytes. > + */ > + size_t len; > + > + /** > + * @brief Sets the bitrate 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. > + */ > +#define SPI_MSGSIZE(N) \ > + ((((N)*(sizeof (struct spi_ioc_transfer))) \ > + < (1 << SPI_MESSAGE_SIZE_BIT_LENGTH)) \ > + ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) > + > +/** > + * @brief Transfers an array with SPI messages. > + */ > +#define SPI_IOC_MESSAGE(n) \ > + _IOW(SPI_IOC_TRANSFER_MAGIC, n, struct spi_ioc_transfer) > + > +/** > + * @brief Reads and writes the SPI mode field (restriction to 8 bits). > + */ > +#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, uint8_t) > +#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, uint8_t) > + > +/** > + * @brief Reads and writes the SPI LSB field. > + */ > +#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, uint8_t) > +#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, uint8_t) > + > +/** > + * @brief Reads and writes the SPI Bits per word field. > + */ > +#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, uint8_t) > +#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, uint8_t) > + > +/** > + * @brief Reads and writes the SPI maximum rx/tx speed field. > + */ > +#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, uint32_t) > +#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, uint32_t) > + > +/** > + * @brief Reads and writes the SPI mode field (extended to 32 bits). > + */ > +#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, uint32_t) > +#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, uint32_t) > + > +#endif /* _UAPI_LINUX_SPI_H */ > diff --git a/cpukit/dev/spi/spi-bus.c b/cpukit/dev/spi/spi-bus.c > new file mode 100644 > index 0000000..a64b93f > --- /dev/null > +++ b/cpukit/dev/spi/spi-bus.c > @@ -0,0 +1,362 @@ > +/** > + * @file > + * > + * @brief Inter-Integrated Circuit (SPI) Bus Implementation > + * > + * @ingroup SPIBus > + */ > + > +/* > + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. > + * > + * embedded brains GmbH > + * Dornierstr. 4 > + * 82178 Puchheim > + * Germany > + * <rt...@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> > + > +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; > +} > + > +void spi_bus_release(spi_bus *bus) > +{ > + rtems_status_code sc; > + > + sc = rtems_semaphore_release(bus->mutex); > + _Assert(sc == RTEMS_SUCCESSFUL); > + (void) sc; > +} > + > +int spi_bus_transfer(spi_bus *bus, spi_ioc_transfer *msgs, uint32_t > msg_count) > +{ > + int err; > + > + _Assert(msg_count > 0); > + > + spi_bus_obtain(bus); > + err = (*bus->transfer)(bus, msgs, msg_count); > + spi_bus_release(bus); > + > + return err; > +} > + > +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, > + .cs_change = bus->cs_change, > + .cs = bus->data.cs, > + .bits_per_word = bus->data.bits_per_word, > + .mode = bus->data.mode, > + .speed_hz = bus->data.speed_hz, > + .delay_usecs = bus->data.delay_usecs > + }; > + int err; > + > + err = spi_bus_transfer(bus, &msg, 1); > + 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 = RTEMS_DECONST(void *, buffer), > + .cs_change = bus->cs_change, > + .cs = bus->data.cs, > + .bits_per_word = bus->data.bits_per_word, > + .mode = bus->data.mode, > + .speed_hz = bus->data.speed_hz, > + .delay_usecs = bus->data.delay_usecs > + }; > + int err; > + > + err = spi_bus_transfer(bus, &msg, 1); > + if (err == 0) { > + return msg.len; > + } else { > + rtems_set_errno_and_return_minus_one(-err); > + } > +} > + > +#if 1 > +static int spi_get_ioc_message(uint32_t cmd, size_t *n_ioc) > +{ > + uint32_t tmp; > + > + tmp = (cmd >> SPI_MESSAGE_SIZE_POS) & ((1 << > SPI_MESSAGE_SIZE_BIT_LENGTH)-1); > + if ((tmp % sizeof(struct spi_ioc_transfer)) != 0){ > + return -EINVAL; > + } > + *n_ioc = tmp / sizeof(struct spi_ioc_transfer); > + > + return 0; > +} > +#endif > + > +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 msg_count; > + struct spi_ioc_transfer *ioc; > + int i; > + > + 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_BUS_GET_CONTROL: > + *(spi_bus **) arg = bus; > + err = 0; > + break; > + case SPI_BUS_SET_CLOCK: > + spi_bus_obtain(bus); > + err = (*bus->set_clock)(bus, (unsigned long) arg); > + spi_bus_release(bus); > + break; > + case SPI_IOC_RD_MODE: > + case SPI_IOC_RD_MODE32: > + *(uint32_t *) arg = bus->data.mode; > + err = 0; > + break; > + case SPI_IOC_RD_LSB_FIRST: > + *(uint32_t *) arg = bus->lsb_first; > + err = 0; > + break; > + case SPI_IOC_RD_BITS_PER_WORD: > + *(uint32_t *) arg = bus->data.bits_per_word; > + err = 0; > + break; > + case SPI_IOC_RD_MAX_SPEED_HZ: > + *(uint32_t *) arg = bus->max_speed; > + err = 0; > + break; > + case SPI_IOC_WR_MODE: > + case SPI_IOC_WR_MODE32: > + bus->data.mode = (uint32_t) arg; > + err = 0; > + break; > + case SPI_IOC_WR_LSB_FIRST: > + bus->lsb_first = (uint32_t) arg; > + err = 0; > + break; > + case SPI_IOC_WR_BITS_PER_WORD: > + bus->data.bits_per_word = (uint32_t) arg; > + err = 0; > + break; > + case SPI_IOC_WR_MAX_SPEED_HZ: > + bus->max_speed = (uint32_t) arg; > + err = 0; > + break; > + default: > + if (IOCGROUP(command) == SPI_IOC_TRANSFER_MAGIC) { > + size_t n; > + > + err = spi_get_ioc_message(command, &n); > + if (err == 0){ > + err = spi_bus_transfer(bus, arg, n); > + } > + } else { > + err = -EINVAL; > + } > + break; > + } > + > + 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, > + spi_ioc_transfer *msgs, > + uint32_t msg_count > +) > +{ > + (void) bus; > + (void) msgs; > + (void) msg_count; > + > + return -EIO; > +} > + > +static int spi_bus_set_clock_default(spi_bus *bus, unsigned long clock) > +{ > + (void) bus; > + (void) clock; > + > + 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->set_clock = spi_bus_set_clock_default; > + bus->destroy = destroy; > + /* Default max speed */ > + bus->max_speed = 50000000; > + > + 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; > +} > diff --git a/cpukit/dev/spi/spi-dev.c b/cpukit/dev/spi/spi-dev.c > new file mode 100644 > index 0000000..72309ef > --- /dev/null > +++ b/cpukit/dev/spi/spi-dev.c > @@ -0,0 +1,291 @@ > +/** > + * @file > + * > + * @brief Inter-Integrated Circuit (SPI) Bus Implementation > + * > + * @ingroup SPIDevice > + */ > + > +/* > + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. > + * > + * embedded brains GmbH > + * Dornierstr. 4 > + * 82178 Puchheim > + * Germany > + * <rt...@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 <rtems/score/assert.h> > + > +#include <fcntl.h> > +#include <stdlib.h> > +#include <unistd.h> > + > +static ssize_t spi_dev_read( > + rtems_libio_t *iop, > + void *buffer, > + size_t count > +) > +{ > + spi_dev *dev = IMFS_generic_get_context_by_iop(iop); > + ssize_t n; > + > + n = (*dev->read)(dev, buffer, count, iop->offset); > + if (n >= 0) { > + iop->offset += n; > + > + return n; > + } else { > + rtems_set_errno_and_return_minus_one(-n); > + } > +} > + > +static ssize_t spi_dev_write( > + rtems_libio_t *iop, > + const void *buffer, > + size_t count > +) > +{ > + spi_dev *dev = IMFS_generic_get_context_by_iop(iop); > + ssize_t n; > + > + n = (*dev->write)(dev, buffer, count, iop->offset); > + if (n >= 0) { > + iop->offset += n; > + > + return n; > + } else { > + rtems_set_errno_and_return_minus_one(-n); > + } > +} > + > +static int spi_dev_ioctl( > + rtems_libio_t *iop, > + ioctl_command_t command, > + void *arg > +) > +{ > + spi_dev *dev = IMFS_generic_get_context_by_iop(iop); > + int err; > + > + err = (*dev->ioctl)(dev, command, arg); > + > + if (err == 0) { > + return 0; > + } else { > + rtems_set_errno_and_return_minus_one(-err); > + } > +} > + > +static int spi_dev_fstat( > + const rtems_filesystem_location_info_t *loc, > + struct stat *buf > +) > +{ > + spi_dev *dev = IMFS_generic_get_context_by_location(loc); > + > + buf->st_size = (*dev->get_size)(dev); > + buf->st_blksize = (*dev->get_block_size)(dev); > + > + return IMFS_stat(loc, buf); > +} > + > +static const rtems_filesystem_file_handlers_r spi_dev_handler = { > + .open_h = rtems_filesystem_default_open, > + .close_h = rtems_filesystem_default_close, > + .read_h = spi_dev_read, > + .write_h = spi_dev_write, > + .ioctl_h = spi_dev_ioctl, > + .lseek_h = rtems_filesystem_default_lseek_file, > + .fstat_h = spi_dev_fstat, > + .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_dev_node_destroy(IMFS_jnode_t *node) > +{ > + spi_dev *dev; > + > + dev = IMFS_generic_get_context_by_node(node); > + (*dev->destroy)(dev); > + > + IMFS_node_destroy_default(node); > +} > + > +static const IMFS_node_control spi_dev_node_control = > IMFS_GENERIC_INITIALIZER( > + &spi_dev_handler, > + IMFS_node_initialize_generic, > + spi_dev_node_destroy > +); > + > +int spi_dev_register( > + spi_dev *dev, > + const char *dev_path > +) > +{ > + int rv; > + > + rv = IMFS_make_generic_node( > + dev_path, > + S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, > + &spi_dev_node_control, > + dev > + ); > + if (rv != 0) { > + (*dev->destroy)(dev); > + } > + > + return rv; > +} > + > +static ssize_t spi_dev_read_default( > + spi_dev *dev, > + void *buf, > + size_t n, > + off_t offset > +) > +{ > + (void) dev; > + (void) buf; > + (void) n; > + (void) offset; > + > + return -EIO; > +} > + > +static ssize_t spi_dev_write_default( > + spi_dev *dev, > + const void *buf, > + size_t n, > + off_t offset > +) > +{ > + (void) dev; > + (void) buf; > + (void) n; > + (void) offset; > + > + return -EIO; > +} > + > +static int spi_dev_ioctl_default( > + spi_dev *dev, > + ioctl_command_t command, > + void *arg > +) > +{ > + (void) dev; > + (void) command; > + (void) arg; > + > + return -ENOTTY; > +} > + > +static off_t spi_dev_get_size_default(spi_dev *dev) > +{ > + (void) dev; > + > + return 0; > +} > + > +static blksize_t spi_dev_get_block_size_default(spi_dev *dev) > +{ > + (void) dev; > + > + return 1; > +} > + > +static int spi_dev_do_init( > + spi_dev *dev, > + const char *bus_path, > + uint16_t address, > + void (*destroy)(spi_dev *dev) > +) > +{ > + int rv; > + > + dev->bus_fd = open(bus_path, O_RDWR); > + if (dev->bus_fd < 0) { > + (*destroy)(dev); > + > + return -1; > + } > + > + rv = ioctl(dev->bus_fd, SPI_BUS_GET_CONTROL, &dev->bus); > + if (rv != 0) { > + (*destroy)(dev); > + > + return -1; > + } > + > + dev->read = spi_dev_read_default; > + dev->write = spi_dev_write_default; > + dev->ioctl = spi_dev_ioctl_default; > + dev->get_size = spi_dev_get_size_default; > + dev->get_block_size = spi_dev_get_block_size_default; > + dev->destroy = destroy; > + dev->address = address; > + > + return 0; > +} > + > +void spi_dev_destroy(spi_dev *dev) > +{ > + int rv; > + > + rv = close(dev->bus_fd); > + _Assert(dev->bus_fd < 0 || rv == 0); > + (void) rv; > +} > + > +void spi_dev_destroy_and_free(spi_dev *dev) > +{ > + spi_dev_destroy(dev); > + free(dev); > +} > + > +int spi_dev_init(spi_dev *dev, const char *bus_path, uint16_t address) > +{ > + return spi_dev_do_init(dev, bus_path, address, spi_dev_destroy); > +} > + > +spi_dev *spi_dev_alloc_and_init( > + size_t size, > + const char *bus_path, > + uint16_t address > +) > +{ > + spi_dev *dev = NULL; > + > + if (size >= sizeof(*dev)) { > + dev = calloc(1, size); > + if (dev != NULL) { > + int rv; > + > + rv = spi_dev_do_init(dev, bus_path, address, spi_dev_destroy_and_free); > + if (rv != 0) { > + return NULL; > + } > + } > + } > + > + return dev; > +} > diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am > index dbd96cb..c00916a 100644 > --- a/testsuites/libtests/Makefile.am > +++ b/testsuites/libtests/Makefile.am > @@ -9,6 +9,7 @@ _SUBDIRS += pwdgrp01 > _SUBDIRS += crypt01 > _SUBDIRS += sha > _SUBDIRS += i2c01 > +_SUBDIRS += spi01 > _SUBDIRS += newlib01 > _SUBDIRS += block17 > _SUBDIRS += exit02 > diff --git a/testsuites/libtests/spi01/buffer_test_io.h > b/testsuites/libtests/spi01/buffer_test_io.h > new file mode 100644 > index 0000000..ac73352 > --- /dev/null > +++ b/testsuites/libtests/spi01/buffer_test_io.h > @@ -0,0 +1,219 @@ > +/* > + * Support for running the test output through a buffer > + */ > + > +#ifndef __BUFFER_TEST_IO_h > +#define __BUFFER_TEST_IO_h > + > +#include <rtems/bspIo.h> > +#include <rtems/test.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +/* > + * Uncomment this to get buffered test output. When commented out, > + * test output behaves as it always has and is printed using stdio. > + */ > + > +/* #define TESTS_BUFFER_OUTPUT */ > +/* #define TESTS_USE_PRINTK */ > + > +/* > + * USE PRINTK TO MINIMIZE SIZE > + */ > +#if defined(TESTS_USE_PRINTK) > + > +#include <rtems/print.h> > + > + #undef printf > + #define printf(...) \ > + do { \ > + printk( __VA_ARGS__); \ > + } while (0) > + > + #undef puts > + #define puts(_s) \ > + do { \ > + printk( "%s\n", _s); \ > + } while (0) > + > + #undef putchar > + #define putchar(_c) \ > + do { \ > + printk( "%c", _c ); \ > + } while (0) > + > + /* Do not call exit() since it closes stdin, etc and pulls in stdio code */ > + #define rtems_test_exit(_s) \ > + do { \ > + rtems_shutdown_executive(0); \ > + } while (0) > + > + #define FLUSH_OUTPUT() \ > + do { \ > + } while (0) > + > + #define TEST_BEGIN() printk(TEST_BEGIN_STRING) > + > + #define TEST_END() printk(TEST_END_STRING) > + > +/* > + * BUFFER TEST OUTPUT > + */ > +#elif defined(TESTS_BUFFER_OUTPUT) > + > + #include <stdio.h> > + #include <stdlib.h> > + > + #define _TEST_OUTPUT_BUFFER_SIZE 2048 > + extern char _test_output_buffer[_TEST_OUTPUT_BUFFER_SIZE]; > + void _test_output_append(char *); > + void _test_output_flush(void); > + > + #define rtems_test_exit(_s) \ > + do { \ > + _test_output_flush(); \ > + exit(_s); \ > + } while (0) > + > + #undef printf > + #define printf(...) \ > + do { \ > + char _buffer[128]; \ > + sprintf( _buffer, __VA_ARGS__); \ > + _test_output_append( _buffer ); \ > + } while (0) > + > + #undef puts > + #define puts(_string) \ > + do { \ > + char _buffer[128]; \ > + sprintf( _buffer, "%s\n", _string ); \ > + _test_output_append( _buffer ); \ > + } while (0) > + > + #undef putchar > + #define putchar(_c) \ > + do { \ > + char _buffer[2]; \ > + _buffer[0] = _c; \ > + _buffer[1] = '\0'; \ > + _test_output_append( _buffer ); \ > + } while (0) > + > + /* we write to stderr when there is a pause() */ > + #define FLUSH_OUTPUT() _test_output_flush() > + > + #if defined(TEST_INIT) || defined(CONFIGURE_INIT) > + > + char _test_output_buffer[_TEST_OUTPUT_BUFFER_SIZE]; > + int _test_output_buffer_index = 0; > + > + void _test_output_append(char *_buffer) > + { > + char *p; > + > + for ( p=_buffer ; *p ; p++ ) { > + _test_output_buffer[_test_output_buffer_index++] = *p; > + _test_output_buffer[_test_output_buffer_index] = '\0'; > + #if 0 > + if ( *p == '\n' ) { > + fprintf( stderr, "BUFFER -- %s", _test_output_buffer ); > + _test_output_buffer_index = 0; > + _test_output_buffer[0] = '\0'; > + } > + #endif > + if ( _test_output_buffer_index >= (_TEST_OUTPUT_BUFFER_SIZE - 80) ) > + _test_output_flush(); > + } > + } > + > + #include <termios.h> > + #include <unistd.h> > + > + void _test_output_flush(void) > + { > + fprintf( stderr, "%s", _test_output_buffer ); > + _test_output_buffer_index = 0; > + tcdrain( 2 ); > + } > + > + #endif > + > +#elif defined(TESTS_USE_PRINTF) > + > + #include <stdio.h> > + #include <stdlib.h> > + > + #define rtems_test_exit(_s) \ > + do { \ > + exit(_s); \ > + } while (0) > + > + #define FLUSH_OUTPUT() \ > + do { \ > + fflush(stdout); \ > + } while (0) > + > + #define TEST_BEGIN() printf(TEST_BEGIN_STRING) > + > + #define TEST_END() printf(TEST_END_STRING) > + > +/* > + * USE IPRINT > + */ > +#else > + > + #include <stdio.h> > + #include <stdlib.h> > + > + /* do not use iprintf if strict ansi mode */ > + #if defined(_NEWLIB_VERSION) && !defined(__STRICT_ANSI__) > + #undef printf > + #define printf(...) \ > + do { \ > + fiprintf( stderr, __VA_ARGS__ ); \ > + } while (0) > + #else > + #undef printf > + #define printf(...) \ > + do { \ > + fprintf( stderr, __VA_ARGS__ ); \ > + } while (0) > + #endif > + > + #undef puts > + #define puts(_s) \ > + do { \ > + printf( "%s\n", _s ); \ > + } while (0) > + > + #undef putchar > + #define putchar(_c) \ > + do { \ > + printf( "%c", _c ); \ > + } while (0) > + > + #define rtems_test_exit(_s) \ > + do { \ > + exit(_s); \ > + } while (0) > + > + #define FLUSH_OUTPUT() \ > + do { \ > + fflush(stdout); \ > + } while (0) > + > + #define TEST_BEGIN() fiprintf( stderr, TEST_BEGIN_STRING) > + > + #define TEST_END() fiprintf( stderr, TEST_END_STRING) > + > +#endif > + > +#ifdef __cplusplus > +}; > +#endif > + > +#endif > diff --git a/testsuites/libtests/spi01/init.c > b/testsuites/libtests/spi01/init.c > new file mode 100644 > index 0000000..e80ad18 > --- /dev/null > +++ b/testsuites/libtests/spi01/init.c > @@ -0,0 +1,256 @@ > +/* > + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. > + * > + * embedded brains GmbH > + * Dornierstr. 4 > + * 82178 Puchheim > + * Germany > + * <rt...@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 <stdlib.h> > +#include <string.h> > +#include <unistd.h> > + > +#include <rtems/libcsupport.h> > + > +#include "tmacros.h" > + > +const char rtems_test_name[] = "SPI 1"; > + > +typedef struct test_device test_device; > + > +struct test_device { > + int (*transfer)( > + spi_bus *bus, > + 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; > + test_device_simple_read_write simple_read_write; > +} test_bus; > + > +static const char bus_path[] = "/dev/spi-0"; > + > +static int test_simple_read_write_transfer( > + spi_bus *bus, > + 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, spi_ioc_transfer *msgs, uint32_t > msg_count) > +{ > + test_bus *bus = (test_bus *) base; > + test_device *dev; > + > + dev = bus->device; > + > + return (*dev->transfer)(&bus->base, msgs, msg_count, dev); > +} > + > +static int test_set_clock(spi_bus *base, unsigned long clock) > +{ > + test_bus *bus = (test_bus *) base; > + > + bus->clock = clock; > + > + 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 == ENOTTY); > + > + 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; > + > + 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.set_clock = test_set_clock; > + bus->base.destroy = test_destroy; > + > + bus->simple_read_write.base.transfer = test_simple_read_write_transfer; > + bus->device = &bus->simple_read_write.base; > + > + 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); > + > + rtems_test_assert(bus->clock == 0); > + rv = ioctl(fd, SPI_BUS_SET_CLOCK, 0xdeadbeefUL); > + rtems_test_assert(rv == 0); > + rtems_test_assert(bus->clock == 0xdeadbeef); > + > + 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, 0x12345678); > + rtems_test_assert(rv == 0); > + rv = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &bus->base.max_speed); > + rtems_test_assert(rv == 0); > + rtems_test_assert(bus->base.max_speed == 0x12345678); > + > + rv = ioctl(fd, SPI_IOC_WR_LSB_FIRST, 1); > + rtems_test_assert(rv == 0); > + rv = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &bus->base.lsb_first); > + rtems_test_assert(rv == 0); > + rtems_test_assert(bus->base.lsb_first == 1); > + > + rv = ioctl(fd, SPI_IOC_WR_MODE, 0xA5); > + rtems_test_assert(rv == 0); > + rv = ioctl(fd, SPI_IOC_RD_MODE, &bus->base.data.mode); > + rtems_test_assert(rv == 0); > + rtems_test_assert(bus->base.data.mode == 0xA5); > + > + rv = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, 8); > + rtems_test_assert(rv == 0); > + rv = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bus->base.data.bits_per_word); > + rtems_test_assert(rv == 0); > + rtems_test_assert(bus->base.data.bits_per_word == 8); > + > + rv = ioctl(fd, SPI_IOC_WR_MODE32, 0x5A5A5A5A); > + rtems_test_assert(rv == 0); > + rv = ioctl(fd, SPI_IOC_RD_MODE32, &bus->base.data.mode); > + rtems_test_assert(rv == 0); > + rtems_test_assert(bus->base.data.mode == 0x5A5A5A5A); > + > + 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> > diff --git a/testsuites/libtests/spi01/spi01.doc > b/testsuites/libtests/spi01/spi01.doc > new file mode 100644 > index 0000000..754aa8a > --- /dev/null > +++ b/testsuites/libtests/spi01/spi01.doc > @@ -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. > diff --git a/testsuites/libtests/spi01/spi01.scn > b/testsuites/libtests/spi01/spi01.scn > new file mode 100644 > index 0000000..6d8f6cc > --- /dev/null > +++ b/testsuites/libtests/spi01/spi01.scn > @@ -0,0 +1,2 @@ > +*** BEGIN OF TEST SPI 1 *** > +*** END OF TEST SPI 1 *** > diff --git a/testsuites/libtests/spi01/tmacros.h > b/testsuites/libtests/spi01/tmacros.h > new file mode 100644 > index 0000000..d8aec2a > --- /dev/null > +++ b/testsuites/libtests/spi01/tmacros.h > @@ -0,0 +1,372 @@ > +/** > + * @file > + * > + * This include file contains macros which are useful in the RTEMS > + * test suites. > + */ > + > +/* > + * COPYRIGHT (c) 1989-2014. > + * On-Line Applications Research Corporation (OAR). > + * > + * 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 __TMACROS_h > +#define __TMACROS_h > + > +#include <inttypes.h> > +#include <bsp.h> /* includes <rtems.h> */ > + > +#include <ctype.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <rtems/error.h> > +#include <rtems/test.h> > +#include <rtems/score/threaddispatch.h> > + > +#include <buffer_test_io.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#define FOREVER 1 /* infinite loop */ > + > +#ifdef CONFIGURE_INIT > +#define TEST_EXTERN > +#else > +#define TEST_EXTERN extern > +#endif > + > +/* > + * Check that that the dispatch disable level is proper for the > + * mode/state of the test. Normally it should be 0 when in task space. > + * > + * This test is only valid when in a non-SMP system. In an smp system > + * another cpu may be accessing the core at any point when this core > + * does not have it locked. > + */ > +#if defined SMPTEST > + #define check_dispatch_disable_level( _expect ) > +#else > + #define check_dispatch_disable_level( _expect ) \ > + do { \ > + if ( (_expect) != -1 \ > + && (((!_Thread_Dispatch_is_enabled()) == false && (_expect) != 0) > \ > + || ((!_Thread_Dispatch_is_enabled()) && (_expect) == 0)) \ > + ) { \ > + printk( \ > + "\n_Thread_Dispatch_disable_level is (%i)" \ > + " not %d detected at %s:%d\n", \ > + !_Thread_Dispatch_is_enabled(), (_expect), __FILE__, __LINE__ ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( 1 ); \ > + } \ > + } while ( 0 ) > +#endif > + > +/* > + * Check that that the allocator mutex is not owned by the executing thread. > + */ > +#include <rtems/score/apimutex.h> > +#define check_if_allocator_mutex_is_not_owned() \ > + do { \ > + if ( _RTEMS_Allocator_is_owner() ) { \ > + printk( \ > + "\nRTEMS Allocator Mutex is owned by executing thread " \ > + "and should not be.\n" \ > + "Detected at %s:%d\n", \ > + __FILE__, \ > + __LINE__ \ > + ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( 1 ); \ > + } \ > + } while ( 0 ) > + > +/* > + * These macros properly report errors within the Classic API > + */ > +#define directive_failed( _dirstat, _failmsg ) \ > + fatal_directive_status( _dirstat, RTEMS_SUCCESSFUL, _failmsg ) > + > +#define directive_failed_with_level( _dirstat, _failmsg, _level ) \ > + fatal_directive_status_with_level( \ > + _dirstat, RTEMS_SUCCESSFUL, _failmsg, _level ) > + > +#define fatal_directive_status( _stat, _desired, _msg ) \ > + fatal_directive_status_with_level( _stat, _desired, _msg, 0 ) > + > +#define fatal_directive_check_status_only( _stat, _desired, _msg ) \ > + do { \ > + if ( (_stat) != (_desired) ) { \ > + printf( "\n%s FAILED -- expected (%s) got (%s)\n", \ > + (_msg), rtems_status_text(_desired), rtems_status_text(_stat) > ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( _stat ); \ > + } \ > + } while ( 0 ) > + > +#define fatal_directive_status_with_level( _stat, _desired, _msg, _level ) \ > + do { \ > + check_dispatch_disable_level( _level ); \ > + check_if_allocator_mutex_is_not_owned(); \ > + fatal_directive_check_status_only( _stat, _desired, _msg ); \ > + } while ( 0 ) > + > +/* > + * These macros properly report errors from the POSIX API > + */ > + > +#define posix_service_failed( _dirstat, _failmsg ) \ > + fatal_posix_service_status( _dirstat, 0, _failmsg ) > + > +#define posix_service_failed_with_level( _dirstat, _failmsg, _level ) \ > + fatal_posix_service_status_with_level( _dirstat, 0, _failmsg, _level ) > + > +#define fatal_posix_service_status_errno( _stat, _desired, _msg ) \ > + if ( (_stat != -1) && (errno) != (_desired) ) { \ > + long statx = _stat; \ > + check_dispatch_disable_level( 0 ); \ > + check_if_allocator_mutex_is_not_owned(); \ > + printf( "\n%s FAILED -- expected (%d - %s) got (%ld %d - %s)\n", \ > + (_msg), _desired, strerror(_desired), \ > + statx, errno, strerror(errno) ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( _stat ); \ > + } > + > +#define fatal_posix_service_status( _stat, _desired, _msg ) \ > + fatal_posix_service_status_with_level( _stat, _desired, _msg, 0 ) > + > +#define fatal_posix_service_status_with_level( _stat, _desired, _msg, _level > ) \ > + do { \ > + check_dispatch_disable_level( _level ); \ > + check_if_allocator_mutex_is_not_owned(); \ > + if ( (_stat) != (_desired) ) { \ > + printf( "\n%s FAILED -- expected (%d - %s) got (%d - %s)\n", \ > + (_msg), _desired, strerror(_desired), _stat, strerror(_stat) > ); \ > + printf( "\n FAILED -- errno (%d - %s)\n", \ > + errno, strerror(errno) ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( _stat ); \ > + } \ > + } while ( 0 ) > + > +/* > + * This macro evaluates the semaphore id returned. > + */ > +#define fatal_posix_sem( _ptr, _msg ) \ > + if ( (_ptr != SEM_FAILED) ) { \ > + check_dispatch_disable_level( 0 ); \ > + printf( "\n%s FAILED -- expected (-1) got (%p - %d/%s)\n", \ > + (_msg), _ptr, errno, strerror(errno) ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( -1 ); \ > + } > + > +/* > + * This macro evaluates the message queue id returned. > + */ > +#define fatal_posix_mqd( _ptr, _msg ) \ > + if ( (_ptr != (mqd_t) -1) ) { \ > + check_dispatch_disable_level( 0 ); \ > + printf( "\n%s FAILED -- expected (-1) got (%" PRId32 " - %d/%s)\n", \ > + (_msg), _ptr, errno, strerror(errno) ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( -1 ); \ > + } > + > +/* > + * Generic integer version of the error reporting > + */ > + > +#define int_service_failed( _dirstat, _failmsg ) \ > + fatal_int_service_status( _dirstat, RTEMS_SUCCESSFUL, _failmsg ) > + > +#define int_service_failed_with_level( _dirstat, _failmsg, _level ) \ > + fatal_int_service_status_with_level( \ > + _dirstat, RTEMS_SUCCESSFUL, _failmsg, _level ) > + > +#define fatal_int_service_status( _stat, _desired, _msg ) \ > + fatal_int_service_status_with_level( _stat, _desired, _msg, 0 ) > + > +#define fatal_int_service_status_with_level( _stat, _desired, _msg, _level ) > \ > + do { \ > + check_dispatch_disable_level( _level ); \ > + if ( (_stat) != (_desired) ) { \ > + printf( "\n%s FAILED -- expected (%d) got (%d)\n", \ > + (_msg), (_desired), (_stat) ); \ > + FLUSH_OUTPUT(); \ > + rtems_test_exit( _stat ); \ > + } \ > + } while ( 0 ) > + > + > +/* > + * Print the time > + */ > + > +#define sprint_time(_str, _s1, _tb, _s2) \ > + do { \ > + sprintf( (_str), "%s%02d:%02d:%02d %02d/%02d/%04d%s", \ > + _s1, (_tb)->hour, (_tb)->minute, (_tb)->second, \ > + (_tb)->month, (_tb)->day, (_tb)->year, _s2 ); \ > + } while ( 0 ) > + > +#define print_time(_s1, _tb, _s2) \ > + do { \ > + printf( "%s%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 " %02" PRIu32 > "/%02" PRIu32 "/%04" PRIu32 "%s", \ > + _s1, (_tb)->hour, (_tb)->minute, (_tb)->second, \ > + (_tb)->month, (_tb)->day, (_tb)->year, _s2 ); \ > + } while ( 0 ) > + > +#define put_dot( _c ) \ > + do { \ > + putchar( _c ); \ > + FLUSH_OUTPUT(); \ > + } while ( 0 ) > + > +#define new_line puts( "" ) > + > +#define puts_nocr printf > + > +#ifdef RTEMS_TEST_NO_PAUSE > +#define rtems_test_pause() \ > + do { \ > + printf( "<pause>\n" ); \ > + FLUSH_OUTPUT(); \ > + } while ( 0 ) > + > +#define rtems_test_pause_and_screen_number( _screen ) \ > + do { \ > + printf( "<pause - screen %d>\n", (_screen) ); \ > + FLUSH_OUTPUT(); \ > + } while ( 0 ) > +#else > +#define rtems_test_pause() \ > + do { \ > + char buffer[ 80 ]; \ > + printf( "<pause>" ); \ > + FLUSH_OUTPUT(); \ > + gets( buffer ); \ > + puts( "" ); \ > + } while ( 0 ) > + > +#define rtems_test_pause_and_screen_number( _screen ) \ > + do { \ > + char buffer[ 80 ]; \ > + printf( "<pause - screen %d>", (_screen) ); \ > + FLUSH_OUTPUT(); \ > + gets( buffer ); \ > + puts( "" ); \ > + } while ( 0 ) > +#endif > + > +#define put_name( name, crlf ) \ > +{ int c0, c1, c2, c3; \ > + c0 = (name >> 24) & 0xff; \ > + c1 = (name >> 16) & 0xff; \ > + c2 = (name >> 8) & 0xff; \ > + c3 = name & 0xff; \ > + putchar( (isprint(c0)) ? c0 : '*' ); \ > + if ( c1 ) putchar( (isprint(c1)) ? c1 : '*' ); \ > + if ( c2 ) putchar( (isprint(c2)) ? c2 : '*' ); \ > + if ( c3 ) putchar( (isprint(c3)) ? c3 : '*' ); \ > + if ( crlf ) \ > + putchar( '\n' ); \ > +} > + > +#ifndef build_time > +#define build_time( TB, MON, DAY, YR, HR, MIN, SEC, TK ) \ > + { (TB)->year = YR; \ > + (TB)->month = MON; \ > + (TB)->day = DAY; \ > + (TB)->hour = HR; \ > + (TB)->minute = MIN; \ > + (TB)->second = SEC; \ > + (TB)->ticks = TK; } > +#endif > + > +#define task_number( tid ) \ > + ( rtems_object_id_get_index( tid ) - \ > + rtems_configuration_get_rtems_api_configuration()-> \ > + number_of_initialization_tasks ) > + > +#define rtems_test_assert(__exp) \ > + do { \ > + if (!(__exp)) { \ > + printf( "%s: %d %s\n", __FILE__, __LINE__, #__exp ); \ > + rtems_test_exit(0); \ > + } \ > + } while (0) > + > +/* > + * Various inttypes.h-stype macros to assist printing > + * certain system types on different targets. > + */ > + > +#if defined(RTEMS_USE_16_BIT_OBJECT) > +#define PRIxrtems_id PRIx16 > +#else > +#define PRIxrtems_id PRIx32 > +#endif > + > +/* c.f. cpukit/score/include/rtems/score/priority.h */ > +#define PRIdPriority_Control PRId32 > +#define PRIxPriority_Control PRIx32 > +/* rtems_task_priority is a typedef to Priority_Control */ > +#define PRIdrtems_task_priority PRIdPriority_Control > +#define PRIxrtems_task_priority PRIxPriority_Control > + > +/* c.f. cpukit/score/include/rtems/score/watchdog.h */ > +#define PRIdWatchdog_Interval PRIu32 > +/* rtems_interval is a typedef to Watchdog_Interval */ > +#define PRIdrtems_interval PRIdWatchdog_Interval > + > +/* c.f. cpukit/score/include/rtems/score/thread.h */ > +#define PRIdThread_Entry_numeric_type PRIuPTR > +/* rtems_task_argument is a typedef to Thread_Entry_numeric_type */ > +#define PRIdrtems_task_argument PRIdThread_Entry_numeric_type > + > +/* rtems_event_set is a typedef to unit32_t */ > +#define PRIxrtems_event_set PRIx32 > + > +/* HACK: newlib defines pthread_t as a typedef to __uint32_t */ > +/* HACK: There is no portable way to print pthread_t's */ > +#define PRIxpthread_t PRIx32 > + > +/* rtems_signal_set is a typedef to uint32_t */ > +#define PRIxrtems_signal_set PRIx32 > + > +/* newlib's ino_t is a typedef to "unsigned long" */ > +#define PRIxino_t "lx" > + > +/** > + * This assists in clearly disabling warnings on GCC in certain very > + * specific cases. > + * > + * + -Wnon-null - If a method is declared as never having a NULL first > + * parameter. We need to explicitly disable this compiler warning to make > + * the code warning free. > + */ > +#ifdef __GNUC__ > + #define COMPILER_DIAGNOSTIC_SETTINGS_PUSH _Pragma("GCC diagnostic push") > + #define COMPILER_DIAGNOSTIC_SETTINGS_POP _Pragma("GCC diagnostic pop") > + #define COMPILER_DIAGNOSTIC_SETTINGS_DISABLE_NONNULL \ > + _Pragma("GCC diagnostic ignored \"-Wnonnull\"") > +#else > + #define COMPILER_DIAGNOSTIC_SETTINGS_PUSH > + #define COMPILER_DIAGNOSTIC_SETTINGS_POP > + #define COMPILER_DIAGNOSTIC_SETTINGS_DISABLE_NONNULL > +#endif > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > -- > 1.8.4.5 > > _______________________________________________ > devel mailing list > devel@rtems.org > http://lists.rtems.org/mailman/listinfo/devel _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel