From 0a420e1eb8664cc36469cfd8372c38be1103eff7 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Tue, 15 Oct 2024 11:58:26 +0000 Subject: [PATCH 1/6] app: add support for generating an USB DFU image Add support for generating a USB DFU image using the dfu-suffix utility from dfu-util when building the CANnectivity firmware application. Signed-off-by: Henrik Brix Andersen --- CMakeLists.txt | 2 ++ app/Kconfig | 36 ++++++++++++++++++++++++++++++++++++ cmake/cannectivity.cmake | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 cmake/cannectivity.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f339335..91d531e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,3 +4,5 @@ zephyr_include_directories(include) add_subdirectory(subsys) + +include(cmake/cannectivity.cmake) diff --git a/app/Kconfig b/app/Kconfig index 9e9f3a5..75b58b1 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -130,6 +130,42 @@ config CANNECTIVITY_TERMINATION_DEFAULT_ON help Enable CAN bus termination resistors on boot-up. +menuconfig CANNECTIVITY_GENERATE_USB_DFU_IMAGE + bool "Generate USB DFU image" + select BUILD_OUTPUT_BIN + help + Enabling this configuration allows automatic generation of an image with USB Device + Firmware Upgrade (DFU) suffix. This depends on the dfu-suffix utility from the dfu-util + software package. + +if CANNECTIVITY_GENERATE_USB_DFU_IMAGE + +config CANNECTIVITY_USB_DFU_VID + hex "USB DFU image Vendor ID (VID)" + default 0xffff + help + CANnectivity USB DFU image Vendor ID (VID). + +config CANNECTIVITY_USB_DFU_PID + hex "USB DFU image Product ID (PID)" + default 0xffff + help + CANnectivity USB DFU image Product ID (PID). + +config CANNECTIVITY_USB_DFU_DID + hex "USB DFU image Device ID" + default 0xffff + help + CANnectivity USB DFU image Device ID. + +config CANNECTIVITY_USB_DFU_SPEC_ID + hex "USB DFU image Specification ID" + default 0x0100 + help + CANnectivity USB DFU image Specification ID. + +endif # CANNECTIVITY_GENERATE_USB_DFU_IMAGE + endmenu source "Kconfig.zephyr" diff --git a/cmake/cannectivity.cmake b/cmake/cannectivity.cmake new file mode 100644 index 0000000..55e80ba --- /dev/null +++ b/cmake/cannectivity.cmake @@ -0,0 +1,38 @@ +# Copyright (c) 2024 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +function(cannectivity_generate_usb_dfu_image) + if(CONFIG_CANNECTIVITY_GENERATE_USB_DFU_IMAGE) + find_program(DFU_SUFFIX dfu-suffix) + + if(NOT ${DFU_SUFFIX} STREQUAL DFU_SUFFIX-NOTFOUND) + set(bin_image ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.bin) + set(dfu_image ${bin_image}.dfu) + get_filename_component(dfu_image_name ${dfu_image} NAME) + + add_custom_command( + OUTPUT ${dfu_image} + COMMAND ${CMAKE_COMMAND} -E copy ${bin_image} ${dfu_image} + COMMAND ${DFU_SUFFIX} + --vid ${CONFIG_CANNECTIVITY_USB_DFU_VID} + --pid ${CONFIG_CANNECTIVITY_USB_DFU_PID} + --did ${CONFIG_CANNECTIVITY_USB_DFU_DID} + --spec ${CONFIG_CANNECTIVITY_USB_DFU_SPEC_ID} + --add ${dfu_image} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${bin_image} + COMMENT "Generating ${dfu_image_name}" + ) + + add_custom_target( + cannectivity_usb_dfu_image + ALL + DEPENDS ${dfu_image} + ) + else() + message(FATAL_ERROR "The dfu-suffix utility was not found, USB DFU image cannot be generated") + endif() + endif() +endfunction() + +cannectivity_generate_usb_dfu_image() From 610263e5d226e94002a5a7291b0d03bb4b144fed Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 16 Oct 2024 11:31:24 +0000 Subject: [PATCH 2/6] app: add support for USB DFU mode via MCUboot Add support for USB Device Firmware Upgrade (DFU) mode via the MCUboot bootloader. Signed-off-by: Henrik Brix Andersen --- app/CMakeLists.txt | 4 ++ app/Kconfig | 16 +++++++ app/Kconfig.sysbuild | 46 +++++++++++++++++++ app/sample.yaml | 6 +++ app/src/cannectivity.h | 7 +++ app/src/dfu.c | 34 ++++++++++++++ app/src/main.c | 8 ++++ app/sysbuild-dfu.conf | 6 +++ app/sysbuild.cmake | 29 ++++++++++++ app/sysbuild/mcuboot/app.overlay | 11 +++++ .../mcuboot/boards/frdm_k64f_mk64f12.conf | 4 ++ app/sysbuild/mcuboot/prj.conf | 17 +++++++ cmake/cannectivity.cmake | 6 ++- west.yml | 1 + 14 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 app/Kconfig.sysbuild create mode 100644 app/src/dfu.c create mode 100644 app/sysbuild-dfu.conf create mode 100644 app/sysbuild.cmake create mode 100644 app/sysbuild/mcuboot/app.overlay create mode 100644 app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf create mode 100644 app/sysbuild/mcuboot/prj.conf diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index dd2398e..9ddf7a4 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -31,3 +31,7 @@ target_sources_ifdef(CONFIG_CANNECTIVITY_TIMESTAMP_COUNTER app PRIVATE target_sources_ifdef(CONFIG_CANNECTIVITY_TERMINATION_GPIO app PRIVATE src/termination.c ) + +target_sources_ifdef(CONFIG_BOOTLOADER_MCUBOOT app PRIVATE + src/dfu.c +) diff --git a/app/Kconfig b/app/Kconfig index 75b58b1..4fb2845 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -130,6 +130,22 @@ config CANNECTIVITY_TERMINATION_DEFAULT_ON help Enable CAN bus termination resistors on boot-up. +if BOOTLOADER_MCUBOOT + +configdefault FLASH + default y + +configdefault STREAM_FLASH + default y + +configdefault FLASH_MAP + default y + +configdefault IMG_MANAGER + default y + +endif # BOOTLOADER_MCUBOOT + menuconfig CANNECTIVITY_GENERATE_USB_DFU_IMAGE bool "Generate USB DFU image" select BUILD_OUTPUT_BIN diff --git a/app/Kconfig.sysbuild b/app/Kconfig.sysbuild new file mode 100644 index 0000000..b000c73 --- /dev/null +++ b/app/Kconfig.sysbuild @@ -0,0 +1,46 @@ +# Copyright (c) 2024 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +menuconfig CANNECTIVITY_USB_DFU + bool "CANnectivity USB DFU mode" + help + Enable support for CANnectivity USB Device Firmware Upgrade (DFU) mode using the MCUboot + bootloader. + +if CANNECTIVITY_USB_DFU + +config CANNECTIVITY_USB_DFU_MANUFACTURER + string "USB DFU mode manufacturer string" + default "CANnectivity" + help + CANnectivity USB DFU mode manufacturer string. + +config CANNECTIVITY_USB_DFU_PRODUCT + string "USB DFU mode product string" + default "CANnectivity USB to CAN adapter in DFU mode" + help + CANnectivity USB DFU mode product string. + +config CANNECTIVITY_USB_DFU_VID + hex "USB DFU mode Vendor ID (VID)" + default 0x1209 + help + CANnectivity USB DFU mode Vendor ID (VID). + +config CANNECTIVITY_USB_DFU_PID + hex "USB DFU mode Product ID (PID)" + default 0x0001 + help + CANnectivity USB DFU mode Product ID (PID). + +config CANNECTIVITY_USB_DFU_MAX_POWER + int "USB DFU mode maximum power" + default 125 + range 0 250 + help + CANnectivity USB DFU mode maximum current draw in milliampere (mA) divided by 2. + A value of 125 results in a maximum current draw value of 250 mA. + +endif # CANNECTIVITY_USB_DFU + +source "share/sysbuild/Kconfig" diff --git a/app/sample.yaml b/app/sample.yaml index 70b8bab68..292945f 100644 --- a/app/sample.yaml +++ b/app/sample.yaml @@ -56,3 +56,9 @@ tests: integration_platforms: - frdm_k64f - lpcxpresso55s16 + app.cannectivity.dfu: + sysbuild: true + depends_on: usb_device can + platform_allow: + - frdm_k64f + extra_args: SB_CONF_FILE=sysbuild-dfu.conf diff --git a/app/src/cannectivity.h b/app/src/cannectivity.h index 1f95710..d557bde 100644 --- a/app/src/cannectivity.h +++ b/app/src/cannectivity.h @@ -129,4 +129,11 @@ int cannectivity_timestamp_init(void); */ int cannectivity_usb_init(void); +/** + * @brief CANnectivity USB DFU initialization function + * + * @return 0 on success, negative error number otherwise. + */ +int cannectivity_dfu_init(void); + #endif /* CANNECTIVITY_APP_CANNECTIVITY_H_ */ diff --git a/app/src/dfu.c b/app/src/dfu.c new file mode 100644 index 0000000..0faa6fc --- /dev/null +++ b/app/src/dfu.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "cannectivity.h" + +LOG_MODULE_REGISTER(dfu, CONFIG_CANNECTIVITY_LOG_LEVEL); + +int cannectivity_dfu_init(void) +{ + int err; + + /* + * Confirm updated image if running under MCUboot booloader. This could be done on + * successful USB enumeration instead, but that could cause unwanted image reverts on + * e.g. self-powered development boards. + */ + if (!boot_is_img_confirmed()) { + err = boot_write_img_confirmed(); + if (err != 0) { + LOG_ERR("failed to confirm image (err %d)", err); + return err; + } + + LOG_INF("image confirmed"); + } + + return 0; +} diff --git a/app/src/main.c b/app/src/main.c index 1d3669d..b1e78dc 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -87,6 +87,14 @@ int main(void) return err; } + if (IS_ENABLED(CONFIG_BOOTLOADER_MCUBOOT)) { + err = cannectivity_dfu_init(); + if (err) { + LOG_ERR("failed to initialize DFU"); + return err; + } + } + LOG_INF("CANnectivity firmware initialized with %u channel%s\n", ARRAY_SIZE(channels), ARRAY_SIZE(channels) > 1 ? "s" : ""); } diff --git a/app/sysbuild-dfu.conf b/app/sysbuild-dfu.conf new file mode 100644 index 0000000..17f1e45 --- /dev/null +++ b/app/sysbuild-dfu.conf @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +SB_CONFIG_CANNECTIVITY_USB_DFU=y diff --git a/app/sysbuild.cmake b/app/sysbuild.cmake new file mode 100644 index 0000000..9ba5887 --- /dev/null +++ b/app/sysbuild.cmake @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +if(SB_CONFIG_CANNECTIVITY_USB_DFU) + if(SB_CONFIG_CANNECTIVITY_USB_DFU_VID EQUAL 0x1209) + if(SB_CONFIG_CANNECTIVITY_USB_DFU_PID LESS_EQUAL 0x0010) + message(WARNING + "SB_CONFIG_CANNECTIVITY_USB_DFU_PID is set to a generic pid.codes Test PID (${SB_CONFIG_CANNECTIVITY_USB_DFU_PID}). +This PID is not unique and should not be used outside test environments." + ) + endif() + endif() + + # Override MCUboot options + set_config_string(mcuboot CONFIG_USB_DEVICE_MANUFACTURER "${SB_CONFIG_CANNECTIVITY_USB_DFU_MANUFACTURER}") + set_config_string(mcuboot CONFIG_USB_DEVICE_PRODUCT "${SB_CONFIG_CANNECTIVITY_USB_DFU_PRODUCT}") + set_config_int(mcuboot CONFIG_USB_DEVICE_VID ${SB_CONFIG_CANNECTIVITY_USB_DFU_VID}) + set_config_int(mcuboot CONFIG_USB_DEVICE_PID ${SB_CONFIG_CANNECTIVITY_USB_DFU_PID}) + set_config_int(mcuboot CONFIG_USB_DEVICE_DFU_PID ${SB_CONFIG_CANNECTIVITY_USB_DFU_PID}) + set_config_int(mcuboot CONFIG_USB_MAX_POWER ${SB_CONFIG_CANNECTIVITY_USB_DFU_MAX_POWER}) + + # Only override CANnectivity firmware application options if the dfu-suffix utility is available + find_program(DFU_SUFFIX dfu-suffix) + if(NOT ${DFU_SUFFIX} STREQUAL DFU_SUFFIX-NOTFOUND) + set_config_bool(${DEFAULT_IMAGE} CONFIG_CANNECTIVITY_GENERATE_USB_DFU_IMAGE TRUE) + set_config_int(${DEFAULT_IMAGE} CONFIG_CANNECTIVITY_USB_DFU_VID ${SB_CONFIG_CANNECTIVITY_USB_DFU_VID}) + set_config_int(${DEFAULT_IMAGE} CONFIG_CANNECTIVITY_USB_DFU_PID ${SB_CONFIG_CANNECTIVITY_USB_DFU_PID}) + endif() +endif() diff --git a/app/sysbuild/mcuboot/app.overlay b/app/sysbuild/mcuboot/app.overlay new file mode 100644 index 0000000..b6af768 --- /dev/null +++ b/app/sysbuild/mcuboot/app.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; diff --git a/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf b/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf new file mode 100644 index 0000000..9cbb0f2 --- /dev/null +++ b/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf @@ -0,0 +1,4 @@ +# Copyright (c) 2024 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_USB_DFU_GPIO=y diff --git a/app/sysbuild/mcuboot/prj.conf b/app/sysbuild/mcuboot/prj.conf new file mode 100644 index 0000000..d83536a --- /dev/null +++ b/app/sysbuild/mcuboot/prj.conf @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_MAIN_STACK_SIZE=10240 + +CONFIG_FLASH=y +CONFIG_PM=n + +CONFIG_CBPRINTF_NANO=y +CONFIG_MINIMAL_LIBC=y + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_MCUBOOT_LOG_LEVEL_INF=y + +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" diff --git a/cmake/cannectivity.cmake b/cmake/cannectivity.cmake index 55e80ba..882dc9c 100644 --- a/cmake/cannectivity.cmake +++ b/cmake/cannectivity.cmake @@ -6,7 +6,11 @@ function(cannectivity_generate_usb_dfu_image) find_program(DFU_SUFFIX dfu-suffix) if(NOT ${DFU_SUFFIX} STREQUAL DFU_SUFFIX-NOTFOUND) - set(bin_image ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.bin) + if(CONFIG_BOOTLOADER_MCUBOOT) + set(bin_image ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.signed.bin) + else() + set(bin_image ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.bin) + endif() set(dfu_image ${bin_image}.dfu) get_filename_component(dfu_image_name ${dfu_image} NAME) diff --git a/west.yml b/west.yml index fd196bb..f2d2770 100644 --- a/west.yml +++ b/west.yml @@ -15,4 +15,5 @@ manifest: - cmsis - hal_nxp - hal_stm32 + - mcuboot - segger From 2815adcb5d7506bc6e2ab96a286ee06c92de215b Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 16 Oct 2024 14:53:19 +0000 Subject: [PATCH 3/6] app: turn off DFU LED once booted Turn off the mcuboot-led0 alias LED upon booting. This optional LED will be turned on by MCUboot when entering USB DFU mode, but it is not turned off again by MCUboot. Signed-off-by: Henrik Brix Andersen --- app/Kconfig | 8 ++++++++ app/src/dfu.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/app/Kconfig b/app/Kconfig index 4fb2845..4c1dd6b 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -132,6 +132,14 @@ config CANNECTIVITY_TERMINATION_DEFAULT_ON if BOOTLOADER_MCUBOOT +config CANNECTIVITY_DFU_LED + bool # hidden + default y + depends on $(dt_alias_enabled,mcuboot-led0) + help + Enable support for controlling the Device Firmware Upgrade (DFU) LED (identified by the + "mcuboot-led0" devicetree alias). + configdefault FLASH default y diff --git a/app/src/dfu.c b/app/src/dfu.c index 0faa6fc..b658d85 100644 --- a/app/src/dfu.c +++ b/app/src/dfu.c @@ -5,12 +5,36 @@ */ #include +#include #include #include "cannectivity.h" LOG_MODULE_REGISTER(dfu, CONFIG_CANNECTIVITY_LOG_LEVEL); +#define DFU_LED_NODE DT_ALIAS(mcuboot_led0) + +#ifdef CONFIG_CANNECTIVITY_DFU_LED +static int dfu_led_init(void) +{ + struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DFU_LED_NODE, gpios); + int err; + + if (!gpio_is_ready_dt(&led)) { + LOG_ERR("DFU LED device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE); + if (err != 0) { + LOG_ERR("failed to turn off DFU LED (err %d)", err); + return err; + } + + return 0; +} +#endif /* CONFIG_CANNECTIVITY_DFU_LED */ + int cannectivity_dfu_init(void) { int err; @@ -30,5 +54,12 @@ int cannectivity_dfu_init(void) LOG_INF("image confirmed"); } +#ifdef CONFIG_CANNECTIVITY_DFU_LED + err = dfu_led_init(); + if (err != 0) { + return err; + } +#endif /* CONFIG_CANNECTIVITY_DFU_LED */ + return 0; } From 571bb644a6db3e85065daa1d30086c36f01c9008 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 16 Oct 2024 14:56:35 +0000 Subject: [PATCH 4/6] boards: add frdm_k64f board extension Add an extension for the frdm_k64f board with the mcuboot-led0 alias defined for testing purposes and enable it in MCUboot. Signed-off-by: Henrik Brix Andersen --- app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf | 1 + boards/extensions/frdm_k64f/frdm_k64f.overlay | 11 +++++++++++ zephyr/module.yml | 1 + 3 files changed, 13 insertions(+) create mode 100644 boards/extensions/frdm_k64f/frdm_k64f.overlay diff --git a/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf b/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf index 9cbb0f2..4042ec6 100644 --- a/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf +++ b/app/sysbuild/mcuboot/boards/frdm_k64f_mk64f12.conf @@ -2,3 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 CONFIG_BOOT_USB_DFU_GPIO=y +CONFIG_MCUBOOT_INDICATION_LED=y diff --git a/boards/extensions/frdm_k64f/frdm_k64f.overlay b/boards/extensions/frdm_k64f/frdm_k64f.overlay new file mode 100644 index 0000000..c9ab053 --- /dev/null +++ b/boards/extensions/frdm_k64f/frdm_k64f.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + mcuboot-led0 = &blue_led; + }; +}; diff --git a/zephyr/module.yml b/zephyr/module.yml index 443cfa6..065ed06 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -6,4 +6,5 @@ build: kconfig: Kconfig cmake: . settings: + board_root: . dts_root: . From 5d82060770c28c41f721af5b5b8d732fd7378154 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Fri, 18 Oct 2024 20:36:27 +0000 Subject: [PATCH 5/6] app: add support for triggering DFU mode via DFU button Add support for triggering DFU mode via pressing and holding the optional DFU button. Visual feedback will provided via the DFU LED, if present. Signed-off-by: Henrik Brix Andersen --- app/Kconfig | 18 ++++++++ app/src/dfu.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 3 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index 4c1dd6b..487753d 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -132,6 +132,24 @@ config CANNECTIVITY_TERMINATION_DEFAULT_ON if BOOTLOADER_MCUBOOT +config CANNECTIVITY_DFU_BUTTON + bool "DFU button support" + default y + depends on $(dt_alias_enabled,mcuboot-button0) + select REBOOT + help + Enable support for rebooting into Device Firmware Upgrade (DFU) mode by holding the DFU + button (identified by the "mcuboot-button0" devicetree alias). + +config CANNECTIVITY_DFU_BUTTON_HOLD_TIME + int "DFU button hold time in seconds" + depends on CANNECTIVITY_DFU_BUTTON + range 1 60 + default 4 + help + Number of seconds the Device Firmware Upgrade (DFU) button must be held to reboot into DFU + mode. + config CANNECTIVITY_DFU_LED bool # hidden default y diff --git a/app/src/dfu.c b/app/src/dfu.c index b658d85..9541f42 100644 --- a/app/src/dfu.c +++ b/app/src/dfu.c @@ -4,28 +4,138 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include +#include #include +#include #include "cannectivity.h" LOG_MODULE_REGISTER(dfu, CONFIG_CANNECTIVITY_LOG_LEVEL); +/* DFU button poll timing */ +#define DFU_BUTTON_POLL_HZ 5 +#define DFU_BUTTON_POLL_INTERVAL_MS (MSEC_PER_SEC / DFU_BUTTON_POLL_HZ) +#define DFU_BUTTON_POLL_TOTAL (CONFIG_CANNECTIVITY_DFU_BUTTON_HOLD_TIME * DFU_BUTTON_POLL_HZ) + +/* DFU button and LED devicetree nodes */ #define DFU_LED_NODE DT_ALIAS(mcuboot_led0) +#define DFU_BUTTON_NODE DT_ALIAS(mcuboot_button0) + +#ifdef CONFIG_CANNECTIVITY_DFU_LED +struct gpio_dt_spec dfu_led = GPIO_DT_SPEC_GET(DFU_LED_NODE, gpios); +#endif /* CONFIG_CANNECTIVITY_DFU_LED */ + +#ifdef CONFIG_CANNECTIVITY_DFU_BUTTON +struct gpio_dt_spec dfu_button = GPIO_DT_SPEC_GET(DFU_BUTTON_NODE, gpios); +static struct gpio_callback dfu_button_cb; +static struct k_work_delayable dfu_button_work; +static struct k_sem dfu_button_sem; + +static void dfu_button_poll(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + int err; + + err = gpio_pin_get_dt(&dfu_button); + if (err < 0) { + LOG_ERR("failed to get DFU button state (err %d)", err); + goto done; + } + + if (err > 0) { +#ifdef CONFIG_CANNECTIVITY_DFU_LED + err = gpio_pin_toggle_dt(&dfu_led); + if (err != 0) { + LOG_ERR("failed to toggle DFU LED (err %d)", err); + goto done; + } +#endif /* CONFIG_CANNECTIVITY_DFU_LED */ + + k_sem_give(&dfu_button_sem); + if (k_sem_count_get(&dfu_button_sem) >= DFU_BUTTON_POLL_TOTAL) { + LOG_INF("rebooting"); + sys_reboot(SYS_REBOOT_COLD); + } + + k_work_reschedule(dwork, K_MSEC(DFU_BUTTON_POLL_INTERVAL_MS)); + return; + } + +done: +#ifdef CONFIG_CANNECTIVITY_DFU_LED + err = gpio_pin_set_dt(&dfu_led, 0); + if (err != 0) { + LOG_ERR("failed to turn off DFU LED (err %d)", err); + return; + } +#endif /* CONFIG_CANNECTIVITY_DFU_LED */ +} + +static void dfu_button_interrupt(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + ARG_UNUSED(port); + ARG_UNUSED(cb); + ARG_UNUSED(pins); + + k_sem_reset(&dfu_button_sem); + k_work_reschedule(&dfu_button_work, K_NO_WAIT); +} + +static int dfu_button_init(void) +{ + int err; + + err = k_sem_init(&dfu_button_sem, 0, K_SEM_MAX_LIMIT); + if (err != 0) { + LOG_ERR("failed to initialize DFU button semaphore (err %d)", err); + return err; + } + + if (!gpio_is_ready_dt(&dfu_button)) { + LOG_ERR("DFU button device not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&dfu_button, GPIO_INPUT); + if (err != 0) { + LOG_ERR("failed to configure DFU button (err %d)", err); + return err; + } + + err = gpio_pin_interrupt_configure_dt(&dfu_button, GPIO_INT_EDGE_TO_ACTIVE); + if (err != 0) { + LOG_ERR("failed to configure DFU button interrupt (err %d)", err); + return err; + } + + k_work_init_delayable(&dfu_button_work, dfu_button_poll); + + gpio_init_callback(&dfu_button_cb, dfu_button_interrupt, BIT(dfu_button.pin)); + err = gpio_add_callback_dt(&dfu_button, &dfu_button_cb); + if (err != 0) { + LOG_ERR("failed to add DFU button callback (err %d)", err); + return err; + } + + return 0; +} +#endif /* CONFIG_CANNECTIVITY_DFU_BUTTON */ #ifdef CONFIG_CANNECTIVITY_DFU_LED static int dfu_led_init(void) { - struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DFU_LED_NODE, gpios); int err; - if (!gpio_is_ready_dt(&led)) { + if (!gpio_is_ready_dt(&dfu_led)) { LOG_ERR("DFU LED device not ready"); return -ENODEV; } - err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE); + err = gpio_pin_configure_dt(&dfu_led, GPIO_OUTPUT_INACTIVE); if (err != 0) { LOG_ERR("failed to turn off DFU LED (err %d)", err); return err; @@ -61,5 +171,12 @@ int cannectivity_dfu_init(void) } #endif /* CONFIG_CANNECTIVITY_DFU_LED */ +#ifdef CONFIG_CANNECTIVITY_DFU_BUTTON + err = dfu_button_init(); + if (err != 0) { + return err; + } +#endif /* CONFIG_CANNECTIVITY_DFU_BUTTON */ + return 0; } From b51329215ca78a1c852f75bc70ce1ea5be1d2d6f Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Fri, 18 Oct 2024 20:38:04 +0000 Subject: [PATCH 6/6] README: document DFU mode Add instructions for how to use MCUboot for USB Device Firmware Upgrade (DFU) mode. Signed-off-by: Henrik Brix Andersen --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index ce84762..c5e92f9 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The CANnectivity firmware supports the following features, some of which depend - CAN bus error reporting - Automatic gs_usb driver loading under Linux using custom [udev rules](99-cannectivity.rules) - Automatic WinUSB driver installation under Microsoft Windows 8.1 and newer +- USB Device Firmware Upgrade (DFU) mode ## Hardware Requirements @@ -98,6 +99,38 @@ provided. These can be selected by setting either `FILE_SUFFIX=usbd_next` or After building, the firmware can be flashed to the board by running the `west flash` command. +## USB Device Firmware Upgrade (DFU) Mode + +CANnectivity supports USB Device Firmware Upgrade +([DFU](https://docs.zephyrproject.org/latest/services/device_mgmt/dfu.html)) via the +[MCUboot](https://www.trustedfirmware.org/projects/mcuboot/) bootloader. This is intended for use +with boards without an on-board programmer. + +To build CANnectivity with MCUboot integration for USB DFU use +[sysbuild](https://docs.zephyrproject.org/latest/build/sysbuild/index.html) with the +`sysbuild-dfu.conf` configuration file when building for your board (here `frdm_k64f`): + +```shell +west build -b frdm_k64f/mk64f12 --sysbuild ../custom/cannectivity/app/ -- -DSB_CONF_FILE=sysbuild-dfu.conf +``` + +After building, MCUboot and the CANnectivity firmware can be flashed to the board by running the +`west flash` command. + +Subsequent CANnectivity firmware updates can be applied via USB DFU. In order to do so, the board +must first be booted into USB DFU mode. If your board has a dedicated DFU button (identified by the +`mcuboot-button0` devicetree alias) press and hold it for 5 seconds or press and hold the button +while powering up the board. If your board has a DFU LED (identified by the `mcuboot-led0` +devicetree alias), the LED will flash while the DFU button is being held and change to constant on +once DFU mode is activated. Refer to your board documentation for further details. + +Once in DFU mode, the CANnectivity firmware can be updated using +[dfu-util](https://dfu-util.sourceforge.net/): + +```shell +dfu-util -a 1 build/app/zephyr/zephyr.signed.bin.dfu +``` + ## CANnectivity as a Zephyr Module The CANnectivity firmware repository is a [Zephyr