Commit 837cf88f authored by Josh Gao's avatar Josh Gao Committed by David Brown
Browse files

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: default avatarJosh Gao <josh@jmgao.dev>
parent 89530010
Loading
Loading
Loading
Loading
+64 −5
Original line number Diff line number Diff line
@@ -355,9 +355,15 @@ config BOOT_FIH_PROFILE_HIGH

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 @@ config BOOT_WAIT_FOR_USB_DFU
	  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 @@ config BOOT_SERIAL_DETECT_PORT
	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 @@ config BOOT_SERIAL_DETECT_PIN
	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 @@ comment "Zephyr configuration options"
# 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

+97 −76
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ const struct boot_uart_funcs boot_funcs = {
};
#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

@@ -365,132 +365,153 @@ void zephyr_boot_log_stop(void)
#endif/* defined(CONFIG_LOG) && !defined(CONFIG_LOG_IMMEDIATE) &&\
        !defined(CONFIG_LOG_PROCESS_THREAD) */

void main(void)
#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)
{
    struct boot_rsp rsp;
    int rc;
    fih_int fih_rc = FIH_FAILURE;

    MCUBOOT_WATCHDOG_FEED();

#if !defined(MCUBOOT_DIRECT_XIP)
    BOOT_LOG_INF("Starting bootloader");
#else
    BOOT_LOG_INF("Starting Direct-XIP bootloader");
#endif

#ifdef CONFIG_MCUBOOT_INDICATION_LED
    /* LED init */
    led_init();
#endif

    os_heap_init();

    ZEPHYR_BOOT_LOG_START();

    (void)rc;

#if (!defined(CONFIG_XTENSA) && defined(DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL))
    if (!flash_device_get_binding(DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL)) {
        BOOT_LOG_ERR("Flash device %s not found",
		     DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL);
        while (1)
            ;
    }
#elif (defined(CONFIG_XTENSA) && defined(JEDEC_SPI_NOR_0_LABEL))
    if (!flash_device_get_binding(JEDEC_SPI_NOR_0_LABEL)) {
        BOOT_LOG_ERR("Flash device %s not found", JEDEC_SPI_NOR_0_LABEL);
        while (1)
            ;
    }
#endif

#ifdef CONFIG_MCUBOOT_SERIAL

    int detect_value;
    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");
    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, CONFIG_BOOT_SERIAL_DETECT_PIN,
    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, "Error of boot detect pin initialization.\n");
    __ASSERT(rc == 0, "Failed to initialize boot detect pin.\n");

#ifdef GPIO_INPUT
    rc = gpio_pin_get_raw(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN);
    rc = gpio_pin_get_raw(detect_port, pin);
    detect_value = rc;
#else
    rc = gpio_pin_read(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN,
                       &detect_value);
    rc = gpio_pin_read(detect_port, 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()) {
    __ASSERT(rc >= 0, "Failed to read boot detect pin.\n");

#if CONFIG_BOOT_SERIAL_DETECT_DELAY > 0 
    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, CONFIG_BOOT_SERIAL_DETECT_PIN);
                rc = gpio_pin_get_raw(detect_port, pin);
                detect_value = rc;
#else
            rc = gpio_pin_read(detect_port, CONFIG_BOOT_SERIAL_DETECT_PIN,
                            &detect_value);
                rc = gpio_pin_read(detect_port, pin, &detect_value);
#endif
            __ASSERT(rc >= 0, "Error of the reading the detect pin.\n");
                __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 loop*/
            if( delta >= CONFIG_BOOT_SERIAL_DETECT_DELAY || 
                detect_value != CONFIG_BOOT_SERIAL_DETECT_PIN_VAL ) {
                /* 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;
    int rc;
    fih_int fih_rc = FIH_FAILURE;

    MCUBOOT_WATCHDOG_FEED();

#if !defined(MCUBOOT_DIRECT_XIP)
    BOOT_LOG_INF("Starting bootloader");
#else
    BOOT_LOG_INF("Starting Direct-XIP bootloader");
#endif

        /* Then run DFU */
        if (detect_value == CONFIG_BOOT_SERIAL_DETECT_PIN_VAL) {
#ifdef CONFIG_MCUBOOT_INDICATION_LED
    /* LED init */
    led_init();
#endif

    os_heap_init();

    ZEPHYR_BOOT_LOG_START();

    (void)rc;

#if (!defined(CONFIG_XTENSA) && defined(DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL))
    if (!flash_device_get_binding(DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL)) {
        BOOT_LOG_ERR("Flash device %s not found",
		     DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL);
        while (1)
            ;
    }
#elif (defined(CONFIG_XTENSA) && defined(JEDEC_SPI_NOR_0_LABEL))
    if (!flash_device_get_binding(JEDEC_SPI_NOR_0_LABEL)) {
        BOOT_LOG_ERR("Flash device %s not found", JEDEC_SPI_NOR_0_LABEL);
        while (1)
            ;
    }
#endif

#ifdef CONFIG_MCUBOOT_SERIAL
    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");
        
        }
    }
#endif

#ifdef CONFIG_BOOT_WAIT_FOR_USB_DFU
#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