cr100/keyboard.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);
}
}