2020-09-17 11:23:45 -06:00

323 lines
12 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
#include <board/kbc.h>
#include <board/kbscan.h>
#include <board/keymap.h>
#include <common/debug.h>
#include <ec/ps2.h>
void kbc_init(void) {
// Disable interrupts
*(KBC.irq) = 0;
*(KBC.control) = 0;
}
#define KBC_TIMEOUT 10000
// System flag
static bool kbc_system = false;
// Enable first port - TODO
bool kbc_first = false;
// Enable second port - TODO
bool kbc_second = false;
// Translate from scancode set 2 to scancode set 1
// for basically no good reason
static bool kbc_translate = true;
// LED state
uint8_t kbc_leds = 0;
// Values from linux/drivers/input/keyboard/atkbd.c
static const uint16_t kbc_typematic_period[32] = {
33, // 30.0 cps = ~33.33ms
37, // 26.7 cps = ~37.45ms
42, // 24.0 cps = ~41.67ms
46, // 21.8 cps = ~45.87ms
50, // 20.7 cps = ~48.30ms
54, // 18.5 cps = ~54.05ms
58, // 17.1 cps = ~58.48ms
63, // 16.0 cps = ~62.50ms
67, // 15.0 cps = ~66.67ms
75, // 13.3 cps = ~75.19ms
83, // 12.0 cps = ~83.33ms
92, // 10.9 cps = ~91.74ms
100, // 10.0 cps = 100ms
109, // 9.2 cps = ~108.70ms
116, // 8.6 cps = ~116.28ms
125, // 8.0 cps = 125ms
133, // 7.5 cps = ~133.33ms
149, // 6.7 cps = ~149.25ms
167, // 6.0 cps = ~166.67ms
182, // 5.5 cps = ~181.82ms
200, // 5.0 cps = 200ms
217, // 4.6 cps = ~217.39ms
232, // 4.3 cps = ~232.56ms
250, // 4.0 cps = 250ms
270, // 3.7 cps = ~270.27ms
303, // 3.3 cps = ~303.03ms
333, // 3.0 cps = ~333.33ms
370, // 2.7 cps = ~370.37ms
400, // 2.5 cps = 400ms
435, // 2.3 cps = ~434.78ms
470, // 2.1 cps = ~478.19ms
500, // 2.0 cps = 500ms
};
bool kbc_scancode(struct Kbc * kbc, uint16_t key, bool pressed) {
if (!kbc_first) return true;
if (kbc_translate) {
key = keymap_translate(key);
}
if (!key) return true;
switch (key & 0xFF00) {
case K_E0:
TRACE(" E0\n");
if (!kbc_keyboard(kbc, 0xE0, KBC_TIMEOUT)) return false;
key &= 0xFF;
// Fall through
case 0x00:
if (!pressed) {
if (kbc_translate) {
key |= 0x80;
} else {
TRACE(" F0\n");
if (!kbc_keyboard(kbc, 0xF0, KBC_TIMEOUT)) return false;
}
}
TRACE(" %02X\n", key);
if (!kbc_keyboard(kbc, (uint8_t)key, KBC_TIMEOUT)) return false;
break;
}
return true;
}
enum KbcState {
KBC_STATE_NORMAL,
KBC_STATE_WRITE_CONFIG,
KBC_STATE_SET_LEDS,
KBC_STATE_SCANCODE,
KBC_STATE_TYPEMATIC,
KBC_STATE_WRITE_PORT,
KBC_STATE_FIRST_PORT_OUTPUT,
KBC_STATE_SECOND_PORT_OUTPUT,
KBC_STATE_SECOND_PORT_INPUT,
};
void kbc_event(struct Kbc * kbc) {
// TODO: state per KBC (we only have one KBC so low priority)
static enum KbcState state = KBC_STATE_NORMAL;
uint8_t sts = kbc_status(kbc);
if (sts & KBC_STS_IBF) {
uint8_t data = kbc_read(kbc);
if (sts & KBC_STS_CMD) {
TRACE("kbc cmd: %02X\n", data);
state = KBC_STATE_NORMAL;
switch (data) {
case 0x20:
TRACE(" read configuration byte\n");
uint8_t config = *kbc->control & 0x03;
if (kbc_system) {
config |= (1 << 2);
}
if (!kbc_first) {
config |= (1 << 4);
}
if (!kbc_second) {
config |= (1 << 5);
}
if (kbc_translate) {
config |= (1 << 6);
}
kbc_keyboard(kbc, config, KBC_TIMEOUT);
break;
case 0x60:
TRACE(" write configuration byte\n");
state = KBC_STATE_WRITE_CONFIG;
break;
case 0xA7:
TRACE(" disable second port\n");
kbc_second = false;
break;
case 0xA8:
TRACE(" enable second port\n");
kbc_second = true;
break;
case 0xA9:
TRACE(" test second port\n");
// TODO: communicate with touchpad?
kbc_keyboard(kbc, 0x00, KBC_TIMEOUT);
break;
case 0xAA:
TRACE(" test controller\n");
// Why not pass the test?
kbc_keyboard(kbc, 0x55, KBC_TIMEOUT);
break;
case 0xAB:
TRACE(" test first port\n");
// We _ARE_ the keyboard, so everything is good.
kbc_keyboard(kbc, 0x00, KBC_TIMEOUT);
break;
case 0xAD:
TRACE(" disable first port\n");
kbc_first = false;
break;
case 0xAE:
TRACE(" enable first port\n");
kbc_first = true;
break;
case 0xD1:
TRACE(" write port byte\n");
state = KBC_STATE_WRITE_PORT;
break;
case 0xD2:
TRACE(" write first port output\n");
state = KBC_STATE_FIRST_PORT_OUTPUT;
break;
case 0xD3:
TRACE(" write second port output\n");
state = KBC_STATE_SECOND_PORT_OUTPUT;
break;
case 0xD4:
TRACE(" write second port input\n");
state = KBC_STATE_SECOND_PORT_INPUT;
break;
}
} else {
TRACE("kbc data: %02X\n", data);
switch (state) {
case KBC_STATE_NORMAL:
TRACE(" keyboard command\n");
switch (data) {
case 0xED:
TRACE(" set leds\n");
state = KBC_STATE_SET_LEDS;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case 0xEE:
TRACE(" echo\n");
// Hey, this is easy. I like easy commands
kbc_keyboard(kbc, 0xEE, KBC_TIMEOUT);
break;
case 0xF0:
TRACE(" get/set scancode\n");
state = KBC_STATE_SCANCODE;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case 0xF2:
TRACE(" identify keyboard\n");
if (kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT)) {
if (kbc_keyboard(kbc, 0xAB, KBC_TIMEOUT)) {
kbc_keyboard(kbc, 0x83, KBC_TIMEOUT);
}
}
break;
case 0xF3:
TRACE(" set typematic rate/delay\n");
state = KBC_STATE_TYPEMATIC;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case 0xF4:
TRACE(" enable scanning\n");
kbscan_enabled = true;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case 0xF5:
TRACE(" disable scanning\n");
kbscan_enabled = false;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case 0xF6:
TRACE(" set default parameters\n");
kbc_leds = 0;
kbscan_repeat_period = 91;
kbscan_repeat_delay = 500;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case 0xFF:
TRACE(" self test\n");
if (kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT)) {
// Yep, everything is still good, I promise
kbc_keyboard(kbc, 0xAA, KBC_TIMEOUT);
}
break;
}
break;
case KBC_STATE_WRITE_CONFIG:
TRACE(" write configuration byte\n");
state = KBC_STATE_NORMAL;
uint8_t control = *kbc->control;
if (data & 1) {
control |= 1;
} else {
control &= ~1;
}
if (data & (1 << 1)) {
control |= (1 << 1);
} else {
control &= ~(1 << 1);
}
kbc_system = (bool)(data & (1 << 2));
kbc_first = (bool)(!(data & (1 << 4)));
kbc_second = (bool)(!(data & (1 << 5)));
kbc_translate = (bool)(data & (1 << 6));
*kbc->control = control;
break;
case KBC_STATE_SET_LEDS:
TRACE(" set leds\n");
state = KBC_STATE_NORMAL;
kbc_leds = data;
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case KBC_STATE_SCANCODE:
TRACE(" get/set scancode\n");
state = KBC_STATE_NORMAL;
#if LEVEL >= LEVEL_TRACE
switch (data) {
case 0x02:
TRACE(" set scancode set 2\n");
break;
}
#endif
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case KBC_STATE_TYPEMATIC:
TRACE(" set typematic rate/delay\n");
state = KBC_STATE_NORMAL;
{
// Rate: bits 0-4
uint16_t period = kbc_typematic_period[data & 0x1F];
kbscan_repeat_period = period;
// Delay: bits 5-6
static const uint16_t delay[4] = {250, 500, 750, 1000};
uint8_t idx = (data & 0x60) >> 5;
kbscan_repeat_delay = delay[idx];
}
kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT);
break;
case KBC_STATE_WRITE_PORT:
TRACE(" write port byte\n");
state = KBC_STATE_NORMAL;
break;
case KBC_STATE_FIRST_PORT_OUTPUT:
TRACE(" write first port output\n");
state = KBC_STATE_NORMAL;
kbc_keyboard(kbc, data, KBC_TIMEOUT);
break;
case KBC_STATE_SECOND_PORT_OUTPUT:
TRACE(" write second port output\n");
state = KBC_STATE_NORMAL;
kbc_mouse(kbc, data, KBC_TIMEOUT);
break;
case KBC_STATE_SECOND_PORT_INPUT:
TRACE(" write second port input\n");
state = KBC_STATE_NORMAL;
ps2_write(&PS2_3, &data, 1);
break;
}
}
}
}