driver/spi_slave: support spi_slave asynchronous
1. support spi slave device poll 2. support read non-block/block mode Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
parent
abc1cade35
commit
9bf66ba3d5
2 changed files with 193 additions and 25 deletions
|
@ -33,6 +33,8 @@
|
|||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/mutex.h>
|
||||
|
@ -40,8 +42,6 @@
|
|||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/spi/slave.h>
|
||||
|
||||
#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,6 +309,15 @@ static ssize_t spi_slave_read(FAR struct file *filep, FAR char *buffer,
|
|||
}
|
||||
|
||||
priv->rx_length = MIN(buflen, sizeof(priv->rx_buffer));
|
||||
ret = nxmutex_lock(&priv->lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
spierr("Failed to get exclusive access: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
remaining_words = SPIS_CTRLR_QPOLL(priv->ctrlr);
|
||||
if (remaining_words == 0)
|
||||
{
|
||||
|
@ -310,10 +328,35 @@ static ssize_t spi_slave_read(FAR struct file *filep, FAR char *buffer,
|
|||
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 */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue