Skip to content

Commit ff2dc0a

Browse files
author
Reinhard Panhuber
committed
Streamline feedback calc, find fb interval from descriptors, inc. checks
1 parent fdfde88 commit ff2dc0a

File tree

2 files changed

+134
-87
lines changed

2 files changed

+134
-87
lines changed

src/class/audio/audio_device.c

Lines changed: 107 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -305,18 +305,29 @@ typedef struct
305305
#endif
306306

307307
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
308-
uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
308+
#if !CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
309+
uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
310+
#endif
309311

310312
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
311-
uint8_t n_frames; // Number of (micro)frames used to estimate feedback value
312-
uint8_t n_frames_current; // Current (micro)frame number
313-
uint32_t n_cycles_old; // Old cycle count
314-
union {
315-
uint32_t i;
316-
float f;
317-
} feedback_param_factor_N; // Numerator of feedback parameter coefficient
318-
uint32_t feedback_param_factor_D; // Denominator of feedback parameter coefficient
319-
uint32_t * feedback_param_p_cycle_count; // Pointer to cycle counter
313+
volatile uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
314+
uint8_t fb_n_frames; // Number of (micro)frames used to estimate feedback value
315+
volatile uint8_t fb_n_frames_current; // Current (micro)frame number
316+
volatile uint32_t fb_n_cycles_old; // Old cycle count
317+
uint32_t * fb_param_p_cycle_count; // Pointer to cycle counter
318+
319+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT
320+
uint8_t fb_power_of_two_val;
321+
#endif
322+
323+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT
324+
float fb_float_val;
325+
#endif
326+
327+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT
328+
uint64_t fb_param_factor_N; // Numerator of feedback parameter coefficient
329+
uint64_t fb_param_factor_D; // Denominator of feedback parameter coefficient
330+
#endif
320331
#endif
321332

322333
#endif
@@ -1527,7 +1538,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
15271538
tu_fifo_clear(&audio->tx_supp_ff[cnt]);
15281539
}
15291540
#endif
1530-
1541+
15311542
// Invoke callback - can be used to stop data sampling
15321543
if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
15331544

@@ -1560,7 +1571,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
15601571
// Close corresponding feedback EP
15611572
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
15621573
usbd_edpt_close(rhport, audio->ep_fb);
1563-
audio->ep_fb = 0; // Necessary?
1574+
audio->ep_fb = 0;
15641575
#endif
15651576
}
15661577
#endif
@@ -1677,8 +1688,9 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
16771688

16781689
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
16791690
usbd_sof_enable(rhport, true); // Enable SOF interrupt
1680-
audio->n_frames_current = 0;
1681-
audio->n_cycles_old = 0;
1691+
audio->fb_n_frames = desc_ep->bInterval;
1692+
audio->fb_n_frames_current = 0;
1693+
audio->fb_n_cycles_old = 0;
16821694
#endif
16831695

16841696
// Invoke callback after ep_out is set
@@ -1711,12 +1723,12 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
17111723
bool disable = true;
17121724

17131725
for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
1726+
{
1727+
if (_audiod_fct[i].ep_fb != 0)
17141728
{
1715-
if (_audiod_fct[i].ep_fb != 0)
1716-
{
1717-
disable = false;
1718-
}
1729+
disable = false;
17191730
}
1731+
}
17201732

17211733
if (disable) usbd_sof_enable(rhport, false);
17221734

@@ -2011,40 +2023,56 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
20112023
}
20122024

20132025
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
2014-
// f_s max is 2^19-1 = 524287 Hz
2015-
// n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
2016-
// f_m max is 2^29/(1 ms * n_frames) for full speed and 2^29/(125 us * n_frames) for high speed
2017-
bool tud_audio_set_feedback_params_fm_fs(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32_t * p_cycle_count, bool use_float)
2026+
2027+
// This function must be called from the user within the tud_audio_set_itf_cb(rhport, p_request) callback function, where p_request needs to be checked as
2028+
// uint8_t const itf = tu_u16_low(p_request->wIndex);
2029+
// uint8_t const alt = tu_u16_low(p_request->wValue);
2030+
// such that tud_audio_set_fb_params() gets called with the parameters corresponding to the defined interface and alternate setting
2031+
// Also, start the main clock cycle counter (or reset its value) within tud_audio_set_itf_cb()
2032+
TU_ATTR_WEAK bool tud_audio_set_fb_params(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32_t * p_cycle_count)
20182033
{
20192034
audiod_function_t* audio = &_audiod_fct[func_id];
2020-
audio->feedback_param_p_cycle_count = p_cycle_count;
2021-
uint8_t n_frame = 1; // TODO: finalize that
2022-
audio->n_frames = n_frame;
2035+
audio->fb_param_p_cycle_count = p_cycle_count;
20232036

2037+
// Check if frame interval is within sane limits
2038+
// The interval value audio->fb_n_frames was taken from the descriptors within audiod_set_interface()
20242039

2040+
// n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed - this lower limit ensures the measures feedback value has sufficient precision
2041+
if ((TUSB_SPEED_FULL == tud_speed_get() && ((2^10 * f_s / f_m) + 1) > audio->fb_n_frames) || (TUSB_SPEED_HIGH == tud_speed_get() && ((2^13 * f_s / f_m) + 1) > audio->fb_n_frames))
2042+
{
2043+
TU_LOG2(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false;
2044+
}
20252045

2026-
// Check if parameters reduce to a power of two shift i.e. is n_f * f_m / f_s a power of two?
2027-
if ((f_m % f_s) == 0 && tu_is_power_of_two(n_frame * f_m / f_s))
2046+
2047+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT
2048+
// Check if parameters really allow for a power of two division
2049+
if ((f_m % f_s) != 0 || !tu_is_power_of_two(audio->fb_n_frames * f_m / f_s))
20282050
{
2029-
audio->feedback_param_factor_N.i = 0; // zero marks power of two option
2030-
audio->feedback_param_factor_D = 16 - tu_log2(n_frame * f_m / f_s);
2051+
TU_LOG2(" FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT not possible!\r\n"); TU_BREAKPOINT(); return false;
20312052
}
2032-
else
2053+
2054+
audio->fb_power_of_two_val = 16 - tu_log2(audio->fb_n_frames * f_m / f_s);
2055+
#endif
2056+
2057+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT
2058+
audio->fb_float_val = (float)f_s / audio->fb_n_frames / f_m * (1 << 16);
2059+
#endif
2060+
2061+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT
2062+
2063+
// f_s max is 2^19-1 = 524287 Hz
2064+
// f_m max is 2^29/(1 ms * n_frames) for full speed and 2^29/(125 us * n_frames) for high speed, this means for f_m fitting in an uint32, n_frames must be < 125 for full speed and < 1000 for high speed (1000 does not fit into uint8 so the maximum possible value cannot even be reached by UAC2 - we don't need to check for it)
2065+
2066+
if ((f_s > (2^19)-1) || (TUSB_SPEED_FULL == tud_speed_get() && (audio->fb_n_frames > 125)))
20332067
{
2034-
// Next best option is to use float if a FPU is available - this has to be enabled by the user
2035-
if (use_float)
2036-
{
2037-
audio->feedback_param_factor_N.f = (float)f_s / n_frame / f_m * (1 << 16);
2038-
audio->feedback_param_factor_D = 0; // non zero marks float option
2039-
}
2040-
else
2041-
{
2042-
// Neither a power of two or floats - use fixed point number
2043-
audio->feedback_param_factor_N.i = f_s << 13;
2044-
audio->feedback_param_factor_D = f_m * n_frame;
2045-
}
2068+
// If this check fails, not every thing is lost - you need to re-evaluate the scaling factors of the parameters such that the numbers fit into uint64 again. feedback = n_cycles * S_c / (n_frames * S_n) * f_s * S_s / (f_m * S_m). In the end S_c*S_s / (S_n * S_m) = 2^16 for a 16.16 fixed point precision. If you find something, define your own function of tud_audio_set_feedback_params_fm_fs() and audiod_sof() and use your values
2069+
TU_LOG2(" FEEDBACK_DETERMINATION_MODE_FIXED_POINT not possible!\r\n"); TU_BREAKPOINT(); return false;
20462070
}
20472071

2072+
audio->fb_param_factor_N = (uint64_t)f_s << 13;
2073+
audio->fb_param_factor_D = (uint64_t)f_m * audio->fb_n_frames;
2074+
#endif
2075+
20482076
return true;
20492077

20502078
}
@@ -2053,54 +2081,47 @@ bool tud_audio_set_feedback_params_fm_fs(uint8_t func_id, uint32_t f_m, uint32_t
20532081
void audiod_sof (uint8_t rhport, uint32_t frame_count)
20542082
{
20552083
(void) rhport;
2056-
(void) frame_count;
2084+
(void) frame_count; // frame_count is not used since some devices may not provide the frame count value
20572085

20582086
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
20592087

20602088
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
20612089
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
2062-
// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_cpu is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
2063-
// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_cpu / f_s)), K)
2064-
// Ff = n_cycles / n_frames * f_s / f_cpu in 16.16 format, where n_cycles are the number of CPU cycles within n_frames
2090+
// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
2091+
// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K)
2092+
// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
20652093

20662094
// Iterate over audio functions and set feedback value
20672095
for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
2068-
{
2069-
audiod_function_t* audio = &_audiod_fct[i];
2096+
{
2097+
audiod_function_t* audio = &_audiod_fct[i];
20702098

2071-
if (audio->ep_fb != 0)
2099+
if (audio->ep_fb != 0)
2100+
{
2101+
audio->fb_n_frames_current++;
2102+
if (audio->fb_n_frames_current == audio->fb_n_frames)
20722103
{
2073-
audio->n_frames_current++;
2074-
if (audio->n_frames_current == audio->n_frames)
2075-
{
2076-
uint32_t n_cylces = *audio->feedback_param_p_cycle_count;
2077-
uint32_t feedback;
2104+
uint32_t n_cylces = *audio->fb_param_p_cycle_count;
2105+
uint32_t feedback;
20782106

2079-
if (audio->feedback_param_factor_N.i == 0)
2080-
{
2081-
// Power of two shift
2082-
feedback = n_cylces << audio->feedback_param_factor_D;
2083-
}
2084-
else
2085-
{
2086-
if (audio->feedback_param_factor_D == 0)
2087-
{
2088-
// Float computation
2089-
feedback = (uint32_t)(n_cylces * audio->feedback_param_factor_N.f);
2090-
}
2091-
else
2092-
{
2093-
// Fixed point computation
2094-
feedback = ((n_cylces - audio->n_cycles_old) << 3) * audio->feedback_param_factor_N.i / audio->feedback_param_factor_D; // feeback_param_factor_N has scaling factor of 13 bits, n_cycles 3 and feeback_param_factor_D 1, hence 16.16 precision
2095-
}
2096-
}
2107+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT
2108+
feedback = (n_cylces - audio->fb_n_cycles_old) << audio->fb_power_of_two_val;
2109+
#endif
20972110

2098-
tud_audio_n_fb_set(i, feedback);
2099-
audio->n_frames_current = 0;
2100-
audio->n_cycles_old = n_cylces;
2101-
}
2111+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT
2112+
feedback = (uint32_t)((float)(n_cylces - audio->fb_n_cycles_old) * audio->fb_float_val);
2113+
#endif
2114+
2115+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE == CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT
2116+
feedback = ((n_cylces - audio->fb_n_cycles_old) << 3) * audio->fb_param_factor_N / audio->fb_param_factor_D; // feeback_param_factor_N has scaling factor of 13 bits, n_cycles 3 and feeback_param_factor_D 1, hence 16.16 precision
2117+
#endif
2118+
2119+
tud_audio_n_fb_set(i, feedback);
2120+
audio->fb_n_frames_current = 0;
2121+
audio->fb_n_cycles_old = n_cylces;
21022122
}
21032123
}
2124+
}
21042125

21052126
#endif
21062127
}
@@ -2299,6 +2320,16 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
22992320
// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
23002321
static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf)
23012322
{
2323+
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
2324+
if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) return; // Abort, this interface has no EP, this driver does not support this currently
2325+
#endif
2326+
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
2327+
if (as_itf != audio->ep_in_as_intf_num) return;
2328+
#endif
2329+
#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
2330+
if (as_itf != audio->ep_out_as_intf_num) return;
2331+
#endif
2332+
23022333
p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor
23032334

23042335
while (p_desc < p_desc_end)
@@ -2309,16 +2340,6 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
23092340
// Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
23102341
if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL)
23112342
{
2312-
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
2313-
if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently
2314-
#endif
2315-
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
2316-
if (as_itf != audio->ep_in_as_intf_num) break;
2317-
#endif
2318-
#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
2319-
if (as_itf != audio->ep_out_as_intf_num) break;
2320-
#endif
2321-
23222343
#if CFG_TUD_AUDIO_ENABLE_EP_IN
23232344
if (as_itf == audio->ep_in_as_intf_num)
23242345
{

src/class/audio/audio_device.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,25 @@
196196
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR 1 // 0 or 1
197197
#endif
198198

199+
// Feeback calculation mode
200+
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
201+
202+
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT 1
203+
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT 2
204+
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT 3
205+
206+
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE
207+
#error You must tell the driver the feedback determination mode! Possible choices are: CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT, CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT, or CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT!
208+
#endif
209+
210+
#ifdef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE
211+
#if (CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE != CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT)
212+
#error Unknown CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE. Possible choices are: CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_POWER_OF_TWO_SHIFT, CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FLOAT, or CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_MODE_FIXED_POINT!
213+
#endif
214+
#endif
215+
216+
#endif
217+
199218
// Audio interrupt control EP size - disabled if 0
200219
#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
201220
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
@@ -487,7 +506,14 @@ TU_ATTR_WEAK uint32_t tud_audio_n_get_fm_n_cycles_cb(uint8_t rhport, uint8_t ep_
487506

488507
// f_m : Main clock frequency in Hz i.e. master clock to which sample clock is locked
489508
// f_s : Current sample rate in Hz
490-
bool tud_audio_set_feedback_params_fm_fs(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32_t * p_cycle_count, bool use_float);
509+
// p_cycle_count : Pointer to main clock cycle counter register where the current count can be read from (e.g. a timer register)
510+
511+
// This function must be called from the user within the tud_audio_set_itf_cb(rhport, p_request) callback function, where p_request needs to be checked as
512+
// uint8_t const itf = tu_u16_low(p_request->wIndex);
513+
// uint8_t const alt = tu_u16_low(p_request->wValue);
514+
// such that tud_audio_set_fb_params() gets called with the parameters corresponding to the defined interface and alternate setting
515+
// Also, start the main clock cycle counter (or reset its value) within tud_audio_set_itf_cb()
516+
TU_ATTR_WEAK bool tud_audio_set_fb_params(uint8_t func_id, uint32_t f_m, uint32_t f_s, uint32_t * p_cycle_count);
491517
#endif
492518

493519
#endif

0 commit comments

Comments
 (0)