From 38ef4860dafdf5fc3dd5ab2804152ce7e2b2a082 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 10 May 2022 14:10:17 -0400 Subject: [PATCH] command: Move command processing to new command.c file Move the low-level command parsing and encoding code from canboot_main.c to a new command.c file. Standardize the parameters and naming for command handlers. Rename send_ack() to console_send_ack() and rework parameters to be similar to command handlers. Signed-off-by: Kevin O'Connor --- src/Makefile | 2 +- src/canboot_main.c | 189 +++++++------------------------------------ src/canboot_main.h | 6 -- src/command.c | 152 ++++++++++++++++++++++++++++++++++ src/command.h | 35 ++++++++ src/generic/canbus.c | 11 +-- 6 files changed, 220 insertions(+), 175 deletions(-) create mode 100644 src/command.c diff --git a/src/Makefile b/src/Makefile index 3e8973e..4cfd21d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,3 +1,3 @@ # Main code build rules -src-y += canboot_main.c sched.c led.c +src-y += canboot_main.c sched.c command.c led.c diff --git a/src/canboot_main.c b/src/canboot_main.c index 2e970d7..f866e3c 100644 --- a/src/canboot_main.c +++ b/src/canboot_main.c @@ -11,55 +11,24 @@ #include "board/flash.h" // write_page #include "board/gpio.h" // gpio_in_setup #include "canboot_main.h" // canboot_main +#include "command.h" // command_respond_ack #include "ctr.h" // DECL_CTR #include "led.h" // check_blink_time #include "byteorder.h" // cpu_to_le32 #include "sched.h" // sched_run_init -#define PROTO_VERSION 0x00010000 // Version 1.0.0 -#define PROTO_SIZE 4 -#define CMD_BUF_SIZE (CONFIG_BLOCK_SIZE + 64) -#define MAX_OBUF_SIZE (CONFIG_BLOCK_SIZE + 16) -#define CMD_CONNECT 0x11 -#define CMD_RX_BLOCK 0x12 -#define CMD_RX_EOF 0x13 -#define CMD_REQ_BLOCK 0x14 -#define CMD_COMPLETE 0x15 -#define ACK_COMMAND 0xa0 - -// Command Format: -// <2 byte header> <1 byte cmd> <1 byte data word count> <2 byte crc> <2 byte trailer> -#define CMD_HEADER 0x0188 -#define CMD_TRAILER 0x9903 - #define WAIT_BLINK_TIME 1000000 #define XFER_BLINK_TIME 20000 #define REQUEST_SIG 0x5984E3FA6CA1589B // Random request sig -static uint8_t nack[8] = {0x01, 0x88, 0xF1, 0x00, 0x68, 0x95, 0x99, 0x03}; -static uint8_t cerr[8] = {0x01, 0x88, 0xF2, 0x00, 0x00, 0xbf, 0x99, 0x03}; static uint8_t page_buffer[CONFIG_MAX_FLASH_PAGE_SIZE]; -// Input Tracking -static uint8_t cmd_buf[CMD_BUF_SIZE]; -static uint8_t cmd_pos = 0; // Page Tracking static uint32_t last_page_address = 0; static uint8_t page_pending = 0; static uint32_t blink_time = WAIT_BLINK_TIME; static uint8_t complete = 0; -void -send_ack(uint32_t* data, uint8_t payload_len) -{ - // First four bytes: 2 byte header, ack_type, data length - data[0] = cpu_to_le32(payload_len << 24 | ACK_COMMAND << 16 | 0x8801); - // calculate the CRC - uint16_t crc = crc16_ccitt((uint8_t *)data + 2, payload_len * 4 + 2); - data[payload_len + 1] = cpu_to_le32(0x0399 << 16 | crc); - canboot_sendf((uint8_t *)data, (payload_len + 2) * 4); -} - static void write_page(uint32_t page_address) { @@ -69,178 +38,76 @@ write_page(uint32_t page_address) page_pending = 0; } -static void -process_read_block(uint32_t* data, uint8_t data_len) { - uint32_t block_address = le32_to_cpu(data[0]); - uint8_t word_len = CONFIG_BLOCK_SIZE / 4 + 2; - uint32_t out[MAX_OBUF_SIZE / 4]; - out[1] = cpu_to_le32(CMD_REQ_BLOCK); +void +command_read_block(uint32_t *data) +{ + blink_time = XFER_BLINK_TIME; + uint32_t block_address = le32_to_cpu(data[1]); + uint32_t out[CONFIG_BLOCK_SIZE / 4 + 2 + 2]; out[2] = cpu_to_le32(block_address); flash_read_block(block_address, &out[3]); - send_ack(out, word_len); + command_respond_ack(CMD_REQ_BLOCK, out, ARRAY_SIZE(out)); } -static void -process_write_block(uint32_t* data, uint8_t data_len) { - if (data_len != (CONFIG_BLOCK_SIZE / 4) + 1) { - canboot_sendf(cerr, 8); +void +command_write_block(uint32_t *data) +{ + blink_time = XFER_BLINK_TIME; + if (command_get_arg_count(data) != (CONFIG_BLOCK_SIZE / 4) + 1) { + command_respond_command_error(); return; } - uint32_t block_address = le32_to_cpu(data[0]); + uint32_t block_address = le32_to_cpu(data[1]); if (block_address < CONFIG_APPLICATION_START) { - canboot_sendf(cerr, 8); + command_respond_command_error(); return; } uint32_t flash_page_size = flash_get_page_size(); uint32_t page_pos = block_address % flash_page_size; - memcpy(&page_buffer[page_pos], (uint8_t *)&data[1], CONFIG_BLOCK_SIZE); + memcpy(&page_buffer[page_pos], (uint8_t *)&data[2], CONFIG_BLOCK_SIZE); page_pending = 1; if (page_pos + CONFIG_BLOCK_SIZE == flash_page_size) write_page(block_address - page_pos); uint32_t out[4]; - out[1] = cpu_to_le32(CMD_RX_BLOCK); out[2] = cpu_to_le32(block_address); - send_ack(out, 2); + command_respond_ack(CMD_RX_BLOCK, out, ARRAY_SIZE(out)); } -static void -process_eof(void) +void +command_eof(uint32_t *data) { + blink_time = WAIT_BLINK_TIME; uint32_t flash_page_size = flash_get_page_size(); if (page_pending) { write_page(last_page_address + flash_page_size); } flash_complete(); uint32_t out[4]; - out[1] = cpu_to_le32(CMD_RX_EOF); out[2] = cpu_to_le32( ((last_page_address - CONFIG_APPLICATION_START) / flash_page_size) + 1); - send_ack(out, 2); + command_respond_ack(CMD_RX_EOF, out, ARRAY_SIZE(out)); } -static void -process_complete(void) +void +command_complete(uint32_t *data) { uint32_t out[3]; - out[1] = cpu_to_le32(CMD_COMPLETE); - send_ack(out, 1); + command_respond_ack(CMD_COMPLETE, out, ARRAY_SIZE(out)); complete = 1; } -static void -process_connnect(void) +void +command_connect(uint32_t *data) { uint32_t mcuwords = DIV_ROUND_UP(strlen(CONFIG_MCU), 4); uint32_t out[6 + mcuwords]; memset(out, 0, (6 + mcuwords) * 4); - out[1] = cpu_to_le32(CMD_CONNECT); out[2] = cpu_to_le32(PROTO_VERSION); out[3] = cpu_to_le32(CONFIG_APPLICATION_START); out[4] = cpu_to_le32(CONFIG_BLOCK_SIZE); memcpy(&out[5], CONFIG_MCU, strlen(CONFIG_MCU)); - send_ack(out, 4 + mcuwords); -} - -static inline void -process_command(uint8_t cmd, uint32_t* data, uint8_t data_len) -{ - switch (cmd) { - case CMD_CONNECT: - process_connnect(); - break; - case CMD_RX_BLOCK: - blink_time = XFER_BLINK_TIME; - process_write_block(data, data_len); - break; - case CMD_RX_EOF: - blink_time = WAIT_BLINK_TIME; - process_eof(); - break; - case CMD_REQ_BLOCK: - blink_time = XFER_BLINK_TIME; - process_read_block(data, data_len); - break; - case CMD_COMPLETE: - process_complete(); - break; - case CMD_GET_CANBUS_ID: - command_get_canbus_id(); - break; - default: - // Unknown command or gabage data, NACK it - canboot_sendf(cerr, 8); - } -} - -static inline void -decode_command(void) -{ - uint8_t remaining = cmd_pos; - uint8_t *tmpbuf = cmd_buf; - while (remaining) { - if (tmpbuf[0] == 0x01) { - // potential match - if (remaining >= PROTO_SIZE) { - uint16_t header = tmpbuf[0] << 8 | tmpbuf[1]; - uint8_t cmd = tmpbuf[2]; - uint8_t length = tmpbuf[3]; - uint16_t full_length = PROTO_SIZE * 2 + length * 4; - if (header == CMD_HEADER) { - if (full_length > CMD_BUF_SIZE) { - // packet too large, nack it and move on - canboot_sendf(nack, 8); - } else if (remaining >= full_length) { - remaining -= full_length; - uint16_t fpos = full_length - 4; - uint16_t trailer = tmpbuf[fpos + 2] << 8 | tmpbuf[fpos + 3]; - if (trailer != CMD_TRAILER) { - canboot_sendf(nack, 8); - } else { - uint16_t crc = le16_to_cpu(*(uint16_t *)(&tmpbuf[fpos])); - uint16_t calc_crc = crc16_ccitt(&tmpbuf[2], full_length - 6); - if (crc != calc_crc) { - canboot_sendf(nack, 8); - } else { - // valid command, process - process_command(cmd, (uint32_t *)&tmpbuf[4], length); - } - } - if (!remaining) - break; - } else { - // Header is valid, haven't received full packet - break; - } - } - } else { - // Not enough data, check again after the next read - break; - } - } - remaining--; - tmpbuf++; - } - if (remaining) { - // move the buffer - uint8_t rpos = cmd_pos - remaining; - memmove(&cmd_buf[0], &cmd_buf[rpos], remaining); - } - cmd_pos = remaining; -} - -void -canboot_process_rx(uint8_t *data, uint32_t len) -{ - // read into the command buffer - if (cmd_pos >= CMD_BUF_SIZE) - return; - else if (cmd_pos + len > CMD_BUF_SIZE) - len = CMD_BUF_SIZE - cmd_pos; - memcpy(&cmd_buf[cmd_pos], data, len); - cmd_pos += len; - if (cmd_pos > PROTO_SIZE) - decode_command(); + command_respond_ack(CMD_CONNECT, out, ARRAY_SIZE(out)); } static inline uint8_t diff --git a/src/canboot_main.h b/src/canboot_main.h index b23f97b..8d99bdf 100644 --- a/src/canboot_main.h +++ b/src/canboot_main.h @@ -3,12 +3,6 @@ #include // uint32_t -void canboot_process_rx(uint8_t *data, uint32_t len); -void canboot_sendf(uint8_t* data, uint16_t size); void canboot_main(void); -void send_ack(uint32_t* data, uint8_t payload_len); - -#define CMD_GET_CANBUS_ID 0x16 -void command_get_canbus_id(void); #endif // canboot_main.h diff --git a/src/command.c b/src/command.c new file mode 100644 index 0000000..97a56d5 --- /dev/null +++ b/src/command.c @@ -0,0 +1,152 @@ +// Command processing +// +// Copyright (C) 2021 Eric Callahan +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // memcpy +#include "autoconf.h" // CONFIG_BLOCK_SIZE +#include "board/misc.h" // crc16_ccitt +#include "byteorder.h" // cpu_to_le32 +#include "command.h" // send_ack + +// Input Tracking +#define CMD_BUF_SIZE (CONFIG_BLOCK_SIZE + 64) +static uint8_t cmd_buf[CMD_BUF_SIZE]; +static uint8_t cmd_pos = 0; + +static void +command_respond(uint32_t *data, uint32_t cmdid, uint32_t data_len) +{ + // First four bytes: 2 byte header, ack_type, data length + data[0] = cpu_to_le32((data_len - 2) << 24 | cmdid << 16 | 0x8801); + // calculate the CRC + uint16_t crc = crc16_ccitt((uint8_t *)data + 2, (data_len - 2) * 4 + 2); + data[data_len - 1] = cpu_to_le32(0x0399 << 16 | crc); + console_process_tx((uint8_t *)data, data_len * 4); +} + +void +command_respond_ack(uint32_t acked_cmd, uint32_t *out, uint32_t out_len) +{ + out[1] = cpu_to_le32(acked_cmd); + command_respond(out, RESPONSE_ACK, out_len); +} + +void +command_respond_command_error(void) +{ + uint32_t out[2]; + command_respond(out, RESPONSE_COMMAND_ERROR, ARRAY_SIZE(out)); +} + +static void +command_respond_nack(void) +{ + uint32_t out[2]; + command_respond(out, RESPONSE_NACK, ARRAY_SIZE(out)); +} + +int +command_get_arg_count(uint32_t *data) +{ + return le32_to_cpu(data[0]) >> 24; +} + +static void +process_command(uint8_t cmd, uint32_t *data, uint8_t data_len) +{ + switch (cmd) { + case CMD_CONNECT: + command_connect(data); + break; + case CMD_RX_BLOCK: + command_write_block(data); + break; + case CMD_RX_EOF: + command_eof(data); + break; + case CMD_REQ_BLOCK: + command_read_block(data); + break; + case CMD_COMPLETE: + command_complete(data); + break; + case CMD_GET_CANBUS_ID: + command_get_canbus_id(data); + break; + default: + // Unknown command or gabage data, NACK it + command_respond_command_error(); + } +} + +static void +decode_command(void) +{ + uint8_t remaining = cmd_pos; + uint8_t *tmpbuf = cmd_buf; + while (remaining) { + if (tmpbuf[0] == 0x01) { + // potential match + if (remaining >= PROTO_SIZE) { + uint16_t header = tmpbuf[0] << 8 | tmpbuf[1]; + uint8_t cmd = tmpbuf[2]; + uint8_t length = tmpbuf[3]; + uint16_t full_length = PROTO_SIZE * 2 + length * 4; + if (header == CMD_HEADER) { + if (full_length > CMD_BUF_SIZE) { + // packet too large, nack it and move on + command_respond_nack(); + } else if (remaining >= full_length) { + remaining -= full_length; + uint16_t fpos = full_length - 4; + uint16_t trailer = tmpbuf[fpos + 2] << 8 | tmpbuf[fpos + 3]; + if (trailer != CMD_TRAILER) { + command_respond_nack(); + } else { + uint16_t crc = le16_to_cpu(*(uint16_t *)(&tmpbuf[fpos])); + uint16_t calc_crc = crc16_ccitt(&tmpbuf[2], full_length - 6); + if (crc != calc_crc) { + command_respond_nack(); + } else { + // valid command, process + process_command(cmd, (uint32_t *)tmpbuf, length); + } + } + if (!remaining) + break; + } else { + // Header is valid, haven't received full packet + break; + } + } + } else { + // Not enough data, check again after the next read + break; + } + } + remaining--; + tmpbuf++; + } + if (remaining) { + // move the buffer + uint8_t rpos = cmd_pos - remaining; + memmove(&cmd_buf[0], &cmd_buf[rpos], remaining); + } + cmd_pos = remaining; +} + +void +console_process_rx(uint8_t *data, uint32_t len) +{ + // read into the command buffer + if (cmd_pos >= CMD_BUF_SIZE) + return; + else if (cmd_pos + len > CMD_BUF_SIZE) + len = CMD_BUF_SIZE - cmd_pos; + memcpy(&cmd_buf[cmd_pos], data, len); + cmd_pos += len; + if (cmd_pos > PROTO_SIZE) + decode_command(); +} diff --git a/src/command.h b/src/command.h index 29fb0d5..b461024 100644 --- a/src/command.h +++ b/src/command.h @@ -1,6 +1,7 @@ #ifndef __COMMAND_H #define __COMMAND_H +#include // uint32_t #include "ctr.h" // DECL_CTR // Declare a constant exported to the host @@ -16,4 +17,38 @@ DECL_CTR_INT("DECL_ENUMERATION_RANGE " ENUM " " NAME, \ 2, CTR_INT(VALUE), CTR_INT(COUNT)) +#define PROTO_VERSION 0x00010000 // Version 1.0.0 +#define PROTO_SIZE 4 +#define CMD_CONNECT 0x11 +#define CMD_RX_BLOCK 0x12 +#define CMD_RX_EOF 0x13 +#define CMD_REQ_BLOCK 0x14 +#define CMD_COMPLETE 0x15 +#define CMD_GET_CANBUS_ID 0x16 +#define RESPONSE_ACK 0xa0 +#define RESPONSE_NACK 0xf1 +#define RESPONSE_COMMAND_ERROR 0xf2 + +// Command Format: +// <2 byte header> <1 byte cmd> <1 byte data word count> <2 byte crc> <2 byte trailer> +#define CMD_HEADER 0x0188 +#define CMD_TRAILER 0x9903 + +// command handlers +void command_connect(uint32_t *data); +void command_read_block(uint32_t *data); +void command_write_block(uint32_t *data); +void command_eof(uint32_t *data); +void command_complete(uint32_t *data); +void command_get_canbus_id(uint32_t *data); + +// board specific code +void console_process_tx(uint8_t *data, uint32_t size); + +// command.c +void command_respond_ack(uint32_t acked_cmd, uint32_t *out, uint32_t out_len); +void command_respond_command_error(void); +int command_get_arg_count(uint32_t *data); +void console_process_rx(uint8_t *data, uint32_t len); + #endif // command.h diff --git a/src/generic/canbus.c b/src/generic/canbus.c index 16c67ca..6062c11 100644 --- a/src/generic/canbus.c +++ b/src/generic/canbus.c @@ -7,9 +7,7 @@ // This file may be distributed under the terms of the GNU GPLv3 license. #include // memcpy -#include "byteorder.h" // cpu_to_le32 #include "canbus.h" // canbus_set_uuid -#include "canboot_main.h" #include "command.h" // DECL_TASK #include "sched.h" // sched_wake_task @@ -62,7 +60,7 @@ DECL_TASK(canbus_tx_task); // Encode and transmit a "response" message void -canboot_sendf(uint8_t* data, uint16_t size) +console_process_tx(uint8_t *data, uint32_t size) { // Verify space for message uint32_t tpos = transmit_pos, tmax = transmit_max; @@ -198,7 +196,7 @@ canbus_process_data(uint32_t id, uint32_t len, uint8_t *data) { if (!id || id != canbus_assigned_id) return; - canboot_process_rx(data, len); + console_process_rx(data, len); canbus_notify_rx(); } @@ -230,12 +228,11 @@ DECL_TASK(canbus_rx_task); ****************************************************************/ void -command_get_canbus_id(void) +command_get_canbus_id(uint32_t *data) { uint32_t out[5] = {}; - out[1] = cpu_to_le32(CMD_GET_CANBUS_ID); memcpy(&out[2], canbus_uuid, 6); - send_ack(out, 3); + command_respond_ack(CMD_GET_CANBUS_ID, out, ARRAY_SIZE(out)); } void