1use crate::{
6 sync::SpinLock,
7 uapi::ioctl::Termios,
8 uapi::time::TimeSpec,
9 vfs::{File, FileMode, FsError, InodeMetadata, InodeType},
10};
11use alloc::sync::Arc;
12
13static STDIO_TERMIOS: SpinLock<Termios> = SpinLock::new(Termios::DEFAULT);
15
16static STDIO_WINSIZE: SpinLock<crate::uapi::ioctl::WinSize> =
18 SpinLock::new(crate::uapi::ioctl::WinSize {
19 ws_row: 24,
20 ws_col: 80,
21 ws_xpixel: 0,
22 ws_ypixel: 0,
23 });
24
25pub struct StdinFile;
29
30impl File for StdinFile {
31 fn readable(&self) -> bool {
32 true
33 }
34
35 fn writable(&self) -> bool {
36 false
37 }
38
39 fn read(&self, buf: &mut [u8]) -> Result<usize, FsError> {
40 use crate::arch::lib::sbi::console_getchar;
41
42 let mut count = 0;
43 for byte in buf.iter_mut() {
44 let ch = console_getchar();
45 if ch == usize::MAX {
47 break;
48 }
49
50 *byte = ch as u8;
51 count += 1;
52
53 if ch == b'\n' as usize {
54 break;
55 }
56 }
57
58 Ok(count)
59 }
60
61 fn write(&self, _buf: &[u8]) -> Result<usize, FsError> {
62 Err(FsError::PermissionDenied)
63 }
64
65 fn metadata(&self) -> Result<InodeMetadata, FsError> {
66 Ok(InodeMetadata {
67 inode_no: 0,
68 inode_type: InodeType::CharDevice,
69 mode: FileMode::S_IFCHR | FileMode::S_IRUSR,
70 uid: 0,
71 gid: 0,
72 size: 0,
73 atime: TimeSpec::now(),
74 mtime: TimeSpec::now(),
75 ctime: TimeSpec::now(),
76 nlinks: 1,
77 blocks: 0,
78 rdev: 0,
79 })
80 }
81
82 fn ioctl(&self, request: u32, arg: usize) -> Result<isize, FsError> {
83 stdio_ioctl(request, arg)
84 }
85
86 fn as_any(&self) -> &dyn core::any::Any {
88 self
89 }
90}
91
92pub struct StdoutFile;
96
97impl File for StdoutFile {
98 fn readable(&self) -> bool {
99 false
100 }
101
102 fn writable(&self) -> bool {
103 true
104 }
105
106 fn read(&self, _buf: &mut [u8]) -> Result<usize, FsError> {
107 Err(FsError::PermissionDenied)
108 }
109
110 fn write(&self, buf: &[u8]) -> Result<usize, FsError> {
111 use crate::arch::lib::sbi::console_putchar;
112 for &byte in buf {
113 console_putchar(byte as usize);
114 }
115 Ok(buf.len())
116 }
117
118 fn metadata(&self) -> Result<InodeMetadata, FsError> {
119 Ok(InodeMetadata {
120 inode_no: 1,
121 inode_type: InodeType::CharDevice,
122 mode: FileMode::S_IFCHR | FileMode::S_IWUSR,
123 uid: 0,
124 gid: 0,
125 size: 0,
126 atime: TimeSpec::now(),
127 mtime: TimeSpec::now(),
128 ctime: TimeSpec::now(),
129 nlinks: 1,
130 blocks: 0,
131 rdev: 0,
132 })
133 }
134
135 fn ioctl(&self, request: u32, arg: usize) -> Result<isize, FsError> {
136 stdio_ioctl(request, arg)
137 }
138 fn as_any(&self) -> &dyn core::any::Any {
139 self
140 }
141}
142
143pub struct StderrFile;
147
148impl File for StderrFile {
149 fn readable(&self) -> bool {
150 false
151 }
152
153 fn writable(&self) -> bool {
154 true
155 }
156
157 fn read(&self, _buf: &mut [u8]) -> Result<usize, FsError> {
158 Err(FsError::PermissionDenied)
159 }
160
161 fn write(&self, buf: &[u8]) -> Result<usize, FsError> {
162 use crate::arch::lib::sbi::console_putchar;
163 for &byte in buf {
164 console_putchar(byte as usize);
165 }
166 Ok(buf.len())
167 }
168
169 fn metadata(&self) -> Result<InodeMetadata, FsError> {
170 Ok(InodeMetadata {
171 inode_no: 2,
172 inode_type: InodeType::CharDevice,
173 mode: FileMode::S_IFCHR | FileMode::S_IWUSR,
174 uid: 0,
175 gid: 0,
176 size: 0,
177 atime: TimeSpec::now(),
178 mtime: TimeSpec::now(),
179 ctime: TimeSpec::now(),
180 nlinks: 1,
181 blocks: 0,
182 rdev: 0,
183 })
184 }
185
186 fn ioctl(&self, request: u32, arg: usize) -> Result<isize, FsError> {
187 stdio_ioctl(request, arg)
188 }
189 fn as_any(&self) -> &dyn core::any::Any {
190 self
191 }
192}
193
194fn stdio_ioctl(request: u32, arg: usize) -> Result<isize, FsError> {
196 use crate::arch::trap::SumGuard;
197 use crate::uapi::errno::{EINVAL, ENOTTY};
198 use crate::uapi::ioctl::*;
199
200 match request {
201 TCGETS => {
202 if arg == 0 {
203 return Ok(-EINVAL as isize);
204 }
205
206 unsafe {
207 let _guard = SumGuard::new();
208 let termios_ptr = arg as *mut Termios;
209 if termios_ptr.is_null() {
210 return Ok(-EINVAL as isize);
211 }
212
213 core::ptr::write_bytes(termios_ptr, 0, 1);
215
216 let termios = *STDIO_TERMIOS.lock();
218 core::ptr::write_volatile(termios_ptr, termios);
219
220 use crate::earlyprintln;
222 earlyprintln!(
223 "TCGETS: returning termios: iflag={:#x}, oflag={:#x}, cflag={:#x}, lflag={:#x}, ispeed={:#x}, ospeed={:#x}",
224 termios.c_iflag,
225 termios.c_oflag,
226 termios.c_cflag,
227 termios.c_lflag,
228 termios.c_ispeed,
229 termios.c_ospeed
230 );
231 }
232 Ok(0)
233 }
234
235 TCSETS | TCSETSW | TCSETSF => {
236 if arg == 0 {
237 return Ok(-EINVAL as isize);
238 }
239
240 unsafe {
241 let _guard = SumGuard::new();
242 let termios_ptr = arg as *const Termios;
243 if termios_ptr.is_null() {
244 return Ok(-EINVAL as isize);
245 }
246
247 let new_termios = core::ptr::read_volatile(termios_ptr);
249
250 use crate::earlyprintln;
252 earlyprintln!(
253 "TCSETS: received termios: iflag={:#x}, oflag={:#x}, cflag={:#x}, lflag={:#x}, ispeed={:#x}, ospeed={:#x}",
254 new_termios.c_iflag,
255 new_termios.c_oflag,
256 new_termios.c_cflag,
257 new_termios.c_lflag,
258 new_termios.c_ispeed,
259 new_termios.c_ospeed
260 );
261
262 *STDIO_TERMIOS.lock() = new_termios;
263 }
264 Ok(0)
265 }
266
267 TIOCGWINSZ => {
268 if arg == 0 {
269 return Ok(-EINVAL as isize);
270 }
271
272 unsafe {
273 let _guard = SumGuard::new();
274 let winsize_ptr = arg as *mut crate::uapi::ioctl::WinSize;
275 if winsize_ptr.is_null() {
276 return Ok(-EINVAL as isize);
277 }
278
279 core::ptr::write_bytes(
281 winsize_ptr as *mut u8,
282 0,
283 core::mem::size_of::<crate::uapi::ioctl::WinSize>(),
284 );
285
286 let winsize = *STDIO_WINSIZE.lock();
288 core::ptr::write_volatile(winsize_ptr, winsize);
289
290 use crate::earlyprintln;
291 earlyprintln!(
292 "TIOCGWINSZ: returning {}x{} ({}x{} pixels)",
293 winsize.ws_row,
294 winsize.ws_col,
295 winsize.ws_xpixel,
296 winsize.ws_ypixel
297 );
298 }
299 Ok(0)
300 }
301
302 TIOCSWINSZ => {
303 if arg == 0 {
304 return Ok(-EINVAL as isize);
305 }
306
307 unsafe {
308 let _guard = SumGuard::new();
309 let winsize_ptr = arg as *const crate::uapi::ioctl::WinSize;
310 if winsize_ptr.is_null() {
311 return Ok(-EINVAL as isize);
312 }
313
314 let new_winsize = core::ptr::read_volatile(winsize_ptr);
316 *STDIO_WINSIZE.lock() = new_winsize;
317
318 use crate::earlyprintln;
319 earlyprintln!(
320 "TIOCSWINSZ: set to {}x{} ({}x{} pixels)",
321 new_winsize.ws_row,
322 new_winsize.ws_col,
323 new_winsize.ws_xpixel,
324 new_winsize.ws_ypixel
325 );
326 }
327 Ok(0)
328 }
329
330 _ => Ok(-ENOTTY as isize),
332 }
333}
334
335pub fn create_stdio_files() -> (Arc<dyn File>, Arc<dyn File>, Arc<dyn File>) {
339 (
340 Arc::new(StdinFile),
341 Arc::new(StdoutFile),
342 Arc::new(StderrFile),
343 )
344}