gila/src/kernel/arch/x86_64/serial.rs
August 5a41a9c6a9
Some checks failed
Continuous Integration / Check (push) Successful in 1m7s
Continuous Integration / Clippy (push) Failing after 1m12s
Allocate space and create new page table
2025-11-04 19:58:42 -05:00

391 lines
12 KiB
Rust

// Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: MIT
#![allow(dead_code)]
use enumflags2::{BitFlag, BitFlags};
use intbits::Bits;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use x86_64::instructions::port::Port;
use crate::arch::asm::nop;
/// Represents an x86 port-mapped serial port.
pub struct SerialPort {
base_port: Port<u8>,
interrupt_enable: Port<u8>,
interrupt_id_fifo_control: Port<u8>,
line_control: Port<u8>,
modem_control: Port<u8>,
line_status: Port<u8>,
modem_status: Port<u8>,
scratch: Port<u8>,
fifo_register: u8,
pub crlf: Crlf,
pub wait_tx_clear: bool,
}
#[derive(PartialEq, Eq)]
pub enum Crlf {
Lf,
Crlf,
}
#[derive(FromPrimitive)]
#[repr(u8)]
pub enum DataBits {
Five = 0,
Six = 1,
Seven = 2,
Eight = 3,
}
#[derive(FromPrimitive)]
#[repr(u8)]
pub enum ParityBits {
None = 0b000,
Odd = 0b001,
Even = 0b011,
Mark = 0b101,
Space = 0b111,
}
#[enumflags2::bitflags]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum InterruptEnable {
ModemStatus = 0b1000,
ReceiverLineStatus = 0b100,
TransmitterHoldingRegisterEmpty = 0b10,
ReceivedDataAvail = 0b1,
}
#[enumflags2::bitflags]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum ModemControl {
DataTerminalReady = 0b00000001,
RequestToSend = 0b00000010,
Out1 = 0b00000100,
Out2 = 0b00001000,
Loop = 0b00010000,
}
#[repr(u8)]
#[derive(FromPrimitive)]
pub enum TriggerLevel {
OneByte = 0b00,
FourBytes = 0b01,
EightBytes = 0b10,
FourteenBytes = 0b11,
}
#[repr(u8)]
#[derive(FromPrimitive, PartialEq, Eq)]
pub enum InterruptState {
ModemStatus = 0b00,
TransmitterEmpty = 0b01,
ReceivedDataAvail = 0b10,
ReceiverLineStatus = 0b11,
}
#[repr(u8)]
#[derive(FromPrimitive)]
pub enum FifoState {
NoFifo = 0b00,
FifoEnabledUnusable = 0b01,
FifoEnabled = 0b10,
}
#[repr(u8)]
#[enumflags2::bitflags]
#[derive(Clone, Copy, Debug)]
pub enum LineStatus {
DataReady = 0b00000001,
OverrunError = 0b00000010,
ParityError = 0b00000100,
FramingError = 0b00001000,
BreakIndicator = 0b00010000,
TransmitterHoldingRegisterEmpty = 0b00100000,
TransmitterEmpty = 0b01000000,
ImpendingError = 0b10000000,
}
#[repr(u8)]
#[enumflags2::bitflags]
#[derive(Clone, Copy, Debug)]
pub enum ModemStatus {
DeltaClearToSend = 0b00000001,
DeltaDataSetReady = 0b00000010,
TrailingEdgeRingIndicator = 0b00000100,
DeltaDataCarrierDetect = 0b00001000,
ClearToSend = 0b00010000,
DataSetReady = 0b00100000,
RingIndicator = 0b01000000,
DataCarrierDetect = 0b10000000,
}
impl SerialPort {
pub fn log_write(&mut self, msg: &str) {
if self.crlf == Crlf::Crlf {
for c in msg.chars() {
if c == '\n' {
self.write_char('\r');
}
self.write_char(c);
}
} else {
for c in msg.chars() {
self.write_char(c);
}
}
}
/// Get whether the transmit buffer is empty.
pub fn is_transmit_empty(&mut self) -> bool {
self.get_line_status()
.contains(LineStatus::TransmitterEmpty)
}
fn block_until_transmit_empty(&mut self) {
while !self.is_transmit_empty() {
nop();
}
}
/// Write a message with a newline appended to the serial port.
pub fn log_writeln(&mut self, msg: &str) {
self.log_write(msg);
self.log_write("\n");
}
/// Create a new port-mapped serial port with the base address `port_addr`.
pub unsafe fn from_port_unchecked(port_addr: u16) -> Self {
let mut port = SerialPort {
base_port: Port::new(port_addr),
interrupt_enable: Port::new(port_addr + 1),
interrupt_id_fifo_control: Port::new(port_addr + 2),
line_control: Port::new(port_addr + 3),
modem_control: Port::new(port_addr + 4),
line_status: Port::new(port_addr + 5),
modem_status: Port::new(port_addr + 6),
scratch: Port::new(port_addr + 7),
fifo_register: 0,
crlf: Crlf::Crlf,
wait_tx_clear: true,
};
// ensure this is false
port.set_dlab(false);
port
}
pub fn try_from_port(port_addr: u16) -> Option<Self> {
let mut port = unsafe { SerialPort::from_port_unchecked(port_addr) };
// Assert that port scratch register is writable
unsafe { port.scratch.write(192) };
if unsafe { port.scratch.read() } != 192 {
return None;
}
// Enable loopback mode
let mut port_modem = port.get_modem_control();
port_modem.insert(ModemControl::Loop);
port.set_modem_control(port_modem);
port.write_char(0xae as char);
// Assert that loopback mode worked
if port.read_char() != (0xae as u8) {
return None;
}
// Disable loopback mode
port_modem.remove(ModemControl::Loop);
port.set_modem_control(port_modem);
Some(port)
}
fn get_line_control(&mut self) -> u8 {
unsafe { self.line_control.read() }
}
fn set_line_control(&mut self, value: u8) {
unsafe { self.line_control.write(value) };
}
fn get_dlab(&mut self) -> bool {
self.get_line_control().bit(7)
}
fn set_dlab(&mut self, dlab: bool) {
let mut new_lcr = unsafe { self.line_control.read() };
new_lcr.set_bit(7, dlab);
unsafe { self.line_control.write(new_lcr) };
}
/// Read a single character from the RX buffer.
pub fn read_char(&mut self) -> u8 {
unsafe { self.base_port.read() }
}
/// Write a single character to the TX buffer.
pub fn write_char(&mut self, c: char) {
// Wait for the TX Buffer to be empty
if self.wait_tx_clear {
self.block_until_transmit_empty();
}
unsafe { self.base_port.write(c as u8) };
}
/// Get the baud rate divisor.
pub fn get_divisor(&mut self) -> u16 {
self.set_dlab(true);
let result: u16 =
unsafe { self.base_port.read() as u16 | ((self.interrupt_enable.read() as u16) << 8) };
self.set_dlab(false);
result
}
/// Set the baud rate divisor.
pub fn set_divisor(&mut self, divisor: u16) {
self.set_dlab(true);
unsafe {
self.base_port.write((divisor & 0b11111111) as u8);
self.interrupt_enable
.write(((divisor & 0b1111111100000000) >> 8) as u8);
}
self.set_dlab(false);
}
/// Get the number of bits in a character.
pub fn get_data_bits(&mut self) -> DataBits {
DataBits::from_u8(self.get_line_control().bits(0..=1)).unwrap()
}
/// Set the number of bits in a character.
pub fn set_data_bits(&mut self, bits: DataBits) {
let new_value = self.get_line_control().bits(2..=7) | bits as u8;
self.set_line_control(new_value);
}
/// Get the number of bits sent after each character.
/// A value of False means only 1 bit will be sent.
/// A value of True means 2 bits will be sent.
/// If the character length is 5 bits, a value of True means 1.5 bits will be sent.
pub fn get_stop_bits(&mut self) -> bool {
self.get_line_control().bit(2)
}
/// Set the number of bits sent after each character.
/// A value of False means only 1 bit will be sent.
/// A value of True means 2 bits will be sent.
/// If the character length is 5 bits, a value of True means 1.5 bits will be sent.
pub fn set_stop_bits(&mut self, bit: bool) {
let mut new_value = self.get_line_control();
new_value.set_bit(2, bit);
self.set_line_control(new_value);
}
/// Get the parity strategy.
pub fn get_parity_bits(&mut self) -> ParityBits {
ParityBits::from_u8(self.get_line_control().bits(3..=5) >> 3).unwrap()
}
/// Set the parity strategy.
pub fn set_parity_bits(&mut self, parity: ParityBits) {
let mut new_value = self.get_line_control();
new_value.set_bits(3..=5, parity as u8);
self.set_line_control(new_value);
}
/// Get the Interrupt Enable control register.
pub fn get_interrupt_enable(&mut self) -> BitFlags<InterruptEnable> {
InterruptEnable::from_bits(unsafe { self.interrupt_enable.read().bits(0..=3) }).unwrap()
}
/// Set the Interrupt Enable control register.
pub fn set_interrupt_enable(&mut self, interrupt: InterruptEnable) {
unsafe { self.interrupt_enable.write(interrupt as u8) };
}
/// Set the FIFO control register.
pub fn set_fifo_control(&mut self, value: u8) {
unsafe { self.interrupt_id_fifo_control.write(value) };
}
/// Clear the transmitter FIFO.
pub fn clear_tx_fifo(&mut self) {
let mut new_value = self.fifo_register;
new_value.set_bit(2, true);
self.set_fifo_control(new_value);
}
/// Clear the receiver FIFO.
pub fn clear_rx_fifo(&mut self) {
let mut new_value = self.fifo_register;
new_value.set_bit(1, true);
self.set_fifo_control(new_value);
}
/// Enable or disable FIFOs.
pub fn set_fifo_enable(&mut self, enable: bool) {
self.fifo_register.set_bit(0, enable);
self.set_fifo_control(self.fifo_register);
}
/// Get whether FIFOs were enabled using this API.
pub fn get_fifo_enable(&mut self) -> bool {
self.fifo_register.bit(0)
}
/// Set the DMA mode of the serial controller.
pub fn set_dma_mode(&mut self, enable: bool) {
self.fifo_register.set_bit(3, enable);
self.set_fifo_control(self.fifo_register);
}
/// Get whether DMA mode was enabled using this API.
pub fn get_dma_mode(&mut self) -> bool {
self.fifo_register.bit(3)
}
/// Get the Interrupt Trigger Level.
pub fn get_interrupt_trigger_level(&mut self) -> TriggerLevel {
TriggerLevel::from_u8(self.fifo_register.bits(6..=7) >> 6).unwrap()
}
/// Set the Interrupt Trigger Level.
pub fn set_interrupt_trigger_level(&mut self, level: TriggerLevel) {
self.fifo_register.set_bits(6..=7, level as u8);
self.set_fifo_control(self.fifo_register);
}
/// Get the Interrupt ID status register.
pub fn get_interrupt_id(&mut self) -> u8 {
unsafe { self.interrupt_id_fifo_control.read() }
}
/// Get the state of the FIFO buffers.
pub fn get_fifo_buffer_state(&mut self) -> FifoState {
FifoState::from_u8(self.get_interrupt_id().bits(6..=7) >> 6).unwrap()
}
/// Get whether a timeout interrupt is pending. Only used on UART 16550 chips, otherwise reserved.
pub fn get_timeout_pending(&mut self) -> bool {
self.get_interrupt_id().bit(3)
}
/// Get the Interrupt State of the controller.
pub fn get_interrupt_state(&mut self) -> InterruptState {
InterruptState::from_u8(self.get_interrupt_id().bits(1..=2) >> 1).unwrap()
}
/// Get whether an interrupt is pending.
pub fn get_interrupt_pending(&mut self) -> bool {
!self.get_interrupt_id().bit(0)
}
/// Set the Modem Control register flags.
/// This OVERWRITES all held flags. To set specific flags, first store the current
/// value using `get_modem_control()`, then modify the flags you want to change.
pub fn set_modem_control(&mut self, control: BitFlags<ModemControl>) {
unsafe { self.modem_control.write(control.bits()) };
}
/// Get the Modem Control register flags.
pub fn get_modem_control(&mut self) -> BitFlags<ModemControl> {
ModemControl::from_bits(unsafe { self.modem_control.read() & 0b0001111 }).unwrap()
}
/// Get the Line Status Register flags.
pub fn get_line_status(&mut self) -> BitFlags<LineStatus> {
LineStatus::from_bits(unsafe { self.line_status.read() }).unwrap()
}
/// Get the Modem Status Register flags.
pub fn get_modem_status(&mut self) -> BitFlags<ModemStatus> {
ModemStatus::from_bits(unsafe { self.modem_status.read() }).unwrap()
}
}