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 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.
|
||||
|
|
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" {
|
||||
std.debug.warn("", .{});
|
||||
const size = 1024;
|
||||
var region = try testing.allocator.alloc(u8, size);
|
||||
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 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();
|
||||
}
|
||||
|
|
|
@ -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 :)
|
||||
|
|
Loading…
Reference in a new issue