1
1
//! Minimal support for uart_16550 serial I/O.
2
2
//!
3
3
//! # Usage
4
+ //! ## With `port_{stable, nightly}` feature
4
5
//!
5
- //! ```no_run
6
+ //! ```rust
6
7
//! use uart_16550::SerialPort;
7
8
//!
8
9
//! const SERIAL_IO_PORT: u16 = 0x3F8;
16
17
//! // To receive a byte:
17
18
//! let data = serial_port.receive();
18
19
//! ```
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
+ //! ```
19
37
20
38
#![ no_std]
21
39
#![ warn( missing_docs) ]
40
+ #![ cfg_attr( feature = "mmio_nightly" , feature( const_ptr_offset) ) ]
22
41
23
42
use bitflags:: bitflags;
24
43
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" ) ) ]
25
60
use x86_64:: instructions:: port:: Port ;
26
61
62
+ #[ cfg( any( feature = "mmio_stable" , feature = "mmio_nightly" ) ) ]
63
+ use core:: sync:: atomic:: { AtomicPtr , Ordering } ;
64
+
27
65
macro_rules! wait_for {
28
66
( $cond: expr) => {
29
67
while !$cond {
@@ -53,6 +91,7 @@ bitflags! {
53
91
}
54
92
}
55
93
94
+ #[ cfg( any( feature = "port_stable" , feature = "port_nightly" ) ) ]
56
95
/// An interface to a serial port that allows sending out individual bytes.
57
96
pub struct SerialPort {
58
97
data : Port < u8 > ,
@@ -63,14 +102,15 @@ pub struct SerialPort {
63
102
line_sts : Port < u8 > ,
64
103
}
65
104
105
+ #[ cfg( any( feature = "port_stable" , feature = "port_nightly" ) ) ]
66
106
impl SerialPort {
67
107
/// Creates a new serial port interface on the given I/O port.
68
108
///
69
109
/// This function is unsafe because the caller must ensure that the given base address
70
110
/// 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 {
74
114
data : Port :: new ( base) ,
75
115
int_en : Port :: new ( base + 1 ) ,
76
116
fifo_ctrl : Port :: new ( base + 2 ) ,
@@ -84,9 +124,9 @@ impl SerialPort {
84
124
///
85
125
/// This function is unsafe because the caller must ensure that the given base address
86
126
/// 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 {
90
130
data : Port :: new ( base) ,
91
131
int_en : Port :: new ( base + 1 ) ,
92
132
fifo_ctrl : Port :: new ( base + 2 ) ,
@@ -160,6 +200,120 @@ impl SerialPort {
160
200
}
161
201
}
162
202
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
+
163
317
impl fmt:: Write for SerialPort {
164
318
fn write_str ( & mut self , s : & str ) -> fmt:: Result {
165
319
for byte in s. bytes ( ) {
0 commit comments