drivers/i2s/i2schar: Implement ioctl commands for i2schar
This commit implements a set of commands to the i2s character driver. The implemented commands allows getting and setting parameters like data width, sample rate and number of channels for both receiver and transmitter. If no ioctl is found, the command is redirected to the lower driver implementation if it has the ioctl callback registered. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
This commit is contained in:
parent
aadb929526
commit
6f5a8c6b25
3 changed files with 326 additions and 29 deletions
|
|
@ -124,16 +124,28 @@ static const struct file_operations g_i2schar_fops =
|
|||
* Name: i2schar_rxcallback
|
||||
*
|
||||
* Description:
|
||||
* I2S RX transfer complete callback.
|
||||
* Callback function invoked upon completion of an I2S RX (receive)
|
||||
* transfer. This function is called by the lower-half I2S driver when
|
||||
* the reception of an audio buffer is complete.
|
||||
*
|
||||
* NOTE: In this test driver, the RX is simply dumped in the bit bucket.
|
||||
* You would not do this in a real application. You would return the
|
||||
* received data to the caller via some IPC.
|
||||
* In this test driver implementation, the received buffer is simply
|
||||
* freed. This is acceptable if this driver has the sole reference to the
|
||||
* buffer; in that case, the buffer will be freed. Otherwise, this may
|
||||
* result in a memory leak. A more efficient design would recycle the
|
||||
* audio buffers for future use.
|
||||
*
|
||||
* Also, the test buffer is simply freed. This will work if this driver
|
||||
* has the sole reference to buffer; in that case the buffer will be freed.
|
||||
* Otherwise -- memory leak! A more efficient design would recycle the
|
||||
* audio buffers.
|
||||
* In a real application, the received data would typically be returned
|
||||
* to the caller or passed to another subsystem via IPC or a queue.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Pointer to the I2S device structure
|
||||
* apb - Pointer to the audio buffer structure that was received
|
||||
* arg - Pointer to the private data structure (i2schar_dev_s)
|
||||
* result - The result of the transfer (OK on success, negated errno on
|
||||
* failure)
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -165,12 +177,25 @@ static void i2schar_rxcallback(FAR struct i2s_dev_s *dev,
|
|||
* Name: i2schar_txcallback
|
||||
*
|
||||
* Description:
|
||||
* I2S TX transfer complete callback
|
||||
* Callback function invoked upon completion of an I2S TX (transmit)
|
||||
* transfer. This function is called by the lower-half I2S driver when
|
||||
* the transmission of an audio buffer is complete.
|
||||
*
|
||||
* NOTE: The test buffer is simply freed. This will work if this driver
|
||||
* has the sole reference to buffer; in that case the buffer will be freed.
|
||||
* Otherwise -- memory leak! A more efficient design would recycle the
|
||||
* audio buffers.
|
||||
* In this test driver implementation, the transmitted buffer is simply
|
||||
* freed. This is acceptable if this driver has the sole reference to the
|
||||
* buffer; in that case, the buffer will be freed. Otherwise, this may
|
||||
* result in a memory leak. A more efficient design would recycle the
|
||||
* audio buffers for future use.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Pointer to the I2S device structure
|
||||
* apb - Pointer to the audio buffer structure that was transmitted
|
||||
* arg - Pointer to the private data structure (i2schar_dev_s)
|
||||
* result - The result of the transfer (OK on success, negated errno on
|
||||
* failure)
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -187,7 +212,7 @@ static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
|||
|
||||
/* 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 complete.
|
||||
* want to let some application know that the transfer has completed.
|
||||
*/
|
||||
|
||||
/* Release our reference to the audio buffer. Hopefully it will be freed
|
||||
|
|
@ -202,7 +227,26 @@ static void i2schar_txcallback(FAR struct i2s_dev_s *dev,
|
|||
* Name: i2schar_read
|
||||
*
|
||||
* Description:
|
||||
* Standard character driver read method
|
||||
* Standard character driver read method. This function reads audio data
|
||||
* from the I2S character device. The data is expected to be provided in
|
||||
* the form of a single, correctly sized audio buffer (struct ap_buffer_s
|
||||
* followed by audio data). The function adds a reference to the buffer,
|
||||
* passes it to the lower-half I2S driver for reception, and waits for the
|
||||
* transfer to complete via a callback. Upon completion, the function
|
||||
* returns the number of bytes read or a negated errno value on failure.
|
||||
*
|
||||
* Input Parameters:
|
||||
* filep - Pointer to the file structure instance
|
||||
* buffer - Pointer to the buffer where the received audio data will be
|
||||
* stored. This must point to a struct ap_buffer_s followed by
|
||||
* sufficient space for the audio data.
|
||||
* buflen - The length of the buffer in bytes. Must be at least the size of
|
||||
* struct ap_buffer_s plus the number of audio data bytes.
|
||||
*
|
||||
* Returned Value:
|
||||
* On success, returns the number of bytes read (including the
|
||||
* struct ap_buffer_s header and the audio data). On failure, returns
|
||||
* a negated errno value indicating the error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -274,7 +318,26 @@ errout_with_reference:
|
|||
* Name: i2schar_write
|
||||
*
|
||||
* Description:
|
||||
* Standard character driver write method
|
||||
* Standard character driver write method. This function writes audio data
|
||||
* to the I2S character device. The data must be provided in the form of a
|
||||
* single, correctly sized audio buffer (struct ap_buffer_s followed by
|
||||
* audio data). The function adds a reference to the buffer, sends it to
|
||||
* the lower-half I2S driver for transmission, and waits for the transfer
|
||||
* to complete via a callback. Upon completion, the function returns the
|
||||
* number of bytes written or a negated errno value on failure.
|
||||
*
|
||||
* Input Parameters:
|
||||
* filep - Pointer to the file structure instance
|
||||
* buffer - Pointer to the buffer containing the audio data to be written.
|
||||
* This must point to a struct ap_buffer_s followed by the audio
|
||||
* data.
|
||||
* buflen - The length of the buffer in bytes. Must be at least the size of
|
||||
* struct ap_buffer_s plus the number of audio data bytes.
|
||||
*
|
||||
* Returned Value:
|
||||
* On success, returns the number of bytes written (including the
|
||||
* struct ap_buffer_s header and the audio data). On failure, returns
|
||||
* a negated errno value indicating the error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -329,24 +392,56 @@ static ssize_t i2schar_write(FAR struct file *filep, FAR const char *buffer,
|
|||
goto errout_with_reference;
|
||||
}
|
||||
|
||||
/* Lie to the caller and tell them that all of the bytes have been
|
||||
* sent.
|
||||
*/
|
||||
/* Wait for the TX callback to signal completion */
|
||||
|
||||
nxmutex_unlock(&priv->lock);
|
||||
return sizeof(struct ap_buffer_s) + nbytes;
|
||||
ret = nxsem_wait(&priv->tx_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->tx_result;
|
||||
|
||||
/* If the operation was successful, return the number of bytes sent */
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
ret = sizeof(struct ap_buffer_s) + apb->nbytes;
|
||||
}
|
||||
|
||||
/* Release our reference to the audio buffer */
|
||||
|
||||
nxmutex_unlock(&priv->tx_lock);
|
||||
return ret;
|
||||
|
||||
errout_with_reference:
|
||||
apb_free(apb);
|
||||
nxmutex_unlock(&priv->lock);
|
||||
nxmutex_unlock(&priv->tx_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: i2char_ioctl
|
||||
* Name: i2schar_ioctl
|
||||
*
|
||||
* Description:
|
||||
* Perform I2S device ioctl if exists
|
||||
* Perform device-specific operations on the I2S character device via ioctl
|
||||
* commands. This function handles a set of standard I2S ioctl commands for
|
||||
* getting and setting data width, channel count, and sample rate for both
|
||||
* RX and TX directions. If the command is not recognized, it is forwarded
|
||||
* to the lower-half I2S driver's i2s_ioctl method if available.
|
||||
*
|
||||
* Input Parameters:
|
||||
* filep - Pointer to the file structure instance
|
||||
* cmd - The ioctl command to be performed
|
||||
* arg - The argument accompanying the ioctl command (may be a pointer or
|
||||
* a value, depending on the command)
|
||||
*
|
||||
* Returned Value:
|
||||
* Returns zero (OK) on success; a negated errno value on failure.
|
||||
* If the command is not supported, returns -ENOTTY.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -354,7 +449,7 @@ static int i2schar_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||
{
|
||||
FAR struct inode *inode;
|
||||
FAR struct i2schar_dev_s *priv;
|
||||
int ret = -ENOTTY;
|
||||
int ret = OK;
|
||||
|
||||
/* Get our private data structure */
|
||||
|
||||
|
|
@ -363,9 +458,92 @@ static int i2schar_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||
priv = inode->i_private;
|
||||
DEBUGASSERT(priv != NULL && priv->i2s && priv->i2s->ops);
|
||||
|
||||
if (priv->i2s->ops->i2s_ioctl)
|
||||
switch (cmd)
|
||||
{
|
||||
ret = priv->i2s->ops->i2s_ioctl(priv->i2s, cmd, arg);
|
||||
case I2SIOC_GRXDATAWIDTH:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_RXDATAWIDTH(priv->i2s, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case I2SIOC_GTXDATAWIDTH:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_TXDATAWIDTH(priv->i2s, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_GRXCHANNELS:
|
||||
{
|
||||
*(FAR int *)arg = I2S_RXCHANNELS(priv->i2s, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_GTXCHANNELS:
|
||||
{
|
||||
*(FAR int *)arg = I2S_TXCHANNELS(priv->i2s, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_GRXSAMPLERATE:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_RXSAMPLERATE(priv->i2s, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_GTXSAMPLERATE:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_TXSAMPLERATE(priv->i2s, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_SRXDATAWIDTH:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_RXDATAWIDTH(priv->i2s, arg);
|
||||
break;
|
||||
}
|
||||
|
||||
case I2SIOC_STXDATAWIDTH:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_TXDATAWIDTH(priv->i2s, arg);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_SRXCHANNELS:
|
||||
{
|
||||
*(FAR int *)arg = I2S_RXCHANNELS(priv->i2s, arg);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_STXCHANNELS:
|
||||
{
|
||||
*(FAR int *)arg = I2S_TXCHANNELS(priv->i2s, arg);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_SRXSAMPLERATE:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_RXSAMPLERATE(priv->i2s, arg);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2SIOC_STXSAMPLERATE:
|
||||
{
|
||||
*(FAR uint32_t *)arg = I2S_TXSAMPLERATE(priv->i2s, arg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (priv->i2s->ops->i2s_ioctl)
|
||||
{
|
||||
ret = priv->i2s->ops->i2s_ioctl(priv->i2s, cmd, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -41,8 +41,119 @@
|
|||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* IOCTL Commands ***********************************************************/
|
||||
|
||||
/* The I2S module uses a standard character driver framework with device
|
||||
* control interface for configuring I2S parameters such as data width,
|
||||
* channel count, and sample rate for both RX and TX directions. The I2S
|
||||
* ioctl commands are listed below:
|
||||
*
|
||||
* I2SIOC_GRXDATAWIDTH - Get RX Data Width
|
||||
* ioctl argument: Pointer to uint32_t to receive the current RX data
|
||||
* width in bits
|
||||
* Returned value: uint32_t - The current RX data width in bits (typically
|
||||
* 8, 16, or 32)
|
||||
* Error codes: -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_GTXDATAWIDTH - Get TX Data Width
|
||||
* ioctl argument: Pointer to uint32_t to receive the current TX data
|
||||
* width in bits
|
||||
* Returned value: uint32_t - The current TX data width in bits (typically
|
||||
* 8, 16, or 32)
|
||||
* Error codes: -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_GRXCHANNELS - Get RX Channel Count
|
||||
* ioctl argument: Pointer to int to receive the current RX channel count
|
||||
* Returned value: int - The current number of RX channels (typically 1
|
||||
* or 2)
|
||||
* Error codes: -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_GTXCHANNELS - Get TX Channel Count
|
||||
* ioctl argument: Pointer to int to receive the current TX channel count
|
||||
* Returned value: int - The current number of TX channels (typically 1
|
||||
* or 2)
|
||||
* Error codes: -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_GRXSAMPLERATE - Get RX Sample Rate
|
||||
* ioctl argument: Pointer to uint32_t to receive the current RX sample
|
||||
* rate
|
||||
* Returned value: uint32_t - The current RX sample rate in samples per
|
||||
* second (e.g., 44100)
|
||||
* Error codes: -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_GTXSAMPLERATE - Get TX Sample Rate
|
||||
* ioctl argument: Pointer to uint32_t to receive the current TX sample
|
||||
* rate
|
||||
* Returned value: uint32_t - The current TX sample rate in samples per
|
||||
* second (e.g., 44100)
|
||||
* Error codes: -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_SRXDATAWIDTH - Set RX Data Width
|
||||
* ioctl argument: uint32_t value specifying the desired RX data width
|
||||
* in bits
|
||||
* Returned value: uint32_t - The resulting bitrate (sample_rate *
|
||||
* data_width) or 0 on success
|
||||
* Error codes: -EINVAL if invalid data width, -ENOSYS if unsupported
|
||||
* width, -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_STXDATAWIDTH - Set TX Data Width
|
||||
* ioctl argument: uint32_t value specifying the desired TX data width
|
||||
* in bits
|
||||
* Returned value: uint32_t - The resulting bitrate (sample_rate *
|
||||
* data_width) or 0 on success
|
||||
* Error codes: -EINVAL if invalid data width, -ENOSYS if unsupported
|
||||
* width, -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_SRXCHANNELS - Set RX Channel Count
|
||||
* ioctl argument: int value specifying the desired RX channel count
|
||||
* Returned value: int - The number of RX channels set or a negated
|
||||
* errno value
|
||||
* Error codes: -EINVAL if invalid channel count (not 1 or 2),
|
||||
* -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_STXCHANNELS - Set TX Channel Count
|
||||
* ioctl argument: int value specifying the desired TX channel count
|
||||
* Returned value: int - The number of TX channels set or a negated
|
||||
* errno value
|
||||
* Error codes: -EINVAL if invalid channel count (not 1 or 2),
|
||||
* -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_SRXSAMPLERATE - Set RX Sample Rate
|
||||
* ioctl argument: uint32_t value specifying the desired RX sample rate
|
||||
* in Hz
|
||||
* Returned value: uint32_t - The resulting bitrate or 0 on success
|
||||
* Error codes: -EINVAL if invalid sample rate (typically < 8000 Hz),
|
||||
* -ENOTTY if not supported by driver
|
||||
*
|
||||
* I2SIOC_STXSAMPLERATE - Set TX Sample Rate
|
||||
* ioctl argument: uint32_t value specifying the desired TX sample rate
|
||||
* in Hz
|
||||
* Returned value: uint32_t - The resulting bitrate or 0 on success
|
||||
* Error codes: -EINVAL if invalid sample rate (typically < 8000 Hz),
|
||||
* -ENOTTY if not supported by driver
|
||||
*
|
||||
* Note: The bitrate is calculated as sample_rate * data_width. Some drivers
|
||||
* may have coupling between RX and TX parameters, so changing one may
|
||||
* affect the other. Sample rates are typically constrained to a minimum of
|
||||
* 8000 Hz, and data widths are typically limited to 8, 16, or 32 bits
|
||||
* depending on the hardware capabilities.
|
||||
*/
|
||||
|
||||
/* Access macros ************************************************************/
|
||||
|
||||
#define I2SIOC_GRXDATAWIDTH _I2SOC(1)
|
||||
#define I2SIOC_GTXDATAWIDTH _I2SOC(2)
|
||||
#define I2SIOC_GRXCHANNELS _I2SOC(3)
|
||||
#define I2SIOC_GTXCHANNELS _I2SOC(4)
|
||||
#define I2SIOC_GRXSAMPLERATE _I2SOC(5)
|
||||
#define I2SIOC_GTXSAMPLERATE _I2SOC(6)
|
||||
#define I2SIOC_SRXDATAWIDTH _I2SOC(7)
|
||||
#define I2SIOC_STXDATAWIDTH _I2SOC(8)
|
||||
#define I2SIOC_SRXCHANNELS _I2SOC(9)
|
||||
#define I2SIOC_STXCHANNELS _I2SOC(10)
|
||||
#define I2SIOC_SRXSAMPLERATE _I2SOC(11)
|
||||
#define I2SIOC_STXSAMPLERATE _I2SOC(12)
|
||||
|
||||
/****************************************************************************
|
||||
* Name: I2S_RXCHANNELS
|
||||
*
|
||||
|
|
@ -55,7 +166,7 @@
|
|||
* channel - The I2S channel num
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; a negated errno value on failure.
|
||||
* Returns the number of RX channels
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
@ -147,7 +258,7 @@
|
|||
* channel - The I2S channel num
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success; a negated errno value on failure.
|
||||
* Returns the number of TX channels
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@
|
|||
#define _PCIBASE (0x4100) /* Pci ioctl commands */
|
||||
#define _I3CBASE (0x4200) /* I3C driver ioctl commands */
|
||||
#define _MSIOCBASE (0x4300) /* Mouse ioctl commands */
|
||||
#define _I2SOCBASE (0x4400) /* I2S driver ioctl commands */
|
||||
#define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */
|
||||
|
||||
/* boardctl() commands share the same number space */
|
||||
|
|
@ -775,6 +776,13 @@
|
|||
#define _PINCTRLIOCVALID(c) (_IOC_TYPE(c)==_PINCTRLBASE)
|
||||
#define _PINCTRLIOC(nr) _IOC(_PINCTRLBASE,nr)
|
||||
|
||||
/* NuttX i2s driver ioctl definitions ***************************************/
|
||||
|
||||
/* (see nuttx/audio/i2s.h) */
|
||||
|
||||
#define _I2SOCVALID(c) (_IOC_TYPE(c)==_I2SOCBASE)
|
||||
#define _I2SOC(nr) _IOC(_I2SOCBASE,nr)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Type Definitions
|
||||
****************************************************************************/
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue