Skip to content

Commit ecb100a

Browse files
authored
Merge pull request #938 from HiFiPhile/uac_example
Bug fix and Enhancements of UAC2
2 parents 4468b14 + 72f9164 commit ecb100a

File tree

10 files changed

+298
-153
lines changed

10 files changed

+298
-153
lines changed

examples/device/uac2_headset/.skip.MCU_LPC11UXX

Whitespace-only changes.

examples/device/uac2_headset/.skip.MCU_LPC13XX

Whitespace-only changes.

examples/device/uac2_headset/.skip.MCU_NUC121

Whitespace-only changes.

examples/device/uac2_headset/src/main.c

Lines changed: 96 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
// MACRO CONSTANT TYPEDEF PROTOTYPES
3535
//--------------------------------------------------------------------+
3636

37-
#ifndef AUDIO_SAMPLE_RATE
38-
#define AUDIO_SAMPLE_RATE 48000
39-
#endif
37+
// List of supported sample rates
38+
const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
39+
uint32_t current_sample_rate = 44100;
40+
41+
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
4042

4143
/* Blink pattern
4244
* - 25 ms : streaming data
@@ -76,11 +78,16 @@ int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master chan
7678
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
7779

7880
// Buffer for microphone data
79-
int16_t mic_buf[1000];
81+
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
8082
// Buffer for speaker data
81-
int16_t spk_buf[1000];
83+
int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4];
8284
// Speaker data size received in the last frame
8385
int spk_data_size;
86+
// Resolution per format
87+
const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX,
88+
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX};
89+
// Current resolution, update on format change
90+
uint8_t current_resolution;
8491

8592
void led_blinking_task(void);
8693
void audio_task(void);
@@ -135,70 +142,76 @@ void tud_resume_cb(void)
135142
blink_interval_ms = BLINK_MOUNTED;
136143
}
137144

138-
typedef struct TU_ATTR_PACKED
139-
{
140-
union
141-
{
142-
struct TU_ATTR_PACKED
143-
{
144-
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
145-
uint8_t type : 2; ///< Request type tusb_request_type_t.
146-
uint8_t direction : 1; ///< Direction type. tusb_dir_t
147-
} bmRequestType_bit;
148-
149-
uint8_t bmRequestType;
150-
};
151-
152-
uint8_t bRequest; ///< Request type audio_cs_req_t
153-
uint8_t bChannelNumber;
154-
uint8_t bControlSelector;
155-
union
156-
{
157-
uint8_t bInterface;
158-
uint8_t bEndpoint;
159-
};
160-
uint8_t bEntityID;
161-
uint16_t wLength;
162-
} audio_control_request_t;
163-
164145
// Helper for clock get requests
165146
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
166147
{
167148
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
168149

169-
// Example supports only single frequency, same value will be used for current value and range
170150
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
171151
{
172152
if (request->bRequest == AUDIO_CS_REQ_CUR)
173153
{
174-
TU_LOG2("Clock get current freq %u\r\n", AUDIO_SAMPLE_RATE);
154+
TU_LOG1("Clock get current freq %u\r\n", current_sample_rate);
175155

176-
audio_control_cur_4_t curf = { tu_htole32(AUDIO_SAMPLE_RATE) };
156+
audio_control_cur_4_t curf = { tu_htole32(current_sample_rate) };
177157
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
178158
}
179159
else if (request->bRequest == AUDIO_CS_REQ_RANGE)
180160
{
181-
audio_control_range_4_n_t(1) rangef =
161+
audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
182162
{
183-
.wNumSubRanges = tu_htole16(1),
184-
.subrange[0] = { tu_htole32(AUDIO_SAMPLE_RATE), tu_htole32(AUDIO_SAMPLE_RATE), 0}
163+
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)
185164
};
186-
TU_LOG2("Clock get freq range (%d, %d, %d)\r\n", (int)rangef.subrange[0].bMin, (int)rangef.subrange[0].bMax, (int)rangef.subrange[0].bRes);
165+
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
166+
for(uint8_t i = 0; i < N_SAMPLE_RATES; i++)
167+
{
168+
rangef.subrange[i].bMin = sample_rates[i];
169+
rangef.subrange[i].bMax = sample_rates[i];
170+
rangef.subrange[i].bRes = 0;
171+
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);
172+
}
173+
187174
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef));
188175
}
189176
}
190177
else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
191178
request->bRequest == AUDIO_CS_REQ_CUR)
192179
{
193180
audio_control_cur_1_t cur_valid = { .bCur = 1 };
194-
TU_LOG2("Clock get is valid %u\r\n", cur_valid.bCur);
181+
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
195182
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid));
196183
}
197184
TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
198185
request->bEntityID, request->bControlSelector, request->bRequest);
199186
return false;
200187
}
201188

189+
// Helper for clock set requests
190+
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
191+
{
192+
(void)rhport;
193+
194+
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
195+
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
196+
197+
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
198+
{
199+
TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
200+
201+
current_sample_rate = ((audio_control_cur_4_t *)buf)->bCur;
202+
203+
TU_LOG1("Clock set current freq: %d\r\n", current_sample_rate);
204+
205+
return true;
206+
}
207+
else
208+
{
209+
TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
210+
request->bEntityID, request->bControlSelector, request->bRequest);
211+
return false;
212+
}
213+
}
214+
202215
// Helper for feature unit get requests
203216
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request)
204217
{
@@ -207,7 +220,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
207220
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR)
208221
{
209222
audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] };
210-
TU_LOG2("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
223+
TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
211224
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1));
212225
}
213226
else if (UAC2_ENTITY_SPK_FEATURE_UNIT && request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
@@ -218,14 +231,14 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
218231
.wNumSubRanges = tu_htole16(1),
219232
.subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) }
220233
};
221-
TU_LOG2("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
234+
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
222235
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
223236
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol));
224237
}
225238
else if (request->bRequest == AUDIO_CS_REQ_CUR)
226239
{
227240
audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) };
228-
TU_LOG2("Get channel %u volume %u dB\r\n", request->bChannelNumber, cur_vol.bCur);
241+
TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
229242
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol));
230243
}
231244
}
@@ -249,7 +262,7 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
249262

250263
mute[request->bChannelNumber] = ((audio_control_cur_1_t *)buf)->bCur;
251264

252-
TU_LOG2("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
265+
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
253266

254267
return true;
255268
}
@@ -259,7 +272,7 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
259272

260273
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur;
261274

262-
TU_LOG2("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
275+
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
263276

264277
return true;
265278
}
@@ -299,7 +312,8 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
299312

300313
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
301314
return tud_audio_feature_unit_set_request(rhport, request, buf);
302-
315+
if (request->bEntityID == UAC2_ENTITY_CLOCK)
316+
return tud_audio_clock_set_request(rhport, request, buf);
303317
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
304318
request->bEntityID, request->bControlSelector, request->bRequest);
305319

@@ -329,6 +343,13 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
329343
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0)
330344
blink_interval_ms = BLINK_STREAMING;
331345

346+
// Clear buffer when streaming format is changed
347+
spk_data_size = 0;
348+
if(alt != 0)
349+
{
350+
current_resolution = resolutions_per_format[alt-1];
351+
}
352+
332353
return true;
333354
}
334355

@@ -362,20 +383,40 @@ void audio_task(void)
362383
{
363384
// When new data arrived, copy data from speaker buffer, to microphone buffer
364385
// and send it over
386+
// Only support speaker & headphone both have the same resolution
387+
// If one is 16bit another is 24bit be care of LOUD noise !
365388
if (spk_data_size)
366389
{
367-
int16_t *src = spk_buf;
368-
int16_t *limit = spk_buf + spk_data_size / 2;
369-
int16_t *dst = mic_buf;
370-
while (src < limit)
390+
if (current_resolution == 16)
371391
{
372-
// Combine two channels into one
373-
int32_t left = *src++;
374-
int32_t right = *src++;
375-
*dst++ = (int16_t)((left + right) / 2);
392+
int16_t *src = (int16_t*)spk_buf;
393+
int16_t *limit = (int16_t*)spk_buf + spk_data_size / 2;
394+
int16_t *dst = (int16_t*)mic_buf;
395+
while (src < limit)
396+
{
397+
// Combine two channels into one
398+
int32_t left = *src++;
399+
int32_t right = *src++;
400+
*dst++ = (left >> 1) + (right >> 1);
401+
}
402+
tud_audio_write((uint8_t *)mic_buf, spk_data_size / 2);
403+
spk_data_size = 0;
404+
}
405+
else if (current_resolution == 24)
406+
{
407+
int32_t *src = spk_buf;
408+
int32_t *limit = spk_buf + spk_data_size / 4;
409+
int32_t *dst = mic_buf;
410+
while (src < limit)
411+
{
412+
// Combine two channels into one
413+
int32_t left = *src++;
414+
int32_t right = *src++;
415+
*dst++ = ((left >> 1) + (right >> 1)) & 0xffffff00;
416+
}
417+
tud_audio_write((uint8_t *)mic_buf, spk_data_size / 2);
418+
spk_data_size = 0;
376419
}
377-
tud_audio_write((uint8_t *)mic_buf, spk_data_size / 2);
378-
spk_data_size = 0;
379420
}
380421
}
381422

examples/device/uac2_headset/src/tusb_config.h

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,33 +93,49 @@ extern "C" {
9393
//--------------------------------------------------------------------
9494
// AUDIO CLASS DRIVER CONFIGURATION
9595
//--------------------------------------------------------------------
96-
#define CFG_TUD_AUDIO_IN_PATH (CFG_TUD_AUDIO)
97-
#define CFG_TUD_AUDIO_OUT_PATH (CFG_TUD_AUDIO)
9896

99-
//#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN 220 // This equals TUD_AUDIO_HEADSET_STEREO_DESC_LEN, however, including it from usb_descriptors.h is not possible due to some strange include hassle
100-
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN
97+
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN
98+
99+
// How many formats are used, need to adjust USB descriptor if changed
100+
#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2
101101

102102
// Audio format type I specifications
103-
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1
104-
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2
105-
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
106-
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
107-
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0
103+
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 96000 // 24bit/96kHz is the best quality for full-speed, high-speed is needed beyond this
104+
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1
105+
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
106+
107+
// 16bit in 16bit slots
108+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX 2
109+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX 16
110+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX 2
111+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX 16
112+
113+
// 24bit in 32bit slots
114+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 4
115+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 24
116+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX 4
117+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX 24
108118

109119
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
110120
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
111-
#define CFG_TUD_AUDIO_EP_SZ_IN (CFG_TUD_AUDIO_IN_PATH * (48 + 1) * (CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX) * (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)) // 48 Samples (48 kHz) x 2 Bytes/Sample x n Channels
112-
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN
113-
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN // Maximum EP IN size for all AS alternate settings used
121+
122+
#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)
123+
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(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)
124+
125+
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*2
126+
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
114127

115128
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
116129
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
117-
#define CFG_TUD_AUDIO_EP_OUT_SZ (CFG_TUD_AUDIO_OUT_PATH * ((48 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * (CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX) * (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX))) // N Samples (N kHz) x 2 Bytes/Sample x n Channels
118-
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ CFG_TUD_AUDIO_EP_OUT_SZ*3
119-
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX CFG_TUD_AUDIO_EP_OUT_SZ // Maximum EP IN size for all AS alternate settings used
130+
131+
#define CFG_TUD_AUDIO_UNC_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)
132+
#define CFG_TUD_AUDIO_UNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(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)
133+
134+
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_UNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_UNC_1_FORMAT_2_EP_SZ_OUT)*2
135+
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_UNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_UNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
120136

121137
// 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)
122-
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1
138+
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2
123139

124140
// Size of control request buffer
125141
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64

examples/device/uac2_headset/src/usb_descriptors.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ uint8_t const desc_configuration[] =
9393
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
9494

9595
// Interface number, string index, EP Out & EP In address, EP size
96-
TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, 2, 16, EPNUM_AUDIO, CFG_TUD_AUDIO_EP_OUT_SZ, EPNUM_AUDIO | 0x80, CFG_TUD_AUDIO_EP_SZ_IN)
96+
TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO, EPNUM_AUDIO | 0x80)
9797
};
9898

9999
// Invoked when received GET CONFIGURATION DESCRIPTOR

0 commit comments

Comments
 (0)