Skip to content

Commit

Permalink
usb: dfu: add permanent download and automatic reboot
Browse files Browse the repository at this point in the history
This commit adds the USB_DFU_PERMANENT_DOWNLOAD and USB_DFU_REBOOT and
symbols.When the permanent download symbol is enabled, slot 1 will be
marked as confirmed. With the reboot symbol enabled, the devices
automatically reboots after the download is completed.

The functionality is split into two symbols to allow the automatic
reboot without confirming the image. This enables image confirmation via
another channel. For example via the shell’s Mcuboot commands.

This functionality allows downloading an image to the device without the
user having to interact with the device. It is useful in cases where
ease of use is more important then safety. For example  when using USB
download for daily development. This is especially applicable for
devices with a closed case.

The changes were tested on an nrf52840dk. The following line can be used
to build the USB DFU example with the symbols enabled.

west build -b nrf52840dk_nrf52840 zephyr/samples/subsys/usb/dfu \
-d build-dfu -- -DCONFIG_BOOTLOADER_MCUBOOT=y \
-DOVERLAY_CONFIG=overlay-reboot-permanent.conf \
-DCONFIG_MCUBOOT_SIGNATURE_KEY_FILE\
=\"bootloader/mcuboot/root-rsa-2048.pem\"

Fixes #41921

Signed-off-by: Martijn Stommels <[email protected]>
  • Loading branch information
hackwerken authored and carlescufi committed Mar 31, 2022
1 parent 94e50a7 commit 49cb653
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 19 deletions.
3 changes: 3 additions & 0 deletions samples/subsys/usb/dfu/overlay-permanent-download.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_USB_DFU_REBOOT=y

CONFIG_USB_DFU_PERMANENT_DOWNLOAD=y
31 changes: 18 additions & 13 deletions samples/subsys/usb/dfu/sample.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
sample:
name: USB DFU sample

common:
build_only: True
platform_exclude: native_posix native_posix_64 mimxrt1010_evk
mimxrt1050_evk_qspi mimxrt1020_evk mimxrt1015_evk mimxrt1060_evk sam4l_ek
mimxrt1050_evk mimxrt1060_evk_hyperflash nucleo_f207zg teensy40 teensy41
b_u585i_iot02a
depends_on: usb_device
filter: dt_label_with_parent_compat_enabled("slot0_partition", "fixed-partitions") and
dt_label_with_parent_compat_enabled("slot1_partition", "fixed-partitions") and
dt_chosen_enabled("zephyr,flash-controller") and
CONFIG_FLASH_HAS_DRIVER_ENABLED
integration_platforms:
- nrf52840dk_nrf52840
- frdm_k64f
tests:
sample.usb.dfu:
build_only: True
platform_exclude: native_posix native_posix_64 mimxrt1010_evk
mimxrt1050_evk_qspi mimxrt1020_evk mimxrt1015_evk mimxrt1060_evk sam4l_ek
mimxrt1050_evk mimxrt1060_evk_hyperflash nucleo_f207zg teensy40 teensy41
b_u585i_iot02a
depends_on: usb_device
filter: dt_label_with_parent_compat_enabled("slot0_partition", "fixed-partitions") and
dt_label_with_parent_compat_enabled("slot1_partition", "fixed-partitions") and
dt_chosen_enabled("zephyr,flash-controller") and
CONFIG_FLASH_HAS_DRIVER_ENABLED
tags: usb
integration_platforms:
- nrf52840dk_nrf52840
- frdm_k64f
sample.usb.dfu.permanent.download:
tags: usb
extra_args: OVERLAY_CONFIG=overlay-permanent-download.conf
16 changes: 16 additions & 0 deletions subsys/usb/class/dfu/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,20 @@ config USB_DFU_ENABLE_UPLOAD
the executable image is always decrypted despite the image
encryption is enabled.

config USB_DFU_REBOOT
bool "Reboot after download"
select REBOOT
help
When enabled the device will automatically reboot after a download
so the bootloader can swap the images.

config USB_DFU_PERMANENT_DOWNLOAD
bool "Mark slot 1 as permanent after download"
help
When enabled the image written to slot 1 will be marked as permanent.
WARNING: This bypasses Mcuboot's test-confirm mechanism!
Downloading the wrong image will cause a bricked device.
Make sure there is some kind of recovery mechanism.


endif # USB_DFU_CLASS
56 changes: 50 additions & 6 deletions subsys/usb/class/dfu/usb_dfu.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <dfu/mcuboot.h>
#include <dfu/flash_img.h>
#include <sys/byteorder.h>
#include <sys/reboot.h>
#include <usb/usb_device.h>
#include <usb/class/usb_dfu.h>
#include <usb_descriptor.h>
Expand All @@ -65,15 +66,22 @@ LOG_MODULE_REGISTER(usb_dfu);

#define INTERMITTENT_CHECK_DELAY 50

#if IS_ENABLED(CONFIG_USB_DFU_REBOOT)
#define DFU_DESC_ATTRIBUTES_MANIF_TOL 0
#else
#define DFU_DESC_ATTRIBUTES_MANIF_TOL DFU_ATTR_MANIFESTATION_TOLERANT
#endif

#if IS_ENABLED(CONFIG_USB_DFU_ENABLE_UPLOAD)
#define DFU_DESC_ATTRIBUTES (DFU_ATTR_CAN_DNLOAD | \
DFU_ATTR_CAN_UPLOAD | \
DFU_ATTR_MANIFESTATION_TOLERANT)
#define DFU_DESC_ATTRIBUTES_CAN_UPLOAD DFU_ATTR_CAN_UPLOAD
#else
#define DFU_DESC_ATTRIBUTES (DFU_ATTR_CAN_DNLOAD | \
DFU_ATTR_MANIFESTATION_TOLERANT)
#define DFU_DESC_ATTRIBUTES_CAN_UPLOAD 0
#endif

#define DFU_DESC_ATTRIBUTES (DFU_ATTR_CAN_DNLOAD | \
DFU_DESC_ATTRIBUTES_CAN_UPLOAD |\
DFU_DESC_ATTRIBUTES_MANIF_TOL)

static struct k_poll_event dfu_event;
static struct k_poll_signal dfu_signal;
static struct k_timer dfu_timer;
Expand Down Expand Up @@ -371,10 +379,14 @@ static void dfu_flash_write(uint8_t *data, size_t len)
dfu_data.state = dfuERROR;
dfu_data.status = errWRITE;
} else if (!len) {
const bool should_confirm = IS_ENABLED(CONFIG_USB_DFU_PERMANENT_DOWNLOAD);

LOG_DBG("flash write done");
dfu_data.state = dfuMANIFEST_SYNC;
dfu_reset_counters();
if (boot_request_upgrade(false)) {

LOG_DBG("Should confirm: %d", should_confirm);
if (boot_request_upgrade(should_confirm)) {
dfu_data.state = dfuERROR;
dfu_data.status = errWRITE;
}
Expand All @@ -394,6 +406,28 @@ static void dfu_timer_expired(struct k_timer *timer)
}
}

#ifdef CONFIG_USB_DFU_REBOOT
static struct k_work_delayable reboot_work;

static void reboot_work_handler(struct k_work *item)
{
ARG_UNUSED(item);

sys_reboot(SYS_REBOOT_WARM);
}

static void reboot_schedule(void)
{
LOG_DBG("Scheduling reboot in 500ms");

/*
* Reboot with a delay,
* so there is some time to send the status to the host
*/
k_work_schedule_for_queue(&USB_WORK_Q, &reboot_work, K_MSEC(500));
}
#endif

static int dfu_class_handle_to_host(struct usb_setup_packet *setup,
int32_t *data_len, uint8_t **data)
{
Expand All @@ -407,7 +441,13 @@ static int dfu_class_handle_to_host(struct usb_setup_packet *setup,
dfu_data.status, dfu_data.state);

if (dfu_data.state == dfuMANIFEST_SYNC) {

#if IS_ENABLED(CONFIG_USB_DFU_REBOOT)
dfu_data.state = dfuMANIFEST_WAIT_RST;
reboot_schedule();
#else
dfu_data.state = dfuIDLE;
#endif
}

/* bStatus */
Expand Down Expand Up @@ -837,6 +877,10 @@ static int usb_dfu_init(const struct device *dev)
k_poll_signal_init(&dfu_signal);
k_timer_init(&dfu_timer, dfu_timer_expired, NULL);

#ifdef CONFIG_USB_DFU_REBOOT
k_work_init_delayable(&reboot_work, reboot_work_handler);
#endif

if (flash_area_open(dfu_data.flash_area_id, &fa)) {
return -EIO;
}
Expand Down

0 comments on commit 49cb653

Please sign in to comment.