diff --git a/drivers/spi/spi_slave_driver.c b/drivers/spi/spi_slave_driver.c index ce9d5f3afe9..75c4942dea4 100644 --- a/drivers/spi/spi_slave_driver.c +++ b/drivers/spi/spi_slave_driver.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -40,8 +42,6 @@ #include #include -#ifdef CONFIG_SPI_SLAVE_DRIVER - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -64,7 +64,15 @@ struct spi_slave_driver_s /* Reference to SPI Slave controller interface */ - struct spi_slave_ctrlr_s *ctrlr; + FAR struct spi_slave_ctrlr_s *ctrlr; + + /* The poll waiter */ + + FAR struct pollfd *fds; + + /* The semphore reader */ + + sem_t wait; /* Receive buffer */ @@ -75,9 +83,9 @@ struct spi_slave_driver_s uint8_t tx_buffer[CONFIG_SPI_SLAVE_DRIVER_BUFFER_SIZE]; uint32_t tx_length; /* Location of next TX value */ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS mutex_t lock; /* Mutual exclusion */ int16_t crefs; /* Number of open references */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS bool unlinked; /* Indicates if the driver has been unlinked */ #endif }; @@ -94,18 +102,22 @@ static ssize_t spi_slave_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t spi_slave_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); +static int spi_slave_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); static int spi_slave_unlink(FAR struct inode *inode); /* SPI Slave driver methods */ -static void spi_slave_select(FAR struct spi_slave_dev_s *sdev, +static void spi_slave_select(FAR struct spi_slave_dev_s *dev, bool selected); -static void spi_slave_cmddata(FAR struct spi_slave_dev_s *sdev, +static void spi_slave_cmddata(FAR struct spi_slave_dev_s *dev, bool data); -static size_t spi_slave_getdata(FAR struct spi_slave_dev_s *sdev, +static size_t spi_slave_getdata(FAR struct spi_slave_dev_s *dev, FAR const void **data); -static size_t spi_slave_receive(FAR struct spi_slave_dev_s *sdev, +static size_t spi_slave_receive(FAR struct spi_slave_dev_s *dev, FAR const void *data, size_t nwords); +static void spi_slave_notify(FAR struct spi_slave_dev_s *dev, + spi_slave_state_t state); /**************************************************************************** * Private Data @@ -113,20 +125,15 @@ static size_t spi_slave_receive(FAR struct spi_slave_dev_s *sdev, static const struct file_operations g_spislavefops = { -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS spi_slave_open, /* open */ spi_slave_close, /* close */ -#else - NULL, /* open */ - NULL, /* close */ -#endif spi_slave_read, /* read */ spi_slave_write, /* write */ NULL, /* seek */ NULL, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + spi_slave_poll /* poll */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , spi_slave_unlink /* unlink */ #endif @@ -138,6 +145,7 @@ static const struct spi_slave_devops_s g_spisdev_ops = spi_slave_cmddata, /* cmddata */ spi_slave_getdata, /* getdata */ spi_slave_receive, /* receive */ + spi_slave_notify, /* notify */ }; /**************************************************************************** @@ -159,7 +167,6 @@ static const struct spi_slave_devops_s g_spisdev_ops = * ****************************************************************************/ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static int spi_slave_open(FAR struct file *filep) { FAR struct inode *inode; @@ -194,7 +201,6 @@ static int spi_slave_open(FAR struct file *filep) nxmutex_unlock(&priv->lock); return OK; } -#endif /**************************************************************************** * Name: spi_slave_close @@ -211,7 +217,6 @@ static int spi_slave_open(FAR struct file *filep) * ****************************************************************************/ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static int spi_slave_close(FAR struct file *filep) { FAR struct inode *inode; @@ -247,7 +252,11 @@ static int spi_slave_close(FAR struct file *filep) * unlinked, then dispose of the driver resources. */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS if (priv->crefs <= 0 && priv->unlinked) +#else + if (priv->crefs <= 0) +#endif { nxmutex_destroy(&priv->lock); kmm_free(priv); @@ -258,7 +267,6 @@ static int spi_slave_close(FAR struct file *filep) nxmutex_unlock(&priv->lock); return OK; } -#endif /**************************************************************************** * Name: spi_slave_read @@ -285,6 +293,7 @@ static ssize_t spi_slave_read(FAR struct file *filep, FAR char *buffer, FAR struct spi_slave_driver_s *priv; size_t read_bytes; size_t remaining_words; + int ret; spiinfo("filep=%p buffer=%p buflen=%zu\n", filep, buffer, buflen); @@ -300,20 +309,54 @@ static ssize_t spi_slave_read(FAR struct file *filep, FAR char *buffer, } priv->rx_length = MIN(buflen, sizeof(priv->rx_buffer)); - remaining_words = SPIS_CTRLR_QPOLL(priv->ctrlr); - if (remaining_words == 0) + ret = nxmutex_lock(&priv->lock); + if (ret < 0) { - spiinfo("All words retrieved!\n"); + spierr("Failed to get exclusive access: %d\n", ret); + return ret; } - else + + do { - spiinfo("%zu words left in the buffer\n", remaining_words); + remaining_words = SPIS_CTRLR_QPOLL(priv->ctrlr); + if (remaining_words == 0) + { + spiinfo("All words retrieved!\n"); + } + else + { + spiinfo("%zu words left in the buffer\n", remaining_words); + } + + if (priv->rx_length == 0) + { + nxmutex_unlock(&priv->lock); + if (filep->f_oflags & O_NONBLOCK) + { + return -EAGAIN; + } + + ret = nxsem_wait(&priv->wait); + if (ret < 0) + { + return ret; + } + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + spierr("Failed to get exclusive access: %d\n", ret); + return ret; + } + } } + while (priv->rx_length == 0); read_bytes = MIN(buflen, priv->rx_length); memcpy(buffer, priv->rx_buffer, read_bytes); + nxmutex_unlock(&priv->lock); return (ssize_t)read_bytes; } @@ -363,6 +406,66 @@ static ssize_t spi_slave_write(FAR struct file *filep, return (ssize_t)enqueued_bytes; } +/**************************************************************************** + * Name: spi_slave_poll + ****************************************************************************/ + +static int spi_slave_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct spi_slave_driver_s *priv; + FAR struct inode *inode; + int ret; + + /* Get our private data structure */ + + inode = filep->f_inode; + priv = (FAR struct spi_slave_driver_s *)inode->i_private; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + spierr("Failed to get exclusive access: %d\n", ret); + return ret; + } + + if (setup) + { + pollevent_t eventset = 0; + + if (priv->fds == NULL) + { + priv->fds = fds; + fds->priv = &priv->fds; + } + else + { + ret = -EBUSY; + } + + SPIS_CTRLR_QPOLL(priv->ctrlr); + if (priv->rx_length > 0) + { + eventset |= POLLOUT; + } + + if (!SPIS_CTRLR_QFULL(priv->ctrlr)) + { + eventset |= POLLIN; + } + + poll_notify(&priv->fds, 1, eventset); + } + else if (fds->priv != NULL) + { + priv->fds = NULL; + fds->priv = NULL; + } + + nxmutex_unlock(&priv->lock); + return ret; +} + /**************************************************************************** * Name: spi_slave_unlink * @@ -552,6 +655,46 @@ static size_t spi_slave_receive(FAR struct spi_slave_dev_s *dev, return BYTES2WORDS(recv_bytes); } +/**************************************************************************** + * Name: spi_slave_notify + * + * Description: + * This is a SPI device callback that is used when the SPI controller + * receives and sends complete or fail to notify spi slave upper half. + * And this callback can call in interrupt handler. + * + * Input Parameters: + * dev - SPI Slave device interface instance + * state - The Receive and send state, type of state is spi_slave_state_t + * + ****************************************************************************/ + +static void spi_slave_notify(FAR struct spi_slave_dev_s *dev, + spi_slave_state_t state) +{ + FAR struct spi_slave_driver_s *priv = (FAR struct spi_slave_driver_s *)dev; + + if (state == SPISLAVE_TX_COMPLETE) + { + poll_notify(&priv->fds, 1, POLLIN); + } + else if (state == SPISLAVE_RX_COMPLETE) + { + int semcnt; + + poll_notify(&priv->fds, 1, POLLOUT); + nxsem_get_value(&priv->wait, &semcnt); + if (semcnt < 1) + { + nxsem_post(&priv->wait); + } + } + else + { + spiinfo("sdev: %p transfer failed\n", dev); + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -630,5 +773,3 @@ int spi_slave_register(FAR struct spi_slave_ctrlr_s *ctrlr, int bus) return ret; } - -#endif /* CONFIG_SPI_SLAVE_DRIVER */ diff --git a/include/nuttx/spi/slave.h b/include/nuttx/spi/slave.h index 44c07257b98..02de4e622d1 100644 --- a/include/nuttx/spi/slave.h +++ b/include/nuttx/spi/slave.h @@ -293,6 +293,22 @@ #define SPIS_DEV_RECEIVE(d,v,l) ((d)->ops->receive(d,v,l)) +/**************************************************************************** + * Name: SPIS_DEV_NOTIFY + * + * Description: + * This is a SPI device callback that is used when the SPI controller + * receives and sends complete or fail to notify spi slave upper half. + * And this callback can call in interrupt handler. + * + * Input Parameters: + * dev - SPI Slave device interface instance + * state - The Receive and send state, type of state is spi_slave_state_t + * + ****************************************************************************/ + +#define SPIS_DEV_NOTIFY(d,s) ((d)->ops->notify(d,s)) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -475,6 +491,15 @@ enum spi_slave_mode_e SPISLAVE_MODE3 /* CPOL=1 CPHA=1 */ }; +/* The SPI slave transfer state */ + +typedef enum +{ + SPISLAVE_RX_COMPLETE = 0, + SPISLAVE_TX_COMPLETE, + SPISLAVE_TRANSFER_FAILED +} spi_slave_state_t; + /* The SPI slave controller driver vtable */ struct spi_slave_ctrlr_s; /* Forward reference */ @@ -516,6 +541,8 @@ struct spi_slave_devops_s FAR const void **data); CODE size_t (*receive)(FAR struct spi_slave_dev_s *sdev, FAR const void *data, size_t nwords); + CODE void (*notify)(FAR struct spi_slave_dev_s *sdev, + spi_slave_state_t state); }; /* SPI slave device private data. This structure only defines the initial