mirror of
https://github.com/hathach/tinyusb.git
synced 2025-03-19 01:21:01 +00:00
commit
fdeac8508b
@ -305,9 +305,35 @@ typedef struct
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
|
||||
#endif
|
||||
struct {
|
||||
uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
|
||||
uint32_t min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1.
|
||||
uint32_t max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1.
|
||||
|
||||
uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS)
|
||||
uint8_t compute_method;
|
||||
|
||||
union {
|
||||
uint8_t power_of_2; // pre-computed power of 2 shift
|
||||
float float_const; // pre-computed float constant
|
||||
|
||||
struct {
|
||||
uint32_t sample_freq;
|
||||
uint32_t mclk_freq;
|
||||
}fixed;
|
||||
|
||||
#if 0 // implement later
|
||||
struct {
|
||||
uint32_t nominal_value;
|
||||
uint32_t threshold_bytes;
|
||||
}fifo_count;
|
||||
#endif
|
||||
}compute;
|
||||
|
||||
} feedback;
|
||||
#endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
|
||||
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
|
||||
tu_fifo_t ep_in_ff;
|
||||
@ -1039,7 +1065,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio)
|
||||
{
|
||||
return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->fb_val, 4);
|
||||
return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->feedback.value, 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1510,7 +1536,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
tu_fifo_clear(&audio->tx_supp_ff[cnt]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Invoke callback - can be used to stop data sampling
|
||||
if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
|
||||
|
||||
@ -1543,7 +1569,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
// Close corresponding feedback EP
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
usbd_edpt_close(rhport, audio->ep_fb);
|
||||
audio->ep_fb = 0; // Necessary?
|
||||
audio->ep_fb = 0;
|
||||
tu_memclr(&audio->feedback, sizeof(audio->feedback));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@ -1602,8 +1629,6 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
#endif
|
||||
|
||||
#endif
|
||||
// Invoke callback - can be used to trigger data sampling if not already running
|
||||
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
|
||||
|
||||
// Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded
|
||||
// It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there
|
||||
@ -1635,16 +1660,6 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
// In case of asynchronous EP, call Cb after ep_fb is set
|
||||
if ( !(desc_ep->bmAttributes.sync == 0x01 && audio->ep_fb == 0) )
|
||||
{
|
||||
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
|
||||
}
|
||||
#else
|
||||
// Invoke callback
|
||||
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
|
||||
#endif
|
||||
// Prepare for incoming data
|
||||
#if USE_LINEAR_BUFFER_RX
|
||||
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
|
||||
@ -1657,12 +1672,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) // Check if usage is explicit data feedback
|
||||
{
|
||||
audio->ep_fb = ep_addr;
|
||||
audio->feedback.frame_shift = desc_ep->bInterval -1;
|
||||
|
||||
// Invoke callback after ep_out is set
|
||||
if (audio->ep_out != 0)
|
||||
{
|
||||
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
|
||||
}
|
||||
// Enable SOF interrupt if callback is implemented
|
||||
if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true);
|
||||
}
|
||||
#endif
|
||||
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
@ -1674,6 +1687,49 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
|
||||
TU_VERIFY(foundEPs == nEps);
|
||||
|
||||
// Invoke one callback for a final set interface
|
||||
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
// Prepare feedback computation if callback is available
|
||||
if (tud_audio_feedback_params_cb)
|
||||
{
|
||||
audio_feedback_params_t fb_param;
|
||||
|
||||
tud_audio_feedback_params_cb(func_id, alt, &fb_param);
|
||||
audio->feedback.compute_method = fb_param.method;
|
||||
|
||||
// Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
|
||||
uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
|
||||
audio->feedback.min_value = (fb_param.sample_freq/frame_div - 1) << 16;
|
||||
audio->feedback.max_value = (fb_param.sample_freq/frame_div + 1) << 16;
|
||||
|
||||
switch(fb_param.method)
|
||||
{
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
|
||||
set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
|
||||
break;
|
||||
|
||||
#if 0 // implement later
|
||||
case AUDIO_FEEDBACK_METHOD_FIFO_COUNT:
|
||||
{
|
||||
uint64_t fb64 = ((uint64_t) fb_param.sample_freq) << 16;
|
||||
audio->feedback.compute.fifo_count.nominal_value = (uint32_t) (fb64 / frame_div);
|
||||
audio->feedback.compute.fifo_count.threshold_bytes = fb_param.fifo_count.threshold_bytes;
|
||||
|
||||
tud_audio_fb_set(audio->feedback.compute.fifo_count.nominal_value);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
// nothing to do
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// We are done - abort loop
|
||||
break;
|
||||
}
|
||||
@ -1682,6 +1738,20 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
// Disable SOF interrupt if no driver has any enabled feedback EP
|
||||
bool disable = true;
|
||||
for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
|
||||
{
|
||||
if (_audiod_fct[i].ep_fb != 0)
|
||||
{
|
||||
disable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (disable) usbd_sof_enable(rhport, false);
|
||||
#endif
|
||||
|
||||
tud_control_status(rhport, p_request);
|
||||
|
||||
return true;
|
||||
@ -1898,14 +1968,14 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
|
||||
(void) xferred_bytes;
|
||||
|
||||
// Search for interface belonging to given end point address and proceed as required
|
||||
uint8_t func_id;
|
||||
for (func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
|
||||
for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
|
||||
{
|
||||
audiod_function_t* audio = &_audiod_fct[func_id];
|
||||
|
||||
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
|
||||
|
||||
// Data transmission of control interrupt finished
|
||||
if (_audiod_fct[func_id].ep_int_ctr == ep_addr)
|
||||
if (audio->ep_int_ctr == ep_addr)
|
||||
{
|
||||
// According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
|
||||
// In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
|
||||
@ -1922,7 +1992,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN
|
||||
|
||||
// Data transmission of audio packet finished
|
||||
if (_audiod_fct[func_id].ep_in == ep_addr && _audiod_fct[func_id].alt_setting != 0)
|
||||
if (audio->ep_in == ep_addr && audio->alt_setting != 0)
|
||||
{
|
||||
// USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
|
||||
// That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
|
||||
@ -1933,7 +2003,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
|
||||
// This is the only place where we can fill something into the EPs buffer!
|
||||
|
||||
// Load new data
|
||||
TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
|
||||
TU_VERIFY(audiod_tx_done_cb(rhport, audio));
|
||||
|
||||
// Transmission of ZLP is done by audiod_tx_done_cb()
|
||||
return true;
|
||||
@ -1943,24 +2013,24 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
|
||||
// New audio packet received
|
||||
if (_audiod_fct[func_id].ep_out == ep_addr)
|
||||
if (audio->ep_out == ep_addr)
|
||||
{
|
||||
TU_VERIFY(audiod_rx_done_cb(rhport, &_audiod_fct[func_id], (uint16_t) xferred_bytes));
|
||||
TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t) xferred_bytes));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
// Transmission of feedback EP finished
|
||||
if (_audiod_fct[func_id].ep_fb == ep_addr)
|
||||
if (audio->ep_fb == ep_addr)
|
||||
{
|
||||
if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
|
||||
if (tud_audio_fb_done_cb) tud_audio_fb_done_cb(func_id);
|
||||
|
||||
// Schedule a transmit with the new value if EP is not busy
|
||||
if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb))
|
||||
if (!usbd_edpt_busy(rhport, audio->ep_fb))
|
||||
{
|
||||
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
|
||||
return audiod_fb_send(rhport, &_audiod_fct[func_id]);
|
||||
return audiod_fb_send(rhport, audio);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1970,6 +2040,111 @@ 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
|
||||
|
||||
static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq)
|
||||
{
|
||||
// Check if frame interval is within sane limits
|
||||
// The interval value n_frames was taken from the descriptors within audiod_set_interface()
|
||||
|
||||
// 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
|
||||
uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
|
||||
uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
|
||||
|
||||
if ( (((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame )
|
||||
{
|
||||
TU_LOG1(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false;
|
||||
}
|
||||
|
||||
// Check if parameters really allow for a power of two division
|
||||
if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq))
|
||||
{
|
||||
audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
|
||||
audio->feedback.compute.power_of_2 = 16 - audio->feedback.frame_shift - tu_log2(mclk_freq / sample_freq);
|
||||
}
|
||||
else if ( audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT)
|
||||
{
|
||||
audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - audio->feedback.frame_shift));
|
||||
}
|
||||
else
|
||||
{
|
||||
audio->feedback.compute.fixed.sample_freq = sample_freq;
|
||||
audio->feedback.compute.fixed.mclk_freq = mclk_freq;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles)
|
||||
{
|
||||
audiod_function_t* audio = &_audiod_fct[func_id];
|
||||
uint32_t feedback;
|
||||
|
||||
switch (audio->feedback.compute_method)
|
||||
{
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
|
||||
feedback = (cycles << audio->feedback.compute.power_of_2);
|
||||
break;
|
||||
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
|
||||
feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
|
||||
break;
|
||||
|
||||
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
|
||||
{
|
||||
uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - audio->feedback.frame_shift);
|
||||
feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
|
||||
}
|
||||
break;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
// For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
|
||||
// The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
|
||||
// This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
|
||||
// (audio slot = channel count samples).
|
||||
if ( feedback > audio->feedback.max_value ) feedback = audio->feedback.max_value;
|
||||
if ( feedback < audio->feedback.min_value ) feedback = audio->feedback.min_value;
|
||||
|
||||
tud_audio_n_fb_set(func_id, feedback);
|
||||
|
||||
return feedback;
|
||||
}
|
||||
#endif
|
||||
|
||||
void audiod_sof_isr (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
|
||||
// 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_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)
|
||||
// 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)
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
{
|
||||
// HS shift need to be adjusted since SOF event is generated for frame only
|
||||
uint8_t const hs_adjust = (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0;
|
||||
uint32_t const interval = 1UL << (audio->feedback.frame_shift - hs_adjust);
|
||||
if ( 0 == (frame_count & (interval-1)) )
|
||||
{
|
||||
if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
}
|
||||
|
||||
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
|
||||
@ -2164,6 +2339,16 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
|
||||
// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
|
||||
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)
|
||||
{
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
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
|
||||
#endif
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
if (as_itf != audio->ep_in_as_intf_num) return;
|
||||
#endif
|
||||
#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
if (as_itf != audio->ep_out_as_intf_num) return;
|
||||
#endif
|
||||
|
||||
p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor
|
||||
|
||||
while (p_desc < p_desc_end)
|
||||
@ -2174,16 +2359,6 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
|
||||
// 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
|
||||
if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL)
|
||||
{
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
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
|
||||
#endif
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
if (as_itf != audio->ep_in_as_intf_num) break;
|
||||
#endif
|
||||
#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
if (as_itf != audio->ep_out_as_intf_num) break;
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN
|
||||
if (as_itf == audio->ep_in_as_intf_num)
|
||||
{
|
||||
@ -2255,7 +2430,7 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
|
||||
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
|
||||
if ( TUSB_SPEED_FULL == tud_speed_get() )
|
||||
{
|
||||
uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val;
|
||||
uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].feedback.value;
|
||||
|
||||
// For FS format is 10.14
|
||||
*(fb++) = (feedback >> 2) & 0xFF;
|
||||
@ -2267,7 +2442,7 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
|
||||
#else
|
||||
{
|
||||
// Send value as-is, caller will choose the appropriate format
|
||||
_audiod_fct[func_id].fb_val = feedback;
|
||||
_audiod_fct[func_id].feedback.value = feedback;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -458,7 +458,14 @@ TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_byte
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
|
||||
TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id);
|
||||
|
||||
|
||||
// determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 8 frames), i.e. a larger delay is introduced.
|
||||
|
||||
// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter) - see tud_audio_set_fb_params(). Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller (e.g. 2 frames) and thus a smaller delay is possible, disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. This option is a great starting point to try the SOF ISR option but depending on your hardware setup (performance of the CPU) it might not work. If so, figure out why and use the next option. (The most critical point is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced).
|
||||
|
||||
// Feedback value is determined by the user by use of SOF interrupt. The user may use tud_audio_sof_isr() which is called every SOF (of course only invoked when an alternate interface other than zero was set). The number of frames used to determine the feedback value for the currently active alternate setting can be get by tud_audio_get_fb_n_frames(). The feedback value must be set by use of tud_audio_n_fb_set().
|
||||
|
||||
// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed.
|
||||
//
|
||||
@ -468,9 +475,61 @@ 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.
|
||||
|
||||
// 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_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)
|
||||
// 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)
|
||||
// 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
|
||||
|
||||
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
|
||||
static inline bool tud_audio_fb_set(uint32_t feedback);
|
||||
|
||||
// Update feedback value with passed cycles since last time this update function is called.
|
||||
// Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented
|
||||
// This function will also call tud_audio_feedback_set()
|
||||
// return feedback value in 16.16 for reference (0 for error)
|
||||
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles);
|
||||
|
||||
enum {
|
||||
AUDIO_FEEDBACK_METHOD_DISABLED,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2,
|
||||
|
||||
// impelemnt later
|
||||
// AUDIO_FEEDBACK_METHOD_FIFO_COUNT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t method;
|
||||
uint32_t sample_freq; // sample frequency in Hz
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
|
||||
}frequency;
|
||||
|
||||
#if 0 // implement later
|
||||
struct {
|
||||
uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready
|
||||
}fifo_count;
|
||||
#endif
|
||||
};
|
||||
}audio_feedback_params_t;
|
||||
|
||||
// Invoked when needed to set feedback parameters
|
||||
TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
|
||||
|
||||
// Callback in ISR context, invoked periodically according to feedback endpoint bInterval.
|
||||
// Could be used to compute and update feedback value
|
||||
// frame_number : current SOF count
|
||||
// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor
|
||||
TU_ATTR_WEAK void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
|
||||
|
||||
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
|
||||
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
|
||||
TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied);
|
||||
@ -612,10 +671,12 @@ 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
|
||||
|
||||
static inline bool tud_audio_fb_set(uint32_t feedback)
|
||||
{
|
||||
return tud_audio_n_fb_set(0, feedback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -626,6 +687,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_isr (uint8_t rhport, uint32_t frame_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -134,12 +134,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { retur
|
||||
//------------- Mathematics -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; }
|
||||
|
||||
/// inclusive range checking TODO remove
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
|
||||
{
|
||||
return (lower <= value) && (value <= upper);
|
||||
}
|
||||
|
||||
// log2 of a value is its MSB's position
|
||||
// TODO use clz TODO remove
|
||||
static inline uint8_t tu_log2(uint32_t value)
|
||||
@ -149,6 +143,16 @@ static inline uint8_t tu_log2(uint32_t value)
|
||||
return result;
|
||||
}
|
||||
|
||||
//static inline uint8_t tu_log2(uint32_t value)
|
||||
//{
|
||||
// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1;
|
||||
//}
|
||||
|
||||
static inline bool tu_is_power_of_two(uint32_t value)
|
||||
{
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
//------------- Unaligned Access -------------//
|
||||
#if TUP_ARCH_STRICT_ALIGN
|
||||
|
||||
|
@ -77,6 +77,11 @@ typedef struct TU_ATTR_ALIGNED(4)
|
||||
tusb_speed_t speed;
|
||||
} bus_reset;
|
||||
|
||||
// SOF
|
||||
struct {
|
||||
uint32_t frame_count;
|
||||
}sof;
|
||||
|
||||
// SETUP_RECEIVED
|
||||
tusb_control_request_t setup_received;
|
||||
|
||||
@ -132,6 +137,9 @@ void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
|
||||
// Disconnect by disabling internal pull-up resistor on D+/D-
|
||||
void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
|
||||
|
||||
// Enable/Disable Start-of-frame interrupt. Default is disabled
|
||||
void dcd_sof_enable(uint8_t rhport, bool en);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
@ -209,6 +217,13 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr)
|
||||
{
|
||||
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF };
|
||||
event.sof.frame_count = frame_count;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -69,7 +69,7 @@ typedef struct
|
||||
volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
|
||||
uint8_t speed;
|
||||
|
||||
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
|
||||
uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
|
||||
uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
|
||||
|
||||
tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2];
|
||||
@ -98,7 +98,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = cdcd_open,
|
||||
.control_xfer_cb = cdcd_control_xfer_cb,
|
||||
.xfer_cb = cdcd_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -110,7 +110,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = mscd_open,
|
||||
.control_xfer_cb = mscd_control_xfer_cb,
|
||||
.xfer_cb = mscd_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -122,7 +122,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = hidd_open,
|
||||
.control_xfer_cb = hidd_control_xfer_cb,
|
||||
.xfer_cb = hidd_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -134,7 +134,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = audiod_open,
|
||||
.control_xfer_cb = audiod_control_xfer_cb,
|
||||
.xfer_cb = audiod_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = audiod_sof_isr
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -146,7 +146,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = videod_open,
|
||||
.control_xfer_cb = videod_control_xfer_cb,
|
||||
.xfer_cb = videod_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -158,7 +158,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.reset = midid_reset,
|
||||
.control_xfer_cb = midid_control_xfer_cb,
|
||||
.xfer_cb = midid_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -170,7 +170,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = vendord_open,
|
||||
.control_xfer_cb = tud_vendor_control_xfer_cb,
|
||||
.xfer_cb = vendord_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -182,7 +182,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = usbtmcd_open_cb,
|
||||
.control_xfer_cb = usbtmcd_control_xfer_cb,
|
||||
.xfer_cb = usbtmcd_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -194,7 +194,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = dfu_rtd_open,
|
||||
.control_xfer_cb = dfu_rtd_control_xfer_cb,
|
||||
.xfer_cb = NULL,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -206,7 +206,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = dfu_moded_open,
|
||||
.control_xfer_cb = dfu_moded_control_xfer_cb,
|
||||
.xfer_cb = NULL,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -218,7 +218,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = netd_open,
|
||||
.control_xfer_cb = netd_control_xfer_cb,
|
||||
.xfer_cb = netd_xfer_cb,
|
||||
.sof = NULL,
|
||||
.sof_isr = NULL,
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -230,7 +230,7 @@ static usbd_class_driver_t const _usbd_driver[] =
|
||||
.open = btd_open,
|
||||
.control_xfer_cb = btd_control_xfer_cb,
|
||||
.xfer_cb = btd_xfer_cb,
|
||||
.sof = NULL
|
||||
.sof_isr = NULL
|
||||
},
|
||||
#endif
|
||||
};
|
||||
@ -264,6 +264,8 @@ static inline usbd_class_driver_t const * get_driver(uint8_t drvid)
|
||||
// DCD Event
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//static tud_sof_isr_t _sof_isr = NULL;
|
||||
|
||||
enum { RHPORT_INVALID = 0xFFu };
|
||||
static uint8_t _usbd_rhport = RHPORT_INVALID;
|
||||
|
||||
@ -371,6 +373,12 @@ bool tud_connect(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
//void tud_sof_isr_set(tud_sof_isr_t sof_isr)
|
||||
//{
|
||||
// _sof_isr = sof_isr;
|
||||
// dcd_sof_enable(_usbd_rhport, _sof_isr != NULL);
|
||||
//}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Task
|
||||
//--------------------------------------------------------------------+
|
||||
@ -413,11 +421,13 @@ bool tud_init (uint8_t rhport)
|
||||
driver->init();
|
||||
}
|
||||
|
||||
_usbd_rhport = rhport;
|
||||
//_sof_isr = NULL;
|
||||
|
||||
// Init device controller driver
|
||||
dcd_init(rhport);
|
||||
dcd_int_enable(rhport);
|
||||
|
||||
_usbd_rhport = rhport;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -576,20 +586,12 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
}
|
||||
break;
|
||||
|
||||
case DCD_EVENT_SOF:
|
||||
TU_LOG2("\r\n");
|
||||
for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
|
||||
{
|
||||
usbd_class_driver_t const * driver = get_driver(i);
|
||||
if ( driver->sof ) driver->sof(event.rhport);
|
||||
}
|
||||
break;
|
||||
|
||||
case USBD_EVENT_FUNC_CALL:
|
||||
TU_LOG2("\r\n");
|
||||
if ( event.func_call.func ) event.func_call.func(event.func_call.param);
|
||||
break;
|
||||
|
||||
case DCD_EVENT_SOF:
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
break;
|
||||
@ -1105,14 +1107,30 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const * event, bool in_isr)
|
||||
break;
|
||||
|
||||
case DCD_EVENT_SOF:
|
||||
// SOF driver handler in ISR context
|
||||
for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
|
||||
{
|
||||
usbd_class_driver_t const * driver = get_driver(i);
|
||||
if (driver->sof_isr)
|
||||
{
|
||||
driver->sof_isr(event->rhport, event->sof.frame_count);
|
||||
}
|
||||
}
|
||||
|
||||
// SOF user handler in ISR context
|
||||
// if (_sof_isr) _sof_isr(event->sof.frame_count);
|
||||
|
||||
// Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
|
||||
// which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
|
||||
if ( _usbd_dev.suspended )
|
||||
{
|
||||
_usbd_dev.suspended = 0;
|
||||
|
||||
dcd_event_t const event_resume = { .rhport = event->rhport, .event_id = DCD_EVENT_RESUME };
|
||||
osal_queue_send(_usbd_q, &event_resume, in_isr);
|
||||
}
|
||||
|
||||
// skip osal queue for SOF in usbd task
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1357,4 +1375,11 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr)
|
||||
return;
|
||||
}
|
||||
|
||||
void usbd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
// TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more.
|
||||
// Only if all drivers switched off SOF calls the SOF interrupt may be disabled
|
||||
dcd_sof_enable(rhport, en);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -33,6 +33,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// typedef void (*tud_sof_isr_t) (uint32_t frame_count);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
@ -93,6 +95,10 @@ bool tud_disconnect(void);
|
||||
// Return false on unsupported MCUs
|
||||
bool tud_connect(void);
|
||||
|
||||
// Set Start-of-frame (1ms interval) IRQ handler
|
||||
// NULL means disabled, frame_count may not be supported on mcus
|
||||
// void tud_sof_isr_set(tud_sof_isr_t sof_isr);
|
||||
|
||||
// Carry out Data and Status stage of control transfer
|
||||
// - If len = 0, it is equivalent to sending status only
|
||||
// - If len > wLength : it will be truncated
|
||||
|
@ -48,7 +48,7 @@ typedef struct
|
||||
uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
|
||||
bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void (* sof ) (uint8_t rhport); /* optional */
|
||||
void (* sof_isr ) (uint8_t rhport, uint32_t frame_count); // optional
|
||||
} usbd_class_driver_t;
|
||||
|
||||
// Invoked when initializing device stack to get additional class drivers.
|
||||
@ -102,6 +102,9 @@ bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr)
|
||||
return !usbd_edpt_busy(rhport, ep_addr) && !usbd_edpt_stalled(rhport, ep_addr);
|
||||
}
|
||||
|
||||
// Enable SOF interrupt
|
||||
void usbd_sof_enable(uint8_t rhport, bool en);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Helper
|
||||
*------------------------------------------------------------------*/
|
||||
|
@ -649,6 +649,13 @@ void dcd_disconnect(uint8_t rhport)
|
||||
_ft90x_phy_enable(false);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -266,6 +266,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -882,6 +882,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
REG_CLR_BIT(USB_MCTRL_REG, USB_NAT);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
|
||||
{
|
||||
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
|
||||
|
@ -240,6 +240,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB0.dctl |= USB_SFTDISCON_M;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
@ -648,6 +648,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB0->POWER &= ~USB_POWER_SOFTCONN;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -186,6 +186,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB_REGS->POWERbits.SOFTCONN = 1;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
|
||||
{
|
||||
return (_CP0_GET_STATUS() & (_CP0_STATUS_EXL_MASK | _CP0_STATUS_IPL_MASK)) != 0;
|
||||
|
@ -180,6 +180,14 @@ void dcd_connect(uint8_t rhport)
|
||||
USB->DEVICE.CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
@ -210,6 +210,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
UDP->UDP_TXVC = UDP_TXVC_TXVDIS_Msk;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -191,6 +191,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB_REG->DEVCTRL &=~(DEVCTRL_ADDEN | DEVCTRL_UADD);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
static tusb_speed_t get_speed(void)
|
||||
{
|
||||
switch (USB_REG->SR & SR_SPEED) {
|
||||
|
@ -305,6 +305,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB_OTG_FS->CTL = 0;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -307,6 +307,14 @@ void dcd_connect(uint8_t rhport)
|
||||
NRF_USBD->USBPULLUP = 1;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -497,4 +497,12 @@ void dcd_connect(uint8_t rhport)
|
||||
usb_attach();
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -551,4 +551,12 @@ void dcd_connect(uint8_t rhport)
|
||||
usb_attach();
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -720,4 +720,12 @@ void dcd_connect(uint8_t rhport)
|
||||
usb_attach();
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -328,6 +328,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
KHCI->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -228,6 +228,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 0);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CONTROL HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -277,6 +277,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_reg->DEVCMDSTAT &= ~CMDSTAT_DEVICE_CONNECT_MASK;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DCD Endpoint Port
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -294,6 +294,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -247,6 +247,12 @@ static void dcd_rp2040_irq(void)
|
||||
uint32_t const status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
|
||||
if (status & USB_INTF_DEV_SOF_BITS)
|
||||
{
|
||||
handled |= USB_INTF_DEV_SOF_BITS;
|
||||
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
|
||||
}
|
||||
|
||||
// xfer events are handled before setup req. So if a transfer completes immediately
|
||||
// before closing the EP, the events will be delivered in same order.
|
||||
if (status & USB_INTS_BUFF_STATUS_BITS)
|
||||
@ -424,6 +430,23 @@ void dcd_connect(__unused uint8_t rhport)
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
uint32_t inte = usb_hw->inte;
|
||||
|
||||
if (en)
|
||||
{
|
||||
inte |= USB_INTS_DEV_SOF_BITS;
|
||||
}else
|
||||
{
|
||||
inte &= ~USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
|
||||
usb_hw->inte = inte;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
@ -687,6 +687,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USB0.SYSCFG.BIT.DPRPU = 0;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -247,6 +247,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
DEV_DISCONNECT(usbdev);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -287,6 +287,14 @@ void dcd_connect(uint8_t rhport)
|
||||
}
|
||||
#endif
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
// Enable device interrupt
|
||||
void dcd_int_enable (uint8_t rhport)
|
||||
{
|
||||
|
@ -602,6 +602,13 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dev->DCTL |= USB_OTG_DCTL_SDIS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
|
@ -909,6 +909,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
USBC_REG_clear_bit_b(USBC_BP_POWER_D_SOFT_CONNECT, USBC_REG_PCTL(USBC0_BASE));
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
(void)rhport;
|
||||
|
@ -93,6 +93,9 @@ static uint16_t ep0_pending[2]; // Index determines direction
|
||||
static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
|
||||
static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
|
||||
|
||||
// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
|
||||
static bool _sof_en;
|
||||
|
||||
// Calculate the RX FIFO size according to recommendations from reference manual
|
||||
static inline uint16_t calc_rx_ff_size(uint16_t ep_size)
|
||||
{
|
||||
@ -126,6 +129,8 @@ static void bus_reset(uint8_t rhport)
|
||||
tu_memclr(xfer_status, sizeof(xfer_status));
|
||||
_out_ep_closed = false;
|
||||
|
||||
_sof_en = false;
|
||||
|
||||
// clear device address
|
||||
dwc2->dcfg &= ~DCFG_DAD_Msk;
|
||||
|
||||
@ -588,6 +593,24 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dwc2->dctl |= DCTL_SDIS;
|
||||
}
|
||||
|
||||
// Be advised: audio, video and possibly other iso-ep classes use dcd_sof_enable() to enable/disable its corresponding ISR on purpose!
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
dwc2_regs_t * dwc2 = DWC2_REG(rhport);
|
||||
|
||||
_sof_en = en;
|
||||
|
||||
if (en)
|
||||
{
|
||||
dwc2->gintsts = GINTSTS_SOF;
|
||||
dwc2->gintmsk |= GINTMSK_SOFM;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
@ -1251,8 +1274,16 @@ void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
dwc2->gotgint = GINTSTS_SOF;
|
||||
|
||||
// Disable SOF interrupt since currently only used for remote wakeup detection
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
if (_sof_en)
|
||||
{
|
||||
uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
}
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
|
||||
}
|
||||
|
@ -82,6 +82,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
(void) rhport;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -222,6 +222,14 @@ void dcd_disconnect(uint8_t rhport)
|
||||
dcd_int_enable(rhport);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
@ -401,6 +401,13 @@ void dcd_disconnect(uint8_t rhport)
|
||||
usb_pullup_out_write(0);
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DCD Endpoint Port
|
||||
|
@ -296,6 +296,10 @@
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_INTERFACE_MAX
|
||||
#define CFG_TUD_INTERFACE_MAX 16
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CDC
|
||||
#define CFG_TUD_CDC 0
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user