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