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