kernel/device/char/
mod.rs

1use core::any::Any;
2
3use super::Device;
4use crate::object::capability::selectable::{
5    ReadyInterest, ReadySet, SelectWaitOutcome, Selectable,
6};
7use crate::object::capability::{ControlOps, MemoryMappingOps};
8
9extern crate alloc;
10
11/// OS/ABI-agnostic TTY control interface
12///
13/// This trait intentionally avoids Linux/POSIX terms and numbers.
14/// ABI adapters (e.g., abi/linux) should translate their own termios/ioctl
15/// to these neutral controls.
16pub trait TtyControl {
17    /// Enable or disable local echo.
18    fn set_echo(&self, enabled: bool);
19    /// Returns whether local echo is enabled.
20    fn is_echo_enabled(&self) -> bool;
21
22    /// Enable or disable canonical (line) mode.
23    fn set_canonical(&self, enabled: bool);
24    /// Returns whether canonical (line) mode is enabled.
25    fn is_canonical(&self) -> bool;
26
27    /// Set terminal window size in character cells.
28    fn set_winsize(&self, cols: u16, rows: u16);
29    /// Get terminal window size in character cells.
30    fn get_winsize(&self) -> (u16, u16);
31}
32
33/// Composite endpoint trait for TTY devices (byte stream + neutral controls)
34pub trait TtyDeviceEndpoint: CharDevice + TtyControl {}
35impl<T: CharDevice + TtyControl> TtyDeviceEndpoint for T {}
36
37/// Seek operations for character device positioning
38#[derive(Debug, Clone, Copy)]
39pub enum SeekFrom {
40    /// Seek from the beginning of the device
41    Start(u64),
42    /// Seek relative to the current position
43    Current(i64),
44    /// Seek relative to the end of the device
45    End(i64),
46}
47
48/// Character device interface
49///
50/// This trait defines the interface for character devices.
51/// It provides methods for querying device information and handling character I/O operations.
52/// Uses internal mutability for thread-safe shared access.
53pub trait CharDevice: Device {
54    /// Read a single byte from the device
55    ///
56    /// For blocking devices (like TTY), this method will block until data is available.
57    /// For non-blocking devices, this returns None if no data is available.
58    ///
59    /// # Returns
60    ///
61    /// The byte read from the device, or None if no data is available
62    fn read_byte(&self) -> Option<u8>;
63
64    /// Write a single byte to the device
65    ///
66    /// # Arguments
67    ///
68    /// * `byte` - The byte to write to the device
69    ///
70    /// # Returns
71    ///
72    /// Result indicating success or failure
73    fn write_byte(&self, byte: u8) -> Result<(), &'static str>;
74
75    /// Read multiple bytes from the device
76    ///
77    /// # Arguments
78    ///
79    /// * `buffer` - The buffer to read data into
80    ///
81    /// # Returns
82    ///
83    /// The number of bytes actually read
84    fn read(&self, buffer: &mut [u8]) -> usize {
85        let mut bytes_read = 0;
86        for i in 0..buffer.len() {
87            if let Some(byte) = self.read_byte() {
88                buffer[i] = byte;
89                bytes_read += 1;
90            } else {
91                break;
92            }
93        }
94        bytes_read
95    }
96
97    /// Write multiple bytes to the device
98    ///
99    /// # Arguments
100    ///
101    /// * `buffer` - The buffer containing data to write
102    ///
103    /// # Returns
104    ///
105    /// Result containing the number of bytes written or an error
106    fn write(&self, buffer: &[u8]) -> Result<usize, &'static str> {
107        let mut bytes_written = 0;
108        for &byte in buffer {
109            self.write_byte(byte)?;
110            bytes_written += 1;
111        }
112        Ok(bytes_written)
113    }
114
115    /// Check if the device is ready for reading
116    fn can_read(&self) -> bool;
117
118    /// Check if the device is ready for writing
119    fn can_write(&self) -> bool;
120
121    /// Read data from a specific position in the device
122    ///
123    /// Default implementation falls back to sequential read for stream devices.
124    /// Devices that support random access (like framebuffer, memory devices) should override this.
125    ///
126    /// # Arguments
127    ///
128    /// * `position` - Byte offset to read from
129    /// * `buffer` - Buffer to read data into
130    ///
131    /// # Returns
132    ///
133    /// Result containing the number of bytes read or an error
134    fn read_at(&self, _position: u64, buffer: &mut [u8]) -> Result<usize, &'static str> {
135        // Default: use sequential read for stream devices
136        Ok(self.read(buffer))
137    }
138
139    /// Write data to a specific position in the device
140    ///
141    /// Default implementation falls back to sequential write for stream devices.
142    /// Devices that support random access (like framebuffer, memory devices) should override this.
143    ///
144    /// # Arguments
145    ///
146    /// * `position` - Byte offset to write to
147    /// * `buffer` - Buffer containing data to write
148    ///
149    /// # Returns
150    ///
151    /// Result containing the number of bytes written or an error
152    fn write_at(&self, _position: u64, buffer: &[u8]) -> Result<usize, &'static str> {
153        // Default: use sequential write for stream devices
154        self.write(buffer)
155    }
156
157    /// Check if this device supports seek operations
158    ///
159    /// Default implementation returns false for stream devices.
160    /// Devices that support seeking should override this.
161    ///
162    /// # Returns
163    ///
164    /// True if the device supports seek operations
165    fn can_seek(&self) -> bool {
166        false
167    }
168}
169
170/// A generic implementation of a character device
171pub struct GenericCharDevice {
172    device_name: &'static str,
173    read_fn: fn() -> Option<u8>,
174    write_fn: fn(u8) -> Result<(), &'static str>,
175    can_read_fn: fn() -> bool,
176    can_write_fn: fn() -> bool,
177}
178
179impl GenericCharDevice {
180    pub fn new(
181        device_name: &'static str,
182        read_fn: fn() -> Option<u8>,
183        write_fn: fn(u8) -> Result<(), &'static str>,
184        can_read_fn: fn() -> bool,
185        can_write_fn: fn() -> bool,
186    ) -> Self {
187        Self {
188            device_name,
189            read_fn,
190            write_fn,
191            can_read_fn,
192            can_write_fn,
193        }
194    }
195}
196
197impl Device for GenericCharDevice {
198    fn device_type(&self) -> super::DeviceType {
199        super::DeviceType::Char
200    }
201
202    fn name(&self) -> &'static str {
203        self.device_name
204    }
205
206    fn as_any(&self) -> &dyn Any {
207        self
208    }
209
210    fn as_any_mut(&mut self) -> &mut dyn Any {
211        self
212    }
213
214    fn as_char_device(&self) -> Option<&dyn CharDevice> {
215        Some(self)
216    }
217}
218
219impl CharDevice for GenericCharDevice {
220    fn read_byte(&self) -> Option<u8> {
221        (self.read_fn)()
222    }
223
224    fn write_byte(&self, byte: u8) -> Result<(), &'static str> {
225        (self.write_fn)(byte)
226    }
227
228    fn can_read(&self) -> bool {
229        (self.can_read_fn)()
230    }
231
232    fn can_write(&self) -> bool {
233        (self.can_write_fn)()
234    }
235}
236
237impl ControlOps for GenericCharDevice {
238    // Generic character devices don't support control operations by default
239    fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
240        Err("Control operations not supported")
241    }
242}
243
244impl MemoryMappingOps for GenericCharDevice {
245    fn get_mapping_info(
246        &self,
247        _offset: usize,
248        _length: usize,
249    ) -> Result<(usize, usize, bool), &'static str> {
250        Err("Memory mapping not supported by this character device")
251    }
252
253    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
254        // Generic character devices don't support memory mapping
255    }
256
257    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
258        // Generic character devices don't support memory mapping
259    }
260
261    fn supports_mmap(&self) -> bool {
262        false
263    }
264}
265
266impl Selectable for GenericCharDevice {
267    fn current_ready(&self, interest: ReadyInterest) -> ReadySet {
268        let mut set = ReadySet::none();
269        if interest.read {
270            set.read = self.can_read();
271        }
272        if interest.write {
273            set.write = self.can_write();
274        }
275        if interest.except {
276            set.except = false;
277        }
278        set
279    }
280
281    fn wait_until_ready(
282        &self,
283        _interest: ReadyInterest,
284        _trapframe: &mut crate::arch::Trapframe,
285        _timeout_ticks: Option<u64>,
286    ) -> SelectWaitOutcome {
287        SelectWaitOutcome::Ready
288    }
289
290    fn is_nonblocking(&self) -> bool {
291        true
292    }
293}
294
295#[cfg(test)]
296mod tests;
297
298#[cfg(test)]
299pub mod mockchar;
300
301pub mod tty;