src/kernel/drivers/ps2_keyboard.c (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
#include "cedos/drivers/keyboard.h"
#include "cedos/interrupts.h"
#include "cedos/pic.h"
#include "cedos/core.h"
#include "cedos/sched/sched.h"
#include "assembly.h"
#define PS2_DATA 0x60
#define PS2_COMMAND 0x64
#define PS2_READ_CONF 0x20
#define PS2_WRTE_CONF 0x60
#define PS2_DISABLE_A 0xAD
#define PS2_DISABLE_B 0xA7
#define PS2_ENABLE_A 0xAE
#define PS2_ENABLE_B 0xA8
#define PS2_SUCCESS 0xFA
#define PS2_FAILURE 0xFC
#define BUFFER_LENGTH (128)
/*!
* Initializes the PS/2 keyboard.
* \return 1 on success, 0 on fail
*/
int ps2_kb_init(void);
/*!
* Reads a single character from the PS/2 keyboard
* \return A single char corresponding to a key press.
*/
uint32_t ps2_kb_read(void);
//! PS/2 keyboard driver (default driver)
KB_DRIVER ps2_kb = {
"PS/2 keyboard driver",
ps2_kb_init,
ps2_kb_read
};
uint8_t buffer[BUFFER_LENGTH];
uint32_t buffer_head, buffer_tail;
__attribute__((always_inline)) inline void buffer_enqueue(uint8_t value) {
buffer[buffer_head] = value;
buffer_head = (buffer_head + 1) % BUFFER_LENGTH;
if (buffer_head == buffer_tail) {
buffer_tail = (buffer_tail + 1) % BUFFER_LENGTH;
}
}
__attribute__((always_inline)) inline uint8_t buffer_dequeue(void) {
uint8_t res = buffer[buffer_tail];
buffer_tail = (buffer_tail + 1) % BUFFER_LENGTH;
return res;
}
__attribute__((always_inline)) inline int buffer_empty(void) {
return (buffer_head == buffer_tail);
}
__attribute__((interrupt)) void keyboard_int_handler(INTERRUPT_FRAME *frame) {
while (inb(PS2_COMMAND) & 0x01) {
nop(); nop(); nop(); nop();
buffer_enqueue(inb(PS2_DATA));
nop(); nop(); nop(); nop();
}
pic1_eoi();
}
int ps2_kb_init(void) {
// clear incoming data
inb(PS2_DATA);
nop(); nop(); nop(); nop();
// get keyboard configuration
outb(PS2_READ_CONF, PS2_COMMAND);
nop(); nop(); nop(); nop();
uint8_t conf = inb(PS2_DATA);
// set keyboard configuration
conf = (conf | 0x01) & 0x75;
outb(PS2_WRTE_CONF, PS2_COMMAND);
nop(); nop(); nop(); nop();
outb(conf, PS2_DATA);
install_interrupt(PIC1_IRQ(0x01), keyboard_int_handler, 0x08, INT_GATE);
pic_unmask_interrupt(0x01);
return 1;
}
uint32_t ps2_kb_read(void) {
while (buffer_empty()) {
sched_yield();
}
return buffer_dequeue();
}