blob: d4a1e3209610a6a0bd602908ebce0b394fddf039 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2014-2017 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
* This file implements objects which provide an abstraction layer between
* a platform's Bluetooth Low Energy (BLE) implementation and the CHIP
* stack.
*
* The BleLayer obect accepts BLE data and control input from the
* application via a functional interface. It performs the fragmentation
* and reassembly required to transmit CHIP message via a BLE GATT
* characteristic interface, and drives incoming messages up the CHIP
* stack.
*
* During initialization, the BleLayer object requires a pointer to the
* platform's implementation of the BlePlatformDelegate and
* BleApplicationDelegate objects.
*
* The BlePlatformDelegate provides the CHIP stack with an interface
* by which to form and cancel GATT subscriptions, read and write
* GATT characteristic values, send GATT characteristic notifications,
* respond to GATT read requests, and close BLE connections.
*
* The BleApplicationDelegate provides a mechanism for CHIP to inform
* the application when it has finished using a given BLE connection,
* i.e when the chipConnection object wrapping this connection has
* closed. This allows the application to either close the BLE connection
* or continue to keep it open for non-CHIP purposes.
*
* To enable CHIP over BLE for a new platform, the application developer
* must provide an implementation for both delegates, provides points to
* instances of these delegates on startup, and ensure that the
* application calls the necessary BleLayer functions when appropriate to
* drive BLE data and control input up the stack.
*/
#define _CHIP_BLE_BLE_H
#include "BleLayer.h"
#include <cstring>
#include <utility>
#include <lib/core/CHIPEncoding.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SetupDiscriminator.h>
#include <lib/support/logging/CHIPLogging.h>
#include <system/SystemLayer.h>
#include <system/SystemPacketBuffer.h>
#include "BLEEndPoint.h"
#include "BleApplicationDelegate.h"
#include "BleConfig.h"
#include "BleConnectionDelegate.h"
#include "BleError.h"
#include "BleLayerDelegate.h"
#include "BlePlatformDelegate.h"
#include "BleRole.h"
#include "BleUUID.h"
// Magic values expected in first 2 bytes of valid BLE transport capabilities request or response:
#define CAPABILITIES_MSG_CHECK_BYTE_1 0b01100101
#define CAPABILITIES_MSG_CHECK_BYTE_2 0b01101100
namespace chip {
namespace Ble {
class BleEndPointPool
{
public:
int Size() const { return BLE_LAYER_NUM_BLE_ENDPOINTS; }
BLEEndPoint * Get(size_t i) const
{
static union
{
uint8_t Pool[sizeof(BLEEndPoint) * BLE_LAYER_NUM_BLE_ENDPOINTS];
BLEEndPoint::AlignT ForceAlignment;
} sEndPointPool;
if (i < BLE_LAYER_NUM_BLE_ENDPOINTS)
{
return reinterpret_cast<BLEEndPoint *>(sEndPointPool.Pool + (sizeof(BLEEndPoint) * i));
}
return nullptr;
}
BLEEndPoint * Find(BLE_CONNECTION_OBJECT c) const
{
if (c == BLE_CONNECTION_UNINITIALIZED)
{
return nullptr;
}
for (size_t i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
{
BLEEndPoint * elem = Get(i);
if (elem->mBle != nullptr && elem->mConnObj == c)
{
return elem;
}
}
return nullptr;
}
BLEEndPoint * GetFree() const
{
for (size_t i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
{
BLEEndPoint * elem = Get(i);
if (elem->mBle == nullptr)
{
return elem;
}
}
return nullptr;
}
};
// EndPoint Pools
//
static BleEndPointPool sBLEEndPointPool;
// UUIDs used internally by BleLayer:
const ChipBleUUID BleLayer::CHIP_BLE_CHAR_1_ID = { { // 18EE2EF5-263D-4559-959F-4F9C429F9D11
0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42,
0x9F, 0x9D, 0x11 } };
const ChipBleUUID BleLayer::CHIP_BLE_CHAR_2_ID = { { // 18EE2EF5-263D-4559-959F-4F9C429F9D12
0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42,
0x9F, 0x9D, 0x12 } };
const ChipBleUUID BleLayer::CHIP_BLE_CHAR_3_ID = { { // 64630238-8772-45F2-B87D-748A83218F04
0x64, 0x63, 0x02, 0x38, 0x87, 0x72, 0x45, 0xF2, 0xB8, 0x7D, 0x74, 0x8A, 0x83,
0x21, 0x8F, 0x04 } };
// BleTransportCapabilitiesRequestMessage implementation:
void BleTransportCapabilitiesRequestMessage::SetSupportedProtocolVersion(uint8_t index, uint8_t version)
{
uint8_t mask;
// If even-index, store version in lower 4 bits; else, higher 4 bits.
if (index % 2 == 0)
{
mask = 0x0F;
}
else
{
mask = 0xF0;
version = static_cast<uint8_t>(version << 4);
}
version &= mask;
uint8_t & slot = mSupportedProtocolVersions[(index / 2)];
slot = static_cast<uint8_t>(slot & ~mask); // Clear version at index; leave other version in same byte alone
slot |= version;
}
CHIP_ERROR BleTransportCapabilitiesRequestMessage::Encode(const PacketBufferHandle & msgBuf) const
{
uint8_t * p = msgBuf->Start();
// Verify we can write the fixed-length request without running into the end of the buffer.
VerifyOrReturnError(msgBuf->MaxDataLength() >= kCapabilitiesRequestLength, CHIP_ERROR_NO_MEMORY);
chip::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_1);
chip::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_2);
for (uint8_t version : mSupportedProtocolVersions)
{
chip::Encoding::Write8(p, version);
}
chip::Encoding::LittleEndian::Write16(p, mMtu);
chip::Encoding::Write8(p, mWindowSize);
msgBuf->SetDataLength(kCapabilitiesRequestLength);
return CHIP_NO_ERROR;
}
CHIP_ERROR BleTransportCapabilitiesRequestMessage::Decode(const PacketBufferHandle & msgBuf,
BleTransportCapabilitiesRequestMessage & msg)
{
const uint8_t * p = msgBuf->Start();
// Verify we can read the fixed-length request without running into the end of the buffer.
VerifyOrReturnError(msgBuf->DataLength() >= kCapabilitiesRequestLength, CHIP_ERROR_MESSAGE_INCOMPLETE);
VerifyOrReturnError(CAPABILITIES_MSG_CHECK_BYTE_1 == chip::Encoding::Read8(p), BLE_ERROR_INVALID_MESSAGE);
VerifyOrReturnError(CAPABILITIES_MSG_CHECK_BYTE_2 == chip::Encoding::Read8(p), BLE_ERROR_INVALID_MESSAGE);
static_assert(kCapabilitiesRequestSupportedVersionsLength == sizeof(msg.mSupportedProtocolVersions),
"Expected capability sizes and storage must match");
for (unsigned char & version : msg.mSupportedProtocolVersions)
{
version = chip::Encoding::Read8(p);
}
msg.mMtu = chip::Encoding::LittleEndian::Read16(p);
msg.mWindowSize = chip::Encoding::Read8(p);
return CHIP_NO_ERROR;
}
// BleTransportCapabilitiesResponseMessage implementation:
CHIP_ERROR BleTransportCapabilitiesResponseMessage::Encode(const PacketBufferHandle & msgBuf) const
{
uint8_t * p = msgBuf->Start();
// Verify we can write the fixed-length request without running into the end of the buffer.
VerifyOrReturnError(msgBuf->MaxDataLength() >= kCapabilitiesResponseLength, CHIP_ERROR_NO_MEMORY);
chip::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_1);
chip::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_2);
chip::Encoding::Write8(p, mSelectedProtocolVersion);
chip::Encoding::LittleEndian::Write16(p, mFragmentSize);
chip::Encoding::Write8(p, mWindowSize);
msgBuf->SetDataLength(kCapabilitiesResponseLength);
return CHIP_NO_ERROR;
}
CHIP_ERROR BleTransportCapabilitiesResponseMessage::Decode(const PacketBufferHandle & msgBuf,
BleTransportCapabilitiesResponseMessage & msg)
{
const uint8_t * p = msgBuf->Start();
// Verify we can read the fixed-length response without running into the end of the buffer.
VerifyOrReturnError(msgBuf->DataLength() >= kCapabilitiesResponseLength, CHIP_ERROR_MESSAGE_INCOMPLETE);
VerifyOrReturnError(CAPABILITIES_MSG_CHECK_BYTE_1 == chip::Encoding::Read8(p), BLE_ERROR_INVALID_MESSAGE);
VerifyOrReturnError(CAPABILITIES_MSG_CHECK_BYTE_2 == chip::Encoding::Read8(p), BLE_ERROR_INVALID_MESSAGE);
msg.mSelectedProtocolVersion = chip::Encoding::Read8(p);
msg.mFragmentSize = chip::Encoding::LittleEndian::Read16(p);
msg.mWindowSize = chip::Encoding::Read8(p);
return CHIP_NO_ERROR;
}
// BleLayer implementation:
BleLayer::BleLayer()
{
mState = kState_NotInitialized;
}
CHIP_ERROR BleLayer::Init(BlePlatformDelegate * platformDelegate, BleConnectionDelegate * connDelegate,
BleApplicationDelegate * appDelegate, chip::System::Layer * systemLayer)
{
Ble::RegisterLayerErrorFormatter();
// It is totally valid to not have a connDelegate. In this case the client application
// will take care of the connection steps.
VerifyOrReturnError(platformDelegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(appDelegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(systemLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
if (mState != kState_NotInitialized)
{
return CHIP_ERROR_INCORRECT_STATE;
}
mConnectionDelegate = connDelegate;
mPlatformDelegate = platformDelegate;
mApplicationDelegate = appDelegate;
mSystemLayer = systemLayer;
memset(&sBLEEndPointPool, 0, sizeof(sBLEEndPointPool));
mState = kState_Initialized;
return CHIP_NO_ERROR;
}
CHIP_ERROR BleLayer::Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate,
chip::System::Layer * systemLayer)
{
return Init(platformDelegate, nullptr, appDelegate, systemLayer);
}
void BleLayer::IndicateBleClosing()
{
mState = kState_Disconnecting;
}
void BleLayer::Shutdown()
{
mState = kState_NotInitialized;
CloseAllBleConnections();
}
void BleLayer::CloseAllBleConnections()
{
// Close and free all BLE end points.
for (size_t i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
{
BLEEndPoint * elem = sBLEEndPointPool.Get(i);
// If end point was initialized, and has not since been freed...
if (elem->mBle != nullptr)
{
// If end point hasn't already been closed...
if (elem->mState != BLEEndPoint::kState_Closed)
{
// Close end point such that callbacks are suppressed and pending transmissions aborted.
elem->Abort();
}
// If end point was closed, but is still waiting for GATT unsubscribe to complete, free it anyway.
// This cancels the unsubscribe timer (plus all the end point's other timers).
if (elem->IsUnsubscribePending())
{
elem->Free();
}
}
}
}
void BleLayer::CloseBleConnection(BLE_CONNECTION_OBJECT connObj)
{
// Close and free all BLE endpoints.
for (size_t i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
{
BLEEndPoint * elem = sBLEEndPointPool.Get(i);
// If end point was initialized, and has not since been freed...
if (elem->mBle != nullptr && elem->ConnectionObjectIs(connObj))
{
// If end point hasn't already been closed...
if (elem->mState != BLEEndPoint::kState_Closed)
{
// Close end point such that callbacks are suppressed and pending transmissions aborted.
elem->Abort();
}
// If end point was closed, but is still waiting for GATT unsubscribe to complete, free it anyway.
// This cancels the unsubscribe timer (plus all the end point's other timers).
if (elem->IsUnsubscribePending())
{
elem->Free();
}
}
}
}
CHIP_ERROR BleLayer::CancelBleIncompleteConnection()
{
VerifyOrReturnError(mState == kState_Initialized, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mConnectionDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
CHIP_ERROR err = mConnectionDelegate->CancelConnection();
if (err == CHIP_ERROR_NOT_IMPLEMENTED)
{
ChipLogError(Ble, "BleConnectionDelegate::CancelConnection is not implemented.");
}
return err;
}
CHIP_ERROR BleLayer::NewBleConnectionByDiscriminator(const SetupDiscriminator & connDiscriminator, void * appState,
BleConnectionDelegate::OnConnectionCompleteFunct onSuccess,
BleConnectionDelegate::OnConnectionErrorFunct onError)
{
VerifyOrReturnError(mState == kState_Initialized, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mConnectionDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mBleTransport != nullptr, CHIP_ERROR_INCORRECT_STATE);
mConnectionDelegate->OnConnectionComplete = onSuccess;
mConnectionDelegate->OnConnectionError = onError;
mConnectionDelegate->NewConnection(this, appState == nullptr ? this : appState, connDiscriminator);
return CHIP_NO_ERROR;
}
CHIP_ERROR BleLayer::NewBleConnectionByObject(BLE_CONNECTION_OBJECT connObj, void * appState,
BleConnectionDelegate::OnConnectionCompleteFunct onSuccess,
BleConnectionDelegate::OnConnectionErrorFunct onError)
{
VerifyOrReturnError(mState == kState_Initialized, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mConnectionDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mBleTransport != nullptr, CHIP_ERROR_INCORRECT_STATE);
mConnectionDelegate->OnConnectionComplete = onSuccess;
mConnectionDelegate->OnConnectionError = onError;
mConnectionDelegate->NewConnection(this, appState == nullptr ? this : appState, connObj);
return CHIP_NO_ERROR;
}
CHIP_ERROR BleLayer::NewBleConnectionByObject(BLE_CONNECTION_OBJECT connObj)
{
VerifyOrReturnError(mState == kState_Initialized, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mBleTransport != nullptr, CHIP_ERROR_INCORRECT_STATE);
OnConnectionComplete(this, connObj);
return CHIP_NO_ERROR;
}
CHIP_ERROR BleLayer::NewBleEndPoint(BLEEndPoint ** retEndPoint, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose)
{
*retEndPoint = nullptr;
if (mState != kState_Initialized)
{
return CHIP_ERROR_INCORRECT_STATE;
}
if (connObj == BLE_CONNECTION_UNINITIALIZED)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
*retEndPoint = sBLEEndPointPool.GetFree();
if (*retEndPoint == nullptr)
{
ChipLogError(Ble, "%s endpoint pool FULL", "Ble");
return CHIP_ERROR_ENDPOINT_POOL_FULL;
}
(*retEndPoint)->Init(this, connObj, role, autoClose);
(*retEndPoint)->mBleTransport = mBleTransport;
return CHIP_NO_ERROR;
}
// Handle remote central's initiation of CHIP over BLE protocol handshake.
CHIP_ERROR BleLayer::HandleBleTransportConnectionInitiated(BLE_CONNECTION_OBJECT connObj, PacketBufferHandle && pBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
BLEEndPoint * newEndPoint = nullptr;
// Only BLE peripherals can receive GATT writes, so specify this role in our creation of the BLEEndPoint.
// Set autoClose = false. Peripherals only notify the application when an end point releases a BLE connection.
err = NewBleEndPoint(&newEndPoint, connObj, kBleRole_Peripheral, false);
SuccessOrExit(err);
newEndPoint->mBleTransport = mBleTransport;
err = newEndPoint->Receive(std::move(pBuf));
SuccessOrExit(err); // If we fail here, end point will have already released connection and freed itself.
exit:
// If we failed to allocate a new end point, release underlying BLE connection. Central's handshake will time out
// if the application decides to keep the BLE connection open.
if (newEndPoint == nullptr)
{
mApplicationDelegate->NotifyChipConnectionClosed(connObj);
}
if (err != CHIP_NO_ERROR)
{
ChipLogError(Ble, "HandleChipConnectionReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
}
return err;
}
bool BleLayer::HandleWriteReceived(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId,
PacketBufferHandle && pBuf)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
ChipLogError(Ble, "ble write rcvd on unknown svc id");
return true;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_1_ID, charId))
{
if (pBuf.IsNull())
{
ChipLogError(Ble, "rcvd null ble write");
return true;
}
// Find matching connection end point.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
CHIP_ERROR status = endPoint->Receive(std::move(pBuf));
if (status != CHIP_NO_ERROR)
{
ChipLogError(Ble, "BLEEndPoint rcv failed, err = %" CHIP_ERROR_FORMAT, status.Format());
}
}
else
{
CHIP_ERROR status = HandleBleTransportConnectionInitiated(connObj, std::move(pBuf));
if (status != CHIP_NO_ERROR)
{
ChipLogError(Ble, "failed handle new chip BLE connection, status = %" CHIP_ERROR_FORMAT, status.Format());
}
}
}
else
{
ChipLogError(Ble, "ble write rcvd on unknown char");
}
return true;
}
bool BleLayer::HandleIndicationReceived(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId,
PacketBufferHandle && pBuf)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_2_ID, charId))
{
if (pBuf.IsNull())
{
ChipLogError(Ble, "rcvd null ble indication");
return true;
}
// find matching connection end point.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
CHIP_ERROR status = endPoint->Receive(std::move(pBuf));
if (status != CHIP_NO_ERROR)
{
ChipLogError(Ble, "BLEEndPoint rcv failed, err = %" CHIP_ERROR_FORMAT, status.Format());
}
}
else
{
ChipLogDetail(Ble, "no endpoint for rcvd indication");
}
}
else
{
ChipLogError(Ble, "ble ind rcvd on unknown char");
}
return true;
}
bool BleLayer::HandleWriteConfirmation(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_1_ID, charId))
{
HandleAckReceived(connObj);
}
else
{
ChipLogError(Ble, "ble write con rcvd on unknown char");
}
return true;
}
bool BleLayer::HandleIndicationConfirmation(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_2_ID, charId))
{
HandleAckReceived(connObj);
}
else
{
ChipLogError(Ble, "ble ind con rcvd on unknown char");
}
return true;
}
void BleLayer::HandleAckReceived(BLE_CONNECTION_OBJECT connObj)
{
// find matching connection end point.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
CHIP_ERROR status = endPoint->HandleGattSendConfirmationReceived();
if (status != CHIP_NO_ERROR)
{
ChipLogError(Ble, "endpoint conf recvd failed, err = %" CHIP_ERROR_FORMAT, status.Format());
}
}
else
{
ChipLogError(Ble, "no endpoint for BLE sent data ack");
}
}
bool BleLayer::HandleSubscribeReceived(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_2_ID, charId) || UUIDsMatch(&CHIP_BLE_CHAR_3_ID, charId))
{
// Find end point already associated with BLE connection, if any.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
endPoint->HandleSubscribeReceived();
}
else
{
ChipLogError(Ble, "no endpoint for sub recvd");
}
}
return true;
}
bool BleLayer::HandleSubscribeComplete(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_2_ID, charId) || UUIDsMatch(&CHIP_BLE_CHAR_3_ID, charId))
{
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
endPoint->HandleSubscribeComplete();
}
else
{
ChipLogError(Ble, "no endpoint for sub complete");
}
}
return true;
}
bool BleLayer::HandleUnsubscribeReceived(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_2_ID, charId) || UUIDsMatch(&CHIP_BLE_CHAR_3_ID, charId))
{
// Find end point already associated with BLE connection, if any.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
endPoint->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_CENTRAL_UNSUBSCRIBED);
}
else
{
ChipLogError(Ble, "no endpoint for unsub recvd");
}
}
return true;
}
bool BleLayer::HandleUnsubscribeComplete(BLE_CONNECTION_OBJECT connObj, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
if (!UUIDsMatch(&CHIP_BLE_SVC_ID, svcId))
{
return false;
}
if (UUIDsMatch(&CHIP_BLE_CHAR_2_ID, charId) || UUIDsMatch(&CHIP_BLE_CHAR_3_ID, charId))
{
// Find end point already associated with BLE connection, if any.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
endPoint->HandleUnsubscribeComplete();
}
else
{
ChipLogError(Ble, "no endpoint for unsub complete");
}
}
return true;
}
void BleLayer::HandleConnectionError(BLE_CONNECTION_OBJECT connObj, CHIP_ERROR err)
{
// BLE connection has failed somehow, we must find and abort matching connection end point.
BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
if (endPoint != nullptr)
{
if (err == BLE_ERROR_GATT_UNSUBSCRIBE_FAILED && endPoint->IsUnsubscribePending())
{
// If end point was already closed and just waiting for unsubscribe to complete, free it. Call to Free()
// stops unsubscribe timer.
endPoint->Free();
}
else
{
endPoint->DoClose(kBleCloseFlag_AbortTransmission, err);
}
}
}
BleTransportProtocolVersion BleLayer::GetHighestSupportedProtocolVersion(const BleTransportCapabilitiesRequestMessage & reqMsg)
{
BleTransportProtocolVersion retVersion = kBleTransportProtocolVersion_None;
uint8_t shift_width = 4;
for (int i = 0; i < NUM_SUPPORTED_PROTOCOL_VERSIONS; i++)
{
shift_width ^= 4;
uint8_t version = reqMsg.mSupportedProtocolVersions[(i / 2)];
version = static_cast<uint8_t>((version >> shift_width) & 0x0F); // Grab just the nibble we want.
if ((version >= CHIP_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) &&
(version <= CHIP_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION) && (version > retVersion))
{
retVersion = static_cast<BleTransportProtocolVersion>(version);
}
else if (version == kBleTransportProtocolVersion_None) // Signifies end of supported versions list
{
break;
}
}
return retVersion;
}
void BleLayer::OnConnectionComplete(void * appState, BLE_CONNECTION_OBJECT connObj)
{
BleLayer * layer = reinterpret_cast<BleLayer *>(appState);
BLEEndPoint * endPoint = nullptr;
CHIP_ERROR err = CHIP_NO_ERROR;
SuccessOrExit(err = layer->NewBleEndPoint(&endPoint, connObj, kBleRole_Central, true));
layer->mBleTransport->OnBleConnectionComplete(endPoint);
exit:
if (err != CHIP_NO_ERROR)
{
OnConnectionError(layer, err);
}
}
void BleLayer::OnConnectionError(void * appState, CHIP_ERROR err)
{
BleLayer * layer = reinterpret_cast<BleLayer *>(appState);
layer->mBleTransport->OnBleConnectionError(err);
}
} /* namespace Ble */
} /* namespace chip */