xref: /aosp_15_r20/system/chre/chpp/transport.c (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "chpp/transport.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #include "chpp/app.h"
26 #include "chpp/clients.h"
27 #include "chpp/clients/discovery.h"
28 #include "chpp/crc.h"
29 #include "chpp/link.h"
30 #include "chpp/log.h"
31 #include "chpp/macros.h"
32 #include "chpp/memory.h"
33 #include "chpp/platform/platform_link.h"
34 #include "chpp/services.h"
35 #include "chpp/time.h"
36 
37 /************************************************
38  *  Prototypes
39  ***********************************************/
40 
41 static void chppSetRxState(struct ChppTransportState *context,
42                            enum ChppRxState newState);
43 static size_t chppConsumePreamble(struct ChppTransportState *context,
44                                   const uint8_t *buf, size_t len);
45 static size_t chppConsumeHeader(struct ChppTransportState *context,
46                                 const uint8_t *buf, size_t len);
47 static size_t chppConsumePayload(struct ChppTransportState *context,
48                                  const uint8_t *buf, size_t len);
49 static size_t chppConsumeFooter(struct ChppTransportState *context,
50                                 const uint8_t *buf, size_t len);
51 static void chppAbortRxPacket(struct ChppTransportState *context);
52 #ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK
53 static void chppProcessTransportLoopbackRequest(
54     struct ChppTransportState *context);
55 #endif
56 #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
57 static void chppProcessTransportLoopbackResponse(
58     struct ChppTransportState *context);
59 #endif
60 static void chppSetResetComplete(struct ChppTransportState *context);
61 static void chppProcessResetAck(struct ChppTransportState *context);
62 static void chppProcessRxPacket(struct ChppTransportState *context);
63 static void chppProcessRxPayload(struct ChppTransportState *context);
64 static void chppClearRxDatagram(struct ChppTransportState *context);
65 static bool chppRxChecksumIsOk(const struct ChppTransportState *context);
66 static enum ChppTransportErrorCode chppRxHeaderCheck(
67     const struct ChppTransportState *context);
68 static void chppRegisterRxAck(struct ChppTransportState *context);
69 
70 static void chppEnqueueTxPacket(struct ChppTransportState *context,
71                                 uint8_t packetCode);
72 static size_t chppAddPreamble(uint8_t *buf);
73 static struct ChppTransportHeader *chppAddHeader(
74     struct ChppTransportState *context);
75 static void chppAddPayload(struct ChppTransportState *context);
76 static void chppAddFooter(struct ChppTransportState *context);
77 // Can not be static (used in tests).
78 size_t chppDequeueTxDatagram(struct ChppTransportState *context);
79 static void chppClearTxDatagramQueue(struct ChppTransportState *context);
80 static void chppTransportDoWork(struct ChppTransportState *context);
81 static void chppAppendToPendingTxPacket(struct ChppTransportState *context,
82                                         const uint8_t *buf, size_t len);
83 static const char *chppGetPacketAttrStr(uint8_t packetCode);
84 static bool chppEnqueueTxDatagram(struct ChppTransportState *context,
85                                   uint8_t packetCode, void *buf, size_t len);
86 static enum ChppLinkErrorCode chppSendPendingPacket(
87     struct ChppTransportState *context);
88 
89 static void chppResetTransportContext(struct ChppTransportState *context);
90 static void chppReset(struct ChppTransportState *context,
91                       enum ChppTransportPacketAttributes resetType,
92                       enum ChppTransportErrorCode error);
93 struct ChppAppHeader *chppTransportGetRequestTimeoutResponse(
94     struct ChppTransportState *context, enum ChppEndpointType type);
95 static const char *chppGetRxStatusLabel(enum ChppRxState state);
96 static void chppWorkHandleTimeout(struct ChppTransportState *context);
97 
98 /************************************************
99  *  Private Functions
100  ***********************************************/
101 
102 /** Returns a string representation of the passed ChppRxState */
chppGetRxStatusLabel(enum ChppRxState state)103 static const char *chppGetRxStatusLabel(enum ChppRxState state) {
104   switch (state) {
105     case CHPP_STATE_PREAMBLE:
106       return "PREAMBLE (0)";
107     case CHPP_STATE_HEADER:
108       return "HEADER (1)";
109     case CHPP_STATE_PAYLOAD:
110       return "PAYLOAD (2)";
111     case CHPP_STATE_FOOTER:
112       return "FOOTER (3)";
113   }
114 
115   return "invalid";
116 }
117 
118 /**
119  * Called any time the Rx state needs to be changed. Ensures that the location
120  * counter among that state (rxStatus.locInState) is also reset at the same
121  * time.
122  *
123  * @param context State of the transport layer.
124  * @param newState Next Rx state.
125  */
chppSetRxState(struct ChppTransportState * context,enum ChppRxState newState)126 static void chppSetRxState(struct ChppTransportState *context,
127                            enum ChppRxState newState) {
128   CHPP_LOGD(
129       "Changing RX transport state from %s to %s"
130       " after %" PRIuSIZE " bytes",
131       chppGetRxStatusLabel(context->rxStatus.state),
132       chppGetRxStatusLabel(newState), context->rxStatus.locInState);
133   context->rxStatus.locInState = 0;
134   context->rxStatus.state = newState;
135 }
136 
137 /**
138  * Called by chppRxDataCb to find a preamble (i.e. packet start delimiter) in
139  * the incoming data stream.
140  * Moves the state to CHPP_STATE_HEADER as soon as it has seen a complete
141  * preamble.
142  * Any future backwards-incompatible versions of CHPP Transport will use a
143  * different preamble.
144  *
145  * @param context State of the transport layer.
146  * @param buf Input data.
147  * @param len Length of input data in bytes.
148  *
149  * @return Length of consumed data in bytes.
150  */
chppConsumePreamble(struct ChppTransportState * context,const uint8_t * buf,size_t len)151 static size_t chppConsumePreamble(struct ChppTransportState *context,
152                                   const uint8_t *buf, size_t len) {
153   size_t consumed = 0;
154 
155   // TODO: Optimize loop, maybe using memchr() / memcmp() / SIMD, especially if
156   // serial port calling chppRxDataCb does not implement zero filter
157   while (consumed < len &&
158          context->rxStatus.locInState < CHPP_PREAMBLE_LEN_BYTES) {
159     size_t offset = context->rxStatus.locInState;
160     if ((offset == 0 && buf[consumed] == CHPP_PREAMBLE_BYTE_FIRST) ||
161         (offset == 1 && buf[consumed] == CHPP_PREAMBLE_BYTE_SECOND)) {
162       // Correct byte of preamble observed
163       context->rxStatus.locInState++;
164 
165     } else if (buf[consumed] == CHPP_PREAMBLE_BYTE_FIRST) {
166       // Previous search failed but first byte of another preamble observed
167       context->rxStatus.locInState = 1;
168 
169     } else {
170       // Continue search for a valid preamble from the start
171       context->rxStatus.locInState = 0;
172     }
173 
174     consumed++;
175   }
176 
177   // Let's see why we exited the above loop
178   if (context->rxStatus.locInState == CHPP_PREAMBLE_LEN_BYTES) {
179     // Complete preamble observed, move on to next state
180     context->rxStatus.packetStartTimeNs = chppGetCurrentTimeNs();
181     chppSetRxState(context, CHPP_STATE_HEADER);
182   }
183 
184   return consumed;
185 }
186 
187 /**
188  * Called by chppRxDataCb to process the packet header from the incoming data
189  * stream.
190  * Moves the Rx state to CHPP_STATE_PAYLOAD afterwards.
191  *
192  * @param context State of the transport layer.
193  * @param buf Input data.
194  * @param len Length of input data in bytes.
195  *
196  * @return Length of consumed data in bytes.
197  */
chppConsumeHeader(struct ChppTransportState * context,const uint8_t * buf,size_t len)198 static size_t chppConsumeHeader(struct ChppTransportState *context,
199                                 const uint8_t *buf, size_t len) {
200   CHPP_ASSERT(context->rxStatus.locInState <
201               sizeof(struct ChppTransportHeader));
202   size_t bytesToCopy = MIN(
203       len, (sizeof(struct ChppTransportHeader) - context->rxStatus.locInState));
204   memcpy(((uint8_t *)&context->rxHeader) + context->rxStatus.locInState, buf,
205          bytesToCopy);
206   context->rxStatus.locInState += bytesToCopy;
207 
208   if (context->rxStatus.locInState == sizeof(struct ChppTransportHeader)) {
209     // Header fully copied. Move on
210 
211     enum ChppTransportErrorCode headerCheckResult = chppRxHeaderCheck(context);
212     if (headerCheckResult != CHPP_TRANSPORT_ERROR_NONE) {
213       // Header fails consistency check. NACK and return to preamble state
214       chppEnqueueTxPacket(
215           context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(CHPP_TRANSPORT_ATTR_NONE,
216                                                       headerCheckResult));
217       chppSetRxState(context, CHPP_STATE_PREAMBLE);
218 
219     } else if (context->rxHeader.length == 0) {
220       // Non-payload packet
221       chppSetRxState(context, CHPP_STATE_FOOTER);
222 
223     } else {
224       // Payload bearing packet
225       uint8_t *tempPayload;
226 
227       if (context->rxDatagram.length == 0) {
228         // Packet is a new datagram
229         tempPayload = chppMalloc(context->rxHeader.length);
230       } else {
231         // Packet is a continuation of a fragmented datagram
232         tempPayload =
233             chppRealloc(context->rxDatagram.payload,
234                         context->rxDatagram.length + context->rxHeader.length,
235                         context->rxDatagram.length);
236       }
237 
238       if (tempPayload == NULL) {
239         CHPP_LOG_OOM();
240         chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_OOM);
241         chppSetRxState(context, CHPP_STATE_PREAMBLE);
242       } else {
243         context->rxDatagram.payload = tempPayload;
244         context->rxDatagram.length += context->rxHeader.length;
245         chppSetRxState(context, CHPP_STATE_PAYLOAD);
246       }
247     }
248   }
249 
250   return bytesToCopy;
251 }
252 
253 /**
254  * Called by chppRxDataCb to copy the payload, the length of which is determined
255  * by the header, from the incoming data stream.
256  * Moves the Rx state to CHPP_STATE_FOOTER afterwards.
257  *
258  * @param context State of the transport layer.
259  * @param buf Input data
260  * @param len Length of input data in bytes
261  *
262  * @return Length of consumed data in bytes
263  */
chppConsumePayload(struct ChppTransportState * context,const uint8_t * buf,size_t len)264 static size_t chppConsumePayload(struct ChppTransportState *context,
265                                  const uint8_t *buf, size_t len) {
266   CHPP_ASSERT(context->rxStatus.locInState < context->rxHeader.length);
267   size_t bytesToCopy =
268       MIN(len, (context->rxHeader.length - context->rxStatus.locInState));
269   memcpy(context->rxDatagram.payload + context->rxStatus.locInDatagram, buf,
270          bytesToCopy);
271   context->rxStatus.locInDatagram += bytesToCopy;
272   context->rxStatus.locInState += bytesToCopy;
273 
274   if (context->rxStatus.locInState == context->rxHeader.length) {
275     // Entire packet payload copied. Move on
276     chppSetRxState(context, CHPP_STATE_FOOTER);
277   }
278 
279   return bytesToCopy;
280 }
281 
282 /**
283  * Called by chppRxDataCb to process the packet footer from the incoming data
284  * stream. Checks checksum, triggering the correct response (ACK / NACK).
285  * Moves the Rx state to CHPP_STATE_PREAMBLE afterwards.
286  *
287  * @param context State of the transport layer.
288  * @param buf Input data.
289  * @param len Length of input data in bytes.
290  *
291  * @return Length of consumed data in bytes.
292  */
chppConsumeFooter(struct ChppTransportState * context,const uint8_t * buf,size_t len)293 static size_t chppConsumeFooter(struct ChppTransportState *context,
294                                 const uint8_t *buf, size_t len) {
295   CHPP_ASSERT(context->rxStatus.locInState <
296               sizeof(struct ChppTransportFooter));
297   size_t bytesToCopy = MIN(
298       len, (sizeof(struct ChppTransportFooter) - context->rxStatus.locInState));
299   memcpy(((uint8_t *)&context->rxFooter) + context->rxStatus.locInState, buf,
300          bytesToCopy);
301 
302   context->rxStatus.locInState += bytesToCopy;
303   if (context->rxStatus.locInState == sizeof(struct ChppTransportFooter)) {
304     // Footer copied. Move on
305 
306     if (CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode) !=
307         CHPP_TRANSPORT_ERROR_NONE) {
308       CHPP_LOGE("RX packet len=%" PRIu16 " seq=%" PRIu8 " ackSeq=%" PRIu8
309                 " attr=0x%" PRIx8 " ERR=%" PRIu8 " flags=0x%" PRIx8,
310                 context->rxHeader.length, context->rxHeader.seq,
311                 context->rxHeader.ackSeq,
312                 (uint8_t)CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode),
313                 (uint8_t)CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode),
314                 context->rxHeader.flags);
315     } else {
316       CHPP_LOGD("RX packet len=%" PRIu16 " seq=%" PRIu8 " ackSeq=%" PRIu8
317                 " attr=0x%" PRIx8 " err=%" PRIu8 " flags=0x%" PRIx8,
318                 context->rxHeader.length, context->rxHeader.seq,
319                 context->rxHeader.ackSeq,
320                 (uint8_t)CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode),
321                 (uint8_t)CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode),
322                 context->rxHeader.flags);
323     }
324 
325     if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
326         CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST) {
327 #ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK
328       chppProcessTransportLoopbackRequest(context);
329 #endif
330 
331     } else if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
332                CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE) {
333 #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
334       chppProcessTransportLoopbackResponse(context);
335 #endif
336 
337     } else if (!chppRxChecksumIsOk(context)) {
338       CHPP_LOGE("Bad checksum seq=%" PRIu8 " len=%" PRIu16,
339                 context->rxHeader.seq, context->rxHeader.length);
340       chppAbortRxPacket(context);
341       chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_CHECKSUM);  // NACK
342 
343     } else if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
344                CHPP_TRANSPORT_ATTR_RESET) {
345       CHPP_LOGI("RX RESET packet seq=%" PRIu8 " err=%" PRIu8,
346                 context->rxHeader.seq,
347                 CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode));
348       chppMutexUnlock(&context->mutex);
349       chppReset(context, CHPP_TRANSPORT_ATTR_RESET_ACK,
350                 CHPP_TRANSPORT_ERROR_NONE);
351       chppMutexLock(&context->mutex);
352 
353     } else if (context->resetState == CHPP_RESET_STATE_PERMANENT_FAILURE) {
354       // Only a reset is accepted in this state
355       CHPP_LOGE("RX discarded in perm fail seq=%" PRIu8 " len=%" PRIu16,
356                 context->rxHeader.seq, context->rxHeader.length);
357       chppAbortRxPacket(context);
358 
359     } else if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
360                CHPP_TRANSPORT_ATTR_RESET_ACK) {
361       CHPP_LOGI("RX RESET-ACK packet seq=%" PRIu8, context->rxHeader.seq);
362       chppProcessResetAck(context);
363 
364     } else if (context->resetState == CHPP_RESET_STATE_RESETTING) {
365       CHPP_LOGE("RX discarded in reset seq=%" PRIu8 " len=%" PRIu16,
366                 context->rxHeader.seq, context->rxHeader.length);
367       chppAbortRxPacket(context);
368 
369     } else {
370       chppProcessRxPacket(context);
371     }
372 
373     // Done with this packet. Wait for next packet
374     chppSetRxState(context, CHPP_STATE_PREAMBLE);
375   }
376 
377   return bytesToCopy;
378 }
379 
380 /**
381  * Discards of an incomplete Rx packet during receive (e.g. due to a timeout or
382  * bad checksum).
383  *
384  * @param context State of the transport layer.
385  */
chppAbortRxPacket(struct ChppTransportState * context)386 static void chppAbortRxPacket(struct ChppTransportState *context) {
387   size_t undoLen = 0;
388   size_t undoLoc = 0;
389 
390   switch (context->rxStatus.state) {
391     case (CHPP_STATE_PREAMBLE):
392     case (CHPP_STATE_HEADER): {
393       break;
394     }
395 
396     case (CHPP_STATE_PAYLOAD): {
397       undoLen = context->rxHeader.length;
398       undoLoc = context->rxStatus.locInState;
399       break;
400     }
401 
402     case (CHPP_STATE_FOOTER): {
403       undoLen = context->rxHeader.length;
404       undoLoc = context->rxHeader.length;
405       break;
406     }
407 
408     default: {
409       CHPP_DEBUG_ASSERT(false);
410     }
411   }
412 
413   if (undoLen > 0) {
414     // Packet has a payload we need to discard of
415 
416     CHPP_ASSERT(context->rxDatagram.length >= undoLen);
417     CHPP_ASSERT(context->rxStatus.locInDatagram >= undoLoc);
418     context->rxDatagram.length -= undoLen;
419     context->rxStatus.locInDatagram -= undoLoc;
420 
421     if (context->rxDatagram.length == 0) {
422       // Discarding this packet == discarding entire datagram
423       CHPP_FREE_AND_NULLIFY(context->rxDatagram.payload);
424 
425     } else {
426       // Discarding this packet == discarding part of datagram
427       uint8_t *tempPayload =
428           chppRealloc(context->rxDatagram.payload, context->rxDatagram.length,
429                       context->rxDatagram.length + undoLen);
430 
431       if (tempPayload == NULL) {
432         CHPP_LOG_OOM();
433       } else {
434         context->rxDatagram.payload = tempPayload;
435       }
436     }
437   }
438 
439   chppSetRxState(context, CHPP_STATE_PREAMBLE);
440 }
441 
442 /**
443  * Processes a request that is determined to be for a transport-layer loopback.
444  *
445  * @param context State of the transport layer.
446  */
447 #ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK
chppProcessTransportLoopbackRequest(struct ChppTransportState * context)448 static void chppProcessTransportLoopbackRequest(
449     struct ChppTransportState *context) {
450   if (context->txStatus.linkBusy) {
451     CHPP_LOGE("Link busy; trans-loopback dropped");
452 
453   } else {
454     uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
455     context->txStatus.linkBusy = true;
456     context->linkBufferSize = 0;
457     context->linkBufferSize += chppAddPreamble(&linkTxBuffer[0]);
458 
459     struct ChppTransportHeader *txHeader =
460         (struct ChppTransportHeader *)&linkTxBuffer[context->linkBufferSize];
461     context->linkBufferSize += sizeof(*txHeader);
462 
463     *txHeader = context->rxHeader;
464     txHeader->packetCode = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
465         CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE, txHeader->packetCode);
466 
467     size_t payloadLen =
468         MIN(context->rxDatagram.length, chppTransportTxMtuSize(context));
469     chppAppendToPendingTxPacket(context, context->rxDatagram.payload,
470                                 payloadLen);
471     CHPP_FREE_AND_NULLIFY(context->rxDatagram.payload);
472     chppClearRxDatagram(context);
473 
474     chppAddFooter(context);
475 
476     CHPP_LOGD("Trans-looping back len=%" PRIu16 " RX len=%" PRIuSIZE,
477               txHeader->length, context->rxDatagram.length);
478     enum ChppLinkErrorCode error = chppSendPendingPacket(context);
479 
480     if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
481       chppLinkSendDoneCb(context, error);
482     }
483   }
484 }
485 #endif
486 
487 /**
488  * Processes a response that is determined to be for a transport-layer loopback.
489  *
490  * @param context State of the transport layer.
491  */
492 #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
chppProcessTransportLoopbackResponse(struct ChppTransportState * context)493 static void chppProcessTransportLoopbackResponse(
494     struct ChppTransportState *context) {
495   if (context->transportLoopbackData.length != context->rxDatagram.length) {
496     CHPP_LOGE("RX len=%" PRIuSIZE " != TX len=%" PRIuSIZE,
497               context->rxDatagram.length,
498               context->transportLoopbackData.length - CHPP_PREAMBLE_LEN_BYTES -
499                   sizeof(struct ChppTransportHeader) -
500                   sizeof(struct ChppTransportFooter));
501     context->loopbackResult = CHPP_APP_ERROR_INVALID_LENGTH;
502 
503   } else if (memcmp(context->rxDatagram.payload,
504                     context->transportLoopbackData.payload,
505                     context->rxDatagram.length) != 0) {
506     CHPP_LOGE("RX & TX data don't match: len=%" PRIuSIZE,
507               context->rxDatagram.length);
508     context->loopbackResult = CHPP_APP_ERROR_INVALID_ARG;
509 
510   } else {
511     context->loopbackResult = CHPP_APP_ERROR_NONE;
512 
513     CHPP_LOGD("RX successful transport-loopback (payload len=%" PRIuSIZE ")",
514               context->rxDatagram.length);
515   }
516 
517   context->transportLoopbackData.length = 0;
518   CHPP_FREE_AND_NULLIFY(context->transportLoopbackData.payload);
519   CHPP_FREE_AND_NULLIFY(context->rxDatagram.payload);
520   chppClearRxDatagram(context);
521 }
522 #endif
523 
524 /**
525  * Method to invoke when the reset sequence is completed.
526  *
527  * @param context State of the transport layer.
528  */
chppSetResetComplete(struct ChppTransportState * context)529 static void chppSetResetComplete(struct ChppTransportState *context) {
530   context->resetState = CHPP_RESET_STATE_NONE;
531   context->resetCount = 0;
532   chppConditionVariableSignal(&context->resetCondVar);
533 }
534 
535 /**
536  * An incoming reset-ack packet indicates that a reset is complete at the other
537  * end of the CHPP link.
538  *
539  * @param context State of the transport layer.
540  */
chppProcessResetAck(struct ChppTransportState * context)541 static void chppProcessResetAck(struct ChppTransportState *context) {
542   if (context->resetState == CHPP_RESET_STATE_NONE) {
543     CHPP_LOGW("Unexpected reset-ack seq=%" PRIu8 " code=0x%" PRIx8,
544               context->rxHeader.seq, context->rxHeader.packetCode);
545     // In a reset race condition with both endpoints sending resets and
546     // reset-acks, the sent resets and reset-acks will both have a sequence
547     // number of 0.
548     // By ignoring the received reset-ack, the next expected sequence number
549     // will remain at 1 (following a reset with a sequence number of 0).
550     // Therefore, no further correction is necessary (beyond ignoring the
551     // received reset-ack), as the next packet (e.g. discovery) will have a
552     // sequence number of 1.
553 
554     chppDatagramProcessDoneCb(context, context->rxDatagram.payload);
555     chppClearRxDatagram(context);
556 
557     return;
558   }
559 
560   chppSetResetComplete(context);
561   context->rxStatus.receivedPacketCode = context->rxHeader.packetCode;
562   context->rxStatus.expectedSeq = context->rxHeader.seq + 1;
563   chppRegisterRxAck(context);
564 
565   // TODO: Configure transport layer based on (optional?) received config
566 
567   chppDatagramProcessDoneCb(context, context->rxDatagram.payload);
568   chppClearRxDatagram(context);
569 
570 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
571   if (context->appContext->isDiscoveryComplete) {
572     chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
573   }
574 #else
575   chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
576 #endif
577 
578   // Inform the App Layer that a reset has completed
579   chppMutexUnlock(&context->mutex);
580   chppAppProcessReset(context->appContext);
581   chppMutexLock(&context->mutex);
582 }
583 
584 /**
585  * Process a received, checksum-validated packet.
586  *
587  * @param context State of the transport layer.
588  */
chppProcessRxPacket(struct ChppTransportState * context)589 static void chppProcessRxPacket(struct ChppTransportState *context) {
590   uint64_t now = chppGetCurrentTimeNs();
591   context->rxStatus.lastGoodPacketTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC);
592   context->rxStatus.receivedPacketCode = context->rxHeader.packetCode;
593   chppRegisterRxAck(context);
594 
595   enum ChppTransportErrorCode errorCode = CHPP_TRANSPORT_ERROR_NONE;
596   if (context->rxHeader.length > 0 &&
597       context->rxHeader.seq != context->rxStatus.expectedSeq) {
598     // Out of order payload
599     errorCode = CHPP_TRANSPORT_ERROR_ORDER;
600   }
601 
602   if (context->txDatagramQueue.pending > 0 ||
603       errorCode == CHPP_TRANSPORT_ERROR_ORDER) {
604     // There are packets to send out (could be new or retx)
605     // Note: For a future ACK window > 1, makes more sense to cap the NACKs
606     // to one instead of flooding with out of order NACK errors.
607 
608     // If the sender is retrying a packet we've already received successfully,
609     // send an ACK so it will continue normally
610     enum ChppTransportErrorCode errorCodeToSend = errorCode;
611     if (context->rxHeader.length > 0 &&
612         context->rxHeader.seq == context->rxStatus.expectedSeq - 1) {
613       errorCodeToSend = CHPP_TRANSPORT_ERROR_NONE;
614     }
615 
616     chppEnqueueTxPacket(
617         context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(CHPP_TRANSPORT_ATTR_NONE,
618                                                     errorCodeToSend));
619   }
620 
621   if (errorCode == CHPP_TRANSPORT_ERROR_ORDER) {
622     CHPP_LOGE("Out of order RX discarded seq=%" PRIu8 " expect=%" PRIu8
623               " len=%" PRIu16,
624               context->rxHeader.seq, context->rxStatus.expectedSeq,
625               context->rxHeader.length);
626     chppAbortRxPacket(context);
627 
628   } else if (context->rxHeader.length > 0) {
629     // Process payload and send ACK
630     chppProcessRxPayload(context);
631   } else if (!context->txStatus.hasPacketsToSend) {
632     // Nothing to send and nothing to receive, i.e. this is an ACK before an
633     // indefinite period of inactivity. Kick the work thread so it recalculates
634     // the notifier timeout.
635     chppNotifierSignal(&context->notifier,
636                        CHPP_TRANSPORT_SIGNAL_RECALC_TIMEOUT);
637   }
638 }
639 
640 /**
641  * Process the payload of a validated payload-bearing packet and send out the
642  * ACK.
643  *
644  * @param context State of the transport layer.
645  */
chppProcessRxPayload(struct ChppTransportState * context)646 static void chppProcessRxPayload(struct ChppTransportState *context) {
647   context->rxStatus.expectedSeq++;  // chppProcessRxPacket() already confirms
648                                     // that context->rxStatus.expectedSeq ==
649                                     // context->rxHeader.seq, protecting against
650                                     // duplicate and out-of-order packets.
651 
652   if (context->rxHeader.flags & CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM) {
653     // Packet is part of a larger datagram
654     CHPP_LOGD("RX packet for unfinished datagram. Seq=%" PRIu8 " len=%" PRIu16
655               ". Datagram len=%" PRIuSIZE ". Sending ACK=%" PRIu8,
656               context->rxHeader.seq, context->rxHeader.length,
657               context->rxDatagram.length, context->rxStatus.expectedSeq);
658 
659   } else {
660     // End of this packet is end of a datagram
661 
662     // Send the payload to the App Layer
663     // Note that it is up to the app layer to free the buffer using
664     // chppDatagramProcessDoneCb() after is is done.
665     chppMutexUnlock(&context->mutex);
666     chppAppProcessRxDatagram(context->appContext, context->rxDatagram.payload,
667                              context->rxDatagram.length);
668     chppMutexLock(&context->mutex);
669 
670     CHPP_LOGD("App layer processed datagram with len=%" PRIuSIZE
671               ", ending packet seq=%" PRIu8 ", len=%" PRIu16
672               ". Sending ACK=%" PRIu8 " (previously sent=%" PRIu8 ")",
673               context->rxDatagram.length, context->rxHeader.seq,
674               context->rxHeader.length, context->rxStatus.expectedSeq,
675               context->txStatus.sentAckSeq);
676     chppClearRxDatagram(context);
677   }
678 
679   // Send ACK because we had RX a payload-bearing packet
680   chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
681 }
682 
683 /**
684  * Resets the incoming datagram state, i.e. after the datagram has been
685  * processed.
686  * Note that this is independent from freeing the payload. It is up to the app
687  * layer to inform the transport layer using chppDatagramProcessDoneCb() once it
688  * is done with the buffer so it is freed.
689  *
690  * @param context State of the transport layer.
691  */
chppClearRxDatagram(struct ChppTransportState * context)692 static void chppClearRxDatagram(struct ChppTransportState *context) {
693   context->rxStatus.locInDatagram = 0;
694   context->rxDatagram.length = 0;
695   context->rxDatagram.payload = NULL;
696 }
697 
698 /**
699  * Validates the checksum of an incoming packet.
700  *
701  * @param context State of the transport layer.
702  *
703  * @return True if and only if the checksum is correct.
704  */
chppRxChecksumIsOk(const struct ChppTransportState * context)705 static bool chppRxChecksumIsOk(const struct ChppTransportState *context) {
706   uint32_t crc = chppCrc32(0, (const uint8_t *)&context->rxHeader,
707                            sizeof(context->rxHeader));
708   crc = chppCrc32(
709       crc,
710       &context->rxDatagram
711            .payload[context->rxStatus.locInDatagram - context->rxHeader.length],
712       context->rxHeader.length);
713 
714   if (context->rxFooter.checksum != crc) {
715     CHPP_LOGE("RX BAD checksum: footer=0x%" PRIx32 ", calc=0x%" PRIx32
716               ", len=%" PRIuSIZE,
717               context->rxFooter.checksum, crc,
718               (size_t)(context->rxHeader.length +
719                        sizeof(struct ChppTransportHeader)));
720   }
721 
722   return (context->rxFooter.checksum == crc);
723 }
724 
725 /**
726  * Performs consistency checks on received packet header to determine if it is
727  * obviously corrupt / invalid / duplicate / out-of-order.
728  *
729  * @param context State of the transport layer.
730  *
731  * @return True if and only if header passes checks
732  */
chppRxHeaderCheck(const struct ChppTransportState * context)733 static enum ChppTransportErrorCode chppRxHeaderCheck(
734     const struct ChppTransportState *context) {
735   enum ChppTransportErrorCode result = CHPP_TRANSPORT_ERROR_NONE;
736 
737   if (context->rxHeader.length > chppTransportRxMtuSize(context)) {
738     result = CHPP_TRANSPORT_ERROR_HEADER;
739   }
740 
741   if (result != CHPP_TRANSPORT_ERROR_NONE) {
742     CHPP_LOGE("Bad header. seq=%" PRIu8 " expect=%" PRIu8 " len=%" PRIu16
743               " err=%" PRIu8,
744               context->rxHeader.seq, context->rxStatus.expectedSeq,
745               context->rxHeader.length, result);
746   }
747 
748   return result;
749 }
750 
751 /**
752  * Registers a received ACK. If an outgoing datagram is fully ACKed, it is
753  * popped from the TX queue.
754  *
755  * @param context State of the transport layer.
756  */
chppRegisterRxAck(struct ChppTransportState * context)757 static void chppRegisterRxAck(struct ChppTransportState *context) {
758   uint8_t rxAckSeq = context->rxHeader.ackSeq;
759 
760   if (context->rxStatus.receivedAckSeq != rxAckSeq) {
761     // A previously sent packet was actually ACKed
762     // Note: For a future ACK window >1, we should loop by # of ACKed packets
763     if ((uint8_t)(context->rxStatus.receivedAckSeq + 1) != rxAckSeq) {
764       CHPP_LOGE("Out of order ACK: last=%" PRIu8 " rx=%" PRIu8,
765                 context->rxStatus.receivedAckSeq, rxAckSeq);
766     } else {
767       CHPP_LOGD(
768           "ACK received (last registered=%" PRIu8 ", received=%" PRIu8
769           "). Prior queue depth=%" PRIu8 ", front datagram=%" PRIu8
770           " at loc=%" PRIuSIZE " of len=%" PRIuSIZE,
771           context->rxStatus.receivedAckSeq, rxAckSeq,
772           context->txDatagramQueue.pending, context->txDatagramQueue.front,
773           context->txStatus.ackedLocInDatagram,
774           context->txDatagramQueue.datagram[context->txDatagramQueue.front]
775               .length);
776 
777       context->rxStatus.receivedAckSeq = rxAckSeq;
778       if (context->txStatus.txAttempts > 1) {
779         CHPP_LOGW("Seq %" PRIu8 " ACK'd after %" PRIuSIZE " reTX",
780                   context->rxHeader.ackSeq - 1,
781                   context->txStatus.txAttempts - 1);
782       }
783       context->txStatus.txAttempts = 0;
784 
785       // Process and if necessary pop from Tx datagram queue
786       context->txStatus.ackedLocInDatagram += chppTransportTxMtuSize(context);
787       if (context->txStatus.ackedLocInDatagram >=
788           context->txDatagramQueue.datagram[context->txDatagramQueue.front]
789               .length) {
790         // We are done with datagram
791 
792         context->txStatus.ackedLocInDatagram = 0;
793         context->txStatus.sentLocInDatagram = 0;
794 
795         // Note: For a future ACK window >1, we need to update the queue
796         // position of the datagram being sent as well (relative to the
797         // front-of-queue). e.g. context->txStatus.datagramBeingSent--;
798 
799         if (chppDequeueTxDatagram(context) == 0) {
800           context->txStatus.hasPacketsToSend = false;
801         }
802       }
803     }
804   }  // else {nothing was ACKed}
805 }
806 
807 /**
808  * Enqueues an outgoing packet with the specified error code. The error code
809  * refers to the optional reason behind a NACK, if any. An error code of
810  * CHPP_TRANSPORT_ERROR_NONE indicates that no error was reported (i.e. either
811  * an ACK or an implicit NACK)
812  *
813  * Note that the decision as to whether to include a payload will be taken
814  * later, i.e. before the packet is being sent out from the queue. A payload is
815  * expected to be included if there is one or more pending Tx datagrams and we
816  * are not waiting on a pending ACK. A (repeat) payload is also included if we
817  * have received a NACK.
818  *
819  * Further note that even for systems with an ACK window greater than one, we
820  * would only need to send an ACK for the last (correct) packet, hence we only
821  * need a queue length of one here.
822  *
823  * @param context State of the transport layer.
824  * @param packetCode Error code and packet attributes to be sent.
825  */
chppEnqueueTxPacket(struct ChppTransportState * context,uint8_t packetCode)826 static void chppEnqueueTxPacket(struct ChppTransportState *context,
827                                 uint8_t packetCode) {
828   context->txStatus.hasPacketsToSend = true;
829   context->txStatus.packetCodeToSend = packetCode;
830 
831   CHPP_LOGD("chppEnqueueTxPacket called with packet code=0x%" PRIx8,
832             packetCode);
833 
834   // Notifies the main CHPP Transport Layer to run chppTransportDoWork().
835   chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EVENT);
836 }
837 
838 /**
839  * Adds a CHPP preamble to the beginning of buf.
840  *
841  * @param buf The CHPP preamble will be added to buf.
842  *
843  * @return Size of the added preamble.
844  */
chppAddPreamble(uint8_t * buf)845 static size_t chppAddPreamble(uint8_t *buf) {
846   buf[0] = CHPP_PREAMBLE_BYTE_FIRST;
847   buf[1] = CHPP_PREAMBLE_BYTE_SECOND;
848   return CHPP_PREAMBLE_LEN_BYTES;
849 }
850 
851 /**
852  * Adds the packet header to link tx buffer.
853  *
854  * @param context State of the transport layer.
855  *
856  * @return Pointer to the added packet header.
857  */
chppAddHeader(struct ChppTransportState * context)858 static struct ChppTransportHeader *chppAddHeader(
859     struct ChppTransportState *context) {
860   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
861   struct ChppTransportHeader *txHeader =
862       (struct ChppTransportHeader *)&linkTxBuffer[context->linkBufferSize];
863   context->linkBufferSize += sizeof(*txHeader);
864 
865   txHeader->packetCode = context->txStatus.packetCodeToSend;
866   context->txStatus.packetCodeToSend = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
867       context->txStatus.packetCodeToSend, CHPP_TRANSPORT_ERROR_NONE);
868 
869   txHeader->ackSeq = context->rxStatus.expectedSeq;
870   context->txStatus.sentAckSeq = txHeader->ackSeq;
871 
872   return txHeader;
873 }
874 
875 /**
876  * Adds the packet payload to link tx buffer.
877  *
878  * @param context State of the transport layer.
879  */
chppAddPayload(struct ChppTransportState * context)880 static void chppAddPayload(struct ChppTransportState *context) {
881   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
882   struct ChppTransportHeader *txHeader =
883       (struct ChppTransportHeader *)&linkTxBuffer[CHPP_PREAMBLE_LEN_BYTES];
884 
885   size_t remainingBytes =
886       context->txDatagramQueue.datagram[context->txDatagramQueue.front].length -
887       context->txStatus.ackedLocInDatagram;
888 
889   CHPP_LOGD("Adding payload to seq=%" PRIu8 ", remainingBytes=%" PRIuSIZE
890             " of pending datagrams=%" PRIu8,
891             txHeader->seq, remainingBytes, context->txDatagramQueue.pending);
892 
893   if (remainingBytes > chppTransportTxMtuSize(context)) {
894     // Send an unfinished part of a datagram
895     txHeader->flags = CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM;
896     txHeader->length = (uint16_t)chppTransportTxMtuSize(context);
897   } else {
898     // Send final (or only) part of a datagram
899     txHeader->flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM;
900     txHeader->length = (uint16_t)remainingBytes;
901   }
902 
903   // Copy payload
904   chppAppendToPendingTxPacket(
905       context,
906       context->txDatagramQueue.datagram[context->txDatagramQueue.front]
907               .payload +
908           context->txStatus.ackedLocInDatagram,
909       txHeader->length);
910 
911   context->txStatus.sentLocInDatagram =
912       context->txStatus.ackedLocInDatagram + txHeader->length;
913 }
914 
915 /**
916  * Adds a footer (containing the checksum) to a packet.
917  *
918  * @param context State of the transport layer.
919  */
chppAddFooter(struct ChppTransportState * context)920 static void chppAddFooter(struct ChppTransportState *context) {
921   struct ChppTransportFooter footer;
922   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
923   size_t bufferSize = context->linkBufferSize;
924 
925   footer.checksum = chppCrc32(0, &linkTxBuffer[CHPP_PREAMBLE_LEN_BYTES],
926                               bufferSize - CHPP_PREAMBLE_LEN_BYTES);
927 
928   CHPP_LOGD("Adding transport footer. Checksum=0x%" PRIx32 ", len: %" PRIuSIZE
929             " -> %" PRIuSIZE,
930             footer.checksum, bufferSize, bufferSize + sizeof(footer));
931 
932   chppAppendToPendingTxPacket(context, (const uint8_t *)&footer,
933                               sizeof(footer));
934 }
935 
936 /**
937  * Dequeues the datagram at the front of the datagram tx queue, if any, and
938  * frees the payload. Returns the number of remaining datagrams in the queue.
939  *
940  * @param context State of the transport layer.
941  * @return Number of remaining datagrams in queue.
942  */
chppDequeueTxDatagram(struct ChppTransportState * context)943 size_t chppDequeueTxDatagram(struct ChppTransportState *context) {
944   if (context->txDatagramQueue.pending == 0) {
945     CHPP_LOGE("Can not dequeue empty datagram queue");
946 
947   } else {
948     CHPP_LOGD("Dequeuing front datagram with index=%" PRIu8 ", len=%" PRIuSIZE
949               ". Queue depth: %" PRIu8 "->%d",
950               context->txDatagramQueue.front,
951               context->txDatagramQueue.datagram[context->txDatagramQueue.front]
952                   .length,
953               context->txDatagramQueue.pending,
954               context->txDatagramQueue.pending - 1);
955 
956     CHPP_FREE_AND_NULLIFY(
957         context->txDatagramQueue.datagram[context->txDatagramQueue.front]
958             .payload);
959     context->txDatagramQueue.datagram[context->txDatagramQueue.front].length =
960         0;
961 
962     context->txDatagramQueue.pending--;
963     context->txDatagramQueue.front++;
964     context->txDatagramQueue.front %= CHPP_TX_DATAGRAM_QUEUE_LEN;
965   }
966 
967   return context->txDatagramQueue.pending;
968 }
969 
970 /**
971  * Flushes the Tx datagram queue of any pending packets.
972  *
973  * @param context State of the transport layer.
974  */
chppClearTxDatagramQueue(struct ChppTransportState * context)975 static void chppClearTxDatagramQueue(struct ChppTransportState *context) {
976   while (context->txDatagramQueue.pending > 0) {
977     chppDequeueTxDatagram(context);
978   }
979   context->txStatus.hasPacketsToSend = false;
980 }
981 
982 /**
983  * Sends out a pending outgoing packet based on a notification from
984  * chppEnqueueTxPacket().
985  *
986  * A payload may or may not be included be according the following:
987  * No payload: If Tx datagram queue is empty OR we are waiting on a pending ACK.
988  * New payload: If there is one or more pending Tx datagrams and we are not
989  * waiting on a pending ACK.
990  * Repeat payload: If we haven't received an ACK yet for our previous payload,
991  * i.e. we have registered an explicit or implicit NACK.
992  *
993  * @param context State of the transport layer.
994  */
chppTransportDoWork(struct ChppTransportState * context)995 static void chppTransportDoWork(struct ChppTransportState *context) {
996   bool havePacketForLinkLayer = false;
997   struct ChppTransportHeader *txHeader;
998 
999   // Note: For a future ACK window >1, there needs to be a loop outside the lock
1000   chppMutexLock(&context->mutex);
1001 
1002   if (context->txStatus.hasPacketsToSend && !context->txStatus.linkBusy) {
1003     // There are pending outgoing packets and the link isn't busy
1004     havePacketForLinkLayer = true;
1005     context->txStatus.linkBusy = true;
1006 
1007     context->linkBufferSize = 0;
1008     uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
1009     const struct ChppLinkConfiguration linkConfig =
1010         context->linkApi->getConfig(context->linkContext);
1011     memset(linkTxBuffer, 0, linkConfig.txBufferLen);
1012 
1013     // Add preamble
1014     context->linkBufferSize += chppAddPreamble(linkTxBuffer);
1015 
1016     // Add header
1017     txHeader = chppAddHeader(context);
1018 
1019     // If applicable, add payload
1020     if ((context->txDatagramQueue.pending > 0)) {
1021       // Note: For a future ACK window >1, we need to rewrite this payload
1022       // adding code to base the next packet on the sent location within the
1023       // last sent datagram, except for the case of a NACK (explicit or
1024       // timeout). For a NACK, we would need to base the next packet off the
1025       // last ACKed location.
1026 
1027       txHeader->seq = context->rxStatus.receivedAckSeq;
1028       context->txStatus.sentSeq = txHeader->seq;
1029 
1030       if (context->txStatus.txAttempts > CHPP_TRANSPORT_MAX_RETX &&
1031           context->resetState != CHPP_RESET_STATE_RESETTING) {
1032         CHPP_LOGE("Resetting after %d reTX", CHPP_TRANSPORT_MAX_RETX);
1033         havePacketForLinkLayer = false;
1034 
1035         chppMutexUnlock(&context->mutex);
1036         chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
1037                   CHPP_TRANSPORT_ERROR_MAX_RETRIES);
1038         chppMutexLock(&context->mutex);
1039 
1040       } else {
1041         chppAddPayload(context);
1042         context->txStatus.txAttempts++;
1043       }
1044 
1045     } else {
1046       // No payload
1047       context->txStatus.hasPacketsToSend = false;
1048     }
1049 
1050     chppAddFooter(context);
1051 
1052   } else {
1053     CHPP_LOGW(
1054         "DoWork nothing to send. hasPackets=%d, linkBusy=%d, pending=%" PRIu8
1055         ", RX ACK=%" PRIu8 ", TX seq=%" PRIu8 ", RX state=%s",
1056         context->txStatus.hasPacketsToSend, context->txStatus.linkBusy,
1057         context->txDatagramQueue.pending, context->rxStatus.receivedAckSeq,
1058         context->txStatus.sentSeq,
1059         chppGetRxStatusLabel(context->rxStatus.state));
1060   }
1061 
1062   chppMutexUnlock(&context->mutex);
1063 
1064   if (havePacketForLinkLayer) {
1065     CHPP_LOGD("TX->Link: len=%" PRIuSIZE " flags=0x%" PRIx8 " code=0x%" PRIx8
1066               " ackSeq=%" PRIu8 " seq=%" PRIu8 " payloadLen=%" PRIu16
1067               " pending=%" PRIu8,
1068               context->linkBufferSize, txHeader->flags, txHeader->packetCode,
1069               txHeader->ackSeq, txHeader->seq, txHeader->length,
1070               context->txDatagramQueue.pending);
1071     enum ChppLinkErrorCode error = chppSendPendingPacket(context);
1072 
1073     if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
1074       // Platform implementation for platformLinkSend() is synchronous or an
1075       // error occurred. In either case, we should call chppLinkSendDoneCb()
1076       // here to release the contents of tx link buffer.
1077       chppLinkSendDoneCb(context, error);
1078     }
1079   }
1080 
1081 #ifdef CHPP_CLIENT_ENABLED
1082   {  // create a scope to declare timeoutResponse (C89).
1083     struct ChppAppHeader *timeoutResponse =
1084         chppTransportGetRequestTimeoutResponse(context, CHPP_ENDPOINT_CLIENT);
1085 
1086     if (timeoutResponse != NULL) {
1087       CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8,
1088                 timeoutResponse->handle, timeoutResponse->command,
1089                 timeoutResponse->transaction);
1090       chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse,
1091                                sizeof(struct ChppAppHeader));
1092     }
1093   }
1094 #endif  // CHPP_CLIENT_ENABLED
1095 #ifdef CHPP_SERVICE_ENABLED
1096   {  // create a scope to declare timeoutResponse (C89).
1097     struct ChppAppHeader *timeoutResponse =
1098         chppTransportGetRequestTimeoutResponse(context, CHPP_ENDPOINT_SERVICE);
1099 
1100     if (timeoutResponse != NULL) {
1101       CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8,
1102                 timeoutResponse->handle, timeoutResponse->command,
1103                 timeoutResponse->transaction);
1104       chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse,
1105                                sizeof(struct ChppAppHeader));
1106     }
1107   }
1108 #endif  // CHPP_SERVICE_ENABLED
1109 }
1110 
1111 /**
1112  * Appends data from a buffer of length len to a link tx buffer, updating its
1113  * length.
1114  *
1115  * @param context State of the transport layer.
1116  * @param buf Input data to be copied from.
1117  * @param len Length of input data in bytes.
1118  */
chppAppendToPendingTxPacket(struct ChppTransportState * context,const uint8_t * buf,size_t len)1119 static void chppAppendToPendingTxPacket(struct ChppTransportState *context,
1120                                         const uint8_t *buf, size_t len) {
1121   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
1122 
1123   size_t bufferSize = context->linkBufferSize;
1124 
1125   CHPP_ASSERT(bufferSize + len <=
1126               context->linkApi->getConfig(context->linkContext).txBufferLen);
1127   memcpy(&linkTxBuffer[bufferSize], buf, len);
1128   context->linkBufferSize += len;
1129 }
1130 
1131 /**
1132  * @return A human readable form of the packet attribution.
1133  */
chppGetPacketAttrStr(uint8_t packetCode)1134 static const char *chppGetPacketAttrStr(uint8_t packetCode) {
1135   switch (CHPP_TRANSPORT_GET_ATTR(packetCode)) {
1136     case CHPP_TRANSPORT_ATTR_RESET:
1137       return "(RESET)";
1138     case CHPP_TRANSPORT_ATTR_RESET_ACK:
1139       return "(RESET-ACK)";
1140     case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
1141       return "(LOOP-REQ)";
1142     case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
1143       return "(LOOP-RES)";
1144     default:
1145       return "";
1146   }
1147 }
1148 
1149 /**
1150  * Enqueues an outgoing datagram of a specified length. The payload must have
1151  * been allocated by the caller using chppMalloc.
1152  *
1153  * If enqueueing is successful, the payload will be freed by this function
1154  * once it has been sent out.
1155  * If enqueueing is unsuccessful, it is up to the caller to decide when or if
1156  * to free the payload and/or resend it later.
1157  *
1158  * @param context State of the transport layer.
1159  * @param packetCode Error code and packet attributes to be sent.
1160  * @param buf Datagram payload allocated through chppMalloc. Cannot be null.
1161  * @param len Datagram length in bytes.
1162  *
1163  * @return True informs the sender that the datagram was successfully enqueued.
1164  * False informs the sender that the queue was full.
1165  */
chppEnqueueTxDatagram(struct ChppTransportState * context,uint8_t packetCode,void * buf,size_t len)1166 static bool chppEnqueueTxDatagram(struct ChppTransportState *context,
1167                                   uint8_t packetCode, void *buf, size_t len) {
1168   bool success = false;
1169 
1170   if (len == 0) {
1171     CHPP_DEBUG_ASSERT_LOG(false, "Enqueue TX len=0!");
1172 
1173   } else {
1174     if ((len < sizeof(struct ChppAppHeader)) ||
1175         (CHPP_TRANSPORT_GET_ATTR(packetCode) != 0)) {
1176       CHPP_LOGD("Enqueue TX: code=0x%" PRIx8 "%s len=%" PRIuSIZE
1177                 " pending=%" PRIu8,
1178                 packetCode, chppGetPacketAttrStr(packetCode), len,
1179                 (uint8_t)(context->txDatagramQueue.pending + 1));
1180     } else {
1181       struct ChppAppHeader *header = buf;
1182       CHPP_LOGD(
1183           "Enqueue TX: len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
1184           " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16 " pending=%" PRIu8,
1185           len, header->handle, header->type, header->transaction, header->error,
1186           header->command, (uint8_t)(context->txDatagramQueue.pending + 1));
1187     }
1188 
1189     chppMutexLock(&context->mutex);
1190 
1191     if (context->txDatagramQueue.pending >= CHPP_TX_DATAGRAM_QUEUE_LEN) {
1192       CHPP_LOGE("Cannot enqueue TX datagram");
1193 
1194     } else {
1195       uint16_t end =
1196           (context->txDatagramQueue.front + context->txDatagramQueue.pending) %
1197           CHPP_TX_DATAGRAM_QUEUE_LEN;
1198       context->txDatagramQueue.datagram[end].length = len;
1199       context->txDatagramQueue.datagram[end].payload = buf;
1200       context->txDatagramQueue.pending++;
1201 
1202       if (context->txDatagramQueue.pending == 1) {
1203         // Queue was empty prior. Need to kickstart transmission.
1204         chppEnqueueTxPacket(context, packetCode);
1205       }
1206 
1207       success = true;
1208     }
1209 
1210     chppMutexUnlock(&context->mutex);
1211   }
1212 
1213   return success;
1214 }
1215 
1216 /**
1217  * Sends the pending outgoing packet over to the link
1218  * layer using Send() and updates the last Tx packet time.
1219  *
1220  * @param context State of the transport layer.
1221  *
1222  * @return Result of Send().
1223  */
chppSendPendingPacket(struct ChppTransportState * context)1224 static enum ChppLinkErrorCode chppSendPendingPacket(
1225     struct ChppTransportState *context) {
1226   enum ChppLinkErrorCode error =
1227       context->linkApi->send(context->linkContext, context->linkBufferSize);
1228 
1229   context->txStatus.lastTxTimeNs = chppGetCurrentTimeNs();
1230 
1231   return error;
1232 }
1233 
1234 /**
1235  * Resets the transport state, maintaining the link layer parameters.
1236  *
1237  * @param context State of the transport layer.
1238  */
chppResetTransportContext(struct ChppTransportState * context)1239 static void chppResetTransportContext(struct ChppTransportState *context) {
1240   memset(&context->rxStatus, 0, sizeof(struct ChppRxStatus));
1241   memset(&context->rxDatagram, 0, sizeof(struct ChppDatagram));
1242 
1243   memset(&context->txStatus, 0, sizeof(struct ChppTxStatus));
1244   memset(&context->txDatagramQueue, 0, sizeof(struct ChppTxDatagramQueue));
1245 
1246   context->txStatus.sentSeq =
1247       UINT8_MAX;  // So that the seq # of the first TX packet is 0
1248   context->resetState = CHPP_RESET_STATE_RESETTING;
1249 }
1250 
1251 /**
1252  * Re-initializes the CHPP transport and app layer states, e.g. when receiving a
1253  * reset packet, and sends out a reset or reset-ack packet over the link in
1254  * order to reset the remote side or inform the counterpart of a reset,
1255  * respectively.
1256  *
1257  * If the link layer is busy, this function will reset the link as well.
1258  * This function retains and restores the platform-specific values of
1259  * transportContext.linkContext.
1260  *
1261  * @param transportContext State of the transport layer.
1262  * @param resetType Type of reset to send after resetting CHPP (reset vs.
1263  * reset-ack), as defined in the ChppTransportPacketAttributes struct.
1264  * @param error Provides the error that led to the reset.
1265  */
chppReset(struct ChppTransportState * transportContext,enum ChppTransportPacketAttributes resetType,enum ChppTransportErrorCode error)1266 static void chppReset(struct ChppTransportState *transportContext,
1267                       enum ChppTransportPacketAttributes resetType,
1268                       enum ChppTransportErrorCode error) {
1269   // TODO: Configure transport layer based on (optional?) received config before
1270   // datagram is wiped
1271 
1272   chppMutexLock(&transportContext->mutex);
1273   struct ChppAppState *appContext = transportContext->appContext;
1274   transportContext->resetState = CHPP_RESET_STATE_RESETTING;
1275 
1276   // Reset asynchronous link layer if busy
1277   if (transportContext->txStatus.linkBusy == true) {
1278     // TODO: Give time for link layer to finish before resorting to a reset
1279 
1280     transportContext->linkApi->reset(transportContext->linkContext);
1281   }
1282 
1283   // Free memory allocated for any ongoing rx datagrams
1284   if (transportContext->rxDatagram.length > 0) {
1285     transportContext->rxDatagram.length = 0;
1286     CHPP_FREE_AND_NULLIFY(transportContext->rxDatagram.payload);
1287   }
1288 
1289   // Free memory allocated for any ongoing tx datagrams
1290   for (size_t i = 0; i < CHPP_TX_DATAGRAM_QUEUE_LEN; i++) {
1291     if (transportContext->txDatagramQueue.datagram[i].length > 0) {
1292       CHPP_FREE_AND_NULLIFY(
1293           transportContext->txDatagramQueue.datagram[i].payload);
1294     }
1295   }
1296 
1297   // Reset Transport Layer but restore Rx sequence number and packet code
1298   // (context->rxHeader is not wiped in reset)
1299   chppResetTransportContext(transportContext);
1300   transportContext->rxStatus.receivedPacketCode =
1301       transportContext->rxHeader.packetCode;
1302   transportContext->rxStatus.expectedSeq = transportContext->rxHeader.seq + 1;
1303 
1304   // Send reset or reset-ACK
1305   chppMutexUnlock(&transportContext->mutex);
1306   chppTransportSendReset(transportContext, resetType, error);
1307 
1308   // Inform the App Layer that a reset has completed
1309   if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1310     chppAppProcessReset(appContext);
1311   }  // else reset is sent out. Rx of reset-ack will indicate completion.
1312 }
1313 
1314 /**
1315  * Checks for a timed out request and generates a timeout response if a timeout
1316  * has occurred.
1317  *
1318  * @param context State of the transport layer.
1319  * @param type The type of the endpoint.
1320  * @return App layer response header if a timeout has occurred. Null otherwise.
1321  */
chppTransportGetRequestTimeoutResponse(struct ChppTransportState * context,enum ChppEndpointType type)1322 struct ChppAppHeader *chppTransportGetRequestTimeoutResponse(
1323     struct ChppTransportState *context, enum ChppEndpointType type) {
1324   CHPP_DEBUG_NOT_NULL(context);
1325 
1326   struct ChppAppState *appState = context->appContext;
1327   struct ChppAppHeader *response = NULL;
1328 
1329   bool timeoutEndpointFound = false;
1330   uint8_t timedOutEndpointIdx;
1331   uint16_t timedOutCmd;
1332 
1333   chppMutexLock(&context->mutex);
1334 
1335   if (*getNextRequestTimeoutNs(appState, type) <= chppGetCurrentTimeNs()) {
1336     // Determine which request has timed out
1337     const uint8_t endpointCount = getRegisteredEndpointCount(appState, type);
1338     uint64_t firstTimeout = CHPP_TIME_MAX;
1339 
1340     for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) {
1341       const uint16_t cmdCount =
1342           getRegisteredEndpointOutReqCount(appState, endpointIdx, type);
1343       const struct ChppEndpointState *endpointState =
1344           getRegisteredEndpointState(appState, endpointIdx, type);
1345       const struct ChppOutgoingRequestState *reqStates =
1346           &endpointState->outReqStates[0];
1347       for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) {
1348         const struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx];
1349 
1350         if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT &&
1351             reqState->responseTimeNs != CHPP_TIME_NONE &&
1352             reqState->responseTimeNs < firstTimeout) {
1353           firstTimeout = reqState->responseTimeNs;
1354           timedOutEndpointIdx = endpointIdx;
1355           timedOutCmd = cmdIdx;
1356           timeoutEndpointFound = true;
1357         }
1358       }
1359     }
1360 
1361     if (!timeoutEndpointFound) {
1362       CHPP_LOGE("Timeout at %" PRIu64 " but no endpoint",
1363                 *getNextRequestTimeoutNs(appState, type) / CHPP_NSEC_PER_MSEC);
1364       chppRecalculateNextTimeout(appState, CHPP_ENDPOINT_CLIENT);
1365     }
1366   }
1367 
1368   if (timeoutEndpointFound) {
1369     CHPP_LOGE("Endpoint=%" PRIu8 " cmd=%" PRIu16 " timed out",
1370               timedOutEndpointIdx, timedOutCmd);
1371     response = chppMalloc(sizeof(struct ChppAppHeader));
1372     if (response == NULL) {
1373       CHPP_LOG_OOM();
1374     } else {
1375       const struct ChppEndpointState *endpointState =
1376           getRegisteredEndpointState(appState, timedOutEndpointIdx, type);
1377       response->handle = endpointState->handle;
1378       response->type = type == CHPP_ENDPOINT_CLIENT
1379                            ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
1380                            : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
1381       response->transaction =
1382           endpointState->outReqStates[timedOutCmd].transaction;
1383       response->error = CHPP_APP_ERROR_TIMEOUT;
1384       response->command = timedOutCmd;
1385     }
1386   }
1387 
1388   chppMutexUnlock(&context->mutex);
1389 
1390   return response;
1391 }
1392 
1393 /************************************************
1394  *  Public Functions
1395  ***********************************************/
1396 
chppTransportInit(struct ChppTransportState * transportContext,struct ChppAppState * appContext,void * linkContext,const struct ChppLinkApi * linkApi)1397 void chppTransportInit(struct ChppTransportState *transportContext,
1398                        struct ChppAppState *appContext, void *linkContext,
1399                        const struct ChppLinkApi *linkApi) {
1400   CHPP_NOT_NULL(transportContext);
1401   CHPP_NOT_NULL(appContext);
1402 
1403   CHPP_ASSERT_LOG(!transportContext->initialized,
1404                   "CHPP transport already init");
1405   CHPP_LOGD("Initializing CHPP transport");
1406 
1407   chppResetTransportContext(transportContext);
1408   chppMutexInit(&transportContext->mutex);
1409   chppNotifierInit(&transportContext->notifier);
1410   chppConditionVariableInit(&transportContext->resetCondVar);
1411 #ifdef CHPP_ENABLE_WORK_MONITOR
1412   chppWorkMonitorInit(&transportContext->workMonitor);
1413 #endif
1414 
1415   transportContext->appContext = appContext;
1416   transportContext->initialized = true;
1417 
1418   CHPP_NOT_NULL(linkApi);
1419   CHPP_DEBUG_NOT_NULL(linkApi->init);
1420   CHPP_DEBUG_NOT_NULL(linkApi->deinit);
1421   CHPP_DEBUG_NOT_NULL(linkApi->send);
1422   CHPP_DEBUG_NOT_NULL(linkApi->doWork);
1423   CHPP_DEBUG_NOT_NULL(linkApi->reset);
1424   CHPP_DEBUG_NOT_NULL(linkApi->getConfig);
1425   CHPP_DEBUG_NOT_NULL(linkApi->getTxBuffer);
1426   transportContext->linkApi = linkApi;
1427 
1428   CHPP_NOT_NULL(linkContext);
1429   linkApi->init(linkContext, transportContext);
1430   transportContext->linkContext = linkContext;
1431 
1432 #ifdef CHPP_DEBUG_ASSERT_ENABLED
1433   const struct ChppLinkConfiguration linkConfig =
1434       linkApi->getConfig(linkContext);
1435   CHPP_ASSERT_LOG(
1436       linkConfig.txBufferLen > CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES,
1437       "The link TX buffer is too small");
1438   CHPP_ASSERT_LOG(
1439       linkConfig.rxBufferLen > CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES,
1440       "The link RX buffer is too small");
1441 #endif  // CHPP_DEBUG_ASSERT_ENABLED
1442 }
1443 
chppTransportDeinit(struct ChppTransportState * transportContext)1444 void chppTransportDeinit(struct ChppTransportState *transportContext) {
1445   CHPP_NOT_NULL(transportContext);
1446   CHPP_ASSERT_LOG(transportContext->initialized,
1447                   "CHPP transport already deinitialized");
1448 
1449   transportContext->linkApi->deinit(transportContext->linkContext);
1450 #ifdef CHPP_ENABLE_WORK_MONITOR
1451   chppWorkMonitorDeinit(&transportContext->workMonitor);
1452 #endif
1453   chppConditionVariableDeinit(&transportContext->resetCondVar);
1454   chppNotifierDeinit(&transportContext->notifier);
1455   chppMutexDeinit(&transportContext->mutex);
1456 
1457   chppClearTxDatagramQueue(transportContext);
1458 
1459   CHPP_FREE_AND_NULLIFY(transportContext->rxDatagram.payload);
1460 
1461   transportContext->initialized = false;
1462 }
1463 
chppTransportWaitForResetComplete(struct ChppTransportState * transportContext,uint64_t timeoutMs)1464 bool chppTransportWaitForResetComplete(
1465     struct ChppTransportState *transportContext, uint64_t timeoutMs) {
1466   bool success = true;
1467   chppMutexLock(&transportContext->mutex);
1468   while (success && transportContext->resetState != CHPP_RESET_STATE_NONE) {
1469     success = chppConditionVariableTimedWait(&transportContext->resetCondVar,
1470                                              &transportContext->mutex,
1471                                              timeoutMs * CHPP_NSEC_PER_MSEC);
1472   }
1473   chppMutexUnlock(&transportContext->mutex);
1474   return success;
1475 }
1476 
chppRxDataCb(struct ChppTransportState * context,const uint8_t * buf,size_t len)1477 bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
1478                   size_t len) {
1479   CHPP_NOT_NULL(buf);
1480   CHPP_NOT_NULL(context);
1481 
1482   chppMutexLock(&context->mutex);
1483   if (context->rxStatus.state != CHPP_STATE_PREAMBLE &&
1484       chppGetCurrentTimeNs() >
1485           context->rxStatus.packetStartTimeNs + CHPP_TRANSPORT_RX_TIMEOUT_NS) {
1486     CHPP_LOGE("Packet RX timeout");
1487     chppAbortRxPacket(context);
1488   }
1489   chppMutexUnlock(&context->mutex);
1490 
1491   CHPP_LOGD("RX %" PRIuSIZE " bytes: state=%s", len,
1492             chppGetRxStatusLabel(context->rxStatus.state));
1493   uint64_t now = chppGetCurrentTimeNs();
1494   context->rxStatus.lastDataTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC);
1495   context->rxStatus.numTotalDataBytes += len;
1496 
1497   size_t consumed = 0;
1498   while (consumed < len) {
1499     chppMutexLock(&context->mutex);
1500     // TODO: Investigate fine-grained locking, e.g. separating variables that
1501     // are only relevant to a particular path.
1502     // Also consider removing some of the finer-grained locks altogether for
1503     // non-multithreaded environments with clear documentation.
1504 
1505     switch (context->rxStatus.state) {
1506       case CHPP_STATE_PREAMBLE:
1507         consumed +=
1508             chppConsumePreamble(context, &buf[consumed], len - consumed);
1509         break;
1510 
1511       case CHPP_STATE_HEADER:
1512         consumed += chppConsumeHeader(context, &buf[consumed], len - consumed);
1513         break;
1514 
1515       case CHPP_STATE_PAYLOAD:
1516         consumed += chppConsumePayload(context, &buf[consumed], len - consumed);
1517         break;
1518 
1519       case CHPP_STATE_FOOTER:
1520         consumed += chppConsumeFooter(context, &buf[consumed], len - consumed);
1521         break;
1522 
1523       default:
1524         CHPP_DEBUG_ASSERT_LOG(false, "Invalid RX state %" PRIu8,
1525                               context->rxStatus.state);
1526         chppSetRxState(context, CHPP_STATE_PREAMBLE);
1527     }
1528 
1529     chppMutexUnlock(&context->mutex);
1530   }
1531 
1532   return (context->rxStatus.state == CHPP_STATE_PREAMBLE &&
1533           context->rxStatus.locInState == 0);
1534 }
1535 
chppRxPacketCompleteCb(struct ChppTransportState * context)1536 void chppRxPacketCompleteCb(struct ChppTransportState *context) {
1537   chppMutexLock(&context->mutex);
1538   if (context->rxStatus.state != CHPP_STATE_PREAMBLE) {
1539     CHPP_LOGE("RX pkt ended early: state=%s seq=%" PRIu8 " len=%" PRIu16,
1540               chppGetRxStatusLabel(context->rxStatus.state),
1541               context->rxHeader.seq, context->rxHeader.length);
1542     chppAbortRxPacket(context);
1543     chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_HEADER);  // NACK
1544   }
1545   chppMutexUnlock(&context->mutex);
1546 }
1547 
chppEnqueueTxDatagramOrFail(struct ChppTransportState * context,void * buf,size_t len)1548 bool chppEnqueueTxDatagramOrFail(struct ChppTransportState *context, void *buf,
1549                                  size_t len) {
1550   bool success = false;
1551   bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
1552 
1553   if (len == 0) {
1554     CHPP_DEBUG_ASSERT_LOG(false, "Enqueue datagram len=0!");
1555 
1556   } else if (resetting || !chppEnqueueTxDatagram(
1557                               context, CHPP_TRANSPORT_ERROR_NONE, buf, len)) {
1558     uint8_t *handle = buf;
1559     CHPP_LOGE("Resetting=%d. Discarding %" PRIuSIZE " bytes for H#%" PRIu8,
1560               resetting, len, *handle);
1561 
1562     CHPP_FREE_AND_NULLIFY(buf);
1563 
1564   } else {
1565     success = true;
1566   }
1567 
1568   return success;
1569 }
1570 
1571 // TODO(b/192359485): Consider removing this function, or making it more robust.
chppEnqueueTxErrorDatagram(struct ChppTransportState * context,enum ChppTransportErrorCode errorCode)1572 void chppEnqueueTxErrorDatagram(struct ChppTransportState *context,
1573                                 enum ChppTransportErrorCode errorCode) {
1574   chppMutexLock(&context->mutex);
1575   bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
1576   if (resetting) {
1577     CHPP_LOGE("Discarding app error 0x%" PRIx8 " (resetting)", errorCode);
1578   } else {
1579     switch (errorCode) {
1580       case CHPP_TRANSPORT_ERROR_OOM: {
1581         CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_OOM");
1582         break;
1583       }
1584       case CHPP_TRANSPORT_ERROR_APPLAYER: {
1585         CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_APPLAYER");
1586         break;
1587       }
1588       default: {
1589         // App layer should not invoke any other errors
1590         CHPP_DEBUG_ASSERT_LOG(false, "App enqueueing invalid err=%" PRIu8,
1591                               errorCode);
1592       }
1593     }
1594     chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
1595                                      CHPP_TRANSPORT_ATTR_NONE, errorCode));
1596   }
1597   chppMutexUnlock(&context->mutex);
1598 }
1599 
chppTransportGetTimeUntilNextDoWorkNs(struct ChppTransportState * context)1600 uint64_t chppTransportGetTimeUntilNextDoWorkNs(
1601     struct ChppTransportState *context) {
1602   uint64_t currentTime = chppGetCurrentTimeNs();
1603   // This function is called in the context of the transport worker thread.
1604   // As we do not know if the transport is used in the context of a service
1605   // or a client, we use the min of both timeouts.
1606   uint64_t nextDoWorkTime =
1607       MIN(context->appContext->nextClientRequestTimeoutNs,
1608           context->appContext->nextServiceRequestTimeoutNs);
1609 
1610   if (context->txStatus.hasPacketsToSend ||
1611       context->resetState == CHPP_RESET_STATE_RESETTING) {
1612     nextDoWorkTime =
1613         MIN(nextDoWorkTime, CHPP_TRANSPORT_TX_TIMEOUT_NS +
1614                                 ((context->txStatus.lastTxTimeNs == 0)
1615                                      ? currentTime
1616                                      : context->txStatus.lastTxTimeNs));
1617   }
1618 
1619   if (nextDoWorkTime == CHPP_TIME_MAX) {
1620     CHPP_LOGD("NextDoWork=n/a currentTime=%" PRIu64,
1621               currentTime / CHPP_NSEC_PER_MSEC);
1622     return CHPP_TRANSPORT_TIMEOUT_INFINITE;
1623   }
1624 
1625   CHPP_LOGD("NextDoWork=%" PRIu64 " currentTime=%" PRIu64 " delta=%" PRId64,
1626             nextDoWorkTime / CHPP_NSEC_PER_MSEC,
1627             currentTime / CHPP_NSEC_PER_MSEC,
1628             (nextDoWorkTime > currentTime ? nextDoWorkTime - currentTime : 0) /
1629                 (int64_t)CHPP_NSEC_PER_MSEC);
1630 
1631   return nextDoWorkTime <= currentTime ? CHPP_TRANSPORT_TIMEOUT_IMMEDIATE
1632                                        : nextDoWorkTime - currentTime;
1633 }
1634 
chppWorkThreadStart(struct ChppTransportState * context)1635 void chppWorkThreadStart(struct ChppTransportState *context) {
1636   chppTransportSendReset(context, CHPP_TRANSPORT_ATTR_RESET,
1637                          CHPP_TRANSPORT_ERROR_NONE);
1638   CHPP_LOGD("CHPP Work Thread started");
1639 
1640   uint32_t signals;
1641   do {
1642     uint64_t timeout = chppTransportGetTimeUntilNextDoWorkNs(context);
1643     if (timeout == CHPP_TRANSPORT_TIMEOUT_IMMEDIATE) {
1644       signals = chppNotifierGetSignal(&context->notifier);
1645     } else if (timeout == CHPP_TRANSPORT_TIMEOUT_INFINITE) {
1646       signals = chppNotifierWait(&context->notifier);
1647     } else {
1648       signals = chppNotifierTimedWait(&context->notifier, timeout);
1649     }
1650 
1651   } while (chppWorkThreadHandleSignal(context, signals));
1652 }
1653 
chppWorkThreadHandleSignal(struct ChppTransportState * context,uint32_t signals)1654 bool chppWorkThreadHandleSignal(struct ChppTransportState *context,
1655                                 uint32_t signals) {
1656   bool continueProcessing = false;
1657 
1658 #ifdef CHPP_ENABLE_WORK_MONITOR
1659   chppWorkMonitorPreProcess(&context->workMonitor);
1660 #endif
1661 
1662   if (signals & CHPP_TRANSPORT_SIGNAL_EXIT) {
1663     CHPP_LOGD("CHPP Work Thread terminated");
1664   } else {
1665     continueProcessing = true;
1666     if (signals == 0) {
1667       // Triggered by timeout.
1668       chppWorkHandleTimeout(context);
1669     } else {
1670       if (signals & CHPP_TRANSPORT_SIGNAL_EVENT) {
1671         chppTransportDoWork(context);
1672       }
1673       if (signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK) {
1674         context->linkApi->doWork(context->linkContext,
1675                                  signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK);
1676       }
1677     }
1678   }
1679 
1680 #ifdef CHPP_ENABLE_WORK_MONITOR
1681   chppWorkMonitorPostProcess(&context->workMonitor);
1682 #endif
1683 
1684   return continueProcessing;
1685 }
1686 
1687 /**
1688  * Handle timeouts in the worker thread.
1689  *
1690  * Timeouts occurs when either:
1691  * 1. There are packets to send and last packet send was more than
1692  *    CHPP_TRANSPORT_TX_TIMEOUT_NS ago
1693  * 2. We haven't received a response to a request in time
1694  * 3. We haven't received the reset ACK
1695  *
1696  * For 1 and 2, chppTransportDoWork should be called to respectively
1697  * - Transmit the packet
1698  * - Send a timeout response
1699  */
chppWorkHandleTimeout(struct ChppTransportState * context)1700 static void chppWorkHandleTimeout(struct ChppTransportState *context) {
1701   const uint64_t currentTimeNs = chppGetCurrentTimeNs();
1702   const bool isTxTimeout = currentTimeNs - context->txStatus.lastTxTimeNs >=
1703                            CHPP_TRANSPORT_TX_TIMEOUT_NS;
1704   const bool isResetting = context->resetState == CHPP_RESET_STATE_RESETTING;
1705 
1706   // Call chppTransportDoWork for both TX and request timeouts.
1707   if (isTxTimeout) {
1708     CHPP_LOGE("ACK timeout. Tx t=%" PRIu64 ", attempt %zu, isResetting=%d",
1709               context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC,
1710               context->txStatus.txAttempts, isResetting);
1711     chppTransportDoWork(context);
1712   } else {
1713     const uint64_t requestTimeoutNs =
1714         MIN(context->appContext->nextClientRequestTimeoutNs,
1715             context->appContext->nextServiceRequestTimeoutNs);
1716     const bool isRequestTimeout = requestTimeoutNs <= currentTimeNs;
1717     if (isRequestTimeout) {
1718       chppTransportDoWork(context);
1719     }
1720   }
1721 
1722   if (isResetting && (currentTimeNs - context->resetTimeNs >=
1723                       CHPP_TRANSPORT_RESET_TIMEOUT_NS)) {
1724     if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) {
1725       CHPP_LOGE("RESET-ACK timeout; retrying");
1726       context->resetCount++;
1727       chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
1728                 CHPP_TRANSPORT_ERROR_TIMEOUT);
1729     } else {
1730       CHPP_LOGE("RESET-ACK timeout; giving up");
1731       context->txStatus.txAttempts = 0;
1732       context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE;
1733       chppClearTxDatagramQueue(context);
1734     }
1735   }
1736 }
1737 
chppWorkThreadStop(struct ChppTransportState * context)1738 void chppWorkThreadStop(struct ChppTransportState *context) {
1739   chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EXIT);
1740 }
1741 
chppLinkSendDoneCb(struct ChppTransportState * context,enum ChppLinkErrorCode error)1742 void chppLinkSendDoneCb(struct ChppTransportState *context,
1743                         enum ChppLinkErrorCode error) {
1744   if (error != CHPP_LINK_ERROR_NONE_SENT) {
1745     CHPP_LOGE("Async send failure: %" PRIu8, error);
1746   }
1747 
1748   chppMutexLock(&context->mutex);
1749 
1750   context->txStatus.linkBusy = false;
1751 
1752   // No need to free anything as link Tx buffer is static. Likewise, we
1753   // keep linkBufferSize to assist testing.
1754 
1755   chppMutexUnlock(&context->mutex);
1756 }
1757 
chppDatagramProcessDoneCb(struct ChppTransportState * context,uint8_t * buf)1758 void chppDatagramProcessDoneCb(struct ChppTransportState *context,
1759                                uint8_t *buf) {
1760   UNUSED_VAR(context);
1761 
1762   CHPP_FREE_AND_NULLIFY(buf);
1763 }
1764 
chppRunTransportLoopback(struct ChppTransportState * context,uint8_t * buf,size_t len)1765 uint8_t chppRunTransportLoopback(struct ChppTransportState *context,
1766                                  uint8_t *buf, size_t len) {
1767   UNUSED_VAR(buf);
1768   UNUSED_VAR(len);
1769   uint8_t result = CHPP_APP_ERROR_UNSUPPORTED;
1770   context->loopbackResult = result;
1771 
1772 #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
1773   result = CHPP_APP_ERROR_NONE;
1774   context->loopbackResult = CHPP_APP_ERROR_UNSPECIFIED;
1775 
1776   if (len == 0 || len > chppTransportTxMtuSize(context)) {
1777     result = CHPP_APP_ERROR_INVALID_LENGTH;
1778     context->loopbackResult = result;
1779 
1780   } else if (context->txStatus.linkBusy) {
1781     result = CHPP_APP_ERROR_BLOCKED;
1782     context->loopbackResult = result;
1783 
1784   } else if (context->transportLoopbackData.payload != NULL) {
1785     result = CHPP_APP_ERROR_BUSY;
1786     context->loopbackResult = result;
1787 
1788   } else if ((context->transportLoopbackData.payload = chppMalloc(len)) ==
1789              NULL) {
1790     result = CHPP_APP_ERROR_OOM;
1791     context->loopbackResult = result;
1792 
1793   } else {
1794     uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
1795     context->transportLoopbackData.length = len;
1796     memcpy(context->transportLoopbackData.payload, buf, len);
1797 
1798     context->txStatus.linkBusy = true;
1799     context->linkBufferSize = 0;
1800     const struct ChppLinkConfiguration linkConfig =
1801         context->linkApi->getConfig(context->linkContext);
1802     memset(linkTxBuffer, 0, linkConfig.txBufferLen);
1803     context->linkBufferSize += chppAddPreamble(linkTxBuffer);
1804 
1805     struct ChppTransportHeader *txHeader =
1806         (struct ChppTransportHeader *)&linkTxBuffer[context->linkBufferSize];
1807     context->linkBufferSize += sizeof(*txHeader);
1808 
1809     txHeader->packetCode = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
1810         CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST, txHeader->packetCode);
1811 
1812     size_t payloadLen = MIN(len, chppTransportTxMtuSize(context));
1813     txHeader->length = (uint16_t)payloadLen;
1814     chppAppendToPendingTxPacket(context, buf, payloadLen);
1815 
1816     chppAddFooter(context);
1817 
1818     CHPP_LOGD("Sending transport-loopback request (packet len=%" PRIuSIZE
1819               ", payload len=%" PRIu16 ", asked len was %" PRIuSIZE ")",
1820               context->linkBufferSize, txHeader->length, len);
1821     enum ChppLinkErrorCode error = chppSendPendingPacket(context);
1822 
1823     if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
1824       // Either sent synchronously or an error has occurred
1825       chppLinkSendDoneCb(context, error);
1826 
1827       if (error != CHPP_LINK_ERROR_NONE_SENT) {
1828         // An error has occurred
1829         CHPP_FREE_AND_NULLIFY(context->transportLoopbackData.payload);
1830         context->transportLoopbackData.length = 0;
1831         result = CHPP_APP_ERROR_UNSPECIFIED;
1832       }
1833     }
1834   }
1835 
1836   if (result != CHPP_APP_ERROR_NONE) {
1837     CHPP_LOGE("Trans-loopback failure: %" PRIu8, result);
1838   }
1839 #endif
1840   return result;
1841 }
1842 
chppTransportSendReset(struct ChppTransportState * context,enum ChppTransportPacketAttributes resetType,enum ChppTransportErrorCode error)1843 void chppTransportSendReset(struct ChppTransportState *context,
1844                             enum ChppTransportPacketAttributes resetType,
1845                             enum ChppTransportErrorCode error) {
1846   // Make sure CHPP is in an initialized state
1847   CHPP_ASSERT_LOG((context->txDatagramQueue.pending == 0 &&
1848                    context->txDatagramQueue.front == 0),
1849                   "Not init to send reset");
1850 
1851   struct ChppTransportConfiguration *config =
1852       chppMalloc(sizeof(struct ChppTransportConfiguration));
1853   if (config == NULL) {
1854     CHPP_LOG_OOM();
1855   } else {
1856     // CHPP transport version
1857     config->version.major = 1;
1858     config->version.minor = 0;
1859     config->version.patch = 0;
1860 
1861     config->reserved1 = 0;
1862     config->reserved2 = 0;
1863     config->reserved3 = 0;
1864 
1865     if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1866       CHPP_LOGD("Sending RESET-ACK");
1867       chppSetResetComplete(context);
1868     } else {
1869       CHPP_LOGD("Sending RESET");
1870     }
1871 
1872     context->resetTimeNs = chppGetCurrentTimeNs();
1873 
1874     chppEnqueueTxDatagram(context,
1875                           CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(resetType, error),
1876                           config, sizeof(*config));
1877   }
1878 }
1879 
chppTransportTxMtuSize(const struct ChppTransportState * context)1880 size_t chppTransportTxMtuSize(const struct ChppTransportState *context) {
1881   const struct ChppLinkConfiguration linkConfig =
1882       context->linkApi->getConfig(context->linkContext);
1883 
1884   return linkConfig.txBufferLen - CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES;
1885 }
1886 
chppTransportRxMtuSize(const struct ChppTransportState * context)1887 size_t chppTransportRxMtuSize(const struct ChppTransportState *context) {
1888   const struct ChppLinkConfiguration linkConfig =
1889       context->linkApi->getConfig(context->linkContext);
1890 
1891   return linkConfig.rxBufferLen - CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES;
1892 }