CeDOS - src/kernel/drivers/ps2_keyboard.c

src/kernel/drivers/ps2_keyboard.c (view raw)

#include "cedos/drivers/keyboard.h"
#include "cedos/interrupts.h"
#include "cedos/pic.h"
#include "cedos/core.h"
#include "cedos/sched/sched.h"
#include "cedos/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;
}