This commit is contained in:
2026-03-02 00:25:40 +01:00
parent e7d6d288a8
commit b665cb5def
32 changed files with 3287 additions and 953 deletions

View File

@@ -19,7 +19,7 @@
#define AUDIO_WORD_WIDTH 16
#define AUDIO_SAMPLE_RATE 16000
LOG_MODULE_REGISTER(audio, LOG_LEVEL_DBG);
LOG_MODULE_REGISTER(audio, LOG_LEVEL_INF);
/* Dauer eines Blocks in ms (4096 Bytes / (16kHz * 2 Kanäle * 2 Bytes)) = 64 ms */
#define BLOCK_DURATION_MS ((AUDIO_BLOCK_SIZE * 1000) / (AUDIO_SAMPLE_RATE * 2 * (AUDIO_WORD_WIDTH / 8)))

View File

@@ -5,9 +5,15 @@
#include <zephyr/pm/device.h>
#include <fs.h>
#include <utils.h>
LOG_MODULE_REGISTER(buzz_fs, LOG_LEVEL_DBG);
LOG_MODULE_REGISTER(buzz_fs, LOG_LEVEL_INF);
#define TAG_MAGIC "TAG!"
#define TAG_MAGIC_LEN 4U
#define TAG_LEN_FIELD_LEN 2U
#define TAG_VERSION_LEN 1U
#define TAG_FOOTER_V1_LEN (TAG_VERSION_LEN + TAG_LEN_FIELD_LEN + TAG_MAGIC_LEN)
#define TAG_FORMAT_VERSION 0x01
#define STORAGE_PARTITION_ID FIXED_PARTITION_ID(littlefs_storage)
#define SLOT1_ID FIXED_PARTITION_ID(slot1_partition)
@@ -162,6 +168,15 @@ int fs_pm_statvfs(const char *path, struct fs_statvfs *stat)
return rc;
}
int fs_pm_stat(const char *path, struct fs_dirent *entry)
{
LOG_DBG("PM Getting stat for '%s'", path);
fs_pm_flash_resume();
int rc = fs_stat(path, entry);
fs_pm_flash_suspend();
return rc;
}
int fs_pm_mkdir(const char *path)
{
LOG_DBG("PM Creating directory '%s'", path);
@@ -171,28 +186,91 @@ int fs_pm_mkdir(const char *path)
return rc;
}
ssize_t fs_get_audio_data_len(struct fs_file_t *fp) {
int fs_pm_rename(const char *old_path, const char *new_path)
{
LOG_DBG("PM Renaming '%s' to '%s'", old_path, new_path);
fs_pm_flash_resume();
int rc = fs_rename(old_path, new_path);
fs_pm_flash_suspend();
return rc;
}
static int fs_get_tag_bounds(struct fs_file_t *fp, off_t file_size,
size_t *audio_limit, size_t *payload_len, bool *has_tag)
{
uint8_t footer[6];
uint16_t tag_len;
if (audio_limit == NULL || payload_len == NULL || has_tag == NULL) {
return -EINVAL;
}
*has_tag = false;
*audio_limit = (size_t)file_size;
*payload_len = 0U;
if (file_size < (off_t)TAG_FOOTER_V1_LEN) {
return 0;
}
fs_seek(fp, -(off_t)(TAG_LEN_FIELD_LEN + TAG_MAGIC_LEN), FS_SEEK_END);
if (fs_read(fp, footer, sizeof(footer)) != sizeof(footer)) {
fs_seek(fp, 0, FS_SEEK_SET);
return -EIO;
}
if (memcmp(&footer[2], TAG_MAGIC, TAG_MAGIC_LEN) != 0) {
fs_seek(fp, 0, FS_SEEK_SET);
return 0;
}
tag_len = (uint16_t)footer[0] | ((uint16_t)footer[1] << 8);
if (tag_len > (uint16_t)file_size || tag_len < TAG_FOOTER_V1_LEN) {
fs_seek(fp, 0, FS_SEEK_SET);
return -EBADMSG;
}
uint8_t tag_version = 0;
fs_seek(fp, -(off_t)TAG_FOOTER_V1_LEN, FS_SEEK_END);
if (fs_read(fp, &tag_version, 1) != 1) {
fs_seek(fp, 0, FS_SEEK_SET);
return -EIO;
}
if (tag_version != TAG_FORMAT_VERSION) {
fs_seek(fp, 0, FS_SEEK_SET);
return -ENOTSUP;
}
*has_tag = true;
*audio_limit = (size_t)file_size - tag_len;
*payload_len = tag_len - TAG_FOOTER_V1_LEN;
fs_seek(fp, 0, FS_SEEK_SET);
return 0;
}
ssize_t fs_get_audio_data_len(struct fs_file_t *fp) {
off_t file_size;
size_t audio_limit = 0U;
size_t payload_len = 0U;
bool has_tag = false;
fs_seek(fp, 0, FS_SEEK_END);
file_size = fs_tell(fp);
fs_seek(fp, 0, FS_SEEK_SET);
if (file_size < 6) return file_size;
fs_seek(fp, -6, FS_SEEK_END);
if (fs_read(fp, footer, 6) == 6) {
if (memcmp(&footer[2], "TAG!", 4) == 0) {
uint16_t tag_len = footer[0] | (footer[1] << 8);
if (tag_len <= file_size) {
fs_seek(fp, 0, FS_SEEK_SET);
return file_size - tag_len;
}
}
if (file_size < 0) {
fs_seek(fp, 0, FS_SEEK_SET);
return -EIO;
}
if (fs_get_tag_bounds(fp, file_size, &audio_limit, &payload_len, &has_tag) < 0) {
fs_seek(fp, 0, FS_SEEK_SET);
return -EIO;
}
fs_seek(fp, 0, FS_SEEK_SET);
return file_size;
return has_tag ? (ssize_t)audio_limit : file_size;
}
ssize_t fs_read_audio(struct fs_file_t *fp, void *buffer, size_t len, size_t audio_limit) {
@@ -206,88 +284,85 @@ ssize_t fs_read_audio(struct fs_file_t *fp, void *buffer, size_t len, size_t aud
return fs_read(fp, buffer, to_read);
}
int fs_write_hex_tag(struct fs_file_t *fp, const char *hex_str) {
size_t hex_len = strlen(hex_str);
// Ein Hex-String muss eine gerade Anzahl an Zeichen haben
if (hex_len % 2 != 0) return -EINVAL;
size_t payload_len = hex_len / 2;
uint16_t total_footer_len = (uint16_t)(payload_len + 6);
// 1. Audio-Ende bestimmen und dorthin seeken
size_t audio_limit = fs_get_audio_data_len(fp);
fs_seek(fp, audio_limit, FS_SEEK_SET);
// 2. Payload Byte für Byte konvertieren und schreiben
for (size_t i = 0; i < hex_len; i += 2) {
int high = hex2int(hex_str[i]);
int low = hex2int(hex_str[i+1]);
if (high < 0 || low < 0) return -EINVAL; // Ungültiges Hex-Zeichen
uint8_t byte = (uint8_t)((high << 4) | low);
fs_write(fp, &byte, 1);
}
// 3. Die 2 Bytes Länge schreiben (Little Endian)
uint8_t len_bytes[2];
len_bytes[0] = (uint8_t)(total_footer_len & 0xFF);
len_bytes[1] = (uint8_t)((total_footer_len >> 8) & 0xFF);
fs_write(fp, len_bytes, 2);
// 4. Magic Bytes schreiben
fs_write(fp, "TAG!", 4);
// 5. Datei am aktuellen Punkt abschneiden
off_t current_pos = fs_tell(fp);
return fs_truncate(fp, current_pos);
}
int fs_read_hex_tag(struct fs_file_t *fp, char *hex_str, size_t hex_str_size) {
if (hex_str == NULL || hex_str_size == 0) {
int fs_tag_open_read(struct fs_file_t *fp, uint8_t *version, size_t *payload_len)
{
if (fp == NULL || version == NULL || payload_len == NULL) {
return -EINVAL;
}
hex_str[0] = '\0';
// Dateigröße ermitteln
fs_seek(fp, 0, FS_SEEK_END);
off_t file_size = fs_tell(fp);
size_t audio_limit = 0U;
size_t payload_size = 0U;
bool has_tag = false;
// Audio-Limit finden (Anfang des Payloads)
size_t audio_limit = fs_get_audio_data_len(fp);
// Prüfen, ob überhaupt ein Tag existiert (audio_limit < file_size)
if (audio_limit >= file_size) {
// Kein Tag vorhanden -> leerer String
return 0;
if (file_size < 0) {
return -EIO;
}
// Die Payload-Länge ist: Gesamtgröße - Audio - 6 Bytes (Länge + Magic)
size_t payload_len = file_size - audio_limit - 6;
if ((payload_len * 2U) + 1U > hex_str_size) {
return -ENOMEM; // Nicht genug Platz im Zielpuffer
int rc = fs_get_tag_bounds(fp, file_size, &audio_limit, &payload_size, &has_tag);
if (rc < 0) {
return rc;
}
if (!has_tag) {
return -ENOENT;
}
// Zum Anfang des Payloads springen
fs_seek(fp, audio_limit, FS_SEEK_SET);
*version = TAG_FORMAT_VERSION;
*payload_len = payload_size;
return 0;
}
uint8_t byte;
for (size_t i = 0; i < payload_len; i++) {
if (fs_read(fp, &byte, 1) != 1) {
return -EIO;
}
ssize_t fs_tag_read_chunk(struct fs_file_t *fp, void *buffer, size_t len)
{
return fs_read(fp, buffer, len);
}
// Jedes Byte als zwei Hex-Zeichen in den Zielpuffer schreiben
hex_str[i * 2] = int2hex(byte >> 4);
hex_str[i * 2 + 1] = int2hex(byte & 0x0F);
int fs_tag_open_write(struct fs_file_t *fp)
{
ssize_t audio_limit = fs_get_audio_data_len(fp);
if (audio_limit < 0) {
return (int)audio_limit;
}
fs_seek(fp, audio_limit, FS_SEEK_SET);
return 0;
}
ssize_t fs_tag_write_chunk(struct fs_file_t *fp, const void *buffer, size_t len)
{
return fs_write(fp, buffer, len);
}
int fs_tag_finish_write(struct fs_file_t *fp, uint8_t version, size_t payload_len)
{
if (version != TAG_FORMAT_VERSION) {
return -ENOTSUP;
}
hex_str[payload_len * 2] = '\0';
size_t total_footer_len = payload_len + TAG_FOOTER_V1_LEN;
if (total_footer_len > UINT16_MAX) {
return -EFBIG;
}
return 0;
if (fs_write(fp, &version, 1) != 1) {
return -EIO;
}
uint8_t len_bytes[2];
len_bytes[0] = (uint8_t)(total_footer_len & 0xFFU);
len_bytes[1] = (uint8_t)((total_footer_len >> 8) & 0xFFU);
if (fs_write(fp, len_bytes, sizeof(len_bytes)) != sizeof(len_bytes)) {
return -EIO;
}
if (fs_write(fp, TAG_MAGIC, TAG_MAGIC_LEN) != TAG_MAGIC_LEN) {
return -EIO;
}
off_t current_pos = fs_tell(fp);
return fs_truncate(fp, current_pos);
}
int flash_get_slot_info(slot_info_t *info) {

View File

@@ -74,6 +74,15 @@ int fs_pm_unlink(const char *path);
*/
int fs_pm_statvfs(const char *path, struct fs_statvfs *stat);
/**
* @brief Wrapper around fs_stat that handles power management for the flash
* Resumes the flash before stat and suspends it afterwards
* @param path Path to file or directory
* @param entry Pointer to fs_dirent structure to receive metadata
* @return 0 on success, negative error code on failure
*/
int fs_pm_stat(const char *path, struct fs_dirent *entry);
/**
* @brief Wrapper around fs_mkdir that handles power management for the flash
* Resumes the flash before creating the directory and suspends it afterwards
@@ -82,6 +91,15 @@ int fs_pm_statvfs(const char *path, struct fs_statvfs *stat);
*/
int fs_pm_mkdir(const char *path);
/**
* @brief Wrapper around fs_rename that handles power management for the flash
* Resumes the flash before renaming and suspends it afterwards
* @param old_path Current path of the file or directory
* @param new_path New path for the file or directory
* @return 0 on success, negative error code on failure
*/
int fs_pm_rename(const char *old_path, const char *new_path);
/**
* @brief Gets the length of the audio data in a file, accounting for any metadata tags
* @param fp Pointer to an open fs_file_t structure representing the audio file
@@ -100,21 +118,47 @@ int fs_get_audio_data_len(struct fs_file_t *fp);
int fs_read_audio(struct fs_file_t *fp, void *buffer, size_t len, size_t audio_limit);
/**
* @brief Writes a hexadecimal string as a metadata tag at the end of an audio file
* @brief Positions file pointer at start of tag payload if tags exist.
* @param fp Pointer to an open fs_file_t structure representing the audio file
* @param hex_str Null-terminated string containing hexadecimal characters (0-9, a-f, A-F)
* @return 0 on success, negative error code on failure
* @param version Pointer to receive tag format version
* @param payload_len Pointer to receive tag payload length in bytes
* @return 0 on success, -ENOENT if no tags exist, negative error code on failure
*/
int fs_write_hex_tag(struct fs_file_t *fp, const char *hex_str);
int fs_tag_open_read(struct fs_file_t *fp, uint8_t *version, size_t *payload_len);
/**
* @brief Reads a hexadecimal string from a metadata tag at the end of an audio file
* @brief Reads a chunk from current tag payload position.
* @param fp Pointer to an open fs_file_t positioned in tag payload
* @param buffer Destination buffer
* @param len Maximum bytes to read
* @return Number of bytes read, 0 at payload end, negative error code on failure
*/
ssize_t fs_tag_read_chunk(struct fs_file_t *fp, void *buffer, size_t len);
/**
* @brief Positions file pointer for tag payload overwrite at end of audio data.
* @param fp Pointer to an open fs_file_t structure representing the audio file
* @param hex_str Buffer to be filled with the hexadecimal string (must be large enough to hold the data)
* @param hex_str_size Size of the hex_str buffer
* @return 0 on success, negative error code on failure
*/
int fs_read_hex_tag(struct fs_file_t *fp, char *hex_str, size_t hex_str_size);
int fs_tag_open_write(struct fs_file_t *fp);
/**
* @brief Writes a raw tag payload chunk.
* @param fp Pointer to an open fs_file_t positioned for tag payload write
* @param buffer Source buffer
* @param len Number of bytes to write
* @return Number of bytes written, negative error code on failure
*/
ssize_t fs_tag_write_chunk(struct fs_file_t *fp, const void *buffer, size_t len);
/**
* @brief Finalizes tags by appending version + footer and truncating file.
* @param fp Pointer to an open fs_file_t structure representing the audio file
* @param version Tag format version to write
* @param payload_len Tag payload length in bytes
* @return 0 on success, negative error code on failure
*/
int fs_tag_finish_write(struct fs_file_t *fp, uint8_t version, size_t payload_len);
/**
* @brief Retrieves information about the firmware slot, such as start address and size

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,53 @@
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <zephyr/kernel.h>
#include <stdint.h>
#define PROTOCOL_MAX_PATH_LEN 32U
typedef enum {
PS_WAITING_FOR_COMMAND,
PS_READING_COMMAND,
PS_READING_PARAMETERS,
PS_WAITING_FOR_END_OF_LINE,
PS_WAIT_SYNC = 0,
PS_READ_HEADER,
PS_READ_PAYLOAD,
PS_READ_PAYLOAD_CRC,
} protocol_state_t;
typedef enum {
CMD_INVALID = 0,
CMD_INFO,
CMD_LS,
CMD_PUT_BINARY_FILE,
CMD_MKDIR,
CMD_RM,
CMD_CONFIRM,
CMD_REBOOT,
CMD_PLAY,
CMD_SET_TAG,
CMD_GET_TAG,
CMD_CHECK,
/* Weitere Kommandos folgen hier */
CMD_GET_PROTOCOL_VERSION = 0x00,
CMD_GET_FIRMWARE_STATUS = 0x01,
CMD_GET_FLASH_STATUS = 0x02,
CMD_CONFIRM_FIRMWARE = 0x03,
CMD_REBOOT = 0x04,
CMD_LIST_DIR = 0x10,
CMD_CHECK_FILE_CRC = 0x11,
CMD_MKDIR = 0x12,
CMD_RM = 0x13,
CMD_PUT_FILE_START = 0x14,
CMD_PUT_FILE_CHUNK = 0x15,
CMD_PUT_FILE_END = 0x16,
CMD_PUT_FW_START = 0x17,
CMD_STAT = 0x18,
CMD_RENAME = 0x19,
CMD_RM_R = 0x1A,
CMD_GET_FILE = 0x1B,
CMD_GET_TAG_BLOB = 0x20,
CMD_SET_TAG_BLOB_START = 0x21,
CMD_SET_TAG_BLOB_CHUNK = 0x22,
CMD_SET_TAG_BLOB_END = 0x23,
} protocol_cmd_t;
typedef enum {
FRAME_REQ = 0x01,
FRAME_RESP_ACK = 0x10,
FRAME_RESP_DATA = 0x11,
FRAME_RESP_STREAM_START = 0x12,
FRAME_RESP_STREAM_CHUNK = 0x13,
FRAME_RESP_STREAM_END = 0x14,
FRAME_RESP_ERROR = 0x7F,
} protocol_frame_type_t;
typedef enum {
P_ERR_NONE = 0x00,
P_ERR_INVALID_COMMAND = 0x01,
@@ -47,4 +71,7 @@ typedef enum {
P_ERR_BUSY = 0x31,
P_ERR_INTERNAL = 0x32,
} protocol_error_t;
void protocol_thread_entry(void *p1, void *p2, void *p3);
#endif // PROTOCOL_H

View File

@@ -51,15 +51,3 @@ uint8_t get_reboot_status(void)
}
return status;
}
int hex2int(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1; // Fehlerhaftes Zeichen
}
char int2hex(uint8_t i) {
if (i < 10) return '0' + i;
return 'a' + (i - 10);
}

View File

@@ -22,18 +22,4 @@ void reboot_with_status(uint8_t status_code);
*/
uint8_t get_reboot_status();
/**
* @brief Converts a hexadecimal character to its integer value.
* @param c The hexadecimal character (0-9, a-f, A-F) to convert.
* @return The integer value of the hexadecimal character, or -1 if the character is not a valid hexadecimal digit.
*/
int hex2int(char c);
/**
* @brief Converts an integer value to its hexadecimal character representation.
* @param i The integer value to convert (0-15).
* @return The hexadecimal character representation of the integer value.
*/
char int2hex(uint8_t i);
#endif // UTILS_H