/* * * Copyright (c) 2020-2022 Project CHIP Authors * Copyright (c) 2019 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * Provides an implementation of the BLEManager object for cc13xx_cc26xx * platform using the Texas Instruments SDK and the OpenThread stack. */ #include /* this file behaves like a config.h, comes first */ #include #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE #include #include #include "FreeRTOS.h" #include #include /* Include DMM module */ #include "chipOBleProfile.h" #include "hal_types.h" #include "ti_dmm_application_policy.h" #include #include #include #include #include #include #include #include #include extern "C" { #include "ti_ble_config.h" #include "ti_drivers_config.h" #ifndef ICALL_FEATURE_SEPARATE_IMGINFO #include #endif /* ICALL_FEATURE_SEPARATE_IMGINFO */ } // BLE Manager Debug Logs extern "C" { #ifdef BLEMGR_DBG_LOGGING extern void cc13xx_26xxLog(const char * aFormat, ...); #define BLEMGR_LOG(...) cc13xx_26xxLog(__VA_ARGS__); #else #define BLEMGR_LOG(...) #endif } #ifndef USE_DEFAULT_USER_CFG #include "ble_user_config.h" // BLE user defined configuration. Required to be globally accesible for BLE initialization icall_userCfg_t user0Cfg = BLE_USER_CFG; #endif // USE_DEFAULT_USER_CFG using namespace ::chip; using namespace ::chip::Ble; namespace chip { namespace DeviceLayer { namespace Internal { /* Static class member initialization */ BLEManagerImpl BLEManagerImpl::sInstance; TaskHandle_t BLEManagerImpl::sBleTaskHndl; ICall_EntityID BLEManagerImpl::sSelfEntity; ICall_SyncHandle BLEManagerImpl::sSyncEvent; QueueHandle_t BLEManagerImpl::sEventHandlerMsgQueueID; chipOBleProfileCBs_t BLEManagerImpl::CHIPoBLEProfile_CBs = { // Provisioning GATT Characteristic value change callback CHIPoBLEProfile_charValueChangeCB }; // GAP Bond Manager Callbacks gapBondCBs_t BLEManagerImpl::BLEMgr_BondMgrCBs = { PasscodeCb, // Passcode callback PairStateCb // Pairing/Bonding state Callback }; // ===== Members that implement the BLEManager internal interface. CHIP_ERROR BLEManagerImpl::_Init(void) { CHIP_ERROR err = CHIP_NO_ERROR; BLEMGR_LOG("BLEMGR: BLE Initialization Start"); // Initialize the CHIP BleLayer. err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); if (err != CHIP_NO_ERROR) { return err; } /* Register BLE Stack assert handler */ RegisterAssertCback(AssertHandler); mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART); mFlags.Set(Flags::kFastAdvertisingEnabled, true); mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; err = CreateEventHandler(); return err; } bool BLEManagerImpl::_IsAdvertisingEnabled(void) { return mFlags.Has(Flags::kAdvertisingEnabled); } /* Post event to app processing loop to begin CHIP advertising */ CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val) { mFlags.Set(Flags::kAdvertisingEnabled, val); /* Send event to process state change request */ return DriveBLEState(); } CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode) { CHIP_ERROR ret = CHIP_NO_ERROR; switch (mode) { case BLEAdvertisingMode::kFastAdvertising: mFlags.Set(Flags::kFastAdvertisingEnabled, true); break; case BLEAdvertisingMode::kSlowAdvertising: mFlags.Set(Flags::kFastAdvertisingEnabled, false); break; default: return CHIP_ERROR_INVALID_ARGUMENT; } mFlags.Set(Flags::kAdvertisingRefreshNeeded); ret = DriveBLEState(); return ret; } bool BLEManagerImpl::_IsAdvertising(void) { return mFlags.Has(Flags::kAdvertising); } CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize) { CHIP_ERROR ret = CHIP_NO_ERROR; if (bufSize <= GAP_DEVICE_NAME_LEN) { Platform::CopyString(buf, bufSize, mDeviceName); } else { ret = CHIP_ERROR_BUFFER_TOO_SMALL; } return ret; } CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName) { CHIP_ERROR ret = CHIP_NO_ERROR; if (strlen(deviceName) <= GAP_DEVICE_NAME_LEN) { Platform::CopyString(mDeviceName, deviceName); mFlags.Set(Flags::kBLEStackGATTNameUpdate); mFlags.Set(Flags::kAdvertisingRefreshNeeded); ret = DriveBLEState(); } else { ret = CHIP_ERROR_BUFFER_TOO_SMALL; } return ret; } uint16_t BLEManagerImpl::_NumConnections(void) { uint8_t i, numConns = 0; // Try to find an available entry for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if (connList[i].connHandle != LL_CONNHANDLE_INVALID) { numConns++; } } return numConns; } void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) { switch (event->Type) { case DeviceEventType::kCHIPoBLESubscribe: { ChipDeviceEvent connEstEvent; BLEMGR_LOG("BLEMGR: OnPlatformEvent, kCHIPoBLESubscribe"); HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); connEstEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; PlatformMgr().PostEventOrDie(&connEstEvent); } break; case DeviceEventType::kCHIPoBLEUnsubscribe: { BLEMGR_LOG("BLEMGR: OnPlatformEvent, kCHIPoBLEUnsubscribe"); HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); } break; case DeviceEventType::kCHIPoBLEWriteReceived: { BLEMGR_LOG("BLEMGR: OnPlatformEvent, kCHIPoBLEWriteReceived"); HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_1_UUID, PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); } break; case DeviceEventType::kCHIPoBLEIndicateConfirm: HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); break; case DeviceEventType::kCHIPoBLEConnectionError: { BLEMGR_LOG("BLEMGR: OnPlatformEvent, kCHIPoBLEConnectionError"); HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); } break; default: break; } } // ===== Members that implement virtual methods on BlePlatformDelegate. CHIP_ERROR BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) { void * pMsg = (void *) ICall_malloc(sizeof(void *)); pMsg = (void *) conId; EnqueueEvtHdrMsg(BLEManagerIMPL_CHIPOBLE_CLOSE_CONN_EVT, (void *) pMsg); return CHIP_NO_ERROR; } uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const { uint8_t index; uint16_t mtu = 0; index = GetBLEConnIndex(*((uint32_t *) conId)); if (index != MAX_NUM_BLE_CONNS) { mtu = sInstance.connList[index].mtu; /* Prior to MTU update event, MTU is determined by the below formula */ if (mtu == 0) { mtu = MAX_PDU_SIZE - 4; } } return mtu; } // ===== Members that implement virtual methods on BleApplicationDelegate. void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) { // Unused } CHIP_ERROR BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, PacketBufferHandle data) { BLEMGR_LOG("BLEMGR: BLE SendIndication "); // Allocate buffers to send to BLE app task uint8_t dataLen = static_cast(data->DataLength()); CHIPoBLEIndEvt_t * pMsg; uint8_t * pBuf; pMsg = (CHIPoBLEIndEvt_t *) ICall_malloc(sizeof(CHIPoBLEIndEvt_t)); if (NULL == pMsg) { return CHIP_ERROR_NO_MEMORY; } pBuf = (uint8_t *) ICall_malloc(dataLen); if (NULL == pBuf) { ICall_free((void *) pMsg); return CHIP_ERROR_NO_MEMORY; } memset(pBuf, 0x00, dataLen); memcpy(pBuf, data->Start(), dataLen); pMsg->pData = pBuf; pMsg->len = dataLen; EnqueueEvtHdrMsg(BLEManagerIMPL_CHIPOBLE_TX_IND_EVT, (void *) pMsg); BLEMGR_LOG("BLEMGR: BLE SendIndication RETURN, Length: %d ", dataLen); return CHIP_NO_ERROR; } CHIP_ERROR BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId) { /* Unsupported on TI peripheral device implementation */ return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId) { /* Unsupported on TI peripheral device implementation */ return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, PacketBufferHandle pBuf) { /* Unsupported on TI peripheral device implementation */ return CHIP_ERROR_NOT_IMPLEMENTED; } // ===== Helper Members that implement the Low level BLE Stack behavior. /********************************************************************* * @fn ConfigureAdvertisements * * @brief Initialize CHIPoBLE Advertisements. */ void BLEManagerImpl::ConfigureAdvertisements(void) { bStatus_t status = FAILURE; uint16_t deviceDiscriminator; uint8_t localDeviceNameLen; uint8_t scanIndex = 0; uint8_t advIndex = 0; uint8_t scanResLength; uint8_t advLength; GapAdv_params_t advParams = { .eventProps = GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_LEGACY | GAP_ADV_PROP_SCANNABLE, .primIntMin = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MIN, .primIntMax = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MIN, .primChanMap = GAP_ADV_CHAN_ALL, .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID, .peerAddr = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, .filterPolicy = GAP_ADV_AL_POLICY_ANY_REQ, .txPower = GAP_ADV_TX_POWER_NO_PREFERENCE, .primPhy = GAP_ADV_PRIM_PHY_1_MBPS, .secPhy = GAP_ADV_SEC_PHY_1_MBPS, .sid = 0 }; BLEMGR_LOG("BLEMGR: ConfigureAdvertisements"); ChipBLEDeviceIdentificationInfo mDeviceIdInfo; ConfigurationMgr().GetBLEDeviceIdentificationInfo(mDeviceIdInfo); memset(sInstance.mScanResDatachipOBle, 0, CHIPOBLE_ADV_DATA_MAX_SIZE); memset(sInstance.mAdvDatachipOBle, 0, CHIPOBLE_ADV_DATA_MAX_SIZE); // Verify device name was not already set if (!sInstance.mFlags.Has(Flags::kBLEStackGATTNameSet)) { /* Default device name is CHIP- */ deviceDiscriminator = mDeviceIdInfo.GetDeviceDiscriminator(); localDeviceNameLen = strlen(CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX) + CHIPOBLE_DEVICE_DESC_LENGTH; memset(sInstance.mDeviceName, 0, GAP_DEVICE_NAME_LEN); snprintf(sInstance.mDeviceName, GAP_DEVICE_NAME_LEN, "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, deviceDiscriminator); // Set the Device Name characteristic in the GAP GATT Service // For more information, see the section in the User's Guide: // http://software-dl.ti.com/lprf/ble5stack-latest/ GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, sInstance.mDeviceName); BLEMGR_LOG("BLEMGR: AdvInit New device name set: %s", sInstance.mDeviceName); } else { sInstance.mFlags.Clear(Flags::kBLEStackGATTNameUpdate); localDeviceNameLen = strlen(sInstance.mDeviceName); } scanResLength = localDeviceNameLen + CHIPOBLE_SCANRES_SIZE_NO_NAME; /* Verify scan response data length */ assert(scanResLength < CHIPOBLE_ADV_DATA_MAX_SIZE); sInstance.mScanResDatachipOBle[scanIndex++] = localDeviceNameLen + 1; sInstance.mScanResDatachipOBle[scanIndex++] = GAP_ADTYPE_LOCAL_NAME_COMPLETE; memcpy(&sInstance.mScanResDatachipOBle[scanIndex], sInstance.mDeviceName, localDeviceNameLen); scanIndex += localDeviceNameLen; sInstance.mScanResDatachipOBle[scanIndex++] = 0x03; sInstance.mScanResDatachipOBle[scanIndex++] = GAP_ADTYPE_16BIT_COMPLETE; sInstance.mScanResDatachipOBle[scanIndex++] = static_cast(LO_UINT16(CHIPOBLE_SERV_UUID)); sInstance.mScanResDatachipOBle[scanIndex++] = static_cast(HI_UINT16(CHIPOBLE_SERV_UUID)); for (uint8_t temp = 0; temp < scanIndex; temp++) { BLEMGR_LOG("BLEMGR: AdvInit Scan Response Data: %x", sInstance.mScanResDatachipOBle[temp]); } advLength = sizeof(static_cast(CHIPOBLE_SERV_UUID)) + static_cast(sizeof(mDeviceIdInfo)) + 1; /* Verify advertising data length */ assert((CHIPOBLE_ADV_SIZE_NO_DEVICE_ID_INFO + advLength) < CHIPOBLE_ADV_DATA_MAX_SIZE); BLEMGR_LOG("BLEMGR: AdvInit: MDeviceIDInfo Size: %d", sizeof(mDeviceIdInfo)); BLEMGR_LOG("BLEMGR: AdvInit: advlength: %d", advLength); BLEMGR_LOG("BLEMGR: AdvInit:Desc : %d", mDeviceIdInfo.GetDeviceDiscriminator()); sInstance.mAdvDatachipOBle[advIndex++] = 0x02; sInstance.mAdvDatachipOBle[advIndex++] = GAP_ADTYPE_FLAGS; sInstance.mAdvDatachipOBle[advIndex++] = GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED | GAP_ADTYPE_FLAGS_GENERAL; sInstance.mAdvDatachipOBle[advIndex++] = advLength; sInstance.mAdvDatachipOBle[advIndex++] = GAP_ADTYPE_SERVICE_DATA; sInstance.mAdvDatachipOBle[advIndex++] = static_cast(LO_UINT16(CHIPOBLE_SERV_UUID)); sInstance.mAdvDatachipOBle[advIndex++] = static_cast(HI_UINT16(CHIPOBLE_SERV_UUID)); memcpy(&sInstance.mAdvDatachipOBle[advIndex], (void *) &mDeviceIdInfo, static_cast(sizeof(mDeviceIdInfo))); // Setup and start Advertising // For more information, see the GAP section in the User's Guide: // http://software-dl.ti.com/lprf/ble5stack-latest/ if (!sInstance.mFlags.Has(Flags::kBLEStackAdvInitialized)) { // Create Advertisement set #1 and assign handle status = (bStatus_t) GapAdv_create(&advCallback, &advParams, &sInstance.advHandleLegacy); assert(status == SUCCESS); // Set event mask for set #1 status = (bStatus_t) GapAdv_setEventMask(sInstance.advHandleLegacy, GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END_AFTER_DISABLE | GAP_ADV_EVT_MASK_SET_TERMINATED); Util_constructClock(&sInstance.clkAdvTimeout, AdvTimeoutHandler, ADV_TIMEOUT, 0, false, (uintptr_t) NULL); } if (sInstance.mFlags.Has(Flags::kBLEStackAdvInitialized)) { // Don't free anything since we're going to use the same buffer to re-load GapAdv_prepareLoadByHandle(sInstance.advHandleLegacy, GAP_ADV_FREE_OPTION_DONT_FREE); } // Load advertising data for set #1 that is statically allocated by the app status = (bStatus_t) GapAdv_loadByHandle(sInstance.advHandleLegacy, GAP_ADV_DATA_TYPE_ADV, CHIPOBLE_ADV_SIZE_NO_DEVICE_ID_INFO + advLength, sInstance.mAdvDatachipOBle); assert(status == SUCCESS); if (sInstance.mFlags.Has(Flags::kBLEStackAdvInitialized)) { // Don't free anything since we're going to use the same buffer to re-load GapAdv_prepareLoadByHandle(sInstance.advHandleLegacy, GAP_ADV_FREE_OPTION_DONT_FREE); } // Load scan response data for set #1 that is statically allocated by the app status = (bStatus_t) GapAdv_loadByHandle(sInstance.advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP, scanResLength, sInstance.mScanResDatachipOBle); assert(status == SUCCESS); } /********************************************************************* * @fn EventHandler_init * * @brief Called during initialization and contains application * specific initialization (ie. hardware initialization/setup, * table initialization, power up notification, etc), and * profile initialization/setup. */ void BLEManagerImpl::EventHandler_init(void) { BLEMGR_LOG("BLEMGR: EventHandler_init"); /* Update User Configuration of the stack */ user0Cfg.appServiceInfo->timerTickPeriod = ICall_getTickPeriod(); user0Cfg.appServiceInfo->timerMaxMillisecond = ICall_getMaxMSecs(); /* Initialize ICall module */ ICall_init(); /* Start tasks of external images */ ICall_createRemoteTasks(); BLEManagerImpl::sBleTaskHndl = (TaskHandle_t) (*((TaskHandle_t *) ICall_getRemoteTaskHandle(0))); DMMSch_registerClient((TaskHandle_t) BLEManagerImpl::sBleTaskHndl, DMMPolicy_StackRole_BlePeripheral); /* set the stacks in default states */ DMMPolicy_updateStackState(DMMPolicy_StackRole_BlePeripheral, DMMPOLICY_BLE_IDLE); vTaskPrioritySet(xTaskGetCurrentTaskHandle(), BLE_MANAGER_TASK_PRIORITY); // ****************************************************************** // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp // ****************************************************************** // Register the current thread as an ICall dispatcher application // so that the application can send and receive messages. ICall_registerApp(&BLEManagerImpl::sSelfEntity, &BLEManagerImpl::sSyncEvent); #ifdef USE_RCOSC RCOSC_enableCalibration(); #endif // USE_RCOSC // Create an RTOS queue for message from profile to be sent to app. Util_constructQueue(&BLEManagerImpl::sEventHandlerMsgQueueID); // Configure GAP { uint16_t paramUpdateDecision = DEFAULT_PARAM_UPDATE_REQ_DECISION; // Pass all parameter update requests to the app for it to decide GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, paramUpdateDecision); } // Setup the GAP Bond Manager. For more information see the GAP Bond Manager // section in the User's Guide: // http://software-dl.ti.com/lprf/ble5stack-latest/ setBondManagerParameters(); // Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES); // GAP GATT Service GATTServApp_AddService(GATT_ALL_SERVICES); // GATT Service DevInfo_AddService(); // Device Information Service CHIPoBLEProfile_AddService(GATT_ALL_SERVICES); // Start Bond Manager and register callback VOID GAPBondMgr_Register(&BLEMgr_BondMgrCBs); // Register with GAP for HCI/Host messages. This is needed to receive HCI // events. For more information, see the HCI section in the User's Guide: // http://software-dl.ti.com/lprf/ble5stack-latest/ GAP_RegisterForMsgs(BLEManagerImpl::sSelfEntity); // Register for GATT local events and ATT Responses pending for transmission GATT_RegisterForMsgs(BLEManagerImpl::sSelfEntity); CHIPoBLEProfile_RegisterAppCBs(&CHIPoBLEProfile_CBs); // Set default values for Data Length Extension // Extended Data Length Feature is already enabled by default { // This API is documented in hci.h // See the LE Data Length Extension section in the BLE5-Stack User's Guide for information on using this command: // http://software-dl.ti.com/lprf/ble5stack-latest/ HCI_LE_WriteSuggestedDefaultDataLenCmd(BLEMANAGER_SUGGESTED_PDU_SIZE, BLEMANAGER_SUGGESTED_TX_TIME); } // Initialize GATT Client GATT_InitClient(""); // Initialize Connection List ClearBLEConnListEntry(LL_CONNHANDLE_ALL); // Initialize GAP layer for Peripheral role and register to receive GAP events GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, BLEManagerImpl::sSelfEntity, sInstance.addrMode, &pRandomAddress); // Initialize array to store connection handle and RSSI values InitPHYRSSIArray(); BLEMGR_LOG("BLEMGR: EventHandler_init Done"); } /********************************************************************* * @fn InitPHYRSSIArray * * @brief Initializes the array of structure/s to store data related * RSSI based auto PHy change * * @param connHandle - the connection handle * * @param addr - pointer to device address * * @return index of connection handle */ void BLEManagerImpl::InitPHYRSSIArray(void) { BLEMGR_LOG("BLEMGR: InitPHYRSSIArray"); // Initialize array to store connection handle and RSSI values memset(sInstance.connList, 0, sizeof(sInstance.connList)); for (uint8_t index = 0; index < MAX_NUM_BLE_CONNS; index++) { sInstance.connList[index].connHandle = INVALID_HANDLE; } } /********************************************************************* * @fn CreateEventHandler * * @brief Create FreeRTOS Task for BLE Event handling * */ CHIP_ERROR BLEManagerImpl::CreateEventHandler(void) { BLEMGR_LOG("BLEMGR: CreateEventHandler"); BaseType_t xReturned; /* Create the task, storing the handle. */ xReturned = xTaskCreate(EventHandler, /* Function that implements the task. */ "ble_hndlr", /* Text name for the task. */ BLEMANAGER_EVENT_HANDLER_STACK_SIZE, /* Stack size in words, not bytes. */ this, /* Parameter passed into the task. */ BLE_STACK_TASK_PRIORITY, /* Keep priority the same as ICALL until init is complete */ NULL); /* Used to pass out the created task's handle. */ if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) { return CHIP_ERROR_NO_MEMORY; } else { return CHIP_NO_ERROR; } } /********************************************************************* * @fn RemoteDisplay_processStackMsg * * @brief Process an incoming stack message. * * @param pMsg - message to process * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ uint8_t BLEManagerImpl::ProcessStackEvent(ICall_Hdr * pMsg) { // Always dealloc pMsg unless set otherwise uint8_t safeToDealloc = TRUE; switch (pMsg->event) { case GAP_MSG_EVENT: ProcessGapMessage((gapEventHdr_t *) pMsg); break; case GATT_MSG_EVENT: // Process GATT message safeToDealloc = ProcessGATTMsg((gattMsgEvent_t *) pMsg); break; case HCI_GAP_EVENT_EVENT: { // Process HCI message switch (pMsg->status) { case HCI_COMMAND_COMPLETE_EVENT_CODE: // Process HCI Command Complete Events here { // RemoteDisplay_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg); break; } case HCI_BLE_HARDWARE_ERROR_EVENT_CODE: assert(false); break; // HCI Commands Events case HCI_COMMAND_STATUS_EVENT_CODE: { hciEvt_CommandStatus_t * pMyMsg = (hciEvt_CommandStatus_t *) pMsg; switch (pMyMsg->cmdOpcode) { case HCI_LE_SET_PHY: { if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE) { /* PHY Change failure, peer does not support this */ } break; } default: break; } break; } case HCI_LE_EVENT_CODE: { hciEvt_BLEPhyUpdateComplete_t * pPUC = (hciEvt_BLEPhyUpdateComplete_t *) pMsg; // A Phy Update Has Completed or Failed if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT) { if (pPUC->status != SUCCESS) { /* PHY Change failure */ } else { /* PHY Update successful */ } } break; } default: break; } break; } default: // do nothing break; } return safeToDealloc; } /********************************************************************* * @fn ProcessEvtHdrMsg * * @brief Process an incoming callback from a profile. * * @param pMsg - message to process * * @return None. */ void BLEManagerImpl::ProcessEvtHdrMsg(QueuedEvt_t * pMsg) { bool dealloc = TRUE; switch (pMsg->event) { /* External CHIPoBLE Event trigger */ case BLEManagerIMPL_STATE_UPDATE_EVT: { bStatus_t status; /* Verify BLE service mode is enabled */ if ((sInstance.mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled) && sInstance.mFlags.Has(Flags::kBLEStackInitialized)) { if (sInstance.mFlags.Has(Flags::kAdvertisingEnabled)) { BLEMGR_LOG("BLEMGR: BLE Process Application Message: kAdvertisingEnabled"); if (sInstance.mFlags.Has(Flags::kAdvertisingRefreshNeeded)) { BLEMGR_LOG("BLEMGR: BLE Process Application Message: kAdvertisingRefreshNeeded"); // Disable advertisements and proceed with updates sInstance.mFlags.Clear(Flags::kAdvertisingRefreshNeeded); GapAdv_disable(sInstance.advHandleLegacy); sInstance.mFlags.Clear(Flags::kAdvertising); uint32_t newParamMax = 0, newParamMin = 0; if (sInstance.mFlags.Has(Flags::kFastAdvertisingEnabled)) { // Update advertising interval BLEMGR_LOG("BLEMGR: ConfigureAdvertisements: Fast Advertising Enabled"); newParamMax = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MAX; newParamMin = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MIN; } else { // Decrease advertising interval BLEMGR_LOG("BLEMGR: ConfigureAdvertisements: Slow Advertising Enabled"); newParamMax = CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MAX; newParamMin = CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MIN; } // Set a parameter GapAdv_setParam(sInstance.advHandleLegacy, GAP_ADV_PARAM_PRIMARY_INTERVAL_MAX, &newParamMax); GapAdv_setParam(sInstance.advHandleLegacy, GAP_ADV_PARAM_PRIMARY_INTERVAL_MIN, &newParamMin); // Update advertisement parameters ConfigureAdvertisements(); } } // Turn on advertisements if (sInstance.mFlags.Has(Flags::kAdvertisingEnabled) && !sInstance.mFlags.Has(Flags::kAdvertising)) { // Send notification to thread manager that CHIPoBLE advertising is starting // Enable legacy advertising for set #1 status = (bStatus_t) GapAdv_enable(sInstance.advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX, 0); // If adverisement fails, keep flags set if (status == SUCCESS) { // Start advertisement timeout timer if (sInstance.mFlags.Has(Flags::kFastAdvertisingEnabled)) { Util_rescheduleClock(&sInstance.clkAdvTimeout, CHIP_DEVICE_CONFIG_BLE_ADVERTISING_INTERVAL_CHANGE_TIME); } else { Util_rescheduleClock(&sInstance.clkAdvTimeout, ADV_TIMEOUT - CHIP_DEVICE_CONFIG_BLE_ADVERTISING_INTERVAL_CHANGE_TIME); } Util_startClock(&sInstance.clkAdvTimeout); sInstance.mFlags.Set(Flags::kAdvertising); } } // Advertising should be disabled if ((!sInstance.mFlags.Has(Flags::kAdvertisingEnabled)) && sInstance.mFlags.Has(Flags::kAdvertising)) { BLEMGR_LOG("BLEMGR: BLE Process Application Message: ADvertisements disabled"); // Stop advertising GapAdv_disable(sInstance.advHandleLegacy); sInstance.mFlags.Clear(Flags::kAdvertising); Util_stopClock(&sInstance.clkAdvTimeout); // reset fast advertising sInstance.mFlags.Set(Flags::kFastAdvertisingEnabled); } } } break; case BLEManagerIMPL_CHIPOBLE_CLOSE_CONN_EVT: { uint16_t connHandle = *((uint16_t *) (pMsg->pData)); // Close active connection GAP_TerminateLinkReq(connHandle, HCI_DISCONNECT_REMOTE_USER_TERM); } break; case BLEManagerIMPL_CHIPOBLE_TX_IND_EVT: { uint8_t dataLen = ((CHIPoBLEIndEvt_t *) (pMsg->pData))->len; CHIPoBLEProfile_SetParameter(CHIPOBLEPROFILE_TX_CHAR, dataLen, (void *) (((CHIPoBLEIndEvt_t *) (pMsg->pData))->pData), BLEManagerImpl::sSelfEntity); BLEMGR_LOG("BLEMGR: BLE Process Application Message: BLEManagerIMPL_CHIPOBLE_TX_IND_EVT: Length: %d", dataLen); ICall_free((void *) (((CHIPoBLEIndEvt_t *) (pMsg->pData))->pData)); dealloc = TRUE; } break; case CHIPOBLE_CHAR_CHANGE_EVT: { uint16_t writeLen = ((CHIPoBLEProfChgEvt_t *) (pMsg->pData))->len; uint8_t paramId = ((CHIPoBLEProfChgEvt_t *) (pMsg->pData))->paramId; uint16_t connHandleId = ((CHIPoBLEProfChgEvt_t *) (pMsg->pData))->connHandle; void * connHandle; ChipDeviceEvent event; uint8_t i; ConnRec_t * activeConnObj = NULL; // Find active connection for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if (sInstance.connList[i].connHandle == connHandleId) { activeConnObj = &sInstance.connList[i]; } } connHandle = (void *) &activeConnObj->connHandle; if (paramId == CHIPOBLEPROFILE_RX_CHAR) { BLEMGR_LOG("BLEMGR: BLE Process Application Message: CHIPOBLE_CHAR_CHANGE_EVT, CHIPOBLEPROFILE_RX_CHAR"); // Pull written data from CHIPOBLE Profile based on extern server write uint8_t * rxBuf = (uint8_t *) ICall_malloc(writeLen); if (rxBuf == NULL) { // alloc error return; } memset(rxBuf, 0x00, writeLen); BLEMGR_LOG("BLEMGR: BLE Process Application Message: CHIPOBLE_CHAR_CHANGE_EVT, length: %d", writeLen); CHIPoBLEProfile_GetParameter(CHIPOBLEPROFILE_RX_CHAR, rxBuf, writeLen); System::PacketBufferHandle packetBuf = System::PacketBufferHandle::NewWithData(rxBuf, writeLen, 0, 0); ICall_free(rxBuf); if (packetBuf.IsNull()) { // alloc error return; } // Arrange to post a CHIPoBLERXWriteEvent event to the CHIP queue. event.Type = DeviceEventType::kCHIPoBLEWriteReceived; event.CHIPoBLEWriteReceived.ConId = (void *) connHandle; event.CHIPoBLEWriteReceived.Data = std::move(packetBuf).UnsafeRelease(); } else if (paramId == CHIPOBLEPROFILE_CCCWrite) { BLEMGR_LOG("BLEMGR: BLE Process Application Message: CHIPOBLE_CHAR_CHANGE_EVT, CHIPOBLEPROFILE_CCCWrite"); // TODO: Add check to see if subscribing OR unsubscribing from char indications uint8_t cccValue; CHIPoBLEProfile_GetParameter(CHIPOBLEPROFILE_CCCWrite, &cccValue, 1); // Check whether it is a sub/unsub event. 0x1 = Notifications enabled, 0x2 = Indications enabled if (cccValue & 0x2) { // Post event to CHIP BLEMGR_LOG("BLEMGR: BLE Process Application Message: CHIPOBLE_CHAR_CHANGE_EVT, Subscrbe"); event.Type = DeviceEventType::kCHIPoBLESubscribe; } else { BLEMGR_LOG("BLEMGR: BLE Process Application Message: CHIPOBLE_CHAR_CHANGE_EVT, unsubscrbe"); event.Type = DeviceEventType::kCHIPoBLEUnsubscribe; } // Post event to CHIP event.CHIPoBLESubscribe.ConId = (void *) connHandle; } PlatformMgr().PostEventOrDie(&event); } break; case ADV_EVT: ProcessAdvEvent((GapAdvEventData_t *) (pMsg->pData)); break; case PAIR_STATE_EVT: { BLEMGR_LOG("BLEMGR: PAIR_STATE_EVT"); } break; case PASSCODE_EVT: { BLEMGR_LOG("BLEMGR: PASSCODE_EVT"); } break; case READ_RPA_EVT: UpdateBLERPA(); break; case SEND_PARAM_UPDATE_EVT: { // Extract connection handle from data uint16_t connHandle = *(uint16_t *) (((ClockEventData_t *) pMsg->pData)->data); if (CHIP_NO_ERROR != ProcessParamUpdate(connHandle)) { // error return; } // This data is not dynamically allocated dealloc = FALSE; /* If we are sending a param update request then the service discovery * should have ended. Changed state to connected */ DMMPolicy_updateStackState(DMMPolicy_StackRole_BlePeripheral, DMMPOLICY_BLE_CONNECTED); break; } case CONN_EVT: break; default: // Do nothing. break; } // Free message data if it exists and we are to dealloc if ((dealloc == TRUE) && (pMsg->pData != NULL)) { ICall_free(pMsg->pData); } } /********************************************************************* * @fn ProcessGapMessage * * @brief Process an incoming GAP event. * * @param pMsg - message to process */ void BLEManagerImpl::ProcessGapMessage(gapEventHdr_t * pMsg) { BLEMGR_LOG("BLEMGR: ProcessGapMessage"); switch (pMsg->opcode) { case GAP_DEVICE_INIT_DONE_EVENT: { BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_DEVICE_INIT_DONE_EVENT"); gapDeviceInitDoneEvent_t * pPkt = (gapDeviceInitDoneEvent_t *) pMsg; if (pPkt->hdr.status == SUCCESS) { // Store the system ID uint8_t systemId[DEVINFO_SYSTEM_ID_LEN]; // use 6 bytes of device address for 8 bytes of system ID value systemId[0] = pPkt->devAddr[0]; systemId[1] = pPkt->devAddr[1]; systemId[2] = pPkt->devAddr[2]; // set middle bytes to zero systemId[4] = 0x00; systemId[3] = 0x00; // shift three bytes up systemId[7] = pPkt->devAddr[5]; systemId[6] = pPkt->devAddr[4]; systemId[5] = pPkt->devAddr[3]; // Set Device Info Service Parameter DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId); ConfigureAdvertisements(); sInstance.mFlags.Set(Flags::kBLEStackInitialized); sInstance.mFlags.Set(Flags::kBLEStackAdvInitialized); /* Trigger post-initialization state update */ DriveBLEState(); if (sInstance.addrMode > ADDRMODE_RANDOM) { UpdateBLERPA(); // Create one-shot clock for RPA check event. Util_constructClock(&sInstance.clkRpaRead, ClockHandler, READ_RPA_EVT_PERIOD, 0, true, (uintptr_t) &sInstance.argRpaRead); } } break; } case GAP_LINK_ESTABLISHED_EVENT: { gapEstLinkReqEvent_t * pPkt = (gapEstLinkReqEvent_t *) pMsg; BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_LINK_ESTABLISHED_EVENT"); // Display the amount of current connections uint8_t numActive = (uint8_t) linkDB_NumActive(""); if (pPkt->hdr.status == SUCCESS) { // Add connection to list and start RSSI AddBLEConn(pPkt->connectionHandle); } DMMPolicy_updateStackState(DMMPolicy_StackRole_BlePeripheral, DMMPOLICY_BLE_HIGH_BANDWIDTH); if (numActive >= MAX_NUM_BLE_CONNS) { // Stop advertising since there is no room for more connections BLEMGR_LOG("BLEMGR: BLE event GAP_LINK_ESTABLISHED_EVENT: MAX connections"); sInstance.mFlags.Clear(Flags::kAdvertisingEnabled).Clear(Flags::kAdvertising); } /* Stop advertisement timeout timer */ Util_stopClock(&sInstance.clkAdvTimeout); // reset fast advertising sInstance.mFlags.Set(Flags::kFastAdvertisingEnabled); DriveBLEState(); break; } case GAP_LINK_TERMINATED_EVENT: { gapTerminateLinkEvent_t * pPkt = (gapTerminateLinkEvent_t *) pMsg; BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_LINK_TERMINATED_EVENT, reason: %d", pPkt->reason); // Remove the connection from the list and disable RSSI if needed RemoveBLEConn(pPkt->connectionHandle); ChipDeviceEvent event; event.Type = DeviceEventType::kCHIPoBLEConnectionError; event.CHIPoBLEConnectionError.ConId = (void *) &pPkt->connectionHandle; event.CHIPoBLEConnectionError.Reason = BLE_ERROR_REMOTE_DEVICE_DISCONNECTED; PlatformMgr().PostEventOrDie(&event); DriveBLEState(); break; } case GAP_UPDATE_LINK_PARAM_REQ_EVENT: { BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_UPDATE_LINK_PARAM_REQ_EVENT"); gapUpdateLinkParamReqReply_t rsp; gapUpdateLinkParamReqEvent_t * pReq = (gapUpdateLinkParamReqEvent_t *) pMsg; rsp.connectionHandle = pReq->req.connectionHandle; rsp.signalIdentifier = pReq->req.signalIdentifier; // Only accept connection intervals with slave latency of 0 // This is just an example of how the application can send a response if (pReq->req.connLatency == 0) { rsp.intervalMin = pReq->req.intervalMin; rsp.intervalMax = pReq->req.intervalMax; rsp.connLatency = pReq->req.connLatency; rsp.connTimeout = pReq->req.connTimeout; rsp.accepted = TRUE; BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_UPDATE_LINK_PARAM_REQ_EVENT Accecpted"); } else { rsp.accepted = FALSE; BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_UPDATE_LINK_PARAM_REQ_EVENT Rejected"); } // Send Reply VOID GAP_UpdateLinkParamReqReply(&rsp); break; } case GAP_LINK_PARAM_UPDATE_EVENT: { BLEMGR_LOG("BLEMGR: ProcessGapMessage: GAP_LINK_PARAM_UPDATE_EVENT"); gapLinkUpdateEvent_t * pPkt = (gapLinkUpdateEvent_t *) pMsg; // Get the address from the connection handle linkDBInfo_t linkInfo; linkDB_GetInfo(pPkt->connectionHandle, &linkInfo); // Check if there are any queued parameter updates ConnHandleEntry_t * connHandleEntry = (ConnHandleEntry_t *) List_get(&sInstance.paramUpdateList); if (connHandleEntry != NULL) { // Attempt to send queued update now ProcessParamUpdate(connHandleEntry->connHandle); // Free list element ICall_free(connHandleEntry); } break; } default: break; } } /********************************************************************* * @fn ProcessGATTMsg * * @brief Process GATT messages and events. * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ uint8_t BLEManagerImpl::ProcessGATTMsg(gattMsgEvent_t * pMsg) { uint8_t index; BLEMGR_LOG("BLEMGR: ProcessGATTMsg"); if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT) { // ATT request-response or indication-confirmation flow control is // The app is informed in case it wants to drop the connection. } else if (pMsg->method == ATT_MTU_UPDATED_EVENT) { index = GetBLEConnIndex(pMsg->connHandle); sInstance.connList[index].mtu = pMsg->msg.mtuEvt.MTU; BLEMGR_LOG("BLEMGR: ProcessGATTMsg, ATT_MTU_UPDATED_EVENT: %d", pMsg->msg.mtuEvt.MTU); } else if (pMsg->method == ATT_HANDLE_VALUE_CFM) { void * connHandle; ChipDeviceEvent event; ConnRec_t * activeConnObj = NULL; activeConnObj = &sInstance.connList[0]; connHandle = (void *) &activeConnObj->connHandle; event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm; event.CHIPoBLEIndicateConfirm.ConId = connHandle; PlatformMgr().PostEventOrDie(&event); BLEMGR_LOG("BLEMGR: ProcessGATTMsg, ATT_HANDLE_VALUE_CFM:"); } // Free message payload. Needed only for ATT Protocol messages GATT_bm_free(&pMsg->msg, pMsg->method); // It's safe to free the incoming message return TRUE; } /********************************************************************* * @fn ProcessAdvEvent * * @brief Process advertising event in app context * * @param pEventData */ void BLEManagerImpl::ProcessAdvEvent(GapAdvEventData_t * pEventData) { BLEMGR_LOG("BLEMGR: ProcessAdvEvent"); switch (pEventData->event) { case GAP_EVT_ADV_START_AFTER_ENABLE: { BLEMGR_LOG("BLEMGR: ProcessAdvEvent: GAP_EVT_ADV_START_AFTER_ENABLE"); if (linkDB_NumActive("") == 0) { DMMPolicy_updateStackState(DMMPolicy_StackRole_BlePeripheral, DMMPOLICY_BLE_ADV); } } break; case GAP_EVT_ADV_END_AFTER_DISABLE: { BLEMGR_LOG("BLEMGR: ProcessAdvEvent: GAP_EVT_ADV_END_AFTER_DISABLE"); } break; case GAP_EVT_ADV_START: break; case GAP_EVT_ADV_END: break; // BLE Stack has ended advertising due to connection case GAP_EVT_ADV_SET_TERMINATED: { BLEMGR_LOG("BLEMGR: ProcessAdvEvent: GAP_EVT_ADV_SET_TERMINATED"); } break; case GAP_EVT_SCAN_REQ_RECEIVED: break; case GAP_EVT_INSUFFICIENT_MEMORY: break; default: break; } // All events have associated memory to free except the insufficient memory // event if (pEventData->event != GAP_EVT_INSUFFICIENT_MEMORY) { ICall_free(pEventData->pBuf); } } /********************************************************************* * @fn ProcessParamUpdate * * @brief Process a parameters update request * * @return None */ CHIP_ERROR BLEManagerImpl::ProcessParamUpdate(uint16_t connHandle) { gapUpdateLinkParamReq_t req; uint8_t connIndex; BLEMGR_LOG("BLEMGR: ProcessParamUpdate"); req.connectionHandle = connHandle; req.connLatency = DEFAULT_DESIRED_PERIPHERAL_LATENCY; req.connTimeout = DEFAULT_DESIRED_CONN_TIMEOUT; req.intervalMin = DEFAULT_DESIRED_MIN_CONN_INTERVAL; req.intervalMax = DEFAULT_DESIRED_MAX_CONN_INTERVAL; connIndex = GetBLEConnIndex(connHandle); if (!(connIndex < MAX_NUM_BLE_CONNS)) { return CHIP_ERROR_TOO_MANY_CONNECTIONS; } // Deconstruct the clock object ClockP_destruct(sInstance.connList[connIndex].pUpdateClock); // Free clock struct if (sInstance.connList[connIndex].pUpdateClock != NULL) { ICall_free(sInstance.connList[connIndex].pUpdateClock); sInstance.connList[connIndex].pUpdateClock = NULL; } // Free ParamUpdateEventData if (sInstance.connList[connIndex].pParamUpdateEventData != NULL) { ICall_free(sInstance.connList[connIndex].pParamUpdateEventData); } BLEMGR_LOG("BLEMGR: ProcessParamUpdate Send Link Param Req"); // Send parameter update bStatus_t status = GAP_UpdateLinkParamReq(&req); // If there is an ongoing update, queue this for when the udpate completes if (status == bleAlreadyInRequestedMode) { BLEMGR_LOG("BLEMGR: ProcessParamUpdate pending"); ConnHandleEntry_t * connHandleEntry = (ConnHandleEntry_t *) (ICall_malloc(sizeof(ConnHandleEntry_t))); if (connHandleEntry) { connHandleEntry->connHandle = connHandle; List_put(&sInstance.paramUpdateList, (List_Elem *) connHandleEntry); } } return CHIP_NO_ERROR; } /********************************************************************* * @fn EnqueueEvtHdrMsg * * @brief Creates a message and puts the message in RTOS queue. * * @param event - message event. * @param state - message state. */ status_t BLEManagerImpl::EnqueueEvtHdrMsg(uint8_t event, void * pData) { uint8_t success; if (sInstance.mFlags.Has(Flags::kBLEStackInitialized)) { QueuedEvt_t * pMsg = (QueuedEvt_t *) ICall_malloc(sizeof(QueuedEvt_t)); // Create dynamic pointer to message. if (pMsg) { pMsg->event = event; pMsg->pData = pData; // Enqueue the message. success = Util_enqueueMsg(sEventHandlerMsgQueueID, BLEManagerImpl::sSyncEvent, (uint8_t *) pMsg); return (success) ? SUCCESS : FAILURE; } return bleMemAllocError; } else { return true; } } /********************************************************************* * @fn AddBLEConn * * @brief Add a device to the connected device list * * @return index of the connected device list entry where the new connection * info is put in. * if there is no room, MAX_NUM_BLE_CONNS will be returned. */ uint8_t BLEManagerImpl::AddBLEConn(uint16_t connHandle) { uint8_t i; uint8_t status = bleNoResources; BLEMGR_LOG("BLEMGR: AddBLEConn"); // Try to find an available entry for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if (sInstance.connList[i].connHandle == LL_CONNHANDLE_INVALID) { // Found available entry to put a new connection info in sInstance.connList[i].connHandle = connHandle; // Allocate data to send through clock handler sInstance.connList[i].pParamUpdateEventData = (ClockEventData_t *) ICall_malloc(sizeof(ClockEventData_t) + sizeof(uint16_t)); if (sInstance.connList[i].pParamUpdateEventData) { sInstance.connList[i].pParamUpdateEventData->event = SEND_PARAM_UPDATE_EVT; *((uint16_t *) sInstance.connList[i].pParamUpdateEventData->data) = connHandle; // Create a clock object and start sInstance.connList[i].pUpdateClock = (ClockP_Struct *) ICall_malloc(sizeof(ClockP_Struct)); if (sInstance.connList[i].pUpdateClock) { Util_constructClock(sInstance.connList[i].pUpdateClock, ClockHandler, SEND_PARAM_UPDATE_DELAY, 0, true, (uintptr_t) sInstance.connList[i].pParamUpdateEventData); } else { ICall_free(sInstance.connList[i].pParamUpdateEventData); } } else { status = bleMemAllocError; } break; } } return status; } /********************************************************************* * @fn RemoveBLEConn * * @brief Remove a device from the connected device list * * @return index of the connected device list entry where the new connection * info is removed from. * if connHandle is not found, MAX_NUM_BLE_CONNS will be returned. */ uint8_t BLEManagerImpl::RemoveBLEConn(uint16_t connHandle) { uint8_t connIndex = GetBLEConnIndex(connHandle); BLEMGR_LOG("BLEMGR: RemoveBLEConn"); if (connIndex != MAX_NUM_BLE_CONNS) { ClockP_Struct * pUpdateClock = sInstance.connList[connIndex].pUpdateClock; if (pUpdateClock != NULL) { // Stop and destruct the RTOS clock if it's still alive if (Util_isActive(pUpdateClock)) { Util_stopClock(pUpdateClock); } // Destruct the clock object ClockP_destruct(pUpdateClock); // Free clock struct ICall_free(pUpdateClock); // Free ParamUpdateEventData ICall_free(sInstance.connList[connIndex].pParamUpdateEventData); } // Clear pending update requests from paramUpdateList ClearPendingBLEParamUpdate(connHandle); // Clear Connection List Entry ClearBLEConnListEntry(connHandle); } return connIndex; } /********************************************************************* * @fn GetBLEConnIndex * * @brief Find index in the connected device list by connHandle * * @return the index of the entry that has the given connection handle. * if there is no match, MAX_NUM_BLE_CONNS will be returned. */ uint8_t BLEManagerImpl::GetBLEConnIndex(uint16_t connHandle) const { uint8_t i; BLEMGR_LOG("BLEMGR: GetBLEConnIndex"); for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if (sInstance.connList[i].connHandle == connHandle) { return i; } } return MAX_NUM_BLE_CONNS; } /********************************************************************* * @fn ClearBLEConnListEntry * * @brief Find index in the connected device list by connHandle * * @return the index of the entry that has the given connection handle. * if there is no match, MAX_NUM_BLE_CONNS will be returned. */ uint8_t BLEManagerImpl::ClearBLEConnListEntry(uint16_t connHandle) { uint8_t i; // Set to invalid connection index initially uint8_t connIndex = MAX_NUM_BLE_CONNS; BLEMGR_LOG("BLEMGR: ClearBLEConnListEntry"); if (connHandle != LL_CONNHANDLE_ALL) { // Get connection index from handle connIndex = GetBLEConnIndex(connHandle); if (connIndex >= MAX_NUM_BLE_CONNS) { return bleInvalidRange; } } // Clear specific handle or all handles for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if ((connIndex == i) || (connHandle == LL_CONNHANDLE_ALL)) { sInstance.connList[i].connHandle = LL_CONNHANDLE_INVALID; sInstance.connList[i].currPhy = 0; sInstance.connList[i].phyCngRq = 0; sInstance.connList[i].phyRqFailCnt = 0; sInstance.connList[i].rqPhy = 0; memset(sInstance.connList[i].rssiArr, 0, MAX_RSSI_STORE_DEPTH); sInstance.connList[i].rssiAvg = 0; sInstance.connList[i].rssiCntr = 0; sInstance.connList[i].isAutoPHYEnable = FALSE; sInstance.connList[i].mtu = 0; } } return SUCCESS; } /********************************************************************* * @fn ClearPendingBLEParamUpdate * * @brief clean pending param update request in the paramUpdateList list * * @param connHandle - connection handle to clean * * @return none */ void BLEManagerImpl::ClearPendingBLEParamUpdate(uint16_t connHandle) { List_Elem * curr; BLEMGR_LOG("BLEMGR: ClearPendingBLEParamUpdate"); for (curr = List_head(&sInstance.paramUpdateList); curr != NULL; curr = List_next(curr)) { if (((ConnHandleEntry_t *) curr)->connHandle == connHandle) { List_remove(&sInstance.paramUpdateList, curr); } } } /********************************************************************* * @fn UpdateBLERPA * * @brief Read the current RPA from the stack and update display * if the RPA has changed. * * @param None. * * @return None. */ void BLEManagerImpl::UpdateBLERPA(void) { uint8_t * pRpaNew; // Read the current RPA. pRpaNew = GAP_GetDevAddress(FALSE); if (memcmp(pRpaNew, sInstance.rpa, B_ADDR_LEN)) { memcpy(sInstance.rpa, pRpaNew, B_ADDR_LEN); } } void BLEManagerImpl::EventHandler(void * arg) { PlatformMgr().LockChipStack(); sInstance.EventHandler_init(); PlatformMgr().UnlockChipStack(); for (;;) { uint32_t events; // Waits for an event to be posted associated with the calling thread. // Note that an event associated with a thread is posted when a // message is queued to the message receive queue of the thread xQueueReceive((QueueHandle_t) BLEManagerImpl::sSyncEvent, (char *) &events, portMAX_DELAY); if (events) { ICall_EntityID dest; ICall_ServiceEnum src; ICall_HciExtEvt * hcipMsg = NULL; /* Lock CHIP Stack while processing BLE Stack/App events */ PlatformMgr().LockChipStack(); // Fetch any available messages that might have been sent from the stack if (ICall_fetchServiceMsg(&src, &dest, (void **) &hcipMsg) == ICALL_ERRNO_SUCCESS) { uint8 safeToDealloc = TRUE; if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == BLEManagerImpl::sSelfEntity)) { ICall_Stack_Event * pEvt = (ICall_Stack_Event *) hcipMsg; // Check for non-BLE stack events if (pEvt->signature != 0xffff) { // Process inter-task message safeToDealloc = sInstance.ProcessStackEvent((ICall_Hdr *) hcipMsg); } } if (hcipMsg && safeToDealloc) { ICall_freeMsg(hcipMsg); } } // If RTOS queue is not empty, process CHIP messages. if (events & QUEUE_EVT) { QueuedEvt_t * pMsg; for (;;) { pMsg = (QueuedEvt_t *) Util_dequeueMsg(BLEManagerImpl::sEventHandlerMsgQueueID); if (NULL != pMsg) { // Process message. sInstance.ProcessEvtHdrMsg(pMsg); // Free the space from the message. ICall_free(pMsg); } else { break; } } } PlatformMgr().UnlockChipStack(); } } } /* Post event to app processing loop to begin CHIP advertising */ CHIP_ERROR BLEManagerImpl::DriveBLEState(void) { CHIP_ERROR err = CHIP_NO_ERROR; BLEMGR_LOG("BLEMGR: DriveBLEState"); if (sInstance.EnqueueEvtHdrMsg(BLEManagerIMPL_STATE_UPDATE_EVT, NULL) != SUCCESS) { err = CHIP_ERROR_NO_MEMORY; } return err; } /********************************************************************* * @fn advCallback * * @brief GapAdv module callback * * @param pMsg - message to process */ void BLEManagerImpl::advCallback(uint32_t event, void * pBuf, uintptr_t arg) { BLEMGR_LOG("BLEMGR: advCallback"); GapAdvEventData_t * pData = (GapAdvEventData_t *) ICall_malloc(sizeof(GapAdvEventData_t)); if (pData) { pData->event = event; pData->pBuf = pBuf; if (sInstance.EnqueueEvtHdrMsg(ADV_EVT, pData) != SUCCESS) { ICall_free(pData); } } } void BLEManagerImpl::AdvTimeoutHandler(uintptr_t arg) { BLEMGR_LOG("BLEMGR: AdvTimeoutHandler"); if (sInstance.mFlags.Has(Flags::kAdvertisingEnabled)) { if (sInstance.mFlags.Has(Flags::kFastAdvertisingEnabled)) { BLEMGR_LOG("BLEMGR: Fast advertising timeout reached"); sInstance.mFlags.Clear(Flags::kFastAdvertisingEnabled); sInstance.mFlags.Set(Flags::kAdvertisingRefreshNeeded); } else { BLEMGR_LOG("BLEMGR: Advertising timeout reached"); sInstance.mFlags.Clear(Flags::kAdvertisingEnabled); } /* Send event to process state change request */ DriveBLEState(); } } void BLEManagerImpl::ClockHandler(uintptr_t arg) { ClockEventData_t * pData = (ClockEventData_t *) arg; if (pData->event == READ_RPA_EVT) { // Start the next period Util_startClock(&sInstance.clkRpaRead); // Post event to read the current RPA sInstance.EnqueueEvtHdrMsg(READ_RPA_EVT, NULL); } else if (pData->event == SEND_PARAM_UPDATE_EVT) { // Send message to app if (sInstance.EnqueueEvtHdrMsg(SEND_PARAM_UPDATE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /********************************************************************* * @fn CHIPoBLEProfile_charValueChangeCB * * @brief Callback from CHIPoBLE Profile indicating a characteristic * value change. * Calling context (BLE Stack Task) * * @param paramId - parameter Id of the value that was changed. * * @return None. */ void BLEManagerImpl::CHIPoBLEProfile_charValueChangeCB(uint8_t paramId, uint16_t len, uint16_t connHandle) { CHIPoBLEProfChgEvt_t * pValue = (CHIPoBLEProfChgEvt_t *) ICall_malloc(sizeof(CHIPoBLEProfChgEvt_t)); BLEMGR_LOG("BLEMGR: CHIPoBLEProfile_charValueChangeCB"); if (pValue) { pValue->paramId = paramId; pValue->len = len; pValue->connHandle = connHandle; if (sInstance.EnqueueEvtHdrMsg(CHIPOBLE_CHAR_CHANGE_EVT, pValue) != SUCCESS) { ICall_free(pValue); } } } /********************************************************************* * @fn RemoteDisplay_passcodeCb * * @brief Passcode callback. * * @return none */ void BLEManagerImpl::PasscodeCb(uint8_t * pDeviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison) { PasscodeData_t * pData = (PasscodeData_t *) ICall_malloc(sizeof(PasscodeData_t)); // Allocate space for the passcode event. if (pData) { pData->connHandle = connHandle; memcpy(pData->deviceAddr, pDeviceAddr, B_ADDR_LEN); pData->uiInputs = uiInputs; pData->uiOutputs = uiOutputs; pData->numComparison = numComparison; // Enqueue the event. if (sInstance.EnqueueEvtHdrMsg(PASSCODE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /********************************************************************* * @fn PairStateCb * * @brief Pairing state callback. * * @return none */ void BLEManagerImpl::PairStateCb(uint16_t connHandle, uint8_t state, uint8_t status) { PairStateData_t * pData = (PairStateData_t *) ICall_malloc(sizeof(PairStateData_t)); // Allocate space for the event data. if (pData) { pData->state = state; pData->connHandle = connHandle; pData->status = status; // Queue the event. if (sInstance.EnqueueEvtHdrMsg(PAIR_STATE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /******************************************************************************* * @fn AssertHandler * * @brief This is the Application's callback handler for asserts raised * in the stack. When EXT_HAL_ASSERT is defined in the Stack * project this function will be called when an assert is raised, * and can be used to observe or trap a violation from expected * behavior. * * As an example, for Heap allocation failures the Stack will raise * HAL_ASSERT_CAUSE_OUT_OF_MEMORY as the assertCause and * HAL_ASSERT_SUBCAUSE_NONE as the assertSubcause. An application * developer could trap any malloc failure on the stack by calling * HAL_ASSERT_SPINLOCK under the matching case. * * An application developer is encouraged to extend this function * for use by their own application. To do this, add hal_assert.c * to your project workspace, the path to hal_assert.h (this can * be found on the stack side). Asserts are raised by including * hal_assert.h and using macro HAL_ASSERT(cause) to raise an * assert with argument assertCause. the assertSubcause may be * optionally set by macro HAL_ASSERT_SET_SUBCAUSE(subCause) prior * to asserting the cause it describes. More information is * available in hal_assert.h. * * input parameters * * @param assertCause - Assert cause as defined in hal_assert.h. * @param assertSubcause - Optional assert subcause (see hal_assert.h). * * output parameters * * @param None. * * @return None. */ void BLEManagerImpl::AssertHandler(uint8 assertCause, uint8 assertSubcause) { // check the assert cause switch (assertCause) { case HAL_ASSERT_CAUSE_OUT_OF_MEMORY: assert(false); break; case HAL_ASSERT_CAUSE_INTERNAL_ERROR: // check the subcause if (assertSubcause == HAL_ASSERT_SUBCAUSE_FW_INERNAL_ERROR) { assert(false); } else { assert(false); } break; case HAL_ASSERT_CAUSE_ICALL_ABORT: assert(false); case HAL_ASSERT_CAUSE_ICALL_TIMEOUT: assert(false); break; case HAL_ASSERT_CAUSE_WRONG_API_CALL: assert(false); break; default: assert(false); break; } return; } } // namespace Internal } // namespace DeviceLayer } // namespace chip #endif /* CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE */