Import ble code
diff --git a/src/ble/BLEEndPoint.cpp b/src/ble/BLEEndPoint.cpp
new file mode 100644
index 0000000..cb437dc
--- /dev/null
+++ b/src/ble/BLEEndPoint.cpp
@@ -0,0 +1,1748 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 a Bluetooth Low Energy (BLE) connection
+ * endpoint abstraction for the byte-streaming,
+ * connection-oriented Weave over Bluetooth Low Energy (WoBLE)
+ * Bluetooth Transport Protocol (BTP).
+ *
+ */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+#include <string.h>
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+#include <Weave/Core/WeaveConfig.h>
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Support/CodeUtils.h>
+#include <Weave/Support/FlagUtils.hpp>
+#include <Weave/Support/WeaveFaultInjection.h>
+
+#include <BleLayer/BLEEndPoint.h>
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/WoBle.h>
+#if WEAVE_ENABLE_WOBLE_TEST
+#include "WoBleTest.h"
+#endif
+
+// clang-format off
+
+// Define below to enable extremely verbose, BLE end point-specific debug logging.
+#undef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
+
+#ifdef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
+#define WeaveLogDebugBleEndPoint(MOD, MSG, ...) WeaveLogError(MOD, MSG, ## __VA_ARGS__)
+#else
+#define WeaveLogDebugBleEndPoint(MOD, MSG, ...)
+#endif
+
+/**
+ * @def BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD
+ *
+ * @brief
+ * If an end point's receive window drops equal to or below this value, it will send an immediate acknowledgement
+ * packet to re-open its window instead of waiting for the send-ack timer to expire.
+ *
+ */
+#define BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD 1
+
+/**
+ * @def BLE_CONNECT_TIMEOUT_MS
+ *
+ * @brief
+ * This is the amount of time, in milliseconds, after a BLE end point initiates a transport protocol connection
+ * or receives the initial portion of a connect request before the end point will automatically release its BLE
+ * connection and free itself if the transport connection has not been established.
+ *
+ */
+#define BLE_CONNECT_TIMEOUT_MS 5000 // 5 seconds
+
+/**
+ * @def BLE_UNSUBSCRIBE_TIMEOUT_MS
+ *
+ * @brief
+ * This is amount of time, in milliseconds, which a BLE end point will wait for an unsubscribe operation to complete
+ * before it automatically releases its BLE connection and frees itself. The default value of 5 seconds is arbitary.
+ *
+ */
+#define BLE_UNSUBSCRIBE_TIMEOUT_MS 5000 // 5 seconds
+
+#define BTP_ACK_RECEIVED_TIMEOUT_MS 15000 // 15 seconds
+#define BTP_ACK_SEND_TIMEOUT_MS 2500 // 2.5 seconds
+
+#define BTP_WINDOW_NO_ACK_SEND_THRESHOLD 1 // Data fragments may only be sent without piggybacked
+ // acks if receiver's window size is above this threshold.
+
+// clang-format on
+
+namespace nl {
+namespace Ble {
+
+BLE_ERROR BLEEndPoint::StartConnect()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ BleTransportCapabilitiesRequestMessage req;
+ PacketBuffer * buf = NULL;
+ int i;
+ int numVersions;
+
+ // Ensure we're in the correct state.
+ VerifyOrExit(mState == kState_Ready, err = BLE_ERROR_INCORRECT_STATE);
+ mState = kState_Connecting;
+
+ // Build BLE transport protocol capabilities request.
+ buf = PacketBuffer::New();
+ VerifyOrExit(buf != NULL, err = BLE_ERROR_NO_MEMORY);
+
+ // Zero-initialize BLE transport capabilities request.
+ memset(&req, 0, sizeof(req));
+
+ req.mMtu = mBle->mPlatformDelegate->GetMTU(mConnObj);
+
+ req.mWindowSize = BLE_MAX_RECEIVE_WINDOW_SIZE;
+
+ // Populate request with highest supported protocol versions
+ numVersions = NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION + 1;
+ VerifyOrExit(numVersions <= NUM_SUPPORTED_PROTOCOL_VERSIONS, err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS);
+ for (i = 0; i < numVersions; i++)
+ {
+ req.SetSupportedProtocolVersion(i, NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - i);
+ }
+
+ err = req.Encode(buf);
+ SuccessOrExit(err);
+
+ // Start connect timer. Canceled when end point freed or connection established.
+ err = StartConnectTimer();
+ SuccessOrExit(err);
+
+ // Send BLE transport capabilities request to peripheral via GATT write.
+ if (!SendWrite(buf))
+ {
+ err = BLE_ERROR_GATT_WRITE_FAILED;
+ ExitNow();
+ }
+
+ // Free request buffer on write confirmation. Stash a reference to it in mSendQueue, which we don't use anyway
+ // until the connection has been set up.
+ QueueTx(buf, kType_Data);
+ buf = NULL;
+
+exit:
+ if (buf != NULL)
+ {
+ PacketBuffer::Free(buf);
+ }
+
+ // If we failed to initiate the connection, close the end point.
+ if (err != BLE_NO_ERROR)
+ {
+ StopConnectTimer();
+ DoClose(kBleCloseFlag_AbortTransmission, err);
+ }
+
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleConnectComplete()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ mState = kState_Connected;
+
+ // Cancel the connect timer.
+ StopConnectTimer();
+
+ // We've successfully completed the BLE transport protocol handshake, so let the application know we're open for business.
+ if (OnConnectComplete != NULL)
+ {
+ // Indicate connect complete to next-higher layer.
+ OnConnectComplete(this, BLE_NO_ERROR);
+ }
+ else
+ {
+ // If no connect complete callback has been set up, close the end point.
+ err = BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK;
+ }
+
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleReceiveConnectionComplete()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ WeaveLogDebugBleEndPoint(Ble, "entered HandleReceiveConnectionComplete");
+ mState = kState_Connected;
+
+ // Cancel receive connection timer.
+ StopReceiveConnectionTimer();
+
+ // We've successfully completed the BLE transport protocol handshake, so let the application know we're open for business.
+ if (mBle->OnWeaveBleConnectReceived != NULL)
+ {
+ // Indicate BLE transport protocol connection received to next-higher layer.
+ mBle->OnWeaveBleConnectReceived(this);
+ }
+ else
+ {
+ err = BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK;
+ }
+
+ return err;
+}
+
+void BLEEndPoint::HandleSubscribeReceived()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ VerifyOrExit(mState == kState_Connecting || mState == kState_Aborting, err = BLE_ERROR_INCORRECT_STATE);
+ VerifyOrExit(mSendQueue != NULL, err = BLE_ERROR_INCORRECT_STATE);
+
+ // Send BTP capabilities response to peripheral via GATT indication.
+#if WEAVE_ENABLE_WOBLE_TEST
+ VerifyOrExit(mWoBle.PopPacketTag(mSendQueue) == kType_Data, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+#endif
+ if (!SendIndication(mSendQueue))
+ {
+ // Ensure transmit queue is empty and set to NULL.
+ QueueTxLock();
+ PacketBuffer::Free(mSendQueue);
+ mSendQueue = NULL;
+ QueueTxUnlock();
+
+ WeaveLogError(Ble, "cap resp ind failed");
+ err = BLE_ERROR_GATT_INDICATE_FAILED;
+ ExitNow();
+ }
+
+ // Shrink remote receive window counter by 1, since we've sent an indication which requires acknowledgement.
+ mRemoteReceiveWindowSize -= 1;
+ WeaveLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
+
+ // Start ack recvd timer for handshake indication.
+ err = StartAckReceivedTimer();
+ SuccessOrExit(err);
+
+ WeaveLogDebugBleEndPoint(Ble, "got subscribe, sent indication w/ capabilities response");
+
+ // If SendIndication returns true, mSendQueue is freed on indication confirmation, or on close in case of
+ // connection error.
+
+ if (mState != kState_Aborting)
+ {
+ // If peripheral accepted the BTP connection, its end point must enter the connected state here, i.e. before it
+ // receives a GATT confirmation for the capabilities response indication. This behavior is required to handle the
+ // case where a peripheral's BLE controller passes up the central's first message fragment write before the
+ // capabilities response indication confirmation. If the end point waited for this indication confirmation before
+ // it entered the connected state, it'd be in the wrong state to receive the central's first data write, and drop
+ // the corresponding message fragment.
+ err = HandleReceiveConnectionComplete();
+ SuccessOrExit(err);
+ } // Else State == kState_Aborting, so we'll close end point when indication confirmation received.
+
+exit:
+ if (err != BLE_NO_ERROR)
+ {
+ DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, err);
+ }
+
+ return;
+}
+
+void BLEEndPoint::HandleSubscribeComplete()
+{
+ WeaveLogProgress(Ble, "subscribe complete, ep = %p", this);
+ SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, false);
+
+ BLE_ERROR err = DriveSending();
+
+ if (err != BLE_NO_ERROR)
+ {
+ DoClose(kBleCloseFlag_AbortTransmission, BLE_NO_ERROR);
+ }
+}
+
+void BLEEndPoint::HandleUnsubscribeComplete()
+{
+ // Don't bother to clear GattOperationInFlight, we're about to free the end point anyway.
+ Free();
+}
+
+bool BLEEndPoint::IsConnected(uint8_t state) const
+{
+ return (state == kState_Connected || state == kState_Closing);
+}
+
+bool BLEEndPoint::IsUnsubscribePending() const
+{
+ return (GetFlag(mTimerStateFlags, kTimerState_UnsubscribeTimerRunning));
+}
+
+void BLEEndPoint::Abort()
+{
+ // No more callbacks after this point, since application explicitly called Abort().
+ OnConnectComplete = NULL;
+ OnConnectionClosed = NULL;
+ OnMessageReceived = NULL;
+#if WEAVE_ENABLE_WOBLE_TEST
+ OnCommandReceived = NULL;
+#endif
+
+ DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, BLE_NO_ERROR);
+}
+
+void BLEEndPoint::Close()
+{
+ // No more callbacks after this point, since application explicitly called Close().
+ OnConnectComplete = NULL;
+ OnConnectionClosed = NULL;
+ OnMessageReceived = NULL;
+#if WEAVE_ENABLE_WOBLE_TEST
+ OnCommandReceived = NULL;
+#endif
+
+ DoClose(kBleCloseFlag_SuppressCallback, BLE_NO_ERROR);
+}
+
+void BLEEndPoint::DoClose(uint8_t flags, BLE_ERROR err)
+{
+ uint8_t oldState = mState;
+
+ // If end point is not closed or closing, OR end point was closing gracefully, but tx abort has been specified...
+ if ((mState != kState_Closed && mState != kState_Closing) ||
+ (mState == kState_Closing && (flags & kBleCloseFlag_AbortTransmission)))
+ {
+ // Cancel Connect and ReceiveConnect timers if they are running.
+ // Check role first to avoid needless iteration over timer pool.
+ if (mRole == kBleRole_Central)
+ {
+ StopConnectTimer();
+ }
+ else // (mRole == kBleRole_Peripheral), verified on Init
+ {
+ StopReceiveConnectionTimer();
+ }
+
+ // If transmit buffer is empty or a transmission abort was specified...
+ if (mWoBle.TxState() == WoBle::kState_Idle || (flags & kBleCloseFlag_AbortTransmission))
+ {
+ FinalizeClose(oldState, flags, err);
+ }
+ else
+ {
+ // Wait for send queue and fragmenter's tx buffer to become empty, to ensure all pending messages have been
+ // sent. Only free end point and tell platform it can throw away the underlying BLE connection once all
+ // pending messages have been sent and acknowledged by the remote WoBLE stack, or once the remote stack
+ // closes the WoBLE connection.
+ //
+ // In so doing, BLEEndPoint attempts to emulate the level of reliability afforded by TCPEndPoint and TCP
+ // sockets in general with a typical default SO_LINGER option. That said, there is no hard guarantee that
+ // pending messages will be sent once (Do)Close() is called, so developers should use application-level
+ // messages to confirm the receipt of all data sent prior to a Close() call.
+ mState = kState_Closing;
+
+ if ((flags & kBleCloseFlag_SuppressCallback) == 0)
+ {
+ DoCloseCallback(oldState, flags, err);
+ }
+ }
+ }
+}
+
+void BLEEndPoint::FinalizeClose(uint8_t oldState, uint8_t flags, BLE_ERROR err)
+{
+ mState = kState_Closed;
+
+ // Ensure transmit queue is empty and set to NULL.
+ QueueTxLock();
+ PacketBuffer::Free(mSendQueue);
+ mSendQueue = NULL;
+ QueueTxUnlock();
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ PacketBuffer::Free(mWoBleTest.mCommandReceiveQueue);
+ mWoBleTest.mCommandReceiveQueue = NULL;
+#endif
+
+ // Fire application's close callback if we haven't already, and it's not suppressed.
+ if (oldState != kState_Closing && (flags & kBleCloseFlag_SuppressCallback) == 0)
+ {
+ DoCloseCallback(oldState, flags, err);
+ }
+
+ // If underlying BLE connection has closed, connection object is invalid, so just free the end point and return.
+ if (err == BLE_ERROR_REMOTE_DEVICE_DISCONNECTED || err == BLE_ERROR_APP_CLOSED_CONNECTION)
+ {
+ mConnObj = BLE_CONNECTION_UNINITIALIZED; // Clear handle to BLE connection, so we don't double-close it.
+ Free();
+ }
+ else // Otherwise, try to signal close to remote device before end point releases BLE connection and frees itself.
+ {
+ if (mRole == kBleRole_Central && GetFlag(mConnStateFlags, kConnState_DidBeginSubscribe))
+ {
+ // Cancel send and receive-ack timers, if running.
+ StopAckReceivedTimer();
+ StopSendAckTimer();
+
+ // Indicate close of WeaveConnection to peripheral via GATT unsubscribe. Keep end point allocated until
+ // unsubscribe completes or times out, so platform doesn't close underlying BLE connection before
+ // we're really sure the unsubscribe request has been sent.
+ if (!mBle->mPlatformDelegate->UnsubscribeCharacteristic(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_2_ID))
+ {
+ WeaveLogError(Ble, "WoBle unsub failed");
+
+ // If unsubscribe fails, release BLE connection and free end point immediately.
+ Free();
+ }
+ else if (mConnObj != BLE_CONNECTION_UNINITIALIZED)
+ {
+ // Unsubscribe request was sent successfully, and a confirmation wasn't spontaneously generated or
+ // received in the downcall to UnsubscribeCharacteristic, so set timer for the unsubscribe to complete.
+ err = StartUnsubscribeTimer();
+
+ if (err != BLE_NO_ERROR)
+ {
+ Free();
+ }
+
+ // Mark unsubscribe GATT operation in progress.
+ SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+ }
+ }
+ else // mRole == kBleRole_Peripheral, OR GetFlag(mTimerStateFlags, kConnState_DidBeginSubscribe) == false...
+ {
+ Free();
+ }
+ }
+}
+
+void BLEEndPoint::DoCloseCallback(uint8_t state, uint8_t flags, BLE_ERROR err)
+{
+ if (state == kState_Connecting)
+ {
+ if (OnConnectComplete != NULL)
+ {
+ OnConnectComplete(this, err);
+ }
+ }
+ else
+ {
+ if (OnConnectionClosed != NULL)
+ {
+ OnConnectionClosed(this, err);
+ }
+ }
+
+ // Callback fires once per end point lifetime.
+ OnConnectComplete = NULL;
+ OnConnectionClosed = NULL;
+}
+
+void BLEEndPoint::ReleaseBleConnection()
+{
+ if (mConnObj != BLE_CONNECTION_UNINITIALIZED)
+ {
+ if (GetFlag(mConnStateFlags, kConnState_AutoClose))
+ {
+ WeaveLogProgress(Ble, "Auto-closing end point's BLE connection.");
+ mBle->mPlatformDelegate->CloseConnection(mConnObj);
+ }
+ else
+ {
+ WeaveLogProgress(Ble, "Releasing end point's BLE connection back to application.");
+ mBle->mApplicationDelegate->NotifyWeaveConnectionClosed(mConnObj);
+ }
+
+ // Never release the same BLE connection twice.
+ mConnObj = BLE_CONNECTION_UNINITIALIZED;
+ }
+}
+
+void BLEEndPoint::Free()
+{
+ // Release BLE connection. Will close connection if AutoClose enabled for this end point. Otherwise, informs
+ // application that Weave is done with this BLE connection, and application makes decision about whether to close
+ // and clean up or retain connection.
+ ReleaseBleConnection();
+
+ // Clear fragmentation and reassembly engine's Tx and Rx buffers. Counters will be reset by next engine init.
+ FreeWoBle();
+
+ // Clear pending ack buffer, if any.
+ PacketBuffer::Free(mAckToSend);
+
+ // Cancel all timers.
+ StopConnectTimer();
+ StopReceiveConnectionTimer();
+ StopAckReceivedTimer();
+ StopSendAckTimer();
+ StopUnsubscribeTimer();
+#if WEAVE_ENABLE_WOBLE_TEST
+ mWoBleTest.StopTestTimer();
+ // Clear callback
+ OnCommandReceived = NULL;
+#endif
+
+ // Clear callbacks.
+ OnConnectComplete = NULL;
+ OnMessageReceived = NULL;
+ OnConnectionClosed = NULL;
+
+ // Clear handle to underlying BLE connection.
+ mConnObj = BLE_CONNECTION_UNINITIALIZED;
+
+ // Release the AddRef() that happened when the end point was allocated.
+ Release();
+}
+
+void BLEEndPoint::FreeWoBle()
+{
+ PacketBuffer * buf;
+
+ // Free transmit disassembly buffer
+ buf = mWoBle.TxPacket();
+ mWoBle.ClearTxPacket();
+ PacketBuffer::Free(buf);
+
+ // Free receive reassembly buffer
+ buf = mWoBle.RxPacket();
+ mWoBle.ClearRxPacket();
+ PacketBuffer::Free(buf);
+}
+
+BLE_ERROR BLEEndPoint::Init(BleLayer * bleLayer, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ bool expectInitialAck;
+
+ // Fail if already initialized.
+ VerifyOrExit(mBle == NULL, err = BLE_ERROR_INCORRECT_STATE);
+
+ // Validate args.
+ VerifyOrExit(bleLayer != NULL, err = BLE_ERROR_BAD_ARGS);
+ VerifyOrExit(connObj != BLE_CONNECTION_UNINITIALIZED, err = BLE_ERROR_BAD_ARGS);
+ VerifyOrExit((role == kBleRole_Central || role == kBleRole_Peripheral), err = BLE_ERROR_BAD_ARGS);
+
+ // Null-initialize callbacks and data members.
+ //
+ // Beware this line should we ever use virtuals in this class or its
+ // super(s). See similar lines in Weave::System::Layer end points.
+ memset((void *) this, 0, sizeof(*this));
+
+ // If end point plays peripheral role, expect ack for indication sent as last step of BTP handshake.
+ // If central, periperal's handshake indication 'ack's write sent by central to kick off the BTP handshake.
+ expectInitialAck = (role == kBleRole_Peripheral);
+
+ err = mWoBle.Init(this, expectInitialAck);
+ if (err != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "WoBle init failed");
+ ExitNow();
+ }
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ err = (BLE_ERROR) mTxQueueMutex.Init(mTxQueueMutex);
+ if (err != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "%s: Mutex init failed", __FUNCTION__);
+ ExitNow();
+ }
+ err = mWoBleTest.Init(this);
+ if (err != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "WoBleTest init failed");
+ ExitNow();
+ }
+#endif
+
+ // BleLayerObject initialization:
+ mBle = bleLayer;
+ mRefCount = 1;
+
+ // BLEEndPoint data members:
+ mConnObj = connObj;
+ mRole = role;
+ mConnStateFlags = 0;
+ mTimerStateFlags = 0;
+ SetFlag(mConnStateFlags, kConnState_AutoClose, autoClose);
+ mLocalReceiveWindowSize = 0;
+ mRemoteReceiveWindowSize = 0;
+ mReceiveWindowMaxSize = 0;
+ mSendQueue = NULL;
+ mAckToSend = NULL;
+
+ WeaveLogDebugBleEndPoint(Ble, "initialized local rx window, size = %u", mLocalReceiveWindowSize);
+
+ // End point is ready to connect or receive a connection.
+ mState = kState_Ready;
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::SendCharacteristic(PacketBuffer * buf)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ if (mRole == kBleRole_Central)
+ {
+ if (!SendWrite(buf))
+ {
+ err = BLE_ERROR_GATT_WRITE_FAILED;
+ }
+ else
+ {
+ // Write succeeded, so shrink remote receive window counter by 1.
+ mRemoteReceiveWindowSize -= 1;
+ WeaveLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
+ }
+ }
+ else // (mRole == kBleRole_Peripheral), verified on Init
+ {
+ if (!SendIndication(buf))
+ {
+ err = BLE_ERROR_GATT_INDICATE_FAILED;
+ }
+ else
+ {
+ // Indication succeeded, so shrink remote receive window counter by 1.
+ mRemoteReceiveWindowSize -= 1;
+ WeaveLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Routine to queue the Tx packet with a packet type
+ * kType_Data(0) - data packet
+ * kType_Control(1) - control packet
+ */
+void BLEEndPoint::QueueTx(PacketBuffer * data, PacketType_t type)
+{
+#if WEAVE_ENABLE_WOBLE_TEST
+ WeaveLogDebugBleEndPoint(Ble, "%s: data->%p, type %d, len %d", __FUNCTION__, data, type, data->DataLength());
+ mWoBle.PushPacketTag(data, type);
+#endif
+
+ QueueTxLock();
+
+ if (mSendQueue == NULL)
+ {
+ mSendQueue = data;
+ WeaveLogDebugBleEndPoint(Ble, "%s: Set data as new mSendQueue %p, type %d", __FUNCTION__, mSendQueue, type);
+ }
+ else
+ {
+ mSendQueue->AddToEnd(data);
+ WeaveLogDebugBleEndPoint(Ble, "%s: Append data to mSendQueue %p, type %d", __FUNCTION__, mSendQueue, type);
+ }
+
+ QueueTxUnlock();
+}
+
+BLE_ERROR BLEEndPoint::Send(PacketBuffer * data)
+{
+ WeaveLogDebugBleEndPoint(Ble, "entered Send");
+
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+ VerifyOrExit(IsConnected(mState), err = BLE_ERROR_INCORRECT_STATE);
+
+ // Ensure outgoing message fits in a single contiguous PacketBuffer, as currently required by the
+ // message fragmentation and reassembly engine.
+ if (data->Next() != NULL)
+ {
+ data->CompactHead();
+
+ if (data->Next() != NULL)
+ {
+ err = BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG;
+ ExitNow();
+ }
+ }
+
+ // Add new message to send queue.
+ QueueTx(data, kType_Data);
+ data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
+
+ // Send first fragment of new message, if we can.
+ err = DriveSending();
+ SuccessOrExit(err);
+
+exit:
+ WeaveLogDebugBleEndPoint(Ble, "exiting Send");
+
+ if (data != NULL)
+ {
+ PacketBuffer::Free(data);
+ }
+
+ if (err != BLE_NO_ERROR)
+ {
+ DoClose(kBleCloseFlag_AbortTransmission, err);
+ }
+
+ return err;
+}
+
+bool BLEEndPoint::PrepareNextFragment(PacketBuffer * data, bool & sentAck)
+{
+ // If we have a pending fragment acknowledgement to send, piggyback it on the fragment we're about to transmit.
+ if (GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning))
+ {
+ // Reset local receive window counter.
+ mLocalReceiveWindowSize = mReceiveWindowMaxSize;
+ WeaveLogDebugBleEndPoint(Ble, "reset local rx window on piggyback ack tx, size = %u", mLocalReceiveWindowSize);
+
+ // Tell caller AND fragmenter we have an ack to piggyback.
+ sentAck = true;
+ }
+ else
+ {
+ // No ack to piggyback.
+ sentAck = false;
+ }
+
+ return mWoBle.HandleCharacteristicSend(data, sentAck);
+}
+
+BLE_ERROR BLEEndPoint::SendNextMessage()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ bool sentAck;
+
+ // Get the first queued packet to send
+ QueueTxLock();
+#if WEAVE_ENABLE_WOBLE_TEST
+ // Return if tx queue is empty
+ // Note: DetachTail() does not check an empty queue
+ if (mSendQueue == NULL)
+ {
+ QueueTxUnlock();
+ return err;
+ }
+#endif
+
+ PacketBuffer * data = mSendQueue;
+ mSendQueue = mSendQueue->DetachTail();
+ QueueTxUnlock();
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ // Get and consume the packet tag in message buffer
+ PacketType_t type = mWoBle.PopPacketTag(data);
+ mWoBle.SetTxPacketType(type);
+ mWoBleTest.DoTxTiming(data, WOBLE_TX_START);
+#endif
+
+ // Hand whole message payload to the fragmenter.
+ VerifyOrExit(PrepareNextFragment(data, sentAck), err = BLE_ERROR_WOBLE_PROTOCOL_ABORT);
+ data = NULL; // Ownership passed to fragmenter's tx buf on PrepareNextFragment success.
+
+ // Send first message fragment over the air.
+ WEAVE_FAULT_INJECT(nl::Weave::FaultInjection::kFault_WOBLESend,
+ {
+ if (mRole == kBleRole_Central)
+ {
+ err = BLE_ERROR_GATT_WRITE_FAILED;
+ } else {
+ err = BLE_ERROR_GATT_INDICATE_FAILED;
+ }
+ ExitNow();
+ }
+ );
+ err = SendCharacteristic(mWoBle.TxPacket());
+ SuccessOrExit(err);
+
+ if (sentAck)
+ {
+ // If sent piggybacked ack, stop send-ack timer.
+ StopSendAckTimer();
+ }
+
+ // Start ack received timer, if it's not already running.
+ err = StartAckReceivedTimer();
+ SuccessOrExit(err);
+
+exit:
+ if (data != NULL)
+ {
+ PacketBuffer::Free(data);
+ }
+
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::ContinueMessageSend()
+{
+ BLE_ERROR err;
+ bool sentAck;
+
+ if (!PrepareNextFragment(NULL, sentAck))
+ {
+ // Log BTP error
+ WeaveLogError(Ble, "btp fragmenter error on send!");
+ mWoBle.LogState();
+
+ err = BLE_ERROR_WOBLE_PROTOCOL_ABORT;
+ ExitNow();
+ }
+
+ err = SendCharacteristic(mWoBle.TxPacket());
+ SuccessOrExit(err);
+
+ if (sentAck)
+ {
+ // If sent piggybacked ack, stop send-ack timer.
+ StopSendAckTimer();
+ }
+
+ // Start ack received timer, if it's not already running.
+ err = StartAckReceivedTimer();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleHandshakeConfirmationReceived()
+{
+ WeaveLogDebugBleEndPoint(Ble, "entered HandleHandshakeConfirmationReceived");
+
+ BLE_ERROR err = BLE_NO_ERROR;
+ uint8_t closeFlags = kBleCloseFlag_AbortTransmission;
+
+ // Free capabilities request/response payload.
+ QueueTxLock();
+ mSendQueue = PacketBuffer::FreeHead(mSendQueue);
+ QueueTxUnlock();
+
+ if (mRole == kBleRole_Central)
+ {
+ // Subscribe to characteristic which peripheral will use to send indications. Prompts peripheral to send
+ // BLE transport capabilities indication.
+ VerifyOrExit(mBle->mPlatformDelegate->SubscribeCharacteristic(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_2_ID),
+ err = BLE_ERROR_GATT_SUBSCRIBE_FAILED);
+
+ // We just sent a GATT subscribe request, so make sure to attempt unsubscribe on close.
+ SetFlag(mConnStateFlags, kConnState_DidBeginSubscribe, true);
+
+ // Mark GATT operation in progress for subscribe request.
+ SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+ }
+ else // (mRole == kBleRole_Peripheral), verified on Init
+ {
+ WeaveLogDebugBleEndPoint(Ble, "got peripheral handshake indication confirmation");
+
+ if (mState == kState_Connected) // If we accepted BTP connection...
+ {
+ // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
+ // pending on which to piggyback an ack, send immediate stand-alone ack.
+ if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD && mSendQueue == NULL)
+ {
+ err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
+ SuccessOrExit(err);
+ }
+ else
+ {
+ // Drive sending in case application callend Send() after we sent the handshake indication, but
+ // before the GATT confirmation for this indication was received.
+ err = DriveSending();
+ SuccessOrExit(err);
+ }
+ }
+ else if (mState == kState_Aborting) // Else, if we rejected BTP connection...
+ {
+ closeFlags |= kBleCloseFlag_SuppressCallback;
+ err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
+ ExitNow();
+ }
+ }
+
+exit:
+ WeaveLogDebugBleEndPoint(Ble, "exiting HandleHandshakeConfirmationReceived");
+
+ if (err != BLE_NO_ERROR)
+ {
+ DoClose(closeFlags, err);
+ }
+
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleFragmentConfirmationReceived()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ WeaveLogDebugBleEndPoint(Ble, "entered HandleFragmentConfirmationReceived");
+
+ // Suppress error logging if GATT confirmation overlaps with unsubscribe on final close.
+ if (IsUnsubscribePending())
+ {
+ WeaveLogDebugBleEndPoint(Ble, "send conf rx'd while unsubscribe in flight");
+ ExitNow();
+ }
+
+ // Ensure we're in correct state to receive confirmation of non-handshake GATT send.
+ VerifyOrExit(IsConnected(mState), err = BLE_ERROR_INCORRECT_STATE);
+
+ // TODO PacketBuffer high water mark optimization: if ack pending, but fragmenter state == complete, free fragmenter's
+ // tx buf before sending ack.
+
+ if (GetFlag(mConnStateFlags, kConnState_StandAloneAckInFlight))
+ {
+ // If confirmation was received for stand-alone ack, free its tx buffer.
+ PacketBuffer::Free(mAckToSend);
+ mAckToSend = NULL;
+
+ SetFlag(mConnStateFlags, kConnState_StandAloneAckInFlight, false);
+ }
+
+ // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
+ // pending on which to piggyback an ack, send immediate stand-alone ack.
+ //
+ // This check covers the case where the local receive window has shrunk between transmission and confirmation of
+ // the stand-alone ack, and also the case where a window size < the immediate ack threshold was detected in
+ // Receive(), but the stand-alone ack was deferred due to a pending outbound message fragment.
+ if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD &&
+ !(mSendQueue != NULL || mWoBle.TxState() == WoBle::kState_InProgress) )
+ {
+ err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
+ SuccessOrExit(err);
+ }
+ else
+ {
+ err = DriveSending();
+ SuccessOrExit(err);
+ }
+
+exit:
+ if (err != BLE_NO_ERROR)
+ {
+ DoClose(kBleCloseFlag_AbortTransmission, err);
+ }
+
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleGattSendConfirmationReceived()
+{
+ WeaveLogDebugBleEndPoint(Ble, "entered HandleGattSendConfirmationReceived");
+
+ // Mark outstanding GATT operation as finished.
+ SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, false);
+
+ // If confirmation was for outbound portion of BTP connect handshake...
+ if (!GetFlag(mConnStateFlags, kConnState_CapabilitiesConfReceived))
+ {
+ SetFlag(mConnStateFlags, kConnState_CapabilitiesConfReceived, true);
+
+ return HandleHandshakeConfirmationReceived();
+ }
+ else
+ {
+ return HandleFragmentConfirmationReceived();
+ }
+}
+
+BLE_ERROR BLEEndPoint::DriveStandAloneAck()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ // Stop send-ack timer if running.
+ StopSendAckTimer();
+
+ // If stand-alone ack not already pending, allocate new payload buffer here.
+ if (mAckToSend == NULL)
+ {
+ mAckToSend = PacketBuffer::New();
+ VerifyOrExit(mAckToSend != NULL, err = BLE_ERROR_NO_MEMORY);
+ }
+
+ // Attempt to send stand-alone ack.
+ err = DriveSending();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::DoSendStandAloneAck()
+{
+ WeaveLogDebugBleEndPoint(Ble, "entered DoSendStandAloneAck; sending stand-alone ack");
+
+ // Encode and transmit stand-alone ack.
+ mWoBle.EncodeStandAloneAck(mAckToSend);
+ BLE_ERROR err = SendCharacteristic(mAckToSend);
+ SuccessOrExit(err);
+
+ // Reset local receive window counter.
+ mLocalReceiveWindowSize = mReceiveWindowMaxSize;
+ WeaveLogDebugBleEndPoint(Ble, "reset local rx window on stand-alone ack tx, size = %u", mLocalReceiveWindowSize);
+
+ SetFlag(mConnStateFlags, kConnState_StandAloneAckInFlight, true);
+
+ // Start ack received timer, if it's not already running.
+ err = StartAckReceivedTimer();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::DriveSending()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ WeaveLogDebugBleEndPoint(Ble, "entered DriveSending");
+
+ // If receiver's window is almost closed and we don't have an ack to send, OR we do have an ack to send but
+ // receiver's window is completely empty, OR another GATT operation is in flight, awaiting confirmation...
+ if ((mRemoteReceiveWindowSize <= BTP_WINDOW_NO_ACK_SEND_THRESHOLD &&
+ !GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning) && mAckToSend == NULL) ||
+ (mRemoteReceiveWindowSize == 0) || (GetFlag(mConnStateFlags, kConnState_GattOperationInFlight)))
+ {
+#ifdef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
+ if (mRemoteReceiveWindowSize <= BTP_WINDOW_NO_ACK_SEND_THRESHOLD &&
+ !GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning) && mAckToSend == NULL)
+ {
+ WeaveLogDebugBleEndPoint(Ble, "NO SEND: receive window almost closed, and no ack to send");
+ }
+
+ if (mRemoteReceiveWindowSize == 0)
+ {
+ WeaveLogDebugBleEndPoint(Ble, "NO SEND: remote receive window closed");
+ }
+
+ if (GetFlag(mConnStateFlags, kConnState_GattOperationInFlight))
+ {
+ WeaveLogDebugBleEndPoint(Ble, "NO SEND: Gatt op in flight");
+ }
+#endif
+
+ // Can't send anything.
+ ExitNow();
+ }
+
+ // Otherwise, let's see what we can send.
+
+ if (mAckToSend != NULL) // If immediate, stand-alone ack is pending, send it.
+ {
+ err = DoSendStandAloneAck();
+ SuccessOrExit(err);
+ }
+ else if (mWoBle.TxState() == WoBle::kState_Idle) // Else send next message fragment, if any.
+ {
+ // Fragmenter's idle, let's see what's in the send queue...
+ if (mSendQueue != NULL)
+ {
+ // Transmit first fragment of next whole message in send queue.
+ err = SendNextMessage();
+ SuccessOrExit(err);
+ }
+ else
+ {
+ // Nothing to send!
+ }
+ }
+ else if (mWoBle.TxState() == WoBle::kState_InProgress)
+ {
+ // Send next fragment of message currently held by fragmenter.
+ err = ContinueMessageSend();
+ SuccessOrExit(err);
+ }
+ else if (mWoBle.TxState() == WoBle::kState_Complete)
+ {
+ // Clear fragmenter's pointer to sent message buffer and reset its Tx state.
+ PacketBuffer * sentBuf = mWoBle.TxPacket();
+#if WEAVE_ENABLE_WOBLE_TEST
+ mWoBleTest.DoTxTiming(sentBuf, WOBLE_TX_DONE);
+#endif // WEAVE_ENABLE_WOBLE_TEST
+ mWoBle.ClearTxPacket();
+
+ // Free sent buffer.
+ PacketBuffer::Free(sentBuf);
+ sentBuf = NULL;
+
+ if (mSendQueue != NULL)
+ {
+ // Transmit first fragment of next whole message in send queue.
+ err = SendNextMessage();
+ SuccessOrExit(err);
+ }
+ else if (mState == kState_Closing && !mWoBle.ExpectingAck()) // and mSendQueue is NULL, per above...
+ {
+ // If end point closing, got last ack, and got out-of-order confirmation for last send, finalize close.
+ FinalizeClose(mState, kBleCloseFlag_SuppressCallback, BLE_NO_ERROR);
+ }
+ else
+ {
+ // Nothing to send!
+ }
+ }
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleCapabilitiesRequestReceived(PacketBuffer * data)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ BleTransportCapabilitiesRequestMessage req;
+ BleTransportCapabilitiesResponseMessage resp;
+ PacketBuffer * responseBuf = NULL;
+ uint16_t mtu;
+
+ VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+
+ mState = kState_Connecting;
+
+ // Decode BTP capabilities request.
+ err = BleTransportCapabilitiesRequestMessage::Decode((*data), req);
+ SuccessOrExit(err);
+
+ responseBuf = PacketBuffer::New();
+ VerifyOrExit(responseBuf != NULL, err = BLE_ERROR_NO_MEMORY);
+
+ // Determine BLE connection's negotiated ATT MTU, if possible.
+ if (req.mMtu > 0) // If MTU was observed and provided by central...
+ {
+ mtu = req.mMtu; // Accept central's observation of the MTU.
+ }
+ else // Otherwise, retrieve it via the platform delegate...
+ {
+ mtu = mBle->mPlatformDelegate->GetMTU(mConnObj);
+ }
+
+ // Select fragment size for connection based on ATT MTU.
+ if (mtu > 0) // If one or both device knows connection's MTU...
+ {
+ resp.mFragmentSize =
+ nl::Weave::min(static_cast<uint16_t>(mtu - 3), WoBle::sMaxFragmentSize); // Reserve 3 bytes of MTU for ATT header.
+ }
+ else // Else, if neither device knows MTU...
+ {
+ WeaveLogProgress(Ble, "cannot determine ATT MTU; selecting default fragment size = %u", WoBle::sDefaultFragmentSize);
+ resp.mFragmentSize = WoBle::sDefaultFragmentSize;
+ }
+
+ // Select local and remote max receive window size based on local resources available for both incoming writes AND
+ // GATT confirmations.
+ mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize =
+ nl::Weave::min(req.mWindowSize, static_cast<uint8_t>(BLE_MAX_RECEIVE_WINDOW_SIZE));
+ resp.mWindowSize = mReceiveWindowMaxSize;
+
+ WeaveLogProgress(Ble, "local and remote recv window sizes = %u", resp.mWindowSize);
+
+ // Select BLE transport protocol version from those supported by central, or none if no supported version found.
+ resp.mSelectedProtocolVersion = BleLayer::GetHighestSupportedProtocolVersion(req);
+ WeaveLogProgress(Ble, "selected BTP version %d", resp.mSelectedProtocolVersion);
+
+ if (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_None)
+ {
+ // If BLE transport protocol versions incompatible, prepare to close connection after subscription has been
+ // received and capabilities response has been sent.
+ WeaveLogError(Ble, "incompatible BTP versions; peripheral expected between %d and %d",
+ NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
+ mState = kState_Aborting;
+ }
+ else if ((resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V1) ||
+ (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V2))
+ {
+ // Set Rx and Tx fragment sizes to the same value
+ mWoBle.SetRxFragmentSize(resp.mFragmentSize);
+ mWoBle.SetTxFragmentSize(resp.mFragmentSize);
+ }
+ else // resp.SelectedProtocolVersion >= kBleTransportProtocolVersion_V3
+ {
+ // This is the peripheral, so set Rx fragment size, and leave Tx at default
+ mWoBle.SetRxFragmentSize(resp.mFragmentSize);
+ }
+ WeaveLogProgress(Ble, "using BTP fragment sizes rx %d / tx %d.", mWoBle.GetRxFragmentSize(), mWoBle.GetTxFragmentSize());
+
+ err = resp.Encode(responseBuf);
+ SuccessOrExit(err);
+
+ // Stash capabilities response payload and wait for subscription from central.
+ QueueTx(responseBuf, kType_Data);
+ responseBuf = NULL;
+
+ // Start receive timer. Canceled when end point freed or connection established.
+ err = StartReceiveConnectionTimer();
+ SuccessOrExit(err);
+
+exit:
+ if (responseBuf != NULL)
+ {
+ PacketBuffer::Free(responseBuf);
+ }
+
+ if (data != NULL)
+ {
+ PacketBuffer::Free(data);
+ }
+
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleCapabilitiesResponseReceived(PacketBuffer * data)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ BleTransportCapabilitiesResponseMessage resp;
+
+ VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+
+ // Decode BTP capabilities response.
+ err = BleTransportCapabilitiesResponseMessage::Decode((*data), resp);
+ SuccessOrExit(err);
+
+ VerifyOrExit(resp.mFragmentSize > 0, err = BLE_ERROR_INVALID_FRAGMENT_SIZE);
+
+ WeaveLogProgress(Ble, "peripheral chose BTP version %d; central expected between %d and %d", resp.mSelectedProtocolVersion,
+ NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
+
+ if ((resp.mSelectedProtocolVersion < NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) ||
+ (resp.mSelectedProtocolVersion > NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION))
+ {
+ err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
+ ExitNow();
+ }
+
+ // Set fragment size as minimum of (reported ATT MTU, WoBLE characteristic size)
+ resp.mFragmentSize = nl::Weave::min(resp.mFragmentSize, WoBle::sMaxFragmentSize);
+
+ if ((resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V1) ||
+ (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V2))
+ {
+ mWoBle.SetRxFragmentSize(resp.mFragmentSize);
+ mWoBle.SetTxFragmentSize(resp.mFragmentSize);
+ }
+ else // resp.SelectedProtocolVersion >= kBleTransportProtocolVersion_V3
+ {
+ // This is the central, so set Tx fragement size, and leave Rx at default.
+ mWoBle.SetTxFragmentSize(resp.mFragmentSize);
+ }
+ WeaveLogProgress(Ble, "using BTP fragment sizes rx %d / tx %d.", mWoBle.GetRxFragmentSize(), mWoBle.GetTxFragmentSize());
+
+ // Select local and remote max receive window size based on local resources available for both incoming indications
+ // AND GATT confirmations.
+ mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize = resp.mWindowSize;
+
+ WeaveLogProgress(Ble, "local and remote recv window size = %u", resp.mWindowSize);
+
+ // Shrink local receive window counter by 1, since connect handshake indication requires acknowledgement.
+ mLocalReceiveWindowSize -= 1;
+ WeaveLogDebugBleEndPoint(Ble, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
+
+ // Send ack for connection handshake indication when timer expires. Sequence numbers always start at 0,
+ // and the reassembler's "last received seq num" is initialized to 0 and updated when new fragments are
+ // received from the peripheral, so we don't need to explicitly mark the ack num to send here.
+ err = StartSendAckTimer();
+ SuccessOrExit(err);
+
+ // We've sent a capabilities request write and received a compatible response, so the connect
+ // operation has completed successfully.
+ err = HandleConnectComplete();
+ SuccessOrExit(err);
+
+exit:
+ if (data != NULL)
+ {
+ PacketBuffer::Free(data);
+ }
+
+ return err;
+}
+
+// Returns number of open slots in remote receive window given the input values.
+SequenceNumber_t BLEEndPoint::AdjustRemoteReceiveWindow(SequenceNumber_t lastReceivedAck, SequenceNumber_t maxRemoteWindowSize,
+ SequenceNumber_t newestUnackedSentSeqNum)
+{
+ // Assumption: SequenceNumber_t is uint8_t.
+ // Assumption: Maximum possible sequence number value is UINT8_MAX.
+ // Assumption: Sequence numbers incremented past maximum value wrap to 0.
+ // Assumption: newest unacked sent sequence number never exceeds current (and by extension, new and un-wrapped)
+ // window boundary, so it never wraps relative to last received ack, if new window boundary would not
+ // also wrap.
+
+ // Define new window boundary (inclusive) as uint16_t, so its value can temporarily exceed UINT8_MAX.
+ uint16_t newRemoteWindowBoundary = lastReceivedAck + maxRemoteWindowSize;
+
+ if (newRemoteWindowBoundary > UINT8_MAX && newestUnackedSentSeqNum < lastReceivedAck)
+ {
+ // New window boundary WOULD wrap, and latest unacked seq num already HAS wrapped, so add offset to difference.
+ return (newRemoteWindowBoundary - (newestUnackedSentSeqNum + UINT8_MAX));
+ }
+ else
+ {
+ // Neither values would or have wrapped, OR new boundary WOULD wrap but latest unacked seq num does not, so no
+ // offset required.
+ return (newRemoteWindowBoundary - newestUnackedSentSeqNum);
+ }
+}
+
+BLE_ERROR BLEEndPoint::Receive(PacketBuffer * data)
+{
+ WeaveLogDebugBleEndPoint(Ble, "+++++++++++++++++++++ entered receive");
+ BLE_ERROR err = BLE_NO_ERROR;
+ SequenceNumber_t receivedAck = 0;
+ uint8_t closeFlags = kBleCloseFlag_AbortTransmission;
+ bool didReceiveAck = false;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ if (mWoBle.IsCommandPacket(data))
+ {
+ WeaveLogDebugBleEndPoint(Ble, "%s: Received Control frame: Flags %x", __FUNCTION__, *(data->Start()));
+ }
+ else
+#endif
+ { // This is a special handling on the first Woble data packet, the CapabilitiesRequest.
+ // Suppress error logging if peer's send overlaps with our unsubscribe on final close.
+ if (IsUnsubscribePending())
+ {
+ WeaveLogDebugBleEndPoint(Ble, "characteristic rx'd while unsubscribe in flight");
+ ExitNow();
+ }
+
+ // If we're receiving the first inbound packet of a BLE transport connection handshake...
+ if (!GetFlag(mConnStateFlags, kConnState_CapabilitiesMsgReceived))
+ {
+ if (mRole == kBleRole_Central) // If we're a central receiving a capabilities response indication...
+ {
+ // Ensure end point's in the right state before continuing.
+ VerifyOrExit(mState == kState_Connecting, err = BLE_ERROR_INCORRECT_STATE);
+ SetFlag(mConnStateFlags, kConnState_CapabilitiesMsgReceived, true);
+
+ err = HandleCapabilitiesResponseReceived(data);
+ data = NULL;
+ SuccessOrExit(err);
+ }
+ else // Or, a peripheral receiving a capabilities request write...
+ {
+ // Ensure end point's in the right state before continuing.
+ VerifyOrExit(mState == kState_Ready, err = BLE_ERROR_INCORRECT_STATE);
+ SetFlag(mConnStateFlags, kConnState_CapabilitiesMsgReceived, true);
+
+ err = HandleCapabilitiesRequestReceived(data);
+ data = NULL;
+
+ if (err != BLE_NO_ERROR)
+ {
+ // If an error occurred decoding and handling the capabilities request, release the BLE connection.
+ // Central's connect attempt will time out if peripheral's application decides to keep the BLE
+ // connection open, or fail immediately if the application closes the connection.
+ closeFlags = closeFlags | kBleCloseFlag_SuppressCallback;
+ ExitNow();
+ }
+ }
+
+ // If received data was handshake packet, don't feed it to message reassembler.
+ ExitNow();
+ }
+ } // End handling the CapabilitiesRequest
+
+ WeaveLogDebugBleEndPoint(Ble, "prepared to rx post-handshake btp packet");
+
+ // We've received a post-handshake BTP packet.
+ // Ensure end point's in the right state before continuing.
+ if (!IsConnected(mState))
+ {
+ WeaveLogError(Ble, "ep rx'd packet in bad state");
+ err = BLE_ERROR_INCORRECT_STATE;
+
+ ExitNow();
+ }
+
+ WeaveLogDebugBleEndPoint(Ble, "woble about to rx characteristic, state before:");
+ mWoBle.LogStateDebug();
+
+ // Pass received packet into BTP protocol engine.
+ err = mWoBle.HandleCharacteristicReceived(data, receivedAck, didReceiveAck);
+ data = NULL; // Buffer consumed by protocol engine; either freed or added to message reassembly area.
+
+ WeaveLogDebugBleEndPoint(Ble, "woble rx'd characteristic, state after:");
+ mWoBle.LogStateDebug();
+
+ SuccessOrExit(err);
+
+ // Protocol engine accepted the fragment, so shrink local receive window counter by 1.
+ mLocalReceiveWindowSize -= 1;
+ WeaveLogDebugBleEndPoint(Ble, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
+
+ // Respond to received ack, if any.
+ if (didReceiveAck)
+ {
+ WeaveLogDebugBleEndPoint(Ble, "got btp ack = %u", receivedAck);
+
+ // If ack was rx'd for neweset unacked sent fragment, stop ack received timer.
+ if (!mWoBle.ExpectingAck())
+ {
+ WeaveLogDebugBleEndPoint(Ble, "got ack for last outstanding fragment");
+ StopAckReceivedTimer();
+
+ if (mState == kState_Closing && mSendQueue == NULL && mWoBle.TxState() == WoBle::kState_Idle)
+ {
+ // If end point closing, got confirmation for last send, and waiting for last ack, finalize close.
+ FinalizeClose(mState, kBleCloseFlag_SuppressCallback, BLE_NO_ERROR);
+ ExitNow();
+ }
+ }
+ else // Else there are still sent fragments for which acks are expected, so restart ack received timer.
+ {
+ WeaveLogDebugBleEndPoint(Ble, "still expecting ack(s), restarting timer...");
+ err = RestartAckReceivedTimer();
+ SuccessOrExit(err);
+ }
+
+ WeaveLogDebugBleEndPoint(Ble, "about to adjust remote rx window; got ack num = %u, newest unacked sent seq num = %u, \
+ old window size = %u, max window size = %u",
+ receivedAck, mWoBle.GetNewestUnackedSentSequenceNumber(), mRemoteReceiveWindowSize,
+ mReceiveWindowMaxSize);
+
+ // Open remote device's receive window according to sequence number it just acknowledged.
+ mRemoteReceiveWindowSize =
+ AdjustRemoteReceiveWindow(receivedAck, mReceiveWindowMaxSize, mWoBle.GetNewestUnackedSentSequenceNumber());
+
+ WeaveLogDebugBleEndPoint(Ble, "adjusted remote rx window, new size = %u", mRemoteReceiveWindowSize);
+
+ // Restart message transmission if it was previously paused due to window exhaustion.
+ err = DriveSending();
+ SuccessOrExit(err);
+ }
+
+ // The previous DriveSending() might have generated a piggyback acknowledgement if there was
+ // previously un-acked data. Otherwise, prepare to send acknowledgement for newly received fragment.
+ //
+ // If local receive window is below immediate ack threshold, AND there is no previous stand-alone ack in
+ // flight, AND there is no pending outbound message fragment on which the ack can and will be piggybacked,
+ // send immediate stand-alone ack to reopen window for sender.
+ //
+ // The "GATT operation in flight" check below covers "pending outbound message fragment" by extension, as when
+ // a message has been passed to the end point via Send(), its next outbound fragment must either be in flight
+ // itself, or awaiting the completion of another in-flight GATT operation.
+ //
+ // If any GATT operation is in flight that is NOT a stand-alone ack, the window size will be checked against
+ // this threshold again when the GATT operation is confirmed.
+ if (mWoBle.HasUnackedData())
+ {
+ if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD &&
+ !GetFlag(mConnStateFlags, kConnState_GattOperationInFlight))
+ {
+ WeaveLogDebugBleEndPoint(Ble, "sending immediate ack");
+ err = DriveStandAloneAck();
+ SuccessOrExit(err);
+ }
+ else
+ {
+ WeaveLogDebugBleEndPoint(Ble, "starting send-ack timer");
+
+ // Send ack when timer expires.
+ err = StartSendAckTimer();
+ SuccessOrExit(err);
+ }
+ }
+
+ // If we've reassembled a whole message...
+ if (mWoBle.RxState() == WoBle::kState_Complete)
+ {
+ // Take ownership of message PacketBuffer
+ PacketBuffer * full_packet = mWoBle.RxPacket();
+ mWoBle.ClearRxPacket();
+
+ WeaveLogDebugBleEndPoint(Ble, "reassembled whole msg, len = %d", full_packet->DataLength());
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ // If we have a control message received callback, and end point is not closing...
+ if (mWoBle.RxPacketType() == kType_Control && OnCommandReceived && mState != kState_Closing)
+ {
+ WeaveLogDebugBleEndPoint(Ble, "%s: calling OnCommandReceived, seq# %u, len = %u, type %u", __FUNCTION__, receivedAck,
+ full_packet->DataLength(), mWoBle.RxPacketType());
+ // Pass received control message up the stack.
+ mWoBle.SetRxPacketSeq(receivedAck);
+ OnCommandReceived(this, full_packet);
+ }
+ else
+#endif
+ // If we have a message received callback, and end point is not closing...
+ if (OnMessageReceived && mState != kState_Closing)
+ {
+ // Pass received message up the stack.
+ OnMessageReceived(this, full_packet);
+ }
+ else
+ {
+ // Free received message if there's no one to own it.
+ PacketBuffer::Free(full_packet);
+ }
+ }
+
+exit:
+ if (data != NULL)
+ {
+ PacketBuffer::Free(data);
+ }
+
+ if (err != BLE_NO_ERROR)
+ {
+ DoClose(closeFlags, err);
+ }
+
+ return err;
+}
+
+bool BLEEndPoint::SendWrite(PacketBuffer * buf)
+{
+ // Add reference to message fragment for duration of platform's GATT write attempt. Weave retains partial
+ // ownership of message fragment's PacketBuffer, since this is the same buffer as that of the whole message, just
+ // with a fragmenter-modified payload offset and data length. Buffer must be decref'd (i.e. PacketBuffer::Free'd) by
+ // platform when BLE GATT operation completes.
+ buf->AddRef();
+
+ SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+
+ return mBle->mPlatformDelegate->SendWriteRequest(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_1_ID, buf);
+}
+
+bool BLEEndPoint::SendIndication(PacketBuffer * buf)
+{
+ // Add reference to message fragment for duration of platform's GATT indication attempt. Weave retains partial
+ // ownership of message fragment's PacketBuffer, since this is the same buffer as that of the whole message, just
+ // with a fragmenter-modified payload offset and data length. Buffer must be decref'd (i.e. PacketBuffer::Free'd) by
+ // platform when BLE GATT operation completes.
+ buf->AddRef();
+
+ SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+
+ return mBle->mPlatformDelegate->SendIndication(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_2_ID, buf);
+}
+
+BLE_ERROR BLEEndPoint::StartConnectTimer()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ Weave::System::Error timerErr;
+
+ timerErr = mBle->mSystemLayer->StartTimer(BLE_CONNECT_TIMEOUT_MS, HandleConnectTimeout, this);
+ VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+ SetFlag(mTimerStateFlags, kTimerState_ConnectTimerRunning, true);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::StartReceiveConnectionTimer()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ Weave::System::Error timerErr;
+
+ timerErr = mBle->mSystemLayer->StartTimer(BLE_CONNECT_TIMEOUT_MS, HandleReceiveConnectionTimeout, this);
+ VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+ SetFlag(mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning, true);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::StartAckReceivedTimer()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ Weave::System::Error timerErr;
+
+ if (!GetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning))
+ {
+ timerErr = mBle->mSystemLayer->StartTimer(BTP_ACK_RECEIVED_TIMEOUT_MS, HandleAckReceivedTimeout, this);
+ VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+
+ SetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning, true);
+ }
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::RestartAckReceivedTimer()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ VerifyOrExit(GetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning), err = BLE_ERROR_INCORRECT_STATE);
+
+ StopAckReceivedTimer();
+
+ err = StartAckReceivedTimer();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::StartSendAckTimer()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ Weave::System::Error timerErr;
+
+ WeaveLogDebugBleEndPoint(Ble, "entered StartSendAckTimer");
+
+ if (!GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning))
+ {
+ WeaveLogDebugBleEndPoint(Ble, "starting new SendAckTimer");
+ timerErr = mBle->mSystemLayer->StartTimer(BTP_ACK_SEND_TIMEOUT_MS, HandleSendAckTimeout, this);
+ VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+
+ SetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning, true);
+ }
+
+exit:
+ return err;
+}
+
+BLE_ERROR BLEEndPoint::StartUnsubscribeTimer()
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ Weave::System::Error timerErr;
+
+ timerErr = mBle->mSystemLayer->StartTimer(BLE_UNSUBSCRIBE_TIMEOUT_MS, HandleUnsubscribeTimeout, this);
+ VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+ SetFlag(mTimerStateFlags, kTimerState_UnsubscribeTimerRunning, true);
+
+exit:
+ return err;
+}
+
+void BLEEndPoint::StopConnectTimer()
+{
+ // Cancel any existing connect timer.
+ mBle->mSystemLayer->CancelTimer(HandleConnectTimeout, this);
+ SetFlag(mTimerStateFlags, kTimerState_ConnectTimerRunning, false);
+}
+
+void BLEEndPoint::StopReceiveConnectionTimer()
+{
+ // Cancel any existing receive connection timer.
+ mBle->mSystemLayer->CancelTimer(HandleReceiveConnectionTimeout, this);
+ SetFlag(mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning, false);
+}
+
+void BLEEndPoint::StopAckReceivedTimer()
+{
+ // Cancel any existing ack-received timer.
+ mBle->mSystemLayer->CancelTimer(HandleAckReceivedTimeout, this);
+ SetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning, false);
+}
+
+void BLEEndPoint::StopSendAckTimer()
+{
+ // Cancel any existing send-ack timer.
+ mBle->mSystemLayer->CancelTimer(HandleSendAckTimeout, this);
+ SetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning, false);
+}
+
+void BLEEndPoint::StopUnsubscribeTimer()
+{
+ // Cancel any existing unsubscribe timer.
+ mBle->mSystemLayer->CancelTimer(HandleUnsubscribeTimeout, this);
+ SetFlag(mTimerStateFlags, kTimerState_UnsubscribeTimerRunning, false);
+}
+
+void BLEEndPoint::HandleConnectTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+ BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+ // Check for event-based timer race condition.
+ if (GetFlag(ep->mTimerStateFlags, kTimerState_ConnectTimerRunning))
+ {
+ WeaveLogError(Ble, "connect handshake timed out, closing ep %p", ep);
+ SetFlag(ep->mTimerStateFlags, kTimerState_ConnectTimerRunning, false);
+ ep->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_CONNECT_TIMED_OUT);
+ }
+}
+
+void BLEEndPoint::HandleReceiveConnectionTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+ BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+ // Check for event-based timer race condition.
+ if (GetFlag(ep->mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning))
+ {
+ WeaveLogError(Ble, "receive handshake timed out, closing ep %p", ep);
+ SetFlag(ep->mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning, false);
+ ep->DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, BLE_ERROR_RECEIVE_TIMED_OUT);
+ }
+}
+
+void BLEEndPoint::HandleAckReceivedTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+ BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+ // Check for event-based timer race condition.
+ if (GetFlag(ep->mTimerStateFlags, kTimerState_AckReceivedTimerRunning))
+ {
+ WeaveLogError(Ble, "ack recv timeout, closing ep %p", ep);
+ ep->mWoBle.LogStateDebug();
+ SetFlag(ep->mTimerStateFlags, kTimerState_AckReceivedTimerRunning, false);
+ ep->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_FRAGMENT_ACK_TIMED_OUT);
+ }
+}
+
+void BLEEndPoint::HandleSendAckTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+ BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+ // Check for event-based timer race condition.
+ if (GetFlag(ep->mTimerStateFlags, kTimerState_SendAckTimerRunning))
+ {
+ SetFlag(ep->mTimerStateFlags, kTimerState_SendAckTimerRunning, false);
+
+ // If previous stand-alone ack isn't still in flight...
+ if (!GetFlag(ep->mConnStateFlags, kConnState_StandAloneAckInFlight))
+ {
+ BLE_ERROR sendErr = ep->DriveStandAloneAck();
+
+ if (sendErr != BLE_NO_ERROR)
+ {
+ ep->DoClose(kBleCloseFlag_AbortTransmission, sendErr);
+ }
+ }
+ }
+}
+
+void BLEEndPoint::HandleUnsubscribeTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+ BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+ // Check for event-based timer race condition.
+ if (GetFlag(ep->mTimerStateFlags, kTimerState_UnsubscribeTimerRunning))
+ {
+ WeaveLogError(Ble, "unsubscribe timed out, ble ep %p", ep);
+ SetFlag(ep->mTimerStateFlags, kTimerState_UnsubscribeTimerRunning, false);
+ ep->HandleUnsubscribeComplete();
+ }
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/BLEEndPoint.h b/src/ble/BLEEndPoint.h
new file mode 100644
index 0000000..4894ef5
--- /dev/null
+++ b/src/ble/BLEEndPoint.h
@@ -0,0 +1,235 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines a Bluetooth Low Energy (BLE) connection
+ * endpoint abstraction for the byte-streaming,
+ * connection-oriented Weave over Bluetooth Low Energy (WoBLE)
+ * Bluetooth Transport Protocol (BTP).
+ *
+ */
+
+#ifndef BLEENDPOINT_H_
+#define BLEENDPOINT_H_
+
+#include <SystemLayer/SystemMutex.h>
+
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/WoBle.h>
+#if WEAVE_ENABLE_WOBLE_TEST
+#include <BleLayer/WoBleTest.h>
+#endif
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+enum
+{
+ kBleCloseFlag_SuppressCallback = 0x01,
+ kBleCloseFlag_AbortTransmission = 0x02
+};
+
+// Forward declarations
+class BleLayer;
+class BleEndPointPool;
+#if WEAVE_ENABLE_WOBLE_TEST
+class WoBleTest;
+#endif
+
+class NL_DLL_EXPORT BLEEndPoint : public BleLayerObject
+{
+ friend class BleLayer;
+ friend class BleEndPointPool;
+#if WEAVE_ENABLE_WOBLE_TEST
+ friend class WoBleTest;
+#endif
+
+public:
+ typedef uint64_t AlignT;
+
+ // Public data members:
+ enum
+ {
+ kState_Ready = 0,
+ kState_Connecting = 1,
+ kState_Aborting = 2,
+ kState_Connected = 3,
+ kState_Closing = 4,
+ kState_Closed = 5
+ } mState; // [READ-ONLY] End point connection state. Refers to state of Weave over
+ // BLE transport protocol connection, not of underlying BLE connection.
+
+ // Public function pointers:
+ typedef void (*OnConnectCompleteFunct)(BLEEndPoint * endPoint, BLE_ERROR err);
+ OnConnectCompleteFunct OnConnectComplete;
+
+ typedef void (*OnMessageReceivedFunct)(BLEEndPoint * endPoint, PacketBuffer * msg);
+ OnMessageReceivedFunct OnMessageReceived;
+
+ typedef void (*OnConnectionClosedFunct)(BLEEndPoint * endPoint, BLE_ERROR err);
+ OnConnectionClosedFunct OnConnectionClosed;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ typedef void (*OnCommandReceivedFunct)(BLEEndPoint * endPoint, PacketBuffer * msg);
+ OnCommandReceivedFunct OnCommandReceived;
+ inline void SetOnCommandReceivedCB(OnCommandReceivedFunct cb) { OnCommandReceived = cb; };
+ WoBleTest mWoBleTest;
+ inline void SetTxWindowSize(uint8_t size) { mRemoteReceiveWindowSize = size; };
+ inline void SetRxWindowSize(uint8_t size) { mReceiveWindowMaxSize = size; };
+#endif
+
+public:
+ // Public functions:
+ BLE_ERROR Send(PacketBuffer * data);
+ BLE_ERROR Receive(PacketBuffer * data);
+ BLE_ERROR StartConnect(void);
+
+ bool IsUnsubscribePending(void) const;
+ void Close(void);
+ void Abort(void);
+
+private:
+ // Private data members:
+ enum ConnectionStateFlags
+ {
+ kConnState_AutoClose = 0x01, // End point should close underlying BLE conn on BTP close.
+ kConnState_CapabilitiesConfReceived = 0x02, // GATT confirmation received for sent capabilities req/resp.
+ kConnState_CapabilitiesMsgReceived = 0x04, // Capabilities request or response message received.
+ kConnState_DidBeginSubscribe = 0x08, // GATT subscribe request sent; must unsubscribe on close.
+ kConnState_StandAloneAckInFlight = 0x10, // Stand-alone ack in flight, awaiting GATT confirmation.
+ kConnState_GattOperationInFlight = 0x20 // GATT write, indication, subscribe, or unsubscribe in flight,
+ // awaiting GATT confirmation.
+ };
+
+ enum TimerStateFlags
+ {
+ kTimerState_ConnectTimerRunning = 0x01, // BTP connect completion timer running.
+ kTimerState_ReceiveConnectionTimerRunning = 0x02, // BTP receive connection completion timer running.
+ kTimerState_AckReceivedTimerRunning = 0x04, // Ack received timer running due to unacked sent fragment.
+ kTimerState_SendAckTimerRunning = 0x08, // Send ack timer running; indicates pending ack to send.
+ kTimerState_UnsubscribeTimerRunning = 0x10, // Unsubscribe completion timer running.
+#if WEAVE_ENABLE_WOBLE_TEST
+ kTimerState_UnderTestTimerRunnung = 0x80 // running throughput Tx test
+#endif
+ };
+
+ // BLE connection to which an end point is uniquely bound. Type BLE_CONNECTION_OBJECT is defined by the platform or
+ // void* by default. This object is passed back to the platform delegate with each call to send traffic over or
+ // modify the state of the underlying BLE connection.
+ BLE_CONNECTION_OBJECT mConnObj;
+
+ // Queue of outgoing messages to send when current WoBle transmission completes.
+ //
+ // Re-used during connection setup to cache capabilities request and response payloads; payloads are freed when
+ // connection is established.
+ PacketBuffer * mSendQueue;
+
+ // Pending stand-alone BTP acknolwedgement. Pre-empts regular send queue or fragmented message transmission in
+ // progress.
+ PacketBuffer * mAckToSend;
+
+ WoBle mWoBle;
+ BleRole mRole;
+ uint8_t mConnStateFlags;
+ uint8_t mTimerStateFlags;
+ SequenceNumber_t mLocalReceiveWindowSize;
+ SequenceNumber_t mRemoteReceiveWindowSize;
+ SequenceNumber_t mReceiveWindowMaxSize;
+#if WEAVE_ENABLE_WOBLE_TEST
+ nl::Weave::System::Mutex mTxQueueMutex; // For MT-safe Tx queuing
+#endif
+
+private:
+ // Private functions:
+ BLEEndPoint(void); // not defined
+ ~BLEEndPoint(void); // not defined
+
+ BLE_ERROR Init(BleLayer * bleLayer, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose);
+ bool IsConnected(uint8_t state) const;
+ void DoClose(uint8_t flags, BLE_ERROR err);
+
+ // Transmit path:
+ BLE_ERROR DriveSending(void);
+ BLE_ERROR DriveStandAloneAck(void);
+ bool PrepareNextFragment(PacketBuffer * data, bool & sentAck);
+ BLE_ERROR SendNextMessage(void);
+ BLE_ERROR ContinueMessageSend(void);
+ BLE_ERROR DoSendStandAloneAck(void);
+ BLE_ERROR SendCharacteristic(PacketBuffer * buf);
+ bool SendIndication(PacketBuffer * buf);
+ bool SendWrite(PacketBuffer * buf);
+
+ // Receive path:
+ BLE_ERROR HandleConnectComplete(void);
+ BLE_ERROR HandleReceiveConnectionComplete(void);
+ void HandleSubscribeReceived(void);
+ void HandleSubscribeComplete(void);
+ void HandleUnsubscribeComplete(void);
+ BLE_ERROR HandleGattSendConfirmationReceived(void);
+ BLE_ERROR HandleHandshakeConfirmationReceived(void);
+ BLE_ERROR HandleFragmentConfirmationReceived(void);
+ BLE_ERROR HandleCapabilitiesRequestReceived(PacketBuffer * data);
+ BLE_ERROR HandleCapabilitiesResponseReceived(PacketBuffer * data);
+ SequenceNumber_t AdjustRemoteReceiveWindow(SequenceNumber_t lastReceivedAck, SequenceNumber_t maxRemoteWindowSize,
+ SequenceNumber_t newestUnackedSentSeqNum);
+
+ // Timer control functions:
+ BLE_ERROR StartConnectTimer(void); // Start connect timer.
+ BLE_ERROR StartReceiveConnectionTimer(void); // Start receive connection timer.
+ BLE_ERROR StartAckReceivedTimer(void); // Start ack-received timer if it's not already running.
+ BLE_ERROR RestartAckReceivedTimer(void); // Restart ack-received timer.
+ BLE_ERROR StartSendAckTimer(void); // Start send-ack timer if it's not already running.
+ BLE_ERROR StartUnsubscribeTimer(void);
+ void StopConnectTimer(void); // Stop connect timer.
+ void StopReceiveConnectionTimer(void); // Stop receive connection timer.
+ void StopAckReceivedTimer(void); // Stop ack-received timer.
+ void StopSendAckTimer(void); // Stop send-ack timer.
+ void StopUnsubscribeTimer(void); // Stop unsubscribe timer.
+
+ // Timer expired callbacks:
+ static void HandleConnectTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+ static void HandleReceiveConnectionTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+ static void HandleAckReceivedTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+ static void HandleSendAckTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+ static void HandleUnsubscribeTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+
+ // Close functions:
+ void DoCloseCallback(uint8_t state, uint8_t flags, BLE_ERROR err);
+ void FinalizeClose(uint8_t state, uint8_t flags, BLE_ERROR err);
+ void ReleaseBleConnection(void);
+ void Free(void);
+ void FreeWoBle(void);
+
+ // Mutex lock on Tx queue. Used only in WoBle test build for now.
+#if WEAVE_ENABLE_WOBLE_TEST
+ inline void QueueTxLock() { mTxQueueMutex.Lock(); };
+ inline void QueueTxUnlock() { mTxQueueMutex.Unlock(); };
+#else
+ inline void QueueTxLock() { };
+ inline void QueueTxUnlock() { };
+#endif
+ void QueueTx(PacketBuffer * data, PacketType_t type);
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEENDPOINT_H_ */
diff --git a/src/ble/Ble.h b/src/ble/Ble.h
new file mode 100644
index 0000000..a653bc0
--- /dev/null
+++ b/src/ble/Ble.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright (c) 2015-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 is an umbrella header for the Ble library, a
+ * portable Bluetooth Low Energy (BLE), also known as Bluetooth
+ * Smart, layer for transporting Weave over a BLE connection.
+ *
+ */
+
+#ifndef NL_BLE_H
+#define NL_BLE_H
+
+#include <BleLayer/BleApplicationDelegate.h>
+#include <BleLayer/BleConfig.h>
+#include <BleLayer/BLEEndPoint.h>
+#include <BleLayer/BleError.h>
+#include <BleLayer/Ble.h>
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/BlePlatformDelegate.h>
+#include <BleLayer/BleUUID.h>
+#include <BleLayer/WoBle.h>
+
+/**
+ * @namespace nl::Ble
+ *
+ * @brief
+ * This namespace includes all interfaces within Weave for
+ * Bluetooth Low Energy (BLE), also known as Bluetooth Smart.
+ */
+
+#endif // NL_BLE_H
diff --git a/src/ble/BleApplicationDelegate.h b/src/ble/BleApplicationDelegate.h
new file mode 100644
index 0000000..c2f2111
--- /dev/null
+++ b/src/ble/BleApplicationDelegate.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines the interface for upcalls from BleLayer
+ * to a client application.
+ */
+
+#ifndef BLEAPPLICATIONDELEGATE_H_
+#define BLEAPPLICATIONDELEGATE_H_
+
+#include <Weave/Support/NLDLLUtil.h>
+
+#include "BleConfig.h"
+
+namespace nl {
+namespace Ble {
+
+// Platform-agnostic BLE interface
+class NL_DLL_EXPORT BleApplicationDelegate
+{
+public:
+ // Weave calls this function once it closes the last BLEEndPoint associated with a BLE given connection object.
+ // A call to this function means Weave no longer cares about the state of the given BLE connection.
+ // The application can use this callback to e.g. close the underlying BLE conection if it is no longer needed,
+ // decrement the connection's refcount if it has one, or perform any other sort of cleanup as desired.
+ virtual void NotifyWeaveConnectionClosed(BLE_CONNECTION_OBJECT connObj) = 0;
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEAPPLICATIONDELEGATE_H_ */
diff --git a/src/ble/BleConfig.h b/src/ble/BleConfig.h
new file mode 100644
index 0000000..0a4bf56
--- /dev/null
+++ b/src/ble/BleConfig.h
@@ -0,0 +1,254 @@
+/*
+ *
+ * Copyright (c) 2014-2018 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines default compile-time configuration constants
+ * for the Nest BleLayer, a Bluetooth Low Energy communications
+ * abstraction layer.
+ *
+ * Package integrators that wish to override these values should
+ * either use preprocessor definitions or create a project-
+ * specific BleProjectConfig.h header and then assert
+ * HAVE_BLEPROJECTCONFIG_H via the package configuration tool
+ * via --with-weave-ble-project-includes=DIR where DIR is the
+ * directory that contains the header.
+ *
+ * NOTE WELL: On some platforms, this header is included by C-language programs.
+ *
+ */
+
+#ifndef BLECONFIG_H_
+#define BLECONFIG_H_
+
+#include <SystemLayer/SystemConfig.h>
+
+/* Include a project-specific configuration file, if defined.
+ *
+ * An application or module that incorporates Weave can define a project configuration
+ * file to override standard BLE Layer configuration with application-specific values.
+ * The project config file is typically located outside the OpenWeave source tree,
+ * alongside the source code for the application.
+ */
+#ifdef BLE_PROJECT_CONFIG_INCLUDE
+#include BLE_PROJECT_CONFIG_INCLUDE
+#endif
+
+/* Include a platform-specific configuration file, if defined.
+ *
+ * A platform configuration file contains overrides to standard BLE Layer configuration
+ * that are specific to the platform or OS on which Weave is running. It is typically
+ * provided as apart of an adaptation layer that adapts OpenWeave to the target
+ * environment. This adaptation layer may be included in the OpenWeave source tree
+ * itself or implemented externally.
+ */
+#ifdef BLE_PLATFORM_CONFIG_INCLUDE
+#include BLE_PLATFORM_CONFIG_INCLUDE
+#endif
+
+// clang-format off
+
+/**
+ * @def BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+ *
+ * @brief
+ * This boolean configuration option is (1) if the obsolescent interfaces
+ * of the BLE layer that now reside elsewhere, for example, in the Weave System
+ * Layer or the INET layer, are aliased for transitional purposes.
+ *
+ */
+#ifndef BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#define BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES 0
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#if !WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#error "REQUIRED: if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES then WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES!"
+#endif // !WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+#include <InetLayer/InetBuffer.h>
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+/**
+ * @def BLE_LAYER_NUM_BLE_ENDPOINTS
+ *
+ * @brief
+ * This defines the number of BLEEndPoint objects allocated for use by the
+ * BleLayer subsystem. Value should be defined as the minimum of (max number
+ * of simultaneous BLE connections the system supports, max number of
+ * simultaneous BLE connections the application will establish).
+ */
+#ifndef BLE_LAYER_NUM_BLE_ENDPOINTS
+#define BLE_LAYER_NUM_BLE_ENDPOINTS 1
+#endif // BLE_LAYER_NUM_BLE_ENDPOINTS
+
+#if (BLE_LAYER_NUM_BLE_ENDPOINTS < 1)
+#error "BLE_LAYER_NUM_BLE_ENDPOINTS must be greater than 0. configure options may be used to disable Weave over BLE."
+#endif
+
+/**
+ * @def BLE_CONNECTION_OBJECT
+ *
+ * @brief
+ * This defines the type of BLE_CONNECTION_OBJECT parameters passed between
+ * BLE platform code and the BleLayer subsystem.
+ *
+ * This type must support operator == such that BLE_CONNECTION_OBJECT instances
+ * which refer to the same BLE connection are considered equivalent.
+ *
+ * Most platforms should be able to retain this type's default definition as
+ * (void *), and pass [pointers to] connection handles generated by their
+ * platform interface where BLE_CONNECTION_OBJECT arguments are required by
+ * BleLayer input functions.
+ *
+ */
+#ifndef BLE_CONNECTION_OBJECT
+#define BLE_CONNECTION_OBJECT void*
+#endif // BLE_CONNECTION_OBJECT
+
+/**
+ * @def BLE_CONFIG_BLUEZ_MTU_FEATURE
+ *
+ * @brief
+ * This define if BLUEZ MTU FEATURE is enabled or not
+ */
+#ifndef BLE_CONFIG_BLUEZ_MTU_FEATURE
+#define BLE_CONFIG_BLUEZ_MTU_FEATURE 0
+#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
+
+/**
+ * @def BLE_CONNECTION_UNINITIALIZED
+ *
+ * @brief
+ * This defines the value of an uninitialized BLE_CONNECTION_OBJECT.
+ *
+ */
+#ifndef BLE_CONNECTION_UNINITIALIZED
+#define BLE_CONNECTION_UNINITIALIZED NULL
+#endif // BLE_CONNECTION_UNINITIALIZED
+
+/**
+ * @def BLE_READ_REQUEST_CONTEXT
+ *
+ * @brief
+ * This defines the type of BLE_READ_REQUEST_CONTEXT parameters passed between
+ * BLE platform code and the BleLayer subsystem.
+ *
+ * BLE_READ_REQUEST_CONTEXT objects are handed to BleLayer when a read request
+ * is received by the BLE platform. BleLayer hands these objects back to the
+ * appropriate platform delegate function when sending the read response.
+ *
+ */
+#ifndef BLE_READ_REQUEST_CONTEXT
+#define BLE_READ_REQUEST_CONTEXT void*
+#endif // BLE_READ_REQUEST_CONTEXT
+
+/**
+ * @def BLE_MAX_RECEIVE_WINDOW_SIZE
+ *
+ * @brief
+ * This is the maximum allowed size of a BLE end point's receive window, defined as the number of fragments the
+ * end point may reliably receive without BTP-layer acknowledgement. This value should be no larger than the floor
+ * of ONE-HALF the total number of slots or buffers reserved for GATT operations at any point along a platform's
+ * BLE pipeline. The BLE layer reserves all of these buffers for its own use - one half for incoming GATT writes or
+ * indications, and the other half for incoming GATT confirmations.
+ *
+ * This value must be greater than 1, or race condition avoidance logic will prevent send the on remote device. This
+ * logic prevents any send with no piggybacked ack when the receiver's window has only 1 slot open. Without this
+ * logic, simultaneous data transmissions could fill both receiver's windows, leaving no room for the acks required
+ * to re-open them. Both senders would wedge, and the BTP connection would stall.
+ *
+ * This value must also exceed (BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD + 1), or ***immediate*** stand-alone
+ * acks will forever be sent without delay in response to one another as each peer's window size dips below
+ * BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD with receipt of any single message fragment.
+ *
+ * Default value of 3 is absolute minimum for stable performance, and an attempt to ensure safe window sizes on new
+ * platforms.
+ *
+ */
+#ifndef BLE_MAX_RECEIVE_WINDOW_SIZE
+#define BLE_MAX_RECEIVE_WINDOW_SIZE 3
+#endif
+
+#if (BLE_MAX_RECEIVE_WINDOW_SIZE < 3)
+#error "BLE_MAX_RECEIVE_WINDOW_SIZE must be greater than 2 for BLE transport protocol stability."
+#endif
+
+/**
+ * @def BLE_CONFIG_ERROR_TYPE
+ *
+ * @brief
+ * This defines the data type used to represent errors for the
+ * BleLayer subsystem.
+ *
+ */
+#ifndef BLE_CONFIG_ERROR_TYPE
+#include <stdint.h>
+#define BLE_CONFIG_ERROR_TYPE int32_t
+#endif // BLE_CONFIG_ERROR_TYPE
+
+/**
+ * @def BLE_CONFIG_NO_ERROR
+ *
+ * @brief
+ * This defines the BleLayer error code for no error or success.
+ *
+ */
+#ifndef BLE_CONFIG_NO_ERROR
+#define BLE_CONFIG_NO_ERROR 0
+#endif // BLE_CONFIG_NO_ERROR
+
+/**
+ * @def BLE_CONFIG_ERROR_MIN
+ *
+ * @brief
+ * This defines the base or minimum BleLayer error number range.
+ *
+ */
+#ifndef BLE_CONFIG_ERROR_MIN
+#define BLE_CONFIG_ERROR_MIN 6000
+#endif // BLE_CONFIG_ERROR_MIN
+
+/**
+ * @def BLE_CONFIG_ERROR_MAX
+ *
+ * @brief
+ * This defines the top or maximum BleLayer error number range.
+ *
+ */
+#ifndef BLE_CONFIG_ERROR_MAX
+#define BLE_CONFIG_ERROR_MAX 6999
+#endif // BLE_CONFIG_ERROR_MAX
+
+/**
+ * @def _BLE_CONFIG_ERROR
+ *
+ * @brief
+ * This defines a mapping function for BleLayer errors that allows
+ * mapping such errors into a platform- or system-specific range.
+ *
+ */
+#ifndef _BLE_CONFIG_ERROR
+#define _BLE_CONFIG_ERROR(e) (BLE_ERROR_MIN + (e))
+#endif // _BLE_CONFIG_ERROR
+
+// clang-format on
+
+#include <Weave/Core/WeaveConfig.h>
+
+#endif /* BLECONFIG_H_ */
diff --git a/src/ble/BleError.cpp b/src/ble/BleError.cpp
new file mode 100644
index 0000000..9d86de6
--- /dev/null
+++ b/src/ble/BleError.cpp
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright (c) 2019 Google LLC.
+ * All rights reserved.
+ *
+ * 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 contains functions for working with BLE Layer errors.
+ */
+
+#include <stddef.h>
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/BleError.h>
+
+#include <Weave/Support/ErrorStr.h>
+
+namespace nl {
+namespace Ble {
+
+/**
+ * Register a text error formatter for BLE Layer errors.
+ */
+void RegisterBleLayerErrorFormatter(void)
+{
+ static ErrorFormatter sBleLayerErrorFormatter =
+ {
+ FormatBleLayerError,
+ NULL
+ };
+
+ RegisterErrorFormatter(&sBleLayerErrorFormatter);
+}
+
+bool FormatBleLayerError(char * buf, uint16_t bufSize, int32_t err)
+{
+ const char * desc = NULL;
+
+ if (err < BLE_ERROR_MIN || err > BLE_ERROR_MAX)
+ {
+ return false;
+ }
+
+#if !WEAVE_CONFIG_SHORT_ERROR_STR
+ switch (err)
+ {
+ case BLE_ERROR_BAD_ARGS : desc = "Bad arguments"; break;
+ case BLE_ERROR_INCORRECT_STATE : desc = "Incorrect state"; break;
+ case BLE_ERROR_NO_ENDPOINTS : desc = "No more BLE endpoints"; break;
+ case BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK : desc = "No Weave over BLE connection received callback set"; break;
+ case BLE_ERROR_CENTRAL_UNSUBSCRIBED : desc = "BLE central unsubscribed"; break;
+ case BLE_ERROR_GATT_SUBSCRIBE_FAILED : desc = "GATT subscribe operation failed"; break;
+ case BLE_ERROR_GATT_UNSUBSCRIBE_FAILED : desc = "GATT unsubscribe operation failed"; break;
+ case BLE_ERROR_GATT_WRITE_FAILED : desc = "GATT write characteristic operation failed"; break;
+ case BLE_ERROR_GATT_INDICATE_FAILED : desc = "GATT indicate characteristic operation failed"; break;
+ case BLE_ERROR_NOT_IMPLEMENTED : desc = "Not implemented"; break;
+ case BLE_ERROR_WOBLE_PROTOCOL_ABORT : desc = "BLE transport protocol fired abort"; break;
+ case BLE_ERROR_REMOTE_DEVICE_DISCONNECTED : desc = "Remote device closed BLE connection"; break;
+ case BLE_ERROR_APP_CLOSED_CONNECTION : desc = "Application closed BLE connection"; break;
+ case BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG : desc = "Outbound message too big"; break;
+ case BLE_ERROR_NOT_WEAVE_DEVICE : desc = "BLE device doesn't seem to support Weave"; break;
+ case BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS : desc = "Incompatible BLE transport protocol versions"; break;
+ case BLE_ERROR_NO_MEMORY : desc = "No memory"; break;
+ case BLE_ERROR_MESSAGE_INCOMPLETE : desc = "Message incomplete"; break;
+ case BLE_ERROR_INVALID_FRAGMENT_SIZE : desc = "Invalid fragment size"; break;
+ case BLE_ERROR_START_TIMER_FAILED : desc = "Start timer failed"; break;
+ case BLE_ERROR_CONNECT_TIMED_OUT : desc = "Connect handshake timed out"; break;
+ case BLE_ERROR_RECEIVE_TIMED_OUT : desc = "Receive handshake timed out"; break;
+ case BLE_ERROR_INVALID_MESSAGE : desc = "Invalid message"; break;
+ case BLE_ERROR_FRAGMENT_ACK_TIMED_OUT : desc = "Message fragment acknowledgement timed out"; break;
+ case BLE_ERROR_KEEP_ALIVE_TIMED_OUT : desc = "Keep-alive receipt timed out"; break;
+ case BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK : desc = "Missing required callback"; break;
+ case BLE_ERROR_INVALID_ACK : desc = "Received invalid BLE transport protocol fragment acknowledgement"; break;
+ case BLE_ERROR_REASSEMBLER_MISSING_DATA : desc = "BLE message reassembler did not receive enough data"; break;
+ case BLE_ERROR_INVALID_BTP_HEADER_FLAGS : desc = "Received invalid BLE transport protocol header flags"; break;
+ case BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER : desc = "Received invalid BLE transport protocol sequence number"; break;
+ case BLE_ERROR_REASSEMBLER_INCORRECT_STATE : desc = "BLE message reassembler received packet in incorrect state"; break;
+ case BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG : desc = "Message received by BLE message reassembler was too large"; break;
+ }
+#endif // !WEAVE_CONFIG_SHORT_ERROR_STR
+
+ FormatError(buf, bufSize, "Ble", err, desc);
+
+ return true;
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif // CONFIG_NETWORK_LAYER_BLE
diff --git a/src/ble/BleError.h b/src/ble/BleError.h
new file mode 100644
index 0000000..df00d3d
--- /dev/null
+++ b/src/ble/BleError.h
@@ -0,0 +1,430 @@
+/*
+ *
+ * Copyright (c) 2015-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines constants for the Nest BleLayer subsystem.
+ *
+ * Error types, ranges, and mappings overrides may be made by
+ * defining the appropriate BLE_CONFIG_* or _BLE_CONFIG_*
+ * macros.
+ *
+ * NOTE WELL: On some platforms, this header is included by C-language programs.
+ *
+ */
+
+#ifndef BLEERROR_H_
+#define BLEERROR_H_
+
+#include "BleConfig.h"
+
+// clang-format off
+
+/**
+ * @def BLE_NO_ERROR
+ *
+ * @brief
+ * This defines the BleLayer error code for success or no
+ * error. This value may be configured via #BLE_CONFIG_NO_ERROR.
+ *
+ */
+#define BLE_NO_ERROR BLE_CONFIG_NO_ERROR
+
+/**
+ * @def BLE_ERROR_MIN
+ *
+ * @brief
+ * This defines the base or minimum value of the BleLayer error number
+ * range. This value may be configured via #BLE_CONFIG_ERROR_MIN.
+ *
+ */
+#define BLE_ERROR_MIN BLE_CONFIG_ERROR_MIN
+
+/**
+ * @def BLE_ERROR_MAX
+ *
+ * @brief
+ * This defines the top or maximum value of the BleLayer error number
+ * range. This value may be configured via #BLE_CONFIG_ERROR_MAX.
+ *
+ */
+#define BLE_ERROR_MAX BLE_CONFIG_ERROR_MAX
+
+/**
+ * @def _BLE_ERROR(e)
+ *
+ * @brief
+ * This defines a mapping function for BleLayer errors that allows
+ * mapping such errors into a platform- or system-specific
+ * range. This function may be configured via
+ * #_BLE_CONFIG_ERROR.
+ *
+ * @param[in] e The BleLayer error to map.
+ *
+ * @return The mapped BleLayer error.
+ *
+ */
+#define _BLE_ERROR(e) _BLE_CONFIG_ERROR(e)
+
+/**
+ * @typedef BLE_ERROR
+ *
+ * The basic type for all BleLayer errors.
+ *
+ * This is defined to a platform- or system-specific type.
+ *
+ */
+typedef BLE_CONFIG_ERROR_TYPE BLE_ERROR;
+
+/**
+ * @name Error Definitions
+ *
+ * @{
+ */
+
+/**
+ * @def BLE_ERROR_BAD_ARGS
+ *
+ * @brief
+ * An invalid argument or arguments were supplied.
+ *
+ */
+#define BLE_ERROR_BAD_ARGS _BLE_ERROR(0)
+
+/**
+ * @def BLE_ERROR_INCORRECT_STATE
+ *
+ * @brief
+ * An unexpected state was encountered.
+ *
+ */
+#define BLE_ERROR_INCORRECT_STATE _BLE_ERROR(1)
+
+/**
+ * @def BLE_ERROR_NO_ENDPOINTS
+ *
+ * @brief
+ * No BLE endpoint is available.
+ *
+ */
+#define BLE_ERROR_NO_ENDPOINTS _BLE_ERROR(2)
+
+/**
+ * @def BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK
+ *
+ * @brief
+ * No callback was registered to receive a BLE Transport Protocol (BTP)
+ * connection.
+ *
+ */
+#define BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK _BLE_ERROR(3)
+
+/**
+ * @def BLE_ERROR_CENTRAL_UNSUBSCRIBED
+ *
+ * @brief
+ * A BLE central device unsubscribed from a peripheral device's BLE
+ * Transport Protocol (BTP) transmit characteristic.
+ *
+ */
+#define BLE_ERROR_CENTRAL_UNSUBSCRIBED _BLE_ERROR(4)
+
+/**
+ * @def BLE_ERROR_GATT_SUBSCRIBE_FAILED
+ *
+ * @brief
+ * A BLE central device failed to subscribe to a peripheral device's BLE
+ * Transport Protocol (BTP) transmit characteristic.
+ *
+ */
+#define BLE_ERROR_GATT_SUBSCRIBE_FAILED _BLE_ERROR(5)
+
+/**
+ * @def BLE_ERROR_GATT_UNSUBSCRIBE_FAILED
+ *
+ * @brief
+ * A BLE central device failed to unsubscribe from a peripheral device's
+ * BLE Transport Protocol (BTP) transmit characteristic.
+ *
+ */
+#define BLE_ERROR_GATT_UNSUBSCRIBE_FAILED _BLE_ERROR(6)
+
+/**
+ * @def BLE_ERROR_GATT_WRITE_FAILED
+ *
+ * @brief
+ * A General Attribute Profile (GATT) write operation failed.
+ *
+ */
+#define BLE_ERROR_GATT_WRITE_FAILED _BLE_ERROR(7)
+
+/**
+ * @def BLE_ERROR_GATT_INDICATE_FAILED
+ *
+ * @brief
+ * A General Attribute Profile (GATT) indicate operation failed.
+ *
+ */
+#define BLE_ERROR_GATT_INDICATE_FAILED _BLE_ERROR(8)
+
+/**
+ * @def BLE_ERROR_NOT_IMPLEMENTED
+ *
+ * @brief
+ * A requested function or feature is not implemented.
+ *
+ */
+#define BLE_ERROR_NOT_IMPLEMENTED _BLE_ERROR(9)
+
+/*
+ * Unused _BLE_ERROR(10)
+ */
+
+/**
+ * @def BLE_ERROR_WOBLE_PROTOCOL_ABORT
+ *
+ * @brief
+ * A BLE Transport Protocol (BTP) error was encountered.
+ *
+ */
+#define BLE_ERROR_WOBLE_PROTOCOL_ABORT _BLE_ERROR(11)
+
+/**
+ * @def BLE_ERROR_REMOTE_DEVICE_DISCONNECTED
+ *
+ * @brief
+ * A remote BLE connection peer disconnected, either actively or due to the
+ * expiration of a BLE connection supervision timeout.
+ *
+ */
+#define BLE_ERROR_REMOTE_DEVICE_DISCONNECTED _BLE_ERROR(12)
+
+/**
+ * @def BLE_ERROR_APP_CLOSED_CONNECTION
+ *
+ * @brief
+ * The local application closed a BLE connection, and has informed BleLayer.
+ *
+ */
+#define BLE_ERROR_APP_CLOSED_CONNECTION _BLE_ERROR(13)
+
+/**
+ * @def BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG
+ *
+ * @brief
+ * More outbound message data is pending than available buffer space
+ * available to copy it.
+ *
+ */
+#define BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG _BLE_ERROR(14)
+
+/**
+ * @def BLE_ERROR_NOT_WEAVE_DEVICE
+ *
+ * @brief
+ * A BLE peripheral device did not expose the General Attribute Profile
+ * (GATT) service required by the Bluetooth Transport Protocol (BTP).
+ *
+ */
+#define BLE_ERROR_NOT_WEAVE_DEVICE _BLE_ERROR(15)
+
+/**
+ * @def BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS
+ *
+ * @brief
+ * A remote device does not offer a compatible version of the Bluetooth
+ * Transport Protocol (BTP).
+ *
+ */
+#define BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS _BLE_ERROR(16)
+
+/**
+ * @def BLE_ERROR_NO_MEMORY
+ *
+ * @brief
+ * A request for memory could not be fulfilled.
+ *
+ */
+#define BLE_ERROR_NO_MEMORY _BLE_ERROR(17)
+
+/**
+ * @def BLE_ERROR_MESSAGE_INCOMPLETE
+ *
+ * @brief
+ * A received Bluetooth Transport Protocol (BTP) message was incomplete.
+ *
+ */
+#define BLE_ERROR_MESSAGE_INCOMPLETE _BLE_ERROR(18)
+
+/**
+ * @def BLE_ERROR_INVALID_FRAGMENT_SIZE
+ *
+ * @brief
+ * A remote device selected in invalid Bluetooth Transport Protocol (BTP)
+ * fragment size.
+ *
+ */
+#define BLE_ERROR_INVALID_FRAGMENT_SIZE _BLE_ERROR(19)
+
+/**
+ * @def BLE_ERROR_START_TIMER_FAILED
+ *
+ * @brief
+ * A timer failed to start within BleLayer.
+ *
+ */
+#define BLE_ERROR_START_TIMER_FAILED _BLE_ERROR(20)
+
+/**
+ * @def BLE_ERROR_CONNECT_TIMED_OUT
+ *
+ * @brief
+ * A remote BLE peripheral device's Bluetooth Transport Protocol (BTP)
+ * connect handshake response timed out.
+ *
+ */
+#define BLE_ERROR_CONNECT_TIMED_OUT _BLE_ERROR(21)
+
+/**
+ * @def BLE_ERROR_RECEIVE_TIMED_OUT
+ *
+ * @brief
+ * A remote BLE central device's Bluetooth Transport Protocol (BTP) connect
+ * handshake timed out.
+ *
+ */
+#define BLE_ERROR_RECEIVE_TIMED_OUT _BLE_ERROR(22)
+
+/**
+ * @def BLE_ERROR_INVALID_MESSAGE
+ *
+ * @brief
+ * An invalid Bluetooth Transport Protocol (BTP) message was received.
+ *
+ */
+#define BLE_ERROR_INVALID_MESSAGE _BLE_ERROR(23)
+
+/**
+ * @def BLE_ERROR_FRAGMENT_ACK_TIMED_OUT
+ *
+ * @brief
+ * Receipt of an expected Bluetooth Transport Protocol (BTP) fragment
+ * acknowledgement timed out.
+ *
+ */
+#define BLE_ERROR_FRAGMENT_ACK_TIMED_OUT _BLE_ERROR(24)
+
+/**
+ * @def BLE_ERROR_KEEP_ALIVE_TIMED_OUT
+ *
+ * @brief
+ * Receipt of an expected Bluetooth Transport Protocol (BTP) keep-alive
+ * fragment timed out.
+ *
+ */
+#define BLE_ERROR_KEEP_ALIVE_TIMED_OUT _BLE_ERROR(25)
+
+/**
+ * @def BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK
+ *
+ * @brief
+ * No callback was registered to handle Bluetooth Transport Protocol (BTP)
+ * connect completion.
+ *
+ */
+#define BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK _BLE_ERROR(26)
+
+/**
+ * @def BLE_ERROR_INVALID_ACK
+ *
+ * @brief
+ * A Bluetooth Transport Protcol (BTP) fragment acknowledgement was invalid.
+ *
+ */
+#define BLE_ERROR_INVALID_ACK _BLE_ERROR(27)
+
+/**
+ * @def BLE_ERROR_REASSEMBLER_MISSING_DATA
+ *
+ * @brief
+ * A Bluetooth Transport Protocol (BTP) end-of-message fragment was
+ * received, but the total size of the received fragments is less than
+ * the indicated size of the original fragmented message.
+ *
+ */
+#define BLE_ERROR_REASSEMBLER_MISSING_DATA _BLE_ERROR(28)
+
+/**
+ * @def BLE_ERROR_INVALID_BTP_HEADER_FLAGS
+ *
+ * @brief
+ * A set of Bluetooth Transport Protocol (BTP) header flags is invalid.
+ *
+ */
+#define BLE_ERROR_INVALID_BTP_HEADER_FLAGS _BLE_ERROR(29)
+
+/**
+ * @def BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER
+ *
+ * @brief
+ * A Bluetooth Transport Protocol (BTP) fragment sequence number is invalid.
+ *
+ */
+#define BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER _BLE_ERROR(30)
+
+/**
+ * @def BLE_ERROR_REASSEMBLER_INCORRECT_STATE
+ *
+ * @brief
+ * The Bluetooth Transport Protocol (BTP) message reassembly engine
+ * encountered an unexpected state.
+ *
+ */
+#define BLE_ERROR_REASSEMBLER_INCORRECT_STATE _BLE_ERROR(31)
+
+/**
+ * @def BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG
+ *
+ * @brief
+ * More inbound message data is pending than available buffer space
+ * available to copy it.
+ *
+ */
+#define BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG _BLE_ERROR(32)
+
+// !!!!! IMPORTANT !!!!! If you add new Ble errors, please update the translation
+// of error codes to strings in BleError.cpp, and add them to unittest
+// in test-apps/TestErrorStr.cpp
+
+/**
+ * @}
+ */
+
+// clang-format on
+
+namespace nl {
+namespace Ble {
+
+extern void RegisterBleLayerErrorFormatter(void);
+extern bool FormatBleLayerError(char * buf, uint16_t bufSize, int32_t err);
+
+} /* namespace Ble */
+} /* namespace nl */
+
+
+#endif /* BLEERROR_H_ */
diff --git a/src/ble/BleLayer.am b/src/ble/BleLayer.am
new file mode 100644
index 0000000..21bb8b3
--- /dev/null
+++ b/src/ble/BleLayer.am
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2014-2017 Nest Labs, Inc.
+# All rights reserved.
+#
+# 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.
+#
+
+#
+# Description:
+# This file is the GNU automake header for the Nest BleLayer
+# library sources.
+#
+# These sources are shared by other SDK makefiles and consequently
+# must be anchored relative to the top build directory.
+#
+
+nl_BleLayer_sources = \
+ @top_builddir@/src/ble/BleLayer.cpp \
+ @top_builddir@/src/ble/BLEEndPoint.cpp \
+ @top_builddir@/src/ble/WoBle.cpp \
+ @top_builddir@/src/ble/BleUUID.cpp \
+ @top_builddir@/src/ble/BleError.cpp \
+ $(NULL)
+
+if WEAVE_ENABLE_WOBLE_TEST
+nl_BleLayer_sources += \
+ @top_builddir@/src/device-manager/WoBleTest.cpp \
+ $(NULL)
+endif # WEAVE_ENABLE_WOBLE_TEST
diff --git a/src/ble/BleLayer.cpp b/src/ble/BleLayer.cpp
new file mode 100644
index 0000000..83b25f0
--- /dev/null
+++ b/src/ble/BleLayer.cpp
@@ -0,0 +1,738 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 Weave
+ * 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 Weave message via a BLE GATT
+ * characteristic interface, and drives incoming messages up the Weave
+ * stack.
+ *
+ * During initialization, the BleLayer object requires a pointer to the
+ * platform's implementation of the BlePlatformDelegate and
+ * BleApplicationDelegate objects.
+ *
+ * The BlePlatformDelegate provides the Weave 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 Weave to inform
+ * the application when it has finished using a given BLE connection,
+ * i.e when the WeaveConnection object wrapping this connection has
+ * closed. This allows the application to either close the BLE connection
+ * or continue to keep it open for non-Weave purposes.
+ *
+ * To enable Weave 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.
+ */
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <string.h>
+
+#include <BleLayer/BlePlatformDelegate.h>
+#include <BleLayer/BleApplicationDelegate.h>
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/BleUUID.h>
+#include <BleLayer/BLEEndPoint.h>
+
+#include <Weave/Core/WeaveEncoding.h>
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Support/CodeUtils.h>
+
+// clang-format off
+
+#define CAPABILITIES_REQUEST_MAGICNUM_LEN 2
+#define CAPABILITIES_REQUEST_L2CAP_MTU_LEN 2
+#define CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN 4
+#define CAPABILITIES_REQUEST_WINDOW_SIZE_LEN 1
+#define CAPABILITIES_REQUEST_LEN (CAPABILITIES_REQUEST_MAGICNUM_LEN + \
+ CAPABILITIES_REQUEST_L2CAP_MTU_LEN + \
+ CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN + \
+ CAPABILITIES_REQUEST_WINDOW_SIZE_LEN)
+
+#define CAPABILITIES_RESPONSE_MAGICNUM_LEN 2
+#define CAPABILITIES_RESPONSE_L2CAP_MTU_LEN 2
+#define CAPABILITIES_RESPONSE_SELECTED_PROTOCOL_VERSION_LEN 1
+#define CAPABILITIES_RESPONSE_WINDOW_SIZE_LEN 1
+#define CAPABILITIES_RESPONSE_LEN (CAPABILITIES_RESPONSE_MAGICNUM_LEN + \
+ CAPABILITIES_RESPONSE_L2CAP_MTU_LEN + \
+ CAPABILITIES_RESPONSE_SELECTED_PROTOCOL_VERSION_LEN + \
+ CAPABILITIES_RESPONSE_WINDOW_SIZE_LEN)
+
+// Magic values expected in first 2 bytes of valid BLE transport capabilities request or response:
+#define CAPABILITIES_MSG_CHECK_BYTE_1 'n'
+#define CAPABILITIES_MSG_CHECK_BYTE_2 'l'
+
+// clang-format on
+
+namespace nl {
+namespace Ble {
+
+class BleEndPointPool
+{
+public:
+ int Size() const { return BLE_LAYER_NUM_BLE_ENDPOINTS; }
+
+ BLEEndPoint * Get(int 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 (BLEEndPoint *) (sEndPointPool.Pool + (sizeof(BLEEndPoint) * i));
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ BLEEndPoint * Find(BLE_CONNECTION_OBJECT c)
+ {
+ if (c == BLE_CONNECTION_UNINITIALIZED)
+ {
+ return NULL;
+ }
+
+ for (int i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
+ {
+ BLEEndPoint * elem = Get(i);
+ if (elem->mBle != NULL && elem->mConnObj == c)
+ {
+ return elem;
+ }
+ }
+
+ return NULL;
+ }
+
+ BLEEndPoint * GetFree() const
+ {
+ for (int i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
+ {
+ BLEEndPoint * elem = Get(i);
+ if (elem->mBle == NULL)
+ {
+ return elem;
+ }
+ }
+ return NULL;
+ }
+};
+
+// EndPoint Pools
+//
+static BleEndPointPool sBLEEndPointPool;
+
+// UUIDs used internally by BleLayer:
+
+const WeaveBleUUID BleLayer::WEAVE_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 WeaveBleUUID BleLayer::WEAVE_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 } };
+
+void BleLayerObject::Release()
+{
+ // Decrement the ref count. When it reaches zero, NULL out the pointer to the Weave::System::Layer
+ // object. This effectively declared the object free and ready for re-allocation.
+ mRefCount--;
+ if (mRefCount == 0)
+ {
+ mBle = NULL;
+ }
+}
+
+// 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 = version << 4;
+ }
+
+ version &= mask;
+
+ mSupportedProtocolVersions[(index / 2)] &= ~mask; // Clear version at index; leave other version in same byte alone
+ mSupportedProtocolVersions[(index / 2)] |= version;
+}
+
+BLE_ERROR BleTransportCapabilitiesRequestMessage::Encode(PacketBuffer * msgBuf) const
+{
+ uint8_t * p = msgBuf->Start();
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ // Verify we can write the fixed-length request without running into the end of the buffer.
+ VerifyOrExit(msgBuf->MaxDataLength() >= CAPABILITIES_REQUEST_LEN, err = BLE_ERROR_NO_MEMORY);
+
+ nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_1);
+ nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_2);
+
+ for (int i = 0; i < CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN; i++)
+ {
+ nl::Weave::Encoding::Write8(p, mSupportedProtocolVersions[i]);
+ }
+
+ nl::Weave::Encoding::LittleEndian::Write16(p, mMtu);
+ nl::Weave::Encoding::Write8(p, mWindowSize);
+
+ msgBuf->SetDataLength(CAPABILITIES_REQUEST_LEN);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BleTransportCapabilitiesRequestMessage::Decode(const PacketBuffer & msgBuf, BleTransportCapabilitiesRequestMessage & msg)
+{
+ const uint8_t * p = msgBuf.Start();
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ // Verify we can read the fixed-length request without running into the end of the buffer.
+ VerifyOrExit(msgBuf.DataLength() >= CAPABILITIES_REQUEST_LEN, err = BLE_ERROR_MESSAGE_INCOMPLETE);
+
+ VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_1 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+ VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_2 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+
+ for (int i = 0; i < CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN; i++)
+ {
+ msg.mSupportedProtocolVersions[i] = nl::Weave::Encoding::Read8(p);
+ }
+
+ msg.mMtu = nl::Weave::Encoding::LittleEndian::Read16(p);
+ msg.mWindowSize = nl::Weave::Encoding::Read8(p);
+
+exit:
+ return err;
+}
+
+// BleTransportCapabilitiesResponseMessage implementation:
+
+BLE_ERROR BleTransportCapabilitiesResponseMessage::Encode(PacketBuffer * msgBuf) const
+{
+ uint8_t * p = msgBuf->Start();
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ // Verify we can write the fixed-length request without running into the end of the buffer.
+ VerifyOrExit(msgBuf->MaxDataLength() >= CAPABILITIES_RESPONSE_LEN, err = BLE_ERROR_NO_MEMORY);
+
+ nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_1);
+ nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_2);
+
+ nl::Weave::Encoding::Write8(p, mSelectedProtocolVersion);
+ nl::Weave::Encoding::LittleEndian::Write16(p, mFragmentSize);
+ nl::Weave::Encoding::Write8(p, mWindowSize);
+
+ msgBuf->SetDataLength(CAPABILITIES_RESPONSE_LEN);
+
+exit:
+ return err;
+}
+
+BLE_ERROR BleTransportCapabilitiesResponseMessage::Decode(const PacketBuffer & msgBuf,
+ BleTransportCapabilitiesResponseMessage & msg)
+{
+ const uint8_t * p = msgBuf.Start();
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ // Verify we can read the fixed-length response without running into the end of the buffer.
+ VerifyOrExit(msgBuf.DataLength() >= CAPABILITIES_RESPONSE_LEN, err = BLE_ERROR_MESSAGE_INCOMPLETE);
+
+ VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_1 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+ VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_2 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+
+ msg.mSelectedProtocolVersion = nl::Weave::Encoding::Read8(p);
+ msg.mFragmentSize = nl::Weave::Encoding::LittleEndian::Read16(p);
+ msg.mWindowSize = nl::Weave::Encoding::Read8(p);
+
+exit:
+ return err;
+}
+
+// BleLayer implementation:
+
+BleLayer::BleLayer()
+{
+ mState = kState_NotInitialized;
+}
+
+BLE_ERROR BleLayer::Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate,
+ Weave::System::Layer * systemLayer)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ RegisterBleLayerErrorFormatter();
+
+ VerifyOrExit(platformDelegate != NULL, err = BLE_ERROR_BAD_ARGS);
+ VerifyOrExit(appDelegate != NULL, err = BLE_ERROR_BAD_ARGS);
+ VerifyOrExit(systemLayer != NULL, err = BLE_ERROR_BAD_ARGS);
+
+ if (mState != kState_NotInitialized)
+ {
+ return BLE_ERROR_INCORRECT_STATE;
+ }
+
+ mPlatformDelegate = platformDelegate;
+ mApplicationDelegate = appDelegate;
+ mSystemLayer = systemLayer;
+
+ memset(&sBLEEndPointPool, 0, sizeof(sBLEEndPointPool));
+
+ mState = kState_Initialized;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ mTestBleEndPoint = NULL;
+#endif
+
+exit:
+ return err;
+}
+
+BLE_ERROR BleLayer::Shutdown()
+{
+ mState = kState_NotInitialized;
+
+ // Close and free all BLE end points.
+ for (int 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 != NULL)
+ {
+ // 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();
+ }
+ }
+ }
+
+ return BLE_NO_ERROR;
+}
+
+BLE_ERROR BleLayer::NewBleEndPoint(BLEEndPoint ** retEndPoint, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose)
+{
+ *retEndPoint = NULL;
+
+ if (mState != kState_Initialized)
+ {
+ return BLE_ERROR_INCORRECT_STATE;
+ }
+
+ if (connObj == BLE_CONNECTION_UNINITIALIZED)
+ {
+ return BLE_ERROR_BAD_ARGS;
+ }
+
+ *retEndPoint = sBLEEndPointPool.GetFree();
+ if (*retEndPoint == NULL)
+ {
+ WeaveLogError(Ble, "%s endpoint pool FULL", "Ble");
+ return BLE_ERROR_NO_ENDPOINTS;
+ }
+
+ (*retEndPoint)->Init(this, connObj, role, autoClose);
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ mTestBleEndPoint = *retEndPoint;
+#endif
+
+ return BLE_NO_ERROR;
+}
+
+// Handle remote central's initiation of Weave over BLE protocol handshake.
+BLE_ERROR BleLayer::HandleBleTransportConnectionInitiated(BLE_CONNECTION_OBJECT connObj, PacketBuffer * pBuf)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ BLEEndPoint * newEndPoint = NULL;
+
+ // 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->mAppState = mAppState;
+
+ err = newEndPoint->Receive(pBuf);
+ pBuf = NULL;
+ SuccessOrExit(err); // If we fail here, end point will have already released connection and freed itself.
+
+exit:
+ if (pBuf != NULL)
+ {
+ PacketBuffer::Free(pBuf);
+ }
+
+ // 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 == NULL)
+ {
+ mApplicationDelegate->NotifyWeaveConnectionClosed(connObj);
+ }
+
+ if (err != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "HandleWeaveConnectionReceived failed, err = %d", err);
+ }
+
+ return err;
+}
+
+bool BleLayer::HandleWriteReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ WeaveLogError(Ble, "ble write rcvd on unknown svc id");
+ ExitNow();
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_1_ID, charId))
+ {
+ if (pBuf == NULL)
+ {
+ WeaveLogError(Ble, "rcvd null ble write");
+ ExitNow();
+ }
+
+ // Find matching connection end point.
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ BLE_ERROR status = endPoint->Receive(pBuf);
+ pBuf = NULL;
+ if (status != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "BLEEndPoint rcv failed, err = %d", status);
+ }
+ }
+ else
+ {
+ BLE_ERROR status = HandleBleTransportConnectionInitiated(connObj, pBuf);
+ pBuf = NULL;
+ if (status != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "failed handle new Weave BLE connection, status = %d", status);
+ }
+ }
+ }
+ else
+ {
+ WeaveLogError(Ble, "ble write rcvd on unknown char");
+ }
+
+exit:
+ if (pBuf != NULL)
+ {
+ PacketBuffer::Free(pBuf);
+ }
+
+ return true;
+}
+
+bool BleLayer::HandleIndicationReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+ {
+ if (pBuf == NULL)
+ {
+ WeaveLogError(Ble, "rcvd null ble indication");
+ ExitNow();
+ }
+
+ // find matching connection end point.
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ BLE_ERROR status = endPoint->Receive(pBuf);
+ pBuf = NULL;
+ if (status != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "BLEEndPoint rcv failed, err = %d", status);
+ }
+ }
+ else
+ {
+ WeaveLogDetail(Ble, "no endpoint for rcvd indication");
+ }
+ }
+ else
+ {
+ WeaveLogError(Ble, "ble ind rcvd on unknown char");
+ }
+
+exit:
+ if (pBuf != NULL)
+ {
+ PacketBuffer::Free(pBuf);
+ }
+
+ return true;
+}
+
+bool BleLayer::HandleWriteConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_1_ID, charId))
+ {
+ HandleAckReceived(connObj);
+ }
+ else
+ {
+ WeaveLogError(Ble, "ble write con rcvd on unknown char");
+ }
+
+ return true;
+}
+
+bool BleLayer::HandleIndicationConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+ {
+ HandleAckReceived(connObj);
+ }
+ else
+ {
+ WeaveLogError(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 != NULL)
+ {
+ BLE_ERROR status = endPoint->HandleGattSendConfirmationReceived();
+
+ if (status != BLE_NO_ERROR)
+ {
+ WeaveLogError(Ble, "endpoint conf recvd failed, err = %d", status);
+ }
+ }
+ else
+ {
+ WeaveLogError(Ble, "no endpoint for BLE sent data ack");
+ }
+}
+
+bool BleLayer::HandleSubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+ {
+ // Find end point already associated with BLE connection, if any.
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ endPoint->HandleSubscribeReceived();
+ }
+ else
+ {
+ WeaveLogError(Ble, "no endpoint for sub recvd");
+ }
+ }
+
+ return true;
+}
+
+bool BleLayer::HandleSubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+ {
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ endPoint->HandleSubscribeComplete();
+ }
+ else
+ {
+ WeaveLogError(Ble, "no endpoint for sub complete");
+ }
+ }
+
+ return true;
+}
+
+bool BleLayer::HandleUnsubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+ {
+ // Find end point already associated with BLE connection, if any.
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ endPoint->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_CENTRAL_UNSUBSCRIBED);
+ }
+ else
+ {
+ WeaveLogError(Ble, "no endpoint for unsub recvd");
+ }
+ }
+
+ return true;
+}
+
+bool BleLayer::HandleUnsubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+ if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+ {
+ return false;
+ }
+
+ if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+ {
+ // Find end point already associated with BLE connection, if any.
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ endPoint->HandleUnsubscribeComplete();
+ }
+ else
+ {
+ WeaveLogError(Ble, "no endpoint for unsub complete");
+ }
+ }
+
+ return true;
+}
+
+void BleLayer::HandleConnectionError(BLE_CONNECTION_OBJECT connObj, BLE_ERROR err)
+{
+ // BLE connection has failed somehow, we must find and abort matching connection end point.
+ BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+ if (endPoint != NULL)
+ {
+ 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 = (version >> shift_width) & 0x0F; // Grab just the nibble we want.
+
+ if ((version >= NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) &&
+ (version <= NL_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;
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/BleLayer.h b/src/ble/BleLayer.h
new file mode 100644
index 0000000..56c1afe
--- /dev/null
+++ b/src/ble/BleLayer.h
@@ -0,0 +1,373 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines objects that provide an abstraction layer between a
+ * platform's Bluetooth Low Energy (BLE) implementation and the Weave
+ * 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 Weave message via a BLE GATT
+ * characteristic interface, and drives incoming messages up the Weave
+ * stack.
+ *
+ * During initialization, the BleLayer object requires a pointer to the
+ * platform's implementation of the BleAdapter object. This object is
+ * defined but not implemented by the Weave stack, and provides the
+ * BleLayer with a functional interface to drive outgoing GATT
+ * characteristic writes and indications. It also provides a mechanism
+ * for Weave to inform the application when it has finished using a given
+ * BLE connection, i.e., when the WeaveConnection object wrapping this
+ * connection has closed.
+ *
+ * To enable Weave over BLE for a new platform, the application developer
+ * must implement the BleAdapter class for their platform, pass it to the
+ * BleLayer on startup, pass a pointer to this BleLayer to their instance
+ * of WeaveMessageLayer, and ensure that the application calls the
+ * necessary BleLayer functions to drive BLE data and control input up the
+ * stack.
+ */
+
+#ifndef BLELAYER_H_
+#define BLELAYER_H_
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+
+#include <BleLayer/BleConfig.h>
+
+#include <SystemLayer/SystemLayer.h>
+#include <SystemLayer/SystemPacketBuffer.h>
+
+#include <BleLayer/BlePlatformDelegate.h>
+#include <BleLayer/BleApplicationDelegate.h>
+#include <BleLayer/BleUUID.h>
+#include <BleLayer/BleError.h>
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#include <InetLayer/InetLayer.h>
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+/**
+ * @def NUM_SUPPORTED_PROTOCOL_VERSIONS
+ *
+ * Number of unsigned 4-bit representations of supported transport protocol
+ * versions encapsulated in a BleTransportCapabilitiesRequest. Defined by Weave
+ * over BLE protocol specification.
+ */
+#define NUM_SUPPORTED_PROTOCOL_VERSIONS 8
+/// Version(s) of the Nest BLE Transport Protocol that this stack supports.
+#define NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION kBleTransportProtocolVersion_V2
+#define NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION kBleTransportProtocolVersion_V3
+
+/// Forward declarations.
+class BleLayer;
+class BLEEndPoint;
+
+/// Role of end points' associated BLE connections. Determines means used by end points to send and receive data.
+typedef enum
+{
+ kBleRole_Central = 0,
+ kBleRole_Peripheral = 1
+} BleRole;
+
+/// Enum defining versions of Weave over BLE transport protocol.
+typedef enum
+{
+ kBleTransportProtocolVersion_None = 0,
+ kBleTransportProtocolVersion_V1 = 1, // Prototype WoBLe version without ACKs or flow-control.
+ kBleTransportProtocolVersion_V2 = 2, // First WoBLE version with ACKs and flow-control.
+ kBleTransportProtocolVersion_V3 = 3 // First WoBLE version with asymetric fragement sizes.
+} BleTransportProtocolVersion;
+
+class BleLayerObject
+{
+ friend class BleLayer;
+
+public:
+ // Public data members:
+ BleLayer * mBle; ///< [READ-ONLY] Pointer to the BleLayer object that owns this object.
+ void * mAppState; ///< Generic pointer to app-specific data associated with the object.
+
+protected:
+ uint32_t mRefCount;
+
+ void AddRef(void) { mRefCount++; }
+ void Release(void);
+};
+
+class BleTransportCapabilitiesRequestMessage
+{
+public:
+ /**
+ * An array of size NUM_SUPPORTED_PROTOCOL_VERSIONS listing versions of the
+ * BLE transport protocol that this node supports. Each protocol version is
+ * specified as a 4-bit unsigned integer. A zero-value represents unused
+ * array elements. Counting up from the zero-index, the first zero-value
+ * specifies the end of the list of supported protocol versions.
+ */
+ uint8_t mSupportedProtocolVersions[(NUM_SUPPORTED_PROTOCOL_VERSIONS / 2) + (NUM_SUPPORTED_PROTOCOL_VERSIONS % 2)];
+
+ /**
+ * The MTU that has been negotiated for this BLE connection. Specified in
+ * the BleTransportCapabilitiesRequestMessage because the remote node may
+ * be unable to glean this info from its own BLE hardware/software stack,
+ * such as on older Android platforms.
+ *
+ * A value of 0 means that the central could not determine the negotiated
+ * BLE connection MTU.
+ */
+ uint16_t mMtu;
+
+ /**
+ * The initial and maximum receive window size offered by the central,
+ * defined in terms of GATT indication payloads.
+ */
+ uint8_t mWindowSize;
+
+ /**
+ * Set supported version value at given index in
+ * SupportedProtocolVersions. uint8_t version argument is truncated to 4
+ * least-significant bits. Index shall be 0 through number of
+ * SupportedProtocolVersions elements - 1.
+ */
+ void SetSupportedProtocolVersion(uint8_t index, uint8_t version);
+
+ /// Must be able to reserve 20 byte data length in msgBuf.
+ BLE_ERROR Encode(PacketBuffer * msgBuf) const;
+
+ static BLE_ERROR Decode(const PacketBuffer & msgBuf, BleTransportCapabilitiesRequestMessage & msg);
+};
+
+class BleTransportCapabilitiesResponseMessage
+{
+public:
+ /**
+ * The lower 4 bits specify the BLE transport protocol version that the BLE
+ * peripheral has selected for this connection.
+ *
+ * A value of kBleTransportProtocolVersion_None means that no supported
+ * protocol version was found in the central's capabilities request. The
+ * central should unsubscribe after such a response has been sent to free
+ * up the peripheral for connections from devices with supported protocol
+ * versions.
+ */
+ uint8_t mSelectedProtocolVersion;
+
+ /**
+ * BLE transport fragment size selected by peripheral in response to MTU
+ * value in BleTransportCapabilitiesRequestMessage and its local
+ * observation of the BLE connection MTU.
+ */
+ uint16_t mFragmentSize;
+
+ /**
+ * The initial and maximum receive window size offered by the peripheral,
+ * defined in terms of GATT write payloads.
+ */
+ uint8_t mWindowSize;
+
+ /// Must be able to reserve 20 byte data length in msgBuf.
+ BLE_ERROR Encode(PacketBuffer * msgBuf) const;
+
+ static BLE_ERROR Decode(const PacketBuffer & msgBuf, BleTransportCapabilitiesResponseMessage & msg);
+};
+
+/**
+ * @class BleLayer
+ *
+ * @brief
+ * This class provides an interface for a single thread to drive data
+ * either up the stack via the BleLayer platform interface functions,
+ * or down the stack via a WeaveConnection object associated with a
+ * BLEEndPoint.
+ *
+ * There are two ways to associate a WeaveConnection (defined by the
+ * WeaveMessageLayer) with a BLE connection:
+ *
+ * First, the application can passively receive an incoming BLE connection
+ * and hand the platform-specific BLE_CONNECTION_OBJECT that this receipt
+ * generates to BleLayer via the corresponding platform interface function.
+ * This causes BleLayer to wrap the BLE_CONNECTION_OBJECT in a BLEEndPoint,
+ * and notify WeaveMessageLayer that a new BLE conneciotn has been received.
+ * The message layer then wraps the new BLEEndPoint object in a
+ * WeaveConnection, and hands this object to the application via the message
+ * layer's OnConnectionReceived callback.
+ *
+ * Second, the application can actively form an outgoing BLE connection, e.g.,
+ * by connecting to a BLE peripheral. It then creates a new WeaveConnection
+ * via the WeaveMessageLayer, assigns an authentication type to this
+ * connection, and binds it to the BLE_CONNECTION_OBJECT for the new BLE
+ * connection via WeaveConnection::ConnectBle. This function then
+ * establishes the secure session type specified by the WeaveConnection's
+ * authentication type member variable.
+ *
+ */
+class NL_DLL_EXPORT BleLayer
+{
+ friend class BLEEndPoint;
+#if WEAVE_ENABLE_WOBLE_TEST
+ friend class WoBleTest;
+#endif
+
+public:
+ // Public data members:
+ enum
+ {
+ kState_NotInitialized = 0,
+ kState_Initialized = 1
+ } mState; ///< [READ-ONLY] Current state
+
+ void * mAppState;
+
+ typedef void (*BleConnectionReceivedFunct)(BLEEndPoint * newEndPoint);
+ BleConnectionReceivedFunct OnWeaveBleConnectReceived;
+
+public:
+ // Public functions:
+ BleLayer(void);
+
+ BLE_ERROR Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate,
+ Weave::System::Layer * systemLayer);
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+ BLE_ERROR Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate, Inet::InetLayer * inetLayer);
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+ BLE_ERROR Shutdown(void);
+
+ BLE_ERROR NewBleEndPoint(BLEEndPoint ** retEndPoint, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose);
+
+ nl::Weave::System::Error ScheduleWork(nl::Weave::System::Layer::TimerCompleteFunct aComplete, void* aAppState)
+ {
+ return mSystemLayer->ScheduleWork(aComplete, aAppState);
+ }
+
+ /**< Platform interface functions:
+
+ * Calling conventions:
+ * Weave takes ownership of PacketBuffers received through these functions,
+ * and will free them or pass ownership up the stack.
+ *
+ * Beyond each call, no guarantees are provided as to the lifetime of UUID arguments.
+ *
+ * A 'true' return value means the Weave stack successfully handled the
+ * corresponding message or state indication. 'false' means the Weave stack either
+ * failed or chose not to handle this. In case of 'false,' the Weave stack will not
+ * have freed or taken ownership of any PacketBuffer arguments. This contract allows the
+ * platform to pass BLE events to Weave without needing to know which characteristics
+ * Weave cares about.
+
+ * Platform must call this function when a GATT subscription has been established to any Weave service
+ * charateristic.
+ *
+ * If this function returns true, Weave has accepted the BLE connection and wrapped it
+ * in a WeaveConnection object. If Weave accepts a BLE connection, the platform MUST
+ * notify Weave if the subscription is canceled or the underlying BLE connection is
+ * closed, or the associated WeaveConnection will never be closed or freed. */
+ bool HandleSubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+ /// Call when a GATT subscribe request succeeds.
+ bool HandleSubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+ /**< Platform must call this function when a GATT unsubscribe is requested on any Weave
+ * service charateristic, that is, when an existing GATT subscription on a Weave service
+ * characteristic is canceled. */
+ bool HandleUnsubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+ /// Call when a GATT unsubscribe request succeeds.
+ bool HandleUnsubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+ /// Call when a GATT write request is received.
+ bool HandleWriteReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf);
+
+ /// Call when a GATT indication is received.
+ bool HandleIndicationReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf);
+
+ /// Call when an outstanding GATT write request receives a positive receipt confirmation.
+ bool HandleWriteConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+ /// Call when an oustanding GATT indication receives a positive receipt confirmation.
+ bool HandleIndicationConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+ /// Call when a GATT read request is received.
+ bool HandleReadReceived(BLE_CONNECTION_OBJECT connObj, BLE_READ_REQUEST_CONTEXT requestContext, const WeaveBleUUID * svcId,
+ const WeaveBleUUID * charId);
+
+ /**< Platform must call this function when any previous operation undertaken by the BleLayer via BleAdapter
+ * fails, such as a characteristic write request or subscribe attempt, or when a BLE connection is closed.
+ *
+ * In most cases, this will prompt Weave to close the associated WeaveConnection and notify that platform that
+ * it has abandoned the underlying BLE connection.
+ *
+ * NOTE: if the application explicitly closes a BLE connection with an associated WeaveConnection such that
+ * the BLE connection close will not generate an upcall to Weave, HandleConnectionError must be called with
+ * err = BLE_ERROR_APP_CLOSED_CONNECTION to prevent the leak of this WeaveConnection and its end point object. */
+ void HandleConnectionError(BLE_CONNECTION_OBJECT connObj, BLE_ERROR err);
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ BLEEndPoint * mTestBleEndPoint;
+#endif
+
+private:
+ // Private data members:
+
+ // UUID of Weave service characteristic used for central writes.
+ static const WeaveBleUUID WEAVE_BLE_CHAR_1_ID;
+ // UUID of Weave service characteristic used for peripheral indications.
+ static const WeaveBleUUID WEAVE_BLE_CHAR_2_ID;
+
+ BlePlatformDelegate * mPlatformDelegate;
+ BleApplicationDelegate * mApplicationDelegate;
+ Weave::System::Layer * mSystemLayer;
+
+private:
+ // Private functions:
+ void HandleDataReceived(BLE_CONNECTION_OBJECT connObj, PacketBuffer * pBuf);
+ void HandleAckReceived(BLE_CONNECTION_OBJECT connObj);
+ void DriveSending(void);
+ BLE_ERROR HandleBleTransportConnectionInitiated(BLE_CONNECTION_OBJECT connObj, PacketBuffer * pBuf);
+
+ static BleTransportProtocolVersion GetHighestSupportedProtocolVersion(const BleTransportCapabilitiesRequestMessage & reqMsg);
+};
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+inline BLE_ERROR BleLayer::Init(BlePlatformDelegate * aPlatformDelegate, BleApplicationDelegate * aAppDelegate,
+ Inet::InetLayer * aInetLayer)
+{
+ return Init(aPlatformDelegate, aAppDelegate, aInetLayer->SystemLayer());
+}
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#include "BLEEndPoint.h"
+
+#endif /* BLELAYER_H_ */
diff --git a/src/ble/BlePlatformDelegate.h b/src/ble/BlePlatformDelegate.h
new file mode 100644
index 0000000..4425cec
--- /dev/null
+++ b/src/ble/BlePlatformDelegate.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines the interface for downcalls from BleLayer
+ * to a platform's BLE framework.
+ */
+
+#ifndef BLEPLATFORMDELEGATE_H_
+#define BLEPLATFORMDELEGATE_H_
+
+#include <BleLayer/BleConfig.h>
+#include <BleLayer/BleUUID.h>
+
+#include <SystemLayer/SystemPacketBuffer.h>
+#include <Weave/Support/NLDLLUtil.h>
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+// Platform-agnostic BLE interface
+class NL_DLL_EXPORT BlePlatformDelegate
+{
+public:
+ // Following APIs must be implemented by platform:
+
+ // Subscribe to updates and indications on the specfied characteristic
+ virtual bool SubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId,
+ const WeaveBleUUID * charId) = 0;
+
+ // Unsubscribe from updates and indications on the specified characteristic
+ virtual bool UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId,
+ const WeaveBleUUID * charId) = 0;
+
+ // Close the underlying BLE connection.
+ virtual bool CloseConnection(BLE_CONNECTION_OBJECT connObj) = 0;
+
+ // Get MTU size negotiated for specified BLE connection. Return value of 0 means MTU size could not be determined.
+ virtual uint16_t GetMTU(BLE_CONNECTION_OBJECT connObj) const = 0;
+
+ // Data path calling convention:
+ // The Weave stack retains partial ownership of pBufs sent via the below functions. These buffers are freed by
+ // Weave after either they're acknowledged by the peer's BLE controller, or Weave shuts down the pBuf's
+ // associated BLEEndPoint.
+ //
+ // For its part, the platform MUST call PacketBuffer::Free on each pBuf it receives via a Send* function once it no
+ // longer requires a reference to this buffer, e.g. when a NL_CLIENT_EVENT_BLE_PBUF_CLEAR event is received on
+ // platforms with the Nest BLE SDK.
+ //
+ // On platforms such as iOS or Android where the contents of the pBuf PacketBuffer are copied into a separate
+ // buffer for transmission, pBuf may be freed on the downcall to the platform delegate once the copy completes.
+ //
+ // A 'true' return value from a Send* function indicates that the characteristic was written or updated
+ // successfully. A 'false' value indicates failure, and is used to report this failure to the user via the return
+ // value of WeaveConnection::SendMessage.
+ //
+ // If a Send* function returns false, it must release its reference to pBuf prior to return.
+
+ // Send GATT characteristic indication request
+ virtual bool SendIndication(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf) = 0;
+
+ // Send GATT characteristic write request
+ virtual bool SendWriteRequest(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf) = 0;
+
+ // Send GATT characteristic read request
+ virtual bool SendReadRequest(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+ PacketBuffer * pBuf) = 0;
+
+ // Send response to remote host's GATT chacteristic read response
+ virtual bool SendReadResponse(BLE_CONNECTION_OBJECT connObj, BLE_READ_REQUEST_CONTEXT requestContext,
+ const WeaveBleUUID * svcId, const WeaveBleUUID * charId) = 0;
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEPLATFORMDELEGATE_H_ */
diff --git a/src/ble/BleUUID.cpp b/src/ble/BleUUID.cpp
new file mode 100644
index 0000000..9831ed0
--- /dev/null
+++ b/src/ble/BleUUID.cpp
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <stdint.h>
+#include <string.h>
+
+#include "BleUUID.h"
+
+namespace nl {
+namespace Ble {
+
+const WeaveBleUUID WEAVE_BLE_SVC_ID = { { // 0000FEAF-0000-1000-8000-00805F9B34FB
+ 0x00, 0x00, 0xFE, 0xAF, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34,
+ 0xFB } };
+
+bool UUIDsMatch(const WeaveBleUUID * idOne, const WeaveBleUUID * idTwo)
+{
+ if ((idOne == NULL) || (idTwo == NULL))
+ {
+ return false;
+ }
+ return (memcmp(idOne->bytes, idTwo->bytes, 16) == 0);
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/BleUUID.h b/src/ble/BleUUID.h
new file mode 100644
index 0000000..b0102af
--- /dev/null
+++ b/src/ble/BleUUID.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+#ifndef BLEUUID_H_
+#define BLEUUID_H_
+
+#include <stdint.h>
+
+namespace nl {
+namespace Ble {
+
+// Type to represent 128-bit BLE UUIDs. 16-bit short UUIDs may be combined with
+// the Bluetooth Base UUID to form full 128-bit UUIDs as described in the
+// Service Discovery Protocol (SDP) definition, part of the Bluetooth Core
+// Specification.
+typedef struct
+{
+ uint8_t bytes[16];
+} WeaveBleUUID;
+
+// UUID of Nest Weave BLE service. Exposed for use in scan filter.
+extern const WeaveBleUUID WEAVE_BLE_SVC_ID;
+
+bool UUIDsMatch(const WeaveBleUUID * idOne, const WeaveBleUUID * idTwo);
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEUUID_H_ */
diff --git a/src/ble/Makefile.am b/src/ble/Makefile.am
new file mode 100644
index 0000000..40e2cd7
--- /dev/null
+++ b/src/ble/Makefile.am
@@ -0,0 +1,44 @@
+#
+#
+# Copyright (c) 2014-2017 Nest Labs, Inc.
+# All rights reserved.
+#
+# 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.
+#
+
+#
+# Description:
+# This file is the GNU automake template for the Nest BleLayer
+# library.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+include BleLayer.am
+
+lib_LIBRARIES = libBleLayer.a
+
+libBleLayer_a_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ $(LWIP_CPPFLAGS) \
+ $(NULL)
+
+if WEAVE_ENABLE_WOBLE_TEST
+libBleLayer_a_CPPFLAGS += \
+ -I$(top_srcdir)/src/device-manager\
+ $(NULL)
+endif # WEAVE_ENABLE_WOBLE_TEST
+
+libBleLayer_a_SOURCES = $(nl_BleLayer_sources)
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/src/ble/WeaveBleServiceData.h b/src/ble/WeaveBleServiceData.h
new file mode 100644
index 0000000..4b65485
--- /dev/null
+++ b/src/ble/WeaveBleServiceData.h
@@ -0,0 +1,111 @@
+/*
+ *
+ * Copyright (c) 2019 Google LLC.
+ * All rights reserved.
+ *
+ * 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
+ * Definitions for Weave BLE service advertisement data.
+ */
+
+#ifndef WEAVE_BLE_SERVICE_DATA_H
+#define WEAVE_BLE_SERVICE_DATA_H
+
+namespace nl {
+namespace Ble {
+
+/**
+ * Weave data block types that may appear with Weave BLE service advertisement data.
+ */
+enum WeaveBLEServiceDataType
+{
+ kWeaveBLEServiceDataType_DeviceIdentificationInfo = 0x01,
+ kWeaveBLEServiceDataType_TokenIdentificationInfo = 0x02,
+};
+
+/**
+ * Weave BLE Device Identification Information Block
+ *
+ * Defines the over-the-air encoded format of the device identification information block that appears
+ * within Weave BLE service advertisement data.
+ */
+struct WeaveBLEDeviceIdentificationInfo
+{
+ enum
+ {
+ kMajorVersion = 0,
+ kMinorVersion = 1,
+ };
+
+ enum
+ {
+ kPairingStatus_Unpaired = 0,
+ kPairingStatus_Paired = 1,
+ };
+
+ uint8_t BlockLen;
+ uint8_t BlockType;
+ uint8_t MajorVersion;
+ uint8_t MinorVersion;
+ uint8_t DeviceVendorId[2];
+ uint8_t DeviceProductId[2];
+ uint8_t DeviceId[8];
+ uint8_t PairingStatus;
+
+ void Init()
+ {
+ memset(this, 0, sizeof(*this));
+ BlockLen = sizeof(*this) - sizeof(BlockLen); // size of all fields EXCEPT BlockLen
+ BlockType = kWeaveBLEServiceDataType_DeviceIdentificationInfo;
+ MajorVersion = kMajorVersion;
+ MinorVersion = kMinorVersion;
+ }
+
+ uint16_t GetVendorId(void)
+ {
+ return nl::Weave::Encoding::LittleEndian::Get16(DeviceVendorId);
+ }
+
+ void SetVendorId(uint16_t vendorId)
+ {
+ nl::Weave::Encoding::LittleEndian::Put16(DeviceVendorId, vendorId);
+ }
+
+ uint16_t GetProductId(void)
+ {
+ return nl::Weave::Encoding::LittleEndian::Get16(DeviceProductId);
+ }
+
+ void SetProductId(uint16_t productId)
+ {
+ nl::Weave::Encoding::LittleEndian::Put16(DeviceProductId, productId);
+ }
+
+ uint64_t GetDeviceId(void)
+ {
+ return nl::Weave::Encoding::LittleEndian::Get64(DeviceId);
+ }
+
+ void SetDeviceId(uint64_t deviceId)
+ {
+ nl::Weave::Encoding::LittleEndian::Put64(DeviceId, deviceId);
+ }
+} __attribute__((packed));
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif // WEAVE_BLE_SERVICE_DATA_H
diff --git a/src/ble/WoBle.cpp b/src/ble/WoBle.cpp
new file mode 100644
index 0000000..f044220
--- /dev/null
+++ b/src/ble/WoBle.cpp
@@ -0,0 +1,630 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 types and an object for the Weave over
+ * Bluetooth Low Energy (WoBLE) byte-stream, connection-oriented
+ * adaptation of Weave for point-to-point Bluetooth Low Energy
+ * (BLE) links.
+ *
+ */
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <BleLayer/WoBle.h>
+#if WEAVE_ENABLE_WOBLE_TEST
+#include <BleLayer/WoBleTest.h>
+#endif
+
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Support/CodeUtils.h>
+
+// Define below to enable extremely verbose BLE-specific debug logging.
+#undef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+
+#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+#define WeaveLogDebugBtpEngine(MOD, MSG, ...) WeaveLogError(MOD, MSG, ##__VA_ARGS__)
+#else
+#define WeaveLogDebugBtpEngine(MOD, MSG, ...)
+#endif
+
+#define NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE 1 // Size in bytes of enocded BTP fragment header flag bits
+#define NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE 1 // Size in bytes of encoded BTP sequence number
+#define NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE 1 // Size in bytes of encoded BTP fragment acknowledgement number
+#define NL_BLE_TRANSFER_PROTOCOL_MSG_LEN_SIZE 2 // Size in byte of encoded BTP total fragmented message length
+
+#define NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE \
+ (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE + \
+ NL_BLE_TRANSFER_PROTOCOL_MSG_LEN_SIZE)
+
+#define NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE \
+ (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE)
+
+#define NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE \
+ (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE)
+
+namespace nl {
+namespace Ble {
+
+static inline void IncSeqNum(SequenceNumber_t & a_seq_num)
+{
+ a_seq_num = 0xff & ((a_seq_num) + 1);
+}
+
+static inline bool DidReceiveData(uint8_t rx_flags)
+{
+ return (GetFlag(rx_flags, WoBle::kHeaderFlag_StartMessage) ||
+ GetFlag(rx_flags, WoBle::kHeaderFlag_ContinueMessage) ||
+ GetFlag(rx_flags, WoBle::kHeaderFlag_EndMessage));
+}
+
+static void PrintBufDebug(PacketBuffer * buf)
+{
+#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+ uint8_t * b = buf->Start();
+
+ for (int i = 0; i < buf->DataLength(); i++)
+ {
+ WeaveLogError(Ble, "\t%02x", b[i]);
+ }
+#endif
+}
+
+const uint16_t WoBle::sDefaultFragmentSize = 20; // 23-byte minimum ATT_MTU - 3 bytes for ATT operation header
+const uint16_t WoBle::sMaxFragmentSize = 128; // Size of write and indication characteristics
+
+BLE_ERROR WoBle::Init(void * an_app_state, bool expect_first_ack)
+{
+ mAppState = an_app_state;
+ mRxState = kState_Idle;
+ mRxBuf = NULL;
+ mRxNewestUnackedSeqNum = 0;
+ mRxOldestUnackedSeqNum = 0;
+ mRxFragmentSize = sDefaultFragmentSize;
+ mTxState = kState_Idle;
+ mTxBuf = NULL;
+ mTxFragmentSize = sDefaultFragmentSize;
+ mRxCharCount = 0;
+ mRxPacketCount = 0;
+ mTxCharCount = 0;
+ mTxPacketCount = 0;
+ mTxNewestUnackedSeqNum = 0;
+ mTxOldestUnackedSeqNum = 0;
+#if WEAVE_ENABLE_WOBLE_TEST
+ mTxPacketType = kType_Data; // Default WoBle Data packet
+ mRxPacketType = kType_Data; // Default WoBle Data packet
+#endif
+
+ if (expect_first_ack)
+ {
+ mTxNextSeqNum = 1;
+ mExpectingAck = true;
+ mRxNextSeqNum = 0;
+ }
+ else
+ {
+ mTxNextSeqNum = 0;
+ mExpectingAck = false;
+ mRxNextSeqNum = 1;
+ }
+
+ return BLE_NO_ERROR;
+}
+
+SequenceNumber_t WoBle::GetAndIncrementNextTxSeqNum()
+{
+ SequenceNumber_t ret = mTxNextSeqNum;
+
+ // If not already expecting ack...
+ if (!mExpectingAck)
+ {
+ mExpectingAck = true;
+ mTxOldestUnackedSeqNum = mTxNextSeqNum;
+ }
+
+ // Update newest unacknowledged sequence number.
+ mTxNewestUnackedSeqNum = mTxNextSeqNum;
+
+ // Increment mTxNextSeqNum.
+ IncSeqNum(mTxNextSeqNum);
+
+ return ret;
+}
+
+SequenceNumber_t WoBle::GetAndRecordRxAckSeqNum()
+{
+ SequenceNumber_t ret = mRxNewestUnackedSeqNum;
+
+ mRxNewestUnackedSeqNum = mRxNextSeqNum;
+ mRxOldestUnackedSeqNum = mRxNextSeqNum;
+
+ return ret;
+}
+
+bool WoBle::HasUnackedData() const
+{
+ return (mRxOldestUnackedSeqNum != mRxNextSeqNum);
+}
+
+bool WoBle::IsValidAck(SequenceNumber_t ack_num) const
+{
+ WeaveLogDebugBtpEngine(Ble, "entered IsValidAck, ack = %u, oldest = %u, newest = %u", ack_num, mTxOldestUnackedSeqNum,
+ mTxNewestUnackedSeqNum);
+
+ // Return false if not awaiting any ack.
+ if (!mExpectingAck)
+ {
+ WeaveLogDebugBtpEngine(Ble, "unexpected ack is invalid");
+ return false;
+ }
+
+ // Assumption: maximum valid sequence number equals maximum value of SequenceNumber_t.
+
+ if (mTxNewestUnackedSeqNum >= mTxOldestUnackedSeqNum) // If current unacked interval does NOT wrap...
+ {
+ return (ack_num <= mTxNewestUnackedSeqNum && ack_num >= mTxOldestUnackedSeqNum);
+ }
+ else // Else, if current unacked interval DOES wrap...
+ {
+ return (ack_num <= mTxNewestUnackedSeqNum || ack_num >= mTxOldestUnackedSeqNum);
+ }
+}
+
+BLE_ERROR WoBle::HandleAckReceived(SequenceNumber_t ack_num)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+
+ WeaveLogDebugBtpEngine(Ble, "entered HandleAckReceived, ack_num = %u", ack_num);
+
+ // Ensure ack_num falls within range of ack values we're expecting.
+ VerifyOrExit(IsValidAck(ack_num), err = BLE_ERROR_INVALID_ACK);
+
+ if (mTxNewestUnackedSeqNum == ack_num) // If ack is for newest outstanding unacknowledged fragment...
+ {
+ mTxOldestUnackedSeqNum = ack_num;
+
+ // All oustanding fragments have been acknowledged.
+ mExpectingAck = false;
+ }
+ else // If ack is valid, but not for newest oustanding unacknowledged fragment...
+ {
+ // Update newest unacknowledged fragment to one past that which was just acknowledged.
+ mTxOldestUnackedSeqNum = ack_num;
+ IncSeqNum(mTxOldestUnackedSeqNum);
+ }
+
+exit:
+ return err;
+}
+
+// Calling convention:
+// EncodeStandAloneAck may only be called if data arg is commited for immediate, synchronous subsequent transmission.
+BLE_ERROR WoBle::EncodeStandAloneAck(PacketBuffer * data)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ uint8_t * characteristic;
+
+ // Ensure enough headroom exists for the lower BLE layers.
+ VerifyOrExit(data->EnsureReservedSize(WEAVE_CONFIG_BLE_PKT_RESERVED_SIZE), err = BLE_ERROR_NO_MEMORY);
+
+ // Ensure enough space for standalone ack payload.
+ VerifyOrExit(data->MaxDataLength() >= NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE, err = BLE_ERROR_NO_MEMORY);
+ characteristic = data->Start();
+
+ // Since there's no preexisting message payload, we can write BTP header without adjusting data start pointer.
+ characteristic[0] = kHeaderFlag_FragmentAck;
+
+ // Acknowledge most recently received sequence number.
+ characteristic[1] = GetAndRecordRxAckSeqNum();
+ WeaveLogDebugBtpEngine(Ble, "===> encoded stand-alone ack = %u", characteristic[1]);
+
+ // Include sequence number for stand-alone ack itself.
+ characteristic[2] = GetAndIncrementNextTxSeqNum();
+
+ // Set ack payload data length.
+ data->SetDataLength(NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE);
+
+exit:
+ return err;
+}
+
+// Calling convention:
+// WoBle does not retain ownership of reassembled messages, layer above needs to free when done.
+//
+// WoBle does not reset itself on error. Upper layer should free outbound message and inbound reassembly buffers
+// if there is a problem.
+
+// HandleCharacteristicReceived():
+//
+// Non-NULL characteristic data arg is always either designated as or appended to the message reassembly buffer,
+// or freed if it holds a stand-alone ack. In all cases, caller must clear its reference to data arg when this
+// function returns.
+//
+// Upper layer must immediately clean up and reinitialize protocol engine if returned err != BLE_NO_ERROR.
+BLE_ERROR WoBle::HandleCharacteristicReceived(PacketBuffer * data, SequenceNumber_t & receivedAck, bool & didReceiveAck)
+{
+ BLE_ERROR err = BLE_NO_ERROR;
+ uint8_t rx_flags = 0;
+ uint8_t cursor = 0;
+ uint8_t * characteristic = data->Start();
+
+ VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+
+ mRxCharCount++;
+
+ // Get header flags, always in first byte.
+ rx_flags = characteristic[cursor++];
+#if WEAVE_ENABLE_WOBLE_TEST
+ if (GetFlag(rx_flags, kHeaderFlag_CommandMessage))
+ SetRxPacketType(kType_Control);
+ else
+ SetRxPacketType(kType_Data);
+#endif
+
+ didReceiveAck = GetFlag(rx_flags, kHeaderFlag_FragmentAck);
+
+ // Get ack number, if any.
+ if (didReceiveAck)
+ {
+ receivedAck = characteristic[cursor++];
+
+ err = HandleAckReceived(receivedAck);
+ SuccessOrExit(err);
+ }
+
+ // Get sequence number.
+ mRxNewestUnackedSeqNum = characteristic[cursor++];
+
+ // Verify that received sequence number is the next one we'd expect.
+ VerifyOrExit(mRxNewestUnackedSeqNum == mRxNextSeqNum, err = BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER);
+
+ // Increment next expected rx sequence number.
+ IncSeqNum(mRxNextSeqNum);
+
+ // If fragment was stand-alone ack, we're done here; no payload for message reassembler.
+ if (!DidReceiveData(rx_flags))
+ {
+ // Free stand-alone ack buffer.
+ PacketBuffer::Free(data);
+ data = NULL;
+
+ ExitNow();
+ }
+
+ // Truncate the incoming fragment length by the mRxFragmentSize as the negotiated
+ // mRxFragnentSize may be smaller than the characteristic size.
+ data->SetDataLength(nl::Weave::min(data->DataLength(), mRxFragmentSize));
+
+ WeaveLogDebugBtpEngine(Ble, ">>> BTP reassembler received data:");
+ PrintBufDebug(data);
+
+ if (mRxState == kState_Idle)
+ {
+ // Verify StartMessage header flag set.
+ VerifyOrExit(rx_flags & kHeaderFlag_StartMessage, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+
+ mRxLength = (characteristic[(cursor + 1)] << 8) | characteristic[cursor];
+ cursor += 2;
+
+ mRxState = kState_InProgress;
+
+ data->SetStart(&(characteristic[cursor]));
+
+ // Create a new buffer for use as the Rx re-assembly area.
+ mRxBuf = PacketBuffer::New();
+
+ VerifyOrExit(mRxBuf != NULL, err = BLE_ERROR_NO_MEMORY);
+
+ mRxBuf->AddToEnd(data);
+ mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
+ data = NULL;
+ }
+ else if (mRxState == kState_InProgress)
+ {
+ // Verify StartMessage header flag NOT set, since we're in the middle of receiving a message.
+ VerifyOrExit((rx_flags & kHeaderFlag_StartMessage) == 0, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+
+ // Verify ContinueMessage or EndMessage header flag set.
+ VerifyOrExit((rx_flags & kHeaderFlag_ContinueMessage) || (rx_flags & kHeaderFlag_EndMessage),
+ err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+
+ // Add received fragment to reassembled message buffer.
+ data->SetStart(&(characteristic[cursor]));
+ mRxBuf->AddToEnd(data);
+ mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
+ data = NULL;
+
+ // For now, limit WoBle message size to max length of 1 pbuf, as we do for Weave messages sent via IP.
+ // TODO add support for WoBle messages longer than 1 pbuf
+ VerifyOrExit(mRxBuf->Next() == NULL, err = BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG);
+ }
+ else
+ {
+ err = BLE_ERROR_REASSEMBLER_INCORRECT_STATE;
+ ExitNow();
+ }
+
+ if (rx_flags & kHeaderFlag_EndMessage)
+ {
+ // Trim remainder, if any, of received PacketBuffer based on sender-specified length of reassembled message.
+ int padding = mRxBuf->DataLength() - mRxLength;
+
+ if (padding > 0)
+ {
+ mRxBuf->SetDataLength(mRxLength);
+ }
+
+ // Ensure all received fragments add up to sender-specified total message size.
+ VerifyOrExit(mRxBuf->DataLength() == mRxLength, err = BLE_ERROR_REASSEMBLER_MISSING_DATA);
+
+ // We've reassembled the entire message.
+ mRxState = kState_Complete;
+ mRxPacketCount++;
+ }
+
+exit:
+ if (err != BLE_NO_ERROR)
+ {
+ mRxState = kState_Error;
+
+ // Dump protocol engine state, plus header flags and received data length.
+ WeaveLogError(Ble, "HandleCharacteristicReceived failed, err = %d, rx_flags = %u", err, rx_flags);
+ if (didReceiveAck)
+ {
+ WeaveLogError(Ble, "With rx'd ack = %u", receivedAck);
+ }
+ if (mRxBuf != NULL)
+ {
+ WeaveLogError(Ble, "With rx buf data length = %u", mRxBuf->DataLength());
+ }
+ LogState();
+
+ if (data != NULL)
+ {
+ // Tack received data onto rx buffer, to be freed when end point resets protocol engine on close.
+ if (mRxBuf != NULL)
+ {
+ mRxBuf->AddToEnd(data);
+ }
+ else
+ {
+ mRxBuf = data;
+ }
+ }
+ }
+
+ return err;
+}
+
+PacketBuffer * WoBle::RxPacket()
+{
+ return mRxBuf;
+}
+
+bool WoBle::ClearRxPacket()
+{
+ if (mRxState == kState_Complete)
+ {
+ mRxState = kState_Idle;
+ mRxBuf = NULL;
+ // do not reset mRxNextSeqNum
+ return true;
+ }
+
+ return false;
+}
+
+// Calling convention:
+// May only be called if data arg is commited for immediate, synchronous subsequent transmission.
+// Returns false on error. Caller must free data arg on error.
+bool WoBle::HandleCharacteristicSend(PacketBuffer * data, bool send_ack)
+{
+ uint8_t * characteristic;
+ mTxCharCount++;
+
+ if (send_ack && !HasUnackedData())
+ {
+ WeaveLogError(Ble, "HandleCharacteristicSend: send_ack true, but nothing to acknowledge.");
+ return false;
+ }
+
+ if (mTxState == kState_Idle)
+ {
+ if (data == NULL)
+ {
+ return false;
+ }
+
+ mTxBuf = data;
+ mTxState = kState_InProgress;
+ mTxLength = mTxBuf->DataLength();
+
+ WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send whole message:");
+ PrintBufDebug(data);
+
+ // Determine fragment header size.
+ uint8_t header_size = send_ack ? NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE
+ : (NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE - NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE);
+
+ // Ensure enough headroom exists for the BTP header, and any headroom needed by the lower BLE layers.
+ if (!mTxBuf->EnsureReservedSize(header_size + WEAVE_CONFIG_BLE_PKT_RESERVED_SIZE))
+ {
+ // handle error
+ WeaveLogError(Ble, "HandleCharacteristicSend: not enough headroom");
+ mTxState = kState_Error;
+ mTxBuf = NULL; // Avoid double-free after assignment above, as caller frees data on error.
+
+ return false;
+ }
+
+ // prepend header.
+ characteristic = mTxBuf->Start();
+ characteristic -= header_size;
+ mTxBuf->SetStart(characteristic);
+ uint8_t cursor = 1; // first position past header flags byte
+
+ characteristic[0] = kHeaderFlag_StartMessage;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ if (TxPacketType() == kType_Control)
+ SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
+#endif
+
+ if (send_ack)
+ {
+ SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
+ characteristic[cursor++] = GetAndRecordRxAckSeqNum();
+ WeaveLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor - 1]);
+ }
+
+ characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
+ characteristic[cursor++] = mTxLength & 0xff;
+ characteristic[cursor++] = mTxLength >> 8;
+
+ if ((mTxLength + cursor) <= mTxFragmentSize)
+ {
+ mTxBuf->SetDataLength(mTxLength + cursor);
+ mTxLength = 0;
+ SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
+ mTxState = kState_Complete;
+ mTxPacketCount++;
+ }
+ else
+ {
+ mTxBuf->SetDataLength(mTxFragmentSize);
+ mTxLength -= mTxFragmentSize - cursor;
+ }
+
+ WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send first fragment:");
+ PrintBufDebug(data);
+ }
+ else if (mTxState == kState_InProgress)
+ {
+ if (data != NULL)
+ {
+ return false;
+ }
+
+ // advance past the previous fragment
+ characteristic = mTxBuf->Start();
+ characteristic += mTxFragmentSize;
+
+ // prepend header
+ characteristic -= send_ack
+ ? NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE
+ : (NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE - NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE);
+ mTxBuf->SetStart(characteristic);
+ uint8_t cursor = 1; // first position past header flags byte
+
+ characteristic[0] = kHeaderFlag_ContinueMessage;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+ if (TxPacketType() == kType_Control)
+ SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
+#endif
+
+ if (send_ack)
+ {
+ SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
+ characteristic[cursor++] = GetAndRecordRxAckSeqNum();
+ WeaveLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor - 1]);
+ }
+
+ characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
+
+ if ((mTxLength + cursor) <= mTxFragmentSize)
+ {
+ mTxBuf->SetDataLength(mTxLength + cursor);
+ mTxLength = 0;
+ SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
+ mTxState = kState_Complete;
+ mTxPacketCount++;
+ }
+ else
+ {
+ mTxBuf->SetDataLength(mTxFragmentSize);
+ mTxLength -= mTxFragmentSize - cursor;
+ }
+
+ WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send additional fragment:");
+ PrintBufDebug(mTxBuf);
+ }
+ else
+ {
+ // Invalid tx state.
+ return false;
+ }
+
+ return true;
+}
+
+PacketBuffer * WoBle::TxPacket()
+{
+ return mTxBuf;
+}
+
+bool WoBle::ClearTxPacket()
+{
+ if (mTxState == kState_Complete)
+ {
+ mTxState = kState_Idle;
+ mTxBuf = NULL;
+ // do not reset mTxNextSeqNum
+ return true;
+ }
+
+ return false;
+}
+
+void WoBle::LogState() const
+{
+ WeaveLogError(Ble, "mAppState: %p", mAppState);
+
+ WeaveLogError(Ble, "mRxFragmentSize: %d", mRxFragmentSize);
+ WeaveLogError(Ble, "mRxState: %d", mRxState);
+ WeaveLogError(Ble, "mRxBuf: %p", mRxBuf);
+ WeaveLogError(Ble, "mRxNextSeqNum: %d", mRxNextSeqNum);
+ WeaveLogError(Ble, "mRxNewestUnackedSeqNum: %d", mRxNewestUnackedSeqNum);
+ WeaveLogError(Ble, "mRxOldestUnackedSeqNum: %d", mRxOldestUnackedSeqNum);
+ WeaveLogError(Ble, "mRxCharCount: %d", mRxCharCount);
+ WeaveLogError(Ble, "mRxPacketCount: %d", mRxPacketCount);
+
+ WeaveLogError(Ble, "mTxFragmentSize: %d", mTxFragmentSize);
+ WeaveLogError(Ble, "mTxState: %d", mTxState);
+ WeaveLogError(Ble, "mTxBuf: %p", mTxBuf);
+ WeaveLogError(Ble, "mTxNextSeqNum: %d", mTxNextSeqNum);
+ WeaveLogError(Ble, "mTxNewestUnackedSeqNum: %d", mTxNewestUnackedSeqNum);
+ WeaveLogError(Ble, "mTxOldestUnackedSeqNum: %d", mTxOldestUnackedSeqNum);
+ WeaveLogError(Ble, "mTxCharCount: %d", mTxCharCount);
+ WeaveLogError(Ble, "mTxPacketCount: %d", mTxPacketCount);
+}
+
+void WoBle::LogStateDebug() const
+{
+#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+ LogState();
+#endif
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/WoBle.h b/src/ble/WoBle.h
new file mode 100644
index 0000000..c8a7d04
--- /dev/null
+++ b/src/ble/WoBle.h
@@ -0,0 +1,194 @@
+/*
+ *
+ * Copyright (c) 2014-2017 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * 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 defines types and an object for the Weave over
+ * Bluetooth Low Energy (WoBLE) byte-stream, connection-oriented
+ * adaptation of Weave for point-to-point Bluetooth Low Energy
+ * (BLE) links.
+ *
+ */
+
+#ifndef WOBLE_H_
+#define WOBLE_H_
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+
+#include <stdint.h>
+#include <string.h>
+
+#include <BleLayer/BleConfig.h>
+#include <BleLayer/BleError.h>
+#include <SystemLayer/SystemPacketBuffer.h>
+#include <Weave/Support/FlagUtils.hpp>
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+typedef uint8_t SequenceNumber_t; // If type changed from uint8_t, adjust assumptions in WoBle::IsValidAck and
+ // BLEEndPoint::AdjustReceiveWindow.
+
+#if WEAVE_ENABLE_WOBLE_TEST
+class BLEEndPoint;
+#endif
+
+// Public data members:
+typedef enum
+{
+ kType_Data = 0, // Default 0 for data
+ kType_Control = 1,
+} PacketType_t; // WoBle packet types
+
+class WoBle
+{
+#if WEAVE_ENABLE_WOBLE_TEST
+ friend class BLEEndPoint;
+#endif
+
+public:
+ // Public data members:
+ typedef enum
+ {
+ kState_Idle = 0,
+ kState_InProgress = 1,
+ kState_Complete = 2,
+ kState_Error = 3
+ } State_t; // [READ-ONLY] Current state
+
+ enum
+ {
+ kHeaderFlag_StartMessage = 0x01,
+ kHeaderFlag_ContinueMessage = 0x02,
+ kHeaderFlag_EndMessage = 0x04,
+ kHeaderFlag_FragmentAck = 0x08,
+#if WEAVE_ENABLE_WOBLE_TEST
+ kHeaderFlag_CommandMessage = 0x10,
+#endif
+ }; // Masks for BTP fragment header flag bits.
+
+ static const uint16_t sDefaultFragmentSize;
+ static const uint16_t sMaxFragmentSize;
+
+public:
+ // Public functions:
+ WoBle(void) { };
+ ~WoBle(void) { };
+
+ BLE_ERROR Init(void * an_app_state, bool expect_first_ack);
+
+ inline void SetTxFragmentSize(uint8_t size) { mTxFragmentSize = size; };
+ inline void SetRxFragmentSize(uint8_t size) { mRxFragmentSize = size; };
+
+ uint16_t GetRxFragmentSize(void) { return mRxFragmentSize; };
+ uint16_t GetTxFragmentSize(void) { return mTxFragmentSize; };
+
+ SequenceNumber_t GetAndIncrementNextTxSeqNum(void);
+ SequenceNumber_t GetAndRecordRxAckSeqNum(void);
+
+ inline SequenceNumber_t GetLastReceivedSequenceNumber(void) { return mRxNewestUnackedSeqNum; };
+ inline SequenceNumber_t GetNewestUnackedSentSequenceNumber(void) { return mTxNewestUnackedSeqNum; };
+
+ inline bool ExpectingAck(void) const { return mExpectingAck; };
+
+ inline State_t RxState(void) { return mRxState; }
+ inline State_t TxState(void) { return mTxState; }
+#if WEAVE_ENABLE_WOBLE_TEST
+ inline PacketType_t SetTxPacketType(PacketType_t type) { return (mTxPacketType = type); };
+ inline PacketType_t SetRxPacketType(PacketType_t type) { return (mRxPacketType = type); };
+ inline PacketType_t TxPacketType() { return mTxPacketType; };
+ inline PacketType_t RxPacketType() { return mRxPacketType; };
+ inline SequenceNumber_t SetTxPacketSeq(SequenceNumber_t seq) { return (mTxPacketSeq = seq); };
+ inline SequenceNumber_t SetRxPacketSeq(SequenceNumber_t seq) { return (mRxPacketSeq = seq); };
+ inline SequenceNumber_t TxPacketSeq() { return mTxPacketSeq; };
+ inline SequenceNumber_t RxPacketSeq() { return mRxPacketSeq; };
+ inline bool IsCommandPacket(PacketBuffer * p) { return GetFlag(*(p->Start()), kHeaderFlag_CommandMessage); }
+ inline void PushPacketTag(PacketBuffer * p, PacketType_t type)
+ {
+ p->SetStart(p->Start() - sizeof(type));
+ memcpy(p->Start(), &type, sizeof(type));
+ };
+ inline PacketType_t PopPacketTag(PacketBuffer * p)
+ {
+ PacketType_t type;
+ memcpy(&type, p->Start(), sizeof(type));
+ p->SetStart(p->Start() + sizeof(type));
+ return type;
+ };
+#endif // WEAVE_ENABLE_WOBLE_TEST
+
+ bool HasUnackedData(void) const;
+
+ BLE_ERROR HandleCharacteristicReceived(PacketBuffer * data, SequenceNumber_t & receivedAck, bool & didReceiveAck);
+ bool HandleCharacteristicSend(PacketBuffer * data, bool send_ack);
+ BLE_ERROR EncodeStandAloneAck(PacketBuffer * data);
+
+ PacketBuffer * RxPacket(void);
+ PacketBuffer * TxPacket(void);
+
+ bool ClearRxPacket(void);
+ bool ClearTxPacket(void);
+
+ void LogState(void) const;
+ void LogStateDebug(void) const;
+
+private:
+ // Private data members:
+#if WEAVE_ENABLE_WOBLE_TEST
+ PacketType_t mTxPacketType;
+ PacketType_t mRxPacketType;
+ SequenceNumber_t mTxPacketSeq;
+ SequenceNumber_t mRxPacketSeq;
+#endif
+ State_t mRxState;
+ uint16_t mRxLength;
+ void * mAppState;
+ PacketBuffer * mRxBuf;
+ SequenceNumber_t mRxNextSeqNum;
+ SequenceNumber_t mRxNewestUnackedSeqNum;
+ SequenceNumber_t mRxOldestUnackedSeqNum;
+ uint16_t mRxFragmentSize;
+
+ State_t mTxState;
+ uint16_t mTxLength;
+ PacketBuffer * mTxBuf;
+ SequenceNumber_t mTxNextSeqNum;
+ SequenceNumber_t mTxNewestUnackedSeqNum;
+ SequenceNumber_t mTxOldestUnackedSeqNum;
+ bool mExpectingAck;
+ uint16_t mTxFragmentSize;
+
+ uint16_t mRxCharCount;
+ uint16_t mRxPacketCount;
+ uint16_t mTxCharCount;
+ uint16_t mTxPacketCount;
+
+private:
+ // Private functions:
+ bool IsValidAck(SequenceNumber_t ack_num) const;
+ BLE_ERROR HandleAckReceived(SequenceNumber_t ack_num);
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* WOBLE_H_ */