Move NS16650A UART driver outside `arch`
This commit is contained in:
parent
dee39e21d1
commit
81c2f8d4bd
|
|
@ -26,6 +26,7 @@ rto = "rto"
|
|||
typ = "typ"
|
||||
sigfault = "sigfault"
|
||||
sems = "sems"
|
||||
THRE = "THRE"
|
||||
|
||||
# Files with svg suffix are ignored to check.
|
||||
[type.svg]
|
||||
|
|
|
|||
|
|
@ -4,104 +4,62 @@
|
|||
|
||||
use spin::Once;
|
||||
|
||||
use crate::arch::{boot::DEVICE_TREE, mm::paddr_to_daddr};
|
||||
use crate::{
|
||||
arch::{boot::DEVICE_TREE, mm::paddr_to_daddr},
|
||||
console::uart_ns16650a::{Ns16550aAccess, Ns16550aRegister, Ns16550aUart},
|
||||
sync::{LocalIrqDisabled, SpinLock},
|
||||
};
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct LineStatusRegisterFlags: u8 {
|
||||
const DR = 1 << 0;
|
||||
const OE = 1 << 1;
|
||||
const PE = 1 << 2;
|
||||
const FE = 1 << 3;
|
||||
const BI = 1 << 4;
|
||||
const TFE = 1 << 5;
|
||||
const TE = 1 << 6;
|
||||
const ERROR = 1 << 7;
|
||||
}
|
||||
/// The primary serial port, which serves as an early console.
|
||||
pub static SERIAL_PORT: Once<SpinLock<Ns16550aUart<SerialAccess>, LocalIrqDisabled>> = Once::new();
|
||||
|
||||
/// Access to serial registers via I/O memory in LoongArch.
|
||||
pub struct SerialAccess {
|
||||
base: *mut u8,
|
||||
}
|
||||
|
||||
/// A memory-mapped UART driver for LoongArch.
|
||||
///
|
||||
/// Reference: <https://loongson.github.io/LoongArch-Documentation/Loongson-3A5000-usermanual-EN.html#uart-controller>
|
||||
struct Serial {
|
||||
base_address: *mut u8,
|
||||
}
|
||||
unsafe impl Send for SerialAccess {}
|
||||
unsafe impl Sync for SerialAccess {}
|
||||
|
||||
impl Serial {
|
||||
const DATA_TRANSPORT_REGISTER_OFFSET: usize = 0;
|
||||
const LINE_STATUS_REGISTER_OFFSET: usize = 5;
|
||||
|
||||
/// Creates a serial driver.
|
||||
///
|
||||
impl SerialAccess {
|
||||
/// # Safety
|
||||
///
|
||||
/// The base address must be a valid UART base address.
|
||||
const unsafe fn new(base_address: *mut u8) -> Self {
|
||||
Self { base_address }
|
||||
}
|
||||
|
||||
/// Sends data to the UART.
|
||||
fn send(&self, c: u8) {
|
||||
while !self
|
||||
.line_status_register_flags()
|
||||
.contains(LineStatusRegisterFlags::TFE)
|
||||
{
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
// SAFETY: The safety requirements are upheld by the constructor.
|
||||
unsafe {
|
||||
self.base_address
|
||||
.add(Self::DATA_TRANSPORT_REGISTER_OFFSET)
|
||||
.write_volatile(c);
|
||||
/// The caller must ensure that the base address is a valid serial base address and that it has
|
||||
/// exclusive ownership of the serial registers.
|
||||
const unsafe fn new(base: *mut u8) -> Self {
|
||||
Self { base }
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives data from the UART.
|
||||
fn recv(&self) -> Option<u8> {
|
||||
if self
|
||||
.line_status_register_flags()
|
||||
.contains(LineStatusRegisterFlags::DR)
|
||||
{
|
||||
// SAFETY: The safety requirements are upheld by the constructor.
|
||||
Some(unsafe {
|
||||
self.base_address
|
||||
.add(Self::DATA_TRANSPORT_REGISTER_OFFSET)
|
||||
.read_volatile()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
impl Ns16550aAccess for SerialAccess {
|
||||
fn read(&self, reg: Ns16550aRegister) -> u8 {
|
||||
// SAFETY: `self.base + reg` is a valid register of the serial port.
|
||||
unsafe { core::ptr::read_volatile(self.base.add(reg as u8 as usize)) }
|
||||
}
|
||||
|
||||
fn line_status_register_flags(&self) -> LineStatusRegisterFlags {
|
||||
// SAFETY: The safety requirements are upheld by the constructor.
|
||||
let c = unsafe {
|
||||
self.base_address
|
||||
.add(Self::LINE_STATUS_REGISTER_OFFSET)
|
||||
.read_volatile()
|
||||
};
|
||||
LineStatusRegisterFlags::from_bits_truncate(c)
|
||||
fn write(&mut self, reg: Ns16550aRegister, val: u8) {
|
||||
// SAFETY: `self.base + reg` is a valid register of the serial port.
|
||||
unsafe { core::ptr::write_volatile(self.base.add(reg as u8 as usize), val) };
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: For correctness purposes, the UART registers should not be accessed concurrently.
|
||||
// However, doing so will not cause memory safety violations.
|
||||
unsafe impl Send for Serial {}
|
||||
unsafe impl Sync for Serial {}
|
||||
|
||||
/// The console UART.
|
||||
static CONSOLE_COM1: Once<Serial> = Once::new();
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub(crate) fn init() {
|
||||
let Some(base_address) = lookup_uart_base_address() else {
|
||||
return;
|
||||
};
|
||||
// SAFETY: It is safe because the UART address is acquired from the device tree,
|
||||
// and be mapped in DMW2.
|
||||
CONSOLE_COM1.call_once(|| unsafe { Serial::new(paddr_to_daddr(base_address) as *mut u8) });
|
||||
|
||||
// SAFETY:
|
||||
// 1. The base address is valid and correct because it is acquired from the device tree and
|
||||
// mapped in DMW2.
|
||||
// 2. FIXME: We should reserve the address region in `io_mem_allocator` to ensure the
|
||||
// exclusive ownership.
|
||||
let access = unsafe { SerialAccess::new(paddr_to_daddr(base_address) as *mut u8) };
|
||||
let mut serial = Ns16550aUart::new(access);
|
||||
serial.init();
|
||||
SERIAL_PORT.call_once(move || SpinLock::new(serial));
|
||||
}
|
||||
|
||||
// FIXME: We should reserve the address region in `io_mem_allocator`.
|
||||
fn lookup_uart_base_address() -> Option<usize> {
|
||||
let device_tree = DEVICE_TREE.get().unwrap();
|
||||
let stdout_path = device_tree
|
||||
|
|
@ -115,19 +73,3 @@ fn lookup_uart_base_address() -> Option<usize> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a byte on the serial port.
|
||||
pub(crate) fn send(data: u8) {
|
||||
// Note: It is the caller's responsibility to acquire the correct lock and ensure sequential
|
||||
// access to the UART registers.
|
||||
let Some(uart) = CONSOLE_COM1.get() else {
|
||||
return;
|
||||
};
|
||||
match data {
|
||||
b'\n' => {
|
||||
uart.send(b'\r');
|
||||
uart.send(b'\n');
|
||||
}
|
||||
c => uart.send(c),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,35 @@
|
|||
|
||||
//! The console I/O.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use spin::Once;
|
||||
|
||||
use crate::sync::{LocalIrqDisabled, SpinLock};
|
||||
|
||||
/// The primary serial port, which serves as an early console.
|
||||
pub(crate) static SERIAL_PORT: Once<SpinLock<SbiSerial, LocalIrqDisabled>> =
|
||||
Once::initialized(SpinLock::new(SbiSerial::new()));
|
||||
|
||||
/// A serial port that is implemented via RISC-V SBI.
|
||||
pub(crate) struct SbiSerial {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl SbiSerial {
|
||||
const fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for SbiSerial {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for c in s.as_bytes() {
|
||||
sbi_rt::console_write_byte(*c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub(crate) fn init() {}
|
||||
|
||||
/// Sends a byte on the serial port.
|
||||
pub(crate) fn send(data: u8) {
|
||||
sbi_rt::console_write_byte(data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,3 @@
|
|||
//! Device-related APIs.
|
||||
|
||||
pub mod io_port;
|
||||
pub mod serial;
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! A port-mapped UART. Copied from uart_16550.
|
||||
|
||||
#![expect(dead_code)]
|
||||
|
||||
use crate::{
|
||||
arch::device::io_port::{ReadWriteAccess, WriteOnlyAccess},
|
||||
io::IoPort,
|
||||
};
|
||||
/// A serial port.
|
||||
///
|
||||
/// Serial ports are a legacy communications port common on IBM-PC compatible computers.
|
||||
/// Ref: <https://wiki.osdev.org/Serial_Ports>
|
||||
pub struct SerialPort {
|
||||
/// Data Register
|
||||
data: IoPort<u8, ReadWriteAccess>,
|
||||
/// Interrupt Enable Register
|
||||
int_en: IoPort<u8, WriteOnlyAccess>,
|
||||
/// First In First Out Control Register
|
||||
fifo_ctrl: IoPort<u8, WriteOnlyAccess>,
|
||||
/// Line control Register
|
||||
line_ctrl: IoPort<u8, WriteOnlyAccess>,
|
||||
/// Modem Control Register
|
||||
modem_ctrl: IoPort<u8, WriteOnlyAccess>,
|
||||
/// Line status Register
|
||||
line_status: IoPort<u8, ReadWriteAccess>,
|
||||
/// Modem Status Register
|
||||
modem_status: IoPort<u8, ReadWriteAccess>,
|
||||
}
|
||||
|
||||
impl SerialPort {
|
||||
/// Creates a serial port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the base port is a valid serial base port and that it has
|
||||
/// exclusive ownership of the serial ports.
|
||||
pub const unsafe fn new(port: u16) -> Self {
|
||||
// SAFETY: The safety is upheld by the caller.
|
||||
unsafe {
|
||||
Self {
|
||||
data: IoPort::new(port),
|
||||
int_en: IoPort::new(port + 1),
|
||||
fifo_ctrl: IoPort::new(port + 2),
|
||||
line_ctrl: IoPort::new(port + 3),
|
||||
modem_ctrl: IoPort::new(port + 4),
|
||||
line_status: IoPort::new(port + 5),
|
||||
modem_status: IoPort::new(port + 6),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub fn init(&self) {
|
||||
// Disable interrupts
|
||||
self.int_en.write(0x00);
|
||||
// Enable DLAB
|
||||
self.line_ctrl.write(0x80);
|
||||
// Set maximum speed to 38400 bps by configuring DLL and DLM
|
||||
self.data.write(0x03);
|
||||
self.int_en.write(0x00);
|
||||
// Disable DLAB and set data word length to 8 bits
|
||||
self.line_ctrl.write(0x03);
|
||||
// Enable FIFO, clear TX/RX queues and
|
||||
// set interrupt watermark at 14 bytes
|
||||
self.fifo_ctrl.write(0xC7);
|
||||
// Mark data terminal ready, signal request to send
|
||||
// and enable auxiliary output #2 (used as interrupt line for CPU)
|
||||
self.modem_ctrl.write(0x0B);
|
||||
// Enable interrupts
|
||||
self.int_en.write(0x01);
|
||||
}
|
||||
|
||||
/// Sends data to the data port
|
||||
#[inline]
|
||||
pub fn send(&self, data: u8) {
|
||||
self.data.write(data);
|
||||
}
|
||||
|
||||
/// Receives data from the data port
|
||||
#[inline]
|
||||
pub fn recv(&self) -> u8 {
|
||||
self.data.read()
|
||||
}
|
||||
|
||||
/// Gets line status
|
||||
#[inline]
|
||||
pub fn line_status(&self) -> u8 {
|
||||
self.line_status.read()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,42 +2,87 @@
|
|||
|
||||
//! The console I/O.
|
||||
|
||||
use super::device::serial::SerialPort;
|
||||
use crate::io::reserve_io_port_range;
|
||||
use spin::Once;
|
||||
use x86_64::instructions::port::ReadWriteAccess;
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct LineSts: u8 {
|
||||
const INPUT_FULL = 1;
|
||||
const OUTPUT_EMPTY = 1 << 5;
|
||||
}
|
||||
}
|
||||
use crate::{
|
||||
console::uart_ns16650a::{Ns16550aAccess, Ns16550aRegister, Ns16550aUart},
|
||||
io::{IoPort, reserve_io_port_range},
|
||||
sync::{LocalIrqDisabled, SpinLock},
|
||||
};
|
||||
|
||||
static CONSOLE_COM1_PORT: SerialPort = unsafe { SerialPort::new(0x3F8) };
|
||||
/// The primary serial port, which serves as an early console.
|
||||
pub static SERIAL_PORT: Once<SpinLock<Ns16550aUart<SerialAccess>, LocalIrqDisabled>> =
|
||||
Once::initialized(SpinLock::new(Ns16550aUart::new(
|
||||
// SAFETY:
|
||||
// 1. It is assumed that the serial port exists and can be accessed via the I/O registers.
|
||||
// (FIXME: This needs to be confirmed by checking the ACPI table or using kernel
|
||||
// parameters to obtain early information for building the early console.)
|
||||
// 2. `reserve_io_port_range` guarantees exclusive ownership of the I/O registers.
|
||||
unsafe { SerialAccess::new(0x3F8) },
|
||||
)));
|
||||
reserve_io_port_range!(0x3F8..0x400);
|
||||
|
||||
/// Access to serial registers via I/O ports in x86.
|
||||
#[derive(Debug)]
|
||||
pub struct SerialAccess {
|
||||
data: IoPort<u8, ReadWriteAccess>,
|
||||
int_en: IoPort<u8, ReadWriteAccess>,
|
||||
fifo_ctrl: IoPort<u8, ReadWriteAccess>,
|
||||
line_ctrl: IoPort<u8, ReadWriteAccess>,
|
||||
modem_ctrl: IoPort<u8, ReadWriteAccess>,
|
||||
line_stat: IoPort<u8, ReadWriteAccess>,
|
||||
modem_stat: IoPort<u8, ReadWriteAccess>,
|
||||
}
|
||||
|
||||
impl SerialAccess {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the base port is a valid serial base port and that it has
|
||||
/// exclusive ownership of the serial registers.
|
||||
const unsafe fn new(port: u16) -> Self {
|
||||
// SAFETY: The safety is upheld by the caller.
|
||||
unsafe {
|
||||
Self {
|
||||
data: IoPort::new(port),
|
||||
int_en: IoPort::new(port + 1),
|
||||
fifo_ctrl: IoPort::new(port + 2),
|
||||
line_ctrl: IoPort::new(port + 3),
|
||||
modem_ctrl: IoPort::new(port + 4),
|
||||
line_stat: IoPort::new(port + 5),
|
||||
modem_stat: IoPort::new(port + 6),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ns16550aAccess for SerialAccess {
|
||||
fn read(&self, reg: Ns16550aRegister) -> u8 {
|
||||
match reg {
|
||||
Ns16550aRegister::DataOrDivisorLo => self.data.read(),
|
||||
Ns16550aRegister::IntEnOrDivisorHi => self.int_en.read(),
|
||||
Ns16550aRegister::FifoCtrl => self.fifo_ctrl.read(),
|
||||
Ns16550aRegister::LineCtrl => self.line_ctrl.read(),
|
||||
Ns16550aRegister::ModemCtrl => self.modem_ctrl.read(),
|
||||
Ns16550aRegister::LineStat => self.line_stat.read(),
|
||||
Ns16550aRegister::ModemStat => self.modem_stat.read(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, reg: Ns16550aRegister, val: u8) {
|
||||
match reg {
|
||||
Ns16550aRegister::DataOrDivisorLo => self.data.write(val),
|
||||
Ns16550aRegister::IntEnOrDivisorHi => self.int_en.write(val),
|
||||
Ns16550aRegister::FifoCtrl => self.fifo_ctrl.write(val),
|
||||
Ns16550aRegister::LineCtrl => self.line_ctrl.write(val),
|
||||
Ns16550aRegister::ModemCtrl => self.modem_ctrl.write(val),
|
||||
Ns16550aRegister::LineStat => self.line_stat.write(val),
|
||||
Ns16550aRegister::ModemStat => self.modem_stat.write(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub(crate) fn init() {
|
||||
CONSOLE_COM1_PORT.init();
|
||||
}
|
||||
|
||||
fn line_sts() -> LineSts {
|
||||
LineSts::from_bits_truncate(CONSOLE_COM1_PORT.line_status())
|
||||
}
|
||||
|
||||
/// Sends a byte on the serial port.
|
||||
pub(crate) fn send(data: u8) {
|
||||
match data {
|
||||
8 | 0x7F => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.send(8);
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.send(b' ');
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.send(8);
|
||||
}
|
||||
_ => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.send(data);
|
||||
}
|
||||
}
|
||||
SERIAL_PORT.get().unwrap().lock().init();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,35 +2,28 @@
|
|||
|
||||
//! Console output.
|
||||
|
||||
use core::fmt::{self, Arguments, Write};
|
||||
use core::fmt::{Arguments, Write};
|
||||
|
||||
use crate::sync::{LocalIrqDisabled, SpinLock};
|
||||
use crate::arch::serial::SERIAL_PORT;
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for &c in s.as_bytes() {
|
||||
crate::arch::serial::send(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
static STDOUT: SpinLock<Stdout, LocalIrqDisabled> = SpinLock::new(Stdout);
|
||||
pub mod uart_ns16650a;
|
||||
|
||||
/// Prints formatted arguments to the console.
|
||||
pub fn early_print(args: Arguments) {
|
||||
let Some(serial) = SERIAL_PORT.get() else {
|
||||
return;
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
crate::arch::if_tdx_enabled!({
|
||||
// Hold the lock to prevent the logs from interleaving.
|
||||
let _guard = STDOUT.lock();
|
||||
let _guard = serial.lock();
|
||||
tdx_guest::print(args);
|
||||
} else {
|
||||
STDOUT.lock().write_fmt(args).unwrap();
|
||||
serial.lock().write_fmt(args).unwrap();
|
||||
});
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
STDOUT.lock().write_fmt(args).unwrap();
|
||||
serial.lock().write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
/// Prints to the console.
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! NS16550A UART.
|
||||
//!
|
||||
//! This is used as an early console in x86 and LoongArch. It also exists on some (but not all)
|
||||
//! RISC-V and ARM platforms.
|
||||
//!
|
||||
//! Reference: <https://bitsavers.trailing-edge.com/components/national/_appNotes/AN-0491.pdf>
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
/// Registers of a NS16550A UART.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Ns16550aRegister {
|
||||
/// Receive/Transmit Data Register or Divisor Latch Low.
|
||||
DataOrDivisorLo,
|
||||
/// Interrupt Enable Register or Divisor Latch High.
|
||||
IntEnOrDivisorHi,
|
||||
/// FIFO Control Register.
|
||||
FifoCtrl,
|
||||
/// Line Control Register.
|
||||
LineCtrl,
|
||||
/// Modem Control Register.
|
||||
ModemCtrl,
|
||||
/// Line Status Register.
|
||||
LineStat,
|
||||
/// Modem Status Register.
|
||||
ModemStat,
|
||||
}
|
||||
|
||||
/// A trait that provides methods to access NS16550A registers.
|
||||
pub trait Ns16550aAccess {
|
||||
/// Reads from an NS16550A register.
|
||||
fn read(&self, reg: Ns16550aRegister) -> u8;
|
||||
|
||||
/// Writes to an NS16550A register.
|
||||
fn write(&mut self, reg: Ns16550aRegister, val: u8);
|
||||
}
|
||||
|
||||
/// An NS16550A UART.
|
||||
#[derive(Debug)]
|
||||
pub struct Ns16550aUart<A: Ns16550aAccess> {
|
||||
access: A,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct LineStat: u8 {
|
||||
/// Data ready (DR).
|
||||
const DR = 1 << 0;
|
||||
/// Transmitter holding register empty (THRE).
|
||||
const THRE = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ns16550aAccess> Ns16550aUart<A> {
|
||||
/// Creates a new instance.
|
||||
pub const fn new(access: A) -> Self {
|
||||
Self { access }
|
||||
}
|
||||
|
||||
/// Initializes the device.
|
||||
///
|
||||
/// This will set the baud rate to 115200 bps and configure IRQs to trigger when new data is
|
||||
/// received.
|
||||
pub fn init(&mut self) {
|
||||
// Divisor Latch Access Bit.
|
||||
const DLAB: u8 = 0x80;
|
||||
|
||||
// Baud Rate: 115200 bps / divisor
|
||||
self.access.write(Ns16550aRegister::LineCtrl, DLAB);
|
||||
self.access.write(Ns16550aRegister::DataOrDivisorLo, 0x01);
|
||||
self.access.write(Ns16550aRegister::IntEnOrDivisorHi, 0x00);
|
||||
|
||||
// Line Control: 8-bit, no parity, one stop bit.
|
||||
self.access.write(Ns16550aRegister::LineCtrl, 0x03);
|
||||
// FIFO Control: Disabled.
|
||||
self.access.write(Ns16550aRegister::FifoCtrl, 0x00);
|
||||
// Modem Control: IRQs enabled, RTS/DSR set.
|
||||
self.access.write(Ns16550aRegister::ModemCtrl, 0x0B);
|
||||
// Interrupt Enable: IRQs on received data.
|
||||
self.access.write(Ns16550aRegister::IntEnOrDivisorHi, 0x01);
|
||||
}
|
||||
|
||||
/// Sends a byte.
|
||||
///
|
||||
/// If no room is available, it will spin until there is room.
|
||||
pub fn send(&mut self, data: u8) {
|
||||
while !self.line_stat().contains(LineStat::THRE) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.access.write(Ns16550aRegister::DataOrDivisorLo, data);
|
||||
}
|
||||
|
||||
/// Receives a byte.
|
||||
///
|
||||
/// If no byte is available, it will return `None`.
|
||||
pub fn recv(&mut self) -> Option<u8> {
|
||||
if !self.line_stat().contains(LineStat::DR) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.access.read(Ns16550aRegister::DataOrDivisorLo))
|
||||
}
|
||||
|
||||
fn line_stat(&self) -> LineStat {
|
||||
LineStat::from_bits_truncate(self.access.read(Ns16550aRegister::LineStat))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ns16550aAccess> fmt::Write for Ns16550aUart<A> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for c in s.as_bytes() {
|
||||
if *c == b'\n' {
|
||||
self.send(b'\r');
|
||||
}
|
||||
self.send(*c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ use crate::{Error, prelude::*};
|
|||
/// PORT.write(PORT.read() + 1)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub struct IoPort<T, A> {
|
||||
port: u16,
|
||||
is_overlapping: bool,
|
||||
|
|
|
|||
Loading…
Reference in New Issue