xref: /DragonOS/kernel/src/driver/tty/kthread.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
1 //! tty刷新内核线程
2 
3 use alloc::{string::ToString, sync::Arc};
4 use kdepends::thingbuf::StaticThingBuf;
5 
6 use crate::{
7     arch::CurrentIrqArch,
8     driver::tty::virtual_terminal::vc_manager,
9     exception::InterruptArch,
10     process::{
11         kthread::{KernelThreadClosure, KernelThreadMechanism},
12         ProcessControlBlock, ProcessManager,
13     },
14     sched::{schedule, SchedMode},
15 };
16 
17 /// 用于缓存键盘输入的缓冲区
18 static KEYBUF: StaticThingBuf<u8, 512> = StaticThingBuf::new();
19 
20 static mut TTY_REFRESH_THREAD: Option<Arc<ProcessControlBlock>> = None;
21 
22 pub(super) fn tty_flush_thread_init() {
23     let closure =
24         KernelThreadClosure::StaticEmptyClosure((&(tty_refresh_thread as fn() -> i32), ()));
25     let pcb = KernelThreadMechanism::create_and_run(closure, "tty_refresh".to_string())
26         .ok_or("")
27         .expect("create tty_refresh thread failed");
28     unsafe {
29         TTY_REFRESH_THREAD = Some(pcb);
30     }
31 }
32 
33 fn tty_refresh_thread() -> i32 {
34     const TO_DEQUEUE_MAX: usize = 256;
35     loop {
36         if KEYBUF.is_empty() {
37             // 如果缓冲区为空,就休眠
38             let _guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
39             ProcessManager::mark_sleep(true).expect("TTY_REFRESH_THREAD can not mark sleep");
40             schedule(SchedMode::SM_NONE);
41         }
42 
43         let to_dequeue = core::cmp::min(KEYBUF.len(), TO_DEQUEUE_MAX);
44         if to_dequeue == 0 {
45             continue;
46         }
47         let mut data = [0u8; TO_DEQUEUE_MAX];
48         for item in data.iter_mut().take(to_dequeue) {
49             *item = KEYBUF.pop().unwrap();
50         }
51 
52         if let Some(cur_vc) = vc_manager().current_vc() {
53             let _ = cur_vc
54                 .port()
55                 .receive_buf(&data[0..to_dequeue], &[], to_dequeue);
56         } else {
57             // 这里由于stdio未初始化,所以无法找到port
58             // TODO: 考虑改用双端队列,能够将丢失的输入插回
59         }
60     }
61 }
62 
63 /// 发送数据到tty刷新线程
64 pub fn send_to_tty_refresh_thread(data: &[u8]) {
65     if unsafe { TTY_REFRESH_THREAD.is_none() } {
66         return;
67     }
68 
69     for item in data {
70         KEYBUF.push(*item).ok();
71     }
72     let _ = ProcessManager::wakeup(unsafe { TTY_REFRESH_THREAD.as_ref().unwrap() });
73 }
74