Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2025 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * This file implements a WiFiPAF endpoint abstraction for CHIP over WiFiPAF (CHIPoPAF)
21 : * Public Action Frame Transport Protocol (PAFTP).
22 : *
23 : */
24 :
25 : #include "WiFiPAFEndPoint.h"
26 :
27 : #include <algorithm>
28 : #include <cstdint>
29 : #include <cstring>
30 : #include <utility>
31 :
32 : #include <lib/support/BitFlags.h>
33 : #include <lib/support/BufferReader.h>
34 : #include <lib/support/CodeUtils.h>
35 : #include <lib/support/logging/CHIPLogging.h>
36 : #include <system/SystemClock.h>
37 : #include <system/SystemLayer.h>
38 : #include <system/SystemPacketBuffer.h>
39 :
40 : #include "WiFiPAFConfig.h"
41 : #include "WiFiPAFError.h"
42 : #include "WiFiPAFLayer.h"
43 : #include "WiFiPAFTP.h"
44 :
45 : // Define below to enable extremely verbose, WiFiPAF end point-specific debug logging.
46 : #undef CHIP_WIFIPAF_END_POINT_DEBUG_LOGGING_ENABLED
47 : #define CHIP_WIFIPAF_END_POINT_DEBUG_LOGGING_LEVEL 0
48 :
49 : #ifdef CHIP_WIFIPAF_END_POINT_DEBUG_LOGGING_ENABLED
50 : #define ChipLogDebugWiFiPAFEndPoint_L0(MOD, MSG, ...) ChipLogDetail(MOD, MSG, ##__VA_ARGS__)
51 : #if (CHIP_WIFIPAF_END_POINT_DEBUG_LOGGING_LEVEL == 0)
52 : #define ChipLogDebugWiFiPAFEndPoint(MOD, MSG, ...)
53 : #else
54 : #define ChipLogDebugWiFiPAFEndPoint(MOD, MSG, ...) ChipLogDetail(MOD, MSG, ##__VA_ARGS__)
55 : #endif // CHIP_WIFIPAF_END_POINT_DEBUG_LOGGING_LEVEL
56 : #define ChipLogDebugBufferWiFiPAFEndPoint(MOD, BUF) \
57 : ChipLogByteSpan(MOD, ByteSpan((BUF)->Start(), ((BUF)->DataLength() < 8 ? (BUF)->DataLength() : 8u)))
58 : #else
59 : #define ChipLogDebugWiFiPAFEndPoint(MOD, MSG, ...)
60 : #define ChipLogDebugBufferWiFiPAFEndPoint(MOD, BUF)
61 : #endif
62 :
63 : /**
64 : * @def WIFIPAF_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD
65 : *
66 : * @brief
67 : * If an end point's receive window drops equal to or below this value, it will send an immediate acknowledgement
68 : * packet to re-open its window instead of waiting for the send-ack timer to expire.
69 : *
70 : */
71 : #define WIFIPAF_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD 1
72 :
73 : #define WIFIPAF_ACK_SEND_TIMEOUT_MS 2500
74 : #define WIFIPAF_WAIT_RES_TIMEOUT_MS 1000
75 : // Drop the connection if network resources remain unavailable for the period.
76 : #define WIFIPAF_MAX_RESOURCE_BLOCK_COUNT (PAFTP_CONN_IDLE_TIMEOUT_MS / WIFIPAF_WAIT_RES_TIMEOUT_MS)
77 :
78 : /**
79 : * @def WIFIPAF_WINDOW_NO_ACK_SEND_THRESHOLD
80 : *
81 : * @brief
82 : * Data fragments may only be sent without piggybacked acks if receiver's window size is above this threshold.
83 : *
84 : */
85 : #define WIFIPAF_WINDOW_NO_ACK_SEND_THRESHOLD 1
86 :
87 : namespace chip {
88 : namespace WiFiPAF {
89 :
90 1 : CHIP_ERROR WiFiPAFEndPoint::StartConnect()
91 : {
92 1 : CHIP_ERROR err = CHIP_NO_ERROR;
93 : PAFTransportCapabilitiesRequestMessage req;
94 1 : PacketBufferHandle buf;
95 1 : constexpr uint8_t numVersions =
96 : CHIP_PAF_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - CHIP_PAF_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION + 1;
97 : static_assert(numVersions <= NUM_PAFTP_SUPPORTED_PROTOCOL_VERSIONS, "Incompatibly protocol versions");
98 :
99 : // Ensure we're in the correct state.
100 1 : VerifyOrExit(mState == kState_Ready, err = CHIP_ERROR_INCORRECT_STATE);
101 1 : mState = kState_Connecting;
102 :
103 : // Build PAF transport protocol capabilities request.
104 1 : buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
105 1 : VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
106 :
107 : // Zero-initialize PAF transport capabilities request.
108 1 : memset(&req, 0, sizeof(req));
109 1 : req.mMtu = CHIP_PAF_DEFAULT_MTU;
110 1 : req.mWindowSize = PAF_MAX_RECEIVE_WINDOW_SIZE;
111 :
112 : // Populate request with highest supported protocol versions
113 2 : for (uint8_t i = 0; i < numVersions; i++)
114 : {
115 1 : req.SetSupportedProtocolVersion(i, static_cast<uint8_t>(CHIP_PAF_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - i));
116 : }
117 :
118 1 : err = req.Encode(buf);
119 1 : SuccessOrExit(err);
120 :
121 : // Start connect timer. Canceled when end point freed or connection established.
122 1 : err = StartConnectTimer();
123 1 : SuccessOrExit(err);
124 :
125 : // Send PAF transport capabilities request to peripheral.
126 : // Add reference to message fragment. CHIP retains partial ownership of message fragment's packet buffer,
127 : // since this is the same buffer as that of the whole message, just with a fragmenter-modified payload offset
128 : // and data length, by a Retain() on the handle when calling this function.
129 1 : err = SendWrite(buf.Retain());
130 1 : SuccessOrExit(err);
131 : // Free request buffer on write confirmation. Stash a reference to it in mSendQueue, which we don't use anyway
132 : // until the connection has been set up.
133 1 : QueueTx(std::move(buf), kType_Data);
134 :
135 1 : exit:
136 : // If we failed to initiate the connection, close the end point.
137 2 : if (err != CHIP_NO_ERROR)
138 : {
139 0 : StopConnectTimer();
140 0 : DoClose(kWiFiPAFCloseFlag_AbortTransmission, err);
141 : }
142 :
143 2 : return err;
144 1 : }
145 :
146 2 : CHIP_ERROR WiFiPAFEndPoint::HandleConnectComplete()
147 : {
148 2 : CHIP_ERROR err = CHIP_NO_ERROR;
149 :
150 2 : mState = kState_Connected;
151 : // Cancel the connect timer.
152 2 : StopConnectTimer();
153 :
154 : // We've successfully completed the PAF transport protocol handshake, so let the application know we're open for business.
155 2 : if (mWiFiPafLayer != nullptr)
156 : {
157 : // Indicate connect complete to next-higher layer.
158 2 : mWiFiPafLayer->OnEndPointConnectComplete(this, CHIP_NO_ERROR);
159 : }
160 : else
161 : {
162 : // If no connect complete callback has been set up, close the end point.
163 0 : err = WIFIPAF_ERROR_NO_CONNECT_COMPLETE_CALLBACK;
164 : }
165 2 : return err;
166 : }
167 :
168 14 : bool WiFiPAFEndPoint::IsConnected(uint8_t state) const
169 : {
170 14 : return (state == kState_Connected || state == kState_Closing);
171 : }
172 :
173 4 : void WiFiPAFEndPoint::DoClose(uint8_t flags, CHIP_ERROR err)
174 : {
175 4 : uint8_t oldState = mState;
176 :
177 : // If end point is not closed or closing, OR end point was closing gracefully, but tx abort has been specified...
178 4 : if ((mState != kState_Closed && mState != kState_Closing) ||
179 0 : (mState == kState_Closing && (flags & kWiFiPAFCloseFlag_AbortTransmission)))
180 : {
181 : // Cancel Connect and ReceiveConnect timers if they are running.
182 : // Check role first to avoid needless iteration over timer pool.
183 4 : if (mRole == kWiFiPafRole_Subscriber)
184 : {
185 2 : StopConnectTimer();
186 : }
187 :
188 : // Free the packets in re-order queue if ones exist
189 28 : for (uint8_t qidx = 0; qidx < PAFTP_REORDER_QUEUE_SIZE; qidx++)
190 : {
191 24 : if (!ReorderQueue[qidx].IsNull())
192 : {
193 2 : ReorderQueue[qidx] = nullptr;
194 : }
195 : }
196 4 : ItemsInReorderQueue = 0;
197 :
198 : // If transmit buffer is empty or a transmission abort was specified...
199 4 : if (mPafTP.TxState() == WiFiPAFTP::kState_Idle || (flags & kWiFiPAFCloseFlag_AbortTransmission))
200 : {
201 4 : FinalizeClose(oldState, flags, err);
202 : }
203 : else
204 : {
205 : // Wait for send queue and fragmenter's tx buffer to become empty, to ensure all pending messages have been
206 : // sent. Only free end point and tell platform it can throw away the underlying connection once all
207 : // pending messages have been sent and acknowledged by the remote CHIPoPAF stack, or once the remote stack
208 : // closes the CHIPoPAF connection.
209 : //
210 : // In so doing, WiFiPAFEndPoint attempts to emulate the level of reliability afforded by TCPEndPoint and TCP
211 : // sockets in general with a typical default SO_LINGER option. That said, there is no hard guarantee that
212 : // pending messages will be sent once (Do)Close() is called, so developers should use application-level
213 : // messages to confirm the receipt of all data sent prior to a Close() call.
214 0 : mState = kState_Closing;
215 :
216 0 : if ((flags & kWiFiPAFCloseFlag_SuppressCallback) == 0)
217 : {
218 0 : DoCloseCallback(oldState, flags, err);
219 : }
220 : }
221 : }
222 4 : }
223 :
224 4 : void WiFiPAFEndPoint::FinalizeClose(uint8_t oldState, uint8_t flags, CHIP_ERROR err)
225 : {
226 4 : mState = kState_Closed;
227 :
228 : // Ensure transmit queue is empty and set to NULL.
229 4 : mSendQueue = nullptr;
230 : // Clear the session information
231 4 : ChipLogProgress(WiFiPAF, "Shutdown PAF session (%u, %u)", mSessionInfo.id, mSessionInfo.role);
232 4 : TEMPORARY_RETURN_IGNORED mWiFiPafLayer->mWiFiPAFTransport->WiFiPAFCloseSession(mSessionInfo);
233 4 : memset(&mSessionInfo, 0, sizeof(mSessionInfo));
234 : // Fire application's close callback if we haven't already, and it's not suppressed.
235 4 : if (oldState != kState_Closing && (flags & kWiFiPAFCloseFlag_SuppressCallback) == 0)
236 : {
237 3 : DoCloseCallback(oldState, flags, err);
238 : }
239 :
240 : // If underlying WiFiPAF connection has closed, connection object is invalid, so just free the end point and return.
241 12 : if (err == WIFIPAF_ERROR_REMOTE_DEVICE_DISCONNECTED || err == WIFIPAF_ERROR_APP_CLOSED_CONNECTION)
242 : {
243 2 : Free();
244 : }
245 : else // Otherwise, try to signal close to remote device before end point releases WiFiPAF connection and frees itself.
246 : {
247 2 : if (mRole == kWiFiPafRole_Subscriber)
248 : {
249 : // Cancel send and receive-ack timers, if running.
250 1 : StopAckReceivedTimer();
251 1 : StopSendAckTimer();
252 1 : StopWaitResourceTimer();
253 1 : mConnStateFlags.Set(ConnectionStateFlag::kOperationInFlight);
254 : }
255 : else
256 : {
257 1 : Free();
258 : }
259 : }
260 4 : ClearAll();
261 4 : }
262 :
263 3 : void WiFiPAFEndPoint::DoCloseCallback(uint8_t state, uint8_t flags, CHIP_ERROR err)
264 : {
265 : // Callback fires once per end point lifetime.
266 3 : mOnPafSubscribeComplete = nullptr;
267 3 : mOnPafSubscribeError = nullptr;
268 3 : OnConnectionClosed = nullptr;
269 3 : }
270 :
271 3 : void WiFiPAFEndPoint::Free()
272 : {
273 : // Clear fragmentation and reassembly engine's Tx and Rx buffers. Counters will be reset by next engine init.
274 3 : FreePAFtpEngine();
275 :
276 : // Clear pending ack buffer, if any.
277 3 : mAckToSend = nullptr;
278 :
279 : // Cancel all timers.
280 3 : StopConnectTimer();
281 3 : StopAckReceivedTimer();
282 3 : StopSendAckTimer();
283 3 : StopWaitResourceTimer();
284 :
285 : // Clear callbacks.
286 3 : mOnPafSubscribeComplete = nullptr;
287 3 : mOnPafSubscribeError = nullptr;
288 3 : OnMessageReceived = nullptr;
289 3 : OnConnectionClosed = nullptr;
290 3 : }
291 :
292 3 : void WiFiPAFEndPoint::FreePAFtpEngine()
293 : {
294 : // Free transmit disassembly buffer
295 3 : mPafTP.ClearTxPacket();
296 :
297 : // Free receive reassembly buffer
298 3 : mPafTP.ClearRxPacket();
299 3 : }
300 :
301 5 : CHIP_ERROR WiFiPAFEndPoint::Init(WiFiPAFLayer * WiFiPafLayer, WiFiPAFSession & SessionInfo)
302 : {
303 : // Fail if already initialized.
304 5 : VerifyOrReturnError(mWiFiPafLayer == nullptr, CHIP_ERROR_INCORRECT_STATE);
305 :
306 : // Validate args.
307 5 : VerifyOrReturnError(WiFiPafLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
308 :
309 : // If end point plays subscriber role, expect ack as last step of PAFTP handshake.
310 : // If being publisher, subscriber's handshake indication 'ack's write sent by publisher to kick off the PAFTP handshake.
311 5 : bool expectInitialAck = (SessionInfo.role == kWiFiPafRole_Publisher);
312 :
313 5 : CHIP_ERROR err = mPafTP.Init(this, expectInitialAck);
314 10 : if (err != CHIP_NO_ERROR)
315 : {
316 0 : ChipLogError(WiFiPAF, "WiFiPAFTP init failed");
317 0 : return err;
318 : }
319 :
320 5 : mWiFiPafLayer = WiFiPafLayer;
321 :
322 : // WiFiPAF EndPoint data members:
323 5 : memcpy(&mSessionInfo, &SessionInfo, sizeof(mSessionInfo));
324 5 : mRole = SessionInfo.role;
325 5 : mTimerStateFlags.ClearAll();
326 5 : mLocalReceiveWindowSize = 0;
327 5 : mRemoteReceiveWindowSize = 0;
328 5 : mReceiveWindowMaxSize = 0;
329 5 : mSendQueue = nullptr;
330 5 : mAckToSend = nullptr;
331 :
332 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "initialized local rx window, size = %u", mLocalReceiveWindowSize);
333 :
334 : // End point is ready.
335 5 : mState = kState_Ready;
336 :
337 5 : return CHIP_NO_ERROR;
338 : }
339 :
340 9 : CHIP_ERROR WiFiPAFEndPoint::SendCharacteristic(PacketBufferHandle && buf)
341 : {
342 9 : CHIP_ERROR err = CHIP_NO_ERROR;
343 :
344 9 : SuccessOrExit(err = SendWrite(std::move(buf)));
345 : // Write succeeded, so shrink remote receive window counter by 1.
346 9 : mRemoteReceiveWindowSize = static_cast<SequenceNumber_t>(mRemoteReceiveWindowSize - 1);
347 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
348 9 : exit:
349 9 : return err;
350 : }
351 :
352 : /*
353 : * Routine to queue the Tx packet with a packet type
354 : * kType_Data(0) - data packet
355 : * kType_Control(1) - control packet
356 : */
357 9 : void WiFiPAFEndPoint::QueueTx(PacketBufferHandle && data, PacketType_t type)
358 : {
359 9 : if (mSendQueue.IsNull())
360 : {
361 7 : mSendQueue = std::move(data);
362 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "%s: Set data as new mSendQueue %p, type %d", __FUNCTION__, mSendQueue->Start(), type);
363 : }
364 : else
365 : {
366 2 : mSendQueue->AddToEnd(std::move(data));
367 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "%s: Append data to mSendQueue %p, type %d", __FUNCTION__, mSendQueue->Start(), type);
368 : }
369 9 : }
370 :
371 7 : CHIP_ERROR WiFiPAFEndPoint::Send(PacketBufferHandle && data)
372 : {
373 7 : CHIP_ERROR err = CHIP_NO_ERROR;
374 :
375 7 : VerifyOrExit(!data.IsNull(), err = CHIP_ERROR_INVALID_ARGUMENT);
376 7 : VerifyOrExit(IsConnected(mState), err = CHIP_ERROR_INCORRECT_STATE);
377 :
378 : // Ensure outgoing message fits in a single contiguous packet buffer, as currently required by the
379 : // message fragmentation and reassembly engine.
380 7 : if (data->HasChainedBuffer())
381 : {
382 1 : data->CompactHead();
383 :
384 1 : if (data->HasChainedBuffer())
385 : {
386 0 : err = CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG;
387 0 : ExitNow();
388 : }
389 : }
390 :
391 : // Add new message to send queue.
392 7 : QueueTx(std::move(data), kType_Data);
393 :
394 : // Send first fragment of new message, if we can.
395 7 : err = DriveSending();
396 7 : SuccessOrExit(err);
397 7 : exit:
398 14 : if (err != CHIP_NO_ERROR)
399 : {
400 0 : DoClose(kWiFiPAFCloseFlag_AbortTransmission, err);
401 : }
402 :
403 7 : return err;
404 : }
405 :
406 8 : bool WiFiPAFEndPoint::PrepareNextFragment(PacketBufferHandle && data, bool & sentAck)
407 : {
408 : // If we have a pending fragment acknowledgement to send, piggyback it on the fragment we're about to transmit.
409 8 : if (mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning))
410 : {
411 : // Reset local receive window counter.
412 2 : mLocalReceiveWindowSize = mReceiveWindowMaxSize;
413 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "reset local rx window on piggyback ack tx, size = %u", mLocalReceiveWindowSize);
414 :
415 : // Tell caller AND fragmenter we have an ack to piggyback.
416 2 : sentAck = true;
417 : }
418 : else
419 : {
420 : // No ack to piggyback.
421 6 : sentAck = false;
422 : }
423 :
424 8 : return mPafTP.HandleCharacteristicSend(std::move(data), sentAck);
425 : }
426 :
427 7 : CHIP_ERROR WiFiPAFEndPoint::SendNextMessage()
428 : {
429 : // Get the first queued packet to send
430 7 : PacketBufferHandle data = mSendQueue.PopHead();
431 :
432 : // Hand whole message payload to the fragmenter.
433 : bool sentAck;
434 7 : VerifyOrReturnError(PrepareNextFragment(std::move(data), sentAck), WIFIPAF_ERROR_CHIPPAF_PROTOCOL_ABORT);
435 :
436 7 : ReturnErrorOnFailure(SendCharacteristic(mPafTP.BorrowTxPacket()));
437 :
438 7 : if (sentAck)
439 : {
440 : // If sent piggybacked ack, stop send-ack timer.
441 2 : StopSendAckTimer();
442 : }
443 :
444 : // Start ack received timer, if it's not already running.
445 7 : return StartAckReceivedTimer();
446 7 : }
447 :
448 1 : CHIP_ERROR WiFiPAFEndPoint::ContinueMessageSend()
449 : {
450 : bool sentAck;
451 :
452 1 : if (!PrepareNextFragment(nullptr, sentAck))
453 : {
454 : // Log PAFTP error
455 0 : ChipLogError(WiFiPAF, "paftp fragmenter error on send!");
456 0 : mPafTP.LogState();
457 :
458 0 : return WIFIPAF_ERROR_CHIPPAF_PROTOCOL_ABORT;
459 : }
460 :
461 1 : ReturnErrorOnFailure(SendCharacteristic(mPafTP.BorrowTxPacket()));
462 :
463 1 : if (sentAck)
464 : {
465 : // If sent piggybacked ack, stop send-ack timer.
466 0 : StopSendAckTimer();
467 : }
468 :
469 : // Start ack received timer, if it's not already running.
470 1 : return StartAckReceivedTimer();
471 : }
472 :
473 2 : CHIP_ERROR WiFiPAFEndPoint::HandleHandshakeConfirmationReceived()
474 : {
475 : // Free capabilities request/response payload.
476 2 : mSendQueue.FreeHead();
477 :
478 2 : return CHIP_NO_ERROR;
479 : }
480 :
481 7 : CHIP_ERROR WiFiPAFEndPoint::HandleFragmentConfirmationReceived(bool result)
482 : {
483 7 : CHIP_ERROR err = CHIP_NO_ERROR;
484 : // Ensure we're in correct state to receive confirmation of non-handshake GATT send.
485 7 : VerifyOrExit(IsConnected(mState), err = CHIP_ERROR_INCORRECT_STATE);
486 :
487 7 : if (mConnStateFlags.Has(ConnectionStateFlag::kStandAloneAckInFlight))
488 : {
489 : // If confirmation was received for stand-alone ack, free its tx buffer.
490 0 : mAckToSend = nullptr;
491 0 : mConnStateFlags.Clear(ConnectionStateFlag::kStandAloneAckInFlight);
492 : }
493 :
494 7 : if (result != true)
495 : {
496 : // Something wrong in writing packets
497 0 : ChipLogError(WiFiPAF, "Failed to send PAF packet");
498 0 : err = CHIP_ERROR_SENDING_BLOCKED;
499 0 : StopAckReceivedTimer();
500 0 : SuccessOrExit(err);
501 : }
502 :
503 : // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
504 : // pending on which to piggyback an ack, send immediate stand-alone ack.
505 : //
506 : // This check covers the case where the local receive window has shrunk between transmission and confirmation of
507 : // the stand-alone ack, and also the case where a window size < the immediate ack threshold was detected in
508 : // Receive(), but the stand-alone ack was deferred due to a pending outbound message fragment.
509 7 : if (mLocalReceiveWindowSize <= WIFIPAF_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD && mSendQueue.IsNull() &&
510 0 : mPafTP.TxState() != WiFiPAFTP::kState_InProgress)
511 : {
512 0 : err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
513 0 : SuccessOrExit(err);
514 : }
515 : else
516 : {
517 7 : err = DriveSending();
518 7 : SuccessOrExit(err);
519 : }
520 :
521 7 : exit:
522 14 : if (err != CHIP_NO_ERROR)
523 : {
524 0 : DoClose(kWiFiPAFCloseFlag_AbortTransmission, err);
525 : }
526 :
527 7 : return err;
528 : }
529 :
530 9 : CHIP_ERROR WiFiPAFEndPoint::HandleSendConfirmationReceived(bool result)
531 : {
532 : // Mark outstanding operation as finished.
533 9 : mConnStateFlags.Clear(ConnectionStateFlag::kOperationInFlight);
534 :
535 : // If confirmation was for outbound portion of PAFTP connect handshake...
536 9 : if (!mConnStateFlags.Has(ConnectionStateFlag::kCapabilitiesConfReceived))
537 : {
538 2 : mConnStateFlags.Set(ConnectionStateFlag::kCapabilitiesConfReceived);
539 2 : return HandleHandshakeConfirmationReceived();
540 : }
541 :
542 7 : return HandleFragmentConfirmationReceived(result);
543 : }
544 :
545 1 : CHIP_ERROR WiFiPAFEndPoint::DriveStandAloneAck()
546 : {
547 : // Stop send-ack timer if running.
548 1 : StopSendAckTimer();
549 :
550 : // If stand-alone ack not already pending, allocate new payload buffer here.
551 1 : if (mAckToSend.IsNull())
552 : {
553 1 : mAckToSend = System::PacketBufferHandle::New(kTransferProtocolStandaloneAckHeaderSize);
554 1 : VerifyOrReturnError(!mAckToSend.IsNull(), CHIP_ERROR_NO_MEMORY);
555 : }
556 :
557 : // Attempt to send stand-alone ack.
558 1 : return DriveSending();
559 : }
560 :
561 1 : CHIP_ERROR WiFiPAFEndPoint::DoSendStandAloneAck()
562 : {
563 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "sending stand-alone ack");
564 :
565 : // Encode and transmit stand-alone ack.
566 1 : ReturnErrorOnFailure(mPafTP.EncodeStandAloneAck(mAckToSend));
567 1 : ReturnErrorOnFailure(SendCharacteristic(mAckToSend.Retain()));
568 :
569 : // Reset local receive window counter.
570 1 : mLocalReceiveWindowSize = mReceiveWindowMaxSize;
571 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "reset local rx window on stand-alone ack tx, size = %u", mLocalReceiveWindowSize);
572 :
573 1 : mConnStateFlags.Set(ConnectionStateFlag::kStandAloneAckInFlight);
574 :
575 : // Start ack received timer, if it's not already running.
576 1 : return StartAckReceivedTimer();
577 : }
578 :
579 19 : CHIP_ERROR WiFiPAFEndPoint::DriveSending()
580 : {
581 : // 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
582 : // receiver's window is completely empty, OR another operation is in flight, awaiting confirmation...
583 39 : if ((mRemoteReceiveWindowSize <= WIFIPAF_WINDOW_NO_ACK_SEND_THRESHOLD &&
584 1 : !mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning) && mAckToSend.IsNull()) ||
585 20 : (mRemoteReceiveWindowSize == 0) || (mConnStateFlags.Has(ConnectionStateFlag::kOperationInFlight)))
586 : {
587 4 : if (mRemoteReceiveWindowSize <= WIFIPAF_WINDOW_NO_ACK_SEND_THRESHOLD &&
588 3 : !mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning) && mAckToSend.IsNull())
589 : {
590 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "NO SEND: receive window almost closed, and no ack to send");
591 : }
592 :
593 3 : if (mRemoteReceiveWindowSize == 0)
594 : {
595 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "NO SEND: remote receive window closed");
596 : }
597 :
598 3 : if (mConnStateFlags.Has(ConnectionStateFlag::kOperationInFlight))
599 : {
600 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "NO SEND: Operation in flight");
601 : }
602 : // Can't send anything.
603 3 : return CHIP_NO_ERROR;
604 : }
605 :
606 16 : if (!mWiFiPafLayer->mWiFiPAFTransport->WiFiPAFResourceAvailable() && (!mAckToSend.IsNull() || !mSendQueue.IsNull()))
607 : {
608 : // Resource is currently unavailable, send packets later
609 1 : return StartWaitResourceTimer();
610 : }
611 15 : mResourceWaitCount = 0;
612 :
613 : // Otherwise, let's see what we can send.
614 15 : if ((!mAckToSend.IsNull()) && !mConnStateFlags.Has(ConnectionStateFlag::kStandAloneAckInFlight))
615 : {
616 : // If immediate, stand-alone ack is pending, send it.
617 0 : ChipLogProgress(WiFiPAF, "Send the pending stand-alone ack");
618 0 : ReturnErrorOnFailure(DoSendStandAloneAck());
619 : }
620 15 : else if (mPafTP.TxState() == WiFiPAFTP::kState_Idle) // Else send next message fragment, if any.
621 : {
622 : // Fragmenter's idle, let's see what's in the send queue...
623 9 : if (!mSendQueue.IsNull())
624 : {
625 : // Transmit first fragment of next whole message in send queue.
626 6 : ReturnErrorOnFailure(SendNextMessage());
627 : }
628 : else
629 : {
630 : // Nothing to send!
631 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "=> No pending packets, nothing to send!");
632 : }
633 : }
634 6 : else if (mPafTP.TxState() == WiFiPAFTP::kState_InProgress)
635 : {
636 : // Send next fragment of message currently held by fragmenter.
637 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "Send the next fragment");
638 1 : ReturnErrorOnFailure(ContinueMessageSend());
639 : }
640 5 : else if (mPafTP.TxState() == WiFiPAFTP::kState_Complete)
641 : {
642 : // Clear fragmenter's pointer to sent message buffer and reset its Tx state.
643 : // Buffer will be freed at scope exit.
644 5 : PacketBufferHandle sentBuf = mPafTP.TakeTxPacket();
645 :
646 5 : if (!mSendQueue.IsNull())
647 : {
648 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "Send the next pkt");
649 : // Transmit first fragment of next whole message in send queue.
650 1 : ReturnErrorOnFailure(SendNextMessage());
651 : }
652 4 : else if (mState == kState_Closing && !mPafTP.ExpectingAck()) // and mSendQueue is NULL, per above...
653 : {
654 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "Closing and no expect ack!");
655 : // If end point closing, got last ack, and got out-of-order confirmation for last send, finalize close.
656 0 : FinalizeClose(mState, kWiFiPAFCloseFlag_SuppressCallback, CHIP_NO_ERROR);
657 : }
658 : else
659 : {
660 : // Nothing to send!
661 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "No more packets to send");
662 : }
663 5 : }
664 : else
665 : {
666 0 : ChipLogError(WiFiPAF, "Unknown TxState: %u", mPafTP.TxState());
667 : }
668 15 : return CHIP_NO_ERROR;
669 : }
670 :
671 2 : CHIP_ERROR WiFiPAFEndPoint::HandleCapabilitiesRequestReceived(PacketBufferHandle && data)
672 : {
673 : PAFTransportCapabilitiesRequestMessage req;
674 : PAFTransportCapabilitiesResponseMessage resp;
675 : uint16_t mtu;
676 :
677 2 : VerifyOrReturnError(!data.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
678 :
679 2 : mState = kState_Connecting;
680 :
681 : // Decode PAFTP capabilities request.
682 2 : ReturnErrorOnFailure(PAFTransportCapabilitiesRequestMessage::Decode(data, req));
683 :
684 1 : PacketBufferHandle responseBuf = System::PacketBufferHandle::New(kCapabilitiesResponseLength);
685 1 : VerifyOrReturnError(!responseBuf.IsNull(), CHIP_ERROR_NO_MEMORY);
686 :
687 1 : if (req.mMtu > 0) // If MTU was observed and provided by central...
688 : {
689 1 : mtu = req.mMtu; // Accept central's observation of the MTU.
690 : }
691 : else
692 : {
693 0 : mtu = CHIP_PAF_DEFAULT_MTU;
694 : }
695 :
696 : // Select fragment size for connection based on MTU.
697 1 : VerifyOrReturnError(mtu >= WiFiPAFTP::sMinFragmentSize, WIFIPAF_ERROR_INVALID_FRAGMENT_SIZE);
698 1 : resp.mFragmentSize = std::min(static_cast<uint16_t>(mtu), WiFiPAFTP::sMaxFragmentSize);
699 :
700 : // Select local and remote max receive window size based on local resources available for both incoming writes
701 1 : auto windowSize = std::min(req.mWindowSize, static_cast<uint8_t>(PAF_MAX_RECEIVE_WINDOW_SIZE));
702 1 : if (windowSize < PAF_MIN_RECEIVE_WINDOW_SIZE)
703 : {
704 : // The Window size should be at least PAF_MIN_RECEIVE_WINDOW_SIZE to ensure PAF stability and avoid underflow problems.
705 0 : ChipLogError(WiFiPAF, "Small window size: %u, reject due to stability requirement", windowSize);
706 0 : mState = kState_Aborting;
707 0 : return CHIP_ERROR_INVALID_ARGUMENT;
708 : }
709 1 : mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize = windowSize;
710 1 : resp.mWindowSize = mReceiveWindowMaxSize;
711 1 : ChipLogProgress(WiFiPAF, "local and remote recv window sizes = %u", resp.mWindowSize);
712 :
713 : // Select PAF transport protocol version from those supported by central, or none if no supported version found.
714 1 : resp.mSelectedProtocolVersion = WiFiPAFLayer::GetHighestSupportedProtocolVersion(req);
715 1 : ChipLogProgress(WiFiPAF, "selected PAFTP version %d", resp.mSelectedProtocolVersion);
716 :
717 1 : if (resp.mSelectedProtocolVersion == kWiFiPAFTransportProtocolVersion_None)
718 : {
719 : // If WiFiPAF transport protocol versions incompatible, prepare to close connection after capabilities response
720 : // has been sent.
721 0 : ChipLogError(WiFiPAF, "incompatible PAFTP versions; peripheral expected between %d and %d",
722 : CHIP_PAF_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, CHIP_PAF_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
723 0 : mState = kState_Aborting;
724 : }
725 : else
726 : {
727 : // Set Rx and Tx fragment sizes to the same value
728 1 : mPafTP.SetRxFragmentSize(resp.mFragmentSize);
729 1 : mPafTP.SetTxFragmentSize(resp.mFragmentSize);
730 : }
731 :
732 1 : ChipLogProgress(WiFiPAF, "using PAFTP fragment sizes rx %d / tx %d.", mPafTP.GetRxFragmentSize(), mPafTP.GetTxFragmentSize());
733 1 : ReturnErrorOnFailure(resp.Encode(responseBuf));
734 :
735 : CHIP_ERROR err;
736 1 : err = SendWrite(responseBuf.Retain());
737 1 : SuccessOrExit(err);
738 :
739 : // Stash capabilities response payload
740 1 : QueueTx(std::move(responseBuf), kType_Data);
741 :
742 : // Response has been sent
743 1 : return HandleConnectComplete();
744 0 : exit:
745 0 : return err;
746 1 : }
747 :
748 1 : CHIP_ERROR WiFiPAFEndPoint::HandleCapabilitiesResponseReceived(PacketBufferHandle && data)
749 : {
750 : PAFTransportCapabilitiesResponseMessage resp;
751 :
752 1 : VerifyOrReturnError(!data.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
753 :
754 : // Decode PAFTP capabilities response.
755 1 : ReturnErrorOnFailure(PAFTransportCapabilitiesResponseMessage::Decode(data, resp));
756 :
757 1 : VerifyOrReturnError(resp.mFragmentSize >= WiFiPAFTP::sMinFragmentSize, WIFIPAF_ERROR_INVALID_FRAGMENT_SIZE);
758 :
759 1 : ChipLogProgress(WiFiPAF, "Publisher chose PAFTP version %d; subscriber expected between %d and %d",
760 : resp.mSelectedProtocolVersion, CHIP_PAF_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION,
761 : CHIP_PAF_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
762 :
763 1 : if ((resp.mSelectedProtocolVersion < CHIP_PAF_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) ||
764 1 : (resp.mSelectedProtocolVersion > CHIP_PAF_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION))
765 : {
766 0 : return WIFIPAF_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
767 : }
768 :
769 : // Set fragment size as minimum of (reported ATT MTU, BTP characteristic size)
770 1 : resp.mFragmentSize = std::min(resp.mFragmentSize, WiFiPAFTP::sMaxFragmentSize);
771 :
772 1 : mPafTP.SetRxFragmentSize(resp.mFragmentSize);
773 1 : mPafTP.SetTxFragmentSize(resp.mFragmentSize);
774 :
775 1 : ChipLogProgress(WiFiPAF, "using PAFTP fragment sizes rx %d / tx %d.", mPafTP.GetRxFragmentSize(), mPafTP.GetTxFragmentSize());
776 :
777 : // Select local and remote max receive window size based on local resources available for both incoming indications
778 1 : if (resp.mWindowSize < PAF_MIN_RECEIVE_WINDOW_SIZE)
779 : {
780 : // The Window size should be at least PAF_MIN_RECEIVE_WINDOW_SIZE to ensure PAF stability and avoid underflow problems.
781 0 : ChipLogError(WiFiPAF, "Small window size: %u, reject due to stability requirement", resp.mWindowSize);
782 0 : mState = kState_Aborting;
783 0 : return CHIP_ERROR_INVALID_ARGUMENT;
784 : }
785 1 : mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize =
786 1 : std::min(resp.mWindowSize, static_cast<uint8_t>(PAF_MAX_RECEIVE_WINDOW_SIZE));
787 1 : ChipLogProgress(WiFiPAF, "local and remote recv window size = %u", mReceiveWindowMaxSize);
788 :
789 : // Shrink local receive window counter by 1, since connect handshake indication requires acknowledgement.
790 1 : mLocalReceiveWindowSize = static_cast<SequenceNumber_t>(mLocalReceiveWindowSize - 1);
791 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
792 :
793 : // Send ack for connection handshake indication when timer expires. Sequence numbers always start at 0,
794 : // and the reassembler's "last received seq num" is initialized to 0 and updated when new fragments are
795 : // received from the peripheral, so we don't need to explicitly mark the ack num to send here.
796 1 : ReturnErrorOnFailure(StartSendAckTimer());
797 :
798 : // We've sent a capabilities request write and received a compatible response, so the connect
799 : // operation has completed successfully.
800 1 : return HandleConnectComplete();
801 : }
802 :
803 : // Returns number of open slots in remote receive window given the input values.
804 4 : SequenceNumber_t WiFiPAFEndPoint::AdjustRemoteReceiveWindow(SequenceNumber_t lastReceivedAck, SequenceNumber_t maxRemoteWindowSize,
805 : SequenceNumber_t newestUnackedSentSeqNum)
806 : {
807 : // Assumption: SequenceNumber_t is uint8_t.
808 : // Assumption: Maximum possible sequence number value is UINT8_MAX.
809 : // Assumption: Sequence numbers incremented past maximum value wrap to 0.
810 : // Assumption: newest unacked sent sequence number never exceeds current (and by extension, new and un-wrapped)
811 : // window boundary, so it never wraps relative to last received ack, if new window boundary would not
812 : // also wrap.
813 :
814 : // Define new window boundary (inclusive) as uint16_t, so its value can temporarily exceed UINT8_MAX.
815 4 : uint16_t newRemoteWindowBoundary = static_cast<uint16_t>(lastReceivedAck + maxRemoteWindowSize);
816 :
817 4 : if (newRemoteWindowBoundary > UINT8_MAX && newestUnackedSentSeqNum < lastReceivedAck)
818 : {
819 : // New window boundary WOULD wrap, and latest unacked seq num already HAS wrapped, so add offset to difference.
820 0 : return static_cast<uint8_t>(newRemoteWindowBoundary - (newestUnackedSentSeqNum + UINT8_MAX));
821 : }
822 :
823 : // Neither values would or have wrapped, OR new boundary WOULD wrap but latest unacked seq num does not, so no
824 : // offset required.
825 4 : return static_cast<uint8_t>(newRemoteWindowBoundary - newestUnackedSentSeqNum);
826 : }
827 :
828 8 : CHIP_ERROR WiFiPAFEndPoint::GetPktSn(Encoding::LittleEndian::Reader & reader, uint8_t * pHead, SequenceNumber_t & seqNum)
829 : {
830 8 : BitFlags<WiFiPAFTP::HeaderFlags> rx_flags;
831 8 : size_t SnOffset = 0;
832 : SequenceNumber_t * pSn;
833 8 : ReturnErrorOnFailure(reader.Read8(rx_flags.RawStorage()).StatusCode());
834 8 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kHankshake))
835 : {
836 : // Handkshake message => No ack/sn
837 2 : return CHIP_ERROR_INTERNAL;
838 : }
839 : // Always has header flag
840 6 : SnOffset += kTransferProtocolHeaderFlagsSize;
841 6 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kManagementOpcode)) // Has Mgmt_Op
842 : {
843 1 : SnOffset += kTransferProtocolMgmtOpSize;
844 : }
845 6 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kFragmentAck)) // Has ack
846 : {
847 6 : SnOffset += kTransferProtocolAckSize;
848 : }
849 6 : VerifyOrReturnError(SnOffset + sizeof(seqNum) <= reader.OctetsRead() + reader.Remaining(), CHIP_ERROR_MESSAGE_INCOMPLETE);
850 5 : pSn = pHead + SnOffset;
851 5 : seqNum = *pSn;
852 :
853 5 : return CHIP_NO_ERROR;
854 : }
855 :
856 18 : CHIP_ERROR WiFiPAFEndPoint::DebugPktAckSn(const PktDirect_t PktDirect, Encoding::LittleEndian::Reader & reader, uint8_t * pHead)
857 : {
858 : #ifdef CHIP_WIFIPAF_END_POINT_DEBUG_LOGGING_ENABLED
859 : BitFlags<WiFiPAFTP::HeaderFlags> rx_flags;
860 : CHIP_ERROR err;
861 : uint8_t * pAct = nullptr;
862 : char AckBuff[4];
863 : uint8_t * pSn;
864 : size_t SnOffset = 0;
865 :
866 : err = reader.Read8(rx_flags.RawStorage()).StatusCode();
867 : SuccessOrExit(err);
868 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kHankshake))
869 : {
870 : // Handkshake message => No ack/sn
871 : return CHIP_NO_ERROR;
872 : }
873 : // Always has header flag
874 : SnOffset += kTransferProtocolHeaderFlagsSize;
875 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kManagementOpcode)) // Has Mgmt_Op
876 : {
877 : SnOffset += kTransferProtocolMgmtOpSize;
878 : }
879 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kFragmentAck)) // Has ack
880 : {
881 : pAct = pHead + kTransferProtocolHeaderFlagsSize;
882 : SnOffset += kTransferProtocolAckSize;
883 : }
884 : VerifyOrExit(SnOffset + sizeof(*pSn) <= reader.OctetsRead() + reader.Remaining(), err = CHIP_ERROR_MESSAGE_INCOMPLETE);
885 : pSn = pHead + SnOffset;
886 : if (pAct == nullptr)
887 : {
888 : strcpy(AckBuff, " ");
889 : }
890 : else
891 : {
892 : snprintf(AckBuff, sizeof(AckBuff), "%02hhu", *pAct);
893 : }
894 : if (PktDirect == PktDirect_t::kTx)
895 : {
896 : ChipLogDebugWiFiPAFEndPoint_L0(WiFiPAF, "==>[tx] [Sn, Ack] = [ %02u, -- %s]", *pSn, AckBuff);
897 : }
898 : else if (PktDirect == PktDirect_t::kRx)
899 : {
900 : ChipLogDebugWiFiPAFEndPoint_L0(WiFiPAF, "<==[rx] [Ack, Sn] = [-- %s, %02u]", AckBuff, *pSn);
901 : }
902 : exit:
903 : return err;
904 : #else
905 18 : return CHIP_NO_ERROR;
906 : #endif
907 : }
908 :
909 8 : CHIP_ERROR WiFiPAFEndPoint::Receive(PacketBufferHandle && data)
910 : {
911 8 : SequenceNumber_t ExpRxNextSeqNum = mPafTP.GetRxNextSeqNum();
912 : SequenceNumber_t seqNum;
913 8 : Encoding::LittleEndian::Reader reader(data->Start(), data->DataLength());
914 8 : CHIP_ERROR err = CHIP_NO_ERROR;
915 :
916 8 : err = GetPktSn(reader, data->Start(), seqNum);
917 16 : if (err != CHIP_NO_ERROR)
918 : {
919 : // Failed to get SeqNum. => Pass down to PAFTP engine directly
920 3 : return RxPacketProcess(std::move(data));
921 : }
922 : /*
923 : If reorder-queue is not empty => Need to queue the packet whose SeqNum is the next one at
924 : offset 0 to fill the hole.
925 : */
926 5 : if ((ExpRxNextSeqNum == seqNum) && (ItemsInReorderQueue == 0))
927 2 : return RxPacketProcess(std::move(data));
928 :
929 3 : ChipLogError(WiFiPAF, "Reorder the packet: [%u, %u]", ExpRxNextSeqNum, seqNum);
930 : // Start reordering packets
931 3 : SequenceNumber_t offset = OffsetSeqNum(seqNum, ExpRxNextSeqNum);
932 3 : if (offset >= PAFTP_REORDER_QUEUE_SIZE)
933 : {
934 : // Offset is too big
935 : // => It may be the unexpected packet or duplicate packet => drop it
936 1 : ChipLogError(WiFiPAF, "Offset (%u) is too big => drop the packet", offset);
937 : ChipLogDebugBufferWiFiPAFEndPoint(WiFiPAF, data);
938 1 : return CHIP_NO_ERROR;
939 : }
940 :
941 : // Save the packet to the reorder-queue
942 2 : if (ReorderQueue[offset].IsNull())
943 : {
944 2 : ReorderQueue[offset] = std::move(data);
945 2 : ItemsInReorderQueue++;
946 : }
947 :
948 : // Consume the packets in the reorder queue if no hole exists
949 2 : if (ReorderQueue[0].IsNull())
950 : {
951 : // The hole still exists => Can't continue
952 1 : ChipLogError(WiFiPAF, "The hole still exists. Packets in reorder-queue: %u", ItemsInReorderQueue);
953 1 : return CHIP_NO_ERROR;
954 : }
955 : uint8_t qidx;
956 3 : for (qidx = 0; qidx < PAFTP_REORDER_QUEUE_SIZE; qidx++)
957 : {
958 : // The head slots should have been filled. => Do rx processing
959 3 : if (ReorderQueue[qidx].IsNull())
960 : {
961 : // Stop consuming packets until the hole or no packets
962 1 : break;
963 : }
964 : // Consume the saved packets
965 2 : ChipLogProgress(WiFiPAF, "Rx processing from the re-order queue [%u]", qidx);
966 2 : err = RxPacketProcess(std::move(ReorderQueue[qidx]));
967 2 : ItemsInReorderQueue--;
968 : }
969 : // Has reached the 1st hole in the queue => move the rest items forward
970 : // Note: It's to continue => No need to reinit "i"
971 5 : for (uint8_t newId = 0; qidx < PAFTP_REORDER_QUEUE_SIZE; qidx++, newId++)
972 : {
973 4 : if (!ReorderQueue[qidx].IsNull())
974 : {
975 0 : ReorderQueue[newId] = std::move(ReorderQueue[qidx]);
976 0 : ReorderQueue[qidx] = nullptr;
977 : }
978 : }
979 1 : return err;
980 : }
981 :
982 7 : CHIP_ERROR WiFiPAFEndPoint::RxPacketProcess(PacketBufferHandle && data)
983 : {
984 : ChipLogDebugBufferWiFiPAFEndPoint(WiFiPAF, data);
985 :
986 7 : CHIP_ERROR err = CHIP_NO_ERROR;
987 7 : SequenceNumber_t receivedAck = 0;
988 7 : uint8_t closeFlags = kWiFiPAFCloseFlag_AbortTransmission;
989 7 : bool didReceiveAck = false;
990 7 : BitFlags<WiFiPAFTP::HeaderFlags> rx_flags;
991 7 : Encoding::LittleEndian::Reader reader(data->Start(), data->DataLength());
992 7 : TEMPORARY_RETURN_IGNORED DebugPktAckSn(PktDirect_t::kRx, reader, data->Start());
993 :
994 : { // This is a special handling on the first CHIPoPAF data packet, the CapabilitiesRequest.
995 : // If we're receiving the first inbound packet of a PAF transport connection handshake...
996 7 : if (!mConnStateFlags.Has(ConnectionStateFlag::kCapabilitiesMsgReceived))
997 : {
998 3 : if (mRole == kWiFiPafRole_Subscriber) // If we're a central receiving a capabilities response indication...
999 : {
1000 : // Ensure end point's in the right state before continuing.
1001 1 : VerifyOrExit(mState == kState_Connecting, err = CHIP_ERROR_INCORRECT_STATE);
1002 1 : mConnStateFlags.Set(ConnectionStateFlag::kCapabilitiesMsgReceived);
1003 1 : err = HandleCapabilitiesResponseReceived(std::move(data));
1004 1 : SuccessOrExit(err);
1005 : }
1006 : else // Or, a peripheral receiving a capabilities request write...
1007 : {
1008 : // Ensure end point's in the right state before continuing.
1009 2 : VerifyOrExit(mState == kState_Ready, err = CHIP_ERROR_INCORRECT_STATE);
1010 2 : mConnStateFlags.Set(ConnectionStateFlag::kCapabilitiesMsgReceived);
1011 2 : err = HandleCapabilitiesRequestReceived(std::move(data));
1012 4 : if (err != CHIP_NO_ERROR)
1013 : {
1014 : // If an error occurred decoding and handling the capabilities request, release the BLE connection.
1015 : // Central's connect attempt will time out if peripheral's application decides to keep the BLE
1016 : // connection open, or fail immediately if the application closes the connection.
1017 1 : closeFlags = closeFlags | kWiFiPAFCloseFlag_SuppressCallback;
1018 1 : ExitNow();
1019 : }
1020 : }
1021 : // If received data was handshake packet, don't feed it to message reassembler.
1022 2 : ExitNow();
1023 : }
1024 : } // End handling the CapabilitiesRequest
1025 :
1026 4 : err = reader.Read8(rx_flags.RawStorage()).StatusCode();
1027 4 : SuccessOrExit(err);
1028 4 : if (rx_flags.Has(WiFiPAFTP::HeaderFlags::kHankshake))
1029 : {
1030 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "Unexpected handshake packet => drop");
1031 0 : ExitNow();
1032 : }
1033 :
1034 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "PAFTP about to rx characteristic, state before:");
1035 4 : mPafTP.LogStateDebug();
1036 :
1037 : // Pass received packet into PAFTP protocol engine.
1038 4 : err = mPafTP.HandleCharacteristicReceived(std::move(data), receivedAck, didReceiveAck);
1039 :
1040 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "PAFTP rx'd characteristic, state after:");
1041 4 : mPafTP.LogStateDebug();
1042 4 : SuccessOrExit(err);
1043 :
1044 : // Protocol engine accepted the fragment, so shrink local receive window counter by 1.
1045 4 : mLocalReceiveWindowSize = static_cast<SequenceNumber_t>(mLocalReceiveWindowSize - 1);
1046 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
1047 :
1048 : // Respond to received ack, if any.
1049 4 : if (didReceiveAck)
1050 : {
1051 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "got paftp ack = %u", receivedAck);
1052 :
1053 : // If ack was rx'd for newest unacked sent fragment, stop ack received timer.
1054 4 : if (!mPafTP.ExpectingAck())
1055 : {
1056 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "got ack for last outstanding fragment");
1057 1 : StopAckReceivedTimer();
1058 :
1059 1 : if (mState == kState_Closing && mSendQueue.IsNull() && mPafTP.TxState() == WiFiPAFTP::kState_Idle)
1060 : {
1061 : // If end point closing, got confirmation for last send, and waiting for last ack, finalize close.
1062 0 : FinalizeClose(mState, kWiFiPAFCloseFlag_SuppressCallback, CHIP_NO_ERROR);
1063 0 : ExitNow();
1064 : }
1065 : }
1066 : else // Else there are still sent fragments for which acks are expected, so restart ack received timer.
1067 : {
1068 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "still expecting ack(s), restarting timer...");
1069 3 : err = RestartAckReceivedTimer();
1070 3 : SuccessOrExit(err);
1071 : }
1072 :
1073 : ChipLogDebugWiFiPAFEndPoint(
1074 : WiFiPAF, "about to adjust remote rx window; got ack num = %u, newest unacked sent seq num = %u, \
1075 : old window size = %u, max window size = %u",
1076 : receivedAck, mPafTP.GetNewestUnackedSentSequenceNumber(), mRemoteReceiveWindowSize, mReceiveWindowMaxSize);
1077 :
1078 : // Open remote device's receive window according to sequence number it just acknowledged.
1079 4 : mRemoteReceiveWindowSize =
1080 4 : AdjustRemoteReceiveWindow(receivedAck, mReceiveWindowMaxSize, mPafTP.GetNewestUnackedSentSequenceNumber());
1081 :
1082 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "adjusted remote rx window, new size = %u", mRemoteReceiveWindowSize);
1083 :
1084 : // Restart message transmission if it was previously paused due to window exhaustion.
1085 4 : err = DriveSending();
1086 4 : SuccessOrExit(err);
1087 : }
1088 :
1089 : // The previous DriveSending() might have generated a piggyback acknowledgement if there was
1090 : // previously un-acked data. Otherwise, prepare to send acknowledgement for newly received fragment.
1091 : //
1092 : // If local receive window is below immediate ack threshold, AND there is no previous stand-alone ack in
1093 : // flight, AND there is no pending outbound message fragment on which the ack can and will be piggybacked,
1094 : // send immediate stand-alone ack to reopen window for sender.
1095 : //
1096 : // The "operation in flight" check below covers "pending outbound message fragment" by extension, as when
1097 : // a message has been passed to the end point via Send(), its next outbound fragment must either be in flight
1098 : // itself, or awaiting the completion of another in-flight operation.
1099 : //
1100 : // If any operation is in flight that is NOT a stand-alone ack, the window size will be checked against
1101 : // this threshold again when the operation is confirmed.
1102 4 : if (mPafTP.HasUnackedData())
1103 : {
1104 4 : if (mLocalReceiveWindowSize <= WIFIPAF_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD &&
1105 0 : !mConnStateFlags.Has(ConnectionStateFlag::kOperationInFlight))
1106 : {
1107 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "sending immediate ack");
1108 0 : err = DriveStandAloneAck();
1109 0 : SuccessOrExit(err);
1110 : }
1111 : else
1112 : {
1113 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "starting send-ack timer");
1114 : // Send ack when timer expires.
1115 4 : err = StartSendAckTimer();
1116 4 : SuccessOrExit(err);
1117 : }
1118 : }
1119 :
1120 : // If we've reassembled a whole message...
1121 4 : if (mPafTP.RxState() == WiFiPAFTP::kState_Complete)
1122 : {
1123 : // Take ownership of message buffer
1124 2 : System::PacketBufferHandle full_packet = mPafTP.TakeRxPacket();
1125 :
1126 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "reassembled whole msg, len = %u", static_cast<unsigned>(full_packet->DataLength()));
1127 :
1128 : // If we have a message received callback, and end point is not closing...
1129 2 : if (mWiFiPafLayer != nullptr && mState != kState_Closing)
1130 : {
1131 : // Pass received message up the stack.
1132 2 : err = mWiFiPafLayer->OnWiFiPAFMsgRxComplete(mSessionInfo, std::move(full_packet));
1133 : }
1134 2 : }
1135 :
1136 2 : exit:
1137 14 : if (err != CHIP_NO_ERROR)
1138 : {
1139 1 : DoClose(closeFlags, err);
1140 : }
1141 :
1142 7 : return err;
1143 : }
1144 :
1145 11 : CHIP_ERROR WiFiPAFEndPoint::SendWrite(PacketBufferHandle && buf)
1146 : {
1147 11 : mConnStateFlags.Set(ConnectionStateFlag::kOperationInFlight);
1148 :
1149 : ChipLogDebugBufferWiFiPAFEndPoint(WiFiPAF, buf);
1150 11 : Encoding::LittleEndian::Reader reader(buf->Start(), buf->DataLength());
1151 11 : TEMPORARY_RETURN_IGNORED DebugPktAckSn(PktDirect_t::kTx, reader, buf->Start());
1152 11 : return mWiFiPafLayer->mWiFiPAFTransport->WiFiPAFMessageSend(mSessionInfo, std::move(buf));
1153 : }
1154 :
1155 1 : CHIP_ERROR WiFiPAFEndPoint::StartConnectTimer()
1156 : {
1157 1 : const CHIP_ERROR timerErr = mWiFiPafLayer->mSystemLayer->StartTimer(System::Clock::Milliseconds32(PAFTP_CONN_RSP_TIMEOUT_MS),
1158 1 : HandleConnectTimeout, this);
1159 1 : ReturnErrorOnFailure(timerErr);
1160 1 : mTimerStateFlags.Set(TimerStateFlag::kConnectTimerRunning);
1161 :
1162 1 : return CHIP_NO_ERROR;
1163 : }
1164 :
1165 12 : CHIP_ERROR WiFiPAFEndPoint::StartAckReceivedTimer()
1166 : {
1167 12 : if (!mTimerStateFlags.Has(TimerStateFlag::kAckReceivedTimerRunning))
1168 : {
1169 6 : const CHIP_ERROR timerErr = mWiFiPafLayer->mSystemLayer->StartTimer(System::Clock::Milliseconds32(PAFTP_ACK_TIMEOUT_MS),
1170 6 : HandleAckReceivedTimeout, this);
1171 6 : ReturnErrorOnFailure(timerErr);
1172 :
1173 6 : mTimerStateFlags.Set(TimerStateFlag::kAckReceivedTimerRunning);
1174 : }
1175 :
1176 12 : return CHIP_NO_ERROR;
1177 : }
1178 :
1179 3 : CHIP_ERROR WiFiPAFEndPoint::RestartAckReceivedTimer()
1180 : {
1181 3 : VerifyOrReturnError(mTimerStateFlags.Has(TimerStateFlag::kAckReceivedTimerRunning), CHIP_ERROR_INCORRECT_STATE);
1182 :
1183 3 : StopAckReceivedTimer();
1184 :
1185 3 : return StartAckReceivedTimer();
1186 : }
1187 :
1188 5 : CHIP_ERROR WiFiPAFEndPoint::StartSendAckTimer()
1189 : {
1190 5 : if (!mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning))
1191 : {
1192 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "starting new SendAckTimer");
1193 3 : const CHIP_ERROR timerErr = mWiFiPafLayer->mSystemLayer->StartTimer(
1194 3 : System::Clock::Milliseconds32(WIFIPAF_ACK_SEND_TIMEOUT_MS), HandleSendAckTimeout, this);
1195 3 : ReturnErrorOnFailure(timerErr);
1196 :
1197 3 : mTimerStateFlags.Set(TimerStateFlag::kSendAckTimerRunning);
1198 : }
1199 :
1200 5 : return CHIP_NO_ERROR;
1201 : }
1202 :
1203 1 : CHIP_ERROR WiFiPAFEndPoint::StartWaitResourceTimer()
1204 : {
1205 1 : mResourceWaitCount++;
1206 1 : if (mResourceWaitCount >= WIFIPAF_MAX_RESOURCE_BLOCK_COUNT)
1207 : {
1208 0 : ChipLogError(WiFiPAF, "Network resource has been unavailable for a long time");
1209 0 : mResourceWaitCount = 0;
1210 0 : DoClose(kWiFiPAFCloseFlag_AbortTransmission, CHIP_ERROR_NOT_CONNECTED);
1211 0 : return CHIP_NO_ERROR;
1212 : }
1213 1 : if (!mTimerStateFlags.Has(TimerStateFlag::kWaitResTimerRunning))
1214 : {
1215 : ChipLogDebugWiFiPAFEndPoint(WiFiPAF, "starting new WaitResTimer");
1216 1 : const CHIP_ERROR timerErr = mWiFiPafLayer->mSystemLayer->StartTimer(
1217 1 : System::Clock::Milliseconds32(WIFIPAF_WAIT_RES_TIMEOUT_MS), HandleWaitResourceTimeout, this);
1218 1 : ReturnErrorOnFailure(timerErr);
1219 1 : mTimerStateFlags.Set(TimerStateFlag::kWaitResTimerRunning);
1220 : }
1221 1 : return CHIP_NO_ERROR;
1222 : }
1223 :
1224 7 : void WiFiPAFEndPoint::StopConnectTimer()
1225 : {
1226 : // Cancel any existing connect timer.
1227 7 : mWiFiPafLayer->mSystemLayer->CancelTimer(HandleConnectTimeout, this);
1228 7 : mTimerStateFlags.Clear(TimerStateFlag::kConnectTimerRunning);
1229 7 : }
1230 :
1231 8 : void WiFiPAFEndPoint::StopAckReceivedTimer()
1232 : {
1233 : // Cancel any existing ack-received timer.
1234 8 : mWiFiPafLayer->mSystemLayer->CancelTimer(HandleAckReceivedTimeout, this);
1235 8 : mTimerStateFlags.Clear(TimerStateFlag::kAckReceivedTimerRunning);
1236 8 : }
1237 :
1238 7 : void WiFiPAFEndPoint::StopSendAckTimer()
1239 : {
1240 : // Cancel any existing send-ack timer.
1241 7 : mWiFiPafLayer->mSystemLayer->CancelTimer(HandleSendAckTimeout, this);
1242 7 : mTimerStateFlags.Clear(TimerStateFlag::kSendAckTimerRunning);
1243 7 : }
1244 :
1245 4 : void WiFiPAFEndPoint::StopWaitResourceTimer()
1246 : {
1247 : // Cancel any existing wait-resource timer.
1248 4 : mWiFiPafLayer->mSystemLayer->CancelTimer(HandleWaitResourceTimeout, this);
1249 4 : mTimerStateFlags.Clear(TimerStateFlag::kWaitResTimerRunning);
1250 4 : }
1251 :
1252 0 : void WiFiPAFEndPoint::HandleConnectTimeout(chip::System::Layer * systemLayer, void * appState)
1253 : {
1254 0 : WiFiPAFEndPoint * ep = static_cast<WiFiPAFEndPoint *>(appState);
1255 :
1256 : // Check for event-based timer race condition.
1257 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kConnectTimerRunning))
1258 : {
1259 0 : ChipLogError(WiFiPAF, "connect handshake timed out, closing ep %p", ep);
1260 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kConnectTimerRunning);
1261 0 : ep->DoClose(kWiFiPAFCloseFlag_AbortTransmission, WIFIPAF_ERROR_CONNECT_TIMED_OUT);
1262 : }
1263 0 : }
1264 :
1265 0 : void WiFiPAFEndPoint::HandleAckReceivedTimeout(chip::System::Layer * systemLayer, void * appState)
1266 : {
1267 0 : WiFiPAFEndPoint * ep = static_cast<WiFiPAFEndPoint *>(appState);
1268 :
1269 : // Check for event-based timer race condition.
1270 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kAckReceivedTimerRunning))
1271 : {
1272 0 : ChipLogError(WiFiPAF, "ack recv timeout, closing ep %p", ep);
1273 0 : ep->mPafTP.LogStateDebug();
1274 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kAckReceivedTimerRunning);
1275 0 : ep->DoClose(kWiFiPAFCloseFlag_AbortTransmission, WIFIPAF_ERROR_FRAGMENT_ACK_TIMED_OUT);
1276 : }
1277 0 : }
1278 :
1279 0 : void WiFiPAFEndPoint::HandleSendAckTimeout(chip::System::Layer * systemLayer, void * appState)
1280 : {
1281 0 : WiFiPAFEndPoint * ep = static_cast<WiFiPAFEndPoint *>(appState);
1282 :
1283 : // Check for event-based timer race condition.
1284 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kSendAckTimerRunning))
1285 : {
1286 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kSendAckTimerRunning);
1287 :
1288 : // If previous stand-alone ack isn't still in flight...
1289 0 : if (!ep->mConnStateFlags.Has(ConnectionStateFlag::kStandAloneAckInFlight))
1290 : {
1291 0 : CHIP_ERROR sendErr = ep->DriveStandAloneAck();
1292 :
1293 0 : if (sendErr != CHIP_NO_ERROR)
1294 : {
1295 0 : ep->DoClose(kWiFiPAFCloseFlag_AbortTransmission, sendErr);
1296 : }
1297 : }
1298 : }
1299 0 : }
1300 :
1301 0 : void WiFiPAFEndPoint::HandleWaitResourceTimeout(chip::System::Layer * systemLayer, void * appState)
1302 : {
1303 0 : WiFiPAFEndPoint * ep = static_cast<WiFiPAFEndPoint *>(appState);
1304 :
1305 : // Check for event-based timer race condition.
1306 0 : if (ep->mTimerStateFlags.Has(TimerStateFlag::kWaitResTimerRunning))
1307 : {
1308 0 : ep->mTimerStateFlags.Clear(TimerStateFlag::kWaitResTimerRunning);
1309 0 : CHIP_ERROR sendErr = ep->DriveSending();
1310 0 : if (sendErr != CHIP_NO_ERROR)
1311 : {
1312 0 : ep->DoClose(kWiFiPAFCloseFlag_AbortTransmission, sendErr);
1313 : }
1314 : }
1315 0 : }
1316 :
1317 5 : void WiFiPAFEndPoint::ClearAll()
1318 : {
1319 : // Return the end point to the free state the pool relies on (GetFree()/Find() key off
1320 : // mWiFiPafLayer == nullptr). The endpoint owns ref-counted PacketBufferHandles (mSendQueue,
1321 : // mAckToSend, ReorderQueue and buffers inside mPafTP); release those explicitly, then reset
1322 : // the trivially-copyable state. Any owning member added here later must be released too.
1323 5 : mSendQueue = nullptr;
1324 5 : mAckToSend = nullptr;
1325 35 : for (auto & queued : ReorderQueue)
1326 : {
1327 30 : queued = nullptr;
1328 : }
1329 5 : ItemsInReorderQueue = 0;
1330 5 : mPafTP.ClearRxPacket();
1331 5 : mPafTP.ClearTxPacket();
1332 :
1333 5 : mWiFiPafLayer = nullptr;
1334 5 : mOnPafSubscribeComplete = nullptr;
1335 5 : mOnPafSubscribeError = nullptr;
1336 5 : mAppState = nullptr;
1337 5 : OnMessageReceived = nullptr;
1338 5 : OnConnectionClosed = nullptr;
1339 :
1340 5 : mState = kState_Ready;
1341 5 : mRole = kWiFiPafRole_Publisher;
1342 5 : mRxAck = 0;
1343 5 : mConnStateFlags.ClearAll();
1344 5 : mTimerStateFlags.ClearAll();
1345 5 : mLocalReceiveWindowSize = 0;
1346 5 : mRemoteReceiveWindowSize = 0;
1347 5 : mReceiveWindowMaxSize = 0;
1348 5 : mResourceWaitCount = 0;
1349 5 : mSessionInfo = WiFiPAFSession{};
1350 5 : }
1351 :
1352 : } /* namespace WiFiPAF */
1353 : } /* namespace chip */
|