/**************************************************************************************************

    Phyplus Microelectronics Limited confidential and proprietary.
    All rights reserved.

    IMPORTANT: All rights of this software belong to Phyplus Microelectronics
    Limited ("Phyplus"). Your use of this Software is limited to those
    specific rights granted under  the terms of the business contract, the
    confidential agreement, the non-disclosure agreement and any other forms
    of agreements as a customer or a partner of Phyplus. You may not use this
    Software unless you agree to abide by the terms of these agreements.
    You acknowledge that the Software may not be modified, copied,
    distributed or disclosed unless embedded on a Phyplus Bluetooth Low Energy
    (BLE) integrated circuit, either as a product or is integrated into your
    products.  Other than for the aforementioned purposes, you may not use,
    reproduce, copy, prepare derivative works of, modify, distribute, perform,
    display or sell this Software and/or its documentation for any purposes.

    YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
    PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
    INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
    NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
    PHYPLUS OR ITS SUBSIDIARIES BE LIABLE OR OBLIGATED UNDER CONTRACT,
    NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
    LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
    INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
    OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
    OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
    (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

**************************************************************************************************/

/**************************************************************************************************
    Filename:       gapbondmgr.c
    Revised:
    Revision:

    Description:    GAP peripheral profile manages bonded connections



**************************************************************************************************/

#if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )

/*********************************************************************
    INCLUDES
*/
#include "bcomdef.h"
#include "OSAL.h"
#include "osal_snv.h"
#include "gap.h"
#include "linkdb.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "hci.h"
#include "gattservapp.h"
#include "gapgattserver.h"
#include "gapbondmgr.h"

// temp set
// #define osal_snv_write( a, b, c )      (1)
// #define osal_snv_read( a, b, c )       (1)
// #define osal_snv_compact( a )          (1)
#define BOND_LOG(...)
//#define BOND_LOG   LOG

/*********************************************************************
    MACROS
*/

/*********************************************************************
    CONSTANTS
*/
// Task event types
//#define GAP_BOND_SYNC_CC_EVT                            0x0001 // Sync char config
//#define GAP_BOND_SAVE_REC_EVT                           0x0002 // Save bond record in NV

// Once NV usage reaches this percentage threshold, NV compaction gets triggered.
#define NV_COMPACT_THRESHOLD                            80

// Bonded State Flags
#define GAP_BONDED_STATE_AUTHENTICATED                  0x0001
#define GAP_BONDED_STATE_SERVICE_CHANGED                0x0002

/**
    GAP Bond Manager NV layout

    The NV definitions:
       BLE_NVID_GAP_BOND_START - starting NV ID
       GAP_BONDINGS_MAX - Maximum number of bonding allowed (10 is max for number of NV IDs allocated in bcomdef.h).

    A single bonding entry consists of 6 components (NV items):
       Bond Record - defined as gapBondRec_t and uses GAP_BOND_REC_ID_OFFSET for an NV ID
       local LTK Info - defined as gapBondLTK_t and uses GAP_BOND_LOCAL_LTK_OFFSET for an NV ID
       device LTK Info - defined as gapBondLTK_t and uses GAP_BOND_DEV_LTK_OFFSET for an NV ID
       device IRK - defined as "uint8 devIRK[KEYLEN]" and uses GAP_BOND_DEV_IRK_OFFSET for an NV ID
       device CSRK - defined as "uint8 devCSRK[KEYLEN]" and uses GAP_BOND_DEV_CSRK_OFFSET for an NV ID
       device Sign Counter - defined as a uint32 and uses GAP_BOND_DEV_SIGN_COUNTER_OFFSET for an NV ID

    When the device is initialized for the first time, all (GAP_BONDINGS_MAX) NV items are created and
    initialized to all 0xFF's. A bonding record of all 0xFF's indicates that the bonding record is empty
    and free to use.

    The calculation for each bonding records NV IDs:
      mainRecordNvID = ((bondIdx * GAP_BOND_REC_IDS) + BLE_NVID_GAP_BOND_START)
      localLTKNvID = (((bondIdx * GAP_BOND_REC_IDS) + GAP_BOND_LOCAL_LTK_OFFSET) + BLE_NVID_GAP_BOND_START)

*/
#define GAP_BOND_REC_ID_OFFSET              0 //!< NV ID for the main bonding record
#define GAP_BOND_LOCAL_LTK_OFFSET           1 //!< NV ID for the bonding record's local LTK information
#define GAP_BOND_DEV_LTK_OFFSET             2 //!< NV ID for the bonding records' device LTK information
#define GAP_BOND_DEV_IRK_OFFSET             3 //!< NV ID for the bonding records' device IRK
#define GAP_BOND_DEV_CSRK_OFFSET            4 //!< NV ID for the bonding records' device CSRK
#define GAP_BOND_DEV_SIGN_COUNTER_OFFSET    5 //!< NV ID for the bonding records' device Sign Counter

#define GAP_BOND_REC_IDS                    6

// Macros to calculate the index/offset in to NV space
#define calcNvID(Idx, offset)               (((((Idx) * GAP_BOND_REC_IDS) + (offset))) + BLE_NVID_GAP_BOND_START)
#define mainRecordNvID(bondIdx)             (calcNvID((bondIdx), GAP_BOND_REC_ID_OFFSET))
#define localLTKNvID(bondIdx)               (calcNvID((bondIdx), GAP_BOND_LOCAL_LTK_OFFSET))
#define devLTKNvID(bondIdx)                 (calcNvID((bondIdx), GAP_BOND_DEV_LTK_OFFSET))
#define devIRKNvID(bondIdx)                 (calcNvID((bondIdx), GAP_BOND_DEV_IRK_OFFSET))
#define devCSRKNvID(bondIdx)                (calcNvID((bondIdx), GAP_BOND_DEV_CSRK_OFFSET))
#define devSignCounterNvID(bondIdx)         (calcNvID((bondIdx), GAP_BOND_DEV_SIGN_COUNTER_OFFSET))

// Macros to calculate the GATT index/offset in to NV space
#define gattCfgNvID(Idx)                    ((Idx) + BLE_NVID_GATT_CFG_START)

// Key Size Limits
#define MIN_ENC_KEYSIZE                     7  //!< Minimum number of bytes for the encryption key
#define MAX_ENC_KEYSIZE                     16 //!< Maximum number of bytes for the encryption key

/*********************************************************************
    TYPEDEFS
*/

// Structure of NV data for the connected device's encryption information
typedef struct
{
    uint8   LTK[KEYLEN];              // Long Term Key (LTK)
    uint16  div;  //lint -e754        // LTK eDiv
    uint8   rand[B_RANDOM_NUM_SIZE];  // LTK random number
    uint8   keySize;                  // LTK key size
} gapBondLTK_t;

// Structure of NV data for the connected device's address information
typedef struct
{
    uint8   publicAddr[B_ADDR_LEN];     // Master's address
    uint8   reconnectAddr[B_ADDR_LEN];  // Privacy Reconnection Address
    uint16  stateFlags;                 // State flags: SM_AUTH_STATE_AUTHENTICATED & SM_AUTH_STATE_BONDING
} gapBondRec_t;

// Structure of NV data for the connected device's characteristic configuration
typedef struct
{
    uint16 attrHandle;  // attribute handle
    uint8  value;       // attribute value for this device
} gapBondCharCfg_t;

/*********************************************************************
    GLOBAL VARIABLES
*/

/*********************************************************************
    EXTERNAL VARIABLES
*/

/*********************************************************************
    EXTERNAL FUNCTIONS
*/

/*********************************************************************
    LOCAL VARIABLES
*/
static uint8 gapBondMgr_TaskID = INVALID_TASK_ID;   // Task ID for internal task/event processing

// GAPBonding Parameters
uint8 gapBond_PairingMode[MAX_NUM_LL_CONN] = {GAPBOND_PAIRING_MODE_WAIT_FOR_REQ};
static uint16 gapBond_InitiateWait = 1000;  // Default to 1 second
static uint8 gapBond_MITM = FALSE;
static uint8 gapBond_IOCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
static uint8 gapBond_OOBDataFlag = FALSE;
static uint8 gapBond_OOBData[KEYLEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static uint8 gapBond_Bonding = FALSE;
// 2020-04-22 support secure connection pairing
static uint8 gapBond_SC = FALSE;
static uint8 gapBond_keyPress = FALSE;
static uint8 gapBond_CT2 = FALSE;
static uint8 gapBond_AutoFail = FALSE;
static uint8 gapBond_AutoFailReason = SMP_PAIRING_FAILED_NOT_SUPPORTED;
static uint8 gapBond_KeyDistList =
    (
        GAPBOND_KEYDIST_SENCKEY     // sEncKey enabled, to send the encryption key
        | GAPBOND_KEYDIST_SIDKEY   // sIdKey enabled, to send the IRK, and BD_ADDR
        | GAPBOND_KEYDIST_SSIGN    // sSign enabled, to send the CSRK
        | GAPBOND_KEYDIST_MENCKEY  // mEncKey enabled, to get the master's encryption key
        | GAPBOND_KEYDIST_MIDKEY   // mIdKey enabled, to get the master's IRK and BD_ADDR
        | GAPBOND_KEYDIST_MSIGN    // mSign enabled, to get the master's CSRK
    );
static uint32 gapBond_Passcode = 0;
static uint8  gapBond_KeySize = MAX_ENC_KEYSIZE;
#if (GAP_BOND_MGR_INDEX_REPLACE)
    static uint8 bondReplaceCnt=0;
#endif
#if ( HOST_CONFIG & CENTRAL_CFG )
    static uint8  gapBond_BondFailOption = GAPBOND_FAIL_TERMINATE_LINK;
#endif

static const gapBondCBs_t* pGapBondCB = NULL;

// Local RAM shadowed bond records
static gapBondRec_t bonds[GAP_BONDINGS_MAX] = {0};

static uint8 autoSyncWhiteList = FALSE;

static uint8 eraseAllBonds = FALSE;

static uint8 bondsToDelete[GAP_BONDINGS_MAX] = {FALSE};

// Globals used for saving bond record and CCC values in NV
static uint8 bondIdx = GAP_BONDINGS_MAX;
static gapAuthCompleteEvent_t* pAuthEvt[MAX_NUM_LL_CONN] = {NULL};
//static gapAuthCompleteEvent_t* pAuthBond[GAP_BONDINGS_MAX] = {NULL};


/*********************************************************************
    LOCAL FUNCTIONS
*/
static uint8 gapBondMgrChangeState( uint8 idx, uint16 state, uint8 set );
static uint8 gapBondMgrUpdateCharCfg( uint8 idx, uint16 attrHandle, uint16 value );
static gapBondCharCfg_t* gapBondMgrFindCharCfgItem( uint16 attrHandle,
                                                    gapBondCharCfg_t* charCfgTbl );
static void gapBondMgrInvertCharCfgItem( gapBondCharCfg_t* charCfgTbl );
static uint8 gapBondMgrAddBond( gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt );
static uint8 gapBondMgrGetStateFlags( uint8 idx );
static bStatus_t gapBondMgrGetPublicAddr( uint8 idx, uint8* pAddr );
static uint8 gapBondMgrFindReconnectAddr( uint8* pReconnectAddr );
static uint8 gapBondMgrFindAddr( uint8* pDevAddr );
static uint8 gapBondMgrResolvePrivateAddr( uint8* pAddr );
static void gapBondMgrReadBonds( void );
static uint8 gapBondMgrFindEmpty( void );
static uint8 gapBondMgrBondTotal( void );
static bStatus_t gapBondMgrEraseAllBondings( void );
static bStatus_t gapBondMgrEraseBonding( uint8 idx );
static uint8 gapBondMgr_ProcessOSALMsg( osal_event_hdr_t* pMsg );
static void gapBondMgrSendServiceChange( linkDBItem_t* pLinkItem );
static void gapBondMgr_ProcessGATTMsg( gattMsgEvent_t* pMsg );
static void gapBondMgr_ProcessGATTServMsg( gattEventHdr_t* pMsg );
static void gapBondSetupPrivFlag( void );
static void gapBondMgrBondReq( uint16 connHandle, uint8 idx, uint8 stateFlags,
                               uint8 role, uint8 startEncryption );
static void gapBondMgrAuthenticate( uint16 connHandle, uint8 addrType,
                                    gapPairingReq_t* pPairReq );
static void gapBondMgr_SyncWhiteList( void );
static uint8 gapBondMgr_SyncCharCfg( uint16 connHandle );
static void gapBondFreeAuthEvt( uint16 connHandle );

static void gapBondRecvEvt(uint16 connHandle,gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt );

#if ( HOST_CONFIG & PERIPHERAL_CFG )
    static void gapBondMgrSlaveSecurityReq( uint16 connHandle );
#endif

/*********************************************************************
    NETWORK LAYER CALLBACKS
*/

/*********************************************************************
    PUBLIC FUNCTIONS
*/

/*********************************************************************
    @brief   Set a GAP Bond Manager parameter.

    Public function defined in gapbondmgr.h.
*/
bStatus_t GAPBondMgr_SetParameter( uint16 param, uint8 len, void* pValue )
{
    bStatus_t ret = SUCCESS;  // return value

    switch ( param )
    {
    case GAPBOND_PAIRING_MODE:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_PAIRING_MODE_INITIATE) )
        {
//            gapBond_PairingMode = *((uint8*)pValue);
            osal_memset(gapBond_PairingMode,*((uint8*)pValue),sizeof(gapBond_PairingMode));
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_INITIATE_WAIT:
        if ( len == sizeof ( uint16 ) )
        {
            gapBond_InitiateWait = *((uint16*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_MITM_PROTECTION:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
        {
            gapBond_MITM = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_IO_CAPABILITIES:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_IO_CAP_KEYBOARD_DISPLAY) )
        {
            gapBond_IOCap = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_OOB_ENABLED:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
        {
            gapBond_OOBDataFlag = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_OOB_DATA:
        if ( len == KEYLEN )
        {
            VOID osal_memcpy( gapBond_OOBData, pValue, KEYLEN ) ;
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_BONDING_ENABLED:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
        {
            gapBond_Bonding = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_KEY_DIST_LIST:
        if ( len == sizeof ( uint8 ) )
        {
            gapBond_KeyDistList = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_DEFAULT_PASSCODE:
        if ( (len == sizeof ( uint32 ))
                && (*((uint32*)pValue) <= GAP_PASSCODE_MAX) )
        {
            gapBond_Passcode = *((uint32*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_ERASE_ALLBONDS:
        if ( len == 0 )
        {
            // Make sure there's no active connection
            if ( GAP_NumActiveConnections() == 0 )
            {
                // Erase all bonding records
                VOID gapBondMgrEraseAllBondings();
                // See if NV needs a compaction
                VOID osal_snv_compact( NV_COMPACT_THRESHOLD );
                // Make sure Bond RAM Shadow is up-to-date
                gapBondMgrReadBonds();
            }
            else
            {
                eraseAllBonds = TRUE;
            }
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_ERASE_SINGLEBOND:
        if ( len == (1 + B_ADDR_LEN) )
        {
            uint8 idx;
            uint8 devAddr[B_ADDR_LEN];
            // Reverse bytes
            VOID osal_revmemcpy( devAddr, (uint8*)pValue+1, B_ADDR_LEN );
            // Resolve address and find index
            idx = GAPBondMgr_ResolveAddr( *((uint8*)pValue), devAddr, NULL );

            if ( idx < GAP_BONDINGS_MAX )
            {
                // Make sure there's no active connection
                if ( GAP_NumActiveConnections() == 0 )
                {
                    // Erase bond
                    gapBondMgrEraseBonding( idx );
                    // See if NV needs a compaction
                    VOID osal_snv_compact( NV_COMPACT_THRESHOLD );
                    // Make sure Bond RAM Shadow is up-to-date
                    gapBondMgrReadBonds();
                }
                else
                {
                    // Mark entry to be deleted when disconnected
                    bondsToDelete[idx] = TRUE;
                }
            }
            else
            {
                ret = INVALIDPARAMETER;
            }
        }
        else
        {
            // Parameter is not the correct length
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_AUTO_FAIL_PAIRING:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
        {
            gapBond_AutoFail = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_AUTO_FAIL_REASON:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= SMP_PAIRING_FAILED_REPEATED_ATTEMPTS) )
        {
            gapBond_AutoFailReason = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_KEYSIZE:
        if ( (len == sizeof ( uint8 ))
                && ((*((uint8*)pValue) >= MIN_ENC_KEYSIZE) && (*((uint8*)pValue) <= MAX_ENC_KEYSIZE)) )
        {
            gapBond_KeySize = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;

    case GAPBOND_AUTO_SYNC_WL:
        if ( len == sizeof( uint8 ) )
        {
            uint8 oldVal = autoSyncWhiteList;
            autoSyncWhiteList = *((uint8*)pValue);

            // only call if parameter changes from FALSE to TRUE
            if ( ( oldVal == FALSE ) && ( autoSyncWhiteList == TRUE ) )
            {
                // make sure bond is updated from NV
                gapBondMgrReadBonds();
            }
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;
        #if ( HOST_CONFIG & CENTRAL_CFG )

    case GAPBOND_BOND_FAIL_ACTION:
        if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_FAIL_TERMINATE_ERASE_BONDS) )
        {
            gapBond_BondFailOption = *((uint8*)pValue);
        }
        else
        {
            ret = bleInvalidRange;
        }

        break;
        #endif

    default:

        // The param value isn't part of this profile, try the GAP.
        if ( (param < TGAP_PARAMID_MAX) && (len == sizeof ( uint16 )) )
        {
            ret = GAP_SetParamValue( param, *((uint16*)pValue) );
        }
        else
        {
            ret = INVALIDPARAMETER;
        }

        break;
    }

    return ( ret );
}

/*********************************************************************
    @brief   Get a GAP Bond Manager parameter.

    Public function defined in gapbondmgr.h.
*/
bStatus_t GAPBondMgr_GetParameter( uint16 param, void* pValue )
{
    bStatus_t ret = SUCCESS;  // return value

    switch ( param )
    {
    case GAPBOND_PAIRING_MODE:
//        *((uint8*)pValue) = gapBond_PairingMode;
        break;

    case GAPBOND_INITIATE_WAIT:
        *((uint16*)pValue) = gapBond_InitiateWait;
        break;

    case GAPBOND_MITM_PROTECTION:
        *((uint8*)pValue) = gapBond_MITM;
        break;

    case GAPBOND_IO_CAPABILITIES:
        *((uint8*)pValue) = gapBond_IOCap;
        break;

    case GAPBOND_OOB_ENABLED:
        *((uint8*)pValue) = gapBond_OOBDataFlag;
        break;

    case GAPBOND_OOB_DATA:
        VOID osal_memcpy( pValue, gapBond_OOBData, KEYLEN ) ;
        break;

    case GAPBOND_BONDING_ENABLED:
        *((uint8*)pValue) = gapBond_Bonding;
        break;

    case GAPBOND_KEY_DIST_LIST:
        *((uint8*)pValue) = gapBond_KeyDistList;
        break;

    case GAPBOND_DEFAULT_PASSCODE:
        *((uint32*)pValue) = gapBond_Passcode;
        break;

    case GAPBOND_AUTO_FAIL_PAIRING:
        *((uint8*)pValue) = gapBond_AutoFail;
        break;

    case GAPBOND_AUTO_FAIL_REASON:
        *((uint8*)pValue) = gapBond_AutoFailReason;
        break;

    case GAPBOND_KEYSIZE:
        *((uint8*)pValue) = gapBond_KeySize;
        break;

    case GAPBOND_AUTO_SYNC_WL:
        *((uint8*)pValue) = autoSyncWhiteList;
        break;

    case GAPBOND_BOND_COUNT:
        *((uint8*)pValue) = gapBondMgrBondTotal();
        break;

    default:

        // The param value isn't part of this profile, try the GAP.
        if ( param < TGAP_PARAMID_MAX )
        {
            *((uint16*)pValue) = GAP_GetParamValue( param );
        }
        else
        {
            ret = INVALIDPARAMETER;
        }

        break;
    }

    return ( ret );
}

/*********************************************************************
    @brief   Notify the Bond Manager that a connection has been made.

    Public function defined in gapbondmgr.h.
*/
bStatus_t GAPBondMgr_LinkEst( uint8 addrType, uint8* pDevAddr, uint16 connHandle, uint8 role )
{
    uint8 idx;                          // NV Index
    uint8 publicAddr[B_ADDR_LEN]        // Place to put the public address
        = {0, 0, 0, 0, 0, 0};
    idx = GAPBondMgr_ResolveAddr( addrType, pDevAddr, publicAddr );

    if ( idx < GAP_BONDINGS_MAX )
    {
        uint8 stateFlags = gapBondMgrGetStateFlags( idx );
        smSigningInfo_t signingInfo;
        gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Space to read a char cfg record from NV
        // On peripheral, load the key information for the bonding
        // On central and initiaiting security, load key to initiate encyption
//        gapBondMgrBondReq( connHandle, idx, stateFlags, role,
//                           ((gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE ) ? TRUE : FALSE) );
        // bugfix : for multi-role , because GAP_Bond gapProfileRole & central to initiate bond request
        // if slave , shall not send bond request
        gapBondMgrBondReq( connHandle, idx, stateFlags, role,
                           ((role == GAP_PROFILE_CENTRAL ) ? TRUE : FALSE) );
        // Load the Signing Key
        VOID osal_memset( &signingInfo, 0, sizeof ( smSigningInfo_t ) );

        if ( osal_snv_read( devCSRKNvID(idx), KEYLEN, signingInfo.srk ) == SUCCESS )
        {
            if ( osal_isbufset( signingInfo.srk, 0xFF, KEYLEN ) == FALSE )
            {
                // Load the signing information for this connection
                VOID osal_snv_read( devSignCounterNvID(idx), sizeof ( uint32 ), &(signingInfo.signCounter) );
                VOID GAP_Signable( connHandle,
                                   ((stateFlags & GAP_BONDED_STATE_AUTHENTICATED) ? TRUE : FALSE),
                                   &signingInfo );
            }
        }

        // Load the characteristic configuration
        if ( osal_snv_read( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ) == SUCCESS )
        {
            gapBondMgrInvertCharCfgItem( charCfg );

            for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ )
            {
                gapBondCharCfg_t* pItem = &(charCfg[i]);

                // Apply the characteristic configuration for this connection
                if ( pItem->attrHandle != GATT_INVALID_HANDLE )
                {
                    VOID GATTServApp_UpdateCharCfg( connHandle, pItem->attrHandle,
                                                    (uint16)(pItem->value) );
                }
            }
        }

        // Has there been a service change?
        if ( stateFlags & GAP_BONDED_STATE_SERVICE_CHANGED )
        {
            VOID GATTServApp_SendServiceChangedInd( connHandle, gapBondMgr_TaskID );
        }
    }

    #if ( HOST_CONFIG & CENTRAL_CFG )
    else if ( role == GAP_PROFILE_CENTRAL &&
              gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE )
    {
        // If Central and initiating and not bonded, then initiate pairing
        gapBondMgrAuthenticate( connHandle, addrType, NULL );

        // Call app state callback
        if ( pGapBondCB && pGapBondCB->pairStateCB )
        {
            pGapBondCB->pairStateCB( connHandle, GAPBOND_PAIRING_STATE_STARTED, SUCCESS );
        }
    }

    #endif
    #if ( HOST_CONFIG & PERIPHERAL_CFG )

    // If Peripheral and initiating, send a slave security request to
    // initiate either pairing or encryption
    if ( role == GAP_PROFILE_PERIPHERAL &&
            gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE )
    {
        gapBondMgrSlaveSecurityReq( connHandle );
    }

    #endif
    return ( SUCCESS );
}

/*********************************************************************
    @brief   Resolve an address from bonding information.

    Public function defined in gapbondmgr.h.
*/
uint8 GAPBondMgr_ResolveAddr( uint8 addrType, uint8* pDevAddr, uint8* pResolvedAddr )
{
    uint8 idx = GAP_BONDINGS_MAX;

    switch ( addrType )
    {
    case ADDRTYPE_PUBLIC:
    case ADDRTYPE_STATIC:
        idx = gapBondMgrFindAddr( pDevAddr );

        if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) )
        {
            VOID osal_memcpy( pResolvedAddr, pDevAddr, B_ADDR_LEN );
        }

        break;

    case ADDRTYPE_PRIVATE_NONRESOLVE:
        // This could be a reconnection address
        idx = gapBondMgrFindReconnectAddr( pDevAddr );

        if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) )
        {
            VOID gapBondMgrGetPublicAddr( idx, pResolvedAddr );
        }

        break;

    case ADDRTYPE_PRIVATE_RESOLVE:
        // Master's don't use Private Resolvable addresses but just in case
        idx = gapBondMgrResolvePrivateAddr( pDevAddr );

        if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) )
        {
            VOID gapBondMgrGetPublicAddr( idx, pResolvedAddr );
        }

        break;

    default:
        break;
    }

    return ( idx );
}

/*********************************************************************
    @brief   Set/clear the service change indication in a bond record.

    Public function defined in gapbondmgr.h.
*/
bStatus_t GAPBondMgr_ServiceChangeInd( uint16 connectionHandle, uint8 setParam )
{
    bStatus_t ret = bleNoResources; // return value

    if ( connectionHandle == 0xFFFF )
    {
        uint8 idx;  // loop counter

        // Run through the bond database and update the Service Change indication
        for ( idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
        {
            if ( gapBondMgrChangeState( idx, GAP_BONDED_STATE_SERVICE_CHANGED, setParam ) )
            {
                ret = SUCCESS;
            }
        }

        // If the service change indication is TRUE, tell the connected devices
        if ( setParam )
        {
            // Run connected database
            linkDB_PerformFunc( gapBondMgrSendServiceChange );
        }
    }
    else
    {
        // Find connection information
        linkDBItem_t* pLinkItem = linkDB_Find( connectionHandle );

        if ( pLinkItem )
        {
            uint8 idx; // loop counter
            idx = GAPBondMgr_ResolveAddr( pLinkItem->addrType, pLinkItem->addr, NULL );

            if ( idx < GAP_BONDINGS_MAX )
            {
                // Bond found, update it.
                VOID gapBondMgrChangeState( idx, GAP_BONDED_STATE_SERVICE_CHANGED, setParam );
                ret = SUCCESS;
            }

            // If the service change indication is TRUE, tell the connected device
            if ( setParam )
            {
                gapBondMgrSendServiceChange( pLinkItem );
            }
        }
        else
        {
            ret = bleNotConnected;
        }
    }

    return ( ret );
}

/*********************************************************************
    @brief   Update the Characteristic Configuration in a bond record.

    Public function defined in gapbondmgr.h.
*/
bStatus_t GAPBondMgr_UpdateCharCfg( uint16 connectionHandle, uint16 attrHandle, uint16 value )
{
    bStatus_t ret = bleNoResources; // return value

    if ( connectionHandle == INVALID_CONNHANDLE )
    {
        uint8 idx;  // loop counter

        // Run through the bond database and update the Characteristic Configuration
        for ( idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
        {
            if ( gapBondMgrUpdateCharCfg( idx, attrHandle, value ) )
            {
                ret = SUCCESS;
            }
        }
    }
    else
    {
        // Find connection information
        linkDBItem_t* pLinkItem = linkDB_Find( connectionHandle );

        if ( pLinkItem )
        {
            uint8 idx = GAPBondMgr_ResolveAddr( pLinkItem->addrType, pLinkItem->addr, NULL );

            if ( idx < GAP_BONDINGS_MAX )
            {
                // Bond found, update it.
                VOID gapBondMgrUpdateCharCfg( idx, attrHandle, value );
                ret = SUCCESS;
            }
        }
        else
        {
            ret = bleNotConnected;
        }
    }

    return ( ret );
}

/*********************************************************************
    @brief   Register callback functions with the bond manager.

    Public function defined in gapbondmgr.h.
*/
void GAPBondMgr_Register( gapBondCBs_t* pCB )
{
    pGapBondCB = pCB;

    if(gapBondMgr_TaskID != INVALID_TASK_ID)
    {
        // Take over the processing of Authentication messages
        VOID GAP_SetParamValue( TGAP_AUTH_TASK_ID, gapBondMgr_TaskID );
        // Register with GATT Server App for event messages
        GATTServApp_RegisterForMsg( gapBondMgr_TaskID );
    }
}

/*********************************************************************
    @brief   Respond to a passcode request.

    Public function defined in gapbondmgr.h.
*/
bStatus_t GAPBondMgr_PasscodeRsp( uint16 connectionHandle, uint8 status, uint32 passcode )
{
    bStatus_t ret = SUCCESS;

    if ( status == SUCCESS )
    {
        // Truncate the passcode
        passcode = passcode % (GAP_PASSCODE_MAX + 1);
        ret = GAP_PasscodeUpdate( passcode, connectionHandle );

        if ( ret != SUCCESS )
        {
            VOID GAP_TerminateAuth( connectionHandle, SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED );
        }
    }
    else
    {
        VOID GAP_TerminateAuth( connectionHandle, status );
    }

    return ret;
}

/*********************************************************************
    @brief   This is a bypass mechanism to allow the bond manager to process
                GAP messages.

    Public function defined in gapbondmgr.h.
*/
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[pPkt->connectionHandle] == 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
                gapBondRecvEvt( pPkt->connectionHandle,&bondRec, pPkt );
//            osal_set_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT );
//              AT_LOG("osal_set_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT )\n");
                // 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 );
        }
    }
    break;

    case GAP_BOND_COMPLETE_EVENT:
        // This message is received when the bonding is complete.  If hdr.status is SUCCESS
        // then call app state callback.  If hdr.status is NOT SUCCESS, the connection will be
        // dropped at the LL because of a MIC failure, so again nothing to do.
    {
        gapBondCompleteEvent_t* pPkt = (gapBondCompleteEvent_t*)pMsg;
        #if ( HOST_CONFIG & CENTRAL_CFG )

        if ( pPkt->hdr.status == LL_ENC_KEY_REQ_REJECTED )
        {
            // LTK not found on peripheral device (Pin or Key Missing). See which
            // option was configured for unsuccessful bonding.
            linkDBItem_t* pLinkItem = linkDB_Find( pPkt->connectionHandle );

            if ( pLinkItem )
            {
                switch ( gapBond_BondFailOption )
                {
                case GAPBOND_FAIL_INITIATE_PAIRING:
                    // Initiate pairing
                    gapBondMgrAuthenticate( pPkt->connectionHandle, pLinkItem->addrType, NULL );
                    break;

                case GAPBOND_FAIL_TERMINATE_LINK:
                    // Drop connection
                    GAP_TerminateLinkReq( pLinkItem->taskID, pPkt->connectionHandle, HCI_DISCONNECT_AUTH_FAILURE );
                    break;

                case GAPBOND_FAIL_TERMINATE_ERASE_BONDS:
                    // Set up bond manager to erase all existing bonds after connection terminates
                    VOID GAPBondMgr_SetParameter( GAPBOND_ERASE_ALLBONDS, 0, NULL );
                    // Drop connection
                    GAP_TerminateLinkReq( pLinkItem->taskID, pPkt->connectionHandle, HCI_DISCONNECT_AUTH_FAILURE );
                    break;

                case GAPBOND_FAIL_NO_ACTION:

                // fall through
                default:
                    // do nothing
                    break;
                }
            }
        }

        #endif

        if ( pGapBondCB && pGapBondCB->pairStateCB )
        {
            pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_BONDED, pMsg->hdr.status );
        }
    }
    break;

    case GAP_SIGNATURE_UPDATED_EVENT:
    {
        uint8 idx;
        gapSignUpdateEvent_t* pPkt = (gapSignUpdateEvent_t*)pMsg;
        idx = GAPBondMgr_ResolveAddr( pPkt->addrType, pPkt->devAddr, NULL );

        if ( idx < GAP_BONDINGS_MAX )
        {
            // Save the sign counter
            VOID osal_snv_write( devSignCounterNvID(idx), sizeof ( uint32 ), &(pPkt->signCounter) );
        }
    }
    break;
    #if ( HOST_CONFIG & PERIPHERAL_CFG )

    case GAP_PAIRING_REQ_EVENT:
    {
        gapPairingReqEvent_t* pPkt = (gapPairingReqEvent_t*)pMsg;

        if ( gapBond_AutoFail != FALSE )
        {
            // Auto Fail TEST MODE (DON'T USE THIS) - Sends pre-setup reason
            VOID GAP_TerminateAuth( pPkt->connectionHandle, gapBond_AutoFailReason );
        }
        else if ( gapBond_PairingMode[pPkt->connectionHandle] == GAPBOND_PAIRING_MODE_NO_PAIRING )
        {
            // No Pairing - Send error
            VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_NOT_SUPPORTED );
        }
        else
        {
            linkDBItem_t* pLinkItem = linkDB_Find( pPkt->connectionHandle );

            // Requesting bonding?
            if ( pPkt->pairReq.authReq & SM_AUTH_STATE_BONDING )
            {
                if ( pLinkItem )
                {
                    if ( (pLinkItem->addrType != ADDRTYPE_PUBLIC) && (pPkt->pairReq.keyDist.mIdKey == FALSE) )
                    {
                        uint8 publicAddr[B_ADDR_LEN];

                        // Check if we already have the public address in NV
                        if ( GAPBondMgr_ResolveAddr(pLinkItem->addrType, pLinkItem->addr, publicAddr ) == FALSE )
                        {
                            // Can't bond to a non-public address if we don't know the public address
                            VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_AUTH_REQ );
                            break;
                        }
                    }
                }
                else
                {
                    // Can't find the connection, ignore the message
                    break;
                }
            }

            // Send pairing response
            gapBondMgrAuthenticate( pPkt->connectionHandle, pLinkItem->addrType, &(pPkt->pairReq) );

            // Call app state callback
            if ( pGapBondCB && pGapBondCB->pairStateCB )
            {
                pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_STARTED, SUCCESS );
            }
        }
    }
    break;
    #endif
    #if ( HOST_CONFIG & CENTRAL_CFG )

    case GAP_SLAVE_REQUESTED_SECURITY_EVENT:
    {
        uint16 connHandle = ((gapSlaveSecurityReqEvent_t*)pMsg)->connectionHandle;
        uint8 idx;
        uint8 publicAddr[B_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
        linkDBItem_t* pLink = linkDB_Find( connHandle );

        // If link found and not already initiating security
        if (pLink != NULL && gapBond_PairingMode[connHandle] != GAPBOND_PAIRING_MODE_INITIATE)
        {
            // If already bonded initiate encryption
            idx = GAPBondMgr_ResolveAddr( pLink->addrType, pLink->addr, publicAddr );

            if ( idx < GAP_BONDINGS_MAX )
            {
                gapBondMgrBondReq( connHandle, idx, gapBondMgrGetStateFlags( idx ),
                                   GAP_PROFILE_CENTRAL, TRUE );
            }
            else if ( gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_NO_PAIRING )
            {
                VOID GAP_TerminateAuth( connHandle, SMP_PAIRING_FAILED_NOT_SUPPORTED );
            }
            else if (gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_WAIT_FOR_REQ)
            {
                gapBondMgrAuthenticate( connHandle, pLink->addrType, NULL );
            }
        }
    }
    break;
    #endif

    case GAP_LINK_TERMINATED_EVENT:
    {
//        gapTerminateLinkEvent_t* pPkt = (gapTerminateLinkEvent_t*)pMsg;
//        gapAuthCompleteEvent_t* pAuthEvtTmp = NULL;
        if ( GAP_NumActiveConnections() == 0 )
        {
            // See if we're asked to erase all bonding records
            if ( eraseAllBonds == TRUE )
            {
                VOID gapBondMgrEraseAllBondings();
                eraseAllBonds = FALSE;
                // Reset bonds to delete table
                osal_memset( bondsToDelete, FALSE, sizeof( bondsToDelete ) );
            }
            // See if we're asked to erase all bonding records
            // Reset bonds to delete table
            else
            {
                // See if we're asked to erase any single bonding records
                for (uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++)
                {
                    if ( bondsToDelete[idx] == TRUE )
                    {
                        VOID gapBondMgrEraseBonding( idx );
                        bondsToDelete[idx] = FALSE;
                    }
                }
            }

            // See if NV needs a compaction
            VOID osal_snv_compact( NV_COMPACT_THRESHOLD );
            // Make sure Bond RAM Shadow is up-to-date
            // Make sure Bond RAM Shadow is up-to-date
            gapBondMgrReadBonds();
        }
    }
    break;

    default:
        break;
    }

    return ( TRUE );
}

/*********************************************************************
    LOCAL FUNCTION PROTOTYPES
*/

/*********************************************************************
    @fn      gapBondMgrChangeState

    @brief   Change a state flag in the stateFlags field of the bond record.

    @param   idx - Bond NV index
    @param   state - state flage to set or clear
    @param   set - TRUE to set the flag, FALSE to clear the flag

    @return  TRUE if NV Record exists, FALSE if NV Record is empty
*/
static uint8 gapBondMgrChangeState( uint8 idx, uint16 state, uint8 set )
{
    gapBondRec_t bondRec;   // Space to read a Bond record from NV

    // Look for public address that is used (not all 0xFF's)
    if ( (osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS)
            && (osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE) )
    {
        // Update the state of the bonded device.
        uint8 stateFlags = bondRec.stateFlags;

        if ( set )
        {
            stateFlags |= state;
        }
        else
        {
            stateFlags &= ~(state);
        }

        if ( stateFlags != bondRec.stateFlags )
        {
            bondRec.stateFlags = stateFlags;
            VOID osal_snv_write( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec );
        }

        return ( TRUE );
    }

    return ( FALSE );
}

/*********************************************************************
    @fn      gapBondMgrUpdateCharCfg

    @brief   Update the Characteristic Configuration of the bond record.

    @param   idx - Bond NV index
    @param   attrHandle - attribute handle (0 means all handles)
    @param   value - characteristic configuration value

    @return  TRUE if NV Record exists, FALSE if NV Record is empty
*/
static uint8 gapBondMgrUpdateCharCfg( uint8 idx, uint16 attrHandle, uint16 value )
{
    gapBondRec_t bondRec;   // Space to read a Bond record from NV

    // Look for public address that is used (not all 0xFF's)
    if ( ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS )
            && ( osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE ) )
    {
        gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Space to read a char cfg record from NV

        if ( osal_snv_read( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ) == SUCCESS )
        {
            uint8 update = FALSE;
            gapBondMgrInvertCharCfgItem( charCfg );

            if ( attrHandle == GATT_INVALID_HANDLE )
            {
                if ( osal_isbufset( (uint8*)charCfg, 0x00, sizeof ( charCfg ) ) == FALSE )
                {
                    // Clear all characteristic configuration for this device
                    VOID osal_memset( (void*)charCfg, 0x00, sizeof ( charCfg ) );
                    update = TRUE;
                }
            }
            else
            {
                gapBondCharCfg_t* pItem = gapBondMgrFindCharCfgItem( attrHandle, charCfg );

                if ( pItem == NULL )
                {
                    // Must be a new item; ignore if the value is no operation (default)
                    if ( ( value == GATT_CFG_NO_OPERATION ) ||
                            ( ( pItem = gapBondMgrFindCharCfgItem( GATT_INVALID_HANDLE, charCfg ) ) == NULL ) )
                    {
                        return ( FALSE ); // No empty entry found
                    }

                    pItem->attrHandle = attrHandle;
                }

                if ( pItem->value != value )
                {
                    // Update characteristic configuration
                    pItem->value = (uint8)value;

                    if ( value == GATT_CFG_NO_OPERATION )
                    {
                        // Erease the item
                        pItem->attrHandle = GATT_INVALID_HANDLE;
                    }

                    update = TRUE;
                }
            }

            // Update the characteristic configuration of the bonded device.
            if ( update )
            {
                gapBondMgrInvertCharCfgItem( charCfg );
                VOID osal_snv_write( gattCfgNvID(idx), sizeof( charCfg ), charCfg );
            }
        }

        return ( TRUE );
    }

    return ( FALSE );
}

/*********************************************************************
    @fn      gapBondMgrFindCharCfgItem

    @brief   Find the Characteristic Configuration for a given attribute.
            Uses the attribute handle to search the charactersitic
            configuration table of a bonded device.

    @param   attrHandle - attribute handle.
    @param   charCfgTbl - characteristic configuration table.

    @return  pointer to the found item. NULL, otherwise.
*/
static gapBondCharCfg_t* gapBondMgrFindCharCfgItem( uint16 attrHandle,
                                                    gapBondCharCfg_t* charCfgTbl )
{
    for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ )
    {
        if ( charCfgTbl[i].attrHandle == attrHandle )
        {
            return ( &(charCfgTbl[i]) );
        }
    }

    return ( (gapBondCharCfg_t*)NULL );
}

/*********************************************************************
    @fn      gapBondMgrFindCharCfgItem

    @brief   Invert the Characteristic Configuration for a given client.

    @param   charCfgTbl - characteristic configuration table.

    @return  none.
*/
static void gapBondMgrInvertCharCfgItem( gapBondCharCfg_t* charCfgTbl )
{
    for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ )
    {
        charCfgTbl[i].attrHandle = ~(charCfgTbl[i].attrHandle);
        charCfgTbl[i].value = ~(charCfgTbl[i].value);
    }
}

/*********************************************************************
    @fn      gapBondMgrAddBond

    @brief   Save a bond from a GAP Auth Complete Event

    @param   pBondRec - basic bond record
    @param   pLocalLTK - LTK used by this device during pairing
    @param   pDevLTK - LTK used by the connected device during pairing
    @param   pIRK - IRK used by the connected device during pairing
    @param   pSRK - SRK used by the connected device during pairing
    @param   signCounter - Sign counter used by the connected device during pairing

    @return  TRUE, if done processing bond record. FALSE, otherwise.
*/
static uint8 gapBondMgrAddBond( gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt )
{
    // See if this is a new bond record
    if ( pAuthEvt[pPkt->connectionHandle] == NULL )
    {
        // Make sure we have bonding info
        if ( ( pBondRec == NULL ) || ( pPkt == NULL ) )
        {
            return ( TRUE );
        }

        // First see if we already have an existing bond for this device
        bondIdx = gapBondMgrFindAddr( pBondRec->publicAddr );

        if ( bondIdx >= GAP_BONDINGS_MAX )
        {
            bondIdx = gapBondMgrFindEmpty();
        }

        #if(GAP_BOND_MGR_INDEX_REPLACE)

        /*replace bondIdx*/
        if(bondIdx==GAP_BONDINGS_MAX)
        {
            bondIdx = (bondReplaceCnt++)%GAP_BONDINGS_MAX;
        }

        #endif
    }

    if ( bondIdx < GAP_BONDINGS_MAX )
    {
        // See if this is a new bond record
        if ( pAuthEvt[pPkt->connectionHandle] == NULL )
        {
            gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX];
            // Save the main information
            osal_snv_write( mainRecordNvID(bondIdx), sizeof ( gapBondRec_t ), pBondRec );
            // Write out FF's over the charactersitic configuration entry, to overwrite
            // any previous bond data that may have been stored
            VOID osal_memset( charCfg, 0xFF, sizeof ( charCfg ) );
            VOID osal_snv_write( gattCfgNvID(bondIdx), sizeof ( charCfg ), charCfg );
            // Update Bond RAM Shadow just with the newly added bond entry
            VOID osal_memcpy( &(bonds[bondIdx]), pBondRec, sizeof ( gapBondRec_t ) );
            // Keep the OSAL message to store the security keys later - will be freed then
            pAuthEvt[pPkt->connectionHandle] = pPkt;
        }
        else
        {
            // If available, save the LTK information
            if ( pAuthEvt[pPkt->connectionHandle]->pSecurityInfo )
            {
                VOID osal_snv_write( localLTKNvID(bondIdx), sizeof ( gapBondLTK_t ), pAuthEvt[pPkt->connectionHandle]->pSecurityInfo );
                pAuthEvt[pPkt->connectionHandle]->pSecurityInfo = NULL;
            }

            // If availabe, save the connected device's LTK information
            if ( pAuthEvt[pPkt->connectionHandle]->pDevSecInfo )
            {
                VOID osal_snv_write( devLTKNvID(bondIdx), sizeof ( gapBondLTK_t ), pAuthEvt[pPkt->connectionHandle]->pDevSecInfo );
                pAuthEvt[pPkt->connectionHandle]->pDevSecInfo = NULL;
            }

            // If available, save the connected device's IRK
            if ( pAuthEvt[pPkt->connectionHandle]->pIdentityInfo )
            {
                VOID osal_snv_write( devIRKNvID(bondIdx), KEYLEN, pAuthEvt[pPkt->connectionHandle]->pIdentityInfo->irk );
                pAuthEvt[pPkt->connectionHandle]->pIdentityInfo = NULL;
            }

            // If available, save the connected device's Signature information
            if ( pAuthEvt[pPkt->connectionHandle]->pSigningInfo )
            {
                VOID osal_snv_write( devCSRKNvID(bondIdx), KEYLEN, pAuthEvt[pPkt->connectionHandle]->pSigningInfo->srk );
                VOID osal_snv_write( devSignCounterNvID(bondIdx), sizeof ( uint32 ), &(pAuthEvt[pPkt->connectionHandle]->pSigningInfo->signCounter) );
                pAuthEvt[pPkt->connectionHandle]->pSigningInfo = NULL;
            }

//      else
            {
                if ( autoSyncWhiteList )
                {
                    gapBondMgr_SyncWhiteList();
                }

                // Update the GAP Privacy Flag Properties
                gapBondSetupPrivFlag();
                return ( TRUE );
            }
        }

        // We have more info to store
        return ( FALSE );
    }

    return ( TRUE );
}

/*********************************************************************
    @fn      gapBondMgrGetStateFlags

    @brief   Gets the state flags field of a bond record in NV

    @param   idx

    @return  stateFlags field
*/
static uint8 gapBondMgrGetStateFlags( uint8 idx )
{
    gapBondRec_t bondRec;

    if ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS )
    {
        return ( bondRec.stateFlags );
    }

    return ( 0 );
}

/*********************************************************************
    @fn      gapBondMgrGetPublicAddr

    @brief   Copy the public Address from a bonding record

    @param   idx - Bond record index
    @param   pAddr - a place to put the public address from NV

    @return  SUCCESS if successful.
            Otherwise failure.
*/
static bStatus_t gapBondMgrGetPublicAddr( uint8 idx, uint8* pAddr )
{
    bStatus_t stat;         // return value
    gapBondRec_t bondRec;   // Work space for main bond record

    // Check parameters
    if ( (idx >= GAP_BONDINGS_MAX) || (pAddr == NULL) )
    {
        return ( INVALIDPARAMETER );
    }

    stat = osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec );

    if ( stat == SUCCESS )
    {
        VOID osal_memcpy( pAddr, bondRec.publicAddr, B_ADDR_LEN );
    }

    return ( stat );
}

/*********************************************************************
    @fn      gapBondMgrFindReconnectAddr

    @brief   Look through the bonding entries to find a
            reconnection address.

    @param   pReconnectAddr - device address to look for

    @return  index to found bonding (0 - (GAP_BONDINGS_MAX-1),
            GAP_BONDINGS_MAX if no empty entries
*/
static uint8 gapBondMgrFindReconnectAddr( uint8* pReconnectAddr )
{
    // Item doesn't exist, so create all the items
    for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
        // compare reconnection address
        if ( osal_memcmp( bonds[idx].reconnectAddr, pReconnectAddr, B_ADDR_LEN ) )
        {
            return ( idx ); // Found it
        }
    }

    return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
    @fn      gapBondMgrFindAddr

    @brief   Look through the bonding entries to find an address.

    @param   pDevAddr - device address to look for

    @return  index to empty bonding (0 - (GAP_BONDINGS_MAX-1),
            GAP_BONDINGS_MAX if no empty entries
*/
static uint8 gapBondMgrFindAddr( uint8* pDevAddr )
{
    // Item doesn't exist, so create all the items
    for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
        // Read in NV Main Bond Record and compare public address
        if ( osal_memcmp( bonds[idx].publicAddr, pDevAddr, B_ADDR_LEN ) )
        {
            return ( idx ); // Found it
        }
    }

    return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
    @fn      gapBondMgrResolvePrivateAddr

    @brief   Look through the NV bonding entries to resolve a private
            address.

    @param   pDevAddr - device address to look for

    @return  index to found bonding (0 - (GAP_BONDINGS_MAX-1),
            GAP_BONDINGS_MAX if no entry found
*/
static uint8 gapBondMgrResolvePrivateAddr( uint8* pDevAddr )
{
    for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
        uint8 IRK[KEYLEN];

        // Read in NV IRK Record and compare resovable address
        if ( osal_snv_read( devIRKNvID(idx), KEYLEN, IRK ) == SUCCESS )
        {
            if ( ( osal_isbufset( IRK, 0xFF, KEYLEN ) == FALSE ) &&
                    ( GAP_ResolvePrivateAddr( IRK, pDevAddr ) == SUCCESS ) )
            {
                return ( idx ); // Found it
            }
        }
    }

    return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
    @fn      gapBondMgrReadBonds

    @brief   Read through NV and store them in RAM.

    @param   none

    @return  none
*/
static void gapBondMgrReadBonds( void )
{
    for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
        // See if the entry exists in NV
        if ( osal_snv_read( mainRecordNvID(idx), sizeof( gapBondRec_t ), &(bonds[idx]) ) != SUCCESS )
        {
            // Can't read the entry, assume that it doesn't exist
            VOID osal_memset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN );
            VOID osal_memset( bonds[idx].reconnectAddr, 0xFF, B_ADDR_LEN );
            bonds[idx].stateFlags = 0;
        }
    }

    if ( autoSyncWhiteList )
    {
        gapBondMgr_SyncWhiteList();
    }

    // Update the GAP Privacy Flag Properties
    gapBondSetupPrivFlag();
}

/*********************************************************************
    @fn      gapBondMgrFindEmpty

    @brief   Look through the bonding NV entries to find an empty.

    @param   none

    @return  index to empty bonding (0 - (GAP_BONDINGS_MAX-1),
            GAP_BONDINGS_MAX if no empty entries
*/
static uint8 gapBondMgrFindEmpty( void )
{
    // Item doesn't exist, so create all the items
    for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
        // Look for public address of all 0xFF's
        if ( osal_isbufset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ) )
        {
            return ( idx ); // Found one
        }
    }

    return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
    @fn      gapBondMgrBondTotal

    @brief   Look through the bonding NV entries calculate the number
            entries.

    @param   none

    @return  total number of bonds found
*/
static uint8 gapBondMgrBondTotal( void )
{
    uint8 numBonds = 0;

    // Item doesn't exist, so create all the items
    for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
        // Look for public address that are not 0xFF's
        if ( osal_isbufset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ) == FALSE )
        {
            numBonds++; // Found one
        }
    }

    return ( numBonds );
}

/*********************************************************************
    @fn      gapBondMgrEraseAllBondings

    @brief   Write all 0xFF's to all of the bonding entries

    @param   none

    @return  SUCCESS if successful.
            Otherwise, NV_OPER_FAILED for failure.
*/
static bStatus_t gapBondMgrEraseAllBondings( void )
{
    bStatus_t stat = SUCCESS;  // return value

    // Item doesn't exist, so create all the items
    for ( uint8 idx = 0; (idx < GAP_BONDINGS_MAX) && (stat == SUCCESS); idx++ )
    {
        // Erasing will write/create a bonding entry
        stat = gapBondMgrEraseBonding( idx );
    }

    return ( stat );
}

/*********************************************************************
    @fn      gapBondMgrEraseBonding

    @brief   Write all 0xFF's to the complete bonding record

    @param   idx - bonding index

    @return  SUCCESS if successful.
            Otherwise, NV_OPER_FAILED for failure.
*/
static bStatus_t gapBondMgrEraseBonding( uint8 idx )
{
    bStatus_t ret;
    gapBondRec_t bondRec;

    if ( idx == bondIdx )
    {
        // Stop ongoing bond store process to prevent any invalid data be written.
//    osal_clear_event( gapBondMgr_TaskID, GAP_BOND_SYNC_CC_EVT );
//    osal_clear_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT );
//        gapBondFreeAuthEvt();
    }

    // First see if bonding record exists in NV, then write all 0xFF's to it
    if ( ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS )
            && (osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE) )
    {
        gapBondLTK_t ltk;
        gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX];
        VOID osal_memset( &bondRec, 0xFF, sizeof ( gapBondRec_t ) );
        VOID osal_memset( &ltk, 0xFF, sizeof ( gapBondLTK_t ) );
        VOID osal_memset( charCfg, 0xFF, sizeof ( charCfg ) );
        // Write out FF's over the entire bond entry.
        ret = osal_snv_write( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec );
        ret |= osal_snv_write( localLTKNvID(idx), sizeof ( gapBondLTK_t ), &ltk );
        ret |= osal_snv_write( devLTKNvID(idx), sizeof ( gapBondLTK_t ), &ltk );
        ret |= osal_snv_write( devIRKNvID(idx), KEYLEN, ltk.LTK );
        ret |= osal_snv_write( devCSRKNvID(idx), KEYLEN, ltk.LTK );
        ret |= osal_snv_write( devSignCounterNvID(idx), sizeof ( uint32 ), ltk.LTK );
        // Write out FF's over the charactersitic configuration entry.
        ret |= osal_snv_write( gattCfgNvID(idx), sizeof ( charCfg ), charCfg );
    }
    else
    {
        ret = SUCCESS;
    }

    return ( ret );
}

/*********************************************************************
    @brief   Task Initialization function.

    Internal function defined in gapbondmgr.h.
*/
void GAPBondMgr_Init( uint8 task_id )
{
    gapBondMgr_TaskID = task_id;  // Save task ID
    // Setup Bond RAM Shadow
    gapBondMgrReadBonds();
}

/*********************************************************************
    @brief   Task Event Processor function.

    Internal function defined in gapbondmgr.h.
*/
uint16 GAPBondMgr_ProcessEvent( uint8 task_id, uint16 events )
{
    VOID task_id; // OSAL required parameter that isn't used in this function

    if ( events & SYS_EVENT_MSG )
    {
        uint8* pMsg;

        if ( (pMsg = osal_msg_receive( gapBondMgr_TaskID )) != NULL )
        {
            if ( gapBondMgr_ProcessOSALMsg( (osal_event_hdr_t*)pMsg ) )
            {
                // Release the OSAL message
                VOID osal_msg_deallocate( pMsg );
            }

            // return unprocessed events
        }

        // return unprocessed events
        return (events ^ SYS_EVENT_MSG);
    }

//  if ( events & GAP_BOND_SAVE_REC_EVT )
//  {
//
//    // Save bonding record in NV
//    if ( gapBondMgrAddBond( NULL, NULL ) )
//    {
//      // Notify our task to update NV with CCC values stored in GATT database
//      osal_set_event( gapBondMgr_TaskID, GAP_BOND_SYNC_CC_EVT );
//
//      return (events ^ GAP_BOND_SAVE_REC_EVT);
//    }
//
//    return ( GAP_BOND_SAVE_REC_EVT );
//  }
//
//  if ( events & GAP_BOND_SYNC_CC_EVT )
//  {
//    // Update NV to have same CCC values as GATT database
//    // Note: pAuthEvt is a global variable used for deferring the storage
//    if ( gapBondMgr_SyncCharCfg( pAuthEvt->connectionHandle ) )
//    {
//      if ( pGapBondCB && pGapBondCB->pairStateCB )
//      {
//        // Assume SUCCESS since we got this far.
//        pGapBondCB->pairStateCB( pAuthEvt->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, SUCCESS );
//      }
//
//      // We're done storing bond record and CCC values in NV
//      gapBondFreeAuthEvt();
//
//      return (events ^ GAP_BOND_SYNC_CC_EVT);
//    }
//
//    return ( GAP_BOND_SYNC_CC_EVT );
//  }
    // Discard unknown events
    return 0;
}

/*********************************************************************
    @fn      gapBondMgr_ProcessOSALMsg

    @brief   Process an incoming task message.

    @param   pMsg - message to process

    @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
*/
static uint8 gapBondMgr_ProcessOSALMsg( osal_event_hdr_t* pMsg )
{
    uint8 safeToDealloc = TRUE;

    switch ( pMsg->event )
    {
    case GAP_MSG_EVENT:
        safeToDealloc = GAPBondMgr_ProcessGAPMsg( (gapEventHdr_t*)pMsg );
        break;

    case GATT_MSG_EVENT:
        gapBondMgr_ProcessGATTMsg( (gattMsgEvent_t*)pMsg );
        break;

    case GATT_SERV_MSG_EVENT:
        gapBondMgr_ProcessGATTServMsg( (gattEventHdr_t*)pMsg );
        break;

    default:
        break;
    }

    return ( safeToDealloc );
}

/*********************************************************************
    @fn      GAPBondMgr_CheckNVLen

    @brief   This function will check the length of an NV Item.

    @param   id - NV ID.
    @param   len - lengths in bytes of item.

    @return  SUCCESS or FAILURE
*/
uint8 GAPBondMgr_CheckNVLen( uint8 id, uint8 len )
{
    uint8 stat = FAILURE;

    // Convert to index
    switch ( (id - BLE_NVID_GAP_BOND_START) % GAP_BOND_REC_IDS )
    {
    case GAP_BOND_REC_ID_OFFSET:
        if ( len == sizeof ( gapBondRec_t ) )
        {
            stat = SUCCESS;
        }

        break;

    case GAP_BOND_LOCAL_LTK_OFFSET:
    case GAP_BOND_DEV_LTK_OFFSET:
        if ( len == sizeof ( gapBondLTK_t ) )
        {
            stat = SUCCESS;
        }

        break;

    case GAP_BOND_DEV_IRK_OFFSET:
    case GAP_BOND_DEV_CSRK_OFFSET:
        if ( len == KEYLEN )
        {
            stat = SUCCESS;
        }

        break;

    case GAP_BOND_DEV_SIGN_COUNTER_OFFSET:
        if ( len == sizeof ( uint32 ) )
        {
            stat = SUCCESS;
        }

        break;

    default:
        break;
    }

    return ( stat );
}

/*********************************************************************
    @fn          gapBondMgr_ProcessGATTMsg

    @brief       Process an incoming GATT message.

    @param       pMsg - pointer to received message

    @return      none
*/
static void gapBondMgr_ProcessGATTMsg( gattMsgEvent_t* pMsg )
{
    // Process the GATT message
    switch ( pMsg->method )
    {
    case ATT_HANDLE_VALUE_CFM:
        // Clear Service Changed flag for this client
        VOID GAPBondMgr_ServiceChangeInd( pMsg->connHandle, 0x00 );
        break;

    default:
        // Unknown message
        break;
    }
}

/*********************************************************************
    @fn          gapBondMgr_ProcessGATTServMsg

    @brief       Process an incoming GATT Server App message.

    @param       pMsg - pointer to received message

    @return      none
*/
static void gapBondMgr_ProcessGATTServMsg( gattEventHdr_t* pMsg )
{
    // Process the GATT Server App message
    switch ( pMsg->method )
    {
    case GATT_CLIENT_CHAR_CFG_UPDATED_EVENT:
    {
        gattClientCharCfgUpdatedEvent_t* pEvent = (gattClientCharCfgUpdatedEvent_t*)pMsg;
        VOID GAPBondMgr_UpdateCharCfg( pEvent->connHandle, pEvent->attrHandle, pEvent->value );
    }
    break;

    default:
        // Unknown message
        break;
    }
}

/*********************************************************************
    @fn      gapBondMgrSendServiceChange

    @brief   Tell the GATT that a service change is needed.

    @param   pLinkItem - pointer to connection information

    @return  none
*/
static void gapBondMgrSendServiceChange( linkDBItem_t* pLinkItem )
{
    VOID GATTServApp_SendServiceChangedInd( pLinkItem->connectionHandle,
                                            gapBondMgr_TaskID );
}

/*********************************************************************
    @fn      gapBondSetupPrivFlag

    @brief   Setup the GAP Privacy Flag properties.

    @param   none

    @return  none
*/
static void gapBondSetupPrivFlag( void )
{
    uint8 privFlagProp;

    if ( gapBondMgrBondTotal() > 1 )
    {
        privFlagProp = GATT_PROP_READ;
    }
    else
    {
        privFlagProp = GATT_PROP_READ | GATT_PROP_WRITE;
    }

    // Setup the
    VOID GGS_SetParameter( GGS_PERI_PRIVACY_FLAG_PROPS, sizeof ( uint8 ), &privFlagProp );
}

/*********************************************************************
    @fn      gapBondMgrAuthenticate

    @brief   Initiate authentication

    @param   connHandle - connection handle
    @param   addrType - peer address type
    @param   pPairReq - Enter these parameters if the Pairing Request was already received.
            NULL, if waiting for Pairing Request or if initiating.

    @return  none
*/
static void gapBondMgrAuthenticate( uint16 connHandle, uint8 addrType,
                                    gapPairingReq_t* pPairReq )
{
    gapAuthParams_t params;
    VOID osal_memset( &params, 0, sizeof ( gapAuthParams_t ) );
    // Setup the pairing parameters
    params.connectionHandle = connHandle;
    params.secReqs.ioCaps = gapBond_IOCap;
    params.secReqs.oobAvailable = gapBond_OOBDataFlag;
    params.secReqs.maxEncKeySize = gapBond_KeySize;
    params.secReqs.keyDist.sEncKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_SENCKEY) ? TRUE : FALSE;
    params.secReqs.keyDist.sIdKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_SIDKEY) ? TRUE : FALSE;
    params.secReqs.keyDist.mEncKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_MENCKEY) ? TRUE : FALSE;
    params.secReqs.keyDist.mIdKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_MIDKEY) ? TRUE : FALSE;
    params.secReqs.keyDist.mSign = (gapBond_KeyDistList & GAPBOND_KEYDIST_MSIGN) ? TRUE : FALSE;
    params.secReqs.keyDist.sSign = (gapBond_KeyDistList & GAPBOND_KEYDIST_SSIGN) ? TRUE : FALSE;

    // Is bond manager setup for OOB data?
    if ( gapBond_OOBDataFlag )
    {
        VOID osal_memcpy( params.secReqs.oob, gapBond_OOBData, KEYLEN );
    }

    if ( gapBond_Bonding && addrType != ADDRTYPE_PUBLIC )
    {
        // Force a slave ID key
        params.secReqs.keyDist.sIdKey = TRUE;
    }

    params.secReqs.authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0;
    params.secReqs.authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0;
    params.secReqs.authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0;
    params.secReqs.authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0;
    params.secReqs.authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0;
    GAP_Authenticate( &params, pPairReq );
}

#if ( HOST_CONFIG & PERIPHERAL_CFG )
/*********************************************************************
    @fn      gapBondMgrSlaveSecurityReq

    authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0;
    authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0;
    authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0;
    authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0;
    authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0;

    @param   connHandle - connection handle

    @return  none
*/
static void gapBondMgrSlaveSecurityReq( uint16 connHandle )
{
    uint8 authReq = 0;
    authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0;
    authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0;
    authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0;
    authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0;
    authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0;
    GAP_SendSlaveSecurityRequest( connHandle, authReq );
}
#endif

/*********************************************************************
    @fn      gapBondMgrBondReq

    @brief   Initiate a GAP bond request

    @param   connHandle - connection handle
    @param   idx - NV index of bond entry
    @param   stateFlags - bond state flags
    @param   role - master or slave role
    @param   startEncryption - whether or not to start encryption

    @return  none
*/
static void gapBondMgrBondReq( uint16 connHandle, uint8 idx, uint8 stateFlags,
                               uint8 role, uint8 startEncryption )
{
    smSecurityInfo_t ltk;
    osalSnvId_t      nvId;

    if ( role == GAP_PROFILE_CENTRAL )
    {
        nvId = devLTKNvID( idx );
    }
    else
    {
        nvId = localLTKNvID( idx );
    }

    // Initialize the NV structures
    VOID osal_memset( &ltk, 0, sizeof ( smSecurityInfo_t ) );

    if ( osal_snv_read( nvId, sizeof ( smSecurityInfo_t ), &ltk ) == SUCCESS )
    {
        if ( (ltk.keySize >= MIN_ENC_KEYSIZE) && (ltk.keySize <= MAX_ENC_KEYSIZE) )
        {
            bStatus_t ret = GAP_Bond( connHandle,
                                      ((stateFlags & GAP_BONDED_STATE_AUTHENTICATED) ? TRUE : FALSE),
                                      &ltk, startEncryption );
        }
    }
}

/*********************************************************************
    @fn      gapBondMgr_SyncWhiteList

    @brief   syncronize the White List with the bonds

    @param   none

    @return  none
*/
static void gapBondMgr_SyncWhiteList( void )
{
    //erase the White List
    VOID HCI_LE_ClearWhiteListCmd();

    // Write bond addresses into the White List
    for( uint8 i = 0; i < GAP_BONDINGS_MAX; i++)
    {
        // Make sure empty addresses are not added to the White List
        if ( osal_isbufset( bonds[i].publicAddr, 0xFF, B_ADDR_LEN ) == FALSE )
        {
            VOID HCI_LE_AddWhiteListCmd( HCI_PUBLIC_DEVICE_ADDRESS, bonds[i].publicAddr );
        }
    }
}

/*********************************************************************
    @fn          gapBondMgr_SyncCharCfg

    @brief       Update the Bond Manager to have the same configurations as
                the GATT database.

    @param       connHandle - the current connection handle to find client configurations for

    @return      TRUE if sync done. FALSE, otherwise.
*/
static uint8 gapBondMgr_SyncCharCfg( uint16 connHandle )
{
    static gattAttribute_t* pAttr = NULL;
    static uint16 service;

    // Only attributes with attribute handles between and including the Starting
    // Handle parameter and the Ending Handle parameter that match the requested
    // attribute type and the attribute value will be returned.

    // All attribute types are effectively compared as 128-bit UUIDs,
    // even if a 16-bit UUID is provided in this request or defined
    // for an attribute.
    if ( pAttr == NULL )
    {
        pAttr = GATT_FindHandleUUID( GATT_MIN_HANDLE, GATT_MAX_HANDLE,
                                     clientCharCfgUUID, ATT_BT_UUID_SIZE, &service );
    }

    while ( pAttr != NULL )
    {
        uint16 len;
        uint8 attrVal[ATT_BT_UUID_SIZE];

        // It is not possible to use this request on an attribute that has a value
        // that is longer than 2.
        if ( GATTServApp_ReadAttr( connHandle, pAttr, service, attrVal,
                                   &len, 0, ATT_BT_UUID_SIZE ) == SUCCESS )
        {
            // It is not possible to use this request on an attribute that has a value
            // that is longer than 2.
            uint16 value = BUILD_UINT16(attrVal[0], attrVal[1]);

            if ( value != GATT_CFG_NO_OPERATION )
            {
                // NV must be updated to meet configuration of the database
                VOID GAPBondMgr_UpdateCharCfg( connHandle, pAttr->handle, value );
            }
        }

        // Try to find the next attribute
        pAttr = GATT_FindNextAttr( pAttr, GATT_MAX_HANDLE, service, NULL );
    }

    return ( pAttr == NULL );
}

/*********************************************************************
    @fn          gapBondFreeAuthEvt

    @brief       Free GAP Authentication Complete event.

    @param       none

    @return      none
*/
static void gapBondFreeAuthEvt( uint16 connHandle )
{
    if ( pAuthEvt[connHandle] != NULL )
    {
        // Release the OSAL message
        VOID osal_msg_deallocate( (uint8*)pAuthEvt[connHandle] );
        pAuthEvt[connHandle] = NULL;
    }

    bondIdx = GAP_BONDINGS_MAX;
}


/*****************************************************************************************
    fn:  gapBondRecvEvt

    date:2020-04-26

    brief:

    input parameters:connHandle connection handler


    output parameters:


    return       void

 *****************************************************************************************/
static void gapBondRecvEvt(uint16 connHandle, gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt )
{
    if ( gapBondMgrAddBond( pBondRec, pPkt ) )
    {
        // Update NV to have same CCC values as GATT database
        // Note: pAuthEvt is a global variable used for deferring the storage
        if ( gapBondMgr_SyncCharCfg( pAuthEvt[connHandle]->connectionHandle ) )
        {
            if ( pGapBondCB && pGapBondCB->pairStateCB )
            {
                // Assume SUCCESS since we got this far.
                pGapBondCB->pairStateCB( pAuthEvt[connHandle]->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, SUCCESS );
            }

            // We're done storing bond record and CCC values in NV
            gapBondFreeAuthEvt(connHandle);
        }
    }
}


#endif // ( CENTRAL_CFG | PERIPHERAL_CFG )

/*********************************************************************
*********************************************************************/
