# Serial Port драйвер: `serial.rs` ## Назначение Драйвер последовательного порта (UART 16550, COM1) для отладочного вывода. Позволяет видеть сообщения ядра через QEMU serial console, minicom, screen и т.д. ## Аппаратная модель COM1 расположен по портам ввода-вывода `0x3F8`-`0x3FF`: | Порт | Регистр | Назначение | |------|---------|------------| | 0x3F8 | DATA | Чтение/запись данных | | 0x3F9 | IER | Interrupt Enable | | 0x3FA | IIR/FCR | Interrupt ID / FIFO Control | | 0x3FB | LCR | Line Control | | 0x3FC | MCR | Modem Control | | 0x3FD | LSR | Line Status | | 0x3FE | MSR | Modem Status | ## Инициализация ```rust pub unsafe fn init() -> Self { let port = Self::COM1; // 0x3F8 outb(port + 1, 0x00); // IER = 0 (disable interrupts) outb(port + 3, 0x80); // LCR DLAB=1 (enable baud rate programming) outb(port + 0, 0x03); // Divisor LSB = 3 (38400 baud) outb(port + 1, 0x00); // Divisor MSB = 0 outb(port + 3, 0x03); // LCR = 8N1 (8 bits, No parity, 1 stop) outb(port + 2, 0xC7); // FCR = enable FIFO, clear, 14-byte threshold outb(port + 4, 0x0B); // MCR = DTR+RTS+OUT2 (enable IRQ + handshake) SerialPort(port) } ``` ### Детали конфигурации 1. **IER = 0**: отключаем прерывания UART (TODO: включить для RX). 2. **DLAB = 1**: разрешаем программирование делителя бода. 3. **Divisor = 3**: при тактовой 1.8432 MHz → 115200 / 3 = 38400 бод. 4. **LCR = 0x03**: 8N1 — 8 бит данных, нет чётности, 1 стоп-бит. 5. **FCR = 0xC7**: enable FIFO, clear both FIFOs, trigger at 14 bytes. 6. **MCR = 0x0B**: DTR=1, RTS=1, OUT2=1 (необходимо для IRQ на ISA шине). ## Отправка байта ```rust fn is_transmit_empty(&self) -> bool { unsafe { (inb(self.0 + 5) & 0x20) != 0 } // LSR bit 5 = Transmitter Holding Register Empty } pub fn send(&self, data: u8) { while !self.is_transmit_empty() {} // Ждём, пока UART готов unsafe { outb(self.0, data); } } ``` ## fmt::Write реализация ```rust impl core::fmt::Write for SerialPort { fn write_str(&mut self, s: &str) -> core::fmt::Result { for byte in s.bytes() { self.send(byte); } Ok(()) } } ``` ## Глобальный экземпляр ```rust static SERIAL_PORT: Locked> = Locked::new(None); pub fn init_global() { let mut guard = SERIAL_PORT.lock(); *guard = Some(unsafe { SerialPort::init() }); } pub fn write_global(args: core::fmt::Arguments) { let mut guard = SERIAL_PORT.lock(); if let Some(ref mut sp) = *guard { let _ = sp.write_fmt(args); } } ``` `init_global()` вызывается из `kmain()` после инициализации TTY. `write_global()` используется: - В `log!()` макросе (через debug.rs). - В `rust_panic()` обработчике. - В обработчиках исключений (GPF, Double Fault, Early Exception). ## Низкоуровневый I/O ```rust unsafe fn outb(port: u16, val: u8) { asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)); } unsafe fn inb(port: u16) -> u8 { let res: u8; asm!("in al, dx", out("al") res, in("dx") port, options(nomem, nostack, preserves_flags)); res } ```