Skip to content

Commit fb207ba

Browse files
committed
Add support for mmio init
1 parent 5414ab0 commit fb207ba

File tree

1 file changed

+161
-7
lines changed

1 file changed

+161
-7
lines changed

src/lib.rs

Lines changed: 161 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Minimal support for uart_16550 serial I/O.
22
//!
33
//! # Usage
4+
//! ## With `port_{stable, nightly}` feature
45
//!
5-
//! ```no_run
6+
//! ```rust
67
//! use uart_16550::SerialPort;
78
//!
89
//! const SERIAL_IO_PORT: u16 = 0x3F8;
@@ -16,14 +17,51 @@
1617
//! // To receive a byte:
1718
//! let data = serial_port.receive();
1819
//! ```
20+
//!
21+
//! ## With `mmio_{stable, nightly}` feature
22+
//!
23+
//! ```rust
24+
//! use uart_16550::SerialPort;
25+
//!
26+
//! const SERIAL_IO_PORT: usize = 0x1000_0000;
27+
//!
28+
//! let mut serial_port = unsafe { SerialPort::new(SERIAL_IO_PORT) };
29+
//! serial_port.init();
30+
//!
31+
//! // Now the serial port is ready to be used. To send a byte:
32+
//! serial_port.send(42);
33+
//!
34+
//! // To receive a byte:
35+
//! let data = serial_port.receive();
36+
//! ```
1937
2038
#![no_std]
2139
#![warn(missing_docs)]
40+
#![cfg_attr(feature = "mmio_nightly", feature(const_ptr_offset))]
2241

2342
use bitflags::bitflags;
2443
use core::fmt;
44+
45+
#[cfg(any(
46+
all(
47+
not(any(feature = "port_stable", feature = "port_nightly")),
48+
not(any(feature = "mmio_stable", feature = "mmio_nightly"))
49+
),
50+
all(
51+
any(feature = "port_stable", feature = "port_nightly"),
52+
any(feature = "mmio_stable", feature = "mmio_nightly")
53+
)
54+
))]
55+
compile_error!(
56+
"One of these features must be enabled: `port_{stable, nightly}`, `mmio_{stable, nightly}`"
57+
);
58+
59+
#[cfg(any(feature = "port_stable", feature = "port_nightly"))]
2560
use x86_64::instructions::port::Port;
2661

62+
#[cfg(any(feature = "mmio_stable", feature = "mmio_nightly"))]
63+
use core::sync::atomic::{AtomicPtr, Ordering};
64+
2765
macro_rules! wait_for {
2866
($cond:expr) => {
2967
while !$cond {
@@ -53,6 +91,7 @@ bitflags! {
5391
}
5492
}
5593

94+
#[cfg(any(feature = "port_stable", feature = "port_nightly"))]
5695
/// An interface to a serial port that allows sending out individual bytes.
5796
pub struct SerialPort {
5897
data: Port<u8>,
@@ -63,14 +102,15 @@ pub struct SerialPort {
63102
line_sts: Port<u8>,
64103
}
65104

105+
#[cfg(any(feature = "port_stable", feature = "port_nightly"))]
66106
impl SerialPort {
67107
/// Creates a new serial port interface on the given I/O port.
68108
///
69109
/// This function is unsafe because the caller must ensure that the given base address
70110
/// really points to a serial port device.
71-
#[cfg(feature = "nightly")]
72-
pub const unsafe fn new(base: u16) -> SerialPort {
73-
SerialPort {
111+
#[cfg(feature = "port_nightly")]
112+
pub const unsafe fn new(base: u16) -> Self {
113+
Self {
74114
data: Port::new(base),
75115
int_en: Port::new(base + 1),
76116
fifo_ctrl: Port::new(base + 2),
@@ -84,9 +124,9 @@ impl SerialPort {
84124
///
85125
/// This function is unsafe because the caller must ensure that the given base address
86126
/// really points to a serial port device.
87-
#[cfg(not(feature = "nightly"))]
88-
pub unsafe fn new(base: u16) -> SerialPort {
89-
SerialPort {
127+
#[cfg(feature = "port_stable")]
128+
pub unsafe fn new(base: u16) -> Self {
129+
Self {
90130
data: Port::new(base),
91131
int_en: Port::new(base + 1),
92132
fifo_ctrl: Port::new(base + 2),
@@ -160,6 +200,120 @@ impl SerialPort {
160200
}
161201
}
162202

203+
/// An interface to a serial port that allows sending out individual bytes.
204+
#[cfg(any(feature = "mmio_stable", feature = "mmio_nightly"))]
205+
pub struct SerialPort {
206+
data: AtomicPtr<u8>,
207+
int_en: AtomicPtr<u8>,
208+
fifo_ctrl: AtomicPtr<u8>,
209+
line_ctrl: AtomicPtr<u8>,
210+
modem_ctrl: AtomicPtr<u8>,
211+
line_sts: AtomicPtr<u8>,
212+
}
213+
214+
#[cfg(any(feature = "mmio_stable", feature = "mmio_nightly"))]
215+
impl SerialPort {
216+
/// Creates a new serial port interface on the given memory mapped address.
217+
///
218+
/// This function is unsafe because the caller must ensure that the given base address
219+
/// really points to a serial port device.
220+
#[cfg(feature = "mmio_nightly")]
221+
pub const unsafe fn new(base: usize) -> Self {
222+
let base_pointer = base as *mut u8;
223+
Self {
224+
data: AtomicPtr::new(base_pointer),
225+
int_en: AtomicPtr::new(base_pointer.add(1)),
226+
fifo_ctrl: AtomicPtr::new(base_pointer.add(2)),
227+
line_ctrl: AtomicPtr::new(base_pointer.add(3)),
228+
modem_ctrl: AtomicPtr::new(base_pointer.add(4)),
229+
line_sts: AtomicPtr::new(base_pointer.add(5)),
230+
}
231+
}
232+
233+
#[cfg(feature = "mmio_stable")]
234+
pub unsafe fn new(base: usize) -> Self {
235+
let base_pointer = base as *mut u8;
236+
Self {
237+
data: AtomicPtr::new(base_pointer),
238+
int_en: AtomicPtr::new(base_pointer.add(1)),
239+
fifo_ctrl: AtomicPtr::new(base_pointer.add(2)),
240+
line_ctrl: AtomicPtr::new(base_pointer.add(3)),
241+
modem_ctrl: AtomicPtr::new(base_pointer.add(4)),
242+
line_sts: AtomicPtr::new(base_pointer.add(5)),
243+
}
244+
}
245+
246+
/// Initializes the serial port.
247+
///
248+
/// The default configuration of [38400/8-N-1](https://en.wikipedia.org/wiki/8-N-1) is used.
249+
pub fn init(&mut self) {
250+
let self_int_en = self.int_en.load(Ordering::Relaxed);
251+
let self_line_ctrl = self.line_ctrl.load(Ordering::Relaxed);
252+
let self_data = self.data.load(Ordering::Relaxed);
253+
let self_fifo_ctrl = self.fifo_ctrl.load(Ordering::Relaxed);
254+
let self_modem_ctrl = self.modem_ctrl.load(Ordering::Relaxed);
255+
unsafe {
256+
// Disable interrupts
257+
self_int_en.write(0x00);
258+
259+
// Enable DLAB
260+
self_line_ctrl.write(0x80);
261+
262+
// Set maximum speed to 38400 bps by configuring DLL and DLM
263+
self_data.write(0x03);
264+
self_int_en.write(0x00);
265+
266+
// Disable DLAB and set data word length to 8 bits
267+
self_line_ctrl.write(0x03);
268+
269+
// Enable FIFO, clear TX/RX queues and
270+
// set interrupt watermark at 14 bytes
271+
self_fifo_ctrl.write(0xC7);
272+
273+
// Mark data terminal ready, signal request to send
274+
// and enable auxilliary output #2 (used as interrupt line for CPU)
275+
self_modem_ctrl.write(0x0B);
276+
277+
// Enable interrupts
278+
self_int_en.write(0x01);
279+
}
280+
}
281+
282+
fn line_sts(&mut self) -> LineStsFlags {
283+
unsafe { LineStsFlags::from_bits_truncate(*self.line_sts.load(Ordering::Relaxed)) }
284+
}
285+
286+
/// Sends a byte on the serial port.
287+
pub fn send(&mut self, data: u8) {
288+
let self_data = self.data.load(Ordering::Relaxed);
289+
unsafe {
290+
match data {
291+
8 | 0x7F => {
292+
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
293+
self_data.write(8);
294+
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
295+
self_data.write(b' ');
296+
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
297+
self_data.write(8)
298+
}
299+
_ => {
300+
wait_for!(self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY));
301+
self_data.write(data);
302+
}
303+
}
304+
}
305+
}
306+
307+
/// Receives a byte on the serial port.
308+
pub fn receive(&mut self) -> u8 {
309+
let self_data = self.data.load(Ordering::Relaxed);
310+
unsafe {
311+
wait_for!(self.line_sts().contains(LineStsFlags::INPUT_FULL));
312+
self_data.read()
313+
}
314+
}
315+
}
316+
163317
impl fmt::Write for SerialPort {
164318
fn write_str(&mut self, s: &str) -> fmt::Result {
165319
for byte in s.bytes() {

0 commit comments

Comments
 (0)