From 8320aa6c5559dc83a4d73040879e82d5afa6a25d Mon Sep 17 00:00:00 2001
From: Simon Egli <simon.egli@westermo.com>
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 <sample_usbd.h>
+
+#include <zephyr/kernel.h>
+#include <zephyr/usb/usbd.h>
+#include <zephyr/usb/class/usbd_dfu.h>
+#include <zephyr/storage/disk_access.h>
+#include <zephyr/dfu/mcuboot.h>
+#include <zephyr/drivers/uart.h>
+#include <zephyr/device.h>
+
+#include <zephyr/logging/log.h>
+#include <zephyr/toolchain.h>
+#include <zephyr/sys/reboot.h>
+#include <pm_config.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+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

