Merge remote-tracking branch 'plugin/EEPROM-Settings/f/monorepo' into f/monorepo

pull/365/head
Gergely Nagy 6 years ago
commit d8c2fc0022
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -0,0 +1,168 @@
# Kaleidoscope-EEPROM-Settings
To be able to reliably store persistent configuration in `EEPROM`, we need to be
able to split up the available space for plugins to use. We also want to make
sure that we notice when the `EEPROM` contents and the firmware are out of sync.
This plugin provides the tools to do that.
It does not guard against errors, it merely provides the means to discover them,
and let the firmware Sketch handle the case in whatever way it finds reasonable.
It's a building block, and not much else. All Kaleidoscope plugins that need to
store data in `EEPROM` are encouraged to make use of this library.
## Using the plugin
There are a few steps one needs to take to use the plugin: we must first
register it, then either let other plugins request slices of `EEPROM`, or do so
ourselves. And finally, seal it, to signal that we are done setting up. At that
point, we can verify whether the contents of the `EEPROM` agree with our
firmware.
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-EEPROM-Settings.h>
static uint16_t settingsBase;
static struct {
bool someSettingFlag;
} testSettings;
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings, /* Other plugins that use EEPROM... */);
void setup () {
Kaleidoscope.setup();
settingsBase = EEPROMSettings.requestSlice(sizeof(testSettings));
EEPROMSettings.seal();
if (!EEPROMSettings.isValid()) {
// Handle the case where the settings are out of sync...
// Flash LEDs, for example.
return;
}
EEPROM.get(settingsBase, testSettings);
}
```
## Plugin methods
The plugin provides the `EEPROMSettings` object, which has the following methods:
### `requestSlice(size)`
> Requests a slice of the `EEPROM`, and returns the starting address (or 0 on
> error, including when the request arrived after sealing the layout).
>
> Should only be called **before** calling `seal()`.
### `default_layer([id])`
> Sets (or returns, if called without an ID) the default layer. When the
> keyboard boots up, it will automatically switch to the configured layer - if
> any.
>
> This is the Focus counterpart of the `default_layer()` method documented
> above.
### `seal()`
> Seal the `EEPROM` layout, so no new slices can be requested. The CRC checksum
> is considered final at this time, and the `isValid()`, `crc()`, `used()` and
> `version()` methods can be used from this point onwards.
>
> If not called explicitly, the layout will be sealed automatically after
> `setup()` in the sketch finished.
### `update()`
> Updates the `EEPROM` header with the current status quo, including the version
> and the CRC checksum.
>
> This should be called when upgrading from one version to another, or when
> fixing up an out-of-sync case.
### `isValid()`
> Returns whether the `EEPROM` header is valid, that is, if it has the expected
> CRC checksum.
>
> Should only be called after calling `seal()`.
### `invalidate()`
> Invalidates the `EEPROM` header. Use when the version does not match what the
> firmware would expect. This signals to other plugins that the contents of
> `EEPROM` should not be trusted.
### `version([newVersion])`
> Sets or returns the version of the `EEPROM` layout. This is purely for use by
> the firmware, so it can attempt to upgrade the contents, if need be, or alert
> the user in there's a mismatch. Plugins do not use this property.
>
> Should only be called after calling `seal()`.
### `crc()`
> Returns the CRC checksum of the layout. Should only be used after calling
> `seal()`.
### `used()`
> Returns the amount of space requested so far.
>
> Should only be used after calling `seal()`.
## Focus commands
The plugin provides two - optional - [Focus][FocusSerial] command plugins:
`FocusSettingsCommand` and `FocusEEPROMCommand`. These must be explicitly added
to `KALEIDOSCOPE_INIT_PLUGINS` if one wishes to use them. They provide the
following commands:
[FocusSerial]: FocusSerial.md
### `settings.defaultLayer`
> Sets or returns (if called without arguments) the ID of the default layer. If
> set, the keyboard will automatically switch to the given layer when connected.
> Setting it to `255` disables the automatic switching.
### `settings.crc`
> Returns the actual, and the expected checksum of the settings.
### `settings.valid?`
> Returns either `true` or `false`, depending on whether the sealed settings are
> to be considered valid or not.
### `settings.version`
> Returns the (user-set) version of the settings.
### `eeprom.contents`
> Without argument, displays the full contents of the `EEPROM`, including the
> settings header.
>
> With arguments, the command updates as much of the `EEPROM` as arguments are
> provided. It will discard any unnecessary arguments.
### `eeprom.free`
> Returns the amount of free bytes in `EEPROM`.
## Dependencies
* [Kaleidoscope-FocusSerial][FocusSerial]
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
started with the plugin.
[plugin:example]: ../../examples/EEPROM-Settings/EEPROM-Settings.ino

@ -0,0 +1,59 @@
/* -*- mode: c++ -*-
* Kaleidoscope-EEPROM-Settings -- Basic EEPROM settings plugin for Kaleidoscope.
* Copyright (C) 2017, 2018 Keyboard.io, Inc
*
* 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, version 3.
*
* 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 <Kaleidoscope.h>
#include <Kaleidoscope-EEPROM-Settings.h>
// *INDENT-OFF*
const Key keymaps[][ROWS][COLS] PROGMEM = {
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
Key_skip,
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
};
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings);
void setup() {
Serial.begin(9600);
Kaleidoscope.setup();
while (!Serial) {
}
Serial.println(EEPROMSettings.isValid() ? F("valid EEPROM settings") : F("invalid EEPROM settings"));
Serial.println(EEPROMSettings.crc(), HEX);
Serial.println(EEPROMSettings.version());
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,20 @@
/* -*- mode: c++ -*-
* Kaleidoscope-EEPROM-Settings -- Basic EEPROM settings plugin for Kaleidoscope.
* Copyright (C) 2017 Keyboard.io, Inc
*
* 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, version 3.
*
* 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 <kaleidoscope/plugin/EEPROM-Settings.h>

@ -0,0 +1,217 @@
/* -*- mode: c++ -*-
* Kaleidoscope-EEPROM-Settings -- Basic EEPROM settings plugin for Kaleidoscope.
* Copyright (C) 2017, 2018 Keyboard.io, Inc
*
* 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, version 3.
*
* 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 <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-FocusSerial.h>
#include "kaleidoscope/plugin/EEPROM-Settings/crc.h"
namespace kaleidoscope {
namespace plugin {
struct EEPROMSettings::settings EEPROMSettings::settings_;
bool EEPROMSettings::is_valid_;
bool EEPROMSettings::sealed_;
uint16_t EEPROMSettings::next_start_ = sizeof(EEPROMSettings::settings);
EventHandlerResult EEPROMSettings::onSetup() {
EEPROM.get(0, settings_);
return EventHandlerResult::OK;
}
EventHandlerResult EEPROMSettings::beforeEachCycle() {
if (!sealed_)
seal();
return EventHandlerResult::OK;
}
bool EEPROMSettings::isValid(void) {
return is_valid_;
}
uint16_t EEPROMSettings::crc(void) {
if (sealed_)
return settings_.crc;
return 0;
}
uint8_t EEPROMSettings::default_layer(uint8_t layer) {
if (layer == 0xff)
return settings_.default_layer;
settings_.default_layer = layer;
update();
return settings_.default_layer;
}
void EEPROMSettings::seal(void) {
sealed_ = true;
CRC.finalize();
/* If we have a default layer set, switch to it. As 0xff is the default EEPROM
* value, treat it as not having a default layer set. */
if (settings_.default_layer != 0xff)
Layer.move(settings_.default_layer);
/* Until we set a version, consider the EEPROM contents flexible, and always
* update the CRC. This will always result in the settings being considered
* valid. */
if (settings_.version == 0xff) {
return update();
}
if (settings_.crc != CRC.crc)
is_valid_ = false;
}
uint16_t EEPROMSettings::requestSlice(uint16_t size) {
if (sealed_)
return 0;
uint16_t start = next_start_;
next_start_ += size;
CRC.update((const void *)&size, sizeof(size));
return start;
}
void EEPROMSettings::invalidate(void) {
is_valid_ = false;
}
uint16_t EEPROMSettings::used(void) {
return next_start_;
}
void EEPROMSettings::update(void) {
settings_.crc = CRC.crc;
EEPROM.put(0, settings_);
is_valid_ = true;
}
uint8_t EEPROMSettings::version(void) {
return settings_.version;
}
void EEPROMSettings::version(uint8_t ver) {
settings_.version = ver;
update();
}
/** Focus **/
EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) {
enum {
DEFAULT_LAYER,
IS_VALID,
GET_VERSION,
CRC,
} sub_command;
if (::Focus.handleHelp(command, PSTR("settings.defaultLayer\nsettings.valid?\nsettings.version\nsettings.crc")))
return EventHandlerResult::OK;
if (strncmp_P(command, PSTR("settings."), 9) != 0)
return EventHandlerResult::OK;
if (strcmp_P(command + 9, PSTR("defaultLayer")) == 0)
sub_command = DEFAULT_LAYER;
else if (strcmp_P(command + 9, PSTR("valid?")) == 0)
sub_command = IS_VALID;
else if (strcmp_P(command + 9, PSTR("version")) == 0)
sub_command = GET_VERSION;
else if (strcmp_P(command + 9, PSTR("crc")) == 0)
sub_command = CRC;
else
return EventHandlerResult::OK;
switch (sub_command) {
case DEFAULT_LAYER: {
if (Serial.peek() == '\n') {
Serial.println(::EEPROMSettings.default_layer());
} else {
::EEPROMSettings.default_layer(Serial.parseInt());
}
break;
}
case IS_VALID:
::Focus.printBool(::EEPROMSettings.isValid());
Serial.println();
break;
case GET_VERSION:
Serial.println(::EEPROMSettings.version());
break;
case CRC:
Serial.print(::CRC.crc, HEX);
Serial.print(F("/"));
Serial.println(::EEPROMSettings.crc(), HEX);
break;
}
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult FocusEEPROMCommand::onFocusEvent(const char *command) {
enum {
CONTENTS,
FREE,
} sub_command;
if (::Focus.handleHelp(command, PSTR("eeprom.contents\neeprom.free")))
return EventHandlerResult::OK;
if (strcmp_P(command, PSTR("eeprom.contents")) == 0)
sub_command = CONTENTS;
else if (strcmp_P(command, PSTR("eeprom.free")) == 0)
sub_command = FREE;
else
return EventHandlerResult::OK;
switch (sub_command) {
case CONTENTS: {
if (Serial.peek() == '\n') {
for (uint16_t i = 0; i < EEPROM.length(); i++) {
uint8_t d = EEPROM[i];
::Focus.printNumber(d);
::Focus.printSpace();
}
Serial.println();
} else {
for (uint16_t i = 0; i < EEPROM.length() && Serial.peek() != '\n'; i++) {
uint8_t d = Serial.parseInt();
EEPROM.update(i, d);
}
}
break;
}
case FREE:
Serial.println(EEPROM.length() - ::EEPROMSettings.used());
break;
}
return EventHandlerResult::EVENT_CONSUMED;
}
}
}
kaleidoscope::plugin::EEPROMSettings EEPROMSettings;
kaleidoscope::plugin::FocusSettingsCommand FocusSettingsCommand;
kaleidoscope::plugin::FocusEEPROMCommand FocusEEPROMCommand;

@ -0,0 +1,75 @@
/* -*- mode: c++ -*-
* Kaleidoscope-EEPROM-Settings -- Basic EEPROM settings plugin for Kaleidoscope.
* Copyright (C) 2017, 2018 Keyboard.io, Inc
*
* 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, version 3.
*
* 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 <Kaleidoscope.h>
#include <EEPROM.h>
namespace kaleidoscope {
namespace plugin {
class EEPROMSettings : public kaleidoscope::Plugin {
public:
EEPROMSettings(void) {}
EventHandlerResult onSetup();
EventHandlerResult beforeEachCycle();
static void update(void);
static bool isValid(void);
static void invalidate(void);
static uint8_t version(void);
static void version(uint8_t ver);
static uint16_t requestSlice(uint16_t size);
static void seal(void);
static uint16_t crc(void);
static uint16_t used(void);
static uint8_t default_layer(uint8_t layer = 0xff);
private:
static uint16_t next_start_;
static bool is_valid_;
static bool sealed_;
static struct settings {
uint8_t default_layer;
uint8_t version;
uint16_t crc;
} settings_;
};
class FocusSettingsCommand : public kaleidoscope::Plugin {
public:
FocusSettingsCommand() {}
EventHandlerResult onFocusEvent(const char *command);
};
class FocusEEPROMCommand : public kaleidoscope::Plugin {
public:
FocusEEPROMCommand() {}
EventHandlerResult onFocusEvent(const char *command);
};
}
}
extern kaleidoscope::plugin::EEPROMSettings EEPROMSettings;
extern kaleidoscope::plugin::FocusSettingsCommand FocusSettingsCommand;
extern kaleidoscope::plugin::FocusEEPROMCommand FocusEEPROMCommand;

@ -0,0 +1,69 @@
/* -*- mode: c++ -*-
* Kaleidoscope-EEPROM-Settings -- Basic EEPROM settings plugin for Kaleidoscope.
* Copyright (C) 2017 Keyboard.io, Inc
*
* 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, version 3.
*
* 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/>.
*
* Originally generated by pycrc v0.9, https://pycrc.org
*
* using the configuration:
* Width = 16
* Poly = 0x8005
* Xor_In = 0x0000
* ReflectIn = True
* Xor_Out = 0x0000
* ReflectOut = True
* Algorithm = bit-by-bit-fast
*/
#include "crc.h"
void
CRC_::reflect(uint8_t len) {
uint8_t i;
uint16_t newCRC;
newCRC = crc & 0x01;
for (i = 1; i < len; i++) {
crc >>= 1;
newCRC = (newCRC << 1) | (crc & 0x01);
}
crc = newCRC;
}
void
CRC_::update(const void *data, uint8_t len) {
const uint8_t *d = (const uint8_t *)data;
uint8_t i;
bool bit;
uint8_t c;
while (len--) {
c = *d++;
for (i = 0x01; i & 0xff; i <<= 1) {
bit = crc & 0x8000;
if (c & i) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x8005;
}
}
crc &= 0xffff;
}
crc &= 0xffff;
}
CRC_ CRC;

@ -0,0 +1,46 @@
/* -*- mode: c++ -*-
* Kaleidoscope-EEPROM-Settings -- Basic EEPROM settings plugin for Kaleidoscope.
* Copyright (C) 2017 Keyboard.io, Inc
*
* 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, version 3.
*
* 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/>.
*
* Originally generated by pycrc v0.9, https://pycrc.org
*
* using the configuration:
* Width = 16
* Poly = 0x8005
* Xor_In = 0x0000
* ReflectIn = True
* Xor_Out = 0x0000
* ReflectOut = True
* Algorithm = bit-by-bit-fast
*/
#pragma once
#include <Arduino.h>
class CRC_ {
public:
uint16_t crc = 0;
CRC_(void) {};
void update(const void *data, uint8_t len);
void finalize(void) {
reflect(16);
}
void reflect(uint8_t len);
};
extern CRC_ CRC;
Loading…
Cancel
Save