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