Skip to content

Commit fdeac85

Browse files
authored
Merge pull request hathach#1381 from hathach/add-sof-isr
Add SOF IRQ Handler
2 parents ab7c23a + e384d16 commit fdeac85

File tree

38 files changed

+631
-2821
lines changed

38 files changed

+631
-2821
lines changed

src/class/audio/audio_device.c

Lines changed: 220 additions & 45 deletions
Large diffs are not rendered by default.

src/class/audio/audio_device.h

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,14 @@ TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_byte
458458
#endif
459459

460460
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
461-
TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
461+
TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id);
462+
463+
464+
// 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.
465+
466+
// 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).
467+
468+
// 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().
462469

463470
// 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.
464471
//
@@ -468,9 +475,61 @@ TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
468475
//
469476
// 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
470477
// driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format.
478+
479+
// 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.
480+
481+
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
482+
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
483+
// 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)
484+
// 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)
485+
// 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
486+
471487
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
472488
static inline bool tud_audio_fb_set(uint32_t feedback);
489+
490+
// Update feedback value with passed cycles since last time this update function is called.
491+
// Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented
492+
// This function will also call tud_audio_feedback_set()
493+
// return feedback value in 16.16 for reference (0 for error)
494+
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles);
495+
496+
enum {
497+
AUDIO_FEEDBACK_METHOD_DISABLED,
498+
AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
499+
AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
500+
AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2,
501+
502+
// impelemnt later
503+
// AUDIO_FEEDBACK_METHOD_FIFO_COUNT
504+
};
505+
506+
typedef struct {
507+
uint8_t method;
508+
uint32_t sample_freq; // sample frequency in Hz
509+
510+
union {
511+
struct {
512+
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
513+
}frequency;
514+
515+
#if 0 // implement later
516+
struct {
517+
uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready
518+
}fifo_count;
473519
#endif
520+
};
521+
}audio_feedback_params_t;
522+
523+
// Invoked when needed to set feedback parameters
524+
TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
525+
526+
// Callback in ISR context, invoked periodically according to feedback endpoint bInterval.
527+
// Could be used to compute and update feedback value
528+
// frame_number : current SOF count
529+
// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor
530+
TU_ATTR_WEAK void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
531+
532+
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
474533

475534
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
476535
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
612671
#endif
613672

614673
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
674+
615675
static inline bool tud_audio_fb_set(uint32_t feedback)
616676
{
617677
return tud_audio_n_fb_set(0, feedback);
618678
}
679+
619680
#endif
620681

621682
//--------------------------------------------------------------------+
@@ -626,6 +687,7 @@ void audiod_reset (uint8_t rhport);
626687
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
627688
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
628689
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
690+
void audiod_sof_isr (uint8_t rhport, uint32_t frame_count);
629691

630692
#ifdef __cplusplus
631693
}

src/common/tusb_common.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { retur
134134
//------------- Mathematics -------------//
135135
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; }
136136

137-
/// inclusive range checking TODO remove
138-
TU_ATTR_ALWAYS_INLINE static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
139-
{
140-
return (lower <= value) && (value <= upper);
141-
}
142-
143137
// log2 of a value is its MSB's position
144138
// TODO use clz TODO remove
145139
static inline uint8_t tu_log2(uint32_t value)
@@ -149,6 +143,16 @@ static inline uint8_t tu_log2(uint32_t value)
149143
return result;
150144
}
151145

146+
//static inline uint8_t tu_log2(uint32_t value)
147+
//{
148+
// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1;
149+
//}
150+
151+
static inline bool tu_is_power_of_two(uint32_t value)
152+
{
153+
return (value != 0) && ((value & (value - 1)) == 0);
154+
}
155+
152156
//------------- Unaligned Access -------------//
153157
#if TUP_ARCH_STRICT_ALIGN
154158

src/device/dcd.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ typedef struct TU_ATTR_ALIGNED(4)
7777
tusb_speed_t speed;
7878
} bus_reset;
7979

80+
// SOF
81+
struct {
82+
uint32_t frame_count;
83+
}sof;
84+
8085
// SETUP_RECEIVED
8186
tusb_control_request_t setup_received;
8287

@@ -132,6 +137,9 @@ void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
132137
// Disconnect by disabling internal pull-up resistor on D+/D-
133138
void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
134139

140+
// Enable/Disable Start-of-frame interrupt. Default is disabled
141+
void dcd_sof_enable(uint8_t rhport, bool en);
142+
135143
//--------------------------------------------------------------------+
136144
// Endpoint API
137145
//--------------------------------------------------------------------+
@@ -209,6 +217,13 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport
209217
dcd_event_handler(&event, in_isr);
210218
}
211219

220+
static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr)
221+
{
222+
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF };
223+
event.sof.frame_count = frame_count;
224+
dcd_event_handler(&event, in_isr);
225+
}
226+
212227
#ifdef __cplusplus
213228
}
214229
#endif

src/device/usbd.c

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ typedef struct
6969
volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
7070
uint8_t speed;
7171

72-
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
72+
uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
7373
uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
7474

7575
tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2];
@@ -98,7 +98,7 @@ static usbd_class_driver_t const _usbd_driver[] =
9898
.open = cdcd_open,
9999
.control_xfer_cb = cdcd_control_xfer_cb,
100100
.xfer_cb = cdcd_xfer_cb,
101-
.sof = NULL
101+
.sof_isr = NULL
102102
},
103103
#endif
104104

@@ -110,7 +110,7 @@ static usbd_class_driver_t const _usbd_driver[] =
110110
.open = mscd_open,
111111
.control_xfer_cb = mscd_control_xfer_cb,
112112
.xfer_cb = mscd_xfer_cb,
113-
.sof = NULL
113+
.sof_isr = NULL
114114
},
115115
#endif
116116

@@ -122,7 +122,7 @@ static usbd_class_driver_t const _usbd_driver[] =
122122
.open = hidd_open,
123123
.control_xfer_cb = hidd_control_xfer_cb,
124124
.xfer_cb = hidd_xfer_cb,
125-
.sof = NULL
125+
.sof_isr = NULL
126126
},
127127
#endif
128128

@@ -134,7 +134,7 @@ static usbd_class_driver_t const _usbd_driver[] =
134134
.open = audiod_open,
135135
.control_xfer_cb = audiod_control_xfer_cb,
136136
.xfer_cb = audiod_xfer_cb,
137-
.sof = NULL
137+
.sof_isr = audiod_sof_isr
138138
},
139139
#endif
140140

@@ -146,7 +146,7 @@ static usbd_class_driver_t const _usbd_driver[] =
146146
.open = videod_open,
147147
.control_xfer_cb = videod_control_xfer_cb,
148148
.xfer_cb = videod_xfer_cb,
149-
.sof = NULL
149+
.sof_isr = NULL
150150
},
151151
#endif
152152

@@ -158,7 +158,7 @@ static usbd_class_driver_t const _usbd_driver[] =
158158
.reset = midid_reset,
159159
.control_xfer_cb = midid_control_xfer_cb,
160160
.xfer_cb = midid_xfer_cb,
161-
.sof = NULL
161+
.sof_isr = NULL
162162
},
163163
#endif
164164

@@ -170,7 +170,7 @@ static usbd_class_driver_t const _usbd_driver[] =
170170
.open = vendord_open,
171171
.control_xfer_cb = tud_vendor_control_xfer_cb,
172172
.xfer_cb = vendord_xfer_cb,
173-
.sof = NULL
173+
.sof_isr = NULL
174174
},
175175
#endif
176176

@@ -182,7 +182,7 @@ static usbd_class_driver_t const _usbd_driver[] =
182182
.open = usbtmcd_open_cb,
183183
.control_xfer_cb = usbtmcd_control_xfer_cb,
184184
.xfer_cb = usbtmcd_xfer_cb,
185-
.sof = NULL
185+
.sof_isr = NULL
186186
},
187187
#endif
188188

@@ -194,7 +194,7 @@ static usbd_class_driver_t const _usbd_driver[] =
194194
.open = dfu_rtd_open,
195195
.control_xfer_cb = dfu_rtd_control_xfer_cb,
196196
.xfer_cb = NULL,
197-
.sof = NULL
197+
.sof_isr = NULL
198198
},
199199
#endif
200200

@@ -206,7 +206,7 @@ static usbd_class_driver_t const _usbd_driver[] =
206206
.open = dfu_moded_open,
207207
.control_xfer_cb = dfu_moded_control_xfer_cb,
208208
.xfer_cb = NULL,
209-
.sof = NULL
209+
.sof_isr = NULL
210210
},
211211
#endif
212212

@@ -218,7 +218,7 @@ static usbd_class_driver_t const _usbd_driver[] =
218218
.open = netd_open,
219219
.control_xfer_cb = netd_control_xfer_cb,
220220
.xfer_cb = netd_xfer_cb,
221-
.sof = NULL,
221+
.sof_isr = NULL,
222222
},
223223
#endif
224224

@@ -230,7 +230,7 @@ static usbd_class_driver_t const _usbd_driver[] =
230230
.open = btd_open,
231231
.control_xfer_cb = btd_control_xfer_cb,
232232
.xfer_cb = btd_xfer_cb,
233-
.sof = NULL
233+
.sof_isr = NULL
234234
},
235235
#endif
236236
};
@@ -264,6 +264,8 @@ static inline usbd_class_driver_t const * get_driver(uint8_t drvid)
264264
// DCD Event
265265
//--------------------------------------------------------------------+
266266

267+
//static tud_sof_isr_t _sof_isr = NULL;
268+
267269
enum { RHPORT_INVALID = 0xFFu };
268270
static uint8_t _usbd_rhport = RHPORT_INVALID;
269271

@@ -371,6 +373,12 @@ bool tud_connect(void)
371373
return true;
372374
}
373375

376+
//void tud_sof_isr_set(tud_sof_isr_t sof_isr)
377+
//{
378+
// _sof_isr = sof_isr;
379+
// dcd_sof_enable(_usbd_rhport, _sof_isr != NULL);
380+
//}
381+
374382
//--------------------------------------------------------------------+
375383
// USBD Task
376384
//--------------------------------------------------------------------+
@@ -413,11 +421,13 @@ bool tud_init (uint8_t rhport)
413421
driver->init();
414422
}
415423

424+
_usbd_rhport = rhport;
425+
//_sof_isr = NULL;
426+
416427
// Init device controller driver
417428
dcd_init(rhport);
418429
dcd_int_enable(rhport);
419430

420-
_usbd_rhport = rhport;
421431

422432
return true;
423433
}
@@ -576,20 +586,12 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr)
576586
}
577587
break;
578588

579-
case DCD_EVENT_SOF:
580-
TU_LOG2("\r\n");
581-
for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
582-
{
583-
usbd_class_driver_t const * driver = get_driver(i);
584-
if ( driver->sof ) driver->sof(event.rhport);
585-
}
586-
break;
587-
588589
case USBD_EVENT_FUNC_CALL:
589590
TU_LOG2("\r\n");
590591
if ( event.func_call.func ) event.func_call.func(event.func_call.param);
591592
break;
592593

594+
case DCD_EVENT_SOF:
593595
default:
594596
TU_BREAKPOINT();
595597
break;
@@ -1105,14 +1107,30 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const * event, bool in_isr)
11051107
break;
11061108

11071109
case DCD_EVENT_SOF:
1110+
// SOF driver handler in ISR context
1111+
for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
1112+
{
1113+
usbd_class_driver_t const * driver = get_driver(i);
1114+
if (driver->sof_isr)
1115+
{
1116+
driver->sof_isr(event->rhport, event->sof.frame_count);
1117+
}
1118+
}
1119+
1120+
// SOF user handler in ISR context
1121+
// if (_sof_isr) _sof_isr(event->sof.frame_count);
1122+
11081123
// Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
11091124
// which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
11101125
if ( _usbd_dev.suspended )
11111126
{
11121127
_usbd_dev.suspended = 0;
1128+
11131129
dcd_event_t const event_resume = { .rhport = event->rhport, .event_id = DCD_EVENT_RESUME };
11141130
osal_queue_send(_usbd_q, &event_resume, in_isr);
11151131
}
1132+
1133+
// skip osal queue for SOF in usbd task
11161134
break;
11171135

11181136
default:
@@ -1357,4 +1375,11 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr)
13571375
return;
13581376
}
13591377

1378+
void usbd_sof_enable(uint8_t rhport, bool en)
1379+
{
1380+
// TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more.
1381+
// Only if all drivers switched off SOF calls the SOF interrupt may be disabled
1382+
dcd_sof_enable(rhport, en);
1383+
}
1384+
13601385
#endif

0 commit comments

Comments
 (0)