ps2keyboardfilter/ps2keyboardfilter.ino

311 lines
10 KiB
C++

#include <EEPROM.h>
#include "ps2_Keyboard.h"
#include "ps2_NullDiagnostics.h"
#include "ps2_UsbTranslator.h"
#include "HID-Project.h"
#define DEBUG 1
// with serial.print...
#ifdef DEBUG
#define SP(a) Serial.print(a);
#define SPLN(a) Serial.println(a);
#else
#define SP(a) ;
#define SPLN(a) ;
#endif
static ps2::NullDiagnostics nd;
static ps2::UsbTranslator<ps2::NullDiagnostics> keyMapping(nd);
static ps2::Keyboard<4, 2, 16, ps2::NullDiagnostics> ps2Keyboard(nd);
static ps2::UsbKeyboardLeds ledValueLastSentToPs2 = ps2::UsbKeyboardLeds::none;
uint8_t activeFilter = 255;
class Filter {
private:
uint8_t m_keys[8] = {0};
uint8_t m_number;
uint8_t m_numKeys;
public:
Filter(uint8_t number) {
m_number = number;
m_numKeys = 0;
}
bool loadFromEEPROM() {
uint8_t offset = m_number * 10; // 10 bytes per filter config
m_numKeys = EEPROM.read(offset); // number of keys
if (m_numKeys > 8 || m_numKeys == 0) {
m_numKeys = 0;
return false;
}
uint8_t chksum = 0;
for (uint8_t i = 0; i < m_numKeys; ++i) {
uint8_t key = EEPROM.read(offset + i + 1);
m_keys[i] = key;
chksum += key;
}
// validate checksum
if (chksum != EEPROM.read(offset + m_numKeys + 1)) {
SP(m_number);
SPLN(F(" checksum failed"));
m_numKeys = 0;
return false;
}
SP(m_number);
SP(F(" loaded "));
SP(m_numKeys);
SPLN(F(" keys"));
return true;
}
void clear() {
for (uint8_t i = 0; i < m_numKeys; ++i) {
m_keys[i] = 0;
}
m_numKeys = 0;
}
void addKey(uint8_t key) {
for (uint8_t i = 0; i < m_numKeys; ++i) {
if (m_keys[i] == key) {
return;
}
}
if (m_numKeys < 7) {
m_keys[m_numKeys++] = key;
}
}
bool writeToEEPROM() {
uint8_t offset = m_number * 10; // 10 bytes per filter config
EEPROM.write(offset, m_numKeys); // number of keys
uint8_t chksum = 0;
for (uint8_t i = 0; i < m_numKeys; ++i) {
EEPROM.write(offset + i + 1, m_keys[i]);
chksum += m_keys[i];
}
// save checksum
EEPROM.write(offset + m_numKeys + 1, chksum);
SP(m_number);
SP(F(" saved "));
SP(m_numKeys);
SPLN(F(" keys"));
return true;
}
bool isFiltered(uint8_t key) {
for (uint8_t i = 0; i < m_numKeys; ++i) {
if (m_keys[i] == key) {
return true;
}
}
return false;
}
};
#define NUM_FILTERS 12
Filter* filters[NUM_FILTERS] = {0};
enum States {
NONE = 0,
PROGRAMMING_SHIFT,
PROGRAMMING_SCROLL,
PROGRAMMING_SELECT,
PROGRAMMING_ADD,
PROGRAMMING_DONE,
APPLY_SHIFT,
APPLY_SCROLL,
APPLY_SELECT,
APPLY_DONE
} currentState = NONE;
uint32_t pressedTime = 0;
uint8_t programFilter = 255;
// the setup function runs once when you press reset or power the board
void setup() {
ps2Keyboard.begin();
BootKeyboard.begin();
#ifdef DEBUG
delay(2000);
Serial.begin(115200);
SPLN(F("ready"));
#endif
for (uint8_t i = 0; i < NUM_FILTERS; ++i) {
filters[i] = new Filter(i);
filters[i]->loadFromEEPROM();
}
filters[11]->clear();
filters[11]->addKey(KEY_E);
activeFilter = 11;
}
#define BLINK_OFF 0
#define BLINK_VERY_SLOW 2000
#define BLINK_SLOW 1000
#define BLINK_MEDIUM 500
#define BLINK_FAST 250
uint16_t blinkTimer = 0;
void loop() {
uint32_t now = millis();
ps2::UsbKeyboardLeds newLedState = (ps2::UsbKeyboardLeds)BootKeyboard.getLeds();
if (blinkTimer != BLINK_OFF) {
uint8_t diff = (now - pressedTime) / blinkTimer;
if (diff & 1) newLedState = (ps2::UsbKeyboardLeds)((uint8_t)newLedState ^ (uint8_t)ps2::UsbKeyboardLeds::scrollLock);
}
if (newLedState != ledValueLastSentToPs2)
{
ps2Keyboard.sendLedStatus(keyMapping.translateLeds(newLedState));
ledValueLastSentToPs2 = newLedState;
}
ps2::KeyboardOutput scanCode = ps2Keyboard.readScanCode();
if (scanCode != ps2::KeyboardOutput::none && scanCode != ps2::KeyboardOutput::garbled) {
ps2::UsbKeyAction action = keyMapping.translatePs2Keycode(scanCode);
KeyboardKeycode hidCode = (KeyboardKeycode)action.hidCode;
switch(currentState) {
case NONE:
if (action.gesture == ps2::UsbKeyAction::KeyDown) {
if (hidCode == KeyboardKeycode::KEY_LEFT_SHIFT) {
currentState = PROGRAMMING_SHIFT;
SPLN(F("to PROGRAMMING_SHIFT"));
} else if (hidCode == KeyboardKeycode::KEY_RIGHT_SHIFT) {
currentState = APPLY_SHIFT;
SPLN(F("to APPLY_SHIFT"));
}
}
break;
case PROGRAMMING_SHIFT:
if (action.gesture == ps2::UsbKeyAction::KeyDown && hidCode == KeyboardKeycode::KEY_SCROLL_LOCK) {
currentState = PROGRAMMING_SCROLL;
blinkTimer = BLINK_SLOW;
pressedTime = now;
SPLN(F("to PROGRAMMING_SCROLL"));
} else if (action.gesture != ps2::UsbKeyAction::KeyDown || hidCode != KeyboardKeycode::KEY_LEFT_SHIFT) {
currentState = NONE;
SPLN(F("PROGRAMMING_SHIFT_to NONE"));
}
break;
case PROGRAMMING_SCROLL:
// to work around a library quirk insted of
// if (action.gesture == ps2::UsbKeyAction::KeyUp && hidCode == KeyboardKeycode::KEY_SCROLL_LOCK && (now - pressedTime) > 5000) {
if (action.gesture == ps2::UsbKeyAction::None && hidCode == 0 && (now - pressedTime) > 5000) {
blinkTimer = BLINK_MEDIUM;
currentState = PROGRAMMING_SELECT;
pressedTime = now;
SPLN(F("to PROGRAMMING_SELECT"));
} else if (action.gesture == ps2::UsbKeyAction::KeyUp || (hidCode != KEY_LEFT_SHIFT && hidCode != KEY_SCROLL_LOCK)) {
currentState = NONE;
blinkTimer = BLINK_OFF;
SPLN(F("PROGRAMMING_SCROLL to NONE"));
}
break;
case PROGRAMMING_SELECT:
if (action.gesture == ps2::UsbKeyAction::KeyDown) {
if(hidCode >= KeyboardKeycode::KEY_F1 && hidCode <= KeyboardKeycode::KEY_F12) {
programFilter = hidCode - KeyboardKeycode::KEY_F1;
filters[programFilter]->clear();
blinkTimer = BLINK_FAST;
SP(F("set filter to "));
SPLN(programFilter);
currentState = PROGRAMMING_ADD;
SPLN(F("to PROGRAMMING_ADD"));
return; // do not send
} else {
currentState = NONE;
programFilter = 255;
blinkTimer = BLINK_OFF;
SP(F("PROGRAMMING_SELECT != F to NONE"));
SPLN(hidCode);
}
return;
}
break;
case PROGRAMMING_ADD:
if (action.gesture == ps2::UsbKeyAction::KeyDown && hidCode == KeyboardKeycode::KEY_SCROLL_LOCK) {
SPLN(F("to PROGRAMMING_DONE"));
currentState = PROGRAMMING_DONE;
}
if (action.gesture == ps2::UsbKeyAction::KeyUp && hidCode != (KeyboardKeycode::KEY_F1 + programFilter)) {
SP(F("add key "));
SPLN(hidCode);
filters[programFilter]->addKey(hidCode);
}
return;
case PROGRAMMING_DONE:
if (action.gesture == ps2::UsbKeyAction::KeyUp && hidCode == KeyboardKeycode::KEY_SCROLL_LOCK) {
SPLN(F("to NONE"));
filters[programFilter]->writeToEEPROM();
currentState = NONE;
blinkTimer = BLINK_OFF;
}
return;
case APPLY_SHIFT:
if (action.gesture == ps2::UsbKeyAction::KeyDown && hidCode == KeyboardKeycode::KEY_SCROLL_LOCK) {
currentState = APPLY_SCROLL;
blinkTimer = BLINK_SLOW;
pressedTime = now;
SPLN(F("to APPLY_SCROLL"));
} else if (action.gesture != ps2::UsbKeyAction::KeyDown || hidCode != KeyboardKeycode::KEY_RIGHT_SHIFT) {
currentState = NONE;
SPLN(F("APPLY_SHIFT_to NONE"));
}
break;
case APPLY_SCROLL:
// to work around a library quirk insted of
// if (action.gesture == ps2::UsbKeyAction::KeyUp && hidCode == KeyboardKeycode::KEY_SCROLL_LOCK && (now - pressedTime) > 1000) {
if (action.gesture == ps2::UsbKeyAction::None && hidCode == 0 && (now - pressedTime) > 1000) {
blinkTimer = BLINK_MEDIUM;
currentState = APPLY_SELECT;
pressedTime = now;
SPLN(F("to APPLY_SELECT"));
} else if (action.gesture == ps2::UsbKeyAction::KeyUp || (hidCode != KEY_RIGHT_SHIFT && hidCode != KEY_SCROLL_LOCK)) {
currentState = NONE;
blinkTimer = BLINK_OFF;
SPLN(F("APPLY_SCROLL to NONE"));
}
break;
case APPLY_SELECT:
if (action.gesture == ps2::UsbKeyAction::KeyDown) {
if(hidCode == KeyboardKeycode::KEY_ESC || (hidCode >= KeyboardKeycode::KEY_F1 && hidCode <= KeyboardKeycode::KEY_F12)) {
activeFilter = hidCode == KeyboardKeycode::KEY_ESC ? 255 : hidCode - KeyboardKeycode::KEY_F1;
SP(F("set filter to "));
SPLN(activeFilter);
currentState = APPLY_DONE;
SPLN(F("to APPLY_DONE"));
return; // do not send
} else {
currentState = NONE;
blinkTimer = BLINK_OFF;
SP(F("APPLY_SELECT != F to NONE"));
SPLN(hidCode);
}
return;
}
break;
case APPLY_DONE:
if (action.gesture == ps2::UsbKeyAction::KeyUp) {
if(hidCode == KeyboardKeycode::KEY_ESC) {
blinkTimer = BLINK_OFF;
currentState = NONE;
SPLN(F("to NONE"));
}
if (hidCode >= KeyboardKeycode::KEY_F1 && hidCode <= KeyboardKeycode::KEY_F12) {
blinkTimer = BLINK_VERY_SLOW;
currentState = NONE;
SPLN(F("to NONE"));
}
}
return;
}
if (activeFilter != 255) {
if (filters[activeFilter]->isFiltered(hidCode)) {
action.gesture = ps2::UsbKeyAction::None;
}
}
switch (action.gesture) {
case ps2::UsbKeyAction::KeyDown:
BootKeyboard.press(hidCode);
break;
case ps2::UsbKeyAction::KeyUp:
BootKeyboard.release(hidCode);
break;
}
}
}