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