zephyr: add an option to use GPIO to enable USB DFU.
Devices with a physical reset button might prefer to use it to enter USB
DFU mode, instead of always entering it with a timeout. Extract the
existing CONFIG_BOOT_SERIAL_DETECT detection code and use it to enter
DFU mode when CONFIG_BOOT_USB_DFU_GPIO is enabled.
This commit depends on zephyrproject-rtos/zephyr#30015, which changes
wait_for_usb_dfu from a nullary function that waits for a compile-time
fixed amount of time, to one that takes a timeout.
Signed-off-by: Josh Gao <josh@jmgao.dev>
diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig
index 91247c4..2e7ac39 100644
--- a/boot/zephyr/Kconfig
+++ b/boot/zephyr/Kconfig
@@ -355,9 +355,15 @@
endchoice
-config BOOT_WAIT_FOR_USB_DFU
+choice BOOT_USB_DFU
+ prompt "USB DFU"
+ default BOOT_USB_DFU_NO
+
+config BOOT_USB_DFU_NO
+ prompt "Disabled"
+
+config BOOT_USB_DFU_WAIT
bool "Wait for a prescribed duration to see if USB DFU is invoked"
- default n
select USB
select USB_DFU_CLASS
select IMG_MANAGER
@@ -366,6 +372,58 @@
for USB DFU to be invoked. Please note DFU always updates the
slot1 image.
+config BOOT_USB_DFU_GPIO
+ bool "Use GPIO to detect whether to trigger DFU mode"
+ select USB
+ select USB_DFU_CLASS
+ select IMG_MANAGER
+ help
+ If y, MCUboot uses GPIO to detect whether to invoke USB DFU.
+
+endchoice
+
+config BOOT_USB_DFU_WAIT_DELAY_MS
+ int "USB DFU wait duration"
+ depends on BOOT_USB_DFU_WAIT
+ default 12000
+ help
+ Milliseconds to wait for USB DFU to be invoked.
+
+if BOOT_USB_DFU_GPIO
+
+config BOOT_USB_DFU_DETECT_PORT
+ string "GPIO device to trigger USB DFU mode"
+ default GPIO_0 if SOC_FAMILY_NRF
+ help
+ Zephyr GPIO device that contains the pin used to trigger
+ USB DFU.
+
+config BOOT_USB_DFU_DETECT_PIN
+ int "Pin to trigger USB DFU mode"
+ default 6 if BOARD_NRF9160DK_NRF9160
+ default 11 if BOARD_NRF52840DK_NRF52840
+ default 13 if BOARD_NRF52DK_NRF52832
+ default 23 if BOARD_NRF5340_DK_NRF5340_CPUAPP || BOARD_NRF5340_DK_NRF5340_CPUAPPNS
+ help
+ Pin on the DFU detect port that triggers DFU mode.
+
+config BOOT_USB_DFU_DETECT_PIN_VAL
+ int "USB DFU detect pin trigger value"
+ default 0
+ range 0 1
+ help
+ Logic value of the detect pin that triggers USB DFU mode.
+
+config BOOT_USB_DFU_DETECT_DELAY
+ int "Serial detect pin detection delay time [ms]"
+ default 0
+ help
+ Used to prevent the bootloader from loading on button press.
+ Useful for powering on when using the same button as
+ the one used to place the device in bootloader mode.
+
+endif # BOOT_USB_DFU_GPIO
+
config ZEPHYR_TRY_MASS_ERASE
bool "Try to mass erase flash when flashing MCUboot image"
default y
@@ -453,7 +511,7 @@
string "GPIO device to trigger serial recovery mode"
default GPIO_0 if SOC_FAMILY_NRF
help
- Zephyr GPIO device which contains the pin used to trigger
+ Zephyr GPIO device that contains the pin used to trigger
serial recovery mode.
config BOOT_SERIAL_DETECT_PIN
@@ -464,14 +522,14 @@
default 23 if BOARD_NRF5340PDK_NRF5340_CPUAPP || BOARD_NRF5340PDK_NRF5340_CPUAPPNS || \
BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_NRF5340DK_NRF5340_CPUAPPNS
help
- Pin on the serial detect port which triggers serial recovery mode.
+ Pin on the serial detect port that triggers serial recovery mode.
config BOOT_SERIAL_DETECT_PIN_VAL
int "Serial detect pin trigger value"
default 0
range 0 1
help
- Logic value of the detect pin which triggers serial recovery
+ Logic value of the detect pin that triggers serial recovery
mode.
config BOOT_SERIAL_DETECT_DELAY
@@ -576,6 +634,7 @@
# hardware work.
config MULTITHREADING
default y if BOOT_SERIAL_CDC_ACM #usb driver requires MULTITHREADING
+ default y if BOOT_USB_DFU_GPIO || BOOT_USB_DFU_WAIT
default n if SOC_FAMILY_NRF
default y
diff --git a/boot/zephyr/main.c b/boot/zephyr/main.c
index 2471ee4..763f90e 100644
--- a/boot/zephyr/main.c
+++ b/boot/zephyr/main.c
@@ -44,7 +44,7 @@
};
#endif
-#ifdef CONFIG_BOOT_WAIT_FOR_USB_DFU
+#if defined(CONFIG_BOOT_USB_DFU_WAIT) || defined(CONFIG_BOOT_USB_DFU_GPIO)
#include <usb/class/usb_dfu.h>
#endif
@@ -128,7 +128,7 @@
#define LED0_GPIO_LABEL DT_GPIO_LABEL(LED0_NODE, gpios)
#define LED0_GPIO_PIN DT_GPIO_PIN(LED0_NODE, gpios)
#define LED0_GPIO_FLAGS (GPIO_OUTPUT | FLAGS_OR_ZERO(LED0_NODE))
-#else
+#else
/* A build error here means your board isn't set up to drive an LED. */
#error "Unsupported board: led0 devicetree alias is not defined"
#endif
@@ -137,7 +137,7 @@
void led_init(void)
{
-
+
led = device_get_binding(LED0_GPIO_LABEL);
if (led == NULL) {
BOOT_LOG_ERR("Didn't find LED device %s\n", LED0_GPIO_LABEL);
@@ -365,6 +365,71 @@
#endif/* defined(CONFIG_LOG) && !defined(CONFIG_LOG_IMMEDIATE) &&\
!defined(CONFIG_LOG_PROCESS_THREAD) */
+#if defined(CONFIG_MCUBOOT_SERIAL) || defined(CONFIG_BOOT_USB_DFU_GPIO)
+static bool detect_pin(const char* port, int pin, uint32_t expected, int delay)
+{
+ int rc;
+ int detect_value;
+ struct device const *detect_port;
+
+ detect_port = device_get_binding(port);
+ __ASSERT(detect_port, "Error: Bad port for boot detection.\n");
+
+ /* The default presence value is 0 which would normally be
+ * active-low, but historically the raw value was checked so we'll
+ * use the raw interface.
+ */
+ rc = gpio_pin_configure(detect_port, pin,
+#ifdef GPIO_INPUT
+ GPIO_INPUT | GPIO_PULL_UP
+#else
+ GPIO_DIR_IN | GPIO_PUD_PULL_UP
+#endif
+ );
+ __ASSERT(rc == 0, "Failed to initialize boot detect pin.\n");
+
+#ifdef GPIO_INPUT
+ rc = gpio_pin_get_raw(detect_port, pin);
+ detect_value = rc;
+#else
+ rc = gpio_pin_read(detect_port, pin, &detect_value);
+#endif
+ __ASSERT(rc >= 0, "Failed to read boot detect pin.\n");
+
+ if (detect_value == expected) {
+ if (delay > 0) {
+ k_sleep(K_MSEC(50));
+
+ /* Get the uptime for debounce purposes. */
+ int64_t timestamp = k_uptime_get();
+
+ for(;;) {
+#ifdef GPIO_INPUT
+ rc = gpio_pin_get_raw(detect_port, pin);
+ detect_value = rc;
+#else
+ rc = gpio_pin_read(detect_port, pin, &detect_value);
+#endif
+ __ASSERT(rc >= 0, "Failed to read boot detect pin.\n");
+
+ /* Get delta from when this started */
+ uint32_t delta = k_uptime_get() - timestamp;
+
+ /* If not pressed OR if pressed > debounce period, stop. */
+ if (delta >= delay || detect_value != expected) {
+ break;
+ }
+
+ /* Delay 1 ms */
+ k_sleep(K_MSEC(1));
+ }
+ }
+ }
+
+ return detect_value == expected;
+}
+#endif
+
void main(void)
{
struct boot_rsp rsp;
@@ -406,91 +471,47 @@
#endif
#ifdef CONFIG_MCUBOOT_SERIAL
-
- struct device const *detect_port;
- uint32_t detect_value = !CONFIG_BOOT_SERIAL_DETECT_PIN_VAL;
-
- detect_port = device_get_binding(CONFIG_BOOT_SERIAL_DETECT_PORT);
- __ASSERT(detect_port, "Error: Bad port for boot serial detection.\n");
-
- /* The default presence value is 0 which would normally be
- * active-low, but historically the raw value was checked so we'll
- * use the raw interface.
- */
- rc = gpio_pin_configure(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN,
-#ifdef GPIO_INPUT
- GPIO_INPUT | GPIO_PULL_UP
-#else
- GPIO_DIR_IN | GPIO_PUD_PULL_UP
-#endif
- );
- __ASSERT(rc == 0, "Error of boot detect pin initialization.\n");
-
-#ifdef GPIO_INPUT
- rc = gpio_pin_get_raw(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN);
- detect_value = rc;
-#else
- rc = gpio_pin_read(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN,
- &detect_value);
-#endif
- __ASSERT(rc >= 0, "Error of the reading the detect pin.\n");
- if (detect_value == CONFIG_BOOT_SERIAL_DETECT_PIN_VAL &&
- !boot_skip_serial_recovery()) {
-
-#if CONFIG_BOOT_SERIAL_DETECT_DELAY > 0
- k_sleep(K_MSEC(50));
-
- /* Get the uptime for debounce purposes. */
- int64_t timestamp = k_uptime_get();
-
- for(;;) {
-
-#ifdef GPIO_INPUT
- rc = gpio_pin_get_raw(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN);
- detect_value = rc;
-#else
- rc = gpio_pin_read(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN,
- &detect_value);
-#endif
- __ASSERT(rc >= 0, "Error of the reading the detect pin.\n");
-
- /* Get delta from when this started */
- uint32_t delta = k_uptime_get() - timestamp;
-
- /* If not pressed OR if pressed > debounce period stop loop*/
- if( delta >= CONFIG_BOOT_SERIAL_DETECT_DELAY ||
- detect_value != CONFIG_BOOT_SERIAL_DETECT_PIN_VAL ) {
- break;
- }
-
-
- /* Delay 1 ms */
- k_sleep(K_MSEC(1));
- }
-#endif
-
- /* Then run DFU */
- if (detect_value == CONFIG_BOOT_SERIAL_DETECT_PIN_VAL) {
+ if (detect_pin(CONFIG_BOOT_SERIAL_DETECT_PORT,
+ CONFIG_BOOT_SERIAL_DETECT_PIN,
+ CONFIG_BOOT_SERIAL_DETECT_PIN_VAL,
+ CONFIG_BOOT_SERIAL_DETECT_DELAY) &&
+ !boot_skip_serial_recovery()) {
#ifdef CONFIG_MCUBOOT_INDICATION_LED
- gpio_pin_set(led, LED0_GPIO_PIN, 1);
-#endif
- BOOT_LOG_INF("Enter the serial recovery mode");
- rc = boot_console_init();
- __ASSERT(rc == 0, "Error initializing boot console.\n");
- boot_serial_start(&boot_funcs);
- __ASSERT(0, "Bootloader serial process was terminated unexpectedly.\n");
-
- }
-}
+ gpio_pin_set(led, LED0_GPIO_PIN, 1);
#endif
-#ifdef CONFIG_BOOT_WAIT_FOR_USB_DFU
+ BOOT_LOG_INF("Enter the serial recovery mode");
+ rc = boot_console_init();
+ __ASSERT(rc == 0, "Error initializing boot console.\n");
+ boot_serial_start(&boot_funcs);
+ __ASSERT(0, "Bootloader serial process was terminated unexpectedly.\n");
+ }
+#endif
+
+#if defined(CONFIG_BOOT_USB_DFU_GPIO)
+ if (detect_pin(CONFIG_BOOT_USB_DFU_DETECT_PORT,
+ CONFIG_BOOT_USB_DFU_DETECT_PIN,
+ CONFIG_BOOT_USB_DFU_DETECT_PIN_VAL,
+ CONFIG_BOOT_USB_DFU_DETECT_DELAY)) {
+#ifdef CONFIG_MCUBOOT_INDICATION_LED
+ gpio_pin_set(led, LED0_GPIO_PIN, 1);
+#endif
+ rc = usb_enable(NULL);
+ if (rc) {
+ BOOT_LOG_ERR("Cannot enable USB");
+ } else {
+ BOOT_LOG_INF("Waiting for USB DFU");
+ wait_for_usb_dfu(K_FOREVER);
+ BOOT_LOG_INF("USB DFU wait time elapsed");
+ }
+ }
+#elif defined(CONFIG_BOOT_USB_DFU_WAIT)
rc = usb_enable(NULL);
if (rc) {
BOOT_LOG_ERR("Cannot enable USB");
} else {
BOOT_LOG_INF("Waiting for USB DFU");
- wait_for_usb_dfu();
+ wait_for_usb_dfu(K_MSEC(CONFIG_BOOT_USB_DFU_WAIT_DELAY_MS));
BOOT_LOG_INF("USB DFU wait time elapsed");
}
#endif