Merge remote-tracking branch 'upstream/develop' into xap

This commit is contained in:
Nick Brassel 2021-09-15 11:40:29 +10:00
commit 3c66b9b0ec
7256 changed files with 77233 additions and 46094 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,132 +0,0 @@
/*
Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "keyboard.h"
#include "keycode.h"
#include "action_code.h"
#include "action_macro.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Disable macro and function features when LTO is enabled, since they break */
#ifdef LTO_ENABLE
# ifndef NO_ACTION_MACRO
# define NO_ACTION_MACRO
# endif
# ifndef NO_ACTION_FUNCTION
# define NO_ACTION_FUNCTION
# endif
#endif
/* tapping count and state */
typedef struct {
bool interrupted : 1;
bool reserved2 : 1;
bool reserved1 : 1;
bool reserved0 : 1;
uint8_t count : 4;
} tap_t;
/* Key event container for recording */
typedef struct {
keyevent_t event;
#ifndef NO_ACTION_TAPPING
tap_t tap;
#endif
#ifdef COMBO_ENABLE
uint16_t keycode;
#endif
} keyrecord_t;
/* Execute action per keyevent */
void action_exec(keyevent_t event);
/* action for key */
action_t action_for_key(uint8_t layer, keypos_t key);
action_t action_for_keycode(uint16_t keycode);
/* macro */
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt);
/* user defined special function */
void action_function(keyrecord_t *record, uint8_t id, uint8_t opt);
/* keyboard-specific key event (pre)processing */
bool process_record_quantum(keyrecord_t *record);
/* Utilities for actions. */
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
extern bool disable_action_cache;
#endif
/* Code for handling one-handed key modifiers. */
#ifdef SWAP_HANDS_ENABLE
extern bool swap_hands;
extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
# if (MATRIX_COLS <= 8)
typedef uint8_t swap_state_row_t;
# elif (MATRIX_COLS <= 16)
typedef uint16_t swap_state_row_t;
# elif (MATRIX_COLS <= 32)
typedef uint32_t swap_state_row_t;
# else
# error "MATRIX_COLS: invalid value"
# endif
void process_hand_swap(keyevent_t *record);
#endif
void process_record_nocache(keyrecord_t *record);
void process_record(keyrecord_t *record);
void process_record_handler(keyrecord_t *record);
void post_process_record_quantum(keyrecord_t *record);
void process_action(keyrecord_t *record, action_t action);
void register_code(uint8_t code);
void unregister_code(uint8_t code);
void tap_code(uint8_t code);
void tap_code_delay(uint8_t code, uint16_t delay);
void register_mods(uint8_t mods);
void unregister_mods(uint8_t mods);
void register_weak_mods(uint8_t mods);
void unregister_weak_mods(uint8_t mods);
// void set_mods(uint8_t mods);
void clear_keyboard(void);
void clear_keyboard_but_mods(void);
void clear_keyboard_but_mods_and_keys(void);
void layer_switch(uint8_t new_layer);
bool is_tap_key(keypos_t key);
bool is_tap_record(keyrecord_t *record);
bool is_tap_action(action_t action);
#ifndef NO_ACTION_TAPPING
void process_record_tap_hint(keyrecord_t *record);
#endif
/* debug */
void debug_event(keyevent_t event);
void debug_record(keyrecord_t record);
void debug_action(action_t action);
#ifdef __cplusplus
}
#endif

View file

@ -1,308 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
/** \brief Action codes
*
* 16bit code: action_kind(4bit) + action_parameter(12bit)
*
* Key Actions(00xx)
* -----------------
* ACT_MODS(000r):
* 000r|0000|0000 0000 No action code
* 000r|0000|0000 0001 Transparent code
* 000r|0000| keycode Key
* 000r|mods|0000 0000 Modifiers
* 000r|mods| keycode Modifiers+Key(Modified key)
* r: Left/Right flag(Left:0, Right:1)
*
* ACT_MODS_TAP(001r):
* 001r|mods|0000 0000 Modifiers with OneShot
* 001r|mods|0000 0001 Modifiers with tap toggle
* 001r|mods|0000 00xx (reserved)
* 001r|mods| keycode Modifiers with Tap Key(Dual role)
*
* Other Keys(01xx)
* ----------------
* ACT_USAGE(0100): TODO: Not needed?
* 0100|00| usage(10) System control(0x80) - General Desktop page(0x01)
* 0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C)
* 0100|10| usage(10) (reserved)
* 0100|11| usage(10) (reserved)
*
* ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space?
* 0101|xxxx| keycode Mouse key
*
* ACT_SWAP_HANDS(0110):
* 0110|xxxx| keycode Swap hands (keycode on tap, or options)
*
* 0111|xxxx xxxx xxxx (reserved)
*
* Layer Actions(10xx)
* -------------------
* ACT_LAYER(1000):
* 1000|oo00|pppE BBBB Default Layer Bitwise operation
* oo: operation(00:AND, 01:OR, 10:XOR, 11:SET)
* ppp: 4-bit chunk part(0-7)
* EBBBB: bits and extra bit
* 1000|ooee|pppE BBBB Layer Bitwise Operation
* oo: operation(00:AND, 01:OR, 10:XOR, 11:SET)
* ppp: 4-bit chunk part(0-7)
* EBBBB: bits and extra bit
* ee: on event(01:press, 10:release, 11:both)
*
* ACT_LAYER_MODS(1001):
* 1001|LLLL| mods Layer with modifiers held
*
* ACT_LAYER_TAP(101x):
* 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP]
* 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP]
* 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP]
* 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP]
* 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP]
* 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP]
* 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP]
* 101E|LLLL|1111 xxxx Reserved (0xF5-FF)
* ELLLL: layer 0-31(E: extra bit for layer 16-31)
*
* Extensions(11xx)
* ----------------
* ACT_MACRO(1100):
* 1100|opt | id(8) Macro play?
* 1100|1111| id(8) Macro record?
*
* 1101|xxxx xxxx xxxx (reserved)
* 1110|xxxx xxxx xxxx (reserved)
*
* ACT_FUNCTION(1111):
* 1111| address(12) Function?
* 1111|opt | id(8) Function?
*/
enum action_kind_id {
/* Key Actions */
ACT_MODS = 0b0000,
ACT_LMODS = 0b0000,
ACT_RMODS = 0b0001,
ACT_MODS_TAP = 0b0010,
ACT_LMODS_TAP = 0b0010,
ACT_RMODS_TAP = 0b0011,
/* Other Keys */
ACT_USAGE = 0b0100,
ACT_MOUSEKEY = 0b0101,
/* One-hand Support */
ACT_SWAP_HANDS = 0b0110,
/* Layer Actions */
ACT_LAYER = 0b1000,
ACT_LAYER_MODS = 0b1001,
ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */
ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */
/* Extensions */
ACT_MACRO = 0b1100,
ACT_FUNCTION = 0b1111
};
/** \brief Action Code Struct
*
* NOTE:
* In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15).
* AVR looks like a little endian in avr-gcc.
* Not portable across compiler/endianness?
*
* Byte order and bit order of 0x1234:
* Big endian: Little endian:
* -------------------- --------------------
* FEDC BA98 7654 3210 0123 4567 89AB CDEF
* 0001 0010 0011 0100 0010 1100 0100 1000
* 0x12 0x34 0x34 0x12
*/
typedef union {
uint16_t code;
struct action_kind {
uint16_t param : 12;
uint8_t id : 4;
} kind;
struct action_key {
uint8_t code : 8;
uint8_t mods : 4;
uint8_t kind : 4;
} key;
struct action_layer_bitop {
uint8_t bits : 4;
uint8_t xbit : 1;
uint8_t part : 3;
uint8_t on : 2;
uint8_t op : 2;
uint8_t kind : 4;
} layer_bitop;
struct action_layer_mods {
uint8_t mods : 8;
uint8_t layer : 4;
uint8_t kind : 4;
} layer_mods;
struct action_layer_tap {
uint8_t code : 8;
uint8_t val : 5;
uint8_t kind : 3;
} layer_tap;
struct action_usage {
uint16_t code : 10;
uint8_t page : 2;
uint8_t kind : 4;
} usage;
struct action_function {
uint8_t id : 8;
uint8_t opt : 4;
uint8_t kind : 4;
} func;
struct action_swap {
uint8_t code : 8;
uint8_t opt : 4;
uint8_t kind : 4;
} swap;
} action_t;
/* action utility */
#define ACTION_NO 0
#define ACTION_TRANSPARENT 1
#define ACTION(kind, param) ((kind) << 12 | (param))
/** \brief Key Actions
*
* Mod bits: 43210
* bit 0 ||||+- Control
* bit 1 |||+-- Shift
* bit 2 ||+--- Alt
* bit 3 |+---- Gui
* bit 4 +----- LR flag(Left:0, Right:1)
*/
enum mods_bit {
MOD_LCTL = 0x01,
MOD_LSFT = 0x02,
MOD_LALT = 0x04,
MOD_LGUI = 0x08,
MOD_RCTL = 0x11,
MOD_RSFT = 0x12,
MOD_RALT = 0x14,
MOD_RGUI = 0x18,
};
enum mods_codes {
MODS_ONESHOT = 0x00,
MODS_TAP_TOGGLE = 0x01,
};
#define ACTION_KEY(key) ACTION(ACT_MODS, (key))
#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0)
#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key))
#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key))
#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT)
#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE)
/** \brief Other Keys
*/
enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER };
#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id))
#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id))
#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)
/** \brief Layer Actions
*/
enum layer_param_on {
ON_PRESS = 1,
ON_RELEASE = 2,
ON_BOTH = 3,
};
/** \brief Layer Actions
*/
enum layer_param_bit_op {
OP_BIT_AND = 0,
OP_BIT_OR = 1,
OP_BIT_XOR = 2,
OP_BIT_SET = 3,
};
/** \brief Layer Actions
*/
enum layer_param_tap_op {
OP_TAP_TOGGLE = 0xF0,
OP_ON_OFF,
OP_OFF_ON,
OP_SET_CLEAR,
OP_ONESHOT,
};
#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f))
#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key))
/* Default Layer */
#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4))
/* Layer Operation */
#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on))
#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer)
#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE)
#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on))
#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on))
#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on))
#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on))
#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)
#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)
#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)
#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)
#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods))
/* With Tapping */
#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE)
/* Bitwise Operation */
#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on))
#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on))
#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on))
#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on))
/* Default Layer Bitwise Operation */
#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0)
#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0)
#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0)
#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0)
/* Macro */
#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id))
#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id))
#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id))
/* Function */
enum function_opts {
FUNC_TAP = 0x8, /* indciates function is tappable */
};
#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id))
#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id))
#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id))
/* OneHand Support */
enum swap_hands_param_tap_op {
OP_SH_TOGGLE = 0xF0,
OP_SH_TAP_TOGGLE,
OP_SH_ON_OFF,
OP_SH_OFF_ON,
OP_SH_OFF,
OP_SH_ON,
OP_SH_ONESHOT,
};
#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF()
#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE)
#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE)
#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT)
#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key)
#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF)
#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON)
#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON)
#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF)

View file

@ -1,279 +0,0 @@
#include <stdint.h>
#include "keyboard.h"
#include "action.h"
#include "util.h"
#include "action_layer.h"
#ifdef DEBUG_ACTION
# include "debug.h"
#else
# include "nodebug.h"
#endif
/** \brief Default Layer State
*/
layer_state_t default_layer_state = 0;
/** \brief Default Layer State Set At user Level
*
* Run user code on default layer state change
*/
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; }
/** \brief Default Layer State Set At Keyboard Level
*
* Run keyboard code on default layer state change
*/
__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); }
/** \brief Default Layer State Set
*
* Static function to set the default layer state, prints debug info and clears keys
*/
static void default_layer_state_set(layer_state_t state) {
state = default_layer_state_set_kb(state);
debug("default_layer_state: ");
default_layer_debug();
debug(" to ");
default_layer_state = state;
default_layer_debug();
debug("\n");
#ifdef STRICT_LAYER_RELEASE
clear_keyboard_but_mods(); // To avoid stuck keys
#else
clear_keyboard_but_mods_and_keys(); // Don't reset held keys
#endif
}
/** \brief Default Layer Print
*
* Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit.
*/
void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); }
/** \brief Default Layer Set
*
* Sets the default layer state.
*/
void default_layer_set(layer_state_t state) { default_layer_state_set(state); }
#ifndef NO_ACTION_LAYER
/** \brief Default Layer Or
*
* Turns on the default layer based on matching bits between specifed layer and existing layer state
*/
void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); }
/** \brief Default Layer And
*
* Turns on default layer based on matching enabled bits between specifed layer and existing layer state
*/
void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); }
/** \brief Default Layer Xor
*
* Turns on default layer based on non-matching bits between specifed layer and existing layer state
*/
void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); }
#endif
#ifndef NO_ACTION_LAYER
/** \brief Keymap Layer State
*/
layer_state_t layer_state = 0;
/** \brief Layer state set user
*
* Runs user code on layer state change
*/
__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; }
/** \brief Layer state set keyboard
*
* Runs keyboard code on layer state change
*/
__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); }
/** \brief Layer state set
*
* Sets the layer to match the specifed state (a bitmask)
*/
void layer_state_set(layer_state_t state) {
state = layer_state_set_kb(state);
dprint("layer_state: ");
layer_debug();
dprint(" to ");
layer_state = state;
layer_debug();
dprintln();
# ifdef STRICT_LAYER_RELEASE
clear_keyboard_but_mods(); // To avoid stuck keys
# else
clear_keyboard_but_mods_and_keys(); // Don't reset held keys
# endif
}
/** \brief Layer clear
*
* Turn off all layers
*/
void layer_clear(void) { layer_state_set(0); }
/** \brief Layer state is
*
* Return whether the given state is on (it might still be shadowed by a higher state, though)
*/
bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); }
/** \brief Layer state compare
*
* Used for comparing layers {mostly used for unit testing}
*/
bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) {
if (!cmp_layer_state) {
return layer == 0;
}
return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0;
}
/** \brief Layer move
*
* Turns on the given layer and turn off all other layers
*/
void layer_move(uint8_t layer) { layer_state_set((layer_state_t)1 << layer); }
/** \brief Layer on
*
* Turns on given layer
*/
void layer_on(uint8_t layer) { layer_state_set(layer_state | ((layer_state_t)1 << layer)); }
/** \brief Layer off
*
* Turns off given layer
*/
void layer_off(uint8_t layer) { layer_state_set(layer_state & ~((layer_state_t)1 << layer)); }
/** \brief Layer invert
*
* Toggle the given layer (set it if it's unset, or unset it if it's set)
*/
void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ ((layer_state_t)1 << layer)); }
/** \brief Layer or
*
* Turns on layers based on matching bits between specifed layer and existing layer state
*/
void layer_or(layer_state_t state) { layer_state_set(layer_state | state); }
/** \brief Layer and
*
* Turns on layers based on matching enabled bits between specifed layer and existing layer state
*/
void layer_and(layer_state_t state) { layer_state_set(layer_state & state); }
/** \brief Layer xor
*
* Turns on layers based on non-matching bits between specifed layer and existing layer state
*/
void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); }
/** \brief Layer debug printing
*
* Print out the hex value of the 32-bit layer state, as well as the value of the highest bit.
*/
void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); }
#endif
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
/** \brief source layer cache
*/
uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};
/** \brief update source layers cache
*
* Updates the cached keys when changing layers
*/
void update_source_layers_cache(keypos_t key, uint8_t layer) {
const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
const uint8_t storage_row = key_number / 8;
const uint8_t storage_bit = key_number % 8;
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit);
}
}
/** \brief read source layers cache
*
* reads the cached keys stored when the layer was changed
*/
uint8_t read_source_layers_cache(keypos_t key) {
const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
const uint8_t storage_row = key_number / 8;
const uint8_t storage_bit = key_number % 8;
uint8_t layer = 0;
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number;
}
return layer;
}
#endif
/** \brief Store or get action (FIXME: Needs better summary)
*
* Make sure the action triggered when the key is released is the same
* one as the one triggered on press. It's important for the mod keys
* when the layer is switched after the down event but before the up
* event as they may get stuck otherwise.
*/
action_t store_or_get_action(bool pressed, keypos_t key) {
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
if (disable_action_cache) {
return layer_switch_get_action(key);
}
uint8_t layer;
if (pressed) {
layer = layer_switch_get_layer(key);
update_source_layers_cache(key, layer);
} else {
layer = read_source_layers_cache(key);
}
return action_for_key(layer, key);
#else
return layer_switch_get_action(key);
#endif
}
/** \brief Layer switch get layer
*
* Gets the layer based on key info
*/
uint8_t layer_switch_get_layer(keypos_t key) {
#ifndef NO_ACTION_LAYER
action_t action;
action.code = ACTION_TRANSPARENT;
layer_state_t layers = layer_state | default_layer_state;
/* check top layer first */
for (int8_t i = MAX_LAYER - 1; i >= 0; i--) {
if (layers & ((layer_state_t)1 << i)) {
action = action_for_key(i, key);
if (action.code != ACTION_TRANSPARENT) {
return i;
}
}
}
/* fall back to layer 0 */
return 0;
#else
return get_highest_layer(default_layer_state);
#endif
}
/** \brief Layer switch get layer
*
* Gets action code based on key position
*/
action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); }

View file

@ -1,147 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "keyboard.h"
#include "action.h"
#ifdef DYNAMIC_KEYMAP_ENABLE
# ifndef DYNAMIC_KEYMAP_LAYER_COUNT
# define DYNAMIC_KEYMAP_LAYER_COUNT 4
# endif
# if DYNAMIC_KEYMAP_LAYER_COUNT <= 8
# ifndef LAYER_STATE_8BIT
# define LAYER_STATE_8BIT
# endif
# elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16
# ifndef LAYER_STATE_16BIT
# define LAYER_STATE_16BIT
# endif
# else
# ifndef LAYER_STATE_32BIT
# define LAYER_STATE_32BIT
# endif
# endif
#endif
#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT)
# define LAYER_STATE_32BIT
#endif
#if defined(LAYER_STATE_8BIT)
typedef uint8_t layer_state_t;
# define MAX_LAYER_BITS 3
# ifndef MAX_LAYER
# define MAX_LAYER 8
# endif
# define get_highest_layer(state) biton(state)
#elif defined(LAYER_STATE_16BIT)
typedef uint16_t layer_state_t;
# define MAX_LAYER_BITS 4
# ifndef MAX_LAYER
# define MAX_LAYER 16
# endif
# define get_highest_layer(state) biton16(state)
#elif defined(LAYER_STATE_32BIT)
typedef uint32_t layer_state_t;
# define MAX_LAYER_BITS 5
# ifndef MAX_LAYER
# define MAX_LAYER 32
# endif
# define get_highest_layer(state) biton32(state)
#else
# error Layer Mask size not specified. HOW?!
#endif
/*
* Default Layer
*/
extern layer_state_t default_layer_state;
void default_layer_debug(void);
void default_layer_set(layer_state_t state);
__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);
#ifndef NO_ACTION_LAYER
/* bitwise operation */
void default_layer_or(layer_state_t state);
void default_layer_and(layer_state_t state);
void default_layer_xor(layer_state_t state);
#else
# define default_layer_or(state)
# define default_layer_and(state)
# define default_layer_xor(state)
#endif
/*
* Keymap Layer
*/
#ifndef NO_ACTION_LAYER
extern layer_state_t layer_state;
void layer_state_set(layer_state_t state);
bool layer_state_is(uint8_t layer);
bool layer_state_cmp(layer_state_t layer1, uint8_t layer2);
void layer_debug(void);
void layer_clear(void);
void layer_move(uint8_t layer);
void layer_on(uint8_t layer);
void layer_off(uint8_t layer);
void layer_invert(uint8_t layer);
/* bitwise operation */
void layer_or(layer_state_t state);
void layer_and(layer_state_t state);
void layer_xor(layer_state_t state);
layer_state_t layer_state_set_user(layer_state_t state);
layer_state_t layer_state_set_kb(layer_state_t state);
#else
# define layer_state 0
# define layer_state_set(layer)
# define layer_state_is(layer) (layer == 0)
# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0)
# define layer_debug()
# define layer_clear()
# define layer_move(layer) (void)layer
# define layer_on(layer) (void)layer
# define layer_off(layer) (void)layer
# define layer_invert(layer) (void)layer
# define layer_or(state) (void)state
# define layer_and(state) (void)state
# define layer_xor(state) (void)state
# define layer_state_set_kb(state) (void)state
# define layer_state_set_user(state) (void)state
#endif
/* pressed actions cache */
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
void update_source_layers_cache(keypos_t key, uint8_t layer);
uint8_t read_source_layers_cache(keypos_t key);
#endif
action_t store_or_get_action(bool pressed, keypos_t key);
/* return the topmost non-transparent layer currently associated with key */
uint8_t layer_switch_get_layer(keypos_t key);
/* return action depending on current layer status */
action_t layer_switch_get_action(keypos_t key);

View file

@ -1,93 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "action.h"
#include "action_util.h"
#include "action_macro.h"
#include "wait.h"
#ifdef DEBUG_ACTION
# include "debug.h"
#else
# include "nodebug.h"
#endif
#ifndef NO_ACTION_MACRO
# define MACRO_READ() (macro = MACRO_GET(macro_p++))
/** \brief Action Macro Play
*
* FIXME: Needs doc
*/
void action_macro_play(const macro_t *macro_p) {
macro_t macro = END;
uint8_t interval = 0;
if (!macro_p) return;
while (true) {
switch (MACRO_READ()) {
case KEY_DOWN:
MACRO_READ();
dprintf("KEY_DOWN(%02X)\n", macro);
if (IS_MOD(macro)) {
add_macro_mods(MOD_BIT(macro));
send_keyboard_report();
} else {
register_code(macro);
}
break;
case KEY_UP:
MACRO_READ();
dprintf("KEY_UP(%02X)\n", macro);
if (IS_MOD(macro)) {
del_macro_mods(MOD_BIT(macro));
send_keyboard_report();
} else {
unregister_code(macro);
}
break;
case WAIT:
MACRO_READ();
dprintf("WAIT(%u)\n", macro);
{
uint8_t ms = macro;
while (ms--) wait_ms(1);
}
break;
case INTERVAL:
interval = MACRO_READ();
dprintf("INTERVAL(%u)\n", interval);
break;
case 0x04 ... 0x73:
dprintf("DOWN(%02X)\n", macro);
register_code(macro);
break;
case 0x84 ... 0xF3:
dprintf("UP(%02X)\n", macro);
unregister_code(macro & 0x7F);
break;
case END:
default:
return;
}
// interval
{
uint8_t ms = interval;
while (ms--) wait_ms(1);
}
}
}
#endif

View file

@ -1,123 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "progmem.h"
typedef uint8_t macro_t;
#define MACRO_NONE (macro_t *)0
#define MACRO(...) \
({ \
static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \
&__m[0]; \
})
#define MACRO_GET(p) pgm_read_byte(p)
// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped
#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release)))
// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped
#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro)
// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #)
#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod)
// Momentary switch layer when held, sends macro if tapped
#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \
(((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \
layer_on((layer)); \
MACRO_NONE; \
}) \
: MACRO_NONE) \
: (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \
layer_off((layer)); \
MACRO_NONE; \
})))
// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #)
#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer)
#ifndef NO_ACTION_MACRO
void action_macro_play(const macro_t *macro_p);
#else
# define action_macro_play(macro)
#endif
/* Macro commands
* code(0x04-73) // key down(1byte)
* code(0x04-73) | 0x80 // key up(1byte)
* { KEY_DOWN, code(0x04-0xff) } // key down(2bytes)
* { KEY_UP, code(0x04-0xff) } // key up(2bytes)
* WAIT // wait milli-seconds
* INTERVAL // set interval between macro commands
* END // stop macro execution
*
* Ideas(Not implemented):
* modifiers
* system usage
* consumer usage
* unicode usage
* function call
* conditionals
* loop
*/
enum macro_command_id {
/* 0x00 - 0x03 */
END = 0x00,
KEY_DOWN,
KEY_UP,
/* 0x04 - 0x73 (reserved for keycode down) */
/* 0x74 - 0x83 */
WAIT = 0x74,
INTERVAL,
/* 0x84 - 0xf3 (reserved for keycode up) */
/* 0xf4 - 0xff */
};
/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed
* if keycode between 0x04 and 0x73
* keycode / (keycode|0x80)
* else
* {KEY_DOWN, keycode} / {KEY_UP, keycode}
*/
#define DOWN(key) KEY_DOWN, (key)
#define UP(key) KEY_UP, (key)
#define TYPE(key) DOWN(key), UP(key)
#define WAIT(ms) WAIT, (ms)
#define INTERVAL(ms) INTERVAL, (ms)
/* key down */
#define D(key) DOWN(KC_##key)
/* key up */
#define U(key) UP(KC_##key)
/* key type */
#define T(key) TYPE(KC_##key)
/* wait */
#define W(ms) WAIT(ms)
/* interval */
#define I(ms) INTERVAL(ms)
/* for backward comaptibility */
#define MD(key) DOWN(KC_##key)
#define MU(key) UP(KC_##key)

View file

@ -1,456 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include "action.h"
#include "action_layer.h"
#include "action_tapping.h"
#include "keycode.h"
#include "timer.h"
#ifdef DEBUG_ACTION
# include "debug.h"
#else
# include "nodebug.h"
#endif
#ifndef NO_ACTION_TAPPING
# define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
#ifndef COMBO_ENABLE
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
#else
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
#endif
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
# ifdef TAPPING_TERM_PER_KEY
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key))
# else
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
# endif
# ifdef TAPPING_FORCE_HOLD_PER_KEY
__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; }
# endif
# ifdef PERMISSIVE_HOLD_PER_KEY
__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; }
# endif
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; }
# endif
static keyrecord_t tapping_key = {};
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
static uint8_t waiting_buffer_head = 0;
static uint8_t waiting_buffer_tail = 0;
static bool process_tapping(keyrecord_t *record);
static bool waiting_buffer_enq(keyrecord_t record);
static void waiting_buffer_clear(void);
static bool waiting_buffer_typed(keyevent_t event);
static bool waiting_buffer_has_anykey_pressed(void);
static void waiting_buffer_scan_tap(void);
static void debug_tapping_key(void);
static void debug_waiting_buffer(void);
/** \brief Action Tapping Process
*
* FIXME: Needs doc
*/
void action_tapping_process(keyrecord_t record) {
if (process_tapping(&record)) {
if (!IS_NOEVENT(record.event)) {
debug("processed: ");
debug_record(record);
debug("\n");
}
} else {
if (!waiting_buffer_enq(record)) {
// clear all in case of overflow.
debug("OVERFLOW: CLEAR ALL STATES\n");
clear_keyboard();
waiting_buffer_clear();
tapping_key = (keyrecord_t){};
}
}
// process waiting_buffer
if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
debug("---- action_exec: process waiting_buffer -----\n");
}
for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
debug("processed: waiting_buffer[");
debug_dec(waiting_buffer_tail);
debug("] = ");
debug_record(waiting_buffer[waiting_buffer_tail]);
debug("\n\n");
} else {
break;
}
}
if (!IS_NOEVENT(record.event)) {
debug("\n");
}
}
/** \brief Tapping
*
* Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
* (without interfering by typing other key)
*/
/* return true when key event is processed or consumed. */
bool process_tapping(keyrecord_t *keyp) {
keyevent_t event = keyp->event;
// if tapping
if (IS_TAPPING_PRESSED()) {
if (WITHIN_TAPPING_TERM(event)) {
if (tapping_key.tap.count == 0) {
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
// first tap!
debug("Tapping: First tap(0->1).\n");
tapping_key.tap.count = 1;
debug_tapping_key();
process_record(&tapping_key);
// copy tapping state
keyp->tap = tapping_key.tap;
// enqueue
return false;
}
/* Process a key typed within TAPPING_TERM
* This can register the key before settlement of tapping,
* useful for long TAPPING_TERM but may prevent fast typing.
*/
# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
else if (((
# ifdef TAPPING_TERM_PER_KEY
get_tapping_term(get_record_keycode(&tapping_key, false), keyp)
# else
TAPPING_TERM
# endif
>= 500)
# ifdef PERMISSIVE_HOLD_PER_KEY
|| get_permissive_hold(get_record_keycode(&tapping_key, false), keyp)
# elif defined(PERMISSIVE_HOLD)
|| true
# endif
) &&
IS_RELEASED(event) && waiting_buffer_typed(event)) {
debug("Tapping: End. No tap. Interfered by typing key\n");
process_record(&tapping_key);
tapping_key = (keyrecord_t){};
debug_tapping_key();
// enqueue
return false;
}
# endif
/* Process release event of a key pressed before tapping starts
* Without this unexpected repeating will occur with having fast repeating setting
* https://github.com/tmk/tmk_keyboard/issues/60
*/
else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
// Modifier should be retained till end of this tapping.
action_t action = layer_switch_get_action(event.key);
switch (action.kind.id) {
case ACT_LMODS:
case ACT_RMODS:
if (action.key.mods && !action.key.code) return false;
if (IS_MOD(action.key.code)) return false;
break;
case ACT_LMODS_TAP:
case ACT_RMODS_TAP:
if (action.key.mods && keyp->tap.count == 0) return false;
if (IS_MOD(action.key.code)) return false;
break;
}
// Release of key should be process immediately.
debug("Tapping: release event of a key pressed before tapping\n");
process_record(keyp);
return true;
} else {
// set interrupted flag when other key preesed during tapping
if (event.pressed) {
tapping_key.tap.interrupted = true;
# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp))
# endif
{
debug("Tapping: End. No tap. Interfered by pressed key\n");
process_record(&tapping_key);
tapping_key = (keyrecord_t){};
debug_tapping_key();
// enqueue
return false;
}
# endif
}
// enqueue
return false;
}
}
// tap_count > 0
else {
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
debug("Tapping: Tap release(");
debug_dec(tapping_key.tap.count);
debug(")\n");
keyp->tap = tapping_key.tap;
process_record(keyp);
tapping_key = *keyp;
debug_tapping_key();
return true;
} else if (is_tap_record(keyp) && event.pressed) {
if (tapping_key.tap.count > 1) {
debug("Tapping: Start new tap with releasing last tap(>1).\n");
// unregister key
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
#ifdef COMBO_ENABLE
.keycode = tapping_key.keycode,
#endif
});
} else {
debug("Tapping: Start while last tap(1).\n");
}
tapping_key = *keyp;
waiting_buffer_scan_tap();
debug_tapping_key();
return true;
} else {
if (!IS_NOEVENT(event)) {
debug("Tapping: key event while last tap(>0).\n");
}
process_record(keyp);
return true;
}
}
}
// after TAPPING_TERM
else {
if (tapping_key.tap.count == 0) {
debug("Tapping: End. Timeout. Not tap(0): ");
debug_event(event);
debug("\n");
process_record(&tapping_key);
tapping_key = (keyrecord_t){};
debug_tapping_key();
return false;
} else {
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
debug("Tapping: End. last timeout tap release(>0).");
keyp->tap = tapping_key.tap;
process_record(keyp);
tapping_key = (keyrecord_t){};
return true;
} else if (is_tap_record(keyp) && event.pressed) {
if (tapping_key.tap.count > 1) {
debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
// unregister key
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
#ifdef COMBO_ENABLE
.keycode = tapping_key.keycode,
#endif
});
} else {
debug("Tapping: Start while last timeout tap(1).\n");
}
tapping_key = *keyp;
waiting_buffer_scan_tap();
debug_tapping_key();
return true;
} else {
if (!IS_NOEVENT(event)) {
debug("Tapping: key event while last timeout tap(>0).\n");
}
process_record(keyp);
return true;
}
}
}
} else if (IS_TAPPING_RELEASED()) {
if (WITHIN_TAPPING_TERM(event)) {
if (event.pressed) {
if (IS_TAPPING_RECORD(keyp)) {
//# ifndef TAPPING_FORCE_HOLD
# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
if (
# ifdef TAPPING_FORCE_HOLD_PER_KEY
!get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) &&
# endif
!tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
// sequential tap.
keyp->tap = tapping_key.tap;
if (keyp->tap.count < 15) keyp->tap.count += 1;
debug("Tapping: Tap press(");
debug_dec(keyp->tap.count);
debug(")\n");
process_record(keyp);
tapping_key = *keyp;
debug_tapping_key();
return true;
}
# endif
// FIX: start new tap again
tapping_key = *keyp;
return true;
} else if (is_tap_record(keyp)) {
// Sequential tap can be interfered with other tap key.
debug("Tapping: Start with interfering other tap.\n");
tapping_key = *keyp;
waiting_buffer_scan_tap();
debug_tapping_key();
return true;
} else {
// should none in buffer
// FIX: interrupted when other key is pressed
tapping_key.tap.interrupted = true;
process_record(keyp);
return true;
}
} else {
if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
process_record(keyp);
return true;
}
} else {
// FIX: process_action here?
// timeout. no sequential tap.
debug("Tapping: End(Timeout after releasing last tap): ");
debug_event(event);
debug("\n");
tapping_key = (keyrecord_t){};
debug_tapping_key();
return false;
}
}
// not tapping state
else {
if (event.pressed && is_tap_record(keyp)) {
debug("Tapping: Start(Press tap key).\n");
tapping_key = *keyp;
process_record_tap_hint(&tapping_key);
waiting_buffer_scan_tap();
debug_tapping_key();
return true;
} else {
process_record(keyp);
return true;
}
}
}
/** \brief Waiting buffer enq
*
* FIXME: Needs docs
*/
bool waiting_buffer_enq(keyrecord_t record) {
if (IS_NOEVENT(record.event)) {
return true;
}
if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
debug("waiting_buffer_enq: Over flow.\n");
return false;
}
waiting_buffer[waiting_buffer_head] = record;
waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
debug("waiting_buffer_enq: ");
debug_waiting_buffer();
return true;
}
/** \brief Waiting buffer clear
*
* FIXME: Needs docs
*/
void waiting_buffer_clear(void) {
waiting_buffer_head = 0;
waiting_buffer_tail = 0;
}
/** \brief Waiting buffer typed
*
* FIXME: Needs docs
*/
bool waiting_buffer_typed(keyevent_t event) {
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
return true;
}
}
return false;
}
/** \brief Waiting buffer has anykey pressed
*
* FIXME: Needs docs
*/
__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
if (waiting_buffer[i].event.pressed) return true;
}
return false;
}
/** \brief Scan buffer for tapping
*
* FIXME: Needs docs
*/
void waiting_buffer_scan_tap(void) {
// tapping already is settled
if (tapping_key.tap.count > 0) return;
// invalid state: tapping_key released && tap.count == 0
if (!tapping_key.event.pressed) return;
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
tapping_key.tap.count = 1;
waiting_buffer[i].tap.count = 1;
process_record(&tapping_key);
debug("waiting_buffer_scan_tap: found at [");
debug_dec(i);
debug("]\n");
debug_waiting_buffer();
return;
}
}
}
/** \brief Tapping key debug print
*
* FIXME: Needs docs
*/
static void debug_tapping_key(void) {
debug("TAPPING_KEY=");
debug_record(tapping_key);
debug("\n");
}
/** \brief Waiting buffer debug print
*
* FIXME: Needs docs
*/
static void debug_waiting_buffer(void) {
debug("{ ");
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
debug("[");
debug_dec(i);
debug("]=");
debug_record(waiting_buffer[i]);
debug(" ");
}
debug("}\n");
}
#endif

View file

@ -1,42 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
/* period of tapping(ms) */
#ifndef TAPPING_TERM
# define TAPPING_TERM 200
#endif
/* tap count needed for toggling a feature */
#ifndef TAPPING_TOGGLE
# define TAPPING_TOGGLE 5
#endif
#define WAITING_BUFFER_SIZE 8
#ifndef NO_ACTION_TAPPING
uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
void action_tapping_process(keyrecord_t record);
uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
#endif

View file

@ -1,455 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "host.h"
#include "report.h"
#include "debug.h"
#include "action_util.h"
#include "action_layer.h"
#include "timer.h"
#include "keycode_config.h"
extern keymap_config_t keymap_config;
static uint8_t real_mods = 0;
static uint8_t weak_mods = 0;
static uint8_t macro_mods = 0;
#ifdef KEY_OVERRIDE_ENABLE
static uint8_t weak_override_mods = 0;
static uint8_t suppressed_mods = 0;
#endif
#ifdef USB_6KRO_ENABLE
# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
# define RO_INC(a) RO_ADD(a, 1)
# define RO_DEC(a) RO_SUB(a, 1)
static int8_t cb_head = 0;
static int8_t cb_tail = 0;
static int8_t cb_count = 0;
#endif
// TODO: pointer variable is not needed
// report_keyboard_t keyboard_report = {};
report_keyboard_t *keyboard_report = &(report_keyboard_t){};
extern inline void add_key(uint8_t key);
extern inline void del_key(uint8_t key);
extern inline void clear_keys(void);
#ifndef NO_ACTION_ONESHOT
static uint8_t oneshot_mods = 0;
static uint8_t oneshot_locked_mods = 0;
uint8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
void set_oneshot_locked_mods(uint8_t mods) {
if (mods != oneshot_locked_mods) {
oneshot_locked_mods = mods;
oneshot_locked_mods_changed_kb(oneshot_locked_mods);
}
}
void clear_oneshot_locked_mods(void) {
if (oneshot_locked_mods) {
oneshot_locked_mods = 0;
oneshot_locked_mods_changed_kb(oneshot_locked_mods);
}
}
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
static uint16_t oneshot_time = 0;
bool has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; }
# else
bool has_oneshot_mods_timed_out(void) { return false; }
# endif
#endif
/* oneshot layer */
#ifndef NO_ACTION_ONESHOT
/** \brief oneshot_layer_data bits
* LLLL LSSS
* where:
* L => are layer bits
* S => oneshot state bits
*/
static int8_t oneshot_layer_data = 0;
inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }
# ifdef SWAP_HANDS_ENABLE
enum {
SHO_OFF,
SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet
SHO_PRESSED, // Swap hands button is currently pressed
SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys
} swap_hands_oneshot = SHO_OFF;
# endif
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
static uint16_t oneshot_layer_time = 0;
inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
# ifdef SWAP_HANDS_ENABLE
static uint16_t oneshot_swaphands_time = 0;
inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); }
# endif
# endif
# ifdef SWAP_HANDS_ENABLE
void set_oneshot_swaphands(void) {
swap_hands_oneshot = SHO_PRESSED;
swap_hands = true;
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_swaphands_time = timer_read();
if (oneshot_layer_time != 0) {
oneshot_layer_time = oneshot_swaphands_time;
}
# endif
}
void release_oneshot_swaphands(void) {
if (swap_hands_oneshot == SHO_PRESSED) {
swap_hands_oneshot = SHO_ACTIVE;
}
if (swap_hands_oneshot == SHO_USED) {
clear_oneshot_swaphands();
}
}
void use_oneshot_swaphands(void) {
if (swap_hands_oneshot == SHO_PRESSED) {
swap_hands_oneshot = SHO_USED;
}
if (swap_hands_oneshot == SHO_ACTIVE) {
clear_oneshot_swaphands();
}
}
void clear_oneshot_swaphands(void) {
swap_hands_oneshot = SHO_OFF;
swap_hands = false;
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_swaphands_time = 0;
# endif
}
# endif
/** \brief Set oneshot layer
*
* FIXME: needs doc
*/
void set_oneshot_layer(uint8_t layer, uint8_t state) {
if (!keymap_config.oneshot_disable) {
oneshot_layer_data = layer << 3 | state;
layer_on(layer);
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_layer_time = timer_read();
# endif
oneshot_layer_changed_kb(get_oneshot_layer());
} else {
layer_on(layer);
}
}
/** \brief Reset oneshot layer
*
* FIXME: needs doc
*/
void reset_oneshot_layer(void) {
oneshot_layer_data = 0;
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_layer_time = 0;
# endif
oneshot_layer_changed_kb(get_oneshot_layer());
}
/** \brief Clear oneshot layer
*
* FIXME: needs doc
*/
void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
uint8_t start_state = oneshot_layer_data;
oneshot_layer_data &= ~state;
if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) {
layer_off(get_oneshot_layer());
reset_oneshot_layer();
}
}
/** \brief Is oneshot layer active
*
* FIXME: needs doc
*/
bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); }
/** \brief set oneshot
*
* FIXME: needs doc
*/
void oneshot_set(bool active) {
if (keymap_config.oneshot_disable != active) {
keymap_config.oneshot_disable = active;
eeconfig_update_keymap(keymap_config.raw);
dprintf("Oneshot: active: %d\n", active);
}
}
/** \brief toggle oneshot
*
* FIXME: needs doc
*/
void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); }
/** \brief enable oneshot
*
* FIXME: needs doc
*/
void oneshot_enable(void) { oneshot_set(true); }
/** \brief disable oneshot
*
* FIXME: needs doc
*/
void oneshot_disable(void) { oneshot_set(false); }
bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; }
#endif
/** \brief Send keyboard report
*
* FIXME: needs doc
*/
void send_keyboard_report(void) {
keyboard_report->mods = real_mods;
keyboard_report->mods |= weak_mods;
keyboard_report->mods |= macro_mods;
#ifndef NO_ACTION_ONESHOT
if (oneshot_mods) {
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
if (has_oneshot_mods_timed_out()) {
dprintf("Oneshot: timeout\n");
clear_oneshot_mods();
}
# endif
keyboard_report->mods |= oneshot_mods;
if (has_anykey(keyboard_report)) {
clear_oneshot_mods();
}
}
#endif
#ifdef KEY_OVERRIDE_ENABLE
// These need to be last to be able to properly control key overrides
keyboard_report->mods &= ~suppressed_mods;
keyboard_report->mods |= weak_override_mods;
#endif
host_keyboard_send(keyboard_report);
}
/** \brief Get mods
*
* FIXME: needs doc
*/
uint8_t get_mods(void) { return real_mods; }
/** \brief add mods
*
* FIXME: needs doc
*/
void add_mods(uint8_t mods) { real_mods |= mods; }
/** \brief del mods
*
* FIXME: needs doc
*/
void del_mods(uint8_t mods) { real_mods &= ~mods; }
/** \brief set mods
*
* FIXME: needs doc
*/
void set_mods(uint8_t mods) { real_mods = mods; }
/** \brief clear mods
*
* FIXME: needs doc
*/
void clear_mods(void) { real_mods = 0; }
/** \brief get weak mods
*
* FIXME: needs doc
*/
uint8_t get_weak_mods(void) { return weak_mods; }
/** \brief add weak mods
*
* FIXME: needs doc
*/
void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
/** \brief del weak mods
*
* FIXME: needs doc
*/
void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
/** \brief set weak mods
*
* FIXME: needs doc
*/
void set_weak_mods(uint8_t mods) { weak_mods = mods; }
/** \brief clear weak mods
*
* FIXME: needs doc
*/
void clear_weak_mods(void) { weak_mods = 0; }
#ifdef KEY_OVERRIDE_ENABLE
/** \brief set weak mods used by key overrides. DO not call this manually
*/
void set_weak_override_mods(uint8_t mods) { weak_override_mods = mods; }
/** \brief clear weak mods used by key overrides. DO not call this manually
*/
void clear_weak_override_mods(void) { weak_override_mods = 0; }
/** \brief set suppressed mods used by key overrides. DO not call this manually
*/
void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; }
/** \brief clear suppressed mods used by key overrides. DO not call this manually
*/
void clear_suppressed_override_mods(void) { suppressed_mods = 0; }
#endif
/* macro modifier */
/** \brief get macro mods
*
* FIXME: needs doc
*/
uint8_t get_macro_mods(void) { return macro_mods; }
/** \brief add macro mods
*
* FIXME: needs doc
*/
void add_macro_mods(uint8_t mods) { macro_mods |= mods; }
/** \brief del macro mods
*
* FIXME: needs doc
*/
void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; }
/** \brief set macro mods
*
* FIXME: needs doc
*/
void set_macro_mods(uint8_t mods) { macro_mods = mods; }
/** \brief clear macro mods
*
* FIXME: needs doc
*/
void clear_macro_mods(void) { macro_mods = 0; }
#ifndef NO_ACTION_ONESHOT
/** \brief get oneshot mods
*
* FIXME: needs doc
*/
uint8_t get_oneshot_mods(void) { return oneshot_mods; }
void add_oneshot_mods(uint8_t mods) {
if ((oneshot_mods & mods) != mods) {
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_time = timer_read();
# endif
oneshot_mods |= mods;
oneshot_mods_changed_kb(mods);
}
}
void del_oneshot_mods(uint8_t mods) {
if (oneshot_mods & mods) {
oneshot_mods &= ~mods;
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_time = oneshot_mods ? timer_read() : 0;
# endif
oneshot_mods_changed_kb(oneshot_mods);
}
}
/** \brief set oneshot mods
*
* FIXME: needs doc
*/
void set_oneshot_mods(uint8_t mods) {
if (!keymap_config.oneshot_disable) {
if (oneshot_mods != mods) {
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_time = timer_read();
# endif
oneshot_mods = mods;
oneshot_mods_changed_kb(mods);
}
}
}
/** \brief clear oneshot mods
*
* FIXME: needs doc
*/
void clear_oneshot_mods(void) {
if (oneshot_mods) {
oneshot_mods = 0;
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
oneshot_time = 0;
# endif
oneshot_mods_changed_kb(oneshot_mods);
}
}
#endif
/** \brief Called when the one shot modifiers have been changed.
*
* \param mods Contains the active modifiers active after the change.
*/
__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {}
/** \brief Called when the locked one shot modifiers have been changed.
*
* \param mods Contains the active modifiers active after the change.
*/
__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); }
/** \brief Called when the one shot modifiers have been changed.
*
* \param mods Contains the active modifiers active after the change.
*/
__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {}
/** \brief Called when the one shot modifiers have been changed.
*
* \param mods Contains the active modifiers active after the change.
*/
__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); }
/** \brief Called when the one shot layers have been changed.
*
* \param layer Contains the layer that is toggled on, or zero when toggled off.
*/
__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {}
/** \brief Called when the one shot layers have been changed.
*
* \param layer Contains the layer that is toggled on, or zero when toggled off.
*/
__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); }
/** \brief inspect keyboard state
*
* FIXME: needs doc
*/
uint8_t has_anymod(void) { return bitpop(real_mods); }

View file

@ -1,105 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "report.h"
#ifdef __cplusplus
extern "C" {
#endif
extern report_keyboard_t *keyboard_report;
void send_keyboard_report(void);
/* key */
inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); }
inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); }
inline void clear_keys(void) { clear_keys_from_report(keyboard_report); }
/* modifier */
uint8_t get_mods(void);
void add_mods(uint8_t mods);
void del_mods(uint8_t mods);
void set_mods(uint8_t mods);
void clear_mods(void);
/* weak modifier */
uint8_t get_weak_mods(void);
void add_weak_mods(uint8_t mods);
void del_weak_mods(uint8_t mods);
void set_weak_mods(uint8_t mods);
void clear_weak_mods(void);
/* macro modifier */
uint8_t get_macro_mods(void);
void add_macro_mods(uint8_t mods);
void del_macro_mods(uint8_t mods);
void set_macro_mods(uint8_t mods);
void clear_macro_mods(void);
/* oneshot modifier */
uint8_t get_oneshot_mods(void);
void add_oneshot_mods(uint8_t mods);
void del_oneshot_mods(uint8_t mods);
void set_oneshot_mods(uint8_t mods);
void clear_oneshot_mods(void);
bool has_oneshot_mods_timed_out(void);
uint8_t get_oneshot_locked_mods(void);
void set_oneshot_locked_mods(uint8_t mods);
void clear_oneshot_locked_mods(void);
typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t;
void set_oneshot_layer(uint8_t layer, uint8_t state);
uint8_t get_oneshot_layer(void);
void clear_oneshot_layer_state(oneshot_fullfillment_t state);
void reset_oneshot_layer(void);
bool is_oneshot_layer_active(void);
uint8_t get_oneshot_layer_state(void);
bool has_oneshot_layer_timed_out(void);
bool has_oneshot_swaphands_timed_out(void);
void oneshot_locked_mods_changed_user(uint8_t mods);
void oneshot_locked_mods_changed_kb(uint8_t mods);
void oneshot_mods_changed_user(uint8_t mods);
void oneshot_mods_changed_kb(uint8_t mods);
void oneshot_layer_changed_user(uint8_t layer);
void oneshot_layer_changed_kb(uint8_t layer);
void oneshot_toggle(void);
void oneshot_enable(void);
void oneshot_disable(void);
bool is_oneshot_enabled(void);
/* inspect */
uint8_t has_anymod(void);
#ifdef SWAP_HANDS_ENABLE
void set_oneshot_swaphands(void);
void release_oneshot_swaphands(void);
void use_oneshot_swaphands(void);
void clear_oneshot_swaphands(void);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,21 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform_deps.h"
void platform_setup(void) {
// do nothing
}

View file

@ -17,8 +17,28 @@
#include <util/delay.h>
#define wait_ms(ms) _delay_ms(ms)
#define wait_us(us) _delay_us(us)
#define wait_ms(ms) \
do { \
if (__builtin_constant_p(ms)) { \
_delay_ms(ms); \
} else { \
for (uint16_t i = ms; i > 0; i--) { \
_delay_ms(1); \
} \
} \
} while (0)
#define wait_us(us) \
do { \
if (__builtin_constant_p(us)) { \
_delay_us(us); \
} else { \
for (uint16_t i = us; i > 0; i--) { \
_delay_us(1); \
} \
} \
} while (0)
#define wait_cpuclock(n) __builtin_avr_delay_cycles(n)
#define CPU_CLOCK F_CPU
/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.
* But here's more margin to make it two clocks. */
@ -26,4 +46,4 @@
# define GPIO_INPUT_PIN_DELAY 2
#endif
#define waitInputPinDelay() __builtin_avr_delay_cycles(GPIO_INPUT_PIN_DELAY)
#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)

View file

@ -0,0 +1,21 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform_deps.h"
void platform_setup(void) {
// do nothing
}

View file

@ -16,25 +16,6 @@
# include "vusb.h"
#endif
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
#endif
#ifdef AUDIO_ENABLE
# include "audio.h"
#endif /* AUDIO_ENABLE */
#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
# include "rgblight.h"
#endif
#ifdef LED_MATRIX_ENABLE
# include "led_matrix.h"
#endif
#ifdef RGB_MATRIX_ENABLE
# include "rgb_matrix.h"
#endif
/** \brief Suspend idle
*
* FIXME: needs doc
@ -50,17 +31,6 @@ void suspend_idle(uint8_t time) {
// TODO: This needs some cleanup
/** \brief Run keyboard level Power down
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_power_down_user(void) {}
/** \brief Run keyboard level Power down
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); }
#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
// clang-format off
@ -135,41 +105,9 @@ void suspend_power_down(void) {
if (!vusb_suspended) return;
#endif
suspend_power_down_kb();
suspend_power_down_quantum();
#ifndef NO_SUSPEND_POWER_DOWN
// Turn off backlight
# ifdef BACKLIGHT_ENABLE
backlight_set(0);
# endif
// Turn off LED indicators
uint8_t leds_off = 0;
# if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE)
if (is_backlight_enabled()) {
// Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
leds_off |= (1 << USB_LED_CAPS_LOCK);
}
# endif
led_set(leds_off);
// Turn off audio
# ifdef AUDIO_ENABLE
stop_all_notes();
# endif
// Turn off underglow
# if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
rgblight_suspend();
# endif
# if defined(LED_MATRIX_ENABLE)
led_matrix_set_suspend_state(true);
# endif
# if defined(RGB_MATRIX_ENABLE)
rgb_matrix_set_suspend_state(true);
# endif
// Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt)
# if defined(WDT_vect)
power_down(WDTO_15MS);
@ -189,18 +127,6 @@ bool suspend_wakeup_condition(void) {
return false;
}
/** \brief run user level code immediately after wakeup
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_wakeup_init_user(void) {}
/** \brief run keyboard level code immediately after wakeup
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); }
/** \brief run immediately after wakeup
*
* FIXME: needs doc
@ -209,27 +135,7 @@ void suspend_wakeup_init(void) {
// clear keyboard state
clear_keyboard();
// Turn on backlight
#ifdef BACKLIGHT_ENABLE
backlight_init();
#endif
// Restore LED indicators
led_set(host_keyboard_leds());
// Wake up underglow
#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
rgblight_wakeup();
#endif
#if defined(LED_MATRIX_ENABLE)
led_matrix_set_suspend_state(false);
#endif
#if defined(RGB_MATRIX_ENABLE)
rgb_matrix_set_suspend_state(false);
#endif
suspend_wakeup_init_kb();
suspend_wakeup_init_quantum();
}
#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)

View file

@ -0,0 +1,89 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OPTIMIZE__
# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
#endif
#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t"
__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
/* The argument n must be a constant expression.
* That way, compiler optimization will remove unnecessary code. */
if (n < 1) {
return;
}
if (n > 8) {
unsigned int n8 = n / 8;
n = n - n8 * 8;
switch (n8) {
case 16:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 15:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 14:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 13:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 12:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 11:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 10:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 9:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 8:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 7:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 6:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 5:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 4:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 3:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 2:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 1:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 0:
break;
}
}
switch (n) {
case 8:
asm volatile("nop" ::: "memory");
case 7:
asm volatile("nop" ::: "memory");
case 6:
asm volatile("nop" ::: "memory");
case 5:
asm volatile("nop" ::: "memory");
case 4:
asm volatile("nop" ::: "memory");
case 3:
asm volatile("nop" ::: "memory");
case 2:
asm volatile("nop" ::: "memory");
case 1:
asm volatile("nop" ::: "memory");
case 0:
break;
}
}

View file

@ -16,6 +16,7 @@
#pragma once
#include <ch.h>
#include <hal.h>
/* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */
#define wait_ms(ms) \
@ -26,14 +27,23 @@
chThdSleepMicroseconds(1); \
} \
} while (0)
#define wait_us(us) \
do { \
if (us != 0) { \
chThdSleepMicroseconds(us); \
} else { \
chThdSleepMicroseconds(1); \
} \
} while (0)
#ifdef WAIT_US_TIMER
void wait_us(uint16_t duration);
#else
# define wait_us(us) \
do { \
if (us != 0) { \
chThdSleepMicroseconds(us); \
} else { \
chThdSleepMicroseconds(1); \
} \
} while (0)
#endif
#include "_wait.c"
#define CPU_CLOCK STM32_SYSCLK
/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus
* to which the GPIO is connected.
@ -45,11 +55,8 @@
* If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.
* (A fairly large value of 0.25 microseconds is set.)
*/
#include "wait.c"
#ifndef GPIO_INPUT_PIN_DELAY
# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4)
# define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4)
#endif
#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)

View file

@ -14,185 +14,713 @@
* Artur F.
*
* Modifications for QMK and STM32F303 by Yiancar
* Modifications to add flash wear leveling by Ilya Zhuravlev
* Modifications to increase flash density by Don Kjer
*/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "util.h"
#include "debug.h"
#include "eeprom_stm32.h"
/*****************************************************************************
* Allows to use the internal flash to store non volatile data. To initialize
* the functionality use the EEPROM_Init() function. Be sure that by reprogramming
* of the controller just affected pages will be deleted. In other case the non
* volatile data will be lost.
******************************************************************************/
#include "flash_stm32.h"
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Functions -----------------------------------------------------------------*/
/*
* We emulate eeprom by writing a snapshot compacted view of eeprom contents,
* followed by a write log of any change since that snapshot:
*
* === SIMULATED EEPROM CONTENTS ===
*
* Compacted Write Log
* ............[BYTE][BYTE]
* FFFF....FFFF[WRD0][WRD1]
* FFFFFFFFFFFF[WORD][NEXT]
* ....FFFFFFFF[BYTE][WRD0]
*
* PAGE_BASE
* PAGE_LASTWRITE_BASE
* WRITE_LAST
*
* Compacted contents are the 1's complement of the actual EEPROM contents.
* e.g. An 'FFFF' represents a '0000' value.
*
* The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
* The size of the compacted-area and write log are configurable, and the combined
* size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
* Simulated Eeprom contents are located at the end of available flash space.
*
* The following configuration defines can be set:
*
* FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log)
* FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
* NOTE: The current implementation does not include page swapping,
* and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
*
* The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
* FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
* The larger the write log, the less frequently the compacted area needs to be rewritten.
*
*
* *** General Algorithm ***
*
* During initialization:
* The contents of the Compacted-flash area are loaded and the 1's complement value
* is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
* Write log entries are processed until a 0xFFFF is reached.
* Each log entry updates a byte or word in the cache.
*
* During reads:
* EEPROM contents are given back directly from the cache in memory.
*
* During writes:
* The contents of the cache is updated first.
* If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash
* Otherwise:
* If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.
* Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
*
*
* *** Write Log Structure ***
*
* Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
*
* === WRITE LOG ENTRY FORMATS ===
*
* Byte-Entry
* 0XXXXXXXYYYYYYYY
*
* Address Value
*
* 0 <= Address < 0x80 (128)
*
* Word-Encoded 0
* 100XXXXXXXXXXXXX
*
* Address >> 1
* Value: 0
*
* 0 <= Address <= 0x3FFE (16382)
*
* Word-Encoded 1
* 101XXXXXXXXXXXXX
*
* Address >> 1
* Value: 1
*
* 0 <= Address <= 0x3FFE (16382)
*
* Reserved
* 110XXXXXXXXXXXXX
*
*
* Word-Next
* 111XXXXXXXXXXXXXYYYYYYYYYYYYYYYY
*
* (Address-128)>>1 ~Value
*
* ( 0 <= Address < 0x0080 (128): Reserved)
* 0x80 <= Address <= 0x3FFE (16382)
*
* Write Log entry ranges:
* 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
* 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
* 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
* 0xC000 ... 0xDFFF - Reserved
* 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
* 0xFFC0 ... 0xFFFE - Reserved
* 0xFFFF - Unprogrammed
*
*/
#include "eeprom_stm32_defs.h"
#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
# error "not implemented."
#endif
/* These bits are used for optimizing encoding of bytes, 0 and 1 */
#define FEE_WORD_ENCODING 0x8000
#define FEE_VALUE_NEXT 0x6000
#define FEE_VALUE_RESERVED 0x4000
#define FEE_VALUE_ENCODED 0x2000
#define FEE_BYTE_RANGE 0x80
/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
#define FEE_ADDRESS_MAX_SIZE 0x4000
/* Flash word value after erase */
#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
/* Size of combined compacted eeprom and write log pages */
#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)
#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */
# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024)
# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size
# endif
#endif
/* Size of emulated eeprom */
#ifdef FEE_DENSITY_BYTES
# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
# endif
# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE)
# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate!
# endif
# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE)
# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
# endif
# if ((FEE_DENSITY_BYTES) % 2) == 1
# error emulated eeprom: FEE_DENSITY_BYTES must be even
# endif
#else
/* Default to half of allocated space used for emulated eeprom, half for write log */
# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)
#endif
/* Size of write log */
#ifdef FEE_WRITE_LOG_BYTES
# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)
# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE
# endif
# if ((FEE_WRITE_LOG_BYTES) % 2) == 1
# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even
# endif
#else
/* Default to use all remaining space */
# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
#endif
/* Start of the emulated eeprom compacted flash area */
#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS
/* End of the emulated eeprom compacted flash area */
#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)
/* Start of the emulated eeprom write log */
#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS
/* End of the emulated eeprom write log */
#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
#endif
/* In-memory contents of emulated eeprom for faster access */
/* *TODO: Implement page swapping */
static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
static uint8_t *DataBuf = (uint8_t *)WordBuf;
/* Pointer to the first available slot within the write log */
static uint16_t *empty_slot;
// #define DEBUG_EEPROM_OUTPUT
/*
* Debug print utils
*/
#if defined(DEBUG_EEPROM_OUTPUT)
# define debug_eeprom debug_enable
# define eeprom_println(s) println(s)
# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);
#else /* NO_DEBUG */
# define debug_eeprom false
# define eeprom_println(s)
# define eeprom_printf(fmt, ...)
#endif /* NO_DEBUG */
void print_eeprom(void) {
#ifndef NO_DEBUG
int empty_rows = 0;
for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
if (i % 16 == 0) {
if (i >= FEE_DENSITY_BYTES - 16) {
/* Make sure we display the last row */
empty_rows = 0;
}
/* Check if this row is uninitialized */
++empty_rows;
for (uint16_t j = 0; j < 16; j++) {
if (DataBuf[i + j]) {
empty_rows = 0;
break;
}
}
if (empty_rows > 1) {
/* Repeat empty row */
if (empty_rows == 2) {
/* Only display the first repeat empty row */
println("*");
}
i += 15;
continue;
}
xprintf("%04x", i);
}
if (i % 8 == 0) print(" ");
xprintf(" %02x", DataBuf[i]);
if ((i + 1) % 16 == 0) {
println("");
}
}
#endif
}
uint8_t DataBuf[FEE_PAGE_SIZE];
/*****************************************************************************
* Delete Flash Space used for user Data, deletes the whole space between
* RW_PAGE_BASE_ADDRESS and the last uC Flash Page
******************************************************************************/
uint16_t EEPROM_Init(void) {
// unlock flash
FLASH_Unlock();
// Clear Flags
// FLASH_ClearFlag(FLASH_SR_EOP|FLASH_SR_PGERR|FLASH_SR_WRPERR);
return FEE_DENSITY_BYTES;
}
/*****************************************************************************
* Erase the whole reserved Flash Space used for user Data
******************************************************************************/
void EEPROM_Erase(void) {
int page_num = 0;
// delete all pages from specified start page to the last page
do {
FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
page_num++;
} while (page_num < FEE_DENSITY_PAGES);
}
/*****************************************************************************
* Writes once data byte to flash on specified address. If a byte is already
* written, the whole page must be copied to a buffer, the byte changed and
* the manipulated buffer written after PageErase.
*******************************************************************************/
uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
FLASH_Status FlashStatus = FLASH_COMPLETE;
uint32_t page;
int i;
// exit if desired address is above the limit (e.G. under 2048 Bytes for 4 pages)
if (Address > FEE_DENSITY_BYTES) {
return 0;
/* Load emulated eeprom contents from compacted flash into memory */
uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
uint16_t *dest = (uint16_t *)DataBuf;
for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
*dest = ~*src;
}
// calculate which page is affected (Pagenum1/Pagenum2...PagenumN)
page = FEE_ADDR_OFFSET(Address) / FEE_PAGE_SIZE;
if (debug_eeprom) {
println("EEPROM_Init Compacted Pages:");
print_eeprom();
println("EEPROM_Init Write Log:");
}
// if current data is 0xFF, the byte is empty, just overwrite with the new one
if ((*(__IO uint16_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) == FEE_EMPTY_WORD) {
FlashStatus = FLASH_ProgramHalfWord(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address), (uint16_t)(0x00FF & DataByte));
} else {
// Copy Page to a buffer
memcpy(DataBuf, (uint8_t *)FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE), FEE_PAGE_SIZE); // !!! Calculate base address for the desired page
// check if new data is differ to current data, return if not, proceed if yes
if (DataByte == *(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) {
return 0;
/* Replay write log */
uint16_t *log_addr;
for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
uint16_t address = *log_addr;
if (address == FEE_EMPTY_WORD) {
break;
}
// manipulate desired data byte in temp data array if new byte is differ to the current
DataBuf[FEE_ADDR_OFFSET(Address) % FEE_PAGE_SIZE] = DataByte;
// Erase Page
FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE));
// Write new data (whole page) to flash if data has been changed
for (i = 0; i < (FEE_PAGE_SIZE / 2); i++) {
if ((__IO uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]) != 0xFFFF) {
FlashStatus = FLASH_ProgramHalfWord((FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)) + (i * 2), (uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]));
/* Check for lowest 128-bytes optimization */
if (!(address & FEE_WORD_ENCODING)) {
uint8_t bvalue = (uint8_t)address;
address >>= 8;
DataBuf[address] = bvalue;
eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
} else {
uint16_t wvalue;
/* Check if value is in next word */
if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
/* Read value from next word */
if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
break;
}
wvalue = ~*log_addr;
if (!wvalue) {
eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
/* Possibly incomplete write. Ignore and continue */
continue;
}
address &= 0x1FFF;
address <<= 1;
/* Writes to addresses less than 128 are byte log entries */
address += FEE_BYTE_RANGE;
} else {
/* Reserved for future use */
if (address & FEE_VALUE_RESERVED) {
eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
continue;
}
/* Optimization for 0 or 1 values. */
wvalue = (address & FEE_VALUE_ENCODED) >> 13;
address &= 0x1FFF;
address <<= 1;
}
if (address < FEE_DENSITY_BYTES) {
eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
*(uint16_t *)(&DataBuf[address]) = wvalue;
} else {
eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
}
}
}
return FlashStatus;
empty_slot = log_addr;
if (debug_eeprom) {
println("EEPROM_Init Final DataBuf:");
print_eeprom();
}
return FEE_DENSITY_BYTES;
}
/*****************************************************************************
* Read once data byte from a specified address.
*******************************************************************************/
/* Clear flash contents (doesn't touch in-memory DataBuf) */
static void eeprom_clear(void) {
FLASH_Unlock();
for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
}
FLASH_Lock();
empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
}
/* Erase emulated eeprom */
void EEPROM_Erase(void) {
eeprom_println("EEPROM_Erase");
/* Erase compacted pages and write log */
eeprom_clear();
/* re-initialize to reset DataBuf */
EEPROM_Init();
}
/* Compact write log */
static uint8_t eeprom_compact(void) {
/* Erase compacted pages and write log */
eeprom_clear();
FLASH_Unlock();
FLASH_Status final_status = FLASH_COMPLETE;
/* Write emulated eeprom contents from memory to compacted flash */
uint16_t *src = (uint16_t *)DataBuf;
uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
uint16_t value;
for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
value = *src;
if (value) {
eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
if (status != FLASH_COMPLETE) final_status = status;
}
}
FLASH_Lock();
if (debug_eeprom) {
println("eeprom_compacted:");
print_eeprom();
}
return final_status;
}
static uint8_t eeprom_write_direct_entry(uint16_t Address) {
/* Check if we can just write this directly to the compacted flash area */
uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {
/* Write the value directly to the compacted area without a log entry */
uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
/* Early exit if a write isn't needed */
if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
FLASH_Unlock();
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
FLASH_Lock();
return status;
}
return 0;
}
static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
FLASH_Status final_status = FLASH_COMPLETE;
uint16_t value = *(uint16_t *)(&DataBuf[Address]);
eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
/* MSB signifies the lowest 128-byte optimization is not in effect */
uint16_t encoding = FEE_WORD_ENCODING;
uint8_t entry_size;
if (value <= 1) {
encoding |= value << 13;
entry_size = 2;
} else {
encoding |= FEE_VALUE_NEXT;
entry_size = 4;
/* Writes to addresses less than 128 are byte log entries */
Address -= FEE_BYTE_RANGE;
}
/* if we can't find an empty spot, we must compact emulated eeprom */
if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
/* compact the write log into the compacted flash area */
return eeprom_compact();
}
/* Word log writes should be word-aligned. Take back a bit */
Address >>= 1;
Address |= encoding;
/* ok we found a place let's write our data */
FLASH_Unlock();
/* address */
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
/* value */
if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
if (status != FLASH_COMPLETE) final_status = status;
}
FLASH_Lock();
return final_status;
}
static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
/* if couldn't find an empty spot, we must compact emulated eeprom */
if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
/* compact the write log into the compacted flash area */
return eeprom_compact();
}
/* ok we found a place let's write our data */
FLASH_Unlock();
/* Pack address and value into the same word */
uint16_t value = (Address << 8) | DataBuf[Address];
/* write to flash */
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
FLASH_Lock();
return status;
}
uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
/* if the address is out-of-bounds, do nothing */
if (Address >= FEE_DENSITY_BYTES) {
eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
return FLASH_BAD_ADDRESS;
}
/* if the value is the same, don't bother writing it */
if (DataBuf[Address] == DataByte) {
eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
return 0;
}
/* keep DataBuf cache in sync */
DataBuf[Address] = DataByte;
eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
/* perform the write into flash memory */
/* First, attempt to write directly into the compacted flash area */
FLASH_Status status = eeprom_write_direct_entry(Address);
if (!status) {
/* Otherwise append to the write log */
if (Address < FEE_BYTE_RANGE) {
status = eeprom_write_log_byte_entry(Address);
} else {
status = eeprom_write_log_word_entry(Address & 0xFFFE);
}
}
if (status != 0 && status != FLASH_COMPLETE) {
eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
}
return status;
}
uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
/* if the address is out-of-bounds, do nothing */
if (Address >= FEE_DENSITY_BYTES) {
eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
return FLASH_BAD_ADDRESS;
}
/* Check for word alignment */
FLASH_Status final_status = FLASH_COMPLETE;
if (Address % 2) {
final_status = EEPROM_WriteDataByte(Address, DataWord);
FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
if (status != FLASH_COMPLETE) final_status = status;
if (final_status != 0 && final_status != FLASH_COMPLETE) {
eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
}
return final_status;
}
/* if the value is the same, don't bother writing it */
uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
if (oldValue == DataWord) {
eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
return 0;
}
/* keep DataBuf cache in sync */
*(uint16_t *)(&DataBuf[Address]) = DataWord;
eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
/* perform the write into flash memory */
/* First, attempt to write directly into the compacted flash area */
final_status = eeprom_write_direct_entry(Address);
if (!final_status) {
/* Otherwise append to the write log */
/* Check if we need to fall back to byte write */
if (Address < FEE_BYTE_RANGE) {
final_status = FLASH_COMPLETE;
/* Only write a byte if it has changed */
if ((uint8_t)oldValue != (uint8_t)DataWord) {
final_status = eeprom_write_log_byte_entry(Address);
}
FLASH_Status status = FLASH_COMPLETE;
/* Only write a byte if it has changed */
if ((oldValue >> 8) != (DataWord >> 8)) {
status = eeprom_write_log_byte_entry(Address + 1);
}
if (status != FLASH_COMPLETE) final_status = status;
} else {
final_status = eeprom_write_log_word_entry(Address);
}
}
if (final_status != 0 && final_status != FLASH_COMPLETE) {
eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
}
return final_status;
}
uint8_t EEPROM_ReadDataByte(uint16_t Address) {
uint8_t DataByte = 0xFF;
// Get Byte from specified address
DataByte = (*(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address)));
if (Address < FEE_DENSITY_BYTES) {
DataByte = DataBuf[Address];
}
eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
return DataByte;
}
uint16_t EEPROM_ReadDataWord(uint16_t Address) {
uint16_t DataWord = 0xFFFF;
if (Address < FEE_DENSITY_BYTES - 1) {
/* Check word alignment */
if (Address % 2) {
DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
} else {
DataWord = *(uint16_t *)(&DataBuf[Address]);
}
}
eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
return DataWord;
}
/*****************************************************************************
* Wrap library in AVR style functions.
*******************************************************************************/
uint8_t eeprom_read_byte(const uint8_t *Address) {
const uint16_t p = (const uint32_t)Address;
return EEPROM_ReadDataByte(p);
}
uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); }
void eeprom_write_byte(uint8_t *Address, uint8_t Value) {
uint16_t p = (uint32_t)Address;
EEPROM_WriteDataByte(p, Value);
}
void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
void eeprom_update_byte(uint8_t *Address, uint8_t Value) {
uint16_t p = (uint32_t)Address;
EEPROM_WriteDataByte(p, Value);
}
void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
uint16_t eeprom_read_word(const uint16_t *Address) {
const uint16_t p = (const uint32_t)Address;
return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8);
}
uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); }
void eeprom_write_word(uint16_t *Address, uint16_t Value) {
uint16_t p = (uint32_t)Address;
EEPROM_WriteDataByte(p, (uint8_t)Value);
EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
}
void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
void eeprom_update_word(uint16_t *Address, uint16_t Value) {
uint16_t p = (uint32_t)Address;
EEPROM_WriteDataByte(p, (uint8_t)Value);
EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
}
void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
uint32_t eeprom_read_dword(const uint32_t *Address) {
const uint16_t p = (const uint32_t)Address;
return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24);
}
void eeprom_write_dword(uint32_t *Address, uint32_t Value) {
uint16_t p = (const uint32_t)Address;
EEPROM_WriteDataByte(p, (uint8_t)Value);
EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16));
EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
}
void eeprom_update_dword(uint32_t *Address, uint32_t Value) {
uint16_t p = (const uint32_t)Address;
uint32_t existingValue = EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24);
if (Value != existingValue) {
EEPROM_WriteDataByte(p, (uint8_t)Value);
EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16));
EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
const uint16_t p = (const uintptr_t)Address;
/* Check word alignment */
if (p % 2) {
/* Not aligned */
return (uint32_t)EEPROM_ReadDataByte(p) | (uint32_t)(EEPROM_ReadDataWord(p + 1) << 8) | (uint32_t)(EEPROM_ReadDataByte(p + 3) << 24);
} else {
/* Aligned */
return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16);
}
}
void eeprom_write_dword(uint32_t *Address, uint32_t Value) {
uint16_t p = (const uintptr_t)Address;
/* Check word alignment */
if (p % 2) {
/* Not aligned */
EEPROM_WriteDataByte(p, (uint8_t)Value);
EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8));
EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
} else {
/* Aligned */
EEPROM_WriteDataWord(p, (uint16_t)Value);
EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16));
}
}
void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, Value); }
void eeprom_read_block(void *buf, const void *addr, size_t len) {
const uint8_t *p = (const uint8_t *)addr;
const uint8_t *src = (const uint8_t *)addr;
uint8_t * dest = (uint8_t *)buf;
while (len--) {
*dest++ = eeprom_read_byte(p++);
/* Check word alignment */
if (len && (uintptr_t)src % 2) {
/* Read the unaligned first byte */
*dest++ = eeprom_read_byte(src++);
--len;
}
uint16_t value;
bool aligned = ((uintptr_t)dest % 2 == 0);
while (len > 1) {
value = eeprom_read_word((uint16_t *)src);
if (aligned) {
*(uint16_t *)dest = value;
dest += 2;
} else {
*dest++ = value;
*dest++ = value >> 8;
}
src += 2;
len -= 2;
}
if (len) {
*dest = eeprom_read_byte(src);
}
}
void eeprom_write_block(const void *buf, void *addr, size_t len) {
uint8_t * p = (uint8_t *)addr;
const uint8_t *src = (const uint8_t *)buf;
while (len--) {
eeprom_write_byte(p++, *src++);
uint8_t * dest = (uint8_t *)addr;
const uint8_t *src = (const uint8_t *)buf;
/* Check word alignment */
if (len && (uintptr_t)dest % 2) {
/* Write the unaligned first byte */
eeprom_write_byte(dest++, *src++);
--len;
}
uint16_t value;
bool aligned = ((uintptr_t)src % 2 == 0);
while (len > 1) {
if (aligned) {
value = *(uint16_t *)src;
} else {
value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
}
eeprom_write_word((uint16_t *)dest, value);
dest += 2;
src += 2;
len -= 2;
}
if (len) {
eeprom_write_byte(dest, *src);
}
}
void eeprom_update_block(const void *buf, void *addr, size_t len) {
uint8_t * p = (uint8_t *)addr;
const uint8_t *src = (const uint8_t *)buf;
while (len--) {
eeprom_write_byte(p++, *src++);
}
}
void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); }

View file

@ -23,62 +23,11 @@
#pragma once
#include <ch.h>
#include <hal.h>
#include "flash_stm32.h"
// HACK ALERT. This definition may not match your processor
// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc
#if defined(EEPROM_EMU_STM32F303xC)
# define MCU_STM32F303CC
#elif defined(EEPROM_EMU_STM32F103xB)
# define MCU_STM32F103RB
#elif defined(EEPROM_EMU_STM32F072xB)
# define MCU_STM32F072CB
#elif defined(EEPROM_EMU_STM32F042x6)
# define MCU_STM32F042K6
#else
# error "not implemented."
#endif
#ifndef EEPROM_PAGE_SIZE
# if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6)
# define FEE_PAGE_SIZE (uint16_t)0x400 // Page size = 1KByte
# define FEE_DENSITY_PAGES 2 // How many pages are used
# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB)
# define FEE_PAGE_SIZE (uint16_t)0x800 // Page size = 2KByte
# define FEE_DENSITY_PAGES 4 // How many pages are used
# else
# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
# endif
#endif
#ifndef EEPROM_START_ADDRESS
# if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB)
# define FEE_MCU_FLASH_SIZE 128 // Size in Kb
# elif defined(MCU_STM32F042K6)
# define FEE_MCU_FLASH_SIZE 32 // Size in Kb
# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE)
# define FEE_MCU_FLASH_SIZE 512 // Size in Kb
# elif defined(MCU_STM32F103RD)
# define FEE_MCU_FLASH_SIZE 384 // Size in Kb
# elif defined(MCU_STM32F303CC)
# define FEE_MCU_FLASH_SIZE 256 // Size in Kb
# else
# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
# endif
#endif
// DONT CHANGE
// Choose location for the first EEPROM Page address on the top of flash
#define FEE_PAGE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE))
#define FEE_DENSITY_BYTES ((FEE_PAGE_SIZE / 2) * FEE_DENSITY_PAGES - 1)
#define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES))
#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
#define FEE_ADDR_OFFSET(Address) (Address * 2) // 1Byte per Word will be saved to preserve Flash
// Use this function to initialize the functionality
uint16_t EEPROM_Init(void);
void EEPROM_Erase(void);
uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);
uint8_t EEPROM_ReadDataByte(uint16_t Address);
uint16_t EEPROM_ReadDataWord(uint16_t Address);
void print_eeprom(void);

View file

@ -0,0 +1,61 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <hal.h>
#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT)
# if defined(STM32F103xB) || defined(STM32F042x6)
# ifndef FEE_PAGE_SIZE
# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte
# endif
# ifndef FEE_PAGE_COUNT
# define FEE_PAGE_COUNT 2 // How many pages are used
# endif
# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F072xB) || defined(STM32F070xB)
# ifndef FEE_PAGE_SIZE
# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte
# endif
# ifndef FEE_PAGE_COUNT
# define FEE_PAGE_COUNT 4 // How many pages are used
# endif
# endif
#endif
#if !defined(FEE_MCU_FLASH_SIZE)
# if defined(STM32F042x6)
# define FEE_MCU_FLASH_SIZE 32 // Size in Kb
# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB)
# define FEE_MCU_FLASH_SIZE 128 // Size in Kb
# elif defined(STM32F303xC)
# define FEE_MCU_FLASH_SIZE 256 // Size in Kb
# elif defined(STM32F103xE)
# define FEE_MCU_FLASH_SIZE 512 // Size in Kb
# endif
#endif
/* Start of the emulated eeprom */
#if !defined(FEE_PAGE_BASE_ADDRESS)
# if 0
/* TODO: Add support for F4 */
# else
# ifndef FEE_FLASH_BASE
# define FEE_FLASH_BASE 0x8000000
# endif
/* Default to end of flash */
# define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - (FEE_PAGE_COUNT * FEE_PAGE_SIZE))
# endif
#endif

View file

@ -16,22 +16,7 @@
* Modifications for QMK and STM32F303 by Yiancar
*/
#if defined(EEPROM_EMU_STM32F303xC)
# define STM32F303xC
# include "stm32f3xx.h"
#elif defined(EEPROM_EMU_STM32F103xB)
# define STM32F103xB
# include "stm32f1xx.h"
#elif defined(EEPROM_EMU_STM32F072xB)
# define STM32F072xB
# include "stm32f0xx.h"
#elif defined(EEPROM_EMU_STM32F042x6)
# define STM32F042x6
# include "stm32f0xx.h"
#else
# error "not implemented."
#endif
#include <hal.h>
#include "flash_stm32.h"
#if defined(EEPROM_EMU_STM32F103xB)
@ -177,17 +162,3 @@ void FLASH_Lock(void) {
/* Set the Lock Bit to lock the FPEC and the FCR */
FLASH->CR |= FLASH_CR_LOCK;
}
/**
* @brief Clears the FLASH's pending flags.
* @param FLASH_FLAG: specifies the FLASH flags to clear.
* This parameter can be any combination of the following values:
* @arg FLASH_FLAG_PGERR: FLASH Programming error flag flag
* @arg FLASH_FLAG_WRPERR: FLASH Write protected error flag
* @arg FLASH_FLAG_EOP: FLASH End of Programming flag
* @retval None
*/
void FLASH_ClearFlag(uint32_t FLASH_FLAG) {
/* Clear the flags */
FLASH->SR = FLASH_FLAG;
}

View file

@ -22,8 +22,11 @@
extern "C" {
#endif
#include <ch.h>
#include <hal.h>
#include <stdint.h>
#ifdef FLASH_STM32_MOCKED
extern uint8_t FlashBuf[MOCK_FLASH_SIZE];
#endif
typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status;
@ -35,7 +38,6 @@ FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
void FLASH_Unlock(void);
void FLASH_Lock(void);
void FLASH_ClearFlag(uint32_t FLASH_FLAG);
#ifdef __cplusplus
}

View file

@ -0,0 +1,22 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform_deps.h"
void platform_setup(void) {
halInit();
chSysInit();
}

View file

@ -12,25 +12,6 @@
#include "led.h"
#include "wait.h"
#ifdef AUDIO_ENABLE
# include "audio.h"
#endif /* AUDIO_ENABLE */
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
#endif
#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
# include "rgblight.h"
#endif
#ifdef LED_MATRIX_ENABLE
# include "led_matrix.h"
#endif
#ifdef RGB_MATRIX_ENABLE
# include "rgb_matrix.h"
#endif
/** \brief suspend idle
*
* FIXME: needs doc
@ -40,61 +21,12 @@ void suspend_idle(uint8_t time) {
wait_ms(time);
}
/** \brief Run keyboard level Power down
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_power_down_user(void) {}
/** \brief Run keyboard level Power down
*
* FIXME: needs doc
*/
__attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); }
/** \brief suspend power down
*
* FIXME: needs doc
*/
void suspend_power_down(void) {
#ifdef BACKLIGHT_ENABLE
backlight_set(0);
#endif
#ifdef LED_MATRIX_ENABLE
led_matrix_task();
#endif
#ifdef RGB_MATRIX_ENABLE
rgb_matrix_task();
#endif
// Turn off LED indicators
uint8_t leds_off = 0;
#if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE)
if (is_backlight_enabled()) {
// Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
leds_off |= (1 << USB_LED_CAPS_LOCK);
}
#endif
led_set(leds_off);
// TODO: figure out what to power down and how
// shouldn't power down TPM/FTM if we want a breathing LED
// also shouldn't power down USB
#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
rgblight_suspend();
#endif
#if defined(LED_MATRIX_ENABLE)
led_matrix_set_suspend_state(true);
#endif
#if defined(RGB_MATRIX_ENABLE)
rgb_matrix_set_suspend_state(true);
#endif
#ifdef AUDIO_ENABLE
stop_all_notes();
#endif /* AUDIO_ENABLE */
suspend_power_down_kb();
suspend_power_down_quantum();
// on AVR, this enables the watchdog for 15ms (max), and goes to
// SLEEP_MODE_PWR_DOWN
@ -151,19 +83,6 @@ void suspend_wakeup_init(void) {
host_system_send(0);
host_consumer_send(0);
#endif /* EXTRAKEY_ENABLE */
#ifdef BACKLIGHT_ENABLE
backlight_init();
#endif /* BACKLIGHT_ENABLE */
led_set(host_keyboard_leds());
#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
rgblight_wakeup();
#endif
#if defined(LED_MATRIX_ENABLE)
led_matrix_set_suspend_state(false);
#endif
#if defined(RGB_MATRIX_ENABLE)
rgb_matrix_set_suspend_state(false);
#endif
suspend_wakeup_init_kb();
suspend_wakeup_init_quantum();
}

View file

@ -14,76 +14,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OPTIMIZE__
# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
#include <ch.h>
#include <hal.h>
#include "_wait.h"
#ifdef WAIT_US_TIMER
void wait_us(uint16_t duration) {
static const GPTConfig gpt_cfg = {1000000, NULL, 0, 0}; /* 1MHz timer, no callback */
if (duration == 0) {
duration = 1;
}
/*
* Only use this timer on the main thread;
* other threads need to use their own timer.
*/
if (chThdGetSelfX() == &ch.mainthread && duration < (1ULL << (sizeof(gptcnt_t) * 8))) {
gptStart(&WAIT_US_TIMER, &gpt_cfg);
gptPolledDelay(&WAIT_US_TIMER, duration);
} else {
chThdSleepMicroseconds(duration);
}
}
#endif
#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t"
__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
/* The argument n must be a constant expression.
* That way, compiler optimization will remove unnecessary code. */
if (n < 1) {
return;
}
if (n > 8) {
unsigned int n8 = n / 8;
n = n - n8 * 8;
switch (n8) {
case 16:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 15:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 14:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 13:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 12:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 11:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 10:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 9:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 8:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 7:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 6:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 5:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 4:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 3:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 2:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 1:
asm volatile(CLOCK_DELAY_NOP8::: "memory");
case 0:
break;
}
}
switch (n) {
case 8:
asm volatile("nop" ::: "memory");
case 7:
asm volatile("nop" ::: "memory");
case 6:
asm volatile("nop" ::: "memory");
case 5:
asm volatile("nop" ::: "memory");
case 4:
asm volatile("nop" ::: "memory");
case 3:
asm volatile("nop" ::: "memory");
case 2:
asm volatile("nop" ::: "memory");
case 1:
asm volatile("nop" ::: "memory");
case 0:
break;
}
}

View file

@ -1,211 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include "eeprom.h"
#include "eeconfig.h"
#include "action_layer.h"
#ifdef STM32_EEPROM_ENABLE
# include <hal.h>
# include "eeprom_stm32.h"
#endif
#if defined(EEPROM_DRIVER)
# include "eeprom_driver.h"
#endif
#if defined(HAPTIC_ENABLE)
# include "haptic.h"
#endif
/** \brief eeconfig enable
*
* FIXME: needs doc
*/
__attribute__((weak)) void eeconfig_init_user(void) {
// Reset user EEPROM value to blank, rather than to a set value
eeconfig_update_user(0);
}
__attribute__((weak)) void eeconfig_init_kb(void) {
// Reset Keyboard EEPROM value to blank, rather than to a set value
eeconfig_update_kb(0);
eeconfig_init_user();
}
/*
* FIXME: needs doc
*/
void eeconfig_init_quantum(void) {
#ifdef STM32_EEPROM_ENABLE
EEPROM_Erase();
#endif
#if defined(EEPROM_DRIVER)
eeprom_driver_erase();
#endif
eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
eeprom_update_byte(EECONFIG_DEBUG, 0);
eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0);
default_layer_state = 0;
eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0);
eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0);
eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0);
eeprom_update_byte(EECONFIG_BACKLIGHT, 0);
eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default
eeprom_update_dword(EECONFIG_RGBLIGHT, 0);
eeprom_update_byte(EECONFIG_STENOMODE, 0);
eeprom_update_dword(EECONFIG_HAPTIC, 0);
eeprom_update_byte(EECONFIG_VELOCIKEY, 0);
eeprom_update_dword(EECONFIG_RGB_MATRIX, 0);
eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0);
// TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS
// within the emulated eeprom via dfu-util or another tool
#if defined INIT_EE_HANDS_LEFT
# pragma message "Faking EE_HANDS for left hand"
eeprom_update_byte(EECONFIG_HANDEDNESS, 1);
#elif defined INIT_EE_HANDS_RIGHT
# pragma message "Faking EE_HANDS for right hand"
eeprom_update_byte(EECONFIG_HANDEDNESS, 0);
#endif
#if defined(HAPTIC_ENABLE)
haptic_reset();
#else
// this is used in case haptic is disabled, but we still want sane defaults
// in the haptic configuration eeprom. All zero will trigger a haptic_reset
// when a haptic-enabled firmware is loaded onto the keyboard.
eeprom_update_dword(EECONFIG_HAPTIC, 0);
#endif
eeconfig_init_kb();
}
/** \brief eeconfig initialization
*
* FIXME: needs doc
*/
void eeconfig_init(void) { eeconfig_init_quantum(); }
/** \brief eeconfig enable
*
* FIXME: needs doc
*/
void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); }
/** \brief eeconfig disable
*
* FIXME: needs doc
*/
void eeconfig_disable(void) {
#ifdef STM32_EEPROM_ENABLE
EEPROM_Erase();
#endif
#if defined(EEPROM_DRIVER)
eeprom_driver_erase();
#endif
eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);
}
/** \brief eeconfig is enabled
*
* FIXME: needs doc
*/
bool eeconfig_is_enabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); }
/** \brief eeconfig is disabled
*
* FIXME: needs doc
*/
bool eeconfig_is_disabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF); }
/** \brief eeconfig read debug
*
* FIXME: needs doc
*/
uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); }
/** \brief eeconfig update debug
*
* FIXME: needs doc
*/
void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); }
/** \brief eeconfig read default layer
*
* FIXME: needs doc
*/
uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); }
/** \brief eeconfig update default layer
*
* FIXME: needs doc
*/
void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); }
/** \brief eeconfig read keymap
*
* FIXME: needs doc
*/
uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); }
/** \brief eeconfig update keymap
*
* FIXME: needs doc
*/
void eeconfig_update_keymap(uint16_t val) {
eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF);
eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF);
}
/** \brief eeconfig read audio
*
* FIXME: needs doc
*/
uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); }
/** \brief eeconfig update audio
*
* FIXME: needs doc
*/
void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); }
/** \brief eeconfig read kb
*
* FIXME: needs doc
*/
uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); }
/** \brief eeconfig update kb
*
* FIXME: needs doc
*/
void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); }
/** \brief eeconfig read user
*
* FIXME: needs doc
*/
uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); }
/** \brief eeconfig update user
*
* FIXME: needs doc
*/
void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); }
/** \brief eeconfig read haptic
*
* FIXME: needs doc
*/
uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); }
/** \brief eeconfig update haptic
*
* FIXME: needs doc
*/
void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); }
/** \brief eeconfig read split handedness
*
* FIXME: needs doc
*/
bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); }
/** \brief eeconfig update split handedness
*
* FIXME: needs doc
*/
void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); }

View file

@ -1,113 +0,0 @@
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifndef EECONFIG_MAGIC_NUMBER
# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues
#endif
#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
/* EEPROM parameter address */
#define EECONFIG_MAGIC (uint16_t *)0
#define EECONFIG_DEBUG (uint8_t *)2
#define EECONFIG_DEFAULT_LAYER (uint8_t *)3
#define EECONFIG_KEYMAP (uint8_t *)4
#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5
#define EECONFIG_BACKLIGHT (uint8_t *)6
#define EECONFIG_AUDIO (uint8_t *)7
#define EECONFIG_RGBLIGHT (uint32_t *)8
#define EECONFIG_UNICODEMODE (uint8_t *)12
#define EECONFIG_STENOMODE (uint8_t *)13
// EEHANDS for two handed boards
#define EECONFIG_HANDEDNESS (uint8_t *)14
#define EECONFIG_KEYBOARD (uint32_t *)15
#define EECONFIG_USER (uint32_t *)19
#define EECONFIG_VELOCIKEY (uint8_t *)23
#define EECONFIG_HAPTIC (uint32_t *)24
// Mutually exclusive
#define EECONFIG_LED_MATRIX (uint32_t *)28
#define EECONFIG_RGB_MATRIX (uint32_t *)28
// Speed & Flags
#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32
#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32
// TODO: Combine these into a single word and single block of EEPROM
#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34
// Size of EEPROM being used, other code can refer to this for available EEPROM
#define EECONFIG_SIZE 35
/* debug bit */
#define EECONFIG_DEBUG_ENABLE (1 << 0)
#define EECONFIG_DEBUG_MATRIX (1 << 1)
#define EECONFIG_DEBUG_KEYBOARD (1 << 2)
#define EECONFIG_DEBUG_MOUSE (1 << 3)
/* keyconf bit */
#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0)
#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1)
#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2)
#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3)
#define EECONFIG_KEYMAP_NO_GUI (1 << 4)
#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5)
#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6)
#define EECONFIG_KEYMAP_NKRO (1 << 7)
#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP
bool eeconfig_is_enabled(void);
bool eeconfig_is_disabled(void);
void eeconfig_init(void);
void eeconfig_init_quantum(void);
void eeconfig_init_kb(void);
void eeconfig_init_user(void);
void eeconfig_enable(void);
void eeconfig_disable(void);
uint8_t eeconfig_read_debug(void);
void eeconfig_update_debug(uint8_t val);
uint8_t eeconfig_read_default_layer(void);
void eeconfig_update_default_layer(uint8_t val);
uint16_t eeconfig_read_keymap(void);
void eeconfig_update_keymap(uint16_t val);
#ifdef AUDIO_ENABLE
uint8_t eeconfig_read_audio(void);
void eeconfig_update_audio(uint8_t val);
#endif
uint32_t eeconfig_read_kb(void);
void eeconfig_update_kb(uint32_t val);
uint32_t eeconfig_read_user(void);
void eeconfig_update_user(uint32_t val);
#ifdef HAPTIC_ENABLE
uint32_t eeconfig_read_haptic(void);
void eeconfig_update_haptic(uint32_t val);
#endif
bool eeconfig_read_handedness(void);
void eeconfig_update_handedness(bool val);

View file

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "host.h"
#include "util.h"
#include "debug.h"
#include "digitizer.h"
#ifdef NKRO_ENABLE
# include "keycode_config.h"
@ -103,6 +104,24 @@ void host_consumer_send(uint16_t report) {
(*driver->send_consumer)(report);
}
void host_digitizer_send(digitizer_t *digitizer) {
if (!driver) return;
report_digitizer_t report = {
#ifdef DIGITIZER_SHARED_EP
.report_id = REPORT_ID_DIGITIZER,
#endif
.tip = digitizer->tipswitch & 0x1,
.inrange = digitizer->inrange & 0x1,
.x = (uint16_t)(digitizer->x * 0x7FFF),
.y = (uint16_t)(digitizer->y * 0x7FFF),
};
send_digitizer(&report);
}
__attribute__((weak)) void send_digitizer(report_digitizer_t *report) {}
uint16_t host_last_system_report(void) { return last_system_report; }
uint16_t host_last_consumer_report(void) { return last_consumer_report; }

View file

@ -30,3 +30,5 @@ typedef struct {
void (*send_system)(uint16_t);
void (*send_consumer)(uint16_t);
} host_driver_t;
void send_digitizer(report_digitizer_t *report);

View file

@ -1,558 +0,0 @@
/*
Copyright 2011, 2012, 2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "keyboard.h"
#include "matrix.h"
#include "keymap.h"
#include "host.h"
#include "led.h"
#include "keycode.h"
#include "timer.h"
#include "sync_timer.h"
#include "print.h"
#include "debug.h"
#include "command.h"
#include "util.h"
#include "sendchar.h"
#include "eeconfig.h"
#include "action_layer.h"
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
#endif
#ifdef MOUSEKEY_ENABLE
# include "mousekey.h"
#endif
#ifdef PS2_MOUSE_ENABLE
# include "ps2_mouse.h"
#endif
#ifdef SERIAL_MOUSE_ENABLE
# include "serial_mouse.h"
#endif
#ifdef ADB_MOUSE_ENABLE
# include "adb.h"
#endif
#ifdef RGBLIGHT_ENABLE
# include "rgblight.h"
#endif
#ifdef LED_MATRIX_ENABLE
# include "led_matrix.h"
#endif
#ifdef RGB_MATRIX_ENABLE
# include "rgb_matrix.h"
#endif
#ifdef ENCODER_ENABLE
# include "encoder.h"
#endif
#ifdef STENO_ENABLE
# include "process_steno.h"
#endif
#ifdef SERIAL_LINK_ENABLE
# include "serial_link/system/serial_link.h"
#endif
#ifdef VISUALIZER_ENABLE
# include "visualizer/visualizer.h"
#endif
#ifdef POINTING_DEVICE_ENABLE
# include "pointing_device.h"
#endif
#ifdef MIDI_ENABLE
# include "process_midi.h"
#endif
#ifdef JOYSTICK_ENABLE
# include "process_joystick.h"
#endif
#ifdef HD44780_ENABLE
# include "hd44780.h"
#endif
#ifdef QWIIC_ENABLE
# include "qwiic.h"
#endif
#ifdef OLED_DRIVER_ENABLE
# include "oled_driver.h"
#endif
#ifdef ST7565_ENABLE
# include "st7565.h"
#endif
#ifdef VELOCIKEY_ENABLE
# include "velocikey.h"
#endif
#ifdef VIA_ENABLE
# include "via.h"
#endif
#ifdef DIP_SWITCH_ENABLE
# include "dip_switch.h"
#endif
#ifdef STM32_EEPROM_ENABLE
# include "eeprom_stm32.h"
#endif
#ifdef EEPROM_DRIVER
# include "eeprom_driver.h"
#endif
#if defined(CRC_ENABLE)
# include "crc.h"
#endif
static uint32_t last_input_modification_time = 0;
uint32_t last_input_activity_time(void) { return last_input_modification_time; }
uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); }
static uint32_t last_matrix_modification_time = 0;
uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; }
uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); }
void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); }
static uint32_t last_encoder_modification_time = 0;
uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; }
uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); }
void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); }
// Only enable this if console is enabled to print to
#if defined(DEBUG_MATRIX_SCAN_RATE)
static uint32_t matrix_timer = 0;
static uint32_t matrix_scan_count = 0;
static uint32_t last_matrix_scan_count = 0;
void matrix_scan_perf_task(void) {
matrix_scan_count++;
uint32_t timer_now = timer_read32();
if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
# if defined(CONSOLE_ENABLE)
dprintf("matrix scan frequency: %lu\n", matrix_scan_count);
# endif
last_matrix_scan_count = matrix_scan_count;
matrix_timer = timer_now;
matrix_scan_count = 0;
}
}
uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; }
#else
# define matrix_scan_perf_task()
#endif
#ifdef MATRIX_HAS_GHOST
extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) {
matrix_row_t out = 0;
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
// read each key in the row data and check if the keymap defines it as a real key
if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) {
// this creates new row data, if a key is defined in the keymap, it will be set here
out |= 1 << col;
}
}
return out;
}
static inline bool popcount_more_than_one(matrix_row_t rowdata) {
rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero
return rowdata;
}
static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {
/* No ghost exists when less than 2 keys are down on the row.
If there are "active" blanks in the matrix, the key can't be pressed by the user,
there is no doubt as to which keys are really being pressed.
The ghosts will be ignored, they are KC_NO. */
rowdata = get_real_keys(row, rowdata);
if ((popcount_more_than_one(rowdata)) == 0) {
return false;
}
/* Ghost occurs when the row shares a column line with other row,
and two columns are read on each row. Blanks in the matrix don't matter,
so they are filtered out.
If there are two or more real keys pressed and they match columns with
at least two of another row's real keys, the row will be ignored. Keep in mind,
we are checking one row at a time, not all of them at once.
*/
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) {
return true;
}
}
return false;
}
#endif
void disable_jtag(void) {
// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.
#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
MCUCR |= _BV(JTD);
MCUCR |= _BV(JTD);
#elif defined(__AVR_ATmega32A__)
MCUCSR |= _BV(JTD);
MCUCSR |= _BV(JTD);
#endif
}
/** \brief matrix_setup
*
* FIXME: needs doc
*/
__attribute__((weak)) void matrix_setup(void) {}
/** \brief keyboard_pre_init_user
*
* FIXME: needs doc
*/
__attribute__((weak)) void keyboard_pre_init_user(void) {}
/** \brief keyboard_pre_init_kb
*
* FIXME: needs doc
*/
__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); }
/** \brief keyboard_post_init_user
*
* FIXME: needs doc
*/
__attribute__((weak)) void keyboard_post_init_user() {}
/** \brief keyboard_post_init_kb
*
* FIXME: needs doc
*/
__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); }
/** \brief keyboard_setup
*
* FIXME: needs doc
*/
void keyboard_setup(void) {
#ifndef NO_JTAG_DISABLE
disable_jtag();
#endif
print_set_sendchar(sendchar);
#ifdef STM32_EEPROM_ENABLE
EEPROM_Init();
#endif
#ifdef EEPROM_DRIVER
eeprom_driver_init();
#endif
matrix_setup();
keyboard_pre_init_kb();
}
/** \brief is_keyboard_master
*
* FIXME: needs doc
*/
__attribute__((weak)) bool is_keyboard_master(void) { return true; }
/** \brief is_keyboard_left
*
* FIXME: needs doc
*/
__attribute__((weak)) bool is_keyboard_left(void) { return true; }
/** \brief should_process_keypress
*
* Override this function if you have a condition where keypresses processing should change:
* - splits where the slave side needs to process for rgb/oled functionality
*/
__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); }
/** \brief housekeeping_task_kb
*
* Override this function if you have a need to execute code for every keyboard main loop iteration.
* This is specific to keyboard-level functionality.
*/
__attribute__((weak)) void housekeeping_task_kb(void) {}
/** \brief housekeeping_task_user
*
* Override this function if you have a need to execute code for every keyboard main loop iteration.
* This is specific to user/keymap-level functionality.
*/
__attribute__((weak)) void housekeeping_task_user(void) {}
/** \brief housekeeping_task
*
* Invokes hooks for executing code after QMK is done after each loop iteration.
*/
void housekeeping_task(void) {
housekeeping_task_kb();
housekeeping_task_user();
}
/** \brief keyboard_init
*
* FIXME: needs doc
*/
void keyboard_init(void) {
timer_init();
sync_timer_init();
matrix_init();
#if defined(CRC_ENABLE)
crc_init();
#endif
#ifdef VIA_ENABLE
via_init();
#endif
#ifdef QWIIC_ENABLE
qwiic_init();
#endif
#ifdef OLED_DRIVER_ENABLE
oled_init(OLED_ROTATION_0);
#endif
#ifdef ST7565_ENABLE
st7565_init(DISPLAY_ROTATION_0);
#endif
#ifdef PS2_MOUSE_ENABLE
ps2_mouse_init();
#endif
#ifdef SERIAL_MOUSE_ENABLE
serial_mouse_init();
#endif
#ifdef ADB_MOUSE_ENABLE
adb_mouse_init();
#endif
#ifdef BACKLIGHT_ENABLE
backlight_init();
#endif
#ifdef RGBLIGHT_ENABLE
rgblight_init();
#endif
#ifdef ENCODER_ENABLE
encoder_init();
#endif
#ifdef STENO_ENABLE
steno_init();
#endif
#ifdef POINTING_DEVICE_ENABLE
pointing_device_init();
#endif
#if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
keymap_config.nkro = 1;
eeconfig_update_keymap(keymap_config.raw);
#endif
#ifdef DIP_SWITCH_ENABLE
dip_switch_init();
#endif
#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
debug_enable = true;
#endif
keyboard_post_init_kb(); /* Always keep this last */
}
/** \brief key_event_task
*
* This function is responsible for calling into other systems when they need to respond to electrical switch press events.
* This is differnet than keycode events as no layer processing, or filtering occurs.
*/
void switch_events(uint8_t row, uint8_t col, bool pressed) {
#if defined(LED_MATRIX_ENABLE)
process_led_matrix(row, col, pressed);
#endif
#if defined(RGB_MATRIX_ENABLE)
process_rgb_matrix(row, col, pressed);
#endif
}
/** \brief Keyboard task: Do keyboard routine jobs
*
* Do routine keyboard jobs:
*
* * scan matrix
* * handle mouse movements
* * run visualizer code
* * handle midi commands
* * light LEDs
*
* This is repeatedly called as fast as possible.
*/
void keyboard_task(void) {
static matrix_row_t matrix_prev[MATRIX_ROWS];
static uint8_t led_status = 0;
matrix_row_t matrix_row = 0;
matrix_row_t matrix_change = 0;
#ifdef QMK_KEYS_PER_SCAN
uint8_t keys_processed = 0;
#endif
#ifdef ENCODER_ENABLE
bool encoders_changed = false;
#endif
uint8_t matrix_changed = matrix_scan();
if (matrix_changed) last_matrix_activity_trigger();
for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
matrix_row = matrix_get_row(r);
matrix_change = matrix_row ^ matrix_prev[r];
if (matrix_change) {
#ifdef MATRIX_HAS_GHOST
if (has_ghost_in_row(r, matrix_row)) {
continue;
}
#endif
if (debug_matrix) matrix_print();
matrix_row_t col_mask = 1;
for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) {
if (matrix_change & col_mask) {
if (should_process_keypress()) {
action_exec((keyevent_t){
.key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */
});
}
// record a processed key
matrix_prev[r] ^= col_mask;
switch_events(r, c, (matrix_row & col_mask));
#ifdef QMK_KEYS_PER_SCAN
// only jump out if we have processed "enough" keys.
if (++keys_processed >= QMK_KEYS_PER_SCAN)
#endif
// process a key per task call
goto MATRIX_LOOP_END;
}
}
}
}
// call with pseudo tick event when no real key event.
#ifdef QMK_KEYS_PER_SCAN
// we can get here with some keys processed now.
if (!keys_processed)
#endif
action_exec(TICK);
MATRIX_LOOP_END:
#ifdef DEBUG_MATRIX_SCAN_RATE
matrix_scan_perf_task();
#endif
#if defined(RGBLIGHT_ENABLE)
rgblight_task();
#endif
#ifdef LED_MATRIX_ENABLE
led_matrix_task();
#endif
#ifdef RGB_MATRIX_ENABLE
rgb_matrix_task();
#endif
#if defined(BACKLIGHT_ENABLE)
# if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS)
backlight_task();
# endif
#endif
#ifdef ENCODER_ENABLE
encoders_changed = encoder_read();
if (encoders_changed) last_encoder_activity_trigger();
#endif
#ifdef QWIIC_ENABLE
qwiic_task();
#endif
#ifdef OLED_DRIVER_ENABLE
oled_task();
# ifndef OLED_DISABLE_TIMEOUT
// Wake up oled if user is using those fabulous keys or spinning those encoders!
# ifdef ENCODER_ENABLE
if (matrix_changed || encoders_changed) oled_on();
# else
if (matrix_changed) oled_on();
# endif
# endif
#endif
#ifdef ST7565_ENABLE
st7565_task();
# ifndef ST7565_DISABLE_TIMEOUT
// Wake up display if user is using those fabulous keys or spinning those encoders!
# ifdef ENCODER_ENABLE
if (matrix_changed || encoders_changed) st7565_on();
# else
if (matrix_changed) st7565_on();
# endif
# endif
#endif
#ifdef MOUSEKEY_ENABLE
// mousekey repeat & acceleration
mousekey_task();
#endif
#ifdef PS2_MOUSE_ENABLE
ps2_mouse_task();
#endif
#ifdef SERIAL_MOUSE_ENABLE
serial_mouse_task();
#endif
#ifdef ADB_MOUSE_ENABLE
adb_mouse_task();
#endif
#ifdef SERIAL_LINK_ENABLE
serial_link_update();
#endif
#ifdef VISUALIZER_ENABLE
visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
#endif
#ifdef POINTING_DEVICE_ENABLE
pointing_device_task();
#endif
#ifdef MIDI_ENABLE
midi_task();
#endif
#ifdef VELOCIKEY_ENABLE
if (velocikey_enabled()) {
velocikey_decelerate();
}
#endif
#ifdef JOYSTICK_ENABLE
joystick_task();
#endif
// update LED
if (led_status != host_keyboard_leds()) {
led_status = host_keyboard_leds();
keyboard_set_leds(led_status);
}
}
/** \brief keyboard set leds
*
* FIXME: needs doc
*/
void keyboard_set_leds(uint8_t leds) {
if (debug_keyboard) {
debug("keyboard_set_led: ");
debug_hex8(leds);
debug("\n");
}
led_set(leds);
}

View file

@ -1,90 +0,0 @@
/*
Copyright 2011,2012,2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* key matrix position */
typedef struct {
uint8_t col;
uint8_t row;
} keypos_t;
/* key event */
typedef struct {
keypos_t key;
bool pressed;
uint16_t time;
} keyevent_t;
/* equivalent test of keypos_t */
#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)
/* Rules for No Event:
* 1) (time == 0) to handle (keyevent_t){} as empty event
* 2) Matrix(255, 255) to make TICK event available
*/
static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); }
static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); }
static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); }
/* Tick event */
#define TICK \
(keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) }
/* it runs once at early stage of startup before keyboard_init. */
void keyboard_setup(void);
/* it runs once after initializing host side protocol, debug and MCU peripherals. */
void keyboard_init(void);
/* it runs repeatedly in main loop */
void keyboard_task(void);
/* it runs when host LED status is updated */
void keyboard_set_leds(uint8_t leds);
/* it runs whenever code has to behave differently on a slave */
bool is_keyboard_master(void);
/* it runs whenever code has to behave differently on left vs right split */
bool is_keyboard_left(void);
void keyboard_pre_init_kb(void);
void keyboard_pre_init_user(void);
void keyboard_post_init_kb(void);
void keyboard_post_init_user(void);
void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol
void housekeeping_task_kb(void); // To be overridden by keyboard-level code
void housekeeping_task_user(void); // To be overridden by user/keymap-level code
uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity
uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity
uint32_t last_matrix_activity_time(void); // Timestamp of the last matrix activity
uint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity
uint32_t last_encoder_activity_time(void); // Timestamp of the last encoder activity
uint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity
uint32_t get_matrix_scan_rate(void);
#ifdef __cplusplus
}
#endif

View file

@ -1,560 +0,0 @@
/*
Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C)
*
* See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf
* or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older)
*/
#pragma once
/* FIXME: Add doxygen comments here */
#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED)
#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)
#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE)
#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID)
#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31)
#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2)
#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT)
#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8)
#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT)
#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2)
#define MOD_BIT(code) (1 << MOD_INDEX(code))
#define MOD_INDEX(code) ((code)&0x07)
#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL))
#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))
#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT))
#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI))
#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)
#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)
#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)
#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT)
#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI)
#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI)
#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT)
#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI)
#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI)
#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
#define FN_BIT(code) (1 << FN_INDEX(code))
#define FN_INDEX(code) ((code)-KC_FN0)
#define FN_MIN KC_FN0
#define FN_MAX KC_FN31
/*
* Short names for ease of definition of keymap
*/
/* Transparent */
#define KC_TRANSPARENT 0x01
#define KC_TRNS KC_TRANSPARENT
/* Punctuation */
#define KC_ENT KC_ENTER
#define KC_ESC KC_ESCAPE
#define KC_BSPC KC_BSPACE
#define KC_SPC KC_SPACE
#define KC_MINS KC_MINUS
#define KC_EQL KC_EQUAL
#define KC_LBRC KC_LBRACKET
#define KC_RBRC KC_RBRACKET
#define KC_BSLS KC_BSLASH
#define KC_NUHS KC_NONUS_HASH
#define KC_SCLN KC_SCOLON
#define KC_QUOT KC_QUOTE
#define KC_GRV KC_GRAVE
#define KC_COMM KC_COMMA
#define KC_SLSH KC_SLASH
#define KC_NUBS KC_NONUS_BSLASH
/* Lock Keys */
#define KC_CLCK KC_CAPSLOCK
#define KC_CAPS KC_CAPSLOCK
#define KC_SLCK KC_SCROLLLOCK
#define KC_NLCK KC_NUMLOCK
#define KC_LCAP KC_LOCKING_CAPS
#define KC_LNUM KC_LOCKING_NUM
#define KC_LSCR KC_LOCKING_SCROLL
/* Commands */
#define KC_PSCR KC_PSCREEN
#define KC_PAUS KC_PAUSE
#define KC_BRK KC_PAUSE
#define KC_INS KC_INSERT
#define KC_DEL KC_DELETE
#define KC_PGDN KC_PGDOWN
#define KC_RGHT KC_RIGHT
#define KC_APP KC_APPLICATION
#define KC_EXEC KC_EXECUTE
#define KC_SLCT KC_SELECT
#define KC_AGIN KC_AGAIN
#define KC_PSTE KC_PASTE
#define KC_ERAS KC_ALT_ERASE
#define KC_CLR KC_CLEAR
/* Keypad */
#define KC_PSLS KC_KP_SLASH
#define KC_PAST KC_KP_ASTERISK
#define KC_PMNS KC_KP_MINUS
#define KC_PPLS KC_KP_PLUS
#define KC_PENT KC_KP_ENTER
#define KC_P1 KC_KP_1
#define KC_P2 KC_KP_2
#define KC_P3 KC_KP_3
#define KC_P4 KC_KP_4
#define KC_P5 KC_KP_5
#define KC_P6 KC_KP_6
#define KC_P7 KC_KP_7
#define KC_P8 KC_KP_8
#define KC_P9 KC_KP_9
#define KC_P0 KC_KP_0
#define KC_PDOT KC_KP_DOT
#define KC_PEQL KC_KP_EQUAL
#define KC_PCMM KC_KP_COMMA
/* Japanese specific */
#define KC_ZKHK KC_GRAVE
#define KC_RO KC_INT1
#define KC_KANA KC_INT2
#define KC_JYEN KC_INT3
#define KC_HENK KC_INT4
#define KC_MHEN KC_INT5
/* Korean specific */
#define KC_HAEN KC_LANG1
#define KC_HANJ KC_LANG2
/* Modifiers */
#define KC_LCTL KC_LCTRL
#define KC_LSFT KC_LSHIFT
#define KC_LOPT KC_LALT
#define KC_LCMD KC_LGUI
#define KC_LWIN KC_LGUI
#define KC_RCTL KC_RCTRL
#define KC_RSFT KC_RSHIFT
#define KC_ALGR KC_RALT
#define KC_ROPT KC_RALT
#define KC_RCMD KC_RGUI
#define KC_RWIN KC_RGUI
/* Generic Desktop Page (0x01) */
#define KC_PWR KC_SYSTEM_POWER
#define KC_SLEP KC_SYSTEM_SLEEP
#define KC_WAKE KC_SYSTEM_WAKE
/* Consumer Page (0x0C) */
#define KC_MUTE KC_AUDIO_MUTE
#define KC_VOLU KC_AUDIO_VOL_UP
#define KC_VOLD KC_AUDIO_VOL_DOWN
#define KC_MNXT KC_MEDIA_NEXT_TRACK
#define KC_MPRV KC_MEDIA_PREV_TRACK
#define KC_MSTP KC_MEDIA_STOP
#define KC_MPLY KC_MEDIA_PLAY_PAUSE
#define KC_MSEL KC_MEDIA_SELECT
#define KC_EJCT KC_MEDIA_EJECT
#define KC_CALC KC_CALCULATOR
#define KC_MYCM KC_MY_COMPUTER
#define KC_WSCH KC_WWW_SEARCH
#define KC_WHOM KC_WWW_HOME
#define KC_WBAK KC_WWW_BACK
#define KC_WFWD KC_WWW_FORWARD
#define KC_WSTP KC_WWW_STOP
#define KC_WREF KC_WWW_REFRESH
#define KC_WFAV KC_WWW_FAVORITES
#define KC_MFFD KC_MEDIA_FAST_FORWARD
#define KC_MRWD KC_MEDIA_REWIND
#define KC_BRIU KC_BRIGHTNESS_UP
#define KC_BRID KC_BRIGHTNESS_DOWN
/* System Specific */
#define KC_BRMU KC_PAUSE
#define KC_BRMD KC_SCROLLLOCK
/* Mouse Keys */
#define KC_MS_U KC_MS_UP
#define KC_MS_D KC_MS_DOWN
#define KC_MS_L KC_MS_LEFT
#define KC_MS_R KC_MS_RIGHT
#define KC_BTN1 KC_MS_BTN1
#define KC_BTN2 KC_MS_BTN2
#define KC_BTN3 KC_MS_BTN3
#define KC_BTN4 KC_MS_BTN4
#define KC_BTN5 KC_MS_BTN5
#define KC_BTN6 KC_MS_BTN6
#define KC_BTN7 KC_MS_BTN7
#define KC_BTN8 KC_MS_BTN8
#define KC_WH_U KC_MS_WH_UP
#define KC_WH_D KC_MS_WH_DOWN
#define KC_WH_L KC_MS_WH_LEFT
#define KC_WH_R KC_MS_WH_RIGHT
#define KC_ACL0 KC_MS_ACCEL0
#define KC_ACL1 KC_MS_ACCEL1
#define KC_ACL2 KC_MS_ACCEL2
/* Keyboard/Keypad Page (0x07) */
enum hid_keyboard_keypad_usage {
KC_NO = 0x00,
KC_ROLL_OVER,
KC_POST_FAIL,
KC_UNDEFINED,
KC_A,
KC_B,
KC_C,
KC_D,
KC_E,
KC_F,
KC_G,
KC_H,
KC_I,
KC_J,
KC_K,
KC_L,
KC_M, // 0x10
KC_N,
KC_O,
KC_P,
KC_Q,
KC_R,
KC_S,
KC_T,
KC_U,
KC_V,
KC_W,
KC_X,
KC_Y,
KC_Z,
KC_1,
KC_2,
KC_3, // 0x20
KC_4,
KC_5,
KC_6,
KC_7,
KC_8,
KC_9,
KC_0,
KC_ENTER,
KC_ESCAPE,
KC_BSPACE,
KC_TAB,
KC_SPACE,
KC_MINUS,
KC_EQUAL,
KC_LBRACKET,
KC_RBRACKET, // 0x30
KC_BSLASH,
KC_NONUS_HASH,
KC_SCOLON,
KC_QUOTE,
KC_GRAVE,
KC_COMMA,
KC_DOT,
KC_SLASH,
KC_CAPSLOCK,
KC_F1,
KC_F2,
KC_F3,
KC_F4,
KC_F5,
KC_F6,
KC_F7, // 0x40
KC_F8,
KC_F9,
KC_F10,
KC_F11,
KC_F12,
KC_PSCREEN,
KC_SCROLLLOCK,
KC_PAUSE,
KC_INSERT,
KC_HOME,
KC_PGUP,
KC_DELETE,
KC_END,
KC_PGDOWN,
KC_RIGHT,
KC_LEFT, // 0x50
KC_DOWN,
KC_UP,
KC_NUMLOCK,
KC_KP_SLASH,
KC_KP_ASTERISK,
KC_KP_MINUS,
KC_KP_PLUS,
KC_KP_ENTER,
KC_KP_1,
KC_KP_2,
KC_KP_3,
KC_KP_4,
KC_KP_5,
KC_KP_6,
KC_KP_7,
KC_KP_8, // 0x60
KC_KP_9,
KC_KP_0,
KC_KP_DOT,
KC_NONUS_BSLASH,
KC_APPLICATION,
KC_POWER,
KC_KP_EQUAL,
KC_F13,
KC_F14,
KC_F15,
KC_F16,
KC_F17,
KC_F18,
KC_F19,
KC_F20,
KC_F21, // 0x70
KC_F22,
KC_F23,
KC_F24,
KC_EXECUTE,
KC_HELP,
KC_MENU,
KC_SELECT,
KC_STOP,
KC_AGAIN,
KC_UNDO,
KC_CUT,
KC_COPY,
KC_PASTE,
KC_FIND,
KC__MUTE,
KC__VOLUP, // 0x80
KC__VOLDOWN,
KC_LOCKING_CAPS,
KC_LOCKING_NUM,
KC_LOCKING_SCROLL,
KC_KP_COMMA,
KC_KP_EQUAL_AS400,
KC_INT1,
KC_INT2,
KC_INT3,
KC_INT4,
KC_INT5,
KC_INT6,
KC_INT7,
KC_INT8,
KC_INT9,
KC_LANG1, // 0x90
KC_LANG2,
KC_LANG3,
KC_LANG4,
KC_LANG5,
KC_LANG6,
KC_LANG7,
KC_LANG8,
KC_LANG9,
KC_ALT_ERASE,
KC_SYSREQ,
KC_CANCEL,
KC_CLEAR,
KC_PRIOR,
KC_RETURN,
KC_SEPARATOR,
KC_OUT, // 0xA0
KC_OPER,
KC_CLEAR_AGAIN,
KC_CRSEL,
KC_EXSEL,
#if 0
// ***************************************************************
// These keycodes are present in the HID spec, but are *
// nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) *
// for the media and function keys instead - see below. *
// ***************************************************************
KC_KP_00 = 0xB0,
KC_KP_000,
KC_THOUSANDS_SEPARATOR,
KC_DECIMAL_SEPARATOR,
KC_CURRENCY_UNIT,
KC_CURRENCY_SUB_UNIT,
KC_KP_LPAREN,
KC_KP_RPAREN,
KC_KP_LCBRACKET,
KC_KP_RCBRACKET,
KC_KP_TAB,
KC_KP_BSPACE,
KC_KP_A,
KC_KP_B,
KC_KP_C,
KC_KP_D,
KC_KP_E, //0xC0
KC_KP_F,
KC_KP_XOR,
KC_KP_HAT,
KC_KP_PERC,
KC_KP_LT,
KC_KP_GT,
KC_KP_AND,
KC_KP_LAZYAND,
KC_KP_OR,
KC_KP_LAZYOR,
KC_KP_COLON,
KC_KP_HASH,
KC_KP_SPACE,
KC_KP_ATMARK,
KC_KP_EXCLAMATION,
KC_KP_MEM_STORE, //0xD0
KC_KP_MEM_RECALL,
KC_KP_MEM_CLEAR,
KC_KP_MEM_ADD,
KC_KP_MEM_SUB,
KC_KP_MEM_MUL,
KC_KP_MEM_DIV,
KC_KP_PLUS_MINUS,
KC_KP_CLEAR,
KC_KP_CLEAR_ENTRY,
KC_KP_BINARY,
KC_KP_OCTAL,
KC_KP_DECIMAL,
KC_KP_HEXADECIMAL,
#endif
/* Modifiers */
KC_LCTRL = 0xE0,
KC_LSHIFT,
KC_LALT,
KC_LGUI,
KC_RCTRL,
KC_RSHIFT,
KC_RALT,
KC_RGUI
// **********************************************
// * 0xF0-0xFF are unallocated in the HID spec. *
// * QMK uses these for Mouse Keys - see below. *
// **********************************************
};
/* Media and Function keys */
enum internal_special_keycodes {
/* Generic Desktop Page (0x01) */
KC_SYSTEM_POWER = 0xA5,
KC_SYSTEM_SLEEP,
KC_SYSTEM_WAKE,
/* Consumer Page (0x0C) */
KC_AUDIO_MUTE,
KC_AUDIO_VOL_UP,
KC_AUDIO_VOL_DOWN,
KC_MEDIA_NEXT_TRACK,
KC_MEDIA_PREV_TRACK,
KC_MEDIA_STOP,
KC_MEDIA_PLAY_PAUSE,
KC_MEDIA_SELECT,
KC_MEDIA_EJECT, // 0xB0
KC_MAIL,
KC_CALCULATOR,
KC_MY_COMPUTER,
KC_WWW_SEARCH,
KC_WWW_HOME,
KC_WWW_BACK,
KC_WWW_FORWARD,
KC_WWW_STOP,
KC_WWW_REFRESH,
KC_WWW_FAVORITES,
KC_MEDIA_FAST_FORWARD,
KC_MEDIA_REWIND,
KC_BRIGHTNESS_UP,
KC_BRIGHTNESS_DOWN,
/* Fn keys */
KC_FN0 = 0xC0,
KC_FN1,
KC_FN2,
KC_FN3,
KC_FN4,
KC_FN5,
KC_FN6,
KC_FN7,
KC_FN8,
KC_FN9,
KC_FN10,
KC_FN11,
KC_FN12,
KC_FN13,
KC_FN14,
KC_FN15,
KC_FN16, // 0xD0
KC_FN17,
KC_FN18,
KC_FN19,
KC_FN20,
KC_FN21,
KC_FN22,
KC_FN23,
KC_FN24,
KC_FN25,
KC_FN26,
KC_FN27,
KC_FN28,
KC_FN29,
KC_FN30,
KC_FN31
};
enum mouse_keys {
/* Mouse Buttons */
#ifdef VIA_ENABLE
KC_MS_UP = 0xF0,
#else
KC_MS_UP = 0xED,
#endif
KC_MS_DOWN,
KC_MS_LEFT,
KC_MS_RIGHT, // 0xF0
KC_MS_BTN1,
KC_MS_BTN2,
KC_MS_BTN3,
KC_MS_BTN4,
KC_MS_BTN5,
#ifdef VIA_ENABLE
KC_MS_BTN6 = KC_MS_BTN5,
KC_MS_BTN7 = KC_MS_BTN5,
KC_MS_BTN8 = KC_MS_BTN5,
#else
KC_MS_BTN6,
KC_MS_BTN7,
KC_MS_BTN8,
#endif
/* Mouse Wheel */
KC_MS_WH_UP,
KC_MS_WH_DOWN,
KC_MS_WH_LEFT,
KC_MS_WH_RIGHT,
/* Acceleration */
KC_MS_ACCEL0,
KC_MS_ACCEL1,
KC_MS_ACCEL2 // 0xFF
};

View file

@ -31,6 +31,7 @@ enum hid_report_ids {
REPORT_ID_CONSUMER,
REPORT_ID_NKRO,
REPORT_ID_JOYSTICK,
REPORT_ID_DIGITIZER,
REPORT_ID_XAP
};
@ -206,6 +207,17 @@ typedef struct {
int8_t h;
} __attribute__((packed)) report_mouse_t;
typedef struct {
#ifdef DIGITIZER_SHARED_EP
uint8_t report_id;
#endif
uint8_t tip : 1;
uint8_t inrange : 1;
uint8_t pad2 : 6;
uint16_t x;
uint16_t y;
} __attribute__((packed)) report_digitizer_t;
typedef struct {
#if JOYSTICK_AXES_COUNT > 0
# if JOYSTICK_AXES_RESOLUTION > 8

View file

@ -10,8 +10,10 @@ void suspend_wakeup_init(void);
void suspend_wakeup_init_user(void);
void suspend_wakeup_init_kb(void);
void suspend_wakeup_init_quantum(void);
void suspend_power_down_user(void);
void suspend_power_down_kb(void);
void suspend_power_down_quantum(void);
#ifndef USB_SUSPEND_WAKEUP_DELAY
# define USB_SUSPEND_WAKEUP_DELAY 0

View file

@ -0,0 +1,438 @@
/* Copyright 2021 by Don Kjer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
extern "C" {
#include "flash_stm32.h"
#include "eeprom_stm32.h"
#include "eeprom.h"
}
/* Mock Flash Parameters:
*
* === Large Layout ===
* flash size: 65536
* page size: 2048
* density pages: 16
* Simulated EEPROM size: 16384
*
* FlashBuf Layout:
* [Unused | Compact | Write Log ]
* [0......|32768......|49152......65535]
*
* === Tiny Layout ===
* flash size: 1024
* page size: 512
* density pages: 1
* Simulated EEPROM size: 256
*
* FlashBuf Layout:
* [Unused | Compact | Write Log ]
* [0......|512......|768......1023]
*
*/
#define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_PAGE_COUNT / 2)
#define LOG_SIZE EEPROM_SIZE
#define LOG_BASE (MOCK_FLASH_SIZE - LOG_SIZE)
#define EEPROM_BASE (LOG_BASE - EEPROM_SIZE)
/* Log encoding helpers */
#define BYTE_VALUE(addr, value) (((addr) << 8) | (value))
#define WORD_ZERO(addr) (0x8000 | ((addr) >> 1))
#define WORD_ONE(addr) (0xA000 | ((addr) >> 1))
#define WORD_NEXT(addr) (0xE000 | (((addr)-0x80) >> 1))
class EepromStm32Test : public testing::Test {
public:
EepromStm32Test() {}
~EepromStm32Test() {}
protected:
void SetUp() override { EEPROM_Erase(); }
void TearDown() override {
#ifdef EEPROM_DEBUG
dumpEepromDataBuf();
#endif
}
};
TEST_F(EepromStm32Test, TestErase) {
EEPROM_WriteDataByte(0, 0x42);
EEPROM_Erase();
EXPECT_EQ(EEPROM_ReadDataByte(0), 0);
EXPECT_EQ(EEPROM_ReadDataByte(1), 0);
}
TEST_F(EepromStm32Test, TestReadGarbage) {
uint8_t garbage = 0x3c;
for (int i = 0; i < MOCK_FLASH_SIZE; ++i) {
garbage ^= 0xa3;
garbage += i;
FlashBuf[i] = garbage;
}
EEPROM_Init(); // Just verify we don't crash
}
TEST_F(EepromStm32Test, TestWriteBadAddress) {
EXPECT_EQ(EEPROM_WriteDataByte(EEPROM_SIZE, 0x42), FLASH_BAD_ADDRESS);
EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE - 1, 0xbeef), FLASH_BAD_ADDRESS);
EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE, 0xbeef), FLASH_BAD_ADDRESS);
}
TEST_F(EepromStm32Test, TestReadBadAddress) {
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE), 0xFF);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 1), 0xFFFF);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE), 0xFFFF);
EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0);
EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 3)), 0xFF000000);
EXPECT_EQ(eeprom_read_dword((uint32_t*)EEPROM_SIZE), 0xFFFFFFFF);
}
TEST_F(EepromStm32Test, TestReadByte) {
/* Direct compacted-area baseline: Address < 0x80 */
FlashBuf[EEPROM_BASE + 2] = ~0xef;
FlashBuf[EEPROM_BASE + 3] = ~0xbe;
/* Direct compacted-area baseline: Address >= 0x80 */
FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78;
FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56;
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);
EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);
/* Write Log byte value */
FlashBuf[LOG_BASE] = 0x65;
FlashBuf[LOG_BASE + 1] = 3;
/* Write Log word value */
*(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_NEXT(EEPROM_SIZE - 2);
*(uint16_t*)&FlashBuf[LOG_BASE + 4] = ~0x9abc;
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);
EXPECT_EQ(EEPROM_ReadDataByte(3), 0x65);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0xbc);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x9a);
}
TEST_F(EepromStm32Test, TestWriteByte) {
/* Direct compacted-area baseline: Address < 0x80 */
EEPROM_WriteDataByte(2, 0xef);
EEPROM_WriteDataByte(3, 0xbe);
/* Direct compacted-area baseline: Address >= 0x80 */
EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78);
EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56);
/* Check values */
/* First write in each aligned word should have been direct */
EXPECT_EQ(FlashBuf[EEPROM_BASE + 2], (uint8_t)~0xef);
EXPECT_EQ(FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint8_t)~0x78);
/* Second write per aligned word requires a log entry */
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(3, 0xbe));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(EEPROM_SIZE - 1));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0x5678);
}
TEST_F(EepromStm32Test, TestByteRoundTrip) {
/* Direct compacted-area: Address < 0x80 */
EEPROM_WriteDataWord(0, 0xdead);
EEPROM_WriteDataByte(2, 0xef);
EEPROM_WriteDataByte(3, 0xbe);
/* Direct compacted-area: Address >= 0x80 */
EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78);
EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataByte(0), 0xad);
EXPECT_EQ(EEPROM_ReadDataByte(1), 0xde);
EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);
EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);
/* Write log entries */
EEPROM_WriteDataByte(2, 0x80);
EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x3c);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataByte(2), 0x80);
EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x3c);
EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);
}
TEST_F(EepromStm32Test, TestReadWord) {
/* Direct compacted-area baseline: Address < 0x80 */
FlashBuf[EEPROM_BASE + 0] = ~0xad;
FlashBuf[EEPROM_BASE + 1] = ~0xde;
/* Direct compacted-area baseline: Address >= 0x80 */
FlashBuf[EEPROM_BASE + 200] = ~0xcd;
FlashBuf[EEPROM_BASE + 201] = ~0xab;
FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4] = ~0x34;
FlashBuf[EEPROM_BASE + EEPROM_SIZE - 3] = ~0x12;
FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78;
FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56;
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead);
EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678);
/* Write Log word zero-encoded */
*(uint16_t*)&FlashBuf[LOG_BASE] = WORD_ZERO(200);
/* Write Log word one-encoded */
*(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_ONE(EEPROM_SIZE - 4);
/* Write Log word value */
*(uint16_t*)&FlashBuf[LOG_BASE + 4] = WORD_NEXT(EEPROM_SIZE - 2);
*(uint16_t*)&FlashBuf[LOG_BASE + 6] = ~0x9abc;
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(200), 0);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 1);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x9abc);
}
TEST_F(EepromStm32Test, TestWriteWord) {
/* Direct compacted-area: Address < 0x80 */
EEPROM_WriteDataWord(0, 0xdead); // Aligned
EEPROM_WriteDataWord(3, 0xbeef); // Unaligned
/* Direct compacted-area: Address >= 0x80 */
EEPROM_WriteDataWord(200, 0xabcd); // Aligned
EEPROM_WriteDataWord(203, 0x9876); // Unaligned
EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234);
EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678);
/* Write Log word zero-encoded */
EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0);
/* Write Log word one-encoded */
EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1);
/* Write Log word value aligned */
EEPROM_WriteDataWord(200, 0x4321); // Aligned
/* Write Log word value unaligned */
EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte
EEPROM_WriteDataWord(203, 0xcdef); // Unaligned
/* Check values */
/* Direct compacted-area */
EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE], (uint16_t)~0xdead);
EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 3], (uint16_t)~0xbeef);
EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 200], (uint16_t)~0xabcd);
EXPECT_EQ(FlashBuf[EEPROM_BASE + 203], (uint8_t)~0x76);
EXPECT_EQ(FlashBuf[EEPROM_BASE + 204], (uint8_t)~0x98);
EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4], (uint16_t)~0x1234);
EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint16_t)~0x5678);
/* Write Log word zero-encoded */
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], WORD_ZERO(EEPROM_SIZE - 4));
/* Write Log word one-encoded */
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_ONE(EEPROM_SIZE - 2));
/* Write Log word value aligned */
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], WORD_NEXT(200));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], (uint16_t)~0x4321);
/* Write Log word value unaligned */
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], WORD_NEXT(202));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], (uint16_t)~0x763c);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(202));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xef3c);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(204));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0x00cd);
}
TEST_F(EepromStm32Test, TestWordRoundTrip) {
/* Direct compacted-area: Address < 0x80 */
EEPROM_WriteDataWord(0, 0xdead); // Aligned
EEPROM_WriteDataWord(3, 0xbeef); // Unaligned
/* Direct compacted-area: Address >= 0x80 */
EEPROM_WriteDataWord(200, 0xabcd); // Aligned
EEPROM_WriteDataWord(203, 0x9876); // Unaligned
EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234);
EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead);
EXPECT_EQ(EEPROM_ReadDataWord(3), 0xbeef);
EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd);
EXPECT_EQ(EEPROM_ReadDataWord(203), 0x9876);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678);
/* Write Log word zero-encoded */
EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0);
/* Write Log word one-encoded */
EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1);
/* Write Log word value aligned */
EEPROM_WriteDataWord(200, 0x4321); // Aligned
/* Write Log word value unaligned */
EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte
EEPROM_WriteDataWord(203, 0xcdef); // Unaligned
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(200), 0x4321);
EXPECT_EQ(EEPROM_ReadDataByte(202), 0x3c);
EXPECT_EQ(EEPROM_ReadDataWord(203), 0xcdef);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0);
EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 1);
}
TEST_F(EepromStm32Test, TestByteWordBoundary) {
/* Direct compacted-area write */
EEPROM_WriteDataWord(0x7e, 0xdead);
EEPROM_WriteDataWord(0x80, 0xbeef);
/* Byte log entry */
EEPROM_WriteDataByte(0x7f, 0x3c);
/* Word log entry */
EEPROM_WriteDataByte(0x80, 0x18);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0x3cad);
EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xbe18);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(0x7f, 0x3c));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(0x80));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0xbe18);
/* Byte log entries */
EEPROM_WriteDataWord(0x7e, 0xcafe);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0xcafe);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], BYTE_VALUE(0x7e, 0xfe));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], BYTE_VALUE(0x7f, 0xca));
/* Byte and Word log entries */
EEPROM_WriteDataWord(0x7f, 0xba5e);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(0x7f), 0xba5e);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], BYTE_VALUE(0x7f, 0x5e));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(0x80));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xbeba);
/* Word log entry */
EEPROM_WriteDataWord(0x80, 0xf00d);
/* Check values */
EEPROM_Init();
EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xf00d);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(0x80));
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0xf00d);
}
TEST_F(EepromStm32Test, TestDWordRoundTrip) {
/* Direct compacted-area: Address < 0x80 */
eeprom_write_dword((uint32_t*)0, 0xdeadbeef); // Aligned
eeprom_write_dword((uint32_t*)9, 0x12345678); // Unaligned
/* Direct compacted-area: Address >= 0x80 */
eeprom_write_dword((uint32_t*)200, 0xfacef00d);
eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xba5eba11); // Aligned
eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0xcafed00d); // Unaligned
/* Check direct values */
EEPROM_Init();
EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);
EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x12345678);
EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 0xfacef00d);
EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xba5eba11); // Aligned
EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0xcafed00d); // Unaligned
/* Write Log byte encoded */
eeprom_write_dword((uint32_t*)0, 0xdecafbad);
eeprom_write_dword((uint32_t*)9, 0x87654321);
/* Write Log word encoded */
eeprom_write_dword((uint32_t*)200, 1);
/* Write Log word value aligned */
eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xdeadc0de); // Aligned
eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0x6789abcd); // Unaligned
/* Check log values */
EEPROM_Init();
EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdecafbad);
EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x87654321);
EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 1);
EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xdeadc0de); // Aligned
EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0x6789abcd); // Unaligned
}
TEST_F(EepromStm32Test, TestBlockRoundTrip) {
char src0[] = "0123456789abcdef";
void* src1 = (void*)&src0[1];
/* Various alignments of src & dst, Address < 0x80 */
eeprom_write_block(src0, (void*)0, sizeof(src0));
eeprom_write_block(src0, (void*)21, sizeof(src0));
eeprom_write_block(src1, (void*)40, sizeof(src0) - 1);
eeprom_write_block(src1, (void*)61, sizeof(src0) - 1);
/* Various alignments of src & dst, Address >= 0x80 */
eeprom_write_block(src0, (void*)140, sizeof(src0));
eeprom_write_block(src0, (void*)161, sizeof(src0));
eeprom_write_block(src1, (void*)180, sizeof(src0) - 1);
eeprom_write_block(src1, (void*)201, sizeof(src0) - 1);
/* Check values */
EEPROM_Init();
char dstBuf[256] = {0};
char* dst0a = (char*)dstBuf;
char* dst0b = (char*)&dstBuf[20];
char* dst1a = (char*)&dstBuf[41];
char* dst1b = (char*)&dstBuf[61];
char* dst0c = (char*)&dstBuf[80];
char* dst0d = (char*)&dstBuf[100];
char* dst1c = (char*)&dstBuf[121];
char* dst1d = (char*)&dstBuf[141];
eeprom_read_block((void*)dst0a, (void*)0, sizeof(src0));
eeprom_read_block((void*)dst0b, (void*)21, sizeof(src0));
eeprom_read_block((void*)dst1a, (void*)40, sizeof(src0) - 1);
eeprom_read_block((void*)dst1b, (void*)61, sizeof(src0) - 1);
eeprom_read_block((void*)dst0c, (void*)140, sizeof(src0));
eeprom_read_block((void*)dst0d, (void*)161, sizeof(src0));
eeprom_read_block((void*)dst1c, (void*)180, sizeof(src0) - 1);
eeprom_read_block((void*)dst1d, (void*)201, sizeof(src0) - 1);
EXPECT_EQ(strcmp((char*)src0, dst0a), 0);
EXPECT_EQ(strcmp((char*)src0, dst0b), 0);
EXPECT_EQ(strcmp((char*)src0, dst0c), 0);
EXPECT_EQ(strcmp((char*)src0, dst0d), 0);
EXPECT_EQ(strcmp((char*)src1, dst1a), 0);
EXPECT_EQ(strcmp((char*)src1, dst1b), 0);
EXPECT_EQ(strcmp((char*)src1, dst1c), 0);
EXPECT_EQ(strcmp((char*)src1, dst1d), 0);
}
TEST_F(EepromStm32Test, TestCompaction) {
/* Direct writes */
eeprom_write_dword((uint32_t*)0, 0xdeadbeef);
eeprom_write_byte((uint8_t*)4, 0x3c);
eeprom_write_word((uint16_t*)6, 0xd00d);
eeprom_write_dword((uint32_t*)150, 0xcafef00d);
eeprom_write_dword((uint32_t*)200, 0x12345678);
/* Fill write log entries */
uint32_t i;
uint32_t val = 0xd8453c6b;
for (i = 0; i < (LOG_SIZE / (sizeof(uint32_t) * 2)); i++) {
val ^= 0x593ca5b3;
val += i;
eeprom_write_dword((uint32_t*)200, val);
}
/* Check values pre-compaction */
EEPROM_Init();
EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);
EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x3c);
EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d);
EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d);
EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val);
EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF);
EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF);
/* Run compaction */
eeprom_write_byte((uint8_t*)4, 0x1f);
EEPROM_Init();
EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);
EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x1f);
EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d);
EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d);
EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF);
EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF);
}

View file

@ -0,0 +1,49 @@
/* Copyright 2021 by Don Kjer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdbool.h>
#include "flash_stm32.h"
uint8_t FlashBuf[MOCK_FLASH_SIZE] = {0};
static bool flash_locked = true;
FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
if (flash_locked) return FLASH_ERROR_WRP;
Page_Address -= (uintptr_t)FlashBuf;
Page_Address -= (Page_Address % FEE_PAGE_SIZE);
if (Page_Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS;
memset(&FlashBuf[Page_Address], '\xff', FEE_PAGE_SIZE);
return FLASH_COMPLETE;
}
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
if (flash_locked) return FLASH_ERROR_WRP;
Address -= (uintptr_t)FlashBuf;
if (Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS;
uint16_t oldData = *(uint16_t*)&FlashBuf[Address];
if (oldData == 0xFFFF || Data == 0) {
*(uint16_t*)&FlashBuf[Address] = Data;
return FLASH_COMPLETE;
} else {
return FLASH_ERROR_PG;
}
}
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { return FLASH_COMPLETE; }
void FLASH_Unlock(void) { flash_locked = false; }
void FLASH_Lock(void) { flash_locked = true; }

View file

@ -0,0 +1,18 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Just here to please eeprom tests

View file

@ -0,0 +1,21 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform_deps.h"
void platform_setup(void) {
// do nothing
}

View file

@ -0,0 +1,23 @@
eeprom_stm32_DEFS := -DFLASH_STM32_MOCKED -DNO_PRINT -DFEE_FLASH_BASE=FlashBuf
eeprom_stm32_tiny_DEFS := $(eeprom_stm32_DEFS) \
-DFEE_MCU_FLASH_SIZE=1 \
-DMOCK_FLASH_SIZE=1024 \
-DFEE_PAGE_SIZE=512 \
-DFEE_PAGE_COUNT=1
eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) \
-DFEE_MCU_FLASH_SIZE=64 \
-DMOCK_FLASH_SIZE=65536 \
-DFEE_PAGE_SIZE=2048 \
-DFEE_PAGE_COUNT=16
eeprom_stm32_INC := \
$(TMK_PATH)/common/chibios/
eeprom_stm32_tiny_INC := $(eeprom_stm32_INC)
eeprom_stm32_large_INC := $(eeprom_stm32_INC)
eeprom_stm32_SRC := \
$(TMK_PATH)/common/test/eeprom_stm32_tests.cpp \
$(TMK_PATH)/common/test/flash_stm32_mock.c \
$(TMK_PATH)/common/chibios/eeprom_stm32.c
eeprom_stm32_tiny_SRC := $(eeprom_stm32_SRC)
eeprom_stm32_large_SRC := $(eeprom_stm32_SRC)

View file

@ -0,0 +1 @@
TEST_LIST += eeprom_stm32_tiny eeprom_stm32_large