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:
commit
2e2f950ca7
46
bootloaders/riotboot_dfu/Makefile
Normal file
46
bootloaders/riotboot_dfu/Makefile
Normal 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)
|
66
bootloaders/riotboot_dfu/main.c
Normal file
66
bootloaders/riotboot_dfu/main.c
Normal 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) {}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
69
sys/include/riotboot/usb_dfu.h
Normal file
69
sys/include/riotboot/usb_dfu.h
Normal 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
136
sys/include/usb/dfu.h
Normal 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 */
|
||||
/** @} */
|
63
sys/include/usb/usbus/dfu.h
Normal file
63
sys/include/usb/usbus/dfu.h
Normal 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
41
sys/riotboot/usb_dfu.c
Normal 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);
|
||||
}
|
||||
}
|
@ -43,5 +43,6 @@ config USBUS_EP0_SIZE_64
|
||||
endchoice
|
||||
|
||||
rsource "cdc/Kconfig"
|
||||
rsource "dfu/Kconfig"
|
||||
|
||||
endif # KCONFIG_USEMODULE_USBUS
|
||||
|
@ -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
35
sys/usb/usbus/dfu/Kconfig
Normal 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
|
4
sys/usb/usbus/dfu/Makefile
Normal file
4
sys/usb/usbus/dfu/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
MODULE = usbus_dfu
|
||||
SRC = dfu.c
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
285
sys/usb/usbus/dfu/dfu.c
Normal file
285
sys/usb/usbus/dfu/dfu.c
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user