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:
Tiago Medicci Serrano 2025-08-25 14:34:22 -03:00 committed by Xiang Xiao
parent 6f5a8c6b25
commit 585c25bac0

View file

@ -44,6 +44,7 @@
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <semaphore.h>
#include <nuttx/mutex.h>
#include <nuttx/kmalloc.h>
@ -76,7 +77,12 @@
struct i2schar_dev_s
{
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;
DEBUGASSERT(priv != NULL && apb != NULL);
UNUSED(priv);
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
* test I2S data transfer, then this is the point where you would
* 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;
DEBUGASSERT(priv != NULL && apb != NULL);
UNUSED(priv);
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
* test I2S data transfer, then this is the point where you would
* 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 */
ret = nxmutex_lock(&priv->lock);
ret = nxmutex_lock(&priv->rx_lock);
if (ret < 0)
{
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;
}
/* Lie to the caller and tell them that all of the bytes have been
* received
*/
/* Wait for the RX callback to signal completion */
nxmutex_unlock(&priv->lock);
return sizeof(struct ap_buffer_s) + nbytes;
ret = nxsem_wait(&priv->rx_sem);
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:
apb_free(apb);
nxmutex_unlock(&priv->lock);
nxmutex_unlock(&priv->rx_lock);
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 */
ret = nxmutex_lock(&priv->lock);
ret = nxmutex_lock(&priv->tx_lock);
if (ret < 0)
{
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 */
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 */
@ -607,7 +650,10 @@ int i2schar_register(FAR struct i2s_dev_s *i2s, int minor)
* 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);
return ret;
}