drivers/i2s/i2schar: Implement blocking read/write operations
This commit implements simple (blocking) read and write operations for the i2schar driver. Although it already contained such methods, the read/write operation was not being properly handled by the `i2schar_[rx|tx]callback` (called when the operation has finished). A semaphore is used to ensure that read data was received by the receiver and another semaphore ensures that data was written by the transmitter. Please note that the already-existing mutex for the I2S peripheral was replaced by two mutexes, one for the receiver and other for the transmitter to allow full-duplex communication. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
This commit is contained in:
parent
6f5a8c6b25
commit
585c25bac0
1 changed files with 59 additions and 13 deletions
|
|
@ -44,6 +44,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
#include <nuttx/mutex.h>
|
#include <nuttx/mutex.h>
|
||||||
#include <nuttx/kmalloc.h>
|
#include <nuttx/kmalloc.h>
|
||||||
|
|
@ -76,7 +77,12 @@
|
||||||
struct i2schar_dev_s
|
struct i2schar_dev_s
|
||||||
{
|
{
|
||||||
FAR struct i2s_dev_s *i2s; /* The lower half i2s driver */
|
FAR struct i2s_dev_s *i2s; /* The lower half i2s driver */
|
||||||
mutex_t lock; /* Assures mutually exclusive access */
|
mutex_t rx_lock; /* Assures mutually exclusive access to RX operations */
|
||||||
|
mutex_t tx_lock; /* Assures mutually exclusive access to TX operations */
|
||||||
|
sem_t rx_sem; /* Semaphore for blocking read operations */
|
||||||
|
sem_t tx_sem; /* Semaphore for blocking write operations */
|
||||||
|
int rx_result; /* Result of the last read operation */
|
||||||
|
int tx_result; /* Result of the last write operation */
|
||||||
};
|
};
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
|
@ -156,10 +162,17 @@ static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
||||||
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
||||||
|
|
||||||
DEBUGASSERT(priv != NULL && apb != NULL);
|
DEBUGASSERT(priv != NULL && apb != NULL);
|
||||||
UNUSED(priv);
|
|
||||||
|
|
||||||
i2sinfo("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
|
i2sinfo("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
|
||||||
|
|
||||||
|
/* Store the result and signal completion */
|
||||||
|
|
||||||
|
priv->rx_result = result;
|
||||||
|
|
||||||
|
/* Signal that the read operation has completed */
|
||||||
|
|
||||||
|
nxsem_post(&priv->rx_sem);
|
||||||
|
|
||||||
/* REVISIT: If you want this to actually do something other than
|
/* REVISIT: If you want this to actually do something other than
|
||||||
* test I2S data transfer, then this is the point where you would
|
* test I2S data transfer, then this is the point where you would
|
||||||
* want to pass the received I2S to some application.
|
* want to pass the received I2S to some application.
|
||||||
|
|
@ -206,10 +219,17 @@ static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
||||||
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
||||||
|
|
||||||
DEBUGASSERT(priv != NULL && apb != NULL);
|
DEBUGASSERT(priv != NULL && apb != NULL);
|
||||||
UNUSED(priv);
|
|
||||||
|
|
||||||
i2sinfo("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
|
i2sinfo("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
|
||||||
|
|
||||||
|
/* Store the result and signal completion */
|
||||||
|
|
||||||
|
priv->tx_result = result;
|
||||||
|
|
||||||
|
/* Signal that the write operation has completed */
|
||||||
|
|
||||||
|
nxsem_post(&priv->tx_sem);
|
||||||
|
|
||||||
/* REVISIT: If you want this to actually do something other than
|
/* REVISIT: If you want this to actually do something other than
|
||||||
* test I2S data transfer, then this is the point where you would
|
* test I2S data transfer, then this is the point where you would
|
||||||
* want to let some application know that the transfer has completed.
|
* want to let some application know that the transfer has completed.
|
||||||
|
|
@ -284,7 +304,7 @@ static ssize_t i2schar_read(FAR struct file *filep, FAR char *buffer,
|
||||||
|
|
||||||
/* Get exclusive access to i2c character driver */
|
/* Get exclusive access to i2c character driver */
|
||||||
|
|
||||||
ret = nxmutex_lock(&priv->lock);
|
ret = nxmutex_lock(&priv->rx_lock);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
i2serr("ERROR: nxsem_wait returned: %d\n", ret);
|
i2serr("ERROR: nxsem_wait returned: %d\n", ret);
|
||||||
|
|
@ -301,16 +321,34 @@ static ssize_t i2schar_read(FAR struct file *filep, FAR char *buffer,
|
||||||
goto errout_with_reference;
|
goto errout_with_reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lie to the caller and tell them that all of the bytes have been
|
/* Wait for the RX callback to signal completion */
|
||||||
* received
|
|
||||||
*/
|
|
||||||
|
|
||||||
nxmutex_unlock(&priv->lock);
|
ret = nxsem_wait(&priv->rx_sem);
|
||||||
return sizeof(struct ap_buffer_s) + nbytes;
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
i2serr("ERROR: nxsem_wait returned: %d\n", ret);
|
||||||
|
goto errout_with_reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the result from the private data */
|
||||||
|
|
||||||
|
ret = priv->rx_result;
|
||||||
|
|
||||||
|
/* If the operation was successful, return the number of bytes received */
|
||||||
|
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
ret = sizeof(struct ap_buffer_s) + apb->nbytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release our reference to the audio buffer */
|
||||||
|
|
||||||
|
nxmutex_unlock(&priv->rx_lock);
|
||||||
|
return ret;
|
||||||
|
|
||||||
errout_with_reference:
|
errout_with_reference:
|
||||||
apb_free(apb);
|
apb_free(apb);
|
||||||
nxmutex_unlock(&priv->lock);
|
nxmutex_unlock(&priv->rx_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,7 +413,7 @@ static ssize_t i2schar_write(FAR struct file *filep, FAR const char *buffer,
|
||||||
|
|
||||||
/* Get exclusive access to i2c character driver */
|
/* Get exclusive access to i2c character driver */
|
||||||
|
|
||||||
ret = nxmutex_lock(&priv->lock);
|
ret = nxmutex_lock(&priv->tx_lock);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
i2serr("ERROR: nxsem_wait returned: %d\n", ret);
|
i2serr("ERROR: nxsem_wait returned: %d\n", ret);
|
||||||
|
|
@ -595,7 +633,12 @@ int i2schar_register(FAR struct i2s_dev_s *i2s, int minor)
|
||||||
/* Initialize the I2S character device structure */
|
/* Initialize the I2S character device structure */
|
||||||
|
|
||||||
priv->i2s = i2s;
|
priv->i2s = i2s;
|
||||||
nxmutex_init(&priv->lock);
|
nxmutex_init(&priv->rx_lock);
|
||||||
|
nxmutex_init(&priv->tx_lock);
|
||||||
|
nxsem_init(&priv->rx_sem, 0, 0);
|
||||||
|
nxsem_init(&priv->tx_sem, 0, 0);
|
||||||
|
priv->rx_result = 0;
|
||||||
|
priv->tx_result = 0;
|
||||||
|
|
||||||
/* Create the character device name */
|
/* Create the character device name */
|
||||||
|
|
||||||
|
|
@ -607,7 +650,10 @@ int i2schar_register(FAR struct i2s_dev_s *i2s, int minor)
|
||||||
* device.
|
* device.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
nxmutex_destroy(&priv->lock);
|
nxsem_destroy(&priv->tx_sem);
|
||||||
|
nxsem_destroy(&priv->rx_sem);
|
||||||
|
nxmutex_destroy(&priv->rx_lock);
|
||||||
|
nxmutex_destroy(&priv->tx_lock);
|
||||||
kmm_free(priv);
|
kmm_free(priv);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue