BigOS/kernel/keyboard.c

536 lines
11 KiB
C

#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
#include "keyboard.h"
// #include "keymap.h"
#include "x86.h"
#include "stdio.h"
#include "assert.h"
#include "minix_keymap.h"
#include "serialport.h"
#include "memman.h"
static int code_with_E0;
static int shift_l; /* l shift state */
static int shift_r; /* r shift state */
static int alt_l; /* l alt state */
static int alt_r; /* r left state */
static int ctrl_l; /* l ctrl state */
static int ctrl_r; /* l ctrl state */
static int caps_lock; /* Caps Lock */
static int num_lock; /* Num Lock */
static int scroll_lock; /* Scroll Lock */
static int mouse_count;
static u8 mouse_data[MOUSE_IN_BYTES];
static void set_leds();
static void set_mouse_leds();
static void kb_wait();
static void kbd_process(unsigned char scode);
/*
* Helper Procedures for PS/2 Mouse Operation
* */
static void mouse_wait(u8 a_type)
{
int _time_out = 100000;
if (a_type == 0)
{
while (_time_out--)
if ((inb(PS2_PORT_CMD) & 1) == 1)
return;
return;
}
else
{
while (_time_out--)
if ((inb(PS2_PORT_CMD) & 2) == 0)
return;
return;
}
}
static void mouse_write(u8 a_write)
{
mouse_wait(1); // Wait to be able to send a command
outb(PS2_PORT_CMD, PS2_CMD_2ND_OUT); // Tell the mouse we are sending a command
mouse_wait(1); // Wait for the final part
outb(PS2_PORT_DATA, a_write); // Finally write
}
// Get's response from mouse
static u8 mouse_read()
{
mouse_wait(0);
return inb(PS2_PORT_DATA);
}
static void mouse_set_rate(u8 rate)
{
mouse_write(0xf3); // set sample rate cmd
assert(mouse_read() == PS2_ACK);
mouse_write(rate);
assert(mouse_read() == PS2_ACK);
}
static u8 mouse_get_id()
{
mouse_write(PS2_CMD_DATA_REPORT_DIS);
assert(mouse_read() == PS2_ACK);
mouse_write(PS2_CMD_GET_DEVICE); // report
assert(mouse_read() == PS2_ACK);
u8 device_type = mouse_read();
mouse_write(PS2_CMD_DATA_REPORT_ENB);
assert(mouse_read() == PS2_ACK);
return device_type;
}
static void set_mouse_leds()
{
kb_wait();
outb(KB_CMD, KBCMD_EN_MOUSE_INTFACE);
kb_wait();
outb(KB_CMD, KEYCMD_SENDTO_MOUSE);
kb_wait();
outb(KB_DATA, MOUSECMD_ENABLE);
kb_wait();
outb(KB_CMD, KEYCMD_WRITE_MODE);
kb_wait();
outb(KB_DATA, KBC_MODE);
}
void mouse_handler(int irq)
{
u8 scan_code = inb(PS2_PORT_DATA);
static int aux_state = 0;
int i;
u32 pack;
if (mouse_count == 0 && !(scan_code & 0x08))
return; // resync
mouse_data[mouse_count++] = scan_code;
if (mouse_count < 4)
return;
mouse_count = 0;
for (i = 0; i < 3; i++)
{
pack = ISMOUSE | MOUSEBTN;
if ((aux_state ^ mouse_data[0]) & (1 << i))
{
aux_state ^= (1 << i);
pack |= !!(aux_state & (1 << i)) ? MOUSEBTN_CLICK : MOUSEBTN_RELEASE;
pack |= (1 << i);
cur_ntty->recvbuf(cur_ntty, pack);
}
}
pack = ISMOUSE | MOUSESCR;
if ((signed char)mouse_data[3] > 0)
{
// actually it is 0x1
pack |= MOUSESCR_DOWN;
cur_ntty->recvbuf(cur_ntty, pack);
}
else if ((signed char)mouse_data[3] < 0)
{
// actually it is 0xff
pack |= MOUSESCR_UP;
cur_ntty->recvbuf(cur_ntty, pack);
}
for (i = 0; i < 2; i++)
{
pack = mouse_data[1 + i];
if (pack != 0)
{
pack |= ISMOUSE | MOUSEPOS;
if (mouse_data[0] & (0x10 << i))
pack |= MOUSEPOS_NEG;
if (i == 1)
pack |= MOUSEPOS_XY;
cur_ntty->recvbuf(cur_ntty, pack);
}
}
// kprintf("0x%02x 0x%02x 0x%02x 0x%02x\n", mouse_data[0], mouse_data[1], mouse_data[2], mouse_data[3]);
}
void init_mouse()
{
mouse_count = 0;
put_irq_handler(MOUSE_IRQ, mouse_handler);
enable_irq(MOUSE_IRQ);
mouse_wait(1);
outb(PS2_PORT_CMD, PS2_CMD_2ND_ENB); // Enable second PS/2 port
mouse_wait(1);
outb(PS2_PORT_CMD, PS2_CMD_READRAM); // Read "byte 0" from internal RAM
mouse_wait(0);
u8 _status = inb(PS2_PORT_DATA) | 0x2; // bit-1 <=> Second PS/2 port interrupt
mouse_wait(1);
outb(PS2_PORT_CMD, PS2_PORT_DATA); // Write next byte to "byte 0" of internal RAM
mouse_wait(1);
outb(PS2_PORT_DATA, _status); // Write back to internal RAM
mouse_write(PS2_CMD_SET_DEFAULT); // Set Defaults
assert(mouse_read() == PS2_ACK);
mouse_write(PS2_CMD_DATA_REPORT_ENB); // Enable Data Reporting
assert(mouse_read() == PS2_ACK); // Remember to read ACK, or it may block
assert(mouse_get_id() == 0x00); // by init, id should be 0(standard ps/2 mouse)
mouse_set_rate(200);
mouse_set_rate(100);
mouse_set_rate(80); // tricks to enable z-axis, from osdev
assert(mouse_get_id() == 0x03); // z-axis enabled, id should be 3 (Mouse with scroll wheel)
mouse_write(PS2_CMD_DATA_REPORT_ENB);
assert(mouse_read() == PS2_ACK);
set_mouse_leds();
}
/**
* Wait until the input buffer of 8042 is empty.
*/
static void kb_wait() /* 等待 8042 的输入缓冲区空 */
{
u8 kb_stat;
do
{
kb_stat = inb(KB_CMD);
} while (kb_stat & 0x02);
}
static void kb_ack()
{
u8 kb_read;
do {
kb_read = inb(KB_DATA);
} while (kb_read != KB_ACK);
}
/**
* Set the leds according to: caps_lock, num_lock & scroll_lock.
*/
static void set_leds()
{
kb_wait();
outb(KB_CMD, KEYCMD_WRITE_MODE);
kb_wait();
outb(KB_DATA, KBC_MODE);
}
void kb_handler(int irq)
{
u8 scan_code = inb(PS2_PORT_DATA);
kbd_process(scan_code);
// kprintf("kb");
};
void init_kb()
{
shift_l = shift_r = 0;
alt_l = alt_r = 0;
ctrl_l = ctrl_r = 0;
caps_lock = 0;
num_lock = 1;
scroll_lock = 0;
set_leds();
init_mouse();
put_irq_handler(KEYBOARD_IRQ, kb_handler);
enable_irq(KEYBOARD_IRQ);
}
static u16 map_key(int code)
{
int caps, column;
int shift = shift_l | shift_r;
int alt = alt_l | alt_r;
int ctrl = ctrl_l | ctrl_r;
u16 *keyrow = minix_keymap[code];
caps = shift_l | shift_r;
if (num_lock && (keyrow[0] & HASNUM))
caps = !caps;
if (caps_lock && (keyrow[0] & HASCAPS))
caps = !caps;
if (alt)
{
column = 2;
if (ctrl || alt_r)
column = 3; /* Ctrl + Alt == AltGr */
if (caps)
column = 4;
}
else
{
column = 0;
if (caps)
column = 1;
if (ctrl)
column = 5;
}
return keyrow[column] & ~(HASNUM | HASCAPS);
}
static void kbd_process(unsigned char scode)
{
int press, index, page, code;
static int kbd_state = 0;
press = !(scode & SCAN_RELEASE) ? INPUT_PRESS : INPUT_RELEASE;
index = scode & ~SCAN_RELEASE;
switch (kbd_state)
{
case 1:
page = scanmap_escaped[index].page;
code = scanmap_escaped[index].code;
break;
case 2:
kbd_state = (index == SCAN_CTRL) ? 3 : 0;
return;
case 3:
if (index == SCAN_NUMLOCK)
{
page = INPUT_PAGE_KEY;
code = INPUT_KEY_PAUSE;
break;
}
/* FALLTHROUGH */
default:
switch (scode)
{
case SCAN_EXT0:
kbd_state = 1;
return;
case SCAN_EXT1:
kbd_state = 2;
return;
}
page = scanmap_normal[index].page;
code = scanmap_normal[index].code;
break;
}
if (page)
{
switch (code)
{
case INPUT_KEY_LEFT_SHIFT:
shift_l = press;
break;
case INPUT_KEY_RIGHT_SHIFT:
shift_r = press;
break;
case INPUT_KEY_LEFT_CTRL:
ctrl_l = press;
break;
case INPUT_KEY_RIGHT_CTRL:
ctrl_r = press;
break;
case INPUT_KEY_LEFT_ALT:
alt_l = press;
break;
case INPUT_KEY_RIGHT_ALT:
alt_r = press;
break;
case INPUT_KEY_NUM_LOCK:
if (press){
num_lock = !num_lock;
set_leds();
}
break;
case INPUT_KEY_CAPS_LOCK:
if (press){
caps_lock = !caps_lock;
set_leds();
}
break;
case INPUT_KEY_SCROLL_LOCK:
if (press){
scroll_lock = !scroll_lock;
set_leds();
}
break;
default:
if (press)
cur_ntty->recvbuf(cur_ntty, map_key(code));
}
// inputdriver_send_event(FALSE /*mouse*/, page, code, press, 0);
}
kbd_state = 0;
}
static u32 kbdlock;
#define LASTKEY(x) LAST(x, TTY_IN_BYTES)
#define NEXTKEY(x) NEXT(x, TTY_IN_BYTES)
// static u8 keybuf[3][TTY_IN_BYTES];
void ps2_tty_init(NTTY *tty)
{
static int _cnt = 0;
assert(tty->driver_type == 1);
assert(tty->input_buf);
keyboard_buf *kbd = (keyboard_buf *)tty->input_buf;
// kbd->buf = (void *)keybuf[_cnt++];
kbd->buf = (void*)K_PHY2LIN(do_kmalloc(TTY_IN_BYTES));
// kprintf("kbd buf: %p\n", kbd->buf);
kbd->head = kbd->tail = kbd->readable = 0;
kbd->len = 0;
kbdlock = 0;
}
int ps2_tty_read(NTTY *tty, char *buf, int nr)
{
assert(tty->input_buf);
keyboard_buf *kbd = (keyboard_buf *)tty->input_buf;
int i = 0;
while (1)
{
while (xchg(&kbdlock, 1) == 1)
sys_yield();
if (kbd->readable)
{
break;
}
xchg(&kbdlock, 0);
sys_yield();
}
assert(kbd->buf);
u8 *ibuf = kbd->buf;
for (; i < nr && i < kbd->readable; ++i)
{
*buf ++ = ibuf[kbd->head];
// kprintf("[r]%p;", ibuf+kbd->head);
kbd->head = NEXTKEY(kbd->head);
}
kbd->readable -= i;
kbd->len -= i;
xchg(&kbdlock, 0);
return i;
}
void ps2_tty_flush(NTTY *tty)
{
disable_int();
assert(tty->input_buf);
keyboard_buf *kbd = (keyboard_buf *)tty->input_buf;
kbd->head = kbd->tail = kbd->len = kbd->readable = 0;
enable_int();
}
void ps2_tty_recvbuf(NTTY *tty, u32 key)
{
// kprintf("%x\n", key);
// disable_int();
assert(tty->input_buf);
keyboard_buf *kbd = (keyboard_buf *)tty->input_buf;
u8* buf = kbd->buf;
if (key & ISMOUSE)
{
if (key & MOUSESCR)
{
if (key & MOUSESCR_UP)
{
// kprintf("scroll up\n");
tty->ioctl(tty, 1, TTY_SCROLL_UP);
}
else
{
// kprintf("scroll down\n");
tty->ioctl(tty, 1, TTY_SCROLL_DOWN);
}
}
else if (key & MOUSEBTN)
{
if ((key & MOUSEBTN_CLICK) && (key & MOUSEBTN_M))
{
// kprintf("middle btn click %x\n", key);
tty->ioctl(tty, 1, TTY_SCROLL_TOCUR);
}
}
}
else if (key & EXT)
{
switch (key)
{
case F1: case F2: case F3: case F4: case F5: case F6:
case F7: case F8: case F9: case F10: case F11: case F12:
{
int conno = (key & 0xff) - (F1 & 0xff);
if (conno < NR_CONSOLES)
{
// kprintf("select console %d\n", conno);
tty->ioctl(tty, 2, conno);
}
break;
}
default:
break;
}
}
else
{
switch (key)
{
case '\r':
case '\n': // escape ENTER
while (xchg(&kbdlock, 1) == 1)
sys_yield();
tty->write(tty, '\n');
buf[kbd->tail] = '\n';
kbd->len++;
kbd->tail = NEXTKEY(kbd->tail);
kbd->readable = CYCLE_SUB(kbd->head, kbd->tail, TTY_IN_BYTES);
xchg(&kbdlock, 0);
// kprintf("len=%d, h=%d, t=%d, rd=%d\n", kbd->len, kbd->head, kbd->tail, kbd->readable);
break;
case '\b':
while (xchg(&kbdlock, 1) == 1)
sys_yield();
if (kbd->len > kbd->readable)
{
// vga_tty_backspace(tty);
tty->write(tty, '\b');
tty->write(tty, ' ');
tty->write(tty, '\b');
kbd->len--;
kbd->tail = LASTKEY(kbd->tail);
}
xchg(&kbdlock, 0);
break;
default:
while (xchg(&kbdlock, 1) == 1)
sys_yield();
if ((key & 0xff) == 0)
return;
if (kbd->len == TTY_IN_BYTES - 1)
return; // leave one space for ctrl ascii
buf[kbd->tail] = (u8)(key & 0xff);
// kprintf("%d %d %d", key & 0xff, kbd->tail, buf[kbd->tail]);
kbd->len++;
kbd->tail = NEXTKEY(kbd->tail);
xchg(&kbdlock, 0);
tty->write(tty, key);
// kprintf("len=%d, h=%d, t=%d, rd=%d\n", kbd->len, kbd->head, kbd->tail, kbd->readable);
break;
}
tty->ioctl(tty, IOCTL_CMD_TTY_FLUSH, 0);
}
// enable_int();
}