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