Add simple uart driver, more functionality etc etc etc
This commit is contained in:
7
x86_drivers/Cargo.lock
generated
Normal file
7
x86_drivers/Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "x86_drivers"
|
||||
version = "0.1.0"
|
||||
6
x86_drivers/Cargo.toml
Normal file
6
x86_drivers/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "x86_drivers"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
37
x86_drivers/src/core.rs
Normal file
37
x86_drivers/src/core.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::uart::{Uart1, Uart2, Uart3, Uart4};
|
||||
|
||||
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[must_use]
|
||||
pub struct Core {
|
||||
pub uart1: Uart1,
|
||||
pub uart2: Uart2,
|
||||
pub uart3: Uart3,
|
||||
pub uart4: Uart4,
|
||||
}
|
||||
|
||||
impl Core {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
uart1: Uart1::new(),
|
||||
uart2: Uart2::new(),
|
||||
uart3: Uart3::new(),
|
||||
uart4: Uart4::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all of the peripherals this core has.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this has already been called it panics.
|
||||
pub fn take() -> Self {
|
||||
assert!(
|
||||
!TAKEN.swap(true, Ordering::SeqCst),
|
||||
"Core already initialized"
|
||||
);
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
198
x86_drivers/src/gdt.rs
Normal file
198
x86_drivers/src/gdt.rs
Normal file
@@ -0,0 +1,198 @@
|
||||
#[repr(transparent)]
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
pub struct GdtEntry {
|
||||
inner: u64,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl GdtEntry {
|
||||
const LIMIT_L: u64 = 0;
|
||||
const LIMIT_H: u64 = 15;
|
||||
const BASE_L: u64 = 16;
|
||||
const BASE_H: u64 = 39;
|
||||
const ACCESS_L: u64 = 40;
|
||||
const ACCESS_H: u64 = 47;
|
||||
const FLAGS_L: u64 = 52;
|
||||
const FLAGS_H: u64 = 55;
|
||||
|
||||
// NOTE: Skipping higher bits since they are zeroed.
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[must_use]
|
||||
pub const fn access_code(&self) -> u8 {
|
||||
self.inner as u8
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn new(access_code: AccessFlags, flags: Flags) -> Self {
|
||||
let mut rpr = 0u64;
|
||||
rpr |= (access_code.to_bits() as u64) << Self::ACCESS_L;
|
||||
rpr |= (flags.to_bits() as u64) << Self::FLAGS_L;
|
||||
Self { inner: rpr }
|
||||
}
|
||||
|
||||
pub const fn raw(&self) -> u64 {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub const fn null() -> Self {
|
||||
Self { inner: 0 }
|
||||
}
|
||||
|
||||
pub const fn user_code() -> Self {
|
||||
Self::new(
|
||||
AccessFlags {
|
||||
ring: DPL::User,
|
||||
data_kind: DataKind::ReadableCodeContained,
|
||||
},
|
||||
Flags::new(true),
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn kernel_code() -> Self {
|
||||
Self::new(
|
||||
AccessFlags {
|
||||
ring: DPL::Kernel,
|
||||
data_kind: DataKind::ReadableCodeContained,
|
||||
},
|
||||
Flags::new(true),
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn kernel_data() -> Self {
|
||||
Self::new(
|
||||
AccessFlags {
|
||||
ring: DPL::Kernel,
|
||||
data_kind: DataKind::WriteableDataIncreasing,
|
||||
},
|
||||
Flags::new(false),
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn user_data() -> Self {
|
||||
Self::new(
|
||||
AccessFlags {
|
||||
ring: DPL::User,
|
||||
data_kind: DataKind::WriteableDataIncreasing,
|
||||
},
|
||||
Flags::new(false),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
pub enum DPL {
|
||||
Kernel = 0,
|
||||
Ring1 = 1,
|
||||
Ring2 = 2,
|
||||
User = 3,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug)]
|
||||
pub enum DataKind {
|
||||
// Means descriptor == 0, executable = 0, direction = dc, pull to 0 for good measure.
|
||||
System = 0b000,
|
||||
// Desc == 1, exec = 0, direction = 1, W = 0
|
||||
ProtectedDataDecreasing = 0b1010,
|
||||
// Desc == 1, exec = 0, direction = 1, W = 1
|
||||
WriteableDataDecreasing = 0b1011,
|
||||
// Desc == 1, exec = 0, direction = 0, W = 0
|
||||
ProtectedDataIncreasing = 0b1000,
|
||||
// Desc == 1, exec = 0, direction = 0, W = 1
|
||||
WriteableDataIncreasing = 0b1001,
|
||||
// Desc == 1, exec = 1, direction = 0, R = 0
|
||||
//
|
||||
// This can only be reached at the appropriated DPL.
|
||||
ProtectedCodeContained = 0b1100,
|
||||
// Desc == 1, exec = 1, direction = 0, R = 1
|
||||
//
|
||||
// This can only be reached at the appropriated DPL.
|
||||
ReadableCodeContained = 0b1101,
|
||||
// Desc == 1, exec = 1, direction = 1, R = 0
|
||||
//
|
||||
// This can be called from lower priorities, think syscalls. But requires long jumps.
|
||||
ProtectedCodeExposed = 0b1110,
|
||||
// Desc == 1, exec = 1, direction = 1, R = 1
|
||||
//
|
||||
// This can be called from lower priorities, think syscalls. But requires long jumps.
|
||||
ReadableCodeExposed = 0b1111,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccessFlags {
|
||||
ring: DPL,
|
||||
data_kind: DataKind,
|
||||
}
|
||||
|
||||
impl AccessFlags {
|
||||
pub const fn new(ring: DPL, data_kind: DataKind) -> Self {
|
||||
Self { ring, data_kind }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
const fn to_bits(self) -> u8 {
|
||||
let mut ret = 0b1000_0001;
|
||||
ret |= (self.ring as u8) << 5;
|
||||
ret |= (self.data_kind as u8) << 1;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add remaining flags.
|
||||
pub struct Flags {
|
||||
long_mode: bool,
|
||||
}
|
||||
|
||||
impl Flags {
|
||||
pub const fn new(long_mode: bool) -> Self {
|
||||
Self { long_mode }
|
||||
}
|
||||
|
||||
const fn to_bits(&self) -> u8 {
|
||||
0b0 | ((self.long_mode as u8) << 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct GdtSelector {
|
||||
inner: u16,
|
||||
}
|
||||
|
||||
impl GdtSelector {
|
||||
/// Returns a new gdt selector.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the index is out of bounds.
|
||||
pub const fn new(priviledge_level: DPL, table: &[GdtEntry], idx: u16) -> Self {
|
||||
let _ = table[idx as usize]; // Rust provided length check.
|
||||
Self {
|
||||
inner: (idx << 3) | priviledge_level as u16,
|
||||
}
|
||||
}
|
||||
pub const fn raw(&self) -> u16 {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Debug)]
|
||||
pub struct GdtPointer {
|
||||
limit: u16,
|
||||
base: u64,
|
||||
}
|
||||
|
||||
impl GdtPointer {
|
||||
pub fn new(table: &[GdtEntry]) -> Self {
|
||||
Self {
|
||||
limit: core::mem::size_of_val(table) as u16 - 1,
|
||||
base: table.as_ptr() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
56
x86_drivers/src/idt.rs
Normal file
56
x86_drivers/src/idt.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::gdt::{DPL, GdtSelector};
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct InterruptTableDescriptor {
|
||||
size: u16,
|
||||
offset: u64,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct IdtTableEntry {
|
||||
inner: u128,
|
||||
}
|
||||
|
||||
pub type ISR = extern "x86-interrupt" fn(InterruptStackFrame) -> ();
|
||||
|
||||
#[repr(C)]
|
||||
pub struct InterruptStackFrame {
|
||||
ip: usize,
|
||||
cs: usize,
|
||||
flags: usize,
|
||||
sp: usize,
|
||||
ss: usize,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum Gate {
|
||||
Interrupt = 0b1110,
|
||||
Trap = 0b1111,
|
||||
}
|
||||
|
||||
impl IdtTableEntry {
|
||||
pub fn new(isr: ISR, selector: GdtSelector, ist: u8, gate: Gate, priority: DPL) -> Self {
|
||||
let mut inner = 0b0;
|
||||
let offset = (isr as *const ()) as u64;
|
||||
|
||||
let bytes = (offset >> 32) as u32;
|
||||
inner |= (bytes as u128) << 64;
|
||||
|
||||
let bytes = (offset as u32) >> 16;
|
||||
inner |= (bytes as u128) << 48;
|
||||
|
||||
let bytes = offset as u16;
|
||||
inner |= bytes as u128;
|
||||
|
||||
inner |= (selector.raw() as u128) << 16;
|
||||
|
||||
assert!(ist <= 0b111);
|
||||
|
||||
inner |= (ist as u128) << 32;
|
||||
inner |= (gate as u128) << 40;
|
||||
inner |= (priority as u128) << 45;
|
||||
inner |= 1 << 47;
|
||||
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
42
x86_drivers/src/lib.rs
Normal file
42
x86_drivers/src/lib.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
#![no_std]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![deny(
|
||||
clippy::all,
|
||||
clippy::perf,
|
||||
clippy::pedantic,
|
||||
clippy::nursery,
|
||||
clippy::complexity,
|
||||
clippy::correctness,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::undocumented_unsafe_blocks,
|
||||
rustdoc::all,
|
||||
rust_2018_idioms,
|
||||
warnings
|
||||
)]
|
||||
|
||||
#[allow(
|
||||
missing_docs,
|
||||
clippy::inline_always,
|
||||
clippy::missing_safety_doc,
|
||||
clippy::undocumented_unsafe_blocks,
|
||||
clippy::unused_self,
|
||||
clippy::must_use_candidate,
|
||||
clippy::new_without_default,
|
||||
clippy::cast_possible_truncation
|
||||
)]
|
||||
pub mod uart;
|
||||
|
||||
pub mod gdt;
|
||||
pub mod idt;
|
||||
|
||||
pub mod core;
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::core::Core;
|
||||
pub use crate::uart::{InitUart, Uart};
|
||||
}
|
||||
234
x86_drivers/src/uart/mod.rs
Normal file
234
x86_drivers/src/uart/mod.rs
Normal file
@@ -0,0 +1,234 @@
|
||||
mod pac;
|
||||
|
||||
use core::{fmt, marker::PhantomData};
|
||||
|
||||
use pac::{
|
||||
COM1_BASE, DLH, DLL, FCR, IER, LCR, LSR, LineControlRegister, MCR, P_CLK_FREQ, RBR, THR,
|
||||
WordLength, inb, outb,
|
||||
};
|
||||
|
||||
use crate::uart::pac::{COM2_BASE, COM3_BASE, COM4_BASE, LineStatus};
|
||||
|
||||
// TODO: wrap all of these in a single core critical section.
|
||||
pub trait HwUart: crate::private::Sealed {
|
||||
const BASE: u16;
|
||||
|
||||
#[inline(always)]
|
||||
fn tx_byte(val: u8) {
|
||||
unsafe { outb(Self::BASE + THR, val) };
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn line_status() -> LineStatus {
|
||||
unsafe {
|
||||
LineStatus {
|
||||
bits: inb(Self::BASE + LSR),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(unused)]
|
||||
fn rx_byte() -> u8 {
|
||||
unsafe { inb(Self::BASE + RBR) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_baudrate(cfg: &UartConfig<true>) {
|
||||
let [upper, lower] = cfg.prescaler.to_ne_bytes();
|
||||
let mut lcr = Self::read_control_register();
|
||||
lcr.enable_baud_config();
|
||||
Self::write_control_register(lcr.bits());
|
||||
unsafe { outb(Self::BASE + DLL, lower) };
|
||||
unsafe { outb(Self::BASE + DLH, upper) };
|
||||
let mut lcr = Self::read_control_register();
|
||||
lcr.disable_baud_config();
|
||||
Self::write_control_register(lcr.bits());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_word_length(length: WordLength) {
|
||||
let mut lcr = Self::read_control_register();
|
||||
lcr.set_word_length(length);
|
||||
Self::write_control_register(lcr.bits());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_control_register() -> LineControlRegister {
|
||||
unsafe {
|
||||
LineControlRegister {
|
||||
bits: inb(Self::BASE + LCR),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_control_register(data: u8) {
|
||||
unsafe { outb(Self::BASE + LCR, data) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn disable_interrupts() {
|
||||
unsafe { outb(Self::BASE + IER, 0) };
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clear_fifo_and_modem_setting() {
|
||||
unsafe {
|
||||
outb(Self::BASE + FCR, 0);
|
||||
outb(Self::BASE + MCR, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitUart<M: HwUart> {
|
||||
fn init(&self) -> Uart<M> {
|
||||
Uart::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct UartConfig<const BAUD_SET: bool> {
|
||||
prescaler: u16,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum BaudRate {
|
||||
B115_200 = 115_200,
|
||||
B57_600 = 57_600,
|
||||
B38_400 = 38_400,
|
||||
B19_200 = 19_200,
|
||||
B9_600 = 9_600,
|
||||
B1_200 = 1_200,
|
||||
B300 = 300,
|
||||
}
|
||||
|
||||
pub struct Uart1 {
|
||||
_private: PhantomData<()>,
|
||||
}
|
||||
pub struct Uart2 {
|
||||
_private: PhantomData<()>,
|
||||
}
|
||||
pub struct Uart3 {
|
||||
_private: PhantomData<()>,
|
||||
}
|
||||
pub struct Uart4 {
|
||||
_private: PhantomData<()>,
|
||||
}
|
||||
|
||||
/// 16550 UART driver.
|
||||
#[must_use]
|
||||
pub struct Uart<M: HwUart> {
|
||||
_t: PhantomData<M>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl UartConfig<false> {
|
||||
const fn set_baud(self, target_baud: BaudRate) -> UartConfig<true> {
|
||||
let pres = P_CLK_FREQ / (16 * target_baud as u32);
|
||||
UartConfig {
|
||||
prescaler: pres as u16,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
const fn set_baud_raw(self, target_baud: u16) -> Option<UartConfig<true>> {
|
||||
let pres = P_CLK_FREQ / (16 * target_baud as u32);
|
||||
let real_baud = P_CLK_FREQ / (pres * 16);
|
||||
if target_baud != real_baud as u16 {
|
||||
return None;
|
||||
}
|
||||
Some(UartConfig {
|
||||
prescaler: pres as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: HwUart> Uart<M> {
|
||||
fn new() -> Self {
|
||||
let uart = Self { _t: PhantomData };
|
||||
uart.init();
|
||||
uart
|
||||
}
|
||||
|
||||
fn init(&self) {
|
||||
let config = UartConfig::default().set_baud(BaudRate::B115_200);
|
||||
M::disable_interrupts();
|
||||
M::set_baudrate(&config);
|
||||
M::set_word_length(WordLength::W8);
|
||||
M::clear_fifo_and_modem_setting();
|
||||
}
|
||||
|
||||
fn write_raw(&self, byte: u8) {
|
||||
M::tx_byte(byte);
|
||||
}
|
||||
|
||||
fn spin_write_byte(&self, byte: u8) {
|
||||
while !M::line_status().tx_ready() {}
|
||||
self.write_raw(byte);
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::private::Sealed for Uart1 {}
|
||||
|
||||
impl HwUart for Uart1 {
|
||||
const BASE: u16 = COM1_BASE;
|
||||
}
|
||||
impl crate::private::Sealed for Uart2 {}
|
||||
|
||||
impl HwUart for Uart2 {
|
||||
const BASE: u16 = COM2_BASE;
|
||||
}
|
||||
impl crate::private::Sealed for Uart3 {}
|
||||
|
||||
impl HwUart for Uart3 {
|
||||
const BASE: u16 = COM3_BASE;
|
||||
}
|
||||
impl crate::private::Sealed for Uart4 {}
|
||||
|
||||
impl HwUart for Uart4 {
|
||||
const BASE: u16 = COM4_BASE;
|
||||
}
|
||||
|
||||
impl<M: HwUart> InitUart<M> for M {}
|
||||
|
||||
impl<M: HwUart> fmt::Write for Uart<M> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
if byte == b'\n' {
|
||||
self.spin_write_byte(b'\r');
|
||||
}
|
||||
self.spin_write_byte(byte);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Uart1 {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
_private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Uart2 {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
_private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Uart3 {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
_private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Uart4 {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
_private: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
106
x86_drivers/src/uart/pac.rs
Normal file
106
x86_drivers/src/uart/pac.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
pub const COM1_BASE: u16 = 0x3F8;
|
||||
pub const COM2_BASE: u16 = 0x2F8; // IRQ 3
|
||||
pub const COM3_BASE: u16 = 0x3E8; // IRQ 4 (shared with COM1)
|
||||
pub const COM4_BASE: u16 = 0x2E8; // IRQ 3 (shared with COM2)
|
||||
|
||||
// Write here to pend a transfer.
|
||||
pub const THR: u16 = 0;
|
||||
// Holds a byte if there are any.
|
||||
pub const RBR: u16 = 0;
|
||||
pub const DLL: u16 = 0;
|
||||
pub const DLH: u16 = 1;
|
||||
pub const IER: u16 = 1;
|
||||
pub const FCR: u16 = 2;
|
||||
pub const LCR: u16 = 3;
|
||||
pub const MCR: u16 = 4;
|
||||
pub const LSR: u16 = 5;
|
||||
|
||||
pub const P_CLK_FREQ: u32 = 1_843_200;
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn inb(port: u16) -> u8 {
|
||||
let value: u8;
|
||||
// SAFETY: port address is valid 16550 register offset from a standard COM base.
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"in al, dx",
|
||||
out("al") value,
|
||||
in("dx") port,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn outb(port: u16, value: u8) {
|
||||
// SAFETY: port address is valid 16550 register offset from a standard COM base.
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"out dx, al",
|
||||
in("dx") port,
|
||||
in("al") value,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[must_use]
|
||||
pub struct LineStatus {
|
||||
pub bits: u8,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl LineStatus {
|
||||
const DR: u8 = 0;
|
||||
const OE: u8 = 1;
|
||||
const PE: u8 = 2;
|
||||
const FE: u8 = 3;
|
||||
const BI: u8 = 4;
|
||||
const THRE: u8 = 5;
|
||||
const TEMT: u8 = 6;
|
||||
const E_RCVR_FIFO: u8 = 7;
|
||||
|
||||
pub const fn rx_ready(&self) -> bool {
|
||||
(self.bits & (1 << Self::DR)) != 0
|
||||
}
|
||||
|
||||
pub const fn tx_ready(&self) -> bool {
|
||||
(self.bits & (1 << Self::THRE)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[must_use]
|
||||
pub struct LineControlRegister {
|
||||
pub bits: u8,
|
||||
}
|
||||
|
||||
impl LineControlRegister {
|
||||
#[inline(always)]
|
||||
pub const fn bits(&self) -> u8 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
pub const fn enable_baud_config(&mut self) {
|
||||
self.bits |= 1 << 7;
|
||||
}
|
||||
|
||||
pub const fn disable_baud_config(&mut self) {
|
||||
self.bits &= !(1 << 7);
|
||||
}
|
||||
|
||||
pub const fn set_word_length(&mut self, word_length: WordLength) {
|
||||
const BIT_MASK: u8 = 0b0000_0011u8;
|
||||
self.bits = (self.bits & !BIT_MASK) | (word_length as u8 & BIT_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum WordLength {
|
||||
W8 = 0b11,
|
||||
W7 = 0b10,
|
||||
W6 = 0b01,
|
||||
W5 = 0b00,
|
||||
}
|
||||
Reference in New Issue
Block a user