diff --git a/drivers/i2s/i2schar.c b/drivers/i2s/i2schar.c index 26e6324842..b2fd7c3b60 100644 --- a/drivers/i2s/i2schar.c +++ b/drivers/i2s/i2schar.c @@ -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; diff --git a/include/nuttx/audio/i2s.h b/include/nuttx/audio/i2s.h index 2c8a16f77a..618ec03bb5 100644 --- a/include/nuttx/audio/i2s.h +++ b/include/nuttx/audio/i2s.h @@ -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 * ****************************************************************************/ diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 469badb629..b87dc75c95 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -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 ****************************************************************************/