1 /**
2  * Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 #include "tusb.h"
7 
8 #include "pico/bootrom.h"
9 
10 #if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE)
11 
12 #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)
13 #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.
14 #endif
15 
16 #if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
17 #include "pico/stdio_usb/reset_interface.h"
18 #include "hardware/watchdog.h"
19 #include "device/usbd_pvt.h"
20 
21 static uint8_t itf_num;
22 
resetd_init(void)23 static void resetd_init(void) {
24 }
25 
resetd_reset(uint8_t __unused rhport)26 static void resetd_reset(uint8_t __unused rhport) {
27     itf_num = 0;
28 }
29 
resetd_open(uint8_t __unused rhport,tusb_desc_interface_t const * itf_desc,uint16_t max_len)30 static uint16_t resetd_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
31     TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
32               RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
33               RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
34 
35     uint16_t const drv_len = sizeof(tusb_desc_interface_t);
36     TU_VERIFY(max_len >= drv_len, 0);
37 
38     itf_num = itf_desc->bInterfaceNumber;
39     return drv_len;
40 }
41 
42 // Support for parameterized reset via vendor interface control request
resetd_control_xfer_cb(uint8_t __unused rhport,uint8_t stage,tusb_control_request_t const * request)43 static bool resetd_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const * request) {
44     // nothing to do with DATA & ACK stage
45     if (stage != CONTROL_STAGE_SETUP) return true;
46 
47     if (request->wIndex == itf_num) {
48 
49 #if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
50         if (request->bRequest == RESET_REQUEST_BOOTSEL) {
51 #ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
52             uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
53 #else
54             uint gpio_mask = 0u;
55 #endif
56 #if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED
57             if (request->wValue & 0x100) {
58                 gpio_mask = 1u << (request->wValue >> 9u);
59             }
60 #endif
61             reset_usb_boot(gpio_mask, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK);
62             // does not return, otherwise we'd return true
63         }
64 #endif
65 
66 #if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT
67         if (request->bRequest == RESET_REQUEST_FLASH) {
68             watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS);
69             return true;
70         }
71 #endif
72 
73     }
74     return false;
75 }
76 
resetd_xfer_cb(uint8_t __unused rhport,uint8_t __unused ep_addr,xfer_result_t __unused result,uint32_t __unused xferred_bytes)77 static bool resetd_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result, uint32_t __unused xferred_bytes) {
78     return true;
79 }
80 
81 static usbd_class_driver_t const _resetd_driver =
82 {
83 #if CFG_TUSB_DEBUG >= 2
84     .name = "RESET",
85 #endif
86     .init             = resetd_init,
87     .reset            = resetd_reset,
88     .open             = resetd_open,
89     .control_xfer_cb  = resetd_control_xfer_cb,
90     .xfer_cb          = resetd_xfer_cb,
91     .sof              = NULL
92 };
93 
94 // Implement callback to add our custom driver
usbd_app_driver_get_cb(uint8_t * driver_count)95 usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
96     *driver_count = 1;
97     return &_resetd_driver;
98 }
99 #endif
100 
101 #if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
102 // Support for default BOOTSEL reset by changing baud rate
tud_cdc_line_coding_cb(__unused uint8_t itf,cdc_line_coding_t const * p_line_coding)103 void tud_cdc_line_coding_cb(__unused uint8_t itf, cdc_line_coding_t const* p_line_coding) {
104     if (p_line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) {
105 #ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
106         const uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
107 #else
108         const uint gpio_mask = 0u;
109 #endif
110         reset_usb_boot(gpio_mask, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK);
111     }
112 }
113 #endif
114 
115 #endif