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

Extending application of SDK 12.3 for legacy buttonless DFU

Hello,

I need to develop an application for nRF51822 that uses SoftDevice 110 v8.0.0 using SDK 12.3 and extend it to use buttonless DFU for the legacy DFU bootloader of SDK 10/11. I've successfully added S110 v8.0.0 to SDK 12.3 and have its ble_app_uart example building and working well using Segger Embedded Studio 4.18 on macOS (on nRF51 DK).

The DFU bootloader of 12.3 is the newer secure bootloader, so instructions/examples for extending an application for buttonless DFU are for this newer secure bootloader. A couple tickets (here and here) state that it is possible to port the legacy DFU service and related libraries from SDK 10/11 to SDK 12.3.

The guide for extending an app for buttonless DFU in SDK 11 state that the application must use Device Manager. I understand that Device Manager was removed in SDK 12.x and replaced with Peer Manager. I'm a bit lost on how to go about this. Adding buttonless legacy DFU support to an application in SDK 10/11 relies on:

bootloader_util_arm.c in nRF51SDK1000dc26b5e/examples/dfu/experimental_ant_bootloader/
dfu_app_handler.c in nRF51SDK1000dc26b5e/components/libraries/bootloader_dfu
ble_dfu.c in nRF51SDK1000dc26b5e/components/ble/ble_services/ble_dfu

Adding buttonless DFU (secure) support to an application in SDK 12.3 relies on:

ble_dfu.c in
nrf_dfu_settings.c

I've tried approaching this a number of different ways, all unsuccessful so far. Before providing details, thought I would ask for some initial guidance. General steps for extending the ble_app_uart application of SDK 12.3 with S110 v8.0.0 for buttonless legacy DFU.

Many thanks,

Tim

Parents
  • Hi Tim, 

    as far as I remember, the Device Manager is only used to fetch the Peer Data. So I think that you only have to modify the dfu_app_peer_data_set() function 

    /**@brief Function for providing peer information to DFU for re-establishing a bonded connection in
     *        DFU mode.
     *
     * @param[in] conn_handle   Connection handle for the connection requesting DFU mode.
     */
    static void dfu_app_peer_data_set(uint16_t conn_handle)
    {
        uint32_t                 err_code;
        dm_sec_keyset_t          key_set;
        uint32_t                 app_context_data = 0;
        dm_application_context_t app_context;
    
    
    /** [DFU bond sharing] */
        err_code = dm_handle_get(conn_handle, &m_dm_handle);
        if (err_code == NRF_SUCCESS)
        {
            err_code = dm_distributed_keys_get(&m_dm_handle, &key_set);
            if (err_code == NRF_SUCCESS)
            {
                APP_ERROR_CHECK(err_code);
    
                m_peer_data.addr              = key_set.keys_central.p_id_key->id_addr_info;
                m_peer_data.irk               = key_set.keys_central.p_id_key->id_info;
                m_peer_data.enc_key.enc_info  = key_set.keys_periph.enc_key.p_enc_key->enc_info;
                m_peer_data.enc_key.master_id = key_set.keys_periph.enc_key.p_enc_key->master_id;
    
                err_code = dfu_ble_svc_peer_data_set(&m_peer_data);
                APP_ERROR_CHECK(err_code);
    
                app_context_data   = (DFU_APP_ATT_TABLE_CHANGED << DFU_APP_ATT_TABLE_POS);
                app_context.len    = sizeof(app_context_data);
                app_context.p_data = (uint8_t *)&app_context_data;
                app_context.flags  = 0;
    
                err_code = dm_application_context_set(&m_dm_handle, &app_context);
                APP_ERROR_CHECK(err_code);
            }
            else
            {
                // Keys were not available, thus we have a non-encrypted connection.
                err_code = dm_peer_addr_get(&m_dm_handle, &m_peer_data.addr);
                APP_ERROR_CHECK(err_code);
    
                err_code = dfu_ble_svc_peer_data_set(&m_peer_data);
                APP_ERROR_CHECK(err_code);
            }
        }
    /** [DFU bond sharing] */
    }

    Now to replace the DM code with the equivalent Peer Manager APIs I suggest that you take a look at the retrieve_peer_data() function in ble_dfu_bonded.c in SDK v14.1.0. SDK v12.x and v13.x  did not have any bond sharing feature implementend, hence its better to look at v14.x.

    static uint32_t retrieve_peer_data(void)
    {
        ret_code_t              ret_code;
        pm_peer_data_bonding_t  bonding_data;
        pm_peer_id_t            peer_id;
    
        ret_code = pm_peer_id_get(mp_dfu->conn_handle, &peer_id);
        VERIFY_SUCCESS(ret_code);
    
        if (peer_id == PM_PEER_ID_INVALID)
        {
            return NRF_ERROR_FORBIDDEN;
        }
    
        ret_code = pm_peer_data_bonding_load(peer_id, &bonding_data);
        VERIFY_SUCCESS(ret_code);
    
        memcpy(&m_peer_data.ble_id, &bonding_data.peer_ble_id, sizeof(ble_gap_id_key_t));
        memcpy(&m_peer_data.enc_key, &bonding_data.own_ltk, sizeof(ble_gap_enc_key_t));
    
        uint16_t len = SYSTEM_SERVICE_ATT_SIZE;
        ret_code = sd_ble_gatts_sys_attr_get(mp_dfu->conn_handle,
                                             m_peer_data.sys_serv_attr,
                                             &len,
                                             BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
    
        NRF_LOG_INFO("---------------system attribute table: %d--------------", len);
    
        VERIFY_SUCCESS(ret_code);
    
        return NRF_SUCCESS;
    }

    So dm_handle_get() and dm_distributed_keys_get() can be replaced with pm_peer_id_get() and pm_peer_data_bonding_load(). Once you have the bonding data ( pm_peer_data_bonding_t) you just need to copy the correct content into a dfu_ble_peer_data_t struct and then pass that to dfu_ble_svc_peer_data_set. 

    typedef struct
    {
        uint8_t           own_role;    /**< @brief The BLE role of the local device during bonding. See @ref BLE_GAP_ROLES. */
        ble_gap_id_key_t  peer_ble_id; /**< @brief The peer's Bluetooth address and identity resolution key (IRK). */
        ble_gap_enc_key_t peer_ltk;    /**< @brief The peer's long-term encryption key (LTK) and master ID. */
        ble_gap_enc_key_t own_ltk;     /**< @brief Locally generated long-term encryption key (LTK) and master ID, distributed to the peer. */
    } pm_peer_data_bonding_t;
    
    typedef struct
    {
        ble_gap_addr_t      addr;                                   /**< BLE GAP address of the device that initiated the DFU process. */
        ble_gap_irk_t       irk;                                    /**< IRK of the device that initiated the DFU process if this device uses Private Resolvable Addresses. */
        ble_gap_enc_key_t   enc_key;                                /**< Encryption key structure containing encrypted diversifier and LTK for re-establishing the bond. */
        uint8_t             sys_serv_attr[SYSTEM_SERVICE_ATT_SIZE]; /**< System service attributes for restoring of Service Changed Indication setting in DFU mode. */
    } dfu_ble_peer_data_t;

    On the bootloader side you should not have to do any changes. 

    Best regards

    Bjørn

  • Hi Bjørn, and thanks.

    I'm now unclear about whether or not Peer Manager is required for our application. Our application is based on the ble_app_uart example of SDK 12.3, to be extended for legacy buttonless DFU. I've manually added S110 to SDK 12.3 and have ble_app_uart working with it.

    An encrypted link (bonding) with the central is not necessary, either for our application's normal use or for DFU over BLE.

    If bonding is not necessary for legacy buttonless DFU, can I avoid the use of Peer Manager altogether? Is the work of adding legacy buttonless DFU to SDK 12.3 app thereby simplified? Could I simply reduce dfu_app_peer_data_set() in dfu_app_handler.c (SDK 11.0) to this code, with dm calls/types replaced with non-dm, non-pm equivalents?:

    static void dfu_app_peer_data_set(uint16_t conn_handle)
    {
        uint32_t                 err_code;
        dm_sec_keyset_t          key_set;
        uint32_t                 app_context_data = 0;
        dm_application_context_t app_context;
    
    
    /** [DFU bond sharing] */
        err_code = dm_handle_get(conn_handle, &m_dm_handle);
        if (err_code == NRF_SUCCESS)
        {
            // Keys were not available, thus we have a non-encrypted connection.
            err_code = dm_peer_addr_get(&m_dm_handle, &m_peer_data.addr);
            APP_ERROR_CHECK(err_code);
    
            err_code = dfu_ble_svc_peer_data_set(&m_peer_data);
            APP_ERROR_CHECK(err_code);
        }
    /** [DFU bond sharing] */
    }

    If our application must use Peer Manager in order to support legacy buttonless DFU, do I need to follow the instructions of your Migrating to Peer Manager guide in addition to the code changes you note?

    I'm finding compile errors because of incompatibilities between SDK 12.3 Peer Manager and S110, which I manually added to SDK 12.3.

    In a way I feel very close to a solution, but my inexperience with low level BLE and Nordic SDK is making it challenging.

    Thank you for your assistance.

    Tim

  • Hi Tim, 
    If you don't plan to forward any peer data to the bootloader, it will be quite simple to do buttonless DFU. 

    The main function of buttonless DFU is to switch to the bootloader and tell the bootloader to enter DFU mode instead of booting up the application. 

    You can do this by, in your application, simply write to NRF_POWER->GPREGRET value 0xB1 (BOOTLOADER_DFU_START) and then jump to the bootloader. I belive you just need to call this function, the similar to what  in the ble_app_hrs buttonless in SDK v11:

        err_code = sd_power_gpregret_set(BOOTLOADER_DFU_START);
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_softdevice_disable();
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_softdevice_vector_table_base_set(NRF_UICR->NRFFW[0]);
        APP_ERROR_CHECK(err_code);
    
        NVIC_ClearPendingIRQ(SWI2_IRQn);
    
        interrupts_disable();
        bootloader_util_app_start(NRF_UICR->NRFFW[0]);

    Just simply ignore the peer data stuff, it will be ignored by the bootloader if you don't write anything to it. But note that the bootloader will do advertises with the origin address +1. This is to avoid the original address is bonded to the phone, the bootloader will advertise as a new device. 

Reply
  • Hi Tim, 
    If you don't plan to forward any peer data to the bootloader, it will be quite simple to do buttonless DFU. 

    The main function of buttonless DFU is to switch to the bootloader and tell the bootloader to enter DFU mode instead of booting up the application. 

    You can do this by, in your application, simply write to NRF_POWER->GPREGRET value 0xB1 (BOOTLOADER_DFU_START) and then jump to the bootloader. I belive you just need to call this function, the similar to what  in the ble_app_hrs buttonless in SDK v11:

        err_code = sd_power_gpregret_set(BOOTLOADER_DFU_START);
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_softdevice_disable();
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_softdevice_vector_table_base_set(NRF_UICR->NRFFW[0]);
        APP_ERROR_CHECK(err_code);
    
        NVIC_ClearPendingIRQ(SWI2_IRQn);
    
        interrupts_disable();
        bootloader_util_app_start(NRF_UICR->NRFFW[0]);

    Just simply ignore the peer data stuff, it will be ignored by the bootloader if you don't write anything to it. But note that the bootloader will do advertises with the origin address +1. This is to avoid the original address is bonded to the phone, the bootloader will advertise as a new device. 

Children
No Data
Related