From 669a8ec7b320f1053032dbccb2f8714b72ac06bf Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:11:31 +0100 Subject: [PATCH 1/8] usbus/dfu: introduce initial Device Firmware Upgrade support for USBUS --- sys/Makefile.dep | 6 + sys/Makefile.include | 5 + sys/include/usb/dfu.h | 136 +++++++++++++++++ sys/include/usb/usbus/dfu.h | 63 ++++++++ sys/usb/usbus/Makefile | 3 + sys/usb/usbus/dfu/Makefile | 4 + sys/usb/usbus/dfu/dfu.c | 285 ++++++++++++++++++++++++++++++++++++ 7 files changed, 502 insertions(+) create mode 100644 sys/include/usb/dfu.h create mode 100644 sys/include/usb/usbus/dfu.h create mode 100644 sys/usb/usbus/dfu/Makefile create mode 100644 sys/usb/usbus/dfu/dfu.c diff --git a/sys/Makefile.dep b/sys/Makefile.dep index af860973f8..adef24f7cb 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -976,6 +976,12 @@ ifneq (,$(filter usbus_hid,$(USEMODULE))) USEMODULE += usbus endif +ifneq (,$(filter usbus_dfu,$(USEMODULE))) + FEATURES_REQUIRED += riotboot + USEMODULE += usbus + USEMODULE += riotboot_slot +endif + ifneq (,$(filter uuid,$(USEMODULE))) USEMODULE += hashes USEMODULE += random diff --git a/sys/Makefile.include b/sys/Makefile.include index 38bf6afeac..f9c46b14ad 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -130,3 +130,8 @@ endif ifneq (,$(filter prng,$(USEMODULE))) include $(RIOTBASE)/sys/random/Makefile.include endif + +ifneq (,$(filter usbus_dfu,$(USEMODULE))) + CFLAGS += -DCPU_RAM_BASE=$(RAM_START_ADDR) + CFLAGS += -DCPU_RAM_SIZE=$(RAM_LEN) +endif diff --git a/sys/include/usb/dfu.h b/sys/include/usb/dfu.h new file mode 100644 index 0000000000..2ad7f6b01a --- /dev/null +++ b/sys/include/usb/dfu.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @defgroup usb_dfu DFU - USB Device Firmware Upgrade + * @ingroup usb + * @brief Generic USB DFU defines and helpers + * + * @{ + * + * @file + * @brief Definition for USB DFU interfaces + * + * @author Dylan Laduranty + */ + +#ifndef USB_DFU_H +#define USB_DFU_H + +#include + +#include "usb.h" +#include "usb/descriptor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define USB_IF_DESCRIPTOR_DFU 0x21 /**< USB DFU type descriptor*/ +#define USB_DFU_VERSION_BCD 0x0110 /**< USB DFU version in BCD */ + +/** + * @name Default USB detach timeout for DFU descriptor + * @{ + */ +#ifndef USB_DFU_DETACH_TIMEOUT_MS +#define USB_DFU_DETACH_TIMEOUT_MS 255 +#endif +/** @} */ + +/** + * @name USB DFU interface attributes + * @{ + */ +#define USB_DFU_CAN_DOWNLOAD 0x01 /**< DFU Download attribute */ +#define USB_DFU_CAN_UPLOAD 0x02 /**< DFU Upload attribute */ +#define USB_DFU_MANIFEST_TOLERANT 0x04 /**< DFU Manifest tolerant attribute */ +#define USB_DFU_WILL_DETACH 0x08 /**< DFU Detach capabability attribute */ +/** @} */ + +/** + * @name USB DFU interface type + * @{ + */ +#define USB_DFU_INTERFACE 0xFE /** Application Specific Interface */ +/** @} */ + +/** + * @name USB DFU subclass types + * @anchor usb_dfu_subtype + * @{ + */ +#define USB_DFU_SUBCLASS_DFU 0x01 /**< DFU subclass */ +/** @} */ + +/** + * @name USB DFU protocol types + * @{ + */ +#define USB_DFU_PROTOCOL_RUNTIME_MODE 0x01 /**< Runtime mode */ +#define USB_DFU_PROTOCOL_DFU_MODE 0x02 /**< DFU mode */ +/** @} */ + +/** + * @name USB DFU setup request + * @{ + */ +#define DFU_DETACH 0x00 /**< DFU Detach request */ +#define DFU_DOWNLOAD 0x01 /**< DFU Download request */ +#define DFU_UPLOAD 0x02 /**< DFU Upload request */ +#define DFU_GET_STATUS 0x03 /**< DFU Get Status request */ +#define DFU_CLR_STATUS 0x04 /**< DFU Clear Status request */ +#define DFU_GET_STATE 0x05 /**< DFU Get State request */ +#define DFU_ABORT 0x06 /**< DFU Abort request */ +/** @} */ + +/** + * @brief USBUS DFU internal state + */ +typedef enum { + USB_DFU_STATE_APP_IDLE, /**< DFU application idle */ + USB_DFU_STATE_APP_DETACH, /**< DFU application detach (reboot to DFU mode) */ + USB_DFU_STATE_DFU_IDLE, /**< DFU runtime mode idle */ + USB_DFU_STATE_DFU_DL_SYNC, /**< DFU download synchronization */ + USB_DFU_STATE_DFU_DL_BUSY, /**< DFU download busy */ + USB_DFU_STATE_DFU_DL_IDLE, /**< DFU download idle */ + USB_DFU_STATE_DFU_MANIFEST_SYNC, /**< DFU manifest synchronization */ + USB_DFU_STATE_DFU_MANIFEST, /**< DFU manifest mode */ + USB_DFU_STATE_DFU_MANIFEST_WAIT_RST, /**< DFU manifest wait for CPU reset */ + USB_DFU_STATE_DFU_UP_IDLE, /**< DFU upload idle */ + USB_DFU_STATE_DFU_ERROR /**< DFU internal error */ +} usb_dfu_state_t; + +/** + * @brief USB DFU interface descriptor + */ +typedef struct __attribute__((packed)) { + uint8_t length; /**< Descriptor length */ + uint8_t type; /**< Descriptor type */ + uint8_t attribute; /**< Descriptor attributes flags */ + uint16_t detach_timeout; /**< Descriptor detach timeout (ms) */ + uint16_t xfer_size; /**< Descriptor transaction size */ + uint16_t bcd_dfu; /**< Descriptor bcd version */ +} usb_desc_if_dfu_t; + +/** + * @brief USB DFU get_status control request packet + */ +typedef struct __attribute__((packed)) { + uint8_t status; /**< DFU status response */ + uint32_t timeout : 24; /**< DFU timeout (ms) response */ + uint8_t state; /**< DFU internal state machine */ + uint8_t string; /**< DFU string */ +} dfu_get_status_pkt_t; + +#ifdef __cplusplus +} +#endif + +#endif /* USB_DFU_H */ +/** @} */ diff --git a/sys/include/usb/usbus/dfu.h b/sys/include/usb/usbus/dfu.h new file mode 100644 index 0000000000..6751b7ab92 --- /dev/null +++ b/sys/include/usb/usbus/dfu.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @defgroup usbus_dfu USBUS DFU - USB Device Firmware Upgrade + * @ingroup usb + * @brief Specific USB DFU defines and helpers for USBUS + * + * @{ + * + * @author Dylan Laduranty + */ + +#ifndef USB_USBUS_DFU_H +#define USB_USBUS_DFU_H + +#include "usb/dfu.h" +#include "riotboot/flashwrite.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief USBUS DFU device interface context + */ +typedef struct usbus_dfu_device { + usbus_handler_t handler_ctrl; /**< Control interface handler */ + usbus_interface_t iface; /**< Control interface */ + usbus_descr_gen_t dfu_descr; /**< DFU descriptor generator */ + usbus_string_t slot0_str; /**< Descriptor string for Slot 0 */ +#ifdef MODULE_RIOTBOOT_USB_DFU + usbus_interface_alt_t iface_alt_slot1; /**< Alt interface for secondary slot */ + usbus_string_t slot1_str; /**< Descriptor string for Slot 1 */ + riotboot_flashwrite_t writer; /**< DFU firmware update state structure */ +#endif + bool skip_signature; /**< Skip RIOTBOOT signature status */ + usbus_t *usbus; /**< Ptr to the USBUS context */ + unsigned mode; /**< 0 - APP mode, 1 DFU mode */ + unsigned selected_slot; /**< Slot used for upgrade */ + usb_dfu_state_t dfu_state; /**< Internal DFU state machine */ +} usbus_dfu_device_t; + +/** + * @brief DFU initialization function + * + * @param usbus USBUS thread to use + * @param handler DFU device struct + * @param mode DFU start mode (0 runtime mode / 1 dfu mode) + */ +void usbus_dfu_init(usbus_t *usbus, usbus_dfu_device_t *handler, unsigned mode); + +#ifdef __cplusplus +} +#endif + +#endif /* USB_USBUS_DFU_H */ +/** @} */ diff --git a/sys/usb/usbus/Makefile b/sys/usb/usbus/Makefile index c33c0aed0d..85b28d39be 100644 --- a/sys/usb/usbus/Makefile +++ b/sys/usb/usbus/Makefile @@ -7,6 +7,9 @@ endif ifneq (,$(filter usbus_cdc_acm,$(USEMODULE))) DIRS += cdc/acm endif +ifneq (,$(filter usbus_dfu,$(USEMODULE))) + DIRS += dfu/ +endif ifneq (,$(filter usbus_hid,$(USEMODULE))) DIRS += hid endif diff --git a/sys/usb/usbus/dfu/Makefile b/sys/usb/usbus/dfu/Makefile new file mode 100644 index 0000000000..110e5288a1 --- /dev/null +++ b/sys/usb/usbus/dfu/Makefile @@ -0,0 +1,4 @@ +MODULE = usbus_dfu +SRC = dfu.c + +include $(RIOTBASE)/Makefile.base diff --git a/sys/usb/usbus/dfu/dfu.c b/sys/usb/usbus/dfu/dfu.c new file mode 100644 index 0000000000..272118f00b --- /dev/null +++ b/sys/usb/usbus/dfu/dfu.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2020 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup usbus_dfu + * @{ + * @file USBUS implementation for device firmware upgrade + * + * + * @author Dylan Laduranty + * @} + */ + +#define USB_H_USER_IS_RIOT_INTERNAL + +#include "usb/dfu.h" +#include "usb/descriptor.h" +#include "usb/usbus.h" +#include "usb/usbus/control.h" +#include "usb/usbus/dfu.h" +#include "riotboot/usb_dfu.h" +#ifdef MODULE_RIOTBOOT_USB_DFU +#include "xtimer.h" +#endif +#include "periph/pm.h" + +#include "riotboot/slot.h" + +#include + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static void _event_handler(usbus_t *usbus, usbus_handler_t *handler, + usbus_event_usb_t event); +static int _control_handler(usbus_t *usbus, usbus_handler_t *handler, + usbus_control_request_state_t state, + usb_setup_t *setup); +static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler, + usbdev_ep_t *ep, usbus_event_transfer_t event); +static void _init(usbus_t *usbus, usbus_handler_t *handler); + +#ifdef MODULE_RIOTBOOT_USB_DFU +static void _reboot(void *arg); +static xtimer_t scheduled_reboot = { .callback=_reboot }; +#define REBOOT_DELAY 2 +#endif + +#define DEFAULT_XFER_SIZE 64 + +static size_t _gen_dfu_descriptor(usbus_t *usbus, void *arg) +{ + (void)arg; + usb_desc_if_dfu_t if_desc; + + /* functional dfu descriptor */ + if_desc.length = sizeof(usb_desc_if_dfu_t); + if_desc.type = USB_IF_DESCRIPTOR_DFU; + if_desc.attribute = USB_DFU_WILL_DETACH | USB_DFU_CAN_DOWNLOAD; + if_desc.detach_timeout = USB_DFU_DETACH_TIMEOUT_MS; + if_desc.xfer_size = DEFAULT_XFER_SIZE; + if_desc.bcd_dfu = USB_DFU_VERSION_BCD; + + usbus_control_slicer_put_bytes(usbus, (uint8_t *)&if_desc, sizeof(if_desc)); + return sizeof(usb_desc_if_dfu_t); +} + +static const usbus_handler_driver_t dfu_driver = { + .init = _init, + .event_handler = _event_handler, + .transfer_handler = _transfer_handler, + .control_handler = _control_handler, +}; + +/* Descriptors */ +static const usbus_descr_gen_funcs_t _dfu_descriptor = { + .fmt_post_descriptor = _gen_dfu_descriptor, + .fmt_pre_descriptor = NULL, + .len = { + .fixed_len = sizeof(usb_desc_if_dfu_t), + }, + .len_type = USBUS_DESCR_LEN_FIXED, +}; + +#ifdef MODULE_RIOTBOOT_USB_DFU +static void _reboot(void *arg) +{ + (void)arg; + pm_reboot(); +} +#endif + +void usbus_dfu_init(usbus_t *usbus, usbus_dfu_device_t *handler, unsigned mode) +{ + DEBUG("DFU: initialization\n"); + assert(usbus); + assert(handler); + assert((SLOT0_OFFSET % FLASHPAGE_SIZE) == 0); + memset(handler, 0, sizeof(usbus_dfu_device_t)); + handler->usbus = usbus; + handler->handler_ctrl.driver = &dfu_driver; + handler->mode = mode; + handler->selected_slot = UINT32_MAX; + handler->skip_signature = true; + handler->dfu_state = (handler->mode == USB_DFU_PROTOCOL_DFU_MODE) ? + USB_DFU_STATE_DFU_IDLE : USB_DFU_STATE_APP_IDLE; + + usbus_register_event_handler(usbus, (usbus_handler_t *)handler); +} + +static void _init(usbus_t *usbus, usbus_handler_t *handler) +{ + usbus_dfu_device_t *dfu = (usbus_dfu_device_t*) handler; + /* Set up descriptor generators */ + dfu->dfu_descr.next = NULL; + dfu->dfu_descr.funcs = &_dfu_descriptor; + dfu->dfu_descr.arg = dfu; + + /* Configure Interface 0 as control interface */ + dfu->iface.class = USB_DFU_INTERFACE; + dfu->iface.subclass = USB_DFU_SUBCLASS_DFU; + + dfu->iface.protocol = dfu->mode; + + dfu->iface.descr_gen = &dfu->dfu_descr; + dfu->iface.handler = handler; + + /* Create needed string descriptor for the interface and its alternate settings */ + if (IS_ACTIVE(MODULE_RIOTBOOT_USB_DFU)) { + usbus_add_string_descriptor(usbus, &dfu->slot0_str, USB_DFU_MODE_SLOT0_NAME); + } + else { + usbus_add_string_descriptor(usbus, &dfu->slot0_str, USB_APP_MODE_SLOT_NAME); + } + + /* Add string descriptor to the interface */ + dfu->iface.descr = &dfu->slot0_str; + +#ifdef MODULE_RIOTBOOT_USB_DFU + /* Create needed string descriptor for the alternate settings */ + usbus_add_string_descriptor(usbus, &dfu->slot1_str, USB_DFU_MODE_SLOT1_NAME); + + /* Add string descriptor to the alternate settings */ + dfu->iface_alt_slot1.descr = &dfu->slot1_str; + + /* attached alternate settings to their interface */ + usbus_add_interface_alt(&dfu->iface, &dfu->iface_alt_slot1); + + /* Start xtimer for scheduled reboot after firmware upgrade */ + xtimer_init(); +#endif + /* Add interface to the stack */ + usbus_add_interface(usbus, &dfu->iface); + usbus_handler_set_flag(handler, USBUS_HANDLER_FLAG_RESET); +} + +static void _dfu_class_control_req(usbus_t *usbus, usbus_dfu_device_t *dfu, usb_setup_t *pkt) +{ + static const usbopt_enable_t disable = USBOPT_DISABLE; + + DEBUG("DFU control request:%x\n", pkt->request); + switch (pkt->request) { + case DFU_DETACH: + /* Detach USB bus */ + usbdev_set(usbus->dev, USBOPT_ATTACH, &disable, sizeof(usbopt_enable_t)); + /* Restart and jump into the bootloader */ + uint32_t *reset_addr = (uint32_t *)RIOTBOOT_DFU_ADDR; + *reset_addr = RIOTBOOT_MAGIC_NUMBER; + pm_reboot(); + break; +#ifdef MODULE_RIOTBOOT_USB_DFU + case DFU_DOWNLOAD: + /* Host indicates end of firmware download */ + if (pkt->length == 0) { + /* Set DFU to manifest sync */ + dfu->dfu_state = USB_DFU_STATE_DFU_MANIFEST_SYNC; + riotboot_flashwrite_flush(&dfu->writer); + riotboot_flashwrite_finish(&dfu->writer); + } + else if (dfu->dfu_state != USB_DFU_STATE_DFU_DL_SYNC) { + dfu->dfu_state = USB_DFU_STATE_DFU_DL_SYNC; + } + else { + /* Retrieve firmware data */ + size_t len = 0; + uint8_t *data = usbus_control_get_out_data(usbus, &len); + /* skip writing the riotboot signature */ + if (dfu->skip_signature) { + riotboot_flashwrite_init(&dfu->writer, dfu->selected_slot); + len -= RIOTBOOT_FLASHWRITE_SKIPLEN; + dfu->skip_signature = false; + riotboot_flashwrite_putbytes(&dfu->writer, + &data[RIOTBOOT_FLASHWRITE_SKIPLEN], + len, true); + } + else { + riotboot_flashwrite_putbytes(&dfu->writer, data, len, true); + } + } + break; +#endif + case DFU_GET_STATUS: + { + dfu_get_status_pkt_t buf; + + if (dfu->dfu_state == USB_DFU_STATE_DFU_DL_SYNC) { + dfu->dfu_state = USB_DFU_STATE_DFU_DL_IDLE; + DEBUG("GET STATUS GO TO IDLE\n"); + } + else if (dfu->dfu_state == USB_DFU_STATE_DFU_MANIFEST_SYNC) { + /* Scheduled reboot, so we can answer back dfu-util before rebooting */ + dfu->dfu_state = USB_DFU_STATE_DFU_DL_IDLE; +#ifdef MODULE_RIOTBOOT_USB_DFU + xtimer_set(&scheduled_reboot, 1 * US_PER_SEC); +#endif + } + memset(&buf, 0, sizeof(buf)); + buf.status = 0; + buf.timeout = USB_DFU_DETACH_TIMEOUT_MS; + buf.state = dfu->dfu_state; + /* Send answer to host */ + usbus_control_slicer_put_bytes(usbus, (uint8_t*)&buf, sizeof(buf)); + DEBUG("send answer\n"); + break; + } + case DFU_CLR_STATUS: + DEBUG("CLRSTATUS To be implemented\n"); + break; + default: + DEBUG("Unhandled DFU control request:%d\n", pkt->request); + } +} + +static int _control_handler(usbus_t *usbus, usbus_handler_t *handler, + usbus_control_request_state_t state, + usb_setup_t *setup) +{ + (void)usbus; + (void)state; + usbus_dfu_device_t *dfu = (usbus_dfu_device_t *)handler; + DEBUG("DFU: Request: 0x%x\n", setup->request); + + /* Process DFU class request */ + if (setup->type & USB_SETUP_REQUEST_TYPE_CLASS) { + _dfu_class_control_req(usbus, dfu, setup); + } + else { + switch (setup->request) { + case USB_SETUP_REQ_SET_INTERFACE: + DEBUG("DFU: Select alt interface %d\n", setup->value); + dfu->selected_slot = (unsigned)setup->value; + break; + + default: + return -1; + } + } + return 1; +} + +static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler, + usbdev_ep_t *ep, usbus_event_transfer_t event) +{ + (void)event; + (void)usbus; + (void)handler; + (void)ep; +} + +static void _event_handler(usbus_t *usbus, usbus_handler_t *handler, + usbus_event_usb_t event) +{ + (void) usbus; + (void) handler; + switch (event) { + default: + DEBUG("Unhandled event :0x%x\n", event); + break; + } +} From 9d02efb4ea20fe15a7fb2d4d4f84b9969c0d8b57 Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:12:22 +0100 Subject: [PATCH 2/8] cpu/cortexm_common: increase RIOTBOOT_LEN size when usbus_dfu is used --- cpu/cortexm_common/Makefile.include | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cpu/cortexm_common/Makefile.include b/cpu/cortexm_common/Makefile.include index 72dac672a8..eef3eb0092 100644 --- a/cpu/cortexm_common/Makefile.include +++ b/cpu/cortexm_common/Makefile.include @@ -70,8 +70,12 @@ else endif # Configure riotboot bootloader and slot lengths -# 4KB are currently enough -RIOTBOOT_LEN ?= 0x1000 +# 4KB are currently enough, set it to 16KB if USB-DFU is used +ifneq (,$(filter usbus_dfu,$(USEMODULE))) + RIOTBOOT_LEN ?= 0x4000 +else + RIOTBOOT_LEN ?= 0x1000 +endif # Currently 2 slots are supported by default, equals in length NUM_SLOTS ?= 2 # Take the whole flash minus RIOTBOOT_LEN and divide it by NUM_SLOTS From f3908bed9f275c3e507e49062f57f78f949cd0bb Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:13:11 +0100 Subject: [PATCH 3/8] sys/auto_init: add support for USB DFU --- sys/auto_init/usb/auto_init_usb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sys/auto_init/usb/auto_init_usb.c b/sys/auto_init/usb/auto_init_usb.c index 176aa8819c..21ae458d73 100644 --- a/sys/auto_init/usb/auto_init_usb.c +++ b/sys/auto_init/usb/auto_init_usb.c @@ -35,6 +35,10 @@ usbus_cdcecm_device_t cdcecm; #ifdef MODULE_USBUS_CDC_ACM #include "usb/usbus/cdc/acm.h" #endif +#ifdef MODULE_USBUS_DFU +#include "usb/usbus/dfu.h" +static usbus_dfu_device_t dfu; +#endif static char _stack[USBUS_STACKSIZE]; static usbus_t usbus; @@ -58,6 +62,10 @@ void auto_init_usb(void) usbus_cdcecm_init(&usbus, &cdcecm); #endif +#ifdef MODULE_USBUS_DFU + usbus_dfu_init(&usbus, &dfu, USB_DFU_PROTOCOL_RUNTIME_MODE); +#endif + /* Finally initialize USBUS thread */ usbus_create(_stack, USBUS_STACKSIZE, USBUS_PRIO, USBUS_TNAME, &usbus); } From beb0fb752c5d0b3a4bff534e0a778786f647c26f Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:14:18 +0100 Subject: [PATCH 4/8] sys/riotboot: add DFU support to riotboot --- sys/Makefile.dep | 6 +++ sys/include/riotboot/usb_dfu.h | 69 ++++++++++++++++++++++++++++++++++ sys/riotboot/usb_dfu.c | 41 ++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 sys/include/riotboot/usb_dfu.h create mode 100644 sys/riotboot/usb_dfu.c diff --git a/sys/Makefile.dep b/sys/Makefile.dep index adef24f7cb..575c7db8da 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -1002,6 +1002,12 @@ ifneq (,$(filter riotboot_hdr, $(USEMODULE))) USEMODULE += riotboot endif +ifneq (,$(filter riotboot_usb_dfu, $(USEMODULE))) + USEMODULE += usbus_dfu + USEMODULE += riotboot_flashwrite + FEATURES_REQUIRED += no_idle_thread +endif + ifneq (,$(filter irq_handler,$(USEMODULE))) USEMODULE += event endif diff --git a/sys/include/riotboot/usb_dfu.h b/sys/include/riotboot/usb_dfu.h new file mode 100644 index 0000000000..3eab034041 --- /dev/null +++ b/sys/include/riotboot/usb_dfu.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_riotboot_usb_dfu Initialization of USB Device Firmware + * Upgrade for riotboot + * @ingroup sys + * @{ + * + * @file + * @brief USB DFU initialization for riotboot + * + * @author Dylan Laduranty + * + * @} + */ + +#ifndef RIOTBOOT_USB_DFU_H +#define RIOTBOOT_USB_DFU_H + +#include "riotboot/hdr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name USB DFU RAM information for riotboot + * @{ + */ +#ifndef RIOTBOOT_DFU_ADDR +#define RIOTBOOT_DFU_ADDR (CPU_RAM_BASE + CPU_RAM_SIZE - 4) /**< DFU default magic address */ +#endif +#ifndef RIOTBOOT_MAGIC_NUMBER +#define RIOTBOOT_MAGIC_NUMBER RIOTBOOT_MAGIC /**< DFU default magic value */ +#endif +/** @} */ + +/** + * @name USB DFU Default slots name + * @{ + */ +#ifndef USB_DFU_MODE_SLOT0_NAME +#define USB_DFU_MODE_SLOT0_NAME "RIOT-OS Slot 0" +#endif +#ifndef USB_DFU_MODE_SLOT1_NAME +#define USB_DFU_MODE_SLOT1_NAME "RIOT-OS Slot 1" +#endif +#ifndef USB_APP_MODE_SLOT_NAME +#define USB_APP_MODE_SLOT_NAME "RIOT-OS bootloader" +#endif + +/** @} */ + +/** + * @brief Initialize usbus DFU for riotboot bootloader + */ +void riotboot_usb_dfu_init(unsigned forced); + +#ifdef __cplusplus +} +#endif + +#endif /* RIOTBOOT_USB_DFU_H */ diff --git a/sys/riotboot/usb_dfu.c b/sys/riotboot/usb_dfu.c new file mode 100644 index 0000000000..5d9028ad1b --- /dev/null +++ b/sys/riotboot/usb_dfu.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_riotboot_usb_dfu + * @{ + * + * @file + * @brief USB Device Firmware Upgrade initialization for riotboot + * + * @author Dylan Laduranty + * + * @} + */ + +#include "cpu.h" +#include "thread.h" + +#include "usb/usbus.h" +#include "usb/dfu.h" +#include "usb/usbus/dfu.h" +#include "riotboot/usb_dfu.h" + +static usbus_dfu_device_t dfu; +static char _stack[USBUS_STACKSIZE]; +static usbus_t usbus; + +void riotboot_usb_dfu_init(unsigned forced) { + uint32_t *reset_addr = (uint32_t *)RIOTBOOT_DFU_ADDR; + if (forced == 1 || *reset_addr == RIOTBOOT_MAGIC_NUMBER) { + *reset_addr = 0; + usbus_init(&usbus, usbdev_get_ctx(0)); + usbus_dfu_init(&usbus, &dfu, USB_DFU_PROTOCOL_DFU_MODE); + usbus_create(_stack, USBUS_STACKSIZE, USBUS_PRIO, USBUS_TNAME, &usbus); + } +} From e87bb6a0f9b7078515169dd745cca3c63f03c5b1 Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:14:43 +0100 Subject: [PATCH 5/8] bootloader: create new bootloader application with DFU support --- bootloaders/riotboot_dfu/Makefile | 46 +++++++++++++++++++++ bootloaders/riotboot_dfu/main.c | 66 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 bootloaders/riotboot_dfu/Makefile create mode 100644 bootloaders/riotboot_dfu/main.c diff --git a/bootloaders/riotboot_dfu/Makefile b/bootloaders/riotboot_dfu/Makefile new file mode 100644 index 0000000000..5c5b1b08f5 --- /dev/null +++ b/bootloaders/riotboot_dfu/Makefile @@ -0,0 +1,46 @@ +# Default RIOT bootloader +APPLICATION = riotboot_dfu + +# Default testing board +BOARD ?= samr21-xpro + +# Select the boards with riotboot feature +FEATURES_REQUIRED += riotboot + +# Set RIOTBOOT_BUILD to indicate a riotboot application build +RIOTBOOT_BUILD = 1 +# Provide a define to detect if building the bootloader +CFLAGS += -DRIOTBOOT + +# Disable unused modules +CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE +DISABLE_MODULE += core_init core_msg core_panic +DISABLE_MODULE += auto_init auto_init_% + +# avoid using stdio +USEMODULE += stdio_null + +# Include riotboot flash partition functionality +USEMODULE += riotboot_slot + +# Add RIOTBOOT USB DFU integration +USEMODULE=riotboot_usb_dfu + +# Use xtimer for scheduled reboot +USEMODULE += xtimer + +# RIOT codebase +RIOTBASE ?= $(CURDIR)/../../ + +# USB device vendor and product ID +# pid.codes test VID/PID, not globally unique + +USB_VID ?= ${USB_VID_TESTING} +USB_PID ?= ${USB_PID_TESTING} + +include $(RIOTBASE)/Makefile.include + +# limit riotboot bootloader size +# TODO: Manage to set this variable for boards which already embed a +# bootloader, currently it will be overwritten +ROM_LEN := $(RIOTBOOT_LEN) diff --git a/bootloaders/riotboot_dfu/main.c b/bootloaders/riotboot_dfu/main.c new file mode 100644 index 0000000000..2ec7c67429 --- /dev/null +++ b/bootloaders/riotboot_dfu/main.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * Inria + * 2020 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup bootloaders + * @{ + * + * @file + * @brief RIOT-based bootloader with USB-DFU support + * + * @author Kaspar Schleiser + * @author Francisco Acosta + * @author Dylan Laduranty + * + * @} + */ + +#include "cpu.h" +#include "panic.h" +#include "riotboot/slot.h" +#include "riotboot/usb_dfu.h" + +void kernel_init(void) +{ + uint32_t version = 0; + int slot = -1; + + for (unsigned i = 0; i < riotboot_slot_numof; i++) { + const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i); + if (riotboot_slot_validate(i)) { + /* skip slot if metadata broken */ + continue; + } + if (riot_hdr->start_addr != riotboot_slot_get_image_startaddr(i)) { + continue; + } + if (slot == -1 || riot_hdr->version > version) { + version = riot_hdr->version; + slot = i; + } + } + + /* Flash the unused slot if magic word is set */ + riotboot_usb_dfu_init(0); + + if (slot != -1) { + riotboot_slot_jump(slot); + } + + /* Nothing to boot, stay in DFU mode to flash a slot */ + riotboot_usb_dfu_init(1); +} + +NORETURN void core_panic(core_panic_t crash_code, const char *message) +{ + (void)crash_code; + (void)message; + while (1) {} +} From 25d0dffa61db6369569c1cbc353b1a5cd8bece6f Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:15:20 +0100 Subject: [PATCH 6/8] Kconfig: add Kconfig support for DFU --- sys/usb/usbus/Kconfig | 1 + sys/usb/usbus/dfu/Kconfig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 sys/usb/usbus/dfu/Kconfig diff --git a/sys/usb/usbus/Kconfig b/sys/usb/usbus/Kconfig index 823c195e47..f1ce681ba2 100644 --- a/sys/usb/usbus/Kconfig +++ b/sys/usb/usbus/Kconfig @@ -43,5 +43,6 @@ config USBUS_EP0_SIZE_64 endchoice rsource "cdc/Kconfig" +rsource "dfu/Kconfig" endif # KCONFIG_USEMODULE_USBUS diff --git a/sys/usb/usbus/dfu/Kconfig b/sys/usb/usbus/dfu/Kconfig new file mode 100644 index 0000000000..55039b7427 --- /dev/null +++ b/sys/usb/usbus/dfu/Kconfig @@ -0,0 +1,35 @@ + +# Copyright (c) 2020 Mesotic SAS +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# +menuconfig KCONFIG_USEMODULE_USBUS_DFU + bool "Configure USBUS DFU" + depends on USEMODULE_USBUS_DFU + help + Configure the USBUS DFU module via Kconfig. + +if KCONFIG_USEMODULE_USBUS_DFU + +config USB_DFU_DETACH_TIMEOUT_MS + int + range 0 65535 + prompt "DFU detach timeout (ms)" + default 255 + help + Indicates the detach timeout USB device should advertise to + the host USB. Host USB should abort the pending operation if + device doesn't detach after this timeout. + +config CUSTOM_RIOTBOOT_DFU_ADDR + bool "Use custom DFU magic address" + help + Say n to use the default address, which is the last in RAM. + +config RIOTBOOT_DFU_ADDR + int "DFU magic address" + depends on CUSTOM_RIOTBOOT_DFU_ADDR + +endif # KCONFIG_USEMODULE_USBUS_DFU From bca340d338eab5fdf1b5a036b3d31b7615cc81ad Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:17:05 +0100 Subject: [PATCH 7/8] cpu/nrf52: update RIOTBOOT_LEN w/ MODULE_USBUS_DFU --- cpu/nrf52/Makefile.include | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cpu/nrf52/Makefile.include b/cpu/nrf52/Makefile.include index cc3f0622e9..1fa7f149cf 100644 --- a/cpu/nrf52/Makefile.include +++ b/cpu/nrf52/Makefile.include @@ -1,7 +1,11 @@ # Slot size is determined by "((total_flash_size - RIOTBOOT_LEN) / 2)". # If RIOTBOOT_LEN uses an uneven number of flashpages, the remainder of the # flash cannot be divided by two slots while staying FLASHPAGE_SIZE aligned. -RIOTBOOT_LEN ?= 0x2000 +ifneq (,$(filter usbus_dfu,$(USEMODULE))) + RIOTBOOT_LEN ?= 0x4000 +else + RIOTBOOT_LEN ?= 0x2000 +endif # Export internal ROM alignment and slot sizes for bootloader support export MCUBOOT_IMAGE_ALIGN = 8 From 7fc5bb483bb5e6b554cbd209a0117df1c97db7e1 Mon Sep 17 00:00:00 2001 From: dylad Date: Tue, 15 Dec 2020 17:17:44 +0100 Subject: [PATCH 8/8] makefiles/dfu-util: automatically generate DFU_USB_ID --- makefiles/tools/dfu-util.inc.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/tools/dfu-util.inc.mk b/makefiles/tools/dfu-util.inc.mk index 6ff3dc05ae..6b0e28bc37 100644 --- a/makefiles/tools/dfu-util.inc.mk +++ b/makefiles/tools/dfu-util.inc.mk @@ -3,6 +3,7 @@ FLASHER ?= $(DFU) FLASHFILE ?= $(BINFILE) DFU_ALT ?= 0 +DFU_USB_ID ?= ${USB_VID}:${USB_PID} ROM_OFFSET ?= 0 _ROM_ADDR_WITH_OFFSET ?= $(shell printf "0x%x" $$(($(ROM_START_ADDR) + $(ROM_OFFSET))))