/** * @file * @author chipsea * @brief * @version 0.1 * @date 2020-11-30 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd. * @note */ /********************************************************************* * INCLUDES */ #include "sdk_config.h" #include "bcomdef.h" #include "OSAL.h" #include "osal_cbTimer.h" #include "osal_snv.h" #include "hci_tl.h" #include "l2cap.h" #include "linkdb.h" #include "gap.h" #include "gapbondmgr.h" #include "central.h" /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ // Profile Events #define START_ADVERTISING_EVT 0x0001 #define RSSI_READ_EVT 0x0002 #define UPDATE_PARAMS_TIMEOUT_EVT 0x0004 // Profile OSAL Message IDs #define GAPCENTRALROLE_RSSI_MSG_EVT 0xE0 /********************************************************************* * TYPEDEFS */ // RSSI read data structure typedef struct { uint16 period; uint16 connHandle; uint8 timerId; } gapCentralRoleRssi_t; // OSAL event structure for RSSI timer events typedef struct { osal_event_hdr_t hdr; gapCentralRoleRssi_t *pRssi; } gapCentralRoleRssiEvent_t; /********************************************************************* * GLOBAL VARIABLES */ /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ // Task ID static uint8 gapCentralRoleTaskId; // App callbacks static gapCentralRoleCB_t *pGapCentralRoleCB; // Array of RSSI read structures static gapCentralRoleRssi_t gapCentralRoleRssi[GAPCENTRALROLE_NUM_RSSI_LINKS]; /********************************************************************* * Profile Parameters - reference GAPCENTRALROLE_PROFILE_PARAMETERS for * descriptions */ static uint8 gapCentralRoleIRK[KEYLEN]; static uint8 gapCentralRoleSRK[KEYLEN]; static uint32 gapCentralRoleSignCounter; static uint8 gapCentralRoleBdAddr[B_ADDR_LEN]; static uint8 gapCentralRoleMaxScanRes = 0; /********************************************************************* * LOCAL FUNCTIONS */ static void gapCentralRole_ProcessOSALMsg( osal_event_hdr_t *pMsg ); static void gapCentralRole_ProcessGAPMsg( gapEventHdr_t *pMsg ); static gapCentralRoleRssi_t *gapCentralRole_RssiAlloc( uint16 connHandle ); static gapCentralRoleRssi_t *gapCentralRole_RssiFind( uint16 connHandle ); static void gapCentralRole_RssiFree( uint16 connHandle ); static void gapCentralRole_timerCB( uint8 *pData ); /********************************************************************* * PUBLIC FUNCTIONS */ /** * @brief Start the device in Central role. This function is typically * called once during system startup. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_StartDevice( gapCentralRoleCB_t *pAppCallbacks ) { if ( pAppCallbacks ) { pGapCentralRoleCB = pAppCallbacks; } return GAP_DeviceInit( gapCentralRoleTaskId, GAP_PROFILE_CENTRAL, gapCentralRoleMaxScanRes, gapCentralRoleIRK, gapCentralRoleSRK, &gapCentralRoleSignCounter ); } /** * @brief Set a parameter in the Central Profile. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_SetParameter( uint16 param, uint8 len, void *pValue ) { bStatus_t ret = SUCCESS; switch ( param ) { case GAPCENTRALROLE_IRK: if ( len == KEYLEN ) { VOID osal_memcpy( gapCentralRoleIRK, pValue, KEYLEN ) ; } else { ret = bleInvalidRange; } break; case GAPCENTRALROLE_SRK: if ( len == KEYLEN ) { VOID osal_memcpy( gapCentralRoleSRK, pValue, KEYLEN ) ; } else { ret = bleInvalidRange; } break; case GAPCENTRALROLE_SIGNCOUNTER: if ( len == sizeof ( uint32 ) ) { gapCentralRoleSignCounter = *((uint32*)pValue); } else { ret = bleInvalidRange; } break; case GAPCENTRALROLE_MAX_SCAN_RES: if ( len == sizeof ( uint8 ) ) { gapCentralRoleMaxScanRes = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; default: ret = INVALIDPARAMETER; break; } return ret; } /** * @brief Get a parameter in the Central Profile. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_GetParameter( uint16 param, void *pValue ) { bStatus_t ret = SUCCESS; switch ( param ) { case GAPCENTRALROLE_IRK: VOID osal_memcpy( pValue, gapCentralRoleIRK, KEYLEN ) ; break; case GAPCENTRALROLE_SRK: VOID osal_memcpy( pValue, gapCentralRoleSRK, KEYLEN ) ; break; case GAPCENTRALROLE_SIGNCOUNTER: *((uint32*)pValue) = gapCentralRoleSignCounter; break; case GAPCENTRALROLE_BD_ADDR: VOID osal_memcpy( pValue, gapCentralRoleBdAddr, B_ADDR_LEN ) ; break; case GAPCENTRALROLE_MAX_SCAN_RES: *((uint8*)pValue) = gapCentralRoleMaxScanRes; break; default: ret = INVALIDPARAMETER; break; } return ret; } /** * @brief Terminate a link. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_TerminateLink( uint16 connHandle ) { return GAP_TerminateLinkReq( gapCentralRoleTaskId, connHandle, HCI_DISCONNECT_REMOTE_USER_TERM ) ; } /** * @brief Establish a link to a peer device. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_EstablishLink( uint8 highDutyCycle, uint8 whiteList, uint8 addrTypePeer, uint8 *peerAddr ) { gapEstLinkReq_t params; params.taskID = gapCentralRoleTaskId; params.highDutyCycle = highDutyCycle; params.whiteList = whiteList; params.addrTypePeer = addrTypePeer; VOID osal_memcpy( params.peerAddr, peerAddr, B_ADDR_LEN ); return GAP_EstablishLinkReq( ¶ms ); } /** * @brief Update the link connection parameters. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_UpdateLink( uint16 connHandle, uint16 connIntervalMin, uint16 connIntervalMax, uint16 connLatency, uint16 connTimeout ) { return (bStatus_t) HCI_LE_ConnUpdateCmd( connHandle, connIntervalMin, connIntervalMax, connLatency, connTimeout, 0, 0 ); } /** * @brief Start a device discovery scan. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_StartDiscovery( uint8 mode, uint8 activeScan, uint8 whiteList ) { gapDevDiscReq_t params; params.taskID = gapCentralRoleTaskId; params.mode = mode; params.activeScan = activeScan; params.whiteList = whiteList; return GAP_DeviceDiscoveryRequest( ¶ms ); } /** * @brief Cancel a device discovery scan. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_CancelDiscovery( void ) { return GAP_DeviceDiscoveryCancel( gapCentralRoleTaskId ); } /** * @brief Start periodic RSSI reads on a link. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_StartRssi( uint16 connHandle, uint16 period ) { gapCentralRoleRssi_t *pRssi; // Verify link is up if (!linkDB_Up(connHandle)) { return bleIncorrectMode; } // If already allocated if ((pRssi = gapCentralRole_RssiFind( connHandle )) != NULL) { // Stop timer osal_CbTimerStop( pRssi->timerId ); } // Allocate structure else if ((pRssi = gapCentralRole_RssiAlloc( connHandle )) != NULL) { pRssi->period = period; } // Allocate failed else { return bleNoResources; } // Start timer osal_CbTimerStart( gapCentralRole_timerCB, (uint8 *) pRssi, period, &pRssi->timerId ); return SUCCESS; } /** * @brief Cancel periodic RSSI reads on a link. * * Public function defined in central.h. */ bStatus_t GAPCentralRole_CancelRssi(uint16 connHandle ) { gapCentralRoleRssi_t *pRssi; if ((pRssi = gapCentralRole_RssiFind( connHandle )) != NULL) { // Stop timer osal_CbTimerStop( pRssi->timerId ); // Free RSSI structure gapCentralRole_RssiFree( connHandle ); return SUCCESS; } // Not found return bleIncorrectMode; } /** * @brief Central Profile Task initialization function. * * @param taskId - Task ID. * * @return void */ void GAPCentralRole_Init( uint8 taskId ) { uint8 i; gapCentralRoleTaskId = taskId; // Initialize internal data for ( i = 0; i < GAPCENTRALROLE_NUM_RSSI_LINKS; i++ ) { gapCentralRoleRssi[i].connHandle = GAP_CONNHANDLE_ALL; gapCentralRoleRssi[i].timerId = INVALID_TIMER_ID; } // Initialize parameters // Retore items from NV // VOID osal_snv_read( BLE_NVID_IRK, KEYLEN, gapCentralRoleIRK ); // VOID osal_snv_read( BLE_NVID_CSRK, KEYLEN, gapCentralRoleSRK ); // VOID osal_snv_read( BLE_NVID_SIGNCOUNTER, sizeof( uint32 ), &gapCentralRoleSignCounter ); // Register for HCI messages (for RSSI) GAP_RegisterForHCIMsgs( taskId ); } /** * @brief Central Profile Task event processing function. * * @param taskId - Task ID * @param events - Events. * * @return events not processed */ uint16 GAPCentralRole_ProcessEvent( uint8 taskId, uint16 events ) { if ( events & SYS_EVENT_MSG ) { uint8 *pMsg; if ( (pMsg = osal_msg_receive( gapCentralRoleTaskId )) != NULL ) { gapCentralRole_ProcessOSALMsg( (osal_event_hdr_t *) pMsg ); // Release the OSAL message VOID osal_msg_deallocate( pMsg ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } if ( events & GAP_EVENT_SIGN_COUNTER_CHANGED ) { // Sign counter changed, save it to NV // VOID osal_snv_write( BLE_NVID_SIGNCOUNTER, sizeof( uint32 ), &gapCentralRoleSignCounter ); return ( events ^ GAP_EVENT_SIGN_COUNTER_CHANGED ); } // Discard unknown events return 0; } /********************************************************************* * @fn gapCentralRole_ProcessOSALMsg * * @brief Process an incoming task message. * * @param pMsg - message to process * * @return none */ static void gapCentralRole_ProcessOSALMsg( osal_event_hdr_t *pMsg ) { switch ( pMsg->event ) { case HCI_GAP_EVENT_EVENT: if ( pMsg->status == HCI_COMMAND_COMPLETE_EVENT_CODE ) { hciEvt_CmdComplete_t *pPkt = (hciEvt_CmdComplete_t *) pMsg; if ( pPkt->cmdOpcode == HCI_READ_RSSI ) { uint16 connHandle = BUILD_UINT16( pPkt->pReturnParam[1], pPkt->pReturnParam[2] ); int8 rssi = (int8) pPkt->pReturnParam[3]; // Report RSSI to app if ( pGapCentralRoleCB && pGapCentralRoleCB->rssiCB ) { pGapCentralRoleCB->rssiCB( connHandle, rssi ); } } } break; case GAP_MSG_EVENT: gapCentralRole_ProcessGAPMsg( (gapEventHdr_t *) pMsg ); break; case GAPCENTRALROLE_RSSI_MSG_EVT: { gapCentralRoleRssi_t *pRssi = ((gapCentralRoleRssiEvent_t *) pMsg)->pRssi; // If link is up and RSSI reads active if (pRssi->connHandle != GAP_CONNHANDLE_ALL && linkDB_Up(pRssi->connHandle)) { // Restart timer osal_CbTimerStart( gapCentralRole_timerCB, (uint8 *) pRssi, pRssi->period, &pRssi->timerId ); // Read RSSI VOID HCI_ReadRssiCmd( pRssi->connHandle ); } } break; default: break; } } /********************************************************************* * @fn gapCentralRole_ProcessGAPMsg * * @brief Process an incoming task message from GAP. * * @param pMsg - message to process * * @return none */ static void gapCentralRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) { switch ( pMsg->opcode ) { case GAP_DEVICE_INIT_DONE_EVENT: { gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *) pMsg; if ( pPkt->hdr.status == SUCCESS ) { // Save off the generated keys // VOID osal_snv_write( BLE_NVID_IRK, KEYLEN, gapCentralRoleIRK ); // VOID osal_snv_write( BLE_NVID_CSRK, KEYLEN, gapCentralRoleSRK ); // Save off the information VOID osal_memcpy( gapCentralRoleBdAddr, pPkt->devAddr, B_ADDR_LEN ); } } break; case GAP_LINK_ESTABLISHED_EVENT: { gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *) pMsg; if (pPkt->hdr.status == SUCCESS) { // Notify the Bond Manager of the connection VOID GAPBondMgr_LinkEst( pPkt->devAddrType, pPkt->devAddr, pPkt->connectionHandle, GAP_PROFILE_CENTRAL ); } } break; case GAP_LINK_TERMINATED_EVENT: { uint16 connHandle = ((gapTerminateLinkEvent_t *) pMsg)->connectionHandle; VOID GAPBondMgr_ProcessGAPMsg( (gapEventHdr_t *)pMsg ); // Cancel RSSI reads GAPCentralRole_CancelRssi( connHandle ); } break; // temporary workaround case GAP_SLAVE_REQUESTED_SECURITY_EVENT: VOID GAPBondMgr_ProcessGAPMsg( pMsg ); break; default: break; } // Pass event to app if ( pGapCentralRoleCB && pGapCentralRoleCB->eventCB ) { pGapCentralRoleCB->eventCB( (gapCentralRoleEvent_t *) pMsg ); } } /********************************************************************* * @fn gapCentralRole_RssiAlloc * * @brief Allocate an RSSI structure. * * @param connHandle - Connection handle * * @return pointer to structure or NULL if allocation failed. */ static gapCentralRoleRssi_t *gapCentralRole_RssiAlloc( uint16 connHandle ) { uint8 i; // Find free RSSI structure for ( i = 0; i < GAPCENTRALROLE_NUM_RSSI_LINKS; i++ ) { if ( gapCentralRoleRssi[i].connHandle == GAP_CONNHANDLE_ALL ) { gapCentralRoleRssi[i].connHandle = connHandle; return &gapCentralRoleRssi[i]; } } // No free structure found return NULL; } /********************************************************************* * @fn gapCentralRole_RssiFind * * @brief Find an RSSI structure. * * @param connHandle - Connection handle * * @return pointer to structure or NULL if not found. */ static gapCentralRoleRssi_t *gapCentralRole_RssiFind( uint16 connHandle ) { uint8 i; // Find free RSSI structure for ( i = 0; i < GAPCENTRALROLE_NUM_RSSI_LINKS; i++ ) { if ( gapCentralRoleRssi[i].connHandle == connHandle ) { return &gapCentralRoleRssi[i]; } } // Not found return NULL; } /********************************************************************* * @fn gapCentralRole_RssiFree * * @brief Free an RSSI structure. * * @param connHandle - Connection handle * * @return none */ static void gapCentralRole_RssiFree( uint16 connHandle ) { uint8 i; // Find RSSI structure for ( i = 0; i < GAPCENTRALROLE_NUM_RSSI_LINKS; i++ ) { if ( gapCentralRoleRssi[i].connHandle == connHandle ) { gapCentralRoleRssi[i].connHandle = GAP_CONNHANDLE_ALL; break; } } } /********************************************************************* * @fn gapCentralRole_timerCB * * @brief OSAL timer callback function * * @param pData - Data pointer * * @return none */ static void gapCentralRole_timerCB( uint8 *pData ) { gapCentralRoleRssiEvent_t *pMsg; // Timer has expired so clear timer ID ((gapCentralRoleRssi_t *) pData)->timerId = INVALID_TIMER_ID; // Send OSAL message pMsg = (gapCentralRoleRssiEvent_t *) osal_msg_allocate( sizeof(gapCentralRoleRssiEvent_t) ); if ( pMsg ) { pMsg->hdr.event = GAPCENTRALROLE_RSSI_MSG_EVT; pMsg->pRssi = (gapCentralRoleRssi_t *) pData; osal_msg_send ( gapCentralRoleTaskId, (uint8 *) pMsg ); } } /********************************************************************* *********************************************************************/