cynix

x86 UNIX-like OS
git clone git://git.2f30.org/cynix
Log | Files | Refs | README | LICENSE

keyboard.c (5090B)


      1 /*
      2  *  core/keyboard.c
      3  *
      4  *  Copyright (C) 2009 stateless
      5  */
      6 
      7 #include <keyboard.h>
      8 #include <x86.h>
      9 #include <idt.h>
     10 #include <common.h>
     11 #include <kdb.h>
     12 #include <tty.h>
     13 
     14 enum { KBD_BUF_SIZE = 4096 };
     15 enum { CMD_PORT = 0x64, DATA_PORT = 0x60 /* to/from the kbd */, STATUS_PORT = 0x64 };
     16 
     17 /* special keys */
     18 enum {
     19 	LALT, RALT,
     20 	LCTRL, RCTRL,
     21 	LSHIFT, RSHIFT,
     22 	F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
     23 	CAPSLK, NUMLK, SCRLK, SYSRQ,
     24 	ESC = 27,
     25 	INSERT, DEL, HOME, END, PGUP, PGDN, LEFT, RIGHT, UP, DOWN,
     26 	NUM_DOT, NUM_ENTER, NUM_PLUS, NUM_MINUS, NUM_MUL, NUM_DIV,
     27 	NUM_0, NUM_1, NUM_2, NUM_3, NUM_4, NUM_5, NUM_6, NUM_7, NUM_8, NUM_9,
     28 	BACKSP = 127
     29 };
     30 
     31 int f_kdb = 0;
     32 
     33 static uint8_t kbd_buffer[KBD_BUF_SIZE]; /* plain ascii 0 - 127 */
     34 static uint32_t head = 0, tail = 0;
     35 static bool shift_st = 0;
     36 
     37 static int keycodes_lower[] = {
     38 	0, ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', BACKSP,      /* 0 - e   */
     39 	'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\r',          /* f - 1c  */
     40 	LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',              /* 1d - 29 */
     41 	LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', RSHIFT,          /* 2a - 36 */
     42 	NUM_MUL, LALT, ' ', CAPSLK, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10,             /* 37 - 44 */
     43 	NUMLK, SCRLK, NUM_7, NUM_8, NUM_9, NUM_MINUS, NUM_4, NUM_5, NUM_6, NUM_PLUS,     /* 45 - 4e */
     44 	NUM_1, NUM_2, NUM_3, NUM_0, NUM_DOT, SYSRQ, 0, 0, F11, F12,                      /* 4d - 58 */
     45 	0, 0, 0, 0, 0, 0, 0,                                                             /* 59 - 5f */
     46 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                  /* 60 - 6f */
     47 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                                   /* 70 - 7f */
     48 };
     49 
     50 static int keycodes_upper[] = {
     51 	0, ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', BACKSP,      /* 0 - e   */
     52 	'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\r',          /* f - 1c  */
     53 	LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',               /* 1d - 29 */
     54 	LSHIFT, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', RSHIFT,           /* 2a - 36 */
     55 	NUM_MUL, LALT, ' ', CAPSLK, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10,             /* 37 - 44 */
     56 	NUMLK, SCRLK, NUM_7, NUM_8, NUM_9, NUM_MINUS, NUM_4, NUM_5, NUM_6, NUM_PLUS,     /* 45 - 4e */
     57 	NUM_1, NUM_2, NUM_3, NUM_0, NUM_DOT, SYSRQ, 0, 0, F11, F12,                      /* 4d - 58 */
     58 	0, 0, 0, 0, 0, 0, 0,                                                             /* 59 - 5f */
     59 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                  /* 60 - 6f */
     60 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                                   /* 70 - 7f */
     61 };
     62 
     63 static inline void
     64 wait_cmd_buffer(void)
     65 {
     66 	while (inb(STATUS_PORT) & 0x2)
     67 		;
     68 }
     69 
     70 static inline void
     71 wait_result_buffer(void)
     72 {
     73 	while (!(inb(STATUS_PORT) & 0x1))
     74 		;
     75 }
     76 
     77 static inline void
     78 send_kbd_cmd(uint8_t byte)
     79 {
     80 	wait_cmd_buffer();
     81 	outb(CMD_PORT, byte);
     82 	wait_cmd_buffer();
     83 }
     84 
     85 static inline uint8_t
     86 recv_kbd_res(void)
     87 {
     88 	uint8_t byte;
     89 	wait_result_buffer();
     90 	byte = inb(DATA_PORT);
     91 	return byte;
     92 }
     93 
     94 void
     95 keyboard_callback(__attribute__ ((unused)) struct trapframe_t *regs)
     96 {
     97 	wait_result_buffer();
     98 	uint8_t scancode = inb(DATA_PORT);
     99 
    100 	if (!(scancode & 0x80)) {
    101 		if (tail != KBD_BUF_SIZE) {
    102 			if (scancode == 0x2a)
    103 				shift_st = 1;
    104 			if (scancode == 0x58) {
    105 				if (!f_kdb) {
    106 					f_kdb = 1;
    107 					set_trapframe(regs);
    108 					assert(!IS_ERR(create_kthread("kdb", kdb_enter)));
    109 					freeze_tasks();
    110 				}
    111 				return;
    112 			}
    113 			if (!is_tty_attached()) return;
    114 			kbd_buffer[tail++] = (shift_st) ? keycodes_upper[scancode]
    115 					     : keycodes_lower[scancode];
    116 			if (scancode == 0x1c) kbd_buffer[tail - 1] = '\n';
    117 			resume_task(tty_get_sleep_chan());
    118 		}
    119 	} else if (scancode == 0xaa)
    120 		shift_st = 0;
    121 }
    122 
    123 int
    124 kbd_getchar(void)
    125 {
    126 	int ch;
    127 	uint32_t state;
    128 
    129 	save_flags(&state);
    130 	cli();
    131 	if (head == tail) {
    132 		if (tail == KBD_BUF_SIZE)
    133 			head = tail = 0;
    134 		load_flags(state);
    135 		return -1;
    136 	}
    137 	ch = kbd_buffer[head];
    138 	++head;
    139 	load_flags(state);
    140 	return ch;
    141 }
    142 
    143 void
    144 initialize_keyboard(void)
    145 {
    146 	uint8_t byte;
    147 
    148 	/* disable keyboard */
    149 	send_kbd_cmd(0xad);
    150 
    151 	/* perform a self-test */
    152 	send_kbd_cmd(0xaa);
    153 	if (recv_kbd_res() != 0x55)
    154 		panic("keyboard self-test failed!");
    155 
    156 	/* read the CCB */
    157 	send_kbd_cmd(0x20);
    158 	byte = recv_kbd_res();
    159 
    160 	/* if keyboard translation is not enabled, try to enable it */
    161 	if (!(byte & (1 << 6))) {
    162 		info("translation is not enabled, attempting to enable it ...\n");
    163 		byte |= (1 << 6);
    164 		send_kbd_cmd(0x60);
    165 		outb(DATA_PORT, byte);
    166 		wait_cmd_buffer();
    167 		send_kbd_cmd(0x20);
    168 		if (!(recv_kbd_res() & (1 << 6))) {
    169 			printf("failed!\n");
    170 			hlt();
    171 		}
    172 		printf("OK!\n");
    173 	}
    174 
    175 	/* check if we are in polling mode and if so enable interrupts on IRQ1 */
    176 	if (!(byte & 0x1)) {
    177 		byte |= 0x1;
    178 		send_kbd_cmd(0x60);
    179 		outb(DATA_PORT, byte);
    180 		wait_cmd_buffer();
    181 	}
    182 
    183 	/* enable keyboard */
    184 	send_kbd_cmd(0xae);
    185 	register_isr_handler(33, keyboard_callback);
    186 }
    187