#include "keymap.h"
#include "vc.h"
#include "vc_struct.h"
#include "tty.h"
#include "video_driver.h"
#include "tty_queue.h"
#include <panic.h>
#include <printk.h>
#include <asm.h>
#include <string.h>
#define CT_PULSE_PORTS 0xf0
#define KB_WRITE_LEDS 0xed
#define KB_ACK 0xfa
#define KB_RESEND 0xfe
#define E0_KPENTER 96
#define E0_RCTRL 97
#define E0_KPSLASH 98
#define E0_PRSCR 99
#define E0_RALT 100
#define E0_BREAK 101
#define E0_HOME 102
#define E0_UP 103
#define E0_PGUP 104
#define E0_LEFT 105
#define E0_RIGHT 106
#define E0_END 107
#define E0_DOWN 108
#define E0_PGDN 109
#define E0_INS 110
#define E0_DEL 111
#define E1_PAUSE 119
#define E0_MSLW 125
#define E0_MSRW 126
#define E0_MSTM 127
static unsigned char e0_keys[128] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,
E0_RALT, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, E0_BREAK, E0_HOME,
E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, 0, E0_END,
E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,
0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
#define KT_LATIN 0
#define KT_FN 1
#define KT_SPEC 2
#define KT_PAD 3
#define KT_DEAD 4
#define KT_CONS 5
#define KT_CUR 6
#define KT_SHIFT 7
#define KT_META 8
#define KT_ASCII 9
#define KT_LOCK 10
#define KT_LETTER 11
#define KT_SLOCK 12
#define K_FIND 20
#define K_INSERT 21
#define K_REMOVE 22
#define K_SELECT 23
#define K_PGUP 24
#define K_PGDN 25
#define K_P0 0
#define K_P1 1
#define K_P2 2
#define K_P3 3
#define K_P4 4
#define K_P5 5
#define K_P6 6
#define K_P7 7
#define K_P8 8
#define K_P9 9
#define K_PPLUS 10
#define K_PMINUS 11
#define K_PSTAR 12
#define K_PSLASH 13
#define K_PENTER 14
#define K_PDOT 16
#define K_DOWN 0
#define K_LEFT 1
#define K_RIGHT 2
#define K_UP 3
#define K_SHIFT 0
#define K_CTRL 2
#define K_ALT 2
#define K_ALTGR 1
#define K_SHIFTL 4
#define K_SHIFTR 5
#define K_CTRLL 6
#define K_CTRLR 7
#define K_CAPSSHIFT 8
static struct vc_struct dummy_con;
static int shift_down[9];
static int dead_key_next;
static unsigned char diacr;
static int repeating;
static unsigned short prev_scancode;
static unsigned char prev_keycode;
static struct vc_struct *con;
static int char_put;
#define put_queue(c) ({ \
tty_put(&con->tty, (c)); \
char_put = 1; \
})
#define puts_queue(s) ({ \
const char *tmp = (s); \
while (*tmp) \
put_queue(*tmp++); \
})
static int dummy()
{
return 0;
}
static const struct tty_vtbl_struct dummy_con_tty_vtbl = {
.do_output = vc_tty_do_output,
.do_stop = vc_tty_do_stop,
.do_start = vc_tty_do_start,
.do_ioctl = vc_tty_do_ioctl
};
static const struct vc_vtbl_struct dummy_con_vc_vtbl = {
.do_stopped_changed = (void (*)())dummy
};
static const struct video_driver_struct dummy_video_driver = {
.get_lines = (unsigned short (*)())dummy,
.get_columns = (unsigned short (*)())dummy,
.get_cursor_type = (unsigned char (*)())dummy,
.set_cursor_type = (void (*)(unsigned char))dummy,
.get_cursor_pos = (unsigned short (*)())dummy,
.set_cursor_pos = (void (*)(unsigned short))dummy,
.set_attr = (void (*)(const struct attr_mode_struct *))dummy,
.putc = (void (*)(unsigned short, unsigned char))dummy,
.clear = (void (*)(unsigned short, unsigned short))dummy,
.scroll = (void (*)())dummy,
.history_scroll = (void (*)(short))dummy,
.history_cancel = (void (*)())dummy
};
static void dummy_con_init()
{
dummy_con.tty.vptr = &dummy_con_tty_vtbl;
dummy_con.vptr = &dummy_con_vc_vtbl;
vc_init(&dummy_con, 0, 0);
dummy_con.vt.do_kb_reset = (void (*)())dummy;
dummy_con.vt.driver = &dummy_video_driver;
dummy_con.vt.do_beep = (void (*)())dummy;
dummy_con.lockstate = 0;
}
static unsigned char read_status()
{
return inb(0x64);
}
static inline int wait_input_full()
{
int i;
for (i = 0; i < 0x10000; i++)
if (read_status() & 0x01)
return 1;
return 0;
}
static inline int wait_output_empty()
{
int i;
for (i = 0; i < 0x10000; i++)
if (!(read_status() & 0x02))
return 1;
return 0;
}
static void write_command(unsigned char command)
{
wait_output_empty();
outb(command, 0x64);
}
static unsigned char read_data()
{
if (!wait_input_full())
return 0;
return inb(0x60);
}
static void write_data(unsigned char data)
{
wait_output_empty();
outb(data, 0x60);
}
static int send_data(unsigned char data)
{
int try;
for (try = 0; try < 3; try++) {
write_data(data);
switch (read_data()) {
case KB_ACK:
return 1;
case KB_RESEND:
continue;
default:
return 0;
}
}
return 0;
}
static void set_leds(unsigned char leds)
{
if (!send_data(KB_WRITE_LEDS))
return;
send_data(leds);
}
static void reset_cpu()
{
write_command(CT_PULSE_PORTS | 0xe);
}
static void synchronize()
{
unsigned int i;
con->modifiers = 0;
for (i = 0; i < sizeof shift_down / sizeof *shift_down; i++)
if (shift_down[i])
con->modifiers |= 1 << i;
set_leds(con->lockstate);
}
static void applkey(unsigned char value)
{
put_queue('\033');
put_queue('[');
put_queue(value);
}
static unsigned char handle_diacr(unsigned char c)
{
unsigned char d;
unsigned int i;
d = diacr;
diacr = 0;
for (i = 0; i < accent_table_size; i++)
if (accent_table[i].diacr == d && accent_table[i].base == c)
return accent_table[i].result;
if (c == ' ' || c == d)
return d;
put_queue(d);
return c;
}
static void __attribute__ ((noreturn)) reboot()
{
reset_cpu();
die();
}
static int translate(unsigned char scancode, unsigned char *keycode)
{
unsigned char up_flag;
if (scancode == 0xe0 || scancode == 0xe1) {
prev_scancode = scancode;
return 0;
}
if (scancode == 0xfa || scancode == 0xfe)
return 0;
if (scancode == 0x00 || scancode == 0xff) {
prev_scancode = 0;
return 0;
}
up_flag = scancode & 0x80;
scancode &= 0x7f;
if (prev_scancode) {
if (prev_scancode != 0xe0) {
if (prev_scancode == 0xe1 && scancode == 0x1d) {
prev_scancode = 0x100;
return 0;
}
else if (prev_scancode == 0x100 && scancode == 0x45) {
*keycode = E1_PAUSE;
prev_scancode = 0;
}
else {
prev_scancode = 0;
return 0;
}
}
else {
prev_scancode = 0;
if (scancode == 0x2a || scancode == 0x36)
return 0;
if (e0_keys[scancode])
*keycode = e0_keys[scancode];
else
return 0;
}
}
else if (scancode >= 89)
return 0;
else
*keycode = scancode;
*keycode |= up_flag;
repeating = *keycode == prev_keycode;
prev_keycode = *keycode;
return 1;
}
static void fn_enter()
{
if (diacr) {
put_queue(diacr);
diacr = 0;
}
put_queue('\r');
}
static void fn_caps_toggle()
{
if (repeating)
return;
con->lockstate ^= CAPS_LOCK;
set_leds(con->lockstate);
}
static void fn_hold()
{
if (repeating)
return;
con->lockstate ^= SCROLL_LOCK;
set_leds(con->lockstate);
con->lockstate & SCROLL_LOCK ? tty_stop(&con->tty) : tty_start(&con->tty);
}
static void fn_num()
{
if (repeating)
return;
con->lockstate ^= NUM_LOCK;
set_leds(con->lockstate);
}
static void fn_history_pgdn()
{
vc_history_pgdn(con);
}
static void fn_history_pgup()
{
vc_history_pgup(con);
}
static void fn_history_down()
{
vc_history_down(con);
}
static void fn_history_up()
{
vc_history_up(con);
}
static void __attribute__ ((noreturn)) fn_reboot()
{
reboot();
}
static void fn_compose()
{
dead_key_next = 1;
}
static void fn_null() {}
static void (*fn_handler[])() = {
fn_null, fn_enter, fn_null, fn_null,
fn_null, fn_null, fn_null, fn_caps_toggle,
fn_num, fn_hold, fn_history_pgdn, fn_history_pgup,
fn_reboot, fn_null, fn_compose, fn_null,
fn_null, fn_null, fn_null, fn_num,
fn_history_down, fn_history_up
};
static void k_self(unsigned char value, int up_flag)
{
if (up_flag)
return;
if (diacr)
value = handle_diacr(value);
if (dead_key_next) {
dead_key_next = 0;
diacr = value;
return;
}
put_queue(value);
}
static void k_fn(unsigned char value, int up_flag)
{
if (up_flag)
return;
if (func_table[value])
puts_queue(func_table[value]);
}
static void k_spec(unsigned char value, int up_flag)
{
if (up_flag)
return;
if (value >= sizeof fn_handler / sizeof *fn_handler)
return;
fn_handler[value]();
}
static void k_cur(unsigned char value, int up_flag)
{
static const char *cur_chars = "BDCA";
if (up_flag)
return;
applkey(cur_chars[value]);
}
static void k_shift(unsigned char value, int up_flag)
{
if (repeating)
return;
if (value == K_CAPSSHIFT) {
value = K_SHIFT;
con->lockstate &= ~CAPS_LOCK;
set_leds(con->lockstate);
}
if (up_flag) {
if (shift_down[value])
shift_down[value]--;
}
else
shift_down[value]++;
if (shift_down[value])
con->modifiers |= (1 << value);
else
con->modifiers &= ~(1 << value);
}
static void k_meta(unsigned char value, int up_flag)
{
if (up_flag)
return;
put_queue('\033');
put_queue(value);
}
static void k_dead(unsigned char value, int up_flag)
{
if (up_flag)
return;
diacr = diacr ? handle_diacr(value) : value;
}
static void k_ignore(unsigned char value, int up_flag) {}
static void k_pad(unsigned char value, int up_flag)
{
static const char *pad_chars = "0123456789+-*/\015,.?()#";
if (up_flag)
return;
if (!(con->lockstate & NUM_LOCK))
switch (value) {
case K_P0:
return k_fn(K_INSERT, 0);
case K_P1:
return k_fn(K_SELECT, 0);
case K_P2:
return k_cur(K_DOWN, 0);
case K_P3:
return k_fn(K_PGDN, 0);
case K_P4:
return k_cur(K_LEFT, 0);
case K_P5:
return applkey('G');
case K_P6:
return k_cur(K_RIGHT, 0);
case K_P7:
return k_fn(K_FIND, 0);
case K_P8:
return k_cur(K_UP, 0);
case K_P9:
return k_fn(K_PGUP, 0);
case K_PDOT:
return k_fn(K_REMOVE, 0);
}
put_queue(pad_chars[value]);
}
static void (*const k_handler[16])(unsigned char value, int up_flag) = {
k_self, k_fn, k_spec, k_pad,
k_ignore, k_ignore, k_cur, k_shift,
k_meta, k_ignore, k_ignore, k_ignore,
k_ignore, k_dead, k_ignore, k_ignore
};
static void process_keycode(unsigned char keycode)
{
unsigned char up_flag;
const unsigned short *key_map;
unsigned short keysym;
unsigned char type;
up_flag = keycode & 0x80;
keycode &= 0x7f;
if (con->modifiers >= sizeof key_maps / sizeof *key_maps)
return;
key_map = key_maps[con->modifiers];
if (!key_map)
return;
keysym = key_map[keycode];
type = keysym >> 8;
if (type < 0xf0)
return;
type -= 0xf0;
if (type == KT_LETTER) {
if (con->lockstate & CAPS_LOCK) {
key_map = key_maps[con->modifiers ^ SHIFT];
if (key_map)
keysym = key_map[keycode];
}
type = KT_LATIN;
}
char_put = 0;
(*k_handler[type])(keysym & 0xff, up_flag);
if (char_put) {
vc_history_cancel(con);
tty_process_input_queue(&con->tty);
}
}
void kb_init()
{
dummy_con_init();
memset(shift_down, 0, sizeof shift_down / sizeof *shift_down);
dead_key_next = 0;
diacr = 0;
repeating = 0;
prev_scancode = 0;
prev_keycode = 0;
con = &dummy_con;
synchronize();
}
void kb_print_info()
{
printk("Keyboard controller: Intel 8042\n");
printk("Keymap: " KEYMAP_NAME "\n");
}
void kb_set_con(struct vc_struct *vc)
{
int intr;
disable_intr(intr);
con = vc;
synchronize();
restore_intr(intr);
}
void kb_reset()
{
int intr;
disable_intr(intr);
con->lockstate = 0;
synchronize();
restore_intr(intr);
}
void __attribute__ ((noreturn)) kb_reboot()
{
cli();
reboot();
}
void do_keyboard_interrupt()
{
unsigned char scancode, keycode;
if (!(scancode = read_data()))
return;
if (translate(scancode, &keycode))
process_keycode(keycode);
}