370 lines
9.8 KiB
C
370 lines
9.8 KiB
C
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <string.h>
|
|
#include <fs.h>
|
|
#include <app_version.h>
|
|
#include <zephyr/sys/crc.h>
|
|
|
|
#include <usb.h>
|
|
#include <protocol.h>
|
|
|
|
#define PROTOCOL_VERSION 1
|
|
|
|
LOG_MODULE_REGISTER(protocol, LOG_LEVEL_DBG);
|
|
|
|
#define PROTOCOL_STACK_SIZE 2048
|
|
#define PROTOCOL_PRIORITY 5
|
|
#define BUFFER_SIZE 512
|
|
|
|
static uint8_t buffer[BUFFER_SIZE];
|
|
static volatile uint32_t rx_index = 0;
|
|
|
|
static protocol_state_t current_protocol_state = PS_WAITING_FOR_COMMAND;
|
|
static protocol_cmd_t current_command = CMD_INVALID;
|
|
|
|
void send_ok()
|
|
{
|
|
const char *response = "OK\n";
|
|
usb_write_buffer((const uint8_t *)response, strlen(response));
|
|
}
|
|
|
|
void send_error(int32_t error_code)
|
|
{
|
|
char response[32];
|
|
snprintf(response, sizeof(response), "ERR %d\n", error_code);
|
|
usb_write_buffer((const uint8_t *)response, strlen(response));
|
|
}
|
|
|
|
int send_ls(const char *path)
|
|
{
|
|
struct fs_dir_t dirp;
|
|
struct fs_dirent entry;
|
|
const char *ls_path = (path == NULL || path[0] == '\0') ? "/" : path;
|
|
fs_dir_t_init(&dirp);
|
|
|
|
if (fs_opendir(&dirp, ls_path) < 0)
|
|
{
|
|
LOG_ERR("Failed to open directory '%s'", ls_path);
|
|
return ENOENT;
|
|
}
|
|
|
|
char tx_buffer[300];
|
|
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
|
|
{
|
|
snprintf(tx_buffer, sizeof(tx_buffer), "%s,%u,%s\n", entry.type == FS_DIR_ENTRY_FILE ? "F" : "D", entry.size, entry.name);
|
|
usb_write_buffer((const uint8_t *)tx_buffer, strlen(tx_buffer));
|
|
}
|
|
|
|
fs_closedir(&dirp);
|
|
return 0;
|
|
}
|
|
|
|
int send_info()
|
|
{
|
|
char info[112];
|
|
struct fs_statvfs stat;
|
|
int rc = fs_statvfs("/lfs", &stat);
|
|
if (rc)
|
|
{
|
|
LOG_ERR("Failed to get filesystem stats: %d", rc);
|
|
return -rc;
|
|
}
|
|
snprintf(info, sizeof(info), "%u;%s;%lu;%lu;%lu\n", PROTOCOL_VERSION, APP_VERSION_STRING, stat.f_frsize, stat.f_blocks, stat.f_bfree);
|
|
usb_write_buffer((const uint8_t *)info, strlen(info));
|
|
return 0;
|
|
}
|
|
|
|
int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_crc32)
|
|
{
|
|
int rc;
|
|
struct fs_file_t file;
|
|
ssize_t bytes_written = 0;
|
|
uint32_t running_crc32 = 0;
|
|
uint32_t retry_count = 0;
|
|
|
|
fs_file_t_init(&file);
|
|
LOG_DBG("Opening file '%s' for writing (expected size: %zd bytes, expected CRC32: 0x%08x)", filename, filesize, expected_crc32);
|
|
rc = fs_open(&file, filename, FS_O_CREATE | FS_O_WRITE);
|
|
if (rc < 0)
|
|
{
|
|
LOG_ERR("Failed to open file '%s' for writing: %d", filename, rc);
|
|
return -rc;
|
|
}
|
|
|
|
usb_write_buffer((const uint8_t *)"READY\n", 6);
|
|
|
|
uint32_t start = k_uptime_get_32();
|
|
while (bytes_written < filesize)
|
|
{
|
|
size_t to_write = MIN(sizeof(buffer), filesize - bytes_written);
|
|
ssize_t read = usb_read_buffer(buffer, to_write);
|
|
|
|
if (read < 0)
|
|
{
|
|
LOG_ERR("Error reading from USB: %d", read);
|
|
fs_close(&file);
|
|
return -read;
|
|
}
|
|
else if (read == 0)
|
|
{
|
|
if (retry_count >= 10)
|
|
{
|
|
LOG_ERR("No data received from USB after multiple attempts");
|
|
fs_close(&file);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
usb_resume_rx();
|
|
// LOG_DBG("No data available from USB, waiting for data... (attempt %u)", retry_count + 1);
|
|
|
|
if (bytes_written == 0)
|
|
{
|
|
usb_wait_for_data(K_SECONDS(30));
|
|
}
|
|
else
|
|
{
|
|
usb_wait_for_data(K_SECONDS(1));
|
|
}
|
|
retry_count++;
|
|
continue;
|
|
}
|
|
|
|
// ssize_t written = fs_write(&file, buffer, read);
|
|
ssize_t written = read;
|
|
if (written < 0)
|
|
{
|
|
LOG_ERR("Error writing to file '%s': %d", filename, (int)written);
|
|
fs_close(&file);
|
|
return (int)written;
|
|
}
|
|
running_crc32 = crc32_ieee_update(running_crc32, buffer, written);
|
|
|
|
bytes_written += written;
|
|
retry_count = 0;
|
|
}
|
|
uint32_t duration = k_uptime_get_32() - start;
|
|
uint32_t kb_per_s = (filesize * 1000) / (duration * 1024 +1);
|
|
LOG_DBG("Received file '%s' (%zd bytes) in %u ms (%u kb/s), CRC32: 0x%08x", filename, filesize, duration, kb_per_s, running_crc32);
|
|
fs_close(&file);
|
|
|
|
if (running_crc32 != expected_crc32)
|
|
{
|
|
LOG_ERR("CRC32 mismatch for file '%s': expected 0x%08x, got 0x%08x", filename, expected_crc32, running_crc32);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void execute_current_command(void)
|
|
{
|
|
int rc;
|
|
switch (current_command)
|
|
{
|
|
case CMD_LS:
|
|
LOG_DBG("Executing LS command with parameters: '%s'", buffer);
|
|
rc = send_ls((char *)buffer);
|
|
if (rc == 0)
|
|
{
|
|
send_ok();
|
|
}
|
|
else
|
|
{
|
|
send_error(rc);
|
|
}
|
|
break;
|
|
case CMD_INFO:
|
|
if (buffer[0] != '\0')
|
|
{
|
|
LOG_WRN("INFO command received with unexpected parameters: '%s'", buffer);
|
|
}
|
|
LOG_DBG("Executing INFO command");
|
|
rc = send_info();
|
|
if (rc == 0)
|
|
{
|
|
send_ok();
|
|
}
|
|
else
|
|
{
|
|
send_error(rc);
|
|
}
|
|
break;
|
|
case CMD_PUT_BINARY_FILE:
|
|
char filename[128];
|
|
ssize_t filesize;
|
|
uint32_t crc32;
|
|
|
|
rc = sscanf((char *)buffer, "%127[^;];%zd;%i", filename, &filesize, &crc32);
|
|
if (rc != 3)
|
|
{
|
|
LOG_ERR("Invalid parameters for PUT_BINARY_FILE command (got %d): '%s'", rc, buffer);
|
|
send_error(EINVAL);
|
|
break;
|
|
}
|
|
LOG_DBG("Executing PUT_BINARY_FILE command filename: '%s', filesize: %zd, crc32: 0x%08x", filename, filesize, crc32);
|
|
rc = put_binary_file(filename, filesize, crc32);
|
|
if (rc == 0)
|
|
{
|
|
send_ok();
|
|
}
|
|
else
|
|
{
|
|
send_error(rc);
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("No execution logic for command %d", current_command);
|
|
send_error(ENOSYS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
protocol_state_t waiting_for_command(uint8_t byte)
|
|
{
|
|
if (byte < 'a' || byte > 'z')
|
|
{
|
|
rx_index = 0;
|
|
return PS_WAITING_FOR_COMMAND;
|
|
}
|
|
buffer[rx_index++] = byte;
|
|
return PS_READING_COMMAND;
|
|
}
|
|
|
|
protocol_state_t reading_command(uint8_t byte)
|
|
{
|
|
if (byte == ' ' || byte == '\n' || byte == '\r')
|
|
{
|
|
buffer[rx_index] = '\0';
|
|
rx_index = 0;
|
|
|
|
if (strcmp((char *)buffer, "ls") == 0)
|
|
{
|
|
LOG_DBG("Received LS command");
|
|
current_command = CMD_LS;
|
|
}
|
|
else if (strcmp((char *)buffer, "info") == 0)
|
|
{
|
|
LOG_DBG("Received INFO command");
|
|
current_command = CMD_INFO;
|
|
}
|
|
else if (strcmp((char *)buffer, "put") == 0)
|
|
{
|
|
LOG_DBG("Received PUT_BINARY_FILE command");
|
|
current_command = CMD_PUT_BINARY_FILE;
|
|
}
|
|
|
|
else
|
|
{
|
|
LOG_DBG("Unknown command: %s", buffer);
|
|
current_command = CMD_INVALID;
|
|
send_error(EILSEQ);
|
|
if (byte != '\n' && byte != '\r')
|
|
return PS_WAITING_FOR_END_OF_LINE;
|
|
return PS_WAITING_FOR_COMMAND;
|
|
}
|
|
|
|
if (byte == ' ')
|
|
{
|
|
rx_index = 0;
|
|
return PS_READING_PARAMETERS;
|
|
}
|
|
else
|
|
{
|
|
buffer[0] = '\0';
|
|
rx_index = 0;
|
|
execute_current_command();
|
|
return PS_WAITING_FOR_COMMAND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rx_index < BUFFER_SIZE - 1)
|
|
{
|
|
buffer[rx_index++] = byte;
|
|
}
|
|
else
|
|
{
|
|
send_error(EMSGSIZE);
|
|
return PS_WAITING_FOR_END_OF_LINE;
|
|
}
|
|
}
|
|
return PS_READING_COMMAND;
|
|
}
|
|
|
|
protocol_state_t reading_parameters(uint8_t byte)
|
|
{
|
|
if (byte == '\n' || byte == '\r')
|
|
{
|
|
buffer[rx_index] = '\0';
|
|
rx_index = 0;
|
|
execute_current_command();
|
|
return PS_WAITING_FOR_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
buffer[rx_index++] = byte;
|
|
if (rx_index >= BUFFER_SIZE)
|
|
{
|
|
rx_index = 0;
|
|
send_error(EMSGSIZE);
|
|
return PS_WAITING_FOR_COMMAND;
|
|
}
|
|
return PS_READING_PARAMETERS;
|
|
}
|
|
}
|
|
|
|
protocol_state_t waiting_for_end_of_line(uint8_t byte)
|
|
{
|
|
if (byte == '\n' || byte == '\r')
|
|
{
|
|
return PS_WAITING_FOR_COMMAND;
|
|
}
|
|
else
|
|
{
|
|
return PS_WAITING_FOR_END_OF_LINE;
|
|
}
|
|
}
|
|
|
|
void protocol_thread_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
uint8_t rx_byte;
|
|
|
|
LOG_DBG("Protocol thread started, waiting for data...");
|
|
|
|
while (1)
|
|
{
|
|
/* 1. Thread schläft, bis der USB-Interrupt triggert */
|
|
if (usb_wait_for_data(K_FOREVER))
|
|
{
|
|
|
|
while (usb_read_char(&rx_byte) > 0)
|
|
{
|
|
switch (current_protocol_state)
|
|
{
|
|
case PS_WAITING_FOR_COMMAND:
|
|
current_protocol_state = waiting_for_command(rx_byte);
|
|
break;
|
|
case PS_READING_COMMAND:
|
|
current_protocol_state = reading_command(rx_byte);
|
|
break;
|
|
case PS_READING_PARAMETERS:
|
|
current_protocol_state = reading_parameters(rx_byte);
|
|
break;
|
|
case PS_WAITING_FOR_END_OF_LINE:
|
|
current_protocol_state = waiting_for_end_of_line(rx_byte);
|
|
break;
|
|
default:
|
|
LOG_ERR("Invalid protocol state: %d", current_protocol_state);
|
|
current_protocol_state = PS_WAITING_FOR_COMMAND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
usb_resume_rx();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Thread statisch definieren und automatisch starten lassen */
|
|
K_THREAD_DEFINE(protocol_tid, PROTOCOL_STACK_SIZE,
|
|
protocol_thread_entry, NULL, NULL, NULL,
|
|
PROTOCOL_PRIORITY, 0, 0); |