From 73d0b2fd3d071ca73f1b68fef94b31459839bcc1 Mon Sep 17 00:00:00 2001 From: Sam Tebbs Date: Tue, 14 Jul 2020 21:52:54 +0100 Subject: [PATCH] Add keyboard and an x86 driver --- src/kernel/arch/x86/arch.zig | 19 ++ src/kernel/arch/x86/keyboard.zig | 369 +++++++++++++++++++++++++++++++ src/kernel/heap.zig | 1 + src/kernel/keyboard.zig | 354 +++++++++++++++++++++++++++++ src/kernel/kmain.zig | 32 +++ test/mock/kernel/arch_mock.zig | 5 + 6 files changed, 780 insertions(+) create mode 100644 src/kernel/arch/x86/keyboard.zig create mode 100644 src/kernel/keyboard.zig diff --git a/src/kernel/arch/x86/arch.zig b/src/kernel/arch/x86/arch.zig index 1372a37..3de9009 100644 --- a/src/kernel/arch/x86/arch.zig +++ b/src/kernel/arch/x86/arch.zig @@ -17,9 +17,11 @@ const vga = @import("vga.zig"); const mem = @import("../../mem.zig"); const multiboot = @import("multiboot.zig"); const vmm = @import("../../vmm.zig"); +const keyboard = @import("keyboard.zig"); const Serial = @import("../../serial.zig").Serial; const panic = @import("../../panic.zig").panic; const TTY = @import("../../tty.zig").TTY; +const Keyboard = @import("../../keyboard.zig").Keyboard; const MemProfile = mem.MemProfile; /// The virtual end of the kernel code. @@ -449,6 +451,23 @@ pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile { }; } +/// +/// Initialise the keyboard that may depend on the chipset or architecture in general. +/// x86 initialises the keyboard connected to the PS/2 port +/// +/// Arguments: +/// IN allocator: *std.mem.Allocator - The allocator to use if necessary +/// +/// Return: *Keyboard +/// The initialised PS/2 keyboard +/// +/// Error: std.mem.Allocator.Error +/// OutOfMemory - There wasn't enough memory to allocate what was needed +/// +pub fn initKeyboard(allocator: *Allocator) Allocator.Error!*Keyboard { + return keyboard.init(allocator); +} + /// /// Initialise a 32bit kernel stack used for creating a task. /// Currently only support fn () noreturn functions for the entry point. diff --git a/src/kernel/arch/x86/keyboard.zig b/src/kernel/arch/x86/keyboard.zig new file mode 100644 index 0000000..3ea30b1 --- /dev/null +++ b/src/kernel/arch/x86/keyboard.zig @@ -0,0 +1,369 @@ +const builtin = @import("builtin"); +const build_options = @import("build_options"); +const std = @import("std"); +const Allocator = std.mem.Allocator; +const testing = std.testing; +const irq = @import("irq.zig"); +const pic = @import("pic.zig"); +const arch = if (builtin.is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig"); +const log = if (builtin.is_test) @import(build_options.arch_mock_path ++ "log_mock.zig") else @import("../../log.zig"); +const panic = @import("../../panic.zig").panic; +const kb = @import("../../keyboard.zig"); +const Keyboard = kb.Keyboard; +const KeyPosition = kb.KeyPosition; +const KeyAction = kb.KeyAction; + +/// The initialised keyboard +var keyboard: *Keyboard = undefined; +/// The number of keys pressed without a corresponding release +var pressed_keys: usize = 0; +/// If we're in the middle of a special key sequence (e.g. print_screen and arrow keys) +var special_sequence = false; +/// The number of release scan codes expected before registering a release event +/// This is used in special sequences since they end in a certain number of release scan codes +var expected_releases: usize = 0; +/// If a print_screen press is being processed +var on_print_screen = false; + +/// +/// Read a byte from the keyboard buffer +/// +/// Return: u8 +/// The byte waiting in the keyboard buffer +/// +fn readKeyboardBuffer() u8 { + return arch.inb(0x60); +} + +/// +/// Parse a keyboard scan code and return the associated keyboard action. +/// Some keys require a specific sequence of scan codes so this function acts as a state machine +/// +/// Arguments: +/// IN scan_code: u8 - The scan code from the keyboard +/// +/// Return: ?KeyAction +/// The keyboard action resulting from processing the scan code, or null if the scan code doesn't result in a finished keyboard action +/// +fn parseScanCode(scan_code: u8) ?KeyAction { + var released = false; + // The print screen key requires special processing since it uses a unique byte sequence + if (on_print_screen or scan_code >= 128) { + released = true; + if (special_sequence or on_print_screen) { + // Special sequences are followed by a certain number of release scan codes that should be ignored. Update the expected number + if (expected_releases >= 1) { + expected_releases -= 1; + return null; + } + } else { + if (pressed_keys == 0) { + // A special sequence is started by a lone key release scan code + special_sequence = true; + return null; + } + } + } + // Cut off the top bit, which denotes that the key was released + const key_code = @truncate(u7, scan_code); + var key_pos: ?KeyPosition = null; + if (special_sequence or on_print_screen) { + if (!released) { + // Most special sequences are followed by an extra key release byte + expected_releases = 1; + } + switch (key_code) { + 72 => key_pos = KeyPosition.UP_ARROW, + 75 => key_pos = KeyPosition.LEFT_ARROW, + 77 => key_pos = KeyPosition.RIGHT_ARROW, + 80 => key_pos = KeyPosition.DOWN_ARROW, + // First byte sent for the pause key + 29 => return null, + 42 => { + // The print screen key is followed by five extra key release bytes + key_pos = KeyPosition.PRINT_SCREEN; + if (!released) { + on_print_screen = true; + expected_releases = 5; + } + }, + // Second and final byte sent for the pause key + 69 => { + // The pause key is followed by two extra key release bytes + key_pos = KeyPosition.PAUSE; + if (!released) { + expected_releases = 2; + } + }, + 82 => key_pos = KeyPosition.INSERT, + 71 => key_pos = KeyPosition.HOME, + 73 => key_pos = KeyPosition.PAGE_UP, + 83 => key_pos = KeyPosition.DELETE, + 79 => key_pos = KeyPosition.END, + 81 => key_pos = KeyPosition.PAGE_DOWN, + 53 => key_pos = KeyPosition.KEYPAD_SLASH, + 28 => key_pos = KeyPosition.KEYPAD_ENTER, + 56 => key_pos = KeyPosition.RIGHT_ALT, + 91 => key_pos = KeyPosition.SPECIAL, + else => return null, + } + } + key_pos = key_pos orelse switch (key_code) { + 1 => KeyPosition.ESC, + // Number keys and second row + 2...28 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.ONE) + (key_code - 2)), + 29 => KeyPosition.LEFT_CTRL, + 30...40 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.A) + (key_code - 30)), + 41 => KeyPosition.BACKTICK, + 42 => KeyPosition.LEFT_SHIFT, + 43 => KeyPosition.HASH, + 44...54 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.Z) + (key_code - 44)), + 55 => KeyPosition.KEYPAD_ASTERISK, + 56 => KeyPosition.LEFT_ALT, + 57 => KeyPosition.SPACE, + 58 => KeyPosition.CAPS_LOCK, + 59...68 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.F1) + (key_code - 59)), + 69 => KeyPosition.NUM_LOCK, + 70 => KeyPosition.SCROLL_LOCK, + 71...73 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.KEYPAD_7) + (key_code - 71)), + 74 => KeyPosition.KEYPAD_MINUS, + 75...77 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.KEYPAD_4) + (key_code - 75)), + 78 => KeyPosition.KEYPAD_PLUS, + 79...81 => @intToEnum(KeyPosition, @enumToInt(KeyPosition.KEYPAD_1) + (key_code - 79)), + 82 => KeyPosition.KEYPAD_0, + 83 => KeyPosition.KEYPAD_DOT, + 86 => KeyPosition.BACKSLASH, + 87 => KeyPosition.F11, + 88 => KeyPosition.F12, + else => null, + }; + if (key_pos) |k| { + // If we're releasing a key decrement the number of keys pressed, else increment it + if (!released) { + pressed_keys += 1; + } else { + pressed_keys -= 1; + // Releasing a special key means we are no longer on that special key + special_sequence = false; + } + return KeyAction{ .position = k, .released = released }; + } + return null; +} + +/// +/// Register a keyboard action. Should only be called in response to a keyboard IRQ +/// +/// Arguments: +/// IN ctx: *arch.CpuState - The state of the CPU when the keyboard action occurred +/// +/// Return: usize +/// The stack pointer value to use when returning from the interrupt +/// +fn onKeyEvent(ctx: *arch.CpuState) usize { + const scan_code = readKeyboardBuffer(); + if (parseScanCode(scan_code)) |action| { + if (!keyboard.writeKey(action)) { + std.log.notice(.x86_keyboard, "No room for keyboard action {}\n", .{action}); + } + } + return @ptrToInt(ctx); +} + +/// +/// Initialise the PS/2 keyboard +/// +/// Arguments: +/// IN allocator: *Allocator - The allocator to use to create the keyboard instance +/// +/// Return: *Keyboard +/// The keyboard created +/// +/// Error: std.mem.Allocator.Error +/// OutOfMemory - There isn't enough memory to allocate the keyboard instance +/// +pub fn init(allocator: *Allocator) Allocator.Error!*Keyboard { + irq.registerIrq(pic.IRQ_KEYBOARD, onKeyEvent) catch |e| { + panic(@errorReturnTrace(), "Failed to register keyboard IRQ: {}\n", .{e}); + }; + keyboard = try allocator.create(Keyboard); + keyboard.* = Keyboard.init(); + return keyboard; +} + +fn testResetGlobals() void { + pressed_keys = 0; + special_sequence = false; + expected_releases = 0; + on_print_screen = false; +} + +test "parseScanCode" { + testResetGlobals(); + + // Test basic keys + const basic_keys = &[_]?KeyPosition{ + KeyPosition.ESC, + KeyPosition.ONE, + KeyPosition.TWO, + KeyPosition.THREE, + KeyPosition.FOUR, + KeyPosition.FIVE, + KeyPosition.SIX, + KeyPosition.SEVEN, + KeyPosition.EIGHT, + KeyPosition.NINE, + KeyPosition.ZERO, + KeyPosition.HYPHEN, + KeyPosition.EQUALS, + KeyPosition.BACKSPACE, + KeyPosition.TAB, + KeyPosition.Q, + KeyPosition.W, + KeyPosition.E, + KeyPosition.R, + KeyPosition.T, + KeyPosition.Y, + KeyPosition.U, + KeyPosition.I, + KeyPosition.O, + KeyPosition.P, + KeyPosition.LEFT_BRACKET, + KeyPosition.RIGHT_BRACKET, + KeyPosition.ENTER, + KeyPosition.LEFT_CTRL, + KeyPosition.A, + KeyPosition.S, + KeyPosition.D, + KeyPosition.F, + KeyPosition.G, + KeyPosition.H, + KeyPosition.J, + KeyPosition.K, + KeyPosition.L, + KeyPosition.SEMICOLON, + KeyPosition.APOSTROPHE, + KeyPosition.BACKTICK, + KeyPosition.LEFT_SHIFT, + KeyPosition.HASH, + KeyPosition.Z, + KeyPosition.X, + KeyPosition.C, + KeyPosition.V, + KeyPosition.B, + KeyPosition.N, + KeyPosition.M, + KeyPosition.COMMA, + KeyPosition.DOT, + KeyPosition.FORWARD_SLASH, + KeyPosition.RIGHT_SHIFT, + KeyPosition.KEYPAD_ASTERISK, + KeyPosition.LEFT_ALT, + KeyPosition.SPACE, + KeyPosition.CAPS_LOCK, + KeyPosition.F1, + KeyPosition.F2, + KeyPosition.F3, + KeyPosition.F4, + KeyPosition.F5, + KeyPosition.F6, + KeyPosition.F7, + KeyPosition.F8, + KeyPosition.F9, + KeyPosition.F10, + KeyPosition.NUM_LOCK, + KeyPosition.SCROLL_LOCK, + KeyPosition.KEYPAD_7, + KeyPosition.KEYPAD_8, + KeyPosition.KEYPAD_9, + KeyPosition.KEYPAD_MINUS, + KeyPosition.KEYPAD_4, + KeyPosition.KEYPAD_5, + KeyPosition.KEYPAD_6, + KeyPosition.KEYPAD_PLUS, + KeyPosition.KEYPAD_1, + KeyPosition.KEYPAD_2, + KeyPosition.KEYPAD_3, + KeyPosition.KEYPAD_0, + KeyPosition.KEYPAD_DOT, + null, + null, + KeyPosition.BACKSLASH, + KeyPosition.F11, + KeyPosition.F12, + }; + comptime var scan_code = 1; + inline for (basic_keys) |key| { + var res = parseScanCode(scan_code); + if (key) |k| { + const r = res orelse unreachable; + testing.expectEqual(k, r.position); + testing.expectEqual(false, r.released); + testing.expectEqual(pressed_keys, 1); + } + testing.expectEqual(on_print_screen, false); + testing.expectEqual(special_sequence, false); + testing.expectEqual(expected_releases, 0); + // Test release scan code for key + if (key) |k| { + res = parseScanCode(scan_code | 128); + const r = res orelse unreachable; + testing.expectEqual(k, r.position); + testing.expectEqual(true, r.released); + testing.expectEqual(pressed_keys, 0); + testing.expectEqual(on_print_screen, false); + testing.expectEqual(special_sequence, false); + testing.expectEqual(expected_releases, 0); + } + scan_code += 1; + } + + // Test the special keys + // 'simple' sepcial keys consist of one release byte, a key then an extra release byte + const simple_special_keys = &[_]?KeyPosition{ + KeyPosition.UP_ARROW, + KeyPosition.LEFT_ARROW, + KeyPosition.RIGHT_ARROW, + KeyPosition.DOWN_ARROW, + KeyPosition.INSERT, + KeyPosition.HOME, + KeyPosition.PAGE_UP, + KeyPosition.DELETE, + KeyPosition.END, + KeyPosition.PAGE_DOWN, + KeyPosition.KEYPAD_SLASH, + KeyPosition.KEYPAD_ENTER, + KeyPosition.RIGHT_ALT, + KeyPosition.SPECIAL, + }; + const simple_special_codes = &[_]u8{ 72, 75, 77, 80, 82, 71, 73, 83, 79, 81, 53, 28, 56, 91 }; + for (simple_special_keys) |key, i| { + testing.expectEqual(parseScanCode(128), null); + testing.expectEqual(pressed_keys, 0); + testing.expectEqual(on_print_screen, false); + testing.expectEqual(special_sequence, true); + testing.expectEqual(expected_releases, 0); + + var res = parseScanCode(simple_special_codes[i]) orelse unreachable; + testing.expectEqual(false, res.released); + testing.expectEqual(key, res.position); + testing.expectEqual(pressed_keys, 1); + testing.expectEqual(on_print_screen, false); + testing.expectEqual(special_sequence, true); + testing.expectEqual(expected_releases, 1); + + testing.expectEqual(parseScanCode(128), null); + testing.expectEqual(pressed_keys, 1); + testing.expectEqual(on_print_screen, false); + testing.expectEqual(special_sequence, true); + testing.expectEqual(expected_releases, 0); + + res = parseScanCode(simple_special_codes[i] | 128) orelse unreachable; + testing.expectEqual(true, res.released); + testing.expectEqual(key, res.position); + testing.expectEqual(pressed_keys, 0); + testing.expectEqual(on_print_screen, false); + testing.expectEqual(special_sequence, false); + testing.expectEqual(expected_releases, 0); + } +} diff --git a/src/kernel/heap.zig b/src/kernel/heap.zig index 9293dba..1559c98 100644 --- a/src/kernel/heap.zig +++ b/src/kernel/heap.zig @@ -543,6 +543,7 @@ const FreeListAllocator = struct { } test "resize" { + std.debug.warn("", .{}); const size = 1024; var region = try testing.allocator.alloc(u8, size); defer testing.allocator.free(region); diff --git a/src/kernel/keyboard.zig b/src/kernel/keyboard.zig new file mode 100644 index 0000000..1b00103 --- /dev/null +++ b/src/kernel/keyboard.zig @@ -0,0 +1,354 @@ +const std = @import("std"); +const testing = std.testing; +const ArrayList = std.ArrayList; +const Allocator = std.mem.Allocator; +const arch = @import("arch.zig").internals; + +/// An arbitrary number of keys to remember before dropping any more that arrive. Is a power of two so we can use nice overflowing addition +pub const QUEUE_SIZE = 32; + +/// The position of a key on a keyboard using the qwerty layout. This does not determine the key pressed, just the position on the keyboard. +pub const KeyPosition = enum(u7) { + ESC, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + PRINT_SCREEN, + SCROLL_LOCK, + PAUSE, + BACKTICK, + ONE, + TWO, + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + ZERO, + HYPHEN, + EQUALS, + BACKSPACE, + TAB, + Q, + W, + E, + R, + T, + Y, + U, + I, + O, + P, + LEFT_BRACKET, + RIGHT_BRACKET, + ENTER, + CAPS_LOCK, + A, + S, + D, + F, + G, + H, + J, + K, + L, + SEMICOLON, + APOSTROPHE, + HASH, + LEFT_SHIFT, + BACKSLASH, + Z, + X, + C, + V, + B, + N, + M, + COMMA, + DOT, + FORWARD_SLASH, + RIGHT_SHIFT, + LEFT_CTRL, + SPECIAL, + LEFT_ALT, + SPACE, + RIGHT_ALT, + FN, + SPECIAL2, + RIGHT_CTRL, + INSERT, + HOME, + PAGE_UP, + DELETE, + END, + PAGE_DOWN, + LEFT_ARROW, + UP_ARROW, + DOWN_ARROW, + RIGHT_ARROW, + NUM_LOCK, + KEYPAD_SLASH, + KEYPAD_ASTERISK, + KEYPAD_MINUS, + KEYPAD_7, + KEYPAD_8, + KEYPAD_9, + KEYPAD_PLUS, + KEYPAD_4, + KEYPAD_5, + KEYPAD_6, + KEYPAD_1, + KEYPAD_2, + KEYPAD_3, + KEYPAD_ENTER, + KEYPAD_0, + KEYPAD_DOT, +}; + +/// A keyboard action, either a press or release +pub const KeyAction = struct { + /// The position of the key + position: KeyPosition, + /// Whether it was a release or press + released: bool, +}; + +/// The type used to index the keyboard queue +const QueueIndex = std.meta.IntType(false, std.math.log2(QUEUE_SIZE)); + +/// A keyboard buffer that stores keyboard actions. This corresponds to a single hardware keyboard +pub const Keyboard = struct { + /// A circular queue storing key presses until full + queue: [QUEUE_SIZE]KeyAction, + /// The front of the queue i.e. the next item to be dequeued + queue_front: QueueIndex, + /// The end of the queue i.e. where the next item is enqueued + queue_end: QueueIndex, + + /// + /// Initialise a keyboard with an empty key buffer + /// + /// Return: Keyboard + /// They keyboard created + /// + pub fn init() Keyboard { + return .{ + .queue = [_]KeyAction{undefined} ** QUEUE_SIZE, + .queue_front = 0, + .queue_end = 0, + }; + } + + /// + /// Check if the keyboard queue is empty + /// + /// Arguments: + /// self: *const Keyboard - The keyboard to check + /// + /// Return: bool + /// True if the keyboard queue is empty, else false + /// + pub fn isEmpty(self: *const Keyboard) bool { + return self.queue_end == self.queue_front; + } + + /// + /// Check if the keyboard queue is full + /// + /// Arguments: + /// self: *const Keyboard - The keyboard to check + /// + /// Return: bool + /// True if the keyboard queue is full, else false + /// + pub fn isFull(self: *const Keyboard) bool { + var end_plus_one: QueueIndex = undefined; + // This is a circular queue so overflow is allowed + _ = @addWithOverflow(QueueIndex, self.queue_end, 1, &end_plus_one); + return end_plus_one == self.queue_front; + } + + /// + /// Add a keyboard action to the keyboard's queue + /// + /// Arguments: + /// self: *Keyboard - The keyboard whose queue it should be added to + /// key: KeyAction - The action to add + /// + /// Return: bool + /// True if there was room for the key, else false + /// + pub fn writeKey(self: *Keyboard, key: KeyAction) bool { + if (!self.isFull()) { + self.queue[self.queue_end] = key; + _ = @addWithOverflow(QueueIndex, self.queue_end, 1, &self.queue_end); + return true; + } + return false; + } + + /// + /// Read and remove the next key from the keyboard's queue + /// + /// Arguments: + /// self: *Keyboard - The keyboard to get and remove the key from + /// + /// Return: ?KeyAction + /// The first keyboard action in the queue, else null if there were none + /// + pub fn readKey(self: *Keyboard) ?KeyAction { + if (self.isEmpty()) return null; + const key = self.queue[self.queue_front]; + _ = @addWithOverflow(QueueIndex, self.queue_front, 1, &self.queue_front); + return key; + } + + test "init" { + const keyboard = Keyboard.init(); + testing.expectEqual(keyboard.queue_front, 0); + testing.expectEqual(keyboard.queue_end, 0); + } + + test "isEmpty" { + var keyboard = Keyboard.init(); + testing.expect(keyboard.isEmpty()); + + keyboard.queue_end += 1; + testing.expect(!keyboard.isEmpty()); + + keyboard.queue_front += 1; + testing.expect(keyboard.isEmpty()); + + keyboard.queue_end = std.math.maxInt(QueueIndex); + keyboard.queue_front = 0; + testing.expect(!keyboard.isEmpty()); + + keyboard.queue_front = std.math.maxInt(QueueIndex); + testing.expect(keyboard.isEmpty()); + } + + test "isFull" { + var keyboard = Keyboard.init(); + testing.expect(!keyboard.isFull()); + + keyboard.queue_end += 1; + testing.expect(!keyboard.isFull()); + + keyboard.queue_front += 1; + testing.expect(!keyboard.isFull()); + + keyboard.queue_end = 0; + testing.expect(keyboard.isFull()); + + keyboard.queue_front = 0; + keyboard.queue_end = std.math.maxInt(QueueIndex); + testing.expect(keyboard.isFull()); + } + + test "writeKey" { + var keyboard = Keyboard.init(); + + comptime var i = 0; + inline while (i < QUEUE_SIZE - 1) : (i += 1) { + testing.expectEqual(keyboard.writeKey(.{ + .position = @intToEnum(KeyPosition, i), + .released = false, + }), true); + testing.expectEqual(keyboard.queue[i].position, @intToEnum(KeyPosition, i)); + testing.expectEqual(keyboard.queue_end, i + 1); + testing.expectEqual(keyboard.queue_front, 0); + } + + testing.expectEqual(keyboard.writeKey(.{ + .position = @intToEnum(KeyPosition, 33), + .released = false, + }), false); + testing.expect(keyboard.isFull()); + } + + test "readKey" { + var keyboard = Keyboard.init(); + + comptime var i = 0; + inline while (i < QUEUE_SIZE - 1) : (i += 1) { + testing.expectEqual(keyboard.writeKey(.{ + .position = @intToEnum(KeyPosition, i), + .released = false, + }), true); + } + + i = 0; + inline while (i < QUEUE_SIZE - 1) : (i += 1) { + testing.expectEqual(keyboard.readKey().?.position, @intToEnum(KeyPosition, i)); + testing.expectEqual(keyboard.queue_end, QUEUE_SIZE - 1); + testing.expectEqual(keyboard.queue_front, i + 1); + } + + testing.expect(keyboard.isEmpty()); + testing.expectEqual(keyboard.readKey(), null); + } +}; + +/// The registered keyboards +var keyboards: ArrayList(*Keyboard) = undefined; + +/// +/// Get the keyboard associated with an ID +/// +/// Arguments: +/// id: usize - The ID of the keyboard to get +/// +/// Return: ?*Keyboard +/// The keyboard associated with the ID, or null if there isn't one +/// +pub fn getKeyboard(id: usize) ?*Keyboard { + if (keyboards.items.len <= id) { + return null; + } + return keyboards.items[id]; +} + +/// +/// Add a keyboard to list of known keyboards +/// +/// Arguments: +/// kb: *Keyboard - The keyboard to add +/// +/// Error: std.mem.Allocator.Error +/// OutOfMemory - Adding the keyboard to the list failed due to there not being enough memory free +/// +pub fn addKeyboard(kb: *Keyboard) Allocator.Error!void { + try keyboards.append(kb); +} + +/// +/// Initialise the keyboard system and the architecture's keyboard +/// +/// Arguments: +/// allocator: *std.mem.Allocator - The allocator to initialise the keyboard list and architecture keyboard with +/// +/// Return: ?*Keyboard +/// The architecture keyboard found, else null if one wasn't detected +/// +/// Error: std.mem.Allocator.Error +/// OutOfMemory - There wasn't enough memory to initialise the keyboard list or the architecture keyboard +/// +pub fn init(allocator: *Allocator) Allocator.Error!?*Keyboard { + keyboards = ArrayList(*Keyboard).init(allocator); + return arch.initKeyboard(allocator); +} + +test "" { + _ = Keyboard.init(); +} diff --git a/src/kernel/kmain.zig b/src/kernel/kmain.zig index e411900..23c44e7 100644 --- a/src/kernel/kmain.zig +++ b/src/kernel/kmain.zig @@ -17,6 +17,7 @@ const heap = @import("heap.zig"); const scheduler = @import("scheduler.zig"); const vfs = @import("filesystem/vfs.zig"); const initrd = @import("filesystem/initrd.zig"); +const keyboard = @import("keyboard.zig"); comptime { if (!is_test) { @@ -92,6 +93,12 @@ export fn kmain(boot_payload: arch.BootPayload) void { }; tty.init(&kernel_heap.allocator, boot_payload); + var arch_kb = keyboard.init(&fixed_allocator.allocator) catch |e| { + panic_root.panic(@errorReturnTrace(), "Failed to inititalise keyboard: {}\n", .{e}); + }; + if (arch_kb) |kb| { + keyboard.addKeyboard(kb) catch |e| panic_root.panic(@errorReturnTrace(), "Failed to add architecture keyboard: {}\n", .{e}); + } scheduler.init(&kernel_heap.allocator) catch |e| { panic_root.panic(@errorReturnTrace(), "Failed to initialise scheduler: {}\n", .{e}); @@ -171,6 +178,31 @@ fn initStage2() noreturn { else => {}, } + const kb = keyboard.getKeyboard(0) orelse unreachable; + var shift = false; + while (true) { + if (kb.readKey()) |key| { + if (key.released) { + if (key.position == keyboard.KeyPosition.LEFT_SHIFT) { + shift = false; + } + continue; + } + var char: ?u8 = switch (key.position) { + keyboard.KeyPosition.LEFT_SHIFT, keyboard.KeyPosition.RIGHT_SHIFT => blk: { + shift = true; + break :blk null; + }, + keyboard.KeyPosition.Q => if (shift) @as(u8, 'Q') else @as(u8, 'q'), + keyboard.KeyPosition.W => if (shift) @as(u8, 'W') else @as(u8, 'w'), + else => null, + }; + if (char) |ch| { + tty.print("{c}", .{ch}); + } + } + } + // Can't return for now, later this can return maybe arch.spinWait(); } diff --git a/test/mock/kernel/arch_mock.zig b/test/mock/kernel/arch_mock.zig index 66ad6da..b8e36df 100644 --- a/test/mock/kernel/arch_mock.zig +++ b/test/mock/kernel/arch_mock.zig @@ -8,6 +8,7 @@ const vmm = @import("vmm_mock.zig"); const paging = @import("paging_mock.zig"); const Serial = @import("../../../src/kernel/serial.zig").Serial; const TTY = @import("../../../src/kernel/tty.zig").TTY; +const Keyboard = @import("../../../src/kernel/keyboard.zig").Keyboard; pub const task = @import("task_mock.zig"); @@ -142,6 +143,10 @@ pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error! return ret; } +pub fn initKeyboard(allocator: *Allocator) Allocator.Error!?*Keyboard { + return null; +} + pub fn init(mem_profile: *const MemProfile) void { // I'll get back to this as this doesn't effect the current testing. // When I come on to the mem.zig testing, I'll fix :)