diff --git a/middleware/include/eeprom_alloc.h b/middleware/include/eeprom_alloc.h new file mode 100644 index 00000000..adca5e6a --- /dev/null +++ b/middleware/include/eeprom_alloc.h @@ -0,0 +1,88 @@ +/** + * @file eeprom_alloc.h + * @author Sogo Nishihara (sogonishi@gmail.com) + * @brief EEPROM Block Allocation Management + * @version 0.1 + * @date 2026-01-02 + * + * This file provides functions for managing data block allocation in the + * EEPROM. It maintains a bit-vector allocation table to track which blocks are + * in use. + * + */ + +#ifndef EEPROM_ALLOC_H +#define EEPROM_ALLOC_H + +#include + +#include "eeprom_directory_struct.h" + +/** + * @brief Initialize allocation table by loading it from EEPROM memory. + * + * This function reads the allocation table from EEPROM and populates the + * directory's alloc_table structure in memory. + * + * @param directory Pointer to the initialized directory structure. + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR If EEPROM read operation fails. + */ +eeprom_status_t init_alloc_table(eeprom_directory_t *directory); + +/** + * @brief Print the allocation table in a human-readable format. + * + * This function prints the allocation table showing which blocks are allocated + * (1) and which are free (0). The output is formatted with 8 bits per group and + * 64 bits per line. + * + * @param directory Pointer to the initialized directory structure. + */ +void print_alloc_table(eeprom_directory_t *directory); + +/** + * @brief Allocate a single block from the EEPROM. + * + * This function finds the first free block in the allocation table, marks it as + * allocated in both local memory and EEPROM, and returns its ID. + * + * @param directory Pointer to the initialized directory structure. + * + * @return uint16_t Returns the allocated block ID on success, or BLOCK_COUNT if + * allocation fails. + * + * @retval BLOCK_COUNT If no free blocks are available or EEPROM update fails. + */ +uint16_t alloc_block(eeprom_directory_t *directory); + +/** + * @brief Free one or more blocks in the EEPROM. + * + * This function marks the specified blocks as free in both local memory and + * EEPROM. If a block is already freed, this function does nothing for that + * block. + * + * @param directory Pointer to the initialized directory structure. + * @param ids Array of block IDs to free. + * @param size Number of block IDs in the array. + */ +void free_block(eeprom_directory_t *directory, uint16_t *ids, uint8_t size); + +/** + * @brief Check if a block is allocated. + * + * This function checks the allocation table to determine if a specific block is + * currently allocated or free. + * + * @param directory Pointer to the initialized directory structure. + * @param id Block ID to check. + * + * @return int Returns 1 if the block is allocated, 0 if it is free. + */ +int is_allocated(eeprom_directory_t *directory, uint16_t id); + +#endif diff --git a/middleware/include/eeprom_directory.h b/middleware/include/eeprom_directory.h index a60d2720..f81f545c 100644 --- a/middleware/include/eeprom_directory.h +++ b/middleware/include/eeprom_directory.h @@ -1,83 +1,115 @@ /** - * @file eepromdirectory.h - * @brief EEPROM Directory Management - * - * This file provides functions to initialize and manage an EEPROM directory with partitions. - * Functions return `eeprom_status_t` error codes, which are defined in `eeprom_status.h`. - * - */ + * @file eeprom_directory.h + * @author Sogo Nishihara (sogonishi@gmail.com) + * @brief EEPROM Directory Management API + * + * This file defines the public API for a simple key-value directory + * stored on EEPROM device. + * + * The directory provides block-based persistent storage, managing + * allocation, lookup, insertion, and deletion of values associated + * with fixed-size (4-byte) keys. + * + * All functions return eeprom_status_t error codes defined in + * eeprom_status.h. */ #ifndef EEPROM_DIRECTORY_H #define EEPROM_DIRECTORY_H -#include "eeprom_status.h" -#include -#include - -struct partition_cfg { - const char *id; /* The ID of the partition */ - uint16_t size; /* The size of the partition in bytes */ - uint16_t address; - uint16_t head_address; -}; +#include +#include +#include -typedef struct { - struct partition_cfg *partitions; - size_t num_partitions; -} eeprom_directory_t; +#include "eeprom_alloc.h" +#include "eeprom_directory_struct.h" +#include "eeprom_status.h" +#include "eeprom_storage.h" +#include "m24c32.h" /** - * @brief Initializes an EEPROM directory with partition IDs and sizes. - * - * This function sets up partition configurations within the EEPROM memory. - * The total allocated address space is calculated based on the provided partitions. - * - * @param directory Pointer to the EEPROM directory structure. - * @param partitions Array of partition configurations, including IDs and sizes. - * @param num_partitions Number of partitions to create. - * @return eeprom_status_t Returns EEPROM_OK on success or an error code on failure. + * @brief Initialize the EEPROM directory structure. + * + * This function initializes the directory structure with the provided device + * interface, loads the allocation table and key map from EEPROM memory. + * + * @param device Pointer to the M24C32 device interface structure. + * @param directory Pointer to the directory structure to initialize. Must be + * allocated by caller. + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_NULL_POINTER If device or directory is NULL. + * @retval EEPROM_ERROR If initialization of alloc table or storage fails. */ -eeprom_status_t directory_init(eeprom_directory_t *directory, - const struct partition_cfg partitions[], - size_t num_partitions); +eeprom_status_t directory_init(m24c32_t *device, eeprom_directory_t *directory); /** - * @brief Get the address of the beginning of a partition. - * - * This function searches for a partition by its name and returns its starting address. - * - * @param directory Pointer to the EEPROM directory structure. - * @param key Name of the partition. - * @param address Pointer to store the retrieved base address. - * @return eeprom_status_t Returns EEPROM_OK on success or an error code if the partition is not found. + * @brief Retrieve a value from the directory by key. + * + * This function looks up a key in the directory and retrieves the associated + * data. The output buffer is allocated by this function and must be freed by + * the caller. + * + * @param directory Pointer to the initialized directory structure. + * @param key Pointer to a 4-byte key (not null-terminated). + * @param out Pointer to a pointer that will receive the allocated output + * buffer. The caller is responsible for freeing this buffer. + * @param out_size Pointer to store the size of the output data in bytes. + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_NULL_POINTER If directory, key, out, or out_size is + * NULL. + * @retval EEPROM_ERROR_NOT_FOUND If the key does not exist in the directory. + * @retval EEPROM_ERROR_ALLOCATION If memory allocation fails. */ -eeprom_status_t eeprom_get_base_address(const eeprom_directory_t *directory, - const char *key, uint16_t *address); +eeprom_status_t get_directory_value(eeprom_directory_t *directory, + const uint8_t *key, uint8_t **out, + uint16_t *out_size); /** - * @brief Get the current read/write head of a partition. - * - * This function provides the address of the next location available for writing within the partition. - * - * @param directory Pointer to EEPROM directory struct. - * @param key Name of the partition. - * @param address Pointer to store the retrieved head address. - * @return eeprom_status_t Returns EEPROM_OK on success or an error code if the partition is not found. + * @brief Store a value in the directory with the specified key. + * + * This function stores data in the directory. If the key already exists, the + * old value is deleted first. The data is stored in blocks of BLOCK_SIZE bytes, + * with remaining bytes padded with zeros. + * + * @param directory Pointer to the initialized directory structure. + * @param key Pointer to a 4-byte key (not null-terminated). + * @param value Pointer to the data to store. + * @param value_size Size of the data in bytes (maximum 16 bytes, i.e., 4 + * blocks). + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_NULL_POINTER If directory or value is NULL. + * @retval EEPROM_ERROR If value_size is 0. + * @retval EEPROM_ERROR_TOO_BIG If value_size exceeds 16 bytes (4 blocks). + * @retval EEPROM_ERROR_ALLOCATION If block allocation fails. */ -eeprom_status_t eeprom_get_head_address(const eeprom_directory_t *directory, - const char *key, uint16_t *address); +eeprom_status_t set_directory_value(eeprom_directory_t *directory, + const uint8_t *key, uint8_t *value, + const uint16_t value_size); /** - * @brief Get the size of a partition - * - * This function returns the allocated size of a specific partition in bytes. - * - * @param directory Pointer to EEPROM directory struct. - * @param key Name of a partition. - * @param size Pointer to store the partition size. - * @return eeprom_status_t Returns EEPROM_OK on success or an error code if the partition is not found. + * @brief Delete a value from the directory by key. + * + * This function removes a key-value pair from the directory and frees the + * associated blocks. The blocks are marked as available for reuse. + * + * @param directory Pointer to the initialized directory structure. + * @param key Pointer to a 4-byte key (not null-terminated). + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_NULL_POINTER If directory or key is NULL. + * @retval EEPROM_ERROR_NOT_FOUND If the key does not exist in the directory. */ -eeprom_status_t eeprom_get_size(const eeprom_directory_t *directory, - const char *key, uint16_t *size); +eeprom_status_t delete_directory_value(eeprom_directory_t *directory, + const uint8_t *key); #endif // EEPROM_DIRECTORY_H \ No newline at end of file diff --git a/middleware/include/eeprom_directory_struct.h b/middleware/include/eeprom_directory_struct.h new file mode 100644 index 00000000..2120360f --- /dev/null +++ b/middleware/include/eeprom_directory_struct.h @@ -0,0 +1,110 @@ +/** + * @file eeprom_directory_struct.h + * @author Sogo Nishihara (sogonishi@gmail.com) + * @brief EEPROM Directory Data Structures and Constants + * @version 0.1 + * @date 2026-01-02 + * + * This file defines the data structures and constants used for the EEPROM + * directory system. It includes memory layout definitions, block allocation + * parameters, and structure definitions. + * + * EEPROM Memory Layout (Total: 4096 bytes) + * + * Address + * 0x0000 + * +--------------------------------------------------+ + * | Allocation Table | + * | - Bit map (1 bit per data block) | + * | - Size : ALLOC_TABLE_SIZE bytes | + * | - Blocks : BLOCK_COUNT | + * +--------------------------------------------------+ + * | Key Map Table | + * | - Array of directory_key_map_t | + * | - Entry size : KEY_MAP_STRUCT_SIZE bytes | + * | - Entry count: KEY_MAP_COUNT | + * | - Total size : KEY_MAP_SIZE bytes | + * +--------------------------------------------------+ + * | Data Space | + * | - Data blocks | + * | - Block size : BLOCK_SIZE bytes | + * | - Block count: BLOCK_COUNT | + * | | + * | +------------------+ | + * | | Block 0 | | + * | +------------------+ | + * | | Block 1 | | + * | +------------------+ | + * | | ... | | + * | +------------------+ | + * | | Block N | | + * | +------------------+ | + * +--------------------------------------------------+ + * 0x0FFF + */ + +#ifndef EEPROM_DIRECTORY_STRUCT +#define EEPROM_DIRECTORY_STRUCT + +/** @brief Size of a single key-map entry structure in bytes (4-byte key + + * 8-byte IDs array). */ +#define KEY_MAP_STRUCT_SIZE 12 + +/** @brief Maximum number of key-value pairs that can be stored in the + * directory. */ +#define KEY_MAP_COUNT 128 + +/** @brief Size of a data block in bytes. */ +#define BLOCK_SIZE 4 + +/** @brief Total number of data blocks available in the EEPROM. */ +#define BLOCK_COUNT 512 + +/** @brief Starting address of the allocation table in EEPROM. */ +#define ALLOC_TABLE_BEGIN 0 + +/** @brief Size of the allocation table in bytes (one bit per block). */ +#define ALLOC_TABLE_SIZE (BLOCK_COUNT / 8) + +/** @brief Starting address of the key-map table in EEPROM. */ +#define KEY_MAP_BEGIN (ALLOC_TABLE_BEGIN + ALLOC_TABLE_SIZE) + +/** @brief Total size of the key-map table in bytes. */ +#define KEY_MAP_SIZE (KEY_MAP_STRUCT_SIZE * KEY_MAP_COUNT) + +/** @brief Starting address of the data space in EEPROM. */ +#define DATA_SPACE_BEGIN (KEY_MAP_BEGIN + KEY_MAP_SIZE) + +/** @brief Total size of the EEPROM in bytes. */ +#define EEPROM_SIZE 4096 + +#include + +#include "m24c32.h" + +/** + * @brief Key-value mapping structure. + * + * This structure maps a 4-byte key to up to 4 block IDs. Each block ID points + * to a BLOCK_SIZE-byte data block in the EEPROM data space. + */ +typedef struct { + uint8_t key[4]; /** 4-byte key (not null-terminated). */ + uint16_t ids[4]; /** Array of up to 4 block IDs (BLOCK_COUNT indicates end). */ +} directory_key_map_t; + +/** + * @brief Main EEPROM directory structure. + * + * This structure contains all the state information for the EEPROM directory, + * including the device interface, allocation table, and key map. + */ +typedef struct { + m24c32_t *device; /** Pointer to the M24C32 device interface. */ + uint8_t alloc_table[ALLOC_TABLE_SIZE]; /** Bit-vector allocation table (one + bit per block). */ + directory_key_map_t + key_map[KEY_MAP_COUNT]; /** Array of key-value mappings. */ +} eeprom_directory_t; + +#endif \ No newline at end of file diff --git a/middleware/include/eeprom_status.h b/middleware/include/eeprom_status.h index 94c3f186..8e396e85 100644 --- a/middleware/include/eeprom_status.h +++ b/middleware/include/eeprom_status.h @@ -1,10 +1,10 @@ /** * @file eeprom_status.h * @brief EEPROM Standardized Error Codes - * - * This file defines the `eeprom_status_t` enumeration, which provides standardized - * error codes for all EEPROM-related operations. - * + * + * This file defines the `eeprom_status_t` enumeration, which provides + * standardized error codes for all EEPROM-related operations. + * */ #ifndef EEPROM_STATUS @@ -19,7 +19,7 @@ typedef enum { EEPROM_ERROR_NULL_POINTER = -5, EEPROM_ERROR_ALLOCATION = -6, EEPROM_ERROR_COMMS = -7, - + EEPROM_ERROR_TOO_BIG = -8 } eeprom_status_t; #endif \ No newline at end of file diff --git a/middleware/include/eeprom_storage.h b/middleware/include/eeprom_storage.h new file mode 100644 index 00000000..4a8bb513 --- /dev/null +++ b/middleware/include/eeprom_storage.h @@ -0,0 +1,93 @@ +/** + * @file eeprom_storage.h + * @author Sogo Nishihara (sogonishi@gmail.com) + * @brief EEPROM Storage Management + * + * This file provides functions for managing data storage in the EEPROM + * directory. Functions handle reading, writing, and deleting data blocks + * associated with block IDs. + * + */ + +#ifndef EEPROM_STORAGE +#define EEPROM_STORAGE + +#include + +#include "eeprom_alloc.h" +#include "eeprom_directory_struct.h" + +/** + * @brief Initialize storage by loading key-map table from EEPROM memory. + * + * This function reads the key-map table from EEPROM and populates the + * directory's key_map structure in memory. + * + * @param directory Pointer to the initialized directory structure. + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_ALLOCATION If memory allocation fails. + * @retval EEPROM_ERROR If EEPROM read operation fails. + */ +eeprom_status_t init_storage(eeprom_directory_t *directory); + +/** + * @brief Retrieve data from EEPROM using block IDs. + * + * This function reads data from multiple blocks in EEPROM and allocates a + * buffer to store the result. The caller is responsible for freeing the output + * buffer. + * + * @param directory Pointer to the initialized directory structure. + * @param ids Array of block IDs (up to 4, terminated by BLOCK_COUNT). + * @param out Pointer to a pointer that will receive the allocated output + * buffer. The caller must free this buffer. + * @param out_size Pointer to store the size of the output data in bytes. + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_ALLOCATION If memory allocation fails. + * @retval EEPROM_ERROR_NOT_FOUND If any block ID is not allocated. + * @retval EEPROM_ERROR If EEPROM read operation fails. + */ +eeprom_status_t get_data(eeprom_directory_t *directory, const uint16_t *ids, + uint8_t **out, uint16_t *out_size); + +/** + * @brief Store data in EEPROM using block IDs. + * + * This function writes data to multiple blocks in EEPROM. Each block is + * BLOCK_SIZE bytes. If the data size is smaller than the total block size, + * remaining bytes are padded with zeros. + * + * @param directory Pointer to the initialized directory structure. + * @param ids Array of block IDs (up to 4, terminated by BLOCK_COUNT). + * @param value Pointer to the data to write. + * @param value_size Size of the data in bytes. + * + * @return eeprom_status_t Returns EEPROM_OK on success, or an error code on + * failure. + * + * @retval EEPROM_ERROR_ALLOCATION If memory allocation for block buffer fails. + * @retval EEPROM_ERROR If EEPROM write operation fails. + */ +eeprom_status_t put_data(eeprom_directory_t *directory, const uint16_t *ids, + uint8_t *value, uint16_t value_size); + +/** + * @brief Delete data blocks from EEPROM. + * + * This function frees the blocks associated with the given IDs, making them + * available for reuse. The blocks are marked as free in the allocation table. + * + * @param directory Pointer to the initialized directory structure. + * @param ids Array of block IDs to free (up to 4, terminated by BLOCK_COUNT). + * + * @return eeprom_status_t Returns EEPROM_OK on success. + */ +eeprom_status_t delete_data(eeprom_directory_t *directory, uint16_t *ids); + +#endif diff --git a/middleware/include/m24c32_eeprom_directory.h b/middleware/include/m24c32_eeprom_directory.h deleted file mode 100644 index 5835a634..00000000 --- a/middleware/include/m24c32_eeprom_directory.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -/** - * @file m24c32_eeprom_directory.h - * @brief EEPROM Partitioned Read/Write Interface - * - * This file provides an interface for reading and writing to specific partitions - * within an EEPROM using a directory-based approach. - * - */ - -#include "m24c32.h" -#include "eeprom_directory.h" - -/** - * @brief Write to a section of the EEPROM. Writes that overflow the size of the partition - * will wrap to the head of the partition. - * - * @param directory Pointer to the EEPROM directory structure. - * @param eeprom Pointer to the EEPROM device structure. - * @param id ID of eeprom partition to write to. - * @param data Data to write. - * @param len Length of data to write in Bytes. - * @param offset Offset from partition base address in Bytes. - * @return eeprom_status_t Returns EEPROM_OK on success or an error code on failure. - */ -eeprom_status_t m24c32_directory_write(const eeprom_directory_t *directory, - m24c32_t *eeprom, const char *id, - uint8_t *data, uint16_t len, - uint16_t offset); - -/** - * @brief Read a section of the EEPROM. Reads that overflow the size of the partition - * will wrap to the head of the partition. - * - * @param directory Pointer to the EEPROM directory structure. - * @param eeprom Pointer to the EEPROM device structure. - * @param id ID of eeprom partition to read from. - * @param data Read output. - * @param len Length of data to write in Bytes. - * @param offset Offset from partition base address in Bytes. - * @return eeprom_status_t Returns EEPROM_OK on success or an error code on failure. - */ -eeprom_status_t m24c32_directory_read(const eeprom_directory_t *directory, - m24c32_t *eeprom, const char *id, - uint8_t *data, uint16_t len, - uint16_t offset); \ No newline at end of file diff --git a/middleware/src/eeprom_alloc.c b/middleware/src/eeprom_alloc.c new file mode 100644 index 00000000..4aa8b2c5 --- /dev/null +++ b/middleware/src/eeprom_alloc.c @@ -0,0 +1,97 @@ +#include "eeprom_alloc.h" + +#define byte_index(n) ((n) / 8) +#define bit_index(n) ((n) % 8) +#define nth_bit_mask(n) (1 << (n)) + +static int get_alloc_table(uint8_t *table, uint16_t id) +{ + return (table[byte_index(id)] >> bit_index(id)) & 1; +} + +static void put_alloc_table(uint8_t *table, uint16_t id, uint8_t val) +{ + uint8_t bit_mask = nth_bit_mask(bit_index(id)); + + if (val) { + table[byte_index(id)] |= bit_mask; + } else { + bit_mask = ~bit_mask; + table[byte_index(id)] &= bit_mask; + } +} + +static eeprom_status_t update_eeprom_alloc_table(eeprom_directory_t *directory, + uint16_t id) +{ + uint16_t data_index = byte_index(id); + m24c32_t *device = directory->device; + uint16_t addr = ALLOC_TABLE_BEGIN + data_index; + uint8_t *data = directory->alloc_table + data_index; + + return m24c32_write(device, addr, data, 1); +} + +eeprom_status_t init_alloc_table(eeprom_directory_t *directory) +{ + m24c32_t *device = directory->device; + + return m24c32_read(device, ALLOC_TABLE_BEGIN, directory->alloc_table, + ALLOC_TABLE_SIZE); +} + +void print_alloc_table(eeprom_directory_t *directory) +{ + uint8_t *alloc_table = directory->alloc_table; + for (int id = 0; id < BLOCK_COUNT; id++) { + putchar(get_alloc_table(alloc_table, id) ? '1' : '0'); + + if ((id + 1) % 64 == 0) { + putchar('\n'); + } else if ((id + 1) % 8 == 0) { + putchar(' '); + } + } + putchar('\n'); +} + +uint16_t alloc_block(eeprom_directory_t *directory) +{ + uint8_t *alloc_table = directory->alloc_table; + + for (uint16_t id = 0; id < BLOCK_COUNT; id++) { + if (get_alloc_table(alloc_table, id) == 0) { + put_alloc_table(alloc_table, id, 1); + eeprom_status_t ret = + update_eeprom_alloc_table(directory, id); + if (ret != EEPROM_OK) { + printf("ERROR: failed to update eeprom alloc table.\n"); + put_alloc_table(alloc_table, id, 0); + return BLOCK_COUNT; + } + return id; + } + } + return BLOCK_COUNT; +} + +void free_block(eeprom_directory_t *directory, uint16_t *ids, uint8_t size) +{ + uint8_t *alloc_table = directory->alloc_table; + + for (int i = 0; i < size; i++) { + uint16_t id = ids[i]; + put_alloc_table(alloc_table, id, 0); + eeprom_status_t ret = update_eeprom_alloc_table(directory, id); + if (ret != EEPROM_OK) { + printf("ERROR: failed to update eeprom allo table.\n"); + } + } +} + +int is_allocated(eeprom_directory_t *directory, uint16_t id) +{ + uint8_t *alloc_table = directory->alloc_table; + + return get_alloc_table(alloc_table, id); +} diff --git a/middleware/src/eeprom_directory.c b/middleware/src/eeprom_directory.c index 19042ee7..87ca270f 100644 --- a/middleware/src/eeprom_directory.c +++ b/middleware/src/eeprom_directory.c @@ -1,88 +1,180 @@ #include "eeprom_directory.h" -#include -#include -#include +static uint16_t *get_ids(eeprom_directory_t *directory, const uint8_t *key) +{ + directory_key_map_t *key_map = directory->key_map; -#define BASE_ADDR 0 + for (int i = 0; i < KEY_MAP_COUNT; i++) { + // Keys are fixed 4-byte arrays, not null-terminated strings + if (memcmp(key_map[i].key, key, 4) == 0) { + return key_map[i].ids; + } + } + return NULL; +} -eeprom_status_t directory_init(eeprom_directory_t *directory, - const struct partition_cfg partitions[], - size_t num_partitions) +static eeprom_status_t set_key(eeprom_directory_t *directory, + const uint8_t *key, uint16_t *ids) { - if ((directory == NULL) || (partitions == NULL)) { - return EEPROM_ERROR_NULL_POINTER; + m24c32_t *device = directory->device; + directory_key_map_t *key_map = directory->key_map; + + for (int i = 0; i < KEY_MAP_COUNT; i++) { + if ((char)key_map[i].key[0] == '\0') { + memcpy(key_map[i].key, key, 4); + memcpy(key_map[i].ids, ids, 8); + + uint16_t addr = KEY_MAP_BEGIN + i * KEY_MAP_STRUCT_SIZE; + return m24c32_write(device, addr, + (uint8_t *)&key_map[i], + KEY_MAP_STRUCT_SIZE); + } } + return EEPROM_ERROR_ALLOCATION; +} - directory->partitions = - malloc(sizeof(struct partition_cfg) * num_partitions); - - if (directory->partitions == NULL) { - return EEPROM_ERROR_ALLOCATION; +static eeprom_status_t delete_key(eeprom_directory_t *directory, + const uint8_t *key) +{ + m24c32_t *device = directory->device; + directory_key_map_t *key_map = directory->key_map; + + for (int i = 0; i < KEY_MAP_COUNT; i++) { + // Keys are fixed 4-byte arrays, not null-terminated strings + if (memcmp(key_map[i].key, key, 4) == 0) { + memset(&key_map[i], 0, KEY_MAP_STRUCT_SIZE); + + uint16_t addr = KEY_MAP_BEGIN + i * KEY_MAP_STRUCT_SIZE; + return m24c32_write(device, addr, + (uint8_t *)&key_map[i], + KEY_MAP_STRUCT_SIZE); + } } + return EEPROM_ERROR_NOT_FOUND; +} - /* Accumulator that gives the address of the start of the partition */ - size_t addr_acc = BASE_ADDR; - - for (int i = 0; i < num_partitions; i++) { - directory->partitions[i] = partitions[i]; - directory->partitions[i].address = addr_acc; - directory->partitions[i].head_address = addr_acc; - addr_acc += partitions[i].size; +eeprom_status_t directory_init(m24c32_t *device, eeprom_directory_t *directory) +{ + if (directory == NULL || device == NULL) { + return EEPROM_ERROR_NULL_POINTER; } - directory->num_partitions = num_partitions; - + directory->device = device; + eeprom_status_t res; + res = init_alloc_table(directory); + if (res != EEPROM_OK) { + return res; + } + res = init_storage(directory); + if (res != EEPROM_OK) { + return res; + } return EEPROM_OK; } -eeprom_status_t eeprom_get_base_address(const eeprom_directory_t *directory, - const char *key, uint16_t *address) +eeprom_status_t get_directory_value(eeprom_directory_t *directory, + const uint8_t *key, uint8_t **out, + uint16_t *out_size) { - if ((directory == NULL) || (key == NULL)) { + if (directory == NULL || key == NULL || out == NULL || + out_size == NULL) { return EEPROM_ERROR_NULL_POINTER; } - for (int i = 0; i < directory->num_partitions; i++) { - if (strcmp(directory->partitions[i].id, key) == 0) { - *address = directory->partitions[i].address; - return EEPROM_OK; - } + uint16_t *ids = get_ids(directory, key); + if (ids == NULL) { + return EEPROM_ERROR_NOT_FOUND; } - return EEPROM_ERROR_NOT_FOUND; + return get_data(directory, ids, out, out_size); } -eeprom_status_t eeprom_get_head_address(const eeprom_directory_t *directory, - const char *key, uint16_t *address) +eeprom_status_t set_directory_value(eeprom_directory_t *directory, + const uint8_t *key, uint8_t *value, + const uint16_t value_size) { - if ((directory == NULL) || (key == NULL)) { + if (directory == NULL || value == NULL) { return EEPROM_ERROR_NULL_POINTER; } + if (value_size <= 0) { + return EEPROM_ERROR; + } + + uint16_t *existing_ids = get_ids(directory, key); - for (int i = 0; i < directory->num_partitions; i++) { - if (strcmp(directory->partitions[i].id, key) == 0) { - *address = directory->partitions[i].head_address; - return EEPROM_OK; + // If key already exists, delete it first + if (existing_ids != NULL) { + eeprom_status_t res = delete_directory_value(directory, key); + if (res != EEPROM_OK) { + return res; } } - return EEPROM_ERROR_NOT_FOUND; + // Allocate new blocks for the value + int block_count = (value_size - 1) / BLOCK_SIZE + 1; + if (block_count > 4) { + return EEPROM_ERROR_TOO_BIG; + } + + uint16_t *ids = malloc(sizeof(uint16_t) * 4); + if (ids == NULL) { + return EEPROM_ERROR_ALLOCATION; + } + + for (int block_idx = 0; block_idx < block_count; block_idx++) { + uint16_t id = alloc_block(directory); + if (id == BLOCK_COUNT) { + // Free already allocated blocks on failure + for (int j = 0; j < block_idx; j++) { + free_block(directory, &ids[j], 1); + } + free(ids); + return EEPROM_ERROR_ALLOCATION; + } + ids[block_idx] = id; + } + for (int block_idx = block_count; block_idx < 4; block_idx++) { + ids[block_idx] = BLOCK_COUNT; + } + + eeprom_status_t res; + res = set_key(directory, key, ids); + if (res != EEPROM_OK) { + // Free allocated blocks on failure + free_block(directory, ids, block_count); + free(ids); + return res; + } + + res = put_data(directory, ids, value, value_size); + if (res != EEPROM_OK) { + // Free allocated blocks and remove key on failure + free_block(directory, ids, block_count); + delete_key(directory, key); + free(ids); + return res; + } + + free(ids); + return EEPROM_OK; } -eeprom_status_t eeprom_get_size(const eeprom_directory_t *directory, - const char *key, uint16_t *size) +eeprom_status_t delete_directory_value(eeprom_directory_t *directory, + const uint8_t *key) { - if ((directory == NULL) || (key == NULL)) { + if (directory == NULL || key == NULL) { return EEPROM_ERROR_NULL_POINTER; } - for (int i = 0; i < directory->num_partitions; i++) { - if (strcmp(directory->partitions[i].id, key) == 0) { - *size = directory->partitions[i].size; - return EEPROM_OK; - } + uint16_t *ids = get_ids(directory, key); + if (ids == NULL) { + return EEPROM_ERROR_NOT_FOUND; } - return EEPROM_ERROR_NOT_FOUND; -} \ No newline at end of file + eeprom_status_t res; + res = delete_data(directory, ids); + if (res != EEPROM_OK) { + return res; + } + return delete_key(directory, key); +} diff --git a/middleware/src/eeprom_storage.c b/middleware/src/eeprom_storage.c new file mode 100644 index 00000000..bc56ce87 --- /dev/null +++ b/middleware/src/eeprom_storage.c @@ -0,0 +1,143 @@ +#include "eeprom_storage.h" +#include + +static uint8_t get_id_count(const uint16_t *ids) +{ + uint8_t count = 0; + + for (int i = 0; i < 4; i++) { + if (ids[i] >= BLOCK_COUNT) { + break; + } + count++; + } + return count; +} + +static uint16_t get_addr_for_data(const uint16_t id) +{ + return DATA_SPACE_BEGIN + id * BLOCK_SIZE; +} + +eeprom_status_t init_storage(eeprom_directory_t *directory) +{ + m24c32_t *device = directory->device; + directory_key_map_t *key_map = directory->key_map; + uint8_t *data = malloc(KEY_MAP_SIZE); + if (data == NULL) { + return EEPROM_ERROR_ALLOCATION; + } + + eeprom_status_t res = + m24c32_read(device, KEY_MAP_BEGIN, data, KEY_MAP_SIZE); + if (res != EEPROM_OK) { + free(data); + return res; + } + + for (int i = 0; i < KEY_MAP_COUNT; i++) { + uint8_t *entry = data + (i * KEY_MAP_STRUCT_SIZE); + + // key[4] + memcpy(key_map[i].key, entry, 4); + + // ids[4] + for (int j = 0; j < 4; j++) { + key_map[i].ids[j] = + (uint16_t)entry[4 + j * 2] | + ((uint16_t)entry[4 + j * 2 + 1] << 8); + } + } + free(data); + return EEPROM_OK; +} + +eeprom_status_t get_data(eeprom_directory_t *directory, const uint16_t *ids, + uint8_t **out, uint16_t *out_size) +{ + uint8_t id_count = get_id_count(ids); + *out_size = BLOCK_SIZE * id_count; + + // Free existing buffer if provided + if (*out) { + free(*out); + *out = NULL; + } + + // Allocate new buffer + *out = malloc(*out_size); + if (*out == NULL) { + return EEPROM_ERROR_ALLOCATION; + } + + for (int i = 0; i < id_count; i++) { + uint16_t id = ids[i]; + if (!is_allocated(directory, id)) { + free(*out); + *out = NULL; + return EEPROM_ERROR_NOT_FOUND; + } + + uint16_t addr = get_addr_for_data(id); + uint8_t *data_ptr = *out + BLOCK_SIZE * i; + eeprom_status_t res; + + res = m24c32_read(directory->device, addr, data_ptr, + BLOCK_SIZE); + if (res != EEPROM_OK) { + free(*out); + *out = NULL; + return res; + } + } + return EEPROM_OK; +} + +eeprom_status_t put_data(eeprom_directory_t *directory, const uint16_t *ids, + uint8_t *value, uint16_t value_size) +{ + uint8_t id_count = get_id_count(ids); + uint8_t *block_buffer = malloc(BLOCK_SIZE); + if (block_buffer == NULL) { + return EEPROM_ERROR_ALLOCATION; + } + + for (int i = 0; i < id_count; i++) { + uint16_t id = ids[i]; + uint16_t addr = get_addr_for_data(id); + + // Calculate how many bytes to copy for this block + uint16_t offset = i * BLOCK_SIZE; + uint16_t bytes_to_copy = BLOCK_SIZE; + if (offset + BLOCK_SIZE > value_size) { + bytes_to_copy = value_size - offset; + } + + // Clear the block buffer + memset(block_buffer, 0, BLOCK_SIZE); + + // Copy the actual data + if (bytes_to_copy > 0) { + memcpy(block_buffer, value + offset, bytes_to_copy); + } + + // Write the entire block (BLOCK_SIZE bytes) to EEPROM + eeprom_status_t res = m24c32_write(directory->device, addr, + block_buffer, BLOCK_SIZE); + if (res != EEPROM_OK) { + free(block_buffer); + return res; + } + } + + free(block_buffer); + return EEPROM_OK; +} + +eeprom_status_t delete_data(eeprom_directory_t *directory, uint16_t *ids) +{ + uint8_t id_count = get_id_count(ids); + + free_block(directory, ids, id_count); + return EEPROM_OK; +} diff --git a/middleware/src/m24c32_eeprom_directory.c b/middleware/src/m24c32_eeprom_directory.c deleted file mode 100644 index 87eaf82c..00000000 --- a/middleware/src/m24c32_eeprom_directory.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "m24c32_eeprom_directory.h" - -eeprom_status_t m24c32_directory_write(const eeprom_directory_t *directory, - m24c32_t *eeprom, const char *id, - uint8_t *data, uint16_t len, - uint16_t offset) -{ - uint16_t base; - eeprom_status_t status = eeprom_get_base_address(directory, id, &base); - - if (status != EEPROM_OK) { - return status; // Invalid ID - } - - uint16_t size; - status = eeprom_get_size(directory, id, &size); - if (status != EEPROM_OK) { - return status; - } - - if (offset >= size) { - return EEPROM_ERROR_OUT_OF_BOUNDS; // Invalid Offset - } - - if (offset + len > size) { - return EEPROM_ERROR_ALIGNMENT; // Overwrting next partition - } - - return m24c32_write(eeprom, base + offset, data, len); -} - -eeprom_status_t m24c32_directory_read(const eeprom_directory_t *directory, - m24c32_t *eeprom, const char *id, - uint8_t *data, uint16_t len, - uint16_t offset) -{ - uint16_t base; - eeprom_status_t status = eeprom_get_base_address(directory, id, &base); - if (status != EEPROM_OK) { - return status; // Invalid ID - } - - uint16_t size; - status = eeprom_get_size(directory, id, &size); - if (status != EEPROM_OK) { - return status; - } - - if (offset >= size) { - return EEPROM_ERROR_OUT_OF_BOUNDS; // Invalid Offset - } - - if (offset + len > size) { - return EEPROM_ERROR_ALIGNMENT; // Reading beyond current partition - } - - return m24c32_read(eeprom, base + offset, data, len); -} \ No newline at end of file