out endpoint buffer size vs in endpoint interrupt #2620
Replies: 8 comments 28 replies
-
No it varies between 188 and 196.
There is no requirement, for working correctly it needs to be 188 -196. You can read |
Beta Was this translation helpful? Give feedback.
-
Wireshark's capture is kind of confusing to read, 480/2 = 240bytes/ms is definitely not good.
Don't pad it, it'll translate to 0s in your audio. The variation of sample length is used to synchronize I2S and USB clock. What's your buffer sizes ? |
Beta Was this translation helpful? Give feedback.
-
Hmm, maybe I am missing a trick here, as currently my I2S and usb are completely disparate. Both rx and tx buffers are as below #define BUFFER_SIZE 9600
uint16_t rxAudio[BUFFER_SIZE];
uint16_t txAudio[BUFFER_SIZE]; My DMA is then enabled once the i2s master achieves lock to its fibre optic network (STM32 is running full duplex slave) HAL_I2SEx_TransmitReceive_DMA(&hi2s2, (uint16_t*)txAudio, (uint16_t*)rxAudio, BUFFER_SIZE); Then after I get my first void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s) {
if(connected == 0x01 && waitingSync == 0x01) {
waitingSync = 0x00;
printf("complete at %d packets = 0\n", (uint32_t)*((uint32_t*)0x40026074));
}
} bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
{
(void)rhport;
(void)func_id;
(void)ep_out;
(void)cur_alt_setting;
if(connected == 0x00) {
connected = 0x01;
waitingSync = 0x01;
}
if (packets == 9600) {
packets = 0;
} else if (packets > 9600) {
printf("out of range!\n");
packets = 0;
}
if(n_bytes_received != 192) {
printf("packets: %d\n", n_bytes_received);
}
if(lastWaitingSync == 0x01 && waitingSync == 0x00) {
packets = 9408;
printf("sync dma at %d packets = 9408\n", (uint8_t)*((uint32_t*)0x40026074));
}
if(waitingSync == 0x00) {
tud_audio_read(&txAudio[packets], n_bytes_received);
uint8_t data[196] = {0};
//tud_audio_write((uint16_t*)&txAudio[packets], n_bytes_received);
memcpy(&data, &txAudio[packets], n_bytes_received);
//uint32_t empty = 0;
tud_audio_write(&data, 196);
// tud_audio_write((uint16_t*)&txAudio[packets], 196);
packets += (n_bytes_received/2);
}
lastWaitingSync = waitingSync;
return true;
} This is my first voyage into C, so I am beginning to think that my approach to this is completely wrong based on your replies so far! I highly doubt that both my streams are super in sync, the I2S is clocked from a MOST fibre optic network, the USB is from a mac, the out of range print also never triggers so I suspect the entire approach I have taken is incorrect! I did read in the examples that using the callbacks in tinyusb is not really done in the real world, and normally the I2S interrupts drive the transfer, so perhaps I need to look into that and remove the I2S DMA from the equation. |
Beta Was this translation helpful? Give feedback.
-
@HiFiPhile I seem to be struggling quite a bit with this one! Apologies for the hassling, but I have moved over to the feedback example. It seems everything enumerates and plays, however the audio out is just a distorted blip every second or so. Looking at the capture I am pretty sure this is the cause, the 3x 7080 length transfers are all zeros, the one 3048 transfer has actual data in it, which I am guessing is the blip I receive. Does anything stand out as an obvious mistake here? I am currently just calling the |
Beta Was this translation helpful? Give feedback.
-
@Marcus2060 did a test but result is quite confusing, as sending 4 bytes while EP descriptor is 3 is out of spec :
In |
Beta Was this translation helpful? Give feedback.
-
@HiFiPhile I've gotten the full flow, microphone and speakers working, both sound bit perfect. Thankyou so much for the help. Incase anyone else stumbles upon this, some of the files are below, happy to submit this as a PR if you want for an example (or someone else copy it in, likely some parts may be slightly wrong, as a lot of trial and error to get it working, learnt a huge amount about descriptors though!) anyway a microphone/speaker/cdc setup below tusb_config.h /*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ha Thach (tinyusb.org)
* Copyright (c) 2020 Jerzy Kasenberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
//#define CFG_AUDIO_DEBUG 1
#ifdef __cplusplus
extern "C" {
#endif
#include "usb_descriptors.h"
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
#define CFG_TUSB_MCU OPT_MCU_STM32F4
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
#define BOARD_DEVICE_RHPORT_NUM 1
#define CFG_TUSB_RHPORT0_MODE 0
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 1
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
// Expose audio class debug information via HID interface
#ifndef CFG_AUDIO_DEBUG
#define CFG_AUDIO_DEBUG 0
#endif
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 0
#if CFG_AUDIO_DEBUG
#define CFG_TUD_HID 1
#else
#define CFG_TUD_HID 0
#endif
#define CFG_TUD_MIDI 0
#define CFG_TUD_AUDIO 1
#define CFG_TUD_VENDOR 0
//--------------------------------------------------------------------
// AUDIO CLASS DRIVER CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN
// Audio format type I specifications
#if defined(__RX__)
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
#else
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
#endif
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 2
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 1
// 16bit in 16bit slots
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2
#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_TX 16
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX 16
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
//#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 64) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
//#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 64) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
// Enable feedback EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1
// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2
// Size of control request buffer
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 64
#define CFG_TUD_CDC_TX_BUFSIZE 64
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */ usb_descriptors.h /*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jerzy Kasenbreg
* Copyright (c) 2022 Angel Molina ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _USB_DESCRIPTORS_H_
#define _USB_DESCRIPTORS_H_
// #include "tusb.h"
// Unit numbers are arbitrary selected
#define UAC2_ENTITY_CLOCK 0x04
#define UAC2_ENTITY_INPUT_TERMINAL 0x01
#define UAC2_ENTITY_FEATURE_UNIT 0x02
#define UAC2_ENTITY_OUTPUT_TERMINAL 0x03
#define UAC2_ENTITY_MIC_INPUT_TERMINAL 0x11
#define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x13
enum
{
ITF_NUM_AUDIO_CONTROL = 0,
ITF_NUM_AUDIO_STREAMING,
ITF_NUM_AUDIO_STREAMING_MIC,
ITF_NUM_CDC,
ITF_NUM_CDC_DATA,
ITF_NUM_TOTAL
};
//enum
//{
// ITF_NUM_AUDIO_CONTROL = 0,
// ITF_NUM_AUDIO_STREAMING_SPK,
// ITF_NUM_AUDIO_STREAMING_MIC,
// ITF_NUM_CDC,
// ITF_NUM_CDC_DATA,
// ITF_NUM_TOTAL
//};
#define TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
+ TUD_AUDIO_DESC_STD_AC_LEN\
+ TUD_AUDIO_DESC_CS_AC_LEN\
+ TUD_AUDIO_DESC_CLK_SRC_LEN\
+ TUD_AUDIO_DESC_INPUT_TERM_LEN\
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
+ TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN\
/* ADD */\
+ TUD_AUDIO_DESC_INPUT_TERM_LEN\
/* ADD */\
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
/* interface 1 */\
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
/* Interface 1, Alternate 0 */\
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ TUD_AUDIO_DESC_CS_AS_INT_LEN\
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN\
/* interface 2 */\
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
/* Interface 2, Alternate 1 */\
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ TUD_AUDIO_DESC_CS_AS_INT_LEN\
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
#define TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epin, _epsize, _epfb) \
/* Standard Interface Association Descriptor (IAD) */\
TUD_AUDIO_DESC_IAD(/*_firstitf*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ 3, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x00, /*_stridx*/ 2),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
/* Clock Source Descriptor(4.7.2.1) */\
TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
/* Input Terminal Descriptor(4.7.2.4) */\
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Output Terminal Descriptor(4.7.2.5) */\
TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ UAC2_ENTITY_FEATURE_UNIT, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS,/*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
/* Input Terminal Descriptor(4.7.2.4) */\
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Output Terminal Descriptor(4.7.2.5) */\
TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1),\
/* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 2, Alternate 1 - alternate interface for data streaming */\
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_TX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)\
#endif usb_descriptors.c /*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ha Thach (tinyusb.org)
* Copyright (c) 2020 Jerzy Kasenberg
* Copyright (c) 2022 Angel Molina ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
#include "usb_descriptors.h"
#include "common.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
// Use Interface Association Descriptor (IAD)
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *)&desc_device;
}
#if CFG_AUDIO_DEBUG
//--------------------------------------------------------------------+
// HID Report Descriptor
//--------------------------------------------------------------------+
uint8_t const desc_hid_report[] =
{
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
HID_USAGE ( 0x01 ),\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
HID_USAGE ( 0x02 ),\
HID_LOGICAL_MIN ( 0x00 ),\
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
HID_REPORT_SIZE ( 8 ),\
HID_REPORT_COUNT( sizeof(audio_debug_info_t) ),\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
HID_COLLECTION_END
};
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
{
(void) itf;
return desc_hid_report;
}
#endif
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
#define EPNUM_AUDIO_IN 0x03
#define EPNUM_AUDIO_OUT 0x03
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_CDC_NOTIF 0x84
#define EPNUM_CDC_OUT 0x05
#define EPNUM_CDC_IN 0x85
#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
// ISO endpoints for NRF5x are fixed to 0x08 (0x88)
#define EPNUM_AUDIO_IN 0x08
#define EPNUM_AUDIO_OUT 0x08
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X
// SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_AUDIO_OUT 0x02
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_CDC_NOTIF 0x83
#define EPNUM_CDC_OUT 0x04
#define EPNUM_CDC_IN 0x85
#elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X
// FT9XX doesn't support a same endpoint number with different direction IN and OUT
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_AUDIO_OUT 0x02
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_CDC_NOTIF 0x83
#define EPNUM_CDC_OUT 0x04
#define EPNUM_CDC_IN 0x85
#else
#define EPNUM_AUDIO_FB 0x02
#define EPNUM_AUDIO_OUT 0x01
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_DEBUG 0x02
#define EPNUM_CDC_NOTIF 0x83
#define EPNUM_CDC_OUT 0x04
#define EPNUM_CDC_IN 0x84
#endif
uint8_t const desc_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
//
// // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback
TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80),
//
//
// // CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
//
// #if CFG_AUDIO_DEBUG
// // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
// TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
// #endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// array of pointer to string descriptors
char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"ModernDayMods", // 1: Manufacturer
"PiMostUSB", // 2: Product
NULL, // 3: Serials will use unique ID if possible
"PiMost Speakers",
"pimost Microphone", // 4: Audio Interface
"PiMost CDC", // 6: Audio Interface
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
size_t chr_count;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = 55555;
break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
if ( chr_count > max_count ) chr_count = max_count;
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
} Added bonus for anyone looking to implement into an stm32 i2s DMA main.c #define BUFFER_SIZE 3072
uint16_t rxAudio[BUFFER_SIZE];
uint16_t txAudio[BUFFER_SIZE];
void HAL_I2SEx_TxRxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {
tud_audio_read(&txAudio[0], 3072);
tud_audio_write(&rxAudio[0], 3072);
}
void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s) {
tud_audio_read(&txAudio[1536], 3072);
tud_audio_write(&rxAudio[1536], 3072);
} |
Beta Was this translation helpful? Give feedback.
-
I made a Hackintosh Ventura VM on my server to see what happens. I can confirm 4 bytes works well, but in my case 3 bytes also 🤔: |
Beta Was this translation helpful? Give feedback.
-
Hi All, |
Beta Was this translation helpful? Give feedback.
-
Hi All,
I have a UAC application running on an stm32f427. I have the MCU running as an i2s full duplex slave, that also appears as a soundcard over usb.
I have the device descriptor set up to appear as 48khz, 16 bit 2 channel in and out. This seems to work well for i2s transmission, so audio playing out of the host, into the device. I get 192 bytes everytime that
tud_audio_rx_done_pre_read_cb
gets called, I've managed to sync with the i2s DMA, no issues.The problem comes when the device is acting as a mic (i2s to host). If in the same function I try and feed 192 bytes to
tud_audio_write
I never get more than 96 bytes transferred to the host per isochronous transfer, looking into the capture, isochronous data is always missing. If I set it to feed 196 bytes (which is whats calculated as the endpoint buffer in the boiler plate code) then I do get audio transferring, but obviously with a bit of distortion due to the extra 4 bytes.Am I missing something as to why
n_bytes_received
is always 192, yet thetud_audio_write
always seems to require 196 bytes to actually transmit data. having the 4 bytes difference adds a whole layer of complexity (as far as I can see) to keep the in and out streams synced to the i2s DMA.Thanks in advance
Rhys
Beta Was this translation helpful? Give feedback.
All reactions