diff --git a/boards.txt b/boards.txt index 65074d3a9f..b82635c93a 100644 --- a/boards.txt +++ b/boards.txt @@ -181,6 +181,7 @@ Nucleo_144.menu.upload_method.serialMethod.upload.tool=stm32CubeProg Nucleo_144.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Nucleo_144.menu.upload_method.dfuMethod.upload.protocol=2 Nucleo_144.menu.upload_method.dfuMethod.upload.options=-g +Nucleo_144.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Nucleo_144.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -466,6 +467,7 @@ Nucleo_64.menu.upload_method.serialMethod.upload.tool=stm32CubeProg Nucleo_64.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Nucleo_64.menu.upload_method.dfuMethod.upload.protocol=2 Nucleo_64.menu.upload_method.dfuMethod.upload.options=-g +Nucleo_64.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Nucleo_64.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -571,6 +573,7 @@ Nucleo_32.menu.upload_method.serialMethod.upload.tool=stm32CubeProg Nucleo_32.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Nucleo_32.menu.upload_method.dfuMethod.upload.protocol=2 Nucleo_32.menu.upload_method.dfuMethod.upload.options=-g +Nucleo_32.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Nucleo_32.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -727,6 +730,7 @@ Disco.menu.upload_method.serialMethod.upload.tool=stm32CubeProg Disco.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Disco.menu.upload_method.dfuMethod.upload.protocol=2 Disco.menu.upload_method.dfuMethod.upload.options=-g +Disco.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Disco.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -759,6 +763,7 @@ Eval.menu.upload_method.swdMethod.upload.tool=stm32CubeProg Eval.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Eval.menu.upload_method.dfuMethod.upload.protocol=2 Eval.menu.upload_method.dfuMethod.upload.options=-g +Eval.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Eval.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -847,6 +852,7 @@ GenF0.menu.upload_method.serialMethod.upload.tool=stm32CubeProg GenF0.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) GenF0.menu.upload_method.dfuMethod.upload.protocol=2 GenF0.menu.upload_method.dfuMethod.upload.options=-g +GenF0.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true GenF0.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -1152,6 +1158,7 @@ GenF1.menu.upload_method.serialMethod.upload.tool=stm32CubeProg GenF1.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) GenF1.menu.upload_method.dfuMethod.upload.protocol=2 GenF1.menu.upload_method.dfuMethod.upload.options=-g +GenF1.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true GenF1.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg GenF1.menu.upload_method.bmpMethod=BMP (Black Magic Probe) @@ -1215,6 +1222,7 @@ GenF3.menu.upload_method.serialMethod.upload.tool=stm32CubeProg GenF3.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) GenF3.menu.upload_method.dfuMethod.upload.protocol=2 GenF3.menu.upload_method.dfuMethod.upload.options=-g +GenF3.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true GenF3.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg GenF3.menu.upload_method.bmpMethod=BMP (Black Magic Probe) @@ -1623,6 +1631,7 @@ GenF4.menu.upload_method.serialMethod.upload.tool=stm32CubeProg GenF4.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) GenF4.menu.upload_method.dfuMethod.upload.protocol=2 GenF4.menu.upload_method.dfuMethod.upload.options=-g +GenF4.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true GenF4.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg GenF4.menu.upload_method.bmpMethod=BMP (Black Magic Probe) @@ -1712,6 +1721,7 @@ GenL0.menu.upload_method.serialMethod.upload.tool=stm32CubeProg GenL0.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) GenL0.menu.upload_method.dfuMethod.upload.protocol=2 GenL0.menu.upload_method.dfuMethod.upload.options=-g +GenL0.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true GenL0.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg GenL0.menu.upload_method.bmpMethod=BMP (Black Magic Probe) @@ -1752,6 +1762,7 @@ ESC_board.menu.upload_method.serialMethod.upload.tool=stm32CubeProg ESC_board.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) ESC_board.menu.upload_method.dfuMethod.upload.protocol=2 ESC_board.menu.upload_method.dfuMethod.upload.options=-g +ESC_board.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true ESC_board.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ @@ -1834,6 +1845,7 @@ LoRa.menu.upload_method.serialMethod.upload.tool=stm32CubeProg LoRa.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) LoRa.menu.upload_method.dfuMethod.upload.protocol=2 LoRa.menu.upload_method.dfuMethod.upload.options=-g +LoRa.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true LoRa.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ############################### @@ -2010,6 +2022,7 @@ LoRa.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg 3dprinter.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) 3dprinter.menu.upload_method.dfuMethod.upload.protocol=2 3dprinter.menu.upload_method.dfuMethod.upload.options=-g +3dprinter.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true 3dprinter.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg @@ -2069,6 +2082,7 @@ Genericflight.menu.upload_method.serialMethod.upload.tool=stm32CubeProg Genericflight.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Genericflight.menu.upload_method.dfuMethod.upload.protocol=2 Genericflight.menu.upload_method.dfuMethod.upload.options=-g +Genericflight.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Genericflight.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg Genericflight.menu.upload_method.bmpMethod=BMP (Black Magic Probe) @@ -2199,6 +2213,7 @@ Midatronics.menu.upload_method.serialMethod.upload.tool=stm32CubeProg Midatronics.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU) Midatronics.menu.upload_method.dfuMethod.upload.protocol=2 Midatronics.menu.upload_method.dfuMethod.upload.options=-g +Midatronics.menu.upload_method.dfuMethod.upload.use_1200bps_touch=true Midatronics.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg ################################################################################ diff --git a/cores/arduino/stm32/bootloader.h b/cores/arduino/stm32/bootloader.h index 118bc7b860..cb077a624a 100644 --- a/cores/arduino/stm32/bootloader.h +++ b/cores/arduino/stm32/bootloader.h @@ -1,6 +1,8 @@ #ifndef _BOOTLOADER_H_ #define _BOOTLOADER_H_ +#include + /* Ensure DTR_TOGGLING_SEQ enabled */ #if defined(BL_LEGACY_LEAF) || defined(BL_HID) #ifndef DTR_TOGGLING_SEQ @@ -12,6 +14,21 @@ extern "C" { #endif /* __cplusplus */ +void scheduleBootloaderReset(); +void cancelBootloaderReset(); +void bootloaderSystickHandler(); + +/* Request to jump to system memory boot */ +void jumpToBootloaderRequested(void); + +/* Jump to system memory boot from user application */ +void jumpToBootloaderIfRequested(void); + +#ifdef DTR_TOGGLING_SEQ +/* DTR toggling sequence management */ +void dtr_togglingHook(uint8_t *buf, uint32_t *len); +#endif + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/cores/arduino/stm32/startup_stm32yyxx.S b/cores/arduino/stm32/startup_stm32yyxx.S index 8572083cdd..088f6ccabc 100644 --- a/cores/arduino/stm32/startup_stm32yyxx.S +++ b/cores/arduino/stm32/startup_stm32yyxx.S @@ -3,3 +3,8 @@ #if defined(CMSIS_STARTUP_FILE) #include CMSIS_STARTUP_FILE #endif + +# Expose Reset_Handler under a different name, to allow overriding it +# with a strong symbol and then calling the original. +.global Original_Reset_Handler +.thumb_set Original_Reset_Handler,Reset_Handler diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc.c b/cores/arduino/stm32/usb/cdc/usbd_cdc.c index 9ee0d7885a..86185360d6 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc.c +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc.c @@ -158,13 +158,26 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */ 0x00, - 0x02, /* bNumInterfaces: 2 interface */ + 0x03, /* bNumInterfaces: 3 interface */ 0x01, /* bConfigurationValue: Configuration value */ 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ 0xC0, /* bmAttributes: self powered */ 0x32, /* MaxPower 0 mA */ + /*---------------------------------------------------------------------------*/ + /*Interface Association Descriptor*/ + 0x08, /* bLength: Descriptor length */ + 0x0B, /* bDescriptorType: IAD */ + 0x00, /* bFirstInterface */ + 0x02, /* bInterfaceCount */ + 0x02, /* bFunctionClass (class of subdevice, should match first interface) */ + 0x02, /* bFunctionSubclass (subclass of subdevice, should match first interface) */ + 0x00, /* bFunctionProtocol (protocol of subdevice, should match first interface) */ + /* TODO: Put a meaningful string here, which shows up in the Windows */ + /* device manager when no driver is installed yet. */ + 0x00, /* iFunction */ + /*---------------------------------------------------------------------------*/ /* Interface Descriptor */ 0x09, /* bLength: Interface Descriptor size */ USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ @@ -233,7 +246,9 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN 0x02, /* bmAttributes: Bulk */ LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), - 0x00 /* bInterval: ignore for Bulk transfer */ + 0x00, /* bInterval: ignore for Bulk transfer */ + + DFU_RT_IFACE_DESC, }; @@ -244,11 +259,25 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */ 0x00, - 0x02, /* bNumInterfaces: 2 interface */ + 0x03, /* bNumInterfaces: 3 interface */ 0x01, /* bConfigurationValue: Configuration value */ 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ 0xC0, /* bmAttributes: self powered */ 0x32, /* MaxPower 0 mA */ + + /*---------------------------------------------------------------------------*/ + /*Interface Association Descriptor*/ + 0x08, /* bLength: Descriptor length */ + 0x0B, /* bDescriptorType: IAD */ + 0x00, /* bFirstInterface */ + 0x02, /* bInterfaceCount */ + 0x02, /* bFunctionClass (class of subdevice, should match first interface) */ + 0x02, /* bFunctionSubclass (subclass of subdevice, should match first interface) */ + 0x00, /* bFunctionProtocol (protocol of subdevice, should match first interface) */ + /* TODO: Put a meaningful string here, which shows up in the Windows */ + /* device manager when no driver is installed yet. */ + 0x00, /* iFunction */ + /*---------------------------------------------------------------------------*/ /* Interface Descriptor */ 0x09, /* bLength: Interface Descriptor size */ @@ -318,7 +347,9 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN 0x02, /* bmAttributes: Bulk */ LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), - 0x00 /* bInterval: ignore for Bulk transfer */ + 0x00, /* bInterval: ignore for Bulk transfer */ + + DFU_RT_IFACE_DESC, }; __ALIGN_BEGIN static uint8_t USBD_CDC_OtherSpeedCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = { @@ -326,11 +357,26 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_OtherSpeedCfgDesc[USB_CDC_CONFIG_DESC_SIZ] USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION, USB_CDC_CONFIG_DESC_SIZ, 0x00, - 0x02, /* bNumInterfaces: 2 interfaces */ + 0x03, /* bNumInterfaces: 3 interfaces */ 0x01, /* bConfigurationValue: */ 0x04, /* iConfiguration: */ 0xC0, /* bmAttributes: */ 0x32, /* MaxPower 100 mA */ + + /*---------------------------------------------------------------------------*/ + /*Interface Association Descriptor*/ + 0x08, /* bLength: Descriptor length */ + 0x0B, /* bDescriptorType: IAD */ + 0x00, /* bFirstInterface */ + 0x02, /* bInterfaceCount */ + 0x02, /* bFunctionClass (class of subdevice, should match first interface) */ + 0x02, /* bFunctionSubclass (subclass of subdevice, should match first interface) */ + 0x00, /* bFunctionProtocol (protocol of subdevice, should match first interface) */ + /* TODO: Put a meaningful string here, which shows up in the Windows */ + /* device manager when no driver is installed yet. */ + 0x00, /* iFunction */ + + /*---------------------------------------------------------------------------*/ /*Interface Descriptor */ 0x09, /* bLength: Interface Descriptor size */ USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ @@ -399,7 +445,9 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_OtherSpeedCfgDesc[USB_CDC_CONFIG_DESC_SIZ] 0x02, /* bmAttributes: Bulk */ 0x40, /* wMaxPacketSize: */ 0x00, - 0x00 /* bInterval */ + 0x00, /* bInterval */ + + DFU_RT_IFACE_DESC, }; /** @@ -539,7 +587,26 @@ static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_CLASS: - if (req->wLength != 0U) { + if ((req->bmRequest & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_INTERFACE + && req->wIndex == DFU_RT_IFACE_NUM) { + // Handle requests to the DFU interface separately + int device_to_host = (req->bmRequest & 0x80U); + + if (!device_to_host && req->wLength > 0) { + // When data is sent, return an error, since the data receiving + // machinery will forget the target interface and handle as a CDC + // request instead. + ret = USBD_FAIL; + } else { + ret = USBD_DFU_Runtime_Control(req->bRequest, req->wValue, (uint8_t *)(void *)hcdc->data, req->wLength); + } + + if (ret == USBD_FAIL) { + USBD_CtlError(pdev, req); + } else if (device_to_host && req->wLength > 0) { + USBD_CtlSendData(pdev, (uint8_t *)(void *)hcdc->data, req->wLength); + } + } else if (req->wLength != 0U) { if ((req->bmRequest & 0x80U) != 0U) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, (uint8_t *)hcdc->data, diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc.h b/cores/arduino/stm32/usb/cdc/usbd_cdc.h index 5395708c06..fd4a8022ae 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc.h +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc.h @@ -28,6 +28,7 @@ extern "C" { /* Includes ------------------------------------------------------------------*/ #include "usbd_ioreq.h" #include "usbd_ep_conf.h" +#include "dfu_runtime.h" /** @addtogroup STM32_USB_DEVICE_LIBRARY * @{ @@ -51,7 +52,7 @@ extern "C" { /* CDC Endpoints parameters */ -#define USB_CDC_CONFIG_DESC_SIZ 67U +#define USB_CDC_CONFIG_DESC_SIZ 67U + /* IAD */ 8 + DFU_RT_IFACE_DESC_SIZE #define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE #define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c b/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c index c09675f95e..b340a6b553 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c @@ -33,6 +33,9 @@ #define CDC_MAX_PACKET_SIZE USB_MAX_EP0_SIZE #endif +// TODO: Put this elsewhere +#define BOOTLOADER_RESET_1200_BAUD + /* * The value USB_CDC_TRANSMIT_TIMEOUT is defined in terms of HAL_GetTick() units. * Typically it is 1ms value. The timeout determines when we would consider the @@ -56,8 +59,7 @@ __IO bool receivePended = true; static uint32_t transmitStart = 0; #ifdef DTR_TOGGLING_SEQ - /* DTR toggling sequence management */ - extern void dtr_togglingHook(uint8_t *buf, uint32_t *len); + uint8_t dtr_toggling = 0; #endif @@ -201,6 +203,19 @@ static int8_t USBD_CDC_Control(uint8_t cmd, uint8_t *pbuf, uint16_t length) break; } +#ifdef BOOTLOADER_RESET_1200_BAUD + if (cmd == CDC_SET_LINE_CODING || cmd == CDC_SET_CONTROL_LINE_STATE) { + // Auto-reset into the bootloader is triggered when the port, already + // open at 1200 bps, is closed. Cancel the reset when the port is + // opened again. + if (linecoding.bitrate == 1200 && !lineState) { + scheduleBootloaderReset(); + } else { + cancelBootloaderReset(); + } + } +#endif /* BOOTLOADER_RESET_1200_BAUD */ + return ((int8_t)USBD_OK); } diff --git a/cores/arduino/stm32/usb/dfu_runtime.h b/cores/arduino/stm32/usb/dfu_runtime.h new file mode 100644 index 0000000000..052d65e5ae --- /dev/null +++ b/cores/arduino/stm32/usb/dfu_runtime.h @@ -0,0 +1,151 @@ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USB_DFU_RUNTIME_H +#define __USB_DFU_RUNTIME_H + +#include + +/**************************************************/ +/* DFU Requests DFU states */ +/**************************************************/ +#define APP_STATE_IDLE 0U +#define APP_STATE_DETACH 1U +#define DFU_STATE_IDLE 2U +#define DFU_STATE_DNLOAD_SYNC 3U +#define DFU_STATE_DNLOAD_BUSY 4U +#define DFU_STATE_DNLOAD_IDLE 5U +#define DFU_STATE_MANIFEST_SYNC 6U +#define DFU_STATE_MANIFEST 7U +#define DFU_STATE_MANIFEST_WAIT_RESET 8U +#define DFU_STATE_UPLOAD_IDLE 9U +#define DFU_STATE_ERROR 10U + +/**************************************************/ +/* DFU errors */ +/**************************************************/ +#define DFU_ERROR_NONE 0x00U +#define DFU_ERROR_TARGET 0x01U +#define DFU_ERROR_FILE 0x02U +#define DFU_ERROR_WRITE 0x03U +#define DFU_ERROR_ERASE 0x04U +#define DFU_ERROR_CHECK_ERASED 0x05U +#define DFU_ERROR_PROG 0x06U +#define DFU_ERROR_VERIFY 0x07U +#define DFU_ERROR_ADDRESS 0x08U +#define DFU_ERROR_NOTDONE 0x09U +#define DFU_ERROR_FIRMWARE 0x0AU +#define DFU_ERROR_VENDOR 0x0BU +#define DFU_ERROR_USB 0x0CU +#define DFU_ERROR_POR 0x0DU +#define DFU_ERROR_UNKNOWN 0x0EU +#define DFU_ERROR_STALLEDPKT 0x0FU + +typedef enum { + DFU_DETACH = 0U, + DFU_DNLOAD, + DFU_UPLOAD, + DFU_GETSTATUS, + DFU_CLRSTATUS, + DFU_GETSTATE, + DFU_ABORT +} DFU_RequestTypeDef; + +#define DFU_DESCRIPTOR_TYPE 0x21U + +// Device will detach by itself (alternative is that the host sends a +// USB reset within DETACH_TIMEOUT). +#define DFU_RT_ATTR_WILL_DETACH 0x08U +// Device is still accessible on USB after flashing (manifestation). +// Probably not so relevant in runtime mode +#define DFU_RT_ATTR_MANIFESTATION_TOLERANT 0x04U +#define DFU_RT_ATTR_CAN_UPLOAD 0x02U +#define DFU_RT_ATTR_CAN_DNLOAD 0x01U + +// Of these, only WILL_DETACH is relevant at runtime, but specify +// CAN_UPLOAD and CAN_DNLOAD too, just in case there is a tool that +// somehow checks these before resetting. +#define DFU_RT_ATTRS DFU_RT_ATTR_WILL_DETACH \ + | DFU_RT_ATTR_CAN_UPLOAD | DFU_RT_ATTR_CAN_DNLOAD + +// Detach timeout is only relevant when ATTR_WILL_DETACH is unset +#define DFU_RT_DETACH_TIMEOUT 0 +// This should be only relevant for actual firmware uploads (the actual +// value is read from the bootloader after reset), but specify a +// conservative value here in case any tool fails to reread the value +// after reset. +// The max packet size for EP0 control transfers is specified in the +// device descriptor. +#define DFU_RT_TRANSFER_SIZE 64 +#define DFU_RT_DFU_VERSION 0x0101 // DFU 1.1 + +#define DFU_RT_IFACE_NUM 2 // XXX: Hardcoded + +#define DFU_RT_IFACE_DESC_SIZE 18U +#define DFU_RT_IFACE_DESC \ + /*DFU Runtime interface descriptor*/ \ + 0x09, /* bLength: Endpoint Descriptor size */ \ + USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ \ + DFU_RT_IFACE_NUM, /* bInterfaceNumber: Number of Interface */ \ + 0x00, /* bAlternateSetting: Alternate setting */ \ + 0x00, /* bNumEndpoints: no endpoints used (only control endpoint) */ \ + 0xFE, /* bInterfaceClass: Application Specific */ \ + 0x01, /* bInterfaceSubClass: Device Firmware Upgrade Code*/ \ + 0x01, /* bInterfaceProtocol: Runtime Protocol*/ \ + /* TODO: Put a meaningful string here, which shows up in the Windows * */ \ + /* device manager when no driver is installed yet. */ \ + 0x00, /* iInterface: */ \ + \ + /*DFU Runtime Functional Descriptor*/ \ + 0x09, /* bFunctionLength */ \ + DFU_DESCRIPTOR_TYPE, /* bDescriptorType: DFU Functional */ \ + DFU_RT_ATTRS, /* bmAttributes: DFU Attributes */ \ + LOBYTE(DFU_RT_DETACH_TIMEOUT), /* wDetachTimeout */ \ + HIBYTE(DFU_RT_DETACH_TIMEOUT), \ + LOBYTE(DFU_RT_TRANSFER_SIZE), /* wTransferSize */ \ + HIBYTE(DFU_RT_TRANSFER_SIZE), \ + LOBYTE(DFU_RT_DFU_VERSION), /* bcdDFUVersion */ \ + HIBYTE(DFU_RT_DFU_VERSION) + +/** + * @brief USBD_DFU_Runtime_Control + * Manage the DFU interface control requests + * @param bRequest: Command code from request + * @param wValue: Value from request + * @param data: Buffer for result + * @param length: Number of data to be sent (in bytes) + * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL + */ +static int8_t USBD_DFU_Runtime_Control(uint8_t bRequest, uint16_t wValue, uint8_t *data, uint16_t len) +{ + UNUSED(wValue); + switch (bRequest) { + case DFU_GETSTATUS: + if (len != 6) { + return (USBD_FAIL); + } + + data[0] = DFU_ERROR_NONE; + // Minimum delay until next GET_STATUS + data[1] = data[2] = data[3] = 0; + data[4] = APP_STATE_IDLE; + // State string descriptor + data[5] = 0; + + return (USBD_OK); + + case DFU_DETACH: + scheduleBootloaderReset(); + return (USBD_OK); + + case DFU_GETSTATE: + if (len != 1) { + return (USBD_FAIL); + } + data[0] = APP_STATE_IDLE; + return (USBD_OK); + + default: + return (USBD_FAIL); + } +} + +#endif // __USB_DFU_RUNTIME_H diff --git a/cores/arduino/stm32/usb/usbd_conf.h b/cores/arduino/stm32/usb/usbd_conf.h index c1a4808380..d0c9a7c8f5 100644 --- a/cores/arduino/stm32/usb/usbd_conf.h +++ b/cores/arduino/stm32/usb/usbd_conf.h @@ -70,7 +70,7 @@ extern "C" { #endif #ifndef USBD_MAX_NUM_INTERFACES -#define USBD_MAX_NUM_INTERFACES 2U +#define USBD_MAX_NUM_INTERFACES 3U #endif /* USBD_MAX_NUM_INTERFACES */ #ifndef USBD_MAX_NUM_CONFIGURATION diff --git a/cores/arduino/stm32/usb/usbd_desc.c b/cores/arduino/stm32/usb/usbd_desc.c index 229112ea5f..6c92dc256c 100644 --- a/cores/arduino/stm32/usb/usbd_desc.c +++ b/cores/arduino/stm32/usb/usbd_desc.c @@ -170,9 +170,9 @@ __ALIGN_BEGIN uint8_t USBD_Class_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { 0x00, /* bcdUSB */ #endif 0x02, - 0x02, /* bDeviceClass */ - 0x02, /* bDeviceSubClass */ - 0x00, /* bDeviceProtocol */ + 0xEF, /* bDeviceClass (Miscellaneous) */ + 0x02, /* bDeviceSubClass (Common Class) */ + 0x01, /* bDeviceProtocol (Interface Association Descriptor) */ USB_MAX_EP0_SIZE, /* bMaxPacketSize */ LOBYTE(USBD_VID), /* idVendor */ HIBYTE(USBD_VID), /* idVendor */ diff --git a/cores/arduino/stm32/usb/usbd_if.c b/cores/arduino/stm32/usb/usbd_if.c index e579140f72..8a8c60301a 100644 --- a/cores/arduino/stm32/usb/usbd_if.c +++ b/cores/arduino/stm32/usb/usbd_if.c @@ -115,6 +115,56 @@ #define USBD_DP_TRICK #endif +/** + * @brief USBD_early_startup_delay + * @param us - number of us to delay + * @retval None + * + * This is a minimal delay which is usable in very early startup, when + * nothing has been initialized yet (no clocks, no memory, no systick + * timer). It works by counting CPU cycles, and assumes the system is + * still running from the HSI. + * + * If the systick timer is already enabled, this assumes everything is + * intialized and instead used the normal delayMicroseconds function. + * + * Max delay depends on HSI, but is around 268 sec with 16Mhz HSI. + */ +void USBD_early_startup_delay_us(uint32_t us) +{ + if (SysTick->CTRL & SysTick_CTRL_ENABLE_Msk) { + delayMicroseconds(us); + return; + } + +#if !HSI_VALUE +#error "Missing HSI_VALUE" +#endif + +#if HSI_VALUE % 4000000 != 0 +#warning "HSI not multiple of 4MHz, early startup delay will be inaccurate!" +#endif + + // To simplify this calculation, this assumes the HSI runs at a + // multiple of 4Mhz (1Mhz to scale to us, times 4 to account for 4 + // cycles per loop). + const uint32_t loops_per_us = (HSI_VALUE / 1000000) / 4; + const uint32_t loop_count = us * loops_per_us; + + // Assembly loop, designed to run at exactly 4 cycles per loop. + asm volatile( + // Use the unified ARM/Thumb syntax, which seems to be more + // universally used and corresponds to what avr-objdump outputs + // See https://sourceware.org/binutils/docs/as/ARM_002dInstruction_002dSet.html + ".syntax unified\n\t" + "1:\n\t" + "nop /* 1 cycle */\n\t" + "subs %[loop], %[loop], #1 /* 1 cycle */\n\t" + "bne 1b /* 2 if taken, 1 otherwise */\n\t" + : : [loop] "l"(loop_count) + ); +} + /** * @brief Force to re-enumerate USB. * @@ -134,7 +184,7 @@ WEAK void USBD_reenumerate(void) digitalWriteFast(USBD_PULLUP_CONTROL_PINNAME, USBD_DETACH_LEVEL); /* Wait */ - delay(USBD_ENUM_DELAY); + USBD_early_startup_delay_us(USBD_ENUM_DELAY * 1000); /* Attach */ #if defined(USBD_DP_TRICK) @@ -144,9 +194,18 @@ WEAK void USBD_reenumerate(void) digitalWriteFast(USBD_PULLUP_CONTROL_PINNAME, USBD_ATTACH_LEVEL); #endif /* defined(USBD_PULLUP_CONTROL_FLOATING) */ #elif defined(USBD_HAVE_INTERNAL_PULLUPS) - USB_DevDisconnect(USBD_USB_INSTANCE); - delay(USBD_ENUM_DELAY); - USB_DevConnect(USBD_USB_INSTANCE); +#ifdef USB_OTG_DCTL_SDIS + uint32_t USBx_BASE = (uint32_t)USBD_USB_INSTANCE; + USBx_DEVICE->DCTL |= USB_OTG_DCTL_SDIS; + //USB_DevDisconnect(USBD_USB_INSTANCE); + USBD_early_startup_delay_us(USBD_ENUM_DELAY * 1000); + //USB_DevConnect(USBD_USB_INSTANCE); + USBx_DEVICE->DCTL &= ~USB_OTG_DCTL_SDIS; +#else + USBD_USB_INSTANCE->BCDR &= (uint16_t)(~(USB_BCDR_DPPU)); + USBD_early_startup_delay_us(USBD_ENUM_DELAY * 1000); + USBD_USB_INSTANCE->BCDR |= (uint16_t)(USB_BCDR_DPPU); +#endif #else #warning "No USB attach/detach method, USB might not be reliable through system resets" #endif diff --git a/libraries/SrcWrapper/src/stm32/bootloader.c b/libraries/SrcWrapper/src/stm32/bootloader.c index d7d86e968e..f75503f4bf 100644 --- a/libraries/SrcWrapper/src/stm32/bootloader.c +++ b/libraries/SrcWrapper/src/stm32/bootloader.c @@ -1,8 +1,156 @@ +#include + #include "bootloader.h" #include "stm32_def.h" #include "backup.h" +#include "stm32yyxx_ll_system.h" +#include "usbd_if.h" + +/* + * STM32 built-in bootloader in system memory support + */ +#if !defined(STM32MP1xx) +static const uint32_t BOOTLOADER_DELAY_MS = 250; +static bool BootIntoBootloaderAfterReset; +static uint32_t countdown = 0; + + +/* Request to jump to system memory boot */ +WEAK void jumpToBootloaderRequested(void) +{ + BootIntoBootloaderAfterReset = true; + NVIC_SystemReset(); +} + +// Defined in startup assembly code +void Original_Reset_Handler(); + +// This overrides the Reset_Handler that is run on reset before +// *anything* else (including memory initialization). Only the stack +// pointer is set up by this time. +void Reset_Handler() +{ + // Jump to the bootloader if needed. + jumpToBootloaderIfRequested(); + + // Continue with regular startup by calling the original reset handler + Original_Reset_Handler(); +} + +/* Figure out where the bootloader lives, remapping memory if needed, + * and return its address. The returned address should point to the + * bootloader's interrupt vector table, so to a SP to load followed by + * an address to jump to. + */ +WEAK uint32_t bootloaderAddress() +{ +#ifdef __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH + /* Remap system Flash memory at address 0x00000000 */ + __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); + // Make the variable volatile to prevent the compiler from seeing a + // null-pointer dereference (which is undefined in C) and generating + // an UDF (undefined) instruction instead of just loading address 0. + return 0; +#elif defined(STM32F1xx) && (defined (STM32F101xG) || defined (STM32F103xG)) + // From AN2606, table 136 "Bootloader device-dependent parameters" + // STM32F10xxx XL-density, aka 768K-1M flash, aka F and G flash size codes + return 0x1FFFE000; +#elif defined(STM32F1xx) && defined (STM32F105xC) || defined (STM32F107xC) + // STM32F105xx/107xx from AN2606, table 136 "Bootloader device-dependent parameters" + return 0x1FFFB000; +#elif defined (STM32F100xB) || defined (STM32F100xE) || defined (STM32F101x6) || \ + defined (STM32F101xB) || defined (STM32F101xE) || defined (STM32F102x6) || \ + defined (STM32F102xB) || defined (STM32F103x6) || defined (STM32F103xB) || \ + defined (STM32F103xE) + // STM32F10xxx from AN2606, table 136 "Bootloader device-dependent parameters" + // This does not check for STM32F1xx, to prevent catching + // STM32F105xx/STM32F107xx or XL-density chips that are introduced later. + // Defines from system/Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h + return 0x1FFFF000; +#elif defined(STM32F7xx) || defined(STM32H7xx) + // From AN2606, table 136 "Bootloader device-dependent parameters" + // TODO: Reference manual for has a different value... + return 0x1FF00000; +#else +#error "System flash address unknown for this CPU" +#endif +} + + +/* Jump to system memory boot from user application */ +WEAK void jumpToBootloaderIfRequested(void) +{ + // Boot into bootloader if BootIntoBootloaderAfterReset is set. + // Note that BootIntoBootloaderAfterReset is a noinit variable, so it + // s not automatically initialized on startup (so it can keep its + // value across resets). At initial poweron, its value can be + // *anything*, so only consider its value after a software reset. In + // all cases, clear its value (this both takes care of giving it an + // initial value after power-up, and prevents booting into the + // bootloader more than once for a single request). + bool doBootloader = __HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) && BootIntoBootloaderAfterReset; + BootIntoBootloaderAfterReset = false; + + if (doBootloader) { + __HAL_RCC_CLEAR_RESET_FLAGS(); + +#ifdef USBCON + USBD_reenumerate(); +#endif + + uint32_t sys = bootloaderAddress(); + + // This is assembly to prevent modifying the stack pointer after + // loading it, and to ensure a jump (not call) to the bootloader. + // Not sure if the barriers are really needed, they were taken from + // https://github.com/GrumpyOldPizza/arduino-STM32L4/blob/ac659033eadd50cfe001ba1590a1362b2d87bb76/system/STM32L4xx/Source/boot_stm32l4xx.c#L159-L165 + asm volatile( + "ldr r0, [%[sys], #0] \n\t" // get address of stack pointer + "msr msp, r0 \n\t" // set stack pointer + "ldr r0, [%[sys], #4] \n\t" // get address of reset handler + "dsb \n\t" // data sync barrier + "isb \n\t" // instruction sync barrier + "bx r0 \n\t" // branch to bootloader + : : [sys] "l"(sys) : "r0" + ); + + __builtin_unreachable(); + } +} + +/** + * Scheduler a reset into the bootloader after a delay. + */ +void scheduleBootloaderReset() +{ + countdown = BOOTLOADER_DELAY_MS; +} + +/** + * Cancel a previously scheduled bootloader reset. + */ +void cancelBootloaderReset() +{ + countdown = 0; +} +#endif /* !STM32MP1xx */ + +/** + * Bootloader systick handler, should be called every ms + */ +void bootloaderSystickHandler() +{ +#ifndef STM32MP1xx + if (countdown && --countdown == 0) { + jumpToBootloaderRequested(); + } +#endif /* !STM32MP1xx */ +} +/* + * Legacy maple bootloader support + */ #ifdef BL_LEGACY_LEAF void dtr_togglingHook(uint8_t *buf, uint32_t *len) { @@ -17,6 +165,9 @@ void dtr_togglingHook(uint8_t *buf, uint32_t *len) } #endif /* BL_LEGACY_LEAF */ +/* + * HID bootloader support + */ #ifdef BL_HID void dtr_togglingHook(uint8_t *buf, uint32_t *len) { diff --git a/libraries/SrcWrapper/src/stm32/clock.c b/libraries/SrcWrapper/src/stm32/clock.c index bea7adcdbc..4c65c98826 100644 --- a/libraries/SrcWrapper/src/stm32/clock.c +++ b/libraries/SrcWrapper/src/stm32/clock.c @@ -37,6 +37,7 @@ */ #include "backup.h" #include "clock.h" +#include "bootloader.h" #include "stm32yyxx_ll_cortex.h" #ifdef __cplusplus @@ -88,6 +89,7 @@ void SysTick_Handler(void) HAL_IncTick(); HAL_SYSTICK_IRQHandler(); osSystickHandler(); + bootloaderSystickHandler(); } /** diff --git a/libraries/SrcWrapper/src/stm32/hw_config.c b/libraries/SrcWrapper/src/stm32/hw_config.c index 3434d6da96..20013b2aa5 100644 --- a/libraries/SrcWrapper/src/stm32/hw_config.c +++ b/libraries/SrcWrapper/src/stm32/hw_config.c @@ -35,10 +35,11 @@ * ****************************************************************************** */ -#include "stm32_def.h" +#include "bootloader.h" +#include "dwt.h" #include "hw_config.h" #include "usbd_if.h" -#include "dwt.h" +#include "stm32_def.h" #ifdef __cplusplus extern "C" { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy