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)
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 
23 #if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR
24 // Support for Microsoft OS 2.0 descriptor
25 #define BOS_TOTAL_LEN      (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
26 
27 #define MS_OS_20_DESC_LEN  166
28 #define USBD_ITF_RPI_RESET 2
29 
30 uint8_t const desc_bos[] =
31 {
32     // total length, number of device caps
33     TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
34 
35     // Microsoft OS 2.0 descriptor
36     TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)
37 };
38 
39 TU_VERIFY_STATIC(sizeof(desc_bos) == BOS_TOTAL_LEN, "Incorrect size");
40 
tud_descriptor_bos_cb(void)41 uint8_t const * tud_descriptor_bos_cb(void)
42 {
43     return desc_bos;
44 }
45 
46 static const uint8_t desc_ms_os_20[] =
47 {
48     // Set header: length, type, windows version, total length
49     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),
50 
51     // Function Subset header: length, type, first interface, reserved, subset length
52     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),
53 
54     // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
55     U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
56     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
57 
58     // MS OS 2.0 Registry property descriptor: length, type
59     U16_TO_U8S_LE(0x0080), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
60     U16_TO_U8S_LE(0x0001), U16_TO_U8S_LE(0x0028), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUID" in UTF-16
61     'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
62     'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00,
63     U16_TO_U8S_LE(0x004E), // wPropertyDataLength
64     // Vendor-defined Property Data: {bc7398c1-73cd-4cb7-98b8-913a8fca7bf6}
65     '{', 0,     'b', 0,     'c', 0,     '7', 0,     '3', 0,     '9', 0,
66     '8', 0,     'c', 0,     '1', 0,     '-', 0,     '7', 0,     '3', 0,
67     'c', 0,     'd', 0,     '-', 0,     '4', 0,     'c', 0,     'b', 0,
68     '7', 0,     '-', 0,     '9', 0,     '8', 0,     'b', 0,     '8', 0,
69     '-', 0,     '9', 0,     '1', 0,     '3', 0,     'a', 0,     '8', 0,
70     'f', 0,     'c', 0,     'a', 0,     '7', 0,     'b', 0,     'f', 0,
71     '6', 0,     '}', 0,       0, 0
72 };
73 
74 TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
75 
tud_vendor_control_xfer_cb(uint8_t rhport,uint8_t stage,tusb_control_request_t const * request)76 bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
77 {
78     // nothing to with DATA & ACK stage
79     if (stage != CONTROL_STAGE_SETUP) return true;
80 
81     if (request->bRequest == 1 && request->wIndex == 7) {
82         // Get Microsoft OS 2.0 compatible descriptor
83         return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, sizeof(desc_ms_os_20));
84     } else {
85         return false;
86     }
87 
88     // stall unknown request
89     return false;
90 }
91 #endif
92 
resetd_init(void)93 static void resetd_init(void) {
94 }
95 
resetd_reset(uint8_t __unused rhport)96 static void resetd_reset(uint8_t __unused rhport) {
97     itf_num = 0;
98 }
99 
resetd_open(uint8_t __unused rhport,tusb_desc_interface_t const * itf_desc,uint16_t max_len)100 static uint16_t resetd_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
101     TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
102               RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
103               RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
104 
105     uint16_t const drv_len = sizeof(tusb_desc_interface_t);
106     TU_VERIFY(max_len >= drv_len, 0);
107 
108     itf_num = itf_desc->bInterfaceNumber;
109     return drv_len;
110 }
111 
112 // 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)113 static bool resetd_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const * request) {
114     // nothing to do with DATA & ACK stage
115     if (stage != CONTROL_STAGE_SETUP) return true;
116 
117     if (request->wIndex == itf_num) {
118 
119 #if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
120         if (request->bRequest == RESET_REQUEST_BOOTSEL) {
121 #ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
122             int gpio = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
123             bool active_low = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW;
124 #else
125             int gpio = -1;
126             bool active_low = false;
127 #endif
128 #if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED
129             if (request->wValue & 0x100) {
130                 gpio = request->wValue >> 9u;
131             }
132             active_low = request->wValue & 0x200;
133 #endif
134             rom_reset_usb_boot_extra(gpio, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low);
135             // does not return, otherwise we'd return true
136         }
137 #endif
138 
139 #if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT
140         if (request->bRequest == RESET_REQUEST_FLASH) {
141             watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS);
142             return true;
143         }
144 #endif
145 
146     }
147     return false;
148 }
149 
resetd_xfer_cb(uint8_t __unused rhport,uint8_t __unused ep_addr,xfer_result_t __unused result,uint32_t __unused xferred_bytes)150 static bool resetd_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result, uint32_t __unused xferred_bytes) {
151     return true;
152 }
153 
154 static usbd_class_driver_t const _resetd_driver =
155 {
156 #if CFG_TUSB_DEBUG >= 2
157     .name = "RESET",
158 #endif
159     .init             = resetd_init,
160     .reset            = resetd_reset,
161     .open             = resetd_open,
162     .control_xfer_cb  = resetd_control_xfer_cb,
163     .xfer_cb          = resetd_xfer_cb,
164     .sof              = NULL
165 };
166 
167 // Implement callback to add our custom driver
usbd_app_driver_get_cb(uint8_t * driver_count)168 usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
169     *driver_count = 1;
170     return &_resetd_driver;
171 }
172 #endif
173 
174 #if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
175 // 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)176 void tud_cdc_line_coding_cb(__unused uint8_t itf, cdc_line_coding_t const* p_line_coding) {
177     if (p_line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) {
178 #ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
179         int gpio = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
180         bool active_low = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW;
181 #else
182         int gpio = -1;
183         bool active_low = false;
184 #endif
185         rom_reset_usb_boot_extra(gpio, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low);
186     }
187 }
188 #endif
189 
190 #endif