#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 "spinlock.h" #include "x86.h" #include "stdio.h" #include "assert.h" #include "minix_keymap.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 MouseState mouse_state; static u8 get_byte_from_kb_buf(); static void set_leds(); static void set_mouse_leds(); static void kb_wait(); static void ps2_push(NTTY* tty, u32 key); static void kbd_process(unsigned char scode); // 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); #ifdef DEBUGNEW kbd_process(scan_code); // kprintf("shit"); #else 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++; } #endif }; void mouse_handler(int irq){ u8 scan_code = inb(PS2_PORT_DATA); static int aux_state = 0; int i; u32 pack; if (mouse_in.count == 0 && !(scan_code & 0x08)) return; // resync mouse_in.buf[mouse_in.count++] = scan_code; if (mouse_in.count < 4) return; mouse_in.count = 0; for (i = 0; i < 3; i++) { pack = ISMOUSE | MOUSEBTN; if ((aux_state ^ mouse_in.buf[0]) & (1 << i)) { aux_state ^= (1 << i); pack |= !!(aux_state & (1 << i)) ? MOUSEBTN_CLICK : MOUSEBTN_RELEASE; pack |= (1 << i); ps2_push(&ntty_table[cur_ntty], pack); } } pack = ISMOUSE | MOUSESCR; if ((signed char)mouse_in.buf[3] > 0) { // actually it is 0x1 pack |= MOUSESCR_DOWN; ps2_push(&ntty_table[cur_ntty], pack); } else if ((signed char)mouse_in.buf[3] < 0) { // actually it is 0xff pack |= MOUSESCR_UP; ps2_push(&ntty_table[cur_ntty], pack); } for (i = 0; i < 2; i++) { pack = mouse_in.buf[1 + i]; if (pack != 0) { pack |= ISMOUSE | MOUSEPOS; if (mouse_in.buf[0] & (0x10 << i)) pack |= MOUSEPOS_NEG; if (i == 1) pack |= MOUSEPOS_XY; ps2_push(&ntty_table[cur_ntty], pack); } } // kprintf("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]); } 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; set_leds(); put_irq_handler(KEYBOARD_IRQ, kb_handler); enable_irq(KEYBOARD_IRQ); init_mouse(); set_mouse_leds(); } #ifndef DEBUGNEW static int column; 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); } } } #endif 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 { // if (sticky_alt_mode && (lk & ALT_LOCK)) { // column = 2; // 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; break; case INPUT_KEY_CAPS_LOCK: if (press) caps_lock = !caps_lock; break; case INPUT_KEY_SCROLL_LOCK: if (press) scroll_lock = !scroll_lock; break; } // inputdriver_send_event(FALSE /*mouse*/, page, code, press, 0); if (press) ps2_push(&ntty_table[cur_ntty], map_key(code)); } kbd_state = 0; } /***************************************************************************** * 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); } /***************************************************************************** * 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); } static struct spinlock buflock; #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->head = kbd->tail = kbd->readable = 0; kbd->len = 0; buflock.locked = 0; } int ps2_tty_read(NTTY* tty, char* buf, int nr) { assert(tty->input_buf); keyboard_buf* kbd = (keyboard_buf*)tty->input_buf; assert(kbd->buf); int i = 0; while(1) { if (kbd->readable) { disable_int(); break; } } u8* ibuf = kbd->buf; for (; i < nr && i < kbd->readable; ++ i) { *(buf+i) = *(ibuf+kbd->head); // kprintf("read %p\n", ibuf+kbd->head); kbd->head = NEXTKEY(kbd->head); } kbd->readable -= i; kbd->len -= i; enable_int(); 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(); } static void ps2_push(NTTY* tty, u32 key) { // kprintf("%x\n", key); disable_int(); assert(tty->input_buf); keyboard_buf* kbd = (keyboard_buf*)tty->input_buf; if (key & ISMOUSE) { if (key & MOUSESCR) { if (key & MOUSESCR_UP) { // kprintf("scroll up\n"); vga_tty_scroll(tty, MOUSESCR_UP); } else { // kprintf("scroll down\n"); vga_tty_scroll(tty, MOUSESCR_DOWN); } } else if (key & MOUSEBTN) { if ((key & MOUSEBTN_CLICK) && (key & MOUSEBTN_M)) { kprintf("middle btn click %x\n", key); vga_tty_scroll(tty, 3); } } } 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: // select_console((key & 0xff) - (F1 & 0xff)); // TODO kprintf("select console %d\n", (key & 0xff) - (F1 & 0xff)); break; default: break; } } else { switch (key) { case '\r': case '\n': // escape ENTER vga_tty_write(tty, '\n'); ((u8*)kbd->buf)[kbd->tail] = '\n'; kbd->len ++; kbd->tail = NEXTKEY(kbd->tail); kbd->readable = CYCLE_SUB(kbd->head, kbd->tail, TTY_IN_BYTES); // kprintf("len=%d, h=%d, t=%d, rd=%d\n", kbd->len, kbd->head, kbd->tail, kbd->readable); break; case '\b': if (kbd->len > kbd->readable) { vga_tty_backspace(tty); kbd->len --; kbd->tail = LASTKEY(kbd->tail); } break; default: if ((key & 0xff) == 0) return; if (kbd->len == TTY_IN_BYTES - 1) return; // leave one space for ctrl ascii vga_tty_write(tty, key); ((u8*)kbd->buf)[kbd->tail] = key & 0xff; kbd->len ++; kbd->tail = NEXTKEY(kbd->tail); // kprintf("len=%d, h=%d, t=%d, rd=%d\n", kbd->len, kbd->head, kbd->tail, kbd->readable); break; } vga_tty_flush(tty); } enable_int(); } //TODO: 1. 修改键盘驱动 2. 添加测试代码 3. 添加鼠标并调试滚动