diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 95bd112500..b5a7399f2c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -30,5 +30,10 @@ config I2C_NTRACE config I2C_DRIVER bool "I2C character driver" default n + ---help--- + Build in support for a character driver at /dev/i2c[N] that may be + used to perform I2C bus transfers from applications. The intent of + this driver is to support I2C testing. It is not suitable for use + in any real driver application. endif # I2C diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ff38027e39..b08c18fdba 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -47,13 +47,6 @@ config SPI_CALLBACK the SPI-based MMC/SD driver to get a notification of changes in the card status when an SD card is inserted or removed. -config SPI_BITBANG - bool "SPI bit-bang device" - default n - ---help--- - Enable support for a generic SPI bit-bang device. - See include/nuttx/spi/spi_bitbang.h for further information. - config SPI_HWFEATURES bool default n @@ -90,6 +83,23 @@ config SPI_CS_DELAY_CONTROL This option enables the setdelay() interface method. +config SPI_DRIVER + bool "SPI character driver" + default n + depends on SPI_EXCHANGE + ---help--- + Build in support for a character driver at /dev/spi[N] that may be + used to perform SPI bus transfers from applications. The intent of + this driver is to support SPI testing. It is not suitable for use + in any real driver application. + +config SPI_BITBANG + bool "SPI bit-bang device" + default n + ---help--- + Enable support for a generic SPI bit-bang device. + See include/nuttx/spi/spi_bitbang.h for further information. + if SPI_BITBANG config SPI_BITBANG_VARWIDTH diff --git a/drivers/spi/Make.defs b/drivers/spi/Make.defs index 0b0539cc95..e489bd600e 100644 --- a/drivers/spi/Make.defs +++ b/drivers/spi/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # drivers/spi/Make.defs # -# Copyright (C) 2013 Gregory Nutt. All rights reserved. +# Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -39,6 +39,9 @@ ifeq ($(CONFIG_SPI),y) ifeq ($(CONFIG_SPI_EXCHANGE),y) CSRCS += spi_transfer.c + ifeq ($(CONFIG_SPI_DRIVER),y) + CSRCS += spi_driver.c + endif endif # Include the selected SPI drivers diff --git a/drivers/spi/spi_driver.c b/drivers/spi/spi_driver.c new file mode 100644 index 0000000000..bbbfb46e68 --- /dev/null +++ b/drivers/spi/spi_driver.c @@ -0,0 +1,411 @@ +/**************************************************************************** + * drivers/spi/spi_driver.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SPI_DRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Device naming ************************************************************/ + +#define DEVNAME_FMT "/dev/spi%d" +#define DEVNAME_FMTLEN (8 + 3 + 1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Driver state structure */ + +struct spi_driver_s +{ + FAR struct spi_dev_s *spi; /* Contained SPI lower half driver */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + sem_t exclsem; /* Mutual exclusion */ + int16_t crefs; /* Number of open references */ + bool unlinked; /* True, driver has been unlinked */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int spidrvr_open(FAR struct file *filep); +static int spidrvr_close(FAR struct file *filep); +static ssize_t spidrvr_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t spidrvr_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int spidrvr_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_unlink(FAR struct inode *inode); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations spidrvr_fops = +{ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + spidrvr_open, /* open */ + spidrvr_close, /* close */ +#else + 0, /* open */ + 0, /* close */ +#endif + spidrvr_read, /* read */ + spidrvr_write, /* write */ + 0, /* seek */ + spidrvr_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , spidrvr_unlink /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spidrvr_open + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct spi_driver_s *priv; + int ret; + + /* Get our private data structure */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + priv = (FAR struct spi_driver_s *)inode->i_private; + DEBUGASSERT(priv); + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Increment the count of open references on the RTC driver */ + + priv->crefs++; + DEBUGASSERT(priv->crefs > 0); + + sem_post(&priv->exclsem); + return OK; +} +#endif + +/**************************************************************************** + * Name: spidrvr_close + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct spi_driver_s *priv; + int ret; + + /* Get our private data structure */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + priv = (FAR struct spi_driver_s *)inode->i_private; + DEBUGASSERT(priv); + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Decrement the count of open references on the RTC driver */ + + DEBUGASSERT(priv->crefs > 0); + priv->crefs--; + + /* If the count has decremented to zero and the driver has been unlinked, + * then commit Hara-Kiri now. + */ + + if (priv->crefs <= 0 && priv->unlinked) + { + sem_destroy(&priv->exclsem); + kmm_free(priv); + return OK; + } + + sem_post(&priv->exclsem); + return OK; +} +#endif + +/**************************************************************************** + * Name: spidrvr_read + ****************************************************************************/ + +static ssize_t spidrvr_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + return 0; /* Return EOF */ +} + +/**************************************************************************** + * Name: spidrvr_write + ****************************************************************************/ + +static ssize_t spidrvr_write(FAR struct file *filep, FAR const char *buffer, + size_t len) +{ + return len; /* Say that everything was written */ +} + +/**************************************************************************** + * Name: spidrvr_ioctl + ****************************************************************************/ + +static int spidrvr_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct spi_driver_s *priv; + FAR struct spi_sequence_s *seq; + int ret; + + spiinfo("cmd=%d arg=%lu\n", cmd, arg); + + /* Get our private data structure */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + priv = (FAR struct spi_driver_s *)inode->i_private; + DEBUGASSERT(priv); + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Process the IOCTL command */ + + switch (cmd) + { + /* Command: SPIIOC_TRANSFER + * Description: Perform a sequence of SPI transfers + * Argument: A reference to an instance of struct spi_sequence_s. + * Dependencies: CONFIG_SPI_DRIVER + */ + + case SPIIOC_TRANSFER: + { + /* Get the reference to the spi_transfer_s structure */ + + seq = (FAR struct spi_sequence_s *)((uintptr_t)arg); + DEBUGASSERT(seq != NULL); + + /* Perform the transfer */ + + ret = spi_transfer(priv->spi, seq); + } + break; + + default: + ret = -ENOTTY; + break; + } + + sem_post(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Name: spidrvr_unlink + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int spidrvr_unlink(FAR struct inode *inode) +{ + FAR struct spi_driver_s *priv; + int ret; + + /* Get our private data structure */ + + DEBUGASSERT(inode != NULL && inode->i_private != NULL); + priv = (FAR struct spi_driver_s *)inode->i_private; + + /* Get exclusive access to the SPI driver state structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode < 0); + return -errcode; + } + + /* Are there open references to the driver data structure? */ + + if (priv->crefs <= 0) + { + sem_destroy(&priv->exclsem); + kmm_free(priv); + return OK; + } + + /* No... just mark the driver as unlinked and free the resouces when the + * last client closes their reference to the driver. + */ + + priv->unlinked = true; + sem_post(&priv->exclsem); + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spi_register + * + * Description: + * Create and register the SPI character driver. + * + * The SPI character driver is a simple character driver that supports SPI + * transfers. The intent of this driver is to support SPI testing. It is + * not suitable for use in any real driver application. + * + * Input Parameters: + * spi - An instance of the lower half SPI driver + * bus - The SPI bus number. This will be used as the SPI device minor + * number. The SPI character device will be registered as /dev/spiN + * where N is the minor number + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int spi_register(FAR struct spi_dev_s *spi, int bus) +{ + FAR struct spi_driver_s *priv; + char devname[DEVNAME_FMTLEN]; + int ret; + + /* Sanity check */ + + DEBUGASSERT(spi != NULL && (unsigned)bus < 1000); + + /* Allocate a SPI character device structure */ + + priv = (FAR struct spi_driver_s *)kmm_zalloc(sizeof(struct spi_driver_s)); + if (priv) + { + /* Initialize the SPI character device structure */ + + priv->spi = spi; +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + sem_init(&priv->exclsem, 0, 1); +#endif + + /* Create the character device name */ + + snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, bus); + ret = register_driver(devname, &spidrvr_fops, 0666, priv); + if (ret < 0) + { + /* Free the device structure if we failed to create the character + * device. + */ + + kmm_free(priv); + } + + /* Return the result of the registration */ + + return OK; + } + + return -ENOMEM; +} + +#endif /* CONFIG_SPI_DRIVER */ diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 63074a3a9b..19fd8012b0 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -83,9 +83,10 @@ #define _LOOPBASE (0x1e00) /* Loop device commands */ #define _MODEMBASE (0x1f00) /* Modem ioctl commands */ #define _I2CBASE (0x2000) /* I2C driver commands */ -#define _GPIOBASE (0x2100) /* GPIO driver commands */ +#define _SPIBASE (0x2100) /* SPI driver commands */ +#define _GPIOBASE (0x2200) /* GPIO driver commands */ -/* boardctl commands share the same number space */ +/* boardctl() commands share the same number space */ #define _BOARDBASE (0xff00) /* boardctl commands */ @@ -385,7 +386,14 @@ #define _I2CIOCVALID(c) (_IOC_TYPE(c)==_I2CBASE) #define _I2CIOC(nr) _IOC(_I2CBASE,nr) +/* SPI driver ioctl definitions **********************************************/ +/* see nuttx/include/spi/spi_transfer.h */ + +#define _SPIIOCVALID(c) (_IOC_TYPE(c)==_SPIBASE) +#define _SPIIOC(nr) _IOC(_SPIBASE,nr) + /* GPIO driver command definitions ******************************************/ +/* see nuttx/include/ioexpander/gpio.h */ #define _GPIOCVALID(c) (_IOC_TYPE(c)==_GPIOBASE) #define _GPIOC(nr) _IOC(_GPIOBASE,nr) diff --git a/include/nuttx/spi/spi_transfer.h b/include/nuttx/spi/spi_transfer.h index b7d2cc0336..d4738ba6f5 100644 --- a/include/nuttx/spi/spi_transfer.h +++ b/include/nuttx/spi/spi_transfer.h @@ -46,10 +46,26 @@ #include #include +#include #include #ifdef CONFIG_SPI_EXCHANGE +/* SPI Character Driver IOCTL Commands **************************************/ +/* The SPI driver is intended to support application testing of the SPI bus. + * The SPI driver simply provides a user-accessible wrapper around the + * OS internal spi_transfer() function. The following IOCTL commands to + * supported by the SPI driver to perform SPI transfers + */ + +/* Command: SPIIOC_TRANSFER + * Description: Perform a sequence of SPI transfers + * Argument: A reference to an instance of struct spi_sequence_s. + * Dependencies: CONFIG_SPI_DRIVER + */ + +#define SPIIOC_TRANSFER _SPIIOC(0x0001) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -129,6 +145,32 @@ struct spi_sequence_s int spi_transfer(FAR struct spi_dev_s *spi, FAR struct spi_sequence_s *seq); +/**************************************************************************** + * Name: spi_register + * + * Description: + * Create and register the SPI character driver. + * + * The SPI character driver is a simple character driver that supports SPI + * transfers. The intent of this driver is to support SPI testing. It is + * not suitable for use in any real driver application. + * + * Input Parameters: + * spi - An instance of the lower half SPI driver + * bus - The SPI bus number. This will be used as the SPI device minor + * number. The SPI character device will be registered as /dev/spiN + * where N is the minor number + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_DRIVER +int spi_register(FAR struct spi_dev_s *spi, int bus); +#endif + #undef EXTERN #if defined(__cplusplus) #define EXTERN extern "C"