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