mirror of
https://github.com/apache/nuttx.git
synced 2026-05-31 23:40:19 +08:00
I2S simplifed audio buffer queuing
This commit is contained in:
@@ -6027,4 +6027,6 @@
|
|||||||
NuttX Kernel build (2013-11-10).
|
NuttX Kernel build (2013-11-10).
|
||||||
* arch/arm/src/sama5/sam_ssc.c and Kconfig: Add configurable support
|
* arch/arm/src/sama5/sam_ssc.c and Kconfig: Add configurable support
|
||||||
for SSC loopback mode (2013-11-10).
|
for SSC loopback mode (2013-11-10).
|
||||||
|
* include/nuttx/audio/i2s.h, arch/arm/src/sama5/sam_ssc.c, and
|
||||||
|
drivers/audio/i2schar.c: Improvied I2S interface design: Simplified
|
||||||
|
audio buffer queuing (2013-11-10).
|
||||||
|
|||||||
@@ -242,11 +242,10 @@
|
|||||||
struct sam_buffer_s
|
struct sam_buffer_s
|
||||||
{
|
{
|
||||||
struct sam_buffer_s *flink; /* Supports a singly linked list */
|
struct sam_buffer_s *flink; /* Supports a singly linked list */
|
||||||
void *callback; /* Function to call when the transfer completes */
|
i2s_callback_t callback; /* Function to call when the transfer completes */
|
||||||
uint32_t timeout; /* The timeout value to use with DMA transfers */
|
uint32_t timeout; /* The timeout value to use with DMA transfers */
|
||||||
void *arg; /* The argument to be returned with the callback */
|
void *arg; /* The argument to be returned with the callback */
|
||||||
void *buffer; /* I/O buffer */
|
struct ap_buffer_s *apb; /* The audio buffer */
|
||||||
size_t nbytes; /* Number of valid bytes in the buffer */
|
|
||||||
int result; /* The result of the transfer */
|
int result; /* The result of the transfer */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -399,13 +398,12 @@ static void ssc_txdma_callback(DMA_HANDLE handle, void *arg, int result);
|
|||||||
|
|
||||||
static uint32_t ssc_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate);
|
static uint32_t ssc_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate);
|
||||||
static uint32_t ssc_rxdatawidth(struct i2s_dev_s *dev, int bits);
|
static uint32_t ssc_rxdatawidth(struct i2s_dev_s *dev, int bits);
|
||||||
static int ssc_receive(struct i2s_dev_s *dev, void *buffer,
|
static int ssc_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
|
||||||
size_t nbytes, i2s_rxcallback_t callback, void *arg,
|
i2s_callback_t callback, void *arg, uint32_t timeout);
|
||||||
uint32_t timeout);
|
|
||||||
static uint32_t ssc_txsamplerate(struct i2s_dev_s *dev, uint32_t rate);
|
static uint32_t ssc_txsamplerate(struct i2s_dev_s *dev, uint32_t rate);
|
||||||
static uint32_t ssc_txdatawidth(struct i2s_dev_s *dev, int bits);
|
static uint32_t ssc_txdatawidth(struct i2s_dev_s *dev, int bits);
|
||||||
static int ssc_send(struct i2s_dev_s *dev, const void *buffer,
|
static int ssc_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
|
||||||
size_t nbytes, i2s_txcallback_t callback, void *arg,
|
i2s_callback_t callback, void *arg,
|
||||||
uint32_t timeout);
|
uint32_t timeout);
|
||||||
|
|
||||||
/* Initialization */
|
/* Initialization */
|
||||||
@@ -1013,6 +1011,7 @@ static void ssc_rxdma_timeout(int argc, uint32_t arg)
|
|||||||
static int ssc_rxdma_setup(struct sam_ssc_s *priv)
|
static int ssc_rxdma_setup(struct sam_ssc_s *priv)
|
||||||
{
|
{
|
||||||
struct sam_buffer_s *bfcontainer;
|
struct sam_buffer_s *bfcontainer;
|
||||||
|
struct ap_buffer_s *apb;
|
||||||
uintptr_t paddr;
|
uintptr_t paddr;
|
||||||
uintptr_t maddr;
|
uintptr_t maddr;
|
||||||
uint32_t timeout;
|
uint32_t timeout;
|
||||||
@@ -1049,17 +1048,21 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
|
|||||||
/* Remove the pending RX transfer at the head of the RX pending queue. */
|
/* Remove the pending RX transfer at the head of the RX pending queue. */
|
||||||
|
|
||||||
bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->rxpend);
|
bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->rxpend);
|
||||||
|
DEBUGASSERT(bfcontainer && bfcontainer->apb);
|
||||||
|
|
||||||
|
apb = bfcontainer->apb;
|
||||||
|
DEBUGASSERT(((uintptr_t)apb->samp & 3) == 0);
|
||||||
|
|
||||||
/* Physical address of the SSC RHR register and of the buffer location
|
/* Physical address of the SSC RHR register and of the buffer location
|
||||||
* in RAM.
|
* in RAM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
paddr = ssc_physregaddr(priv, SAM_SSC_RHR_OFFSET);
|
paddr = ssc_physregaddr(priv, SAM_SSC_RHR_OFFSET);
|
||||||
maddr = sam_physramaddr((uintptr_t)bfcontainer->buffer);
|
maddr = sam_physramaddr((uintptr_t)apb->samp);
|
||||||
|
|
||||||
/* Configure the RX DMA */
|
/* Configure the RX DMA */
|
||||||
|
|
||||||
sam_dmarxsetup(priv->rxdma, paddr, maddr, bfcontainer->nbytes);
|
sam_dmarxsetup(priv->rxdma, paddr, maddr, apb->nmaxbytes);
|
||||||
|
|
||||||
/* Increment the DMA timeout */
|
/* Increment the DMA timeout */
|
||||||
|
|
||||||
@@ -1075,6 +1078,14 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
|
|||||||
/* Add the container to the list of active DMAs */
|
/* Add the container to the list of active DMAs */
|
||||||
|
|
||||||
sq_addlast((sq_entry_t *)bfcontainer, &priv->rxact);
|
sq_addlast((sq_entry_t *)bfcontainer, &priv->rxact);
|
||||||
|
|
||||||
|
/* Invalidate the data cache so that nothing gets flush into the
|
||||||
|
* DMA buffer after starting the DMA transfer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cp15_invalidate_dcache((uintptr_t)apb->samp,
|
||||||
|
(uintptr_t)apb->samp + apb->nmaxbytes);
|
||||||
|
|
||||||
}
|
}
|
||||||
while (!sq_empty(&priv->rxpend));
|
while (!sq_empty(&priv->rxpend));
|
||||||
|
|
||||||
@@ -1082,13 +1093,6 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
|
|||||||
|
|
||||||
ssc_rxdma_sample(priv, DMA_AFTER_SETUP);
|
ssc_rxdma_sample(priv, DMA_AFTER_SETUP);
|
||||||
|
|
||||||
/* Invalidate the data cache so that nothing gets flush into the
|
|
||||||
* DMA buffer after starting the DMA transfer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
cp15_invalidate_dcache((uintptr_t)bfcontainer->buffer,
|
|
||||||
(uintptr_t)bfcontainer->buffer + bfcontainer->nbytes);
|
|
||||||
|
|
||||||
/* Start the DMA, saving the container as the current active transfer */
|
/* Start the DMA, saving the container as the current active transfer */
|
||||||
|
|
||||||
sam_dmastart(priv->rxdma, ssc_rxdma_callback, priv);
|
sam_dmastart(priv->rxdma, ssc_rxdma_callback, priv);
|
||||||
@@ -1140,7 +1144,6 @@ static void ssc_rx_worker(void *arg)
|
|||||||
{
|
{
|
||||||
struct sam_ssc_s *priv = (struct sam_ssc_s *)arg;
|
struct sam_ssc_s *priv = (struct sam_ssc_s *)arg;
|
||||||
struct sam_buffer_s *bfcontainer;
|
struct sam_buffer_s *bfcontainer;
|
||||||
i2s_rxcallback_t callback;
|
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
|
|
||||||
DEBUGASSERT(priv);
|
DEBUGASSERT(priv);
|
||||||
@@ -1203,11 +1206,13 @@ static void ssc_rx_worker(void *arg)
|
|||||||
/* Perform the TX transfer done callback */
|
/* Perform the TX transfer done callback */
|
||||||
|
|
||||||
DEBUGASSERT(bfcontainer && bfcontainer->callback);
|
DEBUGASSERT(bfcontainer && bfcontainer->callback);
|
||||||
|
bfcontainer->callback(&priv->dev, bfcontainer->apb,
|
||||||
callback = (i2s_rxcallback_t)bfcontainer->callback;
|
|
||||||
callback(&priv->dev, bfcontainer->buffer, bfcontainer->nbytes,
|
|
||||||
bfcontainer->arg, bfcontainer->result);
|
bfcontainer->arg, bfcontainer->result);
|
||||||
|
|
||||||
|
/* Release our reference on the audio buffer */
|
||||||
|
|
||||||
|
apb_free(bfcontainer->apb);
|
||||||
|
|
||||||
/* And release the buffer container */
|
/* And release the buffer container */
|
||||||
|
|
||||||
ssc_buf_free(priv, bfcontainer);
|
ssc_buf_free(priv, bfcontainer);
|
||||||
@@ -1381,6 +1386,7 @@ static void ssc_txdma_timeout(int argc, uint32_t arg)
|
|||||||
static int ssc_txdma_setup(struct sam_ssc_s *priv)
|
static int ssc_txdma_setup(struct sam_ssc_s *priv)
|
||||||
{
|
{
|
||||||
struct sam_buffer_s *bfcontainer;
|
struct sam_buffer_s *bfcontainer;
|
||||||
|
struct ap_buffer_s *apb;
|
||||||
uintptr_t paddr;
|
uintptr_t paddr;
|
||||||
uintptr_t maddr;
|
uintptr_t maddr;
|
||||||
uint32_t timeout;
|
uint32_t timeout;
|
||||||
@@ -1417,6 +1423,10 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
|
|||||||
/* Remove the pending TX transfer at the head of the TX pending queue. */
|
/* Remove the pending TX transfer at the head of the TX pending queue. */
|
||||||
|
|
||||||
bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->txpend);
|
bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->txpend);
|
||||||
|
DEBUGASSERT(bfcontainer && bfcontainer->apb);
|
||||||
|
|
||||||
|
apb = bfcontainer->apb;
|
||||||
|
DEBUGASSERT(((uintptr_t)apb->samp & 3) == 0);
|
||||||
|
|
||||||
/* Physical address of the SSC THR register and of the buffer location
|
/* Physical address of the SSC THR register and of the buffer location
|
||||||
* in
|
* in
|
||||||
@@ -1424,11 +1434,11 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
paddr = ssc_physregaddr(priv, SAM_SSC_THR_OFFSET);
|
paddr = ssc_physregaddr(priv, SAM_SSC_THR_OFFSET);
|
||||||
maddr = sam_physramaddr((uintptr_t)bfcontainer->buffer);
|
maddr = sam_physramaddr((uintptr_t)apb->samp);
|
||||||
|
|
||||||
/* Configure the TX DMA */
|
/* Configure the TX DMA */
|
||||||
|
|
||||||
sam_dmatxsetup(priv->txdma, paddr, maddr, bfcontainer->nbytes);
|
sam_dmatxsetup(priv->txdma, paddr, maddr, apb->nbytes);
|
||||||
|
|
||||||
/* Increment the DMA timeout */
|
/* Increment the DMA timeout */
|
||||||
|
|
||||||
@@ -1444,6 +1454,14 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
|
|||||||
/* Add the container to the list of active DMAs */
|
/* Add the container to the list of active DMAs */
|
||||||
|
|
||||||
sq_addlast((sq_entry_t *)bfcontainer, &priv->txact);
|
sq_addlast((sq_entry_t *)bfcontainer, &priv->txact);
|
||||||
|
|
||||||
|
/* Flush the data cache so that everything is in the physical memory
|
||||||
|
* before starting the DMA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cp15_clean_dcache((uintptr_t)apb->samp,
|
||||||
|
(uintptr_t)apb->samp + apb->nbytes);
|
||||||
|
|
||||||
}
|
}
|
||||||
while (!sq_empty(&priv->txpend));
|
while (!sq_empty(&priv->txpend));
|
||||||
|
|
||||||
@@ -1451,13 +1469,6 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
|
|||||||
|
|
||||||
ssc_txdma_sample(priv, DMA_AFTER_SETUP);
|
ssc_txdma_sample(priv, DMA_AFTER_SETUP);
|
||||||
|
|
||||||
/* Flush the data cache so that everything is in the physical memory
|
|
||||||
* before starting the DMA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
cp15_clean_dcache((uintptr_t)bfcontainer->buffer,
|
|
||||||
(uintptr_t)bfcontainer->buffer + bfcontainer->nbytes);
|
|
||||||
|
|
||||||
/* Start the DMA, saving the container as the current active transfer */
|
/* Start the DMA, saving the container as the current active transfer */
|
||||||
|
|
||||||
sam_dmastart(priv->txdma, ssc_txdma_callback, priv);
|
sam_dmastart(priv->txdma, ssc_txdma_callback, priv);
|
||||||
@@ -1509,7 +1520,6 @@ static void ssc_tx_worker(void *arg)
|
|||||||
{
|
{
|
||||||
struct sam_ssc_s *priv = (struct sam_ssc_s *)arg;
|
struct sam_ssc_s *priv = (struct sam_ssc_s *)arg;
|
||||||
struct sam_buffer_s *bfcontainer;
|
struct sam_buffer_s *bfcontainer;
|
||||||
i2s_txcallback_t callback;
|
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
|
|
||||||
DEBUGASSERT(priv);
|
DEBUGASSERT(priv);
|
||||||
@@ -1572,11 +1582,13 @@ static void ssc_tx_worker(void *arg)
|
|||||||
/* Perform the TX transfer done callback */
|
/* Perform the TX transfer done callback */
|
||||||
|
|
||||||
DEBUGASSERT(bfcontainer && bfcontainer->callback);
|
DEBUGASSERT(bfcontainer && bfcontainer->callback);
|
||||||
|
bfcontainer->callback(&priv->dev, bfcontainer->apb,
|
||||||
callback = (i2s_txcallback_t)bfcontainer->callback;
|
|
||||||
callback(&priv->dev, bfcontainer->buffer, bfcontainer->nbytes,
|
|
||||||
bfcontainer->arg, bfcontainer->result);
|
bfcontainer->arg, bfcontainer->result);
|
||||||
|
|
||||||
|
/* Release our reference on the audio buffer */
|
||||||
|
|
||||||
|
apb_free(bfcontainer->apb);
|
||||||
|
|
||||||
/* And release the buffer container */
|
/* And release the buffer container */
|
||||||
|
|
||||||
ssc_buf_free(priv, bfcontainer);
|
ssc_buf_free(priv, bfcontainer);
|
||||||
@@ -1780,9 +1792,7 @@ static uint32_t ssc_rxdatawidth(struct i2s_dev_s *dev, int bits)
|
|||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* dev - Device-specific state data
|
* dev - Device-specific state data
|
||||||
* buffer - A pointer to the buffer in which to recieve data
|
* apb - A pointer to the audio buffer in which to recieve data
|
||||||
* nbytes - The length of data that can be received in the buffer in number
|
|
||||||
* of bytes.
|
|
||||||
* callback - A user provided callback function that will be called at
|
* callback - A user provided callback function that will be called at
|
||||||
* the completion of the transfer. The callback will be
|
* the completion of the transfer. The callback will be
|
||||||
* performed in the context of the worker thread.
|
* performed in the context of the worker thread.
|
||||||
@@ -1804,17 +1814,16 @@ static uint32_t ssc_rxdatawidth(struct i2s_dev_s *dev, int bits)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static int ssc_receive(struct i2s_dev_s *dev, void *buffer,
|
static int ssc_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
|
||||||
size_t nbytes, i2s_rxcallback_t callback,
|
i2s_callback_t callback, void *arg, uint32_t timeout)
|
||||||
void *arg, uint32_t timeout)
|
|
||||||
{
|
{
|
||||||
struct sam_ssc_s *priv = (struct sam_ssc_s *)dev;
|
struct sam_ssc_s *priv = (struct sam_ssc_s *)dev;
|
||||||
struct sam_buffer_s *bfcontainer;
|
struct sam_buffer_s *bfcontainer;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2svdbg("buffer=%p nbytes=%d\n", buffer, (int)nbytes);
|
DEBUGASSERT(priv && apb && ((uintptr_t)apb->samp & 3) == 0);
|
||||||
DEBUGASSERT(priv && buffer && ((uintptr_t)buffer & 3) == 0);
|
i2svdbg("apb=%p nmaxbytes=%d\n", apb, apb->nmaxbytes);
|
||||||
|
|
||||||
#ifdef SSC_HAVE_RX
|
#ifdef SSC_HAVE_RX
|
||||||
/* Allocate a buffer container in advance */
|
/* Allocate a buffer container in advance */
|
||||||
@@ -1835,13 +1844,16 @@ static int ssc_receive(struct i2s_dev_s *dev, void *buffer,
|
|||||||
goto errout_with_exclsem;
|
goto errout_with_exclsem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add a reference to the audio buffer */
|
||||||
|
|
||||||
|
apb_reference(apb);
|
||||||
|
|
||||||
/* Initialize the buffer container structure */
|
/* Initialize the buffer container structure */
|
||||||
|
|
||||||
bfcontainer->callback = (void *)callback;
|
bfcontainer->callback = (void *)callback;
|
||||||
bfcontainer->timeout = timeout;
|
bfcontainer->timeout = timeout;
|
||||||
bfcontainer->arg = arg;
|
bfcontainer->arg = arg;
|
||||||
bfcontainer->buffer = buffer;
|
bfcontainer->apb = apb;
|
||||||
bfcontainer->nbytes = nbytes;
|
|
||||||
bfcontainer->result = -EBUSY;
|
bfcontainer->result = -EBUSY;
|
||||||
|
|
||||||
/* Add the buffer container to the end of the RX pending queue */
|
/* Add the buffer container to the end of the RX pending queue */
|
||||||
@@ -1958,8 +1970,7 @@ static uint32_t ssc_txdatawidth(struct i2s_dev_s *dev, int bits)
|
|||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* dev - Device-specific state data
|
* dev - Device-specific state data
|
||||||
* buffer - A pointer to the buffer of data to be sent
|
* apb - A pointer to the audio buffer from which to send data
|
||||||
* nbytes - the length of data to send from the buffer in number of bytes.
|
|
||||||
* callback - A user provided callback function that will be called at
|
* callback - A user provided callback function that will be called at
|
||||||
* the completion of the transfer. The callback will be
|
* the completion of the transfer. The callback will be
|
||||||
* performed in the context of the worker thread.
|
* performed in the context of the worker thread.
|
||||||
@@ -1981,17 +1992,16 @@ static uint32_t ssc_txdatawidth(struct i2s_dev_s *dev, int bits)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static int ssc_send(struct i2s_dev_s *dev, const void *buffer,
|
static int ssc_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
|
||||||
size_t nbytes, i2s_txcallback_t callback,
|
i2s_callback_t callback, void *arg, uint32_t timeout)
|
||||||
void *arg, uint32_t timeout)
|
|
||||||
{
|
{
|
||||||
struct sam_ssc_s *priv = (struct sam_ssc_s *)dev;
|
struct sam_ssc_s *priv = (struct sam_ssc_s *)dev;
|
||||||
struct sam_buffer_s *bfcontainer;
|
struct sam_buffer_s *bfcontainer;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2svdbg("buffer=%p nbytes=%d\n", buffer, (int)nbytes);
|
DEBUGASSERT(priv && apb && ((uintptr_t)apb->samp & 3) == 0);
|
||||||
DEBUGASSERT(priv && buffer && ((uintptr_t)buffer & 3) == 0);
|
i2svdbg("apb=%p nbytes=%d\n", apb, apb->nbytes);
|
||||||
|
|
||||||
#ifdef SSC_HAVE_TX
|
#ifdef SSC_HAVE_TX
|
||||||
/* Allocate a buffer container in advance */
|
/* Allocate a buffer container in advance */
|
||||||
@@ -2012,13 +2022,16 @@ static int ssc_send(struct i2s_dev_s *dev, const void *buffer,
|
|||||||
goto errout_with_exclsem;
|
goto errout_with_exclsem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add a reference to the audio buffer */
|
||||||
|
|
||||||
|
apb_reference(apb);
|
||||||
|
|
||||||
/* Initialize the buffer container structure */
|
/* Initialize the buffer container structure */
|
||||||
|
|
||||||
bfcontainer->callback = (void *)callback;
|
bfcontainer->callback = (void *)callback;
|
||||||
bfcontainer->timeout = timeout;
|
bfcontainer->timeout = timeout;
|
||||||
bfcontainer->arg = arg;
|
bfcontainer->arg = arg;
|
||||||
bfcontainer->buffer = (void *)buffer;
|
bfcontainer->apb = apb;
|
||||||
bfcontainer->nbytes = nbytes;
|
|
||||||
bfcontainer->result = -EBUSY;
|
bfcontainer->result = -EBUSY;
|
||||||
|
|
||||||
/* Add the buffer container to the end of the TX pending queue */
|
/* Add the buffer container to the end of the TX pending queue */
|
||||||
|
|||||||
+19
-76
@@ -52,7 +52,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <queue.h>
|
|
||||||
|
|
||||||
#include <nuttx/kmalloc.h>
|
#include <nuttx/kmalloc.h>
|
||||||
#include <nuttx/fs/fs.h>
|
#include <nuttx/fs/fs.h>
|
||||||
@@ -100,8 +99,6 @@ 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 */
|
||||||
sem_t exclsem; /* Assures mutually exclusive access */
|
sem_t exclsem; /* Assures mutually exclusive access */
|
||||||
dq_queue_t rxq; /* RX audio buffers awaiting transfer */
|
|
||||||
dq_queue_t txq; /* TX audio buffers awaiting transfer */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@@ -110,9 +107,9 @@ struct i2schar_dev_s
|
|||||||
/* I2S callback function */
|
/* I2S callback function */
|
||||||
|
|
||||||
static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
||||||
FAR void *buffer, size_t nbytes, FAR void *arg, int result);
|
FAR struct ap_buffer_s *apb, FAR void *arg, int result);
|
||||||
static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
||||||
FAR const void *buffer, size_t nbytes, FAR void *arg,
|
FAR struct ap_buffer_s *apb, FAR void *arg,
|
||||||
int result);
|
int result);
|
||||||
|
|
||||||
/* Character driver methods */
|
/* Character driver methods */
|
||||||
@@ -161,48 +158,26 @@ static const struct file_operations i2schar_fops =
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
||||||
FAR void *buffer, size_t nbytes,
|
FAR struct ap_buffer_s *apb,
|
||||||
FAR void *arg, int result)
|
FAR void *arg, int result)
|
||||||
{
|
{
|
||||||
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
||||||
FAR struct ap_buffer_s *apb;
|
|
||||||
|
|
||||||
i2svdbg("buffer=%p nbytes=%d result=%d\n", buffer, (int)nbytes, result);
|
DEBUGASSERT(priv && apb);
|
||||||
DEBUGASSERT(dev && buffer && priv);
|
i2svdbg("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
|
||||||
|
|
||||||
/* The returned buffer should match the buffer at the head of the RX audio
|
|
||||||
* buffer queue.
|
|
||||||
*/
|
|
||||||
|
|
||||||
apb = (FAR struct ap_buffer_s *)dq_peek(&priv->rxq);
|
|
||||||
if ((FAR const void *)apb->samp != buffer)
|
|
||||||
{
|
|
||||||
i2sdbg("ERROR: apb=%p apb->samp=%p buffer=%p\n",
|
|
||||||
apb, apb->samp, buffer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (apb->nmaxbytes != nbytes)
|
|
||||||
{
|
|
||||||
i2sdbg("ERROR: apb->nmaxbytes=%d nbytes=%d\n",
|
|
||||||
(int)apb->nmaxbytes, (int)nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Remove the buffer from the beginning of the RX queue and release
|
/* Release our reference to the audio buffer. Hopefully it will be freed
|
||||||
* our reference to it. Hopefully it will be freed now.
|
* now.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(void)dq_remfirst(&priv->rxq);
|
|
||||||
|
|
||||||
i2svdbg("Freeing apb=%p crefs=%d\n", apb, apb->crefs);
|
i2svdbg("Freeing apb=%p crefs=%d\n", apb, apb->crefs);
|
||||||
apb_free(apb);
|
apb_free(apb);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: i2schar_txcallback
|
* Name: i2schar_txcallback
|
||||||
@@ -218,48 +193,26 @@ static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
||||||
FAR const void *buffer, size_t nbytes,
|
FAR struct ap_buffer_s *apb,
|
||||||
FAR void *arg, int result)
|
FAR void *arg, int result)
|
||||||
{
|
{
|
||||||
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
|
||||||
FAR struct ap_buffer_s *apb;
|
|
||||||
|
|
||||||
i2svdbg("buffer=%p nbytes=%d result=%d\n", buffer, (int)nbytes, result);
|
DEBUGASSERT(priv && apb);
|
||||||
DEBUGASSERT(dev && buffer && priv);
|
i2svdbg("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
|
||||||
|
|
||||||
/* The returned buffer should match the buffer at the head of the RX audio
|
|
||||||
* buffer queue.
|
|
||||||
*/
|
|
||||||
|
|
||||||
apb = (FAR struct ap_buffer_s *)dq_peek(&priv->txq);
|
|
||||||
if ((FAR const void *)apb->samp != buffer)
|
|
||||||
{
|
|
||||||
i2sdbg("ERROR: apb=%p apb->samp=%p buffer=%p\n",
|
|
||||||
apb, apb->samp, buffer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (apb->nmaxbytes != nbytes)
|
|
||||||
{
|
|
||||||
i2sdbg("ERROR: apb->nmaxbytes=%d nbytes=%d\n",
|
|
||||||
(int)apb->nmaxbytes, (int)nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 let some application know that the transfer has complete.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Remove the buffer from the beginning of the RX queue and release
|
/* Release our reference to the audio buffer. Hopefully it will be freed
|
||||||
* our reference to it. Hopefully it will be freed now.
|
* now.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(void)dq_remfirst(&priv->txq);
|
|
||||||
|
|
||||||
i2svdbg("Freeing apb=%p crefs=%d\n", apb, apb->crefs);
|
i2svdbg("Freeing apb=%p crefs=%d\n", apb, apb->crefs);
|
||||||
apb_free(apb);
|
apb_free(apb);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: i2schar_read
|
* Name: i2schar_read
|
||||||
@@ -298,7 +251,7 @@ static ssize_t i2schar_read(FAR struct file *filep, FAR char *buffer,
|
|||||||
nbytes = apb->nmaxbytes;
|
nbytes = apb->nmaxbytes;
|
||||||
DEBUGASSERT(buflen >= (sizeof(struct ap_buffer_s) + nbytes));
|
DEBUGASSERT(buflen >= (sizeof(struct ap_buffer_s) + nbytes));
|
||||||
|
|
||||||
/* Add our buffer reference */
|
/* Add a reference to the audio buffer */
|
||||||
|
|
||||||
apb_reference(apb);
|
apb_reference(apb);
|
||||||
|
|
||||||
@@ -313,13 +266,9 @@ static ssize_t i2schar_read(FAR struct file *filep, FAR char *buffer,
|
|||||||
goto errout_with_reference;
|
goto errout_with_reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the audio buffer to the I2S receiver queue */
|
/* Give the buffer to the I2S driver */
|
||||||
|
|
||||||
dq_addlast(&apb->dq_entry, &priv->rxq);
|
ret = I2S_RECEIVE(priv->i2s, apb, i2schar_rxcallback, priv, 0);
|
||||||
|
|
||||||
/* And give the buffer to the I2S driver */
|
|
||||||
|
|
||||||
ret = I2S_RECEIVE(priv->i2s, apb->samp, nbytes, i2schar_rxcallback, priv, 0);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
i2sdbg("ERROR: I2S_RECEIVE returned: %d\n", ret);
|
i2sdbg("ERROR: I2S_RECEIVE returned: %d\n", ret);
|
||||||
@@ -376,7 +325,7 @@ static ssize_t i2schar_write(FAR struct file *filep, FAR const char *buffer,
|
|||||||
nbytes = apb->nmaxbytes;
|
nbytes = apb->nmaxbytes;
|
||||||
DEBUGASSERT(buflen >= (sizeof(struct ap_buffer_s) + nbytes));
|
DEBUGASSERT(buflen >= (sizeof(struct ap_buffer_s) + nbytes));
|
||||||
|
|
||||||
/* Add our buffer reference */
|
/* Add a reference to the audio buffer */
|
||||||
|
|
||||||
apb_reference(apb);
|
apb_reference(apb);
|
||||||
|
|
||||||
@@ -391,13 +340,9 @@ static ssize_t i2schar_write(FAR struct file *filep, FAR const char *buffer,
|
|||||||
goto errout_with_reference;
|
goto errout_with_reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the audio buffer to the I2S transmitter queue */
|
/* Give the audio buffer to the I2S driver */
|
||||||
|
|
||||||
dq_addlast(&apb->dq_entry, &priv->txq);
|
ret = I2S_SEND(priv->i2s, apb, i2schar_txcallback, priv, 0);
|
||||||
|
|
||||||
/* And give the buffer to the I2S driver */
|
|
||||||
|
|
||||||
ret = I2S_SEND(priv->i2s, apb->samp, nbytes, i2schar_txcallback, priv, 0);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
i2sdbg("ERROR: I2S_SEND returned: %d\n", ret);
|
i2sdbg("ERROR: I2S_SEND returned: %d\n", ret);
|
||||||
@@ -457,8 +402,6 @@ int i2schar_register(FAR struct i2s_dev_s *i2s, int minor)
|
|||||||
|
|
||||||
priv->i2s = i2s;
|
priv->i2s = i2s;
|
||||||
sem_init(&priv->exclsem, 0, 1);
|
sem_init(&priv->exclsem, 0, 1);
|
||||||
dq_init(&priv->rxq);
|
|
||||||
dq_init(&priv->txq);
|
|
||||||
|
|
||||||
/* Create the character device name */
|
/* Create the character device name */
|
||||||
|
|
||||||
|
|||||||
+14
-19
@@ -46,6 +46,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <nuttx/audio/audio.h>
|
||||||
|
|
||||||
#ifdef CONFIG_I2S
|
#ifdef CONFIG_I2S
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@@ -99,9 +101,7 @@
|
|||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* dev - Device-specific state data
|
* dev - Device-specific state data
|
||||||
* buffer - A pointer to the buffer in which to recieve data
|
* apb - A pointer to the audio buffer in which to recieve data
|
||||||
* nbytes - The length of data that can be received in the buffer in number
|
|
||||||
* of bytes.
|
|
||||||
* callback - A user provided callback function that will be called at
|
* callback - A user provided callback function that will be called at
|
||||||
* the completion of the transfer. The callback will be
|
* the completion of the transfer. The callback will be
|
||||||
* performed in the context of the worker thread.
|
* performed in the context of the worker thread.
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#define I2S_RECEIVE(d,b,n,c,a,t) ((d)->ops->i2s_receive(d,b,n,c,a,t))
|
#define I2S_RECEIVE(d,b,c,a,t) ((d)->ops->i2s_receive(d,b,c,a,t))
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: I2S_TXSAMPLERATE
|
* Name: I2S_TXSAMPLERATE
|
||||||
@@ -171,8 +171,7 @@
|
|||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* dev - Device-specific state data
|
* dev - Device-specific state data
|
||||||
* buffer - A pointer to the buffer of data to be sent
|
* apb - A pointer to the audio buffer from which to send data
|
||||||
* nbytes - the length of data to send from the buffer in number of bytes.
|
|
||||||
* callback - A user provided callback function that will be called at
|
* callback - A user provided callback function that will be called at
|
||||||
* the completion of the transfer. The callback will be
|
* the completion of the transfer. The callback will be
|
||||||
* performed in the context of the worker thread.
|
* performed in the context of the worker thread.
|
||||||
@@ -194,7 +193,7 @@
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#define I2S_SEND(d,b,n,c,a,t) ((d)->ops->i2s_send(d,b,n,c,a,t))
|
#define I2S_SEND(d,b,c,a,t) ((d)->ops->i2s_send(d,b,c,a,t))
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Types
|
* Public Types
|
||||||
@@ -202,12 +201,8 @@
|
|||||||
/* Transfer complete callbacks */
|
/* Transfer complete callbacks */
|
||||||
|
|
||||||
struct i2s_dev_s;
|
struct i2s_dev_s;
|
||||||
typedef CODE void (*i2s_rxcallback_t)(FAR struct i2s_dev_s *dev,
|
typedef CODE void (*i2s_callback_t)(FAR struct i2s_dev_s *dev,
|
||||||
FAR void *buffer, size_t nbytes, FAR void *arg,
|
FAR struct ap_buffer_s *apb, FAR void *arg, int result);
|
||||||
int result);
|
|
||||||
typedef CODE void (*i2s_txcallback_t)(FAR struct i2s_dev_s *dev,
|
|
||||||
FAR const void *buffer, size_t nbytes, FAR void *arg,
|
|
||||||
int result);
|
|
||||||
|
|
||||||
/* The I2S vtable */
|
/* The I2S vtable */
|
||||||
|
|
||||||
@@ -217,17 +212,17 @@ struct i2s_ops_s
|
|||||||
|
|
||||||
CODE uint32_t (*i2s_rxsamplerate)(FAR struct i2s_dev_s *dev, uint32_t rate);
|
CODE uint32_t (*i2s_rxsamplerate)(FAR struct i2s_dev_s *dev, uint32_t rate);
|
||||||
CODE uint32_t (*i2s_rxdatawidth)(FAR struct i2s_dev_s *dev, int bits);
|
CODE uint32_t (*i2s_rxdatawidth)(FAR struct i2s_dev_s *dev, int bits);
|
||||||
CODE int (*i2s_receive)(FAR struct i2s_dev_s *dev, FAR void *buffer,
|
CODE int (*i2s_receive)(FAR struct i2s_dev_s *dev,
|
||||||
size_t nbytes, i2s_rxcallback_t callback, FAR void *arg,
|
FAR struct ap_buffer_s *apb, i2s_callback_t callback,
|
||||||
uint32_t timeout);
|
FAR void *arg, uint32_t timeout);
|
||||||
|
|
||||||
/* Transmitter methods */
|
/* Transmitter methods */
|
||||||
|
|
||||||
CODE uint32_t (*i2s_txsamplerate)(FAR struct i2s_dev_s *dev, uint32_t rate);
|
CODE uint32_t (*i2s_txsamplerate)(FAR struct i2s_dev_s *dev, uint32_t rate);
|
||||||
CODE uint32_t (*i2s_txdatawidth)(FAR struct i2s_dev_s *dev, int bits);
|
CODE uint32_t (*i2s_txdatawidth)(FAR struct i2s_dev_s *dev, int bits);
|
||||||
CODE int (*i2s_send)(FAR struct i2s_dev_s *dev, FAR const void *buffer,
|
CODE int (*i2s_send)(FAR struct i2s_dev_s *dev,
|
||||||
size_t nbytes, i2s_txcallback_t callback, FAR void *arg,
|
FAR struct ap_buffer_s *apb, i2s_callback_t callback,
|
||||||
uint32_t timeout);
|
FAR void *arg, uint32_t timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* I2S private data. This structure only defines the initial fields of the
|
/* I2S private data. This structure only defines the initial fields of the
|
||||||
|
|||||||
Reference in New Issue
Block a user