From 5b729322afbd03ca9ea16ebb6f59058d2baf8906 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 18 Nov 2024 14:16:27 +0800 Subject: [PATCH] Add rp2040 reset interface This is the same one implemented in the rp-pico SDK. See [definition](https://github.com/raspberrypi/pico-sdk/blob/2.0.0/src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c#L109) and [usage](https://github.com/raspberrypi/pico-sdk/blob/2.0.0/src/rp2_common/pico_stdio_usb/reset_interface.c). Since QMK does not use the USB descriptors from the SDK, we can't just use `#define FOO PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE` and have to implement it ourselves. Picotool writes a control transfer that arrives in `usb_request_hook_cb` in `usbp->setup` as bytes `21 01 00 00 03 00 00 00`. FWUPD also implements this same reset protocol https://github.com/fwupd/fwupd/tree/main/plugins/rp-pico Using this reset mechanism allows a standard reset into bootloader mode from where UF2 update is easy. The qmk commandline could also implement it to make debugging more seamless. Signed-off-by: Daniel Schaefer --- tmk_core/protocol/chibios/usb_main.c | 16 ++++++++++++++++ tmk_core/protocol/usb_descriptor.c | 13 +++++++++++++ tmk_core/protocol/usb_descriptor.h | 4 ++++ 3 files changed, 33 insertions(+) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 674f4c313e07..b3dab1f4a8c8 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -43,6 +43,7 @@ #include "usb_device_state.h" #include "usb_descriptor.h" #include "usb_driver.h" +#include "platforms/bootloader.h" #ifdef NKRO_ENABLE # include "keycode_config.h" @@ -651,6 +652,21 @@ static bool usb_request_hook_cb(USBDriver *usbp) { * 4,5: (LSB,MSB) wIndex * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ + if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + dprint(" TYPE_CLASS, RECIPIENT_INTERFACE\n"); + if ((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_HOST2DEV) { + if (usbp->setup[4] == RP2040_RESET_INTERFACE) { + switch (usbp->setup[1]) { /* bRequest */ + case 0x01: // RESET_REQUEST_BOOTSEL + case 0x02: // RESET_REQUEST_FLASH + dprint(" Reset interface\n"); + bootloader_jump(); + break; + } + } + } + } + /* Handle HID class specific requests */ if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 51f5f228fdb6..e6a1a71107ad 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -738,6 +738,19 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { .PollingIntervalMS = USB_POLLING_INTERVAL_MS }, #endif + .Rp2040_Reset_Interface = { + .Header = { + .Size = sizeof(USB_Descriptor_Interface_t), + .Type = DTYPE_Interface + }, + .InterfaceNumber = RP2040_RESET_INTERFACE, + .AlternateSetting = 0x00, + .TotalEndpoints = 2, + .Class = 0xFF, + .SubClass = 0x00, // RESET_INTERFACE_SUBCLASS + .Protocol = 0x01, // RESET_INTERFACE_PROTOCOL + .InterfaceStrIndex = NO_DESCRIPTOR + }, #ifdef CONSOLE_ENABLE /* diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index ddc2774e2d64..b6dd69d63357 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -92,6 +92,8 @@ typedef struct { USB_Descriptor_Endpoint_t Shared_INEndpoint; #endif + USB_Descriptor_Interface_t Rp2040_Reset_Interface; + #ifdef CONSOLE_ENABLE // Console HID Interface USB_Descriptor_Interface_t Console_Interface; @@ -173,6 +175,8 @@ enum usb_interfaces { SHARED_INTERFACE, #endif + RP2040_RESET_INTERFACE, + #ifdef CONSOLE_ENABLE CONSOLE_INTERFACE, #endif