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