diff options
Diffstat (limited to 'kernel/src/arch/i686/drivers')
-rw-r--r-- | kernel/src/arch/i686/drivers/ps2ctrl.c | 91 | ||||
-rw-r--r-- | kernel/src/arch/i686/drivers/ps2kb.c | 124 | ||||
-rw-r--r-- | kernel/src/arch/i686/drivers/ps2mouse.c | 81 | ||||
-rw-r--r-- | kernel/src/arch/i686/drivers/rtc.c | 152 | ||||
-rw-r--r-- | kernel/src/arch/i686/drivers/vga.c | 50 |
5 files changed, 498 insertions, 0 deletions
diff --git a/kernel/src/arch/i686/drivers/ps2ctrl.c b/kernel/src/arch/i686/drivers/ps2ctrl.c new file mode 100644 index 0000000..794aabd --- /dev/null +++ b/kernel/src/arch/i686/drivers/ps2ctrl.c @@ -0,0 +1,91 @@ +#include <panic.h> +#include <arch/i686/drivers/ps2ctrl.h> +#include <arch/i686/pic.h> +#include <arch/i686/asm.h> + +#define STATUS_OUT_BUF ((uint8_t)0x01) +#define STATUS_IN_BUF ((uint8_t)0x02) + +#define CONFIG_INT_0 ((uint8_t)0x01) +#define CONFIG_INT_1 ((uint8_t)0x02) +#define CONFIG_SYS ((uint8_t)0x04) +#define CONFIG_CLOCK_0 ((uint8_t)0x10) +#define CONFIG_CLOCK_1 ((uint8_t)0x20) +#define CONFIG_TRANS ((uint8_t)0x40) + +static bool is_init = false; + +uint8_t ps2ctrl_in(void) { + while((ps2ctrl_in_status() & STATUS_OUT_BUF) == 0) { + io_wait(); + } + return inb(0x60); +} + +uint8_t ps2ctrl_in_status(void) { + return inb(0x64); +} + +void ps2ctrl_out_cmd(uint8_t cmd) { + while((ps2ctrl_in_status() & STATUS_IN_BUF) != 0) { + io_wait(); + } + outb(0x64, cmd); +} + +void ps2ctrl_out_data(uint8_t data) { + while((ps2ctrl_in_status() & STATUS_IN_BUF) != 0) { + io_wait(); + } + outb(0x60, data); +} + +void ps2ctrl_set_port2(void) { + outb(0x64, 0xD4); +} + +void ps2ctrl_init(void) { + + is_init = false; + + pic_mask(1); // keyboard + pic_mask(12); // mouse + + inb(0x60); + + // self-test + ps2ctrl_out_cmd(0xAA); + uint8_t response = ps2ctrl_in(); + if(response != 0x55) { + panic("PS/2 controller failed to initialize"); + } + + // set config + ps2ctrl_out_cmd(0x20); + uint8_t config = ps2ctrl_in(); + config = (config | CONFIG_INT_0 | CONFIG_INT_1) & ~CONFIG_TRANS; + // config = 0xFF; + ps2ctrl_out_cmd(0x60); + ps2ctrl_out_data(config); + + // enable port 0 + ps2ctrl_out_cmd(0xAE); + + // enable port 2 + ps2ctrl_out_cmd(0xA9); + response = ps2ctrl_in(); + if (response == 0x01) { + panic("PS/2 port 2 not supported"); + } + + ps2ctrl_out_cmd(0xA8); + + is_init = true; + + pic_unmask(1); + pic_unmask(12); +} + +bool ps2ctrl_is_init(void) { + return is_init; +} diff --git a/kernel/src/arch/i686/drivers/ps2kb.c b/kernel/src/arch/i686/drivers/ps2kb.c new file mode 100644 index 0000000..50bdfb6 --- /dev/null +++ b/kernel/src/arch/i686/drivers/ps2kb.c @@ -0,0 +1,124 @@ +#include <panic.h> +#include <arch/i686/drivers/ps2ctrl.h> +#include <arch/i686/pic.h> +#include <arch/i686/asm.h> +#include <drivers/ps2kb.h> + +#define BUFFER_LEN 16 + +#define KEYCODE_ARRAY_LEN 0x84 + +static uint8_t scancodes[] = { +// 00/08 01/09 02/0A 03/0B 04/0C 05/0D 06/0E 07/0F +/*00*/ KEY_NONE, KEY_F9, KEY_NONE, KEY_F5, KEY_F3, KEY_F1, KEY_F2, KEY_F12, +/*08*/ KEY_NONE, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_BACKTICK, KEY_NONE, +/*10*/ KEY_NONE, KEY_L_ALT, KEY_L_SHIFT, KEY_NONE, KEY_L_CTRL, KEY_Q, KEY_1, KEY_NONE, +/*18*/ KEY_NONE, KEY_NONE, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_NONE, +/*20*/ KEY_NONE, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_NONE, +/*28*/ KEY_NONE, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_NONE, +/*30*/ KEY_NONE, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_NONE, +/*38*/ KEY_NONE, KEY_NONE, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_NONE, +/*40*/ KEY_NONE, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_NONE, +/*48*/ KEY_NONE, KEY_PERIOD, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_NONE, +/*50*/ KEY_NONE, KEY_NONE, KEY_QUOTE, KEY_NONE, KEY_L_BRACE, KEY_EQUAL, KEY_NONE, KEY_NONE, +/*58*/ KEY_CAPS_LOCK, KEY_R_SHIFT, KEY_ENTER, KEY_R_BRACE, KEY_NONE, KEY_BACKSLASH, KEY_NONE, KEY_NONE, +/*60*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_BACKSPACE, KEY_NONE, +/*68*/ KEY_NONE, KEY_NP_1, KEY_NONE, KEY_NP_4, KEY_NP_7, KEY_NONE, KEY_NONE, KEY_NONE, +/*70*/ KEY_NP_0, KEY_NP_PERIOD, KEY_NP_2, KEY_NP_5, KEY_NP_6, KEY_NP_8, KEY_ESCAPE, KEY_NUM_LOCK, +/*78*/ KEY_F11, KEY_NP_PLUS, KEY_NP_3, KEY_NP_MINUS, KEY_NP_ASTERISK, KEY_NP_9, KEY_SCROLL_LOCK, KEY_NONE, +/*80*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_F7, +}; +static uint8_t scancodes_ext[] = { +// 00/08 01/09 02/0A 03/0B 04/0C 05/0D 06/0E 07/0F +/*00*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*08*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*10*/ KEY_UNKNOWN, KEY_R_ALT, KEY_PRINT_SCREEN, KEY_NONE, KEY_R_CTRL, KEY_UNKNOWN, KEY_NONE, KEY_NONE, +/*18*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_L_META, +/*20*/ KEY_UNKNOWN, KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_R_META, +/*28*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_MENU, +/*30*/ KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_UNKNOWN, +/*38*/ KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_UNKNOWN, +/*40*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*48*/ KEY_UNKNOWN, KEY_NONE, KEY_NP_SLASH, KEY_NONE, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, +/*50*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*58*/ KEY_NONE, KEY_NONE, KEY_NP_ENTER, KEY_NONE, KEY_NONE, KEY_NONE, KEY_UNKNOWN, KEY_NONE, +/*60*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*68*/ KEY_NONE, KEY_END, KEY_NONE, KEY_LEFT, KEY_HOME, KEY_NONE, KEY_NONE, KEY_NONE, +/*70*/ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_NONE, KEY_RIGHT, KEY_UP, KEY_NONE, KEY_NONE, +/*78*/ KEY_NONE, KEY_NONE, KEY_PAGE_DOWN, KEY_NONE, KEY_NONE, KEY_PAGE_UP, KEY_NONE, KEY_NONE, +/*80*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +}; + +static struct Keycode last_keycode; + +static bool is_init = false; +static bool state_keyup = false; +static bool state_ext = false; + +void ps2kb_init(void) { + + is_init = false; + pic_mask(1); + + uint8_t result; + + ps2ctrl_out_data(0xFF); + result = ps2ctrl_in(); + if(result != 0xFA) { + panic("Failed to reset PS/2 keyboard: expected 0xFA, got 0x%X\n", result); + return; + } + result = ps2ctrl_in(); + if(result != 0xAA) { + panic("Failed to reset PS/2 keyboard: expected 0xAA, got 0x%X\n", result); + return; + } + + ps2ctrl_out_data(0xF4); + result = ps2ctrl_in(); + if(result != 0xFA) { + panic("Failed to enable PS/2 keyboard: expected 0xFA, got 0x%X\n", result); + return; + } + + pic_unmask(1); + is_init = true; +} + +void ps2kb_recv(void) { + if(!ps2ctrl_is_init() || !is_init) { + inb(0x60); + return; + } + uint8_t code = ps2ctrl_in(); + if (code == 0x00 || code == 0x0F) { + last_keycode.key = KEY_NONE; + last_keycode.flags = KC_FLAG_ERROR; + } else if(code == 0xF0) { + state_keyup = true; + } else if(code == 0xE0) { + state_ext = true; + } else if(code <= KEYCODE_ARRAY_LEN) { + uint8_t *scancode_table = state_ext ? scancodes_ext : scancodes; + uint8_t keycode = scancode_table[code]; + if(keycode != KEY_NONE) { + last_keycode.key = keycode; + last_keycode.flags = state_keyup ? KC_FLAG_KEY_UP : KC_FLAG_KEY_DOWN; + } + state_keyup = false; + state_ext = false; + } +} + +struct Keycode ps2kb_get(void) { + struct Keycode code; + if(is_init) { + code = last_keycode; + } else { + code.key = KEY_NONE; + code.flags = KC_FLAG_ERROR; + } + last_keycode.key = KEY_NONE; + last_keycode.flags = 0; + return code; +} diff --git a/kernel/src/arch/i686/drivers/ps2mouse.c b/kernel/src/arch/i686/drivers/ps2mouse.c new file mode 100644 index 0000000..d1082a1 --- /dev/null +++ b/kernel/src/arch/i686/drivers/ps2mouse.c @@ -0,0 +1,81 @@ +#include <panic.h> +#include <arch/i686/drivers/ps2ctrl.h> +#include <arch/i686/pic.h> +#include <arch/i686/asm.h> +#include <drivers/ps2mouse.h> + +static bool is_init = false; + +static struct MouseEvent last_event; +static uint8_t first_b, second_b, third_b; + +void ps2mouse_init(void) { + + is_init = false; + pic_mask(12); + + uint8_t result; + + ps2ctrl_set_port2(); + ps2ctrl_out_data(0xFF); + result = ps2ctrl_in(); + if (result != 0xFA) { + panic("Failed to reset PS/2 mouse: expected 0xFA, got 0x%X", result); + return; + } + result = ps2ctrl_in(); + if (result != 0xAA) { + panic("Failed to reset PS/2 mouse: expected 0xAA, got 0x%X", result); + return; + } + + ps2ctrl_set_port2(); + ps2ctrl_out_data(0xF4); + + pic_unmask(12); + is_init = true; +} + +static uint8_t packet_num = 0; +void ps2mouse_recv(void) { + if (!ps2ctrl_is_init() || !is_init) { + inb(0x60); + return; + } + + uint8_t packet = ps2ctrl_in(); + switch (packet_num) { + case 0: + first_b = packet; + break; + case 1: + second_b = packet; + break; + case 2: { + third_b = packet; + + int state, d; + state = first_b; + d = second_b; + last_event.relx = d - ((state << 4) & 0x100); + d = third_b; + last_event.rely = d - ((state << 3) & 0x100); + + last_event.lmb = first_b & 0x01; + last_event.rmb = first_b & 0x02; + last_event.mmb = first_b & 0x04; + last_event.updated = true; + break; + } + } + + packet_num += 1; + packet_num %= 3; + +} + +struct MouseEvent ps2mouse_get(void) { + struct MouseEvent event = last_event; + last_event.updated = false; + return event; +} diff --git a/kernel/src/arch/i686/drivers/rtc.c b/kernel/src/arch/i686/drivers/rtc.c new file mode 100644 index 0000000..ba69d15 --- /dev/null +++ b/kernel/src/arch/i686/drivers/rtc.c @@ -0,0 +1,152 @@ +#include "time.h" +#include <stdint.h> +#include <sys.h> +#include <arch/i686/drivers/rtc.h> +#include <arch/i686/asm.h> + +#define CMOS_WRITE_PORT 0x70 +#define CMOS_READ_PORT 0x71 + +#define CMOS_REG_SEC 0x00 +#define CMOS_REG_MIN 0x02 +#define CMOS_REG_HOUR 0x04 +#define CMOS_REG_WDAY 0x06 +#define CMOS_REG_MDAY 0x07 +#define CMOS_REG_MON 0x08 +#define CMOS_REG_YEAR 0x09 +#define CMOS_REG_CEN 0x32 + +// Live buffers to work on data +static struct Time time; +static struct Time localtime; + +// Front buffers so interupts dont request data that is half done +static struct Time cur_time; +static struct Time cur_localtime; + +// Current set Time Zone +static enum Timezone last_timezone = UTC; + +static uint8_t cmos_read(uint8_t reg) { + uint8_t hex, ret; + + outb(CMOS_WRITE_PORT, reg); + hex = inb(CMOS_READ_PORT); + + ret = hex & 0x0F; + ret += (hex & 0xF0) / 16 * 10; + + return ret; +} + +static int mday_offset[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static void update_localtime(void) { + + int change, max; + + // set localtime + localtime = time; + + // if tz is UTC, we dont need to do anythin + if (last_timezone == UTC) { + cur_localtime = localtime; + return; + } + + // check if day rolled over + change = localtime.hour < 0 ? -1 : localtime.hour >= 24 ? 1 : 0; + if (!change) return; + + // roll over day + localtime.hour = (localtime.hour + 24) % 24; + localtime.wday = (localtime.wday + change + 7) % 7; + localtime.mday += change; + localtime.yday += change; + + // check if month rolled over + max = month_days[localtime.mon]; + if (localtime.leap && localtime.mon == 1) max++; + change = localtime.mday < 0 ? -1 : localtime.mday >= max ? 1 : 0; + if (!change) return; + + // roll over month + localtime.mon = (localtime.mon + change + 12) % 12; + + // check if year rolled over + max = localtime.leap ? 366 : 365; + change = localtime.yday < 0 ? -1 : localtime.yday >= max ? 1 : 0; + if (!change) return; + + // roll over year + localtime.yn += change; + + // check if cen rolled over + change = localtime.yn < 0 ? -1 : localtime.yn >= 100 ? 1 : 0; + if (!change) goto year; + + // roll over cen + localtime.cen += change; + + +year: + + localtime.year = localtime.yn + localtime.cen * 100; + localtime.leap = localtime.year % 4 == 0 && localtime.year % 100 != 0; + + if (localtime.leap && localtime.yday == -1) + localtime.yday = 365; + else if (localtime.yday == -1) + localtime.yday = 364; + else + localtime.yday = 0; + + localtime.year -= 1900; + + cur_localtime = localtime; + +} + +void rtc_update(void) { + time.sec = cmos_read(CMOS_REG_SEC); + time.min = cmos_read(CMOS_REG_MIN); + time.hour = cmos_read(CMOS_REG_HOUR); + time.wday = cmos_read(CMOS_REG_WDAY) - 1; + time.mday = cmos_read(CMOS_REG_MDAY); + time.mon = cmos_read(CMOS_REG_MON) - 1; + time.yn = cmos_read(CMOS_REG_YEAR); + time.cen = 20; + + time.year = time.yn + time.cen * 100; + + time.leap = time.year % 4 == 0 && time.year % 100 != 0; + + time.yday = mday_offset[time.mon] + time.mday; + + if (time.leap && time.mon > 2) + time.yday++; + + time.year -= 1900; + + update_localtime(); + + cur_time = time; +} + +struct Time rtc_utctime(void) { + return cur_time; +} + +struct Time rtc_localtime(enum Timezone tz) { + if (tz != last_timezone) { + last_timezone = tz; + update_localtime(); + } + return cur_localtime; +} diff --git a/kernel/src/arch/i686/drivers/vga.c b/kernel/src/arch/i686/drivers/vga.c new file mode 100644 index 0000000..a41ce54 --- /dev/null +++ b/kernel/src/arch/i686/drivers/vga.c @@ -0,0 +1,50 @@ +#include <arch/i686/asm.h> +#include <drivers/vga.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +static uint16_t *buffer = (uint16_t*) 0xC03FF000; +static uint8_t start, end; + +void vgatext_write(char c, enum vga_color color, uint8_t x, uint8_t y) { + const size_t index = y * VGA_TEXT_W + x; + buffer[index] = c | (uint16_t) color << 8; +} + +void vgatext_write_data(uint16_t data, uint16_t index) { + buffer[index] = data; +} + +void vgatext_write_buf(const uint16_t *src) { + memcpy(buffer, src, VGA_TEXT_W * VGA_TEXT_H * sizeof(uint16_t)); +} + +void vgatext_cur_mov(uint8_t x, uint8_t y) { +; const uint16_t pos = y * VGA_TEXT_W + x; + + outb(0x3D4, 0x0F); + outb(0x3D5, (uint8_t) (pos & 0xFF)); + outb(0x3D4, 0x0E); + outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); +} + +void vgatext_cur_resize(uint8_t s, uint8_t e) { + start = s; + end = e; + + outb(0x3D4, 0x0A); + outb(0x3D5, (inb(0x3D5) & 0xC0) | start); + + outb(0x3D4, 0x0B); + outb(0x3D5, (inb(0x3D5) & 0xE0) | end); +} + +void vgatext_cur_visible(bool visible) { + if (visible) { + vgatext_cur_resize(start, end); + } else { + outb(0x3D4, 0x0A); + outb(0x3D5, 0x20); + } +} |