keos/teletype.rs
1//! A teletype (TTY) interface for character-based I/O.
2//!
3//! This module provides a trait [`Teletype`] that defines an interface for
4//! reading from and writing to a teletype device, such as a serial port.
5//! The [`Serial`] struct implements this interface for x86_64 systems.
6
7use crate::{KernelError, spinlock::SpinLock, thread::with_current};
8
9/// The `Teletype` trait represents a generic character-based input/output
10/// device.
11///
12/// Implementations of this trait define methods for:
13/// - Writing data to the teletype (`write`)
14/// - Reading data from the teletype (`read`)
15///
16/// This abstraction allows for different kinds of terminal or serial interfaces
17/// to implement their own communication methods.
18pub trait Teletype {
19 /// Writes data to the teletype.
20 ///
21 /// # Arguments
22 /// - `data`: A byte slice containing the data to be written.
23 ///
24 /// # Returns
25 /// - `Ok(usize)`: The number of bytes successfully written.
26 /// - `Err(KernelError)`: If the write operation failed.
27 fn write(&mut self, data: &[u8]) -> Result<usize, KernelError>;
28
29 /// Reads data from the teletype.
30 ///
31 /// # Arguments
32 /// - `data`: A mutable byte slice where the read data will be stored.
33 ///
34 /// # Returns
35 /// - `Ok(usize)`: The number of bytes successfully read.
36 /// - `Err(KernelError)`: If the read operation failed.
37 fn read(&mut self, data: &mut [u8]) -> Result<usize, KernelError>;
38}
39
40/// A serial teletype interface for x86_64 systems.
41///
42/// This struct provides a basic implementation of a serial TTY using the
43/// **COM1** serial port. It implements the [`Teletype`] trait to allow
44/// read and write operations over a serial interface.
45pub struct Serial {
46 _p: (),
47}
48
49impl Serial {
50 /// Creates a new **COM1** serial interface instance.
51 ///
52 /// This function initializes a serial TTY for performing character-based
53 /// I/O operations. The actual hardware interaction is handled via the
54 /// [`Teletype`] trait methods (`write` and `read`).
55 ///
56 /// # Returns
57 /// - A new instance of `Serial`, representing a COM1 serial interface.
58 pub const fn new() -> Self {
59 Self { _p: () }
60 }
61}
62
63impl Default for Serial {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69/// A global serial device protected by a spinlock.
70///
71/// This static instance of [`Serial`] ensures safe concurrent access to the
72/// serial port. It is wrapped in a [`SpinLock`] to provide mutual exclusion,
73/// preventing race conditions when multiple threads attempt to write to or read
74/// from the serial device.
75///
76/// The [`Serial`] struct typically represents a UART (Universal Asynchronous
77/// Receiver-Transmitter) device used for debugging, logging, or kernel output.
78///
79/// # Safety
80/// - Accessing this global requires acquiring the spinlock before modifying the
81/// serial state.
82/// - Since [`SpinLock`] is used instead of [`Mutex`], it should **only be used
83/// in environments without preemption**, such as kernel mode, to avoid
84/// deadlocks.
85///
86/// [`Mutex`]: struct.Mutex.html
87static SERIAL: SpinLock<Serial> = SpinLock::new(Serial::new());
88
89/// Returns a reference to the global serial device.
90///
91/// This function provides safe access to the global serial interface wrapped in
92/// a [`SpinLock`]. Users must lock the spinlock before performing any
93/// operations on the [`Serial`] instance.
94///
95/// # Example
96/// ```
97/// use keos::teletype::Teletype;
98///
99/// let serial = serial().lock();
100/// serial.write("Hello, serial output!").expect("Failed to write tty");
101/// ```
102///
103/// # Safety
104/// - Since this returns a reference to a global [`SpinLock`], the caller must
105/// **ensure proper locking** before accessing the [`Serial`] device.
106///
107/// # Returns
108/// A reference to the [`SpinLock`] wrapping the global [`Serial`] instance.
109pub fn serial() -> &'static SpinLock<Serial> {
110 &SERIAL
111}
112
113impl Teletype for Serial {
114 /// Writes data to the serial teletype (COM1).
115 ///
116 /// This function attempts to convert the input byte slice into a UTF-8
117 /// string. If the conversion is successful, it prints the string to
118 /// the console. If the data is aligned to a **16-byte boundary**, it
119 /// is printed as a single string; otherwise, it is printed byte by byte.
120 ///
121 /// # Arguments
122 /// - `data`: The byte slice to be written.
123 ///
124 /// # Returns
125 /// - `Ok(usize)`: The number of bytes written.
126 /// - `Err`: If the input data is not valid UTF-8.
127 fn write(&mut self, data: &[u8]) -> Result<usize, KernelError> {
128 with_current(|th| {
129 let b = if data.as_ptr().is_aligned_to(8) {
130 if let Ok(b) = core::str::from_utf8(data) {
131 print!("{}", b);
132 Ok(data.len())
133 } else {
134 Err(KernelError::InvalidArgument)
135 }
136 } else {
137 for b in data {
138 print!("{}", b);
139 }
140 Ok(data.len())
141 };
142 let mut tty_hook = th.tty_hook.lock();
143 let val = match tty_hook.as_mut() {
144 Some(ttyhook) => {
145 let mut guard = ttyhook.lock();
146 let val = guard.write(data);
147 guard.unlock();
148 val
149 }
150 _ => b,
151 };
152 tty_hook.unlock();
153 val
154 })
155 }
156
157 /// Reads data from the serial teletype (COM1).
158 ///
159 /// This function retrieves data from the serial interface and stores it
160 /// in the provided mutable buffer.
161 ///
162 /// # Arguments
163 /// - `data`: A mutable byte slice where the read data will be stored.
164 ///
165 /// # Returns
166 /// - `Ok(usize)`: The number of bytes successfully read.
167 /// - `Err`: If the read operation failed.
168 fn read(&mut self, data: &mut [u8]) -> Result<usize, KernelError> {
169 with_current(|th| {
170 let mut tty_guard = th.tty_hook.lock();
171
172 let val = match tty_guard.as_mut() {
173 Some(ttyhook) => {
174 let mut guard = ttyhook.lock();
175 let val = guard.read(data);
176 guard.unlock();
177 val
178 }
179 _ => abyss::dev::x86_64::serial::read_bytes_busywait(data)
180 .ok_or(KernelError::IOError),
181 };
182 tty_guard.unlock();
183 val
184 })
185 }
186}