ef83b90a7133b7821efffff8bd5b0e2886437f3c
[platform/upstream/connectedhomeip.git] / src / ble / BtpEngine.cpp
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 module implements encode, decode, fragmentation and reassembly of
22  *      Bluetooth Transport Layer (BTP) packet types for transport of a
23  *      CHIP-over-Bluetooth Low Energy (CHIPoBLE) byte-stream over point-to-point
24  *      Bluetooth Low Energy (BLE) links.
25  *
26  */
27
28 #include <ble/BleConfig.h>
29
30 #if CONFIG_NETWORK_LAYER_BLE
31
32 #include <ble/BtpEngine.h>
33 #if CHIP_ENABLE_CHIPOBLE_TEST
34 #include <ble/BtpEngineTest.h>
35 #endif
36
37 #include <support/BufferReader.h>
38 #include <support/CodeUtils.h>
39 #include <support/logging/CHIPLogging.h>
40
41 // Define below to enable extremely verbose BLE-specific debug logging.
42 #undef CHIP_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
43
44 #ifdef CHIP_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
45 #define ChipLogDebugBtpEngine(MOD, MSG, ...) ChipLogError(MOD, MSG, ##__VA_ARGS__)
46 #else
47 #define ChipLogDebugBtpEngine(MOD, MSG, ...)
48 #endif
49
50 namespace chip {
51 namespace Ble {
52
53 static inline void IncSeqNum(SequenceNumber_t & a_seq_num)
54 {
55     a_seq_num = static_cast<SequenceNumber_t>(0xff & ((a_seq_num) + 1));
56 }
57
58 static inline bool DidReceiveData(uint8_t rx_flags)
59 {
60     return (GetFlag(rx_flags, BtpEngine::kHeaderFlag_StartMessage) || GetFlag(rx_flags, BtpEngine::kHeaderFlag_ContinueMessage) ||
61             GetFlag(rx_flags, BtpEngine::kHeaderFlag_EndMessage));
62 }
63
64 static void PrintBufDebug(const System::PacketBufferHandle & buf)
65 {
66 #ifdef CHIP_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
67     uint8_t * b = buf->Start();
68
69     for (int i = 0; i < buf->DataLength(); i++)
70     {
71         ChipLogError(Ble, "\t%02x", b[i]);
72     }
73 #endif
74 }
75
76 const uint16_t BtpEngine::sDefaultFragmentSize = 20;  // 23-byte minimum ATT_MTU - 3 bytes for ATT operation header
77 const uint16_t BtpEngine::sMaxFragmentSize     = 128; // Size of write and indication characteristics
78
79 BLE_ERROR BtpEngine::Init(void * an_app_state, bool expect_first_ack)
80 {
81     mAppState              = an_app_state;
82     mRxState               = kState_Idle;
83     mRxBuf                 = nullptr;
84     mRxNewestUnackedSeqNum = 0;
85     mRxOldestUnackedSeqNum = 0;
86     mRxFragmentSize        = sDefaultFragmentSize;
87     mTxState               = kState_Idle;
88     mTxBuf                 = nullptr;
89     mTxFragmentSize        = sDefaultFragmentSize;
90     mRxCharCount           = 0;
91     mRxPacketCount         = 0;
92     mTxCharCount           = 0;
93     mTxPacketCount         = 0;
94     mTxNewestUnackedSeqNum = 0;
95     mTxOldestUnackedSeqNum = 0;
96 #if CHIP_ENABLE_CHIPOBLE_TEST
97     mTxPacketType = kType_Data; // Default BtpEngine Data packet
98     mRxPacketType = kType_Data; // Default BtpEngine Data packet
99 #endif
100
101     if (expect_first_ack)
102     {
103         mTxNextSeqNum = 1;
104         mExpectingAck = true;
105         mRxNextSeqNum = 0;
106     }
107     else
108     {
109         mTxNextSeqNum = 0;
110         mExpectingAck = false;
111         mRxNextSeqNum = 1;
112     }
113
114     return BLE_NO_ERROR;
115 }
116
117 SequenceNumber_t BtpEngine::GetAndIncrementNextTxSeqNum()
118 {
119     SequenceNumber_t ret = mTxNextSeqNum;
120
121     // If not already expecting ack...
122     if (!mExpectingAck)
123     {
124         mExpectingAck          = true;
125         mTxOldestUnackedSeqNum = mTxNextSeqNum;
126     }
127
128     // Update newest unacknowledged sequence number.
129     mTxNewestUnackedSeqNum = mTxNextSeqNum;
130
131     // Increment mTxNextSeqNum.
132     IncSeqNum(mTxNextSeqNum);
133
134     return ret;
135 }
136
137 SequenceNumber_t BtpEngine::GetAndRecordRxAckSeqNum()
138 {
139     SequenceNumber_t ret = mRxNewestUnackedSeqNum;
140
141     mRxNewestUnackedSeqNum = mRxNextSeqNum;
142     mRxOldestUnackedSeqNum = mRxNextSeqNum;
143
144     return ret;
145 }
146
147 bool BtpEngine::HasUnackedData() const
148 {
149     return (mRxOldestUnackedSeqNum != mRxNextSeqNum);
150 }
151
152 bool BtpEngine::IsValidAck(SequenceNumber_t ack_num) const
153 {
154     ChipLogDebugBtpEngine(Ble, "entered IsValidAck, ack = %u, oldest = %u, newest = %u", ack_num, mTxOldestUnackedSeqNum,
155                           mTxNewestUnackedSeqNum);
156
157     // Return false if not awaiting any ack.
158     if (!mExpectingAck)
159     {
160         ChipLogDebugBtpEngine(Ble, "unexpected ack is invalid");
161         return false;
162     }
163
164     // Assumption: maximum valid sequence number equals maximum value of SequenceNumber_t.
165
166     if (mTxNewestUnackedSeqNum >= mTxOldestUnackedSeqNum) // If current unacked interval does NOT wrap...
167     {
168         return (ack_num <= mTxNewestUnackedSeqNum && ack_num >= mTxOldestUnackedSeqNum);
169     }
170     // Else, if current unacked interval DOES wrap...
171     return (ack_num <= mTxNewestUnackedSeqNum || ack_num >= mTxOldestUnackedSeqNum);
172 }
173
174 BLE_ERROR BtpEngine::HandleAckReceived(SequenceNumber_t ack_num)
175 {
176     BLE_ERROR err = BLE_NO_ERROR;
177
178     ChipLogDebugBtpEngine(Ble, "entered HandleAckReceived, ack_num = %u", ack_num);
179
180     // Ensure ack_num falls within range of ack values we're expecting.
181     VerifyOrExit(IsValidAck(ack_num), err = BLE_ERROR_INVALID_ACK);
182
183     if (mTxNewestUnackedSeqNum == ack_num) // If ack is for newest outstanding unacknowledged fragment...
184     {
185         mTxOldestUnackedSeqNum = ack_num;
186
187         // All oustanding fragments have been acknowledged.
188         mExpectingAck = false;
189     }
190     else // If ack is valid, but not for newest oustanding unacknowledged fragment...
191     {
192         // Update newest unacknowledged fragment to one past that which was just acknowledged.
193         mTxOldestUnackedSeqNum = ack_num;
194         IncSeqNum(mTxOldestUnackedSeqNum);
195     }
196
197 exit:
198     return err;
199 }
200
201 // Calling convention:
202 //   EncodeStandAloneAck may only be called if data arg is commited for immediate, synchronous subsequent transmission.
203 BLE_ERROR BtpEngine::EncodeStandAloneAck(const PacketBufferHandle & data)
204 {
205     BLE_ERROR err = BLE_NO_ERROR;
206     uint8_t * characteristic;
207
208     // Ensure enough headroom exists for the lower BLE layers.
209     VerifyOrExit(data->EnsureReservedSize(CHIP_CONFIG_BLE_PKT_RESERVED_SIZE), err = BLE_ERROR_NO_MEMORY);
210
211     // Ensure enough space for standalone ack payload.
212     VerifyOrExit(data->MaxDataLength() >= kTransferProtocolStandaloneAckHeaderSize, err = BLE_ERROR_NO_MEMORY);
213     characteristic = data->Start();
214
215     // Since there's no preexisting message payload, we can write BTP header without adjusting data start pointer.
216     characteristic[0] = kHeaderFlag_FragmentAck;
217
218     // Acknowledge most recently received sequence number.
219     characteristic[1] = GetAndRecordRxAckSeqNum();
220     ChipLogDebugBtpEngine(Ble, "===> encoded stand-alone ack = %u", characteristic[1]);
221
222     // Include sequence number for stand-alone ack itself.
223     characteristic[2] = GetAndIncrementNextTxSeqNum();
224
225     // Set ack payload data length.
226     data->SetDataLength(kTransferProtocolStandaloneAckHeaderSize);
227
228 exit:
229     return err;
230 }
231
232 // Calling convention:
233 //   BtpEngine does not retain ownership of reassembled messages, layer above needs to free when done.
234 //
235 //   BtpEngine does not reset itself on error. Upper layer should free outbound message and inbound reassembly buffers
236 //   if there is a problem.
237
238 // HandleCharacteristicReceived():
239 //
240 //   Non-NULL characteristic data arg is always either designated as or appended to the message reassembly buffer,
241 //   or freed if it holds a stand-alone ack. In all cases, caller must clear its reference to data arg when this
242 //   function returns.
243 //
244 //   Upper layer must immediately clean up and reinitialize protocol engine if returned err != BLE_NO_ERROR.
245 BLE_ERROR BtpEngine::HandleCharacteristicReceived(System::PacketBufferHandle data, SequenceNumber_t & receivedAck,
246                                                   bool & didReceiveAck)
247 {
248     BLE_ERROR err    = BLE_NO_ERROR;
249     uint8_t rx_flags = 0;
250     // BLE data uses little-endian byte order.
251     Encoding::LittleEndian::Reader reader(data->Start(), data->DataLength());
252
253     VerifyOrExit(!data.IsNull(), err = BLE_ERROR_BAD_ARGS);
254
255     mRxCharCount++;
256
257     // Get header flags, always in first byte.
258     VerifyOrExit(reader.Read8(&rx_flags).StatusCode() == CHIP_NO_ERROR, err = BLE_ERROR_MESSAGE_INCOMPLETE);
259 #if CHIP_ENABLE_CHIPOBLE_TEST
260     if (GetFlag(rx_flags, kHeaderFlag_CommandMessage))
261         SetRxPacketType(kType_Control);
262     else
263         SetRxPacketType(kType_Data);
264 #endif
265
266     didReceiveAck = GetFlag(rx_flags, kHeaderFlag_FragmentAck);
267
268     // Get ack number, if any.
269     if (didReceiveAck)
270     {
271         VerifyOrExit(reader.Read8(&receivedAck).StatusCode() == CHIP_NO_ERROR, err = BLE_ERROR_MESSAGE_INCOMPLETE);
272
273         err = HandleAckReceived(receivedAck);
274         SuccessOrExit(err);
275     }
276
277     // Get sequence number.
278     VerifyOrExit(reader.Read8(&mRxNewestUnackedSeqNum).StatusCode() == CHIP_NO_ERROR, err = BLE_ERROR_MESSAGE_INCOMPLETE);
279
280     // Verify that received sequence number is the next one we'd expect.
281     VerifyOrExit(mRxNewestUnackedSeqNum == mRxNextSeqNum, err = BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER);
282
283     // Increment next expected rx sequence number.
284     IncSeqNum(mRxNextSeqNum);
285
286     // If fragment was stand-alone ack, we're done here; no payload for message reassembler.
287     if (!DidReceiveData(rx_flags))
288     {
289         ExitNow();
290     }
291
292     // Truncate the incoming fragment length by the mRxFragmentSize as the negotiated
293     // mRxFragnentSize may be smaller than the characteristic size.  Make sure
294     // we're not truncating to a data length smaller than what we have already consumed.
295     VerifyOrExit(reader.OctetsRead() <= mRxFragmentSize, err = BLE_ERROR_REASSEMBLER_INCORRECT_STATE);
296     data->SetDataLength(chip::min(data->DataLength(), mRxFragmentSize));
297
298     // Now mark the bytes we consumed as consumed.
299     data->ConsumeHead(reader.OctetsRead());
300
301     ChipLogDebugBtpEngine(Ble, ">>> BTP reassembler received data:");
302     PrintBufDebug(data);
303
304     if (mRxState == kState_Idle)
305     {
306         // We need a new reader, because the state of our outer reader no longer
307         // matches the state of the packetbuffer, both in terms of start
308         // position and available length.
309         Encoding::LittleEndian::Reader startReader(data->Start(), data->DataLength());
310
311         // Verify StartMessage header flag set.
312         VerifyOrExit(rx_flags & kHeaderFlag_StartMessage, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
313
314         VerifyOrExit(startReader.Read16(&mRxLength).StatusCode() == CHIP_NO_ERROR, err = BLE_ERROR_MESSAGE_INCOMPLETE);
315
316         mRxState = kState_InProgress;
317
318         data->ConsumeHead(startReader.OctetsRead());
319
320         // Create a new buffer for use as the Rx re-assembly area.
321         mRxBuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
322
323         VerifyOrExit(!mRxBuf.IsNull(), err = BLE_ERROR_NO_MEMORY);
324
325         mRxBuf->AddToEnd(std::move(data));
326         mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
327     }
328     else if (mRxState == kState_InProgress)
329     {
330         // Verify StartMessage header flag NOT set, since we're in the middle of receiving a message.
331         VerifyOrExit((rx_flags & kHeaderFlag_StartMessage) == 0, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
332
333         // Verify ContinueMessage or EndMessage header flag set.
334         VerifyOrExit((rx_flags & kHeaderFlag_ContinueMessage) || (rx_flags & kHeaderFlag_EndMessage),
335                      err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
336
337         // Add received fragment to reassembled message buffer.
338         mRxBuf->AddToEnd(std::move(data));
339         mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
340
341         // For now, limit BtpEngine message size to max length of 1 pbuf, as we do for chip messages sent via IP.
342         // TODO add support for BtpEngine messages longer than 1 pbuf
343         VerifyOrExit(!mRxBuf->HasChainedBuffer(), err = BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG);
344     }
345     else
346     {
347         err = BLE_ERROR_REASSEMBLER_INCORRECT_STATE;
348         ExitNow();
349     }
350
351     if (rx_flags & kHeaderFlag_EndMessage)
352     {
353         // Trim remainder, if any, of the received packet buffer based on sender-specified length of reassembled message.
354         int padding = mRxBuf->DataLength() - mRxLength;
355
356         if (padding > 0)
357         {
358             mRxBuf->SetDataLength(mRxLength);
359         }
360
361         // Ensure all received fragments add up to sender-specified total message size.
362         VerifyOrExit(mRxBuf->DataLength() == mRxLength, err = BLE_ERROR_REASSEMBLER_MISSING_DATA);
363
364         // We've reassembled the entire message.
365         mRxState = kState_Complete;
366         mRxPacketCount++;
367     }
368
369 exit:
370     if (err != BLE_NO_ERROR)
371     {
372         mRxState = kState_Error;
373
374         // Dump protocol engine state, plus header flags and received data length.
375         ChipLogError(Ble, "HandleCharacteristicReceived failed, err = %d, rx_flags = %u", err, rx_flags);
376         if (didReceiveAck)
377         {
378             ChipLogError(Ble, "With rx'd ack = %u", receivedAck);
379         }
380         if (!mRxBuf.IsNull())
381         {
382             ChipLogError(Ble, "With rx buf data length = %u", mRxBuf->DataLength());
383         }
384         LogState();
385
386         if (!data.IsNull())
387         {
388             // Tack received data onto rx buffer, to be freed when end point resets protocol engine on close.
389             if (!mRxBuf.IsNull())
390             {
391                 mRxBuf->AddToEnd(std::move(data));
392             }
393             else
394             {
395                 mRxBuf = std::move(data);
396             }
397         }
398     }
399
400     return err;
401 }
402
403 PacketBufferHandle BtpEngine::TakeRxPacket()
404 {
405     if (mRxState == kState_Complete)
406     {
407         mRxState = kState_Idle;
408     }
409     return std::move(mRxBuf);
410 }
411
412 // Calling convention:
413 //   May only be called if data arg is commited for immediate, synchronous subsequent transmission.
414 //   Returns false on error. Caller must free data arg on error.
415 bool BtpEngine::HandleCharacteristicSend(System::PacketBufferHandle data, bool send_ack)
416 {
417     uint8_t * characteristic;
418     mTxCharCount++;
419
420     if (send_ack && !HasUnackedData())
421     {
422         ChipLogError(Ble, "HandleCharacteristicSend: send_ack true, but nothing to acknowledge.");
423         return false;
424     }
425
426     if (mTxState == kState_Idle)
427     {
428         if (data.IsNull())
429         {
430             return false;
431         }
432
433         mTxBuf    = std::move(data);
434         mTxState  = kState_InProgress;
435         mTxLength = mTxBuf->DataLength();
436
437         ChipLogDebugBtpEngine(Ble, ">>> CHIPoBle preparing to send whole message:");
438         PrintBufDebug(data);
439
440         // Determine fragment header size.
441         uint8_t header_size =
442             send_ack ? kTransferProtocolMaxHeaderSize : (kTransferProtocolMaxHeaderSize - kTransferProtocolAckSize);
443
444         // Ensure enough headroom exists for the BTP header, and any headroom needed by the lower BLE layers.
445         if (!mTxBuf->EnsureReservedSize(header_size + CHIP_CONFIG_BLE_PKT_RESERVED_SIZE))
446         {
447             // handle error
448             ChipLogError(Ble, "HandleCharacteristicSend: not enough headroom");
449             mTxState = kState_Error;
450             mTxBuf   = nullptr; // Avoid double-free after assignment above, as caller frees data on error.
451
452             return false;
453         }
454
455         // prepend header.
456         characteristic = mTxBuf->Start();
457         characteristic -= header_size;
458         mTxBuf->SetStart(characteristic);
459         uint8_t cursor = 1; // first position past header flags byte
460
461         characteristic[0] = kHeaderFlag_StartMessage;
462
463 #if CHIP_ENABLE_CHIPOBLE_TEST
464         if (TxPacketType() == kType_Control)
465             SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
466 #endif
467
468         if (send_ack)
469         {
470             SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
471             characteristic[cursor++] = GetAndRecordRxAckSeqNum();
472             ChipLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor - 1]);
473         }
474
475         characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
476         characteristic[cursor++] = static_cast<uint8_t>(mTxLength & 0xff);
477         characteristic[cursor++] = static_cast<uint8_t>(mTxLength >> 8);
478
479         if ((mTxLength + cursor) <= mTxFragmentSize)
480         {
481             mTxBuf->SetDataLength(static_cast<uint16_t>(mTxLength + cursor));
482             mTxLength = 0;
483             SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
484             mTxState = kState_Complete;
485             mTxPacketCount++;
486         }
487         else
488         {
489             mTxBuf->SetDataLength(mTxFragmentSize);
490             mTxLength = static_cast<uint16_t>((mTxLength + cursor) - mTxFragmentSize);
491         }
492
493         ChipLogDebugBtpEngine(Ble, ">>> CHIPoBle preparing to send first fragment:");
494         PrintBufDebug(data);
495     }
496     else if (mTxState == kState_InProgress)
497     {
498         if (!data.IsNull())
499         {
500             return false;
501         }
502
503         // advance past the previous fragment
504         characteristic = mTxBuf->Start();
505         characteristic += mTxFragmentSize;
506
507         // prepend header
508         characteristic -= send_ack ? kTransferProtocolMidFragmentMaxHeaderSize
509                                    : (kTransferProtocolMidFragmentMaxHeaderSize - kTransferProtocolAckSize);
510         mTxBuf->SetStart(characteristic);
511         uint8_t cursor = 1; // first position past header flags byte
512
513         characteristic[0] = kHeaderFlag_ContinueMessage;
514
515 #if CHIP_ENABLE_CHIPOBLE_TEST
516         if (TxPacketType() == kType_Control)
517             SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
518 #endif
519
520         if (send_ack)
521         {
522             SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
523             characteristic[cursor++] = GetAndRecordRxAckSeqNum();
524             ChipLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor - 1]);
525         }
526
527         characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
528
529         if ((mTxLength + cursor) <= mTxFragmentSize)
530         {
531             mTxBuf->SetDataLength(static_cast<uint16_t>(mTxLength + cursor));
532             mTxLength = 0;
533             SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
534             mTxState = kState_Complete;
535             mTxPacketCount++;
536         }
537         else
538         {
539             mTxBuf->SetDataLength(mTxFragmentSize);
540             mTxLength = static_cast<uint16_t>((mTxLength + cursor) - mTxFragmentSize);
541         }
542
543         ChipLogDebugBtpEngine(Ble, ">>> CHIPoBle preparing to send additional fragment:");
544         PrintBufDebug(mTxBuf);
545     }
546     else
547     {
548         // Invalid tx state.
549         return false;
550     }
551
552     return true;
553 }
554
555 PacketBufferHandle BtpEngine::TakeTxPacket()
556 {
557     if (mTxState == kState_Complete)
558     {
559         mTxState = kState_Idle;
560     }
561     return std::move(mTxBuf);
562 }
563
564 void BtpEngine::LogState() const
565 {
566     ChipLogError(Ble, "mAppState: %p", mAppState);
567
568     ChipLogError(Ble, "mRxFragmentSize: %d", mRxFragmentSize);
569     ChipLogError(Ble, "mRxState: %d", mRxState);
570     ChipLogError(Ble, "mRxBuf: %d", !mRxBuf.IsNull());
571     ChipLogError(Ble, "mRxNextSeqNum: %d", mRxNextSeqNum);
572     ChipLogError(Ble, "mRxNewestUnackedSeqNum: %d", mRxNewestUnackedSeqNum);
573     ChipLogError(Ble, "mRxOldestUnackedSeqNum: %d", mRxOldestUnackedSeqNum);
574     ChipLogError(Ble, "mRxCharCount: %d", mRxCharCount);
575     ChipLogError(Ble, "mRxPacketCount: %d", mRxPacketCount);
576
577     ChipLogError(Ble, "mTxFragmentSize: %d", mTxFragmentSize);
578     ChipLogError(Ble, "mTxState: %d", mTxState);
579     ChipLogError(Ble, "mTxBuf: %d", !mTxBuf.IsNull());
580     ChipLogError(Ble, "mTxNextSeqNum: %d", mTxNextSeqNum);
581     ChipLogError(Ble, "mTxNewestUnackedSeqNum: %d", mTxNewestUnackedSeqNum);
582     ChipLogError(Ble, "mTxOldestUnackedSeqNum: %d", mTxOldestUnackedSeqNum);
583     ChipLogError(Ble, "mTxCharCount: %d", mTxCharCount);
584     ChipLogError(Ble, "mTxPacketCount: %d", mTxPacketCount);
585 }
586
587 void BtpEngine::LogStateDebug() const
588 {
589 #ifdef CHIP_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
590     LogState();
591 #endif
592 }
593
594 } /* namespace Ble */
595 } /* namespace chip */
596
597 #endif /* CONFIG_NETWORK_LAYER_BLE */