diff --git a/Makefile b/Makefile index 5da3cb0..c011301 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,7 @@ C_SOURCES := \ clks/kernel/tty.c \ clks/kernel/pmm.c \ clks/kernel/heap.c \ + clks/kernel/keyboard.c \ clks/kernel/interrupts.c \ clks/kernel/scheduler.c \ clks/kernel/elf64.c \ diff --git a/clks/include/clks/keyboard.h b/clks/include/clks/keyboard.h new file mode 100644 index 0000000..8bba403 --- /dev/null +++ b/clks/include/clks/keyboard.h @@ -0,0 +1,10 @@ +#ifndef CLKS_KEYBOARD_H +#define CLKS_KEYBOARD_H + +#include + +void clks_keyboard_init(void); +void clks_keyboard_handle_scancode(u8 scancode); +u64 clks_keyboard_hotkey_switch_count(void); + +#endif diff --git a/clks/kernel/interrupts.c b/clks/kernel/interrupts.c index de9b758..a9cbf14 100644 --- a/clks/kernel/interrupts.c +++ b/clks/kernel/interrupts.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -18,9 +19,13 @@ #define CLKS_IRQ_BASE 32U #define CLKS_IRQ_TIMER 32U +#define CLKS_IRQ_KEYBOARD 33U #define CLKS_IRQ_LAST 47U #define CLKS_SYSCALL_VECTOR 128U +#define CLKS_PS2_DATA_PORT 0x60U +#define CLKS_PS2_STATUS_PORT 0x64U + struct clks_idt_entry { u16 offset_low; u16 selector; @@ -192,7 +197,7 @@ static void clks_pic_remap_and_mask(void) { (void)master_mask; (void)slave_mask; - clks_outb(CLKS_PIC1_DATA, 0xFEU); + clks_outb(CLKS_PIC1_DATA, 0xFCU); clks_outb(CLKS_PIC2_DATA, 0xFFU); } @@ -225,6 +230,10 @@ static void clks_load_idt(void) { __asm__ volatile("lidt %0" : : "m"(idtr)); } + +static clks_bool clks_ps2_has_output(void) { + return (clks_inb(CLKS_PS2_STATUS_PORT) & 0x01U) != 0U ? CLKS_TRUE : CLKS_FALSE; +} static void clks_enable_interrupts(void) { __asm__ volatile("sti"); } @@ -248,6 +257,11 @@ void clks_interrupt_dispatch(struct clks_interrupt_frame *frame) { if (vector == CLKS_IRQ_TIMER) { clks_timer_ticks++; clks_scheduler_on_timer_tick(clks_timer_ticks); + } else if (vector == CLKS_IRQ_KEYBOARD) { + if (clks_ps2_has_output() == CLKS_TRUE) { + u8 scancode = clks_inb(CLKS_PS2_DATA_PORT); + clks_keyboard_handle_scancode(scancode); + } } if (vector >= CLKS_IRQ_BASE && vector <= CLKS_IRQ_LAST) { @@ -323,4 +337,4 @@ void clks_interrupts_init(void) { u64 clks_interrupts_timer_ticks(void) { return clks_timer_ticks; -} \ No newline at end of file +} diff --git a/clks/kernel/keyboard.c b/clks/kernel/keyboard.c new file mode 100644 index 0000000..0f2ca09 --- /dev/null +++ b/clks/kernel/keyboard.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#define CLKS_SC_ALT 0x38U +#define CLKS_SC_F1 0x3BU +#define CLKS_SC_F2 0x3CU +#define CLKS_SC_F3 0x3DU +#define CLKS_SC_F4 0x3EU + +static clks_bool clks_kbd_alt_down = CLKS_FALSE; +static u64 clks_kbd_hotkey_switches = 0ULL; + +void clks_keyboard_init(void) { + clks_kbd_alt_down = CLKS_FALSE; + clks_kbd_hotkey_switches = 0ULL; + clks_log(CLKS_LOG_INFO, "KBD", "ALT+F1..F4 TTY HOTKEY ONLINE"); +} + +void clks_keyboard_handle_scancode(u8 scancode) { + clks_bool released = ((scancode & 0x80U) != 0U) ? CLKS_TRUE : CLKS_FALSE; + u8 code = (u8)(scancode & 0x7FU); + + if (code == CLKS_SC_ALT) { + clks_kbd_alt_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE; + return; + } + + if (released == CLKS_TRUE || clks_kbd_alt_down == CLKS_FALSE) { + return; + } + + if (code >= CLKS_SC_F1 && code <= CLKS_SC_F4) { + u32 target = (u32)(code - CLKS_SC_F1); + u32 before = clks_tty_active(); + u32 after; + + clks_tty_switch(target); + after = clks_tty_active(); + + if (after != before) { + clks_kbd_hotkey_switches++; + clks_log(CLKS_LOG_INFO, "TTY", "HOTKEY SWITCH"); + clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)after); + clks_log_hex(CLKS_LOG_INFO, "TTY", "HOTKEY_SWITCHES", clks_kbd_hotkey_switches); + } + } +} + +u64 clks_keyboard_hotkey_switch_count(void) { + return clks_kbd_hotkey_switches; +} diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index f226887..20879da 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,7 @@ static void clks_task_usrd(u64 tick) { clks_userland_tick(tick); } -static void clks_stage14_syscall_probe(void) { +static void clks_stage15_syscall_probe(void) { char child_name[96]; char read_buf[160]; u64 root_children; @@ -170,7 +171,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE14 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE15 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -233,6 +234,7 @@ void clks_kernel_main(void) { } clks_exec_init(); + clks_keyboard_init(); if (clks_userland_init() == CLKS_FALSE) { clks_log(CLKS_LOG_ERROR, "USER", "USERLAND INIT FAILED"); @@ -279,11 +281,10 @@ void clks_kernel_main(void) { syscall_ticks = clks_syscall_invoke_kernel(CLKS_SYSCALL_TIMER_TICKS, 0ULL, 0ULL, 0ULL); clks_log_hex(CLKS_LOG_INFO, "SYSCALL", "TICKS", syscall_ticks); - clks_stage14_syscall_probe(); + clks_stage15_syscall_probe(); clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY"); clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); clks_cpu_halt_forever(); } - diff --git a/docs/stage15.md b/docs/stage15.md new file mode 100644 index 0000000..18100fd --- /dev/null +++ b/docs/stage15.md @@ -0,0 +1,53 @@ +# CLeonOS Stage15 + +## Stage Goal +- Keep ELF path unchanged and continue non-ELF stability work. +- Add PS/2 keyboard IRQ handling in kernel interrupt path. +- Support virtual terminal hotkey switching with `Alt+F1`~`Alt+F4`. +- Keep scheduler, syscall, and existing Stage14 behavior stable. + +## What Was Implemented +- New keyboard module: + - `clks/include/clks/keyboard.h` + - `clks/kernel/keyboard.c` +- Interrupt integration: + - Enable IRQ1 (keyboard) alongside IRQ0 in PIC mask. + - Read scancode from PS/2 data port `0x60`. + - Dispatch scancode to keyboard hotkey handler. +- Hotkey behavior: + - Hold `Alt` + press `F1/F2/F3/F4` to switch TTY 0/1/2/3. + - On successful switch, kernel logs active TTY and switch counter. +- Boot stage update: + - `CLEONOS STAGE15 START`. + +## Acceptance Criteria +- Kernel boots and prints `CLEONOS STAGE15 START`. +- Boot logs include keyboard module online message: + - `[INFO][KBD] ALT+F1..F4 TTY HOTKEY ONLINE` +- In QEMU runtime: + - Press `Alt+F2` / `Alt+F3` / `Alt+F4` / `Alt+F1`. + - Logs show: + - `[INFO][TTY] HOTKEY SWITCH` + - `[INFO][TTY] ACTIVE: 0X...` + - `[INFO][TTY] HOTKEY_SWITCHES: 0X...` + +## Build Targets +- `make setup` +- `make userapps` +- `make iso` +- `make run` +- `make debug` + +## QEMU Command +- `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` + +## Common Bugs and Debugging +- Hotkey has no effect: + - Verify QEMU window is focused and keyboard input is captured. + - Confirm logs show Stage15 keyboard online message. +- IRQ timer works but keyboard does not: + - Re-check PIC mask includes IRQ1 (master mask should allow IRQ0 and IRQ1). +- Wrong function key mapping: + - Current mapping is Set1 scancode: F1=0x3B, F2=0x3C, F3=0x3D, F4=0x3E. +- Excessive hotkey logs: + - Holding key may repeat make scancode by keyboard auto-repeat; this is expected in current stage.