initial commit
This commit is contained in:
commit
b44ce7bd77
2716 files changed
+559005
No files matched your search
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
|
||||
|
||||
project(uac2_headset C CXX ASM)
|
||||
|
||||
# Checks this example is valid for the family and initializes the project
|
||||
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Espressif has its own cmake build system
|
||||
if(FAMILY STREQUAL "espressif")
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_executable(${PROJECT_NAME})
|
||||
|
||||
# Example source
|
||||
target_sources(${PROJECT_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
|
||||
)
|
||||
|
||||
# Example include
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
# Configure compilation flags and libraries for the example without RTOS.
|
||||
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
|
||||
family_configure_device_example(${PROJECT_NAME} noos)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 6,
|
||||
"include": [
|
||||
"../../../hw/bsp/BoardPresets.json"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
include ../../../hw/bsp/family_support.mk
|
||||
|
||||
INC += \
|
||||
src \
|
||||
|
||||
|
||||
# Example source
|
||||
EXAMPLE_SOURCE += $(wildcard src/*.c)
|
||||
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
|
||||
|
||||
include ../../../hw/bsp/family_rules.mk
|
||||
@@ -0,0 +1,59 @@
|
||||
# USB Audio Headset
|
||||
|
||||
A USB audio headset that combines a stereo speaker output and a mono microphone input, adapting its descriptors to the negotiated bus speed — UAC1 at Full-Speed and UAC2 at High-Speed.
|
||||
|
||||
## What it does
|
||||
|
||||
- Enumerates as a headset with a stereo speaker (host → device) and a mono microphone (device → host). The configuration returned depends on link speed: a UAC1 configuration at Full-Speed and a UAC2 configuration at High-Speed, with a device-qualifier and other-speed-configuration descriptor so it works at either speed.
|
||||
- Loops audio back: the audio task reads the speaker stream, mixes the two channels down to one, and writes it to the microphone IN endpoint, so whatever is played to the headset is heard back on its microphone.
|
||||
- Supports 44.1 kHz and 48 kHz, with a 16-bit format and (UAC2) a 24-bit-in-32-bit format selected by the streaming interface's alternate setting.
|
||||
- Handles UAC1 and UAC2 control requests (mute, volume, sample frequency) dispatched on the active audio version.
|
||||
- Reads the on-board button to toggle the speaker volume (0 dB / -30 dB) and notifies the host through the audio interrupt endpoint.
|
||||
- Blinks the on-board LED to indicate USB state, with a fast pattern while the speaker is streaming.
|
||||
|
||||
## USB Descriptors
|
||||
|
||||
| Interface | Class driver |
|
||||
|-----------|--------------|
|
||||
| 0 | Audio control |
|
||||
| 1 | Audio streaming — speaker output (OUT) |
|
||||
| 2 | Audio streaming — microphone input (IN) |
|
||||
|
||||
(UAC1 at Full-Speed, UAC2 at High-Speed.)
|
||||
|
||||
## Configuration
|
||||
|
||||
Notable `tusb_config.h` settings:
|
||||
|
||||
```c
|
||||
#define CFG_TUD_AUDIO 1
|
||||
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 1
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1 // microphone
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 // speaker
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2
|
||||
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // mic (mono)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2 // speaker (stereo)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX 16 // 16-bit
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 24 // 24-bit in 32-bit slots (UAC2)
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
CMake:
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake -DBOARD=raspberry_pi_pico ..
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
Make:
|
||||
|
||||
```bash
|
||||
make BOARD=raspberry_pi_pico all
|
||||
```
|
||||
|
||||
## Try it
|
||||
|
||||
The device appears as both a USB playback (speaker) and capture (microphone) device. Play audio to the speaker (e.g. `aplay -D ...` or select it as the system output) and record from the microphone with `arecord` — you should hear the played audio looped back. Press the on-board button to toggle the volume between full scale and -30 dB and watch the host volume control follow.
|
||||
@@ -0,0 +1,9 @@
|
||||
mcu:LPC11UXX
|
||||
mcu:LPC13XX
|
||||
mcu:NUC121
|
||||
mcu:SAMD11
|
||||
mcu:SAME5X
|
||||
mcu:SAMG
|
||||
board:stm32l052dap52
|
||||
family:espressif
|
||||
mcu:CH583
|
||||
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTOTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// List of supported sample rates
|
||||
const uint32_t sample_rates[] = {44100, 48000};
|
||||
|
||||
uint32_t current_sample_rate = 44100;
|
||||
|
||||
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
|
||||
|
||||
/* Blink pattern
|
||||
* - 25 ms : streaming data
|
||||
* - 250 ms : device not mounted
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum {
|
||||
BLINK_STREAMING = 25,
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
};
|
||||
|
||||
enum {
|
||||
VOLUME_CTRL_0_DB = 0,
|
||||
VOLUME_CTRL_10_DB = 2560,
|
||||
VOLUME_CTRL_20_DB = 5120,
|
||||
VOLUME_CTRL_30_DB = 7680,
|
||||
VOLUME_CTRL_40_DB = 10240,
|
||||
VOLUME_CTRL_50_DB = 12800,
|
||||
VOLUME_CTRL_60_DB = 15360,
|
||||
VOLUME_CTRL_70_DB = 17920,
|
||||
VOLUME_CTRL_80_DB = 20480,
|
||||
VOLUME_CTRL_90_DB = 23040,
|
||||
VOLUME_CTRL_100_DB = 25600,
|
||||
VOLUME_CTRL_SILENCE = 0x8000,
|
||||
};
|
||||
|
||||
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
|
||||
// Audio controls
|
||||
// Current states
|
||||
uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
|
||||
|
||||
// Buffer for microphone data
|
||||
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
|
||||
// Buffer for speaker data
|
||||
int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4];
|
||||
// Speaker data size received in the last frame
|
||||
int spk_data_size;
|
||||
// Resolution per format
|
||||
const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX,
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX};
|
||||
// Current resolution, update on format change
|
||||
uint8_t current_resolution;
|
||||
|
||||
void led_blinking_task(void);
|
||||
void audio_task(void);
|
||||
void audio_control_task(void);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void) {
|
||||
board_init();
|
||||
|
||||
// init device stack on configured roothub port
|
||||
tusb_rhport_init_t dev_init = {
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
board_init_after_tusb();
|
||||
|
||||
TU_LOG1("Headset running\r\n");
|
||||
|
||||
while (1) {
|
||||
tud_task();// TinyUSB device task
|
||||
audio_task();
|
||||
audio_control_task();
|
||||
led_blinking_task();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void) {
|
||||
blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// remote_wakeup_en : if host allow us to perform remote wakeup
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
void tud_suspend_cb(bool remote_wakeup_en) {
|
||||
(void) remote_wakeup_en;
|
||||
blink_interval_ms = BLINK_SUSPENDED;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Audio Callback Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC1 Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static bool audio10_set_req_ep(tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_EP_CTRL_SAMPLING_FREQ:
|
||||
if (p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
|
||||
// Request uses 3 bytes
|
||||
TU_VERIFY(p_request->wLength == 3);
|
||||
|
||||
current_sample_rate = tu_unaligned_read32(pBuff) & 0x00FFFFFF;
|
||||
|
||||
TU_LOG2("EP set current freq: %" PRIu32 "\r\n", current_sample_rate);
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio10_get_req_ep(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_EP_CTRL_SAMPLING_FREQ:
|
||||
if (p_request->bRequest == AUDIO10_CS_REQ_GET_CUR) {
|
||||
TU_LOG2("EP get current freq\r\n");
|
||||
|
||||
uint8_t freq[3];
|
||||
freq[0] = (uint8_t) (current_sample_rate & 0xFF);
|
||||
freq[1] = (uint8_t) ((current_sample_rate >> 8) & 0xFF);
|
||||
freq[2] = (uint8_t) ((current_sample_rate >> 16) & 0xFF);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, freq, sizeof(freq));
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// If request is for our speaker feature unit
|
||||
if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_FU_CTRL_MUTE:
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO10_CS_REQ_SET_CUR:
|
||||
// Only 1st form is supported
|
||||
TU_VERIFY(p_request->wLength == 1);
|
||||
|
||||
mute[channelNum] = pBuff[0];
|
||||
|
||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false; // not supported
|
||||
}
|
||||
|
||||
case AUDIO10_FU_CTRL_VOLUME:
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO10_CS_REQ_SET_CUR:
|
||||
// Only 1st form is supported
|
||||
TU_VERIFY(p_request->wLength == 2);
|
||||
|
||||
volume[channelNum] = (int16_t)tu_unaligned_read16(pBuff) / 256;
|
||||
|
||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false; // not supported
|
||||
}
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
// If request is for our speaker feature unit
|
||||
if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
|
||||
switch (ctrlSel) {
|
||||
case AUDIO10_FU_CTRL_MUTE:
|
||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||
// There does not exist a range parameter block for mute
|
||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
||||
|
||||
case AUDIO10_FU_CTRL_VOLUME:
|
||||
switch (p_request->bRequest) {
|
||||
case AUDIO10_CS_REQ_GET_CUR:
|
||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t vol = (int16_t) volume[channelNum];
|
||||
vol = vol * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
|
||||
}
|
||||
|
||||
case AUDIO10_CS_REQ_GET_MIN:
|
||||
TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t min = -90; // -90 dB
|
||||
min = min * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
|
||||
}
|
||||
|
||||
case AUDIO10_CS_REQ_GET_MAX:
|
||||
TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t max = 30; // +30 dB
|
||||
max = max * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
|
||||
}
|
||||
|
||||
case AUDIO10_CS_REQ_GET_RES:
|
||||
TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum);
|
||||
{
|
||||
int16_t res = 1; // 1 dB
|
||||
res = res * 256; // convert to 1/256 dB units
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
|
||||
}
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC2 Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Helper for clock get requests
|
||||
static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
|
||||
|
||||
if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
|
||||
if (p_request->bRequest == AUDIO20_CS_REQ_CUR) {
|
||||
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
|
||||
|
||||
audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf));
|
||||
} else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) {
|
||||
audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef =
|
||||
{
|
||||
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
|
||||
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
|
||||
for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) {
|
||||
rangef.subrange[i].bMin = (int32_t) sample_rates[i];
|
||||
rangef.subrange[i].bMax = (int32_t) sample_rates[i];
|
||||
rangef.subrange[i].bRes = 0;
|
||||
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
|
||||
}
|
||||
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
|
||||
}
|
||||
} else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID &&
|
||||
p_request->bRequest == AUDIO20_CS_REQ_CUR) {
|
||||
audio20_control_cur_1_t cur_valid = {.bCur = 1};
|
||||
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid));
|
||||
}
|
||||
TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n",
|
||||
ctrl_sel, p_request->bRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper for clock set requests
|
||||
static bool audio20_clock_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
|
||||
|
||||
TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
|
||||
|
||||
if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
|
||||
|
||||
current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n",
|
||||
ctrl_sel, p_request->bRequest);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for feature unit get requests
|
||||
static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
|
||||
|
||||
if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
|
||||
audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]};
|
||||
TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1));
|
||||
} else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) {
|
||||
if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) {
|
||||
audio20_control_range_2_n_t(1) range_vol = {
|
||||
.wNumSubRanges = tu_htole16(1),
|
||||
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
|
||||
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num,
|
||||
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol));
|
||||
} else if (p_request->bRequest == AUDIO20_CS_REQ_CUR) {
|
||||
audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[channel_num])};
|
||||
TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256);
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol));
|
||||
}
|
||||
}
|
||||
TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n",
|
||||
ctrl_sel, p_request->bRequest);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper for feature unit set requests
|
||||
static bool audio20_feature_unit_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) {
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
|
||||
uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
|
||||
|
||||
TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
|
||||
|
||||
if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) {
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
|
||||
|
||||
mute[channel_num] = ((audio20_control_cur_1_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]);
|
||||
|
||||
return true;
|
||||
} else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) {
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
|
||||
|
||||
volume[channel_num] = ((audio20_control_cur_2_t const *) buf)->bCur;
|
||||
|
||||
TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n",
|
||||
ctrl_sel, p_request->bRequest);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
if (entity_id == UAC2_ENTITY_CLOCK)
|
||||
return audio20_clock_get_request(rhport, p_request);
|
||||
if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return audio20_feature_unit_get_request(rhport, p_request);
|
||||
else {
|
||||
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio20_set_req_entity(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
|
||||
|
||||
if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT)
|
||||
return audio20_feature_unit_set_request(rhport, p_request, buf);
|
||||
if (entity_id == UAC2_ENTITY_CLOCK)
|
||||
return audio20_clock_set_request(rhport, p_request, buf);
|
||||
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
|
||||
entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Invoked when audio class specific set request received for an EP
|
||||
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
|
||||
(void) rhport;
|
||||
(void) pBuff;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_set_req_ep(p_request, pBuff);
|
||||
} else if (tud_audio_version() == 2) {
|
||||
// We do not support any requests here
|
||||
}
|
||||
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
// Invoked when audio class specific get request received for an EP
|
||||
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_get_req_ep(rhport, p_request);
|
||||
} else if (tud_audio_version() == 2) {
|
||||
// We do not support any requests here
|
||||
}
|
||||
|
||||
return false;// Yet not implemented
|
||||
}
|
||||
|
||||
// Invoked when audio class specific get request received for an entity
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_get_req_entity(rhport, p_request);
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
} else if (tud_audio_version() == 2) {
|
||||
return audio20_get_req_entity(rhport, p_request);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invoked when audio class specific set request received for an entity
|
||||
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
|
||||
(void) rhport;
|
||||
|
||||
if (tud_audio_version() == 1) {
|
||||
return audio10_set_req_entity(p_request, buf);
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
} else if (tud_audio_version() == 2) {
|
||||
return audio20_set_req_entity(rhport, p_request, buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const itf = tu_u16_low(p_request->wIndex);
|
||||
uint8_t const alt = tu_u16_low(p_request->wValue);
|
||||
|
||||
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
|
||||
(void) rhport;
|
||||
uint8_t const itf = tu_u16_low(p_request->wIndex);
|
||||
uint8_t const alt = tu_u16_low(p_request->wValue);
|
||||
|
||||
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
|
||||
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
|
||||
blink_interval_ms = BLINK_STREAMING;
|
||||
}
|
||||
|
||||
// Clear buffer when streaming format is changed
|
||||
spk_data_size = 0;
|
||||
if (alt != 0) {
|
||||
current_resolution = resolutions_per_format[alt - 1];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// AUDIO Task
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// This task simulates an audio transfer callback, one frame is sent/received every 1ms.
|
||||
// In a real application, this would be replaced with actual I2S send/receive callback.
|
||||
void audio_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
uint32_t curr_ms = tusb_time_millis_api();
|
||||
if (start_ms == curr_ms) return;// not enough time
|
||||
start_ms = curr_ms;
|
||||
// When new data arrived, copy data from speaker buffer, to microphone buffer
|
||||
// and send it over
|
||||
// Only support speaker & headphone both have the same resolution
|
||||
// If one is 16bit another is 24bit be care of LOUD noise !
|
||||
spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf));
|
||||
if (spk_data_size) {
|
||||
if (current_resolution == 16) {
|
||||
int16_t *src = (int16_t *) spk_buf;
|
||||
int16_t *limit = (int16_t *) spk_buf + spk_data_size / 2;
|
||||
int16_t *dst = (int16_t *) mic_buf;
|
||||
while (src < limit) {
|
||||
// Combine two channels into one
|
||||
int32_t left = *src++;
|
||||
int32_t right = *src++;
|
||||
*dst++ = (int16_t) ((left >> 1) + (right >> 1));
|
||||
}
|
||||
tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
|
||||
spk_data_size = 0;
|
||||
} else if (current_resolution == 24) {
|
||||
int32_t *src = spk_buf;
|
||||
int32_t *limit = spk_buf + spk_data_size / 4;
|
||||
int32_t *dst = mic_buf;
|
||||
while (src < limit) {
|
||||
// Combine two channels into one
|
||||
int32_t left = *src++;
|
||||
int32_t right = *src++;
|
||||
*dst++ = (int32_t) ((uint32_t) ((left >> 1) + (right >> 1)) & 0xffffff00ul);
|
||||
}
|
||||
tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
|
||||
spk_data_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_control_task(void) {
|
||||
// Press on-board button to control volume
|
||||
// Open host volume control, volume should switch between 10% and 100%
|
||||
|
||||
// Poll every 50ms
|
||||
const uint32_t interval_ms = 50;
|
||||
static uint32_t start_ms = 0;
|
||||
static uint32_t btn_prev = 0;
|
||||
|
||||
if (tusb_time_millis_api() - start_ms < interval_ms) return;// not enough time
|
||||
start_ms += interval_ms;
|
||||
|
||||
uint32_t btn = board_button_read();
|
||||
|
||||
// Even UAC1 spec have status interrupt support like UAC2, most host do not support it
|
||||
// So you have to either use UAC2 or use old day HID volume control
|
||||
TU_VERIFY((tud_audio_version() == 1),);
|
||||
|
||||
if (!btn_prev && btn) {
|
||||
// Adjust volume between 0dB (100%) and -30dB (10%)
|
||||
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
|
||||
volume[i] = volume[i] == 0 ? -VOLUME_CTRL_30_DB : 0;
|
||||
}
|
||||
|
||||
// 6.1 Interrupt Data Message
|
||||
const audio_interrupt_data_t data = {.v2 = {
|
||||
.bInfo = 0, // Class-specific interrupt, originated from an interface
|
||||
.bAttribute = AUDIO20_CS_REQ_CUR, // Caused by current settings
|
||||
.wValue_cn_or_mcn = 0, // CH0: master volume
|
||||
.wValue_cs = AUDIO20_FU_CTRL_VOLUME, // Volume change
|
||||
.wIndex_ep_or_int = 0, // From the interface itself
|
||||
.wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT,// From feature unit
|
||||
}};
|
||||
|
||||
tud_audio_int_write(&data);
|
||||
}
|
||||
|
||||
btn_prev = btn;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BLINKING TASK
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if (tusb_time_millis_api() - start_ms < blink_interval_ms) return;
|
||||
start_ms += blink_interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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_
|
||||
|
||||
#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
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// 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 0
|
||||
#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
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 0
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_AUDIO 1
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// AUDIO CLASS DRIVER CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Allow volume controlled by on-baord button
|
||||
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 1
|
||||
|
||||
// How many formats are used, need to adjust USB descriptor if changed
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2
|
||||
|
||||
// Audio format type I specifications
|
||||
/* 24bit/48kHz is the best quality for headset or 24bit/96kHz for 2ch speaker,
|
||||
high-speed is needed beyond this */
|
||||
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
|
||||
|
||||
// 16bit in 16bit slots
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX 2
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX 16
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX 2
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX 16
|
||||
|
||||
// 24bit in 32bit slots (UAC2 only)
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 4
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 24
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX 4
|
||||
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX 24
|
||||
|
||||
// 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_IN 1
|
||||
|
||||
// UAC1 (Full-Speed) Endpoint size calculation
|
||||
#define CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(false, 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)
|
||||
|
||||
// UAC2 (High-Speed) Endpoint size calculation
|
||||
#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(true, 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_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
|
||||
|
||||
// Maximum EP IN size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, TU_MAX(CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN))
|
||||
|
||||
// Tx flow control needs buffer size >= 4* EP size to work correctly
|
||||
// Example write FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, TU_MAX(32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, 32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN))
|
||||
|
||||
// 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
|
||||
|
||||
// UAC1 (Full-Speed) Endpoint size calculation
|
||||
#define CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(false, 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)
|
||||
|
||||
// UAC2 (High-Speed) Endpoint size calculation
|
||||
#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(true, 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_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||
|
||||
// Maximum EP OUT size for all AS alternate settings used
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT, TU_MAX(CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_OUT))
|
||||
|
||||
// Rx flow control needs buffer size >= 4* EP size to work correctly
|
||||
// Example read FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
|
||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT, TU_MAX(32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_OUT, 32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_OUT))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TUSB_CONFIG_H_ */
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.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) ? (1 << (n)) : 0)
|
||||
#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
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for Audio
|
||||
// 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;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_HEADSET_STEREO_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_INT 0x01
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_CXD56
|
||||
// CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
|
||||
// 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
|
||||
#define EPNUM_AUDIO_IN 0x01
|
||||
#define EPNUM_AUDIO_OUT 0x02
|
||||
#define EPNUM_AUDIO_INT 0x03
|
||||
|
||||
#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_INT 0x01
|
||||
|
||||
#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
|
||||
// e.g EP1 OUT & EP1 IN cannot exist together
|
||||
#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
|
||||
// Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
|
||||
#define EPNUM_AUDIO_OUT 0x0A
|
||||
#define EPNUM_AUDIO_IN 0x0B
|
||||
#define EPNUM_AUDIO_INT 0x01
|
||||
#else
|
||||
#define EPNUM_AUDIO_IN 0x01
|
||||
#define EPNUM_AUDIO_OUT 0x02
|
||||
#define EPNUM_AUDIO_INT 0x03
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define EPNUM_AUDIO_IN 0x01
|
||||
#define EPNUM_AUDIO_OUT 0x01
|
||||
#define EPNUM_AUDIO_INT 0x02
|
||||
#endif
|
||||
|
||||
#define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_HEADSET_STEREO_DESC_LEN(2))
|
||||
|
||||
uint8_t const desc_uac1_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC1_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, bytes per sample RX/TX, bits used per sample RX/TX, EP Out & EP In address, EP sizes, sample rate
|
||||
TUD_AUDIO10_HEADSET_STEREO_DESCRIPTOR(ITF_NUM_AUDIO_CONTROL, 4, \
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX, \
|
||||
CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX, \
|
||||
EPNUM_AUDIO_OUT, CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT, \
|
||||
EPNUM_AUDIO_IN | 0x80, CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, \
|
||||
44100, 48000)
|
||||
};
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(desc_uac1_configuration) == CONFIG_UAC1_TOTAL_LEN, "Incorrect size");
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
|
||||
#define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_HEADSET_STEREO_DESC_LEN)
|
||||
|
||||
uint8_t const desc_uac2_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC2_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// String index, EP Out & EP In address, EP Interrupt address
|
||||
TUD_AUDIO20_HEADSET_STEREO_DESCRIPTOR(5, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80, EPNUM_AUDIO_INT | 0x80)
|
||||
};
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(desc_uac2_configuration) == CONFIG_UAC2_TOTAL_LEN, "Incorrect size");
|
||||
|
||||
|
||||
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0x00
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const *tud_descriptor_device_qualifier_cb(void)
|
||||
{
|
||||
return (uint8_t const *) &desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_uac1_configuration : desc_uac2_configuration;
|
||||
}
|
||||
#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
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
if(tud_speed_get() == TUSB_SPEED_FULL) {
|
||||
return desc_uac1_configuration;
|
||||
} else {
|
||||
return desc_uac2_configuration;
|
||||
}
|
||||
#else
|
||||
return desc_uac1_configuration;
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// String Descriptor Index
|
||||
enum {
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
static char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Headset", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"TinyUSB UAC1 Headset", // 4: Function
|
||||
"TinyUSB UAC2 Headset", // 5: Function
|
||||
};
|
||||
|
||||
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 = board_usb_get_serial(_desc_str + 1, 32);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jerzy Kasenbreg
|
||||
*
|
||||
* 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_
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_AUDIO_CONTROL = 0,
|
||||
ITF_NUM_AUDIO_STREAMING_SPK,
|
||||
ITF_NUM_AUDIO_STREAMING_MIC,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC2 DESCRIPTOR TEMPLATES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Unit numbers are arbitrary selected
|
||||
#define UAC2_ENTITY_CLOCK 0x04
|
||||
// Speaker path
|
||||
#define UAC2_ENTITY_SPK_INPUT_TERMINAL 0x01
|
||||
#define UAC2_ENTITY_SPK_FEATURE_UNIT 0x02
|
||||
#define UAC2_ENTITY_SPK_OUTPUT_TERMINAL 0x03
|
||||
// Microphone path
|
||||
#define UAC2_ENTITY_MIC_INPUT_TERMINAL 0x11
|
||||
#define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x13
|
||||
|
||||
#define TUD_AUDIO20_HEADSET_STEREO_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AC_LEN\
|
||||
+ TUD_AUDIO20_DESC_CLK_SRC_LEN\
|
||||
+ TUD_AUDIO20_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)\
|
||||
+ TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO20_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AC_INT_EP_LEN\
|
||||
/* Interface 1, Alternate 0 */\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
/* Interface 1, Alternate 1 */\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
|
||||
/* Interface 1, Alternate 2 */\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
|
||||
/* Interface 2, Alternate 0 */\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
/* Interface 2, Alternate 1 */\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
|
||||
/* Interface 2, Alternate 2 */\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN)
|
||||
|
||||
#define TUD_AUDIO20_HEADSET_STEREO_DESCRIPTOR(_stridx, _epout, _epin, _epint) \
|
||||
/* Standard Interface Association Descriptor (IAD) */\
|
||||
TUD_AUDIO20_DESC_IAD(/*_firstitf*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ ITF_NUM_TOTAL, /*_stridx*/ 0x00),\
|
||||
/* Standard AC Interface Descriptor(4.7.1) */\
|
||||
TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
|
||||
/* Clock Source Descriptor(4.7.2.1) */\
|
||||
TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \
|
||||
/* Input Terminal Descriptor(4.7.2.4) */\
|
||||
TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
|
||||
/* Feature Unit Descriptor(4.7.2.8) */\
|
||||
TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS)),\
|
||||
/* Output Terminal Descriptor(4.7.2.5) */\
|
||||
TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
|
||||
/* Input Terminal Descriptor(4.7.2.4) */\
|
||||
TUD_AUDIO20_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*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
|
||||
/* Output Terminal Descriptor(4.7.2.5) */\
|
||||
TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
|
||||
/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */\
|
||||
TUD_AUDIO20_DESC_STD_AC_INT_EP(/*_ep*/ _epint, /*_interval*/ 0x01), \
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
|
||||
TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
|
||||
TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, 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), /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
|
||||
/* Interface 1, Alternate 2 - alternate interface for data streaming */\
|
||||
TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\
|
||||
TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 2, Alternate 1 - alternate interface for data streaming */\
|
||||
TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, 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), /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
|
||||
/* Interface 2, Alternate 2 - alternate interface for data streaming */\
|
||||
TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_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_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// UAC1 DESCRIPTOR TEMPLATES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// UAC1 entity IDs for speaker and microphone
|
||||
// Speaker path
|
||||
#define UAC1_ENTITY_SPK_INPUT_TERMINAL 0x01
|
||||
#define UAC1_ENTITY_SPK_FEATURE_UNIT 0x02
|
||||
#define UAC1_ENTITY_SPK_OUTPUT_TERMINAL 0x03
|
||||
// Microphone path
|
||||
#define UAC1_ENTITY_MIC_INPUT_TERMINAL 0x11
|
||||
#define UAC1_ENTITY_MIC_OUTPUT_TERMINAL 0x13
|
||||
|
||||
#define TUD_AUDIO10_HEADSET_STEREO_DESC_LEN(_nfreqs) (\
|
||||
+TUD_AUDIO10_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AC_LEN(2)\
|
||||
+ TUD_AUDIO10_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)\
|
||||
+ TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO10_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
|
||||
/* Interface 1, Alternate 0 (speaker) */\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_LEN\
|
||||
/* Interface 1, Alternate 1 (speaker) */\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN\
|
||||
/* Interface 2, Alternate 0 (microphone) */\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_LEN\
|
||||
/* Interface 2, Alternate 1 (microphone) */\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
|
||||
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN)
|
||||
|
||||
|
||||
#define TUD_AUDIO10_HEADSET_STEREO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample_RX, _nBitsUsedPerSample_RX, _nBytesPerSample_TX, _nBitsUsedPerSample_TX, _epout, _epoutsize, _epin, _epinsize, ...) \
|
||||
/* Standard AC Interface Descriptor(4.3.1) */\
|
||||
TUD_AUDIO10_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AC Interface Header Descriptor(4.3.2) */\
|
||||
TUD_AUDIO10_DESC_CS_AC(/*_bcdADC*/ 0x0100, /*_totallen*/ (TUD_AUDIO10_DESC_INPUT_TERM_LEN+TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)+TUD_AUDIO10_DESC_OUTPUT_TERM_LEN+TUD_AUDIO10_DESC_INPUT_TERM_LEN+TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(1)+TUD_AUDIO10_DESC_OUTPUT_TERM_LEN), /*_itf*/ ((_itfnum)+1), ((_itfnum)+2)),\
|
||||
/* Speaker Input Terminal Descriptor(4.3.2.1) */\
|
||||
TUD_AUDIO10_DESC_INPUT_TERM(/*_termid*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC1_ENTITY_MIC_OUTPUT_TERMINAL, /*_nchannels*/ 0x02, /*_channelcfg*/ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_stridx*/ 0x00),\
|
||||
/* Speaker Feature Unit Descriptor(4.3.2.5) */\
|
||||
TUD_AUDIO10_DESC_FEATURE_UNIT(/*_unitid*/ UAC1_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlmaster*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch1*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch2*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME)),\
|
||||
/* Speaker Output Terminal Descriptor(4.3.2.2) */\
|
||||
TUD_AUDIO10_DESC_OUTPUT_TERM(/*_termid*/ UAC1_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC1_ENTITY_SPK_FEATURE_UNIT, /*_stridx*/ 0x00),\
|
||||
/* Microphone Input Terminal Descriptor(4.3.2.1) */\
|
||||
TUD_AUDIO10_DESC_INPUT_TERM(/*_termid*/ UAC1_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_nchannels*/ 0x01, /*_channelcfg*/ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_stridx*/ 0x00),\
|
||||
/* Microphone Output Terminal Descriptor(4.3.2.2) */\
|
||||
TUD_AUDIO10_DESC_OUTPUT_TERM(/*_termid*/ UAC1_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_srcid*/ UAC1_ENTITY_MIC_INPUT_TERMINAL, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.5.1) - Speaker Interface 1, Alternate 0 */\
|
||||
TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Standard AS Interface Descriptor(4.5.1) - Speaker Interface 1, Alternate 1 */\
|
||||
TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AS Interface Descriptor(4.5.2) */\
|
||||
TUD_AUDIO10_DESC_CS_AS_INT(/*_termid*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_delay*/ 0x01, /*_formattype*/ AUDIO10_DATA_FORMAT_TYPE_I_PCM),\
|
||||
/* Type I Format Type Descriptor(2.2.5) */\
|
||||
TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x02, /*_subframesize*/ _nBytesPerSample_RX, /*_bitresolution*/ _nBitsUsedPerSample_RX, /*_freqs*/ __VA_ARGS__),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
|
||||
TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01, /*_syncep*/ 0x00),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
|
||||
TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
|
||||
/* Standard AS Interface Descriptor(4.5.1) - Microphone Interface 2, Alternate 0 */\
|
||||
TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+2), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Standard AS Interface Descriptor(4.5.1) - Microphone Interface 2, Alternate 1 */\
|
||||
TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+2), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AS Interface Descriptor(4.5.2) */\
|
||||
TUD_AUDIO10_DESC_CS_AS_INT(/*_termid*/ UAC1_ENTITY_MIC_OUTPUT_TERMINAL, /*_delay*/ 0x01, /*_formattype*/ AUDIO10_DATA_FORMAT_TYPE_I_PCM),\
|
||||
/* Type I Format Type Descriptor(2.2.5) */\
|
||||
TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x01, /*_subframesize*/ _nBytesPerSample_TX, /*_bitresolution*/ _nBitsUsedPerSample_TX, /*_freqs*/ __VA_ARGS__),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
|
||||
TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS), /*_maxEPsize*/ _epinsize, /*_interval*/ 0x01, /*_syncep*/ 0x00),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
|
||||
TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001)
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user