kernel/device/char/
tty.rs

1//! TTY (Terminal) device implementation.
2//!
3//! This module implements a TTY device that acts as a terminal interface
4//! providing line discipline, echo, and basic terminal I/O operations.
5
6extern crate alloc;
7use crate::arch::Trapframe;
8use crate::device::char::{CharDevice, TtyControl};
9use crate::device::events::{DeviceEvent, DeviceEventListener, InputEvent};
10use crate::device::manager::DeviceManager;
11use crate::device::{Device, DeviceCapability, DeviceType};
12use crate::late_initcall;
13use crate::object::capability::selectable::{
14    ReadyInterest, ReadySet, SelectWaitOutcome, Selectable,
15};
16use crate::object::capability::{ControlOps, MemoryMappingOps};
17use crate::sync::waker::Waker;
18use crate::task::mytask;
19use crate::timer::{TimerHandler, add_timer, cancel_timer, get_tick};
20use alloc::collections::VecDeque;
21use alloc::sync::Arc;
22use core::any::Any;
23use core::sync::atomic::{AtomicBool, AtomicU16, Ordering};
24use spin::Mutex;
25
26/// Scarlet-private, OS-agnostic control opcodes for TTY devices.
27/// These are stable only within Scarlet and must be mapped by ABI adapters.
28pub mod tty_ctl {
29    /// Magic 'ST' (0x53, 0x54) followed by sequential IDs to avoid collisions.
30    pub const SCTL_TTY_SET_ECHO: u32 = 0x5354_0001;
31    pub const SCTL_TTY_GET_ECHO: u32 = 0x5354_0002;
32    pub const SCTL_TTY_SET_CANONICAL: u32 = 0x5354_0003;
33    pub const SCTL_TTY_GET_CANONICAL: u32 = 0x5354_0004;
34    /// arg = (cols<<16 | rows)
35    pub const SCTL_TTY_SET_WINSIZE: u32 = 0x5354_0005;
36    /// ret = (cols<<16 | rows)
37    pub const SCTL_TTY_GET_WINSIZE: u32 = 0x5354_0006;
38    /// Set read policy as a neutral abstraction:
39    /// arg = ((timeout_ms as u32) << 16) | (min_ready_bytes as u32)
40    pub const SCTL_TTY_SET_READ_POLICY: u32 = 0x5354_0007;
41    /// Get read policy in the same packed format as SET_READ_POLICY
42    pub const SCTL_TTY_GET_READ_POLICY: u32 = 0x5354_0008;
43    /// Flush input buffer (arg ignored)
44    pub const SCTL_TTY_FLUSH_INPUT: u32 = 0x5354_0009;
45    /// Enable/disable debug logging of received bytes (arg!=0 enable)
46    pub const SCTL_TTY_SET_DEBUG: u32 = 0x5354_000A;
47    /// Get debug logging state (ret=0/1)
48    pub const SCTL_TTY_GET_DEBUG: u32 = 0x5354_000B;
49    /// Set keyboard mode (0=XLATE, 1=MEDIUMRAW, 2=RAW)
50    pub const SCTL_TTY_SET_KBMODE: u32 = 0x5354_000C;
51    /// Get keyboard mode (0=XLATE, 1=MEDIUMRAW, 2=RAW)
52    pub const SCTL_TTY_GET_KBMODE: u32 = 0x5354_000D;
53}
54use tty_ctl::*;
55
56// Provide a static capabilities slice for TTY devices
57static TTY_CAPS: [DeviceCapability; 1] = [DeviceCapability::Tty];
58
59/// TTY subsystem initialization
60fn init_tty_subsystem() {
61    let result = try_init_tty_subsystem();
62    if let Err(e) = result {
63        crate::early_println!("Failed to initialize TTY subsystem: {}", e);
64    }
65}
66
67fn try_init_tty_subsystem() -> Result<(), &'static str> {
68    let device_manager = DeviceManager::get_manager();
69
70    // Prefer a Char device that advertises Serial capability (and is not itself a TTY)
71    let devices_count = device_manager.get_devices_count();
72    let mut serial_device_id: Option<usize> = None;
73    for id in 1..=devices_count {
74        if let Some(dev) = device_manager.get_device(id) {
75            if dev.device_type() == DeviceType::Char
76                && dev.capabilities().contains(&DeviceCapability::Serial)
77                && !dev.capabilities().contains(&DeviceCapability::Tty)
78            {
79                serial_device_id = Some(id);
80                break;
81            }
82        }
83    }
84
85    let uart_device_id =
86        serial_device_id.ok_or("No Serial-capable char device found for TTY initialization")?;
87
88    // Create TTY device with the resolved UART device ID
89    let tty_device = Arc::new(TtyDevice::new("tty0", uart_device_id));
90    let uart_device = device_manager
91        .get_device(uart_device_id)
92        .ok_or("UART device not found")?;
93
94    // Register TTY device as event listener for UART input events via capability
95    if let Some(ec) = uart_device.as_event_capable() {
96        let weak_tty = Arc::downgrade(&tty_device);
97        ec.register_event_listener(weak_tty);
98        crate::early_println!("TTY registered as UART event listener");
99    }
100
101    // Register TTY device with device manager under name tty0
102    let _tty_id =
103        device_manager.register_device_with_name(alloc::string::String::from("tty0"), tty_device);
104
105    crate::early_println!("TTY subsystem initialized successfully");
106    Ok(())
107}
108
109late_initcall!(init_tty_subsystem);
110
111/// TTY device implementation.
112///
113/// This device provides terminal functionality including line discipline,
114/// echo, and basic terminal I/O operations.
115pub struct TtyDevice {
116    name: &'static str,
117    uart_device_id: usize,
118
119    // Input buffer for line discipline
120    input_buffer: Arc<Mutex<VecDeque<u8>>>,
121
122    // Waker for blocking reads
123    input_waker: Waker,
124
125    // Line discipline flags (OS/ABI-neutral)
126    canonical_mode: AtomicBool,
127    echo_enabled: AtomicBool,
128
129    // Neutral read policy: minimum bytes to return and optional timeout (ms)
130    read_min_ready_bytes: AtomicU16,
131    read_timeout_ms: AtomicU16,
132
133    // Window size in character cells (OS/ABI-neutral)
134    winsize_cols: Mutex<u16>,
135    winsize_rows: Mutex<u16>,
136    // Debug logging flag
137    debug_enabled: AtomicBool,
138
139    // Keyboard mode (0=XLATE, 1=MEDIUMRAW, 2=RAW)
140    kb_mode: core::sync::atomic::AtomicU8,
141
142    // Simple escape sequence parser state for arrow keys in raw-ish mode
143    // 0: none, 1: got ESC (0x1B), 2: got ESC '[', 3: got ESC 'O'
144    esc_state: Mutex<u8>,
145    // Per-device non-blocking I/O flag (shared by all FDs referencing this TTY)
146    nonblocking: AtomicBool,
147}
148
149impl TtyDevice {
150    pub fn new(name: &'static str, uart_device_id: usize) -> Self {
151        Self {
152            name,
153            uart_device_id,
154            input_buffer: Arc::new(Mutex::new(VecDeque::new())),
155            input_waker: Waker::new_interruptible("tty_input"),
156            canonical_mode: AtomicBool::new(true),
157            echo_enabled: AtomicBool::new(true),
158            read_min_ready_bytes: AtomicU16::new(1),
159            read_timeout_ms: AtomicU16::new(0),
160            winsize_cols: Mutex::new(80),
161            winsize_rows: Mutex::new(25),
162            // Disable per-byte debug logging by default (can be enabled via SCTL_TTY_SET_DEBUG)
163            debug_enabled: AtomicBool::new(false),
164            kb_mode: core::sync::atomic::AtomicU8::new(0),
165            esc_state: Mutex::new(0),
166            nonblocking: AtomicBool::new(false),
167        }
168    }
169
170    /// Block until the TTY input buffer becomes non-empty.
171    /// Used by polling syscalls (e.g. pselect6) to implement blocking semantics.
172    pub fn wait_until_readable(&self, trapframe: &mut Trapframe) {
173        loop {
174            if self.can_read() {
175                return;
176            }
177            if let Some(task) = mytask() {
178                self.input_waker.wait(task.get_id(), trapframe);
179            } else {
180                // No task context; abort wait
181                return;
182            }
183        }
184    }
185
186    /// Wake all tasks waiting for TTY input readiness.
187    /// This is used by timeout handlers to preempt a blocking wait.
188    pub fn wake_input(&self) {
189        self.input_waker.wake_all();
190    }
191
192    /// Wait until readable with a timeout (in ticks).
193    /// Returns true if timed out, false if input became available.
194    pub fn wait_until_readable_with_timeout_ticks(
195        &self,
196        trapframe: &mut Trapframe,
197        ticks: u64,
198    ) -> bool {
199        if self.can_read() {
200            return false;
201        }
202        if ticks == 0 {
203            return true;
204        }
205
206        struct TtyTimeoutHandler {
207            tty_ptr: *const TtyDevice,
208        }
209        unsafe impl Send for TtyTimeoutHandler {}
210        unsafe impl Sync for TtyTimeoutHandler {}
211        impl TimerHandler for TtyTimeoutHandler {
212            fn on_timer_expired(self: Arc<Self>, _context: usize) {
213                unsafe {
214                    (*self.tty_ptr).wake_input();
215                }
216            }
217        }
218
219        let deadline = get_tick().saturating_add(ticks);
220        let handler: Arc<dyn TimerHandler> = Arc::new(TtyTimeoutHandler {
221            tty_ptr: self as *const TtyDevice,
222        });
223        let timer_id = add_timer(deadline, &handler, 0);
224
225        if let Some(task) = mytask() {
226            self.input_waker.wait(task.get_id(), trapframe);
227        }
228
229        // After wake: cancel timer if still queued and decide reason
230        cancel_timer(timer_id);
231        if self.can_read() { false } else { true }
232    }
233
234    /// Current buffered input length (for diagnostics / readiness debugging)
235    pub fn input_len(&self) -> usize {
236        self.input_buffer.lock().len()
237    }
238
239    /// Read readiness for select/poll semantics.
240    /// - Canonical mode: ready only when a full line (ending with '\n') exists.
241    /// - Non-canonical: honor read_min_ready_bytes; in RAW mode head 0xE0 requires a pair.
242    pub fn is_read_ready_for_select(&self) -> bool {
243        if self.canonical_mode.load(Ordering::Relaxed) {
244            let g = self.input_buffer.lock();
245            return g.iter().any(|&b| b == b'\n');
246        }
247
248        let min_ready = self.read_min_ready_bytes.load(Ordering::Relaxed) as usize;
249        let kb_mode = self.kb_mode.load(Ordering::Relaxed);
250
251        // If min_ready == 0, read() is defined to return immediately
252        // (possibly 0 bytes), so this is non-blocking -> ready.
253        if min_ready == 0 {
254            return true;
255        }
256
257        if kb_mode == 2 {
258            let g = self.input_buffer.lock();
259            let available = g.len();
260            if let Some(&first) = g.front() {
261                if first == 0xE0 {
262                    if available < 2 {
263                        return false;
264                    }
265                    return available >= min_ready;
266                }
267            } else {
268                return false;
269            }
270            available >= min_ready
271        } else {
272            let available = self.input_len();
273            available >= min_ready
274        }
275    }
276
277    /// Handle input byte from UART device.
278    ///
279    /// This method processes incoming bytes and applies line discipline.
280    fn handle_input_byte(&self, byte: u8) {
281        if self.debug_enabled.load(Ordering::Relaxed) {
282            crate::println!(
283                "[TTY] RX byte=0x{:02x} '{}' canonical={} size={}",
284                byte,
285                if byte.is_ascii_graphic() || byte == b' ' {
286                    byte as char
287                } else {
288                    '.'
289                },
290                self.canonical_mode.load(Ordering::Relaxed),
291                self.input_buffer.lock().len()
292            );
293        }
294        // Canonical mode processing
295        if self.canonical_mode.load(Ordering::Relaxed) {
296            match byte {
297                // Backspace/DEL
298                0x08 | 0x7F => {
299                    let mut input_buffer = self.input_buffer.lock();
300                    if input_buffer.pop_back().is_some()
301                        && self.echo_enabled.load(Ordering::Relaxed)
302                    {
303                        self.echo_backspace();
304                    }
305                }
306                // Enter/Line feed
307                b'\r' | b'\n' => {
308                    if self.echo_enabled.load(Ordering::Relaxed) {
309                        self.echo_char(b'\r');
310                        self.echo_char(b'\n');
311                    }
312                    let mut input_buffer = self.input_buffer.lock();
313                    input_buffer.push_back(b'\n');
314                    drop(input_buffer);
315                    self.input_waker.wake_all();
316                }
317                // Ctrl-C (ETX) — policy deferred to ABI layer; no signal delivery here
318                0x03 => {
319                    if self.echo_enabled.load(Ordering::Relaxed) {
320                        self.echo_char('^' as u8);
321                        self.echo_char('C' as u8);
322                        self.echo_char('\r' as u8);
323                        self.echo_char('\n' as u8);
324                    }
325                }
326                // Ctrl-Z (SUB) placeholder
327                0x1A => {
328                    // No job-control semantics in device layer
329                }
330                // Regular characters
331                byte => {
332                    if self.echo_enabled.load(Ordering::Relaxed) {
333                        self.echo_char(byte);
334                    }
335                    let mut input_buffer = self.input_buffer.lock();
336                    input_buffer.push_back(byte);
337                    drop(input_buffer);
338                    if !self.canonical_mode.load(Ordering::Relaxed) {
339                        self.input_waker.wake_all();
340                    }
341                }
342            }
343        } else {
344            // Non-canonical path: honor keyboard mode
345            let kb_mode = self.kb_mode.load(Ordering::Relaxed);
346            if kb_mode == 0 {
347                // XLATE-like: pass through raw byte
348                let mut input_buffer = self.input_buffer.lock();
349                input_buffer.push_back(byte);
350                drop(input_buffer);
351                self.input_waker.wake_all();
352            } else if kb_mode == 1 {
353                // MEDIUMRAW: return 1-byte Linux keycode (press-only)
354                fn ascii_to_linux_keycode(b: u8) -> Option<u8> {
355                    match b {
356                        b'1' => Some(2),
357                        b'2' => Some(3),
358                        b'3' => Some(4),
359                        b'4' => Some(5),
360                        b'5' => Some(6),
361                        b'6' => Some(7),
362                        b'7' => Some(8),
363                        b'8' => Some(9),
364                        b'9' => Some(10),
365                        b'0' => Some(11),
366                        b'-' => Some(12),
367                        b'=' => Some(13),
368                        0x08 | 0x7F => Some(14), // Backspace
369                        b'\t' => Some(15),
370                        b'q' => Some(16),
371                        b'w' => Some(17),
372                        b'e' => Some(18),
373                        b'r' => Some(19),
374                        b't' => Some(20),
375                        b'y' => Some(21),
376                        b'u' => Some(22),
377                        b'i' => Some(23),
378                        b'o' => Some(24),
379                        b'p' => Some(25),
380                        b'[' => Some(26),
381                        b']' => Some(27),
382                        b'\n' | b'\r' => Some(28),
383                        b'a' => Some(30),
384                        b's' => Some(31),
385                        b'd' => Some(32),
386                        b'f' => Some(33),
387                        b'g' => Some(34),
388                        b'h' => Some(35),
389                        b'j' => Some(36),
390                        b'k' => Some(37),
391                        b'l' => Some(38),
392                        b';' => Some(39),
393                        b'\'' => Some(40),
394                        b'`' => Some(41),
395                        b'\\' => Some(43),
396                        b'z' => Some(44),
397                        b'x' => Some(45),
398                        b'c' => Some(46),
399                        b'v' => Some(47),
400                        b'b' => Some(48),
401                        b'n' => Some(49),
402                        b'm' => Some(50),
403                        b',' => Some(51),
404                        b'.' => Some(52),
405                        b'/' => Some(53),
406                        b' ' => Some(57),
407                        // Uppercase letters -> map to same keycode as lowercase
408                        b'A'..=b'Z' => Some(30 + (b.to_ascii_lowercase() - b'a') as u8),
409                        _ => None,
410                    }
411                }
412                fn push_keycode_press_release(dev: &TtyDevice, code: u8) {
413                    let mut input_buffer = dev.input_buffer.lock();
414                    input_buffer.push_back(code);
415                    input_buffer.push_back(code | 0x80); // release
416                    drop(input_buffer);
417                    dev.input_waker.wake_all();
418                }
419                // Interpret ESC/CSI/SS3 as Linux keycodes
420                let mut handled_escape = false;
421                {
422                    let mut st = self.esc_state.lock();
423                    match (*st, byte) {
424                        (0, 0x1B) => {
425                            *st = 1;
426                            handled_escape = true;
427                        }
428                        (1, b'[') => {
429                            *st = 2;
430                            handled_escape = true;
431                        }
432                        (1, b'O') => {
433                            *st = 3;
434                            handled_escape = true;
435                        }
436                        // Arrows (CSI)
437                        (2, b'A') => {
438                            *st = 0;
439                            handled_escape = true;
440                            push_keycode_press_release(self, 103);
441                        } // Up
442                        (2, b'B') => {
443                            *st = 0;
444                            handled_escape = true;
445                            push_keycode_press_release(self, 108);
446                        } // Down
447                        (2, b'C') => {
448                            *st = 0;
449                            handled_escape = true;
450                            push_keycode_press_release(self, 106);
451                        } // Right
452                        (2, b'D') => {
453                            *st = 0;
454                            handled_escape = true;
455                            push_keycode_press_release(self, 105);
456                        } // Left
457                        // Arrows (SS3)
458                        (3, b'H') => {
459                            *st = 0;
460                            handled_escape = true;
461                            push_keycode_press_release(self, 103);
462                        }
463                        (3, b'P') => {
464                            *st = 0;
465                            handled_escape = true;
466                            push_keycode_press_release(self, 108);
467                        }
468                        (3, b'M') => {
469                            *st = 0;
470                            handled_escape = true;
471                            push_keycode_press_release(self, 106);
472                        }
473                        (3, b'K') => {
474                            *st = 0;
475                            handled_escape = true;
476                            push_keycode_press_release(self, 105);
477                        }
478                        // Home/End (CSI)
479                        (2, b'H') => {
480                            *st = 0;
481                            handled_escape = true;
482                            push_keycode_press_release(self, 102);
483                        } // Home
484                        (2, b'F') => {
485                            *st = 0;
486                            handled_escape = true;
487                            push_keycode_press_release(self, 107);
488                        } // End
489                        // Home/End (SS3)
490                        (3, b'F') => {
491                            *st = 0;
492                            handled_escape = true;
493                            push_keycode_press_release(self, 107);
494                        }
495                        // CSI numeric ~ sequences
496                        (2, b'2') => {
497                            *st = 4;
498                            handled_escape = true;
499                        } // -> Insert
500                        (2, b'3') => {
501                            *st = 5;
502                            handled_escape = true;
503                        } // -> Delete
504                        (2, b'5') => {
505                            *st = 6;
506                            handled_escape = true;
507                        } // -> PageUp
508                        (2, b'6') => {
509                            *st = 7;
510                            handled_escape = true;
511                        } // -> PageDown
512                        (4, b'~') => {
513                            *st = 0;
514                            handled_escape = true;
515                            push_keycode_press_release(self, 110);
516                        } // Insert
517                        (5, b'~') => {
518                            *st = 0;
519                            handled_escape = true;
520                            push_keycode_press_release(self, 111);
521                        } // Delete
522                        (6, b'~') => {
523                            *st = 0;
524                            handled_escape = true;
525                            push_keycode_press_release(self, 104);
526                        } // PageUp
527                        (7, b'~') => {
528                            *st = 0;
529                            handled_escape = true;
530                            push_keycode_press_release(self, 109);
531                        } // PageDown
532                        (1, _) | (2, _) | (3, _) => {
533                            *st = 0;
534                        }
535                        _ => {}
536                    }
537                }
538                if !handled_escape {
539                    if let Some(code) = ascii_to_linux_keycode(byte) {
540                        push_keycode_press_release(self, code);
541                    }
542                }
543            } else {
544                // RAW: XT Set1 scancodes (extended codes are E0-prefixed)
545                fn ascii_to_set1_scancode(b: u8) -> Option<u8> {
546                    match b {
547                        b'1' => Some(2),
548                        b'2' => Some(3),
549                        b'3' => Some(4),
550                        b'4' => Some(5),
551                        b'5' => Some(6),
552                        b'6' => Some(7),
553                        b'7' => Some(8),
554                        b'8' => Some(9),
555                        b'9' => Some(10),
556                        b'0' => Some(11),
557                        b'-' => Some(12),
558                        b'=' => Some(13),
559                        0x08 | 0x7F => Some(14),
560                        b'\t' => Some(15),
561                        b'q' => Some(16),
562                        b'w' => Some(17),
563                        b'e' => Some(18),
564                        b'r' => Some(19),
565                        b't' => Some(20),
566                        b'y' => Some(21),
567                        b'u' => Some(22),
568                        b'i' => Some(23),
569                        b'o' => Some(24),
570                        b'p' => Some(25),
571                        b'[' => Some(26),
572                        b']' => Some(27),
573                        b'\n' | b'\r' => Some(28),
574                        b'a' => Some(30),
575                        b's' => Some(31),
576                        b'd' => Some(32),
577                        b'f' => Some(33),
578                        b'g' => Some(34),
579                        b'h' => Some(35),
580                        b'j' => Some(36),
581                        b'k' => Some(37),
582                        b'l' => Some(38),
583                        b';' => Some(39),
584                        b'\'' => Some(40),
585                        b'`' => Some(41),
586                        b'\\' => Some(43),
587                        b'z' => Some(44),
588                        b'x' => Some(45),
589                        b'c' => Some(46),
590                        b'v' => Some(47),
591                        b'b' => Some(48),
592                        b'n' => Some(49),
593                        b'm' => Some(50),
594                        b',' => Some(51),
595                        b'.' => Some(52),
596                        b'/' => Some(53),
597                        b' ' => Some(57),
598                        b'A'..=b'Z' => Some(30 + (b.to_ascii_lowercase() - b'a') as u8),
599                        _ => None,
600                    }
601                }
602                fn push_scancode_press_release(dev: &TtyDevice, code: u8, extended: bool) {
603                    let mut input_buffer = dev.input_buffer.lock();
604                    // press
605                    if extended {
606                        input_buffer.push_back(0xE0);
607                    }
608                    input_buffer.push_back(code);
609                    // release
610                    if extended {
611                        input_buffer.push_back(0xE0);
612                    }
613                    input_buffer.push_back(code | 0x80);
614                    drop(input_buffer);
615                    dev.input_waker.wake_all();
616                }
617                let mut handled_escape = false;
618                {
619                    let mut st = self.esc_state.lock();
620                    match (*st, byte) {
621                        (0, 0x1B) => {
622                            *st = 1;
623                            handled_escape = true;
624                        }
625                        (1, b'[') => {
626                            *st = 2;
627                            handled_escape = true;
628                        }
629                        (1, b'O') => {
630                            *st = 3;
631                            handled_escape = true;
632                        }
633                        // Arrows via CSI
634                        (2, b'A') => {
635                            *st = 0;
636                            handled_escape = true;
637                            push_scancode_press_release(self, 0x48, true);
638                        } // Up   E0 48
639                        (2, b'B') => {
640                            *st = 0;
641                            handled_escape = true;
642                            push_scancode_press_release(self, 0x50, true);
643                        } // Down E0 50
644                        (2, b'C') => {
645                            *st = 0;
646                            handled_escape = true;
647                            push_scancode_press_release(self, 0x4D, true);
648                        } // Right E0 4D
649                        (2, b'D') => {
650                            *st = 0;
651                            handled_escape = true;
652                            push_scancode_press_release(self, 0x4B, true);
653                        } // Left  E0 4B
654                        // Arrows via SS3 keypad-style (DEC application keypad): O H/K/M/P etc.
655                        (3, b'H') => {
656                            *st = 0;
657                            handled_escape = true;
658                            push_scancode_press_release(self, 0x48, true);
659                        } // KP-8 = Up
660                        (3, b'P') => {
661                            *st = 0;
662                            handled_escape = true;
663                            push_scancode_press_release(self, 0x50, true);
664                        } // KP-2 = Down
665                        (3, b'M') => {
666                            *st = 0;
667                            handled_escape = true;
668                            push_scancode_press_release(self, 0x4D, true);
669                        } // KP-6 = Right
670                        (3, b'K') => {
671                            *st = 0;
672                            handled_escape = true;
673                            push_scancode_press_release(self, 0x4B, true);
674                        } // KP-4 = Left
675                        // Home/End via CSI
676                        (2, b'H') => {
677                            *st = 0;
678                            handled_escape = true;
679                            push_scancode_press_release(self, 0x47, true);
680                        } // Home  E0 47
681                        (2, b'F') => {
682                            *st = 0;
683                            handled_escape = true;
684                            push_scancode_press_release(self, 0x4F, true);
685                        } // End   E0 4F
686                        // Home/End via SS3 (common mappings)
687                        (3, b'F') => {
688                            *st = 0;
689                            handled_escape = true;
690                            push_scancode_press_release(self, 0x4F, true);
691                        }
692                        // CSI numeric ~ sequences
693                        (2, b'2') => {
694                            *st = 4;
695                            handled_escape = true;
696                        } // expect '~' => Insert
697                        (2, b'3') => {
698                            *st = 5;
699                            handled_escape = true;
700                        } // expect '~' => Delete
701                        (2, b'5') => {
702                            *st = 6;
703                            handled_escape = true;
704                        } // expect '~' => PageUp
705                        (2, b'6') => {
706                            *st = 7;
707                            handled_escape = true;
708                        } // expect '~' => PageDown
709                        (4, b'~') => {
710                            *st = 0;
711                            handled_escape = true;
712                            push_scancode_press_release(self, 0x52, true);
713                        } // Ins  E0 52
714                        (5, b'~') => {
715                            *st = 0;
716                            handled_escape = true;
717                            push_scancode_press_release(self, 0x53, true);
718                        } // Del  E0 53
719                        (6, b'~') => {
720                            *st = 0;
721                            handled_escape = true;
722                            push_scancode_press_release(self, 0x49, true);
723                        } // PgUp E0 49
724                        (7, b'~') => {
725                            *st = 0;
726                            handled_escape = true;
727                            push_scancode_press_release(self, 0x51, true);
728                        } // PgDn E0 51
729                        (1, _) | (2, _) | (3, _) => {
730                            // Unknown sequence, reset and fall through to ASCII mapping
731                            *st = 0;
732                        }
733                        _ => {}
734                    }
735                }
736                if !handled_escape {
737                    if let Some(code) = ascii_to_set1_scancode(byte) {
738                        // Non-extended: generate press/release
739                        let mut input_buffer = self.input_buffer.lock();
740                        input_buffer.push_back(code);
741                        input_buffer.push_back(code | 0x80);
742                        drop(input_buffer);
743                        self.input_waker.wake_all();
744                    }
745                }
746            }
747        }
748    }
749
750    /// Echo character back to output.
751    fn echo_char(&self, byte: u8) {
752        // Get actual UART device and output
753        let device_manager = DeviceManager::get_manager();
754        if let Some(uart_device) = device_manager.get_device(self.uart_device_id) {
755            // Use the new CharDevice API with internal mutability
756            if let Some(char_device) = uart_device.as_char_device() {
757                let _ = char_device.write_byte(byte);
758            }
759        }
760    }
761
762    /// Echo backspace sequence.
763    fn echo_backspace(&self) {
764        // Backspace echo: BS + space + BS
765        self.echo_char(0x08);
766        self.echo_char(b' ');
767        self.echo_char(0x08);
768    }
769}
770
771impl Selectable for TtyDevice {
772    fn current_ready(&self, interest: ReadyInterest) -> ReadySet {
773        let mut set = ReadySet::none();
774        if interest.read {
775            set.read = self.is_read_ready_for_select();
776        }
777        if interest.write {
778            // TTY writes are considered ready (no internal backpressure yet)
779            set.write = true;
780        }
781        if interest.except {
782            set.except = false;
783        }
784        set
785    }
786
787    fn wait_until_ready(
788        &self,
789        interest: ReadyInterest,
790        trapframe: &mut crate::arch::Trapframe,
791        timeout_ticks: Option<u64>,
792    ) -> SelectWaitOutcome {
793        // Only read interest requires actual waiting; write/except treated as always-ready
794        if interest.read {
795            match timeout_ticks {
796                Some(ticks) => {
797                    let timed_out = self.wait_until_readable_with_timeout_ticks(trapframe, ticks);
798                    if timed_out {
799                        SelectWaitOutcome::TimedOut
800                    } else {
801                        SelectWaitOutcome::Ready
802                    }
803                }
804                None => {
805                    self.wait_until_readable(trapframe);
806                    SelectWaitOutcome::Ready
807                }
808            }
809        } else {
810            SelectWaitOutcome::Ready
811        }
812    }
813
814    fn set_nonblocking(&self, enabled: bool) {
815        crate::println!("[TTY] set_nonblocking: {}", enabled);
816        self.nonblocking.store(enabled, Ordering::Relaxed);
817    }
818
819    fn is_nonblocking(&self) -> bool {
820        self.nonblocking.load(Ordering::Relaxed)
821    }
822}
823
824impl TtyControl for TtyDevice {
825    fn set_echo(&self, enabled: bool) {
826        self.echo_enabled.store(enabled, Ordering::Relaxed);
827    }
828    fn is_echo_enabled(&self) -> bool {
829        self.echo_enabled.load(Ordering::Relaxed)
830    }
831
832    fn set_canonical(&self, enabled: bool) {
833        self.canonical_mode.store(enabled, Ordering::Relaxed);
834    }
835    fn is_canonical(&self) -> bool {
836        self.canonical_mode.load(Ordering::Relaxed)
837    }
838
839    fn set_winsize(&self, cols: u16, rows: u16) {
840        *self.winsize_cols.lock() = cols;
841        *self.winsize_rows.lock() = rows;
842    }
843    fn get_winsize(&self) -> (u16, u16) {
844        (*self.winsize_cols.lock(), *self.winsize_rows.lock())
845    }
846}
847
848impl DeviceEventListener for TtyDevice {
849    fn on_device_event(&self, event: &dyn DeviceEvent) {
850        if let Some(input_event) = event.as_any().downcast_ref::<InputEvent>() {
851            self.handle_input_byte(input_event.data);
852        }
853    }
854
855    fn interested_in(&self, event_type: &str) -> bool {
856        event_type == "input"
857    }
858}
859
860impl MemoryMappingOps for TtyDevice {
861    fn get_mapping_info(
862        &self,
863        _offset: usize,
864        _length: usize,
865    ) -> Result<(usize, usize, bool), &'static str> {
866        Err("Memory mapping not supported by TTY device")
867    }
868
869    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
870        // TTY devices don't support memory mapping
871    }
872
873    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
874        // TTY devices don't support memory mapping
875    }
876
877    fn supports_mmap(&self) -> bool {
878        false
879    }
880}
881
882impl Device for TtyDevice {
883    fn device_type(&self) -> DeviceType {
884        DeviceType::Char
885    }
886
887    fn name(&self) -> &'static str {
888        self.name
889    }
890
891    fn as_any(&self) -> &dyn Any {
892        self
893    }
894
895    fn as_any_mut(&mut self) -> &mut dyn Any {
896        self
897    }
898
899    fn as_char_device(&self) -> Option<&dyn CharDevice> {
900        Some(self)
901    }
902
903    fn capabilities(&self) -> &'static [DeviceCapability] {
904        &TTY_CAPS
905    }
906}
907
908impl CharDevice for TtyDevice {
909    fn read(&self, buffer: &mut [u8]) -> usize {
910        if buffer.is_empty() {
911            return 0;
912        }
913
914        // Fast path: non-blocking immediate return if policy requires 0 and no data
915        let min_ready = self.read_min_ready_bytes.load(Ordering::Relaxed) as usize;
916        let canonical = self.canonical_mode.load(Ordering::Relaxed);
917
918        // Helper to copy out up to buffer.len() from input buffer
919        let copy_out = |buf: &mut [u8], until_newline: bool| -> usize {
920            let mut bytes = 0;
921            let mut guard = self.input_buffer.lock();
922            if until_newline {
923                // Find newline position
924                if let Some(pos) = guard.iter().position(|&b| b == b'\n') {
925                    // Include the newline itself
926                    let take = core::cmp::min(pos + 1, buf.len());
927                    for i in 0..take {
928                        if let Some(b) = guard.pop_front() {
929                            buf[i] = b;
930                            bytes += 1;
931                        } else {
932                            break;
933                        }
934                    }
935                }
936            } else {
937                while bytes < buf.len() {
938                    if let Some(b) = guard.pop_front() {
939                        buf[bytes] = b;
940                        bytes += 1;
941                    } else {
942                        break;
943                    }
944                }
945            }
946            bytes
947        };
948
949        // Canonical mode: block until a full line (ending with '\n') is available
950        if canonical {
951            loop {
952                // If a newline exists, copy out a line chunk
953                {
954                    let has_newline = {
955                        let g = self.input_buffer.lock();
956                        g.iter().any(|&b| b == b'\n')
957                    };
958                    if has_newline {
959                        return copy_out(buffer, true);
960                    }
961                }
962                // Wait for more input
963                if self.nonblocking.load(Ordering::Relaxed) {
964                    return 0; // non-blocking: no data yet
965                }
966                if let Some(task) = mytask() {
967                    self.input_waker.wait(task.get_id(), task.get_trapframe());
968                } else {
969                    // No task context; return nothing
970                    return 0;
971                }
972            }
973        }
974
975        // Non-canonical (raw-ish): honor min_ready_bytes policy, and group E0-prefixed scancodes
976        if min_ready == 0 {
977            // Immediate return with whatever is available (possibly 0)
978            // For RAW prefer to return E0 2-byte pairs atomically
979            let kb_mode = self.kb_mode.load(Ordering::Relaxed);
980            if kb_mode == 2 {
981                let mut guard = self.input_buffer.lock();
982                if let Some(&first) = guard.front() {
983                    if first == 0xE0 {
984                        if guard.len() >= 2 && buffer.len() >= 2 {
985                            let b0 = guard.pop_front().unwrap();
986                            let b1 = guard.pop_front().unwrap();
987                            drop(guard);
988                            buffer[0] = b0;
989                            buffer[1] = b1;
990                            return 2;
991                        }
992                    }
993                }
994                drop(guard);
995            }
996            return copy_out(buffer, false);
997        }
998
999        loop {
1000            let kb_mode = self.kb_mode.load(Ordering::Relaxed);
1001            if kb_mode == 2 {
1002                // RAW: if head is E0 and caller expects 2 bytes, wait for pair.
1003                let (need_pair, have_pair) = {
1004                    let g = self.input_buffer.lock();
1005                    let head_is_e0 = g.front().map(|b| *b == 0xE0).unwrap_or(false);
1006                    let have = g.len() >= 2;
1007                    (head_is_e0 && buffer.len() >= 2, have)
1008                };
1009                if need_pair && !have_pair {
1010                    if self.nonblocking.load(Ordering::Relaxed) {
1011                        return 0;
1012                    }
1013                    if let Some(task) = mytask() {
1014                        self.input_waker.wait(task.get_id(), task.get_trapframe());
1015                        continue;
1016                    } else {
1017                        return 0;
1018                    }
1019                }
1020            }
1021
1022            let available = { self.input_buffer.lock().len() };
1023            if available >= core::cmp::min(min_ready as usize, buffer.len()) {
1024                // Try to return E0 pair atomically if possible
1025                let kb_mode = self.kb_mode.load(Ordering::Relaxed);
1026                if kb_mode == 2 {
1027                    let mut guard = self.input_buffer.lock();
1028                    if let Some(&first) = guard.front() {
1029                        if first == 0xE0 && guard.len() >= 2 && buffer.len() >= 2 {
1030                            let b0 = guard.pop_front().unwrap();
1031                            let b1 = guard.pop_front().unwrap();
1032                            drop(guard);
1033                            buffer[0] = b0;
1034                            buffer[1] = b1;
1035                            return 2;
1036                        }
1037                    }
1038                    drop(guard);
1039                }
1040                return copy_out(buffer, false);
1041            }
1042            // Not enough yet; block until new input arrives
1043            if self.nonblocking.load(Ordering::Relaxed) {
1044                return 0;
1045            }
1046            if let Some(task) = mytask() {
1047                self.input_waker.wait(task.get_id(), task.get_trapframe());
1048            } else {
1049                return 0;
1050            }
1051        }
1052    }
1053    fn read_byte(&self) -> Option<u8> {
1054        // Loop until data becomes available
1055        loop {
1056            let mut input_buffer = self.input_buffer.lock();
1057            if let Some(byte) = input_buffer.pop_front() {
1058                return Some(byte);
1059            }
1060            drop(input_buffer);
1061
1062            // No data available, block the current task
1063            if let Some(task) = mytask() {
1064                // Wait for input to become available
1065                // This will return when the task is woken up by input_waker.wake_all()
1066                self.input_waker.wait(task.get_id(), task.get_trapframe());
1067
1068                // Continue the loop to re-check if data is available
1069                continue;
1070            } else {
1071                // No current task context, return None
1072                return None;
1073            }
1074        }
1075    }
1076
1077    fn write_byte(&self, byte: u8) -> Result<(), &'static str> {
1078        // Forward to UART device with line ending conversion
1079        let device_manager = DeviceManager::get_manager();
1080        if let Some(uart_device) = device_manager.get_device(self.uart_device_id) {
1081            // Use the new CharDevice API with internal mutability
1082            if let Some(char_device) = uart_device.as_char_device() {
1083                // Handle line ending conversion for terminals
1084                if byte == b'\n' {
1085                    char_device.write_byte(b'\r')?;
1086                    char_device.write_byte(b'\n')?;
1087                } else {
1088                    char_device.write_byte(byte)?;
1089                }
1090                return Ok(());
1091            }
1092        }
1093        Err("UART device not available")
1094    }
1095
1096    fn can_read(&self) -> bool {
1097        let input_buffer = self.input_buffer.lock();
1098        !input_buffer.is_empty()
1099    }
1100
1101    fn can_write(&self) -> bool {
1102        // Check if backend char device is available and writable
1103        let device_manager = DeviceManager::get_manager();
1104        if let Some(dev) = device_manager.get_device(self.uart_device_id) {
1105            if let Some(cdev) = dev.as_char_device() {
1106                return cdev.can_write();
1107            }
1108        }
1109        false
1110    }
1111}
1112
1113impl ControlOps for TtyDevice {
1114    // TTY devices accept Scarlet-private, OS-agnostic control opcodes.
1115    fn control(&self, command: u32, arg: usize) -> Result<i32, &'static str> {
1116        match command {
1117            SCTL_TTY_SET_ECHO => {
1118                self.set_echo(arg != 0);
1119                Ok(0)
1120            }
1121            SCTL_TTY_GET_ECHO => Ok(self.is_echo_enabled() as i32),
1122            SCTL_TTY_SET_CANONICAL => {
1123                self.set_canonical(arg != 0);
1124                Ok(0)
1125            }
1126            SCTL_TTY_GET_CANONICAL => Ok(self.is_canonical() as i32),
1127            SCTL_TTY_SET_WINSIZE => {
1128                let cols = ((arg >> 16) & 0xFFFF) as u16;
1129                let rows = (arg & 0xFFFF) as u16;
1130                self.set_winsize(cols, rows);
1131                Ok(0)
1132            }
1133            SCTL_TTY_GET_WINSIZE => {
1134                let (cols, rows) = self.get_winsize();
1135                let packed = ((cols as u32) << 16) | (rows as u32);
1136                Ok(packed as i32)
1137            }
1138            SCTL_TTY_SET_READ_POLICY => {
1139                let min_ready = (arg & 0xFFFF) as u16;
1140                let timeout_ms = ((arg >> 16) & 0xFFFF) as u16;
1141                self.read_min_ready_bytes
1142                    .store(min_ready, Ordering::Relaxed);
1143                self.read_timeout_ms.store(timeout_ms, Ordering::Relaxed);
1144                Ok(0)
1145            }
1146            SCTL_TTY_GET_READ_POLICY => {
1147                let min_ready = self.read_min_ready_bytes.load(Ordering::Relaxed) as u32;
1148                let timeout_ms = self.read_timeout_ms.load(Ordering::Relaxed) as u32;
1149                let packed = (timeout_ms << 16) | min_ready;
1150                Ok(packed as i32)
1151            }
1152            SCTL_TTY_FLUSH_INPUT => {
1153                let mut g = self.input_buffer.lock();
1154                g.clear();
1155                Ok(0)
1156            }
1157            SCTL_TTY_SET_DEBUG => {
1158                self.debug_enabled.store(arg != 0, Ordering::Relaxed);
1159                Ok(0)
1160            }
1161            SCTL_TTY_GET_DEBUG => Ok(self.debug_enabled.load(Ordering::Relaxed) as i32),
1162            SCTL_TTY_SET_KBMODE => {
1163                let v = (arg & 0xFF) as u8;
1164                self.kb_mode.store(v, Ordering::Relaxed);
1165                Ok(0)
1166            }
1167            SCTL_TTY_GET_KBMODE => Ok(self.kb_mode.load(Ordering::Relaxed) as i32),
1168            _ => Err("Unsupported control command for TTY device"),
1169        }
1170    }
1171}