1 use core::{ 2 fmt::Debug, 3 sync::atomic::{AtomicBool, AtomicUsize}, 4 }; 5 6 use alloc::{collections::LinkedList, string::String, sync::Arc, vec::Vec}; 7 use system_error::SystemError; 8 9 use crate::{ 10 driver::serial::serial8250::send_to_default_serial8250_port, 11 libs::{ 12 rwlock::{RwLock, RwLockReadGuard, RwLockUpgradableGuard, RwLockWriteGuard}, 13 spinlock::{SpinLock, SpinLockGuard}, 14 wait_queue::EventWaitQueue, 15 }, 16 mm::VirtAddr, 17 net::event_poll::{EPollEventType, EPollItem}, 18 process::Pid, 19 syscall::user_access::{UserBufferReader, UserBufferWriter}, 20 }; 21 22 use super::{ 23 termios::{ControlMode, PosixTermios, Termios, TtySetTermiosOpt, WindowSize}, 24 tty_driver::{TtyDriver, TtyDriverSubType, TtyDriverType, TtyOperation}, 25 tty_ldisc::{ 26 ntty::{NTtyData, NTtyLinediscipline}, 27 TtyLineDiscipline, 28 }, 29 tty_port::TtyPort, 30 virtual_terminal::{virtual_console::VirtualConsoleData, VIRT_CONSOLES}, 31 }; 32 33 #[derive(Debug)] 34 pub struct TtyCore { 35 core: TtyCoreData, 36 /// 线路规程函数集 37 line_discipline: Arc<dyn TtyLineDiscipline>, 38 } 39 40 impl TtyCore { new(driver: Arc<TtyDriver>, index: usize) -> Arc<Self>41 pub fn new(driver: Arc<TtyDriver>, index: usize) -> Arc<Self> { 42 let name = driver.tty_line_name(index); 43 let termios = driver.init_termios(); 44 let core = TtyCoreData { 45 tty_driver: driver, 46 termios: RwLock::new(termios), 47 name, 48 flags: RwLock::new(TtyFlag::empty()), 49 count: AtomicUsize::new(0), 50 window_size: RwLock::new(WindowSize::default()), 51 read_wq: EventWaitQueue::new(), 52 write_wq: EventWaitQueue::new(), 53 port: RwLock::new(None), 54 index, 55 ctrl: SpinLock::new(TtyContorlInfo::default()), 56 closing: AtomicBool::new(false), 57 flow: SpinLock::new(TtyFlowState::default()), 58 link: None, 59 epitems: SpinLock::new(LinkedList::new()), 60 }; 61 62 return Arc::new(Self { 63 core, 64 line_discipline: Arc::new(NTtyLinediscipline { 65 data: SpinLock::new(NTtyData::new()), 66 }), 67 }); 68 } 69 70 #[inline] core(&self) -> &TtyCoreData71 pub fn core(&self) -> &TtyCoreData { 72 return &self.core; 73 } 74 75 #[inline] ldisc(&self) -> Arc<dyn TtyLineDiscipline>76 pub fn ldisc(&self) -> Arc<dyn TtyLineDiscipline> { 77 self.line_discipline.clone() 78 } 79 write_without_serial(&self, buf: &[u8], nr: usize) -> Result<usize, SystemError>80 pub fn write_without_serial(&self, buf: &[u8], nr: usize) -> Result<usize, SystemError> { 81 self.core 82 .driver() 83 .driver_funcs() 84 .write(self.core(), buf, nr) 85 } 86 reopen(&self) -> Result<(), SystemError>87 pub fn reopen(&self) -> Result<(), SystemError> { 88 let tty_core = self.core(); 89 let driver = tty_core.driver(); 90 91 if driver.tty_driver_type() == TtyDriverType::Pty 92 && driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster 93 { 94 return Err(SystemError::EIO); 95 } 96 97 // if *tty_core.count.read() == 0 { 98 // return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); 99 // } 100 101 // TODO 判断flags 102 103 tty_core.add_count(); 104 105 Ok(()) 106 } 107 108 #[inline] set_port(&self, port: Arc<dyn TtyPort>)109 pub fn set_port(&self, port: Arc<dyn TtyPort>) { 110 *self.core.port.write() = Some(port); 111 } 112 tty_start(&self)113 pub fn tty_start(&self) { 114 let mut flow = self.core.flow.lock_irqsave(); 115 if !flow.stopped || flow.tco_stopped { 116 return; 117 } 118 119 flow.stopped = false; 120 let _ = self.start(self.core()); 121 self.tty_wakeup(); 122 } 123 tty_stop(&self)124 pub fn tty_stop(&self) { 125 let mut flow = self.core.flow.lock_irqsave(); 126 if flow.stopped { 127 return; 128 } 129 flow.stopped = true; 130 131 let _ = self.stop(self.core()); 132 } 133 tty_wakeup(&self)134 pub fn tty_wakeup(&self) { 135 if self.core.flags().contains(TtyFlag::DO_WRITE_WAKEUP) { 136 let _ = self.ldisc().write_wakeup(self.core()); 137 } 138 139 self.core() 140 .write_wq 141 .wakeup(EPollEventType::EPOLLOUT.bits() as u64); 142 } 143 tty_mode_ioctl(tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<usize, SystemError>144 pub fn tty_mode_ioctl(tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<usize, SystemError> { 145 let real_tty; 146 let core = tty.core(); 147 if core.driver().tty_driver_type() == TtyDriverType::Pty 148 && core.driver().tty_driver_sub_type() == TtyDriverSubType::PtyMaster 149 { 150 real_tty = core.link().unwrap(); 151 } else { 152 real_tty = tty; 153 } 154 match cmd { 155 TtyIoctlCmd::TCGETS => { 156 let termios = PosixTermios::from_kernel_termios(real_tty.core.termios().clone()); 157 let mut user_writer = UserBufferWriter::new( 158 VirtAddr::new(arg).as_ptr::<PosixTermios>(), 159 core::mem::size_of::<PosixTermios>(), 160 true, 161 )?; 162 163 user_writer.copy_one_to_user(&termios, 0)?; 164 return Ok(0); 165 } 166 TtyIoctlCmd::TCSETS => { 167 return TtyCore::core_set_termios( 168 real_tty, 169 VirtAddr::new(arg), 170 TtySetTermiosOpt::TERMIOS_OLD, 171 ); 172 } 173 TtyIoctlCmd::TCSETSW => { 174 return TtyCore::core_set_termios( 175 real_tty, 176 VirtAddr::new(arg), 177 TtySetTermiosOpt::TERMIOS_WAIT | TtySetTermiosOpt::TERMIOS_OLD, 178 ); 179 } 180 _ => { 181 return Err(SystemError::ENOIOCTLCMD); 182 } 183 } 184 } 185 core_set_termios( tty: Arc<TtyCore>, arg: VirtAddr, opt: TtySetTermiosOpt, ) -> Result<usize, SystemError>186 pub fn core_set_termios( 187 tty: Arc<TtyCore>, 188 arg: VirtAddr, 189 opt: TtySetTermiosOpt, 190 ) -> Result<usize, SystemError> { 191 #[allow(unused_assignments)] 192 // TERMIOS_TERMIO下会用到 193 let mut tmp_termios = tty.core().termios().clone(); 194 195 if opt.contains(TtySetTermiosOpt::TERMIOS_TERMIO) { 196 todo!() 197 } else { 198 let user_reader = UserBufferReader::new( 199 arg.as_ptr::<PosixTermios>(), 200 core::mem::size_of::<PosixTermios>(), 201 true, 202 )?; 203 204 let mut term = PosixTermios::default(); 205 user_reader.copy_one_from_user(&mut term, 0)?; 206 207 tmp_termios = term.to_kernel_termios(); 208 } 209 210 if opt.contains(TtySetTermiosOpt::TERMIOS_FLUSH) { 211 let ld = tty.ldisc(); 212 let _ = ld.flush_buffer(tty.clone()); 213 } 214 215 if opt.contains(TtySetTermiosOpt::TERMIOS_WAIT) { 216 // TODO 217 } 218 219 TtyCore::set_termios_next(tty, tmp_termios)?; 220 Ok(0) 221 } 222 set_termios_next(tty: Arc<TtyCore>, new_termios: Termios) -> Result<(), SystemError>223 pub fn set_termios_next(tty: Arc<TtyCore>, new_termios: Termios) -> Result<(), SystemError> { 224 let mut termios = tty.core().termios_write(); 225 226 let old_termios = termios.clone(); 227 *termios = new_termios; 228 let tmp = termios.control_mode; 229 termios.control_mode ^= (tmp ^ old_termios.control_mode) & ControlMode::ADDRB; 230 231 let ret = tty.set_termios(tty.clone(), old_termios); 232 if ret.is_err() { 233 termios.control_mode &= ControlMode::HUPCL | ControlMode::CREAD | ControlMode::CLOCAL; 234 termios.control_mode |= old_termios.control_mode 235 & !(ControlMode::HUPCL | ControlMode::CREAD | ControlMode::CLOCAL); 236 termios.input_speed = old_termios.input_speed; 237 termios.output_speed = old_termios.output_speed; 238 } 239 240 drop(termios); 241 let ld = tty.ldisc(); 242 ld.set_termios(tty, Some(old_termios))?; 243 244 Ok(()) 245 } 246 } 247 248 #[derive(Debug)] 249 pub struct TtyContorlInfo { 250 /// 前台进程pid 251 pub session: Option<Pid>, 252 /// 前台进程组id 253 pub pgid: Option<Pid>, 254 255 /// packet模式下使用,目前未用到 256 pub pktstatus: u8, 257 pub packet: bool, 258 } 259 260 impl Default for TtyContorlInfo { default() -> Self261 fn default() -> Self { 262 Self { 263 session: None, 264 pgid: None, 265 pktstatus: Default::default(), 266 packet: Default::default(), 267 } 268 } 269 } 270 271 #[derive(Debug, Default)] 272 pub struct TtyCoreWriteData { 273 /// 写缓冲区 274 pub write_buf: Vec<u8>, 275 /// 写入数量 276 pub write_cnt: usize, 277 } 278 279 #[derive(Debug, Default)] 280 pub struct TtyFlowState { 281 /// 表示流控是否被停止 282 pub stopped: bool, 283 /// 表示 TCO(Transmit Continuous Operation)流控是否被停止 284 pub tco_stopped: bool, 285 } 286 287 #[derive(Debug)] 288 pub struct TtyCoreData { 289 tty_driver: Arc<TtyDriver>, 290 termios: RwLock<Termios>, 291 name: String, 292 flags: RwLock<TtyFlag>, 293 /// 在初始化时即确定不会更改,所以这里不用加锁 294 index: usize, 295 count: AtomicUsize, 296 /// 窗口大小 297 window_size: RwLock<WindowSize>, 298 /// 读等待队列 299 read_wq: EventWaitQueue, 300 /// 写等待队列 301 write_wq: EventWaitQueue, 302 /// 端口 303 port: RwLock<Option<Arc<dyn TtyPort>>>, 304 /// 前台进程 305 ctrl: SpinLock<TtyContorlInfo>, 306 /// 是否正在关闭 307 closing: AtomicBool, 308 /// 流控状态 309 flow: SpinLock<TtyFlowState>, 310 /// 链接tty 311 link: Option<Arc<TtyCore>>, 312 /// epitems 313 epitems: SpinLock<LinkedList<Arc<EPollItem>>>, 314 } 315 316 impl TtyCoreData { 317 #[inline] driver(&self) -> Arc<TtyDriver>318 pub fn driver(&self) -> Arc<TtyDriver> { 319 self.tty_driver.clone() 320 } 321 322 #[inline] flow_irqsave(&self) -> SpinLockGuard<TtyFlowState>323 pub fn flow_irqsave(&self) -> SpinLockGuard<TtyFlowState> { 324 self.flow.lock_irqsave() 325 } 326 327 #[inline] port(&self) -> Option<Arc<dyn TtyPort>>328 pub fn port(&self) -> Option<Arc<dyn TtyPort>> { 329 self.port.read().clone() 330 } 331 332 #[inline] index(&self) -> usize333 pub fn index(&self) -> usize { 334 self.index 335 } 336 337 #[inline] name(&self) -> String338 pub fn name(&self) -> String { 339 self.name.clone() 340 } 341 342 #[inline] flags(&self) -> TtyFlag343 pub fn flags(&self) -> TtyFlag { 344 self.flags.read_irqsave().clone() 345 } 346 347 #[inline] termios(&self) -> RwLockReadGuard<'_, Termios>348 pub fn termios(&self) -> RwLockReadGuard<'_, Termios> { 349 self.termios.read_irqsave() 350 } 351 352 #[inline] termios_write(&self) -> RwLockWriteGuard<Termios>353 pub fn termios_write(&self) -> RwLockWriteGuard<Termios> { 354 self.termios.write_irqsave() 355 } 356 357 #[inline] set_termios(&self, termios: Termios)358 pub fn set_termios(&self, termios: Termios) { 359 let mut termios_guard = self.termios_write(); 360 *termios_guard = termios; 361 } 362 363 #[inline] add_count(&self)364 pub fn add_count(&self) { 365 self.count 366 .fetch_add(1, core::sync::atomic::Ordering::SeqCst); 367 } 368 369 #[inline] read_wq(&self) -> &EventWaitQueue370 pub fn read_wq(&self) -> &EventWaitQueue { 371 &self.read_wq 372 } 373 374 #[inline] write_wq(&self) -> &EventWaitQueue375 pub fn write_wq(&self) -> &EventWaitQueue { 376 &self.write_wq 377 } 378 379 #[inline] contorl_info_irqsave(&self) -> SpinLockGuard<TtyContorlInfo>380 pub fn contorl_info_irqsave(&self) -> SpinLockGuard<TtyContorlInfo> { 381 self.ctrl.lock_irqsave() 382 } 383 384 #[inline] window_size_upgradeable(&self) -> RwLockUpgradableGuard<WindowSize>385 pub fn window_size_upgradeable(&self) -> RwLockUpgradableGuard<WindowSize> { 386 self.window_size.upgradeable_read() 387 } 388 389 #[inline] window_size(&self) -> RwLockReadGuard<WindowSize>390 pub fn window_size(&self) -> RwLockReadGuard<WindowSize> { 391 self.window_size.read() 392 } 393 394 #[inline] is_closing(&self) -> bool395 pub fn is_closing(&self) -> bool { 396 self.closing.load(core::sync::atomic::Ordering::SeqCst) 397 } 398 399 #[inline] vc_data_irqsave(&self) -> SpinLockGuard<VirtualConsoleData>400 pub fn vc_data_irqsave(&self) -> SpinLockGuard<VirtualConsoleData> { 401 VIRT_CONSOLES[self.index].lock_irqsave() 402 } 403 404 #[inline] link(&self) -> Option<Arc<TtyCore>>405 pub fn link(&self) -> Option<Arc<TtyCore>> { 406 self.link.clone() 407 } 408 409 #[inline] add_epitem(&self, epitem: Arc<EPollItem>)410 pub fn add_epitem(&self, epitem: Arc<EPollItem>) { 411 self.epitems.lock().push_back(epitem) 412 } 413 } 414 415 /// TTY 核心接口,不同的tty需要各自实现这个trait 416 pub trait TtyCoreFuncs: Debug + Send + Sync {} 417 418 impl TtyOperation for TtyCore { 419 #[inline] open(&self, tty: &TtyCoreData) -> Result<(), SystemError>420 fn open(&self, tty: &TtyCoreData) -> Result<(), SystemError> { 421 return self.core().tty_driver.driver_funcs().open(tty); 422 } 423 424 #[inline] write_room(&self, tty: &TtyCoreData) -> usize425 fn write_room(&self, tty: &TtyCoreData) -> usize { 426 return self.core().tty_driver.driver_funcs().write_room(tty); 427 } 428 429 #[inline] write(&self, tty: &TtyCoreData, buf: &[u8], nr: usize) -> Result<usize, SystemError>430 fn write(&self, tty: &TtyCoreData, buf: &[u8], nr: usize) -> Result<usize, SystemError> { 431 send_to_default_serial8250_port(buf); 432 return self.core().tty_driver.driver_funcs().write(tty, buf, nr); 433 } 434 435 #[inline] flush_chars(&self, tty: &TtyCoreData)436 fn flush_chars(&self, tty: &TtyCoreData) { 437 self.core().tty_driver.driver_funcs().flush_chars(tty); 438 } 439 440 #[inline] put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError>441 fn put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError> { 442 return self.core().tty_driver.driver_funcs().put_char(tty, ch); 443 } 444 445 #[inline] install(&self, driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError>446 fn install(&self, driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError> { 447 return self.core().tty_driver.driver_funcs().install(driver, tty); 448 } 449 450 #[inline] start(&self, tty: &TtyCoreData) -> Result<(), SystemError>451 fn start(&self, tty: &TtyCoreData) -> Result<(), SystemError> { 452 return self.core().tty_driver.driver_funcs().start(tty); 453 } 454 455 #[inline] stop(&self, tty: &TtyCoreData) -> Result<(), SystemError>456 fn stop(&self, tty: &TtyCoreData) -> Result<(), SystemError> { 457 return self.core().tty_driver.driver_funcs().stop(tty); 458 } 459 460 #[inline] ioctl(&self, tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<(), SystemError>461 fn ioctl(&self, tty: Arc<TtyCore>, cmd: u32, arg: usize) -> Result<(), SystemError> { 462 return self.core().tty_driver.driver_funcs().ioctl(tty, cmd, arg); 463 } 464 465 #[inline] chars_in_buffer(&self) -> usize466 fn chars_in_buffer(&self) -> usize { 467 return self.core().tty_driver.driver_funcs().chars_in_buffer(); 468 } 469 470 #[inline] set_termios(&self, tty: Arc<TtyCore>, old_termios: Termios) -> Result<(), SystemError>471 fn set_termios(&self, tty: Arc<TtyCore>, old_termios: Termios) -> Result<(), SystemError> { 472 return self 473 .core() 474 .tty_driver 475 .driver_funcs() 476 .set_termios(tty, old_termios); 477 } 478 } 479 480 bitflags! { 481 pub struct TtyFlag: u32 { 482 /// 终端被节流 483 const THROTTLED = 1 << 0; 484 /// 终端输入输出错误状态 485 const IO_ERROR = 1 << 1; 486 /// 终端的其他一方已关闭 487 const OTHER_CLOSED = 1 << 2; 488 /// 终端处于独占状态 489 const EXCLUSIVE = 1 << 3; 490 /// 终端执行写唤醒操作 491 const DO_WRITE_WAKEUP = 1 << 5; 492 /// 终端线路驱动程序已打开 493 const LDISC_OPEN = 1 << 11; 494 /// 终端伪终端设备已锁定 495 const PTY_LOCK = 1 << 16; 496 /// 终端禁用写分裂操作 497 const NO_WRITE_SPLIT = 1 << 17; 498 /// 终端挂断(挂起)状态 499 const HUPPED = 1 << 18; 500 /// 终端正在挂断(挂起) 501 const HUPPING = 1 << 19; 502 /// 终端线路驱动程序正在更改 503 const LDISC_CHANGING = 1 << 20; 504 /// 终端线路驱动程序已停止 505 const LDISC_HALTED = 1 << 22; 506 } 507 } 508 509 #[derive(Debug, PartialEq)] 510 pub enum EchoOperation { 511 /// 开始特殊操作。 512 Start, 513 /// 向后移动光标列。 514 MoveBackCol, 515 /// 设置规范模式下的列位置。 516 SetCanonCol, 517 /// 擦除制表符。 518 EraseTab, 519 520 Undefined(u8), 521 } 522 523 impl EchoOperation { from_u8(num: u8) -> EchoOperation524 pub fn from_u8(num: u8) -> EchoOperation { 525 match num { 526 0xff => Self::Start, 527 0x80 => Self::MoveBackCol, 528 0x81 => Self::SetCanonCol, 529 0x82 => Self::EraseTab, 530 _ => Self::Undefined(num), 531 } 532 } 533 to_u8(&self) -> u8534 pub fn to_u8(&self) -> u8 { 535 match *self { 536 EchoOperation::Start => 0xff, 537 EchoOperation::MoveBackCol => 0x80, 538 EchoOperation::SetCanonCol => 0x81, 539 EchoOperation::EraseTab => 0x82, 540 EchoOperation::Undefined(num) => num, 541 } 542 } 543 } 544 545 pub struct TtyIoctlCmd; 546 547 #[allow(dead_code)] 548 impl TtyIoctlCmd { 549 /// 获取终端参数 550 pub const TCGETS: u32 = 0x5401; 551 /// 设置终端参数 552 pub const TCSETS: u32 = 0x5402; 553 /// 设置终端参数并等待所有输出完成 554 pub const TCSETSW: u32 = 0x5403; 555 /// 设置终端参数并且等待所有输出完成,但在这之前将终端清空 556 pub const TCSETSF: u32 = 0x5404; 557 /// 获取终端参数 558 pub const TCGETA: u32 = 0x5405; 559 /// 设置终端参数 560 pub const TCSETA: u32 = 0x5406; 561 /// 设置终端参数并等待所有输出完成 562 pub const TCSETAW: u32 = 0x5407; 563 /// 设置终端参数并且等待所有输出完成,但在这之前将终端清空 564 pub const TCSETAF: u32 = 0x5408; 565 /// 发送零字节,等待所有输出完成 566 pub const TCSBRK: u32 = 0x5409; 567 /// 控制终端的流控 568 pub const TCXONC: u32 = 0x540A; 569 /// 刷新输入/输出缓冲区或者丢弃输入缓冲区 570 pub const TCFLSH: u32 = 0x540B; 571 /// 设置设备为独占模式 572 pub const TIOCEXCL: u32 = 0x540C; 573 /// 设置设备为非独占模式 574 pub const TIOCNXCL: u32 = 0x540D; 575 /// 设置当前进程的控制终端 576 pub const TIOCSCTTY: u32 = 0x540E; 577 /// 获取前台进程组 578 pub const TIOCGPGRP: u32 = 0x540F; 579 ///设置前台进程组 580 pub const TIOCSPGRP: u32 = 0x5410; 581 /// 获取输出队列的字节数 582 pub const TIOCOUTQ: u32 = 0x5411; 583 /// 模拟从终端输入字符 584 pub const TIOCSTI: u32 = 0x5412; 585 /// 获取窗口大小 586 pub const TIOCGWINSZ: u32 = 0x5413; 587 /// 设置窗口大小 588 pub const TIOCSWINSZ: u32 = 0x5414; 589 /// 获取终端控制信号的状态 590 pub const TIOCMGET: u32 = 0x5415; 591 /// 设置终端控制信号的位 592 pub const TIOCMBIS: u32 = 0x5416; 593 /// 清除终端控制信号的位 594 pub const TIOCMBIC: u32 = 0x5417; 595 /// 设置终端控制信号的状态 596 pub const TIOCMSET: u32 = 0x5418; 597 /// 获取软件载波状态 598 pub const TIOCGSOFTCAR: u32 = 0x5419; 599 /// 设置软件载波状态 600 pub const TIOCSSOFTCAR: u32 = 0x541A; 601 /// 获取输入队列的字节数 602 pub const FIONREAD: u32 = 0x541B; 603 /// Linux 特有命令 604 pub const TIOCLINUX: u32 = 0x541C; 605 /// 获取控制台设备 606 pub const TIOCCONS: u32 = 0x541D; 607 /// 获取串行设备参数 608 pub const TIOCGSERIAL: u32 = 0x541E; 609 /// 设置串行设备参数 610 pub const TIOCSSERIAL: u32 = 0x541F; 611 /// 设置套接字的报文模式 612 pub const TIOCPKT: u32 = 0x5420; 613 /// 设置非阻塞 I/O 614 pub const FIONBIO: u32 = 0x5421; 615 /// 清除控制终端 616 pub const TIOCNOTTY: u32 = 0x5422; 617 /// 设置终端线路驱动器 618 pub const TIOCSETD: u32 = 0x5423; 619 /// 获取终端线路驱动器 620 pub const TIOCGETD: u32 = 0x5424; 621 /// 发送终止条件 622 pub const TCSBRKP: u32 = 0x5425; 623 /// 开始发送零比特 624 pub const TIOCSBRK: u32 = 0x5427; 625 /// 停止发送零比特 626 pub const TIOCCBRK: u32 = 0x5428; 627 /// Return the session ID of FD 628 pub const TIOCGSID: u32 = 0x5429; 629 } 630