From 02d4d17119fe212eeca3bbe8c055261eac112914 Mon Sep 17 00:00:00 2001 From: ParrotXray Date: Mon, 20 Oct 2025 19:31:15 +0800 Subject: [PATCH] feat: Complete the basic arranger --- kernel/src/drivers/keyboard.rs | 29 +++---- kernel/src/kernel/k_init.rs | 15 ++-- kernel/src/kernel/k_main.rs | 56 ++++++-------- kernel/src/process/process.rs | 14 ++-- kernel/src/process/scheduler.rs | 115 +++++++++++++++++----------- kernel/src/process/stack.rs | 40 +++++----- kernel/src/shell/mod.rs | 76 +++++++++++-------- kernel/src/tty/device.rs | 130 ++++++++++++++++++++++++++++++++ kernel/src/tty/mod.rs | 3 +- 9 files changed, 310 insertions(+), 168 deletions(-) create mode 100644 kernel/src/tty/device.rs diff --git a/kernel/src/drivers/keyboard.rs b/kernel/src/drivers/keyboard.rs index 4e5e546..5a48b49 100644 --- a/kernel/src/drivers/keyboard.rs +++ b/kernel/src/drivers/keyboard.rs @@ -88,8 +88,8 @@ pub fn handle_scancode(raw: u8) { } match scancode { - 0x1C => print_char(b'\n'), // Numpad Enter - 0x35 => print_char(b'/'), // Numpad / + 0x1C => tty::device::receive_char(b'\n'), // Numpad Enter + 0x35 => tty::device::receive_char(b'/'), // Numpad / 0x47 => kprintln!("[Home]"), 0x48 => kprintln!("[Up]"), 0x49 => kprintln!("[PgUp]"), @@ -190,26 +190,21 @@ pub fn handle_scancode(raw: u8) { if state.ctrl_pressed { match ascii { - b'c' | b'C' => { kprintln!("^C"); return; } - b'd' | b'D' => { kprintln!("^D"); return; } - b'l' | b'L' => { tty::tty::clear(0x000000); return; } - _ => {} + b'c' | b'C' => { + crate::tty::device::receive_char(3); // Ctrl+C + return; + } + b'l' | b'L' => { + tty::device::receive_char(12); // Ctrl+L + return; + } + _ => return, } } - print_char(ascii); + tty::device::receive_char(ascii); } -/// Print a character to the screen -fn print_char(c: u8) { - if c == b'\n' { - shell::process_keyboard_char('\n'); - } else if c == 8 { // Backspace - shell::process_keyboard_char('\x08'); - } else if c.is_ascii_graphic() || c == b' ' { - shell::process_keyboard_char(c as char); - } -} /// Initialize keyboard driver pub fn init() { diff --git a/kernel/src/kernel/k_init.rs b/kernel/src/kernel/k_init.rs index f609b9a..911a828 100644 --- a/kernel/src/kernel/k_init.rs +++ b/kernel/src/kernel/k_init.rs @@ -3,7 +3,7 @@ use bootloader_api::BootInfo; use bootloader_api::info::MemoryRegionKind; use x86_64::structures::paging::OffsetPageTable; use x86_64::{PhysAddr, VirtAddr}; -use crate::mm::{allocator::{frame, heap, pmm}, paging, vma, vmm}; +use crate::mm::{allocator::{frame, heap, pmm}, init_globals, paging, vma, vmm}; use crate::arch::amd64::{gdt, idt}; use crate::tty::tty; use crate::kprintln; @@ -15,6 +15,7 @@ use crate::drivers::keyboard; use crate::hal::{acpi, cpu, rtc, timer}; use crate::hal::apic::{ioapic, lapic}; use crate::hal::cpu::cpu_enable_interrupts; +use crate::process::scheduler::Scheduler; use crate::task::executor; fn _logger_init() { @@ -55,7 +56,7 @@ fn _memory_init( let mut total_usable = 0u64; for region in memory_regions.iter() { - if region.kind == bootloader_api::info::MemoryRegionKind::Usable { + if region.kind == MemoryRegionKind::Usable { usable_start = usable_start.min(region.start); usable_end = usable_end.max(region.end); total_usable += region.end - region.start; @@ -275,29 +276,23 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! { &boot_info.memory_regions, physical_memory_offset ); - let acpi_info = _acpi_init(rsdp_addr, physical_memory_offset); unsafe { - // 將 mapper 和 frame_allocator 轉換為 'static 引用 let mapper_static: &'static mut OffsetPageTable = core::mem::transmute(&mut mapper); let allocator_static: &'static mut frame::BootInfoFrameAllocator = core::mem::transmute(&mut frame_allocator); - // 初始化全局變量 - crate::mm::init_globals(mapper_static, allocator_static); + init_globals(mapper_static, allocator_static); log_info!("Global mapper and frame allocator initialized"); } - // 不要 drop,因為我們把引用給了全局變量 core::mem::forget(mapper); core::mem::forget(frame_allocator); - // ============================================ let acpi_info = _acpi_init(rsdp_addr, physical_memory_offset); - // 現在可以安全地訪問全局 mapper 和 allocator if let Some(mapper_once) = crate::mm::KERNEL_MAPPER.get() { if let Some(allocator_once) = crate::mm::FRAME_ALLOCATOR.get() { let mut mapper_guard = mapper_once.lock(); @@ -307,7 +302,6 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! { } } - _boot_report(&boot_info.memory_regions, physical_memory_offset); kprintln!(); @@ -316,6 +310,7 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! { let mut executor = executor::Executor::new(); + Scheduler::init(); k_main::_kernel_main(); } else { diff --git a/kernel/src/kernel/k_main.rs b/kernel/src/kernel/k_main.rs index 66b0c1a..b33cd36 100644 --- a/kernel/src/kernel/k_main.rs +++ b/kernel/src/kernel/k_main.rs @@ -31,46 +31,32 @@ pub fn _kernel_main() -> ! { if let time = rtc::get_time() { kprintln!("Date/Time: {}", time.format()); } - - kprintln!(); - kprintln!("Type 'help' for available commands"); - kprintln!(); - Scheduler::init(); - - // 創建測試進程 A - Scheduler::spawn(|yielder, _input| { - for i in 0..5 { - crate::kprintln!("Process A: iteration {}", i); - yielder.suspend(()); - } - crate::kprintln!("Process A finished"); - }, 1); - - // 創建測試進程 B - Scheduler::spawn(|yielder, _input| { - for i in 0..5 { - crate::kprintln!("Process B: iteration {}", i); - yielder.suspend(()); - } - crate::kprintln!("Process B finished"); - }, 1); - - // // 創建 Shell 進程(低優先級,在測試進程完成後運行)會 panic + // Process A // Scheduler::spawn(|yielder, _input| { - // crate::shell::init(); - // - // loop { - // // 定期 yield 讓其他進程運行 - // for _ in 0..1000 { - // Scheduler::check_reschedule(); - // crate::hal::cpu::cpu_pause(0); - // } + // for i in 0..5 { + // crate::kprintln!("Process A: iteration {}", i); // yielder.suspend(()); // } - // }, 10); // 低優先級 + // crate::kprintln!("Process A finished"); + // }, 1); + // + // Process B + // Scheduler::spawn(|yielder, _input| { + // for i in 0..5 { + // crate::kprintln!("Process B: iteration {}", i); + // yielder.suspend(()); + // } + // crate::kprintln!("Process B finished"); + // }, 1); + // + // Scheduler::init(); + + Scheduler::spawn(|yielder, _input| { + let pid = Scheduler::current_pid().unwrap(); + shell::shell_main(pid, yielder); + }, 5); - // 運行調度器(永遠不返回) Scheduler::run() } diff --git a/kernel/src/process/process.rs b/kernel/src/process/process.rs index 4c3b086..bbdcdb3 100644 --- a/kernel/src/process/process.rs +++ b/kernel/src/process/process.rs @@ -15,7 +15,6 @@ pub enum ProcessState { pub struct Process { pub id: ProcessId, pub state: ProcessState, - // Coroutine pub coroutine: Option>, pub time_slice: u64, pub priority: u8, @@ -36,13 +35,9 @@ impl Process { where F: FnOnce(&Yielder<(), ()>, ()) + 'static, { - // 創建 stack let stack = ProcessStack::new()?; - - // 創建 coroutine let coro = Coroutine::with_stack(stack, f); self.coroutine = Some(coro); - Some(self) } @@ -52,12 +47,15 @@ impl Process { match coro.resume(()) { CoroutineResult::Yield(()) => { - self.state = ProcessState::Ready; - true // 還需要繼續運行 + // Set to Ready only when not in Blocked state + if self.state == ProcessState::Running { + self.state = ProcessState::Ready; + } + true } CoroutineResult::Return(()) => { self.state = ProcessState::Terminated; - false // 已完成 + false } } } else { diff --git a/kernel/src/process/scheduler.rs b/kernel/src/process/scheduler.rs index e9d2bb3..3b3a5a9 100644 --- a/kernel/src/process/scheduler.rs +++ b/kernel/src/process/scheduler.rs @@ -1,7 +1,7 @@ // kernel/src/process/scheduler.rs use super::process::{Process, ProcessId, ProcessState}; use core::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; -use crate::{log_debug, log_info, log_warn}; +use crate::{hal, log_debug, log_info, log_warn}; const MAX_PROCESSES: usize = 256; const DEFAULT_TIME_SLICE: u64 = 10; @@ -21,18 +21,17 @@ pub struct Scheduler; impl Scheduler { pub fn init() { - log_info!("Initializing preemptive scheduler"); + log_info!("Initializing scheduler with blocking support"); SCHEDULER_ENABLED.store(true, Ordering::Release); } - /// 創建新進程 + /// Create a new process pub fn spawn(f: F, priority: u8) -> Option where F: FnOnce(&corosensei::Yielder<(), ()>, ()) + 'static, { let pid = NEXT_PID.fetch_add(1, Ordering::Relaxed); - // 創建進程(可能失敗) let process = match Process::new(pid, priority, DEFAULT_TIME_SLICE).spawn(f) { Some(p) => p, None => { @@ -46,7 +45,7 @@ impl Scheduler { if PROCESSES[i].is_none() { PROCESSES[i] = Some(process); PROCESS_COUNT.fetch_add(1, Ordering::Release); - log_debug!("Spawned process {} at slot {}", pid, i); + // log_debug!("Spawned process {} at slot {}", pid, i); return Some(pid); } } @@ -56,7 +55,36 @@ impl Scheduler { None } - /// Timer 中斷處理器調用 + /// Block the current process + pub fn block_current() { + let current_idx = CURRENT_PROCESS.load(Ordering::Acquire); + + unsafe { + if let Some(ref mut process) = PROCESSES[current_idx] { + process.state = ProcessState::Blocked; + // log_debug!("Process {} (slot {}) blocked", process.id, current_idx); + } + } + } + + /// Wake up the specified process + pub fn wake_process(pid: u64) { + unsafe { + for i in 0..MAX_PROCESSES { + if let Some(ref mut process) = PROCESSES[i] { + if process.id == pid && process.state == ProcessState::Blocked { + process.state = ProcessState::Ready; + // log_debug!("Process {} (slot {}) woken up", process.id, i); + NEED_RESCHEDULE.store(true, Ordering::Release); + return; + } + } + } + } + log_warn!("Tried to wake non-existent or non-blocked process {}", pid); + } + + /// Timer interrupt call pub fn on_timer_tick() { if !SCHEDULER_ENABLED.load(Ordering::Acquire) { return; @@ -79,26 +107,24 @@ impl Scheduler { } } - /// 在安全點檢查是否需要調度 + /// Check if rescheduling is needed pub fn check_reschedule() { if NEED_RESCHEDULE.swap(false, Ordering::AcqRel) { Self::schedule(); } } - /// 執行調度 + /// Execute scheduling fn schedule() { let count = PROCESS_COUNT.load(Ordering::Acquire); if count == 0 { return; } - let mut current_idx = CURRENT_PROCESS.load(Ordering::Acquire); - let start_idx = current_idx; - let mut found = false; - unsafe { - // 將當前進程設為 Ready(如果還在運行) + let current_idx = CURRENT_PROCESS.load(Ordering::Acquire); + + // Set the current process to Ready (if it is running) if let Some(ref mut process) = PROCESSES[current_idx] { if process.state == ProcessState::Running { process.state = ProcessState::Ready; @@ -106,63 +132,62 @@ impl Scheduler { } } - // Round-robin 查找下一個可運行的進程 + // Round-robin to find the next Ready process + let mut next_idx = current_idx; + let mut attempts = 0; + loop { - current_idx = (current_idx + 1) % MAX_PROCESSES; + next_idx = (next_idx + 1) % MAX_PROCESSES; + attempts += 1; - if let Some(ref mut process) = PROCESSES[current_idx] { + if attempts > MAX_PROCESSES { + // There is no Ready process, all processes are blocked + // log_debug!("All processes blocked or terminated"); + return; + } + + if let Some(ref mut process) = PROCESSES[next_idx] { if process.state == ProcessState::Ready { - CURRENT_PROCESS.store(current_idx, Ordering::Release); - log_debug!("Switching to process {} (slot {})", process.id, current_idx); + CURRENT_PROCESS.store(next_idx, Ordering::Release); + + // log_debug!("Switching to process {} (slot {})", process.id, next_idx); - // Resume 進程 - log_debug!("About to resume process {}", process.id); let result = process.resume(); - log_debug!("Process {} resume returned: {}", process.id, result); if !result { - log_debug!("Process {} terminated", process.id); - PROCESSES[current_idx] = None; + // log_debug!("Process {} terminated", process.id); + PROCESSES[next_idx] = None; PROCESS_COUNT.fetch_sub(1, Ordering::Release); continue; } - found = true; - break; + return; } } - - // 遍歷一圈 - if current_idx == start_idx { - break; - } - } - - if !found { - log_debug!("No runnable process found"); } } } - /// 主調度循環 + /// Main scheduling loop pub fn run() -> ! { - log_info!("Starting scheduler main loop"); - loop { Self::schedule(); - if PROCESS_COUNT.load(Ordering::Acquire) == 0 { - crate::hal::cpu::cpu_halt(); + let count = PROCESS_COUNT.load(Ordering::Acquire); + if count == 0 { + log_info!("No processes remaining, system idle"); + hal::cpu::cpu_halt(); } - crate::hal::cpu::cpu_pause(0); + hal::cpu::cpu_pause(500); } } - /// 獲取統計信息 - pub fn stats() -> (usize, usize) { - let count = PROCESS_COUNT.load(Ordering::Acquire); - let current = CURRENT_PROCESS.load(Ordering::Acquire); - (count, current) + /// Get the current process ID + pub fn current_pid() -> Option { + let current_idx = CURRENT_PROCESS.load(Ordering::Acquire); + unsafe { + PROCESSES[current_idx].as_ref().map(|p| p.id) + } } } \ No newline at end of file diff --git a/kernel/src/process/stack.rs b/kernel/src/process/stack.rs index f197b19..7493ab0 100644 --- a/kernel/src/process/stack.rs +++ b/kernel/src/process/stack.rs @@ -4,7 +4,7 @@ use corosensei::stack::{Stack, StackPointer, STACK_ALIGNMENT}; use crate::mm::{KERNEL_MAPPER, FRAME_ALLOCATOR}; use crate::mm::allocator::pmm; use x86_64::{VirtAddr, structures::paging::{Page, PageTableFlags, Size4KiB, Mapper}}; -use crate::{log_debug, log_error}; +use crate::{log_debug, log_error, mm}; const PROCESS_STACK_SIZE: usize = 64 * 1024; // 64 KiB @@ -19,12 +19,12 @@ impl ProcessStack { pub fn new() -> Option { let page_count = (PROCESS_STACK_SIZE + 4095) / 4096; - // 從 VMM 分配虛擬地址 - let vaddr = crate::mm::vmm::VMM.lock().allocate_pages(page_count)?; + // Allocate virtual addresses from the VMM + let vaddr = mm::vmm::VMM.lock().allocate_pages(page_count)?; - log_debug!("Allocating process stack at {:#x}, {} pages", vaddr.as_u64(), page_count); + // log_debug!("Allocating process stack at {:#x}, {} pages", vaddr.as_u64(), page_count); - // 映射所有頁面 + // Map all pages let mapper = KERNEL_MAPPER.get()?; let allocator = FRAME_ALLOCATOR.get()?; @@ -32,18 +32,18 @@ impl ProcessStack { let page_vaddr = vaddr + (i * 4096) as u64; let page = Page::::containing_address(page_vaddr); - // 分配物理頁面 + // Allocate physical pages let frame = match pmm::allocate_frame() { Some(f) => f, None => { log_error!("Failed to allocate physical frame for stack page {}", i); Self::cleanup_mapped_pages(vaddr, i); - crate::mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count); + mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count); return None; } }; - // 映射頁面 + // Mapping page let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; let mut mapper_guard = mapper.lock(); @@ -60,17 +60,17 @@ impl ProcessStack { drop(mapper_guard); drop(alloc_guard); Self::cleanup_mapped_pages(vaddr, i); - crate::mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count); + mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count); return None; } } } - log_debug!("Mapped stack page {} at {:#x} -> frame {:#x}", - i, page_vaddr.as_u64(), frame.start_address().as_u64()); + // log_debug!("Mapped stack page {} at {:#x} -> frame {:#x}", + // i, page_vaddr.as_u64(), frame.start_address().as_u64()); } - // 計算對齊的 stack base 和 limit + // Calculate the aligned stack base and limit let base_addr = vaddr.as_u64() + PROCESS_STACK_SIZE as u64; let limit_addr = vaddr.as_u64(); @@ -85,7 +85,7 @@ impl ProcessStack { }) } - /// 清理已映射的頁面 + /// Clean up mapped pages fn cleanup_mapped_pages(vaddr: VirtAddr, count: usize) { if let Some(mapper) = KERNEL_MAPPER.get() { let mut mapper_guard = mapper.lock(); @@ -94,7 +94,7 @@ impl ProcessStack { let page_vaddr = vaddr + (i * 4096) as u64; let page = Page::::containing_address(page_vaddr); - // 明確指定類型 + // Explicitly specify the type if let Ok((frame, flush)) = mapper_guard.unmap(page) { flush.flush(); pmm::deallocate_frame(frame); @@ -106,9 +106,9 @@ impl ProcessStack { impl Drop for ProcessStack { fn drop(&mut self) { - log_debug!("Dropping process stack at {:#x}", self.vaddr.as_u64()); + // log_debug!("Dropping process stack at {:#x}", self.vaddr.as_u64()); - // 取消映射並釋放物理頁面 + // Unmap and release physical pages if let Some(mapper) = KERNEL_MAPPER.get() { let mut mapper_guard = mapper.lock(); @@ -116,17 +116,17 @@ impl Drop for ProcessStack { let page_vaddr = self.vaddr + (i * 4096) as u64; let page = Page::::containing_address(page_vaddr); - // 明確指定類型參數 + // Explicitly specify type parameters if let Ok((frame, flush)) = mapper_guard.unmap(page) { flush.flush(); pmm::deallocate_frame(frame); - log_debug!("Unmapped and freed stack page {}", i); + // log_debug!("Unmapped and freed stack page {}", i); } } } - // 釋放虛擬地址 - crate::mm::vmm::VMM.lock().deallocate_pages(self.vaddr, self.page_count); + // Release the virtual address + mm::vmm::VMM.lock().deallocate_pages(self.vaddr, self.page_count); } } diff --git a/kernel/src/shell/mod.rs b/kernel/src/shell/mod.rs index 5902855..e7e8be3 100644 --- a/kernel/src/shell/mod.rs +++ b/kernel/src/shell/mod.rs @@ -1,55 +1,67 @@ // kernel/src/shell/mod.rs use alloc::string::String; -use spin::Mutex; -use crate::{kprint, kprintln, tty, hal::{rtc, timer}}; +use crate::{kprint, kprintln, tty}; +use crate::process::scheduler::Scheduler; +use crate::tty::device; pub mod commands; pub mod math; -static COMMAND_BUFFER: Mutex = Mutex::new(String::new()); +/// Shell main loop (blocking) +pub fn shell_main(pid: u64, yielder: &corosensei::Yielder<(), ()>) { + kprintln!(); + kprintln!("Shell started (PID: {})", pid); + kprintln!("Type 'help' for available commands"); + kprintln!(); -pub fn init() { - show_prompt(); -} + loop { + // Display the prompt + tty::tty::write_str("cure > ", 0x00FF00); -pub fn show_prompt() { - tty::tty::write_str("cure > ", 0x00FF00); -} + // Blocking read of a line + let line = read_line_blocking(pid, yielder); -pub fn process_keyboard_char(c: char) { - let mut buffer = COMMAND_BUFFER.lock(); + // Parse into a string + let cmd = match core::str::from_utf8(&line) { + Ok(s) => s.trim(), + Err(_) => { + kprintln!("Invalid UTF-8 input"); + continue; + } + }; - if c == '\n' { - let cmd = buffer.clone(); - buffer.clear(); - - kprintln!(); - execute_command(&cmd); - show_prompt(); - } else if c == '\x08' { - if !buffer.is_empty() { - buffer.pop(); - kprint!("\x08 \x08"); + // Execute command + if !cmd.is_empty() { + execute_command(cmd); } - } else if c.is_ascii_graphic() || c == ' ' { - buffer.push(c); - kprint!("{}", c); + } +} + +/// Blocking read of a line +fn read_line_blocking(pid: u64, yielder: &corosensei::Yielder<(), ()>) -> alloc::vec::Vec { + loop { + // Try to read + if let Some(line) = device::read_line(pid) { + return line; + } + + // No data, blocking the current process + Scheduler::block_current(); + + // Yield to the scheduler + yielder.suspend(()); + + // After being woken up, continue to try to read } } fn execute_command(cmd: &str) { - let mut cmd = cmd.trim(); - - if cmd.is_empty() { - return; - } - match cmd { "help" => commands::cmd_help(), "clear" | "clr" => commands::cmd_clear(), "time" => commands::cmd_time(), "uptime" => commands::cmd_uptime(), - "sysinfo" | "sys" => commands::cmd_sysinfo(), + "sysinfo" | "sys" => commands::cmd_sysinfo(), "meminfo" | "mem" => commands::cmd_meminfo(), "reboot" => commands::cmd_reboot(), "halt" | "shutdown" | "poweroff" => commands::cmd_shutdown(), diff --git a/kernel/src/tty/device.rs b/kernel/src/tty/device.rs new file mode 100644 index 0000000..a4bcb20 --- /dev/null +++ b/kernel/src/tty/device.rs @@ -0,0 +1,130 @@ +// kernel/src/tty/device +use alloc::{collections::VecDeque, vec::Vec}; +use spin::Mutex; +use crate::{kprint, process}; + +/// TTY device +pub struct Device { + /// Input buffer (complete line) + input_buffer: VecDeque, + /// The line currently being edited + line_buffer: Vec, + /// Waiting to read the process ID + waiting_process: Option, + /// Whether to echo + echo: bool, +} + +impl Device { + pub const fn new() -> Self { + Self { + input_buffer: VecDeque::new(), + line_buffer: Vec::new(), + waiting_process: None, + echo: true, + } + } + + /// Receive a character from the keyboard (interrupt context call) + pub fn receive_char(&mut self, c: u8) { + match c { + b'\n' => { + // Enter: Complete the current line + self.line_buffer.push(b'\n'); + + // echo newline + if self.echo { + kprint!("\n"); + } + + // Move the entire line to the input buffer + self.input_buffer.extend(self.line_buffer.drain(..)); + + //Wake up the waiting process + if let Some(pid) = self.waiting_process.take() { + process::scheduler::Scheduler::wake_process(pid); + } + } + + 8 | 127 => { // Backspace + if !self.line_buffer.is_empty() { + self.line_buffer.pop(); + if self.echo { + kprint!("\x08 \x08"); + } + } + } + + 3 => { // Ctrl+C + if self.echo { + kprint!("^C\n"); + } + self.line_buffer.clear(); + + // Wake up the process (but return a blank line or a special mark) + if let Some(pid) = self.waiting_process.take() { + crate::process::scheduler::Scheduler::wake_process(pid); + } + } + + 12 => { // Ctrl+L (clear screen) + if self.echo { + crate::tty::tty::clear(0x000000); + } + } + + c if c >= 32 && c < 127 => { + self.line_buffer.push(c); + if self.echo { + kprint!("{}", c as char); + } + } + + _ => { + // Ignore other characters + } + } + } + + /// Read a line (blocking call) + /// Returning None indicates a blocking wait. + pub fn read_line(&mut self, pid: u64) -> Option> { + // If there is a complete line, return immediately + if let Some(pos) = self.input_buffer.iter().position(|&c| c == b'\n') { + let mut line = Vec::new(); + for _ in 0..=pos { + if let Some(c) = self.input_buffer.pop_front() { + line.push(c); + } + } + return Some(line); + } + + // Otherwise, record the waiting process and return None + self.waiting_process = Some(pid); + None + } + + /// Check if there is a complete line to read + pub fn has_line(&self) -> bool { + self.input_buffer.iter().any(|&c| c == b'\n') + } +} + +// 全局 TTY 設備 +static TTY0: Mutex = Mutex::new(Device::new()); + +/// 接收字符(給鍵盤驅動調用) +pub fn receive_char(c: u8) { + TTY0.lock().receive_char(c); +} + +/// 讀取一行(給進程調用) +pub fn read_line(pid: u64) -> Option> { + TTY0.lock().read_line(pid) +} + +/// 檢查是否有數據 +pub fn has_input() -> bool { + TTY0.lock().has_line() +} \ No newline at end of file diff --git a/kernel/src/tty/mod.rs b/kernel/src/tty/mod.rs index c803794..cbbae52 100644 --- a/kernel/src/tty/mod.rs +++ b/kernel/src/tty/mod.rs @@ -1,4 +1,5 @@ // src/kernel/tty/mod.rs pub mod tty; -pub mod font; \ No newline at end of file +pub mod font; +pub mod device; \ No newline at end of file