diff --git a/docs/index.h b/docs/index.h index ebf1d0aed..0eff88b51 100644 --- a/docs/index.h +++ b/docs/index.h @@ -128,5 +128,6 @@ * \cond boot_picoboot_headers \defgroup boot_picoboot_headers boot_picoboot_headers \endcond * \cond boot_uf2_headers \defgroup boot_uf2_headers boot_uf2_headers \endcond * \cond pico_usb_reset_interface_headers \defgroup pico_usb_reset_interface_headers pico_usb_reset_interface_headers \endcond + * \cond pico_usb_reset \defgroup pico_usb_reset pico_usb_reset \endcond * @} */ diff --git a/src/cmake/rp2_common.cmake b/src/cmake/rp2_common.cmake index f466433cb..4a6d36092 100644 --- a/src/cmake/rp2_common.cmake +++ b/src/cmake/rp2_common.cmake @@ -124,6 +124,7 @@ if (NOT PICO_BARE_METAL) endif() pico_add_subdirectory(rp2_common/tinyusb) pico_add_subdirectory(rp2_common/pico_stdio_usb) + pico_add_subdirectory(rp2_common/pico_usb_reset) pico_add_subdirectory(rp2_common/pico_i2c_slave) # networking libraries - note dependency order is important diff --git a/src/common/pico_usb_reset_interface_headers/BUILD.bazel b/src/common/pico_usb_reset_interface_headers/BUILD.bazel index 6e827437d..12df0f6b2 100644 --- a/src/common/pico_usb_reset_interface_headers/BUILD.bazel +++ b/src/common/pico_usb_reset_interface_headers/BUILD.bazel @@ -4,6 +4,8 @@ package(default_visibility = ["//visibility:public"]) cc_library( name = "pico_usb_reset_interface_headers", - hdrs = ["include/pico/usb_reset_interface.h"], + hdrs = [ + "include/pico/usb_reset_interface.h", + ], includes = ["include"], ) diff --git a/src/common/pico_usb_reset_interface_headers/CMakeLists.txt b/src/common/pico_usb_reset_interface_headers/CMakeLists.txt index 6be755edc..f1927a890 100644 --- a/src/common/pico_usb_reset_interface_headers/CMakeLists.txt +++ b/src/common/pico_usb_reset_interface_headers/CMakeLists.txt @@ -1,6 +1,4 @@ # don't use pico_add_library here as picotool includes it directly add_library(pico_usb_reset_interface_headers INTERFACE) -add_library(pico_usb_reset_interface INTERFACE) -target_link_libraries(pico_usb_reset_interface INTERFACE pico_usb_reset_interface_headers) target_include_directories(pico_usb_reset_interface_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/src/rp2_common/pico_stdio_usb/BUILD.bazel b/src/rp2_common/pico_stdio_usb/BUILD.bazel index f554378be..5f851bc8d 100644 --- a/src/rp2_common/pico_stdio_usb/BUILD.bazel +++ b/src/rp2_common/pico_stdio_usb/BUILD.bazel @@ -56,7 +56,6 @@ cc_library( cc_library( name = "pico_stdio_usb_enabled", srcs = [ - "reset_interface.c", "stdio_usb.c", "stdio_usb_descriptors.c", ], @@ -72,10 +71,9 @@ cc_library( "//src/common/pico_binary_info", "//src/common/pico_sync", "//src/rp2_common/hardware_irq", - "//src/rp2_common/hardware_watchdog", - "//src/rp2_common/pico_bootrom", "//src/rp2_common/pico_stdio:pico_stdio_headers", "//src/rp2_common/pico_unique_id", + "//src/rp2_common/pico_usb_reset_interface", ], # Ensure `stdio_usb_descriptors.c` isn't affected by link order. alwayslink = True, diff --git a/src/rp2_common/pico_stdio_usb/CMakeLists.txt b/src/rp2_common/pico_stdio_usb/CMakeLists.txt index 8dc672182..c9770d23b 100644 --- a/src/rp2_common/pico_stdio_usb/CMakeLists.txt +++ b/src/rp2_common/pico_stdio_usb/CMakeLists.txt @@ -4,7 +4,6 @@ if (TARGET tinyusb_device_unmarked) target_include_directories(pico_stdio_usb_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) target_sources(pico_stdio_usb INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/reset_interface.c ${CMAKE_CURRENT_LIST_DIR}/stdio_usb.c ${CMAKE_CURRENT_LIST_DIR}/stdio_usb_descriptors.c ) @@ -13,7 +12,7 @@ if (TARGET tinyusb_device_unmarked) pico_stdio pico_time pico_unique_id - pico_usb_reset_interface + pico_usb_reset ) target_link_libraries(pico_stdio_usb INTERFACE tinyusb_device_unmarked diff --git a/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h b/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h index 5cf2e1f66..42aa986e6 100644 --- a/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h +++ b/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h @@ -62,20 +62,6 @@ #endif #endif -// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode if the host sets the baud rate to a magic value (PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE), type=bool, default=1 if application is not using TinyUSB directly, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE -#if !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE -#define PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE 1 -#else -#define PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE 0 -#endif -#endif - -// PICO_CONFIG: PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE, Baud rate that if selected causes a reset into BOOTSEL mode (if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE is set), default=1200, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE -#define PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE 1200 -#endif - // PICO_CONFIG: PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, Maximum number of milliseconds to wait during initialization for a CDC connection from the host (negative means indefinite) during initialization, default=0, group=pico_stdio_usb #ifndef PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS #define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 0 @@ -91,53 +77,6 @@ #define PICO_STDIO_USB_DEINIT_DELAY_MS 110 #endif -// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=47 on RP2350B, 29 otherwise, group=pico_stdio_usb - -// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW, Whether pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE) is active low, type=bool, default=0, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW -#define PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW 0 -#endif - -// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED, Whether the pin specified by PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED is fixed or can be modified by picotool over the VENDOR USB interface, type=bool, default=0, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED -#define PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED 0 -#endif - -// Any modes disabled here can't be re-enabled by picotool via VENDOR_INTERFACE. -// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=3, default=0, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK -#define PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK 0u -#endif - -// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE, Enable/disable resetting into BOOTSEL mode via an additional VENDOR USB interface - enables picotool based reset, type=bool, default=1 if application is not using TinyUSB directly, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE -#if !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE -#define PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE 1 -#else -#define PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE 0 -#endif -#endif - -// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL, If vendor reset interface is included allow rebooting to BOOTSEL mode, type=bool, default=1, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL -#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL 1 -#endif - -// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT, If vendor reset interface is included allow rebooting with regular flash boot, type=bool, default=1, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT -#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT 1 -#endif - -// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR, If vendor reset interface is included add support for Microsoft OS 2.0 Descriptor, type=bool, default=1, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR -#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR 1 -#endif - -// PICO_CONFIG: PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS, Delay in ms before rebooting via regular flash boot, default=100, group=pico_stdio_usb -#ifndef PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS -#define PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS 100 -#endif - // PICO_CONFIG: PICO_STDIO_USB_USE_DEFAULT_DESCRIPTORS, Whether `pico_stdio_usb` provides the USB descriptors needed for USB communication, type=bool, default=1 if the application is not using tinyUSB directly, group=pico_stdio_usb #ifndef PICO_STDIO_USB_USE_DEFAULT_DESCRIPTORS #if !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE diff --git a/src/rp2_common/pico_stdio_usb/include/tusb_config.h b/src/rp2_common/pico_stdio_usb/include/tusb_config.h index 0b9e4306b..302ff0ad8 100644 --- a/src/rp2_common/pico_stdio_usb/include/tusb_config.h +++ b/src/rp2_common/pico_stdio_usb/include/tusb_config.h @@ -48,14 +48,7 @@ #endif // We use a vendor specific interface but with our own driver -// Vendor driver only used for Microsoft OS 2.0 descriptor -#if !PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR #define CFG_TUD_VENDOR (0) -#else -#define CFG_TUD_VENDOR (1) -#define CFG_TUD_VENDOR_RX_BUFSIZE (256) -#define CFG_TUD_VENDOR_TX_BUFSIZE (256) -#endif #endif #endif diff --git a/src/rp2_common/pico_stdio_usb/reset_interface.c b/src/rp2_common/pico_stdio_usb/reset_interface.c deleted file mode 100644 index 061320936..000000000 --- a/src/rp2_common/pico_stdio_usb/reset_interface.c +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "tusb.h" - -#if !defined(LIB_TINYUSB_HOST) || (defined(LIB_TINYUSB_HOST) && defined(CFG_TUH_RPI_PIO_USB)) -#include "pico/bootrom.h" - -#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && !(PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL || PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT) -#warning PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE has been selected but neither PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL nor PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT have been selected. -#endif - -#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE -#include "pico/stdio_usb/reset_interface.h" -#include "hardware/watchdog.h" -#include "device/usbd_pvt.h" - -static uint8_t itf_num; - -#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR -// Support for Microsoft OS 2.0 descriptor -#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) - -#define MS_OS_20_DESC_LEN 166 -#define USBD_ITF_RPI_RESET 2 - -uint8_t const desc_bos[] = -{ - // total length, number of device caps - TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1), - - // Microsoft OS 2.0 descriptor - TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1) -}; - -TU_VERIFY_STATIC(sizeof(desc_bos) == BOS_TOTAL_LEN, "Incorrect size"); - -uint8_t const * tud_descriptor_bos_cb(void) -{ - return desc_bos; -} - -static const uint8_t desc_ms_os_20[] = -{ - // Set header: length, type, windows version, total length - U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), - - // Function Subset header: length, type, first interface, reserved, subset length - U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), USBD_ITF_RPI_RESET, 0, U16_TO_U8S_LE(0x009C), - - // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID - U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible - - // MS OS 2.0 Registry property descriptor: length, type - U16_TO_U8S_LE(0x0080), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), - U16_TO_U8S_LE(0x0001), U16_TO_U8S_LE(0x0028), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUID" in UTF-16 - 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, - 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, - U16_TO_U8S_LE(0x004E), // wPropertyDataLength - // Vendor-defined Property Data: {bc7398c1-73cd-4cb7-98b8-913a8fca7bf6} - '{', 0, 'b', 0, 'c', 0, '7', 0, '3', 0, '9', 0, - '8', 0, 'c', 0, '1', 0, '-', 0, '7', 0, '3', 0, - 'c', 0, 'd', 0, '-', 0, '4', 0, 'c', 0, 'b', 0, - '7', 0, '-', 0, '9', 0, '8', 0, 'b', 0, '8', 0, - '-', 0, '9', 0, '1', 0, '3', 0, 'a', 0, '8', 0, - 'f', 0, 'c', 0, 'a', 0, '7', 0, 'b', 0, 'f', 0, - '6', 0, '}', 0, 0, 0 -}; - -TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size"); - -bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) -{ - // nothing to with DATA & ACK stage - if (stage != CONTROL_STAGE_SETUP) return true; - - if (request->bRequest == 1 && request->wIndex == 7) { - // Get Microsoft OS 2.0 compatible descriptor - return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, sizeof(desc_ms_os_20)); - } else { - return false; - } - - // stall unknown request - return false; -} -#endif - -static void resetd_init(void) { -} - -static void resetd_reset(uint8_t __unused rhport) { - itf_num = 0; -} - -static uint16_t resetd_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass && - RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass && - RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0); - - uint16_t const drv_len = sizeof(tusb_desc_interface_t); - TU_VERIFY(max_len >= drv_len, 0); - - itf_num = itf_desc->bInterfaceNumber; - return drv_len; -} - -// Support for parameterized reset via vendor interface control request -static bool resetd_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const * request) { - // nothing to do with DATA & ACK stage - if (stage != CONTROL_STAGE_SETUP) return true; - - if (request->wIndex == itf_num) { - -#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL - if (request->bRequest == RESET_REQUEST_BOOTSEL) { -#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED - int gpio = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED; - bool active_low = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW; -#else - int gpio = -1; - bool active_low = false; -#endif -#if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED - if (request->wValue & 0x100) { - gpio = request->wValue >> 9u; - } - active_low = request->wValue & 0x200; -#endif - rom_reset_usb_boot_extra(gpio, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low); - // does not return, otherwise we'd return true - } -#endif - -#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT - if (request->bRequest == RESET_REQUEST_FLASH) { - watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS); - return true; - } -#endif - - } - return false; -} - -static bool resetd_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result, uint32_t __unused xferred_bytes) { - return true; -} - -static usbd_class_driver_t const _resetd_driver = -{ -#if CFG_TUSB_DEBUG >= 2 - .name = "RESET", -#endif - .init = resetd_init, - .reset = resetd_reset, - .open = resetd_open, - .control_xfer_cb = resetd_control_xfer_cb, - .xfer_cb = resetd_xfer_cb, - .sof = NULL -}; - -// Implement callback to add our custom driver -usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) { - *driver_count = 1; - return &_resetd_driver; -} -#endif - -#if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE -// Support for default BOOTSEL reset by changing baud rate -void tud_cdc_line_coding_cb(__unused uint8_t itf, cdc_line_coding_t const* p_line_coding) { - if (p_line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) { -#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED - int gpio = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED; - bool active_low = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW; -#else - int gpio = -1; - bool active_low = false; -#endif - rom_reset_usb_boot_extra(gpio, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low); - } -} -#endif -#endif // !defined(LIB_TINYUSB_HOST) || (defined(LIB_TINYUSB_HOST) && defined(CFG_TUH_RPI_PIO_USB)) diff --git a/src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c b/src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c index b805e8fb2..7e5b200f2 100644 --- a/src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c +++ b/src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c @@ -27,7 +27,7 @@ */ #include "pico/stdio_usb.h" -#include "pico/stdio_usb/reset_interface.h" +#include "pico/usb_reset.h" #include "pico/unique_id.h" #include "tusb.h" @@ -53,8 +53,7 @@ #define USBD_PRODUCT "Pico" #endif -#define TUD_RPI_RESET_DESC_LEN 9 -#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#if !PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #else #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_RPI_RESET_DESC_LEN) @@ -68,10 +67,11 @@ #endif #define USBD_ITF_CDC (0) // needs 2 interfaces -#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#if !PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE #define USBD_ITF_MAX (2) #else #define USBD_ITF_RPI_RESET (2) +static_assert(USBD_ITF_RPI_RESET == PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF, "USBD_ITF_RPI_RESET must be equal to PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF"); #define USBD_ITF_MAX (3) #endif @@ -97,7 +97,7 @@ static const tusb_desc_device_t usbd_desc_device = { // This is only needed for driverless access to the reset interface - the CDC interface doesn't require these descriptors // for driverless access, but will still not work if bcdUSB = 0x210 and no descriptor is provided. Therefore always // use bcdUSB = 0x200 if the Microsoft OS 2.0 descriptor isn't enabled -#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR +#if PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE && PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR .bcdUSB = 0x0210, #else .bcdUSB = 0x0200, @@ -115,10 +115,6 @@ static const tusb_desc_device_t usbd_desc_device = { .bNumConfigurations = 1, }; -#define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, _stridx, - static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE, USBD_MAX_POWER_MA), @@ -126,8 +122,8 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), -#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE - TUD_RPI_RESET_DESCRIPTOR(USBD_ITF_RPI_RESET, USBD_STR_RPI_RESET) +#if PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE + TUD_RPI_RESET_DESCRIPTOR(USBD_ITF_RPI_RESET, USBD_STR_RPI_RESET), #endif }; @@ -138,7 +134,7 @@ static const char *const usbd_desc_str[] = { [USBD_STR_PRODUCT] = USBD_PRODUCT, [USBD_STR_SERIAL] = usbd_serial_str, [USBD_STR_CDC] = "Board CDC", -#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#if PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE [USBD_STR_RPI_RESET] = "Reset", #endif }; diff --git a/src/rp2_common/pico_usb_reset/BUILD.bazel b/src/rp2_common/pico_usb_reset/BUILD.bazel new file mode 100644 index 000000000..1eb5f439b --- /dev/null +++ b/src/rp2_common/pico_usb_reset/BUILD.bazel @@ -0,0 +1,27 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("//bazel:defs.bzl", "compatible_with_rp2") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "pico_usb_reset", + srcs = [ + "usb_reset.c", + ], + hdrs = [ + "include/pico/usb_reset.h", + "include/pico/usb_reset_config.h", + "include/pico/usb_reset_tusb.h", + ], + defines = [ + "LIB_PICO_USB_RESET=1", + ], + includes = ["include"], + target_compatible_with = compatible_with_rp2(), + deps = [ + "//bazel/config:PICO_TINYUSB_LIB", + "//src/common/pico_usb_reset_interface_headers", + "//src/rp2_common/hardware_watchdog", + "//src/rp2_common/pico_bootrom", + ], +) diff --git a/src/rp2_common/pico_usb_reset/CMakeLists.txt b/src/rp2_common/pico_usb_reset/CMakeLists.txt new file mode 100644 index 000000000..b4cebe1db --- /dev/null +++ b/src/rp2_common/pico_usb_reset/CMakeLists.txt @@ -0,0 +1,13 @@ +if (TARGET tinyusb_device_unmarked) + pico_add_library(pico_usb_reset) + + target_sources(pico_usb_reset INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/usb_reset.c + ) + + target_include_directories(pico_usb_reset_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) + + target_link_libraries(pico_usb_reset INTERFACE tinyusb_device_unmarked) + + target_link_libraries(pico_usb_reset_headers INTERFACE pico_usb_reset_interface_headers) +endif() diff --git a/src/rp2_common/pico_usb_reset/include/pico/usb_reset.h b/src/rp2_common/pico_usb_reset/include/pico/usb_reset.h new file mode 100644 index 000000000..883873b43 --- /dev/null +++ b/src/rp2_common/pico_usb_reset/include/pico/usb_reset.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_USB_RESET_H +#define _PICO_USB_RESET_H + +/** \file usb_reset.h + * \defgroup pico_usb_reset pico_usb_reset + * + * \brief Functionality to enable the RP-series microcontroller to be reset over the USB interface. + * + * This library can be used to enable the RP-series microcontroller to be reset over the USB interface. + * + * This functionality is included by default when using the `pico_stdio_usb` library and not using TinyUSB directly. + * + * To add this functionality to a project using TinyUSB directly, you need to: + * 1. Link the pico_usb_reset library, and include the `pico/usb_reset.h` header file where needed. + * 2. Define PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE=1 + * 3. Add `TUD_RPI_RESET_DESCRIPTOR(, )` to your USB descriptors (length is `TUD_RPI_RESET_DESC_LEN`) + * 4. Check if your project has an existing `usbd_app_driver_get_cb` function: + * - If it does, you need to add the `usb_reset_interface_driver` to the drivers returned + * - If it does not, and you aren't using the `pico_stdio_usb` library, you need to define `PICO_USB_RESET_INCLUDE_DEFAULT_APP_DRIVER_CB=1` + * 5. Check if your project has an existing Microsoft OS 2.0 Descriptor: + * - If it does, you need to add the Function Subset header `RPI_RESET_MS_OS_20_DESCRIPTOR()` to your Microsoft OS 2.0 Descriptor (length is `RPI_RESET_MS_OS_20_DESC_LEN`) + * - If it does not, you need to define `PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR=1` and `PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF=` + */ + +#include "pico/usb_reset_interface.h" +#include "pico/usb_reset_config.h" +#include "pico/usb_reset_tusb.h" + +#endif \ No newline at end of file diff --git a/src/rp2_common/pico_usb_reset/include/pico/usb_reset_config.h b/src/rp2_common/pico_usb_reset/include/pico/usb_reset_config.h new file mode 100644 index 000000000..6084cdb0e --- /dev/null +++ b/src/rp2_common/pico_usb_reset/include/pico/usb_reset_config.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_USB_RESET_CONFIG_H +#define _PICO_USB_RESET_CONFIG_H + +// PICO_CONFIG: PICO_ENABLE_USB_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode if the host sets the baud rate to a magic value (PICO_USB_RESET_MAGIC_BAUD_RATE), type=bool, default=1 if application is not using TinyUSB directly, group=pico_usb_reset +#ifndef PICO_ENABLE_USB_RESET_VIA_BAUD_RATE +#ifdef PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE // backwards compatibility with SDK <= 2.2.0 +#define PICO_ENABLE_USB_RESET_VIA_BAUD_RATE PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE +#elif !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE +#define PICO_ENABLE_USB_RESET_VIA_BAUD_RATE 1 +#else +#define PICO_ENABLE_USB_RESET_VIA_BAUD_RATE 0 +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_MAGIC_BAUD_RATE, Baud rate that if selected causes a reset into BOOTSEL mode (if PICO_ENABLE_USB_RESET_VIA_BAUD_RATE is set), default=1200, group=pico_usb_reset +#ifndef PICO_USB_RESET_MAGIC_BAUD_RATE +#ifdef PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_MAGIC_BAUD_RATE PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE +#else +#define PICO_USB_RESET_MAGIC_BAUD_RATE 1200 +#endif +#endif + +// PICO_CONFIG: PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE, Enable/disable resetting into BOOTSEL mode via an additional VENDOR USB interface - enables picotool based reset, type=bool, default=1 if application is not using TinyUSB directly, group=pico_usb_reset +#ifndef PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE +#ifdef PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE // backwards compatibility with SDK <= 2.2.0 +#define PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#elif !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE +#define PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE 1 +#else +#define PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE 0 +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR, If vendor reset interface is included add support for Microsoft OS 2.0 Descriptor, type=bool, default=1 if application is not using TinyUSB directly, 0 otherwise, group=pico_usb_reset +#ifndef PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR +#ifdef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR +#elif !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE +#define PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR 1 +#else +#define PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR 0 +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF, If vendor reset interface is included this specifies the USB interface number for the reset interface, type=int, default=2 if application is not using TinyUSB directly, undefined otherwise, group=pico_usb_reset +#ifndef PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF +#if !LIB_TINYUSB_HOST && !LIB_TINYUSB_DEVICE +#define PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF 2 +#elif PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR +#error Must set PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF to the reset interface number when using PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR with custom USB descriptors +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_INCLUDE_DEFAULT_APP_DRIVER_CB, Set to 0 if your application defines its own usbd_app_driver_get_cb function, type=bool, default=1 when using pico_stdio_usb, 0 otherwise, group=pico_usb_reset +#ifndef PICO_USB_RESET_INCLUDE_DEFAULT_APP_DRIVER_CB +#define PICO_USB_RESET_INCLUDE_DEFAULT_APP_DRIVER_CB LIB_PICO_STDIO_USB +#endif + +// PICO_CONFIG: PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL, If vendor reset interface is included allow rebooting to BOOTSEL mode, type=bool, default=1, group=pico_usb_reset +#ifndef PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL +#ifdef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL +#else +#define PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL 1 +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_BOOTSEL_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=47 on RP2350B, 29 otherwise, group=pico_usb_reset +#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_BOOTSEL_ACTIVITY_LED PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED +#endif // default is undefined + +// PICO_CONFIG: PICO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW, Whether pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE) is active low (ignored on RP2040), type=bool, default=0, group=pico_usb_reset +#ifndef PICO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW +#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW +#else +#define PICO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW 0 +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED, Whether the pin specified by PICO_USB_RESET_BOOTSEL_ACTIVITY_LED is fixed or can be modified by picotool over the VENDOR USB interface, type=bool, default=0, group=pico_usb_reset +#ifndef PICO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED +#ifdef PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED +#else +#define PICO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED 0 +#endif +#endif + +// Any modes disabled here can't be re-enabled by picotool via VENDOR_INTERFACE. +// PICO_CONFIG: PICO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=3, default=0, group=pico_usb_reset +#ifndef PICO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK +#ifdef PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK +#else +#define PICO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK 0u +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT, If vendor reset interface is included allow rebooting with regular flash boot, type=bool, default=1, group=pico_usb_reset +#ifndef PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT +#ifdef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT +#else +#define PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT 1 +#endif +#endif + +// PICO_CONFIG: PICO_USB_RESET_RESET_TO_FLASH_DELAY_MS, Delay in ms before rebooting via regular flash boot, default=100, group=pico_usb_reset +#ifndef PICO_USB_RESET_RESET_TO_FLASH_DELAY_MS +#ifdef PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS // backwards compatibility with SDK <= 2.2.0 +#define PICO_USB_RESET_RESET_TO_FLASH_DELAY_MS PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS +#else +#define PICO_USB_RESET_RESET_TO_FLASH_DELAY_MS 100 +#endif +#endif + +#endif \ No newline at end of file diff --git a/src/rp2_common/pico_usb_reset/include/pico/usb_reset_tusb.h b/src/rp2_common/pico_usb_reset/include/pico/usb_reset_tusb.h new file mode 100644 index 000000000..1ef73d7a4 --- /dev/null +++ b/src/rp2_common/pico_usb_reset/include/pico/usb_reset_tusb.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_USB_RESET_TUSB_H +#define _PICO_USB_RESET_TUSB_H + +// Interface descriptor +#define TUD_RPI_RESET_DESC_LEN 9 +#define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \ + /* Interface */\ + TUD_RPI_RESET_DESC_LEN, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, _stridx + + +// Microsoft OS 2.0 Descriptor +#define RPI_RESET_MS_OS_20_DESC_LEN (0x08 + 0x14 + 0x80) +#define RPI_RESET_MS_OS_20_DESCRIPTOR(itf_num) \ + /* Function Subset header: length, type, first interface, reserved, subset length */ \ + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), itf_num, 0, U16_TO_U8S_LE(RPI_RESET_MS_OS_20_DESC_LEN), \ + \ + /* MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID */ \ + U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* sub-compatible */ \ + \ + /* MS OS 2.0 Registry property descriptor: length, type */ \ + U16_TO_U8S_LE(0x0080), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), \ + U16_TO_U8S_LE(0x0001), U16_TO_U8S_LE(0x0028), /* wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUID" in UTF-16 */ \ + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, \ + 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, \ + U16_TO_U8S_LE(0x004E), /* wPropertyDataLength */ \ + /* Vendor-defined Property Data: {bc7398c1-73cd-4cb7-98b8-913a8fca7bf6} */ \ + '{', 0, 'b', 0, 'c', 0, '7', 0, '3', 0, '9', 0, \ + '8', 0, 'c', 0, '1', 0, '-', 0, '7', 0, '3', 0, \ + 'c', 0, 'd', 0, '-', 0, '4', 0, 'c', 0, 'b', 0, \ + '7', 0, '-', 0, '9', 0, '8', 0, 'b', 0, '8', 0, \ + '-', 0, '9', 0, '1', 0, '3', 0, 'a', 0, '8', 0, \ + 'f', 0, 'c', 0, 'a', 0, '7', 0, 'b', 0, 'f', 0, \ + '6', 0, '}', 0, 0, 0 + +#include "stdint.h" +#include "device/usbd_pvt.h" + +void usb_reset_interface_init(void); +void usb_reset_interface_reset(uint8_t __unused rhport); +uint16_t usb_reset_interface_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool usb_reset_interface_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const * request); +bool usb_reset_interface_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result, uint32_t __unused xferred_bytes); + +static usbd_class_driver_t const usb_reset_interface_driver = +{ +#if CFG_TUSB_DEBUG >= 2 + .name = "RESET", +#endif + .init = usb_reset_interface_init, + .reset = usb_reset_interface_reset, + .open = usb_reset_interface_open, + .control_xfer_cb = usb_reset_interface_control_xfer_cb, + .xfer_cb = usb_reset_interface_xfer_cb, + .sof = NULL +}; + +#endif \ No newline at end of file diff --git a/src/rp2_common/pico_usb_reset/usb_reset.c b/src/rp2_common/pico_usb_reset/usb_reset.c new file mode 100644 index 000000000..f40a16969 --- /dev/null +++ b/src/rp2_common/pico_usb_reset/usb_reset.c @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "tusb.h" + +#if !defined(LIB_TINYUSB_HOST) || (defined(LIB_TINYUSB_HOST) && defined(CFG_TUH_RPI_PIO_USB)) +#include "pico/bootrom.h" +#include "pico/usb_reset.h" + +#if PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE && !(PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL || PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT) +#warning PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE has been selected but neither PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL nor PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT have been selected. +#endif + +#if PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE +#include "hardware/watchdog.h" + +static uint8_t itf_num; + +#if PICO_USB_RESET_SUPPORT_MS_OS_20_DESCRIPTOR +// Support for Microsoft OS 2.0 descriptor +#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) + +#define MS_OS_20_DESC_LEN (RPI_RESET_MS_OS_20_DESC_LEN + 0x0A) + +uint8_t const desc_bos[] = +{ + // total length, number of device caps + TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1), + + // Microsoft OS 2.0 descriptor + TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1) +}; + +TU_VERIFY_STATIC(sizeof(desc_bos) == BOS_TOTAL_LEN, "Incorrect size"); + +uint8_t const * tud_descriptor_bos_cb(void) +{ + return desc_bos; +} + +static const uint8_t desc_ms_os_20[] = +{ + // Set header: length, type, windows version, total length + U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), + + RPI_RESET_MS_OS_20_DESCRIPTOR(PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF) +}; + +TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size"); + +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + // nothing to with DATA & ACK stage + if (stage != CONTROL_STAGE_SETUP) return true; + + if (request->bRequest == 1 && request->wIndex == 7) { + // Get Microsoft OS 2.0 compatible descriptor + return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, sizeof(desc_ms_os_20)); + } else { + return false; + } + + // stall unknown request + return false; +} +#endif + +void usb_reset_interface_init(void) { +} + +void usb_reset_interface_reset(uint8_t __unused rhport) { + itf_num = 0; +} + +uint16_t usb_reset_interface_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass && + RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass && + RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0); + + uint16_t const drv_len = sizeof(tusb_desc_interface_t); + TU_VERIFY(max_len >= drv_len, 0); + + itf_num = itf_desc->bInterfaceNumber; + return drv_len; +} + +// Support for parameterized reset via vendor interface control request +bool usb_reset_interface_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const * request) { + // nothing to do with DATA & ACK stage + if (stage != CONTROL_STAGE_SETUP) return true; + + if (request->wIndex == itf_num) { + +#if PICO_USB_RESET_SUPPORT_RESET_TO_BOOTSEL + if (request->bRequest == RESET_REQUEST_BOOTSEL) { +#ifdef PICO_USB_RESET_BOOTSEL_ACTIVITY_LED + int gpio = PICO_USB_RESET_BOOTSEL_ACTIVITY_LED; + bool active_low = PICO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW; +#else + int gpio = -1; + bool active_low = false; +#endif +#if !PICO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED + // wValue layout: + // bits 0-1 : forwarded as the bootrom disable_interface_mask + // bit 7 : 1 if the activity-LED GPIO is active-low + // bit 8 : 1 if an activity-LED GPIO is being specified + // bits 9-15 : activity-LED GPIO number (0-127) + if (request->wValue & (1u << 8)) { + active_low = request->wValue & (1u << 7); + gpio = request->wValue >> 9u; + } +#endif + rom_reset_usb_boot_extra(gpio, (request->wValue & 0x3) | PICO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low); + // does not return, otherwise we'd return true + } +#endif + +#if PICO_USB_RESET_SUPPORT_RESET_TO_FLASH_BOOT + if (request->bRequest == RESET_REQUEST_FLASH) { + watchdog_reboot(0, 0, PICO_USB_RESET_RESET_TO_FLASH_DELAY_MS); + return true; + } +#endif + + } + return false; +} + +bool usb_reset_interface_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result, uint32_t __unused xferred_bytes) { + return true; +} + +#if PICO_USB_RESET_INCLUDE_DEFAULT_APP_DRIVER_CB +// Implement callback to add our custom driver +usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) { + *driver_count = 1; + return &usb_reset_interface_driver; +} +#endif +#endif + +#if PICO_ENABLE_USB_RESET_VIA_BAUD_RATE +// Support for default BOOTSEL reset by changing baud rate +void tud_cdc_line_coding_cb(__unused uint8_t itf, cdc_line_coding_t const* p_line_coding) { + if (p_line_coding->bit_rate == PICO_USB_RESET_MAGIC_BAUD_RATE) { +#ifdef PICO_USB_RESET_BOOTSEL_ACTIVITY_LED + int gpio = PICO_USB_RESET_BOOTSEL_ACTIVITY_LED; + bool active_low = PICO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW; +#else + int gpio = -1; + bool active_low = false; +#endif + rom_reset_usb_boot_extra(gpio, PICO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low); + } +} +#endif +#endif // !defined(LIB_TINYUSB_HOST) || (defined(LIB_TINYUSB_HOST) && defined(CFG_TUH_RPI_PIO_USB))