Clean up TTY termios definitions

This commit is contained in:
Ruihan Li
2025-06-05 21:29:18 +08:00
committed by Jianfeng Jiang
parent 8583eea62b
commit 71e08b3942
2 changed files with 169 additions and 144 deletions

View File

@ -3,7 +3,7 @@
use ostd::const_assert;
use super::{
termio::{KernelTermios, WinSize, CC_C_CHAR},
termio::{CCtrlCharId, CTermios, CWinSize},
PushCharError,
};
use crate::{
@ -32,9 +32,9 @@ pub struct LineDiscipline {
/// Read buffer
read_buffer: RingBuffer<u8>,
/// Termios
termios: KernelTermios,
termios: CTermios,
/// Window size
winsize: WinSize,
winsize: CWinSize,
}
struct CurrentLine {
@ -89,8 +89,8 @@ impl LineDiscipline {
Self {
current_line: CurrentLine::default(),
read_buffer: RingBuffer::new(BUFFER_CAPACITY),
termios: KernelTermios::default(),
winsize: WinSize::default(),
termios: CTermios::default(),
winsize: CWinSize::default(),
}
}
@ -134,12 +134,12 @@ impl LineDiscipline {
// Canonical mode
if ch == *self.termios.get_special_char(CC_C_CHAR::VKILL) {
if ch == self.termios.special_char(CCtrlCharId::VKILL) {
// Erase current line
self.current_line.drain();
}
if ch == *self.termios.get_special_char(CC_C_CHAR::VERASE) {
if ch == self.termios.special_char(CCtrlCharId::VERASE) {
// Type backspace
self.current_line.backspace();
}
@ -166,7 +166,7 @@ impl LineDiscipline {
match ch {
b'\n' => echo_callback(b"\n"),
b'\r' => echo_callback(b"\r\n"),
ch if ch == *self.termios.get_special_char(CC_C_CHAR::VERASE) => {
ch if ch == self.termios.special_char(CCtrlCharId::VERASE) => {
// Write a space to overwrite the current character
echo_callback(b"\x08 \x08");
}
@ -185,8 +185,8 @@ impl LineDiscipline {
/// If no bytes are available or the available bytes are fewer than `min(dst.len(), vmin)`,
/// this method returns [`Errno::EAGAIN`].
pub fn try_read(&mut self, dst: &mut [u8]) -> Result<usize> {
let vmin = *self.termios.get_special_char(CC_C_CHAR::VMIN);
let vtime = *self.termios.get_special_char(CC_C_CHAR::VTIME);
let vmin = self.termios.special_char(CCtrlCharId::VMIN);
let vtime = self.termios.special_char(CCtrlCharId::VTIME);
if vtime != 0 {
warn!("non-zero VTIME is not supported");
@ -236,40 +236,40 @@ impl LineDiscipline {
self.read_buffer.len() + self.current_line.len() >= self.read_buffer.capacity()
}
pub fn termios(&self) -> &KernelTermios {
pub fn termios(&self) -> &CTermios {
&self.termios
}
pub fn set_termios(&mut self, termios: KernelTermios) {
pub fn set_termios(&mut self, termios: CTermios) {
self.termios = termios;
}
pub fn window_size(&self) -> WinSize {
pub fn window_size(&self) -> CWinSize {
self.winsize
}
pub fn set_window_size(&mut self, winsize: WinSize) {
pub fn set_window_size(&mut self, winsize: CWinSize) {
self.winsize = winsize;
}
}
fn is_line_terminator(ch: u8, termios: &KernelTermios) -> bool {
fn is_line_terminator(ch: u8, termios: &CTermios) -> bool {
if ch == b'\n'
|| ch == *termios.get_special_char(CC_C_CHAR::VEOF)
|| ch == *termios.get_special_char(CC_C_CHAR::VEOL)
|| ch == termios.special_char(CCtrlCharId::VEOF)
|| ch == termios.special_char(CCtrlCharId::VEOL)
{
return true;
}
if termios.contains_iexten() && ch == *termios.get_special_char(CC_C_CHAR::VEOL2) {
if termios.contains_iexten() && ch == termios.special_char(CCtrlCharId::VEOL2) {
return true;
}
false
}
fn is_eof(ch: u8, termios: &KernelTermios) -> bool {
ch == *termios.get_special_char(CC_C_CHAR::VEOF)
fn is_eof(ch: u8, termios: &CTermios) -> bool {
ch == termios.special_char(CCtrlCharId::VEOF)
}
fn is_printable_char(ch: u8) -> bool {
@ -284,14 +284,14 @@ fn is_ctrl_char(ch: u8) -> bool {
(0..0x20).contains(&ch)
}
fn char_to_signal(ch: u8, termios: &KernelTermios) -> Option<SigNum> {
fn char_to_signal(ch: u8, termios: &CTermios) -> Option<SigNum> {
if !termios.is_canonical_mode() || !termios.contains_isig() {
return None;
}
match ch {
ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => Some(SIGINT),
ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => Some(SIGQUIT),
ch if ch == termios.special_char(CCtrlCharId::VINTR) => Some(SIGINT),
ch if ch == termios.special_char(CCtrlCharId::VQUIT) => Some(SIGQUIT),
_ => None,
}
}

View File

@ -1,21 +1,17 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(dead_code)]
#![expect(non_camel_case_types)]
use crate::prelude::*;
// This definition is from occlum
const KERNEL_NCCS: usize = 19;
type TcflagT = u32;
type CcT = u8;
type SpeedT = u32;
/// 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 C_IFLAGS: u32 {
pub(super) 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 */
@ -36,16 +32,18 @@ bitflags! {
}
}
impl Default for C_IFLAGS {
impl Default for CInputFlags {
fn default() -> Self {
C_IFLAGS::ICRNL | C_IFLAGS::IXON
Self::ICRNL | Self::IXON
}
}
bitflags! {
/// The output flags; `c_oflags` bits in Linux.
#[repr(C)]
#[derive(Pod)]
pub struct C_OFLAGS: u32 {
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;
@ -57,57 +55,65 @@ bitflags! {
}
}
impl Default for C_OFLAGS {
impl Default for COutputFlags {
fn default() -> Self {
C_OFLAGS::OPOST | C_OFLAGS::ONLCR
Self::OPOST | Self::ONLCR
}
}
/// The control flags; `c_cflags` bits in Linux.
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod)]
pub struct C_CFLAGS(u32);
pub(super) struct CCtrlFlags(u32);
impl Default for C_CFLAGS {
impl Default for CCtrlFlags {
fn default() -> Self {
let cbaud = C_CFLAGS_BAUD::B38400 as u32;
let csize = C_CFLAGS_CSIZE::CS8 as u32;
let c_cflags = cbaud | csize | CREAD;
let cbaud = CCtrlBaud::B38400 as u32;
let csize = CCtrlSize::CS8 as u32;
let c_cflags = cbaud | csize | Self::READ_BIT;
Self(c_cflags)
}
}
impl C_CFLAGS {
pub fn cbaud(&self) -> Result<C_CFLAGS_BAUD> {
let cbaud = self.0 & CBAUD_MASK;
Ok(C_CFLAGS_BAUD::try_from(cbaud)?)
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)?)
}
pub fn csize(&self) -> Result<C_CFLAGS_CSIZE> {
let csize = self.0 & CSIZE_MASK;
Ok(C_CFLAGS_CSIZE::try_from(csize)?)
#[expect(dead_code)]
pub(super) fn size(&self) -> Result<CCtrlSize> {
let size = self.0 & Self::SIZE_MASK;
Ok(CCtrlSize::try_from(size)?)
}
pub fn cread(&self) -> bool {
self.0 & CREAD != 0
#[expect(dead_code)]
pub(super) fn is_read(&self) -> bool {
self.0 & Self::READ_BIT != 0
}
}
const CREAD: u32 = 0x00000080;
const CBAUD_MASK: u32 = 0x0000100f;
const CSIZE_MASK: u32 = 0x00000030;
/// The size part of the control flags ([`CCtrlFlags`]).
#[repr(u32)]
#[derive(Clone, Copy, TryFromInt)]
pub enum C_CFLAGS_CSIZE {
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 enum C_CFLAGS_BAUD {
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,
@ -127,9 +133,11 @@ pub enum C_CFLAGS_BAUD {
}
bitflags! {
/// The local flags; `c_lflags` bits in Linux.
#[repr(C)]
#[derive(Pod)]
pub struct C_LFLAGS: u32 {
pub(super) 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;
@ -149,24 +157,25 @@ bitflags! {
}
}
impl Default for C_LFLAGS {
impl Default for CLocalFlags {
fn default() -> Self {
C_LFLAGS::ICANON
| C_LFLAGS::ECHO
| C_LFLAGS::ISIG
| C_LFLAGS::ECHOE
| C_LFLAGS::ECHOK
| C_LFLAGS::ECHOCTL
| C_LFLAGS::ECHOKE
| C_LFLAGS::IEXTEN
Self::ICANON
| Self::ECHO
| Self::ISIG
| Self::ECHOE
| Self::ECHOK
| Self::ECHOCTL
| Self::ECHOKE
| Self::IEXTEN
}
}
/* c_cc characters index*/
/// An index for a control character ([`CCtrlChar`]).
#[repr(u32)]
#[derive(Debug, Clone, Copy, TryFromInt)]
#[expect(clippy::upper_case_acronyms)]
pub enum CC_C_CHAR {
pub(super) enum CCtrlCharId {
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L42
VINTR = 0,
VQUIT = 1,
VERASE = 2,
@ -186,117 +195,133 @@ pub enum CC_C_CHAR {
VEOL2 = 16,
}
impl CC_C_CHAR {
impl CCtrlCharId {
// The special char is from gvisor
pub fn default_char(&self) -> u8 {
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 {
CC_C_CHAR::VINTR => control_character('C'),
CC_C_CHAR::VQUIT => control_character('\\'),
CC_C_CHAR::VERASE => b'\x7f',
CC_C_CHAR::VKILL => control_character('U'),
CC_C_CHAR::VEOF => control_character('D'),
CC_C_CHAR::VTIME => b'\0',
CC_C_CHAR::VMIN => 1,
CC_C_CHAR::VSWTC => b'\0',
CC_C_CHAR::VSTART => control_character('Q'),
CC_C_CHAR::VSTOP => control_character('S'),
CC_C_CHAR::VSUSP => control_character('Z'),
CC_C_CHAR::VEOL => b'\0',
CC_C_CHAR::VREPRINT => control_character('R'),
CC_C_CHAR::VDISCARD => control_character('O'),
CC_C_CHAR::VWERASE => control_character('W'),
CC_C_CHAR::VLNEXT => control_character('V'),
CC_C_CHAR::VEOL2 => b'\0',
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 KernelTermios {
c_iflags: C_IFLAGS,
c_oflags: C_OFLAGS,
c_cflags: C_CFLAGS,
c_lflags: C_LFLAGS,
c_line: CcT,
c_cc: [CcT; KERNEL_NCCS],
pub(super) 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 KernelTermios {
impl Default for CTermios {
fn default() -> Self {
let mut termios = Self {
c_iflags: C_IFLAGS::default(),
c_oflags: C_OFLAGS::default(),
c_cflags: C_CFLAGS::default(),
c_lflags: C_LFLAGS::default(),
c_iflags: CInputFlags::default(),
c_oflags: COutputFlags::default(),
c_cflags: CCtrlFlags::default(),
c_lflags: CLocalFlags::default(),
c_line: 0,
c_cc: [CcT::default(); KERNEL_NCCS],
c_cc: [CCtrlChar::default(); Self::NUM_CTRL_CHARS],
};
*termios.get_special_char_mut(CC_C_CHAR::VINTR) = CC_C_CHAR::VINTR.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VQUIT) = CC_C_CHAR::VQUIT.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VERASE) = CC_C_CHAR::VERASE.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VKILL) = CC_C_CHAR::VKILL.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VEOF) = CC_C_CHAR::VEOF.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VTIME) = CC_C_CHAR::VTIME.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VMIN) = CC_C_CHAR::VMIN.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSWTC) = CC_C_CHAR::VSWTC.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSTART) = CC_C_CHAR::VSTART.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSTOP) = CC_C_CHAR::VSTOP.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSUSP) = CC_C_CHAR::VSUSP.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL) = CC_C_CHAR::VEOL.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VREPRINT) = CC_C_CHAR::VREPRINT.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VDISCARD) = CC_C_CHAR::VDISCARD.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VWERASE) = CC_C_CHAR::VWERASE.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VLNEXT) = CC_C_CHAR::VLNEXT.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL2) = CC_C_CHAR::VEOL2.default_char();
*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 KernelTermios {
pub fn get_special_char(&self, cc_c_char: CC_C_CHAR) -> &CcT {
&self.c_cc[cc_c_char as usize]
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(super) fn special_char(&self, id: CCtrlCharId) -> CCtrlChar {
self.c_cc[id as usize]
}
pub fn get_special_char_mut(&mut self, cc_c_char: CC_C_CHAR) -> &mut CcT {
&mut self.c_cc[cc_c_char as usize]
pub(super) fn special_char_mut(&mut self, id: CCtrlCharId) -> &mut CCtrlChar {
&mut self.c_cc[id as usize]
}
/// Canonical mode means we will handle input by lines, not by single character
pub fn is_canonical_mode(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ICANON)
/// 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)
}
/// ICRNL means we should map \r to \n
pub fn contains_icrnl(&self) -> bool {
self.c_iflags.contains(C_IFLAGS::ICRNL)
/// Returns whether the input flags contain `ICRNL`.
///
/// The `ICRNL` flag means the `\r` characters in the input should be mapped to `\n`.
pub(super) fn contains_icrnl(&self) -> bool {
self.c_iflags.contains(CInputFlags::ICRNL)
}
pub fn contains_isig(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ISIG)
pub(super) fn contains_isig(&self) -> bool {
self.c_lflags.contains(CLocalFlags::ISIG)
}
pub fn contain_echo(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ECHO)
pub(super) fn contain_echo(&self) -> bool {
self.c_lflags.contains(CLocalFlags::ECHO)
}
pub fn contains_echo_ctl(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ECHOCTL)
pub(super) fn contains_echo_ctl(&self) -> bool {
self.c_lflags.contains(CLocalFlags::ECHOCTL)
}
pub fn contains_iexten(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::IEXTEN)
pub(super) fn contains_iexten(&self) -> bool {
self.c_lflags.contains(CLocalFlags::IEXTEN)
}
}
const fn control_character(c: char) -> u8 {
debug_assert!(c as u8 >= b'A');
c as u8 - b'A' + 1u8
}
/// A window size; `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 WinSize {
pub(super) struct CWinSize {
ws_row: u16,
ws_col: u16,
ws_xpixel: u16,