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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
#include "drivers/keyboard.h"
#include "interrupts.h"
#include "pic.h"
#include "core.h"
#include "sched/sched.h"
#include "mm/memory.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)
#ifdef DEBUG
#define PRINT_DBG(...) printk("[" __FILE__ "] " __VA_ARGS__)
#else
#define PRINT_DBG(...) {}
#endif
/*!
* 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.
*/
int ps2_kb_readc(void);
//! PS/2 keyboard driver (default driver)
KB_DRIVER ps2_kb = {
"PS/2 keyboard driver",
ps2_kb_init,
ps2_kb_readc
};
uint8_t *buffer;
volatile 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) {
buffer_head = 0;
buffer_tail = 0;
buffer = os_kernel_malloc(BUFFER_LENGTH);
// 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, 0x18, INT_GATE);
pic_unmask_interrupt(0x01);
return 1;
}
int ps2_kb_readc(void) {
if (buffer_empty()) {
return -1;
} else {
return (int)(buffer_dequeue());
}
}
int ps2_kb_read(int fd, char *buffer, uint32_t size) {
int res = 0;
for (int i = 0; i < size; i++) {
if (buffer_empty()) { break; }
buffer[i] = buffer_dequeue();
res++;
}
return res;
}