From 1e9a9732676f7c5e12f13d9957a14dcaa81ad82c Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 25 Dec 2023 19:50:24 +0300 Subject: [PATCH] Enable checksum support --- app/expansion_protocol.h | 79 ++++++++++++++++++++++++++++++++++------ app/uart.c | 50 +++++++++++++++---------- 2 files changed, 98 insertions(+), 31 deletions(-) diff --git a/app/expansion_protocol.h b/app/expansion_protocol.h index 7222fa8..a67ab82 100644 --- a/app/expansion_protocol.h +++ b/app/expansion_protocol.h @@ -123,6 +123,11 @@ typedef struct { #pragma pack(pop) +/** + * @brief Expansion checksum type. + */ +typedef uint8_t ExpansionFrameChecksum; + /** * @brief Receive function type declaration. * @@ -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) { @@ -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; + } } /** @@ -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; } } diff --git a/app/uart.c b/app/uart.c index 34b2f6f..a10d168 100644 --- a/app/uart.c +++ b/app/uart.c @@ -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; @@ -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; @@ -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() { @@ -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; } } } @@ -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 = @@ -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