From 8320aa6c5559dc83a4d73040879e82d5afa6a25d Mon Sep 17 00:00:00 2001 From: Simon Egli Date: Tue, 2 Dec 2025 13:01:32 +0100 Subject: [PATCH] Enable dfu via patch --- samples/nrf5340/remote_shell/CMakeLists.txt | 2 + samples/nrf5340/remote_shell/Kconfig | 33 ++ samples/nrf5340/remote_shell/src/dfu.c | 479 ++++++++++++++++++++ samples/nrf5340/remote_shell/src/dfu.h | 3 + 4 files changed, 517 insertions(+) create mode 100644 samples/nrf5340/remote_shell/src/dfu.c create mode 100644 samples/nrf5340/remote_shell/src/dfu.h diff --git a/samples/nrf5340/remote_shell/CMakeLists.txt b/samples/nrf5340/remote_shell/CMakeLists.txt index 6c5f2f46f9..2fb4937b43 100644 --- a/samples/nrf5340/remote_shell/CMakeLists.txt +++ b/samples/nrf5340/remote_shell/CMakeLists.txt @@ -9,9 +9,11 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(NONE) +include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) # NORDIC SDK APP START target_sources(app PRIVATE src/main.c + src/dfu.c src/shell_ipc_host.c ) # NORDIC SDK APP END diff --git a/samples/nrf5340/remote_shell/Kconfig b/samples/nrf5340/remote_shell/Kconfig index a5dd4d9d2f..783c029fb3 100644 --- a/samples/nrf5340/remote_shell/Kconfig +++ b/samples/nrf5340/remote_shell/Kconfig @@ -24,3 +24,36 @@ config REMOTE_SHELL_RX_RING_BUFFER_SIZE them to chosen transport medium. endmenu + +menu "USB DFU sample options" +config APP_USB_DFU_USE_FLASH_BACKEND + select FLASH + select FLASH_MAP + select STREAM_FLASH + select FLASH_HAS_PAGE_LAYOUT + select IMG_MANAGER + select IMG_ERASE_PROGRESSIVELY + bool "Option to clear the flash area before mounting" + help + Use this to force an existing file system to be created. + +if APP_USB_DFU_USE_FLASH_BACKEND + +config USBD_DFU_FLASH + default y + +config USBD_DFU_FLASH_SLOT0 + default n + +config USBD_DFU_FLASH_SLOT1 + default y + +endif + +endmenu + + +# Source common USB sample options used to initialize new experimental USB +# device stack. The scope of these options is limited to USB samples in project +# tree, you cannot use them in your own application. +source "samples/subsys/usb/common/Kconfig.sample_usbd" diff --git a/samples/nrf5340/remote_shell/src/dfu.c b/samples/nrf5340/remote_shell/src/dfu.c new file mode 100644 index 0000000000..cef8fbf0b5 --- /dev/null +++ b/samples/nrf5340/remote_shell/src/dfu.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifdef CONFIG_USBD_DFU +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(USB, LOG_LEVEL_INF); + +USBD_DEVICE_DEFINE(dfu_usbd, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + 0x2b30, 0xffff); + +USBD_DESC_LANG_DEFINE(sample_lang); +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "DFU FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "DFU HS Configuration"); + +static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ? + USB_SCD_SELF_POWERED : 0) | + (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ? + USB_SCD_REMOTE_WAKEUP : 0); +/* Full speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_fs_config, + attributes, + CONFIG_SAMPLE_USBD_MAX_POWER, &fs_cfg_desc); + +/* High speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_hs_config, + attributes, + CONFIG_SAMPLE_USBD_MAX_POWER, &hs_cfg_desc); + + +const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); + +#define RING_BUF_SIZE 1024 +uint8_t ring_buffer[RING_BUF_SIZE]; + +struct ring_buf ringbuf; + +static bool rx_throttled; +static void switch_to_dfu_mode(struct usbd_context *const ctx); + +struct dfu_ramdisk_data { + const char *name; + uint32_t last_block; + uint32_t sector_size; + uint32_t sector_count; + union { + uint32_t uploaded; + uint32_t downloaded; + }; +}; + +static struct dfu_ramdisk_data ramdisk0_data = { + .name = "image0", +}; + +static int init_dfu_ramdisk_data(struct dfu_ramdisk_data *const data) +{ + int err; + + err = disk_access_init(data->name); + if (err) { + return err; + } + + err = disk_access_status(data->name); + if (err) { + return err; + } + + err = disk_access_ioctl(data->name, DISK_IOCTL_GET_SECTOR_COUNT, &data->sector_count); + if (err) { + return err; + } + + err = disk_access_ioctl(data->name, DISK_IOCTL_GET_SECTOR_SIZE, &data->sector_size); + if (err) { + return err; + } + + LOG_INF("disk %s sector count %u sector size %u", + data->name, data->sector_count, data->sector_size); + + return err; +} + +static int ramdisk_read(void *const priv, const uint32_t block, const uint16_t size, + uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE]) +{ + struct dfu_ramdisk_data *const data = priv; + int err; + + if (size == 0) { + /* There is nothing to upload */ + return 0; + } + + if (block == 0) { + if (init_dfu_ramdisk_data(data)) { + LOG_ERR("Failed to init ramdisk data"); + return -EINVAL; + } + + data->last_block = 0; + data->uploaded = 0; + } else { + if (data->last_block + 1U != block) { + return -EINVAL; + } + + } + + if (block >= data->sector_count) { + /* Nothing to upload */ + return 0; + } + + err = disk_access_read(data->name, buf, block, 1); + if (err) { + LOG_ERR("Failed to read from RAMdisk"); + return err; + } + + data->last_block = block; + data->uploaded += MIN(size, data->sector_size); + LOG_INF("block %u size %u uploaded %u", block, size, data->uploaded); + + return size; +} + +static int ramdisk_write(void *const priv, const uint32_t block, const uint16_t size, + const uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE]) +{ + struct dfu_ramdisk_data *const data = priv; + int err; + + if (block == 0) { + if (init_dfu_ramdisk_data(data)) { + LOG_ERR("Failed to init ramdisk data"); + return -EINVAL; + } + + data->last_block = 0; + data->downloaded = 0; + } else { + if (data->last_block + 1U != block) { + return -EINVAL; + } + + } + + if (size == 0) { + /* Nothing to write */ + return 0; + } + + err = disk_access_write(data->name, buf, block, 1); + if (err) { + LOG_ERR("Failed to write to RAMdisk"); + return err; + } + + data->last_block = block; + data->downloaded += size; + LOG_INF("block %u size %u downloaded %u", block, size, data->downloaded); + + return 0; +} + +USBD_DFU_DEFINE_IMG(ramdisk0, "ramdisk0", &ramdisk0_data, ramdisk_read, ramdisk_write, NULL); + +static inline void print_baudrate(const struct device *dev) +{ + uint32_t baudrate; + int ret; + + ret = uart_line_ctrl_get(dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); + if (ret) { + LOG_WRN("Failed to get baudrate, ret code %d", ret); + } else { + LOG_INF("Baudrate %u", baudrate); + } +} + +/* New: log a formatted string to the USB console (CDC ACM UART) */ +void usb_console_log(const char *fmt, ...) +{ + char buf[260]; + int len; + va_list ap; + + if (!device_is_ready(uart_dev)) { + LOG_WRN("UART device not ready"); + return; + } + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf) - 2, fmt, ap); /* reserve space for CRLF */ + va_end(ap); + + if (len < 0) { + return; + } + if (len > (int)(sizeof(buf) - 2)) { + len = sizeof(buf) - 2; + } + + /* append CRLF (LOG_* uses CRLF) */ + buf[len++] = '\r'; + buf[len++] = '\n'; + + /* If called from ISR, do a single non-blocking attempt and return */ + if (k_is_in_isr()) { + (void)uart_fifo_fill(uart_dev, (const uint8_t *)buf, len); + return; + } + + /* Thread-context: enqueue into ring buffer and enable TX IRQ to drain it */ + size_t put = ring_buf_put(&ringbuf, (const uint8_t *)buf, len); + if (put < (size_t)len) { + LOG_WRN("usb_console_log: ring buffer full, dropped %u bytes", (unsigned)(len - put)); + } + + /* Kick the TX IRQ handler to start sending */ + uart_irq_tx_enable(uart_dev); +} + +K_SEM_DEFINE(dtr_sem, 0, 1); +static void msg_cb(struct usbd_context *const usbd_ctx, + const struct usbd_msg *const msg) +{ + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (msg->type == USBD_MSG_CONFIGURATION) { + LOG_INF("\tConfiguration value %d", msg->status); + } + + if (usbd_can_detect_vbus(usbd_ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + if (usbd_enable(usbd_ctx)) { + LOG_ERR("Failed to enable device support"); + } + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + if (usbd_disable(usbd_ctx)) { + LOG_ERR("Failed to disable device support"); + } + } + } + + if (msg->type == USBD_MSG_DFU_APP_DETACH) { + switch_to_dfu_mode(usbd_ctx); + } + + if (msg->type == USBD_MSG_DFU_DOWNLOAD_COMPLETED) { + if (IS_ENABLED(CONFIG_BOOTLOADER_MCUBOOT) && + IS_ENABLED(CONFIG_APP_USB_DFU_USE_FLASH_BACKEND)) { + LOG_INF("Requesting reboot"); + boot_request_upgrade(false); + sys_reboot(SYS_REBOOT_COLD); + } + } + + if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) { + uint32_t dtr = 0U; + + uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr); + if (dtr) { + k_sem_give(&dtr_sem); + } + } + + if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) { + print_baudrate(msg->dev); + } +} + +static void switch_to_dfu_mode(struct usbd_context *const ctx) +{ + int err; + + LOG_INF("Detach USB device"); + usbd_disable(ctx); + usbd_shutdown(ctx); + + err = usbd_add_descriptor(&dfu_usbd, &sample_lang); + if (err) { + LOG_ERR("Failed to initialize language descriptor (%d)", err); + return; + } + + if (usbd_caps_speed(&dfu_usbd) == USBD_SPEED_HS) { + err = usbd_add_configuration(&dfu_usbd, USBD_SPEED_HS, &sample_hs_config); + if (err) { + LOG_ERR("Failed to add High-Speed configuration"); + return; + } + + err = usbd_register_class(&dfu_usbd, "dfu_dfu", USBD_SPEED_HS, 1); + if (err) { + LOG_ERR("Failed to add register classes"); + return; + } + + usbd_device_set_code_triple(&dfu_usbd, USBD_SPEED_HS, 0, 0, 0); + } + + err = usbd_add_configuration(&dfu_usbd, USBD_SPEED_FS, &sample_fs_config); + if (err) { + LOG_ERR("Failed to add Full-Speed configuration"); + return; + } + + err = usbd_register_class(&dfu_usbd, "dfu_dfu", USBD_SPEED_FS, 1); + if (err) { + LOG_ERR("Failed to add register classes"); + return; + } + + usbd_device_set_code_triple(&dfu_usbd, USBD_SPEED_FS, 0, 0, 0); + + err = usbd_init(&dfu_usbd); + if (err) { + LOG_ERR("Failed to initialize USB device support"); + return; + } + + err = usbd_msg_register_cb(&dfu_usbd, msg_cb); + if (err) { + LOG_ERR("Failed to register message callback"); + return; + } + + err = usbd_enable(&dfu_usbd); + if (err) { + LOG_ERR("Failed to enable USB device support"); + } +} + + +static int output_slot(void) { + uint32_t current_addr = (uint32_t)(uintptr_t)&output_slot; + printk("Current addr: %x\n",current_addr); + + if( + current_addr > PM__MCUBOOT_PRIMARY_ADDRESS + && current_addr < PM__MCUBOOT_PRIMARY_END_ADDRESS + ) { + printk("Running from primary slot\n"); + } + + else if( + current_addr > PM__MCUBOOT_SECONDARY_ADDRESS + && current_addr < PM__MCUBOOT_SECONDARY_END_ADDRESS + ) { + printk("Running from secondary slot\n"); + } + else { + printk("Something went wrong"); + } + + return 0; +} + +static void interrupt_handler(const struct device *dev, void *user_data) +{ + ARG_UNUSED(user_data); + + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + if (!rx_throttled && uart_irq_rx_ready(dev)) { + int recv_len; + uint8_t buffer[256]; + + recv_len = uart_fifo_read(dev, buffer, sizeof(buffer)); + if (recv_len < 0) { + LOG_ERR("Failed to read UART FIFO"); + recv_len = 0; + } + + LOG_INF("Received %d bytes: %s", recv_len, buffer); + } + + if (uart_irq_tx_ready(dev)) { + uint8_t buffer[64]; + int rb_len, send_len; + + rb_len = ring_buf_get(&ringbuf, buffer, sizeof(buffer)); + if (!rb_len) { + /* Nothing to send, disable TX IRQ */ + uart_irq_tx_disable(dev); + continue; + } + + if (rx_throttled) { + uart_irq_rx_enable(dev); + rx_throttled = false; + } + + send_len = uart_fifo_fill(dev, buffer, rb_len); + if (send_len < rb_len) { + LOG_WRN("ringbuf -> tty fifo partial write %d/%d", send_len, rb_len); + /* if partial, leftover data is already removed from ringbuf; you may want to + redesign to peek before removing if loss is unacceptable */ + } + } + } +} + +int dfu_init(void) +{ + struct usbd_context *sample_usbd; + int ret; + + output_slot(); + + sample_usbd = sample_usbd_init_device(msg_cb); + if (sample_usbd == NULL) { + LOG_ERR("Failed to initialize USB device"); + return -ENODEV; + } + + if (!usbd_can_detect_vbus(sample_usbd)) { + ret = usbd_enable(sample_usbd); + if (ret) { + LOG_ERR("Failed to enable device support"); + return ret; + } + } + + ring_buf_init(&ringbuf, sizeof(ring_buffer), ring_buffer); + + LOG_INF("Wait for DTR"); + k_sem_take(&dtr_sem, K_FOREVER); + LOG_INF("DTR set"); + + /* They are optional, we use them to test the interrupt endpoint */ + ret = uart_line_ctrl_set(uart_dev, UART_LINE_CTRL_DCD, 1); + if (ret) { + LOG_WRN("Failed to set DCD, ret code %d", ret); + } + + ret = uart_line_ctrl_set(uart_dev, UART_LINE_CTRL_DSR, 1); + if (ret) { + LOG_WRN("Failed to set DSR, ret code %d", ret); + } + + /* Wait 100ms for the host to do all settings */ + k_msleep(100); + + uart_irq_callback_set(uart_dev, interrupt_handler); + /* Enable rx interrupts */ + uart_irq_rx_enable(uart_dev); + /* Enable tx interrupts so uart_fifo_fill data gets drained */ + //uart_irq_tx_enable(uart_dev); + + + LOG_INF("USB DFU sample is initialized"); + + return 0; +} +#endif /* CONFIG_USBD_DFU */ diff --git a/samples/nrf5340/remote_shell/src/dfu.h b/samples/nrf5340/remote_shell/src/dfu.h new file mode 100644 index 0000000000..0119e61fb4 --- /dev/null +++ b/samples/nrf5340/remote_shell/src/dfu.h @@ -0,0 +1,3 @@ +#pragma once +int dfu_init(void); +void usb_console_log(const char *fmt, ...); \ No newline at end of file -- 2.39.5