asterinas/kernel/src/device/tty/termio.rs

346 lines
11 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use crate::prelude::*;
/// A control character; the `cc_t` type in Linux.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits-common.h#L5>.
type CCtrlChar = u8;
bitflags! {
/// The input flags; `c_iflags` bits in Linux.
#[derive(Pod)]
#[repr(C)]
pub struct CInputFlags: u32 {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits-common.h
const IGNBRK = 0x001; /* Ignore break condition */
const BRKINT = 0x002; /* Signal interrupt on break */
const IGNPAR = 0x004; /* Ignore characters with parity errors */
const PARMRK = 0x008; /* Mark parity and framing errors */
const INPCK = 0x010; /* Enable input parity check */
const ISTRIP = 0x020; /* Strip 8th bit off characters */
const INLCR = 0x040; /* Map NL to CR on input */
const IGNCR = 0x080; /* Ignore CR */
const ICRNL = 0x100; /* Map CR to NL on input */
const IXANY = 0x800; /* Any character will restart after stop */
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h
const IUCLC = 0x0200;
const IXON = 0x0400;
const IXOFF = 0x1000;
const IMAXBEL = 0x2000;
const IUTF8 = 0x4000;
}
}
impl Default for CInputFlags {
fn default() -> Self {
Self::ICRNL | Self::IXON
}
}
bitflags! {
/// The output flags; `c_oflags` bits in Linux.
#[repr(C)]
#[derive(Pod)]
pub(super) struct COutputFlags: u32 {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits-common.h#L21
const OPOST = 1 << 0; /* Perform output processing */
const OLCUC = 1 << 1;
const ONLCR = 1 << 2;
const OCRNL = 1 << 3;
const ONOCR = 1 << 4;
const ONLRET = 1 << 5;
const OFILL = 1 << 6;
const OFDEL = 1 << 7;
}
}
impl Default for COutputFlags {
fn default() -> Self {
Self::OPOST | Self::ONLCR
}
}
/// The control flags; `c_cflags` bits in Linux.
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod)]
pub(super) struct CCtrlFlags(u32);
impl Default for CCtrlFlags {
fn default() -> Self {
let cbaud = CCtrlBaud::B38400 as u32;
let csize = CCtrlSize::CS8 as u32;
let c_cflags = cbaud | csize | Self::READ_BIT;
Self(c_cflags)
}
}
impl CCtrlFlags {
const BAUD_MASK: u32 = 0x0000100f;
const SIZE_MASK: u32 = 0x00000030;
const READ_BIT: u32 = 0x00000080;
#[expect(dead_code)]
pub(super) fn baud(&self) -> Result<CCtrlBaud> {
let baud = self.0 & Self::BAUD_MASK;
Ok(CCtrlBaud::try_from(baud)?)
}
#[expect(dead_code)]
pub(super) fn size(&self) -> Result<CCtrlSize> {
let size = self.0 & Self::SIZE_MASK;
Ok(CCtrlSize::try_from(size)?)
}
#[expect(dead_code)]
pub(super) fn is_read(&self) -> bool {
self.0 & Self::READ_BIT != 0
}
}
/// The size part of the control flags ([`CCtrlFlags`]).
#[repr(u32)]
#[derive(Clone, Copy, TryFromInt)]
pub(super) enum CCtrlSize {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L97
CS5 = 0x00000000,
CS6 = 0x00000010,
CS7 = 0x00000020,
CS8 = 0x00000030,
}
/// The baud part of the control flags ([`CCtrlFlags`]).
#[repr(u32)]
#[derive(Debug, Clone, Copy, TryFromInt)]
pub(super) enum CCtrlBaud {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits-common.h#L30
B0 = 0x00000000, /* hang up */
B50 = 0x00000001,
B75 = 0x00000002,
B110 = 0x00000003,
B134 = 0x00000004,
B150 = 0x00000005,
B200 = 0x00000006,
B300 = 0x00000007,
B600 = 0x00000008,
B1200 = 0x00000009,
B1800 = 0x0000000a,
B2400 = 0x0000000b,
B4800 = 0x0000000c,
B9600 = 0x0000000d,
B19200 = 0x0000000e,
B38400 = 0x0000000f,
}
bitflags! {
/// The local flags; `c_lflags` bits in Linux.
#[repr(C)]
#[derive(Pod)]
pub struct CLocalFlags: u32 {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L127
const ISIG = 0x00001;
const ICANON = 0x00002;
const XCASE = 0x00004;
const ECHO = 0x00008;
const ECHOE = 0x00010;
const ECHOK = 0x00020;
const ECHONL = 0x00040;
const NOFLSH = 0x00080;
const TOSTOP = 0x00100;
const ECHOCTL = 0x00200;
const ECHOPRT = 0x00400;
const ECHOKE = 0x00800;
const FLUSHO = 0x01000;
const PENDIN = 0x04000;
const IEXTEN = 0x08000;
const EXTPROC = 0x10000;
}
}
impl Default for CLocalFlags {
fn default() -> Self {
Self::ICANON
| Self::ECHO
| Self::ISIG
| Self::ECHOE
| Self::ECHOK
| Self::ECHOCTL
| Self::ECHOKE
| Self::IEXTEN
}
}
/// An index for a control character ([`CCtrlChar`]).
#[repr(u32)]
#[derive(Debug, Clone, Copy, TryFromInt)]
#[expect(clippy::upper_case_acronyms)]
pub enum CCtrlCharId {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L42
VINTR = 0,
VQUIT = 1,
VERASE = 2,
VKILL = 3,
VEOF = 4,
VTIME = 5,
VMIN = 6,
VSWTC = 7,
VSTART = 8,
VSTOP = 9,
VSUSP = 10,
VEOL = 11,
VREPRINT = 12,
VDISCARD = 13,
VWERASE = 14,
VLNEXT = 15,
VEOL2 = 16,
}
impl CCtrlCharId {
// The special char is from gvisor
pub(super) const fn default_char(&self) -> u8 {
const fn control_character(c: char) -> u8 {
debug_assert!(c as u8 >= b'A');
c as u8 - b'A' + 1u8
}
match self {
Self::VINTR => control_character('C'),
Self::VQUIT => control_character('\\'),
Self::VERASE => b'\x7f',
Self::VKILL => control_character('U'),
Self::VEOF => control_character('D'),
Self::VTIME => b'\0',
Self::VMIN => 1,
Self::VSWTC => b'\0',
Self::VSTART => control_character('Q'),
Self::VSTOP => control_character('S'),
Self::VSUSP => control_character('Z'),
Self::VEOL => b'\0',
Self::VREPRINT => control_character('R'),
Self::VDISCARD => control_character('O'),
Self::VWERASE => control_character('W'),
Self::VLNEXT => control_character('V'),
Self::VEOL2 => b'\0',
}
}
}
/// The termios; `struct termios` in Linux.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L30>.
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct CTermios {
c_iflags: CInputFlags,
c_oflags: COutputFlags,
c_cflags: CCtrlFlags,
c_lflags: CLocalFlags,
c_line: CCtrlChar,
c_cc: [CCtrlChar; Self::NUM_CTRL_CHARS],
}
impl Default for CTermios {
fn default() -> Self {
let mut termios = Self {
c_iflags: CInputFlags::default(),
c_oflags: COutputFlags::default(),
c_cflags: CCtrlFlags::default(),
c_lflags: CLocalFlags::default(),
c_line: 0,
c_cc: [CCtrlChar::default(); Self::NUM_CTRL_CHARS],
};
*termios.special_char_mut(CCtrlCharId::VINTR) = CCtrlCharId::VINTR.default_char();
*termios.special_char_mut(CCtrlCharId::VQUIT) = CCtrlCharId::VQUIT.default_char();
*termios.special_char_mut(CCtrlCharId::VERASE) = CCtrlCharId::VERASE.default_char();
*termios.special_char_mut(CCtrlCharId::VKILL) = CCtrlCharId::VKILL.default_char();
*termios.special_char_mut(CCtrlCharId::VEOF) = CCtrlCharId::VEOF.default_char();
*termios.special_char_mut(CCtrlCharId::VTIME) = CCtrlCharId::VTIME.default_char();
*termios.special_char_mut(CCtrlCharId::VMIN) = CCtrlCharId::VMIN.default_char();
*termios.special_char_mut(CCtrlCharId::VSWTC) = CCtrlCharId::VSWTC.default_char();
*termios.special_char_mut(CCtrlCharId::VSTART) = CCtrlCharId::VSTART.default_char();
*termios.special_char_mut(CCtrlCharId::VSTOP) = CCtrlCharId::VSTOP.default_char();
*termios.special_char_mut(CCtrlCharId::VSUSP) = CCtrlCharId::VSUSP.default_char();
*termios.special_char_mut(CCtrlCharId::VEOL) = CCtrlCharId::VEOL.default_char();
*termios.special_char_mut(CCtrlCharId::VREPRINT) = CCtrlCharId::VREPRINT.default_char();
*termios.special_char_mut(CCtrlCharId::VDISCARD) = CCtrlCharId::VDISCARD.default_char();
*termios.special_char_mut(CCtrlCharId::VWERASE) = CCtrlCharId::VWERASE.default_char();
*termios.special_char_mut(CCtrlCharId::VLNEXT) = CCtrlCharId::VLNEXT.default_char();
*termios.special_char_mut(CCtrlCharId::VEOL2) = CCtrlCharId::VEOL2.default_char();
termios
}
}
impl CTermios {
/// The number of the control characters.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L9>.
const NUM_CTRL_CHARS: usize = 19;
pub fn special_char(&self, id: CCtrlCharId) -> CCtrlChar {
self.c_cc[id as usize]
}
pub(super) fn special_char_mut(&mut self, id: CCtrlCharId) -> &mut CCtrlChar {
&mut self.c_cc[id as usize]
}
/// Returns whether the terminal is in the canonical mode.
///
/// The canonical mode means that the input characters will be handled by lines, not by single
/// characters.
pub(super) fn is_canonical_mode(&self) -> bool {
self.c_lflags.contains(CLocalFlags::ICANON)
}
/// Returns the input flags.
pub fn input_flags(&self) -> &CInputFlags {
&self.c_iflags
}
/// Returns the local flags.
pub fn local_flags(&self) -> &CLocalFlags {
&self.c_lflags
}
}
/// A window size; `struct winsize` in Linux.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termios.h#L15>.
#[derive(Debug, Clone, Copy, Default, Pod)]
#[repr(C)]
pub struct CWinSize {
ws_row: u16,
ws_col: u16,
ws_xpixel: u16,
ws_ypixel: u16,
}
/// A font operation; `struct console_font_op` in Linux.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.15/source/include/uapi/linux/kd.h#L159>.
#[derive(Debug, Clone, Copy, Default, Pod)]
#[repr(C)]
pub struct CFontOp {
pub(super) op: u32,
pub(super) flags: u32,
pub(super) width: u32,
pub(super) height: u32,
pub(super) charcount: u32,
pub(super) data: usize,
}
impl CFontOp {
// https://elixir.bootlin.com/linux/v6.15/source/include/uapi/linux/kd.h#L177
pub(super) const OP_SET: u32 = 0;
pub(super) const OP_SET_DEFAULT: u32 = 2;
pub(super) const OP_SET_TALL: u32 = 4;
// https://elixir.bootlin.com/linux/v6.15/source/drivers/tty/vt/vt.c#L4711
pub(super) const MAX_WIDTH: u32 = 64;
pub(super) const MAX_HEIGHT: u32 = 128;
pub(super) const MAX_CHARCOUNT: u32 = 512;
// https://elixir.bootlin.com/linux/v6.15/source/drivers/tty/vt/vt.c#L4721
pub(super) const NONTALL_VPITCH: u32 = 32;
}