use crate::{
AcpiError,
AcpiTable,
SdtHeader,
Signature,
address::{GenericAddress, RawGenericAddress},
};
use core::{
num::{NonZeroU8, NonZeroU32},
ptr,
slice,
str::{self, Utf8Error},
};
#[repr(C, packed)]
#[derive(Debug)]
pub struct Spcr {
pub header: SdtHeader,
pub interface_type: u8,
_reserved: [u8; 3],
pub base_address: RawGenericAddress,
pub interrupt_type: u8,
pub irq: u8,
pub global_system_interrupt: u32,
pub configured_baud_rate: u8,
pub parity: u8,
pub stop_bits: u8,
pub flow_control: u8,
pub terminal_type: u8,
pub language: u8,
pub pci_device_id: u16,
pub pci_vendor_id: u16,
pub pci_bus_number: u8,
pub pci_device_number: u8,
pub pci_function_number: u8,
pub pci_flags: u32,
pub pci_segment: u8,
pub uart_clock_freq: u32,
pub precise_baud_rate: u32,
pub namespace_string_length: u16,
pub namespace_string_offset: u16,
}
unsafe impl AcpiTable for Spcr {
const SIGNATURE: Signature = Signature::SPCR;
fn header(&self) -> &SdtHeader {
&self.header
}
}
impl Spcr {
pub fn interface_type(&self) -> SpcrInterfaceType {
SpcrInterfaceType::from(self.interface_type)
}
pub fn base_address(&self) -> Option<Result<GenericAddress, AcpiError>> {
(!self.base_address.is_empty()).then(|| GenericAddress::from_raw(self.base_address))
}
fn configured_baud_rate(&self) -> Option<NonZeroU32> {
match self.configured_baud_rate {
3 => unsafe { Some(NonZeroU32::new_unchecked(9600)) },
4 => unsafe { Some(NonZeroU32::new_unchecked(19200)) },
6 => unsafe { Some(NonZeroU32::new_unchecked(57600)) },
7 => unsafe { Some(NonZeroU32::new_unchecked(115200)) },
_ => None,
}
}
pub fn baud_rate(&self) -> Option<NonZeroU32> {
NonZeroU32::new(self.precise_baud_rate).or_else(|| self.configured_baud_rate())
}
pub fn flow_control(&self) -> SpcrFlowControl {
SpcrFlowControl::from_bits_truncate(self.flow_control)
}
pub fn interrupt_type(&self) -> SpcrInterruptType {
SpcrInterruptType::from_bits_truncate(self.interrupt_type)
}
pub fn irq(&self) -> Option<u8> {
self.interrupt_type().contains(SpcrInterruptType::DUAL_8259).then_some(self.irq)
}
pub fn global_system_interrupt(&self) -> Option<u32> {
if self.interrupt_type().difference(SpcrInterruptType::DUAL_8259).is_empty() {
return None;
}
Some(self.global_system_interrupt)
}
pub fn terminal_type(&self) -> SpcrTerminalType {
SpcrTerminalType::from_bits_truncate(self.terminal_type)
}
pub fn pci_device_id(&self) -> Option<u16> {
(self.pci_device_id != 0xffff).then_some(self.pci_device_id)
}
pub fn pci_vendor_id(&self) -> Option<u16> {
(self.pci_vendor_id != 0xffff).then_some(self.pci_vendor_id)
}
pub fn pci_bus_number(&self) -> Option<NonZeroU8> {
NonZeroU8::new(self.pci_bus_number)
}
pub fn pci_device_number(&self) -> Option<NonZeroU8> {
NonZeroU8::new(self.pci_device_number)
}
pub fn pci_function_number(&self) -> Option<NonZeroU8> {
NonZeroU8::new(self.pci_function_number)
}
pub const fn uart_clock_frequency(&self) -> Option<NonZeroU32> {
if self.header.revision <= 2 {
return None;
}
NonZeroU32::new(self.uart_clock_freq)
}
pub fn namespace_string(&self) -> Result<&str, Utf8Error> {
let start = ptr::from_ref(self).cast::<u8>();
let bytes = unsafe {
let str_start = start.add(self.namespace_string_offset as usize);
slice::from_raw_parts(str_start, self.namespace_string_length as usize)
};
str::from_utf8(bytes)
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct SpcrInterruptType: u8 {
const DUAL_8259 = 1 << 0;
const IO_APIC = 1 << 1;
const IO_SAPIC = 1 << 2;
const ARMH_GIC = 1 << 3;
const RISCV_PLIC = 1 << 4;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct SpcrTerminalType: u8 {
const VT1000 = 1 << 0;
const EXTENDED_VT1000 = 1 << 1;
const VT_UTF8 = 1 << 2;
const ANSI = 1 << 3;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct SpcrFlowControl: u8 {
const DCD = 1 << 0;
const RTS_CTS = 1 << 1;
const XON_XOFF = 1 << 2;
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum SpcrInterfaceType {
Full16550,
Full16450,
MAX311xE,
ArmPL011,
MSM8x60,
Nvidia16550,
TiOmap,
APM88xxxx,
Msm8974,
Sam5250,
IntelUSIF,
Imx6,
ArmSBSAGeneric32bit,
ArmSBSAGeneric,
ArmDCC,
Bcm2835,
Sdm845_18432,
Generic16550,
Sdm845_7372,
IntelLPSS,
RiscVSbi,
Unknown(u8),
}
impl From<u8> for SpcrInterfaceType {
fn from(val: u8) -> Self {
match val {
0x00 => Self::Full16550,
0x01 => Self::Full16450,
0x02 => Self::MAX311xE,
0x03 => Self::ArmPL011,
0x04 => Self::MSM8x60,
0x05 => Self::Nvidia16550,
0x06 => Self::TiOmap,
0x08 => Self::APM88xxxx,
0x09 => Self::Msm8974,
0x0A => Self::Sam5250,
0x0B => Self::IntelUSIF,
0x0C => Self::Imx6,
0x0D => Self::ArmSBSAGeneric32bit,
0x0E => Self::ArmSBSAGeneric,
0x0F => Self::ArmDCC,
0x10 => Self::Bcm2835,
0x11 => Self::Sdm845_18432,
0x12 => Self::Generic16550,
0x13 => Self::Sdm845_7372,
0x14 => Self::IntelLPSS,
0x15 => Self::RiscVSbi,
_ => Self::Unknown(val),
}
}
}