Skip to content

Commit

Permalink
Merge pull request #34 from mkroening/try
Browse files Browse the repository at this point in the history
feat: add `try_send_raw` and `try_receive`
  • Loading branch information
phil-opp authored Jul 10, 2024
2 parents b706c76 + b7c1012 commit cedfeb4
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 44 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Add `try_send_raw` and `try_receive` ([#34](https://github.com/rust-osdev/uart_16550/pull/34))
- Update bitflags dependency to version 2 ([#33](https://github.com/rust-osdev/uart_16550/pull/33))

# 0.3.0 – 2023-08-04
Expand Down
22 changes: 19 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,17 @@
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

use core::fmt;

use bitflags::bitflags;

macro_rules! wait_for {
macro_rules! retry_until_ok {
($cond:expr) => {
while !$cond {
core::hint::spin_loop()
loop {
if let Ok(ok) = $cond {
break ok;
}
core::hint::spin_loop();
}
};
}
Expand Down Expand Up @@ -106,3 +111,14 @@ bitflags! {
// 6 and 7 unknown
}
}

/// The `WouldBlockError` error indicates that the serial device was not ready immediately.
#[non_exhaustive]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct WouldBlockError;

impl fmt::Display for WouldBlockError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("serial device not ready")
}
}
58 changes: 38 additions & 20 deletions src/mmio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::{
sync::atomic::{AtomicPtr, Ordering},
};

use crate::LineStsFlags;
use crate::{LineStsFlags, WouldBlockError};

/// A memory-mapped UART.
#[derive(Debug)]
Expand Down Expand Up @@ -76,31 +76,49 @@ impl MmioSerialPort {

/// Sends a byte on the serial port.
pub fn send(&mut self, data: u8) {
let self_data = self.data.load(Ordering::Relaxed);
unsafe {
match data {
8 | 0x7F => {
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
self_data.write(8);
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
self_data.write(b' ');
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
self_data.write(8)
}
_ => {
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
self_data.write(data);
}
match data {
8 | 0x7F => {
self.send_raw(8);
self.send_raw(b' ');
self.send_raw(8);
}
data => {
self.send_raw(data);
}
}
}

/// Sends a raw byte on the serial port, intended for binary data.
pub fn send_raw(&mut self, data: u8) {
retry_until_ok!(self.try_send_raw(data))
}

/// Tries to send a raw byte on the serial port, intended for binary data.
pub fn try_send_raw(&mut self, data: u8) -> Result<(), WouldBlockError> {
if self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {
let self_data = self.data.load(Ordering::Relaxed);
unsafe {
self_data.write(data);
}
Ok(())
} else {
Err(WouldBlockError)
}
}

/// Receives a byte on the serial port.
pub fn receive(&mut self) -> u8 {
let self_data = self.data.load(Ordering::Relaxed);
unsafe {
wait_for!(self.line_sts().contains(LineStsFlags::INPUT_FULL));
self_data.read()
retry_until_ok!(self.try_receive())
}

/// Tries to receive a byte on the serial port.
pub fn try_receive(&mut self) -> Result<u8, WouldBlockError> {
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
let self_data = self.data.load(Ordering::Relaxed);
let data = unsafe { self_data.read() };
Ok(data)
} else {
Err(WouldBlockError)
}
}
}
Expand Down
52 changes: 31 additions & 21 deletions src/port.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::fmt;

use crate::LineStsFlags;
use crate::{LineStsFlags, WouldBlockError};

/// A x86 I/O port-mapped UART.
#[cfg_attr(docsrs, doc(cfg(any(target_arch = "x86", target_arch = "x86_64"))))]
Expand Down Expand Up @@ -101,37 +101,47 @@ impl SerialPort {

/// Sends a byte on the serial port.
pub fn send(&mut self, data: u8) {
unsafe {
match data {
8 | 0x7F => {
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
x86::io::outb(self.port_data(), 8);
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
x86::io::outb(self.port_data(), b' ');
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
x86::io::outb(self.port_data(), 8);
}
_ => {
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
x86::io::outb(self.port_data(), data);
}
match data {
8 | 0x7F => {
self.send_raw(8);
self.send_raw(b' ');
self.send_raw(8);
}
data => {
self.send_raw(data);
}
}
}

/// Sends a raw byte on the serial port, intended for binary data.
pub fn send_raw(&mut self, data: u8) {
unsafe {
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
x86::io::outb(self.port_data(), data);
retry_until_ok!(self.try_send_raw(data))
}

/// Tries to send a raw byte on the serial port, intended for binary data.
pub fn try_send_raw(&mut self, data: u8) -> Result<(), WouldBlockError> {
if self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {
unsafe {
x86::io::outb(self.port_data(), data);
}
Ok(())
} else {
Err(WouldBlockError)
}
}

/// Receives a byte on the serial port.
pub fn receive(&mut self) -> u8 {
unsafe {
wait_for!(self.line_sts().contains(LineStsFlags::INPUT_FULL));
x86::io::inb(self.port_data())
retry_until_ok!(self.try_receive())
}

/// Tries to receive a byte on the serial port.
pub fn try_receive(&mut self) -> Result<u8, WouldBlockError> {
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
let data = unsafe { x86::io::inb(self.port_data()) };
Ok(data)
} else {
Err(WouldBlockError)
}
}
}
Expand Down

0 comments on commit cedfeb4

Please sign in to comment.