From a4f870f25b5550231fd15902d9801e664f5c5710 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 24 Nov 2022 15:13:43 +0100 Subject: [PATCH] bootloaders: add riotboot_tinyusb_dfu bootloader --- bootloaders/riotboot_tinyusb_dfu/Makefile | 19 +++++ bootloaders/riotboot_tinyusb_dfu/doc.txt | 86 +++++++++++++++++++++++ bootloaders/riotboot_tinyusb_dfu/main.c | 86 +++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 bootloaders/riotboot_tinyusb_dfu/Makefile create mode 100644 bootloaders/riotboot_tinyusb_dfu/doc.txt create mode 100644 bootloaders/riotboot_tinyusb_dfu/main.c diff --git a/bootloaders/riotboot_tinyusb_dfu/Makefile b/bootloaders/riotboot_tinyusb_dfu/Makefile new file mode 100644 index 0000000000..82b3508791 --- /dev/null +++ b/bootloaders/riotboot_tinyusb_dfu/Makefile @@ -0,0 +1,19 @@ +# Default RIOT bootloader +APPLICATION = riotboot_tinyusb_dfu + +# Add RIOTBOOT tinyUSB DFU integration +USEMODULE += riotboot_tinyusb_dfu + +# Use xtimer for scheduled reboot +USEMODULE += ztimer +USEMODULE += ztimer_init + +# USB device vendor and product ID +# pid.codes test VID/PID, not globally unique + +# The VID/PID pair allocated for the RIOT bootloader +# as allocated on https://pid.codes/1209/7D02/ +USB_VID ?= 1209 +USB_PID ?= 7D02 + +include ../riotboot_common.mk diff --git a/bootloaders/riotboot_tinyusb_dfu/doc.txt b/bootloaders/riotboot_tinyusb_dfu/doc.txt new file mode 100644 index 0000000000..64fc6df43f --- /dev/null +++ b/bootloaders/riotboot_tinyusb_dfu/doc.txt @@ -0,0 +1,86 @@ +/** +@defgroup pkg_tinyusb_dfu riotboot tinyUSB DFU +@ingroup pkg_tinyusb +@ingroup bootloaders + +# Overview + +`riotboot_tinyusb_dfu` is a variation of @ref bootloader_riotboot that adds +USB device firmware upgrade (DFU) based on the tinyUSB device stack package. +It uses a board's USB interface to allow firmware upgrades from inside the +bootloader. + +At startup, the DFU mode is entered when either + +- none of the slots contains a valid firmware image, or +- the first button was pressed when the board started (configurable at board + level using @ref BTN_BOOTLOADER_PIN), or +- the last running firmware asked the bootloader to go to DFU mode by using a + magic number (see @ref RIOTBOOT_MAGIC_ADDR). + +# Prerequisites + +- The board must have functional USB support and has to support the + `tinyusb_device` feature. +- The board must have functional `riotboot` support, see + @ref bootloader_riotboot. + +# Flashing riotboot_tinyusb_dfu + +The `riotboot_tinyusb_dfu` bootloader can be flashed using a regular programmer +like any other application: + +``` +$ make -C bootloaders/riotboot_tinyusb_dfu BOARD=... all flash +``` + +Depending on your setup, you may need to select the right `PROGRAMMER` +(and its details) in addition to your board. + +# DFU mode + +A device in riotboot DFU mode can be recognized in the USB device list by +its VID/PID pair 1209:7d02: + +``` +$ lsusb +Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub +Bus 001 Device 005: ID 138a:003f [...] +Bus 001 Device 004: ID 8087:0a2b [...] +Bus 001 Device 045: ID 1209:7d02 Generic USB device +Bus 001 Device 001: ID 1d6b:0002 [...] +``` + +When running in DFU mode, the bootloader allows writing to either of the +two firmware slots. + +When the device is attached and in DFU mode (or the current firmware uses the +`tinyusb_dfu` module), new firmware can be flashed to slot 0 using: + +``` +$ FEATURES_REQUIRED+=riotboot USEMODULE+=tinyusb_dfu make -C examples/saul BOARD=particle-xenon \ + PROGRAMMER=dfu-util USB_VID=1209 USB_PID=7d02 all riotboot/flash-slot0 +``` + +Instead of setting `USB_VID` and `USB_PID`, the variable `DFU_USB_ID` could also +be used to specify the DFU device to be used. + +``` +$ FEATURES_REQUIRED+=riotboot USEMODULE+=tinyusb_dfu make -C examples/saul BOARD=particle-xenon \ + PROGRAMMER=dfu-util DFU_USB_ID=1209:7d02 all riotboot/flash-slot0 +``` + +Note that when building and flashing a different slot (e.g. `flash-slot1`), +not only is the image built for that slot, but also `dfu-util` gets passed +`--alt 1` (via the `DFU_ALT` build variable) to store it in the right place. + +# Entering DFU mode + +When RIOT applications are built with `USEMODULE=tinyusb_dfu`, +they implement what is called "runtime mode" in DFU. + +In runtime mode, it is visible to the `dfu-util` that they are upgradable. +On firmware upgrades, the build system can send a command via USB to enter +DFU mode. This can also be done manually using `dfu-util -e`. + +*/ diff --git a/bootloaders/riotboot_tinyusb_dfu/main.c b/bootloaders/riotboot_tinyusb_dfu/main.c new file mode 100644 index 0000000000..e8be5935de --- /dev/null +++ b/bootloaders/riotboot_tinyusb_dfu/main.c @@ -0,0 +1,86 @@ +/* + * 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" +#include "ztimer.h" + +#include "riotboot/bootloader_selection.h" + +#ifdef BTN_BOOTLOADER_PIN +#include "periph/gpio.h" +#endif + +static bool _bootloader_alternative_mode(void) +{ +#ifdef BTN_BOOTLOADER_PIN + gpio_init(BTN_BOOTLOADER_PIN, BTN_BOOTLOADER_MODE); + return (bool)gpio_read(BTN_BOOTLOADER_PIN) != BTN_BOOTLOADER_INVERTED; +#else + return false; +#endif +} + +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; + } + } + + /* Init ztimer before starting DFU mode */ + ztimer_init(); + + /* Flash the unused slot if magic word is set */ + riotboot_usb_dfu_init(0); + + if (slot != -1 && !_bootloader_alternative_mode()) { + 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) {} +}