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