1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00
19010: bootloaders/riotboot: add tinyUSB DFU support r=benpicco a=gschorcht

### Contribution description

This PR provides
- the tinyUSB DFU and DFU Runtime support and
- the `riotboot_tinyusb_dfu` bootloader that uses the tinyUSB DFU mode to flash new application images.

~This PR includes PR #18983 for now to be compilable.~

### Testing procedure

1. Use any board that supports the `riotboot´ and `tinyusb_device` features and flash the bootloader first, for example
   ```
   BOARD=nucleo-f767zi make -C bootloaders/riotboot_tinyusb_dfu flash
   ```
   and check that the `riotboot_tinyusb_dfu` bootloader is in DFU mode:
   ```
   dfu-util --list
   ```
3. Flash a first application using the following command:
    ```
   FEATURES_REQUIRED=riotboot USEMODULE=tinyusb_dfu BOARD=nucleo-f767zi \
   make -C tests/saul PROGRAMMER=dfu-util riotboot/flash-slot0
   ```
   and check that the application starts and is seen as upgradable:
   ```
   dfu-util --list
   ```
4. Restart the node in bootloader DFU mode by:
   ```
   dfu-util -e
   ```
   Flash a second application, for example
   ```
   FEATURES_REQUIRED=riotboot USEMODULE=tinyusb_dfu BOARD=nucleo-f767zi \
   make -C tests/shell PROGRAMMER=dfu-util riotboot/flash-slot1
   ```
   and check that the second application starts and is seen as upgradable:
   ```
   dfu-util --list
   ```
   
### Issues/PRs references

~Depends on PR #18983~

19149: SECURITY: Describe that declassification is an option r=benpicco a=chrysn

### Contribution description

Our security policy does not contain provisions for the case when what is reported is not what we consider an actual security issue. As it is described now, everything reported through security@ would go through the full treatment, including a point release.

I'm not sure it belongs into the text itself (as it's more about how security reporters interact with the project than internals), but declassification should IMO be backed at least by 3 maintainers, and no strong NACK.

### Issues/PRs references

#19141 followed that procedure after some chat on it on the maintainers channel. (In the discussion, I proposed declassification, with 2.5 people supporting it and one "I was about to, but can we be sure nobody is using it?" voice).

Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
Co-authored-by: chrysn <chrysn@fsfe.org>
This commit is contained in:
bors[bot] 2023-01-15 23:31:00 +00:00 committed by GitHub
commit 9ff9704fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 743 additions and 38 deletions

View File

@ -84,6 +84,10 @@ ifneq (,$(filter tinydtls_sock_dtls, $(USEMODULE)))
USEPKG += tinydtls
endif
ifneq (,$(filter tinyusb_%, $(USEMODULE)))
USEPKG += tinyusb
endif
# always select gpio (until explicit dependencies are sorted out)
FEATURES_OPTIONAL += periph_gpio

View File

@ -20,6 +20,14 @@ bottom of this file.
[security-gpg]: https://riot-os.org/assets/keys/security.asc
### Classification of a vulnerability
Unless the reporter explicitly requests not to do so,
the RIOT security maintainers may declassify an issue
if the issue is not deemed critical --
for example when it requires an unlikely combination of circumstances and/or configuration options,
or when it can only be exploited by a user who gains no additional privileges.
## Notification of a Vulnerability
After a fix is provided the security issue will be privately disclosed to the

View File

@ -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

View File

@ -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`.
*/

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
* @author Francisco Acosta <francisco.acosta@inria.fr>
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
*
* @}
*/
#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) {}
}

View File

@ -70,8 +70,8 @@ else
endif
# Configure riotboot bootloader and slot lengths
# 4KB are currently enough, set it to 16KB if USB-DFU is used
ifneq (,$(filter usbus_dfu,$(USEMODULE)))
# 4KB are currently enough, set it to 16KB if USB-DFU or tinyUSB DFU is used
ifneq (,$(filter usbus_dfu tinyusb_dfu,$(USEMODULE)))
RIOTBOOT_LEN ?= 0x4000
else
RIOTBOOT_LEN ?= 0x1000

View File

@ -1,7 +1,7 @@
# 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.
ifneq (,$(filter usbus_dfu,$(USEMODULE)))
ifneq (,$(filter usbus_dfu tinyusb_dfu,$(USEMODULE)))
RIOTBOOT_LEN ?= 0x4000
else
RIOTBOOT_LEN ?= 0x2000

View File

@ -1,6 +1,7 @@
ifneq (,$(filter usbus_dfu,$(USEMODULE)))
ifeq (,$(filter riotboot_usb_dfu,$(USEMODULE)))
# If module usbus_dfu is used but not module riotboot_usb_dfu, the
ifneq (,$(filter usbus_dfu tinyusb_dfu,$(USEMODULE)))
ifeq (,$(filter riotboot_usb_dfu riotboot_tinyusb_dfu,$(USEMODULE)))
# If module usbus_dfu or module tinyusb_dfu is used but neither
# module riotboot_usb_dfu nor module riotboot_tinyusb_dfu, the
# application uses DFU Runtime and dfu-util as programmer to flash the
# application with the bootloader riotboot_dfu which uses the VID/PID pair
# allocated for the RIOT bootloader https://pid.codes/1209/7D02/

View File

@ -17,6 +17,7 @@ config HAS_TINYUSB_HOST
menuconfig PACKAGE_TINYUSB
bool "TinyUSB stack package"
depends on TEST_KCONFIG
depends on HAS_ARCH_32BIT
depends on HAS_TINYUSB_DEVICE || HAS_TINYUSB_HOST
select MODULE_FMT
@ -90,8 +91,8 @@ if PACKAGE_TINYUSB
config MODULE_AUTO_INIT_TINYUSB
bool "Auto-initialize the tinyUSB package"
default y
depends on MODULE_AUTO_INIT
default y
help
The tinyUSB stack including the used peripherals are initialized
automatically at startup. Additionally, the auto-initialization
@ -160,15 +161,8 @@ menu "Device Classes"
depends on MODULE_TINYUSB_DEVICE
rsource "Kconfig.cdc"
config MODULE_TINYUSB_CLASS_DFU
bool "Device Firmware Update (DFU) Runtime"
depends on MODULE_TINYUSB_DEVICE
config MODULE_TINYUSB_CLASS_DFU_RUNTIME
bool "Device Firmware Update (DFU)"
depends on MODULE_TINYUSB_DEVICE
rsource "dfu/Kconfig.dfu"
rsource "dfu/Kconfig.dfu_rt"
rsource "Kconfig.hid"
rsource "Kconfig.msc"
@ -224,6 +218,29 @@ config TUSBD_USE_CUSTOM_DESC
interface. In all other cases, custom descriptors must be implemented
and handled.
config MODULE_TINYUSB_DFU
bool "tinyUSB DFU driver module"
select MODULE_TINYUSB_CLASS_DFU if MODULE_RIOTBOOT_TINYUSB_DFU
select MODULE_TINYUSB_CLASS_DFU_RUNTIME if !MODULE_RIOTBOOT_TINYUSB_DFU
help
Enable tinyUSB Device Firmware Upgrade driver implementation used
either in DFU mode by the bootloader or in DFU runtime mode by the
application. It is enabled by default, if the tinyUSB DFU variant
of the riotboot bootloader is used.
config MODULE_RIOTBOOT_TINYUSB_DFU
# TODO move to sys/riotboot/Kconfig once it is modelled
bool "tinyUSB DFU variant of riotboot bootloader"
depends on HAS_NO_IDLE_THREAD
depends on HAS_PERIPH_PM
select MODULE_RIOTBOOT_FLASHWRITE
select MODULE_TINYUSB_DFU
select MODULE_TINYUSB_CLASS_DFU
select MODULE_ZTIMER_SEC
help
Enable this option to use the tinyUSB DFU variant of the riotboot
bootloader.
endif # MODULE_TINYUSB_DEVICE
endif # PACKAGE_TINYUSB

View File

@ -20,6 +20,9 @@ stdio_tinyusb_cdc_acm:
tinyusb_contrib:
$(QQ)"$(MAKE)" -C $(RIOTPKG)/$(PKG_NAME)/contrib
tinyusb_dfu:
$(QQ)"$(MAKE)" -C $(RIOTPKG)/$(PKG_NAME)/dfu
tinyusb_hw:
$(QQ)"$(MAKE)" -C $(RIOTPKG)/$(PKG_NAME)/hw

View File

@ -8,12 +8,28 @@ USEMODULE += tinyusb_hw
DEFAULT_MODULE += auto_init_tinyusb
ifneq (,$(filter riotboot_tinyusb_dfu, $(USEMODULE)))
FEATURES_REQUIRED += no_idle_thread
FEATURES_REQUIRED += periph_pm
USEMODULE += riotboot_flashwrite
USEMODULE += tinyusb_dfu
USEMODULE += ztimer_sec
endif
ifneq (,$(filter stdio_tinyusb_cdc_acm, $(USEMODULE)))
USEMODULE += stdio_available
USEMODULE += tinyusb_class_cdc
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_dfu,$(USEMODULE)))
ifneq (,$(filter riotboot_tinyusb_dfu,$(USEMODULE)))
USEMODULE += tinyusb_class_dfu
else
USEMODULE += tinyusb_class_dfu_runtime
endif
endif
ifeq (,$(filter tinyusb_class_%,$(USEMODULE)))
$(error At least one tinyusb_class_* module has to be enabled)
endif
@ -30,31 +46,31 @@ endif
# Following device classes work only with tinyUSB device stack
ifneq (,$(filter tinyusb_class_audio,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_bth,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_dfu,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_dfu_runtime,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_midi,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_net_ecm_rndis,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_net_ncm,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_usbtmc,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
ifneq (,$(filter tinyusb_class_video,$(USEMODULE)))
FEATURES_REQUIRED += tinyusb_device
USEMODULE += tinyusb_device
endif
# tinyUSB hardware driver selection

View File

@ -33,3 +33,7 @@ endif
ifneq (,$(filter tinyusb_class_net_ecm_rndis,$(USEMODULE)))
INCLUDES += -I$(PKGDIRBASE)/tinyusb/lib/networking
endif
ifneq (,$(filter tinyusb_dfu,$(USEMODULE)))
INCLUDES += -I$(RIOTBASE)/pkg/tinyusb/dfu/include
endif

View File

@ -41,6 +41,12 @@ enum {
TUSBD_ITF_CDC_1, /**< CDC1 Notification interface */
TUSBD_ITF_CDC_1_DATA, /**< CDC1 Data interface */
#endif
#if CONFIG_TUSBD_DFU_NUMOF
TUSBD_ITF_DFU, /**< DFU interface */
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
TUSBD_ITF_DFU_RT, /**< DFU Runtime interface */
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
TUSBD_ITF_HID_0, /**< HID0 interface */
#endif
@ -101,6 +107,13 @@ enum {
#if CONFIG_TUSBD_CDC_NUMOF > 1
TUSBD_STR_IDX_CDC_1,
#endif
#if CONFIG_TUSBD_DFU_NUMOF
TUSBD_STR_IDX_DFU_SLOT_0,
TUSBD_STR_IDX_DFU_SLOT_1,
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
TUSBD_STR_IDX_DFU_RT,
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
TUSBD_STR_IDX_HID_0,
#endif
@ -117,9 +130,14 @@ enum {
};
#endif /* !defined(HAVE_TUSBD_STR_IDX_TYPE) */
/* only two slots are supported */
#define CONFIG_TUSBD_DFU_ALT_NUMOF 2
#if !defined(TUSBD_DESC_TOTAL_LEN)
#define TUSBD_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + \
(CONFIG_TUSBD_CDC_NUMOF * TUD_CDC_DESC_LEN) + \
(CONFIG_TUSBD_DFU_NUMOF * TUD_DFU_DESC_LEN(CONFIG_TUSBD_DFU_ALT_NUMOF)) + \
(CONFIG_TUSBD_DFU_RT_NUMOF * TUD_DFU_RT_DESC_LEN) + \
(CONFIG_TUSBD_HID_NUMOF * TUD_HID_INOUT_DESC_LEN) + \
(CONFIG_TUSBD_MSC_NUMOF * TUD_MSC_DESC_LEN) + \
(CONFIG_TUSBD_VENDOR_NUMOF * TUD_VENDOR_DESC_LEN))
@ -127,6 +145,8 @@ enum {
#define TUSBD_DESC_ALT_TOTAL_LEN (TUD_CONFIG_DESC_LEN + \
(CONFIG_TUSBD_CDC_NUMOF * TUD_CDC_DESC_LEN) + \
(CONFIG_TUSBD_DFU_NUMOF * TUD_DFU_DESC_LEN(CONFIG_TUSBD_DFU_ALT_NUMOF)) + \
(CONFIG_TUSBD_DFU_RT_NUMOF * TUD_DFU_RT_DESC_LEN) + \
(CONFIG_TUSBD_HID_NUMOF * TUD_HID_INOUT_DESC_LEN) + \
(CONFIG_TUSBD_MSC_NUMOF * TUD_MSC_DESC_LEN) + \
(CONFIG_TUSBD_VENDOR_NUMOF * TUD_VENDOR_DESC_LEN))

View File

@ -75,7 +75,7 @@
#endif
#ifndef CONFIG_TUSBD_DFU_RT_NUMOF
#if MODULE_TINYUSB_DEVICE && MODULE_TINYUSB_CLASS_DFU_RT
#if MODULE_TINYUSB_DEVICE && MODULE_TINYUSB_CLASS_DFU_RUNTIME
#define CONFIG_TUSBD_DFU_RT_NUMOF 1
#else
#define CONFIG_TUSBD_DFU_RT_NUMOF 0
@ -186,10 +186,6 @@
#endif
#endif
#ifndef CONFIG_TUSBD_CDC_NOTIF_EP_SIZE
#define CONFIG_TUSBD_CDC_NOTIF_EP_SIZE 8
#endif
#ifndef CONFIG_TUSBD_EP0_SIZE
#define CONFIG_TUSBD_EP0_SIZE 64
#endif
@ -202,6 +198,10 @@
#define CONFIG_TUSBD_HS_EP_SIZE 512
#endif
#ifndef CONFIG_TUSBD_CDC_NOTIF_EP_SIZE
#define CONFIG_TUSBD_CDC_NOTIF_EP_SIZE 8
#endif
#ifndef CONFIG_TUSBD_CDC_FS_EP_SIZE
#define CONFIG_TUSBD_CDC_FS_EP_SIZE CONFIG_TUSBD_FS_EP_SIZE
#endif
@ -210,6 +210,48 @@
#define CONFIG_TUSBD_CDC_HS_EP_SIZE CONFIG_TUSBD_HS_EP_SIZE
#endif
#ifndef CONFIG_TUSBD_DFU_ATTR
#define CONFIG_TUSBD_DFU_ATTR (DFU_ATTR_CAN_DOWNLOAD | \
DFU_ATTR_WILL_DETACH | \
DFU_ATTR_MANIFESTATION_TOLERANT)
#endif
#ifndef CONFIG_TUSBD_DFU_DETACH_TIMEOUT
#define CONFIG_TUSBD_DFU_DETACH_TIMEOUT 1000
#endif
#ifndef CONFIG_TUSBD_DFU_POLL_TIMEOUT
#define CONFIG_TUSBD_DFU_POLL_TIMEOUT 1
#endif
#ifndef CONFIG_TUSBD_DFU_RESET_DELAY
#define CONFIG_TUSBD_DFU_RESET_DELAY 2
#endif
#ifndef CONFIG_TUSBD_DFU_FS_XFER_SIZE
#define CONFIG_TUSBD_DFU_FS_XFER_SIZE CONFIG_TUSBD_FS_EP_SIZE
#endif
#ifndef CONFIG_TUSBD_DFU_HS_XFER_SIZE
#define CONFIG_TUSBD_DFU_HS_XFER_SIZE CONFIG_TUSBD_HS_EP_SIZE
#endif
#ifndef CONFIG_TUSBD_HID_EP_SIZE
#define CONFIG_TUSBD_HID_EP_SIZE CONFIG_TUSBD_FS_EP_SIZE
#endif
#ifndef CONFIG_TUSBD_DFU_RT_DETACH_TIMEOUT
#define CONFIG_TUSBD_DFU_RT_DETACH_TIMEOUT 1000
#endif
#ifndef CONFIG_TUSBD_DFU_RT_FS_XFER_SIZE
#define CONFIG_TUSBD_DFU_RT_FS_XFER_SIZE CONFIG_TUSBD_FS_EP_SIZE
#endif
#ifndef CONFIG_TUSBD_DFU_RT_HS_XFER_SIZE
#define CONFIG_TUSBD_DFU_RT_HS_XFER_SIZE CONFIG_TUSBD_HS_EP_SIZE
#endif
#ifndef CONFIG_TUSBD_HID_EP_SIZE
#define CONFIG_TUSBD_HID_EP_SIZE CONFIG_TUSBD_FS_EP_SIZE
#endif
@ -263,7 +305,9 @@
#define CFG_TUSB_OS OPT_OS_CUSTOM
/** Debug log level */
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
/**
* @brief DMA memory section and alignment
@ -337,8 +381,8 @@
* @name Typical required DFU device class configurations
* @{
*/
#define CFG_TUD_DFU_XFER_BUFSIZE (TUD_OPT_HIGH_SPEED ? CONFIG_TUSBD_HS_EP_SIZE \
: CONFIG_TUSBD_FS_EP_SIZE)
#define CFG_TUD_DFU_XFER_BUFSIZE (TUD_OPT_HIGH_SPEED ? CONFIG_TUSBD_DFU_HS_XFER_SIZE \
: CONFIG_TUSBD_DFU_FS_XFER_SIZE)
/** @} */
/**

View File

@ -75,7 +75,11 @@ int tinyusb_setup(void)
if ((res = thread_create(_tinyusb_thread_stack,
sizeof(_tinyusb_thread_stack),
TINYUSB_PRIORITY,
#if MODULE_RIOTBOOT_TINYUSB_DFU
THREAD_CREATE_STACKTEST,
#else
THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST,
#endif
_tinyusb_thread_impl, NULL, "tinyusb")) < 0) {
LOG_ERROR("tinyUSB thread couldn't be created, reason %d\n", res);
return res;

View File

@ -30,6 +30,10 @@
#include "tusb.h"
#include "usb.h"
#ifdef MODULE_TINYUSB_DFU
#include "riotboot/usb_dfu.h"
#endif
#include "tinyusb_descriptors.h"
#define ENABLE_DEBUG 0
@ -40,16 +44,17 @@
#if (MODULE_TINYUSB_CLASS_AUDIO || \
MODULE_TINYUSB_CLASS_BTH || \
MODULE_TINYUSB_CLASS_DFU || \
MODULE_TINYUSB_CLASS_DFU_RUNTIME || \
MODULE_TINYUSB_CLASS_MIDI || \
MODULE_TINYUSB_CLASS_NET_ECM_RNDIS || \
MODULE_TINYUSB_CLASS_NET_NCM || \
MODULE_TINYUSB_CLASS_USBTMC || \
MODULE_TINYUSB_CLASS_VIDEO || \
(CONFIG_TUSBD_CDC_NUMOF > 2) || \
(CONFIG_TUSBD_DFU_NUMOF > 1) || \
(CONFIG_TUSBD_DFU_RT_NUMOF > 1) || \
(CONFIG_TUSBD_HID_NUMOF > 2) || \
(CONFIG_TUSBD_MSC_NUMOF > 1))
(CONFIG_TUSBD_MSC_NUMOF > 1) || \
(CONFIG_TUSBD_VENDOR_NUMOF > 1))
#error Using generic descriptors is not possible for the selected combination \
of device class interfaces. Custom descriptors have to be implemented.
#endif
@ -226,6 +231,24 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id,
speed ? CONFIG_TUSBD_CDC_HS_EP_SIZE \
: CONFIG_TUSBD_CDC_FS_EP_SIZE)
#define _TUD_DFU_DESC(speed) \
/* Interface number, alternate count, starting string index, attributes,
* detach timeout, transfer size */ \
TUD_DFU_DESCRIPTOR(TUSBD_ITF_DFU, CONFIG_TUSBD_DFU_ALT_NUMOF, \
TUSBD_STR_IDX_DFU_SLOT_0, CONFIG_TUSBD_DFU_ATTR, \
CONFIG_TUSBD_DFU_DETACH_TIMEOUT, \
speed ? CONFIG_TUSBD_DFU_HS_XFER_SIZE \
: CONFIG_TUSBD_DFU_FS_XFER_SIZE)
#define _TUD_DFU_RT_DESC(speed) \
/* Interface number, alternate count, starting string index, attributes,
* detach timeout, transfer size */ \
TUD_DFU_RT_DESCRIPTOR(TUSBD_ITF_DFU_RT, \
TUSBD_STR_IDX_DFU_RT, DFU_ATTR_WILL_DETACH, \
CONFIG_TUSBD_DFU_RT_DETACH_TIMEOUT, \
speed ? CONFIG_TUSBD_DFU_RT_HS_XFER_SIZE \
: CONFIG_TUSBD_DFU_RT_FS_XFER_SIZE)
#define _TUD_HID_INOUT_DESC(speed, n) \
/* Interface number, string index, protocol, report descriptor len,
* EP Out & EP In address, EP size, polling interval */ \
@ -260,6 +283,12 @@ uint8_t const tusb_desc_fs_config[] = {
#if CONFIG_TUSBD_CDC_NUMOF > 1
_TUD_CDC_DESC(_tusb_speed_fs, 1),
#endif
#if CONFIG_TUSBD_DFU_NUMOF
_TUD_DFU_DESC(_tusb_speed_fs),
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
_TUD_DFU_RT_DESC(_tusb_speed_fs),
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
_TUD_HID_INOUT_DESC(_tusb_speed_fs, 0),
#endif
@ -284,6 +313,12 @@ uint8_t const tusb_desc_fs_config_alt[] = {
#if CONFIG_TUSBD_CDC_NUMOF > 1
_TUD_CDC_DESC(_tusb_speed_fs, 1),
#endif
#if CONFIG_TUSBD_DFU_NUMOF
_TUD_DFU_DESC(_tusb_speed_fs),
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
_TUD_DFU_RT_DESC(_tusb_speed_fs),
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
_TUD_HID_INOUT_DESC(_tusb_speed_fs, 0),
#endif
@ -313,6 +348,12 @@ uint8_t const tusb_desc_hs_config[] = {
#if CONFIG_TUSBD_CDC_NUMOF > 1
_TUD_CDC_DESC(_tusb_speed_hs, 1),
#endif
#if CONFIG_TUSBD_DFU
_TUD_DFU_DESC(_tusb_speed_hs),
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
_TUD_DFU_RT_DESC(_tusb_speed_hs),
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
_TUD_HID_INOUT_DESC(_tusb_speed_hs, 0),
#endif
@ -337,6 +378,12 @@ uint8_t const tusb_desc_hs_config_alt[] = {
#if CONFIG_TUSBD_CDC_NUMOF > 1
_TUD_CDC_DESC(_tusb_speed_hs, 1),
#endif
#if CONFIG_TUSBD_DFU
_TUD_DFU_DESC(_tusb_speed_hs),
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
_TUD_DFU_RT_DESC(_tusb_speed_hs),
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
_TUD_HID_INOUT_DESC(_tusb_speed_hs, 0),
#endif
@ -488,6 +535,18 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
#define CONFIG_TUSBD_CDC_1_STRING "TinyUSB CDC1"
#endif
#ifndef CONFIG_TUSBD_DFU_0_STRING
#define CONFIG_TUSBD_DFU_0_STRING USB_DFU_MODE_SLOT0_NAME
#endif
#ifndef CONFIG_TUSBD_DFU_1_STRING
#define CONFIG_TUSBD_DFU_1_STRING USB_DFU_MODE_SLOT1_NAME
#endif
#ifndef CONFIG_TUSBD_DFU_RT_STRING
#define CONFIG_TUSBD_DFU_RT_STRING USB_APP_MODE_SLOT_NAME
#endif
#ifndef CONFIG_TUSBD_HID_0_STRING
#define CONFIG_TUSBD_HID_0_STRING "TinyUSB HID0 (Generic In/Out)"
#endif
@ -524,6 +583,13 @@ char const* tusb_string_desc_array[] = {
#if CONFIG_TUSBD_CDC_NUMOF > 1
CONFIG_TUSBD_CDC_1_STRING, /* CDC Interface 1 */
#endif
#if CONFIG_TUSBD_DFU_NUMOF
CONFIG_TUSBD_DFU_0_STRING, /* DFU Firmware Slot 0 */
CONFIG_TUSBD_DFU_1_STRING, /* DFU Firmware Slot 1 */
#endif
#if CONFIG_TUSBD_DFU_RT_NUMOF
CONFIG_TUSBD_DFU_RT_STRING, /* APP mode */
#endif
#if CONFIG_TUSBD_HID_NUMOF > 0
CONFIG_TUSBD_HID_0_STRING, /* HID Interface 0 */
#endif

View File

@ -0,0 +1,50 @@
# Copyright (c) 2022 Gunar Schorcht
#
# 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 MODULE_TINYUSB_CLASS_DFU
bool "Device Firmware Update (DFU)"
depends on MODULE_TINYUSB_DEVICE && MODULE_TINYUSB_DFU && MODULE_RIOTBOOT_TINYUSB_DFU
if MODULE_TINYUSB_CLASS_DFU
config TUSBD_DFU_NUMOF
int
default 1
config TUSBD_DFU_FS_XFER_SIZE
int "DFU Full-Speed transfer size [byte]"
default 64
config TUSBD_DFU_HS_XFER_SIZE
int "DFU High-Speed transfer size [byte]"
default 512
config TUSBD_DFU_ALT_NUMOF
int
default 2
help
Number of alternative DFU firmware slots.
config TUSBD_DFU_DETACH_TIMEOUT
int "DFU detach timeout [ms]"
default 1000
config TUSBD_DFU_POLL_TIMEOUT
int "DFU poll timeout [ms]"
default 1
help
DFU Poll Timeout is the time before the host requests the status
from the device during a firmware download or manifestation operation.
config TUSBD_DFU_RESET_DELAY
int "DFU reset delay [s]"
default 2
help
DFU reset delay is the time before the device is restarted after
a firmware download.
endif # MODULE_TINYUSB_CLASS_DFU

View File

@ -0,0 +1,30 @@
# Copyright (c) 2022 Gunar Schorcht
#
# 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 MODULE_TINYUSB_CLASS_DFU_RUNTIME
bool "Device Firmware Update Runtime (DFU Runtime)"
depends on MODULE_TINYUSB_DEVICE && MODULE_TINYUSB_DFU && !MODULE_RIOTBOOT_TINYUSB_DFU
if MODULE_TINYUSB_CLASS_DFU_RUNTIME
config TUSBD_DFU_RT_NUMOF
int
default 1
config TUSBD_DFU_RT_FS_XFER_SIZE
int "DFU Full-Speed transfer size [byte]"
default 64
config TUSBD_DFU_RT_HS_XFER_SIZE
int "DFU High-Speed transfer size [byte]"
default 512
config TUSBD_DFU_RT_DETACH_TIMEOUT
int "DFU detach timeout [ms]"
default 1000
endif # MODULE_TINYUSB_CLASS_DFU_RUNTIME

3
pkg/tinyusb/dfu/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE = tinyusb_dfu
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 Gunar Schorcht
*
* 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 pkg_tinyusb_dfu
* @{
* @file
* @brief TinyUSB specific DFU definitions
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef TINYUSB_DFU_H
#define TINYUSB_DFU_H
#include "riotboot/flashwrite.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief tinyUSB DFU device interface context
*/
typedef struct tinyusb_dfu_device {
bool skip_signature; /**< Skip RIOTBOOT signature status */
uint8_t slot; /**< Download slot */
#ifdef MODULE_RIOTBOOT_TINYUSB_DFU
riotboot_flashwrite_t writer; /**< DFU firmware update state structure */
#endif
} tinyusb_dfu_device_t;
/**
* @brief Initialize the tinyUSB DFU device interface context
*/
void tinyusb_dfu_init(void);
#ifdef __cplusplus
}
#endif
#endif /* TINYUSB_DFU_H */
/** @} */

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2022 Gunar Schorcht
*
* 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 pkg_tinyusb_dfu
* @{
* @file TinyUSB DFU implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include "log.h"
#include "periph/pm.h"
#include "riotboot/magic.h"
#include "riotboot/slot.h"
#ifdef MODULE_RIOTBOOT_TINYUSB_DFU
#include "ztimer.h"
#endif
#include "device/usbd.h"
#include "class/dfu/dfu_device.h"
#include "tinyusb_dfu.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifdef MODULE_RIOTBOOT_TINYUSB_DFU
static void _reboot(void *arg);
static ztimer_t scheduled_reboot = { .callback=_reboot };
#endif
/* there is only one instance of DFU devices */
tinyusb_dfu_device_t _tusb_dfu_dev = { .skip_signature = true };
void tinyusb_dfu_init(void)
{
_tusb_dfu_dev.skip_signature = true;
_tusb_dfu_dev.slot = 0;
}
#ifdef MODULE_RIOTBOOT_TINYUSB_DFU
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
{
(void)alt;
(void)state;
/* Invoked before tud_dfu_download_cb() or tud_dfu_manifest_cb() is called
* to determine the poll timeout for download and manifest operation */
return CONFIG_TUSBD_DFU_POLL_TIMEOUT;
}
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length)
{
/* Invoked when a DFU_DNLOAD (wLength > 0) followed by a DFU_GETSTATUS
* (state = DFU_DNBUSY) requests was received. */
(void)block_num;
int ret = 0;
if (_tusb_dfu_dev.skip_signature) {
/* Avoid underflow condition */
if (length < RIOTBOOT_FLASHWRITE_SKIPLEN) {
return;
}
riotboot_flashwrite_init(&_tusb_dfu_dev.writer, alt);
length -= RIOTBOOT_FLASHWRITE_SKIPLEN;
DEBUG("[tinyusb_dfu] starting the download with %u bytes for slot %u "
"with block %u\n", length, alt, block_num);
_tusb_dfu_dev.skip_signature = false;
_tusb_dfu_dev.slot = alt;
ret = riotboot_flashwrite_putbytes(&_tusb_dfu_dev.writer,
&data[RIOTBOOT_FLASHWRITE_SKIPLEN],
length, true);
}
else {
assert(alt == _tusb_dfu_dev.slot);
DEBUG("[tinyusb_dfu] continue the download with %u bytes for slot %u "
"with block %u\n", length, alt, block_num);
ret = riotboot_flashwrite_putbytes(&_tusb_dfu_dev.writer,
data, length, true);
}
if (ret < 0) {
LOG_ERROR("[tinyusb_dfu] error on writing %d bytes for slot %u\n",
length, alt);
tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE);
}
else {
tud_dfu_finish_flashing(DFU_STATUS_OK);
}
}
void tud_dfu_manifest_cb(uint8_t alt)
{
/* Invoked when the download process is complete and
* DFU_DNLOAD (wLength = 0) followed by a DFU_GETSTATUS (state = Manifest)
* was received. */
(void)alt;
assert(alt == _tusb_dfu_dev.slot);
DEBUG("[tinyusb_dfu] download for slot %u complete, "
"enter manifestation phase\n", alt);
/* the host indicates that the download process is complete */
riotboot_flashwrite_flush(&_tusb_dfu_dev.writer);
riotboot_flashwrite_finish(&_tusb_dfu_dev.writer);
/* indicate that flashing is finished */
tud_dfu_finish_flashing(DFU_STATUS_OK);
/* scheduled reboot after CONFIG_TUSBD_DFU_RESET_DELAY seconds to give
* enough time to finish manifestation */
ztimer_set(ZTIMER_SEC, &scheduled_reboot, CONFIG_TUSBD_DFU_RESET_DELAY);
}
static void _reboot(void *arg)
{
DEBUG("[tinyusb_dfu] reboot\n");
(void)arg;
pm_reboot();
}
#endif /* MODULE_RIOTBOOT_TINYUSB_DFU */
TU_ATTR_WEAK void tud_dfu_detach_cb(void)
{
/* the host sent a DFU_DETACH request */
DEBUG("[tinyusb_dfu] DFU_DETACH request received\n");
uint32_t *reset_addr = (uint32_t *)RIOTBOOT_MAGIC_ADDR;
*reset_addr = RIOTBOOT_MAGIC_NUMBER;
pm_reboot();
}
void tud_dfu_runtime_reboot_to_dfu_cb(void)
{
/* the host sent a DFU_DETACH request */
tud_dfu_detach_cb();
}

View File

@ -92,6 +92,7 @@
* callbacks for any combination of
* - up to two interfaces of the class CDC ACM,
* - up to two interfaces of the Generic In/Out HID class,
* - up to one DFU interface
* - up to one MSC device interface and,
* - up to one interface of the Vendor device class.
*

View File

@ -19,7 +19,7 @@
#include "periph_conf.h"
#include "periph/gpio.h"
#include "periph/pm.h"
#include "pm_layered.h"
#include "tusb.h"
#include "device/usbd.h"

View File

@ -19,7 +19,7 @@
#include "periph_conf.h"
#include "periph/gpio.h"
#include "periph/pm.h"
#include "pm_layered.h"
#include "tusb.h"
#include "device/usbd.h"

View File

@ -869,6 +869,10 @@ ifneq (,$(filter riotboot_usb_dfu, $(USEMODULE)))
FEATURES_REQUIRED += periph_pm
endif
ifneq (,$(filter riotboot_tinyusb_dfu, $(USEMODULE)))
USEPKG += tinyusb
endif
ifneq (,$(filter irq_handler,$(USEMODULE)))
USEMODULE += event
endif

View File

@ -163,7 +163,7 @@ ifneq (,$(filter prng,$(USEMODULE)))
include $(RIOTBASE)/sys/random/Makefile.include
endif
ifneq (,$(filter usbus_dfu riotboot_reset,$(USEMODULE)))
ifneq (,$(filter tinyusb_dfu usbus_dfu riotboot_reset,$(USEMODULE)))
CFLAGS += -DCPU_RAM_BASE=$(RAM_START_ADDR)
CFLAGS += -DCPU_RAM_SIZE=$(RAM_LEN)
endif

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 Gunar Schorcht
*
* 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
* @ingroup pkg_tinyusb_dfu
* @{
* @file tinyUSB Device Firmware Upgrade initialization for riotboot
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include "riotboot/magic.h"
#include "tinyusb.h"
#include "tinyusb_dfu.h"
#define ENABLE_DEBUG 0
#include "debug.h"
extern void tinyusb_dfu_init(void);
void riotboot_usb_dfu_init(unsigned forced)
{
uint32_t *reset_addr = (uint32_t *)RIOTBOOT_MAGIC_ADDR;
if (forced == 1 || *reset_addr == RIOTBOOT_MAGIC_NUMBER) {
*reset_addr = 0;
tinyusb_setup();
tinyusb_dfu_init();
}
}