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

Swift Pairing for custom gatt service

Hi,

I am using custom gatt service & I have included some of the code which supports swift pairing from ble_hid_mouse example. Here is my sniffer log -

mtu_log.pcapng

& the jlink rtt log -

paired - Copy.txt

For my application, the MTU size is 32 & data length is 36.

Windows keeps negotiating the size to be 512.

After I pair to the peripheral by connecting through the Windows bluetooth menu, it takes 1 minute to establish the ble connection & start data communication. If I dont pair, then the connection is quick. Now I know you will tell me the app has to make a connection. But I would also like to support manual pairing. Our device gets bricked if someone accidentally pairs with the device through Windows bluetooth menu. To avoid this I had to add the swift pairing support.

How do I expedite the connection on Windows for swift pairing? (note the same firmware without swift pairing feature works on Android OS perfectly fine with or without pairing.)

Parents
  • Hi

    Can you try adding a small delay in nrf_ble_gatt.c as shown below? This is supposed to avoid an LMP_RESPONSE_TIMEOUT, which your issue might be a variant of.

    static void on_connected_evt(nrf_ble_gatt_t * p_gatt, ble_evt_t const * p_ble_evt)
    
    {
    
    …
    
            nrf_delay_ms(100); // #include "nrf_delay.h"
    
            err_code = sd_ble_gattc_exchange_mtu_request(conn_handle, p_link->att_mtu_desired);
    
    …
    
    }

    Best regards,

    Simon

  • Now it takes more than a minute to connect. Disappointed Behavior is still the same..

    Here are the logs - 

    1. Added delay along with the above 4 lines

    with_delay_and_4_lines.pcapng

    RTT_with_delay_and_4_lines.txt

    2. Added delay only

    with_delay.pcapng

    RTT_with_delay.txt

    In both cases, the peripheral connects to Windows OS but it takes more than a minute.

    I also tried increasing/decreasing the time in these 2 lines, but no luck

    opt.gap_opt.auth_payload_timeout.auth_payload_timeout = 3000;

    nrf_delay_ms(100);

  • Have you done any changes to peer_manager_handler.c or the modules it is using? I don't expect you have, but ask anyways.

    Have you set PM_HANDLER_SEC_DELAY_MS to anything beside 0?

    Can you confirm you are using S132v6.1.1?

    Are you somehow initiating security procedure on connected event manually by for instance calling pm_handler_secure_on_connection() in your application?

    From the log of "with_delay_and_4_lines.pcapng" I can see that the LL_LENGTH_REQ is sent from the peripheral after ~20ms. This means that the 100ms delay was not run, so I suggest to add the 100ms delay in the start of on_connected_evt(), e.g.:

    static void on_connected_evt(nrf_ble_gatt_t * p_gatt, ble_evt_t const * p_ble_evt)
    {
    nrf_delay_ms(100); // Place it in the start of function, #include "nrf_delay.h"
    …
    …
    }

    I assume the real problem here is the LL_PING_RSP that is always sent from the master without any preceding LL_PING_REQ. What details can you provide about the Windows 10 version you are using? https://en.wikipedia.org/wiki/Template:Windows_10_versions 

    Have you tried updating Windows 10 to the latest version? Are all BT drivers updated?

    Best regards,
    Kenneth

  • Hi Kenneth,

    Answers to your questions -

    I did not make any changes to peer_manager_handler.c.

    PM_HANDLER_SEC_DELAY_MS is set to 0 in sdk_config.h

    I'm using S132 from SDK16.0, dont know the version though.

    Security procedure is not initiated manually.

    Here are the new logs-

    https://www.dropbox.com/s/nyj104bcryw8cg4/log_4th_may.pcapng?dl=0

    https://www.dropbox.com/s/jlq7o3u5ei37t6r/rtt_log_4th_may.txt?dl=0


    The sniffer trace looks different. I think you are right about LL_PING_RSP.

    My Windows Specifications-

    & now I have updated to 

    Logs after updating -

    https://www.dropbox.com/s/suasjwkssatsqkt/log_after_update.pcapng?dl=0

    https://www.dropbox.com/s/yx0opw4vu4guvh7/rt_log_after_update.txt?dl=0

  • I think the main problem here is that the slave seem to always send a "Security Request: AuthReq: Bonding", which should not be necessary, since the link is already encrypted or in the process of being encrypted. I think that is what is causing the master to misbehave by sending a LL_PING_RSP later.

    If you open any of the other SDK examples as-is that support bonding, you will see that it is not sending a "Security Request: AuthReq: Bonding". So we need to try to find out what is causing this in your application.

    Can you provide the source for peer_manager_init(), sys_evt_dispatch(), ble_evt_dispatch(),  and pm_evt_handler()?

    Is it possible that you are deleting bonds here somewhere?

    *maybe check this first* Is it possible you are calling pm_conn_secure() somewhere?

    Can you provide your defines of FDS*/FSTORAGE* in sdk_config.h?

    What security level have you set for your various characteristics and services? For instance have you set BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM() or other? See ble_gap.h for a list of available BLE_GAP_CONN_SEC_MODE_SET_MACROS.

    Best regards,
    Kenneth

  • Ahh I see, do you need the code snippets for peer_manager_init(), sys_evt_dispatch() and pm_evt_handler() or do you need the location where these are being called?

    static void peer_manager_init()
    {
        ble_gap_sec_params_t sec_param;
        ret_code_t           err_code;
        err_code = pm_init();
        APP_ERROR_CHECK(err_code);
        memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
        // Security parameters to be used for all security procedures.
        sec_param.bond           = SEC_PARAM_BOND;
        sec_param.mitm           = SEC_PARAM_MITM;
        sec_param.lesc           = SEC_PARAM_LESC;
        sec_param.keypress       = SEC_PARAM_KEYPRESS;
        sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
        sec_param.oob            = SEC_PARAM_OOB;
        sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
        sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
        sec_param.kdist_own.enc  = 1;
        sec_param.kdist_own.id   = 1;
        sec_param.kdist_peer.enc = 1;
        sec_param.kdist_peer.id  = 1;
        err_code = pm_sec_params_set(&sec_param);
        APP_ERROR_CHECK(err_code);
        err_code = pm_register(pm_evt_handler);
        APP_ERROR_CHECK(err_code);
    }

    #define SEC_PARAM_BOND                  1                              /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                           /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                           /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                           /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                        /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                           /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                           /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                          /**< Maximum encryption key size. */

    I couldnt find sys_evt_dispatch() in my project.

    static void pm_evt_handler(pm_evt_t const * p_evt)
    {
    	ret_code_t err_code;
        pm_handler_on_pm_evt(p_evt);
        pm_handler_flash_clean(p_evt);
    	/*not included*/
    	switch (p_evt->evt_id)
        {
    		case PM_EVT_BONDED_PEER_CONNECTED:
            {
                NRF_LOG_INFO("PM_EVT_BONDED_PEER_CONNECTED - Connected to a previously bonded device.");
            } break;
    		case PM_EVT_CONN_SEC_START:
    			NRF_LOG_INFO("PM_EVT_CONN_SEC_START");
    		break;
    		case PM_EVT_CONN_SEC_SUCCEEDED:
            {
                NRF_LOG_INFO("PM_EVT_CONN_SEC_SUCCEEDED - Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.", ble_conn_state_role(p_evt->conn_handle),
                             p_evt->conn_handle, p_evt->params.conn_sec_succeeded.procedure);
            } break;
    		case PM_EVT_CONN_SEC_FAILED:
            {
    			NRF_LOG_INFO("PM_EVT_CONN_SEC_FAILED");
                /* Often, when securing fails, it shouldn't be restarted, for security reasons. Other times, it can be restarted directly.
                 * Sometimes it can be restarted, but only after changing some Security Parameters. Sometimes, it cannot be restarted until the link is disconnected and reconnected.
                 * Sometimes it is impossible, to secure the link, or the peer device does not support it. How to handle this error is highly application dependent. */
            } break;
    		case PM_EVT_CONN_SEC_CONFIG_REQ:
            {
    			NRF_LOG_INFO("PM_EVT_CONN_SEC_CONFIG_REQ");
                // Reject pairing request from an already bonded peer.
                pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
                pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
            } break;
    		case PM_EVT_CONN_SEC_PARAMS_REQ:
    			NRF_LOG_INFO("PM_EVT_CONN_SEC_PARAMS_REQ");
    		break;
    		case PM_EVT_STORAGE_FULL:
            {
    			NRF_LOG_INFO("PM_EVT_STORAGE_FULL");
                // Run garbage collection on the flash.
                err_code = fds_gc();
                if (err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
                {
                    // Retry.
                }
                else
                {
                    APP_ERROR_CHECK(err_code);
                }
            } break;
    		case PM_EVT_ERROR_UNEXPECTED:
            {
    			NRF_LOG_INFO("PM_EVT_ERROR_UNEXPECTED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
            } break;
    		case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
    			NRF_LOG_INFO("PM_EVT_PEER_DATA_UPDATE_SUCCEEDED");
    			if (p_evt->params.peer_data_update_succeeded.flash_changed && (p_evt->params.peer_data_update_succeeded.data_id == PM_PEER_DATA_ID_BONDING))
                {
                    NRF_LOG_INFO("New Bond, add the peer to the whitelist if possible");
    				// Note: You should check on what kind of white list policy your application should use.
                    whitelist_set(PM_PEER_ID_LIST_SKIP_NO_ID_ADDR);	/*For Quest*/
                }
            break;
    		case PM_EVT_PEER_DATA_UPDATE_FAILED:
            {
    			NRF_LOG_INFO("PM_EVT_PEER_DATA_UPDATE_FAILED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error);
            } break;		
    		case PM_EVT_PEER_DELETE_SUCCEEDED:
    			NRF_LOG_INFO("PM_EVT_PEER_DELETE_SUCCEEDED");
    		break;
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
    			NRF_LOG_INFO("PM_EVT_PEERS_DELETE_SUCCEEDED");
                advertising_start(false);
            break;		
    		case PM_EVT_PEER_DELETE_FAILED:
            {
    			NRF_LOG_INFO("PM_EVT_PEER_DELETE_FAILED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
            } break;
    		case PM_EVT_PEERS_DELETE_FAILED:
            {
    			NRF_LOG_INFO("PM_EVT_PEERS_DELETE_FAILED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
            } break;
            case PM_EVT_LOCAL_DB_CACHE_APPLIED:
    			NRF_LOG_INFO("PM_EVT_LOCAL_DB_CACHE_APPLIED");
    		break;
            case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
    			NRF_LOG_INFO("PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED");
    		break;
                // This can happen when the local DB has changed.
            case PM_EVT_SERVICE_CHANGED_IND_SENT:
    			NRF_LOG_INFO("PM_EVT_SERVICE_CHANGED_IND_SENT");
    		break;
            case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
    			NRF_LOG_INFO("PM_EVT_SERVICE_CHANGED_IND_CONFIRMED");
    		break;
    		case PM_EVT_SLAVE_SECURITY_REQ:
    			NRF_LOG_INFO("PM_EVT_SLAVE_SECURITY_REQ");
    		break;
    		case PM_EVT_FLASH_GARBAGE_COLLECTED:
    			NRF_LOG_INFO("PM_EVT_FLASH_GARBAGE_COLLECTED");
    		break;
    		case PM_EVT_FLASH_GARBAGE_COLLECTION_FAILED:
    			NRF_LOG_INFO("PM_EVT_FLASH_GARBAGE_COLLECTION_FAILED");
    		break;
            default:
                break;
    	}
    }

    FDS defines -

    // <e> FDS_ENABLED - fds - Flash data storage module
    //==========================================================
    #ifndef FDS_ENABLED
    #define FDS_ENABLED 1
    #endif
    // <h> Pages - Virtual page settings
    
    // <i> Configure the number of virtual pages to use and their size.
    //==========================================================
    // <o> FDS_VIRTUAL_PAGES - Number of virtual flash pages to use. 
    // <i> One of the virtual pages is reserved by the system for garbage collection.
    // <i> Therefore, the minimum is two virtual pages: one page to store data and one page to be used by the system for garbage collection.
    // <i> The total amount of flash memory that is used by FDS amounts to @ref FDS_VIRTUAL_PAGES * @ref FDS_VIRTUAL_PAGE_SIZE * 4 bytes.
    
    #ifndef FDS_VIRTUAL_PAGES
    #define FDS_VIRTUAL_PAGES 3
    #endif
    
    // <o> FDS_VIRTUAL_PAGE_SIZE  - The size of a virtual flash page.
     
    
    // <i> Expressed in number of 4-byte words.
    // <i> By default, a virtual page is the same size as a physical page.
    // <i> The size of a virtual page must be a multiple of the size of a physical page.
    // <1024=> 1024 
    // <2048=> 2048 
    
    #ifndef FDS_VIRTUAL_PAGE_SIZE
    #define FDS_VIRTUAL_PAGE_SIZE 1024
    #endif
    
    // <o> FDS_VIRTUAL_PAGES_RESERVED - The number of virtual flash pages that are used by other modules. 
    // <i> FDS module stores its data in the last pages of the flash memory.
    // <i> By setting this value, you can move flash end address used by the FDS.
    // <i> As a result the reserved space can be used by other modules.
    
    #ifndef FDS_VIRTUAL_PAGES_RESERVED
    #define FDS_VIRTUAL_PAGES_RESERVED 0
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> Backend - Backend configuration
    
    // <i> Configure which nrf_fstorage backend is used by FDS to write to flash.
    //==========================================================
    // <o> FDS_BACKEND  - FDS flash backend.
     
    
    // <i> NRF_FSTORAGE_SD uses the nrf_fstorage_sd backend implementation using the SoftDevice API. Use this if you have a SoftDevice present.
    // <i> NRF_FSTORAGE_NVMC uses the nrf_fstorage_nvmc implementation. Use this setting if you don't use the SoftDevice.
    // <1=> NRF_FSTORAGE_NVMC 
    // <2=> NRF_FSTORAGE_SD 
    
    #ifndef FDS_BACKEND
    #define FDS_BACKEND 2
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> Queue - Queue settings
    
    //==========================================================
    // <o> FDS_OP_QUEUE_SIZE - Size of the internal queue. 
    // <i> Increase this value if you frequently get synchronous FDS_ERR_NO_SPACE_IN_QUEUES errors.
    
    #ifndef FDS_OP_QUEUE_SIZE
    #define FDS_OP_QUEUE_SIZE 4
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> CRC - CRC functionality
    
    //==========================================================
    // <e> FDS_CRC_CHECK_ON_READ - Enable CRC checks.
    
    // <i> Save a record's CRC when it is written to flash and check it when the record is opened.
    // <i> Records with an incorrect CRC can still be 'seen' by the user using FDS functions, but they cannot be opened.
    // <i> Additionally, they will not be garbage collected until they are deleted.
    //==========================================================
    #ifndef FDS_CRC_CHECK_ON_READ
    #define FDS_CRC_CHECK_ON_READ 0
    #endif
    // <o> FDS_CRC_CHECK_ON_WRITE  - Perform a CRC check on newly written records.
     
    
    // <i> Perform a CRC check on newly written records.
    // <i> This setting can be used to make sure that the record data was not altered while being written to flash.
    // <1=> Enabled 
    // <0=> Disabled 
    
    #ifndef FDS_CRC_CHECK_ON_WRITE
    #define FDS_CRC_CHECK_ON_WRITE 0
    #endif
    
    // </e>
    
    // </h> 
    //==========================================================
    
    // <h> Users - Number of users
    
    //==========================================================
    // <o> FDS_MAX_USERS - Maximum number of callbacks that can be registered. 
    #ifndef FDS_MAX_USERS
    #define FDS_MAX_USERS 4
    #endif

    FSTORAGE defines -

    // <e> NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library
    //==========================================================
    #ifndef NRF_FSTORAGE_ENABLED
    #define NRF_FSTORAGE_ENABLED 1
    #endif
    // <h> nrf_fstorage - Common settings
    
    // <i> Common settings to all fstorage implementations
    //==========================================================
    // <q> NRF_FSTORAGE_PARAM_CHECK_DISABLED  - Disable user input validation
     
    
    // <i> If selected, use ASSERT to validate user input.
    // <i> This effectively removes user input validation in production code.
    // <i> Recommended setting: OFF, only enable this setting if size is a major concern.
    
    #ifndef NRF_FSTORAGE_PARAM_CHECK_DISABLED
    #define NRF_FSTORAGE_PARAM_CHECK_DISABLED 0
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> nrf_fstorage_sd - Implementation using the SoftDevice
    
    // <i> Configuration options for the fstorage implementation using the SoftDevice
    //==========================================================
    // <o> NRF_FSTORAGE_SD_QUEUE_SIZE - Size of the internal queue of operations 
    // <i> Increase this value if API calls frequently return the error @ref NRF_ERROR_NO_MEM.
    
    #ifndef NRF_FSTORAGE_SD_QUEUE_SIZE
    #define NRF_FSTORAGE_SD_QUEUE_SIZE 4
    #endif
    
    // <o> NRF_FSTORAGE_SD_MAX_RETRIES - Maximum number of attempts at executing an operation when the SoftDevice is busy 
    // <i> Increase this value if events frequently return the @ref NRF_ERROR_TIMEOUT error.
    // <i> The SoftDevice might fail to schedule flash access due to high BLE activity.
    
    #ifndef NRF_FSTORAGE_SD_MAX_RETRIES
    #define NRF_FSTORAGE_SD_MAX_RETRIES 8
    #endif
    
    // <o> NRF_FSTORAGE_SD_MAX_WRITE_SIZE - Maximum number of bytes to be written to flash in a single operation 
    // <i> This value must be a multiple of four.
    // <i> Lowering this value can increase the chances of the SoftDevice being able to execute flash operations in between radio activity.
    // <i> This value is bound by the maximum number of bytes that can be written to flash in a single call to @ref sd_flash_write.
    // <i> That is 1024 bytes for nRF51 ICs and 4096 bytes for nRF52 ICs.
    
    #ifndef NRF_FSTORAGE_SD_MAX_WRITE_SIZE
    #define NRF_FSTORAGE_SD_MAX_WRITE_SIZE 4096
    #endif

    pm_conn_secure() is not called in main.c

    I never use the delete_bonds() function & I also dont use BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM().

  • Edit: Removed this reply since it was invalid for new SDK.

Reply Children
  • I tried adding all these functions, seems like they have been deprecated in SDK 16 according to few tickets and the Migration doc.

    I'm getting these errors in my Keil project.

  • Sorry, you are right. I was looking at an old SDK.

    My suggestion now is to take a look at the \examples\ble_peripheral\ble_app_hids_keyboard or ble_hid_mouse, replace all the parameters and callbacks from that project to your project. Double check that you have not by accident modified any of the SDK libraries for instance to test something earlier.

    You will notice that the \examples\ble_peripheral\ble_app_hids_keyboard or ble_hid_mouse will not send the "Security Request: AuthReq: Bonding". If you are not able to find the problem, then only way forward would be to share a project that can be unzipped to the examples\ble_peripheral and run an nRF52-DK to test here.

    Best regards,
    Kenneth

  • This workaround only works for Windows, not Android

    case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
        // Pairing not supported
        err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, NULL, NULL);
        APP_ERROR_CHECK(err_code);
        break;
    case BLE_GATTS_EVT_SYS_ATTR_MISSING:
        // No system attributes have been stored.
        err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
        APP_ERROR_CHECK(err_code);
        break;

    OR

    case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
        // Pairing not supported
        err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
        APP_ERROR_CHECK(err_code);
        break;
    case BLE_GATTS_EVT_SYS_ATTR_MISSING:
        // No system attributes have been stored.
        err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
        APP_ERROR_CHECK(err_code);
        break;

    But on Android, I get an error 'incorrect pin or passkey', how do I overcome that?

  • If you have included peer manager, then those softdevice calls and events are already handled by the module. You should not add additional handling of those two BLE softdevice events directly in your application code. You may however instead use events from the peer manager to override or change the reply by in pm_evt_handler(), you can find a list of events that come from the peer manager module in peer_manager_types.h:

    /**@brief Types of events that can come from the @ref peer_manager module.
     */
    typedef enum
    {
        PM_EVT_BONDED_PEER_CONNECTED,           /**< @brief A connected peer has been identified as one with which we have a bond. When performing bonding with a peer for the first time, this event will not be sent until a new connection is established with the peer. When we are central, this event is always sent when the Peer Manager receives the @ref BLE_GAP_EVT_CONNECTED event. When we are peripheral, this event might in rare cases arrive later. */
        PM_EVT_CONN_SEC_START,                  /**< @brief A security procedure has started on a link, initiated either locally or remotely. The security procedure is using the last parameters provided via @ref pm_sec_params_set. This event is always followed by either a @ref PM_EVT_CONN_SEC_SUCCEEDED or a @ref PM_EVT_CONN_SEC_FAILED event. This is an informational event; no action is needed for the procedure to proceed. */
        PM_EVT_CONN_SEC_SUCCEEDED,              /**< @brief A link has been encrypted, either as a result of a call to @ref pm_conn_secure or a result of an action by the peer. The event structure contains more information about the circumstances. This event might contain a peer ID with the value @ref PM_PEER_ID_INVALID, which means that the peer (central) used an address that could not be identified, but it used an encryption key (LTK) that is present in the database. */
        PM_EVT_CONN_SEC_FAILED,                 /**< @brief A pairing or encryption procedure has failed. In some cases, this means that security is not possible on this link (temporarily or permanently). How to handle this error depends on the application. */
        PM_EVT_CONN_SEC_CONFIG_REQ,             /**< @brief The peer (central) has requested pairing, but a bond already exists with that peer. Reply by calling @ref pm_conn_sec_config_reply before the event handler returns. If no reply is sent, a default is used. */
        PM_EVT_CONN_SEC_PARAMS_REQ,             /**< @brief Security parameters (@ref ble_gap_sec_params_t) are needed for an ongoing security procedure. Reply with @ref pm_conn_sec_params_reply before the event handler returns. If no reply is sent, the parameters given in @ref pm_sec_params_set are used. If a peripheral connection, the central's sec_params will be available in the event. */
        PM_EVT_STORAGE_FULL,                    /**< @brief There is no more room for peer data in flash storage. To solve this problem, delete data that is not needed anymore and run a garbage collection procedure in FDS. */
        PM_EVT_ERROR_UNEXPECTED,                /**< @brief An unrecoverable error happened inside Peer Manager. An operation failed with the provided error. */
        PM_EVT_PEER_DATA_UPDATE_SUCCEEDED,      /**< @brief A piece of peer data was stored, updated, or cleared in flash storage. This event is sent for all successful changes to peer data, also those initiated internally in Peer Manager. To identify an operation, compare the store token in the event with the store token received during the initiating function call. Events from internally initiated changes might have invalid store tokens. */
        PM_EVT_PEER_DATA_UPDATE_FAILED,         /**< @brief A piece of peer data could not be stored, updated, or cleared in flash storage. This event is sent instead of @ref PM_EVT_PEER_DATA_UPDATE_SUCCEEDED for the failed operation. */
        PM_EVT_PEER_DELETE_SUCCEEDED,           /**< @brief A peer was cleared from flash storage, for example because a call to @ref pm_peer_delete succeeded. This event can also be sent as part of a call to @ref pm_peers_delete or internal cleanup. */
        PM_EVT_PEER_DELETE_FAILED,              /**< @brief A peer could not be cleared from flash storage. This event is sent instead of @ref PM_EVT_PEER_DELETE_SUCCEEDED for the failed operation. */
        PM_EVT_PEERS_DELETE_SUCCEEDED,          /**< @brief A call to @ref pm_peers_delete has completed successfully. Flash storage now contains no peer data. */
        PM_EVT_PEERS_DELETE_FAILED,             /**< @brief A call to @ref pm_peers_delete has failed, which means that at least one of the peers could not be deleted. Other peers might have been deleted, or might still be queued to be deleted. No more @ref PM_EVT_PEERS_DELETE_SUCCEEDED or @ref PM_EVT_PEERS_DELETE_FAILED events are sent until the next time @ref pm_peers_delete is called. */
        PM_EVT_LOCAL_DB_CACHE_APPLIED,          /**< @brief Local database values for a peer (taken from flash storage) have been provided to the SoftDevice. */
        PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED,     /**< @brief Local database values for a peer (taken from flash storage) were rejected by the SoftDevice, which means that either the database has changed or the user has manually set the local database to an invalid value (using @ref pm_peer_data_store). */
        PM_EVT_SERVICE_CHANGED_IND_SENT,        /**< @brief A service changed indication has been sent to a peer, as a result of a call to @ref pm_local_database_has_changed. This event will be followed by a @ref PM_EVT_SERVICE_CHANGED_IND_CONFIRMED event if the peer acknowledges the indication. */
        PM_EVT_SERVICE_CHANGED_IND_CONFIRMED,   /**< @brief A service changed indication that was sent has been confirmed by a peer. The peer can now be considered aware that the local database has changed. */
        PM_EVT_SLAVE_SECURITY_REQ,              /**< @brief The peer (peripheral) has requested link encryption, which has been enabled. */
        PM_EVT_FLASH_GARBAGE_COLLECTED,         /**< @brief The flash has been garbage collected (By FDS), possibly freeing up space. */
        PM_EVT_FLASH_GARBAGE_COLLECTION_FAILED, /**< @brief Garbage collection was attempted but failed. */
    } pm_evt_id_t;

Related