/* * * Copyright (c) 2020-2021 Project CHIP Authors * * 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 singleton object * for mbed platforms. */ #include #include #include #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE #include #include #include #include #include #include #include // Show BLE status with LEDs #define _BLEMGRIMPL_USE_LEDS 0 // mbed-os headers #include "ble/BLE.h" #include "ble/Gap.h" #include "platform/Callback.h" #include "platform/NonCopyable.h" #include "platform/Span.h" #if _BLEMGRIMPL_USE_LEDS #include "drivers/DigitalOut.h" #endif using namespace ::chip; using namespace ::chip::Ble; using namespace ::chip::System; namespace chip { namespace DeviceLayer { namespace Internal { namespace { const UUID ShortUUID_CHIPoBLEService(0xFFF6); // RX = BleLayer::CHIP_BLE_CHAR_1_ID const UUID LongUUID_CHIPoBLEChar_RX("18EE2EF5-263D-4559-959F-4F9C429F9D11"); // TX = BleLayer::CHIP_BLE_CHAR_2_ID const UUID LongUUID_CHIPoBLEChar_TX("18EE2EF5-263D-4559-959F-4F9C429F9D12"); } // namespace #if _BLEMGRIMPL_USE_LEDS #include "drivers/DigitalOut.h" // LED1 -- toggle on every call to ble::BLE::processEvents() // LED2 -- on when ble::BLE::init() callback completes // LED3 -- on when advertising mbed::DigitalOut led1(LED1, 1); mbed::DigitalOut led2(LED2, 1); mbed::DigitalOut led3(LED3, 1); #endif class ConnectionInfo { public: struct ConnStatus { ble::connection_handle_t connHandle; uint16_t attMtuSize; }; ConnectionInfo() { for (size_t i = 0; i < kMaxConnections; i++) { mConnStates[i].connHandle = kInvalidHandle; mConnStates[i].attMtuSize = 0; } } CHIP_ERROR setStatus(ble::connection_handle_t conn_handle, uint16_t mtu_size) { size_t new_i = kMaxConnections; for (size_t i = 0; i < kMaxConnections; i++) { if (mConnStates[i].connHandle == conn_handle) { mConnStates[i].attMtuSize = mtu_size; return CHIP_NO_ERROR; } else if (mConnStates[i].connHandle == kInvalidHandle && i < new_i) { new_i = i; } } // Handle not found, has to be added. if (new_i == kMaxConnections) { return CHIP_ERROR_NO_MEMORY; } mConnStates[new_i].connHandle = conn_handle; mConnStates[new_i].attMtuSize = mtu_size; return CHIP_NO_ERROR; } CHIP_ERROR clearStatus(ble::connection_handle_t conn_handle) { for (size_t i = 0; i < kMaxConnections; i++) { if (mConnStates[i].connHandle == conn_handle) { mConnStates[i].connHandle = kInvalidHandle; mConnStates[i].attMtuSize = 0; return CHIP_NO_ERROR; } } return CHIP_ERROR_INVALID_ARGUMENT; } ConnStatus getStatus(ble::connection_handle_t conn_handle) const { for (size_t i = 0; i < kMaxConnections; i++) { if (mConnStates[i].connHandle == conn_handle) { return mConnStates[i]; } } return { kInvalidHandle, 0 }; } uint16_t getMTU(ble::connection_handle_t conn_handle) const { return getStatus(conn_handle).attMtuSize; } private: const size_t kMaxConnections = BLE_LAYER_NUM_BLE_ENDPOINTS; ConnStatus mConnStates[BLE_LAYER_NUM_BLE_ENDPOINTS]; const ble::connection_handle_t kInvalidHandle = 0xf00d; }; static ConnectionInfo sConnectionInfo; #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" class GapEventHandler : private mbed::NonCopyable, public ble::Gap::EventHandler { void onScanRequestReceived(const ble::ScanRequestEvent & event) { // Requires enable action from setScanRequestNotification(true). ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } /* Called when advertising starts. */ void onAdvertisingStart(const ble::AdvertisingStartEvent & event) { #if _BLEMGRIMPL_USE_LEDS led3 = 0; #endif ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); BLEManagerImpl & ble_manager = BLEMgrImpl(); ble_manager.mFlags.Set(ble_manager.kFlag_Advertising); ble_manager.mFlags.Clear(ble_manager.kFlag_AdvertisingRefreshNeeded); // Post a CHIPoBLEAdvertisingChange(Started) event. ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLEAdvertisingChange; chip_event.CHIPoBLEAdvertisingChange.Result = kActivity_Started; PlatformMgrImpl().PostEventOrDie(&chip_event); PlatformMgr().ScheduleWork(ble_manager.DriveBLEState, 0); } /* Called when advertising ends. * * Advertising ends when the process timeout or if it is stopped by the * application or if the local device accepts a connection request. */ void onAdvertisingEnd(const ble::AdvertisingEndEvent & event) { #if _BLEMGRIMPL_USE_LEDS led3 = 1; #endif ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); BLEManagerImpl & ble_manager = BLEMgrImpl(); ble_manager.mFlags.Clear(ble_manager.kFlag_Advertising); // Post a CHIPoBLEAdvertisingChange(Stopped) event. ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLEAdvertisingChange; chip_event.CHIPoBLEAdvertisingChange.Result = kActivity_Stopped; PlatformMgrImpl().PostEventOrDie(&chip_event); if (event.isConnected()) { ble_manager.mFlags.Set(ble_manager.kFlag_AdvertisingRefreshNeeded); ChipLogDetail(DeviceLayer, "Restarting advertising to allow more connections."); } PlatformMgr().ScheduleWork(ble_manager.DriveBLEState, 0); } /* Called when connection attempt ends or an advertising device has been * connected. */ void onConnectionComplete(const ble::ConnectionCompleteEvent & event) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); ble_error_t mbed_err = event.getStatus(); BLEManagerImpl & ble_manager = BLEMgrImpl(); if (mbed_err == BLE_ERROR_NONE) { const ble::address_t & peer_addr = event.getPeerAddress(); ChipLogProgress(DeviceLayer, "BLE connection established with %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", peer_addr[5], peer_addr[4], peer_addr[3], peer_addr[2], peer_addr[1], peer_addr[0]); ble_manager.mGAPConns++; CHIP_ERROR err = sConnectionInfo.setStatus(event.getConnectionHandle(), BLE_GATT_MTU_SIZE_DEFAULT); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Unable to store connection status, error: %s ", ErrorStr(err)); } } else { ChipLogError(DeviceLayer, "BLE connection failed, mbed-os error: %d", mbed_err); } ChipLogProgress(DeviceLayer, "Current number of connections: %u/%d", ble_manager.NumConnections(), ble_manager.kMaxConnections); // The connection established event is propagated when the client has subscribed to // the TX characteristic. } void onUpdateConnectionParametersRequest(const ble::UpdateConnectionParametersRequestEvent & event) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } void onConnectionParametersUpdateComplete(const ble::ConnectionParametersUpdateCompleteEvent & event) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } void onReadPhy(ble_error_t status, ble::connection_handle_t connectionHandle, ble::phy_t txPhy, ble::phy_t rxPhy) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } void onPhyUpdateComplete(ble_error_t status, ble::connection_handle_t connectionHandle, ble::phy_t txPhy, ble::phy_t rxPhy) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } /* Called when a connection has been disconnected. */ void onDisconnectionComplete(const ble::DisconnectionCompleteEvent & event) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); const ble::disconnection_reason_t & reason = event.getReason(); BLEManagerImpl & ble_manager = BLEMgrImpl(); if (ble_manager.NumConnections()) { ble_manager.mGAPConns--; } CHIP_ERROR err = sConnectionInfo.clearStatus(event.getConnectionHandle()); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Unable to clear connection status, error: %s ", ErrorStr(err)); } ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLEConnectionError; chip_event.CHIPoBLEConnectionError.ConId = event.getConnectionHandle(); switch (reason.value()) { case ble::disconnection_reason_t::REMOTE_USER_TERMINATED_CONNECTION: chip_event.CHIPoBLEConnectionError.Reason = BLE_ERROR_REMOTE_DEVICE_DISCONNECTED; break; case ble::disconnection_reason_t::LOCAL_HOST_TERMINATED_CONNECTION: chip_event.CHIPoBLEConnectionError.Reason = BLE_ERROR_APP_CLOSED_CONNECTION; break; default: chip_event.CHIPoBLEConnectionError.Reason = BLE_ERROR_CHIPOBLE_PROTOCOL_ABORT; break; } PlatformMgrImpl().PostEventOrDie(&chip_event); ChipLogProgress(DeviceLayer, "BLE connection terminated, mbed-os reason: %d", reason.value()); ChipLogProgress(DeviceLayer, "Current number of connections: %u/%d", ble_manager.NumConnections(), ble_manager.kMaxConnections); // Force a reconfiguration of advertising in case we switched to non-connectable mode when // the BLE connection was established. ble_manager.mFlags.Set(ble_manager.kFlag_AdvertisingRefreshNeeded); PlatformMgr().ScheduleWork(ble_manager.DriveBLEState, 0); } void onDataLengthChange(ble::connection_handle_t connectionHandle, uint16_t txSize, uint16_t rxSize) { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } void onPrivacyEnabled() { ChipLogDetail(DeviceLayer, "GAP %s", __FUNCTION__); } }; #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" struct CHIPService : public ble::GattServer::EventHandler { CHIPService() {} CHIPService(const CHIPService &) = delete; CHIPService & operator=(const CHIPService &) = delete; CHIP_ERROR init(ble::BLE & ble_interface) { ChipLogDetail(DeviceLayer, "GATT %s", __FUNCTION__); if (mCHIPoBLEChar_RX != nullptr || mCHIPoBLEChar_TX != nullptr) { return CHIP_NO_ERROR; } mCHIPoBLEChar_RX = new GattCharacteristic(LongUUID_CHIPoBLEChar_RX, nullptr, 0, BLE_GATT_MTU_SIZE_DEFAULT, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); mCHIPoBLEChar_TX = new GattCharacteristic(LongUUID_CHIPoBLEChar_TX, nullptr, 0, BLE_GATT_MTU_SIZE_DEFAULT, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Setup callback mCHIPoBLEChar_RX->setWriteAuthorizationCallback(this, &CHIPService::onWriteAuth); GattCharacteristic * chipoble_gatt_characteristics[] = { mCHIPoBLEChar_RX, mCHIPoBLEChar_TX }; auto num_characteristics = sizeof chipoble_gatt_characteristics / sizeof chipoble_gatt_characteristics[0]; GattService chipoble_gatt_service(ShortUUID_CHIPoBLEService, chipoble_gatt_characteristics, num_characteristics); auto mbed_err = ble_interface.gattServer().addService(chipoble_gatt_service); if (mbed_err != BLE_ERROR_NONE) { ChipLogError(DeviceLayer, "Unable to add GATT service, mbed-os err: %d", mbed_err); return CHIP_ERROR_INTERNAL; } // Store the attribute handles in the class so they are reused in // callbacks to discriminate events. mRxHandle = mCHIPoBLEChar_RX->getValueHandle(); mTxHandle = mCHIPoBLEChar_TX->getValueHandle(); // There is a single descriptor in the characteristic, CCCD is at index 0 mTxCCCDHandle = mCHIPoBLEChar_TX->getDescriptor(0)->getHandle(); ChipLogDetail(DeviceLayer, "char handles: rx=%d, tx=%d, cccd=%d", mRxHandle, mTxHandle, mTxCCCDHandle); ble_interface.gattServer().setEventHandler(this); return CHIP_NO_ERROR; } // Write authorization callback void onWriteAuth(GattWriteAuthCallbackParams * params) { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params->connHandle, params->handle); if (params->handle == mRxHandle) { ChipLogDetail(DeviceLayer, "Received BLE packet on RX"); // Allocate a buffer, copy the data. They will be passed into the event auto buf = System::PacketBufferHandle::NewWithData(params->data, params->len); if (buf.IsNull()) { params->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_REQUEST_REJECTED; ChipLogError(DeviceLayer, "Dropping packet, not enough memory"); return; } params->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLEWriteReceived; chip_event.CHIPoBLEWriteReceived.ConId = params->connHandle; chip_event.CHIPoBLEWriteReceived.Data = std::move(buf).UnsafeRelease(); PlatformMgrImpl().PostEventOrDie(&chip_event); } else { params->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_HANDLE; } } // overrides of GattServerEvent Handler void onAttMtuChange(ble::connection_handle_t connectionHandle, uint16_t attMtuSize) override { ChipLogDetail(DeviceLayer, "GATT %s", __FUNCTION__); CHIP_ERROR err = sConnectionInfo.setStatus(connectionHandle, attMtuSize); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Unable to store connection status, error: %s ", ErrorStr(err)); } } void onDataSent(const GattDataSentCallbackParams & params) override { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params.connHandle, params.attHandle); // Note: This is applicable to both notification and indication: If a // notification is sent then onDataSent is called as soon as the data // has been pushed into the Bluetooth controller. For indication, onDataSent // is called when the confirmation has been received. onConfirmationReceived(params); } void onDataWritten(const GattWriteCallbackParams & params) override { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params.connHandle, params.handle); } void onDataRead(const GattReadCallbackParams & params) override { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params.connHandle, params.handle); } void onShutdown(const ble::GattServer & server) override { ChipLogDetail(DeviceLayer, "GATT %s", __FUNCTION__); } void onUpdatesEnabled(const GattUpdatesEnabledCallbackParams & params) override { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params.connHandle, params.attHandle); if (params.attHandle == mTxCCCDHandle) { ChipLogDetail(DeviceLayer, "Updates enabled on TX CCCD"); ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLESubscribe; chip_event.CHIPoBLESubscribe.ConId = params.connHandle; PlatformMgrImpl().PostEventOrDie(&chip_event); } } void onUpdatesDisabled(const GattUpdatesDisabledCallbackParams & params) override { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params.connHandle, params.attHandle); if (params.attHandle == mTxCCCDHandle) { ChipLogDetail(DeviceLayer, "Updates disabled on TX CCCD"); ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLEUnsubscribe; chip_event.CHIPoBLEUnsubscribe.ConId = params.connHandle; PlatformMgrImpl().PostEventOrDie(&chip_event); } } void onConfirmationReceived(const GattConfirmationReceivedCallbackParams & params) override { ChipLogDetail(DeviceLayer, "GATT %s, connHandle=%d, attHandle=%d", __FUNCTION__, params.connHandle, params.attHandle); if (params.attHandle == mTxHandle) { ChipLogDetail(DeviceLayer, "Confirmation received for TX transfer"); ChipDeviceEvent chip_event; chip_event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm; chip_event.CHIPoBLEIndicateConfirm.ConId = params.connHandle; PlatformMgrImpl().PostEventOrDie(&chip_event); } } ble::attribute_handle_t getTxHandle() const { return mTxHandle; } ble::attribute_handle_t getTxCCCDHandle() const { return mTxCCCDHandle; } ble::attribute_handle_t getRxHandle() const { return mRxHandle; } GattCharacteristic * mCHIPoBLEChar_RX = nullptr; GattCharacteristic * mCHIPoBLEChar_TX = nullptr; ble::attribute_handle_t mRxHandle = 0; ble::attribute_handle_t mTxCCCDHandle = 0; ble::attribute_handle_t mTxHandle = 0; }; #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" class SecurityManagerEventHandler : private mbed::NonCopyable, public ble::SecurityManager::EventHandler { void pairingRequest(ble::connection_handle_t connectionHandle) override { ChipLogDetail(DeviceLayer, "SM %s, connHandle=%d", __FUNCTION__, connectionHandle); ble::SecurityManager & security_mgr = ble::BLE::Instance().securityManager(); auto mbed_err = security_mgr.acceptPairingRequest(connectionHandle); if (mbed_err == BLE_ERROR_NONE) { ChipLogProgress(DeviceLayer, "Pairing request authorized."); } else { ChipLogError(DeviceLayer, "Pairing request not authorized, mbed-os err: %d", mbed_err); } } void pairingResult(ble::connection_handle_t connectionHandle, SecurityManager::SecurityCompletionStatus_t result) override { ChipLogDetail(DeviceLayer, "SM %s, connHandle=%d", __FUNCTION__, connectionHandle); if (result == SecurityManager::SEC_STATUS_SUCCESS) { ChipLogProgress(DeviceLayer, "Pairing successful."); } else { ChipLogError(DeviceLayer, "Pairing failed, status: 0x%X.", result); } } void linkEncryptionResult(ble::connection_handle_t connectionHandle, ble::link_encryption_t result) override { ChipLogDetail(DeviceLayer, "SM %s, connHandle=%d", __FUNCTION__, connectionHandle); if (result == ble::link_encryption_t::NOT_ENCRYPTED) { ChipLogDetail(DeviceLayer, "Link NOT_ENCRYPTED."); } else if (result == ble::link_encryption_t::ENCRYPTION_IN_PROGRESS) { ChipLogDetail(DeviceLayer, "Link ENCRYPTION_IN_PROGRESS."); } else if (result == ble::link_encryption_t::ENCRYPTED) { ChipLogDetail(DeviceLayer, "Link ENCRYPTED."); } else if (result == ble::link_encryption_t::ENCRYPTED_WITH_MITM) { ChipLogDetail(DeviceLayer, "Link ENCRYPTED_WITH_MITM."); } else if (result == ble::link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM) { ChipLogDetail(DeviceLayer, "Link ENCRYPTED_WITH_SC_AND_MITM."); } else { ChipLogDetail(DeviceLayer, "Link encryption status UNKNOWN."); } } }; BLEManagerImpl BLEManagerImpl::sInstance; static GapEventHandler sMbedGapEventHandler; static CHIPService sCHIPService; static SecurityManagerEventHandler sSecurityManagerEventHandler; /* Initialize the mbed-os BLE subsystem. Register the BLE event processing * callback to the system event queue. Register the BLE initialization complete * callback that handles the rest of the setup commands. Register the BLE GAP * event handler. */ CHIP_ERROR BLEManagerImpl::_Init() { ble_error_t mbed_err = BLE_ERROR_NONE; mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; mFlags = BitFlags(CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART ? kFlag_AdvertisingEnabled : 0); mGAPConns = 0; if (!mInitialized) { ble::BLE & ble_interface = ble::BLE::Instance(); ble_interface.gap().setEventHandler(&sMbedGapEventHandler); ReturnErrorOnFailure(sCHIPService.init(ble_interface)); ble_interface.onEventsToProcess(FunctionPointerWithContext{ [](ble::BLE::OnEventsToProcessCallbackContext * context) { PlatformMgr().ScheduleWork(DoBLEProcessing, 0); } }); mbed_err = ble_interface.init([](ble::BLE::InitializationCompleteCallbackContext * context) { BLEMgrImpl().HandleInitComplete(context->error == BLE_ERROR_NONE); }); VerifyOrReturnError(mbed_err == BLE_ERROR_NONE, CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); mInitialized = true; } return CHIP_NO_ERROR; } /* Process all the events from the mbed-os BLE subsystem. */ void BLEManagerImpl::DoBLEProcessing(intptr_t arg) { #if _BLEMGRIMPL_USE_LEDS led1 = !led1; #endif ble::BLE::Instance().processEvents(); } /* This is the mbed-os BLE subsystem init complete callback. Initialize the * BLELayer and update the state based on the flags. */ void BLEManagerImpl::HandleInitComplete(bool no_error) { CHIP_ERROR err = CHIP_NO_ERROR; ble_error_t mbed_err = BLE_ERROR_NONE; ble::Gap & gap = ble::BLE::Instance().gap(); ble::SecurityManager & security_mgr = ble::BLE::Instance().securityManager(); ble::own_address_type_t addr_type; ble::address_t addr; VerifyOrExit(no_error, err = CHIP_ERROR_INTERNAL); gap.getAddress(addr_type, addr); ChipLogDetail(DeviceLayer, "Device address: %02X:%02X:%02X:%02X:%02X:%02X", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); mbed_err = security_mgr.init( /*bool enableBonding */ false, /*bool requireMITM */ true, /*SecurityIOCapabilities_t iocaps*/ SecurityManager::IO_CAPS_NONE, /*const Passkey_t passkey */ nullptr, /*bool signing */ true, /*const char *dbFilepath */ nullptr); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); mbed_err = security_mgr.setPairingRequestAuthorisation(true); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); security_mgr.setSecurityManagerEventHandler(&sSecurityManagerEventHandler); err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); SuccessOrExit(err); PlatformMgr().ScheduleWork(DriveBLEState, 0); #if _BLEMGRIMPL_USE_LEDS led2 = 0; #endif exit: if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "BLEManager init error: %s ", ErrorStr(err)); ChipLogError(DeviceLayer, "Disabling CHIPoBLE service."); mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; mInitialized = false; PlatformMgr().ScheduleWork(DriveBLEState, 0); } } void BLEManagerImpl::DriveBLEState(intptr_t arg) { BLEMgrImpl().DriveBLEState(); } /* Update the advertising state based on the flags. */ void BLEManagerImpl::DriveBLEState() { CHIP_ERROR err = CHIP_NO_ERROR; // Perform any initialization actions that must occur after the CHIP task is running. if (!mFlags.Has(kFlag_AsyncInitCompleted)) { mFlags.Set(kFlag_AsyncInitCompleted); } // If the application has enabled CHIPoBLE and BLE advertising... if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && mFlags.Has(kFlag_AdvertisingEnabled) #if CHIP_DEVICE_CONFIG_CHIPOBLE_SINGLE_CONNECTION // and no connections are active... && (_NumConnections() == 0) #endif ) { // Start/re-start advertising if not already advertising, or if the // advertising state needs to be refreshed. if (!mFlags.Has(kFlag_Advertising) || mFlags.Has(kFlag_AdvertisingRefreshNeeded)) { err = StartAdvertising(); SuccessOrExit(err); } } // Otherwise, stop advertising if currently active. else { err = StopAdvertising(); SuccessOrExit(err); } exit: if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; } } CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode) { switch (mode) { case BLEAdvertisingMode::kFastAdvertising: mFlags.Set(Flags::kFlag_FastAdvertisingEnabled, true); break; case BLEAdvertisingMode::kSlowAdvertising: mFlags.Set(Flags::kFlag_AdvertisingEnabled, false); break; default: return CHIP_ERROR_INVALID_ARGUMENT; } mFlags.Set(Flags::kFlag_AdvertisingRefreshNeeded); PlatformMgr().ScheduleWork(DriveBLEState, 0); return CHIP_NO_ERROR; } /* Build the advertising data and start advertising. */ CHIP_ERROR BLEManagerImpl::StartAdvertising(void) { CHIP_ERROR err = CHIP_NO_ERROR; ble_error_t mbed_err = BLE_ERROR_NONE; ble::Gap & gap = ble::BLE::Instance().gap(); ble::AdvertisingDataBuilder adv_data_builder(mAdvertisingDataBuffer); ChipBLEDeviceIdentificationInfo dev_id_info; // Advertise CONNECTABLE if we haven't reached the maximum number of connections. uint16_t num_conns = _NumConnections(); bool connectable = (num_conns < kMaxConnections); ble::advertising_type_t adv_type = connectable ? ble::advertising_type_t::CONNECTABLE_UNDIRECTED : ble::advertising_type_t::SCANNABLE_UNDIRECTED; // Advertise in fast mode if not fully provisioned and there are no CHIPoBLE connections, or // if the application has expressly requested fast advertising. ble::adv_interval_t adv_interval = (num_conns == 0 && !ConfigurationMgr().IsFullyProvisioned()) ? ble::adv_interval_t(CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MAX) : ble::adv_interval_t(CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MAX); // minInterval and maxInterval are equal for CHIP. ble::AdvertisingParameters adv_params(adv_type, adv_interval, adv_interval); // Restart advertising if already active. if (gap.isAdvertisingActive(ble::LEGACY_ADVERTISING_HANDLE)) { mbed_err = gap.stopAdvertising(ble::LEGACY_ADVERTISING_HANDLE); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); ChipLogDetail(DeviceLayer, "Advertising already active. Restarting."); } mbed_err = gap.setAdvertisingParameters(ble::LEGACY_ADVERTISING_HANDLE, adv_params); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); mbed_err = adv_data_builder.setFlags(ble::adv_data_flags_t::BREDR_NOT_SUPPORTED | ble::adv_data_flags_t::LE_GENERAL_DISCOVERABLE); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); if (!mFlags.Has(kFlag_UseCustomDeviceName)) { uint16_t discriminator; SuccessOrExit(err = GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator)); memset(mDeviceName, 0, kMaxDeviceNameLength); snprintf(mDeviceName, kMaxDeviceNameLength, "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator); } mbed_err = adv_data_builder.setName(mDeviceName); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); dev_id_info.Init(); SuccessOrExit(err = ConfigurationMgr().GetBLEDeviceIdentificationInfo(dev_id_info)); mbed_err = adv_data_builder.setServiceData( ShortUUID_CHIPoBLEService, mbed::make_Span(reinterpret_cast(&dev_id_info), sizeof dev_id_info)); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); mbed_err = gap.setAdvertisingPayload(ble::LEGACY_ADVERTISING_HANDLE, adv_data_builder.getAdvertisingData()); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); adv_data_builder.clear(); adv_data_builder.setLocalServiceList(mbed::make_Span(&ShortUUID_CHIPoBLEService, 1)); mbed_err = gap.setAdvertisingScanResponse(ble::LEGACY_ADVERTISING_HANDLE, adv_data_builder.getAdvertisingData()); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); mbed_err = gap.startAdvertising(ble::LEGACY_ADVERTISING_HANDLE); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); ChipLogDetail(DeviceLayer, "Advertising started, type: 0x%x (%sconnectable), interval: [%lu:%lu] ms, device name: %s)", adv_params.getType().value(), connectable ? "" : "non-", adv_params.getMinPrimaryInterval().valueInMs(), adv_params.getMaxPrimaryInterval().valueInMs(), mDeviceName); exit: if (mbed_err != BLE_ERROR_NONE) { ChipLogError(DeviceLayer, "StartAdvertising mbed-os error: %d", mbed_err); } return err; } CHIP_ERROR BLEManagerImpl::StopAdvertising(void) { CHIP_ERROR err = CHIP_NO_ERROR; ble_error_t mbed_err = BLE_ERROR_NONE; ble::Gap & gap = ble::BLE::Instance().gap(); if (!gap.isAdvertisingActive(ble::LEGACY_ADVERTISING_HANDLE)) { ChipLogDetail(DeviceLayer, "No need to stop. Advertising inactive."); return err; } mbed_err = gap.stopAdvertising(ble::LEGACY_ADVERTISING_HANDLE); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); exit: if (mbed_err != BLE_ERROR_NONE) { ChipLogError(DeviceLayer, "StopAdvertising mbed-os error: %d", mbed_err); } return err; } CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); if (mFlags.Has(kFlag_AdvertisingEnabled) != val) { ChipLogDetail(DeviceLayer, "SetAdvertisingEnabled(%s)", val ? "true" : "false"); mFlags.Set(kFlag_AdvertisingEnabled, val); PlatformMgr().ScheduleWork(DriveBLEState, 0); } exit: return err; } CHIP_ERROR BLEManagerImpl::_SetFastAdvertisingEnabled(bool val) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); if (mFlags.Has(kFlag_FastAdvertisingEnabled) != val) { ChipLogDetail(DeviceLayer, "SetFastAdvertisingEnabled(%s)", val ? "true" : "false"); mFlags.Set(kFlag_FastAdvertisingEnabled, val); PlatformMgr().ScheduleWork(DriveBLEState, 0); } exit: return err; } CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(strlen(mDeviceName) < bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); strcpy(buf, mDeviceName); exit: return err; } CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); if (deviceName != nullptr && deviceName[0] != '\0') { VerifyOrExit(strlen(deviceName) < kMaxDeviceNameLength, err = CHIP_ERROR_INVALID_ARGUMENT); strcpy(mDeviceName, deviceName); mFlags.Set(kFlag_UseCustomDeviceName); ChipLogDetail(DeviceLayer, "Device name set to: %s", deviceName); } else { mDeviceName[0] = '\0'; mFlags.Clear(kFlag_UseCustomDeviceName); } exit: return err; } uint16_t BLEManagerImpl::_NumConnections(void) { return mGAPConns; } void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) { switch (event->Type) { case DeviceEventType::kCHIPoBLESubscribe: { ChipDeviceEvent connEstEvent; ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLESubscribe"); HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); connEstEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; PlatformMgrImpl().PostEventOrDie(&connEstEvent); } break; case DeviceEventType::kCHIPoBLEUnsubscribe: { ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEUnsubscribe"); HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); } break; case DeviceEventType::kCHIPoBLEWriteReceived: { ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEWriteReceived"); HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_1_UUID, PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); } break; case DeviceEventType::kCHIPoBLEConnectionError: { ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEConnectionError"); HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); } break; case DeviceEventType::kCHIPoBLEIndicateConfirm: { ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEIndicateConfirm"); HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &Ble::CHIP_BLE_CHAR_2_UUID); } break; default: ChipLogDetail(DeviceLayer, "_OnPlatformEvent default: event->Type = 0x%x", event->Type); break; } } void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) { // no-op } CHIP_ERROR BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__); return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__); return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) { ChipLogProgress(DeviceLayer, "Closing BLE GATT connection, connHandle=%d", conId); ble::Gap & gap = ble::BLE::Instance().gap(); ble_error_t mbed_err = gap.disconnect(conId, ble::local_disconnection_reason_t::USER_TERMINATION); VerifyOrReturnError(mbed_err == BLE_ERROR_NONE, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const { return sConnectionInfo.getMTU(conId); } CHIP_ERROR BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, PacketBufferHandle pBuf) { ChipLogDetail(DeviceLayer, "BlePlatformDelegate %s", __FUNCTION__); CHIP_ERROR err = CHIP_NO_ERROR; ble_error_t mbed_err = BLE_ERROR_NONE; ble::GattServer & gatt_server = ble::BLE::Instance().gattServer(); ble::attribute_handle_t att_handle; // For BLE, the buffer is capped at UINT16_MAX. VerifyOrExit(CanCastTo(pBuf->DataLength()), err = CHIP_ERROR_MESSAGE_TOO_LONG); // No need to do anything fancy here. Only 3 handles are used in this impl. if (UUIDsMatch(charId, &Ble::CHIP_BLE_CHAR_2_UUID)) { att_handle = sCHIPService.getTxHandle(); } else if (UUIDsMatch(charId, &Ble::CHIP_BLE_CHAR_1_UUID)) { // TODO does this make sense? att_handle = sCHIPService.getRxHandle(); } else { ChipLogError(DeviceLayer, "Send indication failed, invalid charID."); return BLE_ERROR_GATT_INDICATE_FAILED; } ChipLogDetail(DeviceLayer, "Sending indication for CHIPoBLE characteristic " "(connHandle=%d, attHandle=%d, data_len=%u)", conId, att_handle, pBuf->DataLength()); mbed_err = gatt_server.write(att_handle, pBuf->Start(), static_cast(pBuf->DataLength()), false); VerifyOrExit(mbed_err == BLE_ERROR_NONE, err = CHIP_ERROR(chip::ChipError::Range::kOS, mbed_err)); exit: if (mbed_err != BLE_ERROR_NONE) { ChipLogError(DeviceLayer, "Send indication failed, mbed-os error: %d", mbed_err); } return err; } CHIP_ERROR BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, PacketBufferHandle pBuf) { ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__); return CHIP_ERROR_NOT_IMPLEMENTED; } } // namespace Internal } // namespace DeviceLayer } // namespace chip #endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE