464 lines
11 KiB
C
464 lines
11 KiB
C
#include "keyboard.h"
|
|
#include "atkbd.pio.h"
|
|
#include "chargen.h"
|
|
#include "pinout.h"
|
|
|
|
#include "hardware/clocks.h"
|
|
#include "pico.h"
|
|
#include "pico/stdlib.h"
|
|
|
|
#define EOF (-1)
|
|
#define DEBUG(...) ((void)0)
|
|
|
|
static int pending_led_value;
|
|
static bool pending_led_flag;
|
|
|
|
static PIO kbd_pio;
|
|
static int kbd_sm;
|
|
|
|
static int ll_kbd_read_timeout(int timeout_us) {
|
|
uint64_t deadline = time_us_64() + timeout_us;
|
|
while (pio_sm_is_rx_fifo_empty(kbd_pio, kbd_sm)) {
|
|
if (time_us_64() > deadline) {
|
|
return EOF;
|
|
}
|
|
}
|
|
return pio_sm_get_blocking(kbd_pio, kbd_sm);
|
|
}
|
|
|
|
static int kbd_read_timeout(int timeout_us) {
|
|
int r = ll_kbd_read_timeout(timeout_us);
|
|
if (r == EOF) {
|
|
return EOF;
|
|
}
|
|
r = (r >> 22) & 0xff;
|
|
return r; // todo: check parity, start & end bits!
|
|
}
|
|
|
|
static int parity(int x) {
|
|
x ^= (x >> 4);
|
|
x ^= (x >> 2);
|
|
x ^= (x >> 1);
|
|
return (x & 1) ^ 1;
|
|
}
|
|
static void kbd_write_blocking(int value) {
|
|
value = value | (parity(value) << 8);
|
|
pio_sm_put_blocking(kbd_pio, kbd_sm, ~value);
|
|
}
|
|
|
|
static bool expect(int expected, const char *msg) {
|
|
int value = kbd_read_timeout(10000000);
|
|
if (expected != value) {
|
|
scrnprintf("%s: Expected 0x%02x, got 0x%02x\r\n", msg, expected, value);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool write_expect_fa(int value, const char *msg) {
|
|
pio_sm_clear_fifos(kbd_pio, kbd_sm);
|
|
kbd_write_blocking(value);
|
|
return expect(0xfa, msg);
|
|
}
|
|
bool keyboard_setup(PIO pio) {
|
|
DEBUG("pre-waiting for keyboard to boot\r");
|
|
sleep_ms(1600);
|
|
|
|
gpio_init(KEYBOARD_DATA_PIN);
|
|
gpio_init(KEYBOARD_DATA_PIN + 1);
|
|
|
|
gpio_pull_up(KEYBOARD_DATA_PIN);
|
|
gpio_pull_up(KEYBOARD_DATA_PIN + 1);
|
|
|
|
#if 0
|
|
int i = 0, j = 1;
|
|
while (!(gpio_get(KEYBOARD_DATA_PIN) && gpio_get(KEYBOARD_DATA_PIN + 1))) {
|
|
if (i++ == j) {
|
|
DEBUG("Waiting for keyboard to boot... %d ms so far\r", i);
|
|
j *= 10;
|
|
}
|
|
sleep_ms(1);
|
|
}
|
|
|
|
sleep_ms(100);
|
|
#endif
|
|
|
|
kbd_pio = pio;
|
|
uint offset = pio_add_program(pio, &atkbd_program);
|
|
kbd_sm = pio_claim_unused_sm(pio, true);
|
|
atkbd_program_init(pio, kbd_sm, offset, KEYBOARD_DATA_PIN);
|
|
|
|
bool ok = write_expect_fa(0xff, "reset keyboard");
|
|
while (!ok) {
|
|
sleep_ms(10000);
|
|
write_expect_fa(0xff, "reset keyboard");
|
|
}
|
|
if (ok) {
|
|
ok = expect(0xaa, "self-test result");
|
|
}
|
|
if (ok) {
|
|
ok = write_expect_fa(0xf0, "set mode");
|
|
}
|
|
if (ok) {
|
|
ok = write_expect_fa(0x03, "mode 3");
|
|
}
|
|
if (ok) {
|
|
ok = write_expect_fa(0xfa, "all repeat");
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
enum {
|
|
LSHIFT = 1,
|
|
LCTRL = 2,
|
|
LALT = 4,
|
|
RSHIFT = 8,
|
|
RCTRL = 16,
|
|
RALT = 32,
|
|
MOD_CAPS = 64,
|
|
MOD_NUM = 128,
|
|
TOGGLING_MODIFIERS = MOD_CAPS | MOD_NUM
|
|
};
|
|
const char keyboard_modifiers[256] = {
|
|
[0x12] = LSHIFT, [0x59] = RSHIFT, [0x11] = LCTRL, [0x58] = RCTRL,
|
|
[0x19] = LALT, [0x39] = RALT, [0x14] = MOD_CAPS, [0x76] = MOD_NUM,
|
|
};
|
|
|
|
enum SYMBOLS {
|
|
F1,
|
|
F2,
|
|
F3,
|
|
F4,
|
|
F5,
|
|
F6,
|
|
F7,
|
|
F8,
|
|
F9,
|
|
F10,
|
|
F11,
|
|
F12,
|
|
PRTSCR,
|
|
SCRLCK,
|
|
PAUSE,
|
|
INSERT,
|
|
DELETE,
|
|
HOME,
|
|
END,
|
|
PAGEUP,
|
|
PAGEDOWN,
|
|
UPARROW,
|
|
DOWNARROW,
|
|
LEFTARROW,
|
|
RIGHTARROW,
|
|
MAX_SYMBOLS
|
|
};
|
|
|
|
const char *const symtab[MAX_SYMBOLS] = {
|
|
#define ENT(x, y) [x] = y
|
|
ENT(F1, "\eOP"), ENT(F2, "\eOQ"), ENT(F3, "\eOR"),
|
|
ENT(F4, "\eOS"), ENT(F5, "\e[15~"), ENT(F6, "\e[17~"),
|
|
ENT(F7, "\e[18~"), ENT(F8, "\e[19~"), ENT(F9, "\e[20~"),
|
|
ENT(F10, "\e[21~"), ENT(F11, "\e[23~"), ENT(F12, "\e[24~"),
|
|
ENT(PRTSCR, "\ei"), ENT(PAUSE, ""), ENT(INSERT, "\e[2~"),
|
|
ENT(DELETE, "\e[3~"), ENT(UPARROW, "\e[A"), ENT(DOWNARROW, "\e[B"),
|
|
ENT(RIGHTARROW, "\e[C"), ENT(LEFTARROW, "\e[D"), ENT(HOME, "\e[H"),
|
|
ENT(END, "\e[F"), ENT(PAGEUP, "\e[5~"), ENT(PAGEDOWN, "\e[6~"),
|
|
#undef ENT
|
|
};
|
|
|
|
enum KEYPAD_SYMBOLS {
|
|
KP_0,
|
|
KP_1,
|
|
KP_2,
|
|
KP_3,
|
|
KP_4,
|
|
KP_5,
|
|
KP_6,
|
|
KP_7,
|
|
KP_8,
|
|
KP_9,
|
|
KP_DOT,
|
|
MAX_KEYPAD
|
|
};
|
|
|
|
typedef uint32_t keycode;
|
|
#define BIT_NUM (0x1 << 30)
|
|
#define BIT_SYM (0x2 << 30)
|
|
#define BIT_CMD (0x3 << 30)
|
|
#define MASK_CLASS (0x3 << 30)
|
|
#define CLASSIFY(x) (x & MASK_CLASS)
|
|
#define VALUE(x) (x & ~MASK_CLASS)
|
|
#define IS_SYM(x) (CLASSIFY(x) == BIT_SYM)
|
|
#define IS_CMD(x) (CLASSIFY(x) == BIT_CMD)
|
|
#define IS_NUM(x) (CLASSIFY(x) == BIT_NUM)
|
|
#define MAKE_SYM(x) (BIT_SYM | (keycode)(x))
|
|
#define MAKE_CMD(x) (BIT_CMD | (keycode)(x))
|
|
#define MAKE_NUM(x) (BIT_NUM | (keycode)(x))
|
|
#define CHAR2(c, d) (keycode) c | (((keycode)d) << 8)
|
|
#define ALPHA(c) CHAR2(c, c ^ ('a' ^ 'A'))
|
|
|
|
#define LO(x) (x & 0xff)
|
|
#define HI(x) ((x >> 8) & 0xff)
|
|
|
|
const keycode keyboard_codes[256] = {
|
|
[0x08] = '\033',
|
|
[0x07] = MAKE_SYM(F1),
|
|
[0x0f] = MAKE_SYM(F2),
|
|
[0x17] = MAKE_SYM(F3),
|
|
[0x1f] = MAKE_SYM(F4),
|
|
[0x27] = MAKE_SYM(F5),
|
|
[0x2f] = MAKE_SYM(F6),
|
|
[0x37] = MAKE_SYM(F7),
|
|
[0x3f] = MAKE_SYM(F8),
|
|
[0x47] = MAKE_SYM(F9),
|
|
[0x4f] = MAKE_SYM(F10),
|
|
[0x56] = MAKE_SYM(F11),
|
|
[0x5e] = MAKE_SYM(F12),
|
|
[0x57] = MAKE_SYM(PRTSCR),
|
|
[0x5f] = MAKE_SYM(SCRLCK),
|
|
[0x62] = MAKE_SYM(PAUSE),
|
|
[0x67] = MAKE_SYM(INSERT),
|
|
[0x64] = MAKE_SYM(DELETE),
|
|
|
|
[0x0e] = CHAR2('`', '~'),
|
|
[0x16] = CHAR2('1', '!'),
|
|
[0x1e] = CHAR2('2', '@'),
|
|
[0x26] = CHAR2('3', '#'),
|
|
[0x25] = CHAR2('4', '$'),
|
|
[0x2e] = CHAR2('5', '%'),
|
|
[0x36] = CHAR2('6', '^'),
|
|
[0x3d] = CHAR2('7', '&'),
|
|
[0x3e] = CHAR2('8', '*'),
|
|
[0x46] = CHAR2('9', '('),
|
|
[0x45] = CHAR2('0', ')'),
|
|
[0x4e] = CHAR2('-', '_'),
|
|
[0x55] = CHAR2('=', '+'),
|
|
[0x66] = '\010',
|
|
|
|
[0x0d] = '\t',
|
|
[0x15] = ALPHA('q'),
|
|
[0x1d] = ALPHA('w'),
|
|
[0x24] = ALPHA('e'),
|
|
[0x2d] = ALPHA('r'),
|
|
[0x2c] = ALPHA('t'),
|
|
[0x35] = ALPHA('y'),
|
|
[0x3c] = ALPHA('u'),
|
|
[0x43] = ALPHA('i'),
|
|
[0x44] = ALPHA('o'),
|
|
[0x4d] = ALPHA('p'),
|
|
[0x54] = CHAR2('[', '{'),
|
|
[0x5b] = CHAR2(']', '}'),
|
|
[0x5c] = CHAR2('\\', '|'),
|
|
|
|
[0x1c] = ALPHA('a'),
|
|
[0x1b] = ALPHA('s'),
|
|
[0x23] = ALPHA('d'),
|
|
[0x2b] = ALPHA('f'),
|
|
[0x34] = ALPHA('g'),
|
|
[0x33] = ALPHA('h'),
|
|
[0x3b] = ALPHA('j'),
|
|
[0x42] = ALPHA('k'),
|
|
[0x4b] = ALPHA('l'),
|
|
[0x4c] = CHAR2(';', ':'),
|
|
[0x52] = CHAR2('\'', '"'),
|
|
[0x5a] = '\n',
|
|
|
|
[0x1a] = ALPHA('z'),
|
|
[0x22] = ALPHA('x'),
|
|
[0x21] = ALPHA('c'),
|
|
[0x2a] = ALPHA('v'),
|
|
[0x32] = ALPHA('b'),
|
|
[0x31] = ALPHA('n'),
|
|
[0x3a] = ALPHA('m'),
|
|
[0x41] = CHAR2(',', '<'),
|
|
[0x49] = CHAR2('.', '>'),
|
|
[0x4a] = CHAR2('/', '?'),
|
|
[0x63] = MAKE_SYM(UPARROW),
|
|
|
|
[0x29] = ' ',
|
|
[0x61] = MAKE_SYM(LEFTARROW),
|
|
[0x6a] = MAKE_SYM(RIGHTARROW),
|
|
[0x60] = MAKE_SYM(DOWNARROW),
|
|
|
|
[0x6e] = MAKE_SYM(HOME),
|
|
[0x65] = MAKE_SYM(END),
|
|
[0x6f] = MAKE_SYM(PAGEUP),
|
|
[0x6d] = MAKE_SYM(PAGEDOWN),
|
|
|
|
[0x70] = MAKE_NUM(KP_0),
|
|
|
|
[0x69] = MAKE_NUM(KP_1),
|
|
[0x72] = MAKE_NUM(KP_2),
|
|
[0x7a] = MAKE_NUM(KP_3),
|
|
|
|
[0x6b] = MAKE_NUM(KP_4),
|
|
[0x73] = MAKE_NUM(KP_5),
|
|
[0x74] = MAKE_NUM(KP_6),
|
|
|
|
[0x6c] = MAKE_NUM(KP_7),
|
|
[0x75] = MAKE_NUM(KP_8),
|
|
[0x7d] = MAKE_NUM(KP_9),
|
|
|
|
[0x71] = MAKE_NUM(KP_DOT),
|
|
|
|
// on keypad
|
|
[0x77] = '/',
|
|
[0x7e] = '*',
|
|
[0x84] = '-',
|
|
[0x7c] = '+',
|
|
[0x79] = '\n',
|
|
};
|
|
|
|
struct nument {
|
|
uint32_t kc_normal, kc_numlock;
|
|
};
|
|
|
|
const struct nument numtab[MAX_KEYPAD] = {
|
|
#define ENT(x, y, z) [x] = {y, z}
|
|
ENT(KP_0, MAKE_SYM(INSERT), '0'), ENT(KP_1, MAKE_SYM(END), '1'),
|
|
ENT(KP_2, MAKE_SYM(DOWNARROW), '2'), ENT(KP_3, MAKE_SYM(PAGEDOWN), '3'),
|
|
ENT(KP_4, MAKE_SYM(LEFTARROW), '4'), ENT(KP_5, 0, '5'),
|
|
ENT(KP_6, MAKE_SYM(RIGHTARROW), '6'), ENT(KP_7, MAKE_SYM(HOME), '7'),
|
|
ENT(KP_8, MAKE_SYM(UPARROW), '8'), ENT(KP_9, MAKE_SYM(PAGEUP), '9'),
|
|
ENT(KP_DOT, MAKE_SYM(DELETE), '.'),
|
|
#undef ENT
|
|
};
|
|
|
|
bool pending_release;
|
|
int current_modifiers = 0;
|
|
|
|
static void queue_add_data(queue_t *q, int data) {
|
|
(void)queue_try_add(q, &data);
|
|
}
|
|
|
|
static void queue_add_str(queue_t *q, const char *s) {
|
|
while (*s)
|
|
queue_add_data(q, *s++);
|
|
}
|
|
|
|
static void queue_handle_event(queue_t *q, bool release, int value) {
|
|
int modifiers = keyboard_modifiers[value];
|
|
DEBUG("queue_handle_event release=%d value=%d modifiers=%d\r\n", release,
|
|
value, modifiers);
|
|
if (modifiers) {
|
|
if (release) {
|
|
if (modifiers & TOGGLING_MODIFIERS) {
|
|
current_modifiers ^= modifiers;
|
|
keyboard_set_leds(
|
|
((current_modifiers & MOD_NUM) ? LED_NUM : 0) |
|
|
((current_modifiers & MOD_CAPS) ? LED_CAPS : 0));
|
|
} else {
|
|
current_modifiers &= ~modifiers;
|
|
}
|
|
} else {
|
|
if (modifiers & TOGGLING_MODIFIERS) {
|
|
/* NOTHING */
|
|
} else {
|
|
current_modifiers |= modifiers;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (release) {
|
|
return;
|
|
}
|
|
|
|
bool is_shift = current_modifiers & (LSHIFT | RSHIFT);
|
|
bool is_ctrl = current_modifiers & (LCTRL | RCTRL);
|
|
bool is_alt = current_modifiers & (LALT | RALT);
|
|
bool is_caps = current_modifiers & (MOD_CAPS);
|
|
bool is_num = (bool)(current_modifiers & (MOD_NUM)) ^ is_shift;
|
|
|
|
keycode kc = keyboard_codes[value];
|
|
|
|
DEBUG("kc=%04x is_[scaX] = %d %d %d %d\r\n", kc, is_shift, is_ctrl, is_alt,
|
|
is_caps);
|
|
|
|
if (!kc) {
|
|
scrnprintf("\r\nUn-mapped key: 0x%02x\r\n", value);
|
|
return;
|
|
}
|
|
|
|
if (IS_NUM(kc)) {
|
|
int sym = VALUE(kc);
|
|
kc = is_num ? numtab[sym].kc_numlock : numtab[sym].kc_normal;
|
|
}
|
|
|
|
if (IS_CMD(kc)) {
|
|
queue_add_data(q, kc);
|
|
return;
|
|
}
|
|
|
|
if (IS_SYM(kc)) {
|
|
int sym = VALUE(kc);
|
|
if (is_ctrl && is_alt) {
|
|
if (sym == DELETE) {
|
|
queue_add_data(q, CMD_REBOOT);
|
|
}
|
|
if (sym == F1) {
|
|
queue_add_data(q, CMD_SWITCH_PORT);
|
|
}
|
|
if (sym == F2) {
|
|
queue_add_data(q, CMD_SWITCH_RATE);
|
|
}
|
|
if (sym == F3) {
|
|
queue_add_data(q, CMD_SWITCH_SETTINGS);
|
|
}
|
|
return;
|
|
}
|
|
queue_add_str(q, symtab[kc & 0x7fff]);
|
|
return;
|
|
}
|
|
|
|
int c = LO(kc);
|
|
// DEBUG("c=%d HI=%d is_shift=%d\n", c, HI(kc), is_shift);
|
|
if (HI(kc) && is_shift)
|
|
c = HI(kc);
|
|
#define CTRLABLE(c) (c >= 64 && c <= 127)
|
|
if (is_ctrl && CTRLABLE(c)) {
|
|
c = c & 0x1f;
|
|
}
|
|
if (is_ctrl && c == 010) {
|
|
c = 0377; // ctrl-backspace = RUBOUT
|
|
}
|
|
#define IS_ALPHA(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
|
if (is_caps && IS_ALPHA(c)) {
|
|
c ^= ('a' ^ 'A');
|
|
}
|
|
if (is_alt) {
|
|
queue_add_data(q, '\033');
|
|
}
|
|
queue_add_data(q, c);
|
|
}
|
|
|
|
void keyboard_poll(queue_t *q) {
|
|
int value = kbd_read_timeout(0);
|
|
|
|
if (value == EOF) {
|
|
return;
|
|
}
|
|
DEBUG("keyboard_poll %02x\r\n", value);
|
|
if (value == 0xfa) {
|
|
if (pending_led_flag) {
|
|
kbd_write_blocking(pending_led_value);
|
|
pending_led_flag = false;
|
|
}
|
|
} else if (value == 0xf0) {
|
|
pending_release = true;
|
|
} else if (value <= 0x84) {
|
|
queue_handle_event(q, pending_release, value);
|
|
pending_release = false;
|
|
}
|
|
}
|
|
|
|
int keyboard_leds;
|
|
|
|
void keyboard_set_leds(int value) {
|
|
keyboard_leds = value;
|
|
// return;
|
|
if (value != pending_led_value) {
|
|
pending_led_value = value;
|
|
pending_led_flag = true;
|
|
kbd_write_blocking(0xed);
|
|
}
|
|
}
|