mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 18:26:48 +00:00
完成与Linux兼容的Ntty (#517)
* 已经完成的功能: - 写:printf能够正常在tty输出 - 读:与键盘驱动接上 - 信号: 能够正常通过ctrl向前台进程发送信号 * 支持目前的shell,改动printk使其与新版tty兼容。 * 删除原有tty文件夹,并更改新tty文件名 * 添加clear清屏程序 * 实现tty部分ioctl,更改部分问题
This commit is contained in:
parent
9993c0fc61
commit
52da9a5937
@ -2,6 +2,7 @@ use core::{intrinsics::unlikely, marker::PhantomData};
|
||||
|
||||
use crate::traits::BitOps;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BitMapCore<T: BitOps> {
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::{bitmap_core::BitMapCore, traits::BitMapOps};
|
||||
/// 静态位图
|
||||
///
|
||||
/// 该位图的大小在编译时确定,不可变
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StaticBitmap<const N: usize>
|
||||
where
|
||||
[(); (N + usize::BITS as usize - 1) / (usize::BITS as usize)]:,
|
||||
|
@ -285,6 +285,8 @@ pub enum SystemError {
|
||||
EVMPRTLDFailed = 517,
|
||||
EVMLAUNCHFailed = 518,
|
||||
KVM_HVA_ERR_BAD = 519,
|
||||
/// 没有对应的ioctlcmd
|
||||
ENOIOCTLCMD = 520,
|
||||
}
|
||||
|
||||
impl SystemError {
|
||||
|
@ -7,7 +7,7 @@ use hashbrown::HashSet;
|
||||
use x86::time::rdtsc;
|
||||
use x86_64::registers::model_specific::EferFlags;
|
||||
|
||||
use crate::driver::tty::serial::serial8250::send_to_default_serial8250_port;
|
||||
use crate::driver::serial::serial8250::send_to_default_serial8250_port;
|
||||
use crate::include::bindings::bindings::{
|
||||
multiboot2_get_load_base, multiboot2_get_memory, multiboot2_iter, multiboot_mmap_entry_t,
|
||||
multiboot_tag_load_base_addr_t,
|
||||
|
@ -1,4 +1,4 @@
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct Major(u32);
|
||||
|
||||
impl Major {
|
||||
@ -10,6 +10,8 @@ impl Major {
|
||||
pub const UNNAMED_MAJOR: Self = Self::new(0);
|
||||
|
||||
pub const IDE0_MAJOR: Self = Self::new(3);
|
||||
pub const TTY_MAJOR: Self = Self::new(4);
|
||||
pub const TTYAUX_MAJOR: Self = Self::new(5);
|
||||
pub const HD_MAJOR: Self = Self::IDE0_MAJOR;
|
||||
|
||||
pub const INPUT_MAJOR: Self = Self::new(13);
|
||||
|
@ -3,10 +3,7 @@ use core::{ffi::c_void, sync::atomic::AtomicI32};
|
||||
use alloc::sync::{Arc, Weak};
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
base::device::device_number::{DeviceNumber, Major},
|
||||
tty::tty_device::TTY_DEVICES,
|
||||
},
|
||||
driver::base::device::device_number::{DeviceNumber, Major},
|
||||
filesystem::{
|
||||
devfs::{devfs_register, DevFS, DeviceINode},
|
||||
vfs::{
|
||||
@ -23,14 +20,7 @@ use system_error::SystemError;
|
||||
pub struct LockedPS2KeyBoardInode(RwLock<PS2KeyBoardInode>, AtomicI32); // self.1 用来记录有多少个文件打开了这个inode
|
||||
|
||||
lazy_static! {
|
||||
static ref PS2_KEYBOARD_FSM: SpinLock<TypeOneFSM> = {
|
||||
let tty0 = TTY_DEVICES
|
||||
.read()
|
||||
.get("tty0")
|
||||
.expect("Initializing PS2_KEYBOARD_FSM: Cannot found TTY0!")
|
||||
.clone();
|
||||
SpinLock::new(TypeOneFSM::new(tty0))
|
||||
};
|
||||
static ref PS2_KEYBOARD_FSM: SpinLock<TypeOneFSM> = SpinLock::new(TypeOneFSM::new());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -7,6 +7,7 @@ pub mod keyboard;
|
||||
pub mod net;
|
||||
pub mod open_firmware;
|
||||
pub mod pci;
|
||||
pub mod serial;
|
||||
pub mod timers;
|
||||
pub mod tty;
|
||||
pub mod video;
|
||||
|
@ -7,11 +7,9 @@ use crate::{driver::base::device::device_number::DeviceNumber, mm::VirtAddr};
|
||||
|
||||
use self::serial8250::serial8250_manager;
|
||||
|
||||
use super::tty_driver::TtyDriver;
|
||||
|
||||
pub mod serial8250;
|
||||
|
||||
pub trait UartDriver: Debug + Send + Sync + TtyDriver {
|
||||
pub trait UartDriver: Debug + Send + Sync {
|
||||
fn device_number(&self) -> DeviceNumber;
|
||||
|
||||
/// 获取最大的设备数量
|
||||
@ -56,6 +54,7 @@ impl UartManager {
|
||||
/// todo: 把uart设备注册到tty层
|
||||
///
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/tty/serial/serial_core.c?fi=uart_register_driver#2720
|
||||
#[allow(dead_code)]
|
||||
pub fn register_driver(&self, _driver: &Arc<dyn UartDriver>) -> Result<(), SystemError> {
|
||||
return Ok(());
|
||||
}
|
@ -11,23 +11,17 @@ use alloc::{
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
base::{
|
||||
class::Class,
|
||||
device::{
|
||||
bus::Bus, device_manager, device_number::DeviceNumber, driver::Driver, Device,
|
||||
DeviceKObjType, DeviceState, DeviceType, IdTable,
|
||||
},
|
||||
kobject::{KObjType, KObject, KObjectState, LockedKObjectState},
|
||||
kset::KSet,
|
||||
platform::{
|
||||
platform_device::{platform_device_manager, PlatformDevice},
|
||||
platform_driver::{platform_driver_manager, PlatformDriver},
|
||||
},
|
||||
driver::base::{
|
||||
class::Class,
|
||||
device::{
|
||||
bus::Bus, device_manager, device_number::DeviceNumber, driver::Driver, Device,
|
||||
DeviceKObjType, DeviceState, DeviceType, IdTable,
|
||||
},
|
||||
tty::{
|
||||
tty_device::TtyDevice,
|
||||
tty_driver::{TtyDriver, TtyDriverMetadata, TtyDriverOperations},
|
||||
kobject::{KObjType, KObject, KObjectState, LockedKObjectState},
|
||||
kset::KSet,
|
||||
platform::{
|
||||
platform_device::{platform_device_manager, PlatformDevice},
|
||||
platform_driver::{platform_driver_manager, PlatformDriver},
|
||||
},
|
||||
},
|
||||
filesystem::kernfs::KernFSInode,
|
||||
@ -377,6 +371,7 @@ impl InnerSerial8250ISADriver {
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cast_to([sync] Driver, PlatformDriver)]
|
||||
#[allow(dead_code)]
|
||||
struct Serial8250ISADriver {
|
||||
inner: RwLock<InnerSerial8250ISADriver>,
|
||||
name: &'static str,
|
||||
@ -402,32 +397,6 @@ impl Serial8250ISADriver {
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyDriver for Serial8250ISADriver {
|
||||
fn driver_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn dev_name(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &TtyDriverMetadata {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn other(&self) -> Option<&Arc<dyn TtyDriver>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ttys(&self) -> &[Arc<TtyDevice>] {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn tty_ops(&self) -> Option<&'static dyn TtyDriverOperations> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl UartDriver for Serial8250ISADriver {
|
||||
fn device_number(&self) -> DeviceNumber {
|
||||
todo!()
|
@ -9,7 +9,7 @@ use alloc::sync::{Arc, Weak};
|
||||
|
||||
use crate::{
|
||||
arch::{io::PortIOArch, CurrentPortIOArch},
|
||||
driver::tty::serial::{AtomicBaudRate, BaudRate, DivisorFraction, UartPort},
|
||||
driver::serial::{AtomicBaudRate, BaudRate, DivisorFraction, UartPort},
|
||||
libs::rwlock::RwLock,
|
||||
};
|
||||
use system_error::SystemError;
|
125
kernel/src/driver/tty/console.rs
Normal file
125
kernel/src/driver/tty/console.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use system_error::SystemError;
|
||||
|
||||
use super::virtual_terminal::virtual_console::{
|
||||
CursorOperation, ScrollDir, VirtualConsoleData, VirtualConsoleIntensity,
|
||||
};
|
||||
|
||||
/// 终端切换相关的回调
|
||||
pub trait ConsoleSwitch: Sync + Send {
|
||||
/// 初始化,会对vc_data进行一系列初始化操作
|
||||
fn con_init(&self, vc_data: &mut VirtualConsoleData, init: bool) -> Result<(), SystemError>;
|
||||
|
||||
/// 进行释放等系列操作,目前未使用
|
||||
fn con_deinit(&self) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 清空console的一片区域
|
||||
/// 该函数的所有参数对应的都是以字符为单位
|
||||
/// ### 参数:
|
||||
/// - vc_data: 对应的ConsoleData
|
||||
/// - sy: 对应区域左上角的y轴
|
||||
/// - sx: 对应区域左上角的x轴
|
||||
/// - height: 区域高度
|
||||
/// - width: 区域宽度
|
||||
fn con_clear(
|
||||
&self,
|
||||
vc_data: &mut VirtualConsoleData,
|
||||
sy: usize,
|
||||
sx: usize,
|
||||
height: usize,
|
||||
width: usize,
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 向console输出一个字符
|
||||
/// ### 参数:
|
||||
/// - vc_data: 对应的ConsoleData
|
||||
/// - ch: 数据
|
||||
/// - ypos: 起始y坐标
|
||||
/// - xpos: 起始x坐标
|
||||
fn con_putc(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
ch: u16,
|
||||
ypos: u32,
|
||||
xpos: u32,
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 向console输出一串字符
|
||||
/// ### 参数:
|
||||
/// - vc_data: 对应的ConsoleData
|
||||
/// - buf: 数据
|
||||
/// - count: 输出字符数量
|
||||
/// - ypos: 起始y坐标
|
||||
/// - xpos: 起始x坐标
|
||||
fn con_putcs(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
buf: &[u16],
|
||||
count: usize,
|
||||
ypos: u32,
|
||||
xpos: u32,
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 根据pos计算出对应xy
|
||||
///
|
||||
/// ### 返回值: (下一行的起始偏移,x,y)
|
||||
fn con_getxy(
|
||||
&self,
|
||||
_vc_data: &VirtualConsoleData,
|
||||
_pos: usize,
|
||||
) -> Result<(usize, usize, usize), SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
|
||||
/// ## 对光标进行操作
|
||||
/// ### 参数:
|
||||
/// - vc_data: 对应的ConsoleData
|
||||
/// - op: 对光标的操作
|
||||
fn con_cursor(&self, vc_data: &VirtualConsoleData, op: CursorOperation);
|
||||
|
||||
/// ## 根据参数构建出对应的属性
|
||||
/// ### 参数:
|
||||
/// - vc_data: 对应的ConsoleData
|
||||
/// - color: 颜色
|
||||
/// - intensity: 字符强度
|
||||
/// - blink: 是否闪烁
|
||||
/// - underline: 下划线
|
||||
/// - reverse: 颜色反转
|
||||
/// - italic: 斜体
|
||||
fn con_build_attr(
|
||||
&self,
|
||||
_vc_data: &VirtualConsoleData,
|
||||
_color: u8,
|
||||
_intensity: VirtualConsoleIntensity,
|
||||
_blink: bool,
|
||||
_underline: bool,
|
||||
_reverse: bool,
|
||||
_italic: bool,
|
||||
) -> Result<u8, SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
|
||||
/// ## 设置调色板
|
||||
/// ### 参数:
|
||||
/// - vc_data: 对应的ConsoleData
|
||||
/// - color_table: 颜色表
|
||||
fn con_set_palette(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
color_table: &[u8],
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 滚动
|
||||
/// ### 参数
|
||||
/// - top:滚动范围顶部
|
||||
/// - bottom: 滚动范围底部
|
||||
/// - dir: 滚动方向
|
||||
/// - nr: 滚动行数
|
||||
fn con_scroll(
|
||||
&self,
|
||||
vc_data: &mut VirtualConsoleData,
|
||||
top: usize,
|
||||
bottom: usize,
|
||||
dir: ScrollDir,
|
||||
nr: usize,
|
||||
) -> bool;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
use super::serial::serial_early_init;
|
||||
use system_error::SystemError;
|
||||
|
||||
pub fn tty_early_init() -> Result<(), SystemError> {
|
||||
serial_early_init()?;
|
||||
return Ok(());
|
||||
}
|
@ -1,418 +1,37 @@
|
||||
use core::intrinsics::unlikely;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
use kdepends::thingbuf::mpsc::{
|
||||
self,
|
||||
errors::{TryRecvError, TrySendError},
|
||||
};
|
||||
|
||||
use crate::libs::rwlock::RwLock;
|
||||
|
||||
pub mod init;
|
||||
pub mod serial;
|
||||
pub mod console;
|
||||
pub mod termios;
|
||||
pub mod tty_core;
|
||||
pub mod tty_device;
|
||||
pub mod tty_driver;
|
||||
pub mod vt;
|
||||
pub mod tty_job_control;
|
||||
pub mod tty_ldisc;
|
||||
pub mod tty_port;
|
||||
pub mod virtual_terminal;
|
||||
|
||||
bitflags! {
|
||||
pub struct TtyCoreState: u32{
|
||||
/// 在读取stdin缓冲区时,由于队列为空,有读者被阻塞
|
||||
const BLOCK_AT_STDIN_READ = (1 << 0);
|
||||
/// 开启输入回显。
|
||||
const ECHO_ON = (1 << 1);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TtyFileFlag:u32{
|
||||
/// 当前文件是stdin文件
|
||||
const STDIN = (1 << 0);
|
||||
/// 当前文件是stdout文件
|
||||
const STDOUT = (1 << 1);
|
||||
/// 当前文件是stderr文件
|
||||
const STDERR = (1 << 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief tty文件的私有信息
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TtyFilePrivateData {
|
||||
flags: TtyFileFlag,
|
||||
}
|
||||
|
||||
/// @brief tty设备的核心功能结构体。在此结构体的基础上,衍生出TTY/PTY/PTS等
|
||||
///
|
||||
/// 每个TTY Core有5个端口:
|
||||
/// - stdin:连接到一个活动进程的stdin文件描述符
|
||||
/// - stdout:连接到多个进程的stdout文件描述符
|
||||
/// - stderr:连接到多个进程的stdout文件描述符
|
||||
/// - 输入端口:向tty设备输入数据的接口。输入到该接口的数据,将被导向stdin接口。
|
||||
/// 如果开启了回显,那么,数据也将同时被导向输出端
|
||||
/// - 输出端口:tty设备对外输出数据的端口。从stdout、stderr输入的数据,将会被导向此端口。
|
||||
/// 此端口可以连接到屏幕、文件、或者是另一个tty core的输入端口。如果开启了
|
||||
/// 输入数据回显,那么,输入端口的数据,将会被同时导向此端口,以及stdin端口
|
||||
#[derive(Debug)]
|
||||
struct TtyCore {
|
||||
/// stdin的mpsc队列输入输出端
|
||||
stdin_rx: mpsc::Receiver<u8>,
|
||||
stdin_tx: mpsc::Sender<u8>,
|
||||
/// 输出的mpsc队列输入输出端
|
||||
output_rx: mpsc::Receiver<u8>,
|
||||
output_tx: mpsc::Sender<u8>,
|
||||
// 前台进程,以后改成前台进程组
|
||||
// front_job: Option<Pid>,
|
||||
/// tty核心的状态
|
||||
state: RwLock<TtyCoreState>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
// 下列结构体暂时放在这
|
||||
/// 键盘/显示器"(Keyboard/Display)模式
|
||||
#[allow(dead_code)]
|
||||
pub enum TtyError {
|
||||
/// 缓冲区满,返回成功传送的字节数
|
||||
BufferFull(usize),
|
||||
/// 缓冲区空,返回成功传送的字节数
|
||||
BufferEmpty(usize),
|
||||
/// 设备已经被关闭
|
||||
Closed,
|
||||
/// End of file(已经读取的字符数,包含eof)
|
||||
EOF(usize),
|
||||
/// 接收到信号终止
|
||||
Stopped(usize),
|
||||
Unknown(String),
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum KDMode {
|
||||
KdText,
|
||||
KdGraphics,
|
||||
KdText0,
|
||||
KdText1,
|
||||
Undefined,
|
||||
}
|
||||
|
||||
impl TtyCore {
|
||||
// 各个缓冲区的大小
|
||||
pub const STDIN_BUF_SIZE: usize = 4096;
|
||||
pub const OUTPUT_BUF_SIZE: usize = 4096;
|
||||
|
||||
/// @brief 创建一个TTY核心组件
|
||||
pub fn new() -> TtyCore {
|
||||
let (stdin_tx, stdin_rx) = mpsc::channel::<u8>(Self::STDIN_BUF_SIZE);
|
||||
let (output_tx, output_rx) = mpsc::channel::<u8>(Self::OUTPUT_BUF_SIZE);
|
||||
let state: RwLock<TtyCoreState> = RwLock::new(TtyCoreState { bits: 0 });
|
||||
|
||||
return TtyCore {
|
||||
stdin_rx,
|
||||
stdin_tx,
|
||||
output_rx,
|
||||
output_tx,
|
||||
state,
|
||||
};
|
||||
}
|
||||
|
||||
/// @brief 向tty的输入端口输入数据
|
||||
///
|
||||
/// @param buf 输入数据
|
||||
///
|
||||
/// @param block 是否允许阻塞
|
||||
///
|
||||
/// @return Ok(成功传送的字节数)
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
pub fn input(&self, buf: &[u8], block: bool) -> Result<usize, TtyError> {
|
||||
let val = self.write_stdin(buf, block)?;
|
||||
// 如果开启了输入回显,那么就写一份到输出缓冲区
|
||||
if self.echo_enabled() {
|
||||
self.write_output(&buf[0..val], true)?;
|
||||
}
|
||||
return Ok(val);
|
||||
}
|
||||
|
||||
/// @brief 从tty的输出端口读出数据
|
||||
///
|
||||
/// @param buf 输出缓冲区
|
||||
///
|
||||
/// @return Ok(成功传送的字节数)
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
#[inline]
|
||||
pub fn output(&self, buf: &mut [u8], block: bool) -> Result<usize, TtyError> {
|
||||
return self.read_output(buf, block);
|
||||
}
|
||||
|
||||
/// @brief tty的stdout接口
|
||||
///
|
||||
/// @param buf 输入缓冲区
|
||||
///
|
||||
/// @return Ok(成功传送的字节数)
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
#[inline]
|
||||
pub fn stdout(&self, buf: &[u8], block: bool) -> Result<usize, TtyError> {
|
||||
return self.write_output(buf, block);
|
||||
}
|
||||
|
||||
/// @brief tty的stderr接口
|
||||
///
|
||||
/// @param buf 输入缓冲区
|
||||
///
|
||||
/// @return Ok(成功传送的字节数)
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
#[inline]
|
||||
pub fn stderr(&self, buf: &[u8], block: bool) -> Result<usize, TtyError> {
|
||||
return self.write_output(buf, block);
|
||||
}
|
||||
|
||||
/// @brief 读取TTY的stdin缓冲区
|
||||
///
|
||||
/// @param buf 读取到的位置
|
||||
/// @param block 是否阻塞读
|
||||
///
|
||||
/// @return Ok(成功读取的字节数)
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
pub fn read_stdin(&self, buf: &mut [u8], block: bool) -> Result<usize, TtyError> {
|
||||
// TODO: 增加对EOF的处理
|
||||
let mut cnt = 0;
|
||||
while cnt < buf.len() {
|
||||
let val: Result<mpsc::RecvRef<u8>, TryRecvError> = self.stdin_rx.try_recv_ref();
|
||||
if let Err(err) = val {
|
||||
match err {
|
||||
TryRecvError::Closed => return Err(TtyError::Closed),
|
||||
TryRecvError::Empty => {
|
||||
if block {
|
||||
continue;
|
||||
} else {
|
||||
return Ok(cnt);
|
||||
}
|
||||
}
|
||||
_ => return Err(TtyError::Unknown(format!("{err:?}"))),
|
||||
}
|
||||
} else {
|
||||
let x = *val.unwrap();
|
||||
buf[cnt] = x;
|
||||
cnt += 1;
|
||||
|
||||
if unlikely(self.stdin_should_return(x)) {
|
||||
return Ok(cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(cnt);
|
||||
}
|
||||
|
||||
fn stdin_should_return(&self, c: u8) -> bool {
|
||||
// 如果是换行符或者是ctrl+d,那么就应该返回
|
||||
return c == b'\n' || c == 4;
|
||||
}
|
||||
|
||||
/// @brief 向stdin缓冲区内写入数据
|
||||
///
|
||||
/// @param buf 输入缓冲区
|
||||
///
|
||||
/// @param block 当缓冲区满的时候,是否阻塞
|
||||
///
|
||||
/// @return Ok(成功传送的字节数)
|
||||
/// @return Err(BufferFull(成功传送的字节数)) 缓冲区满,成功传送的字节数
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
fn write_stdin(&self, buf: &[u8], block: bool) -> Result<usize, TtyError> {
|
||||
let mut cnt = 0;
|
||||
while cnt < buf.len() {
|
||||
let r: Result<mpsc::SendRef<u8>, TrySendError> = self.stdin_tx.try_send_ref();
|
||||
if let Err(e) = r {
|
||||
match e {
|
||||
TrySendError::Closed(_) => return Err(TtyError::Closed),
|
||||
TrySendError::Full(_) => {
|
||||
if block {
|
||||
continue;
|
||||
} else {
|
||||
return Err(TtyError::BufferFull(cnt));
|
||||
}
|
||||
}
|
||||
_ => return Err(TtyError::Unknown(format!("{e:?}"))),
|
||||
}
|
||||
} else {
|
||||
*r.unwrap() = buf[cnt];
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(cnt);
|
||||
}
|
||||
|
||||
/// @brief 读取TTY的output缓冲区
|
||||
///
|
||||
/// @param buf 读取到的位置
|
||||
/// @param block 是否阻塞读
|
||||
///
|
||||
/// @return Ok(成功读取的字节数)
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
fn read_output(&self, buf: &mut [u8], block: bool) -> Result<usize, TtyError> {
|
||||
let mut cnt = 0;
|
||||
while cnt < buf.len() {
|
||||
let val: Result<mpsc::RecvRef<u8>, TryRecvError> = self.output_rx.try_recv_ref();
|
||||
if let Err(err) = val {
|
||||
match err {
|
||||
TryRecvError::Closed => return Err(TtyError::Closed),
|
||||
TryRecvError::Empty => {
|
||||
if block {
|
||||
continue;
|
||||
} else {
|
||||
return Ok(cnt);
|
||||
}
|
||||
}
|
||||
_ => return Err(TtyError::Unknown(format!("{err:?}"))),
|
||||
}
|
||||
} else {
|
||||
buf[cnt] = *val.unwrap();
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
return Ok(cnt);
|
||||
}
|
||||
|
||||
/// @brief 向output缓冲区内写入数据
|
||||
///
|
||||
/// @param buf 输入缓冲区
|
||||
///
|
||||
/// @param block 当缓冲区满的时候,是否阻塞
|
||||
///
|
||||
/// @return Ok(成功传送的字节数)
|
||||
/// @return Err(BufferFull(成功传送的字节数)) 缓冲区满,成功传送的字节数
|
||||
/// @return Err(TtyError) 内部错误信息
|
||||
fn write_output(&self, buf: &[u8], block: bool) -> Result<usize, TtyError> {
|
||||
let mut cnt = 0;
|
||||
while cnt < buf.len() {
|
||||
let r: Result<mpsc::SendRef<u8>, TrySendError> = self.output_tx.try_send_ref();
|
||||
if let Err(e) = r {
|
||||
match e {
|
||||
TrySendError::Closed(_) => return Err(TtyError::Closed),
|
||||
TrySendError::Full(_) => {
|
||||
if block {
|
||||
continue;
|
||||
} else {
|
||||
return Err(TtyError::BufferFull(cnt));
|
||||
}
|
||||
}
|
||||
_ => return Err(TtyError::Unknown(format!("{e:?}"))),
|
||||
}
|
||||
} else {
|
||||
// TODO: 在这里考虑增加对信号发送的处理
|
||||
// if buf[cnt] == 3 {
|
||||
// let pid = ProcessManager::current_pcb().pid();
|
||||
// Signal::SIGKILL.send_signal_info(
|
||||
// Some(&mut SigInfo::new(
|
||||
// Signal::SIGKILL,
|
||||
// 0,
|
||||
// SigCode::SI_USER,
|
||||
// SigType::Kill(pid),
|
||||
// )),
|
||||
// pid,
|
||||
// );
|
||||
// return Err(TtyError::Stopped(cnt));
|
||||
// }
|
||||
*r.unwrap() = buf[cnt];
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
return Ok(cnt);
|
||||
}
|
||||
|
||||
/// @brief 开启tty输入回显(也就是将输入数据传送一份到输出缓冲区)
|
||||
#[inline]
|
||||
pub fn enable_echo(&self) {
|
||||
self.state.write().set(TtyCoreState::ECHO_ON, true);
|
||||
}
|
||||
|
||||
/// @brief 关闭输入回显
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn disable_echo(&self) {
|
||||
self.state.write().set(TtyCoreState::ECHO_ON, false);
|
||||
}
|
||||
|
||||
/// @brief 判断当前tty核心,是否开启了输入回显
|
||||
///
|
||||
/// @return true 开启了输入回显
|
||||
///
|
||||
/// @return false 未开启输入回显
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn echo_enabled(&self) -> bool {
|
||||
return self.state.read().contains(TtyCoreState::ECHO_ON);
|
||||
impl Default for KDMode {
|
||||
fn default() -> Self {
|
||||
Self::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
// ======= 以下代码考虑了“缓冲区满,然后睡眠,当缓冲区有空位就唤醒”的逻辑。
|
||||
// 但是由于在开发过程中的调整,并且由于数据结构发生变化,因此暂时不实现上述优化,因此先注释。
|
||||
//
|
||||
// @brief 读取TTY的stdin缓冲区
|
||||
//
|
||||
// @param buf 读取到的位置
|
||||
// @param block 是否阻塞读
|
||||
//
|
||||
// @return Ok(成功读取的字节数)
|
||||
// @return Err(TtyError) 内部错误信息
|
||||
// pub fn read_stdin(&mut self, buf: &mut [u8], block: bool) -> Result<usize, TtyError> {
|
||||
// let mut cnt = 0;
|
||||
// loop{
|
||||
// if cnt == buf.len(){
|
||||
// break;
|
||||
// }
|
||||
// let val:Option<u8> = self.stdin_queue.dequeue();
|
||||
// // 如果没读到
|
||||
// if val.is_none() {
|
||||
// // 如果阻塞读
|
||||
// if block {
|
||||
// let state_guard: RwLockUpgradableGuard<TtyCoreState> =
|
||||
// self.state.upgradeable_read();
|
||||
// // 判断是否有进程正在stdin上睡眠,如果有,则忙等读
|
||||
// // 理论上,这种情况应该不存在,因为stdin是单读者的
|
||||
// if state_guard.contains(TtyCoreState::BLOCK_AT_STDIN_READ) {
|
||||
// kwarn!("Read stdin: Process {} want to read its' stdin, but previous process {} is sleeping on the stdin.", current_pcb().pid, self.stdin_waiter.read().as_ref().unwrap().pid);
|
||||
// drop(state_guard);
|
||||
// Self::ringbuf_spin_dequeue(&mut buf[cnt], &mut self.stdin_queue);
|
||||
// cnt += 1;
|
||||
// } else {
|
||||
// // 正常情况,阻塞读,将当前进程休眠
|
||||
// let mut state_guard: RwLockWriteGuard<TtyCoreState> = state_guard.upgrade();
|
||||
// let mut stdin_waiter_guard: RwLockWriteGuard<
|
||||
// Option<&mut process_control_block>,
|
||||
// > = self.stdin_waiter.write();
|
||||
|
||||
// // 由于输入数据到stdin的时候,必须先获得state guard的读锁。而这里我们已经获取了state的写锁。
|
||||
// // 因此可以保证,此时没有新的数据会进入stdin_queue. 因此再次尝试读取stdin_queue
|
||||
// let val:Option<u8> = self.stdin_queue.dequeue();
|
||||
// // 读到数据,不用睡眠
|
||||
// if val.is_some(){
|
||||
// buf[cnt] = val.unwrap();
|
||||
// cnt += 1;
|
||||
// continue;
|
||||
// }
|
||||
// // 没读到数据,准备睡眠
|
||||
|
||||
// // 设置等待标志位
|
||||
// state_guard.set(TtyCoreState::BLOCK_AT_STDIN_READ, true);
|
||||
|
||||
// // 将当前进程标记为被其他机制管理
|
||||
// unsafe {
|
||||
// current_pcb().mark_sleep_interruptible();
|
||||
// }
|
||||
|
||||
// *stdin_waiter_guard = Some(current_pcb());
|
||||
// drop(stdin_waiter_guard);
|
||||
// drop(state_guard);
|
||||
// sched();
|
||||
// continue;
|
||||
// }
|
||||
// } else {
|
||||
// // 非阻塞读,没读到就直接返回了
|
||||
// return Ok(cnt);
|
||||
// }
|
||||
// }else{
|
||||
// buf[cnt] = val.unwrap();
|
||||
// cnt += 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return Ok(cnt);
|
||||
// }
|
||||
|
||||
// fn write_stdin(&self)
|
||||
|
||||
// /// @brief 非休眠的,自旋地读队列,直到有元素被读出来
|
||||
// fn ringbuf_spin_dequeue(dst: &mut u8, queue: &mut AllocRingBuffer<u8>) {
|
||||
// loop {
|
||||
// if let Some(val) = queue.dequeue() {
|
||||
// *dst = val;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ConsoleFont {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub count: u32,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
383
kernel/src/driver/tty/termios.rs
Normal file
383
kernel/src/driver/tty/termios.rs
Normal file
@ -0,0 +1,383 @@
|
||||
use super::tty_ldisc::LineDisciplineType;
|
||||
|
||||
/// ## 窗口大小
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct WindowSize {
|
||||
/// 行
|
||||
pub row: u16,
|
||||
/// 列
|
||||
pub col: u16,
|
||||
/// x方向像素数
|
||||
pub xpixel: u16,
|
||||
/// y方向像素数
|
||||
pub ypixel: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Termios {
|
||||
pub input_mode: InputMode,
|
||||
pub output_mode: OutputMode,
|
||||
pub control_mode: ControlMode,
|
||||
pub local_mode: LocalMode,
|
||||
pub control_characters: [u8; CONTORL_CHARACTER_NUM],
|
||||
pub line: LineDisciplineType,
|
||||
pub input_speed: u32,
|
||||
pub output_speed: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PosixTermios {
|
||||
pub c_iflag: u32,
|
||||
pub c_oflag: u32,
|
||||
pub c_cflag: u32,
|
||||
pub c_lflag: u32,
|
||||
pub c_cc: [u8; CONTORL_CHARACTER_NUM],
|
||||
pub c_line: u8,
|
||||
pub c_ispeed: u32,
|
||||
pub c_ospeed: u32,
|
||||
}
|
||||
|
||||
impl PosixTermios {
|
||||
pub fn from_kernel_termios(termios: Termios) -> Self {
|
||||
Self {
|
||||
c_iflag: termios.input_mode.bits,
|
||||
c_oflag: termios.output_mode.bits,
|
||||
c_cflag: termios.control_mode.bits,
|
||||
c_lflag: termios.local_mode.bits,
|
||||
c_cc: termios.control_characters.clone(),
|
||||
c_line: termios.line as u8,
|
||||
c_ispeed: termios.input_speed,
|
||||
c_ospeed: termios.output_speed,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn to_kernel_termios(&self) -> Termios {
|
||||
Termios {
|
||||
input_mode: InputMode::from_bits_truncate(self.c_iflag),
|
||||
output_mode: OutputMode::from_bits_truncate(self.c_oflag),
|
||||
control_mode: ControlMode::from_bits_truncate(self.c_cflag),
|
||||
local_mode: LocalMode::from_bits_truncate(self.c_lflag),
|
||||
control_characters: self.c_cc.clone(),
|
||||
line: LineDisciplineType::from_line(self.c_line),
|
||||
input_speed: self.c_ispeed,
|
||||
output_speed: self.c_ospeed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const INIT_CONTORL_CHARACTERS: [u8; CONTORL_CHARACTER_NUM] = [
|
||||
b'C' - 0x40, // VINTR
|
||||
b'\\' - 0x40, // VQUIT
|
||||
0o177, // VERASE
|
||||
b'U' - 0x40, // VKILL
|
||||
b'D' - 0x40, // VEOF
|
||||
1, // VMIN
|
||||
0, // VEOL
|
||||
0, // VTIME
|
||||
0, // VEOL2
|
||||
0, // VSWTC
|
||||
b'W' - 0x40, // VWERASE
|
||||
b'R' - 0x40, // VREPRINT
|
||||
b'Z' - 0x40, // VSUSP
|
||||
b'Q' - 0x40, // VSTART
|
||||
b'S' - 0x40, // VSTOP
|
||||
b'V' - 0x40, // VLNEXT
|
||||
b'O' - 0x40, // VDISCARD
|
||||
0,
|
||||
0,
|
||||
];
|
||||
|
||||
// pub const INIT_CONTORL_CHARACTERS: [u8; CONTORL_CHARACTER_NUM] = {
|
||||
// let mut chs: [u8; CONTORL_CHARACTER_NUM] = Default::default();
|
||||
// chs[ControlCharIndex::VINTR] = b'C' - 0x40;
|
||||
// chs[ControlCharIndex::VQUIT] = b'\\' - 0x40;
|
||||
// chs[ControlCharIndex::VERASE] = 0o177;
|
||||
// chs[ControlCharIndex::VKILL] = b'U' - 0x40;
|
||||
// chs[ControlCharIndex::VEOF] = b'D' - 0x40;
|
||||
// chs[ControlCharIndex::VSTART] = b'Q' - 0x40;
|
||||
// chs[ControlCharIndex::VSTOP] = b'S' - 0x40;
|
||||
// chs[ControlCharIndex::VSUSP] = b'Z' - 0x40;
|
||||
// chs[ControlCharIndex::VREPRINT] = b'R' - 0x40;
|
||||
// chs[ControlCharIndex::VDISCARD] = b'O' - 0x40;
|
||||
// chs[ControlCharIndex::VWERASE] = b'W' - 0x40;
|
||||
// chs[ControlCharIndex::VLNEXT] = b'V' - 0x40;
|
||||
// // chs[ContorlCharIndex::VDSUSP] = b'Y' - 0x40;
|
||||
// chs[ControlCharIndex::VMIN] = 1;
|
||||
// return chs;
|
||||
// };
|
||||
|
||||
lazy_static! {
|
||||
pub static ref TTY_STD_TERMIOS: Termios = {
|
||||
Termios {
|
||||
input_mode: InputMode::ICRNL | InputMode::IXON,
|
||||
output_mode: OutputMode::OPOST | OutputMode::ONLCR,
|
||||
control_mode: ControlMode::B38400
|
||||
| ControlMode::CREAD
|
||||
| ControlMode::HUPCL
|
||||
| ControlMode::CS8,
|
||||
local_mode: LocalMode::ISIG
|
||||
| LocalMode::ICANON
|
||||
| LocalMode::ECHO
|
||||
| LocalMode::ECHOE
|
||||
| LocalMode::ECHOK
|
||||
| LocalMode::ECHOCTL
|
||||
| LocalMode::ECHOKE
|
||||
| LocalMode::IEXTEN,
|
||||
control_characters: INIT_CONTORL_CHARACTERS.clone(),
|
||||
line: LineDisciplineType::NTty,
|
||||
input_speed: 38400,
|
||||
output_speed: 38400,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const CONTORL_CHARACTER_NUM: usize = 19;
|
||||
|
||||
bitflags! {
|
||||
/// termios输入特性
|
||||
pub struct InputMode: u32 {
|
||||
/// 如果设置了该标志,表示启用软件流控制。
|
||||
const IXON = 0x0200;
|
||||
/// 如果设置了该标志,表示启用输入流控制。
|
||||
const IXOFF = 0x0400;
|
||||
/// Map Uppercase to Lowercase on Input 将大写转换为小写
|
||||
/// 表示不区分大小写
|
||||
const IUCLC = 0x1000;
|
||||
/// 如果设置了该标志,表示当输入队列满时,产生一个响铃信号。
|
||||
const IMAXBEL = 0x2000;
|
||||
/// 如果设置了该标志,表示输入数据被视为 UTF-8 编码。
|
||||
const IUTF8 = 0x4000;
|
||||
|
||||
/// 忽略中断信号
|
||||
const IGNBRK = 0x001;
|
||||
/// 检测到中断信号时生成中断(产生中断信号)
|
||||
const BRKINT = 0x002;
|
||||
/// 忽略具有奇偶校验错误的字符
|
||||
const IGNPAR = 0x004;
|
||||
/// 在检测到奇偶校验错误或帧错误时,将字符以 \377 标记
|
||||
const PARMRK = 0x008;
|
||||
/// 启用输入奇偶校验检查
|
||||
const INPCK = 0x010;
|
||||
/// 从输入字符中剥离第 8 位,即只保留低 7 位
|
||||
const ISTRIP = 0x020;
|
||||
/// 表示将输入的换行符 (\n) 映射为回车符 (\r)
|
||||
const INLCR = 0x040;
|
||||
/// 表示忽略回车符 (\r)
|
||||
const IGNCR = 0x080;
|
||||
/// 表示将输入的回车符 (\r) 映射为换行符 (\n)
|
||||
const ICRNL = 0x100;
|
||||
/// 表示在输入被停止(Ctrl-S)后,任何字符的输入都将重新启动输入
|
||||
const IXANY = 0x800;
|
||||
}
|
||||
|
||||
/// termios输出特性
|
||||
pub struct OutputMode: u32 {
|
||||
/// 在输出时将换行符替换\r\n
|
||||
const ONLCR = 0x00002;
|
||||
/// Map Lowercase to Uppercase on Output 输出字符时将小写字母映射为大写字母
|
||||
const OLCUC = 0x00004;
|
||||
|
||||
/// 与NL协同 配置换行符的处理方式
|
||||
const NLDLY = 0x00300;
|
||||
const NL0 = 0x00000; // 不延迟换行
|
||||
const NL1 = 0x00100; // 延迟换行(输出回车后等待一段时间再输出换行)
|
||||
const NL2 = 0x00200; // NL2 和 NL3保留,暂未使用
|
||||
const NL3 = 0x00300;
|
||||
|
||||
/// 配置水平制表符的处理方式
|
||||
const TABDLY = 0x00c00;
|
||||
const TAB0 = 0x00000; // 不延迟水平制表符
|
||||
const TAB1 = 0x00400; // 在输出水平制表符时,延迟到下一个设置的水平制表符位置
|
||||
const TAB2 = 0x00800; // 在输出水平制表符时,延迟到下一个设置的 8 的倍数的位置
|
||||
const TAB3 = 0x00c00; // TAB3 和 XTABS(与 TAB3 等效)保留,暂未使用
|
||||
const XTABS = 0x00c00;
|
||||
|
||||
/// 配置回车符的处理方式
|
||||
const CRDLY = 0x03000;
|
||||
const CR0 = 0x00000; // 不延迟回车
|
||||
const CR1 = 0x01000; // 延迟回车(输出回车后等待一段时间再输出换行)
|
||||
const CR2 = 0x02000; // CR2 和 CR3保留,暂未使用
|
||||
const CR3 = 0x03000;
|
||||
|
||||
/// 配置换页符(form feed)的处理方式
|
||||
const FFDLY = 0x04000;
|
||||
const FF0 = 0x00000; // 不延迟换页
|
||||
const FF1 = 0x04000; // 延迟换页
|
||||
|
||||
/// 配置退格符(backspace)的处理方式
|
||||
const BSDLY = 0x08000;
|
||||
const BS0 = 0x00000; // 不延迟退格
|
||||
const BS1 = 0x08000; // 延迟退格
|
||||
|
||||
/// 配置垂直制表符(vertical tab)的处理方式
|
||||
const VTDLY = 0x10000;
|
||||
const VT0 = 0x00000; // 不延迟垂直制表符
|
||||
const VT1 = 0x10000; // 延迟垂直制表符
|
||||
|
||||
/// 表示执行输出处理,即启用输出处理函数
|
||||
const OPOST = 0x01;
|
||||
/// 表示将输出的回车符 (\r) 映射为换行符 (\n)
|
||||
const OCRNL = 0x08;
|
||||
/// 表示在输出时,如果光标在第 0 列,则不输出回车符 (\r)
|
||||
const ONOCR = 0x10;
|
||||
/// 表示将回车符 (\r) 映射为换行符 (\n)
|
||||
const ONLRET = 0x20;
|
||||
/// 表示使用填充字符进行延迟。这个填充字符的默认值是空格。
|
||||
const OFILL = 0x40;
|
||||
/// 表示使用删除字符 (DEL, \177) 作为填充字符
|
||||
const OFDEL = 0x80;
|
||||
}
|
||||
|
||||
/// 配置终端设备的基本特性和控制参数
|
||||
pub struct ControlMode: u32 {
|
||||
/// Baud Rate Mask 指定波特率的掩码
|
||||
const CBAUD = 0x000000ff;
|
||||
/// Extra Baud Bits 指定更高的波特率位
|
||||
const CBAUDEX = 0x00000000;
|
||||
/// Custom Baud Rate 指定自定义波特率 如果设置了 BOTHER,则通过以下位来设置自定义的波特率值
|
||||
const BOTHER = 0x0000001f;
|
||||
|
||||
const B0 = 0x00000000;
|
||||
const B50 = 0x00000001;
|
||||
const B75 = 0x00000002;
|
||||
const B110 = 0x00000003;
|
||||
const B134 = 0x00000004;
|
||||
const B150 = 0x00000005;
|
||||
const B200 = 0x00000006;
|
||||
const B300 = 0x00000007;
|
||||
const B600 = 0x00000008;
|
||||
const B1200 = 0x00000009;
|
||||
const B1800 = 0x0000000a;
|
||||
const B2400 = 0x0000000b;
|
||||
const B4800 = 0x0000000c;
|
||||
const B9600 = 0x0000000d;
|
||||
const B19200 = 0x0000000e;
|
||||
const B38400 = 0x0000000f;
|
||||
|
||||
const B57600 = 0x00000010;
|
||||
const B115200 = 0x00000011;
|
||||
const B230400 = 0x00000012;
|
||||
const B460800 = 0x00000013;
|
||||
const B500000 = 0x00000014;
|
||||
const B576000 = 0x00000015;
|
||||
const B921600 = 0x00000016;
|
||||
const B1000000 = 0x00000017;
|
||||
const B1152000 = 0x00000018;
|
||||
const B1500000 = 0x00000019;
|
||||
const B2000000 = 0x0000001a;
|
||||
const B2500000 = 0x0000001b;
|
||||
const B3000000 = 0x0000001c;
|
||||
const B3500000 = 0x0000001d;
|
||||
const B4000000 = 0x0000001e;
|
||||
|
||||
/// 指定字符大小的掩码 以下位为特定字符大小
|
||||
const CSIZE = 0x00000300;
|
||||
const CS5 = 0x00000000;
|
||||
const CS6 = 0x00000100;
|
||||
const CS7 = 0x00000200;
|
||||
const CS8 = 0x00000300;
|
||||
|
||||
/// Stop Bit Select 表示使用两个停止位;否则,表示使用一个停止位
|
||||
const CSTOPB = 0x00000400;
|
||||
/// 表示启用接收器。如果未设置,则禁用接收器。
|
||||
const CREAD = 0x00000800;
|
||||
/// 表示启用奇偶校验。如果未设置,则禁用奇偶校验。
|
||||
const PARENB = 0x00001000;
|
||||
/// 表示启用奇校验。如果未设置,则表示启用偶校验。
|
||||
const PARODD = 0x00002000;
|
||||
/// 表示在终端设备被关闭时挂断线路(执行挂断操作)
|
||||
const HUPCL = 0x00004000;
|
||||
/// 表示忽略调制解调器的状态(DCD、DSR、CTS 等)
|
||||
const CLOCAL = 0x00008000;
|
||||
/// 指定输入波特率的掩码
|
||||
const CIBAUD = 0x00ff0000;
|
||||
|
||||
const ADDRB = 0x20000000;
|
||||
}
|
||||
|
||||
/// 配置终端设备的本地模式(local mode)或控制输入处理的行为
|
||||
pub struct LocalMode: u32 {
|
||||
/// 启用中断字符(Ctrl-C、Ctrl-Z)
|
||||
const ISIG = 0x00000080;
|
||||
/// 表示启用规范模式,即启用行缓冲和回显。在规范模式下,输入被缓冲,并且只有在输入回车符时才会传递给应用程序。
|
||||
const ICANON = 0x00000100;
|
||||
/// 表示启用大写模式,即输入输出都将被转换为大写。
|
||||
const XCASE = 0x00004000;
|
||||
/// 表示启用回显(显示用户输入的字符)
|
||||
const ECHO = 0x00000008;
|
||||
/// 表示在回显时将擦除的字符用 backspace 和空格字符显示。
|
||||
const ECHOE = 0x00000002;
|
||||
/// 表示在回显时将换行符后的字符用空格字符显示。
|
||||
const ECHOK = 0x00000004;
|
||||
/// 表示在回显时将换行符显示为换行和回车符。
|
||||
const ECHONL = 0x00000010;
|
||||
/// 表示在收到中断(Ctrl-C)和退出(Ctrl-\)字符后,不清空输入和输出缓冲区。
|
||||
const NOFLSH = 0x80000000;
|
||||
/// 表示在后台进程尝试写入终端时,发送停止信号(Ctrl-S)
|
||||
const TOSTOP = 0x00400000;
|
||||
/// 表示在回显时,显示控制字符为 ^ 加字符。
|
||||
const ECHOCTL= 0x00000040;
|
||||
/// 表示在回显时显示带有 # 的换行符(为了与 echo -n 命令兼容)。
|
||||
const ECHOPRT= 0x00000020;
|
||||
/// 表示在回显时将 KILL 字符(Ctrl-U)用空格字符显示。
|
||||
const ECHOKE = 0x00000001;
|
||||
/// 表示输出正在被冲刷(flush),通常是由于输入/输出流的状态变化。
|
||||
const FLUSHO = 0x00800000;
|
||||
/// 表示在规范模式下,存在需要重新打印的字符。
|
||||
const PENDIN = 0x20000000;
|
||||
/// 表示启用实现定义的输入处理。
|
||||
const IEXTEN = 0x00000400;
|
||||
/// 表示启用扩展的处理函数
|
||||
const EXTPROC= 0x10000000;
|
||||
}
|
||||
|
||||
pub struct TtySetTermiosOpt: u8 {
|
||||
const TERMIOS_FLUSH =1;
|
||||
const TERMIOS_WAIT =2;
|
||||
const TERMIOS_TERMIO =4;
|
||||
const TERMIOS_OLD =8;
|
||||
}
|
||||
}
|
||||
|
||||
/// 对应termios中控制字符的索引
|
||||
pub struct ControlCharIndex;
|
||||
#[allow(dead_code)]
|
||||
impl ControlCharIndex {
|
||||
pub const DISABLE_CHAR: u8 = b'\0';
|
||||
/// 中断信号
|
||||
pub const VINTR: usize = 0;
|
||||
/// 退出信号
|
||||
pub const VQUIT: usize = 1;
|
||||
/// 退格
|
||||
pub const VERASE: usize = 2;
|
||||
/// 终止输入信号
|
||||
pub const VKILL: usize = 3;
|
||||
/// 文件结束信号 \0?
|
||||
pub const VEOF: usize = 4;
|
||||
/// 指定非规范模式下的最小字符数
|
||||
pub const VMIN: usize = 5;
|
||||
/// 换行符
|
||||
pub const VEOL: usize = 6;
|
||||
/// 指定非规范模式下的超时时间
|
||||
pub const VTIME: usize = 7;
|
||||
/// 换行符
|
||||
pub const VEOL2: usize = 8;
|
||||
/// 未使用,保留
|
||||
pub const VSWTC: usize = 9;
|
||||
/// 擦除前一个单词
|
||||
pub const VWERASE: usize = 10;
|
||||
/// 重新打印整行
|
||||
pub const VREPRINT: usize = 11;
|
||||
/// 挂起信号
|
||||
pub const VSUSP: usize = 12;
|
||||
/// 启动输出信号
|
||||
pub const VSTART: usize = 13;
|
||||
/// 停止输出信号
|
||||
pub const VSTOP: usize = 14;
|
||||
/// 将下一个字符视为字面值,而不是特殊字符
|
||||
pub const VLNEXT: usize = 15;
|
||||
/// 对应于字符丢弃信号,用于丢弃当前输入的行
|
||||
pub const VDISCARD: usize = 16;
|
||||
}
|
592
kernel/src/driver/tty/tty_core.rs
Normal file
592
kernel/src/driver/tty/tty_core.rs
Normal file
@ -0,0 +1,592 @@
|
||||
use core::{fmt::Debug, sync::atomic::AtomicBool};
|
||||
|
||||
use alloc::{string::String, sync::Arc, vec::Vec};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
libs::{
|
||||
rwlock::{RwLock, RwLockReadGuard, RwLockUpgradableGuard, RwLockWriteGuard},
|
||||
spinlock::{SpinLock, SpinLockGuard},
|
||||
wait_queue::EventWaitQueue,
|
||||
},
|
||||
mm::VirtAddr,
|
||||
net::event_poll::EPollEventType,
|
||||
process::Pid,
|
||||
syscall::user_access::UserBufferWriter,
|
||||
};
|
||||
|
||||
use super::{
|
||||
termios::{ControlMode, PosixTermios, Termios, TtySetTermiosOpt, WindowSize},
|
||||
tty_driver::{TtyDriver, TtyDriverSubType, TtyDriverType, TtyOperation},
|
||||
tty_ldisc::{
|
||||
ntty::{NTtyData, NTtyLinediscipline},
|
||||
TtyLineDiscipline,
|
||||
},
|
||||
tty_port::TtyPort,
|
||||
virtual_terminal::{virtual_console::VirtualConsoleData, VIRT_CONSOLES},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TtyCore {
|
||||
core: TtyCoreData,
|
||||
/// 线路规程函数集
|
||||
line_discipline: Arc<dyn TtyLineDiscipline>,
|
||||
}
|
||||
|
||||
impl TtyCore {
|
||||
pub fn new(driver: Arc<TtyDriver>, index: usize) -> Arc<Self> {
|
||||
let name = driver.tty_line_name(index);
|
||||
let termios = driver.init_termios();
|
||||
let core = TtyCoreData {
|
||||
tty_driver: driver,
|
||||
termios: RwLock::new(termios),
|
||||
name,
|
||||
flags: RwLock::new(TtyFlag::empty()),
|
||||
count: RwLock::new(0),
|
||||
window_size: RwLock::new(WindowSize::default()),
|
||||
read_wq: EventWaitQueue::new(),
|
||||
write_wq: EventWaitQueue::new(),
|
||||
port: RwLock::new(None),
|
||||
index,
|
||||
ctrl: SpinLock::new(TtyContorlInfo::default()),
|
||||
closing: AtomicBool::new(false),
|
||||
flow: SpinLock::new(TtyFlowState::default()),
|
||||
};
|
||||
|
||||
return Arc::new(Self {
|
||||
core,
|
||||
line_discipline: Arc::new(NTtyLinediscipline {
|
||||
data: SpinLock::new(NTtyData::new()),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn core(&self) -> &TtyCoreData {
|
||||
return &self.core;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ldisc(&self) -> Arc<dyn TtyLineDiscipline> {
|
||||
self.line_discipline.clone()
|
||||
}
|
||||
|
||||
pub fn reopen(&self) -> Result<(), SystemError> {
|
||||
let tty_core = self.core();
|
||||
let driver = tty_core.driver();
|
||||
|
||||
if driver.tty_driver_type() == TtyDriverType::Pty
|
||||
&& driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster
|
||||
{
|
||||
return Err(SystemError::EIO);
|
||||
}
|
||||
|
||||
// if *tty_core.count.read() == 0 {
|
||||
// return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
|
||||
// }
|
||||
|
||||
// TODO 判断flags
|
||||
|
||||
tty_core.add_count();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_port(&self, port: Arc<dyn TtyPort>) {
|
||||
*self.core.port.write() = Some(port);
|
||||
}
|
||||
|
||||
pub fn tty_start(&self) {
|
||||
let mut flow = self.core.flow.lock_irqsave();
|
||||
if !flow.stopped || flow.tco_stopped {
|
||||
return;
|
||||
}
|
||||
|
||||
flow.stopped = false;
|
||||
let _ = self.start(self.core());
|
||||
self.tty_wakeup();
|
||||
}
|
||||
|
||||
pub fn tty_stop(&self) {
|
||||
let mut flow = self.core.flow.lock_irqsave();
|
||||
if flow.stopped {
|
||||
return;
|
||||
}
|
||||
flow.stopped = true;
|
||||
|
||||
let _ = self.stop(self.core());
|
||||
}
|
||||
|
||||
pub fn tty_wakeup(&self) {
|
||||
if self.core.flags.read().contains(TtyFlag::DO_WRITE_WAKEUP) {
|
||||
let _ = self.ldisc().write_wakeup(self.core());
|
||||
}
|
||||
|
||||
self.core()
|
||||
.write_wq
|
||||
.wakeup(EPollEventType::EPOLLOUT.bits() as u64);
|
||||
}
|
||||
|
||||
pub fn tty_mode_ioctl(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
cmd: u32,
|
||||
arg: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
match cmd {
|
||||
TtyIoctlCmd::TCGETS => {
|
||||
let termios = PosixTermios::from_kernel_termios(self.core.termios().clone());
|
||||
let mut user_writer = UserBufferWriter::new(
|
||||
VirtAddr::new(arg).as_ptr::<PosixTermios>(),
|
||||
core::mem::size_of::<PosixTermios>(),
|
||||
true,
|
||||
)?;
|
||||
|
||||
user_writer.copy_one_to_user(&termios, 0)?;
|
||||
return Ok(0);
|
||||
}
|
||||
TtyIoctlCmd::TCSETSW => {
|
||||
return self.core_set_termios(
|
||||
tty,
|
||||
VirtAddr::new(arg),
|
||||
TtySetTermiosOpt::TERMIOS_WAIT | TtySetTermiosOpt::TERMIOS_OLD,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err(SystemError::ENOIOCTLCMD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn core_set_termios(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
arg: VirtAddr,
|
||||
opt: TtySetTermiosOpt,
|
||||
) -> Result<usize, SystemError> {
|
||||
let tmp_termios = self.core().termios().clone();
|
||||
|
||||
if opt.contains(TtySetTermiosOpt::TERMIOS_TERMIO) {
|
||||
todo!()
|
||||
} else {
|
||||
let mut user_writer = UserBufferWriter::new(
|
||||
arg.as_ptr::<PosixTermios>(),
|
||||
core::mem::size_of::<PosixTermios>(),
|
||||
true,
|
||||
)?;
|
||||
|
||||
user_writer.copy_one_to_user(&tmp_termios, 0)?;
|
||||
}
|
||||
|
||||
if opt.contains(TtySetTermiosOpt::TERMIOS_FLUSH) {
|
||||
let ld = self.ldisc();
|
||||
let _ = ld.flush_buffer(tty.clone());
|
||||
}
|
||||
|
||||
if opt.contains(TtySetTermiosOpt::TERMIOS_WAIT) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
self.set_termios_next(tty, tmp_termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn set_termios_next(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
new_termios: Termios,
|
||||
) -> Result<(), SystemError> {
|
||||
let mut termios = self.core().termios_write();
|
||||
|
||||
let old_termios = termios.clone();
|
||||
|
||||
*termios = new_termios;
|
||||
|
||||
let tmp = termios.control_mode;
|
||||
termios.control_mode ^= (tmp ^ old_termios.control_mode) & ControlMode::ADDRB;
|
||||
|
||||
let ret = self.set_termios(tty.clone(), old_termios);
|
||||
if ret.is_err() {
|
||||
termios.control_mode &= ControlMode::HUPCL | ControlMode::CREAD | ControlMode::CLOCAL;
|
||||
termios.control_mode |= old_termios.control_mode
|
||||
& !(ControlMode::HUPCL | ControlMode::CREAD | ControlMode::CLOCAL);
|
||||
termios.input_speed = old_termios.input_speed;
|
||||
termios.output_speed = old_termios.output_speed;
|
||||
}
|
||||
|
||||
drop(termios);
|
||||
let ld = self.ldisc();
|
||||
ld.set_termios(tty, Some(old_termios))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TtyContorlInfo {
|
||||
/// 前台进程pid
|
||||
pub session: Option<Pid>,
|
||||
/// 前台进程组id
|
||||
pub pgid: Option<Pid>,
|
||||
|
||||
/// packet模式下使用,目前未用到
|
||||
pub pktstatus: u8,
|
||||
pub packet: bool,
|
||||
}
|
||||
|
||||
impl Default for TtyContorlInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
session: None,
|
||||
pgid: None,
|
||||
pktstatus: Default::default(),
|
||||
packet: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TtyCoreWriteData {
|
||||
/// 写缓冲区
|
||||
pub write_buf: Vec<u8>,
|
||||
/// 写入数量
|
||||
pub write_cnt: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TtyFlowState {
|
||||
/// 表示流控是否被停止
|
||||
pub stopped: bool,
|
||||
/// 表示 TCO(Transmit Continuous Operation)流控是否被停止
|
||||
pub tco_stopped: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TtyCoreData {
|
||||
tty_driver: Arc<TtyDriver>,
|
||||
termios: RwLock<Termios>,
|
||||
name: String,
|
||||
flags: RwLock<TtyFlag>,
|
||||
/// 在初始化时即确定不会更改,所以这里不用加锁
|
||||
index: usize,
|
||||
count: RwLock<usize>,
|
||||
/// 窗口大小
|
||||
window_size: RwLock<WindowSize>,
|
||||
/// 读等待队列
|
||||
read_wq: EventWaitQueue,
|
||||
/// 写等待队列
|
||||
write_wq: EventWaitQueue,
|
||||
/// 端口
|
||||
port: RwLock<Option<Arc<dyn TtyPort>>>,
|
||||
/// 前台进程
|
||||
ctrl: SpinLock<TtyContorlInfo>,
|
||||
/// 是否正在关闭
|
||||
closing: AtomicBool,
|
||||
/// 流控状态
|
||||
flow: SpinLock<TtyFlowState>,
|
||||
}
|
||||
|
||||
impl TtyCoreData {
|
||||
#[inline]
|
||||
pub fn driver(&self) -> Arc<TtyDriver> {
|
||||
self.tty_driver.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flow_irqsave(&self) -> SpinLockGuard<TtyFlowState> {
|
||||
self.flow.lock_irqsave()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn port(&self) -> Option<Arc<dyn TtyPort>> {
|
||||
self.port.read().clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> TtyFlag {
|
||||
self.flags.read().clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn termios(&self) -> RwLockReadGuard<'_, Termios> {
|
||||
self.termios.read()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn termios_write(&self) -> RwLockWriteGuard<Termios> {
|
||||
self.termios.write()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_termios(&self, termios: Termios) {
|
||||
let mut termios_guard = self.termios.write();
|
||||
*termios_guard = termios;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_count(&self) {
|
||||
let mut guard = self.count.write();
|
||||
*guard += 1;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_wq(&self) -> &EventWaitQueue {
|
||||
&self.read_wq
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_wq(&self) -> &EventWaitQueue {
|
||||
&self.write_wq
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contorl_info_irqsave(&self) -> SpinLockGuard<TtyContorlInfo> {
|
||||
self.ctrl.lock_irqsave()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn window_size_upgradeable(&self) -> RwLockUpgradableGuard<WindowSize> {
|
||||
self.window_size.upgradeable_read()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn window_size(&self) -> RwLockReadGuard<WindowSize> {
|
||||
self.window_size.read()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_closing(&self) -> bool {
|
||||
self.closing.load(core::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn vc_data_irqsave(&self) -> SpinLockGuard<VirtualConsoleData> {
|
||||
VIRT_CONSOLES[self.index].lock_irqsave()
|
||||
}
|
||||
}
|
||||
|
||||
/// TTY 核心接口,不同的tty需要各自实现这个trait
|
||||
pub trait TtyCoreFuncs: Debug + Send + Sync {}
|
||||
|
||||
impl TtyOperation for TtyCore {
|
||||
#[inline]
|
||||
fn open(&self, tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().open(tty);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_room(&self, tty: &TtyCoreData) -> usize {
|
||||
return self.core().tty_driver.driver_funcs().write_room(tty);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write(&self, tty: &TtyCoreData, buf: &[u8], nr: usize) -> Result<usize, SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().write(tty, buf, nr);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush_chars(&self, tty: &TtyCoreData) {
|
||||
self.core().tty_driver.driver_funcs().flush_chars(tty);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().put_char(tty, ch);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn install(&self, driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().install(driver, tty);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn start(&self, tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().start(tty);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stop(&self, tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().stop(tty);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ioctl(&self, tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<(), SystemError> {
|
||||
return self.core().tty_driver.driver_funcs().ioctl(tty, cmd, arg);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn chars_in_buffer(&self) -> usize {
|
||||
return self.core().tty_driver.driver_funcs().chars_in_buffer();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_termios(&self, tty: Arc<TtyCore>, old_termios: Termios) -> Result<(), SystemError> {
|
||||
return self
|
||||
.core()
|
||||
.tty_driver
|
||||
.driver_funcs()
|
||||
.set_termios(tty, old_termios);
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct TtyFlag: u32 {
|
||||
/// 终端被节流
|
||||
const THROTTLED = 1 << 0;
|
||||
/// 终端输入输出错误状态
|
||||
const IO_ERROR = 1 << 1;
|
||||
/// 终端的其他一方已关闭
|
||||
const OTHER_CLOSED = 1 << 2;
|
||||
/// 终端处于独占状态
|
||||
const EXCLUSIVE = 1 << 3;
|
||||
/// 终端执行写唤醒操作
|
||||
const DO_WRITE_WAKEUP = 1 << 5;
|
||||
/// 终端线路驱动程序已打开
|
||||
const LDISC_OPEN = 1 << 11;
|
||||
/// 终端伪终端设备已锁定
|
||||
const PTY_LOCK = 1 << 16;
|
||||
/// 终端禁用写分裂操作
|
||||
const NO_WRITE_SPLIT = 1 << 17;
|
||||
/// 终端挂断(挂起)状态
|
||||
const HUPPED = 1 << 18;
|
||||
/// 终端正在挂断(挂起)
|
||||
const HUPPING = 1 << 19;
|
||||
/// 终端线路驱动程序正在更改
|
||||
const LDISC_CHANGING = 1 << 20;
|
||||
/// 终端线路驱动程序已停止
|
||||
const LDISC_HALTED = 1 << 22;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EchoOperation {
|
||||
/// 开始特殊操作。
|
||||
Start,
|
||||
/// 向后移动光标列。
|
||||
MoveBackCol,
|
||||
/// 设置规范模式下的列位置。
|
||||
SetCanonCol,
|
||||
/// 擦除制表符。
|
||||
EraseTab,
|
||||
|
||||
Undefined(u8),
|
||||
}
|
||||
|
||||
impl EchoOperation {
|
||||
pub fn from_u8(num: u8) -> EchoOperation {
|
||||
match num {
|
||||
0xff => Self::Start,
|
||||
0x80 => Self::MoveBackCol,
|
||||
0x81 => Self::SetCanonCol,
|
||||
0x82 => Self::EraseTab,
|
||||
_ => Self::Undefined(num),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_u8(&self) -> u8 {
|
||||
match *self {
|
||||
EchoOperation::Start => 0xff,
|
||||
EchoOperation::MoveBackCol => 0x80,
|
||||
EchoOperation::SetCanonCol => 0x81,
|
||||
EchoOperation::EraseTab => 0x82,
|
||||
EchoOperation::Undefined(num) => num,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TtyIoctlCmd;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl TtyIoctlCmd {
|
||||
/// 获取终端参数
|
||||
pub const TCGETS: u32 = 0x5401;
|
||||
/// 设置终端参数
|
||||
pub const TCSETS: u32 = 0x5402;
|
||||
/// 设置终端参数并等待所有输出完成
|
||||
pub const TCSETSW: u32 = 0x5403;
|
||||
/// 设置终端参数并且等待所有输出完成,但在这之前将终端清空
|
||||
pub const TCSETSF: u32 = 0x5404;
|
||||
/// 获取终端参数
|
||||
pub const TCGETA: u32 = 0x5405;
|
||||
/// 设置终端参数
|
||||
pub const TCSETA: u32 = 0x5406;
|
||||
/// 设置终端参数并等待所有输出完成
|
||||
pub const TCSETAW: u32 = 0x5407;
|
||||
/// 设置终端参数并且等待所有输出完成,但在这之前将终端清空
|
||||
pub const TCSETAF: u32 = 0x5408;
|
||||
/// 发送零字节,等待所有输出完成
|
||||
pub const TCSBRK: u32 = 0x5409;
|
||||
/// 控制终端的流控
|
||||
pub const TCXONC: u32 = 0x540A;
|
||||
/// 刷新输入/输出缓冲区或者丢弃输入缓冲区
|
||||
pub const TCFLSH: u32 = 0x540B;
|
||||
/// 设置设备为独占模式
|
||||
pub const TIOCEXCL: u32 = 0x540C;
|
||||
/// 设置设备为非独占模式
|
||||
pub const TIOCNXCL: u32 = 0x540D;
|
||||
/// 设置当前进程的控制终端
|
||||
pub const TIOCSCTTY: u32 = 0x540E;
|
||||
/// 获取前台进程组
|
||||
pub const TIOCGPGRP: u32 = 0x540F;
|
||||
///设置前台进程组
|
||||
pub const TIOCSPGRP: u32 = 0x5410;
|
||||
/// 获取输出队列的字节数
|
||||
pub const TIOCOUTQ: u32 = 0x5411;
|
||||
/// 模拟从终端输入字符
|
||||
pub const TIOCSTI: u32 = 0x5412;
|
||||
/// 获取窗口大小
|
||||
pub const TIOCGWINSZ: u32 = 0x5413;
|
||||
/// 设置窗口大小
|
||||
pub const TIOCSWINSZ: u32 = 0x5414;
|
||||
/// 获取终端控制信号的状态
|
||||
pub const TIOCMGET: u32 = 0x5415;
|
||||
/// 设置终端控制信号的位
|
||||
pub const TIOCMBIS: u32 = 0x5416;
|
||||
/// 清除终端控制信号的位
|
||||
pub const TIOCMBIC: u32 = 0x5417;
|
||||
/// 设置终端控制信号的状态
|
||||
pub const TIOCMSET: u32 = 0x5418;
|
||||
/// 获取软件载波状态
|
||||
pub const TIOCGSOFTCAR: u32 = 0x5419;
|
||||
/// 设置软件载波状态
|
||||
pub const TIOCSSOFTCAR: u32 = 0x541A;
|
||||
/// 获取输入队列的字节数
|
||||
pub const FIONREAD: u32 = 0x541B;
|
||||
/// Linux 特有命令
|
||||
pub const TIOCLINUX: u32 = 0x541C;
|
||||
/// 获取控制台设备
|
||||
pub const TIOCCONS: u32 = 0x541D;
|
||||
/// 获取串行设备参数
|
||||
pub const TIOCGSERIAL: u32 = 0x541E;
|
||||
/// 设置串行设备参数
|
||||
pub const TIOCSSERIAL: u32 = 0x541F;
|
||||
/// 设置套接字的报文模式
|
||||
pub const TIOCPKT: u32 = 0x5420;
|
||||
/// 设置非阻塞 I/O
|
||||
pub const FIONBIO: u32 = 0x5421;
|
||||
/// 清除控制终端
|
||||
pub const TIOCNOTTY: u32 = 0x5422;
|
||||
/// 设置终端线路驱动器
|
||||
pub const TIOCSETD: u32 = 0x5423;
|
||||
/// 获取终端线路驱动器
|
||||
pub const TIOCGETD: u32 = 0x5424;
|
||||
/// 发送终止条件
|
||||
pub const TCSBRKP: u32 = 0x5425;
|
||||
/// 开始发送零比特
|
||||
pub const TIOCSBRK: u32 = 0x5427;
|
||||
/// 停止发送零比特
|
||||
pub const TIOCCBRK: u32 = 0x5428;
|
||||
/// Return the session ID of FD
|
||||
pub const TIOCGSID: u32 = 0x5429;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
use alloc::{
|
||||
collections::BTreeMap,
|
||||
string::{String, ToString},
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
@ -7,104 +6,318 @@ use system_error::SystemError;
|
||||
use unified_init::macros::unified_init;
|
||||
|
||||
use crate::{
|
||||
arch::ipc::signal::Signal,
|
||||
driver::{
|
||||
base::{
|
||||
char::CharDevice,
|
||||
device::{
|
||||
bus::Bus,
|
||||
device_number::{DeviceNumber, Major},
|
||||
device_register,
|
||||
driver::Driver,
|
||||
Device, DeviceKObjType, DeviceType, IdTable,
|
||||
},
|
||||
kobject::{KObject, LockedKObjectState},
|
||||
kset::KSet,
|
||||
},
|
||||
serial::serial_init,
|
||||
},
|
||||
filesystem::{
|
||||
devfs::{devfs_register, DevFS, DeviceINode},
|
||||
vfs::{
|
||||
file::FileMode, syscall::ModeType, FilePrivateData, FileType, IndexNode, Metadata,
|
||||
ROOT_INODE,
|
||||
},
|
||||
kernfs::KernFSInode,
|
||||
vfs::{file::FileMode, syscall::ModeType, FilePrivateData, FileType, IndexNode, Metadata},
|
||||
},
|
||||
init::initcall::INITCALL_DEVICE,
|
||||
kerror,
|
||||
libs::{
|
||||
lib_ui::textui::{textui_putchar, FontColor},
|
||||
rwlock::RwLock,
|
||||
},
|
||||
libs::rwlock::RwLock,
|
||||
mm::VirtAddr,
|
||||
process::ProcessManager,
|
||||
syscall::user_access::UserBufferWriter,
|
||||
};
|
||||
|
||||
use super::{serial::serial_init, TtyCore, TtyError, TtyFileFlag, TtyFilePrivateData};
|
||||
use super::{
|
||||
termios::WindowSize,
|
||||
tty_core::{TtyCore, TtyFlag, TtyIoctlCmd},
|
||||
tty_driver::{TtyDriver, TtyDriverSubType, TtyDriverType, TtyOperation},
|
||||
tty_job_control::TtyJobCtrlManager,
|
||||
virtual_terminal::vty_init,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
/// 所有TTY设备的B树。用于根据名字,找到Arc<TtyDevice>
|
||||
/// TODO: 待设备驱动模型完善,具有类似功能的机制后,删掉这里
|
||||
pub static ref TTY_DEVICES: RwLock<BTreeMap<String, Arc<TtyDevice>>> = RwLock::new(BTreeMap::new());
|
||||
#[derive(Debug)]
|
||||
pub struct InnerTtyDevice {
|
||||
/// 当前设备所述的kset
|
||||
kset: Option<Arc<KSet>>,
|
||||
parent_kobj: Option<Weak<dyn KObject>>,
|
||||
/// 当前设备所述的总线
|
||||
bus: Option<Weak<dyn Bus>>,
|
||||
inode: Option<Arc<KernFSInode>>,
|
||||
driver: Option<Weak<dyn Driver>>,
|
||||
can_match: bool,
|
||||
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
impl InnerTtyDevice {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
kset: None,
|
||||
parent_kobj: None,
|
||||
bus: None,
|
||||
inode: None,
|
||||
driver: None,
|
||||
can_match: false,
|
||||
metadata: Metadata::new(FileType::CharDevice, ModeType::from_bits_truncate(0o755)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief TTY设备
|
||||
#[derive(Debug)]
|
||||
#[cast_to([sync] Device)]
|
||||
pub struct TtyDevice {
|
||||
/// TTY核心
|
||||
core: TtyCore,
|
||||
name: &'static str,
|
||||
id_table: IdTable,
|
||||
inner: RwLock<InnerTtyDevice>,
|
||||
kobj_state: LockedKObjectState,
|
||||
/// TTY所属的文件系统
|
||||
fs: RwLock<Weak<DevFS>>,
|
||||
/// TTY设备私有信息
|
||||
private_data: RwLock<TtyDevicePrivateData>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TtyDevicePrivateData {
|
||||
/// TTY设备名(如tty1)
|
||||
name: String,
|
||||
/// TTY设备文件的元数据
|
||||
metadata: Metadata,
|
||||
// TODO: 增加指向输出端口连接的设备的指针
|
||||
}
|
||||
|
||||
impl TtyDevice {
|
||||
pub fn new(name: &str) -> Arc<TtyDevice> {
|
||||
let result = Arc::new(TtyDevice {
|
||||
core: TtyCore::new(),
|
||||
pub fn new(name: &'static str, id_table: IdTable) -> Arc<TtyDevice> {
|
||||
let dev_num = id_table.device_number();
|
||||
let dev = TtyDevice {
|
||||
name,
|
||||
id_table,
|
||||
inner: RwLock::new(InnerTtyDevice::new()),
|
||||
kobj_state: LockedKObjectState::new(None),
|
||||
fs: RwLock::new(Weak::default()),
|
||||
private_data: TtyDevicePrivateData::new(name),
|
||||
});
|
||||
// 默认开启输入回显
|
||||
result.core.enable_echo();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief 判断文件私有信息是否为TTY文件的私有信息
|
||||
#[inline]
|
||||
fn verify_file_private_data<'a>(
|
||||
dev.inner.write().metadata.raw_dev = dev_num;
|
||||
|
||||
Arc::new(dev)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexNode for TtyDevice {
|
||||
fn open(
|
||||
&self,
|
||||
private_data: &'a mut FilePrivateData,
|
||||
) -> Result<&'a mut TtyFilePrivateData, SystemError> {
|
||||
if let FilePrivateData::Tty(t) = private_data {
|
||||
return Ok(t);
|
||||
}
|
||||
return Err(SystemError::EIO);
|
||||
}
|
||||
data: &mut crate::filesystem::vfs::FilePrivateData,
|
||||
mode: &crate::filesystem::vfs::file::FileMode,
|
||||
) -> Result<(), SystemError> {
|
||||
let dev_num = self.metadata()?.raw_dev;
|
||||
|
||||
/// @brief 获取TTY设备名
|
||||
#[inline]
|
||||
pub fn name(&self) -> String {
|
||||
return self.private_data.read().name.clone();
|
||||
}
|
||||
let tty = TtyDriver::open_tty(dev_num)?;
|
||||
|
||||
/// @brief 检查TTY文件的读写参数是否合法
|
||||
#[inline]
|
||||
pub fn check_rw_param(&self, len: usize, buf: &[u8]) -> Result<(), SystemError> {
|
||||
if len > buf.len() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// 设置privdata
|
||||
*data = FilePrivateData::Tty(TtyFilePrivateData {
|
||||
tty: tty.clone(),
|
||||
mode: *mode,
|
||||
});
|
||||
|
||||
/// @brief 向TTY的输入端口导入数据
|
||||
pub fn input(&self, buf: &[u8]) -> Result<usize, SystemError> {
|
||||
let r: Result<usize, TtyError> = self.core.input(buf, false);
|
||||
if r.is_ok() {
|
||||
return Ok(r.unwrap());
|
||||
let ret = tty.open(tty.core());
|
||||
if ret.is_err() {
|
||||
let err = ret.unwrap_err();
|
||||
if err == SystemError::ENOSYS {
|
||||
return Err(SystemError::ENODEV);
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let r = r.unwrap_err();
|
||||
match r {
|
||||
TtyError::BufferFull(x) => return Ok(x),
|
||||
TtyError::Closed => return Err(SystemError::ENODEV),
|
||||
e => {
|
||||
kerror!("tty error occurred while writing data to its input port, msg={e:?}");
|
||||
return Err(SystemError::EBUSY);
|
||||
let driver = tty.core().driver();
|
||||
// 考虑noctty(当前tty)
|
||||
if !(mode.contains(FileMode::O_NOCTTY) && dev_num == DeviceNumber::new(Major::TTY_MAJOR, 0)
|
||||
|| dev_num == DeviceNumber::new(Major::TTYAUX_MAJOR, 1)
|
||||
|| (driver.tty_driver_type() == TtyDriverType::Pty
|
||||
&& driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster))
|
||||
{
|
||||
let pcb = ProcessManager::current_pcb();
|
||||
let pcb_tty = pcb.sig_info().tty();
|
||||
if pcb_tty.is_none() && tty.core().contorl_info_irqsave().session.is_none() {
|
||||
TtyJobCtrlManager::proc_set_tty(tty);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
len: usize,
|
||||
buf: &mut [u8],
|
||||
data: &mut crate::filesystem::vfs::FilePrivateData,
|
||||
) -> Result<usize, system_error::SystemError> {
|
||||
let (tty, mode) = if let FilePrivateData::Tty(tty_priv) = data {
|
||||
(tty_priv.tty.clone(), tty_priv.mode)
|
||||
} else {
|
||||
return Err(SystemError::EIO);
|
||||
};
|
||||
|
||||
let ld = tty.ldisc();
|
||||
let mut offset = 0;
|
||||
let mut cookie = false;
|
||||
loop {
|
||||
let mut size = if len > buf.len() { buf.len() } else { len };
|
||||
size = ld.read(tty.clone(), buf, size, &mut cookie, offset, mode)?;
|
||||
// 没有更多数据
|
||||
if size == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
offset += size;
|
||||
|
||||
// 缓冲区写满
|
||||
if offset >= len {
|
||||
break;
|
||||
}
|
||||
|
||||
// 没有更多数据
|
||||
if !cookie {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
len: usize,
|
||||
buf: &[u8],
|
||||
data: &mut crate::filesystem::vfs::FilePrivateData,
|
||||
) -> Result<usize, system_error::SystemError> {
|
||||
let mut count = len;
|
||||
let (tty, mode) = if let FilePrivateData::Tty(tty_priv) = data {
|
||||
(tty_priv.tty.clone(), tty_priv.mode)
|
||||
} else {
|
||||
return Err(SystemError::EIO);
|
||||
};
|
||||
|
||||
let ld = tty.ldisc();
|
||||
let core = tty.core();
|
||||
let mut chunk = 2048;
|
||||
if core.flags().contains(TtyFlag::NO_WRITE_SPLIT) {
|
||||
chunk = 65536;
|
||||
}
|
||||
chunk = chunk.min(count);
|
||||
|
||||
let pcb = ProcessManager::current_pcb();
|
||||
let mut written = 0;
|
||||
loop {
|
||||
// 至少需要写多少
|
||||
let size = chunk.min(count);
|
||||
|
||||
// 将数据从buf拷贝到writebuf
|
||||
|
||||
let ret = ld.write(tty.clone(), buf, size, mode)?;
|
||||
|
||||
written += ret;
|
||||
count -= ret;
|
||||
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if pcb.sig_info().sig_pending().has_pending() {
|
||||
return Err(SystemError::ERESTARTSYS);
|
||||
}
|
||||
}
|
||||
|
||||
if written > 0 {
|
||||
// todo: 更新时间
|
||||
}
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn crate::filesystem::vfs::FileSystem> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn as_any_ref(&self) -> &dyn core::any::Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn list(&self) -> Result<alloc::vec::Vec<alloc::string::String>, system_error::SystemError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Result<crate::filesystem::vfs::Metadata, SystemError> {
|
||||
Ok(self.inner.read().metadata.clone())
|
||||
}
|
||||
|
||||
fn close(&self, _data: &mut FilePrivateData) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resize(&self, _len: usize) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: u32, arg: usize, data: &FilePrivateData) -> Result<usize, SystemError> {
|
||||
let (tty, _) = if let FilePrivateData::Tty(tty_priv) = data {
|
||||
(tty_priv.tty.clone(), tty_priv.mode)
|
||||
} else {
|
||||
return Err(SystemError::EIO);
|
||||
};
|
||||
|
||||
match cmd {
|
||||
TtyIoctlCmd::TIOCSETD
|
||||
| TtyIoctlCmd::TIOCSBRK
|
||||
| TtyIoctlCmd::TIOCCBRK
|
||||
| TtyIoctlCmd::TCSBRK
|
||||
| TtyIoctlCmd::TCSBRKP => {
|
||||
TtyJobCtrlManager::tty_check_change(tty.clone(), Signal::SIGTTOU)?;
|
||||
if cmd != TtyIoctlCmd::TIOCCBRK {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match cmd {
|
||||
TtyIoctlCmd::TIOCGWINSZ => {
|
||||
let core = tty.core();
|
||||
let winsize = *core.window_size();
|
||||
|
||||
let mut user_writer = UserBufferWriter::new(
|
||||
VirtAddr::new(arg).as_ptr::<WindowSize>(),
|
||||
core::mem::size_of::<WindowSize>(),
|
||||
true,
|
||||
)?;
|
||||
|
||||
let err = user_writer.copy_one_to_user(&winsize, 0);
|
||||
if err.is_err() {
|
||||
return Err(SystemError::EFAULT);
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
_ => match TtyJobCtrlManager::job_ctrl_ioctl(tty.clone(), cmd, arg) {
|
||||
Ok(_) => {
|
||||
return Ok(0);
|
||||
}
|
||||
Err(e) => {
|
||||
if e != SystemError::ENOIOCTLCMD {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
match tty.ioctl(tty.clone(), cmd, arg) {
|
||||
Ok(_) => {
|
||||
return Ok(0);
|
||||
}
|
||||
Err(e) => {
|
||||
if e != SystemError::ENOIOCTLCMD {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
tty.ldisc().ioctl(tty, cmd, arg)?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,219 +327,181 @@ impl DeviceINode for TtyDevice {
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexNode for TtyDevice {
|
||||
/// @brief 打开TTY设备
|
||||
///
|
||||
/// @param data 文件私有信息
|
||||
/// @param mode 打开模式
|
||||
///
|
||||
/// TTY设备通过mode来确定这个文件到底是stdin/stdout/stderr
|
||||
/// - mode的值为O_RDONLY时,表示这个文件是stdin
|
||||
/// - mode的值为O_WRONLY时,表示这个文件是stdout
|
||||
/// - mode的值为O_WRONLY | O_SYNC时,表示这个文件是stderr
|
||||
fn open(&self, data: &mut FilePrivateData, mode: &FileMode) -> Result<(), SystemError> {
|
||||
let mut p = TtyFilePrivateData::default();
|
||||
|
||||
// 检查打开模式
|
||||
let accmode = mode.accmode();
|
||||
if accmode == FileMode::O_RDONLY.accmode() {
|
||||
p.flags.insert(TtyFileFlag::STDIN);
|
||||
} else if accmode == FileMode::O_WRONLY.accmode() {
|
||||
if mode.contains(FileMode::O_SYNC) {
|
||||
p.flags.insert(TtyFileFlag::STDERR);
|
||||
} else {
|
||||
p.flags.insert(TtyFileFlag::STDOUT);
|
||||
}
|
||||
} else {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
// 保存文件私有信息
|
||||
*data = FilePrivateData::Tty(p);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
len: usize,
|
||||
buf: &mut [u8],
|
||||
data: &mut crate::filesystem::vfs::FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
let _data: &mut TtyFilePrivateData = match self.verify_file_private_data(data) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
kerror!("Try to read tty device, but file private data type mismatch!");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
self.check_rw_param(len, buf)?;
|
||||
|
||||
// 读取stdin队列
|
||||
let r: Result<usize, TtyError> = self.core.read_stdin(&mut buf[0..len], true);
|
||||
if r.is_ok() {
|
||||
return Ok(r.unwrap());
|
||||
}
|
||||
|
||||
match r.unwrap_err() {
|
||||
TtyError::EOF(n) => {
|
||||
return Ok(n);
|
||||
}
|
||||
|
||||
x => {
|
||||
kerror!("Error occurred when reading tty, msg={x:?}");
|
||||
return Err(SystemError::ECONNABORTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
len: usize,
|
||||
buf: &[u8],
|
||||
data: &mut crate::filesystem::vfs::FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
let data: &mut TtyFilePrivateData = match self.verify_file_private_data(data) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
kerror!("Try to write tty device, but file private data type mismatch!");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
self.check_rw_param(len, buf)?;
|
||||
|
||||
let mut cnt: usize = 0;
|
||||
// 根据当前文件是stdout还是stderr,选择不同的发送方式
|
||||
let r: Result<usize, TtyError> = if data.flags.contains(TtyFileFlag::STDOUT) {
|
||||
loop {
|
||||
let r = self.core.stdout(&buf[cnt..len], false);
|
||||
if let Err(TtyError::BufferFull(c)) = r {
|
||||
self.sync().expect("Failed to sync tty device!");
|
||||
cnt += c;
|
||||
} else {
|
||||
break r;
|
||||
}
|
||||
}
|
||||
} else if data.flags.contains(TtyFileFlag::STDERR) {
|
||||
loop {
|
||||
let r = self.core.stderr(&buf[cnt..len], false);
|
||||
if let Err(TtyError::BufferFull(c)) = r {
|
||||
self.sync().expect("Failed to sync tty device!");
|
||||
cnt += c;
|
||||
} else {
|
||||
break r;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(SystemError::EPERM);
|
||||
};
|
||||
|
||||
if r.is_ok() {
|
||||
self.sync().expect("Failed to sync tty device!");
|
||||
return Ok(cnt + r.unwrap());
|
||||
}
|
||||
|
||||
let r: TtyError = r.unwrap_err();
|
||||
kerror!("Error occurred when writing tty deivce. Error msg={r:?}");
|
||||
return Err(SystemError::EIO);
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn crate::filesystem::vfs::FileSystem> {
|
||||
return self.fs.read().upgrade().unwrap();
|
||||
}
|
||||
|
||||
impl KObject for TtyDevice {
|
||||
fn as_any_ref(&self) -> &dyn core::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn list(&self) -> Result<alloc::vec::Vec<alloc::string::String>, SystemError> {
|
||||
return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
|
||||
fn set_inode(&self, inode: Option<Arc<crate::filesystem::kernfs::KernFSInode>>) {
|
||||
self.inner.write().inode = inode;
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Result<Metadata, SystemError> {
|
||||
return Ok(self.private_data.read().metadata.clone());
|
||||
fn inode(&self) -> Option<Arc<crate::filesystem::kernfs::KernFSInode>> {
|
||||
self.inner.read().inode.clone()
|
||||
}
|
||||
|
||||
fn close(&self, _data: &mut FilePrivateData) -> Result<(), SystemError> {
|
||||
return Ok(());
|
||||
fn parent(&self) -> Option<alloc::sync::Weak<dyn KObject>> {
|
||||
self.inner.read().parent_kobj.clone()
|
||||
}
|
||||
|
||||
fn set_parent(&self, parent: Option<alloc::sync::Weak<dyn KObject>>) {
|
||||
self.inner.write().parent_kobj = parent
|
||||
}
|
||||
|
||||
fn kset(&self) -> Option<Arc<crate::driver::base::kset::KSet>> {
|
||||
self.inner.read().kset.clone()
|
||||
}
|
||||
|
||||
fn set_kset(&self, kset: Option<Arc<crate::driver::base::kset::KSet>>) {
|
||||
self.inner.write().kset = kset
|
||||
}
|
||||
|
||||
fn kobj_type(&self) -> Option<&'static dyn crate::driver::base::kobject::KObjType> {
|
||||
Some(&DeviceKObjType)
|
||||
}
|
||||
|
||||
fn set_kobj_type(&self, _ktype: Option<&'static dyn crate::driver::base::kobject::KObjType>) {}
|
||||
|
||||
fn name(&self) -> alloc::string::String {
|
||||
self.name.to_string()
|
||||
}
|
||||
|
||||
fn set_name(&self, _name: alloc::string::String) {
|
||||
// self.name = name
|
||||
}
|
||||
|
||||
fn kobj_state(
|
||||
&self,
|
||||
) -> crate::libs::rwlock::RwLockReadGuard<crate::driver::base::kobject::KObjectState> {
|
||||
self.kobj_state.read()
|
||||
}
|
||||
|
||||
fn kobj_state_mut(
|
||||
&self,
|
||||
) -> crate::libs::rwlock::RwLockWriteGuard<crate::driver::base::kobject::KObjectState> {
|
||||
self.kobj_state.write()
|
||||
}
|
||||
|
||||
fn set_kobj_state(&self, state: crate::driver::base::kobject::KObjectState) {
|
||||
*self.kobj_state.write() = state
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for TtyDevice {
|
||||
fn dev_type(&self) -> crate::driver::base::device::DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id_table(&self) -> crate::driver::base::device::IdTable {
|
||||
self.id_table.clone()
|
||||
}
|
||||
|
||||
fn set_bus(&self, bus: Option<alloc::sync::Weak<dyn crate::driver::base::device::bus::Bus>>) {
|
||||
self.inner.write().bus = bus
|
||||
}
|
||||
|
||||
fn set_class(&self, _class: Option<Arc<dyn crate::driver::base::class::Class>>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn driver(&self) -> Option<Arc<dyn crate::driver::base::device::driver::Driver>> {
|
||||
self.inner.read().driver.clone()?.upgrade()
|
||||
}
|
||||
|
||||
fn set_driver(
|
||||
&self,
|
||||
driver: Option<alloc::sync::Weak<dyn crate::driver::base::device::driver::Driver>>,
|
||||
) {
|
||||
self.inner.write().driver = driver
|
||||
}
|
||||
|
||||
fn is_dead(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn can_match(&self) -> bool {
|
||||
self.inner.read().can_match
|
||||
}
|
||||
|
||||
fn set_can_match(&self, can_match: bool) {
|
||||
self.inner.write().can_match = can_match
|
||||
}
|
||||
|
||||
fn state_synced(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for TtyDevice {
|
||||
fn read(&self, _len: usize, _buf: &mut [u8]) -> Result<usize, SystemError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write(&self, _len: usize, _buf: &[u8]) -> Result<usize, SystemError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<(), SystemError> {
|
||||
// TODO: 引入IO重定向后,需要将输出重定向到对应的设备。
|
||||
// 目前只是简单的输出到屏幕(为了实现的简便)
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8; 512];
|
||||
let r: Result<usize, TtyError> = self.core.output(&mut buf[0..511], false);
|
||||
let len;
|
||||
match r {
|
||||
Ok(x) => {
|
||||
len = x;
|
||||
}
|
||||
Err(TtyError::EOF(x)) | Err(TtyError::BufferEmpty(x)) => {
|
||||
len = x;
|
||||
}
|
||||
_ => return Err(SystemError::EIO),
|
||||
}
|
||||
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
// 输出到屏幕
|
||||
|
||||
for x in 0..len {
|
||||
textui_putchar(buf[x] as char, FontColor::WHITE, FontColor::BLACK).ok();
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
fn resize(&self, _len: usize) -> Result<(), SystemError> {
|
||||
return Ok(());
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyDevicePrivateData {
|
||||
pub fn new(name: &str) -> RwLock<Self> {
|
||||
let mut metadata = Metadata::new(FileType::CharDevice, ModeType::from_bits_truncate(0o755));
|
||||
metadata.size = TtyCore::STDIN_BUF_SIZE as i64;
|
||||
return RwLock::new(TtyDevicePrivateData {
|
||||
name: name.to_string(),
|
||||
metadata,
|
||||
});
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TtyFilePrivateData {
|
||||
tty: Arc<TtyCore>,
|
||||
mode: FileMode,
|
||||
}
|
||||
|
||||
/// @brief 初始化TTY设备
|
||||
/// 初始化tty设备和console子设备
|
||||
#[unified_init(INITCALL_DEVICE)]
|
||||
#[inline(never)]
|
||||
pub fn tty_init() -> Result<(), SystemError> {
|
||||
let tty: Arc<TtyDevice> = TtyDevice::new("tty0");
|
||||
let devfs_root_inode = ROOT_INODE().lookup("/dev");
|
||||
if devfs_root_inode.is_err() {
|
||||
return Err(devfs_root_inode.unwrap_err());
|
||||
}
|
||||
// 当前关闭键盘输入回显
|
||||
// TODO: 完善Termios之后, 改为默认开启键盘输入回显.
|
||||
tty.core.disable_echo();
|
||||
let guard = TTY_DEVICES.upgradeable_read();
|
||||
let tty = TtyDevice::new(
|
||||
"tty0",
|
||||
IdTable::new(
|
||||
String::from("tty0"),
|
||||
Some(DeviceNumber::new(Major::TTY_MAJOR, 0)),
|
||||
),
|
||||
);
|
||||
|
||||
// 如果已经存在了这个设备
|
||||
if guard.contains_key("tty0") {
|
||||
return Err(SystemError::EEXIST);
|
||||
}
|
||||
let console = TtyDevice::new(
|
||||
"console",
|
||||
IdTable::new(
|
||||
String::from("console"),
|
||||
Some(DeviceNumber::new(Major::TTYAUX_MAJOR, 1)),
|
||||
),
|
||||
);
|
||||
|
||||
let mut guard = guard.upgrade();
|
||||
// 注册tty设备
|
||||
// CharDevOps::cdev_add(
|
||||
// tty.clone() as Arc<dyn CharDevice>,
|
||||
// IdTable::new(
|
||||
// String::from("tty0"),
|
||||
// Some(DeviceNumber::new(Major::TTYAUX_MAJOR, 0)),
|
||||
// ),
|
||||
// 1,
|
||||
// )?;
|
||||
|
||||
guard.insert("tty0".to_string(), tty.clone());
|
||||
// CharDevOps::register_chardev_region(DeviceNumber::new(Major::TTYAUX_MAJOR, 0), 1, "/dev/tty")?;
|
||||
|
||||
drop(guard);
|
||||
// 注册console设备
|
||||
// CharDevOps::cdev_add(
|
||||
// console.clone() as Arc<dyn CharDevice>,
|
||||
// IdTable::new(
|
||||
// String::from("console"),
|
||||
// Some(DeviceNumber::new(Major::TTYAUX_MAJOR, 1)),
|
||||
// ),
|
||||
// 1,
|
||||
// )?;
|
||||
|
||||
let r = devfs_register(&tty.name(), tty);
|
||||
if r.is_err() {
|
||||
return Err(devfs_root_inode.unwrap_err());
|
||||
}
|
||||
// CharDevOps::register_chardev_region(DeviceNumber::new(Major::TTYAUX_MAJOR, 1), 1, "/dev/tty")?;
|
||||
|
||||
// 将这两个设备注册到devfs,TODO:这里console设备应该与tty在一个设备group里面
|
||||
device_register(tty.clone())?;
|
||||
device_register(console.clone())?;
|
||||
devfs_register(tty.name, tty)?;
|
||||
devfs_register(console.name, console)?;
|
||||
|
||||
serial_init()?;
|
||||
return Ok(());
|
||||
return vty_init();
|
||||
}
|
||||
|
@ -1,61 +1,446 @@
|
||||
use core::fmt::Debug;
|
||||
use core::{fmt::Debug, sync::atomic::Ordering};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{string::String, sync::Arc, vec::Vec};
|
||||
use hashbrown::HashMap;
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::driver::base::device::driver::Driver;
|
||||
use crate::{
|
||||
driver::{
|
||||
base::{
|
||||
char::CharDevOps,
|
||||
device::{
|
||||
device_number::{DeviceNumber, Major},
|
||||
driver::Driver,
|
||||
},
|
||||
kobject::KObject,
|
||||
},
|
||||
tty::tty_port::TtyPortState,
|
||||
},
|
||||
libs::spinlock::SpinLock,
|
||||
};
|
||||
|
||||
use super::tty_device::TtyDevice;
|
||||
use super::{
|
||||
termios::Termios,
|
||||
tty_core::{TtyCore, TtyCoreData},
|
||||
tty_ldisc::TtyLdiscManager,
|
||||
tty_port::TTY_PORTS,
|
||||
virtual_terminal::virtual_console::CURRENT_VCNUM,
|
||||
};
|
||||
|
||||
/// TTY 驱动
|
||||
///
|
||||
///
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/tty_driver.h#434
|
||||
pub trait TtyDriver: Debug + Send + Sync + Driver {
|
||||
fn driver_name(&self) -> &str;
|
||||
fn dev_name(&self) -> &str;
|
||||
lazy_static! {
|
||||
static ref TTY_DRIVERS: SpinLock<Vec<Arc<TtyDriver>>> = SpinLock::new(Vec::new());
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &TtyDriverMetadata;
|
||||
pub struct TtyDriverManager;
|
||||
impl TtyDriverManager {
|
||||
pub fn lookup_tty_driver(dev_num: DeviceNumber) -> Option<(usize, Arc<TtyDriver>)> {
|
||||
let drivers_guard = TTY_DRIVERS.lock();
|
||||
for (index, driver) in drivers_guard.iter().enumerate() {
|
||||
let base = DeviceNumber::new(driver.major, driver.minor_start);
|
||||
if dev_num < base || dev_num.data() > base.data() + driver.device_count {
|
||||
continue;
|
||||
}
|
||||
return Some((index, driver.clone()));
|
||||
}
|
||||
|
||||
fn other(&self) -> Option<&Arc<dyn TtyDriver>>;
|
||||
|
||||
fn ttys(&self) -> &[Arc<TtyDevice>];
|
||||
|
||||
fn tty_ops(&self) -> Option<&'static dyn TtyDriverOperations> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
pub struct TtyDriverMetadata {
|
||||
/// name of the driver used in /proc/tty
|
||||
driver_name: &'static str,
|
||||
/// used for constructing /dev node name
|
||||
dev_name: &'static str,
|
||||
/// used as a number base for constructing /dev node name
|
||||
name_base: i32,
|
||||
/// major /dev device number (zero for autoassignment)
|
||||
major: i32,
|
||||
/// the first minor /dev device number
|
||||
minor_start: i32,
|
||||
drv_type: TtyDriverType,
|
||||
subtype: TtyDriverSubtype,
|
||||
}
|
||||
/// ## 注册驱动
|
||||
pub fn tty_register_driver(mut driver: TtyDriver) -> Result<(), SystemError> {
|
||||
// 查看是否注册设备号
|
||||
if driver.major == Major::UNNAMED_MAJOR {
|
||||
let dev_num = CharDevOps::alloc_chardev_region(
|
||||
driver.minor_start,
|
||||
driver.device_count,
|
||||
driver.name,
|
||||
)?;
|
||||
driver.major = dev_num.major();
|
||||
driver.minor_start = dev_num.minor();
|
||||
} else {
|
||||
let dev_num = DeviceNumber::new(driver.major, driver.minor_start);
|
||||
CharDevOps::register_chardev_region(dev_num, driver.device_count, driver.name)?;
|
||||
}
|
||||
|
||||
/// https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/tty_driver.h#411
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TtyDriverType {}
|
||||
driver.flags |= TtyDriverFlag::TTY_DRIVER_INSTALLED;
|
||||
|
||||
/// https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/tty_driver.h#412
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TtyDriverSubtype {}
|
||||
// 加入全局TtyDriver表
|
||||
TTY_DRIVERS.lock().push(Arc::new(driver));
|
||||
|
||||
bitflags! {
|
||||
/// https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/tty_driver.h?fi=SERIAL_TYPE_NORMAL#492
|
||||
pub struct TtyDriverFlags: u64 {
|
||||
// TODO: 加入procfs?
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/tty_driver.h#350
|
||||
pub trait TtyDriverOperations {}
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[cast_to([sync] Driver)]
|
||||
pub struct TtyDriver {
|
||||
/// /proc/tty中使用的驱动程序名称
|
||||
driver_name: String,
|
||||
/// 用于构造/dev节点名称,例如name设置为tty,则按照name_base分配节点tty0,tty1等
|
||||
name: &'static str,
|
||||
/// 命名基数
|
||||
name_base: usize,
|
||||
/// 主设备号
|
||||
major: Major,
|
||||
/// 起始次设备号
|
||||
minor_start: u32,
|
||||
/// 最多支持的tty数量
|
||||
device_count: u32,
|
||||
/// tty驱动程序类型
|
||||
tty_driver_type: TtyDriverType,
|
||||
/// 驱动程序子类型
|
||||
tty_driver_sub_type: TtyDriverSubType,
|
||||
/// 每个tty的默认termios
|
||||
init_termios: Termios,
|
||||
/// 懒加载termios,在tty设备关闭时,会将termios按照设备的index保存进这个集合,以便下次打开使用
|
||||
saved_termios: Vec<Termios>,
|
||||
/// 驱动程序标志
|
||||
flags: TtyDriverFlag,
|
||||
/// pty链接此driver的入口
|
||||
pty: Option<Arc<TtyDriver>>,
|
||||
/// 具体类型的tty驱动方法
|
||||
driver_funcs: Arc<dyn TtyOperation>,
|
||||
/// 管理的tty设备列表
|
||||
ttys: SpinLock<HashMap<usize, Arc<TtyCore>>>,
|
||||
// procfs入口?
|
||||
}
|
||||
|
||||
impl TtyDriver {
|
||||
pub fn new(
|
||||
count: u32,
|
||||
node_name: &'static str,
|
||||
node_name_base: usize,
|
||||
major: Major,
|
||||
minor_start: u32,
|
||||
tty_driver_type: TtyDriverType,
|
||||
default_termios: Termios,
|
||||
driver_funcs: Arc<dyn TtyOperation>,
|
||||
) -> Self {
|
||||
TtyDriver {
|
||||
driver_name: Default::default(),
|
||||
name: node_name,
|
||||
name_base: node_name_base,
|
||||
major,
|
||||
minor_start,
|
||||
device_count: count,
|
||||
tty_driver_type,
|
||||
tty_driver_sub_type: Default::default(),
|
||||
init_termios: default_termios,
|
||||
flags: TtyDriverFlag::empty(),
|
||||
pty: Default::default(),
|
||||
driver_funcs,
|
||||
ttys: SpinLock::new(HashMap::new()),
|
||||
saved_termios: Vec::with_capacity(count as usize),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tty_line_name(&self, index: usize) -> String {
|
||||
if self
|
||||
.flags
|
||||
.contains(TtyDriverFlag::TTY_DRIVER_UNNUMBERED_NODE)
|
||||
{
|
||||
return format!("{}", self.name);
|
||||
} else {
|
||||
return format!("{}{}", self.name, index + self.name_base);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tty(&self, tty_core: Arc<TtyCore>) {
|
||||
self.ttys.lock().insert(tty_core.core().index(), tty_core);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn driver_funcs(&self) -> Arc<dyn TtyOperation> {
|
||||
self.driver_funcs.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> TtyDriverFlag {
|
||||
self.flags
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lockup_tty(&self, index: usize) -> Option<Arc<TtyCore>> {
|
||||
let device_guard = self.ttys.lock();
|
||||
return match device_guard.get(&index) {
|
||||
Some(tty) => Some(tty.clone()),
|
||||
None => None,
|
||||
};
|
||||
}
|
||||
|
||||
fn standard_install(&self, tty_core: Arc<TtyCore>) -> Result<(), SystemError> {
|
||||
let tty = tty_core.core();
|
||||
let tty_index = tty.index();
|
||||
// 初始化termios
|
||||
if !self.flags.contains(TtyDriverFlag::TTY_DRIVER_RESET_TERMIOS) {
|
||||
// 先查看是否有已经保存的termios
|
||||
if let Some(t) = self.saved_termios.get(tty_index) {
|
||||
let mut termios = t.clone();
|
||||
termios.line = self.init_termios.line;
|
||||
tty.set_termios(termios);
|
||||
}
|
||||
}
|
||||
// TODO:设置termios波特率?
|
||||
|
||||
tty.add_count();
|
||||
|
||||
self.ttys.lock().insert(tty_index, tty_core);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn driver_install_tty(driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError> {
|
||||
let res = tty.install(driver.clone(), tty.clone());
|
||||
|
||||
if res.is_err() {
|
||||
let err = res.unwrap_err();
|
||||
if err == SystemError::ENOSYS {
|
||||
return driver.standard_install(tty);
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
driver.add_tty(tty);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_tty_device(driver: Arc<TtyDriver>, index: usize) -> Result<Arc<TtyCore>, SystemError> {
|
||||
let tty = TtyCore::new(driver.clone(), index);
|
||||
|
||||
Self::driver_install_tty(driver.clone(), tty.clone())?;
|
||||
|
||||
let core = tty.core();
|
||||
|
||||
if core.port().is_none() {
|
||||
TTY_PORTS[core.index()].setup_tty(Arc::downgrade(&tty));
|
||||
tty.set_port(TTY_PORTS[core.index()].clone());
|
||||
}
|
||||
|
||||
TtyLdiscManager::ldisc_setup(tty.clone(), None)?;
|
||||
|
||||
Ok(tty)
|
||||
}
|
||||
|
||||
/// ## 通过设备号找到对应驱动并且初始化Tty
|
||||
pub fn open_tty(dev_num: DeviceNumber) -> Result<Arc<TtyCore>, SystemError> {
|
||||
let (index, driver) =
|
||||
TtyDriverManager::lookup_tty_driver(dev_num).ok_or(SystemError::ENODEV)?;
|
||||
|
||||
let tty = match driver.lockup_tty(index) {
|
||||
Some(tty) => {
|
||||
// TODO: 暂时这么写,因为还没写TtyPort
|
||||
if tty.core().port().is_none() {
|
||||
kwarn!("{} port is None", tty.core().name());
|
||||
} else {
|
||||
if tty.core().port().unwrap().state() == TtyPortState::KOPENED {
|
||||
return Err(SystemError::EBUSY);
|
||||
}
|
||||
}
|
||||
|
||||
tty.reopen()?;
|
||||
tty
|
||||
}
|
||||
None => Self::init_tty_device(driver, index)?,
|
||||
};
|
||||
|
||||
CURRENT_VCNUM.store(index as isize, Ordering::SeqCst);
|
||||
|
||||
return Ok(tty);
|
||||
}
|
||||
|
||||
pub fn tty_driver_type(&self) -> TtyDriverType {
|
||||
self.tty_driver_type
|
||||
}
|
||||
|
||||
pub fn tty_driver_sub_type(&self) -> TtyDriverSubType {
|
||||
self.tty_driver_sub_type
|
||||
}
|
||||
|
||||
pub fn init_termios(&self) -> Termios {
|
||||
self.init_termios
|
||||
}
|
||||
}
|
||||
|
||||
impl KObject for TtyDriver {
|
||||
fn as_any_ref(&self) -> &dyn core::any::Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_inode(&self, _inode: Option<alloc::sync::Arc<crate::filesystem::kernfs::KernFSInode>>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn inode(&self) -> Option<alloc::sync::Arc<crate::filesystem::kernfs::KernFSInode>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<alloc::sync::Weak<dyn KObject>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_parent(&self, _parent: Option<alloc::sync::Weak<dyn KObject>>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn kset(&self) -> Option<alloc::sync::Arc<crate::driver::base::kset::KSet>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_kset(&self, _kset: Option<alloc::sync::Arc<crate::driver::base::kset::KSet>>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn kobj_type(&self) -> Option<&'static dyn crate::driver::base::kobject::KObjType> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_kobj_type(&self, _ktype: Option<&'static dyn crate::driver::base::kobject::KObjType>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn name(&self) -> alloc::string::String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_name(&self, _name: alloc::string::String) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn kobj_state(
|
||||
&self,
|
||||
) -> crate::libs::rwlock::RwLockReadGuard<crate::driver::base::kobject::KObjectState> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn kobj_state_mut(
|
||||
&self,
|
||||
) -> crate::libs::rwlock::RwLockWriteGuard<crate::driver::base::kobject::KObjectState> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_kobj_state(&self, _state: crate::driver::base::kobject::KObjectState) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for TtyDriver {
|
||||
fn id_table(&self) -> Option<crate::driver::base::device::IdTable> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn devices(
|
||||
&self,
|
||||
) -> alloc::vec::Vec<alloc::sync::Arc<dyn crate::driver::base::device::Device>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add_device(&self, _device: alloc::sync::Arc<dyn crate::driver::base::device::Device>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn delete_device(&self, _device: &alloc::sync::Arc<dyn crate::driver::base::device::Device>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_bus(&self, _bus: Option<alloc::sync::Weak<dyn crate::driver::base::device::bus::Bus>>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TtyOperation: Sync + Send + Debug {
|
||||
fn install(&self, _driver: Arc<TtyDriver>, _tty: Arc<TtyCore>) -> Result<(), SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
|
||||
fn open(&self, tty: &TtyCoreData) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 获取可写字符数
|
||||
fn write_room(&self, _tty: &TtyCoreData) -> usize {
|
||||
// 默认
|
||||
2048
|
||||
}
|
||||
|
||||
fn write(&self, tty: &TtyCoreData, buf: &[u8], nr: usize) -> Result<usize, SystemError>;
|
||||
|
||||
fn flush_chars(&self, tty: &TtyCoreData);
|
||||
|
||||
fn put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError>;
|
||||
|
||||
fn start(&self, _tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
fn stop(&self, _tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
fn flush_buffer(&self, _tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
fn ioctl(&self, tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<(), SystemError>;
|
||||
|
||||
fn chars_in_buffer(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn set_termios(&self, _tty: Arc<TtyCore>, _old_termios: Termios) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum TtyDriverType {
|
||||
System,
|
||||
Console,
|
||||
Serial,
|
||||
Pty,
|
||||
Scc,
|
||||
Syscons,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum TtyDriverSubType {
|
||||
Undefined,
|
||||
Tty,
|
||||
Console,
|
||||
Syscons,
|
||||
Sysptmx,
|
||||
PtyMaster,
|
||||
PtySlave,
|
||||
SerialNormal,
|
||||
}
|
||||
|
||||
impl Default for TtyDriverSubType {
|
||||
fn default() -> Self {
|
||||
Self::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct TtyDriverFlag: u32 {
|
||||
/// 表示 tty 驱动程序已安装
|
||||
const TTY_DRIVER_INSTALLED = 0x0001;
|
||||
/// 请求 tty 层在最后一个进程关闭设备时重置 termios 设置
|
||||
const TTY_DRIVER_RESET_TERMIOS = 0x0002;
|
||||
/// 表示驱动程序将保证在设置了该标志的 tty 上不设置任何特殊字符处理标志(原模式)
|
||||
const TTY_DRIVER_REAL_RAW = 0x0004;
|
||||
|
||||
/// 以下四个标志位为内存分配相关,目前设计无需使用
|
||||
const TTY_DRIVER_DYNAMIC_DEV = 0x0008;
|
||||
const TTY_DRIVER_DEVPTS_MEM = 0x0010;
|
||||
const TTY_DRIVER_HARDWARE_BREAK = 0x0020;
|
||||
const TTY_DRIVER_DYNAMIC_ALLOC = 0x0040;
|
||||
|
||||
/// 表示不创建带有编号的 /dev 节点。
|
||||
/// 例如,创建 /dev/ttyprintk 而不是 /dev/ttyprintk0。仅在为单个 tty 设备分配驱动程序时适用。
|
||||
const TTY_DRIVER_UNNUMBERED_NODE = 0x0080;
|
||||
}
|
||||
}
|
||||
|
139
kernel/src/driver/tty/tty_job_control.rs
Normal file
139
kernel/src/driver/tty/tty_job_control.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use alloc::sync::Arc;
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::ipc::signal::{SigSet, Signal},
|
||||
mm::VirtAddr,
|
||||
process::{Pid, ProcessManager},
|
||||
syscall::{user_access::UserBufferWriter, Syscall},
|
||||
};
|
||||
|
||||
use super::tty_core::{TtyCore, TtyIoctlCmd};
|
||||
|
||||
pub struct TtyJobCtrlManager;
|
||||
|
||||
impl TtyJobCtrlManager {
|
||||
/// ### 设置当前进程的tty
|
||||
pub fn proc_set_tty(tty: Arc<TtyCore>) {
|
||||
let core = tty.core();
|
||||
let mut ctrl = core.contorl_info_irqsave();
|
||||
let pcb = ProcessManager::current_pcb();
|
||||
|
||||
// todo 目前将pgid设置为pid
|
||||
ctrl.pgid = Some(pcb.pid());
|
||||
ctrl.session = Some(pcb.pid());
|
||||
|
||||
assert!(pcb.sig_info_irqsave().tty().is_none());
|
||||
|
||||
let mut singal = pcb.sig_info_mut();
|
||||
drop(ctrl);
|
||||
singal.set_tty(tty);
|
||||
}
|
||||
|
||||
/// ### 检查tty
|
||||
pub fn tty_check_change(tty: Arc<TtyCore>, sig: Signal) -> Result<(), SystemError> {
|
||||
let pcb = ProcessManager::current_pcb();
|
||||
|
||||
if pcb.sig_info().tty().is_none() || !Arc::ptr_eq(&pcb.sig_info().tty().unwrap(), &tty) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let core = tty.core();
|
||||
let ctrl = core.contorl_info_irqsave();
|
||||
|
||||
// todo pgid
|
||||
let pgid = pcb.pid();
|
||||
let tty_pgid = ctrl.pgid;
|
||||
|
||||
if tty_pgid.is_some() && tty_pgid.unwrap() != pgid {
|
||||
if pcb
|
||||
.sig_info_irqsave()
|
||||
.sig_block()
|
||||
.contains(SigSet::from_bits_truncate(1 << sig as u64))
|
||||
|| pcb.sig_struct_irqsave().handlers[sig as usize].is_ignore()
|
||||
{
|
||||
// 忽略该信号
|
||||
if sig == Signal::SIGTTIN {
|
||||
return Err(SystemError::EIO);
|
||||
}
|
||||
} else {
|
||||
// 暂时使用kill而不是killpg
|
||||
Syscall::kill(pgid, sig as i32)?;
|
||||
return Err(SystemError::ERESTART);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn job_ctrl_ioctl(tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<usize, SystemError> {
|
||||
match cmd {
|
||||
TtyIoctlCmd::TIOCSPGRP => {
|
||||
match Self::tty_check_change(tty.clone(), Signal::SIGTTOU) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
if e == SystemError::EIO {
|
||||
return Err(SystemError::ENOTTY);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
// let user_reader = UserBufferReader::new(
|
||||
// VirtAddr::new(arg).as_ptr::<usize>(),
|
||||
// core::mem::size_of::<usize>(),
|
||||
// true,
|
||||
// )?;
|
||||
|
||||
// let pgrp = user_reader.read_one_from_user::<usize>(0)?;
|
||||
|
||||
let current = ProcessManager::current_pcb();
|
||||
|
||||
let mut ctrl = tty.core().contorl_info_irqsave();
|
||||
|
||||
if current.sig_info().tty().is_none()
|
||||
|| !Arc::ptr_eq(¤t.sig_info().tty().clone().unwrap(), &tty)
|
||||
|| ctrl.session.is_none()
|
||||
|| ctrl.session.unwrap() != current.pid()
|
||||
{
|
||||
return Err(SystemError::ENOTTY);
|
||||
}
|
||||
|
||||
ctrl.pgid = Some(Pid::new(arg));
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
TtyIoctlCmd::TIOCGPGRP => {
|
||||
let current = ProcessManager::current_pcb();
|
||||
if current.sig_info().tty().is_some()
|
||||
&& !Arc::ptr_eq(¤t.sig_info().tty().unwrap(), &tty)
|
||||
{
|
||||
return Err(SystemError::ENOTTY);
|
||||
}
|
||||
|
||||
let mut user_writer = UserBufferWriter::new(
|
||||
VirtAddr::new(arg).as_ptr::<i32>(),
|
||||
core::mem::size_of::<i32>(),
|
||||
true,
|
||||
)?;
|
||||
|
||||
user_writer.copy_one_to_user(
|
||||
&(tty
|
||||
.core()
|
||||
.contorl_info_irqsave()
|
||||
.pgid
|
||||
.unwrap_or(Pid::new(0))
|
||||
.data() as i32),
|
||||
0,
|
||||
)?;
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(SystemError::ENOIOCTLCMD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
kernel/src/driver/tty/tty_ldisc/mod.rs
Normal file
117
kernel/src/driver/tty/tty_ldisc/mod.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use core::fmt::Debug;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::filesystem::vfs::file::FileMode;
|
||||
|
||||
use super::{
|
||||
termios::Termios,
|
||||
tty_core::{TtyCore, TtyCoreData},
|
||||
};
|
||||
|
||||
pub mod ntty;
|
||||
|
||||
pub trait TtyLineDiscipline: Sync + Send + Debug {
|
||||
fn open(&self, tty: Arc<TtyCore>) -> Result<(), SystemError>;
|
||||
fn close(&self, tty: Arc<TtyCore>) -> Result<(), SystemError>;
|
||||
fn flush_buffer(&self, tty: Arc<TtyCore>) -> Result<(), SystemError>;
|
||||
|
||||
/// ## tty行规程循环读取函数
|
||||
///
|
||||
/// ### 参数
|
||||
/// - tty: 操作的tty
|
||||
/// - buf: 数据将被读取到buf
|
||||
/// - len: 读取的字节长度
|
||||
/// - cookie: 表示是否是继续上次的读,第一次读取应该传入false
|
||||
/// - offset: 读取的偏移量
|
||||
fn read(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
buf: &mut [u8],
|
||||
len: usize,
|
||||
cookie: &mut bool,
|
||||
offset: usize,
|
||||
mode: FileMode,
|
||||
) -> Result<usize, SystemError>;
|
||||
fn write(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
buf: &[u8],
|
||||
len: usize,
|
||||
mode: FileMode,
|
||||
) -> Result<usize, SystemError>;
|
||||
fn ioctl(&self, tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<usize, SystemError>;
|
||||
|
||||
/// ### 设置termios后更新行规程状态
|
||||
///
|
||||
/// - old: 之前的termios,如果为None则表示第一次设置
|
||||
fn set_termios(&self, tty: Arc<TtyCore>, old: Option<Termios>) -> Result<(), SystemError>;
|
||||
|
||||
fn poll(&self, tty: Arc<TtyCore>) -> Result<(), SystemError>;
|
||||
fn hangup(&self, tty: Arc<TtyCore>) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 接收数据
|
||||
fn receive_buf(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
buf: &[u8],
|
||||
flags: Option<&[u8]>,
|
||||
count: usize,
|
||||
) -> Result<usize, SystemError>;
|
||||
|
||||
/// ## 接收数据
|
||||
fn receive_buf2(
|
||||
&self,
|
||||
tty: Arc<TtyCore>,
|
||||
buf: &[u8],
|
||||
flags: Option<&[u8]>,
|
||||
count: usize,
|
||||
) -> Result<usize, SystemError>;
|
||||
|
||||
/// ## 唤醒线路写者
|
||||
fn write_wakeup(&self, _tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LineDisciplineType {
|
||||
NTty = 0,
|
||||
}
|
||||
|
||||
impl LineDisciplineType {
|
||||
pub fn from_line(line: u8) -> Self {
|
||||
match line {
|
||||
0 => Self::NTty,
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TtyLdiscManager;
|
||||
|
||||
impl TtyLdiscManager {
|
||||
/// ## 为tty初始化ldisc
|
||||
///
|
||||
/// ### 参数
|
||||
/// - tty:需要设置的tty
|
||||
/// - o_tty: other tty 用于pty pair
|
||||
pub fn ldisc_setup(tty: Arc<TtyCore>, _o_tty: Option<Arc<TtyCore>>) -> Result<(), SystemError> {
|
||||
let ld = tty.ldisc();
|
||||
|
||||
let ret = ld.open(tty);
|
||||
if ret.is_err() {
|
||||
let err = ret.unwrap_err();
|
||||
if err == SystemError::ENOSYS {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 处理PTY
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
2016
kernel/src/driver/tty/tty_ldisc/ntty.rs
Normal file
2016
kernel/src/driver/tty/tty_ldisc/ntty.rs
Normal file
File diff suppressed because it is too large
Load Diff
113
kernel/src/driver/tty/tty_port.rs
Normal file
113
kernel/src/driver/tty/tty_port.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use core::fmt::Debug;
|
||||
|
||||
use alloc::{
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use kdepends::thingbuf::mpsc;
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::tty::virtual_terminal::MAX_NR_CONSOLES,
|
||||
libs::spinlock::{SpinLock, SpinLockGuard},
|
||||
};
|
||||
|
||||
use super::tty_core::TtyCore;
|
||||
|
||||
const TTY_PORT_BUFSIZE: usize = 4096;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref TTY_PORTS: Vec<Arc<dyn TtyPort>> = {
|
||||
let mut v: Vec<Arc<dyn TtyPort>> = Vec::with_capacity(MAX_NR_CONSOLES as usize);
|
||||
for _ in 0..MAX_NR_CONSOLES as usize {
|
||||
v.push(Arc::new(DefaultTtyPort::new()))
|
||||
}
|
||||
|
||||
v
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct TtyPortData {
|
||||
flags: i32,
|
||||
iflags: TtyPortState,
|
||||
sender: mpsc::Sender<u8>,
|
||||
receiver: mpsc::Receiver<u8>,
|
||||
tty: Weak<TtyCore>,
|
||||
}
|
||||
|
||||
impl TtyPortData {
|
||||
pub fn new() -> Self {
|
||||
let (sender, receiver) = mpsc::channel::<u8>(TTY_PORT_BUFSIZE);
|
||||
Self {
|
||||
flags: 0,
|
||||
iflags: TtyPortState::Initialized,
|
||||
sender,
|
||||
receiver,
|
||||
tty: Weak::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tty(&self) -> Option<Arc<TtyCore>> {
|
||||
self.tty.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum TtyPortState {
|
||||
Initialized,
|
||||
Suspended,
|
||||
Active,
|
||||
CtsFlow,
|
||||
CheckCD,
|
||||
KOPENED,
|
||||
}
|
||||
|
||||
pub trait TtyPort: Sync + Send + Debug {
|
||||
fn port_data(&self) -> SpinLockGuard<TtyPortData>;
|
||||
|
||||
/// 获取Port的状态
|
||||
fn state(&self) -> TtyPortState {
|
||||
self.port_data().iflags
|
||||
}
|
||||
|
||||
/// 为port设置tty
|
||||
fn setup_tty(&self, tty: Weak<TtyCore>) {
|
||||
self.port_data().tty = tty;
|
||||
}
|
||||
|
||||
/// 作为客户端的tty ports接收数据
|
||||
fn receive_buf(&self, buf: &[u8], _flags: &[u8], count: usize) -> Result<usize, SystemError> {
|
||||
let tty = self.port_data().tty.upgrade().unwrap();
|
||||
|
||||
let ld = tty.ldisc();
|
||||
|
||||
let ret = ld.receive_buf2(tty.clone(), buf, None, count);
|
||||
if ret.is_err() && ret.clone().unwrap_err() == SystemError::ENOSYS {
|
||||
return ld.receive_buf(tty, buf, None, count);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultTtyPort {
|
||||
port_data: SpinLock<TtyPortData>,
|
||||
}
|
||||
|
||||
impl DefaultTtyPort {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
port_data: SpinLock::new(TtyPortData::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyPort for DefaultTtyPort {
|
||||
fn port_data(&self) -> SpinLockGuard<TtyPortData> {
|
||||
self.port_data.lock_irqsave()
|
||||
}
|
||||
}
|
135
kernel/src/driver/tty/virtual_terminal/console_map.rs
Normal file
135
kernel/src/driver/tty/virtual_terminal/console_map.rs
Normal file
@ -0,0 +1,135 @@
|
||||
/// Latin-1 字符集到 Unicode 的映射表。在这个表格中,每个位置都存储了相应 Latin-1 字符的 Unicode 编码。
|
||||
pub const LAT1_MAP: &'static [u16] = &[
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
|
||||
0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
|
||||
0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b,
|
||||
0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053,
|
||||
0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
|
||||
0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083,
|
||||
0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
|
||||
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b,
|
||||
0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
|
||||
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3,
|
||||
0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
||||
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb,
|
||||
0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
|
||||
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3,
|
||||
0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
|
||||
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb,
|
||||
0x00fc, 0x00fd, 0x00fe, 0x00ff,
|
||||
];
|
||||
|
||||
/// 将 VT100 图形字符映射到 Unicode 的映射表
|
||||
pub const GRAF_MAP: &'static [u16] = &[
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
|
||||
0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
|
||||
0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,
|
||||
0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b,
|
||||
0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053,
|
||||
0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
|
||||
0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2591, 0x240b, 0x2518, 0x2510,
|
||||
0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
|
||||
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083,
|
||||
0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
|
||||
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b,
|
||||
0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
|
||||
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3,
|
||||
0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
||||
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb,
|
||||
0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
|
||||
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3,
|
||||
0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
|
||||
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb,
|
||||
0x00fc, 0x00fd, 0x00fe, 0x00ff,
|
||||
];
|
||||
|
||||
/// 将 IBM Codepage 437 字符集映射到 Unicode 的映射表
|
||||
pub const IBMPC_MAP: &'static [u16] = &[
|
||||
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642,
|
||||
0x2640, 0x266a, 0x266b, 0x263c, 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
|
||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, 0x0020, 0x0021, 0x0022, 0x0023,
|
||||
0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b,
|
||||
0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053,
|
||||
0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
|
||||
0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, 0x00c7, 0x00fc, 0x00e9, 0x00e2,
|
||||
0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2,
|
||||
0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502,
|
||||
0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566,
|
||||
0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0,
|
||||
0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a,
|
||||
0x207f, 0x00b2, 0x25a0, 0x00a0,
|
||||
];
|
||||
|
||||
/// 默认直接映射表
|
||||
pub const USER_MAP: &'static [u16] = &[
|
||||
0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007, 0xf008, 0xf009, 0xf00a, 0xf00b,
|
||||
0xf00c, 0xf00d, 0xf00e, 0xf00f, 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
|
||||
0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf020, 0xf021, 0xf022, 0xf023,
|
||||
0xf024, 0xf025, 0xf026, 0xf027, 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f,
|
||||
0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, 0xf039, 0xf03a, 0xf03b,
|
||||
0xf03c, 0xf03d, 0xf03e, 0xf03f, 0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047,
|
||||
0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f, 0xf050, 0xf051, 0xf052, 0xf053,
|
||||
0xf054, 0xf055, 0xf056, 0xf057, 0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f,
|
||||
0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067, 0xf068, 0xf069, 0xf06a, 0xf06b,
|
||||
0xf06c, 0xf06d, 0xf06e, 0xf06f, 0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077,
|
||||
0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f, 0xf080, 0xf081, 0xf082, 0xf083,
|
||||
0xf084, 0xf085, 0xf086, 0xf087, 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f,
|
||||
0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097, 0xf098, 0xf099, 0xf09a, 0xf09b,
|
||||
0xf09c, 0xf09d, 0xf09e, 0xf09f, 0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7,
|
||||
0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af, 0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3,
|
||||
0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7, 0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf,
|
||||
0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7, 0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb,
|
||||
0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf, 0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7,
|
||||
0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df, 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3,
|
||||
0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7, 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef,
|
||||
0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7, 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb,
|
||||
0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff,
|
||||
];
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TranslationMapType {
|
||||
Lat1Map,
|
||||
GrafMap,
|
||||
IbmpcMap,
|
||||
UserMap,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TranslationMap {
|
||||
pub map_type: TranslationMapType,
|
||||
pub map: &'static [u16],
|
||||
}
|
||||
|
||||
impl TranslationMap {
|
||||
pub const fn new(map_type: TranslationMapType) -> Self {
|
||||
let map = match map_type {
|
||||
TranslationMapType::Lat1Map => LAT1_MAP,
|
||||
TranslationMapType::GrafMap => GRAF_MAP,
|
||||
TranslationMapType::IbmpcMap => IBMPC_MAP,
|
||||
TranslationMapType::UserMap => USER_MAP,
|
||||
};
|
||||
|
||||
Self { map_type, map }
|
||||
}
|
||||
|
||||
pub fn translate(&self, c: u32) -> u16 {
|
||||
self.map[c as usize]
|
||||
}
|
||||
}
|
303
kernel/src/driver/tty/virtual_terminal/mod.rs
Normal file
303
kernel/src/driver/tty/virtual_terminal/mod.rs
Normal file
@ -0,0 +1,303 @@
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use alloc::{string::String, sync::Arc, vec::Vec};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
base::device::{
|
||||
device_number::{DeviceNumber, Major},
|
||||
device_register, IdTable,
|
||||
},
|
||||
video::fbdev::base::fbcon::framebuffer_console::BlittingFbConsole,
|
||||
},
|
||||
filesystem::devfs::devfs_register,
|
||||
libs::spinlock::SpinLock,
|
||||
};
|
||||
|
||||
use self::virtual_console::{VirtualConsoleData, CURRENT_VCNUM};
|
||||
|
||||
use super::{
|
||||
console::ConsoleSwitch,
|
||||
termios::{InputMode, TTY_STD_TERMIOS},
|
||||
tty_core::{TtyCore, TtyCoreData},
|
||||
tty_device::TtyDevice,
|
||||
tty_driver::{TtyDriver, TtyDriverManager, TtyDriverType, TtyOperation},
|
||||
};
|
||||
|
||||
pub mod console_map;
|
||||
pub mod virtual_console;
|
||||
|
||||
pub const MAX_NR_CONSOLES: u32 = 63;
|
||||
pub const VC_MAXCOL: usize = 32767;
|
||||
pub const VC_MAXROW: usize = 32767;
|
||||
|
||||
pub const DEFAULT_RED: [u16; 16] = [
|
||||
0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff,
|
||||
];
|
||||
|
||||
pub const DEFAULT_GREEN: [u16; 16] = [
|
||||
0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa, 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff,
|
||||
];
|
||||
|
||||
pub const DEFAULT_BLUE: [u16; 16] = [
|
||||
0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff,
|
||||
];
|
||||
|
||||
pub const COLOR_TABLE: &'static [u8] = &[0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15];
|
||||
|
||||
lazy_static! {
|
||||
pub static ref VIRT_CONSOLES: Vec<Arc<SpinLock<VirtualConsoleData>>> = {
|
||||
let mut v = Vec::with_capacity(MAX_NR_CONSOLES as usize);
|
||||
for i in 0..MAX_NR_CONSOLES as usize {
|
||||
v.push(Arc::new(SpinLock::new(VirtualConsoleData::new(i))));
|
||||
}
|
||||
|
||||
v
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Color {
|
||||
pub red: u16,
|
||||
pub green: u16,
|
||||
pub blue: u16,
|
||||
pub transp: u16,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn from_256(col: u32) -> Self {
|
||||
let mut color = Self::default();
|
||||
if col < 8 {
|
||||
color.red = if col & 1 != 0 { 0xaa } else { 0x00 };
|
||||
color.green = if col & 2 != 0 { 0xaa } else { 0x00 };
|
||||
color.blue = if col & 4 != 0 { 0xaa } else { 0x00 };
|
||||
} else if col < 16 {
|
||||
color.red = if col & 1 != 0 { 0xff } else { 0x55 };
|
||||
color.green = if col & 2 != 0 { 0xff } else { 0x55 };
|
||||
color.blue = if col & 4 != 0 { 0xff } else { 0x55 };
|
||||
} else if col < 232 {
|
||||
color.red = ((col - 16) / 36 * 85 / 2) as u16;
|
||||
color.green = ((col - 16) / 6 % 6 * 85 / 2) as u16;
|
||||
color.blue = ((col - 16) % 6 * 85 / 2) as u16;
|
||||
} else {
|
||||
let col = (col * 10 - 2312) as u16;
|
||||
color.red = col;
|
||||
color.green = col;
|
||||
color.blue = col;
|
||||
}
|
||||
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TtyConsoleDriverInner {
|
||||
console: Arc<BlittingFbConsole>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for TtyConsoleDriverInner {}
|
||||
|
||||
impl TtyConsoleDriverInner {
|
||||
pub fn new() -> Result<Self, SystemError> {
|
||||
Ok(Self {
|
||||
console: Arc::new(BlittingFbConsole::new()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyOperation for TtyConsoleDriverInner {
|
||||
fn install(&self, _driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError> {
|
||||
let tty_core = tty.core();
|
||||
let mut vc_data = VIRT_CONSOLES[tty_core.index()].lock();
|
||||
|
||||
self.console.con_init(&mut vc_data, true)?;
|
||||
if vc_data.complement_mask == 0 {
|
||||
vc_data.complement_mask = if vc_data.color_mode { 0x7700 } else { 0x0800 };
|
||||
}
|
||||
vc_data.s_complement_mask = vc_data.complement_mask;
|
||||
// vc_data.bytes_per_row = vc_data.cols << 1;
|
||||
vc_data.index = tty_core.index();
|
||||
vc_data.bottom = vc_data.rows;
|
||||
vc_data.set_driver_funcs(Arc::downgrade(
|
||||
&(self.console.clone() as Arc<dyn ConsoleSwitch>),
|
||||
));
|
||||
|
||||
// todo: unicode字符集处理?
|
||||
|
||||
if vc_data.cols > VC_MAXCOL || vc_data.rows > VC_MAXROW {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
vc_data.init(None, None, true);
|
||||
vc_data.update_attr();
|
||||
|
||||
let window_size = tty_core.window_size_upgradeable();
|
||||
if window_size.col == 0 && window_size.row == 0 {
|
||||
let mut window_size = window_size.upgrade();
|
||||
window_size.col = vc_data.cols as u16;
|
||||
window_size.row = vc_data.rows as u16;
|
||||
}
|
||||
|
||||
if vc_data.utf {
|
||||
tty_core.termios_write().input_mode.insert(InputMode::IUTF8);
|
||||
} else {
|
||||
tty_core.termios_write().input_mode.remove(InputMode::IUTF8);
|
||||
}
|
||||
|
||||
// 加入sysfs?
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open(&self, _tty: &TtyCoreData) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_room(&self, _tty: &TtyCoreData) -> usize {
|
||||
32768
|
||||
}
|
||||
|
||||
/// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/tty/vt/vt.c#2894
|
||||
fn write(&self, tty: &TtyCoreData, buf: &[u8], mut nr: usize) -> Result<usize, SystemError> {
|
||||
// 关闭中断
|
||||
let mut vc_data = tty.vc_data_irqsave();
|
||||
|
||||
let mut offset = 0;
|
||||
|
||||
// 这个参数是用来扫描unicode字符的,但是这部分目前未完成,先写着
|
||||
let mut rescan = false;
|
||||
let mut ch: u32 = 0;
|
||||
|
||||
let mut draw = DrawRegion::default();
|
||||
|
||||
// 首先隐藏光标再写
|
||||
vc_data.hide_cursor();
|
||||
|
||||
while nr != 0 {
|
||||
if !rescan {
|
||||
ch = buf[offset] as u32;
|
||||
offset += 1;
|
||||
nr -= 1;
|
||||
}
|
||||
|
||||
let (tc, rescan_last) = vc_data.translate(&mut ch);
|
||||
if tc.is_none() {
|
||||
// 表示未转换完成
|
||||
continue;
|
||||
}
|
||||
|
||||
let tc = tc.unwrap();
|
||||
rescan = rescan_last;
|
||||
|
||||
if vc_data.is_control(tc, ch) {
|
||||
vc_data.flush(&mut draw);
|
||||
vc_data.do_control(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
if !vc_data.console_write_normal(tc, ch, &mut draw) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
vc_data.flush(&mut draw);
|
||||
|
||||
// TODO: notify update
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
fn flush_chars(&self, tty: &TtyCoreData) {
|
||||
let mut vc_data = tty.vc_data_irqsave();
|
||||
vc_data.set_cursor();
|
||||
}
|
||||
|
||||
fn put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError> {
|
||||
self.write(tty, &[ch], 1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ioctl(&self, _tty: Arc<TtyCore>, _cmd: u32, _arg: usize) -> Result<(), SystemError> {
|
||||
// TODO
|
||||
Err(SystemError::ENOIOCTLCMD)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VtModeData {
|
||||
mode: VtMode,
|
||||
/// 释放请求时触发的信号
|
||||
relsig: u16,
|
||||
/// 获取请求时触发的信号
|
||||
acqsig: u16,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VtMode {
|
||||
/// 自动切换模式,即在请求输入时自动切换到终端
|
||||
Auto,
|
||||
/// 手动切换模式,需要通过 ioctl 请求切换到终端
|
||||
Process,
|
||||
/// 等待终端确认,即在切换到终端时等待终端的确认信号
|
||||
Ackacq,
|
||||
}
|
||||
|
||||
/// 用于给vc确定要写入的buf位置
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DrawRegion {
|
||||
/// 偏移量
|
||||
pub offset: usize,
|
||||
/// 写入数量
|
||||
pub size: usize,
|
||||
pub x: Option<u32>,
|
||||
}
|
||||
|
||||
// 初始化虚拟终端
|
||||
#[inline(never)]
|
||||
pub fn vty_init() -> Result<(), SystemError> {
|
||||
// 注册虚拟终端设备并将虚拟终端设备加入到文件系统
|
||||
let vc0 = TtyDevice::new(
|
||||
"vc0",
|
||||
IdTable::new(
|
||||
String::from("vc0"),
|
||||
Some(DeviceNumber::new(Major::TTY_MAJOR, 0)),
|
||||
),
|
||||
);
|
||||
// 注册tty设备
|
||||
// CharDevOps::cdev_add(
|
||||
// vc0.clone() as Arc<dyn CharDevice>,
|
||||
// IdTable::new(
|
||||
// String::from("vc0"),
|
||||
// Some(DeviceNumber::new(Major::TTY_MAJOR, 0)),
|
||||
// ),
|
||||
// 1,
|
||||
// )?;
|
||||
|
||||
// CharDevOps::register_chardev_region(DeviceNumber::new(Major::TTY_MAJOR, 0), 1, "/dev/vc/0")?;
|
||||
device_register(vc0.clone())?;
|
||||
devfs_register("vc0", vc0)?;
|
||||
|
||||
// vcs_init?
|
||||
|
||||
let console_driver = TtyDriver::new(
|
||||
MAX_NR_CONSOLES,
|
||||
"tty",
|
||||
1,
|
||||
Major::TTY_MAJOR,
|
||||
0,
|
||||
TtyDriverType::Console,
|
||||
TTY_STD_TERMIOS.clone(),
|
||||
Arc::new(TtyConsoleDriverInner::new()?),
|
||||
);
|
||||
|
||||
TtyDriverManager::tty_register_driver(console_driver)?;
|
||||
|
||||
CURRENT_VCNUM.store(0, Ordering::SeqCst);
|
||||
|
||||
// 初始化键盘?
|
||||
|
||||
// TODO: 为vc
|
||||
|
||||
Ok(())
|
||||
}
|
2015
kernel/src/driver/tty/virtual_terminal/virtual_console.rs
Normal file
2015
kernel/src/driver/tty/virtual_terminal/virtual_console.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
||||
//! Virtual terminal driver.
|
||||
//!
|
||||
//! (TODO) This driver is not implemented yet.
|
||||
|
||||
/// The minimum number of virtual terminals.
|
||||
#[allow(dead_code)]
|
||||
pub const MIN_NR_CONSOLES: usize = 1;
|
||||
/// The maximum number of virtual terminals.
|
||||
#[allow(dead_code)]
|
||||
pub const MAX_NR_CONSOLES: usize = 63;
|
1
kernel/src/driver/video/fbdev/base/fbcmap.rs
Normal file
1
kernel/src/driver/video/fbdev/base/fbcmap.rs
Normal file
@ -0,0 +1 @@
|
||||
|
787
kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs
Normal file
787
kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs
Normal file
@ -0,0 +1,787 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
tty::{
|
||||
console::ConsoleSwitch,
|
||||
virtual_terminal::{
|
||||
virtual_console::{CursorOperation, ScrollDir, VcCursor, VirtualConsoleData},
|
||||
Color,
|
||||
},
|
||||
},
|
||||
video::fbdev::base::{
|
||||
CopyAreaData, FbCursor, FbCursorSetMode, FbImage, FbVisual, FillRectData, FillRectROP,
|
||||
FrameBuffer, ScrollMode, FRAME_BUFFER_SET,
|
||||
},
|
||||
},
|
||||
libs::{
|
||||
font::FontDesc,
|
||||
spinlock::{SpinLock, SpinLockGuard},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{FbConAttr, FrameBufferConsole, FrameBufferConsoleData};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlittingFbConsole {
|
||||
fb: SpinLock<Option<Arc<dyn FrameBuffer>>>,
|
||||
fbcon_data: SpinLock<FrameBufferConsoleData>,
|
||||
}
|
||||
|
||||
unsafe impl Send for BlittingFbConsole {}
|
||||
unsafe impl Sync for BlittingFbConsole {}
|
||||
|
||||
impl BlittingFbConsole {
|
||||
pub fn new() -> Result<Self, SystemError> {
|
||||
Ok(Self {
|
||||
fb: SpinLock::new(None),
|
||||
fbcon_data: SpinLock::new(FrameBufferConsoleData::default()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fb(&self) -> Arc<dyn FrameBuffer> {
|
||||
self.fb.lock().clone().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_color(&self, vc_data: &VirtualConsoleData, c: u16, is_fg: bool) -> u32 {
|
||||
let fb_info = self.fb();
|
||||
let mut color = 0;
|
||||
|
||||
let depth = fb_info.color_depth();
|
||||
|
||||
if depth != 1 {
|
||||
if is_fg {
|
||||
let fg_shift = if vc_data.hi_font_mask != 0 { 9 } else { 8 };
|
||||
color = (c as u32 >> fg_shift) & 0x0f
|
||||
} else {
|
||||
let bg_shift = if vc_data.hi_font_mask != 0 { 13 } else { 12 };
|
||||
color = (c as u32 >> bg_shift) & 0x0f
|
||||
}
|
||||
}
|
||||
|
||||
match depth {
|
||||
1 => {
|
||||
let col = self.mono_color();
|
||||
let fg;
|
||||
let bg;
|
||||
if fb_info.current_fb_fix().visual != FbVisual::Mono01 {
|
||||
fg = col;
|
||||
bg = 0;
|
||||
} else {
|
||||
fg = 0;
|
||||
bg = col;
|
||||
}
|
||||
color = if is_fg { fg } else { bg };
|
||||
}
|
||||
2 => {
|
||||
/*
|
||||
颜色深度为2,即16色,
|
||||
将16色的颜色值映射到4色的灰度,
|
||||
其中颜色0映射为黑色,颜色1到6映射为白色,
|
||||
颜色7到8映射为灰色,其他颜色映射为强烈的白色。
|
||||
*/
|
||||
if color >= 1 && color <= 6 {
|
||||
// 白色
|
||||
color = 2;
|
||||
} else if color >= 7 && color <= 8 {
|
||||
// 灰色
|
||||
color = 1;
|
||||
} else {
|
||||
// 强白
|
||||
color = 3;
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
/*
|
||||
颜色深度为3,即256色,仅保留颜色的低3位,即颜色 0 到 7
|
||||
*/
|
||||
color &= 7;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
color
|
||||
}
|
||||
|
||||
/// ## 计算单色调的函数
|
||||
pub fn mono_color(&self) -> u32 {
|
||||
let fb_info = self.fb();
|
||||
let mut max_len = fb_info
|
||||
.current_fb_var()
|
||||
.green
|
||||
.length
|
||||
.max(fb_info.current_fb_var().red.length);
|
||||
|
||||
max_len = max_len.max(fb_info.current_fb_var().blue.length);
|
||||
|
||||
return (!(0xfff << max_len)) & 0xff;
|
||||
}
|
||||
|
||||
pub fn bit_put_string(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
buf: &[u16],
|
||||
attr: FbConAttr,
|
||||
cnt: u32,
|
||||
cellsize: u32,
|
||||
image: &mut FbImage,
|
||||
) {
|
||||
let charmask = if vc_data.hi_font_mask != 0 {
|
||||
0x1ff
|
||||
} else {
|
||||
0xff
|
||||
};
|
||||
|
||||
let mut offset;
|
||||
let image_line_byte = image.width as usize / 8;
|
||||
|
||||
let byte_width = vc_data.font.width as usize / 8;
|
||||
let font_height = vc_data.font.height as usize;
|
||||
// let mut char_offset = 0;
|
||||
for char_offset in 0..cnt as usize {
|
||||
// 在字符表中的index
|
||||
let ch = buf[char_offset] & charmask;
|
||||
// 计算出在font表中的偏移量
|
||||
let font_offset = ch as usize * cellsize as usize;
|
||||
let font_offset_end = font_offset + cellsize as usize;
|
||||
// 设置image的data
|
||||
|
||||
let src = &vc_data.font.data[font_offset..font_offset_end];
|
||||
let mut dst = Vec::new();
|
||||
dst.resize(src.len(), 0);
|
||||
dst.copy_from_slice(src);
|
||||
|
||||
if !attr.is_empty() {
|
||||
attr.update_attr(&mut dst, src, vc_data)
|
||||
}
|
||||
|
||||
offset = char_offset * byte_width;
|
||||
let mut dst_offset = 0;
|
||||
for _ in 0..font_height {
|
||||
let dst_offset_next = dst_offset + byte_width;
|
||||
image.data[offset..offset + byte_width]
|
||||
.copy_from_slice(&dst[dst_offset..dst_offset_next]);
|
||||
|
||||
offset += image_line_byte;
|
||||
dst_offset = dst_offset_next;
|
||||
}
|
||||
}
|
||||
|
||||
self.fb().fb_image_blit(image);
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleSwitch for BlittingFbConsole {
|
||||
fn con_init(
|
||||
&self,
|
||||
vc_data: &mut VirtualConsoleData,
|
||||
init: bool,
|
||||
) -> Result<(), system_error::SystemError> {
|
||||
let fb_set_guard = FRAME_BUFFER_SET.read();
|
||||
let fb = fb_set_guard.get(vc_data.index);
|
||||
if fb.is_none() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let fb = fb.unwrap();
|
||||
if fb.is_none() {
|
||||
panic!(
|
||||
"The Framebuffer with FbID {} has not been initialized yet.",
|
||||
vc_data.index
|
||||
)
|
||||
}
|
||||
|
||||
let fb = fb.as_ref().unwrap().clone();
|
||||
|
||||
if init {
|
||||
// 初始化字体
|
||||
let var = fb.current_fb_var();
|
||||
let font = FontDesc::get_default_font(var.xres, var.yres, 0, 0);
|
||||
vc_data.font.data = font.data.to_vec();
|
||||
vc_data.font.width = font.width;
|
||||
vc_data.font.height = font.height;
|
||||
vc_data.font.count = font.char_count;
|
||||
} else {
|
||||
kwarn!("The frontend Framebuffer is not implemented");
|
||||
}
|
||||
|
||||
vc_data.color_mode = fb.color_depth() != 1;
|
||||
vc_data.complement_mask = if vc_data.color_mode { 0x7700 } else { 0x0800 };
|
||||
|
||||
if vc_data.font.count == 256 {
|
||||
// ascii
|
||||
vc_data.hi_font_mask = 0;
|
||||
} else {
|
||||
vc_data.hi_font_mask = 0x100;
|
||||
if vc_data.color_mode {
|
||||
vc_data.complement_mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 考虑rotate
|
||||
if init {
|
||||
vc_data.cols = (fb.current_fb_var().xres / vc_data.font.width) as usize;
|
||||
vc_data.rows = (fb.current_fb_var().yres / vc_data.font.height) as usize;
|
||||
|
||||
vc_data.pos = vc_data.state.x + vc_data.state.y * vc_data.cols;
|
||||
|
||||
let new_size = vc_data.cols * vc_data.rows;
|
||||
vc_data.screen_buf.resize(new_size, 0);
|
||||
} else {
|
||||
unimplemented!("Resize is not supported at the moment!");
|
||||
}
|
||||
|
||||
// 初始化fb
|
||||
*self.fb.lock() = Some(fb);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn con_deinit(&self) -> Result<(), system_error::SystemError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn con_clear(
|
||||
&self,
|
||||
vc_data: &mut VirtualConsoleData,
|
||||
sy: usize,
|
||||
sx: usize,
|
||||
height: usize,
|
||||
width: usize,
|
||||
) -> Result<(), system_error::SystemError> {
|
||||
let fb_data = self.fbcon_data();
|
||||
|
||||
if height == 0 || width == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let y_break = (fb_data.display.virt_rows - fb_data.display.yscroll) as usize;
|
||||
if sy < y_break && sy + height - 1 >= y_break {
|
||||
// 分两次clear
|
||||
let b = y_break - sy;
|
||||
let _ = self.clear(
|
||||
&vc_data,
|
||||
fb_data.display.real_y(sy as u32),
|
||||
sx as u32,
|
||||
b as u32,
|
||||
width as u32,
|
||||
);
|
||||
let _ = self.clear(
|
||||
&vc_data,
|
||||
fb_data.display.real_y((sy + b) as u32),
|
||||
sx as u32,
|
||||
(height - b) as u32,
|
||||
width as u32,
|
||||
);
|
||||
} else {
|
||||
let _ = self.clear(
|
||||
&vc_data,
|
||||
fb_data.display.real_y(sy as u32),
|
||||
sx as u32,
|
||||
height as u32,
|
||||
width as u32,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn con_putc(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
ch: u16,
|
||||
xpos: u32,
|
||||
ypos: u32,
|
||||
) -> Result<(), system_error::SystemError> {
|
||||
self.con_putcs(vc_data, &[ch], 1, ypos, xpos)
|
||||
}
|
||||
|
||||
fn con_putcs(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
buf: &[u16],
|
||||
count: usize,
|
||||
ypos: u32,
|
||||
xpos: u32,
|
||||
) -> Result<(), SystemError> {
|
||||
if count == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let fbcon_data = self.fbcon_data();
|
||||
let c = buf[0];
|
||||
self.put_string(
|
||||
vc_data,
|
||||
buf,
|
||||
count as u32,
|
||||
fbcon_data.display.real_y(ypos),
|
||||
xpos,
|
||||
self.get_color(vc_data, c, true),
|
||||
self.get_color(vc_data, c, false),
|
||||
)
|
||||
}
|
||||
|
||||
fn con_getxy(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
pos: usize,
|
||||
) -> Result<(usize, usize, usize), SystemError> {
|
||||
if pos < vc_data.screen_buf.len() {
|
||||
let x = pos % vc_data.cols;
|
||||
let y = pos / vc_data.cols;
|
||||
let mut next_line_start = pos + (vc_data.cols - x);
|
||||
if next_line_start >= vc_data.screen_buf.len() {
|
||||
next_line_start = 0
|
||||
}
|
||||
return Ok((next_line_start, x, y));
|
||||
} else {
|
||||
return Ok((0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
fn con_cursor(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
op: crate::driver::tty::virtual_terminal::virtual_console::CursorOperation,
|
||||
) {
|
||||
let mut fbcon_data = self.fbcon_data();
|
||||
|
||||
let c = vc_data.screen_buf[vc_data.pos];
|
||||
|
||||
if vc_data.cursor_type.contains(VcCursor::CUR_SW) {
|
||||
// 取消硬光标Timer,但是现在没有硬光标,先写在这
|
||||
} else {
|
||||
// 添加硬光标Timer
|
||||
}
|
||||
|
||||
fbcon_data.cursor_flash = op != CursorOperation::Erase;
|
||||
|
||||
drop(fbcon_data);
|
||||
|
||||
self.cursor(
|
||||
vc_data,
|
||||
op,
|
||||
self.get_color(vc_data, c, true),
|
||||
self.get_color(vc_data, c, false),
|
||||
);
|
||||
}
|
||||
|
||||
fn con_set_palette(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
color_table: &[u8],
|
||||
) -> Result<(), SystemError> {
|
||||
let fb_info = self.fb();
|
||||
let depth = fb_info.color_depth();
|
||||
let mut palette = Vec::new();
|
||||
palette.resize(16, Color::default());
|
||||
if depth > 3 {
|
||||
let vc_palette = &vc_data.palette;
|
||||
for i in 0..16 {
|
||||
let idx = color_table[i];
|
||||
let col = palette.get_mut(idx as usize).unwrap();
|
||||
col.red = (vc_palette[i].red << 8) | vc_palette[i].red;
|
||||
col.green = (vc_palette[i].green << 8) | vc_palette[i].green;
|
||||
col.blue = (vc_palette[i].blue << 8) | vc_palette[i].blue;
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
|
||||
self.fb().set_color_map(palette)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn con_scroll(
|
||||
&self,
|
||||
vc_data: &mut VirtualConsoleData,
|
||||
top: usize,
|
||||
bottom: usize,
|
||||
dir: crate::driver::tty::virtual_terminal::virtual_console::ScrollDir,
|
||||
mut count: usize,
|
||||
) -> bool {
|
||||
self.con_cursor(vc_data, CursorOperation::Erase);
|
||||
|
||||
let fbcon_data = self.fbcon_data();
|
||||
let scroll_mode = fbcon_data.display.scroll_mode;
|
||||
|
||||
drop(fbcon_data);
|
||||
|
||||
match dir {
|
||||
ScrollDir::Up => {
|
||||
if count > vc_data.rows {
|
||||
count = vc_data.rows;
|
||||
}
|
||||
|
||||
match scroll_mode {
|
||||
ScrollMode::Move => {
|
||||
let start = top * vc_data.cols;
|
||||
let end = bottom * vc_data.cols;
|
||||
vc_data.screen_buf[start..end].rotate_left(count * vc_data.cols);
|
||||
|
||||
let _ = self.bmove(
|
||||
vc_data,
|
||||
top as i32,
|
||||
0,
|
||||
top as i32 - count as i32,
|
||||
0,
|
||||
(bottom - top) as u32,
|
||||
vc_data.cols as u32,
|
||||
);
|
||||
|
||||
let _ = self.con_clear(vc_data, bottom - count, 0, count, vc_data.cols);
|
||||
|
||||
let offset = vc_data.cols * (bottom - count);
|
||||
for i in
|
||||
vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut()
|
||||
{
|
||||
*i = vc_data.erase_char;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
ScrollMode::PanMove => todo!(),
|
||||
ScrollMode::WrapMove => todo!(),
|
||||
ScrollMode::Redraw => {
|
||||
let start = top * vc_data.cols;
|
||||
let end = bottom * vc_data.cols;
|
||||
vc_data.screen_buf[start..end].rotate_left(count * vc_data.cols);
|
||||
|
||||
let data = &vc_data.screen_buf[start..(bottom - count) * vc_data.cols];
|
||||
|
||||
for line in top..(bottom - count) {
|
||||
let mut start = line * vc_data.cols;
|
||||
let end = start + vc_data.cols;
|
||||
let mut offset = start;
|
||||
let mut attr = 1;
|
||||
let mut x = 0;
|
||||
while offset < end {
|
||||
let c = data[offset];
|
||||
|
||||
if attr != c & 0xff00 {
|
||||
// 属性变化,输出完上一个的并且更新属性
|
||||
attr = c & 0xff00;
|
||||
|
||||
let count = offset - start;
|
||||
let _ = self.con_putcs(
|
||||
vc_data,
|
||||
&data[start..offset],
|
||||
count,
|
||||
line as u32,
|
||||
x,
|
||||
);
|
||||
start = offset;
|
||||
x += count as u32;
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
let _ = self.con_putcs(
|
||||
vc_data,
|
||||
&data[start..offset],
|
||||
offset - start,
|
||||
line as u32,
|
||||
x,
|
||||
);
|
||||
}
|
||||
|
||||
let _ = self.con_clear(vc_data, bottom - count, 0, count, vc_data.cols);
|
||||
|
||||
let offset = vc_data.cols * (bottom - count);
|
||||
for i in
|
||||
vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut()
|
||||
{
|
||||
*i = vc_data.erase_char;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
ScrollMode::PanRedraw => todo!(),
|
||||
}
|
||||
}
|
||||
ScrollDir::Down => {
|
||||
if count > vc_data.rows {
|
||||
count = vc_data.rows;
|
||||
}
|
||||
|
||||
match scroll_mode {
|
||||
ScrollMode::Move => todo!(),
|
||||
ScrollMode::PanMove => todo!(),
|
||||
ScrollMode::WrapMove => todo!(),
|
||||
ScrollMode::Redraw => {
|
||||
// self.scroll_redraw(
|
||||
// vc_data,
|
||||
// bottom - 1,
|
||||
// bottom - top - count,
|
||||
// count * vc_data.cols,
|
||||
// false,
|
||||
// );
|
||||
|
||||
let _ = self.con_clear(vc_data, top, 0, count, vc_data.cols);
|
||||
|
||||
let offset = vc_data.cols * top;
|
||||
for i in
|
||||
vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut()
|
||||
{
|
||||
*i = vc_data.erase_char;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
ScrollMode::PanRedraw => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameBufferConsole for BlittingFbConsole {
|
||||
fn bmove(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
sy: i32,
|
||||
sx: i32,
|
||||
dy: i32,
|
||||
dx: i32,
|
||||
height: u32,
|
||||
width: u32,
|
||||
) -> Result<(), SystemError> {
|
||||
let area = CopyAreaData::new(
|
||||
dx * vc_data.font.width as i32,
|
||||
dy * vc_data.font.height as i32,
|
||||
width * vc_data.font.width,
|
||||
height * vc_data.font.height,
|
||||
sx * vc_data.font.width as i32,
|
||||
sy * vc_data.font.height as i32,
|
||||
);
|
||||
|
||||
self.fb().fb_copyarea(area)
|
||||
}
|
||||
|
||||
fn clear(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
sy: u32,
|
||||
sx: u32,
|
||||
height: u32,
|
||||
width: u32,
|
||||
) -> Result<(), SystemError> {
|
||||
let region = FillRectData::new(
|
||||
sx * vc_data.font.width,
|
||||
sy * vc_data.font.height,
|
||||
width * vc_data.font.width,
|
||||
height * vc_data.font.height,
|
||||
self.get_color(vc_data, vc_data.erase_char, false),
|
||||
FillRectROP::Copy,
|
||||
);
|
||||
|
||||
self.fb().fb_fillrect(region)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn put_string(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
data: &[u16],
|
||||
mut count: u32,
|
||||
y: u32,
|
||||
x: u32,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) -> Result<(), SystemError> {
|
||||
// 向上取整
|
||||
let width = (vc_data.font.width + 7) / 8;
|
||||
let cellsize = width * vc_data.font.height;
|
||||
let fb_info = self.fb();
|
||||
// 一次能输出的最大字数,避免帧缓冲区溢出
|
||||
let max_cnt = (fb_info.current_fb_var().xres * fb_info.current_fb_var().yres) / cellsize;
|
||||
let attr = FbConAttr::get_attr(data[0], fb_info.color_depth());
|
||||
|
||||
let mut image = FbImage {
|
||||
x: x * vc_data.font.width,
|
||||
y: y * vc_data.font.height,
|
||||
width: 0,
|
||||
height: vc_data.font.height,
|
||||
fg,
|
||||
bg,
|
||||
depth: 1,
|
||||
data: Default::default(),
|
||||
};
|
||||
|
||||
image.data.resize(cellsize as usize * count as usize, 0);
|
||||
|
||||
while count > 0 {
|
||||
let cnt = count.min(max_cnt);
|
||||
|
||||
image.width = vc_data.font.width * cnt;
|
||||
|
||||
self.bit_put_string(vc_data, data, attr, cnt, cellsize, &mut image);
|
||||
|
||||
image.x += cnt * vc_data.font.width;
|
||||
count -= cnt;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fbcon_data(&self) -> SpinLockGuard<super::FrameBufferConsoleData> {
|
||||
self.fbcon_data.lock()
|
||||
}
|
||||
|
||||
fn cursor(&self, vc_data: &VirtualConsoleData, op: CursorOperation, fg: u32, bg: u32) {
|
||||
let mut fbcon_data = self.fbcon_data();
|
||||
let fb_info = self.fb();
|
||||
let mut cursor = FbCursor::default();
|
||||
let charmask = if vc_data.hi_font_mask != 0 {
|
||||
0x1ff
|
||||
} else {
|
||||
0xff
|
||||
};
|
||||
|
||||
// 向上取整
|
||||
let w = (vc_data.font.width + 7) / 8;
|
||||
let y = fbcon_data.display.real_y(vc_data.state.y as u32);
|
||||
|
||||
let c = vc_data.screen_buf[vc_data.pos];
|
||||
let attr = FbConAttr::get_attr(c, fb_info.color_depth());
|
||||
let char_offset = (c as usize & charmask) * ((w * vc_data.font.height) as usize);
|
||||
|
||||
if fbcon_data.cursor_state.image.data != &vc_data.font.data[char_offset..]
|
||||
|| fbcon_data.cursor_reset
|
||||
{
|
||||
fbcon_data.cursor_state.image.data = vc_data.font.data[char_offset..].to_vec();
|
||||
cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETIMAGE);
|
||||
}
|
||||
|
||||
if !attr.is_empty() {
|
||||
fbcon_data
|
||||
.cursor_data
|
||||
.resize(w as usize * vc_data.font.height as usize, 0);
|
||||
|
||||
attr.update_attr(
|
||||
&mut fbcon_data.cursor_data,
|
||||
&vc_data.font.data[char_offset..],
|
||||
vc_data,
|
||||
);
|
||||
}
|
||||
|
||||
if fbcon_data.cursor_state.image.fg != fg
|
||||
|| fbcon_data.cursor_state.image.bg != bg
|
||||
|| fbcon_data.cursor_reset
|
||||
{
|
||||
fbcon_data.cursor_state.image.fg = fg;
|
||||
fbcon_data.cursor_state.image.bg = bg;
|
||||
cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETCMAP);
|
||||
}
|
||||
|
||||
if fbcon_data.cursor_state.image.x != (vc_data.font.width * vc_data.state.x as u32)
|
||||
|| fbcon_data.cursor_state.image.y != (vc_data.font.height * y)
|
||||
|| fbcon_data.cursor_reset
|
||||
{
|
||||
fbcon_data.cursor_state.image.x = vc_data.font.width * vc_data.state.x as u32;
|
||||
fbcon_data.cursor_state.image.y = vc_data.font.height * y;
|
||||
cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETPOS);
|
||||
}
|
||||
|
||||
if fbcon_data.cursor_state.image.height != vc_data.font.height
|
||||
|| fbcon_data.cursor_state.image.width != vc_data.font.width
|
||||
|| fbcon_data.cursor_reset
|
||||
{
|
||||
fbcon_data.cursor_state.image.height = vc_data.font.height;
|
||||
fbcon_data.cursor_state.image.width = vc_data.font.width;
|
||||
cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETSIZE);
|
||||
}
|
||||
|
||||
if fbcon_data.cursor_state.hot_x > 0
|
||||
|| fbcon_data.cursor_state.hot_y > 0
|
||||
|| fbcon_data.cursor_reset
|
||||
{
|
||||
fbcon_data.cursor_state.hot_x = 0;
|
||||
cursor.hot_y = 0;
|
||||
cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETHOT);
|
||||
}
|
||||
|
||||
if cursor.set_mode.contains(FbCursorSetMode::FB_CUR_SETSIZE)
|
||||
|| vc_data.cursor_type != fbcon_data.display.cursor_shape
|
||||
|| fbcon_data.cursor_state.mask.is_empty()
|
||||
|| fbcon_data.cursor_reset
|
||||
{
|
||||
fbcon_data.display.cursor_shape = vc_data.cursor_type;
|
||||
cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETSHAPE);
|
||||
|
||||
let cur_height;
|
||||
match fbcon_data.display.cursor_shape.cursor_size() {
|
||||
VcCursor::CUR_NONE => {
|
||||
cur_height = 0;
|
||||
}
|
||||
VcCursor::CUR_UNDERLINE => {
|
||||
if vc_data.font.height < 10 {
|
||||
cur_height = 1;
|
||||
} else {
|
||||
cur_height = 2;
|
||||
}
|
||||
}
|
||||
VcCursor::CUR_LOWER_THIRD => {
|
||||
cur_height = vc_data.font.height / 3;
|
||||
}
|
||||
VcCursor::CUR_LOWER_HALF => {
|
||||
cur_height = vc_data.font.height >> 1;
|
||||
}
|
||||
VcCursor::CUR_TWO_THIRDS => {
|
||||
cur_height = (vc_data.font.height << 1) / 3;
|
||||
}
|
||||
_ => {
|
||||
cur_height = vc_data.font.height;
|
||||
}
|
||||
}
|
||||
|
||||
// 表示空白部分
|
||||
let mut size = (vc_data.font.height - cur_height) * w;
|
||||
while size > 0 {
|
||||
size -= 1;
|
||||
fbcon_data.cursor_state.mask.push(0x00);
|
||||
}
|
||||
size = cur_height * w;
|
||||
// 表示光标显示部分
|
||||
while size > 0 {
|
||||
size -= 1;
|
||||
fbcon_data.cursor_state.mask.push(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
match op {
|
||||
CursorOperation::Erase => {
|
||||
fbcon_data.cursor_state.enable = false;
|
||||
}
|
||||
_ => {
|
||||
fbcon_data.cursor_state.enable = !vc_data.cursor_type.contains(VcCursor::CUR_SW);
|
||||
}
|
||||
}
|
||||
|
||||
if !attr.is_empty() {
|
||||
cursor.image.data = fbcon_data.cursor_data.clone();
|
||||
} else {
|
||||
cursor.image.data = vc_data.font.data
|
||||
[char_offset..char_offset + (w as usize * vc_data.font.height as usize)]
|
||||
.to_vec();
|
||||
}
|
||||
cursor.image.fg = fbcon_data.cursor_state.image.fg;
|
||||
cursor.image.bg = fbcon_data.cursor_state.image.bg;
|
||||
cursor.image.x = fbcon_data.cursor_state.image.x;
|
||||
cursor.image.y = fbcon_data.cursor_state.image.y;
|
||||
cursor.image.height = fbcon_data.cursor_state.image.height;
|
||||
cursor.image.width = fbcon_data.cursor_state.image.width;
|
||||
cursor.hot_x = fbcon_data.cursor_state.hot_x;
|
||||
cursor.hot_y = fbcon_data.cursor_state.hot_y;
|
||||
cursor.mask = fbcon_data.cursor_state.mask.clone();
|
||||
cursor.enable = fbcon_data.cursor_state.enable;
|
||||
cursor.image.depth = 1;
|
||||
cursor.rop = true;
|
||||
|
||||
if fb_info.fb_cursor(&cursor).is_err() {
|
||||
let _ = fb_info.soft_cursor(cursor);
|
||||
}
|
||||
|
||||
fbcon_data.cursor_reset = false;
|
||||
}
|
||||
}
|
@ -1,15 +1,19 @@
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::base::{
|
||||
class::Class,
|
||||
device::{bus::Bus, device_manager, driver::Driver, Device, DeviceType, IdTable},
|
||||
kobject::{KObjType, KObject, KObjectState, LockedKObjectState},
|
||||
kset::KSet,
|
||||
driver::{
|
||||
base::{
|
||||
class::Class,
|
||||
device::{bus::Bus, device_manager, driver::Driver, Device, DeviceType, IdTable},
|
||||
kobject::{KObjType, KObject, KObjectState, LockedKObjectState},
|
||||
kset::KSet,
|
||||
},
|
||||
tty::virtual_terminal::virtual_console::{CursorOperation, VcCursor, VirtualConsoleData},
|
||||
},
|
||||
filesystem::{
|
||||
kernfs::KernFSInode,
|
||||
@ -18,11 +22,13 @@ use crate::{
|
||||
},
|
||||
libs::{
|
||||
rwlock::{RwLockReadGuard, RwLockWriteGuard},
|
||||
spinlock::SpinLock,
|
||||
spinlock::{SpinLock, SpinLockGuard},
|
||||
},
|
||||
};
|
||||
|
||||
use super::fbmem::sys_class_graphics_instance;
|
||||
use super::{fbmem::sys_class_graphics_instance, FbCursor, ScrollMode};
|
||||
|
||||
pub mod framebuffer_console;
|
||||
|
||||
/// framebuffer console设备管理器实例
|
||||
static mut FB_CONSOLE_MANAGER: Option<FbConsoleManager> = None;
|
||||
@ -332,3 +338,166 @@ impl Attribute for AttrCursorBlink {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FrameBufferConsoleData {
|
||||
/// 光标闪烁间隔
|
||||
pub cursor_blink_jiffies: i64,
|
||||
/// 是否刷新光标
|
||||
pub cursor_flash: bool,
|
||||
///
|
||||
pub display: FbConsoleDisplay,
|
||||
/// 光标状态
|
||||
pub cursor_state: FbCursor,
|
||||
/// 重设光标?
|
||||
pub cursor_reset: bool,
|
||||
/// cursor 位图数据
|
||||
pub cursor_data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub trait FrameBufferConsole {
|
||||
fn fbcon_data(&self) -> SpinLockGuard<FrameBufferConsoleData>;
|
||||
|
||||
/// ## 将位块移动到目标位置
|
||||
/// 坐标均以字体为单位而不是pixel
|
||||
/// ### 参数
|
||||
/// ### sy: 起始位置的y坐标
|
||||
/// ### sx: 起始位置的x坐标、
|
||||
/// ### dy: 目标位置的y坐标
|
||||
/// ### dx: 目标位置的x坐标
|
||||
/// ### height: 位图高度
|
||||
/// ### width: 位图宽度
|
||||
fn bmove(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
sy: i32,
|
||||
sx: i32,
|
||||
dy: i32,
|
||||
dx: i32,
|
||||
height: u32,
|
||||
width: u32,
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 清除位图
|
||||
///
|
||||
/// ### 参数
|
||||
/// ### sy: 原位置的y坐标
|
||||
/// ### sx: 原位置的x坐标、
|
||||
/// ### height: 位图高度
|
||||
/// ### width: 位图宽度
|
||||
fn clear(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
sy: u32,
|
||||
sx: u32,
|
||||
height: u32,
|
||||
width: u32,
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
/// ## 显示字符串
|
||||
///
|
||||
/// ### 参数
|
||||
/// ### y: 起始位置y坐标
|
||||
/// ### x: 起始位置的x坐标、
|
||||
/// ### fg: 前景色
|
||||
/// ### bg: 背景色
|
||||
fn put_string(
|
||||
&self,
|
||||
vc_data: &VirtualConsoleData,
|
||||
data: &[u16],
|
||||
count: u32,
|
||||
y: u32,
|
||||
x: u32,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) -> Result<(), SystemError>;
|
||||
|
||||
fn cursor(&self, vc_data: &VirtualConsoleData, op: CursorOperation, fg: u32, bg: u32);
|
||||
}
|
||||
|
||||
/// 表示 framebuffer 控制台与低级帧缓冲设备之间接口的数据结构
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FbConsoleDisplay {
|
||||
/// 硬件滚动的行数
|
||||
pub yscroll: u32,
|
||||
/// 光标
|
||||
pub cursor_shape: VcCursor,
|
||||
/// 滚动模式
|
||||
pub scroll_mode: ScrollMode,
|
||||
virt_rows: u32,
|
||||
}
|
||||
|
||||
impl FbConsoleDisplay {
|
||||
pub fn real_y(&self, mut ypos: u32) -> u32 {
|
||||
let rows = self.virt_rows;
|
||||
ypos += self.yscroll;
|
||||
if ypos < rows {
|
||||
return ypos;
|
||||
} else {
|
||||
return ypos - rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct FbConAttr:u8 {
|
||||
const UNDERLINE = 1;
|
||||
const REVERSE = 2;
|
||||
const BOLD = 4;
|
||||
}
|
||||
}
|
||||
|
||||
impl FbConAttr {
|
||||
pub fn get_attr(c: u16, color_depth: u32) -> Self {
|
||||
let mut attr = Self::empty();
|
||||
if color_depth == 1 {
|
||||
if Self::underline(c) {
|
||||
attr.insert(Self::UNDERLINE);
|
||||
}
|
||||
if Self::reverse(c) {
|
||||
attr.intersects(Self::REVERSE);
|
||||
}
|
||||
if Self::blod(c) {
|
||||
attr.insert(Self::BOLD);
|
||||
}
|
||||
}
|
||||
attr
|
||||
}
|
||||
|
||||
pub fn update_attr(&self, dst: &mut [u8], src: &[u8], vc_data: &VirtualConsoleData) {
|
||||
let mut offset = if vc_data.font.height < 10 { 1 } else { 2 } as usize;
|
||||
|
||||
let width = (vc_data.font.width + 7) / 8;
|
||||
let cellsize = (vc_data.font.height * width) as usize;
|
||||
|
||||
// 大于offset的部分就是下划线
|
||||
offset = cellsize - (offset * width as usize);
|
||||
for i in 0..cellsize {
|
||||
let mut c = src[i];
|
||||
if self.contains(Self::UNDERLINE) && i >= offset {
|
||||
// 下划线
|
||||
c = 0xff;
|
||||
}
|
||||
if self.contains(Self::BOLD) {
|
||||
c |= c >> 1;
|
||||
}
|
||||
if self.contains(Self::REVERSE) {
|
||||
c = !c;
|
||||
}
|
||||
|
||||
dst[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn underline(c: u16) -> bool {
|
||||
c & 0x400 != 0
|
||||
}
|
||||
|
||||
pub fn blod(c: u16) -> bool {
|
||||
c & 0x200 != 0
|
||||
}
|
||||
|
||||
pub fn reverse(c: u16) -> bool {
|
||||
c & 0x800 != 0
|
||||
}
|
||||
}
|
@ -1,13 +1,24 @@
|
||||
use alloc::{string::String, sync::Arc};
|
||||
use alloc::{string::String, sync::Arc, vec::Vec};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::base::device::Device,
|
||||
driver::{base::device::Device, tty::virtual_terminal::Color},
|
||||
init::boot_params,
|
||||
libs::rwlock::RwLock,
|
||||
mm::{ucontext::LockedVMA, PhysAddr, VirtAddr},
|
||||
};
|
||||
|
||||
use self::fbmem::{FbDevice, FrameBufferManager};
|
||||
|
||||
const COLOR_TABLE_8: &'static [u32] = &[
|
||||
0x00000000, 0xff000000, 0x00ff0000, 0xffff0000, 0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00,
|
||||
0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff, 0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff,
|
||||
];
|
||||
|
||||
const COLOR_TABLE_16: &'static [u32] = &[0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff];
|
||||
|
||||
const COLOR_TABLE_32: &'static [u32] = &[0x00000000, 0xffffffff];
|
||||
|
||||
pub mod fbcon;
|
||||
pub mod fbmem;
|
||||
pub mod fbsysfs;
|
||||
@ -16,6 +27,14 @@ pub mod modedb;
|
||||
// 帧缓冲区id
|
||||
int_like!(FbId, u32);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref FRAME_BUFFER_SET: RwLock<Vec<Option<Arc<dyn FrameBuffer>>>> = {
|
||||
let mut ret = Vec::new();
|
||||
ret.resize(FrameBufferManager::FB_MAX, None);
|
||||
RwLock::new(ret)
|
||||
};
|
||||
}
|
||||
|
||||
impl FbId {
|
||||
/// 帧缓冲区id的初始值(无效值)
|
||||
pub const INIT: Self = Self::new(u32::MAX);
|
||||
@ -37,10 +56,240 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
|
||||
|
||||
/// 设置帧缓冲区的id
|
||||
fn set_fb_id(&self, id: FbId);
|
||||
|
||||
/// 通用的软件图像绘画
|
||||
fn generic_imageblit(&self, image: &FbImage) {
|
||||
let boot_param = boot_params().read();
|
||||
let x = image.x;
|
||||
let y = image.y;
|
||||
let byte_per_pixel = core::mem::size_of::<u32>() as u32;
|
||||
let bit_per_pixel = self.current_fb_var().bits_per_pixel;
|
||||
|
||||
// 计算图像在帧缓冲中的起始位
|
||||
let mut bitstart = (y * self.current_fb_fix().line_length * 8) + (x * bit_per_pixel);
|
||||
let start_index = bitstart & (32 - 1);
|
||||
let pitch_index = (self.current_fb_fix().line_length & (byte_per_pixel - 1)) * 8;
|
||||
// 位转字节
|
||||
bitstart /= 8;
|
||||
|
||||
// 对齐到像素字节大小
|
||||
bitstart &= !(byte_per_pixel - 1);
|
||||
|
||||
let dst1 = boot_param.screen_info.lfb_virt_base;
|
||||
if dst1.is_none() {
|
||||
return;
|
||||
}
|
||||
let mut dst1 = dst1.unwrap();
|
||||
dst1 = dst1 + VirtAddr::new(bitstart as usize);
|
||||
|
||||
let _ = self.fb_sync();
|
||||
|
||||
if image.depth == 1 {
|
||||
let fg;
|
||||
let bg;
|
||||
if self.current_fb_fix().visual == FbVisual::TrueColor
|
||||
|| self.current_fb_fix().visual == FbVisual::DirectColor
|
||||
{
|
||||
let fb_info_data = self.framebuffer_info_data().read();
|
||||
fg = fb_info_data.pesudo_palette[image.fg as usize];
|
||||
bg = fb_info_data.pesudo_palette[image.bg as usize];
|
||||
} else {
|
||||
fg = image.fg;
|
||||
bg = image.bg;
|
||||
}
|
||||
|
||||
if 32 % bit_per_pixel == 0
|
||||
&& start_index == 0
|
||||
&& pitch_index == 0
|
||||
&& image.width & (32 / bit_per_pixel - 1) == 0
|
||||
&& bit_per_pixel >= 8
|
||||
&& bit_per_pixel <= 32
|
||||
{
|
||||
unsafe { self.fast_imageblit(image, dst1, fg, bg) }
|
||||
} else {
|
||||
self.slow_imageblit(image, dst1, fg, bg, start_index, pitch_index)
|
||||
}
|
||||
} else {
|
||||
todo!("color image blit todo");
|
||||
}
|
||||
}
|
||||
|
||||
/// 优化的单色图像绘制函数
|
||||
///
|
||||
/// 仅当 bits_per_pixel 为 8、16 或 32 时才能使用。
|
||||
/// 要求 image->width 可以被像素或 dword (ppw) 整除。
|
||||
/// 要求 fix->line_length 可以被 4 整除。
|
||||
/// 扫描线的开始和结束都是 dword 对齐的。
|
||||
unsafe fn fast_imageblit(&self, image: &FbImage, mut dst1: VirtAddr, fg: u32, bg: u32) {
|
||||
let bpp = self.current_fb_var().bits_per_pixel;
|
||||
let mut fgx = fg;
|
||||
let mut bgx = bg;
|
||||
let ppw = 32 / bpp;
|
||||
let spitch = (image.width + 7) / 8;
|
||||
let tab: &[u32];
|
||||
let mut color_tab: [u32; 16] = [0; 16];
|
||||
|
||||
match bpp {
|
||||
8 => {
|
||||
tab = COLOR_TABLE_8;
|
||||
}
|
||||
16 => {
|
||||
tab = COLOR_TABLE_16;
|
||||
}
|
||||
32 => {
|
||||
tab = COLOR_TABLE_32;
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for _ in (0..(ppw - 1)).rev() {
|
||||
fgx <<= bpp;
|
||||
bgx <<= bpp;
|
||||
fgx |= fg;
|
||||
bgx |= bg;
|
||||
}
|
||||
|
||||
let bitmask = (1 << ppw) - 1;
|
||||
let eorx = fgx ^ bgx;
|
||||
let k = image.width / ppw;
|
||||
|
||||
for (idx, val) in tab.iter().enumerate() {
|
||||
color_tab[idx] = (*val & eorx) ^ bgx;
|
||||
}
|
||||
|
||||
let mut dst;
|
||||
let mut shift;
|
||||
let mut src;
|
||||
let mut offset = 0;
|
||||
let mut j = 0;
|
||||
for _ in (0..image.height).rev() {
|
||||
dst = dst1.as_ptr::<u32>();
|
||||
shift = 8;
|
||||
src = offset;
|
||||
match ppw {
|
||||
4 => {
|
||||
// 8bpp
|
||||
j = k;
|
||||
while j >= 2 {
|
||||
*dst = color_tab[(image.data[src] as usize >> 4) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 0) & bitmask];
|
||||
dst = dst.add(1);
|
||||
j -= 2;
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// 16bpp
|
||||
j = k;
|
||||
while j >= 4 {
|
||||
*dst = color_tab[(image.data[src] as usize >> 6) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 4) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 2) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 0) & bitmask];
|
||||
dst = dst.add(1);
|
||||
src += 1;
|
||||
j -= 4;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
// 32 bpp
|
||||
j = k;
|
||||
while j >= 8 {
|
||||
*dst = color_tab[(image.data[src] as usize >> 7) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 6) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 5) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 4) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 3) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 2) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 1) & bitmask];
|
||||
dst = dst.add(1);
|
||||
*dst = color_tab[(image.data[src] as usize >> 0) & bitmask];
|
||||
dst = dst.add(1);
|
||||
src += 1;
|
||||
j -= 8;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
while j != 0 {
|
||||
shift -= ppw;
|
||||
*dst = color_tab[(image.data[src] as usize >> shift) & bitmask];
|
||||
dst = dst.add(1);
|
||||
if shift == 0 {
|
||||
shift = 8;
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
dst1 += VirtAddr::new(self.current_fb_fix().line_length as usize);
|
||||
offset += spitch as usize;
|
||||
}
|
||||
}
|
||||
|
||||
fn slow_imageblit(
|
||||
&self,
|
||||
_image: &FbImage,
|
||||
_dst1: VirtAddr,
|
||||
_fg: u32,
|
||||
_bg: u32,
|
||||
_start_index: u32,
|
||||
_pitch_index: u32,
|
||||
) {
|
||||
todo!();
|
||||
// let bpp = self.current_fb_var().bits_per_pixel;
|
||||
// let pitch = self.current_fb_fix().line_length;
|
||||
// let null_bits = 32 - bpp;
|
||||
// let spitch = (image.width + 7) / 8;
|
||||
|
||||
// // TODO:这里是需要计算的,但是目前用不到,先直接写
|
||||
// let bswapmask = 0;
|
||||
|
||||
// let dst2 = dst1;
|
||||
|
||||
// // 一行一行画
|
||||
// for i in image.height..0 {
|
||||
// let dst = dst1;
|
||||
|
||||
// if start_index > 0 {
|
||||
// let start_mask = !(!(0 as u32) << start_index);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FrameBufferInfoData {
|
||||
/// 颜色映射
|
||||
pub color_map: Vec<Color>,
|
||||
/// 颜色映射表
|
||||
pub pesudo_palette: Vec<u32>,
|
||||
}
|
||||
|
||||
impl FrameBufferInfoData {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 帧缓冲区信息
|
||||
pub trait FrameBufferInfo {
|
||||
pub trait FrameBufferInfo: FrameBufferOps {
|
||||
fn framebuffer_info_data(&self) -> &RwLock<FrameBufferInfoData>;
|
||||
|
||||
/// Amount of ioremapped VRAM or 0
|
||||
fn screen_size(&self) -> usize;
|
||||
|
||||
@ -61,6 +310,54 @@ pub trait FrameBufferInfo {
|
||||
|
||||
/// 获取帧缓冲区的状态
|
||||
fn state(&self) -> FbState;
|
||||
|
||||
/// 颜色位深
|
||||
fn color_depth(&self) -> u32 {
|
||||
return 8;
|
||||
|
||||
// 以下逻辑没问题,但是当前没有初始化好var,所以先直接返回当前vasafb的8
|
||||
|
||||
// let var = self.current_fb_var();
|
||||
// let fix = self.current_fb_fix();
|
||||
// if fix.visual == FbVisual::Mono01 || fix.visual == FbVisual::Mono10 {
|
||||
// return 1;
|
||||
// } else {
|
||||
// if var.green.length == var.blue.length
|
||||
// && var.green.length == var.red.length
|
||||
// && var.green.offset == var.blue.offset
|
||||
// && var.green.offset == var.red.offset
|
||||
// {
|
||||
// kerror!("return {}", var.green.length);
|
||||
// return var.green.length;
|
||||
// } else {
|
||||
// return var.green.length + var.blue.length + var.red.length;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/// ## 设置调色板
|
||||
fn set_color_map(&self, cmap: Vec<Color>) -> Result<(), SystemError> {
|
||||
let ret = self.fb_set_color_map(cmap.clone());
|
||||
if ret.is_err() && ret.clone().unwrap_err() == SystemError::ENOSYS {
|
||||
for (idx, color) in cmap.iter().enumerate() {
|
||||
if self
|
||||
.fb_set_color_register(idx as u16, color.red, color.green, color.blue)
|
||||
.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.framebuffer_info_data().write().color_map = cmap;
|
||||
} else {
|
||||
if ret.is_ok() {
|
||||
self.framebuffer_info_data().write().color_map = cmap;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 帧缓冲区操作
|
||||
@ -122,6 +419,45 @@ pub trait FrameBufferOps {
|
||||
|
||||
/// 卸载与该帧缓冲区相关的所有资源
|
||||
fn fb_destroy(&self);
|
||||
|
||||
/// 画光标
|
||||
fn fb_cursor(&self, _cursor: &FbCursor) -> Result<(), SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
|
||||
/// 画软光标(暂时简要实现)
|
||||
fn soft_cursor(&self, cursor: FbCursor) -> Result<(), SystemError> {
|
||||
let mut image = cursor.image.clone();
|
||||
if cursor.enable {
|
||||
match cursor.rop {
|
||||
true => {
|
||||
for i in 0..image.data.len() {
|
||||
image.data[i] ^= cursor.mask[i];
|
||||
}
|
||||
}
|
||||
false => {
|
||||
for i in 0..image.data.len() {
|
||||
image.data[i] &= cursor.mask[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = self.fb_image_blit(&image);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fb_sync(&self) -> Result<(), SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
|
||||
/// 绘画位图
|
||||
fn fb_image_blit(&self, image: &FbImage);
|
||||
|
||||
fn fb_set_color_map(&self, _cmap: Vec<Color>) -> Result<(), SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
}
|
||||
|
||||
/// 帧缓冲区的状态
|
||||
@ -197,25 +533,27 @@ pub enum FillRectROP {
|
||||
}
|
||||
|
||||
/// `CopyAreaData` 结构体用于表示一个矩形区域,并指定从哪个源位置复制数据。
|
||||
///
|
||||
/// 注意,源位置必须是有意义的(即包围的矩形都必须得在屏幕内)
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct CopyAreaData {
|
||||
/// 目标矩形左上角的x坐标
|
||||
pub dx: u32,
|
||||
pub dx: i32,
|
||||
/// 目标矩形左上角的y坐标
|
||||
pub dy: u32,
|
||||
pub dy: i32,
|
||||
/// 矩形的宽度
|
||||
pub width: u32,
|
||||
/// 矩形的高度
|
||||
pub height: u32,
|
||||
/// 源矩形左上角的x坐标
|
||||
pub sx: u32,
|
||||
pub sx: i32,
|
||||
/// 源矩形左上角的y坐标
|
||||
pub sy: u32,
|
||||
pub sy: i32,
|
||||
}
|
||||
|
||||
impl CopyAreaData {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(dx: u32, dy: u32, width: u32, height: u32, sx: u32, sy: u32) -> Self {
|
||||
pub fn new(dx: i32, dy: i32, width: u32, height: u32, sx: i32, sy: i32) -> Self {
|
||||
Self {
|
||||
dx,
|
||||
dy,
|
||||
@ -850,3 +1188,72 @@ pub enum BootTimeVideoType {
|
||||
/// EFI graphic mode
|
||||
Efi,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FbCursor {
|
||||
/// 设置选项
|
||||
pub set_mode: FbCursorSetMode,
|
||||
/// 开关选项
|
||||
pub enable: bool,
|
||||
/// 表示光标图像的位操作,true表示XOR,false表示COPY
|
||||
pub rop: bool,
|
||||
/// 表示光标掩码(mask)的数据。掩码用于定义光标的形状,指定了哪些像素是光标的一部分。
|
||||
pub mask: Vec<u8>,
|
||||
|
||||
/// 表示光标的热点位置,即在光标图像中被认为是"焦点"的位置
|
||||
pub hot_x: u32,
|
||||
pub hot_y: u32,
|
||||
|
||||
/// 光标图像
|
||||
pub image: FbImage,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// 硬件光标控制
|
||||
#[derive(Default)]
|
||||
pub struct FbCursorSetMode:u8 {
|
||||
/// 设置位图
|
||||
const FB_CUR_SETIMAGE = 0x01;
|
||||
/// 设置位置
|
||||
const FB_CUR_SETPOS = 0x02;
|
||||
/// 设置热点
|
||||
const FB_CUR_SETHOT = 0x04;
|
||||
/// ColorMap
|
||||
const FB_CUR_SETCMAP = 0x08;
|
||||
/// 形状
|
||||
const FB_CUR_SETSHAPE = 0x10;
|
||||
/// Size
|
||||
const FB_CUR_SETSIZE = 0x20;
|
||||
/// 全设置
|
||||
const FB_CUR_SETALL = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ScrollMode {
|
||||
Move,
|
||||
PanMove,
|
||||
WrapMove,
|
||||
Redraw,
|
||||
PanRedraw,
|
||||
}
|
||||
|
||||
impl Default for ScrollMode {
|
||||
/// ## 默认Move
|
||||
fn default() -> Self {
|
||||
Self::Move
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct FbImage {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub fg: u32,
|
||||
pub bg: u32,
|
||||
pub depth: u8,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ use crate::{
|
||||
CompatibleTable,
|
||||
},
|
||||
},
|
||||
tty::serial::serial8250::send_to_default_serial8250_port,
|
||||
video::fbdev::base::{fbmem::frame_buffer_manager, FbVisual},
|
||||
serial::serial8250::send_to_default_serial8250_port,
|
||||
video::fbdev::base::{fbmem::frame_buffer_manager, FbVisual, FRAME_BUFFER_SET},
|
||||
},
|
||||
filesystem::{
|
||||
kernfs::KernFSInode,
|
||||
@ -56,7 +56,7 @@ use crate::{
|
||||
use super::base::{
|
||||
fbmem::FbDevice, BlankMode, BootTimeVideoType, FbAccel, FbActivateFlags, FbId, FbState, FbType,
|
||||
FbVModeFlags, FbVarScreenInfo, FbVideoMode, FixedScreenInfo, FrameBuffer, FrameBufferInfo,
|
||||
FrameBufferOps,
|
||||
FrameBufferInfoData, FrameBufferOps,
|
||||
};
|
||||
|
||||
/// 当前机器上面是否有vesa帧缓冲区
|
||||
@ -89,11 +89,14 @@ lazy_static! {
|
||||
pub struct VesaFb {
|
||||
inner: SpinLock<InnerVesaFb>,
|
||||
kobj_state: LockedKObjectState,
|
||||
fb_data: RwLock<FrameBufferInfoData>,
|
||||
}
|
||||
|
||||
impl VesaFb {
|
||||
pub const NAME: &'static str = "vesa_vga";
|
||||
pub fn new() -> Self {
|
||||
let mut fb_info_data = FrameBufferInfoData::new();
|
||||
fb_info_data.pesudo_palette.resize(256, 0);
|
||||
return Self {
|
||||
inner: SpinLock::new(InnerVesaFb {
|
||||
bus: None,
|
||||
@ -111,6 +114,7 @@ impl VesaFb {
|
||||
fb_state: FbState::Suspended,
|
||||
}),
|
||||
kobj_state: LockedKObjectState::new(None),
|
||||
fb_data: RwLock::new(fb_info_data),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -281,12 +285,46 @@ impl FrameBufferOps for VesaFb {
|
||||
|
||||
fn fb_set_color_register(
|
||||
&self,
|
||||
_regno: u16,
|
||||
_red: u16,
|
||||
_green: u16,
|
||||
_blue: u16,
|
||||
regno: u16,
|
||||
mut red: u16,
|
||||
mut green: u16,
|
||||
mut blue: u16,
|
||||
) -> Result<(), SystemError> {
|
||||
todo!()
|
||||
let mut fb_data = self.framebuffer_info_data().write();
|
||||
let var = self.current_fb_var();
|
||||
if regno as usize >= fb_data.pesudo_palette.len() {
|
||||
return Err(SystemError::E2BIG);
|
||||
}
|
||||
|
||||
if var.bits_per_pixel == 8 {
|
||||
todo!("vesa_setpalette todo");
|
||||
} else if regno < 16 {
|
||||
match var.bits_per_pixel {
|
||||
16 => {
|
||||
if var.red.offset == 10 {
|
||||
// RGB 1:5:5:5
|
||||
fb_data.pesudo_palette[regno as usize] = ((red as u32 & 0xf800) >> 1)
|
||||
| ((green as u32 & 0xf800) >> 6)
|
||||
| ((blue as u32 & 0xf800) >> 11);
|
||||
} else {
|
||||
fb_data.pesudo_palette[regno as usize] = (red as u32 & 0xf800)
|
||||
| ((green as u32 & 0xfc00) >> 5)
|
||||
| ((blue as u32 & 0xf800) >> 11);
|
||||
}
|
||||
}
|
||||
24 | 32 => {
|
||||
red >>= 8;
|
||||
green >>= 8;
|
||||
blue >>= 8;
|
||||
fb_data.pesudo_palette[regno as usize] = ((red as u32) << var.red.offset)
|
||||
| ((green as u32) << var.green.offset)
|
||||
| ((blue as u32) << var.blue.offset);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fb_blank(&self, _blank_mode: BlankMode) -> Result<(), SystemError> {
|
||||
@ -338,6 +376,138 @@ impl FrameBufferOps for VesaFb {
|
||||
|
||||
return Ok(len);
|
||||
}
|
||||
|
||||
fn fb_image_blit(&self, image: &super::base::FbImage) {
|
||||
self.generic_imageblit(image);
|
||||
}
|
||||
|
||||
/// ## 填充矩形
|
||||
fn fb_fillrect(&self, rect: super::base::FillRectData) -> Result<(), SystemError> {
|
||||
// kwarn!("rect {rect:?}");
|
||||
|
||||
let boot_param = boot_params().read();
|
||||
let screen_base = boot_param
|
||||
.screen_info
|
||||
.lfb_virt_base
|
||||
.ok_or(SystemError::ENODEV)?;
|
||||
let fg;
|
||||
if self.current_fb_fix().visual == FbVisual::TrueColor
|
||||
|| self.current_fb_fix().visual == FbVisual::DirectColor
|
||||
{
|
||||
fg = self.fb_data.read().pesudo_palette[rect.color as usize];
|
||||
} else {
|
||||
fg = rect.color;
|
||||
}
|
||||
|
||||
let bpp = self.current_fb_var().bits_per_pixel;
|
||||
// 每行像素数
|
||||
let line_offset = self.current_fb_var().xres;
|
||||
match bpp {
|
||||
32 => {
|
||||
let base = screen_base.as_ptr::<u32>();
|
||||
|
||||
for y in rect.dy..(rect.dy + rect.height) {
|
||||
for x in rect.dx..(rect.dx + rect.width) {
|
||||
unsafe { *base.add((y * line_offset + x) as usize) = fg };
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fb_copyarea(&self, data: super::base::CopyAreaData) -> Result<(), SystemError> {
|
||||
let bp = boot_params().read();
|
||||
let base = bp.screen_info.lfb_virt_base.ok_or(SystemError::ENODEV)?;
|
||||
let var = self.current_fb_var();
|
||||
|
||||
if data.sx < 0
|
||||
|| data.sy < 0
|
||||
|| data.sx as u32 > var.xres
|
||||
|| data.sx as u32 + data.width > var.xres
|
||||
|| data.sy as u32 > var.yres
|
||||
|| data.sy as u32 + data.height > var.yres
|
||||
{
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
let bytes_per_pixel = var.bits_per_pixel >> 3;
|
||||
let bytes_per_line = var.xres * bytes_per_pixel;
|
||||
|
||||
let sy = data.sy as u32;
|
||||
let sx = data.sx as u32;
|
||||
|
||||
let dst = {
|
||||
let mut dst = base;
|
||||
if data.dy < 0 {
|
||||
dst -= VirtAddr::new((((-data.dy) as u32) * bytes_per_line) as usize);
|
||||
} else {
|
||||
dst += VirtAddr::new(((data.dy as u32) * bytes_per_line) as usize);
|
||||
}
|
||||
|
||||
if data.dx > 0 && (data.dx as u32) < var.xres {
|
||||
dst += VirtAddr::new(((data.dx as u32) * bytes_per_pixel) as usize);
|
||||
}
|
||||
|
||||
dst
|
||||
};
|
||||
let src = base + VirtAddr::new((sy * bytes_per_line + sx * bytes_per_pixel) as usize);
|
||||
|
||||
match bytes_per_pixel {
|
||||
4 => {
|
||||
// 32bpp
|
||||
let mut dst = dst.as_ptr::<u32>();
|
||||
let mut src = src.as_ptr::<u32>();
|
||||
|
||||
for y in 0..data.height as usize {
|
||||
if (data.dy + y as i32) < 0 || (data.dy + y as i32) > var.yres as i32 {
|
||||
unsafe {
|
||||
// core::ptr::copy(src, dst, data.width as usize);
|
||||
src = src.add(var.xres as usize);
|
||||
dst = dst.add(var.xres as usize);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if data.dx < 0 {
|
||||
if ((-data.dx) as u32) < data.width {
|
||||
unsafe {
|
||||
core::ptr::copy(
|
||||
src.add((-data.dx) as usize),
|
||||
dst,
|
||||
(data.width as usize) - (-data.dx) as usize,
|
||||
);
|
||||
src = src.add(var.xres as usize);
|
||||
dst = dst.add(var.xres as usize);
|
||||
}
|
||||
}
|
||||
} else if data.dx as u32 + data.width > var.xres {
|
||||
if (data.dx as u32) < var.xres {
|
||||
unsafe {
|
||||
core::ptr::copy(src, dst, (var.xres - data.dx as u32) as usize);
|
||||
src = src.add(var.xres as usize);
|
||||
dst = dst.add(var.xres as usize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i in 0..data.width as usize {
|
||||
unsafe { *(dst.add(i)) = *(src.add(i)) }
|
||||
}
|
||||
unsafe {
|
||||
// core::ptr::copy(src, dst, data.width as usize);
|
||||
src = src.add(var.xres as usize);
|
||||
dst = dst.add(var.xres as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameBufferInfo for VesaFb {
|
||||
@ -368,6 +538,10 @@ impl FrameBufferInfo for VesaFb {
|
||||
fn state(&self) -> FbState {
|
||||
self.inner.lock().fb_state
|
||||
}
|
||||
|
||||
fn framebuffer_info_data(&self) -> &RwLock<FrameBufferInfoData> {
|
||||
&self.fb_data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -679,36 +853,54 @@ fn vesa_fb_device_init() -> Result<(), SystemError> {
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
kinfo!("vesa fb device init");
|
||||
let device = Arc::new(VesaFb::new());
|
||||
|
||||
let mut fix_info_guard = VESAFB_FIX_INFO.write_irqsave();
|
||||
let mut var_info_guard = VESAFB_DEFINED.write_irqsave();
|
||||
let mut fb_fix = VESAFB_FIX_INFO.write_irqsave();
|
||||
let mut fb_var = VESAFB_DEFINED.write_irqsave();
|
||||
|
||||
let boot_params_guard = boot_params().read();
|
||||
let boottime_screen_info = &boot_params_guard.screen_info;
|
||||
|
||||
fix_info_guard.smem_start = Some(boottime_screen_info.lfb_base);
|
||||
fix_info_guard.smem_len = boottime_screen_info.lfb_size;
|
||||
fb_fix.smem_start = Some(boottime_screen_info.lfb_base);
|
||||
fb_fix.smem_len = boottime_screen_info.lfb_size;
|
||||
|
||||
if boottime_screen_info.video_type == BootTimeVideoType::Mda {
|
||||
fix_info_guard.visual = FbVisual::Mono10;
|
||||
var_info_guard.bits_per_pixel = 8;
|
||||
fix_info_guard.line_length = (boottime_screen_info.origin_video_cols as u32)
|
||||
* (var_info_guard.bits_per_pixel / 8);
|
||||
var_info_guard.xres_virtual = boottime_screen_info.origin_video_cols as u32;
|
||||
var_info_guard.yres_virtual = boottime_screen_info.origin_video_lines as u32;
|
||||
fb_fix.visual = FbVisual::Mono10;
|
||||
fb_var.bits_per_pixel = 8;
|
||||
fb_fix.line_length =
|
||||
(boottime_screen_info.origin_video_cols as u32) * (fb_var.bits_per_pixel / 8);
|
||||
fb_var.xres_virtual = boottime_screen_info.origin_video_cols as u32;
|
||||
fb_var.yres_virtual = boottime_screen_info.origin_video_lines as u32;
|
||||
} else {
|
||||
fix_info_guard.visual = FbVisual::TrueColor;
|
||||
var_info_guard.bits_per_pixel = boottime_screen_info.lfb_depth as u32;
|
||||
fix_info_guard.line_length =
|
||||
(boottime_screen_info.lfb_width as u32) * (var_info_guard.bits_per_pixel / 8);
|
||||
var_info_guard.xres_virtual = boottime_screen_info.lfb_width as u32;
|
||||
var_info_guard.yres_virtual = boottime_screen_info.lfb_height as u32;
|
||||
fb_fix.visual = FbVisual::TrueColor;
|
||||
fb_var.bits_per_pixel = boottime_screen_info.lfb_depth as u32;
|
||||
fb_fix.line_length =
|
||||
(boottime_screen_info.lfb_width as u32) * (fb_var.bits_per_pixel / 8);
|
||||
fb_var.xres_virtual = boottime_screen_info.lfb_width as u32;
|
||||
fb_var.yres_virtual = boottime_screen_info.lfb_height as u32;
|
||||
fb_var.xres = boottime_screen_info.lfb_width as u32;
|
||||
fb_var.yres = boottime_screen_info.lfb_height as u32;
|
||||
}
|
||||
|
||||
drop(var_info_guard);
|
||||
drop(fix_info_guard);
|
||||
fb_var.red.length = boottime_screen_info.red_size as u32;
|
||||
fb_var.green.length = boottime_screen_info.green_size as u32;
|
||||
fb_var.blue.length = boottime_screen_info.blue_size as u32;
|
||||
|
||||
fb_var.red.offset = boottime_screen_info.red_pos as u32;
|
||||
fb_var.green.offset = boottime_screen_info.green_pos as u32;
|
||||
fb_var.blue.offset = boottime_screen_info.blue_pos as u32;
|
||||
|
||||
// TODO: 这里是暂时这样写的,初始化为RGB888格式,后续vesa初始化完善后删掉下面
|
||||
fb_var.red.offset = 16;
|
||||
fb_var.green.offset = 8;
|
||||
fb_var.blue.offset = 0;
|
||||
|
||||
if fb_var.bits_per_pixel >= 1 && fb_var.bits_per_pixel <= 8 {
|
||||
fb_var.red.length = fb_var.bits_per_pixel;
|
||||
fb_var.green.length = fb_var.bits_per_pixel;
|
||||
fb_var.blue.length = fb_var.bits_per_pixel;
|
||||
}
|
||||
|
||||
let device = Arc::new(VesaFb::new());
|
||||
device_manager().device_default_initialize(&(device.clone() as Arc<dyn Device>));
|
||||
|
||||
platform_device_manager()
|
||||
@ -719,6 +911,16 @@ fn vesa_fb_device_init() -> Result<(), SystemError> {
|
||||
.register_fb(device.clone() as Arc<dyn FrameBuffer>)
|
||||
.expect("vesa_fb_device_init: frame_buffer_manager().register_fb failed");
|
||||
|
||||
// 加入全局fb表
|
||||
let mut guard = FRAME_BUFFER_SET.write();
|
||||
if guard.get(device.fb_id().data() as usize).unwrap().is_some() {
|
||||
kwarn!(
|
||||
"vesa_fb_device_init: There is already an element {:?} in the FRAME_BUFFER_SET",
|
||||
device.fb_id()
|
||||
);
|
||||
}
|
||||
guard[device.fb_id().data() as usize] = Some(device.clone());
|
||||
|
||||
// 设置vesa fb的状态为运行中
|
||||
device.inner.lock().fb_state = FbState::Running;
|
||||
});
|
||||
|
@ -2,7 +2,6 @@ use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{
|
||||
arch::MMArch,
|
||||
driver::tty::serial::serial8250::send_to_default_serial8250_port,
|
||||
init::boot_params,
|
||||
kinfo,
|
||||
libs::{
|
||||
@ -176,7 +175,10 @@ impl VideoRefreshManager {
|
||||
pub unsafe fn video_init() -> Result<(), SystemError> {
|
||||
use crate::{
|
||||
arch::driver::video::arch_video_early_init,
|
||||
driver::video::fbdev::base::BootTimeVideoType,
|
||||
driver::{
|
||||
serial::serial8250::send_to_default_serial8250_port,
|
||||
video::fbdev::base::BootTimeVideoType,
|
||||
},
|
||||
};
|
||||
|
||||
arch_video_early_init()?;
|
||||
|
@ -6,7 +6,7 @@ use super::vfs::{
|
||||
core::{generate_inode_id, ROOT_INODE},
|
||||
file::FileMode,
|
||||
syscall::ModeType,
|
||||
FileSystem, FileType, FsInfo, IndexNode, Metadata,
|
||||
FilePrivateData, FileSystem, FileType, FsInfo, IndexNode, Metadata,
|
||||
};
|
||||
use crate::{
|
||||
driver::base::device::device_number::DeviceNumber,
|
||||
@ -461,7 +461,12 @@ impl IndexNode for LockedDevFSInode {
|
||||
}
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: u32, _data: usize) -> Result<usize, SystemError> {
|
||||
fn ioctl(
|
||||
&self,
|
||||
_cmd: u32,
|
||||
_data: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
Err(SystemError::EOPNOTSUPP_OR_ENOTSUP)
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,12 @@ impl IndexNode for KernFSInode {
|
||||
return Ok((name, entry.metadata()?));
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: u32, _data: usize) -> Result<usize, SystemError> {
|
||||
fn ioctl(
|
||||
&self,
|
||||
_cmd: u32,
|
||||
_data: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
// 若文件系统没有实现此方法,则返回“不支持”
|
||||
return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use system_error::SystemError;
|
||||
use crate::{
|
||||
driver::{
|
||||
base::{block::SeekFrom, device::DevicePrivateData},
|
||||
tty::TtyFilePrivateData,
|
||||
tty::tty_device::TtyFilePrivateData,
|
||||
},
|
||||
filesystem::procfs::ProcfsFilePrivateData,
|
||||
ipc::pipe::{LockedPipeInode, PipeFsPrivateData},
|
||||
|
@ -339,7 +339,12 @@ pub trait IndexNode: Any + Sync + Send + Debug {
|
||||
///
|
||||
/// @return 成功:Ok()
|
||||
/// 失败:Err(错误码)
|
||||
fn ioctl(&self, _cmd: u32, _data: usize) -> Result<usize, SystemError> {
|
||||
fn ioctl(
|
||||
&self,
|
||||
_cmd: u32,
|
||||
_data: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
// 若文件系统没有实现此方法,则返回“不支持”
|
||||
return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
|
||||
}
|
||||
|
@ -317,8 +317,13 @@ impl IndexNode for MountFSInode {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ioctl(&self, cmd: u32, data: usize) -> Result<usize, SystemError> {
|
||||
return self.inner_inode.ioctl(cmd, data);
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
data: usize,
|
||||
private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
return self.inner_inode.ioctl(cmd, data, private_data);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -297,7 +297,8 @@ impl Syscall {
|
||||
|
||||
// drop guard 以避免无法调度的问题
|
||||
drop(fd_table_guard);
|
||||
let r = file.lock_no_preempt().inode().ioctl(cmd, data);
|
||||
let file = file.lock_no_preempt();
|
||||
let r = file.inode().ioctl(cmd, data, &file.private_data);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -683,7 +684,7 @@ impl Syscall {
|
||||
for i in arg..FileDescriptorVec::PROCESS_MAX_FD {
|
||||
let binding = ProcessManager::current_pcb().fd_table();
|
||||
let mut fd_table_guard = binding.write();
|
||||
if fd_table_guard.get_file_by_fd(fd).is_none() {
|
||||
if fd_table_guard.get_file_by_fd(i as i32).is_none() {
|
||||
return Self::do_dup2(fd, i as i32, &mut fd_table_guard);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
init::{early_setup_arch, setup_arch, setup_arch_post},
|
||||
CurrentIrqArch, CurrentSMPArch, CurrentSchedArch,
|
||||
},
|
||||
driver::{base::init::driver_init, tty::init::tty_early_init, video::VideoRefreshManager},
|
||||
driver::{base::init::driver_init, serial::serial_early_init, video::VideoRefreshManager},
|
||||
exception::{init::irq_init, softirq::softirq_init, InterruptArch},
|
||||
filesystem::vfs::core::vfs_init,
|
||||
include::bindings::bindings::acpi_init,
|
||||
@ -81,7 +81,7 @@ fn do_start_kernel() {
|
||||
/// 在内存管理初始化之前,执行的初始化
|
||||
#[inline(never)]
|
||||
fn init_before_mem_init() {
|
||||
tty_early_init().expect("tty early init failed");
|
||||
serial_early_init().expect("serial early init failed");
|
||||
let video_ok = unsafe { VideoRefreshManager::video_init().is_ok() };
|
||||
scm_init(video_ok);
|
||||
}
|
||||
|
BIN
kernel/src/libs/font/bin/VGA_8X16.bytes
Normal file
BIN
kernel/src/libs/font/bin/VGA_8X16.bytes
Normal file
Binary file not shown.
BIN
kernel/src/libs/font/bin/VGA_8X8.bytes
Normal file
BIN
kernel/src/libs/font/bin/VGA_8X8.bytes
Normal file
Binary file not shown.
2
kernel/src/libs/font/font_type/mod.rs
Normal file
2
kernel/src/libs/font/font_type/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod vga8x16;
|
||||
pub mod vga8x8;
|
10
kernel/src/libs/font/font_type/vga8x16.rs
Normal file
10
kernel/src/libs/font/font_type/vga8x16.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::libs::font::FontDesc;
|
||||
|
||||
pub const FONT_VGA_8X16: FontDesc = FontDesc {
|
||||
index: 1,
|
||||
name: "VGA8x16",
|
||||
width: 8,
|
||||
height: 16,
|
||||
char_count: 256,
|
||||
data: include_bytes!("../bin/VGA_8X16.bytes"),
|
||||
};
|
11
kernel/src/libs/font/font_type/vga8x8.rs
Normal file
11
kernel/src/libs/font/font_type/vga8x8.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use crate::libs::font::FontDesc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const FONT_VGA_8X8: FontDesc = FontDesc {
|
||||
index: 0,
|
||||
name: "VGA8x8",
|
||||
width: 8,
|
||||
height: 8,
|
||||
char_count: 256,
|
||||
data: include_bytes!("../bin/VGA_8X8.bytes"),
|
||||
};
|
49
kernel/src/libs/font/mod.rs
Normal file
49
kernel/src/libs/font/mod.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use self::font_type::vga8x16::FONT_VGA_8X16;
|
||||
|
||||
pub mod font_type;
|
||||
|
||||
pub struct FontDesc {
|
||||
pub index: usize,
|
||||
pub name: &'static str,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub char_count: u32,
|
||||
pub data: &'static [u8],
|
||||
}
|
||||
|
||||
impl FontDesc {
|
||||
pub fn get_default_font(_xres: u32, _yres: u32, _font_w: u32, _font_h: u32) -> &'static Self {
|
||||
// todo: 目前先直接返回一个字体
|
||||
&FONT_VGA_8X16
|
||||
}
|
||||
|
||||
pub const DOUBLE_WIDTH_RANGE: &'static [(u32, u32)] = &[
|
||||
(0x1100, 0x115F),
|
||||
(0x2329, 0x232A),
|
||||
(0x2E80, 0x303E),
|
||||
(0x3040, 0xA4CF),
|
||||
(0xAC00, 0xD7A3),
|
||||
(0xF900, 0xFAFF),
|
||||
(0xFE10, 0xFE19),
|
||||
(0xFE30, 0xFE6F),
|
||||
(0xFF00, 0xFF60),
|
||||
(0xFFE0, 0xFFE6),
|
||||
(0x20000, 0x2FFFD),
|
||||
(0x30000, 0x3FFFD),
|
||||
];
|
||||
pub fn is_double_width(ch: u32) -> bool {
|
||||
if ch < Self::DOUBLE_WIDTH_RANGE.first().unwrap().0
|
||||
|| ch > Self::DOUBLE_WIDTH_RANGE.last().unwrap().1
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (first, last) in Self::DOUBLE_WIDTH_RANGE {
|
||||
if ch > *first && ch < *last {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use crate::driver::tty::tty_device::TtyDevice;
|
||||
use crate::driver::tty::{
|
||||
tty_port::{TtyPort, TTY_PORTS},
|
||||
virtual_terminal::virtual_console::CURRENT_VCNUM,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const NUM_SCAN_CODES: u8 = 0x80;
|
||||
@ -28,25 +33,21 @@ pub enum KeyFlag {
|
||||
pub struct TypeOneFSM {
|
||||
status: ScanCodeStatus,
|
||||
current_state: TypeOneFSMState,
|
||||
tty: Arc<TtyDevice>,
|
||||
}
|
||||
|
||||
impl TypeOneFSM {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(tty: Arc<TtyDevice>) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
status: ScanCodeStatus::new(),
|
||||
current_state: TypeOneFSMState::Start,
|
||||
tty,
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief 解析扫描码
|
||||
#[allow(dead_code)]
|
||||
pub fn parse(&mut self, scancode: u8) -> TypeOneFSMState {
|
||||
self.current_state = self
|
||||
.current_state
|
||||
.parse(scancode, &mut self.status, &self.tty);
|
||||
self.current_state = self.current_state.parse(scancode, &mut self.status);
|
||||
self.current_state
|
||||
}
|
||||
}
|
||||
@ -69,42 +70,30 @@ pub enum TypeOneFSMState {
|
||||
|
||||
impl TypeOneFSMState {
|
||||
/// @brief 状态机总控程序
|
||||
fn parse(
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
fn parse(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState {
|
||||
// kdebug!("the code is {:#x}\n", scancode);
|
||||
match self {
|
||||
TypeOneFSMState::Start => {
|
||||
return self.handle_start(scancode, scancode_status, tty);
|
||||
return self.handle_start(scancode, scancode_status);
|
||||
}
|
||||
TypeOneFSMState::PauseBreak(n) => {
|
||||
return self.handle_pause_break(*n, scancode_status, tty);
|
||||
return self.handle_pause_break(*n, scancode_status);
|
||||
}
|
||||
TypeOneFSMState::Func0 => {
|
||||
return self.handle_func0(scancode, scancode_status, tty);
|
||||
return self.handle_func0(scancode, scancode_status);
|
||||
}
|
||||
TypeOneFSMState::Type3 => {
|
||||
return self.handle_type3(scancode, scancode_status, tty);
|
||||
}
|
||||
TypeOneFSMState::PrtscPress(n) => {
|
||||
return self.handle_prtsc_press(*n, scancode_status, tty)
|
||||
return self.handle_type3(scancode, scancode_status);
|
||||
}
|
||||
TypeOneFSMState::PrtscPress(n) => return self.handle_prtsc_press(*n, scancode_status),
|
||||
TypeOneFSMState::PrtscRelease(n) => {
|
||||
return self.handle_prtsc_release(*n, scancode_status, tty)
|
||||
return self.handle_prtsc_release(*n, scancode_status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief 处理起始状态
|
||||
fn handle_start(
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
fn handle_start(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState {
|
||||
//kdebug!("in handle_start the code is {:#x}\n",scancode);
|
||||
match scancode {
|
||||
0xe1 => {
|
||||
@ -115,7 +104,7 @@ impl TypeOneFSMState {
|
||||
}
|
||||
_ => {
|
||||
//kdebug!("in _d the code is {:#x}\n",scancode);
|
||||
return TypeOneFSMState::Type3.handle_type3(scancode, scancode_status, tty);
|
||||
return TypeOneFSMState::Type3.handle_type3(scancode, scancode_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,17 +114,16 @@ impl TypeOneFSMState {
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
static PAUSE_BREAK_SCAN_CODE: [u8; 6] = [0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5];
|
||||
let i = match self {
|
||||
TypeOneFSMState::PauseBreak(i) => *i,
|
||||
_ => {
|
||||
return self.handle_type3(scancode, scancode_status, tty);
|
||||
return self.handle_type3(scancode, scancode_status);
|
||||
}
|
||||
};
|
||||
if scancode != PAUSE_BREAK_SCAN_CODE[i as usize] {
|
||||
return self.handle_type3(scancode, scancode_status, tty);
|
||||
return self.handle_type3(scancode, scancode_status);
|
||||
} else {
|
||||
if i == 5 {
|
||||
// 所有Pause Break扫描码都被清除
|
||||
@ -146,12 +134,7 @@ impl TypeOneFSMState {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_func0(
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
fn handle_func0(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState {
|
||||
//0xE0
|
||||
match scancode {
|
||||
0x2a => {
|
||||
@ -214,7 +197,7 @@ impl TypeOneFSMState {
|
||||
}
|
||||
0x53 => {
|
||||
scancode_status.del = true;
|
||||
Self::emit(tty, 127);
|
||||
Self::emit(127);
|
||||
}
|
||||
0xd3 => {
|
||||
scancode_status.del = false;
|
||||
@ -233,32 +216,32 @@ impl TypeOneFSMState {
|
||||
}
|
||||
0x48 => {
|
||||
scancode_status.arrow_u = true;
|
||||
Self::emit(tty, 224);
|
||||
Self::emit(tty, 72);
|
||||
Self::emit(224);
|
||||
Self::emit(72);
|
||||
}
|
||||
0xc8 => {
|
||||
scancode_status.arrow_u = false;
|
||||
}
|
||||
0x4b => {
|
||||
scancode_status.arrow_l = true;
|
||||
Self::emit(tty, 224);
|
||||
Self::emit(tty, 75);
|
||||
Self::emit(224);
|
||||
Self::emit(75);
|
||||
}
|
||||
0xcb => {
|
||||
scancode_status.arrow_l = false;
|
||||
}
|
||||
0x50 => {
|
||||
scancode_status.arrow_d = true;
|
||||
Self::emit(tty, 224);
|
||||
Self::emit(tty, 80);
|
||||
Self::emit(224);
|
||||
Self::emit(80);
|
||||
}
|
||||
0xd0 => {
|
||||
scancode_status.arrow_d = false;
|
||||
}
|
||||
0x4d => {
|
||||
scancode_status.arrow_r = true;
|
||||
Self::emit(tty, 224);
|
||||
Self::emit(tty, 77);
|
||||
Self::emit(224);
|
||||
Self::emit(77);
|
||||
}
|
||||
0xcd => {
|
||||
scancode_status.arrow_r = false;
|
||||
@ -269,14 +252,14 @@ impl TypeOneFSMState {
|
||||
scancode_status.kp_forward_slash = true;
|
||||
|
||||
let ch = '/' as u8;
|
||||
Self::emit(tty, ch);
|
||||
Self::emit(ch);
|
||||
}
|
||||
0xb5 => {
|
||||
scancode_status.kp_forward_slash = false;
|
||||
}
|
||||
0x1c => {
|
||||
scancode_status.kp_enter = true;
|
||||
Self::emit(tty, '\n' as u8);
|
||||
Self::emit('\n' as u8);
|
||||
}
|
||||
0x9c => {
|
||||
scancode_status.kp_enter = false;
|
||||
@ -288,12 +271,7 @@ impl TypeOneFSMState {
|
||||
return TypeOneFSMState::Start;
|
||||
}
|
||||
|
||||
fn handle_type3(
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
fn handle_type3(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState {
|
||||
// 判断按键是被按下还是抬起
|
||||
let flag_make = if (scancode & (TYPE1_KEYCODE_FLAG_BREAK as u8)) > 0 {
|
||||
false //up
|
||||
@ -358,18 +336,36 @@ impl TypeOneFSMState {
|
||||
}
|
||||
}
|
||||
|
||||
let ch = TYPE1_KEY_CODE_MAPTABLE[col as usize + 2 * index as usize];
|
||||
let mut ch = TYPE1_KEY_CODE_MAPTABLE[col as usize + 2 * index as usize];
|
||||
if key != KeyFlag::NoneFlag {
|
||||
// kdebug!("EMIT: ch is '{}', keyflag is {:?}\n", ch as char, key);
|
||||
Self::emit(tty, ch);
|
||||
if scancode_status.ctrl_l || scancode_status.ctrl_r {
|
||||
ch = Self::to_ctrl(ch);
|
||||
}
|
||||
Self::emit(ch);
|
||||
}
|
||||
return TypeOneFSMState::Start;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_ctrl(ch: u8) -> u8 {
|
||||
return match ch as char {
|
||||
'a'..='z' => ch - 0x40,
|
||||
'A'..='Z' => ch - 0x40,
|
||||
'@'..='_' => ch - 0x40,
|
||||
_ => ch,
|
||||
};
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn emit(tty: &Arc<TtyDevice>, ch: u8) {
|
||||
fn emit(ch: u8) {
|
||||
// 发送到tty
|
||||
tty.input(&[ch]).ok();
|
||||
let _ = Self::current_port().receive_buf(&[ch], &[], 1);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn current_port() -> Arc<dyn TtyPort> {
|
||||
TTY_PORTS[CURRENT_VCNUM.load(Ordering::SeqCst) as usize].clone()
|
||||
}
|
||||
|
||||
/// @brief 处理Prtsc按下事件
|
||||
@ -377,7 +373,6 @@ impl TypeOneFSMState {
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
static PRTSC_SCAN_CODE: [u8; 4] = [0xe0, 0x2a, 0xe0, 0x37];
|
||||
let i = match self {
|
||||
@ -389,7 +384,7 @@ impl TypeOneFSMState {
|
||||
return TypeOneFSMState::Start;
|
||||
}
|
||||
if scancode != PRTSC_SCAN_CODE[i as usize] {
|
||||
return self.handle_type3(scancode, scancode_status, tty);
|
||||
return self.handle_type3(scancode, scancode_status);
|
||||
} else {
|
||||
if i == 3 {
|
||||
// 成功解析出PrtscPress
|
||||
@ -405,7 +400,6 @@ impl TypeOneFSMState {
|
||||
&self,
|
||||
scancode: u8,
|
||||
scancode_status: &mut ScanCodeStatus,
|
||||
tty: &Arc<TtyDevice>,
|
||||
) -> TypeOneFSMState {
|
||||
static PRTSC_SCAN_CODE: [u8; 4] = [0xe0, 0xb7, 0xe0, 0xaa];
|
||||
let i = match self {
|
||||
@ -417,7 +411,7 @@ impl TypeOneFSMState {
|
||||
return TypeOneFSMState::Start;
|
||||
}
|
||||
if scancode != PRTSC_SCAN_CODE[i as usize] {
|
||||
return self.handle_type3(scancode, scancode_status, tty);
|
||||
return self.handle_type3(scancode, scancode_status);
|
||||
} else {
|
||||
if i == 3 {
|
||||
// 成功解析出PrtscRelease
|
||||
|
@ -8,9 +8,7 @@ use alloc::{boxed::Box, collections::LinkedList, string::String, sync::Arc};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
tty::serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager,
|
||||
},
|
||||
driver::{serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager},
|
||||
libs::{lib_ui::textui::textui_is_enable_put_to_window, rwlock::RwLock, spinlock::SpinLock},
|
||||
mm::VirtAddr,
|
||||
};
|
||||
|
@ -1,6 +1,11 @@
|
||||
use crate::{
|
||||
driver::{
|
||||
tty::serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager,
|
||||
serial::serial8250::send_to_default_serial8250_port,
|
||||
tty::{
|
||||
tty_driver::TtyOperation, tty_port::TTY_PORTS,
|
||||
virtual_terminal::virtual_console::CURRENT_VCNUM,
|
||||
},
|
||||
video::video_refresh_manager,
|
||||
},
|
||||
kdebug, kinfo,
|
||||
libs::{
|
||||
@ -984,6 +989,29 @@ where
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rs_textui_putchar(character: u8, fr_color: u32, bk_color: u32) -> i32 {
|
||||
let current_vcnum = CURRENT_VCNUM.load(Ordering::SeqCst);
|
||||
if current_vcnum != -1 {
|
||||
// tty已经初始化了之后才输出到屏幕
|
||||
let fr = (fr_color & 0x00ff0000) >> 16;
|
||||
let fg = (fr_color & 0x0000ff00) >> 8;
|
||||
let fb = fr_color & 0x000000ff;
|
||||
let br = (bk_color & 0x00ff0000) >> 16;
|
||||
let bg = (bk_color & 0x0000ff00) >> 8;
|
||||
let bb = bk_color & 0x000000ff;
|
||||
let buf = format!(
|
||||
"\x1B[38;2;{fr};{fg};{fb};48;2;{br};{bg};{bb}m{}\x1B[0m",
|
||||
character as char
|
||||
);
|
||||
let port = TTY_PORTS[current_vcnum as usize].clone();
|
||||
let tty = port.port_data().tty();
|
||||
if tty.is_some() {
|
||||
let tty = tty.unwrap();
|
||||
return tty
|
||||
.write(tty.core(), buf.as_bytes(), buf.len())
|
||||
.map(|_| 0)
|
||||
.unwrap_or_else(|e| e.to_posix_errno());
|
||||
}
|
||||
}
|
||||
return textui_putchar(
|
||||
character as char,
|
||||
FontColor::from(fr_color),
|
||||
|
@ -6,7 +6,7 @@ use core::{
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::driver::{
|
||||
tty::serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager,
|
||||
serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager,
|
||||
};
|
||||
|
||||
use super::textui::{
|
||||
|
@ -25,3 +25,5 @@ pub mod volatile;
|
||||
pub mod futex;
|
||||
pub mod rand;
|
||||
pub mod wait_queue;
|
||||
|
||||
pub mod font;
|
||||
|
@ -1,10 +1,20 @@
|
||||
use core::fmt::{self, Write};
|
||||
use core::{
|
||||
fmt::{self, Write},
|
||||
sync::atomic::Ordering,
|
||||
};
|
||||
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::lib_ui::textui::{textui_putstr, FontColor};
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
serial::serial8250::send_to_default_serial8250_port,
|
||||
tty::{
|
||||
tty_driver::TtyOperation, tty_port::TTY_PORTS,
|
||||
virtual_terminal::virtual_console::CURRENT_VCNUM,
|
||||
},
|
||||
},
|
||||
filesystem::procfs::{
|
||||
kmsg::KMSG,
|
||||
log::{LogLevel, LogMessage},
|
||||
@ -57,7 +67,7 @@ macro_rules! kinfo {
|
||||
macro_rules! kwarn {
|
||||
($($arg:tt)*) => {
|
||||
$crate::libs::printk::Logger.log(4,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
|
||||
$crate::libs::printk::PrintkWriter.__write_string_color($crate::libs::lib_ui::textui::FontColor::YELLOW, $crate::libs::lib_ui::textui::FontColor::BLACK, "[ WARN ] ");
|
||||
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[1;33m[ WARN ] \x1B[0m"));
|
||||
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
|
||||
}
|
||||
}
|
||||
@ -66,7 +76,7 @@ macro_rules! kwarn {
|
||||
macro_rules! kerror {
|
||||
($($arg:tt)*) => {
|
||||
$crate::libs::printk::Logger.log(3,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
|
||||
$crate::libs::printk::PrintkWriter.__write_string_color($crate::libs::lib_ui::textui::FontColor::RED, $crate::libs::lib_ui::textui::FontColor::BLACK, "[ ERROR ] ");
|
||||
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[41m[ ERROR ] \x1B[0m"));
|
||||
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
|
||||
}
|
||||
}
|
||||
@ -75,7 +85,7 @@ macro_rules! kerror {
|
||||
macro_rules! kBUG {
|
||||
($($arg:tt)*) => {
|
||||
$crate::libs::printk::Logger.log(1,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
|
||||
$crate::libs::printk::PrintkWriter.__write_string_color($crate::libs::lib_ui::textui::FontColor::RED, $crate::libs::lib_ui::textui::FontColor::BLACK, "[ BUG ] ");
|
||||
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[41m[ BUG ] \x1B[0m"));
|
||||
$crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*)));
|
||||
}
|
||||
}
|
||||
@ -91,11 +101,21 @@ impl PrintkWriter {
|
||||
/// 并输出白底黑字
|
||||
/// @param str: 要写入的字符
|
||||
pub fn __write_string(&mut self, s: &str) {
|
||||
textui_putstr(s, FontColor::WHITE, FontColor::BLACK).ok();
|
||||
}
|
||||
|
||||
pub fn __write_string_color(&self, fr_color: FontColor, bk_color: FontColor, s: &str) {
|
||||
textui_putstr(s, fr_color, bk_color).ok();
|
||||
let current_vcnum = CURRENT_VCNUM.load(Ordering::SeqCst);
|
||||
if current_vcnum != -1 {
|
||||
// tty已经初始化了之后才输出到屏幕
|
||||
let port = TTY_PORTS[current_vcnum as usize].clone();
|
||||
let tty = port.port_data().tty();
|
||||
if tty.is_some() {
|
||||
let tty = tty.unwrap();
|
||||
let _ = tty.write(tty.core(), s.as_bytes(), s.len());
|
||||
send_to_default_serial8250_port(s.as_bytes());
|
||||
} else {
|
||||
let _ = textui_putstr(s, FontColor::WHITE, FontColor::BLACK);
|
||||
}
|
||||
} else {
|
||||
let _ = textui_putstr(s, FontColor::WHITE, FontColor::BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::{fmt::Write, sync::atomic::Ordering};
|
||||
|
||||
use crate::{
|
||||
arch::MMArch, driver::tty::serial::serial8250::send_to_default_serial8250_port,
|
||||
arch::MMArch, driver::serial::serial8250::send_to_default_serial8250_port,
|
||||
filesystem::procfs::kmsg::kmsg_init, libs::printk::PrintkWriter, mm::mmio_buddy::mmio_init,
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
||||
sched::sched,
|
||||
CurrentIrqArch,
|
||||
},
|
||||
driver::tty::tty_core::TtyCore,
|
||||
exception::InterruptArch,
|
||||
filesystem::{
|
||||
procfs::procfs_unregister_pid,
|
||||
@ -1302,6 +1303,8 @@ pub struct ProcessSignalInfo {
|
||||
sig_pending: SigPending,
|
||||
// sig_shared_pending 中存储当前线程所属进程要处理的信号
|
||||
sig_shared_pending: SigPending,
|
||||
// 当前进程对应的tty
|
||||
tty: Option<Arc<TtyCore>>,
|
||||
}
|
||||
|
||||
impl ProcessSignalInfo {
|
||||
@ -1329,6 +1332,14 @@ impl ProcessSignalInfo {
|
||||
&self.sig_shared_pending
|
||||
}
|
||||
|
||||
pub fn tty(&self) -> Option<Arc<TtyCore>> {
|
||||
self.tty.clone()
|
||||
}
|
||||
|
||||
pub fn set_tty(&mut self, tty: Arc<TtyCore>) {
|
||||
self.tty = Some(tty);
|
||||
}
|
||||
|
||||
/// 从 pcb 的 siginfo中取出下一个要处理的信号,先处理线程信号,再处理进程信号
|
||||
///
|
||||
/// ## 参数
|
||||
@ -1351,6 +1362,7 @@ impl Default for ProcessSignalInfo {
|
||||
sig_block: SigSet::empty(),
|
||||
sig_pending: SigPending::default(),
|
||||
sig_shared_pending: SigPending::default(),
|
||||
tty: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use crate::{
|
||||
process::{
|
||||
fork::KernelCloneArgs,
|
||||
resource::{RLimit64, RUsage},
|
||||
ProcessManager,
|
||||
},
|
||||
};
|
||||
|
||||
@ -853,6 +854,11 @@ impl Syscall {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
SYS_SETPGID => {
|
||||
kwarn!("SYS_SETPGID has not yet been implemented");
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
SYS_RT_SIGPROCMASK => {
|
||||
kwarn!("SYS_RT_SIGPROCMASK has not yet been implemented");
|
||||
Ok(0)
|
||||
@ -1009,6 +1015,19 @@ impl Syscall {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
SYS_GETRLIMIT => {
|
||||
let resource = args[0];
|
||||
let rlimit = args[1] as *mut RLimit64;
|
||||
|
||||
Self::prlimit64(
|
||||
ProcessManager::current_pcb().pid(),
|
||||
resource,
|
||||
0 as *const RLimit64,
|
||||
rlimit,
|
||||
)
|
||||
}
|
||||
|
||||
SYS_SCHED_YIELD => Self::sched_yield(),
|
||||
|
||||
_ => panic!("Unsupported syscall ID: {}", syscall_num),
|
||||
|
@ -128,7 +128,12 @@ impl IndexNode for LockedKvmInode {
|
||||
///
|
||||
/// @return 成功:Ok()
|
||||
/// 失败:Err(错误码)
|
||||
fn ioctl(&self, cmd: u32, data: usize) -> Result<usize, SystemError> {
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
data: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
match cmd {
|
||||
0xdeadbeef => {
|
||||
kdebug!("kvm ioctl");
|
||||
|
@ -136,7 +136,12 @@ impl IndexNode for LockedVcpuInode {
|
||||
///
|
||||
/// @return 成功:Ok()
|
||||
/// 失败:Err(错误码)
|
||||
fn ioctl(&self, cmd: u32, data: usize) -> Result<usize, SystemError> {
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
data: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
match cmd {
|
||||
0xdeadbeef => {
|
||||
kdebug!("kvm_cpu ioctl");
|
||||
|
@ -134,7 +134,12 @@ impl IndexNode for LockedVmInode {
|
||||
///
|
||||
/// @return 成功:Ok()
|
||||
/// 失败:Err(错误码)
|
||||
fn ioctl(&self, cmd: u32, data: usize) -> Result<usize, SystemError> {
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
data: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
match cmd {
|
||||
0xdeadbeef => {
|
||||
kdebug!("kvm_vm ioctl");
|
||||
|
3
user/apps/clear/.gitignore
vendored
Normal file
3
user/apps/clear/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
/install/
|
10
user/apps/clear/Cargo.toml
Normal file
10
user/apps/clear/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "clear"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "clear screen"
|
||||
authors = [ "GnoCiYeH <heyicong@dragonos.org>" ]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
56
user/apps/clear/Makefile
Normal file
56
user/apps/clear/Makefile
Normal file
@ -0,0 +1,56 @@
|
||||
TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu"
|
||||
# RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie"
|
||||
|
||||
ifdef DADK_CURRENT_BUILD_DIR
|
||||
# 如果是在dadk中编译,那么安装到dadk的安装目录中
|
||||
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
|
||||
else
|
||||
# 如果是在本地编译,那么安装到当前目录下的install目录中
|
||||
INSTALL_DIR = ./install
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
else ifeq ($(ARCH), riscv64)
|
||||
export RUST_TARGET=riscv64gc-unknown-linux-gnu
|
||||
else
|
||||
# 默认为x86_86,用于本地编译
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
endif
|
||||
|
||||
run:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
|
||||
|
||||
build:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
|
||||
|
||||
clean:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
|
||||
|
||||
test:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
|
||||
|
||||
doc:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
|
||||
|
||||
fmt:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
|
||||
|
||||
fmt-check:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
|
||||
|
||||
run-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
|
||||
|
||||
build-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
|
||||
|
||||
clean-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
|
||||
|
||||
test-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force
|
7
user/apps/clear/README.md
Normal file
7
user/apps/clear/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# DragonOS Tty Clear
|
||||
|
||||
清屏程序:clear指令
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 直接运行`clear`即可
|
3
user/apps/clear/src/main.rs
Normal file
3
user/apps/clear/src/main.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("\x1Bc");
|
||||
}
|
22
user/dadk/config/clear-0.1.0.dadk
Normal file
22
user/dadk/config/clear-0.1.0.dadk
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "clear",
|
||||
"version": "0.1.0",
|
||||
"description": "清屏",
|
||||
"task_type": {
|
||||
"BuildFromSource": {
|
||||
"Local": {
|
||||
"path": "apps/clear"
|
||||
}
|
||||
}
|
||||
},
|
||||
"depends": [],
|
||||
"build": {
|
||||
"build_command": "make install"
|
||||
},
|
||||
"clean": {
|
||||
"clean_command": "make clean"
|
||||
},
|
||||
"install": {
|
||||
"in_dragonos_path": "/"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user