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; transport-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 #ifndef CHPP_CHECKSUM_ENABLED
684 CHPP_LOGD("Assuming Rx checksum 0x%" PRIx32 " = calculated 0x%" PRIx32,
685 context->rxFooter.checksum, crc);
686 crc = context->rxFooter.checksum;
687 #endif // CHPP_CHECKSUM_ENABLED
688
689 if (context->rxFooter.checksum != crc) {
690 CHPP_LOGE("Rx BAD checksum: footer=0x%" PRIx32 ", calc=0x%" PRIx32
691 ", len=%" PRIuSIZE,
692 context->rxFooter.checksum, crc,
693 (size_t)(context->rxHeader.length +
694 sizeof(struct ChppTransportHeader)));
695 }
696
697 return (context->rxFooter.checksum == crc);
698 }
699
700 /**
701 * Performs consistency checks on received packet header to determine if it is
702 * obviously corrupt / invalid / duplicate / out-of-order.
703 *
704 * @param context Maintains status for each transport layer instance.
705 *
706 * @return True if and only if header passes checks
707 */
chppRxHeaderCheck(const struct ChppTransportState * context)708 static enum ChppTransportErrorCode chppRxHeaderCheck(
709 const struct ChppTransportState *context) {
710 enum ChppTransportErrorCode result = CHPP_TRANSPORT_ERROR_NONE;
711
712 if (context->rxHeader.length > CHPP_TRANSPORT_RX_MTU_BYTES) {
713 result = CHPP_TRANSPORT_ERROR_HEADER;
714 }
715
716 if (result != CHPP_TRANSPORT_ERROR_NONE) {
717 CHPP_LOGE("Bad header. seq=%" PRIu8 " expect=%" PRIu8 " len=%" PRIu16
718 " err=%" PRIu8,
719 context->rxHeader.seq, context->rxStatus.expectedSeq,
720 context->rxHeader.length, result);
721 }
722
723 return result;
724 }
725
726 /**
727 * Registers a received ACK. If an outgoing datagram is fully ACKed, it is
728 * popped from the Tx queue.
729 *
730 * @param context Maintains status for each transport layer instance.
731 */
chppRegisterRxAck(struct ChppTransportState * context)732 static void chppRegisterRxAck(struct ChppTransportState *context) {
733 uint8_t rxAckSeq = context->rxHeader.ackSeq;
734
735 if (context->rxStatus.receivedAckSeq != rxAckSeq) {
736 // A previously sent packet was actually ACKed
737 // Note: For a future ACK window >1, we should loop by # of ACKed packets
738 if ((uint8_t)(context->rxStatus.receivedAckSeq + 1) != rxAckSeq) {
739 CHPP_LOGE("Out of order ACK: last=%" PRIu8 " rx=%" PRIu8,
740 context->rxStatus.receivedAckSeq, rxAckSeq);
741 } else {
742 CHPP_LOGD(
743 "ACK received (last registered=%" PRIu8 ", received=%" PRIu8
744 "). Prior queue depth=%" PRIu8 ", front datagram=%" PRIu8
745 " at loc=%" PRIuSIZE " of len=%" PRIuSIZE,
746 context->rxStatus.receivedAckSeq, rxAckSeq,
747 context->txDatagramQueue.pending, context->txDatagramQueue.front,
748 context->txStatus.ackedLocInDatagram,
749 context->txDatagramQueue.datagram[context->txDatagramQueue.front]
750 .length);
751
752 context->rxStatus.receivedAckSeq = rxAckSeq;
753 if (context->txStatus.txAttempts > 1) {
754 CHPP_LOGW("Seq %" PRIu8 " ACK'd after %" PRIuSIZE " reTX",
755 context->rxHeader.seq, context->txStatus.txAttempts - 1);
756 }
757 context->txStatus.txAttempts = 0;
758
759 // Process and if necessary pop from Tx datagram queue
760 context->txStatus.ackedLocInDatagram += CHPP_TRANSPORT_TX_MTU_BYTES;
761 if (context->txStatus.ackedLocInDatagram >=
762 context->txDatagramQueue.datagram[context->txDatagramQueue.front]
763 .length) {
764 // We are done with datagram
765
766 context->txStatus.ackedLocInDatagram = 0;
767 context->txStatus.sentLocInDatagram = 0;
768
769 // Note: For a future ACK window >1, we need to update the queue
770 // position of the datagram being sent as well (relative to the
771 // front-of-queue). e.g. context->txStatus.datagramBeingSent--;
772
773 if (chppDequeueTxDatagram(context) == 0) {
774 context->txStatus.hasPacketsToSend = false;
775 }
776 }
777 }
778 } // else {nothing was ACKed}
779 }
780
781 /**
782 * Enqueues an outgoing packet with the specified error code. The error code
783 * refers to the optional reason behind a NACK, if any. An error code of
784 * CHPP_TRANSPORT_ERROR_NONE indicates that no error was reported (i.e. either
785 * an ACK or an implicit NACK)
786 *
787 * Note that the decision as to wheather to include a payload will be taken
788 * later, i.e. before the packet is being sent out from the queue. A payload is
789 * expected to be included if there is one or more pending Tx datagrams and we
790 * are not waiting on a pending ACK. A (repeat) payload is also included if we
791 * have received a NACK.
792 *
793 * Further note that even for systems with an ACK window greater than one, we
794 * would only need to send an ACK for the last (correct) packet, hence we only
795 * need a queue length of one here.
796 *
797 * @param context Maintains status for each transport layer instance.
798 * @param packetCode Error code and packet attributes to be sent.
799 */
chppEnqueueTxPacket(struct ChppTransportState * context,uint8_t packetCode)800 static void chppEnqueueTxPacket(struct ChppTransportState *context,
801 uint8_t packetCode) {
802 context->txStatus.hasPacketsToSend = true;
803 context->txStatus.packetCodeToSend = packetCode;
804
805 CHPP_LOGD("chppEnqueueTxPacket called with packet code=0x%" PRIx8,
806 packetCode);
807
808 // Notifies the main CHPP Transport Layer to run chppTransportDoWork().
809 chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EVENT);
810 }
811
812 /**
813 * Adds a CHPP preamble to the beginning of buf.
814 *
815 * @param buf The CHPP preamble will be added to buf.
816 *
817 * @return Size of the added preamble.
818 */
chppAddPreamble(uint8_t * buf)819 static size_t chppAddPreamble(uint8_t *buf) {
820 buf[0] = CHPP_PREAMBLE_BYTE_FIRST;
821 buf[1] = CHPP_PREAMBLE_BYTE_SECOND;
822 return CHPP_PREAMBLE_LEN_BYTES;
823 }
824
825 /**
826 * Adds the packet header to pendingTxPacket.
827 *
828 * @param context Maintains status for each transport layer instance.
829 *
830 * @return Pointer to the added packet header.
831 */
chppAddHeader(struct ChppTransportState * context)832 static struct ChppTransportHeader *chppAddHeader(
833 struct ChppTransportState *context) {
834 struct ChppTransportHeader *txHeader =
835 (struct ChppTransportHeader *)&context->pendingTxPacket
836 .payload[context->pendingTxPacket.length];
837 context->pendingTxPacket.length += sizeof(*txHeader);
838
839 txHeader->packetCode = context->txStatus.packetCodeToSend;
840 context->txStatus.packetCodeToSend = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
841 context->txStatus.packetCodeToSend, CHPP_TRANSPORT_ERROR_NONE);
842
843 txHeader->ackSeq = context->rxStatus.expectedSeq;
844 context->txStatus.sentAckSeq = txHeader->ackSeq;
845
846 return txHeader;
847 }
848
849 /**
850 * Adds the packet payload to pendingTxPacket.
851 *
852 * @param context Maintains status for each transport layer instance.
853 */
chppAddPayload(struct ChppTransportState * context)854 static void chppAddPayload(struct ChppTransportState *context) {
855 struct ChppTransportHeader *txHeader =
856 (struct ChppTransportHeader *)&context->pendingTxPacket
857 .payload[CHPP_PREAMBLE_LEN_BYTES];
858
859 size_t remainingBytes =
860 context->txDatagramQueue.datagram[context->txDatagramQueue.front].length -
861 context->txStatus.ackedLocInDatagram;
862
863 CHPP_LOGD("Adding payload to seq=%" PRIu8 ", remainingBytes=%" PRIuSIZE
864 " of pending datagrams=%" PRIu8,
865 txHeader->seq, remainingBytes, context->txDatagramQueue.pending);
866
867 if (remainingBytes > CHPP_TRANSPORT_TX_MTU_BYTES) {
868 // Send an unfinished part of a datagram
869 txHeader->flags = CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM;
870 txHeader->length = CHPP_TRANSPORT_TX_MTU_BYTES;
871 } else {
872 // Send final (or only) part of a datagram
873 txHeader->flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM;
874 txHeader->length = (uint16_t)remainingBytes;
875 }
876
877 // Copy payload
878 chppAppendToPendingTxPacket(
879 &context->pendingTxPacket,
880 context->txDatagramQueue.datagram[context->txDatagramQueue.front]
881 .payload +
882 context->txStatus.ackedLocInDatagram,
883 txHeader->length);
884
885 context->txStatus.sentLocInDatagram =
886 context->txStatus.ackedLocInDatagram + txHeader->length;
887 }
888
889 /**
890 * Adds a footer (containing the checksum) to a packet.
891 *
892 * @param packet The packet from which to calculate the checksum and append the
893 * footer.
894 */
chppAddFooter(struct PendingTxPacket * packet)895 static void chppAddFooter(struct PendingTxPacket *packet) {
896 struct ChppTransportFooter footer;
897 footer.checksum = chppCrc32(0, &packet->payload[CHPP_PREAMBLE_LEN_BYTES],
898 packet->length - CHPP_PREAMBLE_LEN_BYTES);
899
900 CHPP_LOGD("Adding transport footer. Checksum=0x%" PRIx32 ", len: %" PRIuSIZE
901 " -> %" PRIuSIZE,
902 footer.checksum, packet->length, packet->length + sizeof(footer));
903
904 chppAppendToPendingTxPacket(packet, (const uint8_t *)&footer, sizeof(footer));
905 }
906
907 /**
908 * Dequeues the datagram at the front of the datagram tx queue, if any, and
909 * frees the payload. Returns the number of remaining datagrams in the queue.
910 *
911 * @param context Maintains status for each transport layer instance.
912 * @return Number of remaining datagrams in queue.
913 */
chppDequeueTxDatagram(struct ChppTransportState * context)914 size_t chppDequeueTxDatagram(struct ChppTransportState *context) {
915 if (context->txDatagramQueue.pending == 0) {
916 CHPP_LOGE("Can not dequeue datagram because queue is empty");
917
918 } else {
919 CHPP_LOGD("Dequeuing front datagram with index=%" PRIu8 ", len=%" PRIuSIZE
920 ". Queue depth: %" PRIu8 "->%d",
921 context->txDatagramQueue.front,
922 context->txDatagramQueue.datagram[context->txDatagramQueue.front]
923 .length,
924 context->txDatagramQueue.pending,
925 context->txDatagramQueue.pending - 1);
926
927 CHPP_FREE_AND_NULLIFY(
928 context->txDatagramQueue.datagram[context->txDatagramQueue.front]
929 .payload);
930 context->txDatagramQueue.datagram[context->txDatagramQueue.front].length =
931 0;
932
933 context->txDatagramQueue.pending--;
934 context->txDatagramQueue.front++;
935 context->txDatagramQueue.front %= CHPP_TX_DATAGRAM_QUEUE_LEN;
936 }
937
938 return context->txDatagramQueue.pending;
939 }
940
941 /**
942 * Flushes the Tx datagram queue of any pending packets.
943 *
944 * @param context Maintains status for each transport layer instance.
945 */
chppClearTxDatagramQueue(struct ChppTransportState * context)946 static void chppClearTxDatagramQueue(struct ChppTransportState *context) {
947 while (context->txDatagramQueue.pending > 0) {
948 chppDequeueTxDatagram(context);
949 }
950 context->txStatus.hasPacketsToSend = false;
951 }
952
953 /**
954 * Sends out a pending outgoing packet based on a notification from
955 * chppEnqueueTxPacket().
956 *
957 * A payload may or may not be included be according the following:
958 * No payload: If Tx datagram queue is empty OR we are waiting on a pending ACK.
959 * New payload: If there is one or more pending Tx datagrams and we are not
960 * waiting on a pending ACK.
961 * Repeat payload: If we haven't received an ACK yet for our previous payload,
962 * i.e. we have registered an explicit or implicit NACK.
963 *
964 * @param context Maintains status for each transport layer instance.
965 */
chppTransportDoWork(struct ChppTransportState * context)966 static void chppTransportDoWork(struct ChppTransportState *context) {
967 bool havePacketForLinkLayer = false;
968 struct ChppTransportHeader *txHeader;
969 struct ChppAppHeader *timeoutResponse = NULL;
970
971 // Note: For a future ACK window >1, there needs to be a loop outside the lock
972 chppMutexLock(&context->mutex);
973
974 if (context->txStatus.hasPacketsToSend && !context->txStatus.linkBusy) {
975 // There are pending outgoing packets and the link isn't busy
976 havePacketForLinkLayer = true;
977 context->txStatus.linkBusy = true;
978
979 context->pendingTxPacket.length = 0;
980 memset(&context->pendingTxPacket.payload, 0, CHPP_LINK_TX_MTU_BYTES);
981
982 // Add preamble
983 context->pendingTxPacket.length +=
984 chppAddPreamble(&context->pendingTxPacket.payload[0]);
985
986 // Add header
987 txHeader = chppAddHeader(context);
988
989 // If applicable, add payload
990 if ((context->txDatagramQueue.pending > 0)) {
991 // Note: For a future ACK window >1, we need to rewrite this payload
992 // adding code to base the next packet on the sent location within the
993 // last sent datagram, except for the case of a NACK (explicit or
994 // timeout). For a NACK, we would need to base the next packet off the
995 // last ACKed location.
996
997 txHeader->seq = context->rxStatus.receivedAckSeq;
998 context->txStatus.sentSeq = txHeader->seq;
999
1000 if (context->txStatus.txAttempts > CHPP_TRANSPORT_MAX_RETX &&
1001 context->resetState != CHPP_RESET_STATE_RESETTING) {
1002 CHPP_LOGE("Resetting after %d retries", CHPP_TRANSPORT_MAX_RETX);
1003 havePacketForLinkLayer = false;
1004
1005 chppMutexUnlock(&context->mutex);
1006 chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
1007 CHPP_TRANSPORT_ERROR_MAX_RETRIES);
1008 chppMutexLock(&context->mutex);
1009
1010 } else {
1011 chppAddPayload(context);
1012 context->txStatus.txAttempts++;
1013 }
1014
1015 } else {
1016 // No payload
1017 context->txStatus.hasPacketsToSend = false;
1018 }
1019
1020 chppAddFooter(&context->pendingTxPacket);
1021
1022 } else {
1023 CHPP_LOGW(
1024 "DoWork nothing to send. hasPackets=%d, linkBusy=%d, pending=%" PRIu8
1025 ", Rx ACK=%" PRIu8 ", Tx seq=%" PRIu8 ", RX state=%" PRIu8,
1026 context->txStatus.hasPacketsToSend, context->txStatus.linkBusy,
1027 context->txDatagramQueue.pending, context->rxStatus.receivedAckSeq,
1028 context->txStatus.sentSeq, context->rxStatus.state);
1029 }
1030
1031 chppMutexUnlock(&context->mutex);
1032
1033 if (havePacketForLinkLayer) {
1034 CHPP_LOGD("TX->Link: len=%" PRIuSIZE " flags=0x%" PRIx8 " code=0x%" PRIx8
1035 " ackSeq=%" PRIu8 " seq=%" PRIu8 " payloadLen=%" PRIu16
1036 " pending=%" PRIu8,
1037 context->pendingTxPacket.length, txHeader->flags,
1038 txHeader->packetCode, txHeader->ackSeq, txHeader->seq,
1039 txHeader->length, context->txDatagramQueue.pending);
1040 enum ChppLinkErrorCode error = chppSendPendingPacket(context);
1041
1042 if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
1043 // Platform implementation for platformLinkSend() is synchronous or an
1044 // error occurred. In either case, we should call chppLinkSendDoneCb()
1045 // here to release the contents of pendingTxPacket.
1046 chppLinkSendDoneCb(&context->linkParams, error);
1047 }
1048 }
1049
1050 #ifdef CHPP_CLIENT_ENABLED
1051 timeoutResponse = chppTransportGetClientRequestTimeoutResponse(context);
1052 #endif
1053 if (timeoutResponse != NULL) {
1054 CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8,
1055 timeoutResponse->handle, timeoutResponse->command,
1056 timeoutResponse->transaction);
1057 chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse,
1058 sizeof(struct ChppAppHeader));
1059 }
1060 }
1061
1062 /**
1063 * Appends data from a buffer of length len to a PendingTxPacket, updating its
1064 * length.
1065 *
1066 * @param packet The PendingTxBuffer to be appended to.
1067 * @param buf Input data to be copied from.
1068 * @param len Length of input data in bytes.
1069 */
chppAppendToPendingTxPacket(struct PendingTxPacket * packet,const uint8_t * buf,size_t len)1070 static void chppAppendToPendingTxPacket(struct PendingTxPacket *packet,
1071 const uint8_t *buf, size_t len) {
1072 CHPP_ASSERT(packet->length + len <= sizeof(packet->payload));
1073 memcpy(&packet->payload[packet->length], buf, len);
1074 packet->length += len;
1075 }
1076
1077 /**
1078 * @return A human readable form of the packet attribution.
1079 */
chppGetPacketAttrStr(uint8_t packetCode)1080 static const char *chppGetPacketAttrStr(uint8_t packetCode) {
1081 switch (CHPP_TRANSPORT_GET_ATTR(packetCode)) {
1082 case CHPP_TRANSPORT_ATTR_RESET:
1083 return "(RESET)";
1084 case CHPP_TRANSPORT_ATTR_RESET_ACK:
1085 return "(RESET-ACK)";
1086 case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
1087 return "(LOOP-REQ)";
1088 case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
1089 return "(LOOP-RES)";
1090 default:
1091 return "";
1092 }
1093 }
1094
1095 /**
1096 * Enqueues an outgoing datagram of a specified length. The payload must have
1097 * been allocated by the caller using chppMalloc.
1098 *
1099 * If enqueueing is successful, the payload will be freed by this function
1100 * once it has been sent out.
1101 * If enqueueing is unsuccessful, it is up to the caller to decide when or if
1102 * to free the payload and/or resend it later.
1103 *
1104 * @param context Maintains status for each transport layer instance.
1105 * @param packetCode Error code and packet attributes to be sent.
1106 * @param buf Datagram payload allocated through chppMalloc. Cannot be null.
1107 * @param len Datagram length in bytes.
1108 *
1109 * @return True informs the sender that the datagram was successfully enqueued.
1110 * False informs the sender that the queue was full.
1111 */
chppEnqueueTxDatagram(struct ChppTransportState * context,uint8_t packetCode,void * buf,size_t len)1112 static bool chppEnqueueTxDatagram(struct ChppTransportState *context,
1113 uint8_t packetCode, void *buf, size_t len) {
1114 bool success = false;
1115
1116 if (len == 0) {
1117 CHPP_LOGE("Enqueue tx with len 0");
1118 CHPP_DEBUG_ASSERT(false);
1119
1120 } else {
1121 if ((len < sizeof(struct ChppAppHeader)) ||
1122 (CHPP_TRANSPORT_GET_ATTR(packetCode) != 0)) {
1123 CHPP_LOGI("Enqueue TX: code=0x%" PRIx8 "%s len=%" PRIuSIZE
1124 " pending=%" PRIu8,
1125 packetCode, chppGetPacketAttrStr(packetCode), len,
1126 (uint8_t)(context->txDatagramQueue.pending + 1));
1127 } else {
1128 struct ChppAppHeader *header = buf;
1129 CHPP_LOGI(
1130 "Enqueue TX: len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
1131 " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16 " pending=%" PRIu8,
1132 len, header->handle, header->type, header->transaction, header->error,
1133 header->command, (uint8_t)(context->txDatagramQueue.pending + 1));
1134 }
1135
1136 chppMutexLock(&context->mutex);
1137
1138 if (context->txDatagramQueue.pending >= CHPP_TX_DATAGRAM_QUEUE_LEN) {
1139 CHPP_LOGE("Cannot enqueue TX datagram");
1140
1141 } else {
1142 uint16_t end =
1143 (context->txDatagramQueue.front + context->txDatagramQueue.pending) %
1144 CHPP_TX_DATAGRAM_QUEUE_LEN;
1145 context->txDatagramQueue.datagram[end].length = len;
1146 context->txDatagramQueue.datagram[end].payload = buf;
1147 context->txDatagramQueue.pending++;
1148
1149 if (context->txDatagramQueue.pending == 1) {
1150 // Queue was empty prior. Need to kickstart transmission.
1151 chppEnqueueTxPacket(context, packetCode);
1152 }
1153
1154 success = true;
1155 }
1156
1157 chppMutexUnlock(&context->mutex);
1158 }
1159
1160 return success;
1161 }
1162
1163 /**
1164 * Sends the pending outgoing packet (context->pendingTxPacket) over to the link
1165 * layer using chppPlatformLinkSend() and updates the last Tx packet time.
1166 *
1167 * @param context Maintains status for each transport layer instance.
1168 *
1169 * @return Result of chppPlatformLinkSend().
1170 */
chppSendPendingPacket(struct ChppTransportState * context)1171 enum ChppLinkErrorCode chppSendPendingPacket(
1172 struct ChppTransportState *context) {
1173 enum ChppLinkErrorCode error = chppPlatformLinkSend(
1174 &context->linkParams, context->pendingTxPacket.payload,
1175 context->pendingTxPacket.length);
1176
1177 context->txStatus.lastTxTimeNs = chppGetCurrentTimeNs();
1178
1179 return error;
1180 }
1181
1182 /**
1183 * Resets the transport state, maintaining the link layer parameters.
1184 *
1185 * @param context Maintains status for each transport layer instance.
1186 */
chppResetTransportContext(struct ChppTransportState * context)1187 static void chppResetTransportContext(struct ChppTransportState *context) {
1188 memset(&context->rxStatus, 0, sizeof(struct ChppRxStatus));
1189 memset(&context->rxDatagram, 0, sizeof(struct ChppDatagram));
1190
1191 memset(&context->txStatus, 0, sizeof(struct ChppTxStatus));
1192 memset(&context->txDatagramQueue, 0, sizeof(struct ChppTxDatagramQueue));
1193
1194 context->txStatus.sentSeq =
1195 UINT8_MAX; // So that the seq # of the first TX packet is 0
1196 context->resetState = CHPP_RESET_STATE_RESETTING;
1197 }
1198
1199 /**
1200 * Re-initializes the CHPP transport and app layer states, e.g. when receiving a
1201 * reset packet, and sends out a reset or reset-ack packet over the link in
1202 * order to reset the remote side or inform the counterpart of a reset,
1203 * respectively.
1204 *
1205 * If the link layer is busy, this function will reset the link as well.
1206 * This function retains and restores the platform-specific values of
1207 * transportContext.linkParams.
1208 *
1209 * @param transportContext Maintains status for each transport layer instance.
1210 * @param resetType Type of reset to send after resetting CHPP (reset vs.
1211 * reset-ack), as defined in the ChppTransportPacketAttributes struct.
1212 * @param error Provides the error that led to the reset.
1213 */
chppReset(struct ChppTransportState * transportContext,enum ChppTransportPacketAttributes resetType,enum ChppTransportErrorCode error)1214 static void chppReset(struct ChppTransportState *transportContext,
1215 enum ChppTransportPacketAttributes resetType,
1216 enum ChppTransportErrorCode error) {
1217 // TODO: Configure transport layer based on (optional?) received config before
1218 // datagram is wiped
1219
1220 chppMutexLock(&transportContext->mutex);
1221 struct ChppAppState *appContext = transportContext->appContext;
1222 transportContext->resetState = CHPP_RESET_STATE_RESETTING;
1223
1224 // Reset asynchronous link layer if busy
1225 if (transportContext->txStatus.linkBusy == true) {
1226 // TODO: Give time for link layer to finish before resorting to a reset
1227
1228 chppPlatformLinkReset(&transportContext->linkParams);
1229 }
1230
1231 // Free memory allocated for any ongoing rx datagrams
1232 if (transportContext->rxDatagram.length > 0) {
1233 transportContext->rxDatagram.length = 0;
1234 CHPP_FREE_AND_NULLIFY(transportContext->rxDatagram.payload);
1235 }
1236
1237 // Free memory allocated for any ongoing tx datagrams
1238 for (size_t i = 0; i < CHPP_TX_DATAGRAM_QUEUE_LEN; i++) {
1239 if (transportContext->txDatagramQueue.datagram[i].length > 0) {
1240 CHPP_FREE_AND_NULLIFY(
1241 transportContext->txDatagramQueue.datagram[i].payload);
1242 }
1243 }
1244
1245 // Reset Transport Layer but restore Rx sequence number and packet code
1246 // (context->rxHeader is not wiped in reset)
1247 chppResetTransportContext(transportContext);
1248 transportContext->rxStatus.receivedPacketCode =
1249 transportContext->rxHeader.packetCode;
1250 transportContext->rxStatus.expectedSeq = transportContext->rxHeader.seq + 1;
1251
1252 // Send reset or reset-ACK
1253 chppMutexUnlock(&transportContext->mutex);
1254 chppTransportSendReset(transportContext, resetType, error);
1255
1256 // Inform the App Layer that a reset has completed
1257 if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1258 chppAppProcessReset(appContext);
1259 } // else reset is sent out. Rx of reset-ack will indicate completion.
1260 }
1261
1262 /**
1263 * Checks for a timed out client request and generates a timeout response if a
1264 * client request timeout has occurred.
1265 *
1266 * @param context Maintains status for each transport layer instance.
1267 * @return App layer response header if a timeout has occurred. Null otherwise.
1268 */
1269 #ifdef CHPP_CLIENT_ENABLED
chppTransportGetClientRequestTimeoutResponse(struct ChppTransportState * context)1270 struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse(
1271 struct ChppTransportState *context) {
1272 struct ChppAppHeader *response = NULL;
1273
1274 bool timeoutClientFound = false;
1275 uint8_t timedOutClient;
1276 uint16_t timedOutCmd;
1277
1278 chppMutexLock(&context->mutex);
1279
1280 if (context->appContext->nextRequestTimeoutNs <= chppGetCurrentTimeNs()) {
1281 // Determine which request has timed out
1282
1283 uint64_t lowestTimeout = CHPP_TIME_MAX;
1284 for (uint8_t clientIdx = 0;
1285 clientIdx < context->appContext->registeredClientCount; clientIdx++) {
1286 for (uint16_t cmdIdx = 0;
1287 cmdIdx <
1288 context->appContext->registeredClients[clientIdx]->rRStateCount;
1289 cmdIdx++) {
1290 struct ChppRequestResponseState *rRState =
1291 &context->appContext->registeredClientStates[clientIdx]
1292 ->rRStates[cmdIdx];
1293
1294 if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT &&
1295 rRState->responseTimeNs != CHPP_TIME_NONE &&
1296 rRState->responseTimeNs < lowestTimeout) {
1297 lowestTimeout = rRState->responseTimeNs;
1298 timedOutClient = clientIdx;
1299 timedOutCmd = cmdIdx;
1300 timeoutClientFound = true;
1301 }
1302 }
1303 }
1304
1305 if (!timeoutClientFound) {
1306 CHPP_LOGE("Timeout at %" PRIu64 " but no client",
1307 context->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
1308 chppClientRecalculateNextTimeout(context->appContext);
1309 }
1310 }
1311
1312 if (timeoutClientFound) {
1313 CHPP_LOGE("Client=%" PRIu8 " cmd=%" PRIu16 " timed out", timedOutClient,
1314 timedOutCmd);
1315 response = chppMalloc(sizeof(struct ChppAppHeader));
1316 if (response == NULL) {
1317 CHPP_LOG_OOM();
1318 } else {
1319 response->handle = CHPP_SERVICE_HANDLE_OF_INDEX(timedOutClient);
1320 response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
1321 response->transaction =
1322 context->appContext->registeredClientStates[timedOutClient]
1323 ->rRStates[timedOutCmd]
1324 .transaction;
1325 response->error = CHPP_APP_ERROR_TIMEOUT;
1326 response->command = timedOutCmd;
1327 }
1328 }
1329
1330 chppMutexUnlock(&context->mutex);
1331
1332 return response;
1333 }
1334 #endif
1335
1336 /************************************************
1337 * Public Functions
1338 ***********************************************/
1339
chppTransportInit(struct ChppTransportState * transportContext,struct ChppAppState * appContext)1340 void chppTransportInit(struct ChppTransportState *transportContext,
1341 struct ChppAppState *appContext) {
1342 CHPP_NOT_NULL(transportContext);
1343 CHPP_NOT_NULL(appContext);
1344 CHPP_ASSERT_LOG(!transportContext->initialized,
1345 "CHPP transport already initialized");
1346
1347 CHPP_LOGD("Initializing CHPP transport");
1348
1349 chppResetTransportContext(transportContext);
1350 chppMutexInit(&transportContext->mutex);
1351 chppNotifierInit(&transportContext->notifier);
1352 chppConditionVariableInit(&transportContext->resetCondVar);
1353
1354 transportContext->appContext = appContext;
1355 transportContext->initialized = true;
1356
1357 chppPlatformLinkInit(&transportContext->linkParams);
1358 }
1359
chppTransportDeinit(struct ChppTransportState * transportContext)1360 void chppTransportDeinit(struct ChppTransportState *transportContext) {
1361 CHPP_NOT_NULL(transportContext);
1362 CHPP_ASSERT_LOG(transportContext->initialized,
1363 "CHPP transport already deinitialized");
1364
1365 chppPlatformLinkDeinit(&transportContext->linkParams);
1366 chppConditionVariableDeinit(&transportContext->resetCondVar);
1367 chppNotifierDeinit(&transportContext->notifier);
1368 chppMutexDeinit(&transportContext->mutex);
1369
1370 chppClearTxDatagramQueue(transportContext);
1371
1372 transportContext->initialized = false;
1373 }
1374
chppTransportWaitForResetComplete(struct ChppTransportState * transportContext,uint64_t timeoutMs)1375 bool chppTransportWaitForResetComplete(
1376 struct ChppTransportState *transportContext, uint64_t timeoutMs) {
1377 bool success = true;
1378 chppMutexLock(&transportContext->mutex);
1379 while (success && transportContext->resetState != CHPP_RESET_STATE_NONE) {
1380 success = chppConditionVariableTimedWait(&transportContext->resetCondVar,
1381 &transportContext->mutex,
1382 timeoutMs * CHPP_NSEC_PER_MSEC);
1383 }
1384 chppMutexUnlock(&transportContext->mutex);
1385 return success;
1386 }
1387
chppRxDataCb(struct ChppTransportState * context,const uint8_t * buf,size_t len)1388 bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
1389 size_t len) {
1390 CHPP_NOT_NULL(buf);
1391 CHPP_NOT_NULL(context);
1392
1393 chppMutexLock(&context->mutex);
1394 if (context->rxStatus.state != CHPP_STATE_PREAMBLE &&
1395 chppGetCurrentTimeNs() >
1396 context->rxStatus.packetStartTimeNs + CHPP_TRANSPORT_RX_TIMEOUT_NS) {
1397 CHPP_LOGE("Packet RX timeout");
1398 chppAbortRxPacket(context);
1399 }
1400 chppMutexUnlock(&context->mutex);
1401
1402 CHPP_LOGD("RX %" PRIuSIZE " bytes: state=%" PRIu8, len,
1403 context->rxStatus.state);
1404 uint64_t now = chppGetCurrentTimeNs();
1405 context->rxStatus.lastDataTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC);
1406 context->rxStatus.numTotalDataBytes += len;
1407
1408 size_t consumed = 0;
1409 while (consumed < len) {
1410 chppMutexLock(&context->mutex);
1411 // TODO: Investigate fine-grained locking, e.g. separating variables that
1412 // are only relevant to a particular path.
1413 // Also consider removing some of the finer-grained locks altogether for
1414 // non-multithreaded environments with clear documentation.
1415
1416 switch (context->rxStatus.state) {
1417 case CHPP_STATE_PREAMBLE:
1418 consumed +=
1419 chppConsumePreamble(context, &buf[consumed], len - consumed);
1420 break;
1421
1422 case CHPP_STATE_HEADER:
1423 consumed += chppConsumeHeader(context, &buf[consumed], len - consumed);
1424 break;
1425
1426 case CHPP_STATE_PAYLOAD:
1427 consumed += chppConsumePayload(context, &buf[consumed], len - consumed);
1428 break;
1429
1430 case CHPP_STATE_FOOTER:
1431 consumed += chppConsumeFooter(context, &buf[consumed], len - consumed);
1432 break;
1433
1434 default:
1435 CHPP_LOGE("Invalid RX state %" PRIu8, context->rxStatus.state);
1436 CHPP_DEBUG_ASSERT(false);
1437 chppSetRxState(context, CHPP_STATE_PREAMBLE);
1438 }
1439
1440 chppMutexUnlock(&context->mutex);
1441 }
1442
1443 return (context->rxStatus.state == CHPP_STATE_PREAMBLE &&
1444 context->rxStatus.locInState == 0);
1445 }
1446
chppRxPacketCompleteCb(struct ChppTransportState * context)1447 void chppRxPacketCompleteCb(struct ChppTransportState *context) {
1448 chppMutexLock(&context->mutex);
1449 if (context->rxStatus.state != CHPP_STATE_PREAMBLE) {
1450 CHPP_LOGE("Rx pkt ended early: state=%" PRIu8 " packet=%" PRIu8
1451 " len=%" PRIu16,
1452 context->rxStatus.state, context->rxHeader.seq,
1453 context->rxHeader.length);
1454 chppAbortRxPacket(context);
1455 chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_HEADER); // NACK
1456 }
1457 chppMutexUnlock(&context->mutex);
1458 }
1459
chppEnqueueTxDatagramOrFail(struct ChppTransportState * context,void * buf,size_t len)1460 bool chppEnqueueTxDatagramOrFail(struct ChppTransportState *context, void *buf,
1461 size_t len) {
1462 bool success = false;
1463 bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
1464
1465 if (len == 0) {
1466 CHPP_LOGE("Enqueue datagram len 0");
1467 CHPP_DEBUG_ASSERT(false);
1468
1469 } else if (resetting || !chppEnqueueTxDatagram(
1470 context, CHPP_TRANSPORT_ERROR_NONE, buf, len)) {
1471 uint8_t *handle = buf;
1472 CHPP_LOGE("Resetting=%d. Discarding %" PRIuSIZE " bytes for H#%" PRIu8,
1473 resetting, len, *handle);
1474
1475 CHPP_FREE_AND_NULLIFY(buf);
1476
1477 } else {
1478 success = true;
1479 }
1480
1481 return success;
1482 }
1483
1484 // TODO(b/192359485): Consider removing this function, or making it more robust.
chppEnqueueTxErrorDatagram(struct ChppTransportState * context,enum ChppTransportErrorCode errorCode)1485 void chppEnqueueTxErrorDatagram(struct ChppTransportState *context,
1486 enum ChppTransportErrorCode errorCode) {
1487 bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
1488 if (resetting) {
1489 CHPP_LOGE("Discarding app error 0x%" PRIx8 " (resetting)", errorCode);
1490 } else {
1491 switch (errorCode) {
1492 case CHPP_TRANSPORT_ERROR_OOM: {
1493 CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_OOM");
1494 break;
1495 }
1496 case CHPP_TRANSPORT_ERROR_APPLAYER: {
1497 CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_APPLAYER");
1498 break;
1499 }
1500 default: {
1501 // App layer should not invoke any other errors
1502 CHPP_LOGE("App enqueueing invalid err=%" PRIu8, errorCode);
1503 CHPP_DEBUG_ASSERT(false);
1504 }
1505 }
1506 chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
1507 CHPP_TRANSPORT_ATTR_NONE, errorCode));
1508 }
1509 }
1510
chppTransportGetTimeUntilNextDoWorkNs(struct ChppTransportState * context)1511 uint64_t chppTransportGetTimeUntilNextDoWorkNs(
1512 struct ChppTransportState *context) {
1513 uint64_t currentTime = chppGetCurrentTimeNs();
1514 uint64_t nextDoWorkTime = context->appContext->nextRequestTimeoutNs;
1515
1516 if (context->txStatus.hasPacketsToSend ||
1517 context->resetState == CHPP_RESET_STATE_RESETTING) {
1518 nextDoWorkTime =
1519 MIN(nextDoWorkTime, CHPP_TRANSPORT_TX_TIMEOUT_NS +
1520 ((context->txStatus.lastTxTimeNs == 0)
1521 ? currentTime
1522 : context->txStatus.lastTxTimeNs));
1523 }
1524
1525 CHPP_LOGD("NextDoWork=%" PRIu64 " currentTime=%" PRIu64 " delta=%" PRId64,
1526 nextDoWorkTime / CHPP_NSEC_PER_MSEC,
1527 currentTime / CHPP_NSEC_PER_MSEC,
1528 (nextDoWorkTime - currentTime) / (int64_t)CHPP_NSEC_PER_MSEC);
1529
1530 if (nextDoWorkTime == CHPP_TIME_MAX) {
1531 return CHPP_TRANSPORT_TIMEOUT_INFINITE;
1532 } else if (nextDoWorkTime <= currentTime) {
1533 return CHPP_TRANSPORT_TIMEOUT_IMMEDIATE;
1534 } else {
1535 return nextDoWorkTime - currentTime;
1536 }
1537 }
1538
chppWorkThreadStart(struct ChppTransportState * context)1539 void chppWorkThreadStart(struct ChppTransportState *context) {
1540 chppTransportSendReset(context, CHPP_TRANSPORT_ATTR_RESET,
1541 CHPP_TRANSPORT_ERROR_NONE);
1542 CHPP_LOGD("CHPP Work Thread started");
1543
1544 uint32_t signals;
1545 do {
1546 uint64_t timeout = chppTransportGetTimeUntilNextDoWorkNs(context);
1547 if (timeout == CHPP_TRANSPORT_TIMEOUT_IMMEDIATE) {
1548 signals = chppNotifierGetSignal(&context->notifier);
1549 } else if (timeout == CHPP_TRANSPORT_TIMEOUT_INFINITE) {
1550 signals = chppNotifierWait(&context->notifier);
1551 } else {
1552 signals = chppNotifierTimedWait(&context->notifier, timeout);
1553 }
1554
1555 } while (chppWorkThreadHandleSignal(context, signals));
1556 }
1557
chppWorkThreadHandleSignal(struct ChppTransportState * context,uint32_t signals)1558 bool chppWorkThreadHandleSignal(struct ChppTransportState *context,
1559 uint32_t signals) {
1560 if (signals & CHPP_TRANSPORT_SIGNAL_EXIT) {
1561 CHPP_LOGD("CHPP Work Thread terminated");
1562 return false;
1563 }
1564
1565 if (signals & CHPP_TRANSPORT_SIGNAL_EVENT) {
1566 chppTransportDoWork(context);
1567 }
1568
1569 if (signals == 0) {
1570 // Triggered by timeout
1571
1572 if (chppGetCurrentTimeNs() - context->txStatus.lastTxTimeNs >=
1573 CHPP_TRANSPORT_TX_TIMEOUT_NS) {
1574 CHPP_LOGE("ACK timeout. Tx t=%" PRIu64,
1575 context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC);
1576 chppTransportDoWork(context);
1577 }
1578
1579 if ((context->resetState == CHPP_RESET_STATE_RESETTING) &&
1580 (chppGetCurrentTimeNs() - context->resetTimeNs >=
1581 CHPP_TRANSPORT_RESET_TIMEOUT_NS)) {
1582 if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) {
1583 CHPP_LOGE("RESET-ACK timeout; retrying");
1584 context->resetCount++;
1585 chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
1586 CHPP_TRANSPORT_ERROR_TIMEOUT);
1587 } else {
1588 CHPP_LOGE("RESET-ACK timeout; giving up");
1589 context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE;
1590 chppClearTxDatagramQueue(context);
1591 }
1592 }
1593 }
1594
1595 if (signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK) {
1596 chppPlatformLinkDoWork(&context->linkParams,
1597 signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK);
1598 }
1599
1600 return true;
1601 }
1602
chppWorkThreadStop(struct ChppTransportState * context)1603 void chppWorkThreadStop(struct ChppTransportState *context) {
1604 chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EXIT);
1605 }
1606
chppLinkSendDoneCb(struct ChppPlatformLinkParameters * params,enum ChppLinkErrorCode error)1607 void chppLinkSendDoneCb(struct ChppPlatformLinkParameters *params,
1608 enum ChppLinkErrorCode error) {
1609 if (error != CHPP_LINK_ERROR_NONE_SENT) {
1610 CHPP_LOGE("Async send failure: %" PRIu8, error);
1611 }
1612
1613 struct ChppTransportState *context =
1614 container_of(params, struct ChppTransportState, linkParams);
1615
1616 chppMutexLock(&context->mutex);
1617
1618 context->txStatus.linkBusy = false;
1619
1620 // No need to free anything as pendingTxPacket.payload is static. Likewise, we
1621 // keep pendingTxPacket.length to assist testing.
1622
1623 chppMutexUnlock(&context->mutex);
1624 }
1625
chppDatagramProcessDoneCb(struct ChppTransportState * context,uint8_t * buf)1626 void chppDatagramProcessDoneCb(struct ChppTransportState *context,
1627 uint8_t *buf) {
1628 UNUSED_VAR(context);
1629
1630 CHPP_FREE_AND_NULLIFY(buf);
1631 }
1632
chppRunTransportLoopback(struct ChppTransportState * context,uint8_t * buf,size_t len)1633 uint8_t chppRunTransportLoopback(struct ChppTransportState *context,
1634 uint8_t *buf, size_t len) {
1635 UNUSED_VAR(buf);
1636 UNUSED_VAR(len);
1637 uint8_t result = CHPP_APP_ERROR_UNSUPPORTED;
1638 context->loopbackResult = result;
1639
1640 #ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
1641 result = CHPP_APP_ERROR_NONE;
1642 context->loopbackResult = CHPP_APP_ERROR_UNSPECIFIED;
1643
1644 if (len == 0 || len > CHPP_TRANSPORT_TX_MTU_BYTES) {
1645 result = CHPP_APP_ERROR_INVALID_LENGTH;
1646 context->loopbackResult = result;
1647
1648 } else if (context->txStatus.linkBusy) {
1649 result = CHPP_APP_ERROR_BLOCKED;
1650 context->loopbackResult = result;
1651
1652 } else if (context->transportLoopbackData.payload != NULL) {
1653 result = CHPP_APP_ERROR_BUSY;
1654 context->loopbackResult = result;
1655
1656 } else if ((context->transportLoopbackData.payload = chppMalloc(len)) ==
1657 NULL) {
1658 result = CHPP_APP_ERROR_OOM;
1659 context->loopbackResult = result;
1660
1661 } else {
1662 context->transportLoopbackData.length = len;
1663 memcpy(context->transportLoopbackData.payload, buf, len);
1664
1665 context->txStatus.linkBusy = true;
1666 context->pendingTxPacket.length = 0;
1667 memset(&context->pendingTxPacket.payload, 0, CHPP_LINK_TX_MTU_BYTES);
1668 context->pendingTxPacket.length +=
1669 chppAddPreamble(&context->pendingTxPacket.payload[0]);
1670
1671 struct ChppTransportHeader *txHeader =
1672 (struct ChppTransportHeader *)&context->pendingTxPacket
1673 .payload[context->pendingTxPacket.length];
1674 context->pendingTxPacket.length += sizeof(*txHeader);
1675
1676 txHeader->packetCode = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
1677 CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST, txHeader->packetCode);
1678
1679 size_t payloadLen = MIN(len, CHPP_TRANSPORT_TX_MTU_BYTES);
1680 txHeader->length = (uint16_t)payloadLen;
1681 chppAppendToPendingTxPacket(&context->pendingTxPacket, buf, payloadLen);
1682
1683 chppAddFooter(&context->pendingTxPacket);
1684
1685 CHPP_LOGD("Sending transport-loopback request (packet len=%" PRIuSIZE
1686 ", payload len=%" PRIu16 ", asked len was %" PRIuSIZE ")",
1687 context->pendingTxPacket.length, txHeader->length, len);
1688 enum ChppLinkErrorCode error = chppSendPendingPacket(context);
1689
1690 if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
1691 // Either sent synchronously or an error has occurred
1692 chppLinkSendDoneCb(&context->linkParams, error);
1693
1694 if (error != CHPP_LINK_ERROR_NONE_SENT) {
1695 // An error has occurred
1696 CHPP_FREE_AND_NULLIFY(context->transportLoopbackData.payload);
1697 context->transportLoopbackData.length = 0;
1698 result = CHPP_APP_ERROR_UNSPECIFIED;
1699 }
1700 }
1701 }
1702
1703 if (result != CHPP_APP_ERROR_NONE) {
1704 CHPP_LOGE("Transport-loopback failure: %" PRIu8, result);
1705 }
1706 #endif
1707 return result;
1708 }
1709
chppTransportSendReset(struct ChppTransportState * context,enum ChppTransportPacketAttributes resetType,enum ChppTransportErrorCode error)1710 void chppTransportSendReset(struct ChppTransportState *context,
1711 enum ChppTransportPacketAttributes resetType,
1712 enum ChppTransportErrorCode error) {
1713 // Make sure CHPP is in an initialized state
1714 CHPP_ASSERT_LOG((context->txDatagramQueue.pending == 0 &&
1715 context->txDatagramQueue.front == 0),
1716 "Not init to send reset");
1717
1718 struct ChppTransportConfiguration *config =
1719 chppMalloc(sizeof(struct ChppTransportConfiguration));
1720
1721 // CHPP transport version
1722 config->version.major = 1;
1723 config->version.minor = 0;
1724 config->version.patch = 0;
1725
1726 // Rx MTU size
1727 config->rxMtu = CHPP_PLATFORM_LINK_RX_MTU_BYTES;
1728
1729 // Max Rx window size
1730 // Note: current implementation does not support a window size >1
1731 config->windowSize = 1;
1732
1733 // Advertised transport layer (ACK) timeout
1734 config->timeoutInMs = CHPP_PLATFORM_TRANSPORT_TIMEOUT_MS;
1735
1736 if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1737 CHPP_LOGD("Sending RESET-ACK");
1738 } else {
1739 CHPP_LOGD("Sending RESET");
1740 }
1741
1742 if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
1743 chppSetResetComplete(context);
1744 }
1745
1746 context->resetTimeNs = chppGetCurrentTimeNs();
1747
1748 chppEnqueueTxDatagram(context,
1749 CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(resetType, error),
1750 config, sizeof(*config));
1751 }
1752