Compare commits

...

4 Commits

Author SHA1 Message Date
Jonathan Bell
f030323119 main: handle RP2040's broken USB error handling
RP2040-E15 can also be triggered if a Debug Probe is connected to a
board with a floating ground. Typically this causes port ESD protection
to temporarily activate, meaning the Dp/Dm state gets corrupted. If this
happens in the middle of a handshake packet, the SIE can lock up.

The only way to detect this case is if SOF_RD stops advancing without a
corresponding suspend interrupt - so add a watchdog thread that forces a
disconnect if the hardware stops reporting frame counts.

This is disruptive, but immediate notification that the probe broke is
preferable to silently failing until the next character is sent by the host.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2024-11-14 16:00:26 +00:00
Jonathan Bell
89c9a7711f main: add callbacks for discrete USB states
We can save more power by parking threads when suspended, and threads
should be deleted when disconnected.

Also fix an inefficiency in usb_thread wakeups when the device is yet to
be configured, but is addressed - slowing down control transfers.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2024-11-14 15:54:20 +00:00
Jonathan Bell
451513d4f6 dap_edpt_driver: handle deinit properly
Zap pending buffers if the interface went away.
Also fix -Wformat warnings.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2024-11-14 15:51:00 +00:00
Jonathan Bell
ba204d0c24 cdc_uart: be more careful about when the thread gets parked or resumed
tud_cdc_connected tests dtr, not rts - so we should do the same.

Don't unconditionally wake the uart thread when set_line_coding happens
- Windows frequently calls this after every linestate change, including
device close.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
2024-11-14 15:46:51 +00:00
3 changed files with 92 additions and 13 deletions

View File

@@ -26,7 +26,6 @@
#include <pico/stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
#include "tusb.h"
#include "probe_config.h"
@@ -191,7 +190,8 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
*/
uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(line_coding->bit_rate, 1);
/* Modifying state, so park the thread before changing it. */
vTaskSuspend(uart_taskhandle);
if (tud_cdc_connected())
vTaskSuspend(uart_taskhandle);
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS));
probe_info("New baud rate %ld micros %ld interval %lu\n",
@@ -245,7 +245,10 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
}
uart_set_format(PROBE_UART_INTERFACE, data_bits, stop_bits, parity);
vTaskResume(uart_taskhandle);
/* Windows likes to arbitrarily set/get line coding after dtr/rts changes, so
* don't resume if we shouldn't */
if(tud_cdc_connected())
vTaskResume(uart_taskhandle);
}
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
@@ -259,7 +262,7 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
/* CDC drivers use linestate as a bodge to activate/deactivate the interface.
* Resume our UART polling on activate, stop on deactivate */
if (!dtr && !rts) {
if (!dtr) {
vTaskSuspend(uart_taskhandle);
#ifdef PROBE_UART_RX_LED
gpio_put(PROBE_UART_RX_LED, 0);

View File

@@ -45,6 +45,7 @@
#include "led.h"
#include "tusb_edpt_handler.h"
#include "DAP.h"
#include "hardware/structs/usb.h"
// UART0 for debugprobe debug
// UART1 for debugprobe to target device
@@ -58,7 +59,36 @@ static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2)
#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1)
TaskHandle_t dap_taskhandle, tud_taskhandle;
TaskHandle_t dap_taskhandle, tud_taskhandle, mon_taskhandle;
void dev_mon(void *ptr)
{
uint32_t sof[3];
int i = 0;
TickType_t wake;
wake = xTaskGetTickCount();
do {
/* ~5 SOF events per tick */
xTaskDelayUntil(&wake, 100);
if (tud_connected() && !tud_suspended()) {
sof[i++] = usb_hw->sof_rd & USB_SOF_RD_BITS;
i = i % 3;
} else {
for (i = 0; i < 3; i++)
sof[i] = 0;
}
if ((sof[0] | sof[1] | sof[2]) != 0) {
if ((sof[0] == sof[1]) && (sof[1] == sof[2])) {
probe_info("Watchdog timeout! Resetting USBD\n");
/* uh oh, signal disconnect (implicitly resets the controller) */
tud_deinit(0);
/* Make sure the port got the message */
xTaskDelayUntil(&wake, 1);
tud_init(0);
}
}
} while (1);
}
void usb_thread(void *ptr)
{
@@ -72,8 +102,11 @@ void usb_thread(void *ptr)
else
gpio_put(PROBE_USB_CONNECTED_LED, 0);
#endif
// If suspended or disconnected, delay for 1ms (20 ticks)
if (tud_suspended() || !tud_connected())
xTaskDelayUntil(&wake, 20);
// Go to sleep for up to a tick if nothing to do
if (!tud_task_event_ready())
else if (!tud_task_event_ready())
xTaskDelayUntil(&wake, 1);
} while (1);
}
@@ -100,11 +133,10 @@ int main(void) {
probe_info("Welcome to debugprobe!\n");
if (THREADED) {
/* UART needs to preempt USB as if we don't, characters get lost */
xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
xTaskCreate(usb_thread, "TUD", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &tud_taskhandle);
/* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
#if PICO_RP2040
xTaskCreate(dev_mon, "WDOG", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &mon_taskhandle);
#endif
vTaskStartScheduler();
}
@@ -188,6 +220,40 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
}
#endif
void tud_suspend_cb(bool remote_wakeup_en)
{
probe_info("Suspended\n");
/* Join DAP and UART threads? Or just suspend them, for transparency */
vTaskSuspend(uart_taskhandle);
vTaskSuspend(dap_taskhandle);
/* slow down clk_sys for power saving ? */
}
void tud_resume_cb(void)
{
probe_info("Resumed\n");
vTaskResume(uart_taskhandle);
vTaskResume(dap_taskhandle);
}
void tud_unmount_cb(void)
{
probe_info("Disconnected\n");
vTaskSuspend(uart_taskhandle);
vTaskSuspend(dap_taskhandle);
vTaskDelete(uart_taskhandle);
vTaskDelete(dap_taskhandle);
}
void tud_mount_cb(void)
{
probe_info("Connected, Configured\n");
/* UART needs to preempt USB as if we don't, characters get lost */
xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
/* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
}
void vApplicationTickHook (void)
{
};

View File

@@ -41,6 +41,15 @@ void dap_edpt_init(void) {
}
bool dap_edpt_deinit(void)
{
memset(DAPRequestBuffer, 0, sizeof(DAPRequestBuffer));
memset(DAPResponseBuffer, 0, sizeof(DAPResponseBuffer));
USBRequestBuffer.wptr = USBRequestBuffer.rptr = 0;
USBResponseBuffer.wptr = USBResponseBuffer.rptr = 0;
return true;
}
void dap_edpt_reset(uint8_t __unused rhport)
{
itf_num = 0;
@@ -198,7 +207,7 @@ void dap_thread(void *ptr)
*/
n = USBRequestBuffer.rptr;
while (USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] == ID_DAP_QueueCommands) {
probe_info("%u %u DAP queued cmd %s len %02x\n",
probe_info("%lu %lu DAP queued cmd %s len %02x\n",
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
dap_cmd_string[USBRequestBuffer.data[n % DAP_PACKET_COUNT][0]], USBRequestBuffer.data[n % DAP_PACKET_COUNT][1]);
USBRequestBuffer.data[n % DAP_PACKET_COUNT][0] = ID_DAP_ExecuteCommands;
@@ -211,7 +220,7 @@ void dap_thread(void *ptr)
}
// Read a single packet from the USB buffer into the DAP Request buffer
memcpy(DAPRequestBuffer, RD_SLOT_PTR(USBRequestBuffer), DAP_PACKET_SIZE);
probe_info("%u %u DAP cmd %s len %02x\n",
probe_info("%lu %lu DAP cmd %s len %02x\n",
USBRequestBuffer.wptr, USBRequestBuffer.rptr,
dap_cmd_string[DAPRequestBuffer[0]], DAPRequestBuffer[1]);
USBRequestBuffer.rptr++;
@@ -227,7 +236,7 @@ void dap_thread(void *ptr)
}
_resp_len = DAP_ExecuteCommand(DAPRequestBuffer, DAPResponseBuffer);
probe_info("%u %u DAP resp %s\n",
probe_info("%lu %lu DAP resp %s\n",
USBResponseBuffer.wptr, USBResponseBuffer.rptr,
dap_cmd_string[DAPResponseBuffer[0]]);
@@ -262,6 +271,7 @@ void dap_thread(void *ptr)
usbd_class_driver_t const _dap_edpt_driver =
{
.init = dap_edpt_init,
.deinit = dap_edpt_deinit,
.reset = dap_edpt_reset,
.open = dap_edpt_open,
.control_xfer_cb = dap_edpt_control_xfer_cb,