1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

pkg/tinyusb: add DFU and DFU_RT device class implementation

This commit is contained in:
Gunar Schorcht 2022-12-05 17:30:51 +01:00
parent 7fa58f74dd
commit 37b151111f
12 changed files with 343 additions and 10 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

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

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

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

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