Add keyboard and an x86 driver
This commit is contained in:
parent
b8a47d6e08
commit
73d0b2fd3d
6 changed files with 780 additions and 0 deletions
|
@ -17,9 +17,11 @@ const vga = @import("vga.zig");
|
||||||
const mem = @import("../../mem.zig");
|
const mem = @import("../../mem.zig");
|
||||||
const multiboot = @import("multiboot.zig");
|
const multiboot = @import("multiboot.zig");
|
||||||
const vmm = @import("../../vmm.zig");
|
const vmm = @import("../../vmm.zig");
|
||||||
|
const keyboard = @import("keyboard.zig");
|
||||||
const Serial = @import("../../serial.zig").Serial;
|
const Serial = @import("../../serial.zig").Serial;
|
||||||
const panic = @import("../../panic.zig").panic;
|
const panic = @import("../../panic.zig").panic;
|
||||||
const TTY = @import("../../tty.zig").TTY;
|
const TTY = @import("../../tty.zig").TTY;
|
||||||
|
const Keyboard = @import("../../keyboard.zig").Keyboard;
|
||||||
const MemProfile = mem.MemProfile;
|
const MemProfile = mem.MemProfile;
|
||||||
|
|
||||||
/// The virtual end of the kernel code.
|
/// 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.
|
/// Initialise a 32bit kernel stack used for creating a task.
|
||||||
/// Currently only support fn () noreturn functions for the entry point.
|
/// Currently only support fn () noreturn functions for the entry point.
|
||||||
|
|
369
src/kernel/arch/x86/keyboard.zig
Normal file
369
src/kernel/arch/x86/keyboard.zig
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -543,6 +543,7 @@ const FreeListAllocator = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "resize" {
|
test "resize" {
|
||||||
|
std.debug.warn("", .{});
|
||||||
const size = 1024;
|
const size = 1024;
|
||||||
var region = try testing.allocator.alloc(u8, size);
|
var region = try testing.allocator.alloc(u8, size);
|
||||||
defer testing.allocator.free(region);
|
defer testing.allocator.free(region);
|
||||||
|
|
354
src/kernel/keyboard.zig
Normal file
354
src/kernel/keyboard.zig
Normal file
|
@ -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();
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ const heap = @import("heap.zig");
|
||||||
const scheduler = @import("scheduler.zig");
|
const scheduler = @import("scheduler.zig");
|
||||||
const vfs = @import("filesystem/vfs.zig");
|
const vfs = @import("filesystem/vfs.zig");
|
||||||
const initrd = @import("filesystem/initrd.zig");
|
const initrd = @import("filesystem/initrd.zig");
|
||||||
|
const keyboard = @import("keyboard.zig");
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
if (!is_test) {
|
if (!is_test) {
|
||||||
|
@ -92,6 +93,12 @@ export fn kmain(boot_payload: arch.BootPayload) void {
|
||||||
};
|
};
|
||||||
|
|
||||||
tty.init(&kernel_heap.allocator, boot_payload);
|
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| {
|
scheduler.init(&kernel_heap.allocator) catch |e| {
|
||||||
panic_root.panic(@errorReturnTrace(), "Failed to initialise scheduler: {}\n", .{e});
|
panic_root.panic(@errorReturnTrace(), "Failed to initialise scheduler: {}\n", .{e});
|
||||||
|
@ -171,6 +178,31 @@ fn initStage2() noreturn {
|
||||||
else => {},
|
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
|
// Can't return for now, later this can return maybe
|
||||||
arch.spinWait();
|
arch.spinWait();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ const vmm = @import("vmm_mock.zig");
|
||||||
const paging = @import("paging_mock.zig");
|
const paging = @import("paging_mock.zig");
|
||||||
const Serial = @import("../../../src/kernel/serial.zig").Serial;
|
const Serial = @import("../../../src/kernel/serial.zig").Serial;
|
||||||
const TTY = @import("../../../src/kernel/tty.zig").TTY;
|
const TTY = @import("../../../src/kernel/tty.zig").TTY;
|
||||||
|
const Keyboard = @import("../../../src/kernel/keyboard.zig").Keyboard;
|
||||||
|
|
||||||
pub const task = @import("task_mock.zig");
|
pub const task = @import("task_mock.zig");
|
||||||
|
|
||||||
|
@ -142,6 +143,10 @@ pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error!
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initKeyboard(allocator: *Allocator) Allocator.Error!?*Keyboard {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(mem_profile: *const MemProfile) void {
|
pub fn init(mem_profile: *const MemProfile) void {
|
||||||
// I'll get back to this as this doesn't effect the current testing.
|
// 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 :)
|
// When I come on to the mem.zig testing, I'll fix :)
|
||||||
|
|
Loading…
Reference in a new issue