BigOS/kernel/keyboard.c
2022-12-17 18:09:51 +08:00

508 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"
static KB_INPUT kb_in;
static MOUSE_INPUT mouse_in;
static int mouse_init;
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 column;
static u8 get_byte_from_kb_buf();
static void set_leds();
static void set_mouse_leds();
static void kb_wait();
// static void kb_ack();
/*
* 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;
}
void kb_handler(int irq){
u8 scan_code = inb(PS2_PORT_DATA);
if(kb_in.count < KB_IN_BYTES){
*(kb_in.p_head) = scan_code;
kb_in.p_head++;
if(kb_in.p_head==kb_in.buf+KB_IN_BYTES){
kb_in.p_head = kb_in.buf;
}
kb_in.count++;
}
};
#define TTY_FIRST (tty_table)
#define TTY_END (tty_table+NR_CONSOLES)
void mouse_handler(int irq){
u8 scan_code = inb(PS2_PORT_DATA);
mouse_in.buf[mouse_in.count]=scan_code;
if ((mouse_in.buf[0] & 0xc8) == 0x08)
mouse_in.count++; // simple trick to sync mouse data
if(mouse_in.count==4){
// printf("0x%02x 0x%02x 0x%02x 0x%02x\n", mouse_in.buf[0], mouse_in.buf[1], mouse_in.buf[2], mouse_in.buf[3]);
TTY* p_tty;
for (p_tty = TTY_FIRST; p_tty < TTY_END; p_tty++) {
if(p_tty->console==&console_table[current_console]){
p_tty->mouse_left_button = mouse_in.buf[0]&0x01;
u8 mid_button = mouse_in.buf[0]&0b100;
if(mid_button==0b100){
p_tty->mouse_mid_button = 1;
}else{
p_tty->mouse_mid_button = 0;
}
if(p_tty->mouse_left_button){
u8 dir_Y = mouse_in.buf[0]&0x20;
u8 dir_X = mouse_in.buf[0]&0x10;
if(dir_Y==0x20){//down
p_tty->mouse_Y -= 1;
}else{//up
p_tty->mouse_Y += 1;
}
if(dir_X==0x10){//left
p_tty->mouse_X -= 1;
}else{//right
p_tty->mouse_X += 1;
}
}
}
}
mouse_in.count=0;
}
}
void init_mouse(){
mouse_in.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);
}
void init_kb(){
kb_in.count = 0;
kb_in.p_head = kb_in.p_tail = kb_in.buf;
shift_l = shift_r = 0;
alt_l = alt_r = 0;
ctrl_l = ctrl_r = 0;
caps_lock = 0;
num_lock = 1;
scroll_lock = 0;
column = 0;
set_leds();
put_irq_handler(KEYBOARD_IRQ, kb_handler);
enable_irq(KEYBOARD_IRQ);
init_mouse();
set_mouse_leds();
}
void keyboard_read(TTY* p_tty)
{
u8 scan_code;
/**
* 1 : make
* 0 : break
*/
int make;
/**
* We use a integer to record a key press.
* For instance, if the key HOME is pressed, key will be evaluated to
* `HOME' defined in keyboard.h.
*/
u32 key = 0;
/**
* This var points to a row in keymap[]. I don't use two-dimension
* array because I don't like it.
*/
u32* keyrow;
while (kb_in.count > 0) {
code_with_E0 = 0;
scan_code = get_byte_from_kb_buf();
/* parse the scan code below */
if (scan_code == 0xE1) {
int i;
u8 pausebreak_scan_code[] = {0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5};
int is_pausebreak = 1;
for (i = 1; i < 6; i++) {
if (get_byte_from_kb_buf() != pausebreak_scan_code[i]) {
is_pausebreak = 0;
break;
}
}
if (is_pausebreak) {
key = PAUSEBREAK;
}
}
else if (scan_code == 0xE0) {
code_with_E0 = 1;
scan_code = get_byte_from_kb_buf();
/* PrintScreen is pressed */
if (scan_code == 0x2A) {
code_with_E0 = 0;
if ((scan_code = get_byte_from_kb_buf()) == 0xE0) {
code_with_E0 = 1;
if ((scan_code = get_byte_from_kb_buf()) == 0x37) {
key = PRINTSCREEN;
make = 1;
}
}
}
/* PrintScreen is released */
else if (scan_code == 0xB7) {
code_with_E0 = 0;
if ((scan_code = get_byte_from_kb_buf()) == 0xE0) {
code_with_E0 = 1;
if ((scan_code = get_byte_from_kb_buf()) == 0xAA) {
key = PRINTSCREEN;
make = 0;
}
}
}
}
if ((key != PAUSEBREAK) && (key != PRINTSCREEN)) {
int caps;
/* make or break */
make = (scan_code & FLAG_BREAK ? 0 : 1);
keyrow = &keymap[(scan_code & 0x7F) * MAP_COLS];
column = 0;
caps = shift_l || shift_r;
if (caps_lock &&
keyrow[0] >= 'a' && keyrow[0] <= 'z')
caps = !caps;
if (caps)
column = 1;
if (code_with_E0)
column = 2;
key = keyrow[column];
switch(key) {
case SHIFT_L:
shift_l = make;
break;
case SHIFT_R:
shift_r = make;
break;
case CTRL_L:
ctrl_l = make;
break;
case CTRL_R:
ctrl_r = make;
break;
case ALT_L:
alt_l = make;
break;
case ALT_R:
alt_l = make;
break;
case CAPS_LOCK:
if (make) {
caps_lock = !caps_lock;
set_leds();
}
break;
case NUM_LOCK:
if (make) {
num_lock = !num_lock;
set_leds();
}
break;
case SCROLL_LOCK:
if (make) {
scroll_lock = !scroll_lock;
set_leds();
}
break;
default:
break;
}
}
if(make){ /* Break Code is ignored */
int pad = 0;
/* deal with the numpad first */
if ((key >= PAD_SLASH) && (key <= PAD_9)) {
pad = 1;
switch(key) { /* '/', '*', '-', '+',
* and 'Enter' in num pad
*/
case PAD_SLASH:
key = '/';
break;
case PAD_STAR:
key = '*';
break;
case PAD_MINUS:
key = '-';
break;
case PAD_PLUS:
key = '+';
break;
case PAD_ENTER:
key = ENTER;
break;
default:
/* the value of these keys
* depends on the Numlock
*/
if (num_lock) { /* '0' ~ '9' and '.' in num pad */
if (key >= PAD_0 && key <= PAD_9)
key = key - PAD_0 + '0';
else if (key == PAD_DOT)
key = '.';
}
else{
switch(key) {
case PAD_HOME:
key = HOME;
break;
case PAD_END:
key = END;
break;
case PAD_PAGEUP:
key = PAGEUP;
break;
case PAD_PAGEDOWN:
key = PAGEDOWN;
break;
case PAD_INS:
key = INSERT;
break;
case PAD_UP:
key = UP;
break;
case PAD_DOWN:
key = DOWN;
break;
case PAD_LEFT:
key = LEFT;
break;
case PAD_RIGHT:
key = RIGHT;
break;
case PAD_DOT:
key = DELETE;
break;
default:
break;
}
}
break;
}
}
key |= shift_l ? FLAG_SHIFT_L : 0;
key |= shift_r ? FLAG_SHIFT_R : 0;
key |= ctrl_l ? FLAG_CTRL_L : 0;
key |= ctrl_r ? FLAG_CTRL_R : 0;
key |= alt_l ? FLAG_ALT_L : 0;
key |= alt_r ? FLAG_ALT_R : 0;
key |= pad ? FLAG_PAD : 0;
in_process(p_tty,key);
}
}
}
/*****************************************************************************
* get_byte_from_kb_buf
*****************************************************************************/
/**
* Read a byte from the keyboard buffer.
*
* @return The byte read.
*****************************************************************************/
static u8 get_byte_from_kb_buf()
{
u8 scan_code;
while (kb_in.count <= 0) {} /* wait for a byte to arrive */
disable_int(); /* for synchronization */
scan_code = *(kb_in.p_tail);
kb_in.p_tail++;
if (kb_in.p_tail == kb_in.buf + KB_IN_BYTES) {
kb_in.p_tail = kb_in.buf;
}
kb_in.count--;
enable_int(); /* for synchronization */
return scan_code;
}
/*****************************************************************************
* kb_wait
*****************************************************************************/
/**
* 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);
}
/*****************************************************************************
* kb_ack
*****************************************************************************/
/**
* Read from the keyboard controller until a KB_ACK is received.
*
*****************************************************************************/
// static void kb_ack()
// {
// u8 kb_read;
// do {
// kb_read = inb(KB_DATA);
// } while (kb_read != KB_ACK);
// }
/*****************************************************************************
* set_leds
*****************************************************************************/
/**
* 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);
}
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);
}