ergodox-firmware/firmware/lib/layout/eeprom-macro/atmega32u4.c

241 lines
7.3 KiB
C

/* ----------------------------------------------------------------------------
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
* Released under The MIT License (see "doc/licenses/MIT.md")
* Project located at <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
/** description
* Implements the eeprom-macro functionality defined in "../eeprom-macro.h" for
* the ATMega32U4
*
*
* Implementation notes:
*
* - One cannot trust the binary layout of bit-fields. Bit-fields are great,
* but the order of the fields (among other things) is implementation
* defined, and [can change][1], even between different versions of the same
* compiler.
*
*
* [1]: http://avr.2057.n7.nabble.com/Bit-field-packing-order-changed-between-avrgcc-implementations-td19193.html
*/
#include <stdint.h>
#include <avr/eeprom.h>
#include "../../../../firmware/keyboard.h"
#include "../../../../firmware/lib/eeprom.h"
#include "../eeprom-macro.h"
// ----------------------------------------------------------------------------
// TODO: consider moving this, and all similar things in other files, into the
// '.h' files instead (and *always* including the '.h' files in implementing
// '.c' files)
#ifndef OPT__EEPROM_MACRO__EEPROM_SIZE
#error "OPT__EEPROM_MACRO__EEPROM_SIZE not defined"
#endif
/** macros/OPT__EEPROM_MACRO__EEPROM_SIZE/description
* The total size (in bytes) of the EEPROM memory to be allocated by this file
*
* Notes:
* - The ATMega32U4 has 1024 bytes of internal EEPROM total
*/
// ----------------------------------------------------------------------------
/** macros/VERSION/description
* The version number of `struct eeprom`
*/
#define VERSION 1
// ----------------------------------------------------------------------------
/** types/eeprom/description
* The layout of this library's data in the EEPROM
*
*
* TODO: update
* Struct members:
* - `meta`: For keeping track of layout metadata (`[3]` for fault tolerance)
* - `version`: The version of this layout
* - `0x00`, `0xFF` => EEPROM is uninitialized
* - `status`: (see `enum eeprom_status`)
*
* - `table`: To help in quickly failing if there is no macro for a given UID
* - `rows`: The number of rows this table has
* - `columns`: The number of columns this table has
* - `data`: Each entry contains a `uint8_t`, with
* `(bool)( (data[row][column] >> layer) & 0x1 )`
* indicating whether there is a macro defined for that (layer, row,
* column) tuple. This limits the layers we can deal with to those
* between 0 and 7, inclusive.
*
* - `macros`: A block of memory for storing macros
* - `length`: The number of elements in `macros.data`
* - `data`: A collection of `macro_header`s followed by the defined number
* of `macro_action`s. Essentially, a collection of (not necessarily
* contiguous) linked lists of macros, with one list for every row,
* column pair that has a remapping.
*
*
* Notes:
*
* - We keep track of `table.rows`, `table.columns`, and `macros.length`, in
* addition to `header.version`, because they all effect the precise layout
* of the persistent data; if any of them is different, special handling is
* required at the least, and usually the stored data will be unusable.
*
* - The struct must be `packed` and `aligned(1)`, or we risk allocating more
* than `OPT__EEPROM_MACRO__EEPROM_SIZE` bytes. This should be the default
* when compiling with `avr-gcc`, but it's important to emphasize that we
* depend on it.
*
* TODO: implement putting a log at the end :)
* - how to describe shifting data down?
* - max space to leave for compression?
*/
/** types/macro_header/description
* The header for a macro living in `macros.data`
*
* Struct members:
* - `flag`:
* - `true` => `macro_header`
* - `false` => `macro_action`
* - `status`: (see `enum macro_header_status` for explicitly defined values)
* - [other]: to be moved `status - MH_S_TO_MOVE_1 + 1` indices towards
* `macros.data[0]`
* - `run_length`: The number of `macro_action`s following this header
* - `0x00` => this header marks the beginning of unused space
* - `uid`: The Unique IDentifier (UID) of this macro
*/
/** types/macro_action/description
* A single action belonging to a macro living in `macros.data`
*
* Struct members:
* - `flag`:
* - `true` => `macro_header`
* - `false` => `macro_action`
* - The key state (`pressed` or unpressed), `row`, and `column` of the action
* recorded
*
* Notes:
* - To be "executed" by calling `kb__layout__exec_key()` with the appropriate
* arguments.
*/
// ----------------------------------------------------------------------------
// TODO: redesign
struct eeprom {
struct meta {
uint8_t version[3];
} meta;
struct table {
uint8_t rows;
uint8_t columns;
uint8_t data[OPT__KB__ROWS][OPT__KB__COLUMNS];
} table;
struct macros {
uint16_t length;
uint8_t data[ OPT__EEPROM_MACRO__EEPROM_SIZE
- 2 // for `length`
- sizeof(struct meta)
- sizeof(struct table) ];
} macros;
} __attribute__((packed, aligned(1)));
struct macro_header {
uint8_t type;
uint8_t run_length;
uint16_t uid;
};
struct log_header {
uint8_t type;
uint8_t run_length[2];
};
enum {
HEADER_DELETED,
HEADER_MACRO,
LOG_ATOMIC_WRITE,
LOG_ATOMIC_COPY,
HEADER_NULL = 0xFF,
};
struct macro_action {
uint8_t pressed : 1;
uint8_t row : 7;
uint8_t reserved : 1;
uint8_t column : 7;
};
struct log_action_copy {
uint16_t to;
uint16_t from;
uint8_t length;
};
struct log_action_write {
uint16_t to;
uint8_t data;
};
// ----------------------------------------------------------------------------
struct eeprom eeprom EEMEM;
// ----------------------------------------------------------------------------
/** functions/atomic_write/description
* TODO
*/
static void atomic_write(uint8_t length, uint8_t * address[], uint8_t data[]) {
}
/** functions/atomic_copy/description
* TODO
*/
static void atomic_copy(uint8_t * to, uint8_t * from, uint8_t length) {
}
/** functions/compress/description
* Compress `macros.data`
*
* Shift all macros towards index `0`, overwriting the areas previously
* occupied by deleted macros.
*/
static void compress(void) {
}
// ----------------------------------------------------------------------------
uint8_t eeprom_macro__init(void) {
return 0;
}
uint8_t eeprom_macro__record__start(uint8_t skip) {
return 0;
}
uint8_t eeprom_macro__record__stop(uint8_t skip, eeprom_macro__uid_t index) {
return 0;
}
uint8_t eeprom_macro__play(eeprom_macro__uid_t index) {
return 0;
}
void eeprom_macro__clear(eeprom_macro__uid_t index) {
}
void eeprom_macro__clear_all(void) {
}