Add support for resetting RP2040 via the USB connection when using pico_stdio_usb
- setting baud rate to magic value (default=1200) will cause a reset to BOOTSEL mode - a VENDOR interface along side the CDC interface can be used to reset via refular flash boot, or into BOOTSEL mode with control for the reset_usb_boot parameters for the latter either method can be configured/enabled/disabled via #define
This commit is contained in:
parent
61e46fefe5
commit
383e88ea16
@ -23,5 +23,7 @@
|
|||||||
#if !PICO_ON_DEVICE && !defined(PICO_NO_BINARY_INFO)
|
#if !PICO_ON_DEVICE && !defined(PICO_NO_BINARY_INFO)
|
||||||
#define PICO_NO_BINARY_INFO 1
|
#define PICO_NO_BINARY_INFO 1
|
||||||
#endif
|
#endif
|
||||||
|
#if !PICO_NO_BINARY_INFO
|
||||||
#include "pico/binary_info/code.h"
|
#include "pico/binary_info/code.h"
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
@ -4,6 +4,7 @@ if (TARGET tinyusb_device_unmarked)
|
|||||||
target_include_directories(pico_stdio_usb INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
target_include_directories(pico_stdio_usb INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
|
|
||||||
target_sources(pico_stdio_usb INTERFACE
|
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.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/stdio_usb_descriptors.c
|
${CMAKE_CURRENT_LIST_DIR}/stdio_usb_descriptors.c
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
* Note this library is a developer convenience. It is not applicable in all cases; for one it takes full control of the USB device precluding your
|
* Note this library is a developer convenience. It is not applicable in all cases; for one it takes full control of the USB device precluding your
|
||||||
* use of the USB in device or host mode. For this reason, this library will automatically disengage if you try to using it alongside \ref tinyusb_device or
|
* use of the USB in device or host mode. For this reason, this library will automatically disengage if you try to using it alongside \ref tinyusb_device or
|
||||||
* \ref tinyusb_host. It also takes control of a lower level IRQ and sets up a periodic background task.
|
* \ref tinyusb_host. It also takes control of a lower level IRQ and sets up a periodic background task.
|
||||||
|
*
|
||||||
|
* This library also includes (by default) functionality to enable the RP2040 to be reset over the USB interface.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// PICO_CONFIG: PICO_STDIO_USB_DEFAULT_CRLF, Default state of CR/LF translation for USB output, type=bool, default=PICO_STDIO_DEFAULT_CRLF, group=pico_stdio_usb
|
// PICO_CONFIG: PICO_STDIO_USB_DEFAULT_CRLF, Default state of CR/LF translation for USB output, type=bool, default=PICO_STDIO_DEFAULT_CRLF, group=pico_stdio_usb
|
||||||
@ -31,7 +33,7 @@
|
|||||||
#define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000
|
#define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// todo perhaps unnecessarily high?
|
// todo perhaps unnecessarily frequent?
|
||||||
// PICO_CONFIG: PICO_STDIO_USB_TASK_INTERVAL_US, Period of microseconds between calling tud_task in the background, default=1000, advanced=true, group=pico_stdio_usb
|
// PICO_CONFIG: PICO_STDIO_USB_TASK_INTERVAL_US, Period of microseconds between calling tud_task in the background, default=1000, advanced=true, group=pico_stdio_usb
|
||||||
#ifndef PICO_STDIO_USB_TASK_INTERVAL_US
|
#ifndef PICO_STDIO_USB_TASK_INTERVAL_US
|
||||||
#define PICO_STDIO_USB_TASK_INTERVAL_US 1000
|
#define PICO_STDIO_USB_TASK_INTERVAL_US 1000
|
||||||
@ -42,6 +44,36 @@
|
|||||||
#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31
|
#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31
|
||||||
#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, group=pico_stdio_usb
|
||||||
|
#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
|
||||||
|
#define PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE 1
|
||||||
|
#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==1), 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_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode via an additional VENDOR USB interface - enables picotool based reset, type=bool, default=1, group=pico_stdio_usb
|
||||||
|
#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
|
||||||
|
#define PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE 1
|
||||||
|
#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_RESET_TO_FLASH_DELAY_MS, delays 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
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PICO_STDIO_USB_RESET_INTERFACE_H
|
||||||
|
#define _PICO_STDIO_USB_RESET_INTERFACE_H
|
||||||
|
|
||||||
|
// We use VENDOR, 0, 0 for PICOBOOT, so lets use VENDOR, 0, 1 for RESET
|
||||||
|
|
||||||
|
// VENDOR sub-class for the reset interface
|
||||||
|
#define RESET_INTERFACE_SUBCLASS 0x00
|
||||||
|
// VENDOR protocol for the reset interface
|
||||||
|
#define RESET_INTERFACE_PROTOCOL 0x01
|
||||||
|
|
||||||
|
// CONTROL requests:
|
||||||
|
|
||||||
|
// reset to BOOTSEL
|
||||||
|
#define RESET_REQUEST_BOOTSEL 0x01
|
||||||
|
// regular flash boot
|
||||||
|
#define RESET_REQUEST_FLASH 0x02
|
||||||
|
#endif
|
@ -27,10 +27,14 @@
|
|||||||
#ifndef _PICO_STDIO_USB_TUSB_CONFIG_H
|
#ifndef _PICO_STDIO_USB_TUSB_CONFIG_H
|
||||||
#define _PICO_STDIO_USB_TUSB_CONFIG_H
|
#define _PICO_STDIO_USB_TUSB_CONFIG_H
|
||||||
|
|
||||||
|
#include "pico/stdio_usb.h"
|
||||||
|
|
||||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE)
|
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE)
|
||||||
|
|
||||||
#define CFG_TUD_CDC (1)
|
#define CFG_TUD_CDC (1)
|
||||||
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
||||||
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
||||||
|
|
||||||
|
// We use a vendor specific interface but with our own driver
|
||||||
|
#define CFG_TUD_VENDOR (0)
|
||||||
#endif
|
#endif
|
||||||
|
95
src/rp2_common/pico_stdio_usb/reset_interface.c
Normal file
95
src/rp2_common/pico_stdio_usb/reset_interface.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
|
||||||
|
#include "pico/bootrom.h"
|
||||||
|
#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_request_cb(uint8_t __unused rhport, tusb_control_request_t const *request) {
|
||||||
|
if (request->wIndex == itf_num) {
|
||||||
|
#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
|
||||||
|
if (request->bRequest == RESET_REQUEST_BOOTSEL) {
|
||||||
|
uint gpio_mask = 0;
|
||||||
|
if (request->wValue & 0x100) {
|
||||||
|
gpio_mask = 1u << (request->wValue >> 9u);
|
||||||
|
}
|
||||||
|
reset_usb_boot(gpio_mask, request->wValue & 0x7f);
|
||||||
|
// 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, SRAM_END, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool resetd_control_complete_cb(uint8_t __unused rhport, tusb_control_request_t __unused const *request) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_request = resetd_control_request_cb,
|
||||||
|
.control_complete = resetd_control_complete_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(uint8_t itf, cdc_line_coding_t const* p_line_coding) {
|
||||||
|
if (p_line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) {
|
||||||
|
reset_usb_boot(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -8,7 +8,6 @@
|
|||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
#include "pico/time.h"
|
#include "pico/time.h"
|
||||||
#include "pico/stdio_usb.h"
|
|
||||||
#include "pico/stdio/driver.h"
|
#include "pico/stdio/driver.h"
|
||||||
#include "pico/binary_info.h"
|
#include "pico/binary_info.h"
|
||||||
#include "hardware/irq.h"
|
#include "hardware/irq.h"
|
||||||
|
@ -29,15 +29,26 @@
|
|||||||
#if !defined(TINYUSB_HOST_LINKED) && !defined(TINYUSB_DEVICE_LINKED)
|
#if !defined(TINYUSB_HOST_LINKED) && !defined(TINYUSB_DEVICE_LINKED)
|
||||||
|
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
#include "pico/stdio_usb/reset_interface.h"
|
||||||
|
|
||||||
#define USBD_VID (0x2E8A) // Raspberry Pi
|
#define USBD_VID (0x2E8A) // Raspberry Pi
|
||||||
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
|
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
|
||||||
|
|
||||||
|
#define TUD_RPI_RESET_DESC_LEN 9
|
||||||
|
#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
|
||||||
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
|
#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)
|
||||||
|
#endif
|
||||||
#define USBD_MAX_POWER_MA (250)
|
#define USBD_MAX_POWER_MA (250)
|
||||||
|
|
||||||
#define USBD_ITF_CDC (0) // needs 2 interfaces
|
#define USBD_ITF_CDC (0) // needs 2 interfaces
|
||||||
|
#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
|
||||||
#define USBD_ITF_MAX (2)
|
#define USBD_ITF_MAX (2)
|
||||||
|
#else
|
||||||
|
#define USBD_ITF_RPI_RESET (2)
|
||||||
|
#define USBD_ITF_MAX (3)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define USBD_CDC_EP_CMD (0x81)
|
#define USBD_CDC_EP_CMD (0x81)
|
||||||
#define USBD_CDC_EP_OUT (0x02)
|
#define USBD_CDC_EP_OUT (0x02)
|
||||||
@ -50,6 +61,7 @@
|
|||||||
#define USBD_STR_PRODUCT (0x02)
|
#define USBD_STR_PRODUCT (0x02)
|
||||||
#define USBD_STR_SERIAL (0x03)
|
#define USBD_STR_SERIAL (0x03)
|
||||||
#define USBD_STR_CDC (0x04)
|
#define USBD_STR_CDC (0x04)
|
||||||
|
#define USBD_STR_RPI_RESET (0x05)
|
||||||
|
|
||||||
// Note: descriptors returned from callbacks must exist long enough for transfer to complete
|
// Note: descriptors returned from callbacks must exist long enough for transfer to complete
|
||||||
|
|
||||||
@ -70,12 +82,20 @@ static const tusb_desc_device_t usbd_desc_device = {
|
|||||||
.bNumConfigurations = 1,
|
.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] = {
|
static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = {
|
||||||
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
|
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
|
||||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
|
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
|
||||||
|
|
||||||
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD,
|
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),
|
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)
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const usbd_desc_str[] = {
|
static const char *const usbd_desc_str[] = {
|
||||||
@ -83,6 +103,9 @@ static const char *const usbd_desc_str[] = {
|
|||||||
[USBD_STR_PRODUCT] = "Pico",
|
[USBD_STR_PRODUCT] = "Pico",
|
||||||
[USBD_STR_SERIAL] = "000000000000", // TODO
|
[USBD_STR_SERIAL] = "000000000000", // TODO
|
||||||
[USBD_STR_CDC] = "Board CDC",
|
[USBD_STR_CDC] = "Board CDC",
|
||||||
|
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
|
||||||
|
[USBD_STR_RPI_RESET] = "Reset",
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint8_t *tud_descriptor_device_cb(void) {
|
const uint8_t *tud_descriptor_device_cb(void) {
|
||||||
|
Loading…
Reference in New Issue
Block a user