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_LOGE("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     chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
608                                      CHPP_TRANSPORT_ATTR_NONE, errorCode));
609   }
610 
611   if (errorCode == CHPP_TRANSPORT_ERROR_ORDER) {
612     CHPP_LOGE("Out of order RX discarded seq=%" PRIu8 " expect=%" PRIu8
613               " len=%" PRIu16,
614               context->rxHeader.seq, context->rxStatus.expectedSeq,
615               context->rxHeader.length);
616     chppAbortRxPacket(context);
617 
618   } else if (context->rxHeader.length > 0) {
619     // Process payload and send ACK
620     chppProcessRxPayload(context);
621   } else if (!context->txStatus.hasPacketsToSend) {
622     // Nothing to send and nothing to receive, i.e. this is an ACK before an
623     // indefinite period of inactivity. Kick the work thread so it recalculates
624     // the notifier timeout.
625     chppNotifierSignal(&context->notifier,
626                        CHPP_TRANSPORT_SIGNAL_RECALC_TIMEOUT);
627   }
628 }
629 
630 /**
631  * Process the payload of a validated payload-bearing packet and send out the
632  * ACK.
633  *
634  * @param context State of the transport layer.
635  */
chppProcessRxPayload(struct ChppTransportState * context)636 static void chppProcessRxPayload(struct ChppTransportState *context) {
637   context->rxStatus.expectedSeq++;  // chppProcessRxPacket() already confirms
638                                     // that context->rxStatus.expectedSeq ==
639                                     // context->rxHeader.seq, protecting against
640                                     // duplicate and out-of-order packets.
641 
642   if (context->rxHeader.flags & CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM) {
643     // Packet is part of a larger datagram
644     CHPP_LOGD("RX packet for unfinished datagram. Seq=%" PRIu8 " len=%" PRIu16
645               ". Datagram len=%" PRIuSIZE ". Sending ACK=%" PRIu8,
646               context->rxHeader.seq, context->rxHeader.length,
647               context->rxDatagram.length, context->rxStatus.expectedSeq);
648 
649   } else {
650     // End of this packet is end of a datagram
651 
652     // Send the payload to the App Layer
653     // Note that it is up to the app layer to free the buffer using
654     // chppDatagramProcessDoneCb() after is is done.
655     chppMutexUnlock(&context->mutex);
656     chppAppProcessRxDatagram(context->appContext, context->rxDatagram.payload,
657                              context->rxDatagram.length);
658     chppMutexLock(&context->mutex);
659 
660     CHPP_LOGD("App layer processed datagram with len=%" PRIuSIZE
661               ", ending packet seq=%" PRIu8 ", len=%" PRIu16
662               ". Sending ACK=%" PRIu8 " (previously sent=%" PRIu8 ")",
663               context->rxDatagram.length, context->rxHeader.seq,
664               context->rxHeader.length, context->rxStatus.expectedSeq,
665               context->txStatus.sentAckSeq);
666     chppClearRxDatagram(context);
667   }
668 
669   // Send ACK because we had RX a payload-bearing packet
670   chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
671 }
672 
673 /**
674  * Resets the incoming datagram state, i.e. after the datagram has been
675  * processed.
676  * Note that this is independent from freeing the payload. It is up to the app
677  * layer to inform the transport layer using chppDatagramProcessDoneCb() once it
678  * is done with the buffer so it is freed.
679  *
680  * @param context State of the transport layer.
681  */
chppClearRxDatagram(struct ChppTransportState * context)682 static void chppClearRxDatagram(struct ChppTransportState *context) {
683   context->rxStatus.locInDatagram = 0;
684   context->rxDatagram.length = 0;
685   context->rxDatagram.payload = NULL;
686 }
687 
688 /**
689  * Validates the checksum of an incoming packet.
690  *
691  * @param context State of the transport layer.
692  *
693  * @return True if and only if the checksum is correct.
694  */
chppRxChecksumIsOk(const struct ChppTransportState * context)695 static bool chppRxChecksumIsOk(const struct ChppTransportState *context) {
696   uint32_t crc = chppCrc32(0, (const uint8_t *)&context->rxHeader,
697                            sizeof(context->rxHeader));
698   crc = chppCrc32(
699       crc,
700       &context->rxDatagram
701            .payload[context->rxStatus.locInDatagram - context->rxHeader.length],
702       context->rxHeader.length);
703 
704   if (context->rxFooter.checksum != crc) {
705     CHPP_LOGE("RX BAD checksum: footer=0x%" PRIx32 ", calc=0x%" PRIx32
706               ", len=%" PRIuSIZE,
707               context->rxFooter.checksum, crc,
708               (size_t)(context->rxHeader.length +
709                        sizeof(struct ChppTransportHeader)));
710   }
711 
712   return (context->rxFooter.checksum == crc);
713 }
714 
715 /**
716  * Performs consistency checks on received packet header to determine if it is
717  * obviously corrupt / invalid / duplicate / out-of-order.
718  *
719  * @param context State of the transport layer.
720  *
721  * @return True if and only if header passes checks
722  */
chppRxHeaderCheck(const struct ChppTransportState * context)723 static enum ChppTransportErrorCode chppRxHeaderCheck(
724     const struct ChppTransportState *context) {
725   enum ChppTransportErrorCode result = CHPP_TRANSPORT_ERROR_NONE;
726 
727   if (context->rxHeader.length > chppTransportRxMtuSize(context)) {
728     result = CHPP_TRANSPORT_ERROR_HEADER;
729   }
730 
731   if (result != CHPP_TRANSPORT_ERROR_NONE) {
732     CHPP_LOGE("Bad header. seq=%" PRIu8 " expect=%" PRIu8 " len=%" PRIu16
733               " err=%" PRIu8,
734               context->rxHeader.seq, context->rxStatus.expectedSeq,
735               context->rxHeader.length, result);
736   }
737 
738   return result;
739 }
740 
741 /**
742  * Registers a received ACK. If an outgoing datagram is fully ACKed, it is
743  * popped from the TX queue.
744  *
745  * @param context State of the transport layer.
746  */
chppRegisterRxAck(struct ChppTransportState * context)747 static void chppRegisterRxAck(struct ChppTransportState *context) {
748   uint8_t rxAckSeq = context->rxHeader.ackSeq;
749 
750   if (context->rxStatus.receivedAckSeq != rxAckSeq) {
751     // A previously sent packet was actually ACKed
752     // Note: For a future ACK window >1, we should loop by # of ACKed packets
753     if ((uint8_t)(context->rxStatus.receivedAckSeq + 1) != rxAckSeq) {
754       CHPP_LOGE("Out of order ACK: last=%" PRIu8 " rx=%" PRIu8,
755                 context->rxStatus.receivedAckSeq, rxAckSeq);
756     } else {
757       CHPP_LOGD(
758           "ACK received (last registered=%" PRIu8 ", received=%" PRIu8
759           "). Prior queue depth=%" PRIu8 ", front datagram=%" PRIu8
760           " at loc=%" PRIuSIZE " of len=%" PRIuSIZE,
761           context->rxStatus.receivedAckSeq, rxAckSeq,
762           context->txDatagramQueue.pending, context->txDatagramQueue.front,
763           context->txStatus.ackedLocInDatagram,
764           context->txDatagramQueue.datagram[context->txDatagramQueue.front]
765               .length);
766 
767       context->rxStatus.receivedAckSeq = rxAckSeq;
768       if (context->txStatus.txAttempts > 1) {
769         CHPP_LOGW("Seq %" PRIu8 " ACK'd after %" PRIuSIZE " reTX",
770                   context->rxHeader.seq, context->txStatus.txAttempts - 1);
771       }
772       context->txStatus.txAttempts = 0;
773 
774       // Process and if necessary pop from Tx datagram queue
775       context->txStatus.ackedLocInDatagram += chppTransportTxMtuSize(context);
776       if (context->txStatus.ackedLocInDatagram >=
777           context->txDatagramQueue.datagram[context->txDatagramQueue.front]
778               .length) {
779         // We are done with datagram
780 
781         context->txStatus.ackedLocInDatagram = 0;
782         context->txStatus.sentLocInDatagram = 0;
783 
784         // Note: For a future ACK window >1, we need to update the queue
785         // position of the datagram being sent as well (relative to the
786         // front-of-queue). e.g. context->txStatus.datagramBeingSent--;
787 
788         if (chppDequeueTxDatagram(context) == 0) {
789           context->txStatus.hasPacketsToSend = false;
790         }
791       }
792     }
793   }  // else {nothing was ACKed}
794 }
795 
796 /**
797  * Enqueues an outgoing packet with the specified error code. The error code
798  * refers to the optional reason behind a NACK, if any. An error code of
799  * CHPP_TRANSPORT_ERROR_NONE indicates that no error was reported (i.e. either
800  * an ACK or an implicit NACK)
801  *
802  * Note that the decision as to whether to include a payload will be taken
803  * later, i.e. before the packet is being sent out from the queue. A payload is
804  * expected to be included if there is one or more pending Tx datagrams and we
805  * are not waiting on a pending ACK. A (repeat) payload is also included if we
806  * have received a NACK.
807  *
808  * Further note that even for systems with an ACK window greater than one, we
809  * would only need to send an ACK for the last (correct) packet, hence we only
810  * need a queue length of one here.
811  *
812  * @param context State of the transport layer.
813  * @param packetCode Error code and packet attributes to be sent.
814  */
chppEnqueueTxPacket(struct ChppTransportState * context,uint8_t packetCode)815 static void chppEnqueueTxPacket(struct ChppTransportState *context,
816                                 uint8_t packetCode) {
817   context->txStatus.hasPacketsToSend = true;
818   context->txStatus.packetCodeToSend = packetCode;
819 
820   CHPP_LOGD("chppEnqueueTxPacket called with packet code=0x%" PRIx8,
821             packetCode);
822 
823   // Notifies the main CHPP Transport Layer to run chppTransportDoWork().
824   chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EVENT);
825 }
826 
827 /**
828  * Adds a CHPP preamble to the beginning of buf.
829  *
830  * @param buf The CHPP preamble will be added to buf.
831  *
832  * @return Size of the added preamble.
833  */
chppAddPreamble(uint8_t * buf)834 static size_t chppAddPreamble(uint8_t *buf) {
835   buf[0] = CHPP_PREAMBLE_BYTE_FIRST;
836   buf[1] = CHPP_PREAMBLE_BYTE_SECOND;
837   return CHPP_PREAMBLE_LEN_BYTES;
838 }
839 
840 /**
841  * Adds the packet header to link tx buffer.
842  *
843  * @param context State of the transport layer.
844  *
845  * @return Pointer to the added packet header.
846  */
chppAddHeader(struct ChppTransportState * context)847 static struct ChppTransportHeader *chppAddHeader(
848     struct ChppTransportState *context) {
849   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
850   struct ChppTransportHeader *txHeader =
851       (struct ChppTransportHeader *)&linkTxBuffer[context->linkBufferSize];
852   context->linkBufferSize += sizeof(*txHeader);
853 
854   txHeader->packetCode = context->txStatus.packetCodeToSend;
855   context->txStatus.packetCodeToSend = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
856       context->txStatus.packetCodeToSend, CHPP_TRANSPORT_ERROR_NONE);
857 
858   txHeader->ackSeq = context->rxStatus.expectedSeq;
859   context->txStatus.sentAckSeq = txHeader->ackSeq;
860 
861   return txHeader;
862 }
863 
864 /**
865  * Adds the packet payload to link tx buffer.
866  *
867  * @param context State of the transport layer.
868  */
chppAddPayload(struct ChppTransportState * context)869 static void chppAddPayload(struct ChppTransportState *context) {
870   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
871   struct ChppTransportHeader *txHeader =
872       (struct ChppTransportHeader *)&linkTxBuffer[CHPP_PREAMBLE_LEN_BYTES];
873 
874   size_t remainingBytes =
875       context->txDatagramQueue.datagram[context->txDatagramQueue.front].length -
876       context->txStatus.ackedLocInDatagram;
877 
878   CHPP_LOGD("Adding payload to seq=%" PRIu8 ", remainingBytes=%" PRIuSIZE
879             " of pending datagrams=%" PRIu8,
880             txHeader->seq, remainingBytes, context->txDatagramQueue.pending);
881 
882   if (remainingBytes > chppTransportTxMtuSize(context)) {
883     // Send an unfinished part of a datagram
884     txHeader->flags = CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM;
885     txHeader->length = (uint16_t)chppTransportTxMtuSize(context);
886   } else {
887     // Send final (or only) part of a datagram
888     txHeader->flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM;
889     txHeader->length = (uint16_t)remainingBytes;
890   }
891 
892   // Copy payload
893   chppAppendToPendingTxPacket(
894       context,
895       context->txDatagramQueue.datagram[context->txDatagramQueue.front]
896               .payload +
897           context->txStatus.ackedLocInDatagram,
898       txHeader->length);
899 
900   context->txStatus.sentLocInDatagram =
901       context->txStatus.ackedLocInDatagram + txHeader->length;
902 }
903 
904 /**
905  * Adds a footer (containing the checksum) to a packet.
906  *
907  * @param context State of the transport layer.
908  */
chppAddFooter(struct ChppTransportState * context)909 static void chppAddFooter(struct ChppTransportState *context) {
910   struct ChppTransportFooter footer;
911   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
912   size_t bufferSize = context->linkBufferSize;
913 
914   footer.checksum = chppCrc32(0, &linkTxBuffer[CHPP_PREAMBLE_LEN_BYTES],
915                               bufferSize - CHPP_PREAMBLE_LEN_BYTES);
916 
917   CHPP_LOGD("Adding transport footer. Checksum=0x%" PRIx32 ", len: %" PRIuSIZE
918             " -> %" PRIuSIZE,
919             footer.checksum, bufferSize, bufferSize + sizeof(footer));
920 
921   chppAppendToPendingTxPacket(context, (const uint8_t *)&footer,
922                               sizeof(footer));
923 }
924 
925 /**
926  * Dequeues the datagram at the front of the datagram tx queue, if any, and
927  * frees the payload. Returns the number of remaining datagrams in the queue.
928  *
929  * @param context State of the transport layer.
930  * @return Number of remaining datagrams in queue.
931  */
chppDequeueTxDatagram(struct ChppTransportState * context)932 size_t chppDequeueTxDatagram(struct ChppTransportState *context) {
933   if (context->txDatagramQueue.pending == 0) {
934     CHPP_LOGE("Can not dequeue empty datagram queue");
935 
936   } else {
937     CHPP_LOGD("Dequeuing front datagram with index=%" PRIu8 ", len=%" PRIuSIZE
938               ". Queue depth: %" PRIu8 "->%d",
939               context->txDatagramQueue.front,
940               context->txDatagramQueue.datagram[context->txDatagramQueue.front]
941                   .length,
942               context->txDatagramQueue.pending,
943               context->txDatagramQueue.pending - 1);
944 
945     CHPP_FREE_AND_NULLIFY(
946         context->txDatagramQueue.datagram[context->txDatagramQueue.front]
947             .payload);
948     context->txDatagramQueue.datagram[context->txDatagramQueue.front].length =
949         0;
950 
951     context->txDatagramQueue.pending--;
952     context->txDatagramQueue.front++;
953     context->txDatagramQueue.front %= CHPP_TX_DATAGRAM_QUEUE_LEN;
954   }
955 
956   return context->txDatagramQueue.pending;
957 }
958 
959 /**
960  * Flushes the Tx datagram queue of any pending packets.
961  *
962  * @param context State of the transport layer.
963  */
chppClearTxDatagramQueue(struct ChppTransportState * context)964 static void chppClearTxDatagramQueue(struct ChppTransportState *context) {
965   while (context->txDatagramQueue.pending > 0) {
966     chppDequeueTxDatagram(context);
967   }
968   context->txStatus.hasPacketsToSend = false;
969 }
970 
971 /**
972  * Sends out a pending outgoing packet based on a notification from
973  * chppEnqueueTxPacket().
974  *
975  * A payload may or may not be included be according the following:
976  * No payload: If Tx datagram queue is empty OR we are waiting on a pending ACK.
977  * New payload: If there is one or more pending Tx datagrams and we are not
978  * waiting on a pending ACK.
979  * Repeat payload: If we haven't received an ACK yet for our previous payload,
980  * i.e. we have registered an explicit or implicit NACK.
981  *
982  * @param context State of the transport layer.
983  */
chppTransportDoWork(struct ChppTransportState * context)984 static void chppTransportDoWork(struct ChppTransportState *context) {
985   bool havePacketForLinkLayer = false;
986   struct ChppTransportHeader *txHeader;
987 
988   // Note: For a future ACK window >1, there needs to be a loop outside the lock
989   chppMutexLock(&context->mutex);
990 
991   if (context->txStatus.hasPacketsToSend && !context->txStatus.linkBusy) {
992     // There are pending outgoing packets and the link isn't busy
993     havePacketForLinkLayer = true;
994     context->txStatus.linkBusy = true;
995 
996     context->linkBufferSize = 0;
997     uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
998     const struct ChppLinkConfiguration linkConfig =
999         context->linkApi->getConfig(context->linkContext);
1000     memset(linkTxBuffer, 0, linkConfig.txBufferLen);
1001 
1002     // Add preamble
1003     context->linkBufferSize += chppAddPreamble(linkTxBuffer);
1004 
1005     // Add header
1006     txHeader = chppAddHeader(context);
1007 
1008     // If applicable, add payload
1009     if ((context->txDatagramQueue.pending > 0)) {
1010       // Note: For a future ACK window >1, we need to rewrite this payload
1011       // adding code to base the next packet on the sent location within the
1012       // last sent datagram, except for the case of a NACK (explicit or
1013       // timeout). For a NACK, we would need to base the next packet off the
1014       // last ACKed location.
1015 
1016       txHeader->seq = context->rxStatus.receivedAckSeq;
1017       context->txStatus.sentSeq = txHeader->seq;
1018 
1019       if (context->txStatus.txAttempts > CHPP_TRANSPORT_MAX_RETX &&
1020           context->resetState != CHPP_RESET_STATE_RESETTING) {
1021         CHPP_LOGE("Resetting after %d reTX", CHPP_TRANSPORT_MAX_RETX);
1022         havePacketForLinkLayer = false;
1023 
1024         chppMutexUnlock(&context->mutex);
1025         chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
1026                   CHPP_TRANSPORT_ERROR_MAX_RETRIES);
1027         chppMutexLock(&context->mutex);
1028 
1029       } else {
1030         chppAddPayload(context);
1031         context->txStatus.txAttempts++;
1032       }
1033 
1034     } else {
1035       // No payload
1036       context->txStatus.hasPacketsToSend = false;
1037     }
1038 
1039     chppAddFooter(context);
1040 
1041   } else {
1042     CHPP_LOGW(
1043         "DoWork nothing to send. hasPackets=%d, linkBusy=%d, pending=%" PRIu8
1044         ", RX ACK=%" PRIu8 ", TX seq=%" PRIu8 ", RX state=%s",
1045         context->txStatus.hasPacketsToSend, context->txStatus.linkBusy,
1046         context->txDatagramQueue.pending, context->rxStatus.receivedAckSeq,
1047         context->txStatus.sentSeq,
1048         chppGetRxStatusLabel(context->rxStatus.state));
1049   }
1050 
1051   chppMutexUnlock(&context->mutex);
1052 
1053   if (havePacketForLinkLayer) {
1054     CHPP_LOGD("TX->Link: len=%" PRIuSIZE " flags=0x%" PRIx8 " code=0x%" PRIx8
1055               " ackSeq=%" PRIu8 " seq=%" PRIu8 " payloadLen=%" PRIu16
1056               " pending=%" PRIu8,
1057               context->linkBufferSize, txHeader->flags, txHeader->packetCode,
1058               txHeader->ackSeq, txHeader->seq, txHeader->length,
1059               context->txDatagramQueue.pending);
1060     enum ChppLinkErrorCode error = chppSendPendingPacket(context);
1061 
1062     if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
1063       // Platform implementation for platformLinkSend() is synchronous or an
1064       // error occurred. In either case, we should call chppLinkSendDoneCb()
1065       // here to release the contents of tx link buffer.
1066       chppLinkSendDoneCb(context, error);
1067     }
1068   }
1069 
1070 #ifdef CHPP_CLIENT_ENABLED
1071   {  // create a scope to declare timeoutResponse (C89).
1072     struct ChppAppHeader *timeoutResponse =
1073         chppTransportGetRequestTimeoutResponse(context, CHPP_ENDPOINT_CLIENT);
1074 
1075     if (timeoutResponse != NULL) {
1076       CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8,
1077                 timeoutResponse->handle, timeoutResponse->command,
1078                 timeoutResponse->transaction);
1079       chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse,
1080                                sizeof(struct ChppAppHeader));
1081     }
1082   }
1083 #endif  // CHPP_CLIENT_ENABLED
1084 #ifdef CHPP_SERVICE_ENABLED
1085   {  // create a scope to declare timeoutResponse (C89).
1086     struct ChppAppHeader *timeoutResponse =
1087         chppTransportGetRequestTimeoutResponse(context, CHPP_ENDPOINT_SERVICE);
1088 
1089     if (timeoutResponse != NULL) {
1090       CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8,
1091                 timeoutResponse->handle, timeoutResponse->command,
1092                 timeoutResponse->transaction);
1093       chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse,
1094                                sizeof(struct ChppAppHeader));
1095     }
1096   }
1097 #endif  // CHPP_SERVICE_ENABLED
1098 }
1099 
1100 /**
1101  * Appends data from a buffer of length len to a link tx buffer, updating its
1102  * length.
1103  *
1104  * @param context State of the transport layer.
1105  * @param buf Input data to be copied from.
1106  * @param len Length of input data in bytes.
1107  */
chppAppendToPendingTxPacket(struct ChppTransportState * context,const uint8_t * buf,size_t len)1108 static void chppAppendToPendingTxPacket(struct ChppTransportState *context,
1109                                         const uint8_t *buf, size_t len) {
1110   uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
1111 
1112   size_t bufferSize = context->linkBufferSize;
1113 
1114   CHPP_ASSERT(bufferSize + len <=
1115               context->linkApi->getConfig(context->linkContext).txBufferLen);
1116   memcpy(&linkTxBuffer[bufferSize], buf, len);
1117   context->linkBufferSize += len;
1118 }
1119 
1120 /**
1121  * @return A human readable form of the packet attribution.
1122  */
chppGetPacketAttrStr(uint8_t packetCode)1123 static const char *chppGetPacketAttrStr(uint8_t packetCode) {
1124   switch (CHPP_TRANSPORT_GET_ATTR(packetCode)) {
1125     case CHPP_TRANSPORT_ATTR_RESET:
1126       return "(RESET)";
1127     case CHPP_TRANSPORT_ATTR_RESET_ACK:
1128       return "(RESET-ACK)";
1129     case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
1130       return "(LOOP-REQ)";
1131     case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
1132       return "(LOOP-RES)";
1133     default:
1134       return "";
1135   }
1136 }
1137 
1138 /**
1139  * Enqueues an outgoing datagram of a specified length. The payload must have
1140  * been allocated by the caller using chppMalloc.
1141  *
1142  * If enqueueing is successful, the payload will be freed by this function
1143  * once it has been sent out.
1144  * If enqueueing is unsuccessful, it is up to the caller to decide when or if
1145  * to free the payload and/or resend it later.
1146  *
1147  * @param context State of the transport layer.
1148  * @param packetCode Error code and packet attributes to be sent.
1149  * @param buf Datagram payload allocated through chppMalloc. Cannot be null.
1150  * @param len Datagram length in bytes.
1151  *
1152  * @return True informs the sender that the datagram was successfully enqueued.
1153  * False informs the sender that the queue was full.
1154  */
chppEnqueueTxDatagram(struct ChppTransportState * context,uint8_t packetCode,void * buf,size_t len)1155 static bool chppEnqueueTxDatagram(struct ChppTransportState *context,
1156                                   uint8_t packetCode, void *buf, size_t len) {
1157   bool success = false;
1158 
1159   if (len == 0) {
1160     CHPP_DEBUG_ASSERT_LOG(false, "Enqueue TX len=0!");
1161 
1162   } else {
1163     if ((len < sizeof(struct ChppAppHeader)) ||
1164         (CHPP_TRANSPORT_GET_ATTR(packetCode) != 0)) {
1165       CHPP_LOGD("Enqueue TX: code=0x%" PRIx8 "%s len=%" PRIuSIZE
1166                 " pending=%" PRIu8,
1167                 packetCode, chppGetPacketAttrStr(packetCode), len,
1168                 (uint8_t)(context->txDatagramQueue.pending + 1));
1169     } else {
1170       struct ChppAppHeader *header = buf;
1171       CHPP_LOGD(
1172           "Enqueue TX: len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
1173           " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16 " pending=%" PRIu8,
1174           len, header->handle, header->type, header->transaction, header->error,
1175           header->command, (uint8_t)(context->txDatagramQueue.pending + 1));
1176     }
1177 
1178     chppMutexLock(&context->mutex);
1179 
1180     if (context->txDatagramQueue.pending >= CHPP_TX_DATAGRAM_QUEUE_LEN) {
1181       CHPP_LOGE("Cannot enqueue TX datagram");
1182 
1183     } else {
1184       uint16_t end =
1185           (context->txDatagramQueue.front + context->txDatagramQueue.pending) %
1186           CHPP_TX_DATAGRAM_QUEUE_LEN;
1187       context->txDatagramQueue.datagram[end].length = len;
1188       context->txDatagramQueue.datagram[end].payload = buf;
1189       context->txDatagramQueue.pending++;
1190 
1191       if (context->txDatagramQueue.pending == 1) {
1192         // Queue was empty prior. Need to kickstart transmission.
1193         chppEnqueueTxPacket(context, packetCode);
1194       }
1195 
1196       success = true;
1197     }
1198 
1199     chppMutexUnlock(&context->mutex);
1200   }
1201 
1202   return success;
1203 }
1204 
1205 /**
1206  * Sends the pending outgoing packet over to the link
1207  * layer using Send() and updates the last Tx packet time.
1208  *
1209  * @param context State of the transport layer.
1210  *
1211  * @return Result of Send().
1212  */
chppSendPendingPacket(struct ChppTransportState * context)1213 static enum ChppLinkErrorCode chppSendPendingPacket(
1214     struct ChppTransportState *context) {
1215   enum ChppLinkErrorCode error =
1216       context->linkApi->send(context->linkContext, context->linkBufferSize);
1217 
1218   context->txStatus.lastTxTimeNs = chppGetCurrentTimeNs();
1219 
1220   return error;
1221 }
1222 
1223 /**
1224  * Resets the transport state, maintaining the link layer parameters.
1225  *
1226  * @param context State of the transport layer.
1227  */
chppResetTransportContext(struct ChppTransportState * context)1228 static void chppResetTransportContext(struct ChppTransportState *context) {
1229   memset(&context->rxStatus, 0, sizeof(struct ChppRxStatus));
1230   memset(&context->rxDatagram, 0, sizeof(struct ChppDatagram));
1231 
1232   memset(&context->txStatus, 0, sizeof(struct ChppTxStatus));
1233   memset(&context->txDatagramQueue, 0, sizeof(struct ChppTxDatagramQueue));
1234 
1235   context->txStatus.sentSeq =
1236       UINT8_MAX;  // So that the seq # of the first TX packet is 0
1237   context->resetState = CHPP_RESET_STATE_RESETTING;
1238 }
1239 
1240 /**
1241  * Re-initializes the CHPP transport and app layer states, e.g. when receiving a
1242  * reset packet, and sends out a reset or reset-ack packet over the link in
1243  * order to reset the remote side or inform the counterpart of a reset,
1244  * respectively.
1245  *
1246  * If the link layer is busy, this function will reset the link as well.
1247  * This function retains and restores the platform-specific values of
1248  * transportContext.linkContext.
1249  *
1250  * @param transportContext State of the transport layer.
1251  * @param resetType Type of reset to send after resetting CHPP (reset vs.
1252  * reset-ack), as defined in the ChppTransportPacketAttributes struct.
1253  * @param error Provides the error that led to the reset.
1254  */
chppReset(struct ChppTransportState * transportContext,enum ChppTransportPacketAttributes resetType,enum ChppTransportErrorCode error)1255 static void chppReset(struct ChppTransportState *transportContext,
1256                       enum ChppTransportPacketAttributes resetType,
1257                       enum ChppTransportErrorCode error) {
1258   // TODO: Configure transport layer based on (optional?) received config before
1259   // datagram is wiped
1260 
1261   chppMutexLock(&transportContext->mutex);
1262   struct ChppAppState *appContext = transportContext->appContext;
1263   transportContext->resetState = CHPP_RESET_STATE_RESETTING;
1264 
1265   // Reset asynchronous link layer if busy
1266   if (transportContext->txStatus.linkBusy == true) {
1267     // TODO: Give time for link layer to finish before resorting to a reset
1268 
1269     transportContext->linkApi->reset(transportContext->linkContext);
1270   }
1271 
1272   // Free memory allocated for any ongoing rx datagrams
1273   if (transportContext->rxDatagram.length > 0) {
1274     transportContext->rxDatagram.length = 0;
1275     CHPP_FREE_AND_NULLIFY(transportContext->rxDatagram.payload);
1276   }
1277 
1278   // Free memory allocated for any ongoing tx datagrams
1279   for (size_t i = 0; i < CHPP_TX_DATAGRAM_QUEUE_LEN; i++) {
1280     if (transportContext->txDatagramQueue.datagram[i].length > 0) {
1281       CHPP_FREE_AND_NULLIFY(
1282           transportContext->txDatagramQueue.datagram[i].payload);
1283     }
1284   }
1285 
1286   // Reset Transport Layer but restore Rx sequence number and packet code
1287   // (context->rxHeader is not wiped in reset)
1288   chppResetTransportContext(transportContext);
1289   transportContext->rxStatus.receivedPacketCode =
1290       transportContext->rxHeader.packetCode;
1291   transportContext->rxStatus.expectedSeq = transportContext->rxHeader.seq + 1;
1292 
1293   // Send reset or reset-ACK
1294   chppMutexUnlock(&transportContext->mutex);
1295   chppTransportSendReset(transportContext, resetType, error);
1296 
1297   // Inform the App Layer that a reset has completed
1298   if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1299     chppAppProcessReset(appContext);
1300   }  // else reset is sent out. Rx of reset-ack will indicate completion.
1301 }
1302 
1303 /**
1304  * Checks for a timed out request and generates a timeout response if a timeout
1305  * has occurred.
1306  *
1307  * @param context State of the transport layer.
1308  * @param type The type of the endpoint.
1309  * @return App layer response header if a timeout has occurred. Null otherwise.
1310  */
chppTransportGetRequestTimeoutResponse(struct ChppTransportState * context,enum ChppEndpointType type)1311 struct ChppAppHeader *chppTransportGetRequestTimeoutResponse(
1312     struct ChppTransportState *context, enum ChppEndpointType type) {
1313   CHPP_DEBUG_NOT_NULL(context);
1314 
1315   struct ChppAppState *appState = context->appContext;
1316   struct ChppAppHeader *response = NULL;
1317 
1318   bool timeoutEndpointFound = false;
1319   uint8_t timedOutEndpointIdx;
1320   uint16_t timedOutCmd;
1321 
1322   chppMutexLock(&context->mutex);
1323 
1324   if (*getNextRequestTimeoutNs(appState, type) <= chppGetCurrentTimeNs()) {
1325     // Determine which request has timed out
1326     const uint8_t endpointCount = getRegisteredEndpointCount(appState, type);
1327     uint64_t firstTimeout = CHPP_TIME_MAX;
1328 
1329     for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) {
1330       const uint16_t cmdCount =
1331           getRegisteredEndpointOutReqCount(appState, endpointIdx, type);
1332       const struct ChppEndpointState *endpointState =
1333           getRegisteredEndpointState(appState, endpointIdx, type);
1334       const struct ChppOutgoingRequestState *reqStates =
1335           &endpointState->outReqStates[0];
1336       for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) {
1337         const struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx];
1338 
1339         if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT &&
1340             reqState->responseTimeNs != CHPP_TIME_NONE &&
1341             reqState->responseTimeNs < firstTimeout) {
1342           firstTimeout = reqState->responseTimeNs;
1343           timedOutEndpointIdx = endpointIdx;
1344           timedOutCmd = cmdIdx;
1345           timeoutEndpointFound = true;
1346         }
1347       }
1348     }
1349 
1350     if (!timeoutEndpointFound) {
1351       CHPP_LOGE("Timeout at %" PRIu64 " but no endpoint",
1352                 *getNextRequestTimeoutNs(appState, type) / CHPP_NSEC_PER_MSEC);
1353       chppRecalculateNextTimeout(appState, CHPP_ENDPOINT_CLIENT);
1354     }
1355   }
1356 
1357   if (timeoutEndpointFound) {
1358     CHPP_LOGE("Endpoint=%" PRIu8 " cmd=%" PRIu16 " timed out",
1359               timedOutEndpointIdx, timedOutCmd);
1360     response = chppMalloc(sizeof(struct ChppAppHeader));
1361     if (response == NULL) {
1362       CHPP_LOG_OOM();
1363     } else {
1364       const struct ChppEndpointState *endpointState =
1365           getRegisteredEndpointState(appState, timedOutEndpointIdx, type);
1366       response->handle = endpointState->handle;
1367       response->type = type == CHPP_ENDPOINT_CLIENT
1368                            ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
1369                            : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
1370       response->transaction =
1371           endpointState->outReqStates[timedOutCmd].transaction;
1372       response->error = CHPP_APP_ERROR_TIMEOUT;
1373       response->command = timedOutCmd;
1374     }
1375   }
1376 
1377   chppMutexUnlock(&context->mutex);
1378 
1379   return response;
1380 }
1381 
1382 /************************************************
1383  *  Public Functions
1384  ***********************************************/
1385 
chppTransportInit(struct ChppTransportState * transportContext,struct ChppAppState * appContext,void * linkContext,const struct ChppLinkApi * linkApi)1386 void chppTransportInit(struct ChppTransportState *transportContext,
1387                        struct ChppAppState *appContext, void *linkContext,
1388                        const struct ChppLinkApi *linkApi) {
1389   CHPP_NOT_NULL(transportContext);
1390   CHPP_NOT_NULL(appContext);
1391 
1392   CHPP_ASSERT_LOG(!transportContext->initialized,
1393                   "CHPP transport already init");
1394   CHPP_LOGD("Initializing CHPP transport");
1395 
1396   chppResetTransportContext(transportContext);
1397   chppMutexInit(&transportContext->mutex);
1398   chppNotifierInit(&transportContext->notifier);
1399   chppConditionVariableInit(&transportContext->resetCondVar);
1400 #ifdef CHPP_ENABLE_WORK_MONITOR
1401   chppWorkMonitorInit(&transportContext->workMonitor);
1402 #endif
1403 
1404   transportContext->appContext = appContext;
1405   transportContext->initialized = true;
1406 
1407   CHPP_NOT_NULL(linkApi);
1408   CHPP_DEBUG_NOT_NULL(linkApi->init);
1409   CHPP_DEBUG_NOT_NULL(linkApi->deinit);
1410   CHPP_DEBUG_NOT_NULL(linkApi->send);
1411   CHPP_DEBUG_NOT_NULL(linkApi->doWork);
1412   CHPP_DEBUG_NOT_NULL(linkApi->reset);
1413   CHPP_DEBUG_NOT_NULL(linkApi->getConfig);
1414   CHPP_DEBUG_NOT_NULL(linkApi->getTxBuffer);
1415   transportContext->linkApi = linkApi;
1416 
1417   CHPP_NOT_NULL(linkContext);
1418   linkApi->init(linkContext, transportContext);
1419   transportContext->linkContext = linkContext;
1420 
1421 #ifdef CHPP_DEBUG_ASSERT_ENABLED
1422   const struct ChppLinkConfiguration linkConfig =
1423       linkApi->getConfig(linkContext);
1424   CHPP_ASSERT_LOG(
1425       linkConfig.txBufferLen > CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES,
1426       "The link TX buffer is too small");
1427   CHPP_ASSERT_LOG(
1428       linkConfig.rxBufferLen > CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES,
1429       "The link RX buffer is too small");
1430 #endif  // CHPP_DEBUG_ASSERT_ENABLED
1431 }
1432 
chppTransportDeinit(struct ChppTransportState * transportContext)1433 void chppTransportDeinit(struct ChppTransportState *transportContext) {
1434   CHPP_NOT_NULL(transportContext);
1435   CHPP_ASSERT_LOG(transportContext->initialized,
1436                   "CHPP transport already deinitialized");
1437 
1438   transportContext->linkApi->deinit(transportContext->linkContext);
1439 #ifdef CHPP_ENABLE_WORK_MONITOR
1440   chppWorkMonitorDeinit(&transportContext->workMonitor);
1441 #endif
1442   chppConditionVariableDeinit(&transportContext->resetCondVar);
1443   chppNotifierDeinit(&transportContext->notifier);
1444   chppMutexDeinit(&transportContext->mutex);
1445 
1446   chppClearTxDatagramQueue(transportContext);
1447 
1448   CHPP_FREE_AND_NULLIFY(transportContext->rxDatagram.payload);
1449 
1450   transportContext->initialized = false;
1451 }
1452 
chppTransportWaitForResetComplete(struct ChppTransportState * transportContext,uint64_t timeoutMs)1453 bool chppTransportWaitForResetComplete(
1454     struct ChppTransportState *transportContext, uint64_t timeoutMs) {
1455   bool success = true;
1456   chppMutexLock(&transportContext->mutex);
1457   while (success && transportContext->resetState != CHPP_RESET_STATE_NONE) {
1458     success = chppConditionVariableTimedWait(&transportContext->resetCondVar,
1459                                              &transportContext->mutex,
1460                                              timeoutMs * CHPP_NSEC_PER_MSEC);
1461   }
1462   chppMutexUnlock(&transportContext->mutex);
1463   return success;
1464 }
1465 
chppRxDataCb(struct ChppTransportState * context,const uint8_t * buf,size_t len)1466 bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
1467                   size_t len) {
1468   CHPP_NOT_NULL(buf);
1469   CHPP_NOT_NULL(context);
1470 
1471   chppMutexLock(&context->mutex);
1472   if (context->rxStatus.state != CHPP_STATE_PREAMBLE &&
1473       chppGetCurrentTimeNs() >
1474           context->rxStatus.packetStartTimeNs + CHPP_TRANSPORT_RX_TIMEOUT_NS) {
1475     CHPP_LOGE("Packet RX timeout");
1476     chppAbortRxPacket(context);
1477   }
1478   chppMutexUnlock(&context->mutex);
1479 
1480   CHPP_LOGD("RX %" PRIuSIZE " bytes: state=%s", len,
1481             chppGetRxStatusLabel(context->rxStatus.state));
1482   uint64_t now = chppGetCurrentTimeNs();
1483   context->rxStatus.lastDataTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC);
1484   context->rxStatus.numTotalDataBytes += len;
1485 
1486   size_t consumed = 0;
1487   while (consumed < len) {
1488     chppMutexLock(&context->mutex);
1489     // TODO: Investigate fine-grained locking, e.g. separating variables that
1490     // are only relevant to a particular path.
1491     // Also consider removing some of the finer-grained locks altogether for
1492     // non-multithreaded environments with clear documentation.
1493 
1494     switch (context->rxStatus.state) {
1495       case CHPP_STATE_PREAMBLE:
1496         consumed +=
1497             chppConsumePreamble(context, &buf[consumed], len - consumed);
1498         break;
1499 
1500       case CHPP_STATE_HEADER:
1501         consumed += chppConsumeHeader(context, &buf[consumed], len - consumed);
1502         break;
1503 
1504       case CHPP_STATE_PAYLOAD:
1505         consumed += chppConsumePayload(context, &buf[consumed], len - consumed);
1506         break;
1507 
1508       case CHPP_STATE_FOOTER:
1509         consumed += chppConsumeFooter(context, &buf[consumed], len - consumed);
1510         break;
1511 
1512       default:
1513         CHPP_DEBUG_ASSERT_LOG(false, "Invalid RX state %" PRIu8,
1514                               context->rxStatus.state);
1515         chppSetRxState(context, CHPP_STATE_PREAMBLE);
1516     }
1517 
1518     chppMutexUnlock(&context->mutex);
1519   }
1520 
1521   return (context->rxStatus.state == CHPP_STATE_PREAMBLE &&
1522           context->rxStatus.locInState == 0);
1523 }
1524 
chppRxPacketCompleteCb(struct ChppTransportState * context)1525 void chppRxPacketCompleteCb(struct ChppTransportState *context) {
1526   chppMutexLock(&context->mutex);
1527   if (context->rxStatus.state != CHPP_STATE_PREAMBLE) {
1528     CHPP_LOGE("RX pkt ended early: state=%s seq=%" PRIu8 " len=%" PRIu16,
1529               chppGetRxStatusLabel(context->rxStatus.state),
1530               context->rxHeader.seq, context->rxHeader.length);
1531     chppAbortRxPacket(context);
1532     chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_HEADER);  // NACK
1533   }
1534   chppMutexUnlock(&context->mutex);
1535 }
1536 
chppEnqueueTxDatagramOrFail(struct ChppTransportState * context,void * buf,size_t len)1537 bool chppEnqueueTxDatagramOrFail(struct ChppTransportState *context, void *buf,
1538                                  size_t len) {
1539   bool success = false;
1540   bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
1541 
1542   if (len == 0) {
1543     CHPP_DEBUG_ASSERT_LOG(false, "Enqueue datagram len=0!");
1544 
1545   } else if (resetting || !chppEnqueueTxDatagram(
1546                               context, CHPP_TRANSPORT_ERROR_NONE, buf, len)) {
1547     uint8_t *handle = buf;
1548     CHPP_LOGE("Resetting=%d. Discarding %" PRIuSIZE " bytes for H#%" PRIu8,
1549               resetting, len, *handle);
1550 
1551     CHPP_FREE_AND_NULLIFY(buf);
1552 
1553   } else {
1554     success = true;
1555   }
1556 
1557   return success;
1558 }
1559 
1560 // TODO(b/192359485): Consider removing this function, or making it more robust.
chppEnqueueTxErrorDatagram(struct ChppTransportState * context,enum ChppTransportErrorCode errorCode)1561 void chppEnqueueTxErrorDatagram(struct ChppTransportState *context,
1562                                 enum ChppTransportErrorCode errorCode) {
1563   bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
1564   if (resetting) {
1565     CHPP_LOGE("Discarding app error 0x%" PRIx8 " (resetting)", errorCode);
1566   } else {
1567     switch (errorCode) {
1568       case CHPP_TRANSPORT_ERROR_OOM: {
1569         CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_OOM");
1570         break;
1571       }
1572       case CHPP_TRANSPORT_ERROR_APPLAYER: {
1573         CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_APPLAYER");
1574         break;
1575       }
1576       default: {
1577         // App layer should not invoke any other errors
1578         CHPP_DEBUG_ASSERT_LOG(false, "App enqueueing invalid err=%" PRIu8,
1579                               errorCode);
1580       }
1581     }
1582     chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
1583                                      CHPP_TRANSPORT_ATTR_NONE, errorCode));
1584   }
1585 }
1586 
chppTransportGetTimeUntilNextDoWorkNs(struct ChppTransportState * context)1587 uint64_t chppTransportGetTimeUntilNextDoWorkNs(
1588     struct ChppTransportState *context) {
1589   uint64_t currentTime = chppGetCurrentTimeNs();
1590   // This function is called in the context of the transport worker thread.
1591   // As we do not know if the transport is used in the context of a service
1592   // or a client, we use the min of both timeouts.
1593   uint64_t nextDoWorkTime =
1594       MIN(context->appContext->nextClientRequestTimeoutNs,
1595           context->appContext->nextServiceRequestTimeoutNs);
1596 
1597   if (context->txStatus.hasPacketsToSend ||
1598       context->resetState == CHPP_RESET_STATE_RESETTING) {
1599     nextDoWorkTime =
1600         MIN(nextDoWorkTime, CHPP_TRANSPORT_TX_TIMEOUT_NS +
1601                                 ((context->txStatus.lastTxTimeNs == 0)
1602                                      ? currentTime
1603                                      : context->txStatus.lastTxTimeNs));
1604   }
1605 
1606   if (nextDoWorkTime == CHPP_TIME_MAX) {
1607     CHPP_LOGD("NextDoWork=n/a currentTime=%" PRIu64,
1608               currentTime / CHPP_NSEC_PER_MSEC);
1609     return CHPP_TRANSPORT_TIMEOUT_INFINITE;
1610   }
1611 
1612   CHPP_LOGD("NextDoWork=%" PRIu64 " currentTime=%" PRIu64 " delta=%" PRId64,
1613             nextDoWorkTime / CHPP_NSEC_PER_MSEC,
1614             currentTime / CHPP_NSEC_PER_MSEC,
1615             (nextDoWorkTime > currentTime ? nextDoWorkTime - currentTime : 0) /
1616                 (int64_t)CHPP_NSEC_PER_MSEC);
1617 
1618   return nextDoWorkTime <= currentTime ? CHPP_TRANSPORT_TIMEOUT_IMMEDIATE
1619                                        : nextDoWorkTime - currentTime;
1620 }
1621 
chppWorkThreadStart(struct ChppTransportState * context)1622 void chppWorkThreadStart(struct ChppTransportState *context) {
1623   chppTransportSendReset(context, CHPP_TRANSPORT_ATTR_RESET,
1624                          CHPP_TRANSPORT_ERROR_NONE);
1625   CHPP_LOGD("CHPP Work Thread started");
1626 
1627   uint32_t signals;
1628   do {
1629     uint64_t timeout = chppTransportGetTimeUntilNextDoWorkNs(context);
1630     if (timeout == CHPP_TRANSPORT_TIMEOUT_IMMEDIATE) {
1631       signals = chppNotifierGetSignal(&context->notifier);
1632     } else if (timeout == CHPP_TRANSPORT_TIMEOUT_INFINITE) {
1633       signals = chppNotifierWait(&context->notifier);
1634     } else {
1635       signals = chppNotifierTimedWait(&context->notifier, timeout);
1636     }
1637 
1638   } while (chppWorkThreadHandleSignal(context, signals));
1639 }
1640 
chppWorkThreadHandleSignal(struct ChppTransportState * context,uint32_t signals)1641 bool chppWorkThreadHandleSignal(struct ChppTransportState *context,
1642                                 uint32_t signals) {
1643   bool continueProcessing = false;
1644 
1645 #ifdef CHPP_ENABLE_WORK_MONITOR
1646   chppWorkMonitorPreProcess(&context->workMonitor);
1647 #endif
1648 
1649   if (signals & CHPP_TRANSPORT_SIGNAL_EXIT) {
1650     CHPP_LOGD("CHPP Work Thread terminated");
1651   } else {
1652     continueProcessing = true;
1653     if (signals == 0) {
1654       // Triggered by timeout.
1655       chppWorkHandleTimeout(context);
1656     } else {
1657       if (signals & CHPP_TRANSPORT_SIGNAL_EVENT) {
1658         chppTransportDoWork(context);
1659       }
1660       if (signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK) {
1661         context->linkApi->doWork(context->linkContext,
1662                                  signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK);
1663       }
1664     }
1665   }
1666 
1667 #ifdef CHPP_ENABLE_WORK_MONITOR
1668   chppWorkMonitorPostProcess(&context->workMonitor);
1669 #endif
1670 
1671   return continueProcessing;
1672 }
1673 
1674 /**
1675  * Handle timeouts in the worker thread.
1676  *
1677  * Timeouts occurs when either:
1678  * 1. There are packets to send and last packet send was more than
1679  *    CHPP_TRANSPORT_TX_TIMEOUT_NS ago
1680  * 2. We haven't received a response to a request in time
1681  * 3. We haven't received the reset ACK
1682  *
1683  * For 1 and 2, chppTransportDoWork should be called to respectively
1684  * - Transmit the packet
1685  * - Send a timeout response
1686  */
chppWorkHandleTimeout(struct ChppTransportState * context)1687 static void chppWorkHandleTimeout(struct ChppTransportState *context) {
1688   const uint64_t currentTimeNs = chppGetCurrentTimeNs();
1689   const bool isTxTimeout = currentTimeNs - context->txStatus.lastTxTimeNs >=
1690                            CHPP_TRANSPORT_TX_TIMEOUT_NS;
1691 
1692   // Call chppTransportDoWork for both TX and request timeouts.
1693   if (isTxTimeout) {
1694     CHPP_LOGE("ACK timeout. Tx t=%" PRIu64,
1695               context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC);
1696     chppTransportDoWork(context);
1697   } else {
1698     const uint64_t requestTimeoutNs =
1699         MIN(context->appContext->nextClientRequestTimeoutNs,
1700             context->appContext->nextServiceRequestTimeoutNs);
1701     const bool isRequestTimeout = requestTimeoutNs <= currentTimeNs;
1702     if (isRequestTimeout) {
1703       chppTransportDoWork(context);
1704     }
1705   }
1706 
1707   if ((context->resetState == CHPP_RESET_STATE_RESETTING) &&
1708       (currentTimeNs - context->resetTimeNs >=
1709        CHPP_TRANSPORT_RESET_TIMEOUT_NS)) {
1710     if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) {
1711       CHPP_LOGE("RESET-ACK timeout; retrying");
1712       context->resetCount++;
1713       chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
1714                 CHPP_TRANSPORT_ERROR_TIMEOUT);
1715     } else {
1716       CHPP_LOGE("RESET-ACK timeout; giving up");
1717       context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE;
1718       chppClearTxDatagramQueue(context);
1719     }
1720   }
1721 }
1722 
chppWorkThreadStop(struct ChppTransportState * context)1723 void chppWorkThreadStop(struct ChppTransportState *context) {
1724   chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EXIT);
1725 }
1726 
chppLinkSendDoneCb(struct ChppTransportState * context,enum ChppLinkErrorCode error)1727 void chppLinkSendDoneCb(struct ChppTransportState *context,
1728                         enum ChppLinkErrorCode error) {
1729   if (error != CHPP_LINK_ERROR_NONE_SENT) {
1730     CHPP_LOGE("Async send failure: %" PRIu8, error);
1731   }
1732 
1733   chppMutexLock(&context->mutex);
1734 
1735   context->txStatus.linkBusy = false;
1736 
1737   // No need to free anything as link Tx buffer is static. Likewise, we
1738   // keep linkBufferSize to assist testing.
1739 
1740   chppMutexUnlock(&context->mutex);
1741 }
1742 
chppDatagramProcessDoneCb(struct ChppTransportState * context,uint8_t * buf)1743 void chppDatagramProcessDoneCb(struct ChppTransportState *context,
1744                                uint8_t *buf) {
1745   UNUSED_VAR(context);
1746 
1747   CHPP_FREE_AND_NULLIFY(buf);
1748 }
1749 
chppRunTransportLoopback(struct ChppTransportState * context,uint8_t * buf,size_t len)1750 uint8_t chppRunTransportLoopback(struct ChppTransportState *context,
1751                                  uint8_t *buf, size_t len) {
1752   UNUSED_VAR(buf);
1753   UNUSED_VAR(len);
1754   uint8_t result = CHPP_APP_ERROR_UNSUPPORTED;
1755   context->loopbackResult = result;
1756 
1757 #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
1758   result = CHPP_APP_ERROR_NONE;
1759   context->loopbackResult = CHPP_APP_ERROR_UNSPECIFIED;
1760 
1761   if (len == 0 || len > chppTransportTxMtuSize(context)) {
1762     result = CHPP_APP_ERROR_INVALID_LENGTH;
1763     context->loopbackResult = result;
1764 
1765   } else if (context->txStatus.linkBusy) {
1766     result = CHPP_APP_ERROR_BLOCKED;
1767     context->loopbackResult = result;
1768 
1769   } else if (context->transportLoopbackData.payload != NULL) {
1770     result = CHPP_APP_ERROR_BUSY;
1771     context->loopbackResult = result;
1772 
1773   } else if ((context->transportLoopbackData.payload = chppMalloc(len)) ==
1774              NULL) {
1775     result = CHPP_APP_ERROR_OOM;
1776     context->loopbackResult = result;
1777 
1778   } else {
1779     uint8_t *linkTxBuffer = context->linkApi->getTxBuffer(context->linkContext);
1780     context->transportLoopbackData.length = len;
1781     memcpy(context->transportLoopbackData.payload, buf, len);
1782 
1783     context->txStatus.linkBusy = true;
1784     context->linkBufferSize = 0;
1785     const struct ChppLinkConfiguration linkConfig =
1786         context->linkApi->getConfig(context->linkContext);
1787     memset(linkTxBuffer, 0, linkConfig.txBufferLen);
1788     context->linkBufferSize += chppAddPreamble(linkTxBuffer);
1789 
1790     struct ChppTransportHeader *txHeader =
1791         (struct ChppTransportHeader *)&linkTxBuffer[context->linkBufferSize];
1792     context->linkBufferSize += sizeof(*txHeader);
1793 
1794     txHeader->packetCode = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
1795         CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST, txHeader->packetCode);
1796 
1797     size_t payloadLen = MIN(len, chppTransportTxMtuSize(context));
1798     txHeader->length = (uint16_t)payloadLen;
1799     chppAppendToPendingTxPacket(context, buf, payloadLen);
1800 
1801     chppAddFooter(context);
1802 
1803     CHPP_LOGD("Sending transport-loopback request (packet len=%" PRIuSIZE
1804               ", payload len=%" PRIu16 ", asked len was %" PRIuSIZE ")",
1805               context->linkBufferSize, txHeader->length, len);
1806     enum ChppLinkErrorCode error = chppSendPendingPacket(context);
1807 
1808     if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
1809       // Either sent synchronously or an error has occurred
1810       chppLinkSendDoneCb(context, error);
1811 
1812       if (error != CHPP_LINK_ERROR_NONE_SENT) {
1813         // An error has occurred
1814         CHPP_FREE_AND_NULLIFY(context->transportLoopbackData.payload);
1815         context->transportLoopbackData.length = 0;
1816         result = CHPP_APP_ERROR_UNSPECIFIED;
1817       }
1818     }
1819   }
1820 
1821   if (result != CHPP_APP_ERROR_NONE) {
1822     CHPP_LOGE("Trans-loopback failure: %" PRIu8, result);
1823   }
1824 #endif
1825   return result;
1826 }
1827 
chppTransportSendReset(struct ChppTransportState * context,enum ChppTransportPacketAttributes resetType,enum ChppTransportErrorCode error)1828 void chppTransportSendReset(struct ChppTransportState *context,
1829                             enum ChppTransportPacketAttributes resetType,
1830                             enum ChppTransportErrorCode error) {
1831   // Make sure CHPP is in an initialized state
1832   CHPP_ASSERT_LOG((context->txDatagramQueue.pending == 0 &&
1833                    context->txDatagramQueue.front == 0),
1834                   "Not init to send reset");
1835 
1836   struct ChppTransportConfiguration *config =
1837       chppMalloc(sizeof(struct ChppTransportConfiguration));
1838   if (config == NULL) {
1839     CHPP_LOG_OOM();
1840   } else {
1841     // CHPP transport version
1842     config->version.major = 1;
1843     config->version.minor = 0;
1844     config->version.patch = 0;
1845 
1846     config->reserved1 = 0;
1847     config->reserved2 = 0;
1848     config->reserved3 = 0;
1849 
1850     if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1851       CHPP_LOGD("Sending RESET-ACK");
1852       chppSetResetComplete(context);
1853     } else {
1854       CHPP_LOGD("Sending RESET");
1855     }
1856 
1857     context->resetTimeNs = chppGetCurrentTimeNs();
1858 
1859     chppEnqueueTxDatagram(context,
1860                           CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(resetType, error),
1861                           config, sizeof(*config));
1862   }
1863 }
1864 
chppTransportTxMtuSize(const struct ChppTransportState * context)1865 size_t chppTransportTxMtuSize(const struct ChppTransportState *context) {
1866   const struct ChppLinkConfiguration linkConfig =
1867       context->linkApi->getConfig(context->linkContext);
1868 
1869   return linkConfig.txBufferLen - CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES;
1870 }
1871 
chppTransportRxMtuSize(const struct ChppTransportState * context)1872 size_t chppTransportRxMtuSize(const struct ChppTransportState *context) {
1873   const struct ChppLinkConfiguration linkConfig =
1874       context->linkApi->getConfig(context->linkContext);
1875 
1876   return linkConfig.rxBufferLen - CHPP_TRANSPORT_ENCODING_OVERHEAD_BYTES;
1877 }