/* * The MIT License (MIT) * * Copyright (c) 2018 Ha Thach for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * -# Receive start data packet. * -# Based on start packet, prepare NVM area to store received data. * -# Receive data packet. * -# Validate data packet. * -# Write Data packet to NVM. * -# If not finished - Wait for next packet. * -# Receive stop data packet. * -# Activate Image, boot application. * */ #include #include #include #include #include "nrfx.h" #include "nrf_clock.h" #include "nrfx_power.h" #include "nrfx_pwm.h" #include "nordic_common.h" #include "sdk_common.h" #include "dfu_transport.h" #include "bootloader.h" #include "bootloader_util.h" #include "nrf.h" #include "nrf_soc.h" #include "nrf_nvic.h" #include "app_error.h" #include "nrf_gpio.h" #include "ble.h" #include "nrf.h" #include "ble_hci.h" #include "app_scheduler.h" #include "nrf_error.h" #include "boards.h" #include "uf2/uf2.h" #include "pstorage_platform.h" #include "nrf_mbr.h" #include "pstorage.h" #include "nrfx_nvmc.h" #ifdef NRF_USBD #include "nrf_usbd.h" #include "tusb.h" void usb_init(void); void usb_teardown(void); #else #define usb_init() led_state(STATE_USB_MOUNTED) // mark nrf52832 as mounted #define usb_teardown() #endif /* * Blinking patterns: * - DFU Serial : LED Status blink * - DFU OTA : LED Status & Conn blink at the same time * - DFU Flashing : LED Status blink 2x fast * - Factory Reset : LED Status blink 2x fast * - Fatal Error : LED Status & Conn blink one after another */ /* Magic that written to NRF_POWER->GPREGRET by application when it wish to go into DFU * - BOOTLOADER_DFU_OTA_MAGIC used by BLEDfu service : SD is already init * - BOOTLOADER_DFU_OTA_FULLRESET_MAGIC entered by soft reset : SD is not init * - BOOTLOADER_DFU_SERIAL_MAGIC entered by soft reset : SD is not init * * Note: for DFU_MAGIC_OTA_APPJUM Softdevice must not initialized. * since it is already in application. In all other case of OTA SD must be initialized */ #define DFU_MAGIC_OTA_APPJUM BOOTLOADER_DFU_START // 0xB1 #define DFU_MAGIC_OTA_RESET 0xA8 #define DFU_MAGIC_SERIAL_ONLY_RESET 0x4e #define DFU_MAGIC_UF2_RESET 0x57 #define DFU_DBL_RESET_MAGIC 0x5A1AD5 // SALADS #define DFU_DBL_RESET_APP 0x4ee5677e #define DFU_DBL_RESET_DELAY 500 #define DFU_DBL_RESET_MEM 0x20007F7C #define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0] #define DFU_SERIAL_STARTUP_INTERVAL 1000 // Allow for using reset button essentially to swap between application and bootloader. // This is controlled by a flag in the app and is the behavior of CPX and all Arcade boards when using MakeCode. #define APP_ASKS_FOR_SINGLE_TAP_RESET() (*((uint32_t*)(USER_FLASH_START + 0x200)) == 0x87eeb07c) // These value must be the same with one in dfu_transport_ble.c #define BLEGAP_EVENT_LENGTH 6 #define BLEGATT_ATT_MTU_MAX 247 enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; // Adafruit for factory reset #define APPDATA_ADDR_START (BOOTLOADER_REGION_START-DFU_APP_DATA_RESERVED) #ifdef NRF52840_XXAA // Flash 1024 KB STATIC_ASSERT( APPDATA_ADDR_START == 0xED000); #else // Flash 512 KB STATIC_ASSERT( APPDATA_ADDR_START == 0x73000); #endif // void adafruit_factory_reset(void); uint32_t* dbl_reset_mem = ((uint32_t*) DFU_DBL_RESET_MEM ); // true if ble, false if serial bool _ota_connected = false; bool is_ota(void) { return false; } void softdev_mbr_init(void) { sd_mbr_command_t com = { .command = SD_MBR_COMMAND_INIT_SD }; sd_mbr_command(&com); } // Send data out one pin and look for it coming back // on the other. Toggle it up and down. static bool check_loopback(void) { unsigned int i = 0; nrf_gpio_cfg_output(LOOPBACK_TX_PIN); nrf_gpio_cfg_input(LOOPBACK_RX_PIN, NRF_GPIO_PIN_NOPULL); for (i = 0; i < 16; i++) { if ((i & 1) == 0) { nrf_gpio_pin_clear(LOOPBACK_TX_PIN); // Dummy read to force sync (void)nrf_gpio_pin_read(LOOPBACK_TX_PIN); if (nrf_gpio_pin_read(LOOPBACK_RX_PIN)) return false; } else { nrf_gpio_pin_set(LOOPBACK_TX_PIN); // Dummy read to force sync (void)nrf_gpio_pin_read(LOOPBACK_TX_PIN); if (!nrf_gpio_pin_read(LOOPBACK_RX_PIN)) return false; } } return true; } int main(void) { // SD is already Initialized in case of BOOTLOADER_DFU_OTA_MAGIC bool sd_inited = (NRF_POWER->GPREGRET == DFU_MAGIC_OTA_APPJUM); // Serial only mode // bool serial_only_dfu = (NRF_POWER->GPREGRET == DFU_MAGIC_SERIAL_ONLY_RESET); // Pins are shorted together bool loopback_shorted = check_loopback(); // start either serial, uf2 or ble bool dfu_start = loopback_shorted || (NRF_POWER->GPREGRET == DFU_MAGIC_UF2_RESET) || (((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk)); // Clear GPREGRET if it is our values if (dfu_start) NRF_POWER->GPREGRET = 0; // Save bootloader version to pre-defined register, retrieved by application BOOTLOADER_VERSION_REGISTER = (MK_BOOTLOADER_VERSION); // This check ensures that the defined fields in the bootloader corresponds with actual setting in the chip. APP_ERROR_CHECK_BOOL(*((uint32_t *)NRF_UICR_BOOT_START_ADDRESS) == BOOTLOADER_REGION_START); board_init(); bootloader_init(); led_state(STATE_BOOTLOADER_STARTED); // // When updating SoftDevice, bootloader will reset before swapping SD // if (bootloader_dfu_sd_in_progress()) // { // led_state(STATE_WRITING_STARTED); // APP_ERROR_CHECK( bootloader_dfu_sd_update_continue() ); // APP_ERROR_CHECK( bootloader_dfu_sd_update_finalize() ); // led_state(STATE_WRITING_FINISHED); // } /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ // DFU button pressed // dfu_start = dfu_start || button_pressed(BUTTON_DFU); bool const valid_app = bootloader_app_is_valid(DFU_BANK_0_REGION_START); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) dfu_start = 1; // App mode: register 1st reset and DFU startup (nrf52832) if ( ! (just_start_app || dfu_start || !valid_app) ) { // Register our first reset for double reset detection (*dbl_reset_mem) = DFU_DBL_RESET_MAGIC; // #ifdef NRF52832_XXAA // /* Even DFU is not active, we still force an 1000 ms dfu serial mode when startup // * to support auto programming from Arduino IDE // * // * Note: Supposedly during this time if RST is press, it will count as double reset. // * However Double Reset WONT work with nrf52832 since its SRAM got cleared anyway. // */ // bootloader_dfu_start(DFU_SERIAL_STARTUP_INTERVAL); // #else // // if RST is pressed during this delay --> if will enter dfu // NRFX_DELAY_MS(DFU_DBL_RESET_DELAY); // #endif } if (APP_ASKS_FOR_SINGLE_TAP_RESET()) (*dbl_reset_mem) = DFU_DBL_RESET_APP; else (*dbl_reset_mem) = 0; if ( dfu_start || !valid_app) { led_state(STATE_USB_UNMOUNTED); usb_init(); // Initiate an update of the firmware. // APP_ERROR_CHECK( bootloader_dfu_start(0) ); // while (1) { // tud_task(); // tud_cdc_write_flush(); // } bootloader_run(0); usb_teardown(); } // // Adafruit Factory reset // if ( !button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET) ) // { // adafruit_factory_reset(); // } // Reset Board board_teardown(); // Jump to application if valid if (bootloader_app_is_valid(DFU_BANK_0_REGION_START) && !bootloader_dfu_sd_in_progress()) { // MBR must be init before start application if ( !sd_inited ) softdev_mbr_init(); // clear in case we kept DFU_DBL_RESET_APP there (*dbl_reset_mem) = 0; // Select a bank region to use as application region. // @note: Only applications running from DFU_BANK_0_REGION_START is supported. bootloader_app_start(DFU_BANK_0_REGION_START); } NVIC_SystemReset(); } // // Perform factory reset to erase Application + Data // void adafruit_factory_reset(void) // { // led_state(STATE_FACTORY_RESET_STARTED); // // clear all App Data if any // if ( DFU_APP_DATA_RESERVED ) // { // nrfx_nvmc_page_erase(APPDATA_ADDR_START); // } // // Only need to erase the 1st page of Application code to make it invalid // nrfx_nvmc_page_erase(DFU_BANK_0_REGION_START); // // back to normal // led_state(STATE_FACTORY_RESET_FINISHED); // } //--------------------------------------------------------------------+ // Error Handler //--------------------------------------------------------------------+ void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) { volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ NVIC_SystemReset(); } void assert_nrf_callback (uint16_t line_num, uint8_t const * p_file_name) { app_error_fault_handler(0xDEADBEEF, 0, 0); } /*------------------------------------------------------------------*/ /* SoftDevice Event handler *------------------------------------------------------------------*/ // process SOC event from SD uint32_t proc_soc(void) { uint32_t soc_evt; uint32_t err = sd_evt_get(&soc_evt); if (NRF_SUCCESS == err) { pstorage_sys_event_handler(soc_evt); #ifdef NRF_USBD extern void tusb_hal_nrf_power_event(uint32_t event); /*------------- usb power event handler -------------*/ int32_t usbevt = (soc_evt == NRF_EVT_POWER_USB_DETECTED ) ? NRFX_POWER_USB_EVT_DETECTED: (soc_evt == NRF_EVT_POWER_USB_POWER_READY) ? NRFX_POWER_USB_EVT_READY : (soc_evt == NRF_EVT_POWER_USB_REMOVED ) ? NRFX_POWER_USB_EVT_REMOVED : -1; if ( usbevt >= 0) tusb_hal_nrf_power_event(usbevt); #endif } return err; } void ada_sd_task(void* evt_data, uint16_t evt_size) { (void) evt_data; (void) evt_size; // process BLE and SOC until there is no more events while( NRF_ERROR_NOT_FOUND != proc_soc() ) { } } void SD_EVT_IRQHandler(void) { // Use App Scheduler to defer handling code in non-isr context app_sched_event_put(NULL, 0, ada_sd_task); }