| /** |
| * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| #include "tusb.h" |
| |
| #include "pico/bootrom.h" |
| |
| #if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE) |
| |
| #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; |
| |
| 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 |
| uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED; |
| #else |
| uint gpio_mask = 0u; |
| #endif |
| #if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED |
| if (request->wValue & 0x100) { |
| gpio_mask = 1u << (request->wValue >> 9u); |
| } |
| #endif |
| reset_usb_boot(gpio_mask, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK); |
| // 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 |
| const uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED; |
| #else |
| const uint gpio_mask = 0u; |
| #endif |
| reset_usb_boot(gpio_mask, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK); |
| } |
| } |
| #endif |
| |
| #endif |