This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How to detect "this is the first bootup of a new DFU image"

I have been unsuccessful attempting to perform an FDS write or update WITHIN the DFU Event handler... the update or write never completes (no FDS_EVT_WRITE or FDS_EVT_UPDATE). I'm wondering if that is the wrong context in which to do that. I am able to do FDS reads in the DFU Event Handler context, it's only the write/update that times out.

My immediate goal is to detect that a reboot is post-DFU and do something unique. In general I'd like to store more state across any planned reboot, DFU, WDT, or other.

Questions:

  • Is FDS write/update not doable within the DFU Event Handler context?
  • What other method is there to determine if the reboot took place as the result of a DFU?

Thanks,

Jeff

Parents
  • Hi Jeff,

    Which DFU event handler do you refer to, and how do you make FDS calls from there? Please elaborate and show some code.

    My immediate goal is to detect that a reboot is post-DFU and do something unique. In general I'd like to store more state across any planned reboot, DFU, WDT, or other.

    Just looking at this goal another approach could be to include a special version number of similar in the firmware you upgrade to, and also maintain this version in FDS. So every time the application starts you read the firmware version and compare with the version you have stored in FDS. If it is the same do nothing. If not you will know that this is the first boot after DFU update, and you also update the FDS record so that the next time it will be the same.

    Einar

  • // Note: FDS initialization is working, the boot count is updated every reboot as intended and is readable
    //       everywhere and writable everywhere except from the DFU Handler.
    //       Any DFU (even if the code is unchanged) needs to "notify" the robot at the next reboot that the reboot is the result of a DFU even if the update failed.
    //

    //// Tertill DFU Handler

    static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
    {
        switch (event)
        {
            case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:

                NRF_LOG_INFO("Device is preparing to enter bootloader mode.");

                // Tertill - Version #1 - update FDS record earlier in DFU sequence
                clear_boot_count_c(); // robot will shut itself off at next reboot, post-DFU action must be OFF

                break;

            case BLE_DFU_EVT_BOOTLOADER_ENTER:

                NRF_LOG_INFO("Device will enter bootloader mode.");

                // Tertill - Version #2 - update FDS record later in DFU sequence
    //            clear_boot_count_c(); // robot will shut itself off at next reboot, post-DFU action must be OFF

                break;

            case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
                NRF_LOG_ERROR("Request to enter bootloader mode failed asynchroneously.");
                break;

            case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
                NRF_LOG_ERROR("Request to send a response to client failed.");
                APP_ERROR_CHECK(false);
                break;

            default:
                NRF_LOG_ERROR("Unhandled event from ble_dfu_buttonless: %d", event);
                break;
        }
    }

    //// Tertill FDS Code

    // Simple event handler to handle errors during initialization.
    static void fds_evt_handler(fds_evt_t const * p_evt)
    {
        NRF_LOG_INFO("Event: %s received (%s)",
                      fds_evt_str[p_evt->id],
                      fds_err_str[p_evt->result]);

        switch (p_evt->id) {
        case FDS_EVT_INIT:
            if (p_evt->result == FDS_SUCCESS) {
                l_fds_initialized = true;
            }
            break;

        case FDS_EVT_UPDATE:
        case FDS_EVT_WRITE:
        {
            if (p_evt->result == FDS_SUCCESS) {
                NRF_LOG_INFO("Record ID:\t0x%04x",  p_evt->write.record_id);
                NRF_LOG_INFO("File ID:\t0x%04x",    p_evt->write.file_id);
                NRF_LOG_INFO("Record key:\t0x%04x", p_evt->write.record_key);
            }
            l_fds_write_pending = false; // application sets this to true before initiating any write or update
        } break;

        case FDS_EVT_DEL_RECORD:
        {
            if (p_evt->result == FDS_SUCCESS)
            {
                NRF_LOG_INFO("Record ID:\t0x%04x",  p_evt->del.record_id);
                NRF_LOG_INFO("File ID:\t0x%04x",    p_evt->del.file_id);
                NRF_LOG_INFO("Record key:\t0x%04x", p_evt->del.record_key);
            }
            l_delete_all.pending = false;
        } break;

        default:
            break;
        }
    }

    static void set_fds_write_pending()
    {
        l_fds_write_pending = true; // application sets this to true, handler changes to false upon completion
    }

    static void wait_for_fds_write_complete(void)
    {
        while (l_fds_write_pending) {
            (void) sd_app_evt_wait();
        }
    }

    // FDS Write, Update, and Read Long methods

    int Fds::write_long(uint16_t file_id, uint16_t record_key, uint32_t value)
    {
        fds_record_desc_t record_desc;
        fds_record_t      record;
        ret_code_t        ret_code;

        // Wait for any previous write to complete
        wait_for_fds_write_complete();

        // Static copy of input
        static uint32_t value_copy = value;

        // Clear the descriptor
        memset(&record_desc, 0x00, sizeof(fds_record_desc_t));

        // Fill in the record into
        record.file_id           = file_id;
        record.key               = record_key;
        record.data.p_data       = &value_copy;
        record.data.length_words = (sizeof(value_copy) + 3) / 4; // round up to longs

        // Mark write in progress, then start write
        set_fds_write_pending();
        ret_code = fds_record_write(&record_desc, &record);
        if ( ret_code == FDS_SUCCESS ) {
            // NRF_LOG_INFO("write_long() PASS\r\n"); NRF_LOG_FLUSH();
            // return 0;
        } else {
            // NRF_LOG_INFO("write_long() FAIL\r\n"); NRF_LOG_FLUSH();
            return -2;
        }

        return 0;
    }


    int Fds::update_long(uint16_t file_id, uint16_t record_key, uint32_t value)
    {
        fds_record_desc_t record_desc;
        fds_record_t      record;
        ret_code_t        ret_code;
        fds_find_token_t  ftok;

        // Wait for any previous write to complete
        wait_for_fds_write_complete();

        // Static copy of input
        static uint32_t value_copy = value;

        // Clear the descriptor and find token
        memset(&record_desc, 0x00, sizeof(fds_record_desc_t));
        memset(&ftok       , 0x00, sizeof(fds_find_token_t ));

        // Find the record
        ret_code = fds_record_find(file_id, record_key, &record_desc, &ftok);
        if ( ret_code == FDS_SUCCESS ) {

            // Fill in the record info
            record.file_id           = file_id;
            record.key               = record_key;
            record.data.p_data       = &value_copy;
            record.data.length_words = (sizeof(value_copy) + 3) / 4; // round up to longs

            // Mark write in progress, then start write
            set_fds_write_pending();
            ret_code = fds_record_update(&record_desc, &record);
            if ( ret_code == FDS_SUCCESS ) {
                // NRF_LOG_INFO("update_long() PASS\r\n");
                // return 0;
            } else {
                NRF_LOG_INFO("update_long() find pass, update FAIL\r\n");
                return -2;
            }
        } else {
            // NRF_LOG_INFO("update_long() find FAIL\r\n");
            return -1;
        }

        return 0;
    }


    int Fds::read_long(uint16_t file_id, uint16_t record_key, uint32_t *p_value)
    {
        fds_find_token_t    ftok;
        fds_flash_record_t  flash_record;
        fds_record_desc_t   record_desc;
        ret_code_t          fds_ret_code;
    #if 0
        // Wait for any previous write to complete
        wait_for_fds_write_complete();
    #endif
        memset( &ftok        , 0x00, sizeof(fds_find_token_t  ) );
        memset( &flash_record, 0x00, sizeof(fds_flash_record_t) );
        memset( &record_desc , 0x00, sizeof(fds_record_desc_t ) );

        fds_ret_code = fds_record_find(file_id, record_key, &record_desc, &ftok);
        if ( FDS_SUCCESS == fds_ret_code ) {
            fds_ret_code = fds_record_open(&record_desc, &flash_record);
            if ( FDS_SUCCESS == fds_ret_code ) {
                *p_value = *((uint32_t *)(flash_record.p_data));
                fds_record_close(&record_desc);
                // NRF_LOG_INFO("read_long() PASS\r\n"); NRF_LOG_FLUSH();
            } else {
                NRF_LOG_INFO("read_long() find pass, read FAIL\r\n"); NRF_LOG_FLUSH();
                return -2;
            }
        } else {
            // NRF_LOG_INFO("read_long() find FAIL\r\n"); NRF_LOG_FLUSH();
            return -1;
        }
        return 0;
    }

    // When DFU is about to happen we do this to clear the boot count.
    // Clear Boot Count File/Record value from "C" or CPPfs
    // the extern "C" version of clear count
    void clear_boot_count_c()
    {
        robot->fds.clear_boot_count();
    }

    // Class member version of clear count
    void Fds::clear_boot_count()
    {
        uint16_t file_id = 0xb007; // "boot"-like
        uint16_t rec_id  = 0x1234; // "count"-like
        int      res;

        // Read the boot count, this always works and the value is correct
        bool exists = false;
        res = read_long(file_id, rec_id, &m_boot_cnt);
        if ( res == -1 ) {
            NRF_LOG_INFO("boot count CAN'T FIND\r\n"); NRF_LOG_FLUSH();
        } else if ( res == -2 ) {
            NRF_LOG_INFO("boot count found but CAN'T READ\r\n"); NRF_LOG_FLUSH();
        } else {
            NRF_LOG_INFO("boot count: %d\r\n", m_boot_cnt); NRF_LOG_FLUSH();
            exists = true;
        }


        // what's above works, what's below does not work in DFU Handler context but does work after FDS setup


        // Zero the boot count, this tells the next reboot that a DFU just happened.
        m_boot_cnt = 0;
        if ( exists ) {
            res = update_long(file_id, rec_id, m_boot_cnt);
            if ( res == -1 ) {
                NRF_LOG_INFO("update_long() CAN'T FIND\r\n"); NRF_LOG_FLUSH();
            } else if ( res == -2 ) {
                NRF_LOG_INFO("update_long() found but CAN'T UPDATE\r\n"); NRF_LOG_FLUSH();
            }
        } else {
            res = write_long(file_id, rec_id, m_boot_cnt);
            if ( res != 0 ) {
                NRF_LOG_INFO("write_long() CAN'T WRITE\r\n"); NRF_LOG_FLUSH();
            }
        }

        //    wait_for_fds_write_complete(); // This NEVER returns, implying the update or write never finishes

    }

  • After writing to flash you need to delay resetting until after the flash operation has completed. However, your approach may not be the best in this case depending on your interrupt priorities. I would avoid the call to wait_for_fds_write_complete() that never returns, and instead check the l_fds_write_pending flag when you handle the NRF_PWR_MGMT_EVT_PREPARE_DFU event. If flash operation has not completed, return false from the app_shutdown_handler() in that case (I do not see this part of your code so here I am referring to SDK code which might not map exactly to your code). This event will then happen again and again until you are ready (flash operation has completed), and in that case, return true so that reset can occur and the device can start in bootloader DFU mode.

Reply
  • After writing to flash you need to delay resetting until after the flash operation has completed. However, your approach may not be the best in this case depending on your interrupt priorities. I would avoid the call to wait_for_fds_write_complete() that never returns, and instead check the l_fds_write_pending flag when you handle the NRF_PWR_MGMT_EVT_PREPARE_DFU event. If flash operation has not completed, return false from the app_shutdown_handler() in that case (I do not see this part of your code so here I am referring to SDK code which might not map exactly to your code). This event will then happen again and again until you are ready (flash operation has completed), and in that case, return true so that reset can occur and the device can start in bootloader DFU mode.

Children
No Data
Related