Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable checksum support #3

Merged
merged 1 commit into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 68 additions & 11 deletions app/expansion_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ typedef struct {

#pragma pack(pop)

/**
* @brief Expansion checksum type.
*/
typedef uint8_t ExpansionFrameChecksum;

/**
* @brief Receive function type declaration.
*
Expand Down Expand Up @@ -216,17 +221,45 @@ static size_t
return content_size > received_content_size ? content_size - received_content_size : 0;
}

/**
* @brief Enumeration of protocol parser statuses.
*/
typedef enum {
ExpansionProtocolStatusOk, /**< No error has occurred. */
ExpansionProtocolStatusErrorFormat, /**< Invalid frame type. */
ExpansionProtocolStatusErrorChecksum, /**< Checksum mismatch. */
ExpansionProtocolStatusErrorCommunication, /**< Input/output error. */
} ExpansionProtocolStatus;

/**
* @brief Get the checksum byte corresponding to the frame
*
* Lightweight XOR checksum algorithm for basic error detection.
*
* @param[in] data pointer to a byte buffer containing the data.
* @param[in] data_size size of the data buffer.
* @returns checksum byte of the frame.
*/
static ExpansionFrameChecksum
expansion_protocol_get_checksum(const uint8_t* data, size_t data_size) {
ExpansionFrameChecksum checksum = 0;
for(size_t i = 0; i < data_size; ++i) {
checksum ^= data[i];
}
return checksum;
}

/**
* @brief Receive and decode a frame.
*
* Will repeatedly call the receive callback function until enough data is gathered.
* Will repeatedly call the receive callback function until enough data is received.
*
* @param[out] frame pointer to the frame to contain decoded data.
* @param[in] receive pointer to the function used to receive data.
* @param[in,out] context pointer to a user-defined context object. Will be passed to the receive callback function.
* @returns true if a frame was successfully received and decoded, false otherwise.
* @returns ExpansionProtocolStatusOk on success, any other error code on failure.
*/
static bool expansion_frame_decode(
static ExpansionProtocolStatus expansion_protocol_decode(
ExpansionFrame* frame,
ExpansionFrameReceiveCallback receive,
void* context) {
Expand All @@ -235,16 +268,33 @@ static bool expansion_frame_decode(

while(true) {
remaining_size = expansion_frame_get_remaining_size(frame, total_size);
if(remaining_size == 0 || remaining_size == SIZE_MAX) break;

if(remaining_size == SIZE_MAX) {
return ExpansionProtocolStatusErrorFormat;
} else if(remaining_size == 0) {
break;
}

const size_t received_size =
receive((uint8_t*)frame + total_size, remaining_size, context);
if(received_size == 0) break;

if(received_size == 0) {
return ExpansionProtocolStatusErrorCommunication;
}

total_size += received_size;
}

return remaining_size == 0;
ExpansionFrameChecksum checksum;
const size_t received_size = receive(&checksum, sizeof(checksum), context);

if(received_size != sizeof(checksum)) {
return ExpansionProtocolStatusErrorCommunication;
} else if(checksum != expansion_protocol_get_checksum((const uint8_t*)frame, total_size)) {
return ExpansionProtocolStatusErrorChecksum;
} else {
return ExpansionProtocolStatusOk;
}
}

/**
Expand All @@ -253,18 +303,25 @@ static bool expansion_frame_decode(
* @param[in] frame pointer to the frame to be encoded and sent.
* @param[in] send pointer to the function used to send data.
* @param[in,out] context pointer to a user-defined context object. Will be passed to the send callback function.
* @returns true if a frame was successfully encoded and sent, false otherwise.
* @returns ExpansionProtocolStatusOk on success, any other error code on failure.
*/
static bool expansion_frame_encode(
static ExpansionProtocolStatus expansion_protocol_encode(
const ExpansionFrame* frame,
ExpansionFrameSendCallback send,
void* context) {
const size_t encoded_size = expansion_frame_get_encoded_size(frame);
if(encoded_size == 0) {
return ExpansionProtocolStatusErrorFormat;
}

const ExpansionFrameChecksum checksum =
expansion_protocol_get_checksum((const uint8_t*)frame, encoded_size);

if(encoded_size != 0) {
return send((const uint8_t*)frame, encoded_size, context) == encoded_size;
if((send((const uint8_t*)frame, encoded_size, context) != encoded_size) ||
(send(&checksum, sizeof(checksum), context) != sizeof(checksum))) {
return ExpansionProtocolStatusErrorCommunication;
} else {
return false;
return ExpansionProtocolStatusOk;
}
}

Expand Down
50 changes: 30 additions & 20 deletions app/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#define EXPANSION_MODULE_TIMEOUT_MS (EXPANSION_PROTOCOL_TIMEOUT_MS - 50UL)
#define EXPANSION_MODULE_STARTUP_DELAY_MS (250UL)
#define EXPANSION_MODULE_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum))

static StreamBufferHandle_t stream;
static PB_Main rpc_message;
Expand Down Expand Up @@ -57,13 +58,18 @@ static size_t expansion_receive_callback(uint8_t* data, size_t data_size, void*
}

static inline bool expansion_receive_frame(ExpansionFrame* frame) {
return expansion_frame_decode(frame, expansion_receive_callback, NULL);
return expansion_protocol_decode(frame, expansion_receive_callback, NULL) ==
ExpansionProtocolStatusOk;
}

static inline bool expansion_is_heartbeat_frame(const ExpansionFrame* frame) {
return frame->header.type == ExpansionFrameTypeHeartbeat;
}

static inline bool expansion_is_data_frame(const ExpansionFrame* frame) {
return frame->header.type == ExpansionFrameTypeData;
}

static inline bool expansion_is_success_frame(const ExpansionFrame* frame) {
return frame->header.type == ExpansionFrameTypeStatus &&
frame->content.status.error == ExpansionFrameErrorNone;
Expand All @@ -78,7 +84,8 @@ static size_t expansion_send_callback(const uint8_t* data, size_t data_size, voi
}

static inline bool expansion_send_frame(const ExpansionFrame* frame) {
return expansion_frame_encode(frame, expansion_send_callback, NULL);
return expansion_protocol_encode(frame, expansion_send_callback, NULL) ==
ExpansionProtocolStatusOk;
}

static inline bool expansion_send_heartbeat() {
Expand Down Expand Up @@ -138,29 +145,29 @@ static inline bool expansion_rpc_is_read_complete(const ExpansionRpcContext* ctx
return ctx->frame.content.data.size == ctx->read_size;
}

// Read the next frame, process heartbeat if necessary
static inline bool expansion_rpc_read_next_frame(ExpansionRpcContext* ctx) {
for(bool heartbeat_pending = false;;) {
// If no frame has been received in a while, send a heartbeat
if(!expansion_receive_frame(&ctx->frame)) {
if(heartbeat_pending || !expansion_send_heartbeat()) {
return false;
} else {
// Receive a frame while maintaining idle connection
static inline bool expansion_receive_frame_rpc_mode(ExpansionFrame* frame) {
bool heartbeat_pending = false;

while(true) {
const ExpansionProtocolStatus status = expansion_protocol_decode(frame, expansion_receive_callback, NULL);

if(status == ExpansionProtocolStatusErrorCommunication) {
if(!heartbeat_pending && expansion_send_heartbeat()) {
heartbeat_pending = true;
continue;
} else {
return false;
}

} else if(status != ExpansionProtocolStatusOk) {
return false;
}

switch(ctx->frame.header.type) {
case ExpansionFrameTypeHeartbeat:
if(expansion_is_heartbeat_frame(frame)){
heartbeat_pending = false;
break;
case ExpansionFrameTypeData:
ctx->read_size = 0;
} else {
return true;
default:
expansion_send_status(ExpansionFrameErrorUnknown);
return false;
}
}
}
Expand All @@ -173,7 +180,10 @@ static bool
while(received_size != data_size) {
if(expansion_rpc_is_read_complete(ctx)) {
// Read next frame
if(!expansion_rpc_read_next_frame(ctx)) break;
if(!expansion_receive_frame_rpc_mode(&ctx->frame)) break;
if(!expansion_is_data_frame(&ctx->frame)) break;

ctx->read_size = 0;
}

const size_t current_size =
Expand Down Expand Up @@ -329,7 +339,7 @@ static void uart_task(void* unused_arg) {
// startup delay (skip potential module insertion interference)
vTaskDelay(pdMS_TO_TICKS(EXPANSION_MODULE_STARTUP_DELAY_MS));
// init stream buffer
stream = xStreamBufferCreate(sizeof(ExpansionFrame), 1);
stream = xStreamBufferCreate(EXPANSION_MODULE_BUFFER_SIZE, 1);
assert(stream != NULL);

// init uart 0
Expand Down