Skip to content

Commit

Permalink
Implement keyboard driver
Browse files Browse the repository at this point in the history
  • Loading branch information
0x9u committed Jan 29, 2025
1 parent b4026c4 commit 0806b5e
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 10 deletions.
142 changes: 139 additions & 3 deletions kernel/src/arch/x86_64/drivers/keyboard/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,148 @@

#include "../../../../io.h"
#include "../../apic/lapic.h"
#include "../../apic/ioapic.h"
#include "../../cpu/idt.h"
#include "../../serial/serial.h"

__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame) {
#include <stdbool.h>

// clang-format off
enum scan_set_one {
// todo: abbreviate names
S1_ESC = 1, S1_1, S1_2, S1_3, S1_4,
S1_5, S1_6, S1_7, S1_8, S1_9,
S1_0, S1_MINUS, S1_EQUAL, S1_BACKSPACE, S1_TAB,
S1_Q, S1_W, S1_E, S1_R, S1_T,
S1_Y, S1_U, S1_I, S1_O, S1_P, S1_LEFT_BRACKET,
S1_RIGHT_BRACKET, S1_ENTER, S1_LEFT_CTRL, S1_A, S1_S,
S1_D, S1_F, S1_G, S1_H, S1_J,
S1_K, S1_L, S1_SEMI, S1_SINGLE_QUOTE, S1_BACK_TICK,
S1_LEFT_SHIFT, S1_LEFT_SLASH, S1_Z, S1_X, S1_C,
S1_V, S1_B, S1_N, S1_M, S1_COMMA,
S1_DOT, S1_RIGHT_SLASH, S1_RIGHT_SHIFT, S1_KEYPAD_STAR,
S1_LEFT_ALT, S1_SPACE, S1_CAPSLOCK, S1_F1, S1_F2,
S1_F3, S1_F4, S1_F5, S1_F6, S1_F7,
S1_F8, S1_F9, S1_F10, S1_NUMLOCK, S1_SCROLLLOCK,
S1_KEYPAD_7, S1_KEYPAD_8, S1_KEYPAD_9, S1_KEYPAD_MINUS,
S1_KEYPAD_4, S1_KEYPAD_5, S1_KEYPAD_6, S1_KEYPAD_PLUS,
S1_KEYPAD_1, S1_KEYPAD_2, S1_KEYPAD_3, S1_KEYPAD_0,
S1_KEYPAD_DOT, S1_F11, S1_F12,
// RELEASED
// todo: make a map for each key held down
S1_ESC_RELEASED = 0x81,
S1_1_RELEASED, S1_2_RELEASED, S1_3_RELEASED, S1_4_RELEASED,
S1_5_RELEASED, S1_6_RELEASED, S1_7_RELEASED, S1_8_RELEASED,
S1_9_RELEASED, S1_0_RELEASED, S1_MINUS_RELEASED, S1_EQUALS_RELEASED,
S1_BACKSPACE_RELEASED, S1_TAB_RELEASED, S1_Q_RELEASED, S1_W_RELEASED,
S1_E_RELEASED, S1_R_RELEASED, S1_T_RELEASED, S1_Y_RELEASED, S1_U_RELEASED,
S1_I_RELEASED, S1_O_RELEASED, S1_P_RELEASED, S1_LEFT_BRACKET_RELEASED,
S1_RIGHT_BRACKET_RELEASED, S1_ENTER_RELEASED, S1_LEFT_CTRL_RELEASED, S1_A_RELEASED,
S1_S_RELEASED, S1_D_RELEASED, S1_F_RELEASED, S1_G_RELEASED, S1_H_RELEASED,
S1_J_RELEASED, S1_K_RELEASED, S1_L_RELEASED, S1_SEMI_RELEASED, S1_SINGLE_QUOTE_RELEASED,
S1_BACK_TICK_RELEASED, S1_LEFT_SHIFT_RELEASED, S1_LEFT_SLASH_RELEASED, S1_Z_RELEASED,
S1_X_RELEASED, S1_C_RELEASED, S1_V_RELEASED, S1_B_RELEASED, S1_N_RELEASED,
S1_M_RELEASED, S1_COMMA_RELEASED, S1_DOT_RELEASED, S1_RIGHT_SLASH_RELEASED,
S1_RIGHT_SHIFT_RELEASED, S1_KEYPAD_STAR_RELEASED, S1_LEFT_ALT_RELEASED, S1_SPACE_RELEASED,
S1_CAPSLOCK_RELEASED, S1_F1_RELEASED, S1_F2_RELEASED, S1_F3_RELEASED, S1_F4_RELEASED,
S1_F5_RELEASED, S1_F6_RELEASED, S1_F7_RELEASED, S1_F8_RELEASED, S1_F9_RELEASED,
S1_F10_RELEASED, S1_NUMLOCK_RELEASED, S1_SCROLLLOCK_RELEASED, S1_KEYPAD_7_RELEASED,
S1_KEYPAD_8_RELEASED, S1_KEYPAD_9_RELEASED, S1_KEYPAD_MINUS_RELEASED,
S1_KEYPAD_4_RELEASED, S1_KEYPAD_5_RELEASED, S1_KEYPAD_6_RELEASED, S1_KEYPAD_PLUS_RELEASED,
S1_KEYPAD_1_RELEASED, S1_KEYPAD_2_RELEASED, S1_KEYPAD_3_RELEASED, S1_KEYPAD_0_RELEASED,
S1_KEYPAD_DOT_RELEASED, S1_F11_RELEASED, S1_F12_RELEASED
};
// clang-format on

#define MAX_BUFFER_SIZE 512
static char buffer[MAX_BUFFER_SIZE];
static size_t buffer_pos = 0;

static __attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame);

static uint8_t keyboard_code_set_1(const uint8_t data);

static bool caps_lock = 0;

// TODO: use acpi to check if 8042 ps/2 controller is supported
// todo: poll for keyboard status
// todo: check keyboard type (1, 2, 3)
// todo: detect if key is capital or nah (need to detect caps lock and shift)

void keyboard_init(void) {
// Disable any devices connected to ps/2 ports
//outb(0x64, 0xAD);
//outb(0x64, 0xA7);
// todo: do the steps specified by osdev later

int apic_id = apic_get_id();
ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 1, 0x21, apic_id);
setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0);
}

// Keycodes from: https://wiki.osdev.org/PS/2_Keyboard
// CODE SET 1
// ! for some reason 2,3,4 in qemu r different keys
static uint8_t keyboard_code_set_1(const uint8_t data) {
if (data >= S1_1 && data <= S1_EQUAL) {
return "1234567890-="[data - S1_1];
} else if (data >= S1_Q && data <= S1_ENTER) {
return "QWERTYUIOP[]\n"[data - S1_Q];
} else if (data >= S1_A && data <= S1_BACK_TICK) {
return "ASDFGHJKL;'`"[data - S1_A];
} else if (data >= S1_LEFT_SLASH && data <= S1_RIGHT_SLASH) {
return "\\ZXCVBNM,./"[data - S1_LEFT_SLASH];
} else if (data >= S1_KEYPAD_7 && data <= S1_KEYPAD_DOT) {
return "789-456+1230."[data - S1_KEYPAD_7];
} else if (data == S1_SPACE) {
return ' ';
} else if (data == S1_KEYPAD_STAR) {
return '*';
} else if (data == S1_CAPSLOCK) {
caps_lock = !caps_lock;
// enable caps lock LED
if (caps_lock) {
outb(0xED, inb(0xED) | (1 << 2));
} else {
outb(0xED, inb(0xED) & ~(1 << 2));
}
}

return data;
}

// TODO: have switch for different code sets
static char keyboard_convert_code(char data) {
return keyboard_code_set_1(data);
}

// ! figure out how to make it pause/wait for getchar();

// todo: WRITE A FRONTEND FUNCTION FOR KLIB
char getc(void) {
while (buffer_pos == 0) {
__builtin_ia32_pause();
}
char data = buffer[--buffer_pos];
putc(data);
return data;
}

void getstr(char *str, size_t len) {
for (size_t i = 0; i < len; i++) {
str[i] = getc();
}
}

static void keyboard_handler(struct interrupt_frame *frame) {
// Consume a key, not doing this will only let
// the interrupt trigger once
inb(0x60);
puts("Keyboard pressed");
uint8_t data = keyboard_code_set_1(inb(0x60));
if (data >= 'A' && data <= 'Z' && !caps_lock) data += 32;
if (data >= ' ' && data <= '~') buffer[buffer_pos++] = data;
// ! minor bug: could loop over and getc wont be able to get the prev 512 chars
buffer_pos %= MAX_BUFFER_SIZE;
//if (data >= ' ' && data <= '~')
// krintf("Keyboard pressed %c\n", data);
send_apic_eoi();
}
10 changes: 8 additions & 2 deletions kernel/src/arch/x86_64/drivers/keyboard/keyboard.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#pragma once

#include <stddef.h>

struct interrupt_frame;

// Keyboard Handler
void keyboard_handler(struct interrupt_frame *frame);
// Initialise the keyboard driver
void keyboard_init(void);

char getc(void);

void getstr(char *str, size_t len);
2 changes: 1 addition & 1 deletion kernel/src/arch/x86_64/usermode/usermode.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ struct gdt {
} __attribute__((packed));

// Enters usermode
void enter_usermode(void *function_address);
void _Noreturn enter_usermode(void *function_address);
53 changes: 53 additions & 0 deletions kernel/src/arch/x86_64/vga/vga.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "vga.h"

#include "../serial/serial.h"

#include <stddef.h>
#include <stdint.h>

Expand Down Expand Up @@ -91,10 +93,61 @@ void vga_putchar(char c) {
break;
}
}
vga_cursor_update(terminal_column, terminal_row);
}

void vga_write_string(const char *data) {
while (*data) {
vga_putchar(*data++);
}
}

// Found names here: http://www.osdever.net/FreeVGA/vga/crtcreg.htm
#define CURSOR_ADDRESS_REGISTER 0x3D4
#define CURSOR_INDEX_REGISTER 0x3D5

// Cursor address
#define CURSOR_START 0x0A
#define CURSOR_END 0x0B

#define CURSOR_LOCATION_HIGH 0x0E
#define CURSOR_LOCATION_LOW 0x0F

void vga_cursor_enable(size_t start, size_t end) {
// Writes to CURSOR_ADDRESS_REGISTER first
// to specify address

// start determines start of cursor height
// end determines end of cursor height

outb(CURSOR_ADDRESS_REGISTER, CURSOR_START);
// retain bits 6 and 7 because they are reserved.
outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | (uint8_t) (start & 0x3F));

outb(CURSOR_ADDRESS_REGISTER, CURSOR_END);
// retain bits 5, 6, 7 because they are reserved.
outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xE0 | (uint8_t) (end & 0x1f));
}

void vga_cursor_disable(void) {
outb(CURSOR_ADDRESS_REGISTER, CURSOR_START);
// set 5th bit to 1 to disable
outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | 0x20);
}

void vga_cursor_update(size_t x, size_t y) {
// todo: ensure 8 bits
uint16_t pos = y * VGA_WIDTH + x;
outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_LOW);
outb(CURSOR_INDEX_REGISTER, (uint8_t) (pos & 0xFF));
outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_HIGH);
outb(CURSOR_INDEX_REGISTER, (uint8_t) ((pos >> 8) & 0xFF));
}

uint16_t vga_cursor_position(void) {
uint16_t pos = 0;
outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_LOW);
pos |= inb(CURSOR_INDEX_REGISTER);
outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_HIGH);
pos |= inb(CURSOR_INDEX_REGISTER);
}
9 changes: 9 additions & 0 deletions kernel/src/arch/x86_64/vga/vga.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,12 @@ void vga_putchar(char c);

// Prints a null-terminated string
void vga_write_string(const char *data);

// Enables text cursor
void vga_cursor_enable(size_t start, size_t end);

// Disables text cursor
void vga_cursor_disable(void);

// Updates text cursor
void vga_cursor_update(size_t x, size_t y);
18 changes: 14 additions & 4 deletions kernel/src/kernel_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,21 @@ void _Noreturn kernel_main(struct boot_parameters parameters) {
outw(0xf4, 0x10);
_die();
#else
puts("Enter a character: ");
char c = getc();
krintf("Char: %c\n", c);

puts("Enter a string: ");
char *str = kmalloc(10);
getstr(str, 9);
str[9] = '\0';
krintf("String: %s\n", str);
free(str);


char message[] = "X Hello world!\n";

for (size_t i = 0; i < 1; i++) {
for (size_t i = 0; i < 5; i++) {
message[0] = '0' + i;
i = (i + 1) % 10;

Expand Down Expand Up @@ -180,8 +192,6 @@ static void setup_idt(void) {

// system timer
ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 0, 0x20, apic_id);
// keyboard
ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 1, 0x21, apic_id);

for (size_t vector = 0; vector < 32; vector++) {
setup_interrupt_gate(vector, exception_handler, INTERRUPT_64_GATE, 0,
Expand All @@ -190,7 +200,7 @@ static void setup_idt(void) {

// we have interrupt after 31 since 0-31 are reserved for errors
setup_interrupt_gate(0x20, timer_handler, INTERRUPT_64_GATE, 0, 0);
setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0);
keyboard_init();
setup_interrupt_gate(0x80, trap, TRAP_64_GATE, 3, 0);

load_idt();
Expand Down

0 comments on commit 0806b5e

Please sign in to comment.