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

Merge pull request #15460 from dylad/pr/usbus/dfu_riotboot

usbus/dfu: add Device Firmware Upgrade support for USBUS (2nd attempt)
This commit is contained in:
Koen Zandberg 2021-01-12 12:04:58 +01:00 committed by GitHub
commit 2e2f950ca7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 786 additions and 3 deletions

View File

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

View File

@ -0,0 +1,66 @@
/*
* 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"
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) {}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

@ -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 <dylan.laduranty@mesotic.com>
*
* @}
*/
#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 */

136
sys/include/usb/dfu.h Normal file
View File

@ -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 <dylan.laduranty@mesotic.com>
*/
#ifndef USB_DFU_H
#define USB_DFU_H
#include <stdint.h>
#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 */
/** @} */

View File

@ -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 <dylan.laduranty@mesotic.com>
*/
#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 */
/** @} */

41
sys/riotboot/usb_dfu.c Normal file
View File

@ -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 <dylan.laduranty@mesotic.com>
*
* @}
*/
#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);
}
}

View File

@ -43,5 +43,6 @@ config USBUS_EP0_SIZE_64
endchoice
rsource "cdc/Kconfig"
rsource "dfu/Kconfig"
endif # KCONFIG_USEMODULE_USBUS

View File

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

35
sys/usb/usbus/dfu/Kconfig Normal file
View File

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

View File

@ -0,0 +1,4 @@
MODULE = usbus_dfu
SRC = dfu.c
include $(RIOTBASE)/Makefile.base

285
sys/usb/usbus/dfu/dfu.c Normal file
View File

@ -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 <dylan.laduranty@mesotic.com>
* @}
*/
#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 <string.h>
#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;
}
}