This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

BLE Pairing Lock-up

Hello All,

I am experiencing an issue while attempting to pair devices.  From my debug output, I can tell that pairing started successfully, but some point afterwards both devices appear to lock-up. 

My application is setup to utilize a passkey provided by host processors (on both sides of the link).  When the host provides the passkey, I update the GAPBondMgr GAPBOND_DEFAULT_PASSCODE parameter.

My Peripheral device is setup in the following manner:

// GAP Bond Manager Callbacks
static const gapBondCBs_t simpleBLEPeripheral_BondMgrCBs =
{
    // Passcode callback
    NULL,
    // Pairing/Bonding state change callback
    PairingStateNotificationCB
};
static void SetupBondMgr( void )
{
    uint32 passkey = INVALID_PASSCODE;
    uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
    uint8 mitm     = TRUE;
    uint8 ioCap    = GAPBOND_IO_CAP_KEYBOARD_ONLY;
    uint8 bonding  = TRUE;
    uint8 autoSync = FALSE;

    GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE,
                             sizeof( uint32 ),
                             &passkey );

    GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE,
                             sizeof( uint8 ),
                             &pairMode );

    GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION,
                             sizeof( uint8 ),
                             &mitm );

    GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES,
                             sizeof( uint8 ),
                             &ioCap );

    GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED,
                             sizeof( uint8 ),
                             &bonding );

    GAPBondMgr_SetParameter( GAPBOND_AUTO_SYNC_WL,
                             sizeof( uint8 ),
                             &autoSync );
}

My Central device is setup in the following manner:

// Bond Manager Callbacks
static const gapBondCBs_t simpleBLEBondCB =
{
    NULL,
    PairingStateNotificationCB
};

static void SetupBondMgr( void )
{
    uint32 passkey = INVALID_PASSCODE;
    uint8 pairMode = GAPBOND_PAIRING_MODE_INITIATE;
    uint8 mitm     = TRUE;
    uint8 ioCap    = GAPBOND_IO_CAP_KEYBOARD_ONLY;
    uint8 bonding  = TRUE;
    uint8 autoSync = FALSE;

    GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE,
                             sizeof( uint32 ),
                             &passkey );

    GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE,
                             sizeof( uint8 ),
                             &pairMode );

    GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION,
                             sizeof( uint8 ),
                             &mitm );

    GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES,
                             sizeof( uint8 ),
                             &ioCap );

    GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED,
                             sizeof( uint8 ),
                             &bonding );

    GAPBondMgr_SetParameter( GAPBOND_AUTO_SYNC_WL,
                             sizeof( uint8 ),
                             &autoSync );
}

The INVALID_PASSCODE definition is set to 0.  I have a guard condition that prevents a Connection attempt if GAPBOND_DEFAULT_PASSCODE  is 0.

As I mentioned, it appears that the devices are getting hung after pairing has successfully started.  Using the debugger I set breakpoints at the following highlighted locations:

uint8 GAPBondMgr_ProcessGAPMsg( gapEventHdr_t *pMsg )
{
  switch ( pMsg->opcode )
  {
    case GAP_PASSKEY_NEEDED_EVENT:
      {
        gapPasskeyNeededEvent_t *pPkt = (gapPasskeyNeededEvent_t *)pMsg;

        if ( pGapBondCB && pGapBondCB->passcodeCB )
        {
          // Ask app for a passcode
          pGapBondCB->passcodeCB( pPkt->deviceAddr, pPkt->connectionHandle, pPkt->uiInputs, pPkt->uiOutputs );
        }
        else
        {
          // No app support, use the default passcode
          if ( GAP_PasscodeUpdate( gapBond_Passcode, pPkt->connectionHandle ) != SUCCESS )
          {
            VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED );
          }
        }
      }
      break;

    case GAP_AUTHENTICATION_COMPLETE_EVENT:
      {
        gapAuthCompleteEvent_t *pPkt = (gapAuthCompleteEvent_t *)pMsg;

        // Should we save bonding information (one save at a time)
        if ( (pPkt->hdr.status == SUCCESS)             && 
             (pPkt->authState & SM_AUTH_STATE_BONDING) &&
             (pAuthEvt == NULL) )
        {
          gapBondRec_t bondRec;

          VOID osal_memset( &bondRec, 0, sizeof ( gapBondRec_t ) ) ;

          // Do we have a public address in the data?
          if ( pPkt->pIdentityInfo )
          {
            VOID osal_memcpy( bondRec.publicAddr, pPkt->pIdentityInfo->bd_addr, B_ADDR_LEN );
          }
          else
          {
            linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle );
            if ( pLinkItem )
            {
              VOID osal_memcpy( bondRec.publicAddr, pLinkItem->addr, B_ADDR_LEN );
            }
            else
            {
              // We don't have an address, so ignore the message.
              break;
            }
          }

          // Save off of the authentication state
          bondRec.stateFlags |= (pPkt->authState & SM_AUTH_STATE_AUTHENTICATED) ? GAP_BONDED_STATE_AUTHENTICATED : 0;

          if ( !gapBondMgrAddBond( &bondRec, pPkt ) )
          {
            // Notify our task to save bonding information in NV
            osal_set_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT );
      
            // We're not done with this message; it will be freed later
            return ( FALSE );
          }
        }

        // Call app state callback in the fail case. Success is handled after GAP_BOND_SAVE_REC_EVT.
        if ( pGapBondCB && pGapBondCB->pairStateCB )
        {
          pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, pPkt->hdr.status );
        }
      }

I found that the first breakpoint was hit (meaning that I am using the default passkey), but the second breakpoint was never hit (meaning that GAP_AUTHENTICATION_COMPLETE_EVENT was never raised.

I've also determined that GAP_TerminateAuth is never called meaning that either GAP_PasscodeUpdate was successful or it never returned.

Here is a snippet of my debug output on the Peripheral:

[simpleBLEPeripheral.c:748] USER: Peripheral State (0x1): Initialized
[simpleBLEPeripheral.c:751] USER: BLE Address: 0x1CBA8C1C6A62
[simpleBLEPeripheral.c:1418] FLOW: Received a 'Connection Change' Request
[simpleBLEPeripheral.c:1421] FLOW: --> Connect: 0x1
[simpleBLEPeripheral.c:1425] FLOW: Enabling Advertisements (Count 0x12c0)
[simpleBLEPeripheral.c:786] Peripheral State (0x2): Advertising
[simpleBLEPeripheral.c:823] Peripheral State (0x5): Connected
[simpleBLEPeripheral.c:914] Pairing State (0x0): Pairing Started
[simpleBLEPeripheral.c:918] --> Status (0x0): 'SUCCESS'

Here is a snippet of my debug output on the Central:

[simpleBLECentral.c:677] USER: Central Device Initialized
[simpleBLECentral.c:680] USER: BLE Address: 0x1CBA8C1C760A
[DeviceConfig.h:372] USER: Device Filter has been Enabled
[DeviceConfig.h:377] USER: --> Requested Device Filter: '0x1CBA8C1C6A62'
[simpleBLECentral.c:1339] FLOW: Received a 'Connection Change' Request
[simpleBLECentral.c:1342] FLOW: --> Connect: 0x1
[simpleBLECentral.c:1490] Connecting to '0x1CBA8C1C6A62'
[simpleBLECentral.c:962] Pairing State (0x0): Pairing Started
[simpleBLECentral.c:966] --> Status (0x0): 'SUCCESS'
[simpleBLECentral.c:796] Central Event (0x5): Connection Established (Handle 0x0)
[simpleBLECentral.c:799] USER: Connected to '0x1CBA8C1C6A62'

The interesting thing is that it appears that pairing is starting on the Central before the connection established event comes in.

Here is my pairing state notification handler:

static void PairingStateNotificationCB( uint16 connHandle,
                                        uint8 state,
                                        uint8 status )
{
    switch ( state )
    {
        case GAPBOND_PAIRING_STATE_STARTED:
        {
            MY_TRACE_INFO( "Pairing State (0x%x): Pairing Started",
                           state );

            MY_TRACE_INFO( "--> Status (0x%x): '%s'",
                           status,
                           bStatus_to_String( status ) );

            break;
        }
        case GAPBOND_PAIRING_STATE_COMPLETE:
        {
            MY_TRACE_INFO( "Pairing State (0x%x): Pairing Complete",
                           state );

            MY_TRACE_INFO( "--> Status (0x%x): '%s'",
                           status,
                           PairStatus_to_String( status ) );

            break;
        }
        case GAPBOND_PAIRING_STATE_BONDED:
        {
            MY_TRACE_INFO( "Pairing State (0x%x): Bonding Complete",
                           state );

            MY_TRACE_INFO( "--> Status (0x%x): '%s'",
                           status,
                           bStatus_to_String( status ) );

            break;
        }
        default:
        {
            MY_TRACE_ERROR( "Pairing State (0x%x): Invalid/Unhandled State",
                            state );

            MY_TRACE_ERROR( "--> Status (0x%x): '%s'",
                            status,
                            bStatus_to_String( status ) );

            HAL_ASSERT_FORCED();

            break;
        }
    }
}

Do you have any suggestions on how I can troubleshoot this issue?