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