Skip to content

Commit 7d26fe9

Browse files
committed
factor out timed transfer setup & err handling
The code for endpoint transfers and control transfers previously had a bunch of duplicated logic for setup, timeout checking, and result code error handling. This change factors that stuff out into functions, using my new transfer result error handling code from the last commit. Now the endpoint and control transfers can share the setup, timeout, and error check logic. For me, this refactor reduced the firmware image size by 176 bytes.
1 parent f3560f9 commit 7d26fe9

File tree

1 file changed

+57
-71
lines changed

1 file changed

+57
-71
lines changed

shared-module/usb/core/Device.c

Lines changed: 57 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,59 @@ static bool _wait_for_callback(void) {
101101
return result == XFER_RESULT_SUCCESS;
102102
}
103103

104+
static void _prepare_for_transfer(void) {
105+
// Prepare for transfer. Unless there is a timeout, these static globals will
106+
// get modified by the _transfer_done_cb() callback when tinyusb finishes the
107+
// transfer or encounters an error condition.
108+
_xfer_result = XFER_RESULT_INVALID;
109+
_actual_len = 0;
110+
}
111+
112+
static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout) {
113+
if (xfer == NULL) {
114+
mp_raise_usb_core_USBError(NULL);
115+
return 0;
116+
}
117+
uint32_t start_time = supervisor_ticks_ms32();
118+
while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) &&
119+
!mp_hal_is_interrupted() &&
120+
_xfer_result == XFER_RESULT_INVALID) {
121+
// The background tasks include TinyUSB which will call the function
122+
// we provided above. In other words, the callback isn't in an interrupt.
123+
RUN_BACKGROUND_TASKS;
124+
}
125+
if (mp_hal_is_interrupted()) {
126+
// Handle case of VM being interrupted by Ctrl-C or autoreload
127+
tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
128+
return 0;
129+
}
130+
// Handle control transfer result code from TinyUSB
131+
xfer_result_t result = _xfer_result;
132+
_xfer_result = XFER_RESULT_INVALID;
133+
switch (result) {
134+
case XFER_RESULT_SUCCESS:
135+
return _actual_len;
136+
case XFER_RESULT_FAILED:
137+
mp_raise_usb_core_USBError(NULL);
138+
break;
139+
case XFER_RESULT_STALLED:
140+
mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error"));
141+
break;
142+
case XFER_RESULT_TIMEOUT:
143+
// This timeout comes from TinyUSB, so assume that it has stopped the
144+
// transfer (note: timeout logic may be unimplemented on TinyUSB side)
145+
mp_raise_usb_core_USBTimeoutError();
146+
break;
147+
case XFER_RESULT_INVALID:
148+
// This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB
149+
// to stop the transfer
150+
tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
151+
mp_raise_usb_core_USBTimeoutError();
152+
break;
153+
}
154+
return 0;
155+
}
156+
104157
static mp_obj_t _get_string(const uint16_t *temp_buf) {
105158
size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
106159
if (utf16_len == 0) {
@@ -225,38 +278,13 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m
225278
}
226279

227280
static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) {
228-
_xfer_result = XFER_RESULT_INVALID;
281+
_prepare_for_transfer();
229282
xfer->complete_cb = _transfer_done_cb;
230283
if (!tuh_edpt_xfer(xfer)) {
231284
mp_raise_usb_core_USBError(NULL);
232285
return 0;
233286
}
234-
uint32_t start_time = supervisor_ticks_ms32();
235-
while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) &&
236-
!mp_hal_is_interrupted() &&
237-
_xfer_result == XFER_RESULT_INVALID) {
238-
// The background tasks include TinyUSB which will call the function
239-
// we provided above. In other words, the callback isn't in an interrupt.
240-
RUN_BACKGROUND_TASKS;
241-
}
242-
if (mp_hal_is_interrupted()) {
243-
tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
244-
return 0;
245-
}
246-
xfer_result_t result = _xfer_result;
247-
_xfer_result = XFER_RESULT_INVALID;
248-
if (result == XFER_RESULT_STALLED) {
249-
mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error"));
250-
}
251-
if (result == XFER_RESULT_INVALID) {
252-
tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr);
253-
mp_raise_usb_core_USBTimeoutError();
254-
}
255-
if (result == XFER_RESULT_SUCCESS) {
256-
return _actual_len;
257-
}
258-
259-
return 0;
287+
return _handle_timed_transfer_callback(xfer, timeout);
260288
}
261289

262290
static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) {
@@ -355,54 +383,12 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
355383
.complete_cb = _transfer_done_cb,
356384
};
357385

358-
// Prepare for transfer. Unless there is a timeout, these static globals will
359-
// get modified by the _transfer_done_cb() callback when tinyusb finishes the
360-
// transfer or encounters an error condition.
361-
_xfer_result = XFER_RESULT_INVALID;
362-
_actual_len = 0;
363-
386+
_prepare_for_transfer();
364387
if (!tuh_control_xfer(&xfer)) {
365388
mp_raise_usb_core_USBError(NULL);
366389
return 0;
367390
}
368-
uint32_t start_time = supervisor_ticks_ms32();
369-
while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) &&
370-
!mp_hal_is_interrupted() &&
371-
_xfer_result == XFER_RESULT_INVALID) {
372-
// The background tasks include TinyUSB which will call the function
373-
// we provided above. In other words, the callback isn't in an interrupt.
374-
RUN_BACKGROUND_TASKS;
375-
}
376-
if (mp_hal_is_interrupted()) {
377-
// Handle case of VM being interrupted by Ctrl-C or autoreload
378-
tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr);
379-
return 0;
380-
}
381-
// Handle control transfer result code from TinyUSB
382-
xfer_result_t result = _xfer_result;
383-
_xfer_result = XFER_RESULT_INVALID;
384-
switch (result) {
385-
case XFER_RESULT_SUCCESS:
386-
return _actual_len;
387-
case XFER_RESULT_FAILED:
388-
mp_raise_usb_core_USBError(NULL);
389-
break;
390-
case XFER_RESULT_STALLED:
391-
mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error"));
392-
break;
393-
case XFER_RESULT_TIMEOUT:
394-
// This timeout comes from TinyUSB, so assume that it has stopped the
395-
// transfer (note: timeout logic may be unimplemented on TinyUSB side)
396-
mp_raise_usb_core_USBTimeoutError();
397-
break;
398-
case XFER_RESULT_INVALID:
399-
// This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB
400-
// to stop the transfer
401-
tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr);
402-
mp_raise_usb_core_USBTimeoutError();
403-
break;
404-
}
405-
return 0;
391+
return (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout);
406392
}
407393

408394
bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) {

0 commit comments

Comments
 (0)