working on "lib/eeprom-macro": still planning...!

would it be better to just assume power won't fail (and writes will
always be successful), and tell people that if there's a macro they
really care about, they really should write it in the source, and
recompile?

at least initially... and then if i decide that there needs to be fault
tolerance in the library, i can implement some logging of some sort.  it
probably wouldn't take *that* much more space, actually, even to do it
pretty generally, than what i'm trying to do right now... dunno.

but under normal use, it should be safe enough to live dangerously...

and i'm not going to implement a way for people who don't want to get a
nice avr toolchain going to put macros in after compilation anyway (since
it'd be easier to just write a UI... and hopefully other people haven't
lost interest, and will still do that for me... :) lol ).  so the eeprom
is not really a place that permanent macros *shoud* be, anyways...

ya... i think i should do that

</random thoughts> ... i'm tired
partial-rewrite
Ben Blazak 2013-06-06 02:35:23 -07:00
parent 1f4dd59ea1
commit b9ff36161e
2 changed files with 76 additions and 39 deletions

View File

@ -52,19 +52,13 @@ typedef struct {
uint8_t layer : 5;
uint8_t row : 5;
uint8_t column : 5;
} eeprom_macro__index_t __attribute__((packed, aligned(1)));
} eeprom_macro__index_t;
// ----------------------------------------------------------------------------
uint8_t eeprom_macro__init (void);
uint8_t eeprom_macro__record__start (uint8_t skip);
uint8_t eeprom_macro__record__stop (uint8_t skip, eeprom_macro__index_t index);
// TODO: do it this way:
// uint8_t eeprom_macro__record__stop ( uint8_t skip,
// bool pressed,
// uint8_t layer,
// uint8_t row,
// uint8_t column );
uint8_t eeprom_macro__play (eeprom_macro__index_t index);
void eeprom_macro__clear (eeprom_macro__index_t index);
void eeprom_macro__clear_all (void);

View File

@ -7,6 +7,22 @@
/** description
* Implements the eeprom-macro functionality defined in "../eeprom-macro.h" for
* the ATMega32U4
*
*
* Implementation notes:
*
* - Do not 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.
*
* - We use bit-fields in this file quite happily, but when they're read
* from or written to the EEPROM, it should be handled explicitly, field
* by field, so as to not potentially bite users who upgrade their
* compiler, then compile and install a new firmware with the same EEPROM
* layout, expecting their macros to still work :) .
*
*
* [1]: http://avr.2057.n7.nabble.com/Bit-field-packing-order-changed-between-avrgcc-implementations-td19193.html
*/
@ -42,11 +58,26 @@
/** types/eeprom/description
* The layout of this library's data in the EEPROM
*
*
* Struct members:
* - `meta0`: For keeping track of layout metadata
* - `meta`: For keeping track of layout metadata (`[3]` for redundancy and
* fault tolerance)
* - `version`: The version of this layout
* - `0x00`, `0xFF` => uninitialized
* - `free`: The first free element in `macros.data`
* - `0x00`, `0xFF` => EEPROM is uninitialized
* - `first_free`: The index of the first free element in `macros.data`
* (`[5]` for write balancing a little)
* - When `compress_offset` is `0x00`, this value will be the index of
* the beginning of the current `compress_offset` sized block that is
* being updated
* - When `compress_offset` is `0xFE`, this value will be the index of
* the current macro header who's `next` might need to be updated
* - `compress_offset`: The offset (in `macros.data` indices) between what
* we're currently copying, and where we're copying it. This will be
* equal to the amount of `macros.data` elements worth of space that
* belonged to all deleted macros found so far.
* - `0x00` => `compress()` is running, but not copying data yet
* - `0xFE` => `compress()` is updating `next` pointers
* - `0xFF` => `compress()` is not running
*
* - `table`: To help in quickly finding macros based on UID
* - `rows`: The number of rows this table has
@ -54,28 +85,32 @@
* - `data`: Each entry contains the index of the beginning of the first
* macro with the corresponding row and column in its UID
*
* - `meta1`: For redundancy and write balancing (see `meta0`)
*
* - `macros`: A block of memory for storing macros
* - `size`: The number of elements in `macros.data`
* - `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.
*
* - `meta2`: For redundancy and write balancing (see `meta0`)
*
* Notes:
* - We keep track of `table.rows`, `table.columns`, and `macros.size`, in
*
* - 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.
*/
struct eeprom {
struct meta0 {
struct meta {
uint8_t version;
uint8_t free;
} meta0;
uint8_t first_free[5];
uint8_t compress_offset;
} meta[3];
struct table {
uint8_t rows;
@ -83,22 +118,13 @@ struct eeprom {
uint8_t data[OPT__KB__ROWS][OPT__KB__COLUMNS];
} table;
struct meta1 {
uint8_t version;
uint8_t free;
} meta1;
struct macros {
uint8_t size;
uint8_t length;
uint32_t data[ ( OPT__EEPROM_MACRO__EEPROM_SIZE
- sizeof(struct meta0) * 3
- sizeof(uint8_t) // for `macros.length`
- sizeof(struct meta) * 3
- sizeof(struct table) ) >> 2 ];
} macros;
struct meta2 {
uint8_t version;
uint8_t free;
} meta2;
} __attribute__((packed, aligned(1)));
/** types/macro_header/description
@ -116,7 +142,7 @@ struct macro_header {
uint8_t next;
uint8_t length;
eeprom_macro__index_t uid;
} __attribute__((packed, aligned(1)));
};
/** types/macro_action/description
* A single action belonging to a macro living in `eeprom.macros.data`
@ -128,16 +154,13 @@ struct macro_header {
* Notes:
* - To be "executed" by calling `kb__layout__exec_key()` with the appropriate
* arguments.
*
* TODO: binary format is important: use explicit bit shifting/masking instead
* of bitfields
*/
struct macro_action {
uint8_t padding : 1;
bool pressed : 1;
uint8_t row : 7;
uint8_t column : 7;
} __attribute__((packed, aligned(1)));
bool pressed : 1;
uint8_t row : 7;
uint8_t column : 7;
uint8_t padding_0 : 1;
};
// ----------------------------------------------------------------------------
@ -147,7 +170,21 @@ uint8_t test[ sizeof(eeprom.macros.data) ];
// ----------------------------------------------------------------------------
/** functions/compress/description
* Compress `macros.data`
*
* Shift all macros towards index `0`, overwriting the areas previously
* occupied by deleted macros.
*/
static void compress(void) {
// - when we move macros, we need to update the `next` pointers
// appropriately
}
// ----------------------------------------------------------------------------
uint8_t eeprom_macro__init(void) {
// - check for invalid `next` pointers?
return 0;
}
@ -156,6 +193,12 @@ uint8_t eeprom_macro__record__start(uint8_t skip) {
}
uint8_t eeprom_macro__record__stop(uint8_t skip, eeprom_macro__index_t index) {
// - write macro_header : safe, before updating `first_free`
// - update previous `next` : safeish: will have been 0x00 before update
// will be invalid (probably) after
// update, until `first_free` is
// updated
// - update `first_free` : unsafe
return 0;
}