aboutsummaryrefslogtreecommitdiff
path: root/chpp/transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'chpp/transport.c')
-rw-r--r--chpp/transport.c1674
1 files changed, 321 insertions, 1353 deletions
diff --git a/chpp/transport.c b/chpp/transport.c
index f3f76517..c9afeec7 100644
--- a/chpp/transport.c
+++ b/chpp/transport.c
@@ -16,22 +16,6 @@
#include "chpp/transport.h"
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "chpp/app.h"
-#include "chpp/clients.h"
-#include "chpp/clients/discovery.h"
-#include "chpp/crc.h"
-#include "chpp/link.h"
-#include "chpp/log.h"
-#include "chpp/macros.h"
-#include "chpp/memory.h"
-#include "chpp/platform/platform_link.h"
-#include "chpp/time.h"
/************************************************
* Prototypes
@@ -47,51 +31,18 @@ static size_t chppConsumePayload(struct ChppTransportState *context,
const uint8_t *buf, size_t len);
static size_t chppConsumeFooter(struct ChppTransportState *context,
const uint8_t *buf, size_t len);
-static void chppAbortRxPacket(struct ChppTransportState *context);
-#ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK
-static void chppProcessTransportLoopbackRequest(
- struct ChppTransportState *context);
-#endif
-#ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
-static void chppProcessTransportLoopbackResponse(
- struct ChppTransportState *context);
-#endif
-static void chppSetResetComplete(struct ChppTransportState *context);
-static void chppProcessResetAck(struct ChppTransportState *context);
-static void chppProcessRxPacket(struct ChppTransportState *context);
static void chppProcessRxPayload(struct ChppTransportState *context);
-static void chppClearRxDatagram(struct ChppTransportState *context);
static bool chppRxChecksumIsOk(const struct ChppTransportState *context);
-static enum ChppTransportErrorCode chppRxHeaderCheck(
+static enum ChppErrorCode chppRxHeaderCheck(
const struct ChppTransportState *context);
static void chppRegisterRxAck(struct ChppTransportState *context);
static void chppEnqueueTxPacket(struct ChppTransportState *context,
- uint8_t packetCode);
+ enum ChppErrorCode errorCode);
static size_t chppAddPreamble(uint8_t *buf);
-static struct ChppTransportHeader *chppAddHeader(
- struct ChppTransportState *context);
-static void chppAddPayload(struct ChppTransportState *context);
-static void chppAddFooter(struct PendingTxPacket *packet);
-size_t chppDequeueTxDatagram(struct ChppTransportState *context);
-static void chppClearTxDatagramQueue(struct ChppTransportState *context);
-static void chppTransportDoWork(struct ChppTransportState *context);
-static void chppAppendToPendingTxPacket(struct PendingTxPacket *packet,
- const uint8_t *buf, size_t len);
-static const char *chppGetPacketAttrStr(uint8_t packetCode);
-static bool chppEnqueueTxDatagram(struct ChppTransportState *context,
- uint8_t packetCode, void *buf, size_t len);
-enum ChppLinkErrorCode chppSendPendingPacket(
- struct ChppTransportState *context);
-
-static void chppResetTransportContext(struct ChppTransportState *context);
-static void chppReset(struct ChppTransportState *context,
- enum ChppTransportPacketAttributes resetType,
- enum ChppTransportErrorCode error);
-#ifdef CHPP_CLIENT_ENABLED
-struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse(
- struct ChppTransportState *context);
-#endif
+static uint32_t chppCalculateChecksum(uint8_t *buf, size_t len);
+bool chppDequeueTxDatagram(struct ChppTransportState *context);
+void chppTransportDoWork(struct ChppTransportState *context);
/************************************************
* Private Functions
@@ -102,14 +53,13 @@ struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse(
* counter among that state (rxStatus.locInState) is also reset at the same
* time.
*
- * @param context Maintains status for each transport layer instance.
- * @param newState Next Rx state.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
+ * @param newState Next Rx state
*/
static void chppSetRxState(struct ChppTransportState *context,
enum ChppRxState newState) {
- CHPP_LOGD("Changing RX transport state from %" PRIu8 " to %" PRIu8
- " after %" PRIuSIZE " bytes",
- context->rxStatus.state, newState, context->rxStatus.locInState);
+ LOGD("Changing state from %d to %d", context->rxStatus.state, newState);
context->rxStatus.locInState = 0;
context->rxStatus.state = newState;
}
@@ -122,11 +72,12 @@ static void chppSetRxState(struct ChppTransportState *context,
* Any future backwards-incompatible versions of CHPP Transport will use a
* different preamble.
*
- * @param context Maintains status for each transport layer instance.
- * @param buf Input data.
- * @param len Length of input data in bytes.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
+ * @param buf Input data
+ * @param len Length of input data in bytes
*
- * @return Length of consumed data in bytes.
+ * @return Length of consumed data in bytes
*/
static size_t chppConsumePreamble(struct ChppTransportState *context,
const uint8_t *buf, size_t len) {
@@ -136,16 +87,16 @@ static size_t chppConsumePreamble(struct ChppTransportState *context,
// serial port calling chppRxDataCb does not implement zero filter
while (consumed < len &&
context->rxStatus.locInState < CHPP_PREAMBLE_LEN_BYTES) {
- size_t offset = context->rxStatus.locInState;
- if ((offset == 0 && buf[consumed] == CHPP_PREAMBLE_BYTE_FIRST) ||
- (offset == 1 && buf[consumed] == CHPP_PREAMBLE_BYTE_SECOND)) {
+ if (buf[consumed] ==
+ ((CHPP_PREAMBLE_DATA >>
+ (CHPP_PREAMBLE_LEN_BYTES - context->rxStatus.locInState - 1)) &
+ 0xff)) {
// Correct byte of preamble observed
context->rxStatus.locInState++;
-
- } else if (buf[consumed] == CHPP_PREAMBLE_BYTE_FIRST) {
+ } else if (buf[consumed] ==
+ ((CHPP_PREAMBLE_DATA >> (CHPP_PREAMBLE_LEN_BYTES - 1)) & 0xff)) {
// Previous search failed but first byte of another preamble observed
context->rxStatus.locInState = 1;
-
} else {
// Continue search for a valid preamble from the start
context->rxStatus.locInState = 0;
@@ -157,7 +108,6 @@ static size_t chppConsumePreamble(struct ChppTransportState *context,
// Let's see why we exited the above loop
if (context->rxStatus.locInState == CHPP_PREAMBLE_LEN_BYTES) {
// Complete preamble observed, move on to next state
- context->rxStatus.packetStartTimeNs = chppGetCurrentTimeNs();
chppSetRxState(context, CHPP_STATE_HEADER);
}
@@ -169,11 +119,12 @@ static size_t chppConsumePreamble(struct ChppTransportState *context,
* stream.
* Moves the Rx state to CHPP_STATE_PAYLOAD afterwards.
*
- * @param context Maintains status for each transport layer instance.
- * @param buf Input data.
- * @param len Length of input data in bytes.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
+ * @param buf Input data
+ * @param len Length of input data in bytes
*
- * @return Length of consumed data in bytes.
+ * @return Length of consumed data in bytes
*/
static size_t chppConsumeHeader(struct ChppTransportState *context,
const uint8_t *buf, size_t len) {
@@ -181,48 +132,54 @@ static size_t chppConsumeHeader(struct ChppTransportState *context,
sizeof(struct ChppTransportHeader));
size_t bytesToCopy = MIN(
len, (sizeof(struct ChppTransportHeader) - context->rxStatus.locInState));
+
+ LOGD("Copying %zu bytes of header", bytesToCopy);
memcpy(((uint8_t *)&context->rxHeader) + context->rxStatus.locInState, buf,
bytesToCopy);
- context->rxStatus.locInState += bytesToCopy;
+ context->rxStatus.locInState += bytesToCopy;
if (context->rxStatus.locInState == sizeof(struct ChppTransportHeader)) {
// Header fully copied. Move on
- enum ChppTransportErrorCode headerCheckResult = chppRxHeaderCheck(context);
- if (headerCheckResult != CHPP_TRANSPORT_ERROR_NONE) {
- // Header fails consistency check. NACK and return to preamble state
- chppEnqueueTxPacket(
- context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(CHPP_TRANSPORT_ATTR_NONE,
- headerCheckResult));
+ enum ChppErrorCode headerSanity = chppRxHeaderCheck(context);
+ if (headerSanity != CHPP_ERROR_NONE) {
+ // Header fails sanity check. NACK and return to preamble state
+ chppEnqueueTxPacket(context, headerSanity);
chppSetRxState(context, CHPP_STATE_PREAMBLE);
- } else if (context->rxHeader.length == 0) {
- // Non-payload packet
- chppSetRxState(context, CHPP_STATE_FOOTER);
-
} else {
- // Payload bearing packet
- uint8_t *tempPayload;
+ // Header passes sanity check
- if (context->rxDatagram.length == 0) {
- // Packet is a new datagram
- tempPayload = chppMalloc(context->rxHeader.length);
- } else {
- // Packet is a continuation of a fragmented datagram
- tempPayload =
- chppRealloc(context->rxDatagram.payload,
- context->rxDatagram.length + context->rxHeader.length,
- context->rxDatagram.length);
- }
+ if (context->rxHeader.length == 0) {
+ // Non-payload packet
+ chppSetRxState(context, CHPP_STATE_FOOTER);
- if (tempPayload == NULL) {
- CHPP_LOG_OOM();
- chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_OOM);
- chppSetRxState(context, CHPP_STATE_PREAMBLE);
} else {
- context->rxDatagram.payload = tempPayload;
- context->rxDatagram.length += context->rxHeader.length;
- chppSetRxState(context, CHPP_STATE_PAYLOAD);
+ // Payload bearing packet
+ uint8_t *tempPayload;
+
+ if (context->rxDatagram.length == 0) {
+ // Packet is a new datagram
+ tempPayload = chppMalloc(context->rxHeader.length);
+ } else {
+ // Packet is a continuation of a fragmented datagram
+ tempPayload =
+ chppRealloc(context->rxDatagram.payload,
+ context->rxDatagram.length + context->rxHeader.length,
+ context->rxDatagram.length);
+ }
+
+ if (tempPayload == NULL) {
+ LOGE("OOM for packet# %d, len=%u. Previous fragment(s) total len=%zu",
+ context->rxHeader.seq, context->rxHeader.length,
+ context->rxDatagram.length);
+ chppEnqueueTxPacket(context, CHPP_ERROR_OOM);
+ chppSetRxState(context, CHPP_STATE_PREAMBLE);
+ } else {
+ context->rxDatagram.payload = tempPayload;
+ context->rxDatagram.length += context->rxHeader.length;
+ chppSetRxState(context, CHPP_STATE_PAYLOAD);
+ }
}
}
}
@@ -235,7 +192,8 @@ static size_t chppConsumeHeader(struct ChppTransportState *context,
* by the header, from the incoming data stream.
* Moves the Rx state to CHPP_STATE_FOOTER afterwards.
*
- * @param context Maintains status for each transport layer instance.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
* @param buf Input data
* @param len Length of input data in bytes
*
@@ -246,13 +204,17 @@ static size_t chppConsumePayload(struct ChppTransportState *context,
CHPP_ASSERT(context->rxStatus.locInState < context->rxHeader.length);
size_t bytesToCopy =
MIN(len, (context->rxHeader.length - context->rxStatus.locInState));
+
+ LOGD("Copying %zu bytes of payload", bytesToCopy);
+
memcpy(context->rxDatagram.payload + context->rxStatus.locInDatagram, buf,
bytesToCopy);
context->rxStatus.locInDatagram += bytesToCopy;
- context->rxStatus.locInState += bytesToCopy;
+ context->rxStatus.locInState += bytesToCopy;
if (context->rxStatus.locInState == context->rxHeader.length) {
- // Entire packet payload copied. Move on
+ // Payload copied. Move on
+
chppSetRxState(context, CHPP_STATE_FOOTER);
}
@@ -264,11 +226,12 @@ static size_t chppConsumePayload(struct ChppTransportState *context,
* stream. Checks checksum, triggering the correct response (ACK / NACK).
* Moves the Rx state to CHPP_STATE_PREAMBLE afterwards.
*
- * @param context Maintains status for each transport layer instance.
- * @param buf Input data.
- * @param len Length of input data in bytes.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
+ * @param buf Input data
+ * @param len Length of input data in bytes
*
- * @return Length of consumed data in bytes.
+ * @return Length of consumed data in bytes
*/
static size_t chppConsumeFooter(struct ChppTransportState *context,
const uint8_t *buf, size_t len) {
@@ -276,6 +239,8 @@ static size_t chppConsumeFooter(struct ChppTransportState *context,
sizeof(struct ChppTransportFooter));
size_t bytesToCopy = MIN(
len, (sizeof(struct ChppTransportFooter) - context->rxStatus.locInState));
+
+ LOGD("Copying %zu bytes of footer (checksum)", bytesToCopy);
memcpy(((uint8_t *)&context->rxFooter) + context->rxStatus.locInState, buf,
bytesToCopy);
@@ -283,442 +248,140 @@ static size_t chppConsumeFooter(struct ChppTransportState *context,
if (context->rxStatus.locInState == sizeof(struct ChppTransportFooter)) {
// Footer copied. Move on
- if (CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode) !=
- CHPP_TRANSPORT_ERROR_NONE) {
- CHPP_LOGE("RX packet len=%" PRIu16 " seq=%" PRIu8 " ackSeq=%" PRIu8
- " attr=0x%" PRIx8 " ERR=%" PRIu8 " flags=0x%" PRIx8,
- context->rxHeader.length, context->rxHeader.seq,
- context->rxHeader.ackSeq,
- (uint8_t)CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode),
- (uint8_t)CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode),
- context->rxHeader.flags);
- } else {
- CHPP_LOGD("RX packet len=%" PRIu16 " seq=%" PRIu8 " ackSeq=%" PRIu8
- " attr=0x%" PRIx8 " err=%" PRIu8 " flags=0x%" PRIx8,
- context->rxHeader.length, context->rxHeader.seq,
- context->rxHeader.ackSeq,
- (uint8_t)CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode),
- (uint8_t)CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode),
- context->rxHeader.flags);
- }
+ bool hasPayload = (context->rxHeader.length > 0);
+
+ if (!chppRxChecksumIsOk(context)) {
+ // Packet is bad. Discard bad payload data (if any) and NACK
+ LOGE("Discarding CHPP packet# %d len=%u because of bad checksum",
+ context->rxHeader.seq, context->rxHeader.length);
+
+ if (hasPayload) {
+ context->rxDatagram.length -= context->rxHeader.length;
+ context->rxStatus.locInDatagram -= context->rxHeader.length;
+
+ if (context->rxDatagram.length == 0) {
+ // Discarding this packet == discarding entire datagram
+ chppFree(context->rxDatagram.payload);
+ context->rxDatagram.payload = NULL;
+
+ } else {
+ // Discarding this packet == discarding part of datagram
+ uint8_t *tempPayload = chppRealloc(
+ context->rxDatagram.payload, context->rxDatagram.length,
+ context->rxDatagram.length + context->rxHeader.length);
+ if (tempPayload == NULL) {
+ LOGE(
+ "OOM discarding bad continuation packet# %d len=%u. Previous "
+ "fragment(s) total len=%zu",
+ context->rxHeader.seq, context->rxHeader.length,
+ context->rxDatagram.length);
+ } else {
+ context->rxDatagram.payload = tempPayload;
+ }
+ }
+ }
- if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
- CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST) {
-#ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK
- chppProcessTransportLoopbackRequest(context);
-#endif
-
- } else if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
- CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE) {
-#ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
- chppProcessTransportLoopbackResponse(context);
-#endif
-
- } else if (!chppRxChecksumIsOk(context)) {
- CHPP_LOGE("Bad checksum. seq=%" PRIu8 " len=%" PRIu16,
- context->rxHeader.seq, context->rxHeader.length);
- chppAbortRxPacket(context);
- chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_CHECKSUM); // NACK
-
- } else if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
- CHPP_TRANSPORT_ATTR_RESET) {
- CHPP_LOGI("RX RESET packet seq=%" PRIu8 " err=%" PRIu8,
- context->rxHeader.seq,
- CHPP_TRANSPORT_GET_ERROR(context->rxHeader.packetCode));
- chppMutexUnlock(&context->mutex);
- chppReset(context, CHPP_TRANSPORT_ATTR_RESET_ACK,
- CHPP_TRANSPORT_ERROR_NONE);
- chppMutexLock(&context->mutex);
-
- } else if (context->resetState == CHPP_RESET_STATE_PERMANENT_FAILURE) {
- // Only a reset is accepted in this state
- CHPP_LOGE("RX discarded in perm fail. seq=%" PRIu8 " len=%" PRIu16,
- context->rxHeader.seq, context->rxHeader.length);
- chppAbortRxPacket(context);
-
- } else if (CHPP_TRANSPORT_GET_ATTR(context->rxHeader.packetCode) ==
- CHPP_TRANSPORT_ATTR_RESET_ACK) {
- CHPP_LOGI("RX RESET-ACK packet. seq=%" PRIu8, context->rxHeader.seq);
- chppProcessResetAck(context);
-
- } else if (context->resetState == CHPP_RESET_STATE_RESETTING) {
- CHPP_LOGE("RX discarded in reset. seq=%" PRIu8 " len=%" PRIu16,
- context->rxHeader.seq, context->rxHeader.length);
- chppAbortRxPacket(context);
+ chppEnqueueTxPacket(context, CHPP_ERROR_CHECKSUM);
} else {
- chppProcessRxPacket(context);
- }
-
- // Done with this packet. Wait for next packet
- chppSetRxState(context, CHPP_STATE_PREAMBLE);
- }
-
- return bytesToCopy;
-}
-
-/**
- * Discards of an incomplete Rx packet during receive (e.g. due to a timeout or
- * bad checksum).
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppAbortRxPacket(struct ChppTransportState *context) {
- size_t undoLen = 0;
- size_t undoLoc = 0;
-
- switch (context->rxStatus.state) {
- case (CHPP_STATE_PREAMBLE):
- case (CHPP_STATE_HEADER): {
- break;
- }
-
- case (CHPP_STATE_PAYLOAD): {
- undoLen = context->rxHeader.length;
- undoLoc = context->rxStatus.locInState;
- break;
- }
+ // Packet is good. Save received ACK info and process payload if any
- case (CHPP_STATE_FOOTER): {
- undoLen = context->rxHeader.length;
- undoLoc = context->rxHeader.length;
- break;
- }
+ context->rxStatus.receivedErrorCode = context->rxHeader.errorCode;
- default: {
- CHPP_DEBUG_ASSERT(false);
- }
- }
+ chppRegisterRxAck(context);
- if (undoLen > 0) {
- // Packet has a payload we need to discard of
-
- CHPP_ASSERT(context->rxDatagram.length >= undoLen);
- CHPP_ASSERT(context->rxStatus.locInDatagram >= undoLoc);
- context->rxDatagram.length -= undoLen;
- context->rxStatus.locInDatagram -= undoLoc;
-
- if (context->rxDatagram.length == 0) {
- // Discarding this packet == discarding entire datagram
- CHPP_FREE_AND_NULLIFY(context->rxDatagram.payload);
-
- } else {
- // Discarding this packet == discarding part of datagram
- uint8_t *tempPayload =
- chppRealloc(context->rxDatagram.payload, context->rxDatagram.length,
- context->rxDatagram.length + undoLen);
-
- if (tempPayload == NULL) {
- CHPP_LOG_OOM();
- } else {
- context->rxDatagram.payload = tempPayload;
+ if (context->txDatagramQueue.pending > 0) {
+ // There are packets to send out (could be new or retx)
+ chppEnqueueTxPacket(context, CHPP_ERROR_NONE);
}
- }
- }
-
- chppSetRxState(context, CHPP_STATE_PREAMBLE);
-}
-
-/**
- * Processes a request that is determined to be for a transport-layer loopback.
- *
- * @param context Maintains status for each transport layer instance.
- */
-#ifdef CHPP_SERVICE_ENABLED_TRANSPORT_LOOPBACK
-static void chppProcessTransportLoopbackRequest(
- struct ChppTransportState *context) {
- if (context->txStatus.linkBusy) {
- CHPP_LOGE("Link busy; transport-loopback dropped");
-
- } else {
- context->txStatus.linkBusy = true;
- context->pendingTxPacket.length = 0;
- context->pendingTxPacket.length +=
- chppAddPreamble(&context->pendingTxPacket.payload[0]);
-
- struct ChppTransportHeader *txHeader =
- (struct ChppTransportHeader *)&context->pendingTxPacket
- .payload[context->pendingTxPacket.length];
- context->pendingTxPacket.length += sizeof(*txHeader);
- *txHeader = context->rxHeader;
- txHeader->packetCode = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
- CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE, txHeader->packetCode);
-
- size_t payloadLen =
- MIN(context->rxDatagram.length, CHPP_TRANSPORT_TX_MTU_BYTES);
- chppAppendToPendingTxPacket(&context->pendingTxPacket,
- context->rxDatagram.payload, payloadLen);
- CHPP_FREE_AND_NULLIFY(context->rxDatagram.payload);
- chppClearRxDatagram(context);
-
- chppAddFooter(&context->pendingTxPacket);
-
- CHPP_LOGI("Trans-looping back len=%" PRIu16 " RX len=%" PRIuSIZE,
- txHeader->length, context->rxDatagram.length);
- enum ChppLinkErrorCode error = chppSendPendingPacket(context);
-
- if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
- chppLinkSendDoneCb(&context->linkParams, error);
+ if (hasPayload) {
+ chppProcessRxPayload(context);
+ }
}
- }
-}
-#endif
-
-/**
- * Processes a response that is determined to be for a transport-layer loopback.
- *
- * @param context Maintains status for each transport layer instance.
- */
-#ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
-static void chppProcessTransportLoopbackResponse(
- struct ChppTransportState *context) {
- if (context->transportLoopbackData.length != context->rxDatagram.length) {
- CHPP_LOGE("rx len=%" PRIuSIZE " != tx len=%" PRIuSIZE,
- context->rxDatagram.length,
- context->transportLoopbackData.length - CHPP_PREAMBLE_LEN_BYTES -
- sizeof(struct ChppTransportHeader) -
- sizeof(struct ChppTransportFooter));
- context->loopbackResult = CHPP_APP_ERROR_INVALID_LENGTH;
-
- } else if (memcmp(context->rxDatagram.payload,
- context->transportLoopbackData.payload,
- context->rxDatagram.length) != 0) {
- CHPP_LOGE("rx & tx data don't match: len=%" PRIuSIZE,
- context->rxDatagram.length);
- context->loopbackResult = CHPP_APP_ERROR_INVALID_ARG;
-
- } else {
- context->loopbackResult = CHPP_APP_ERROR_NONE;
-
- CHPP_LOGD("Rx successful transport-loopback (payload len=%" PRIuSIZE ")",
- context->rxDatagram.length);
- }
-
- context->transportLoopbackData.length = 0;
- CHPP_FREE_AND_NULLIFY(context->transportLoopbackData.payload);
- CHPP_FREE_AND_NULLIFY(context->rxDatagram.payload);
- chppClearRxDatagram(context);
-}
-#endif
-
-/**
- * Method to invoke when the reset sequence is completed.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppSetResetComplete(struct ChppTransportState *context) {
- context->resetState = CHPP_RESET_STATE_NONE;
- context->resetCount = 0;
- chppConditionVariableSignal(&context->resetCondVar);
-}
-
-/**
- * An incoming reset-ack packet indicates that a reset is complete at the other
- * end of the CHPP link.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppProcessResetAck(struct ChppTransportState *context) {
- if (context->resetState == CHPP_RESET_STATE_NONE) {
- CHPP_LOGE("Unexpected reset-ack seq=%" PRIu8 " code=0x%" PRIx8,
- context->rxHeader.seq, context->rxHeader.packetCode);
- // In a reset race condition with both endpoints sending resets and
- // reset-acks, the sent resets and reset-acks will both have a sequence
- // number of 0.
- // By ignoring the received reset-ack, the next expected sequence number
- // will remain at 1 (following a reset with a sequence number of 0).
- // Therefore, no further correction is necessary (beyond ignoring the
- // received reset-ack), as the next packet (e.g. discovery) will have a
- // sequence number of 1.
-
- chppDatagramProcessDoneCb(context, context->rxDatagram.payload);
- chppClearRxDatagram(context);
-
- return;
- }
-
- chppSetResetComplete(context);
- context->rxStatus.receivedPacketCode = context->rxHeader.packetCode;
- context->rxStatus.expectedSeq = context->rxHeader.seq + 1;
- chppRegisterRxAck(context);
-
- // TODO: Configure transport layer based on (optional?) received config
- chppDatagramProcessDoneCb(context, context->rxDatagram.payload);
- chppClearRxDatagram(context);
-
-#ifdef CHPP_CLIENT_ENABLED_DISCOVERY
- if (!context->appContext->isDiscoveryComplete) {
- chppMutexUnlock(&context->mutex);
- chppInitiateDiscovery(context->appContext);
- chppMutexLock(&context->mutex);
- } else {
- chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
- }
-#else
- chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
-#endif
-
- // Inform the App Layer that a reset has completed
- chppMutexUnlock(&context->mutex);
- chppAppProcessReset(context->appContext);
- chppMutexLock(&context->mutex);
-}
-
-/**
- * Process a received, checksum-validated packet.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppProcessRxPacket(struct ChppTransportState *context) {
- uint64_t now = chppGetCurrentTimeNs();
- context->rxStatus.lastGoodPacketTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC);
- context->rxStatus.receivedPacketCode = context->rxHeader.packetCode;
- chppRegisterRxAck(context);
-
- enum ChppTransportErrorCode errorCode = CHPP_TRANSPORT_ERROR_NONE;
- if (context->rxHeader.length > 0 &&
- context->rxHeader.seq != context->rxStatus.expectedSeq) {
- // Out of order payload
- errorCode = CHPP_TRANSPORT_ERROR_ORDER;
- }
-
- if (context->txDatagramQueue.pending > 0 ||
- errorCode == CHPP_TRANSPORT_ERROR_ORDER) {
- // There are packets to send out (could be new or retx)
- // Note: For a future ACK window > 1, makes more sense to cap the NACKs
- // to one instead of flooding with out of order NACK errors.
- chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
- CHPP_TRANSPORT_ATTR_NONE, errorCode));
+ // Done with this packet. Wait for next packet
+ chppSetRxState(context, CHPP_STATE_PREAMBLE);
}
- if (errorCode == CHPP_TRANSPORT_ERROR_ORDER) {
- CHPP_LOGE("Out of order RX discarded seq=%" PRIu8 " expect=%" PRIu8
- " len=%" PRIu16,
- context->rxHeader.seq, context->rxStatus.expectedSeq,
- context->rxHeader.length);
- chppAbortRxPacket(context);
-
- } else if (context->rxHeader.length > 0) {
- // Process payload and send ACK
- chppProcessRxPayload(context);
- }
+ return bytesToCopy;
}
/**
* Process the payload of a validated payload-bearing packet and send out the
- * ACK.
+ * ACK
*
- * @param context Maintains status for each transport layer instance.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
*/
static void chppProcessRxPayload(struct ChppTransportState *context) {
- context->rxStatus.expectedSeq++; // chppProcessRxPacket() already confirms
- // that context->rxStatus.expectedSeq ==
- // context->rxHeader.seq, protecting against
- // duplicate and out-of-order packets.
-
if (context->rxHeader.flags & CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM) {
- // Packet is part of a larger datagram
- CHPP_LOGD("RX packet for unfinished datagram. Seq=%" PRIu8 " len=%" PRIu16
- ". Datagram len=%" PRIuSIZE ". Sending ACK=%" PRIu8,
- context->rxHeader.seq, context->rxHeader.length,
- context->rxDatagram.length, context->rxStatus.expectedSeq);
+ // packet is part of a larger datagram
+ LOGD(
+ "Received continuation packet# %d len=%u. Previous fragment(s) "
+ "total len=%zu",
+ context->rxHeader.seq, context->rxHeader.length,
+ context->rxDatagram.length);
} else {
// End of this packet is end of a datagram
+ LOGD(
+ "Received packet# %d len=%u completing a datagram. Previous "
+ "fragment(s) total len=%zu",
+ context->rxHeader.seq, context->rxHeader.length,
+ context->rxDatagram.length);
- // Send the payload to the App Layer
- // Note that it is up to the app layer to free the buffer using
- // chppDatagramProcessDoneCb() after is is done.
- chppMutexUnlock(&context->mutex);
- chppAppProcessRxDatagram(context->appContext, context->rxDatagram.payload,
- context->rxDatagram.length);
- chppMutexLock(&context->mutex);
+ // TODO: do something with the data
- CHPP_LOGD("App layer processed datagram with len=%" PRIuSIZE
- ", ending packet seq=%" PRIu8 ", len=%" PRIu16
- ". Sending ACK=%" PRIu8 " (previously sent=%" PRIu8 ")",
- context->rxDatagram.length, context->rxHeader.seq,
- context->rxHeader.length, context->rxStatus.expectedSeq,
- context->txStatus.sentAckSeq);
- chppClearRxDatagram(context);
+ context->rxStatus.locInDatagram = 0;
+ context->rxDatagram.length = 0;
+ chppFree(context->rxDatagram.payload);
+ context->rxDatagram.payload = NULL;
}
- // Send ACK because we had RX a payload-bearing packet
- chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_NONE);
-}
-
-/**
- * Resets the incoming datagram state, i.e. after the datagram has been
- * processed.
- * Note that this is independent from freeing the payload. It is up to the app
- * layer to inform the transport layer using chppDatagramProcessDoneCb() once it
- * is done with the buffer so it is freed.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppClearRxDatagram(struct ChppTransportState *context) {
- context->rxStatus.locInDatagram = 0;
- context->rxDatagram.length = 0;
- context->rxDatagram.payload = NULL;
+ // Update next expected sequence number and send ACK
+ context->rxStatus.expectedSeq = context->rxHeader.seq + 1;
+ chppEnqueueTxPacket(context, CHPP_ERROR_NONE);
}
/**
* Validates the checksum of an incoming packet.
*
- * @param context Maintains status for each transport layer instance.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
*
- * @return True if and only if the checksum is correct.
+ * @return True if and only if the checksum is correct
*/
static bool chppRxChecksumIsOk(const struct ChppTransportState *context) {
- uint32_t crc = chppCrc32(0, (const uint8_t *)&context->rxHeader,
- sizeof(context->rxHeader));
- crc = chppCrc32(
- crc,
- &context->rxDatagram
- .payload[context->rxStatus.locInDatagram - context->rxHeader.length],
- context->rxHeader.length);
-
-#ifndef CHPP_CHECKSUM_ENABLED
- CHPP_LOGD("Assuming Rx checksum 0x%" PRIx32 " = calculated 0x%" PRIx32,
- context->rxFooter.checksum, crc);
- crc = context->rxFooter.checksum;
-#endif // CHPP_CHECKSUM_ENABLED
-
- if (context->rxFooter.checksum != crc) {
- CHPP_LOGE("Rx BAD checksum: footer=0x%" PRIx32 ", calc=0x%" PRIx32
- ", len=%" PRIuSIZE,
- context->rxFooter.checksum, crc,
- (size_t)(context->rxHeader.length +
- sizeof(struct ChppTransportHeader)));
- }
+ // TODO
+ UNUSED_VAR(context);
- return (context->rxFooter.checksum == crc);
+ LOGE("Blindly assuming checksum is correct");
+ return true;
}
/**
- * Performs consistency checks on received packet header to determine if it is
- * obviously corrupt / invalid / duplicate / out-of-order.
+ * Performs sanity check on received packet header. Discards packet if header is
+ * obviously corrupt / invalid.
*
- * @param context Maintains status for each transport layer instance.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
*
- * @return True if and only if header passes checks
+ * @return True if and only if header passes sanity check
*/
-static enum ChppTransportErrorCode chppRxHeaderCheck(
+static enum ChppErrorCode chppRxHeaderCheck(
const struct ChppTransportState *context) {
- enum ChppTransportErrorCode result = CHPP_TRANSPORT_ERROR_NONE;
+ enum ChppErrorCode result = CHPP_ERROR_NONE;
- if (context->rxHeader.length > CHPP_TRANSPORT_RX_MTU_BYTES) {
- result = CHPP_TRANSPORT_ERROR_HEADER;
+ bool invalidSeqNo = (context->rxHeader.seq != context->rxStatus.expectedSeq);
+ bool hasPayload = (context->rxHeader.length > 0);
+ if (invalidSeqNo && hasPayload) {
+ // Note: For a future ACK window > 1, might make more sense to keep quiet
+ // instead of flooding the sender with out of order NACKs
+ result = CHPP_ERROR_ORDER;
}
- if (result != CHPP_TRANSPORT_ERROR_NONE) {
- CHPP_LOGE("Bad header. seq=%" PRIu8 " expect=%" PRIu8 " len=%" PRIu16
- " err=%" PRIu8,
- context->rxHeader.seq, context->rxStatus.expectedSeq,
- context->rxHeader.length, result);
- }
+ // TODO: More sanity checks
return result;
}
@@ -727,53 +390,27 @@ static enum ChppTransportErrorCode chppRxHeaderCheck(
* Registers a received ACK. If an outgoing datagram is fully ACKed, it is
* popped from the Tx queue.
*
- * @param context Maintains status for each transport layer instance.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
*/
static void chppRegisterRxAck(struct ChppTransportState *context) {
- uint8_t rxAckSeq = context->rxHeader.ackSeq;
-
- if (context->rxStatus.receivedAckSeq != rxAckSeq) {
+ if (context->txStatus.ackedSeq != context->rxHeader.ackSeq) {
// A previously sent packet was actually ACKed
- // Note: For a future ACK window >1, we should loop by # of ACKed packets
- if ((uint8_t)(context->rxStatus.receivedAckSeq + 1) != rxAckSeq) {
- CHPP_LOGE("Out of order ACK: last=%" PRIu8 " rx=%" PRIu8,
- context->rxStatus.receivedAckSeq, rxAckSeq);
- } else {
- CHPP_LOGD(
- "ACK received (last registered=%" PRIu8 ", received=%" PRIu8
- "). Prior queue depth=%" PRIu8 ", front datagram=%" PRIu8
- " at loc=%" PRIuSIZE " of len=%" PRIuSIZE,
- context->rxStatus.receivedAckSeq, rxAckSeq,
- context->txDatagramQueue.pending, context->txDatagramQueue.front,
- context->txStatus.ackedLocInDatagram,
- context->txDatagramQueue.datagram[context->txDatagramQueue.front]
- .length);
-
- context->rxStatus.receivedAckSeq = rxAckSeq;
- if (context->txStatus.txAttempts > 1) {
- CHPP_LOGW("Seq %" PRIu8 " ACK'd after %" PRIuSIZE " reTX",
- context->rxHeader.seq, context->txStatus.txAttempts - 1);
- }
- context->txStatus.txAttempts = 0;
+ context->txStatus.ackedSeq = context->rxHeader.ackSeq;
- // Process and if necessary pop from Tx datagram queue
- context->txStatus.ackedLocInDatagram += CHPP_TRANSPORT_TX_MTU_BYTES;
- if (context->txStatus.ackedLocInDatagram >=
- context->txDatagramQueue.datagram[context->txDatagramQueue.front]
- .length) {
- // We are done with datagram
+ // Process and if necessary pop from Tx datagram queue
+ context->txStatus.ackedLocInDatagram += CHPP_TRANSPORT_MTU_BYTES;
+ if (context->txStatus.ackedLocInDatagram >=
+ context->txDatagramQueue.datagram[context->txDatagramQueue.front]
+ .length) {
+ // We are done with datagram
- context->txStatus.ackedLocInDatagram = 0;
- context->txStatus.sentLocInDatagram = 0;
+ context->txStatus.ackedLocInDatagram = 0;
+ context->txStatus.sentLocInDatagram = 0;
- // Note: For a future ACK window >1, we need to update the queue
- // position of the datagram being sent as well (relative to the
- // front-of-queue). e.g. context->txStatus.datagramBeingSent--;
+ // Note: For a future ACK window >1, we should update which datagram too
- if (chppDequeueTxDatagram(context) == 0) {
- context->txStatus.hasPacketsToSend = false;
- }
- }
+ chppDequeueTxDatagram(context);
}
} // else {nothing was ACKed}
}
@@ -781,8 +418,8 @@ static void chppRegisterRxAck(struct ChppTransportState *context) {
/**
* Enqueues an outgoing packet with the specified error code. The error code
* refers to the optional reason behind a NACK, if any. An error code of
- * CHPP_TRANSPORT_ERROR_NONE indicates that no error was reported (i.e. either
- * an ACK or an implicit NACK)
+ * CHPP_ERROR_NONE indicates that no error was reported (i.e. either an ACK or
+ * an implicit NACK)
*
* Note that the decision as to wheather to include a payload will be taken
* later, i.e. before the packet is being sent out from the queue. A payload is
@@ -794,165 +431,86 @@ static void chppRegisterRxAck(struct ChppTransportState *context) {
* would only need to send an ACK for the last (correct) packet, hence we only
* need a queue length of one here.
*
- * @param context Maintains status for each transport layer instance.
- * @param packetCode Error code and packet attributes to be sent.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
+ * @param errorCode Error code for the next outgoing packet
*/
static void chppEnqueueTxPacket(struct ChppTransportState *context,
- uint8_t packetCode) {
+ enum ChppErrorCode errorCode) {
context->txStatus.hasPacketsToSend = true;
- context->txStatus.packetCodeToSend = packetCode;
-
- CHPP_LOGD("chppEnqueueTxPacket called with packet code=0x%" PRIx8,
- packetCode);
+ context->txStatus.errorCodeToSend = errorCode;
- // Notifies the main CHPP Transport Layer to run chppTransportDoWork().
- chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EVENT);
+ // TODO: Notify chppTransportDoWork
}
/**
- * Adds a CHPP preamble to the beginning of buf.
+ * Adds a CHPP preamble to the beginning of buf
*
- * @param buf The CHPP preamble will be added to buf.
+ * @param buf The CHPP preamble will be added to buf
*
- * @return Size of the added preamble.
+ * @return Size of the added preamble
*/
static size_t chppAddPreamble(uint8_t *buf) {
- buf[0] = CHPP_PREAMBLE_BYTE_FIRST;
- buf[1] = CHPP_PREAMBLE_BYTE_SECOND;
+ for (size_t i = 0; i < CHPP_PREAMBLE_LEN_BYTES; i++) {
+ buf[i] = (uint8_t)(CHPP_PREAMBLE_DATA >> (CHPP_PREAMBLE_LEN_BYTES - 1 - i) &
+ 0xff);
+ }
return CHPP_PREAMBLE_LEN_BYTES;
}
/**
- * Adds the packet header to pendingTxPacket.
+ * Calculates the checksum on a buffer indicated by buf with length len
*
- * @param context Maintains status for each transport layer instance.
+ * @param buf Pointer to buffer for the ckecksum to be calculated on.
+ * @param len Length of the buffer.
*
- * @return Pointer to the added packet header.
+ * @return Calculated checksum.
*/
-static struct ChppTransportHeader *chppAddHeader(
- struct ChppTransportState *context) {
- struct ChppTransportHeader *txHeader =
- (struct ChppTransportHeader *)&context->pendingTxPacket
- .payload[context->pendingTxPacket.length];
- context->pendingTxPacket.length += sizeof(*txHeader);
-
- txHeader->packetCode = context->txStatus.packetCodeToSend;
- context->txStatus.packetCodeToSend = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
- context->txStatus.packetCodeToSend, CHPP_TRANSPORT_ERROR_NONE);
-
- txHeader->ackSeq = context->rxStatus.expectedSeq;
- context->txStatus.sentAckSeq = txHeader->ackSeq;
+static uint32_t chppCalculateChecksum(uint8_t *buf, size_t len) {
+ // TODO
- return txHeader;
-}
-
-/**
- * Adds the packet payload to pendingTxPacket.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppAddPayload(struct ChppTransportState *context) {
- struct ChppTransportHeader *txHeader =
- (struct ChppTransportHeader *)&context->pendingTxPacket
- .payload[CHPP_PREAMBLE_LEN_BYTES];
-
- size_t remainingBytes =
- context->txDatagramQueue.datagram[context->txDatagramQueue.front].length -
- context->txStatus.ackedLocInDatagram;
-
- CHPP_LOGD("Adding payload to seq=%" PRIu8 ", remainingBytes=%" PRIuSIZE
- " of pending datagrams=%" PRIu8,
- txHeader->seq, remainingBytes, context->txDatagramQueue.pending);
-
- if (remainingBytes > CHPP_TRANSPORT_TX_MTU_BYTES) {
- // Send an unfinished part of a datagram
- txHeader->flags = CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM;
- txHeader->length = CHPP_TRANSPORT_TX_MTU_BYTES;
- } else {
- // Send final (or only) part of a datagram
- txHeader->flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM;
- txHeader->length = (uint16_t)remainingBytes;
- }
-
- // Copy payload
- chppAppendToPendingTxPacket(
- &context->pendingTxPacket,
- context->txDatagramQueue.datagram[context->txDatagramQueue.front]
- .payload +
- context->txStatus.ackedLocInDatagram,
- txHeader->length);
-
- context->txStatus.sentLocInDatagram =
- context->txStatus.ackedLocInDatagram + txHeader->length;
-}
-
-/**
- * Adds a footer (containing the checksum) to a packet.
- *
- * @param packet The packet from which to calculate the checksum and append the
- * footer.
- */
-static void chppAddFooter(struct PendingTxPacket *packet) {
- struct ChppTransportFooter footer;
- footer.checksum = chppCrc32(0, &packet->payload[CHPP_PREAMBLE_LEN_BYTES],
- packet->length - CHPP_PREAMBLE_LEN_BYTES);
-
- CHPP_LOGD("Adding transport footer. Checksum=0x%" PRIx32 ", len: %" PRIuSIZE
- " -> %" PRIuSIZE,
- footer.checksum, packet->length, packet->length + sizeof(footer));
-
- chppAppendToPendingTxPacket(packet, (const uint8_t *)&footer, sizeof(footer));
+ UNUSED_VAR(buf);
+ UNUSED_VAR(len);
+ return 1;
}
/**
* Dequeues the datagram at the front of the datagram tx queue, if any, and
- * frees the payload. Returns the number of remaining datagrams in the queue.
+ * frees the payload. Returns false if the queue is empty.
*
- * @param context Maintains status for each transport layer instance.
- * @return Number of remaining datagrams in queue.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
+ * @return True indicates success. False indicates failure, i.e. the queue was
+ * empty.
*/
-size_t chppDequeueTxDatagram(struct ChppTransportState *context) {
- if (context->txDatagramQueue.pending == 0) {
- CHPP_LOGE("Can not dequeue datagram because queue is empty");
+bool chppDequeueTxDatagram(struct ChppTransportState *context) {
+ bool success = false;
- } else {
- CHPP_LOGD("Dequeuing front datagram with index=%" PRIu8 ", len=%" PRIuSIZE
- ". Queue depth: %" PRIu8 "->%d",
- context->txDatagramQueue.front,
- context->txDatagramQueue.datagram[context->txDatagramQueue.front]
- .length,
- context->txDatagramQueue.pending,
- context->txDatagramQueue.pending - 1);
-
- CHPP_FREE_AND_NULLIFY(
- context->txDatagramQueue.datagram[context->txDatagramQueue.front]
- .payload);
+ if (context->txDatagramQueue.pending > 0) {
+ chppFree(context->txDatagramQueue.datagram[context->txDatagramQueue.front]
+ .payload);
+ context->txDatagramQueue.datagram[context->txDatagramQueue.front].payload =
+ NULL;
context->txDatagramQueue.datagram[context->txDatagramQueue.front].length =
0;
context->txDatagramQueue.pending--;
context->txDatagramQueue.front++;
context->txDatagramQueue.front %= CHPP_TX_DATAGRAM_QUEUE_LEN;
- }
- return context->txDatagramQueue.pending;
-}
+ // Note: For a future ACK window >1, we need to update the queue position of
+ // the datagram being sent as well (relative to the front-of-queue). i.e.
+ // context->txStatus.datagramBeingSent--;
-/**
- * Flushes the Tx datagram queue of any pending packets.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppClearTxDatagramQueue(struct ChppTransportState *context) {
- while (context->txDatagramQueue.pending > 0) {
- chppDequeueTxDatagram(context);
+ success = true;
}
- context->txStatus.hasPacketsToSend = false;
+
+ return success;
}
/**
* Sends out a pending outgoing packet based on a notification from
- * chppEnqueueTxPacket().
+ * chppEnqueueTxPacket.
*
* A payload may or may not be included be according the following:
* No payload: If Tx datagram queue is empty OR we are waiting on a pending ACK.
@@ -961,428 +519,115 @@ static void chppClearTxDatagramQueue(struct ChppTransportState *context) {
* Repeat payload: If we haven't received an ACK yet for our previous payload,
* i.e. we have registered an explicit or implicit NACK.
*
- * @param context Maintains status for each transport layer instance.
+ * @param context Is used to maintain status. Must be provided and initialized
+ * through chppTransportInit for each transport layer instance. Cannot be null.
*/
-static void chppTransportDoWork(struct ChppTransportState *context) {
- bool havePacketForLinkLayer = false;
- struct ChppTransportHeader *txHeader;
- struct ChppAppHeader *timeoutResponse = NULL;
-
+void chppTransportDoWork(struct ChppTransportState *context) {
// Note: For a future ACK window >1, there needs to be a loop outside the lock
+
chppMutexLock(&context->mutex);
- if (context->txStatus.hasPacketsToSend && !context->txStatus.linkBusy) {
- // There are pending outgoing packets and the link isn't busy
- havePacketForLinkLayer = true;
- context->txStatus.linkBusy = true;
+ if (context->txStatus.hasPacketsToSend) {
+ // There are pending outgoing packets
+
+ // Lock linkLayerMutex before modifying packetToSend
+ chppMutexLock(&context->linkLayerMutex);
- context->pendingTxPacket.length = 0;
- memset(&context->pendingTxPacket.payload, 0, CHPP_LINK_TX_MTU_BYTES);
+ context->packetToSend.length = 0;
+ memset(&context->packetToSend.payload, 0, CHPP_LINK_MTU_BYTES);
// Add preamble
- context->pendingTxPacket.length +=
- chppAddPreamble(&context->pendingTxPacket.payload[0]);
+ context->packetToSend.length +=
+ chppAddPreamble(&context->packetToSend.payload[0]);
// Add header
- txHeader = chppAddHeader(context);
+ struct ChppTransportHeader *txHeader =
+ (struct ChppTransportHeader *)&context->packetToSend
+ .payload[context->packetToSend.length];
+ context->packetToSend.length += sizeof(*txHeader);
+
+ txHeader->errorCode = context->txStatus.errorCodeToSend;
+ txHeader->ackSeq = context->rxStatus.expectedSeq;
// If applicable, add payload
- if ((context->txDatagramQueue.pending > 0)) {
- // Note: For a future ACK window >1, we need to rewrite this payload
- // adding code to base the next packet on the sent location within the
- // last sent datagram, except for the case of a NACK (explicit or
- // timeout). For a NACK, we would need to base the next packet off the
- // last ACKed location.
-
- txHeader->seq = context->rxStatus.receivedAckSeq;
+ if ((context->txDatagramQueue.pending > 0) &&
+ (context->txStatus.sentSeq == context->txStatus.ackedSeq)) {
+ // Note: For a future ACK window >1, seq # check should be against the
+ // window size.
+
+ // Note: For a future ACK window >1, this is only valid for the
+ // (context->rxStatus.receivedErrorCode != CHPP_ERROR_NONE) case,
+ // i.e. we have registered an explicit or implicit NACK. Else,
+ // txHeader->seq = ++(context->txStatus.sentSeq)
+ txHeader->seq = context->txStatus.ackedSeq + 1;
context->txStatus.sentSeq = txHeader->seq;
- if (context->txStatus.txAttempts > CHPP_TRANSPORT_MAX_RETX &&
- context->resetState != CHPP_RESET_STATE_RESETTING) {
- CHPP_LOGE("Resetting after %d retries", CHPP_TRANSPORT_MAX_RETX);
- havePacketForLinkLayer = false;
+ size_t remainingBytes =
+ context->txDatagramQueue.datagram[context->txDatagramQueue.front]
+ .length -
+ context->txStatus.sentLocInDatagram;
- chppMutexUnlock(&context->mutex);
- chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
- CHPP_TRANSPORT_ERROR_MAX_RETRIES);
- chppMutexLock(&context->mutex);
+ if (remainingBytes > CHPP_TRANSPORT_MTU_BYTES) {
+ // Send an unfinished part of a datagram
+ txHeader->flags = CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM;
+ txHeader->length = CHPP_TRANSPORT_MTU_BYTES;
} else {
- chppAddPayload(context);
- context->txStatus.txAttempts++;
+ // Send final (or only) part of a datagram
+ txHeader->flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM;
+ txHeader->length = remainingBytes;
}
- } else {
- // No payload
- context->txStatus.hasPacketsToSend = false;
- }
+ // Copy payload
+ memcpy(&context->packetToSend.payload[context->packetToSend.length],
+ context->txDatagramQueue.datagram[context->txDatagramQueue.front]
+ .payload +
+ context->txStatus.sentLocInDatagram,
+ txHeader->length);
+ context->packetToSend.length += txHeader->length;
- chppAddFooter(&context->pendingTxPacket);
+ context->txStatus.sentLocInDatagram += txHeader->length;
- } else {
- CHPP_LOGW(
- "DoWork nothing to send. hasPackets=%d, linkBusy=%d, pending=%" PRIu8
- ", Rx ACK=%" PRIu8 ", Tx seq=%" PRIu8 ", RX state=%" PRIu8,
- context->txStatus.hasPacketsToSend, context->txStatus.linkBusy,
- context->txDatagramQueue.pending, context->rxStatus.receivedAckSeq,
- context->txStatus.sentSeq, context->rxStatus.state);
- }
+ } // else {no payload}
- chppMutexUnlock(&context->mutex);
+ // Note: For a future ACK window >1, this needs to be updated
+ context->txStatus.hasPacketsToSend = false;
- if (havePacketForLinkLayer) {
- CHPP_LOGD("TX->Link: len=%" PRIuSIZE " flags=0x%" PRIx8 " code=0x%" PRIx8
- " ackSeq=%" PRIu8 " seq=%" PRIu8 " payloadLen=%" PRIu16
- " pending=%" PRIu8,
- context->pendingTxPacket.length, txHeader->flags,
- txHeader->packetCode, txHeader->ackSeq, txHeader->seq,
- txHeader->length, context->txDatagramQueue.pending);
- enum ChppLinkErrorCode error = chppSendPendingPacket(context);
-
- if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
- // Platform implementation for platformLinkSend() is synchronous or an
- // error occurred. In either case, we should call chppLinkSendDoneCb()
- // here to release the contents of pendingTxPacket.
- chppLinkSendDoneCb(&context->linkParams, error);
- }
- }
+ // We are done with context. Unlock mutex ASAP.
+ chppMutexUnlock(&context->mutex);
-#ifdef CHPP_CLIENT_ENABLED
- timeoutResponse = chppTransportGetClientRequestTimeoutResponse(context);
-#endif
- if (timeoutResponse != NULL) {
- CHPP_LOGE("Response timeout H#%" PRIu8 " cmd=%" PRIu16 " ID=%" PRIu8,
- timeoutResponse->handle, timeoutResponse->command,
- timeoutResponse->transaction);
- chppAppProcessRxDatagram(context->appContext, (uint8_t *)timeoutResponse,
- sizeof(struct ChppAppHeader));
- }
-}
+ // Populate checksum
+ uint32_t *checksum = (uint32_t *)&context->packetToSend
+ .payload[context->packetToSend.length];
+ context->packetToSend.length += sizeof(*checksum);
+ *checksum = chppCalculateChecksum(context->packetToSend.payload,
+ context->packetToSend.length);
-/**
- * Appends data from a buffer of length len to a PendingTxPacket, updating its
- * length.
- *
- * @param packet The PendingTxBuffer to be appended to.
- * @param buf Input data to be copied from.
- * @param len Length of input data in bytes.
- */
-static void chppAppendToPendingTxPacket(struct PendingTxPacket *packet,
- const uint8_t *buf, size_t len) {
- CHPP_ASSERT(packet->length + len <= sizeof(packet->payload));
- memcpy(&packet->payload[packet->length], buf, len);
- packet->length += len;
-}
+ // TODO: Send out notification to function that sends out the packet.
+ // context->linkLayerMutex must be unlocked by the function that is actually
+ // sending out the packet, and only after it is done sending.
-/**
- * @return A human readable form of the packet attribution.
- */
-static const char *chppGetPacketAttrStr(uint8_t packetCode) {
- switch (CHPP_TRANSPORT_GET_ATTR(packetCode)) {
- case CHPP_TRANSPORT_ATTR_RESET:
- return "(RESET)";
- case CHPP_TRANSPORT_ATTR_RESET_ACK:
- return "(RESET-ACK)";
- case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
- return "(LOOP-REQ)";
- case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
- return "(LOOP-RES)";
- default:
- return "";
- }
-}
+ // TODO: Do we even need linkLayerMutex? We'll see once the new approach
+ // to signalling is in.
-/**
- * Enqueues an outgoing datagram of a specified length. The payload must have
- * been allocated by the caller using chppMalloc.
- *
- * If enqueueing is successful, the payload will be freed by this function
- * once it has been sent out.
- * If enqueueing is unsuccessful, it is up to the caller to decide when or if
- * to free the payload and/or resend it later.
- *
- * @param context Maintains status for each transport layer instance.
- * @param packetCode Error code and packet attributes to be sent.
- * @param buf Datagram payload allocated through chppMalloc. Cannot be null.
- * @param len Datagram length in bytes.
- *
- * @return True informs the sender that the datagram was successfully enqueued.
- * False informs the sender that the queue was full.
- */
-static bool chppEnqueueTxDatagram(struct ChppTransportState *context,
- uint8_t packetCode, void *buf, size_t len) {
- bool success = false;
-
- if (len == 0) {
- CHPP_LOGE("Enqueue tx with len 0");
- CHPP_DEBUG_ASSERT(false);
+ // TODO: For now, unlocking here, but remove once above is addressed
+ chppMutexUnlock(&context->linkLayerMutex);
} else {
- if ((len < sizeof(struct ChppAppHeader)) ||
- (CHPP_TRANSPORT_GET_ATTR(packetCode) != 0)) {
- CHPP_LOGI("Enqueue TX: code=0x%" PRIx8 "%s len=%" PRIuSIZE
- " pending=%" PRIu8,
- packetCode, chppGetPacketAttrStr(packetCode), len,
- (uint8_t)(context->txDatagramQueue.pending + 1));
- } else {
- struct ChppAppHeader *header = buf;
- CHPP_LOGI(
- "Enqueue TX: len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
- " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16 " pending=%" PRIu8,
- len, header->handle, header->type, header->transaction, header->error,
- header->command, (uint8_t)(context->txDatagramQueue.pending + 1));
- }
-
- chppMutexLock(&context->mutex);
-
- if (context->txDatagramQueue.pending >= CHPP_TX_DATAGRAM_QUEUE_LEN) {
- CHPP_LOGE("Cannot enqueue TX datagram");
-
- } else {
- uint16_t end =
- (context->txDatagramQueue.front + context->txDatagramQueue.pending) %
- CHPP_TX_DATAGRAM_QUEUE_LEN;
- context->txDatagramQueue.datagram[end].length = len;
- context->txDatagramQueue.datagram[end].payload = buf;
- context->txDatagramQueue.pending++;
-
- if (context->txDatagramQueue.pending == 1) {
- // Queue was empty prior. Need to kickstart transmission.
- chppEnqueueTxPacket(context, packetCode);
- }
-
- success = true;
- }
-
+ // There are no pending outgoing packets. Unlock mutex.
chppMutexUnlock(&context->mutex);
}
-
- return success;
}
-/**
- * Sends the pending outgoing packet (context->pendingTxPacket) over to the link
- * layer using chppPlatformLinkSend() and updates the last Tx packet time.
- *
- * @param context Maintains status for each transport layer instance.
- *
- * @return Result of chppPlatformLinkSend().
- */
-enum ChppLinkErrorCode chppSendPendingPacket(
- struct ChppTransportState *context) {
- enum ChppLinkErrorCode error = chppPlatformLinkSend(
- &context->linkParams, context->pendingTxPacket.payload,
- context->pendingTxPacket.length);
-
- context->txStatus.lastTxTimeNs = chppGetCurrentTimeNs();
-
- return error;
-}
-
-/**
- * Resets the transport state, maintaining the link layer parameters.
- *
- * @param context Maintains status for each transport layer instance.
- */
-static void chppResetTransportContext(struct ChppTransportState *context) {
- memset(&context->rxStatus, 0, sizeof(struct ChppRxStatus));
- memset(&context->rxDatagram, 0, sizeof(struct ChppDatagram));
-
- memset(&context->txStatus, 0, sizeof(struct ChppTxStatus));
- memset(&context->txDatagramQueue, 0, sizeof(struct ChppTxDatagramQueue));
-
- context->txStatus.sentSeq =
- UINT8_MAX; // So that the seq # of the first TX packet is 0
- context->resetState = CHPP_RESET_STATE_RESETTING;
-}
-
-/**
- * Re-initializes the CHPP transport and app layer states, e.g. when receiving a
- * reset packet, and sends out a reset or reset-ack packet over the link in
- * order to reset the remote side or inform the counterpart of a reset,
- * respectively.
- *
- * If the link layer is busy, this function will reset the link as well.
- * This function retains and restores the platform-specific values of
- * transportContext.linkParams.
- *
- * @param transportContext Maintains status for each transport layer instance.
- * @param resetType Type of reset to send after resetting CHPP (reset vs.
- * reset-ack), as defined in the ChppTransportPacketAttributes struct.
- * @param error Provides the error that led to the reset.
- */
-static void chppReset(struct ChppTransportState *transportContext,
- enum ChppTransportPacketAttributes resetType,
- enum ChppTransportErrorCode error) {
- // TODO: Configure transport layer based on (optional?) received config before
- // datagram is wiped
-
- chppMutexLock(&transportContext->mutex);
- struct ChppAppState *appContext = transportContext->appContext;
- transportContext->resetState = CHPP_RESET_STATE_RESETTING;
-
- // Reset asynchronous link layer if busy
- if (transportContext->txStatus.linkBusy == true) {
- // TODO: Give time for link layer to finish before resorting to a reset
-
- chppPlatformLinkReset(&transportContext->linkParams);
- }
-
- // Free memory allocated for any ongoing rx datagrams
- if (transportContext->rxDatagram.length > 0) {
- transportContext->rxDatagram.length = 0;
- CHPP_FREE_AND_NULLIFY(transportContext->rxDatagram.payload);
- }
-
- // Free memory allocated for any ongoing tx datagrams
- for (size_t i = 0; i < CHPP_TX_DATAGRAM_QUEUE_LEN; i++) {
- if (transportContext->txDatagramQueue.datagram[i].length > 0) {
- CHPP_FREE_AND_NULLIFY(
- transportContext->txDatagramQueue.datagram[i].payload);
- }
- }
-
- // Reset Transport Layer but restore Rx sequence number and packet code
- // (context->rxHeader is not wiped in reset)
- chppResetTransportContext(transportContext);
- transportContext->rxStatus.receivedPacketCode =
- transportContext->rxHeader.packetCode;
- transportContext->rxStatus.expectedSeq = transportContext->rxHeader.seq + 1;
-
- // Send reset or reset-ACK
- chppMutexUnlock(&transportContext->mutex);
- chppTransportSendReset(transportContext, resetType, error);
-
- // Inform the App Layer that a reset has completed
- if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
- chppAppProcessReset(appContext);
- } // else reset is sent out. Rx of reset-ack will indicate completion.
-}
-
-/**
- * Checks for a timed out client request and generates a timeout response if a
- * client request timeout has occurred.
- *
- * @param context Maintains status for each transport layer instance.
- * @return App layer response header if a timeout has occurred. Null otherwise.
- */
-#ifdef CHPP_CLIENT_ENABLED
-struct ChppAppHeader *chppTransportGetClientRequestTimeoutResponse(
- struct ChppTransportState *context) {
- struct ChppAppHeader *response = NULL;
-
- bool timeoutClientFound = false;
- uint8_t timedOutClient;
- uint16_t timedOutCmd;
-
- chppMutexLock(&context->mutex);
-
- if (context->appContext->nextRequestTimeoutNs <= chppGetCurrentTimeNs()) {
- // Determine which request has timed out
-
- uint64_t lowestTimeout = CHPP_TIME_MAX;
- for (uint8_t clientIdx = 0;
- clientIdx < context->appContext->registeredClientCount; clientIdx++) {
- for (uint16_t cmdIdx = 0;
- cmdIdx <
- context->appContext->registeredClients[clientIdx]->rRStateCount;
- cmdIdx++) {
- struct ChppRequestResponseState *rRState =
- &context->appContext->registeredClientStates[clientIdx]
- ->rRStates[cmdIdx];
-
- if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT &&
- rRState->responseTimeNs != CHPP_TIME_NONE &&
- rRState->responseTimeNs < lowestTimeout) {
- lowestTimeout = rRState->responseTimeNs;
- timedOutClient = clientIdx;
- timedOutCmd = cmdIdx;
- timeoutClientFound = true;
- }
- }
- }
-
- if (!timeoutClientFound) {
- CHPP_LOGE("Timeout at %" PRIu64 " but no client",
- context->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
- chppClientRecalculateNextTimeout(context->appContext);
- }
- }
-
- if (timeoutClientFound) {
- CHPP_LOGE("Client=%" PRIu8 " cmd=%" PRIu16 " timed out", timedOutClient,
- timedOutCmd);
- response = chppMalloc(sizeof(struct ChppAppHeader));
- if (response == NULL) {
- CHPP_LOG_OOM();
- } else {
- response->handle = CHPP_SERVICE_HANDLE_OF_INDEX(timedOutClient);
- response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
- response->transaction =
- context->appContext->registeredClientStates[timedOutClient]
- ->rRStates[timedOutCmd]
- .transaction;
- response->error = CHPP_APP_ERROR_TIMEOUT;
- response->command = timedOutCmd;
- }
- }
-
- chppMutexUnlock(&context->mutex);
-
- return response;
-}
-#endif
-
/************************************************
* Public Functions
***********************************************/
-void chppTransportInit(struct ChppTransportState *transportContext,
- struct ChppAppState *appContext) {
- CHPP_NOT_NULL(transportContext);
- CHPP_NOT_NULL(appContext);
- CHPP_ASSERT_LOG(!transportContext->initialized,
- "CHPP transport already initialized");
-
- CHPP_LOGD("Initializing CHPP transport");
-
- chppResetTransportContext(transportContext);
- chppMutexInit(&transportContext->mutex);
- chppNotifierInit(&transportContext->notifier);
- chppConditionVariableInit(&transportContext->resetCondVar);
-
- transportContext->appContext = appContext;
- transportContext->initialized = true;
-
- chppPlatformLinkInit(&transportContext->linkParams);
-}
-
-void chppTransportDeinit(struct ChppTransportState *transportContext) {
- CHPP_NOT_NULL(transportContext);
- CHPP_ASSERT_LOG(transportContext->initialized,
- "CHPP transport already deinitialized");
-
- chppPlatformLinkDeinit(&transportContext->linkParams);
- chppConditionVariableDeinit(&transportContext->resetCondVar);
- chppNotifierDeinit(&transportContext->notifier);
- chppMutexDeinit(&transportContext->mutex);
-
- chppClearTxDatagramQueue(transportContext);
-
- transportContext->initialized = false;
-}
+void chppTransportInit(struct ChppTransportState *context) {
+ CHPP_NOT_NULL(context);
-bool chppTransportWaitForResetComplete(
- struct ChppTransportState *transportContext, uint64_t timeoutMs) {
- bool success = true;
- chppMutexLock(&transportContext->mutex);
- while (success && transportContext->resetState != CHPP_RESET_STATE_NONE) {
- success = chppConditionVariableTimedWait(&transportContext->resetCondVar,
- &transportContext->mutex,
- timeoutMs * CHPP_NSEC_PER_MSEC);
- }
- chppMutexUnlock(&transportContext->mutex);
- return success;
+ memset(context, 0, sizeof(struct ChppTransportState));
+ chppMutexInit(&context->mutex);
}
bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
@@ -1390,20 +635,8 @@ bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
CHPP_NOT_NULL(buf);
CHPP_NOT_NULL(context);
- chppMutexLock(&context->mutex);
- if (context->rxStatus.state != CHPP_STATE_PREAMBLE &&
- chppGetCurrentTimeNs() >
- context->rxStatus.packetStartTimeNs + CHPP_TRANSPORT_RX_TIMEOUT_NS) {
- CHPP_LOGE("Packet RX timeout");
- chppAbortRxPacket(context);
- }
- chppMutexUnlock(&context->mutex);
-
- CHPP_LOGD("RX %" PRIuSIZE " bytes: state=%" PRIu8, len,
- context->rxStatus.state);
- uint64_t now = chppGetCurrentTimeNs();
- context->rxStatus.lastDataTimeMs = (uint32_t)(now / CHPP_NSEC_PER_MSEC);
- context->rxStatus.numTotalDataBytes += len;
+ LOGD("chppRxDataCb received %zu bytes (state = %d)", len,
+ context->rxStatus.state);
size_t consumed = 0;
while (consumed < len) {
@@ -1432,11 +665,13 @@ bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
break;
default:
- CHPP_LOGE("Invalid RX state %" PRIu8, context->rxStatus.state);
- CHPP_DEBUG_ASSERT(false);
+ LOGE("Invalid state %d", context->rxStatus.state);
chppSetRxState(context, CHPP_STATE_PREAMBLE);
}
+ LOGD("chppRxDataCb consumed %zu of %zu bytes (state = %d)", consumed, len,
+ context->rxStatus.state);
+
chppMutexUnlock(&context->mutex);
}
@@ -1444,308 +679,41 @@ bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
context->rxStatus.locInState == 0);
}
-void chppRxPacketCompleteCb(struct ChppTransportState *context) {
+void chppTxTimeoutTimerCb(struct ChppTransportState *context) {
chppMutexLock(&context->mutex);
- if (context->rxStatus.state != CHPP_STATE_PREAMBLE) {
- CHPP_LOGE("Rx pkt ended early: state=%" PRIu8 " packet=%" PRIu8
- " len=%" PRIu16,
- context->rxStatus.state, context->rxHeader.seq,
- context->rxHeader.length);
- chppAbortRxPacket(context);
- chppEnqueueTxPacket(context, CHPP_TRANSPORT_ERROR_HEADER); // NACK
- }
- chppMutexUnlock(&context->mutex);
-}
-bool chppEnqueueTxDatagramOrFail(struct ChppTransportState *context, void *buf,
- size_t len) {
- bool success = false;
- bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
+ // Implicit NACK. Set received error code accordingly
+ context->rxStatus.receivedErrorCode = CHPP_ERROR_TIMEOUT;
- if (len == 0) {
- CHPP_LOGE("Enqueue datagram len 0");
- CHPP_DEBUG_ASSERT(false);
-
- } else if (resetting || !chppEnqueueTxDatagram(
- context, CHPP_TRANSPORT_ERROR_NONE, buf, len)) {
- uint8_t *handle = buf;
- CHPP_LOGE("Resetting=%d. Discarding %" PRIuSIZE " bytes for H#%" PRIu8,
- resetting, len, *handle);
-
- CHPP_FREE_AND_NULLIFY(buf);
-
- } else {
- success = true;
- }
-
- return success;
-}
-
-// TODO(b/192359485): Consider removing this function, or making it more robust.
-void chppEnqueueTxErrorDatagram(struct ChppTransportState *context,
- enum ChppTransportErrorCode errorCode) {
- bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
- if (resetting) {
- CHPP_LOGE("Discarding app error 0x%" PRIx8 " (resetting)", errorCode);
- } else {
- switch (errorCode) {
- case CHPP_TRANSPORT_ERROR_OOM: {
- CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_OOM");
- break;
- }
- case CHPP_TRANSPORT_ERROR_APPLAYER: {
- CHPP_LOGD("App layer enqueueing CHPP_TRANSPORT_ERROR_APPLAYER");
- break;
- }
- default: {
- // App layer should not invoke any other errors
- CHPP_LOGE("App enqueueing invalid err=%" PRIu8, errorCode);
- CHPP_DEBUG_ASSERT(false);
- }
- }
- chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
- CHPP_TRANSPORT_ATTR_NONE, errorCode));
- }
-}
-
-uint64_t chppTransportGetTimeUntilNextDoWorkNs(
- struct ChppTransportState *context) {
- uint64_t currentTime = chppGetCurrentTimeNs();
- uint64_t nextDoWorkTime = context->appContext->nextRequestTimeoutNs;
-
- if (context->txStatus.hasPacketsToSend ||
- context->resetState == CHPP_RESET_STATE_RESETTING) {
- nextDoWorkTime =
- MIN(nextDoWorkTime, CHPP_TRANSPORT_TX_TIMEOUT_NS +
- ((context->txStatus.lastTxTimeNs == 0)
- ? currentTime
- : context->txStatus.lastTxTimeNs));
- }
-
- CHPP_LOGD("NextDoWork=%" PRIu64 " currentTime=%" PRIu64 " delta=%" PRId64,
- nextDoWorkTime / CHPP_NSEC_PER_MSEC,
- currentTime / CHPP_NSEC_PER_MSEC,
- (nextDoWorkTime - currentTime) / (int64_t)CHPP_NSEC_PER_MSEC);
-
- if (nextDoWorkTime == CHPP_TIME_MAX) {
- return CHPP_TRANSPORT_TIMEOUT_INFINITE;
- } else if (nextDoWorkTime <= currentTime) {
- return CHPP_TRANSPORT_TIMEOUT_IMMEDIATE;
- } else {
- return nextDoWorkTime - currentTime;
- }
-}
-
-void chppWorkThreadStart(struct ChppTransportState *context) {
- chppTransportSendReset(context, CHPP_TRANSPORT_ATTR_RESET,
- CHPP_TRANSPORT_ERROR_NONE);
- CHPP_LOGD("CHPP Work Thread started");
-
- uint32_t signals;
- do {
- uint64_t timeout = chppTransportGetTimeUntilNextDoWorkNs(context);
- if (timeout == CHPP_TRANSPORT_TIMEOUT_IMMEDIATE) {
- signals = chppNotifierGetSignal(&context->notifier);
- } else if (timeout == CHPP_TRANSPORT_TIMEOUT_INFINITE) {
- signals = chppNotifierWait(&context->notifier);
- } else {
- signals = chppNotifierTimedWait(&context->notifier, timeout);
- }
-
- } while (chppWorkThreadHandleSignal(context, signals));
-}
-
-bool chppWorkThreadHandleSignal(struct ChppTransportState *context,
- uint32_t signals) {
- if (signals & CHPP_TRANSPORT_SIGNAL_EXIT) {
- CHPP_LOGD("CHPP Work Thread terminated");
- return false;
- }
-
- if (signals & CHPP_TRANSPORT_SIGNAL_EVENT) {
- chppTransportDoWork(context);
- }
-
- if (signals == 0) {
- // Triggered by timeout
-
- if (chppGetCurrentTimeNs() - context->txStatus.lastTxTimeNs >=
- CHPP_TRANSPORT_TX_TIMEOUT_NS) {
- CHPP_LOGE("ACK timeout. Tx t=%" PRIu64,
- context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC);
- chppTransportDoWork(context);
- }
-
- if ((context->resetState == CHPP_RESET_STATE_RESETTING) &&
- (chppGetCurrentTimeNs() - context->resetTimeNs >=
- CHPP_TRANSPORT_RESET_TIMEOUT_NS)) {
- if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) {
- CHPP_LOGE("RESET-ACK timeout; retrying");
- context->resetCount++;
- chppReset(context, CHPP_TRANSPORT_ATTR_RESET,
- CHPP_TRANSPORT_ERROR_TIMEOUT);
- } else {
- CHPP_LOGE("RESET-ACK timeout; giving up");
- context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE;
- chppClearTxDatagramQueue(context);
- }
- }
- }
-
- if (signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK) {
- chppPlatformLinkDoWork(&context->linkParams,
- signals & CHPP_TRANSPORT_SIGNAL_PLATFORM_MASK);
- }
-
- return true;
-}
-
-void chppWorkThreadStop(struct ChppTransportState *context) {
- chppNotifierSignal(&context->notifier, CHPP_TRANSPORT_SIGNAL_EXIT);
-}
-
-void chppLinkSendDoneCb(struct ChppPlatformLinkParameters *params,
- enum ChppLinkErrorCode error) {
- if (error != CHPP_LINK_ERROR_NONE_SENT) {
- CHPP_LOGE("Async send failure: %" PRIu8, error);
- }
-
- struct ChppTransportState *context =
- container_of(params, struct ChppTransportState, linkParams);
-
- chppMutexLock(&context->mutex);
-
- context->txStatus.linkBusy = false;
-
- // No need to free anything as pendingTxPacket.payload is static. Likewise, we
- // keep pendingTxPacket.length to assist testing.
+ // Enqueue Tx packet which will be a retransmission based on the above
+ chppEnqueueTxPacket(context, CHPP_ERROR_NONE);
chppMutexUnlock(&context->mutex);
}
-void chppDatagramProcessDoneCb(struct ChppTransportState *context,
- uint8_t *buf) {
- UNUSED_VAR(context);
-
- CHPP_FREE_AND_NULLIFY(buf);
-}
-
-uint8_t chppRunTransportLoopback(struct ChppTransportState *context,
- uint8_t *buf, size_t len) {
- UNUSED_VAR(buf);
- UNUSED_VAR(len);
- uint8_t result = CHPP_APP_ERROR_UNSUPPORTED;
- context->loopbackResult = result;
-
-#ifdef CHPP_CLIENT_ENABLED_TRANSPORT_LOOPBACK
- result = CHPP_APP_ERROR_NONE;
- context->loopbackResult = CHPP_APP_ERROR_UNSPECIFIED;
-
- if (len == 0 || len > CHPP_TRANSPORT_TX_MTU_BYTES) {
- result = CHPP_APP_ERROR_INVALID_LENGTH;
- context->loopbackResult = result;
-
- } else if (context->txStatus.linkBusy) {
- result = CHPP_APP_ERROR_BLOCKED;
- context->loopbackResult = result;
-
- } else if (context->transportLoopbackData.payload != NULL) {
- result = CHPP_APP_ERROR_BUSY;
- context->loopbackResult = result;
-
- } else if ((context->transportLoopbackData.payload = chppMalloc(len)) ==
- NULL) {
- result = CHPP_APP_ERROR_OOM;
- context->loopbackResult = result;
-
- } else {
- context->transportLoopbackData.length = len;
- memcpy(context->transportLoopbackData.payload, buf, len);
-
- context->txStatus.linkBusy = true;
- context->pendingTxPacket.length = 0;
- memset(&context->pendingTxPacket.payload, 0, CHPP_LINK_TX_MTU_BYTES);
- context->pendingTxPacket.length +=
- chppAddPreamble(&context->pendingTxPacket.payload[0]);
-
- struct ChppTransportHeader *txHeader =
- (struct ChppTransportHeader *)&context->pendingTxPacket
- .payload[context->pendingTxPacket.length];
- context->pendingTxPacket.length += sizeof(*txHeader);
-
- txHeader->packetCode = CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
- CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST, txHeader->packetCode);
-
- size_t payloadLen = MIN(len, CHPP_TRANSPORT_TX_MTU_BYTES);
- txHeader->length = (uint16_t)payloadLen;
- chppAppendToPendingTxPacket(&context->pendingTxPacket, buf, payloadLen);
-
- chppAddFooter(&context->pendingTxPacket);
+bool chppEnqueueTxDatagram(struct ChppTransportState *context, size_t len,
+ uint8_t *buf) {
+ bool success = false;
+ chppMutexLock(&context->mutex);
- CHPP_LOGD("Sending transport-loopback request (packet len=%" PRIuSIZE
- ", payload len=%" PRIu16 ", asked len was %" PRIuSIZE ")",
- context->pendingTxPacket.length, txHeader->length, len);
- enum ChppLinkErrorCode error = chppSendPendingPacket(context);
+ if (context->txDatagramQueue.pending < CHPP_TX_DATAGRAM_QUEUE_LEN) {
+ uint16_t end =
+ (context->txDatagramQueue.front + context->txDatagramQueue.pending) %
+ CHPP_TX_DATAGRAM_QUEUE_LEN;
- if (error != CHPP_LINK_ERROR_NONE_QUEUED) {
- // Either sent synchronously or an error has occurred
- chppLinkSendDoneCb(&context->linkParams, error);
+ context->txDatagramQueue.datagram[end].length = len;
+ context->txDatagramQueue.datagram[end].payload = buf;
+ context->txDatagramQueue.pending++;
- if (error != CHPP_LINK_ERROR_NONE_SENT) {
- // An error has occurred
- CHPP_FREE_AND_NULLIFY(context->transportLoopbackData.payload);
- context->transportLoopbackData.length = 0;
- result = CHPP_APP_ERROR_UNSPECIFIED;
- }
+ if (context->txDatagramQueue.pending == 1) {
+ // Queue was empty prior. Need to kickstart transmission.
+ chppEnqueueTxPacket(context, CHPP_ERROR_NONE);
}
- }
-
- if (result != CHPP_APP_ERROR_NONE) {
- CHPP_LOGE("Transport-loopback failure: %" PRIu8, result);
- }
-#endif
- return result;
-}
-void chppTransportSendReset(struct ChppTransportState *context,
- enum ChppTransportPacketAttributes resetType,
- enum ChppTransportErrorCode error) {
- // Make sure CHPP is in an initialized state
- CHPP_ASSERT_LOG((context->txDatagramQueue.pending == 0 &&
- context->txDatagramQueue.front == 0),
- "Not init to send reset");
-
- struct ChppTransportConfiguration *config =
- chppMalloc(sizeof(struct ChppTransportConfiguration));
-
- // CHPP transport version
- config->version.major = 1;
- config->version.minor = 0;
- config->version.patch = 0;
-
- // Rx MTU size
- config->rxMtu = CHPP_PLATFORM_LINK_RX_MTU_BYTES;
-
- // Max Rx window size
- // Note: current implementation does not support a window size >1
- config->windowSize = 1;
-
- // Advertised transport layer (ACK) timeout
- config->timeoutInMs = CHPP_PLATFORM_TRANSPORT_TIMEOUT_MS;
-
- if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
- CHPP_LOGD("Sending RESET-ACK");
- } else {
- CHPP_LOGD("Sending RESET");
- }
-
- if (resetType == CHPP_TRANSPORT_ATTR_RESET_ACK) {
- chppSetResetComplete(context);
+ success = true;
}
- context->resetTimeNs = chppGetCurrentTimeNs();
+ chppMutexUnlock(&context->mutex);
- chppEnqueueTxDatagram(context,
- CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(resetType, error),
- config, sizeof(*config));
+ return success;
}