Skip to content

Commit 49628d8

Browse files
authored
Merge pull request #1779 from P33M/rp2040_device_babble_fix
rp2040: avoid device-mode state machine hang
2 parents b03a688 + ddb061f commit 49628d8

File tree

5 files changed

+242
-104
lines changed

5 files changed

+242
-104
lines changed

hw/bsp/rp2040/family.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
116116

117117
target_compile_definitions(tinyusb_additions INTERFACE
118118
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
119+
PICO_RP2040_USB_DEVICE_UFRAME_FIX=1
119120
)
120121

121122
if(DEFINED LOG)

src/portable/raspberrypi/rp2040/dcd_rp2040.c

Lines changed: 108 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@
4646
/* Low level controller
4747
*------------------------------------------------------------------*/
4848

49-
#define usb_hw_set hw_set_alias(usb_hw)
50-
#define usb_hw_clear hw_clear_alias(usb_hw)
51-
5249
// Init these in dcd_init
5350
static uint8_t *next_buffer_ptr;
5451

@@ -247,104 +244,133 @@ static void __tusb_irq_path_func(reset_non_control_endpoints)(void)
247244

248245
static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
249246
{
250-
uint32_t const status = usb_hw->ints;
251-
uint32_t handled = 0;
252-
253-
if (status & USB_INTF_DEV_SOF_BITS)
254-
{
255-
handled |= USB_INTF_DEV_SOF_BITS;
247+
uint32_t const status = usb_hw->ints;
248+
uint32_t handled = 0;
256249

257-
// disable SOF interrupt if it is used for RESUME in remote wakeup
258-
if (!_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
250+
if ( status & USB_INTF_DEV_SOF_BITS )
251+
{
252+
bool keep_sof_alive = false;
259253

260-
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
261-
}
254+
handled |= USB_INTF_DEV_SOF_BITS;
262255

263-
// xfer events are handled before setup req. So if a transfer completes immediately
264-
// before closing the EP, the events will be delivered in same order.
265-
if (status & USB_INTS_BUFF_STATUS_BITS)
266-
{
267-
handled |= USB_INTS_BUFF_STATUS_BITS;
268-
hw_handle_buff_status();
269-
}
256+
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
257+
// Errata 15 workaround for Device Bulk-In endpoint
258+
e15_last_sof = time_us_32();
270259

271-
if (status & USB_INTS_SETUP_REQ_BITS)
260+
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++ )
272261
{
273-
handled |= USB_INTS_SETUP_REQ_BITS;
274-
uint8_t const *setup = (uint8_t const *)&usb_dpram->setup_packet;
275-
276-
// reset pid to both 1 (data and ack)
277-
reset_ep0_pid();
262+
struct hw_endpoint * ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
278263

279-
// Pass setup packet to tiny usb
280-
dcd_event_setup_received(0, setup, true);
281-
usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
282-
}
264+
// Active Bulk IN endpoint requires SOF
265+
if ( (ep->transfer_type == TUSB_XFER_BULK) && ep->active )
266+
{
267+
keep_sof_alive = true;
283268

284-
#if FORCE_VBUS_DETECT == 0
285-
// Since we force VBUS detect On, device will always think it is connected and
286-
// couldn't distinguish between disconnect and suspend
287-
if (status & USB_INTS_DEV_CONN_DIS_BITS)
288-
{
289-
handled |= USB_INTS_DEV_CONN_DIS_BITS;
269+
hw_endpoint_lock_update(ep, 1);
290270

291-
if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
292-
{
293-
// Connected: nothing to do
294-
}else
271+
// Deferred enable?
272+
if ( ep->pending )
295273
{
296-
// Disconnected
297-
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
274+
ep->pending = 0;
275+
hw_endpoint_start_next_buffer(ep);
298276
}
299277

300-
usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
278+
hw_endpoint_lock_update(ep, -1);
279+
}
301280
}
302281
#endif
303282

304-
// SE0 for 2.5 us or more (will last at least 10ms)
305-
if (status & USB_INTS_BUS_RESET_BITS)
283+
// disable SOF interrupt if it is used for RESUME in remote wakeup
284+
if ( !keep_sof_alive && !_sof_enable ) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
285+
286+
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
287+
}
288+
289+
// xfer events are handled before setup req. So if a transfer completes immediately
290+
// before closing the EP, the events will be delivered in same order.
291+
if ( status & USB_INTS_BUFF_STATUS_BITS )
292+
{
293+
handled |= USB_INTS_BUFF_STATUS_BITS;
294+
hw_handle_buff_status();
295+
}
296+
297+
if ( status & USB_INTS_SETUP_REQ_BITS )
298+
{
299+
handled |= USB_INTS_SETUP_REQ_BITS;
300+
uint8_t const * setup = (uint8_t const*) &usb_dpram->setup_packet;
301+
302+
// reset pid to both 1 (data and ack)
303+
reset_ep0_pid();
304+
305+
// Pass setup packet to tiny usb
306+
dcd_event_setup_received(0, setup, true);
307+
usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
308+
}
309+
310+
#if FORCE_VBUS_DETECT == 0
311+
// Since we force VBUS detect On, device will always think it is connected and
312+
// couldn't distinguish between disconnect and suspend
313+
if (status & USB_INTS_DEV_CONN_DIS_BITS)
314+
{
315+
handled |= USB_INTS_DEV_CONN_DIS_BITS;
316+
317+
if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
318+
{
319+
// Connected: nothing to do
320+
}else
306321
{
307-
pico_trace("BUS RESET\n");
322+
// Disconnected
323+
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
324+
}
325+
326+
usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
327+
}
328+
#endif
329+
330+
// SE0 for 2.5 us or more (will last at least 10ms)
331+
if ( status & USB_INTS_BUS_RESET_BITS )
332+
{
333+
pico_trace("BUS RESET\n");
308334

309-
handled |= USB_INTS_BUS_RESET_BITS;
335+
handled |= USB_INTS_BUS_RESET_BITS;
310336

311-
usb_hw->dev_addr_ctrl = 0;
312-
reset_non_control_endpoints();
313-
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
314-
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
337+
usb_hw->dev_addr_ctrl = 0;
338+
reset_non_control_endpoints();
339+
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
340+
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
315341

316342
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
317-
// Only run enumeration walk-around if pull up is enabled
318-
if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix();
343+
// Only run enumeration workaround if pull up is enabled
344+
if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix();
319345
#endif
320-
}
346+
}
321347

322-
/* Note from pico datasheet 4.1.2.6.4 (v1.2)
323-
* If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
324-
* the device is first connected but the bus is idle. The bus can be idle for a few ms before
325-
* the host begins sending start of frame packets. You will also see a suspend interrupt
326-
* when the device is disconnected if you do not have a VBUS detect circuit connected. This is
327-
* because without VBUS detection, it is impossible to tell the difference between
328-
* being disconnected and suspended.
329-
*/
330-
if (status & USB_INTS_DEV_SUSPEND_BITS)
331-
{
332-
handled |= USB_INTS_DEV_SUSPEND_BITS;
333-
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
334-
usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
335-
}
348+
/* Note from pico datasheet 4.1.2.6.4 (v1.2)
349+
* If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
350+
* the device is first connected but the bus is idle. The bus can be idle for a few ms before
351+
* the host begins sending start of frame packets. You will also see a suspend interrupt
352+
* when the device is disconnected if you do not have a VBUS detect circuit connected. This is
353+
* because without VBUS detection, it is impossible to tell the difference between
354+
* being disconnected and suspended.
355+
*/
356+
if ( status & USB_INTS_DEV_SUSPEND_BITS )
357+
{
358+
handled |= USB_INTS_DEV_SUSPEND_BITS;
359+
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
360+
usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
361+
}
336362

337-
if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS)
338-
{
339-
handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
340-
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
341-
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
342-
}
363+
if ( status & USB_INTS_DEV_RESUME_FROM_HOST_BITS )
364+
{
365+
handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
366+
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
367+
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
368+
}
343369

344-
if (status ^ handled)
345-
{
346-
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
347-
}
370+
if ( status ^ handled )
371+
{
372+
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
373+
}
348374
}
349375

350376
#define USB_INTS_ERROR_BITS ( \
@@ -452,7 +478,11 @@ void dcd_sof_enable(uint8_t rhport, bool en)
452478
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
453479
}else
454480
{
481+
// Don't clear immediately if the SOF workaround is in use.
482+
// The SOF handler will conditionally disable the interrupt.
483+
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
455484
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
485+
#endif
456486
}
457487
}
458488

src/portable/raspberrypi/rp2040/hcd_rp2040.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, "");
5656
static struct hw_endpoint ep_pool[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
5757
#define epx (ep_pool[0])
5858

59-
#define usb_hw_set hw_set_alias(usb_hw)
60-
#define usb_hw_clear hw_clear_alias(usb_hw)
61-
6259
// Flags we set by default in sie_ctrl (we add other bits on top)
6360
enum {
6461
SIE_CTRL_BASE = USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |

0 commit comments

Comments
 (0)