Add SOF callback function for feedback value determination in uac - wip!

This commit is contained in:
Reinhard Panhuber
2022-03-14 20:40:33 +01:00
parent 606f932d92
commit f212899b54
5 changed files with 156 additions and 9 deletions

View File

@@ -306,6 +306,13 @@ typedef struct
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
uint8_t n_frames; // Number of (micro)frames used to estimate feedback value
uint8_t n_frames_current; // Current (micro)frame number
uint32_t feeback_param_factor; // TODO: Set this value within some new tud_audio_set_feedback_params_fm_fs function as feeback_param_factor = f_s / (f_cpu * n_frames)!
#endif
#endif
#endif
@@ -421,6 +428,10 @@ static inline uint8_t tu_desc_subtype(void const* desc)
}
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
static bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
#endif
bool tud_audio_n_mounted(uint8_t func_id)
{
TU_VERIFY(func_id < CFG_TUD_AUDIO);
@@ -1658,6 +1669,11 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
{
audio->ep_fb = ep_addr;
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
usbd_sof_enable(rhport, true); // Enable SOF interrupt
audio->n_frames_current = 0;
#endif
// Invoke callback after ep_out is set
if (audio->ep_out != 0)
{
@@ -1682,6 +1698,23 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
p_desc = tu_desc_next(p_desc);
}
// Disable SOF interrupt if no driver has any enabled feedback EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
bool disable = true;
for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
{
if (_audiod_fct[i].ep_fb != 0)
{
disable = false;
}
}
if (disable) usbd_sof_enable(rhport, false);
#endif
tud_control_status(rhport, p_request);
return true;
@@ -1970,6 +2003,52 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
return false;
}
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
bool tud_audio_set_feedback_params_fm_fs(uint8_t func_id, uint32_t f_m, uint32_t f_s)
{
audiod_function_t* audio = &_audiod_fct[func_id];
uint8_t n_frame = 1; // TODO: finalize that
audio->n_frames = n_frame;
audio->feeback_param_factor = f_s / f_m / n_frame; // TODO: Check the 16.16 precision!
return true;
}
#endif
void audiod_sof (uint8_t rhport, uint32_t frame_count)
{
(void) rhport;
(void) frame_count;
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_cpu) 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)
// 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)
// 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
// Iterate over audio functions and set feedback value
for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
{
audiod_function_t* audio = &_audiod_fct[i];
if (audio->ep_fb != 0)
{
audio->n_frames_current++;
if (audio->n_frames_current == audio->n_frames)
{
uint32_t n_cylces = tud_audio_n_get_fm_n_cycles_cb(rhport, audio->ep_fb);
uint32_t feedback = n_cylces * audio->feeback_param_factor;
tud_audio_n_fb_set(i, feedback);
audio->n_frames_current = 0;
}
}
}
#endif
}
bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len)
{
// Handles only sending of data not receiving
@@ -2247,7 +2326,11 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
static bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
#else
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
#endif
{
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);

View File

@@ -191,6 +191,11 @@
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1
#endif
// Determine feedback value within SOF ISR within audio driver - if disabled the user has to call tud_audio_n_fb_set() with a suitable feedback value on its own. If done within audio driver SOF ISR, tud_audio_n_fb_set() is disabled for user
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR 1 // 0 or 1
#endif
// Audio interrupt control EP size - disabled if 0
#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
@@ -468,8 +473,23 @@ TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
//
// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the
// driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format.
// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value.
# if !CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
static inline bool tud_audio_fb_set(uint32_t feedback);
# else
// This callback function is called once the feedback value needs to be updated within the SOF ISR in the audio class. To determine the feedback value, some
// parameters need to be given. The user must implement this callback function and provide the current cycle count of the master clock.
// The feedback endpoint number can be used to identify the correct audio function in case multiple audio functions were defined.
TU_ATTR_WEAK uint32_t tud_audio_n_get_fm_n_cycles_cb(uint8_t rhport, uint8_t ep_fb);
// f_m : Main clock frequency in Hz i.e. master clock to which sample clock is locked
// f_s : Current sample rate in Hz
bool tud_audio_set_feedback_params_fm_fs(uint8_t func_id, uint32_t f_m, uint32_t f_s);
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
@@ -611,7 +631,7 @@ static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t l
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && !CFG_TUD_AUDIO_ENABLE_FEEDBACK_DETERMINATION_WITHIN_SOF_ISR
static inline bool tud_audio_fb_set(uint32_t feedback)
{
return tud_audio_n_fb_set(0, feedback);
@@ -626,6 +646,7 @@ void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
void audiod_sof (uint8_t rhport, uint32_t frame_count);
#ifdef __cplusplus
}