Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : * Copyright (c) 2014-2017 Nest Labs, Inc.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file implements a Bluetooth Low Energy (BLE) connection
22 : * endpoint abstraction for the byte-streaming,
23 : * connection-oriented CHIP over Bluetooth Low Energy (CHIPoBLE)
24 : * Bluetooth Transport Protocol (BTP).
25 : *
26 : */
27 :
28 : #define _CHIP_BLE_BLE_H
29 : #include "BLEEndPoint.h"
30 :
31 : #include <cstdint>
32 : #include <cstring>
33 : #include <utility>
34 :
35 : #include <lib/support/BitFlags.h>
36 : #include <lib/support/CodeUtils.h>
37 : #include <lib/support/logging/CHIPLogging.h>
38 : #include <system/SystemClock.h>
39 : #include <system/SystemLayer.h>
40 : #include <system/SystemPacketBuffer.h>
41 :
42 : #include "BleApplicationDelegate.h"
43 : #include "BleConfig.h"
44 : #include "BleError.h"
45 : #include "BleLayer.h"
46 : #include "BleLayerDelegate.h"
47 : #include "BlePlatformDelegate.h"
48 : #include "BleRole.h"
49 : #include "BleUUID.h"
50 : #include "BtpEngine.h"
51 :
52 : // Define below to enable extremely verbose, BLE end point-specific debug logging.
53 : #undef CHIP_BLE_END_POINT_DEBUG_LOGGING_ENABLED
54 :
55 : #ifdef CHIP_BLE_END_POINT_DEBUG_LOGGING_ENABLED
56 : #define ChipLogDebugBleEndPoint(MOD, MSG, ...) ChipLogDetail(MOD, MSG, ##__VA_ARGS__)
57 : #else
58 : #define ChipLogDebugBleEndPoint(MOD, MSG, ...)
59 : #endif
60 :
61 : /**
62 : * @def BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD
63 : *
64 : * @brief
65 : * If an end point's receive window drops equal to or below this value, it will send an immediate acknowledgement
66 : * packet to re-open its window instead of waiting for the send-ack timer to expire.
67 : *
68 : */
69 : #define BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD 1
70 :
71 : /**
72 : * @def BLE_UNSUBSCRIBE_TIMEOUT_MS
73 : *
74 : * @brief
75 : * This is amount of time, in milliseconds, which a BLE end point will wait for an unsubscribe operation to complete
76 : * before it automatically releases its BLE connection and frees itself. The default value of 5 seconds is arbitrary.
77 : *
78 : */
79 : #define BLE_UNSUBSCRIBE_TIMEOUT_MS 5000
80 :
81 : #define BTP_ACK_SEND_TIMEOUT_MS 2500
82 :
83 : /**
84 : * @def BTP_WINDOW_NO_ACK_SEND_THRESHOLD
85 : *
86 : * @brief
87 : * Data fragments may only be sent without piggybacked acks if receiver's window size is above this threshold.
88 : *
89 : */
90 : #define BTP_WINDOW_NO_ACK_SEND_THRESHOLD 1
91 :
92 : namespace chip {
93 : namespace Ble {
94 :
95 0 : CHIP_ERROR BLEEndPoint::StartConnect()
96 : {
97 0 : CHIP_ERROR err = CHIP_NO_ERROR;
98 : BleTransportCapabilitiesRequestMessage req;
99 0 : PacketBufferHandle buf;
100 0 : constexpr uint8_t numVersions =
101 : CHIP_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - CHIP_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION + 1;
102 : static_assert(numVersions <= NUM_SUPPORTED_PROTOCOL_VERSIONS, "Incompatibly protocol versions");
103 :
104 : // Ensure we're in the correct state.
105 0 : VerifyOrExit(mState == kState_Ready, err = CHIP_ERROR_INCORRECT_STATE);
106 0 : mState = kState_Connecting;
107 :
108 : // Build BLE transport protocol capabilities request.
109 0 : buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
110 0 : VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
111 :
112 : // Zero-initialize BLE transport capabilities request.
113 0 : memset(&req, 0, sizeof(req));
114 :
115 0 : req.mMtu = mBle->mPlatformDelegate->GetMTU(mConnObj);
116 :
117 0 : req.mWindowSize = BLE_MAX_RECEIVE_WINDOW_SIZE;
118 :
119 : // Populate request with highest supported protocol versions
120 0 : for (uint8_t i = 0; i < numVersions; i++)
121 : {
122 0 : req.SetSupportedProtocolVersion(i, static_cast<uint8_t>(CHIP_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - i));
123 : }
124 :
125 0 : err = req.Encode(buf);
126 0 : SuccessOrExit(err);
127 :
128 : // Start connect timer. Canceled when end point freed or connection established.
129 0 : err = StartConnectTimer();
130 0 : SuccessOrExit(err);
131 :
132 : // Send BLE transport capabilities request to peripheral via GATT write.
133 : // Add reference to message fragment for duration of platform's GATT write attempt. CHIP retains partial
134 : // ownership of message fragment's packet buffer, since this is the same buffer as that of the whole message, just
135 : // with a fragmenter-modified payload offset and data length, by a Retain() on the handle when calling this function.
136 0 : err = SendWrite(buf.Retain());
137 0 : SuccessOrExit(err);
138 :
139 : // Free request buffer on write confirmation. Stash a reference to it in mSendQueue, which we don't use anyway
140 : // until the connection has been set up.
141 0 : QueueTx(std::move(buf), kType_Data);
142 :
143 0 : exit:
144 : // If we failed to initiate the connection, close the end point.
145 0 : if (err != CHIP_NO_ERROR)
146 : {
147 0 : StopConnectTimer();
148 0 : DoClose(kBleCloseFlag_AbortTransmission, err);
149 : }
150 :
151 0 : return err;
152 0 : }
153 :
154 0 : CHIP_ERROR BLEEndPoint::HandleConnectComplete()
155 : {
156 0 : CHIP_ERROR err = CHIP_NO_ERROR;
157 :
158 0 : mState = kState_Connected;
159 :
160 : // Cancel the connect timer.
161 0 : StopConnectTimer();
162 :
163 : // We've successfully completed the BLE transport protocol handshake, so let the application know we're open for business.
164 0 : if (mBleTransport != nullptr)
165 : {
166 : // Indicate connect complete to next-higher layer.
167 0 : mBleTransport->OnEndPointConnectComplete(this, CHIP_NO_ERROR);
168 : }
169 : else
170 : {
171 : // If no connect complete callback has been set up, close the end point.
172 0 : err = BLE_ERROR_NO_CONNECT_COMPLETE_CALLBACK;
173 : }
174 :
175 0 : return err;
176 : }
177 :
178 6 : CHIP_ERROR BLEEndPoint::HandleReceiveConnectionComplete()
179 : {
180 6 : CHIP_ERROR err = CHIP_NO_ERROR;
181 :
182 : ChipLogDebugBleEndPoint(Ble, "entered HandleReceiveConnectionComplete");
183 6 : mState = kState_Connected;
184 :
185 : // Cancel receive connection timer.
186 6 : StopReceiveConnectionTimer();
187 :
188 : // We've successfully completed the BLE transport protocol handshake, so let the transport know we're open for business.
189 6 : if (mBleTransport != nullptr)
190 : {
191 : // Indicate BLE transport protocol connection received to next-higher layer.
192 6 : err = mBleTransport->SetEndPoint(this);
193 : }
194 : else
195 : {
196 0 : err = BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK;
197 : }
198 :
199 6 : return err;
200 : }
201 :
202 6 : void BLEEndPoint::HandleSubscribeReceived()
203 : {
204 6 : CHIP_ERROR err = CHIP_NO_ERROR;
205 :
206 6 : VerifyOrExit(mState == kState_Connecting || mState == kState_Aborting, err = CHIP_ERROR_INCORRECT_STATE);
207 6 : VerifyOrExit(!mSendQueue.IsNull(), err = CHIP_ERROR_INCORRECT_STATE);
208 :
209 : // Send BTP capabilities response to peripheral via GATT indication.
210 : // Add reference to message fragment for duration of platform's GATT indication attempt. CHIP retains partial
211 : // ownership of message fragment's packet buffer, since this is the same buffer as that of the whole message, just
212 : // with a fragmenter-modified payload offset and data length.
213 6 : err = SendIndication(mSendQueue.Retain());
214 6 : if (err != CHIP_NO_ERROR)
215 : {
216 : // Ensure transmit queue is empty and set to NULL.
217 0 : mSendQueue = nullptr;
218 :
219 0 : ChipLogError(Ble, "cap resp ind failed");
220 0 : ExitNow();
221 : }
222 :
223 : // Shrink remote receive window counter by 1, since we've sent an indication which requires acknowledgement.
224 6 : mRemoteReceiveWindowSize = static_cast<SequenceNumber_t>(mRemoteReceiveWindowSize - 1);
225 : ChipLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
226 :
227 : // Start ack recvd timer for handshake indication.
228 6 : err = StartAckReceivedTimer();
229 6 : SuccessOrExit(err);
230 :
231 : ChipLogDebugBleEndPoint(Ble, "got subscribe, sent indication w/ capabilities response");
232 :
233 : // If SendIndication returns true, mSendQueue is freed on indication confirmation, or on close in case of
234 : // connection error.
235 :
236 6 : if (mState != kState_Aborting)
237 : {
238 : // If peripheral accepted the BTP connection, its end point must enter the connected state here, i.e. before it
239 : // receives a GATT confirmation for the capabilities response indication. This behavior is required to handle the
240 : // case where a peripheral's BLE controller passes up the central's first message fragment write before the
241 : // capabilities response indication confirmation. If the end point waited for this indication confirmation before
242 : // it entered the connected state, it'd be in the wrong state to receive the central's first data write, and drop
243 : // the corresponding message fragment.
244 6 : err = HandleReceiveConnectionComplete();
245 6 : SuccessOrExit(err);
246 : } // Else State == kState_Aborting, so we'll close end point when indication confirmation received.
247 :
248 0 : exit:
249 6 : if (err != CHIP_NO_ERROR)
250 : {
251 0 : DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, err);
252 : }
253 6 : }
254 :
255 1 : void BLEEndPoint::HandleSubscribeComplete()
256 : {
257 1 : ChipLogProgress(Ble, "subscribe complete, ep = %p", this);
258 1 : mConnStateFlags.Clear(ConnectionStateFlag::kGattOperationInFlight);
259 :
260 1 : CHIP_ERROR err = DriveSending();
261 :
262 1 : if (err != CHIP_NO_ERROR)
263 : {
264 0 : DoClose(kBleCloseFlag_AbortTransmission, CHIP_NO_ERROR);
265 : }
266 1 : }
267 :
268 1 : void BLEEndPoint::HandleUnsubscribeComplete()
269 : {
270 : // Don't bother to clear GattOperationInFlight, we're about to free the end point anyway.
271 1 : Free();
272 1 : }
273 :
274 2 : bool BLEEndPoint::IsConnected(uint8_t state) const
275 : {
276 2 : return (state == kState_Connected || state == kState_Closing);
277 : }
278 :
279 23 : bool BLEEndPoint::IsUnsubscribePending() const
280 : {
281 23 : return mTimerStateFlags.Has(TimerStateFlag::kUnsubscribeTimerRunning);
282 : }
283 :
284 9 : void BLEEndPoint::Abort()
285 : {
286 : // No more callbacks after this point, since application explicitly called Abort().
287 9 : OnConnectComplete = nullptr;
288 9 : OnConnectionClosed = nullptr;
289 9 : OnMessageReceived = nullptr;
290 :
291 9 : DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, CHIP_NO_ERROR);
292 9 : }
293 :
294 0 : void BLEEndPoint::Close()
295 : {
296 : // No more callbacks after this point, since application explicitly called Close().
297 0 : OnConnectComplete = nullptr;
298 0 : OnConnectionClosed = nullptr;
299 0 : OnMessageReceived = nullptr;
300 :
301 0 : DoClose(kBleCloseFlag_SuppressCallback, CHIP_NO_ERROR);
302 0 : }
303 :
304 11 : void BLEEndPoint::DoClose(uint8_t flags, CHIP_ERROR err)
305 : {
306 11 : uint8_t oldState = mState;
307 :
308 : // If end point is not closed or closing, OR end point was closing gracefully, but tx abort has been specified...
309 11 : if ((mState != kState_Closed && mState != kState_Closing) ||
310 0 : (mState == kState_Closing && (flags & kBleCloseFlag_AbortTransmission)))
311 : {
312 : // Cancel Connect and ReceiveConnect timers if they are running.
313 : // Check role first to avoid needless iteration over timer pool.
314 11 : if (mRole == kBleRole_Central)
315 : {
316 0 : StopConnectTimer();
317 : }
318 : else // (mRole == kBleRole_Peripheral), verified on Init
319 : {
320 11 : StopReceiveConnectionTimer();
321 : }
322 :
323 : // If transmit buffer is empty or a transmission abort was specified...
324 11 : if (mBtpEngine.TxState() == BtpEngine::kState_Idle || (flags & kBleCloseFlag_AbortTransmission))
325 : {
326 11 : FinalizeClose(oldState, flags, err);
327 : }
328 : else
329 : {
330 : // Wait for send queue and fragmenter's tx buffer to become empty, to ensure all pending messages have been
331 : // sent. Only free end point and tell platform it can throw away the underlying BLE connection once all
332 : // pending messages have been sent and acknowledged by the remote CHIPoBLE stack, or once the remote stack
333 : // closes the CHIPoBLE connection.
334 : //
335 : // In so doing, BLEEndPoint attempts to emulate the level of reliability afforded by TCPEndPoint and TCP
336 : // sockets in general with a typical default SO_LINGER option. That said, there is no hard guarantee that
337 : // pending messages will be sent once (Do)Close() is called, so developers should use application-level
338 : // messages to confirm the receipt of all data sent prior to a Close() call.
339 0 : mState = kState_Closing;
340 :
341 0 : if ((flags & kBleCloseFlag_SuppressCallback) == 0)
342 : {
343 0 : DoCloseCallback(oldState, flags, err);
344 : }
345 :
346 0 : if (mBleTransport != nullptr && (flags & kBleCloseFlag_SuppressCallback) != 0)
347 : {
348 0 : mBleTransport->OnEndPointConnectionClosed(this, err);
349 : }
350 : }
351 : }
352 11 : }
353 :
354 11 : void BLEEndPoint::FinalizeClose(uint8_t oldState, uint8_t flags, CHIP_ERROR err)
355 : {
356 11 : mState = kState_Closed;
357 :
358 : // Ensure transmit queue is empty and set to NULL.
359 11 : mSendQueue = nullptr;
360 :
361 : // Fire application's close callback if we haven't already, and it's not suppressed.
362 11 : if (oldState != kState_Closing && (flags & kBleCloseFlag_SuppressCallback) == 0)
363 : {
364 2 : DoCloseCallback(oldState, flags, err);
365 : }
366 :
367 11 : if (mBleTransport != nullptr && (flags & kBleCloseFlag_SuppressCallback) != 0)
368 : {
369 9 : mBleTransport->OnEndPointConnectionClosed(this, err);
370 : }
371 :
372 : // If underlying BLE connection has closed, connection object is invalid, so just free the end point and return.
373 11 : if (err == BLE_ERROR_REMOTE_DEVICE_DISCONNECTED || err == BLE_ERROR_APP_CLOSED_CONNECTION)
374 : {
375 0 : mConnObj = BLE_CONNECTION_UNINITIALIZED; // Clear handle to BLE connection, so we don't double-close it.
376 0 : Free();
377 : }
378 : else // Otherwise, try to signal close to remote device before end point releases BLE connection and frees itself.
379 : {
380 11 : if (mRole == kBleRole_Central && mConnStateFlags.Has(ConnectionStateFlag::kDidBeginSubscribe))
381 : {
382 : // Cancel send and receive-ack timers, if running.
383 0 : StopAckReceivedTimer();
384 0 : StopSendAckTimer();
385 :
386 : // Indicate close of chipConnection to peripheral via GATT unsubscribe. Keep end point allocated until
387 : // unsubscribe completes or times out, so platform doesn't close underlying BLE connection before
388 : // we're really sure the unsubscribe request has been sent.
389 0 : err = mBle->mPlatformDelegate->UnsubscribeCharacteristic(mConnObj, &CHIP_BLE_SVC_ID, &CHIP_BLE_CHAR_2_UUID);
390 0 : if (err != CHIP_NO_ERROR)
391 : {
392 0 : ChipLogError(Ble, "BtpEngine unsubscribe failed %" CHIP_ERROR_FORMAT, err.Format());
393 :
394 : // If unsubscribe fails, release BLE connection and free end point immediately.
395 0 : Free();
396 : }
397 0 : else if (mConnObj != BLE_CONNECTION_UNINITIALIZED)
398 : {
399 : // Unsubscribe request was sent successfully, and a confirmation wasn't spontaneously generated or
400 : // received in the downcall to UnsubscribeCharacteristic, so set timer for the unsubscribe to complete.
401 0 : err = StartUnsubscribeTimer();
402 :
403 0 : if (err != CHIP_NO_ERROR)
404 : {
405 0 : Free();
406 : }
407 :
408 : // Mark unsubscribe GATT operation in progress.
409 0 : mConnStateFlags.Set(ConnectionStateFlag::kGattOperationInFlight);
410 : }
411 : }
412 : else // mRole == kBleRole_Peripheral, OR mTimerStateFlags.Has(ConnectionStateFlag::kDidBeginSubscribe) == false...
413 : {
414 11 : Free();
415 : }
416 : }
417 11 : }
418 :
419 2 : void BLEEndPoint::DoCloseCallback(uint8_t state, uint8_t flags, CHIP_ERROR err)
420 : {
421 2 : if (state == kState_Connecting)
422 : {
423 1 : if (mBleTransport != nullptr)
424 : {
425 1 : mBleTransport->OnEndPointConnectComplete(this, err);
426 : }
427 : }
428 : else
429 : {
430 1 : if (mBleTransport != nullptr)
431 : {
432 1 : mBleTransport->OnEndPointConnectionClosed(this, err);
433 : }
434 : }
435 :
436 : // Callback fires once per end point lifetime.
437 2 : OnConnectComplete = nullptr;
438 2 : OnConnectionClosed = nullptr;
439 2 : }
440 :
441 12 : void BLEEndPoint::ReleaseBleConnection()
442 : {
443 12 : if (mConnObj != BLE_CONNECTION_UNINITIALIZED)
444 : {
445 12 : if (mConnStateFlags.Has(ConnectionStateFlag::kAutoClose))
446 : {
447 0 : ChipLogProgress(Ble, "Auto-closing end point's BLE connection.");
448 0 : mBle->mPlatformDelegate->CloseConnection(mConnObj);
449 : }
450 : else
451 : {
452 12 : ChipLogProgress(Ble, "Releasing end point's BLE connection back to application.");
453 12 : mBle->mApplicationDelegate->NotifyChipConnectionClosed(mConnObj);
454 : }
455 :
456 : // Never release the same BLE connection twice.
457 12 : mConnObj = BLE_CONNECTION_UNINITIALIZED;
458 : }
459 12 : }
460 :
461 12 : void BLEEndPoint::Free()
462 : {
463 : // Release BLE connection. Will close connection if AutoClose enabled for this end point. Otherwise, informs
464 : // application that CHIP is done with this BLE connection, and application makes decision about whether to close
465 : // and clean up or retain connection.
466 12 : ReleaseBleConnection();
467 :
468 : // Clear fragmentation and reassembly engine's Tx and Rx buffers. Counters will be reset by next engine init.
469 12 : FreeBtpEngine();
470 :
471 : // Clear pending ack buffer, if any.
472 12 : mAckToSend = nullptr;
473 :
474 : // Cancel all timers.
475 12 : StopConnectTimer();
476 12 : StopReceiveConnectionTimer();
477 12 : StopAckReceivedTimer();
478 12 : StopSendAckTimer();
479 12 : StopUnsubscribeTimer();
480 :
481 : // Clear callbacks.
482 12 : OnConnectComplete = nullptr;
483 12 : OnMessageReceived = nullptr;
484 12 : OnConnectionClosed = nullptr;
485 :
486 : // Clear handle to underlying BLE connection.
487 12 : mConnObj = BLE_CONNECTION_UNINITIALIZED;
488 :
489 : // Release the AddRef() that happened when the end point was allocated.
490 12 : Release();
491 12 : }
492 :
493 12 : void BLEEndPoint::FreeBtpEngine()
494 : {
495 : // Free transmit disassembly buffer
496 12 : mBtpEngine.ClearTxPacket();
497 :
498 : // Free receive reassembly buffer
499 12 : mBtpEngine.ClearRxPacket();
500 12 : }
501 :
502 12 : CHIP_ERROR BLEEndPoint::Init(BleLayer * bleLayer, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose)
503 : {
504 : // Fail if already initialized.
505 12 : VerifyOrReturnError(mBle == nullptr, CHIP_ERROR_INCORRECT_STATE);
506 :
507 : // Validate args.
508 12 : VerifyOrReturnError(bleLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
509 12 : VerifyOrReturnError(connObj != BLE_CONNECTION_UNINITIALIZED, CHIP_ERROR_INVALID_ARGUMENT);
510 12 : VerifyOrReturnError((role == kBleRole_Central || role == kBleRole_Peripheral), CHIP_ERROR_INVALID_ARGUMENT);
511 :
512 : // If end point plays peripheral role, expect ack for indication sent as last step of BTP handshake.
513 : // If central, peripheral's handshake indication 'ack's write sent by central to kick off the BTP handshake.
514 12 : bool expectInitialAck = (role == kBleRole_Peripheral);
515 :
516 12 : CHIP_ERROR err = mBtpEngine.Init(this, expectInitialAck);
517 12 : if (err != CHIP_NO_ERROR)
518 : {
519 0 : ChipLogError(Ble, "BtpEngine init failed");
520 0 : return err;
521 : }
522 :
523 12 : mBle = bleLayer;
524 12 : mRefCount = 1;
525 :
526 : // BLEEndPoint data members:
527 12 : mConnObj = connObj;
528 12 : mRole = role;
529 12 : mTimerStateFlags.ClearAll();
530 12 : mConnStateFlags.ClearAll().Set(ConnectionStateFlag::kAutoClose, autoClose);
531 12 : mLocalReceiveWindowSize = 0;
532 12 : mRemoteReceiveWindowSize = 0;
533 12 : mReceiveWindowMaxSize = 0;
534 12 : mSendQueue = nullptr;
535 12 : mAckToSend = nullptr;
536 :
537 : ChipLogDebugBleEndPoint(Ble, "initialized local rx window, size = %u", mLocalReceiveWindowSize);
538 :
539 : // End point is ready to connect or receive a connection.
540 12 : mState = kState_Ready;
541 :
542 12 : return CHIP_NO_ERROR;
543 : }
544 :
545 0 : void BLEEndPoint::AddRef()
546 : {
547 0 : VerifyOrDie(mRefCount < UINT32_MAX);
548 0 : mRefCount++;
549 0 : }
550 :
551 12 : void BLEEndPoint::Release()
552 : {
553 12 : VerifyOrDie(mRefCount > 0u);
554 : // Decrement the ref count. When it reaches zero, NULL out the pointer to the chip::System::Layer
555 : // object. This effectively declared the object free and ready for re-allocation.
556 12 : mRefCount--;
557 12 : if (mRefCount == 0)
558 : {
559 12 : mBle = nullptr;
560 : }
561 12 : }
562 :
563 1 : CHIP_ERROR BLEEndPoint::SendCharacteristic(PacketBufferHandle && buf)
564 : {
565 1 : CHIP_ERROR err = CHIP_NO_ERROR;
566 :
567 1 : if (mRole == kBleRole_Central)
568 : {
569 0 : SuccessOrExit(err = SendWrite(std::move(buf)));
570 : // Write succeeded, so shrink remote receive window counter by 1.
571 0 : mRemoteReceiveWindowSize = static_cast<SequenceNumber_t>(mRemoteReceiveWindowSize - 1);
572 : ChipLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
573 : }
574 : else // (mRole == kBleRole_Peripheral), verified on Init
575 : {
576 1 : SuccessOrExit(err = SendIndication(std::move(buf)));
577 : // Indication succeeded, so shrink remote receive window counter by 1.
578 1 : mRemoteReceiveWindowSize = static_cast<SequenceNumber_t>(mRemoteReceiveWindowSize - 1);
579 : ChipLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
580 : }
581 :
582 1 : exit:
583 1 : return err;
584 : }
585 :
586 : /*
587 : * Routine to queue the Tx packet with a packet type
588 : * kType_Data(0) - data packet
589 : * kType_Control(1) - control packet
590 : */
591 12 : void BLEEndPoint::QueueTx(PacketBufferHandle && data, PacketType_t type)
592 : {
593 12 : if (mSendQueue.IsNull())
594 : {
595 12 : mSendQueue = std::move(data);
596 : ChipLogDebugBleEndPoint(Ble, "%s: Set data as new mSendQueue %p, type %d", __FUNCTION__, mSendQueue->Start(), type);
597 : }
598 : else
599 : {
600 0 : mSendQueue->AddToEnd(std::move(data));
601 : ChipLogDebugBleEndPoint(Ble, "%s: Append data to mSendQueue %p, type %d", __FUNCTION__, mSendQueue->Start(), type);
602 : }
603 12 : }
604 :
605 0 : CHIP_ERROR BLEEndPoint::Send(PacketBufferHandle && data)
606 : {
607 : ChipLogDebugBleEndPoint(Ble, "entered Send");
608 :
609 0 : CHIP_ERROR err = CHIP_NO_ERROR;
610 :
611 0 : VerifyOrExit(!data.IsNull(), err = CHIP_ERROR_INVALID_ARGUMENT);
612 0 : VerifyOrExit(IsConnected(mState), err = CHIP_ERROR_INCORRECT_STATE);
613 :
614 : // Ensure outgoing message fits in a single contiguous packet buffer, as currently required by the
615 : // message fragmentation and reassembly engine.
616 0 : if (data->HasChainedBuffer())
617 : {
618 0 : data->CompactHead();
619 :
620 0 : if (data->HasChainedBuffer())
621 : {
622 0 : err = CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG;
623 0 : ExitNow();
624 : }
625 : }
626 :
627 : // Add new message to send queue.
628 0 : QueueTx(std::move(data), kType_Data);
629 :
630 : // Send first fragment of new message, if we can.
631 0 : err = DriveSending();
632 0 : SuccessOrExit(err);
633 :
634 0 : exit:
635 : ChipLogDebugBleEndPoint(Ble, "exiting Send");
636 0 : if (err != CHIP_NO_ERROR)
637 : {
638 0 : DoClose(kBleCloseFlag_AbortTransmission, err);
639 : }
640 :
641 0 : return err;
642 : }
643 :
644 1 : bool BLEEndPoint::PrepareNextFragment(PacketBufferHandle && data, bool & sentAck)
645 : {
646 : // If we have a pending fragment acknowledgement to send, piggyback it on the fragment we're about to transmit.
647 1 : if (mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning))
648 : {
649 : // Reset local receive window counter.
650 0 : mLocalReceiveWindowSize = mReceiveWindowMaxSize;
651 : ChipLogDebugBleEndPoint(Ble, "reset local rx window on piggyback ack tx, size = %u", mLocalReceiveWindowSize);
652 :
653 : // Tell caller AND fragmenter we have an ack to piggyback.
654 0 : sentAck = true;
655 : }
656 : else
657 : {
658 : // No ack to piggyback.
659 1 : sentAck = false;
660 : }
661 :
662 1 : return mBtpEngine.HandleCharacteristicSend(std::move(data), sentAck);
663 : }
664 :
665 1 : CHIP_ERROR BLEEndPoint::SendNextMessage()
666 : {
667 : // Get the first queued packet to send
668 1 : PacketBufferHandle data = mSendQueue.PopHead();
669 :
670 : // Hand whole message payload to the fragmenter.
671 : bool sentAck;
672 1 : VerifyOrReturnError(PrepareNextFragment(std::move(data), sentAck), BLE_ERROR_CHIPOBLE_PROTOCOL_ABORT);
673 :
674 : /*
675 : // Todo: reenabled it after integrating fault injection
676 : // Send first message fragment over the air.
677 : CHIP_FAULT_INJECT(chip::FaultInjection::kFault_CHIPOBLESend, {
678 : if (mRole == kBleRole_Central)
679 : {
680 : err = BLE_ERROR_GATT_WRITE_FAILED;
681 : }
682 : else
683 : {
684 : err = BLE_ERROR_GATT_INDICATE_FAILED;
685 : }
686 : ExitNow();
687 : });
688 : */
689 1 : ReturnErrorOnFailure(SendCharacteristic(mBtpEngine.BorrowTxPacket()));
690 :
691 1 : if (sentAck)
692 : {
693 : // If sent piggybacked ack, stop send-ack timer.
694 0 : StopSendAckTimer();
695 : }
696 :
697 : // Start ack received timer, if it's not already running.
698 1 : return StartAckReceivedTimer();
699 1 : }
700 :
701 0 : CHIP_ERROR BLEEndPoint::ContinueMessageSend()
702 : {
703 : bool sentAck;
704 :
705 0 : if (!PrepareNextFragment(nullptr, sentAck))
706 : {
707 : // Log BTP error
708 0 : ChipLogError(Ble, "btp fragmenter error on send!");
709 0 : mBtpEngine.LogState();
710 :
711 0 : return BLE_ERROR_CHIPOBLE_PROTOCOL_ABORT;
712 : }
713 :
714 0 : ReturnErrorOnFailure(SendCharacteristic(mBtpEngine.BorrowTxPacket()));
715 :
716 0 : if (sentAck)
717 : {
718 : // If sent piggybacked ack, stop send-ack timer.
719 0 : StopSendAckTimer();
720 : }
721 :
722 : // Start ack received timer, if it's not already running.
723 0 : return StartAckReceivedTimer();
724 : }
725 :
726 2 : CHIP_ERROR BLEEndPoint::HandleHandshakeConfirmationReceived()
727 : {
728 : ChipLogDebugBleEndPoint(Ble, "entered HandleHandshakeConfirmationReceived");
729 :
730 2 : CHIP_ERROR err = CHIP_NO_ERROR;
731 2 : uint8_t closeFlags = kBleCloseFlag_AbortTransmission;
732 :
733 : // Free capabilities request/response payload.
734 2 : mSendQueue.FreeHead();
735 :
736 2 : if (mRole == kBleRole_Central)
737 : {
738 : // Subscribe to characteristic which peripheral will use to send indications. Prompts peripheral to send
739 : // BLE transport capabilities indication.
740 0 : err = mBle->mPlatformDelegate->SubscribeCharacteristic(mConnObj, &CHIP_BLE_SVC_ID, &CHIP_BLE_CHAR_2_UUID);
741 0 : SuccessOrExit(err);
742 :
743 : // We just sent a GATT subscribe request, so make sure to attempt unsubscribe on close.
744 0 : mConnStateFlags.Set(ConnectionStateFlag::kDidBeginSubscribe);
745 :
746 : // Mark GATT operation in progress for subscribe request.
747 0 : mConnStateFlags.Set(ConnectionStateFlag::kGattOperationInFlight);
748 : }
749 : else // (mRole == kBleRole_Peripheral), verified on Init
750 : {
751 : ChipLogDebugBleEndPoint(Ble, "got peripheral handshake indication confirmation");
752 :
753 2 : if (mState == kState_Connected) // If we accepted BTP connection...
754 : {
755 : // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
756 : // pending on which to piggyback an ack, send immediate stand-alone ack.
757 0 : if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD && mSendQueue.IsNull())
758 : {
759 0 : err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
760 0 : SuccessOrExit(err);
761 : }
762 : else
763 : {
764 : // Drive sending in case application called Send() after we sent the handshake indication, but
765 : // before the GATT confirmation for this indication was received.
766 0 : err = DriveSending();
767 0 : SuccessOrExit(err);
768 : }
769 : }
770 2 : else if (mState == kState_Aborting) // Else, if we rejected BTP connection...
771 : {
772 0 : closeFlags |= kBleCloseFlag_SuppressCallback;
773 0 : err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
774 0 : ExitNow();
775 : }
776 : }
777 :
778 2 : exit:
779 : ChipLogDebugBleEndPoint(Ble, "exiting HandleHandshakeConfirmationReceived");
780 :
781 2 : if (err != CHIP_NO_ERROR)
782 : {
783 0 : DoClose(closeFlags, err);
784 : }
785 :
786 2 : return err;
787 : }
788 :
789 0 : CHIP_ERROR BLEEndPoint::HandleFragmentConfirmationReceived()
790 : {
791 0 : CHIP_ERROR err = CHIP_NO_ERROR;
792 :
793 : ChipLogDebugBleEndPoint(Ble, "entered HandleFragmentConfirmationReceived");
794 :
795 : // Suppress error logging if GATT confirmation overlaps with unsubscribe on final close.
796 0 : if (IsUnsubscribePending())
797 : {
798 : ChipLogDebugBleEndPoint(Ble, "send conf rx'd while unsubscribe in flight");
799 0 : ExitNow();
800 : }
801 :
802 : // Ensure we're in correct state to receive confirmation of non-handshake GATT send.
803 0 : VerifyOrExit(IsConnected(mState), err = CHIP_ERROR_INCORRECT_STATE);
804 :
805 0 : if (mConnStateFlags.Has(ConnectionStateFlag::kStandAloneAckInFlight))
806 : {
807 : // If confirmation was received for stand-alone ack, free its tx buffer.
808 0 : mAckToSend = nullptr;
809 :
810 0 : mConnStateFlags.Clear(ConnectionStateFlag::kStandAloneAckInFlight);
811 : }
812 :
813 : // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
814 : // pending on which to piggyback an ack, send immediate stand-alone ack.
815 : //
816 : // This check covers the case where the local receive window has shrunk between transmission and confirmation of
817 : // the stand-alone ack, and also the case where a window size < the immediate ack threshold was detected in
818 : // Receive(), but the stand-alone ack was deferred due to a pending outbound message fragment.
819 0 : if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD && mSendQueue.IsNull() &&
820 0 : mBtpEngine.TxState() != BtpEngine::kState_InProgress)
821 : {
822 0 : err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
823 0 : SuccessOrExit(err);
824 : }
825 : else
826 : {
827 0 : err = DriveSending();
828 0 : SuccessOrExit(err);
829 : }
830 :
831 0 : exit:
832 0 : if (err != CHIP_NO_ERROR)
833 : {
834 0 : DoClose(kBleCloseFlag_AbortTransmission, err);
835 : }
836 :
837 0 : return err;
838 : }
839 :
840 2 : CHIP_ERROR BLEEndPoint::HandleGattSendConfirmationReceived()
841 : {
842 : ChipLogDebugBleEndPoint(Ble, "entered HandleGattSendConfirmationReceived");
843 :
844 : // Mark outstanding GATT operation as finished.
845 2 : mConnStateFlags.Clear(ConnectionStateFlag::kGattOperationInFlight);
846 :
847 : // If confirmation was for outbound portion of BTP connect handshake...
848 2 : if (!mConnStateFlags.Has(ConnectionStateFlag::kCapabilitiesConfReceived))
849 : {
850 2 : mConnStateFlags.Set(ConnectionStateFlag::kCapabilitiesConfReceived);
851 :
852 2 : return HandleHandshakeConfirmationReceived();
853 : }
854 :
855 0 : return HandleFragmentConfirmationReceived();
856 : }
857 :
858 0 : CHIP_ERROR BLEEndPoint::DriveStandAloneAck()
859 : {
860 : // Stop send-ack timer if running.
861 0 : StopSendAckTimer();
862 :
863 : // If stand-alone ack not already pending, allocate new payload buffer here.
864 0 : if (mAckToSend.IsNull())
865 : {
866 0 : mAckToSend = System::PacketBufferHandle::New(kTransferProtocolStandaloneAckHeaderSize);
867 0 : VerifyOrReturnError(!mAckToSend.IsNull(), CHIP_ERROR_NO_MEMORY);
868 : }
869 :
870 : // Attempt to send stand-alone ack.
871 0 : return DriveSending();
872 : }
873 :
874 0 : CHIP_ERROR BLEEndPoint::DoSendStandAloneAck()
875 : {
876 : ChipLogDebugBleEndPoint(Ble, "entered DoSendStandAloneAck; sending stand-alone ack");
877 :
878 : // Encode and transmit stand-alone ack.
879 0 : mBtpEngine.EncodeStandAloneAck(mAckToSend);
880 0 : ReturnErrorOnFailure(SendCharacteristic(mAckToSend.Retain()));
881 :
882 : // Reset local receive window counter.
883 0 : mLocalReceiveWindowSize = mReceiveWindowMaxSize;
884 : ChipLogDebugBleEndPoint(Ble, "reset local rx window on stand-alone ack tx, size = %u", mLocalReceiveWindowSize);
885 :
886 0 : mConnStateFlags.Set(ConnectionStateFlag::kStandAloneAckInFlight);
887 :
888 : // Start ack received timer, if it's not already running.
889 0 : return StartAckReceivedTimer();
890 : }
891 :
892 1 : CHIP_ERROR BLEEndPoint::DriveSending()
893 : {
894 : ChipLogDebugBleEndPoint(Ble, "entered DriveSending");
895 :
896 : // 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
897 : // receiver's window is completely empty, OR another GATT operation is in flight, awaiting confirmation...
898 2 : if ((mRemoteReceiveWindowSize <= BTP_WINDOW_NO_ACK_SEND_THRESHOLD &&
899 0 : !mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning) && mAckToSend.IsNull()) ||
900 1 : (mRemoteReceiveWindowSize == 0) || (mConnStateFlags.Has(ConnectionStateFlag::kGattOperationInFlight)))
901 : {
902 : #ifdef CHIP_BLE_END_POINT_DEBUG_LOGGING_ENABLED
903 : if (mRemoteReceiveWindowSize <= BTP_WINDOW_NO_ACK_SEND_THRESHOLD &&
904 : !mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning) && mAckToSend.IsNull())
905 : {
906 : ChipLogDebugBleEndPoint(Ble, "NO SEND: receive window almost closed, and no ack to send");
907 : }
908 :
909 : if (mRemoteReceiveWindowSize == 0)
910 : {
911 : ChipLogDebugBleEndPoint(Ble, "NO SEND: remote receive window closed");
912 : }
913 :
914 : if (mConnStateFlags.Has(ConnectionStateFlag::kGattOperationInFlight))
915 : {
916 : ChipLogDebugBleEndPoint(Ble, "NO SEND: Gatt op in flight");
917 : }
918 : #endif
919 :
920 : // Can't send anything.
921 0 : return CHIP_NO_ERROR;
922 : }
923 :
924 : // Otherwise, let's see what we can send.
925 :
926 1 : if (!mAckToSend.IsNull()) // If immediate, stand-alone ack is pending, send it.
927 : {
928 0 : ReturnErrorOnFailure(DoSendStandAloneAck());
929 : }
930 1 : else if (mBtpEngine.TxState() == BtpEngine::kState_Idle) // Else send next message fragment, if any.
931 : {
932 : // Fragmenter's idle, let's see what's in the send queue...
933 1 : if (!mSendQueue.IsNull())
934 : {
935 : // Transmit first fragment of next whole message in send queue.
936 1 : ReturnErrorOnFailure(SendNextMessage());
937 : }
938 : else
939 : {
940 : // Nothing to send!
941 : }
942 : }
943 0 : else if (mBtpEngine.TxState() == BtpEngine::kState_InProgress)
944 : {
945 : // Send next fragment of message currently held by fragmenter.
946 0 : ReturnErrorOnFailure(ContinueMessageSend());
947 : }
948 0 : else if (mBtpEngine.TxState() == BtpEngine::kState_Complete)
949 : {
950 : // Clear fragmenter's pointer to sent message buffer and reset its Tx state.
951 : // Buffer will be freed at scope exit.
952 0 : PacketBufferHandle sentBuf = mBtpEngine.TakeTxPacket();
953 :
954 0 : if (!mSendQueue.IsNull())
955 : {
956 : // Transmit first fragment of next whole message in send queue.
957 0 : ReturnErrorOnFailure(SendNextMessage());
958 : }
959 0 : else if (mState == kState_Closing && !mBtpEngine.ExpectingAck()) // and mSendQueue is NULL, per above...
960 : {
961 : // If end point closing, got last ack, and got out-of-order confirmation for last send, finalize close.
962 0 : FinalizeClose(mState, kBleCloseFlag_SuppressCallback, CHIP_NO_ERROR);
963 : }
964 : else
965 : {
966 : // Nothing to send!
967 0 : mBle->mApplicationDelegate->CheckNonConcurrentBleClosing();
968 : }
969 0 : }
970 :
971 1 : return CHIP_NO_ERROR;
972 : }
973 :
974 12 : CHIP_ERROR BLEEndPoint::HandleCapabilitiesRequestReceived(PacketBufferHandle && data)
975 : {
976 : BleTransportCapabilitiesRequestMessage req;
977 : BleTransportCapabilitiesResponseMessage resp;
978 : uint16_t mtu;
979 :
980 12 : VerifyOrReturnError(!data.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
981 :
982 12 : mState = kState_Connecting;
983 :
984 : // Decode BTP capabilities request.
985 12 : ReturnErrorOnFailure(BleTransportCapabilitiesRequestMessage::Decode(data, req));
986 :
987 12 : PacketBufferHandle responseBuf = System::PacketBufferHandle::New(kCapabilitiesResponseLength);
988 12 : VerifyOrReturnError(!responseBuf.IsNull(), CHIP_ERROR_NO_MEMORY);
989 :
990 : // Determine BLE connection's negotiated ATT MTU, if possible.
991 12 : if (req.mMtu > 0) // If MTU was observed and provided by central...
992 : {
993 12 : mtu = req.mMtu; // Accept central's observation of the MTU.
994 : }
995 : else // Otherwise, retrieve it via the platform delegate...
996 : {
997 0 : mtu = mBle->mPlatformDelegate->GetMTU(mConnObj);
998 : }
999 :
1000 : // Select fragment size for connection based on ATT MTU.
1001 12 : if (mtu > 0) // If one or both device knows connection's MTU...
1002 : {
1003 12 : resp.mFragmentSize =
1004 12 : std::min(static_cast<uint16_t>(mtu - 3), BtpEngine::sMaxFragmentSize); // Reserve 3 bytes of MTU for ATT header.
1005 : }
1006 : else // Else, if neither device knows MTU...
1007 : {
1008 0 : ChipLogProgress(Ble, "cannot determine ATT MTU; selecting default fragment size = %u", BtpEngine::sDefaultFragmentSize);
1009 0 : resp.mFragmentSize = BtpEngine::sDefaultFragmentSize;
1010 : }
1011 :
1012 : // Select local and remote max receive window size based on local resources available for both incoming writes AND
1013 : // GATT confirmations.
1014 12 : mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize =
1015 12 : std::min(req.mWindowSize, static_cast<uint8_t>(BLE_MAX_RECEIVE_WINDOW_SIZE));
1016 12 : resp.mWindowSize = mReceiveWindowMaxSize;
1017 :
1018 12 : ChipLogProgress(Ble, "local and remote recv window sizes = %u", resp.mWindowSize);
1019 :
1020 : // Select BLE transport protocol version from those supported by central, or none if no supported version found.
1021 12 : resp.mSelectedProtocolVersion = BleLayer::GetHighestSupportedProtocolVersion(req);
1022 12 : ChipLogProgress(Ble, "selected BTP version %d", resp.mSelectedProtocolVersion);
1023 :
1024 12 : if (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_None)
1025 : {
1026 : // If BLE transport protocol versions incompatible, prepare to close connection after subscription has been
1027 : // received and capabilities response has been sent.
1028 0 : ChipLogError(Ble, "incompatible BTP versions; peripheral expected between %d and %d",
1029 : CHIP_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, CHIP_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
1030 0 : mState = kState_Aborting;
1031 : }
1032 : else
1033 : {
1034 : // Set Rx and Tx fragment sizes to the same value
1035 12 : mBtpEngine.SetRxFragmentSize(resp.mFragmentSize);
1036 12 : mBtpEngine.SetTxFragmentSize(resp.mFragmentSize);
1037 : }
1038 :
1039 12 : ChipLogProgress(Ble, "using BTP fragment sizes rx %d / tx %d.", mBtpEngine.GetRxFragmentSize(), mBtpEngine.GetTxFragmentSize());
1040 :
1041 12 : ReturnErrorOnFailure(resp.Encode(responseBuf));
1042 :
1043 : // Stash capabilities response payload and wait for subscription from central.
1044 12 : QueueTx(std::move(responseBuf), kType_Data);
1045 :
1046 : // Start receive timer. Canceled when end point freed or connection established.
1047 12 : return StartReceiveConnectionTimer();
1048 12 : }
1049 :
1050 0 : CHIP_ERROR BLEEndPoint::HandleCapabilitiesResponseReceived(PacketBufferHandle && data)
1051 : {
1052 : BleTransportCapabilitiesResponseMessage resp;
1053 :
1054 0 : VerifyOrReturnError(!data.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
1055 :
1056 : // Decode BTP capabilities response.
1057 0 : ReturnErrorOnFailure(BleTransportCapabilitiesResponseMessage::Decode(data, resp));
1058 :
1059 0 : VerifyOrReturnError(resp.mFragmentSize > 0, BLE_ERROR_INVALID_FRAGMENT_SIZE);
1060 :
1061 0 : ChipLogProgress(Ble, "peripheral chose BTP version %d; central expected between %d and %d", resp.mSelectedProtocolVersion,
1062 : CHIP_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, CHIP_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
1063 :
1064 0 : if ((resp.mSelectedProtocolVersion < CHIP_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) ||
1065 0 : (resp.mSelectedProtocolVersion > CHIP_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION))
1066 : {
1067 0 : return BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
1068 : }
1069 :
1070 : // Set fragment size as minimum of (reported ATT MTU, BTP characteristic size)
1071 0 : resp.mFragmentSize = std::min(resp.mFragmentSize, BtpEngine::sMaxFragmentSize);
1072 :
1073 0 : mBtpEngine.SetRxFragmentSize(resp.mFragmentSize);
1074 0 : mBtpEngine.SetTxFragmentSize(resp.mFragmentSize);
1075 :
1076 0 : ChipLogProgress(Ble, "using BTP fragment sizes rx %d / tx %d.", mBtpEngine.GetRxFragmentSize(), mBtpEngine.GetTxFragmentSize());
1077 :
1078 : // Select local and remote max receive window size based on local resources available for both incoming indications
1079 : // AND GATT confirmations.
1080 0 : mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize = resp.mWindowSize;
1081 :
1082 0 : ChipLogProgress(Ble, "local and remote recv window size = %u", resp.mWindowSize);
1083 :
1084 : // Shrink local receive window counter by 1, since connect handshake indication requires acknowledgement.
1085 0 : mLocalReceiveWindowSize = static_cast<SequenceNumber_t>(mLocalReceiveWindowSize - 1);
1086 : ChipLogDebugBleEndPoint(Ble, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
1087 :
1088 : // Send ack for connection handshake indication when timer expires. Sequence numbers always start at 0,
1089 : // and the reassembler's "last received seq num" is initialized to 0 and updated when new fragments are
1090 : // received from the peripheral, so we don't need to explicitly mark the ack num to send here.
1091 0 : ReturnErrorOnFailure(StartSendAckTimer());
1092 :
1093 : // We've sent a capabilities request write and received a compatible response, so the connect
1094 : // operation has completed successfully.
1095 0 : return HandleConnectComplete();
1096 : }
1097 :
1098 : // Returns number of open slots in remote receive window given the input values.
1099 0 : SequenceNumber_t BLEEndPoint::AdjustRemoteReceiveWindow(SequenceNumber_t lastReceivedAck, SequenceNumber_t maxRemoteWindowSize,
1100 : SequenceNumber_t newestUnackedSentSeqNum)
1101 : {
1102 : // Assumption: SequenceNumber_t is uint8_t.
1103 : // Assumption: Maximum possible sequence number value is UINT8_MAX.
1104 : // Assumption: Sequence numbers incremented past maximum value wrap to 0.
1105 : // Assumption: newest unacked sent sequence number never exceeds current (and by extension, new and un-wrapped)
1106 : // window boundary, so it never wraps relative to last received ack, if new window boundary would not
1107 : // also wrap.
1108 :
1109 : // Define new window boundary (inclusive) as uint16_t, so its value can temporarily exceed UINT8_MAX.
1110 0 : uint16_t newRemoteWindowBoundary = static_cast<uint16_t>(lastReceivedAck + maxRemoteWindowSize);
1111 :
1112 0 : if (newRemoteWindowBoundary > UINT8_MAX && newestUnackedSentSeqNum < lastReceivedAck)
1113 : {
1114 : // New window boundary WOULD wrap, and latest unacked seq num already HAS wrapped, so add offset to difference.
1115 0 : return static_cast<uint8_t>(newRemoteWindowBoundary - (newestUnackedSentSeqNum + UINT8_MAX));
1116 : }
1117 :
1118 : // Neither values would or have wrapped, OR new boundary WOULD wrap but latest unacked seq num does not, so no
1119 : // offset required.
1120 0 : return static_cast<uint8_t>(newRemoteWindowBoundary - newestUnackedSentSeqNum);
1121 : }
1122 :
1123 14 : CHIP_ERROR BLEEndPoint::Receive(PacketBufferHandle && data)
1124 : {
1125 : ChipLogDebugBleEndPoint(Ble, "+++++++++++++++++++++ entered receive");
1126 14 : CHIP_ERROR err = CHIP_NO_ERROR;
1127 14 : SequenceNumber_t receivedAck = 0;
1128 14 : uint8_t closeFlags = kBleCloseFlag_AbortTransmission;
1129 14 : bool didReceiveAck = false;
1130 :
1131 : { // This is a special handling on the first CHIPoBLE data packet, the CapabilitiesRequest.
1132 : // Suppress error logging if peer's send overlaps with our unsubscribe on final close.
1133 14 : if (IsUnsubscribePending())
1134 : {
1135 : ChipLogDebugBleEndPoint(Ble, "characteristic rx'd while unsubscribe in flight");
1136 0 : ExitNow();
1137 : }
1138 :
1139 : // If we're receiving the first inbound packet of a BLE transport connection handshake...
1140 14 : if (!mConnStateFlags.Has(ConnectionStateFlag::kCapabilitiesMsgReceived))
1141 : {
1142 12 : if (mRole == kBleRole_Central) // If we're a central receiving a capabilities response indication...
1143 : {
1144 : // Ensure end point's in the right state before continuing.
1145 0 : VerifyOrExit(mState == kState_Connecting, err = CHIP_ERROR_INCORRECT_STATE);
1146 0 : mConnStateFlags.Set(ConnectionStateFlag::kCapabilitiesMsgReceived);
1147 :
1148 0 : err = HandleCapabilitiesResponseReceived(std::move(data));
1149 0 : SuccessOrExit(err);
1150 : }
1151 : else // Or, a peripheral receiving a capabilities request write...
1152 : {
1153 : // Ensure end point's in the right state before continuing.
1154 12 : VerifyOrExit(mState == kState_Ready, err = CHIP_ERROR_INCORRECT_STATE);
1155 12 : mConnStateFlags.Set(ConnectionStateFlag::kCapabilitiesMsgReceived);
1156 :
1157 12 : err = HandleCapabilitiesRequestReceived(std::move(data));
1158 :
1159 12 : if (err != CHIP_NO_ERROR)
1160 : {
1161 : // If an error occurred decoding and handling the capabilities request, release the BLE connection.
1162 : // Central's connect attempt will time out if peripheral's application decides to keep the BLE
1163 : // connection open, or fail immediately if the application closes the connection.
1164 0 : closeFlags = closeFlags | kBleCloseFlag_SuppressCallback;
1165 0 : ExitNow();
1166 : }
1167 : }
1168 :
1169 : // If received data was handshake packet, don't feed it to message reassembler.
1170 12 : ExitNow();
1171 : }
1172 : } // End handling the CapabilitiesRequest
1173 :
1174 : ChipLogDebugBleEndPoint(Ble, "prepared to rx post-handshake btp packet");
1175 :
1176 : // We've received a post-handshake BTP packet.
1177 : // Ensure end point's in the right state before continuing.
1178 2 : if (!IsConnected(mState))
1179 : {
1180 0 : ChipLogError(Ble, "ep rx'd packet in bad state");
1181 0 : err = CHIP_ERROR_INCORRECT_STATE;
1182 :
1183 0 : ExitNow();
1184 : }
1185 :
1186 : ChipLogDebugBleEndPoint(Ble, "BTP about to rx characteristic, state before:");
1187 2 : mBtpEngine.LogStateDebug();
1188 :
1189 : // Pass received packet into BTP protocol engine.
1190 2 : err = mBtpEngine.HandleCharacteristicReceived(std::move(data), receivedAck, didReceiveAck);
1191 :
1192 : ChipLogDebugBleEndPoint(Ble, "BTP rx'd characteristic, state after:");
1193 2 : mBtpEngine.LogStateDebug();
1194 :
1195 2 : SuccessOrExit(err);
1196 :
1197 : // Protocol engine accepted the fragment, so shrink local receive window counter by 1.
1198 2 : mLocalReceiveWindowSize = static_cast<SequenceNumber_t>(mLocalReceiveWindowSize - 1);
1199 : ChipLogDebugBleEndPoint(Ble, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
1200 :
1201 : // Respond to received ack, if any.
1202 2 : if (didReceiveAck)
1203 : {
1204 : ChipLogDebugBleEndPoint(Ble, "got btp ack = %u", receivedAck);
1205 :
1206 : // If ack was rx'd for newest unacked sent fragment, stop ack received timer.
1207 0 : if (!mBtpEngine.ExpectingAck())
1208 : {
1209 : ChipLogDebugBleEndPoint(Ble, "got ack for last outstanding fragment");
1210 0 : StopAckReceivedTimer();
1211 :
1212 0 : if (mState == kState_Closing && mSendQueue.IsNull() && mBtpEngine.TxState() == BtpEngine::kState_Idle)
1213 : {
1214 : // If end point closing, got confirmation for last send, and waiting for last ack, finalize close.
1215 0 : FinalizeClose(mState, kBleCloseFlag_SuppressCallback, CHIP_NO_ERROR);
1216 0 : ExitNow();
1217 : }
1218 : }
1219 : else // Else there are still sent fragments for which acks are expected, so restart ack received timer.
1220 : {
1221 : ChipLogDebugBleEndPoint(Ble, "still expecting ack(s), restarting timer...");
1222 0 : err = RestartAckReceivedTimer();
1223 0 : SuccessOrExit(err);
1224 : }
1225 :
1226 : ChipLogDebugBleEndPoint(Ble, "about to adjust remote rx window; got ack num = %u, newest unacked sent seq num = %u, \
1227 : old window size = %u, max window size = %u",
1228 : receivedAck, mBtpEngine.GetNewestUnackedSentSequenceNumber(), mRemoteReceiveWindowSize,
1229 : mReceiveWindowMaxSize);
1230 :
1231 : // Open remote device's receive window according to sequence number it just acknowledged.
1232 0 : mRemoteReceiveWindowSize =
1233 0 : AdjustRemoteReceiveWindow(receivedAck, mReceiveWindowMaxSize, mBtpEngine.GetNewestUnackedSentSequenceNumber());
1234 :
1235 : ChipLogDebugBleEndPoint(Ble, "adjusted remote rx window, new size = %u", mRemoteReceiveWindowSize);
1236 :
1237 : // Restart message transmission if it was previously paused due to window exhaustion.
1238 0 : err = DriveSending();
1239 0 : SuccessOrExit(err);
1240 : }
1241 :
1242 : // The previous DriveSending() might have generated a piggyback acknowledgement if there was
1243 : // previously un-acked data. Otherwise, prepare to send acknowledgement for newly received fragment.
1244 : //
1245 : // If local receive window is below immediate ack threshold, AND there is no previous stand-alone ack in
1246 : // flight, AND there is no pending outbound message fragment on which the ack can and will be piggybacked,
1247 : // send immediate stand-alone ack to reopen window for sender.
1248 : //
1249 : // The "GATT operation in flight" check below covers "pending outbound message fragment" by extension, as when
1250 : // a message has been passed to the end point via Send(), its next outbound fragment must either be in flight
1251 : // itself, or awaiting the completion of another in-flight GATT operation.
1252 : //
1253 : // If any GATT operation is in flight that is NOT a stand-alone ack, the window size will be checked against
1254 : // this threshold again when the GATT operation is confirmed.
1255 2 : if (mBtpEngine.HasUnackedData())
1256 : {
1257 2 : if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD &&
1258 0 : !mConnStateFlags.Has(ConnectionStateFlag::kGattOperationInFlight))
1259 : {
1260 : ChipLogDebugBleEndPoint(Ble, "sending immediate ack");
1261 0 : err = DriveStandAloneAck();
1262 0 : SuccessOrExit(err);
1263 : }
1264 : else
1265 : {
1266 : ChipLogDebugBleEndPoint(Ble, "starting send-ack timer");
1267 :
1268 : // Send ack when timer expires.
1269 2 : err = StartSendAckTimer();
1270 2 : SuccessOrExit(err);
1271 : }
1272 : }
1273 :
1274 : // If we've reassembled a whole message...
1275 2 : if (mBtpEngine.RxState() == BtpEngine::kState_Complete)
1276 : {
1277 : // Take ownership of message buffer
1278 2 : System::PacketBufferHandle full_packet = mBtpEngine.TakeRxPacket();
1279 :
1280 : ChipLogDebugBleEndPoint(Ble, "reassembled whole msg, len = %u", static_cast<unsigned>(full_packet->DataLength()));
1281 :
1282 : // If we have a message received callback, and end point is not closing...
1283 2 : if (mBleTransport != nullptr && mState != kState_Closing)
1284 : {
1285 : // Pass received message up the stack.
1286 2 : mBleTransport->OnEndPointMessageReceived(this, std::move(full_packet));
1287 : }
1288 2 : }
1289 :
1290 0 : exit:
1291 14 : if (err != CHIP_NO_ERROR)
1292 : {
1293 0 : DoClose(closeFlags, err);
1294 : }
1295 :
1296 14 : return err;
1297 : }
1298 :
1299 0 : CHIP_ERROR BLEEndPoint::SendWrite(PacketBufferHandle && buf)
1300 : {
1301 0 : mConnStateFlags.Set(ConnectionStateFlag::kGattOperationInFlight);
1302 :
1303 0 : auto err = mBle->mPlatformDelegate->SendWriteRequest(mConnObj, &CHIP_BLE_SVC_ID, &CHIP_BLE_CHAR_1_UUID, std::move(buf));
1304 0 : VerifyOrReturnError(err == CHIP_NO_ERROR, err,
1305 : ChipLogError(Ble, "Send write request failed: %" CHIP_ERROR_FORMAT, err.Format()));
1306 :
1307 0 : return err;
1308 : }
1309 :
1310 7 : CHIP_ERROR BLEEndPoint::SendIndication(PacketBufferHandle && buf)
1311 : {
1312 7 : mConnStateFlags.Set(ConnectionStateFlag::kGattOperationInFlight);
1313 :
1314 7 : auto err = mBle->mPlatformDelegate->SendIndication(mConnObj, &CHIP_BLE_SVC_ID, &CHIP_BLE_CHAR_2_UUID, std::move(buf));
1315 7 : VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(Ble, "Send indication failed: %" CHIP_ERROR_FORMAT, err.Format()));
1316 :
1317 7 : return err;
1318 : }
1319 :
1320 0 : CHIP_ERROR BLEEndPoint::StartConnectTimer()
1321 : {
1322 : const CHIP_ERROR timerErr =
1323 0 : mBle->mSystemLayer->StartTimer(System::Clock::Milliseconds32(BTP_CONN_RSP_TIMEOUT_MS), HandleConnectTimeout, this);
1324 0 : ReturnErrorOnFailure(timerErr);
1325 0 : mTimerStateFlags.Set(TimerStateFlag::kConnectTimerRunning);
1326 :
1327 0 : return CHIP_NO_ERROR;
1328 : }
1329 :
1330 12 : CHIP_ERROR BLEEndPoint::StartReceiveConnectionTimer()
1331 : {
1332 12 : const CHIP_ERROR timerErr = mBle->mSystemLayer->StartTimer(System::Clock::Milliseconds32(BTP_CONN_RSP_TIMEOUT_MS),
1333 : HandleReceiveConnectionTimeout, this);
1334 12 : ReturnErrorOnFailure(timerErr);
1335 12 : mTimerStateFlags.Set(TimerStateFlag::kReceiveConnectionTimerRunning);
1336 :
1337 12 : return CHIP_NO_ERROR;
1338 : }
1339 :
1340 7 : CHIP_ERROR BLEEndPoint::StartAckReceivedTimer()
1341 : {
1342 7 : if (!mTimerStateFlags.Has(TimerStateFlag::kAckReceivedTimerRunning))
1343 : {
1344 : const CHIP_ERROR timerErr =
1345 6 : mBle->mSystemLayer->StartTimer(System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS), HandleAckReceivedTimeout, this);
1346 6 : ReturnErrorOnFailure(timerErr);
1347 :
1348 6 : mTimerStateFlags.Set(TimerStateFlag::kAckReceivedTimerRunning);
1349 : }
1350 :
1351 7 : return CHIP_NO_ERROR;
1352 : }
1353 :
1354 0 : CHIP_ERROR BLEEndPoint::RestartAckReceivedTimer()
1355 : {
1356 0 : VerifyOrReturnError(mTimerStateFlags.Has(TimerStateFlag::kAckReceivedTimerRunning), CHIP_ERROR_INCORRECT_STATE);
1357 :
1358 0 : StopAckReceivedTimer();
1359 :
1360 0 : return StartAckReceivedTimer();
1361 : }
1362 :
1363 2 : CHIP_ERROR BLEEndPoint::StartSendAckTimer()
1364 : {
1365 : ChipLogDebugBleEndPoint(Ble, "entered StartSendAckTimer");
1366 :
1367 2 : if (!mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning))
1368 : {
1369 : ChipLogDebugBleEndPoint(Ble, "starting new SendAckTimer");
1370 : const CHIP_ERROR timerErr =
1371 2 : mBle->mSystemLayer->StartTimer(System::Clock::Milliseconds32(BTP_ACK_SEND_TIMEOUT_MS), HandleSendAckTimeout, this);
1372 2 : ReturnErrorOnFailure(timerErr);
1373 :
1374 2 : mTimerStateFlags.Set(TimerStateFlag::kSendAckTimerRunning);
1375 : }
1376 :
1377 2 : return CHIP_NO_ERROR;
1378 : }
1379 :
1380 0 : CHIP_ERROR BLEEndPoint::StartUnsubscribeTimer()
1381 : {
1382 : const CHIP_ERROR timerErr =
1383 0 : mBle->mSystemLayer->StartTimer(System::Clock::Milliseconds32(BLE_UNSUBSCRIBE_TIMEOUT_MS), HandleUnsubscribeTimeout, this);
1384 0 : ReturnErrorOnFailure(timerErr);
1385 0 : mTimerStateFlags.Set(TimerStateFlag::kUnsubscribeTimerRunning);
1386 :
1387 0 : return CHIP_NO_ERROR;
1388 : }
1389 :
1390 12 : void BLEEndPoint::StopConnectTimer()
1391 : {
1392 : // Cancel any existing connect timer.
1393 12 : mBle->mSystemLayer->CancelTimer(HandleConnectTimeout, this);
1394 12 : mTimerStateFlags.Clear(TimerStateFlag::kConnectTimerRunning);
1395 12 : }
1396 :
1397 29 : void BLEEndPoint::StopReceiveConnectionTimer()
1398 : {
1399 : // Cancel any existing receive connection timer.
1400 29 : mBle->mSystemLayer->CancelTimer(HandleReceiveConnectionTimeout, this);
1401 29 : mTimerStateFlags.Clear(TimerStateFlag::kReceiveConnectionTimerRunning);
1402 29 : }
1403 :
1404 12 : void BLEEndPoint::StopAckReceivedTimer()
1405 : {
1406 : // Cancel any existing ack-received timer.
1407 12 : mBle->mSystemLayer->CancelTimer(HandleAckReceivedTimeout, this);
1408 12 : mTimerStateFlags.Clear(TimerStateFlag::kAckReceivedTimerRunning);
1409 12 : }
1410 :
1411 12 : void BLEEndPoint::StopSendAckTimer()
1412 : {
1413 : // Cancel any existing send-ack timer.
1414 12 : mBle->mSystemLayer->CancelTimer(HandleSendAckTimeout, this);
1415 12 : mTimerStateFlags.Clear(TimerStateFlag::kSendAckTimerRunning);
1416 12 : }
1417 :
1418 12 : void BLEEndPoint::StopUnsubscribeTimer()
1419 : {
1420 : // Cancel any existing unsubscribe timer.
1421 12 : mBle->mSystemLayer->CancelTimer(HandleUnsubscribeTimeout, this);
1422 12 : mTimerStateFlags.Clear(TimerStateFlag::kUnsubscribeTimerRunning);
1423 12 : }
1424 :
1425 0 : void BLEEndPoint::HandleConnectTimeout(chip::System::Layer * systemLayer, void * appState)
1426 : {
1427 0 : BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
1428 :
1429 : // Check for event-based timer race condition.
1430 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kConnectTimerRunning))
1431 : {
1432 0 : ChipLogError(Ble, "connect handshake timed out, closing ep %p", ep);
1433 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kConnectTimerRunning);
1434 0 : ep->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_CONNECT_TIMED_OUT);
1435 : }
1436 0 : }
1437 :
1438 0 : void BLEEndPoint::HandleReceiveConnectionTimeout(chip::System::Layer * systemLayer, void * appState)
1439 : {
1440 0 : BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
1441 :
1442 : // Check for event-based timer race condition.
1443 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kReceiveConnectionTimerRunning))
1444 : {
1445 0 : ChipLogError(Ble, "receive handshake timed out, closing ep %p", ep);
1446 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kReceiveConnectionTimerRunning);
1447 0 : ep->DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, BLE_ERROR_RECEIVE_TIMED_OUT);
1448 : }
1449 0 : }
1450 :
1451 0 : void BLEEndPoint::HandleAckReceivedTimeout(chip::System::Layer * systemLayer, void * appState)
1452 : {
1453 0 : BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
1454 :
1455 : // Check for event-based timer race condition.
1456 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kAckReceivedTimerRunning))
1457 : {
1458 0 : ChipLogError(Ble, "ack recv timeout, closing ep %p", ep);
1459 0 : ep->mBtpEngine.LogStateDebug();
1460 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kAckReceivedTimerRunning);
1461 0 : ep->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_FRAGMENT_ACK_TIMED_OUT);
1462 : }
1463 0 : }
1464 :
1465 0 : void BLEEndPoint::HandleSendAckTimeout(chip::System::Layer * systemLayer, void * appState)
1466 : {
1467 0 : BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
1468 :
1469 : // Check for event-based timer race condition.
1470 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning))
1471 : {
1472 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kSendAckTimerRunning);
1473 :
1474 : // If previous stand-alone ack isn't still in flight...
1475 0 : if (!ep->mConnStateFlags.Has(ConnectionStateFlag::kStandAloneAckInFlight))
1476 : {
1477 0 : CHIP_ERROR sendErr = ep->DriveStandAloneAck();
1478 :
1479 0 : if (sendErr != CHIP_NO_ERROR)
1480 : {
1481 0 : ep->DoClose(kBleCloseFlag_AbortTransmission, sendErr);
1482 : }
1483 : }
1484 : }
1485 0 : }
1486 :
1487 0 : void BLEEndPoint::HandleUnsubscribeTimeout(chip::System::Layer * systemLayer, void * appState)
1488 : {
1489 0 : BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
1490 :
1491 : // Check for event-based timer race condition.
1492 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kUnsubscribeTimerRunning))
1493 : {
1494 0 : ChipLogError(Ble, "unsubscribe timed out, ble ep %p", ep);
1495 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kUnsubscribeTimerRunning);
1496 0 : ep->HandleUnsubscribeComplete();
1497 : }
1498 0 : }
1499 :
1500 : } /* namespace Ble */
1501 : } /* namespace chip */
|