blob: cf5914aa4490789d46a98f00d3f6ae216808162b [file] [log] [blame]
/*
*
* 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 <inttypes.h>
#include <stdint.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
#include <platform/mbed/BLEManagerImpl.h>
#include <ble/CHIPBleServiceData.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CommissionableDataProvider.h>
#include <platform/internal/BLEManager.h>
// 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");
const ChipBleUUID ChipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
0x9D, 0x11 } };
// TX = BleLayer::CHIP_BLE_CHAR_2_ID
const UUID LongUUID_CHIPoBLEChar_TX("18EE2EF5-263D-4559-959F-4F9C429F9D12");
const ChipBleUUID ChipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
0x9D, 0x12 } };
} // 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<GapEventHandler>, 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<SecurityManagerEventHandler>,
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<Flags>(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 *>{
[](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(ConfigurationMgr().GetBLEDeviceIdentificationInfo(dev_id_info));
mbed_err = adv_data_builder.setServiceData(
ShortUUID_CHIPoBLEService, mbed::make_Span<const uint8_t>(reinterpret_cast<uint8_t *>(&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<const UUID>(&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, &ChipUUID_CHIPoBLEChar_TX);
connEstEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished;
PlatformMgrImpl().PostEventOrDie(&connEstEvent);
}
break;
case DeviceEventType::kCHIPoBLEUnsubscribe: {
ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEUnsubscribe");
HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX);
}
break;
case DeviceEventType::kCHIPoBLEWriteReceived: {
ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEWriteReceived");
HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_RX,
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, &ChipUUID_CHIPoBLEChar_TX);
}
break;
default:
ChipLogDetail(DeviceLayer, "_OnPlatformEvent default: event->Type = 0x%x", event->Type);
break;
}
}
void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId)
{
// no-op
}
bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__);
return true;
}
bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__);
return true;
}
bool 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);
return mbed_err == BLE_ERROR_NONE;
}
uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const
{
return sConnectionInfo.getMTU(conId);
}
bool 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;
// No need to do anything fancy here. Only 3 handles are used in this impl.
if (UUIDsMatch(charId, &ChipUUID_CHIPoBLEChar_TX))
{
att_handle = sCHIPService.getTxHandle();
}
else if (UUIDsMatch(charId, &ChipUUID_CHIPoBLEChar_RX))
{
// TODO does this make sense?
att_handle = sCHIPService.getRxHandle();
}
else
{
// TODO handle error with chipConnection::SendMessage as described
// in the BlePlatformDelegate.h.
ChipLogError(DeviceLayer, "Send indication failed, invalid charID.");
return false;
}
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(), 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_NO_ERROR;
}
bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId,
PacketBufferHandle pBuf)
{
ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__);
return true;
}
bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId,
PacketBufferHandle pBuf)
{
ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__);
return true;
}
bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQUEST_CONTEXT requestContext,
const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
ChipLogError(DeviceLayer, "%s: NOT IMPLEMENTED", __PRETTY_FUNCTION__);
return true;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip
#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE