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;
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
2342use bitflags:: bitflags;
2443use 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" ) ) ]
2560use x86_64:: instructions:: port:: Port ;
2661
62+ #[ cfg( any( feature = "mmio_stable" , feature = "mmio_nightly" ) ) ]
63+ use core:: sync:: atomic:: { AtomicPtr , Ordering } ;
64+
2765macro_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.
5796pub 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" ) ) ]
66106impl 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+
163317impl fmt:: Write for SerialPort {
164318 fn write_str ( & mut self , s : & str ) -> fmt:: Result {
165319 for byte in s. bytes ( ) {
0 commit comments