3737
3838#![ no_std]
3939#![ warn( missing_docs) ]
40- #![ cfg_attr( feature = "mmio_nightly" , feature( const_ptr_offset) ) ]
41-
42- use core:: fmt;
40+ #![ cfg_attr( feature = "nightly" , feature( const_ptr_offset) ) ]
4341
4442use bitflags:: bitflags;
4543
46- #[ cfg( any(
47- all(
48- not( any( feature = "port_stable" , feature = "port_nightly" ) ) ,
49- not( any( feature = "mmio_stable" , feature = "mmio_nightly" ) )
50- ) ,
51- all(
52- any( feature = "port_stable" , feature = "port_nightly" ) ,
53- any( feature = "mmio_stable" , feature = "mmio_nightly" )
54- )
55- ) ) ]
56- compile_error ! ( "One of these features must be enabled: `port_{stable, nightly}`, `mmio_{stable, nightly}`" ) ;
57-
58- #[ cfg( any( feature = "mmio_stable" , feature = "mmio_nightly" ) ) ]
59- use core:: sync:: atomic:: {
60- AtomicPtr ,
61- Ordering ,
62- } ;
63-
64- #[ cfg( any( feature = "port_stable" , feature = "port_nightly" ) ) ]
65- use x86_64:: instructions:: port:: Port ;
44+ #[ cfg( not( any( feature = "stable" , feature = "nightly" ) ) ) ]
45+ compile_error ! ( "Either the `stable` or `nightly` feature must be enabled" ) ;
6646
6747macro_rules! wait_for {
6848 ( $cond: expr) => {
@@ -72,6 +52,14 @@ macro_rules! wait_for {
7252 } ;
7353}
7454
55+ pub mod mmio;
56+ #[ cfg( target_arch = "x86_64" ) ]
57+ pub mod x86_64;
58+
59+ #[ cfg( target_arch = "x86_64" ) ]
60+ pub use crate :: x86_64:: SerialPort ;
61+ pub use crate :: mmio:: MmioSerialPort ;
62+
7563bitflags ! {
7664 /// Interrupt enable flags
7765 struct IntEnFlags : u8 {
@@ -92,235 +80,3 @@ bitflags! {
9280 // 6 and 7 unknown
9381 }
9482}
95-
96- #[ cfg( any( feature = "port_stable" , feature = "port_nightly" ) ) ]
97- /// An interface to a serial port that allows sending out individual bytes.
98- pub struct SerialPort {
99- data : Port < u8 > ,
100- int_en : Port < u8 > ,
101- fifo_ctrl : Port < u8 > ,
102- line_ctrl : Port < u8 > ,
103- modem_ctrl : Port < u8 > ,
104- line_sts : Port < u8 > ,
105- }
106-
107- #[ cfg( any( feature = "port_stable" , feature = "port_nightly" ) ) ]
108- impl SerialPort {
109- /// Creates a new serial port interface on the given I/O port.
110- ///
111- /// This function is unsafe because the caller must ensure that the given base address
112- /// really points to a serial port device.
113- #[ cfg( feature = "port_nightly" ) ]
114- pub const unsafe fn new ( base : u16 ) -> Self {
115- Self {
116- data : Port :: new ( base) ,
117- int_en : Port :: new ( base + 1 ) ,
118- fifo_ctrl : Port :: new ( base + 2 ) ,
119- line_ctrl : Port :: new ( base + 3 ) ,
120- modem_ctrl : Port :: new ( base + 4 ) ,
121- line_sts : Port :: new ( base + 5 ) ,
122- }
123- }
124-
125- /// Creates a new serial port interface on the given I/O port.
126- ///
127- /// This function is unsafe because the caller must ensure that the given base address
128- /// really points to a serial port device.
129- #[ cfg( feature = "port_stable" ) ]
130- pub unsafe fn new ( base : u16 ) -> Self {
131- Self {
132- data : Port :: new ( base) ,
133- int_en : Port :: new ( base + 1 ) ,
134- fifo_ctrl : Port :: new ( base + 2 ) ,
135- line_ctrl : Port :: new ( base + 3 ) ,
136- modem_ctrl : Port :: new ( base + 4 ) ,
137- line_sts : Port :: new ( base + 5 ) ,
138- }
139- }
140-
141- /// Initializes the serial port.
142- ///
143- /// The default configuration of [38400/8-N-1](https://en.wikipedia.org/wiki/8-N-1) is used.
144- pub fn init ( & mut self ) {
145- unsafe {
146- // Disable interrupts
147- self . int_en . write ( 0x00 ) ;
148-
149- // Enable DLAB
150- self . line_ctrl . write ( 0x80 ) ;
151-
152- // Set maximum speed to 38400 bps by configuring DLL and DLM
153- self . data . write ( 0x03 ) ;
154- self . int_en . write ( 0x00 ) ;
155-
156- // Disable DLAB and set data word length to 8 bits
157- self . line_ctrl . write ( 0x03 ) ;
158-
159- // Enable FIFO, clear TX/RX queues and
160- // set interrupt watermark at 14 bytes
161- self . fifo_ctrl . write ( 0xC7 ) ;
162-
163- // Mark data terminal ready, signal request to send
164- // and enable auxilliary output #2 (used as interrupt line for CPU)
165- self . modem_ctrl . write ( 0x0B ) ;
166-
167- // Enable interrupts
168- self . int_en . write ( 0x01 ) ;
169- }
170- }
171-
172- fn line_sts ( & mut self ) -> LineStsFlags {
173- unsafe { LineStsFlags :: from_bits_truncate ( self . line_sts . read ( ) ) }
174- }
175-
176- /// Sends a byte on the serial port.
177- pub fn send ( & mut self , data : u8 ) {
178- unsafe {
179- match data {
180- 8 | 0x7F => {
181- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
182- self . data . write ( 8 ) ;
183- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
184- self . data . write ( b' ' ) ;
185- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
186- self . data . write ( 8 )
187- } ,
188- _ => {
189- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
190- self . data . write ( data) ;
191- } ,
192- }
193- }
194- }
195-
196- /// Receives a byte on the serial port.
197- pub fn receive ( & mut self ) -> u8 {
198- unsafe {
199- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: INPUT_FULL ) ) ;
200- self . data . read ( )
201- }
202- }
203- }
204-
205- /// An interface to a serial port that allows sending out individual bytes.
206- #[ cfg( any( feature = "mmio_stable" , feature = "mmio_nightly" ) ) ]
207- pub struct MmioSerialPort {
208- data : AtomicPtr < u8 > ,
209- int_en : AtomicPtr < u8 > ,
210- fifo_ctrl : AtomicPtr < u8 > ,
211- line_ctrl : AtomicPtr < u8 > ,
212- modem_ctrl : AtomicPtr < u8 > ,
213- line_sts : AtomicPtr < u8 > ,
214- }
215-
216- #[ cfg( any( feature = "mmio_stable" , feature = "mmio_nightly" ) ) ]
217- impl SerialPort {
218- /// Creates a new serial port interface on the given memory mapped address.
219- ///
220- /// This function is unsafe because the caller must ensure that the given base address
221- /// really points to a serial port device.
222- #[ cfg( feature = "mmio_nightly" ) ]
223- pub const unsafe fn new ( base : usize ) -> Self {
224- let base_pointer = base as * mut u8 ;
225- Self {
226- data : AtomicPtr :: new ( base_pointer) ,
227- int_en : AtomicPtr :: new ( base_pointer. add ( 1 ) ) ,
228- fifo_ctrl : AtomicPtr :: new ( base_pointer. add ( 2 ) ) ,
229- line_ctrl : AtomicPtr :: new ( base_pointer. add ( 3 ) ) ,
230- modem_ctrl : AtomicPtr :: new ( base_pointer. add ( 4 ) ) ,
231- line_sts : AtomicPtr :: new ( base_pointer. add ( 5 ) ) ,
232- }
233- }
234-
235- #[ cfg( feature = "mmio_stable" ) ]
236- pub unsafe fn new ( base : usize ) -> Self {
237- let base_pointer = base as * mut u8 ;
238- Self {
239- data : AtomicPtr :: new ( base_pointer) ,
240- int_en : AtomicPtr :: new ( base_pointer. add ( 1 ) ) ,
241- fifo_ctrl : AtomicPtr :: new ( base_pointer. add ( 2 ) ) ,
242- line_ctrl : AtomicPtr :: new ( base_pointer. add ( 3 ) ) ,
243- modem_ctrl : AtomicPtr :: new ( base_pointer. add ( 4 ) ) ,
244- line_sts : AtomicPtr :: new ( base_pointer. add ( 5 ) ) ,
245- }
246- }
247-
248- /// Initializes the serial port.
249- ///
250- /// The default configuration of [38400/8-N-1](https://en.wikipedia.org/wiki/8-N-1) is used.
251- pub fn init ( & mut self ) {
252- let self_int_en = self . int_en . load ( Ordering :: Relaxed ) ;
253- let self_line_ctrl = self . line_ctrl . load ( Ordering :: Relaxed ) ;
254- let self_data = self . data . load ( Ordering :: Relaxed ) ;
255- let self_fifo_ctrl = self . fifo_ctrl . load ( Ordering :: Relaxed ) ;
256- let self_modem_ctrl = self . modem_ctrl . load ( Ordering :: Relaxed ) ;
257- unsafe {
258- // Disable interrupts
259- self_int_en. write ( 0x00 ) ;
260-
261- // Enable DLAB
262- self_line_ctrl. write ( 0x80 ) ;
263-
264- // Set maximum speed to 38400 bps by configuring DLL and DLM
265- self_data. write ( 0x03 ) ;
266- self_int_en. write ( 0x00 ) ;
267-
268- // Disable DLAB and set data word length to 8 bits
269- self_line_ctrl. write ( 0x03 ) ;
270-
271- // Enable FIFO, clear TX/RX queues and
272- // set interrupt watermark at 14 bytes
273- self_fifo_ctrl. write ( 0xC7 ) ;
274-
275- // Mark data terminal ready, signal request to send
276- // and enable auxilliary output #2 (used as interrupt line for CPU)
277- self_modem_ctrl. write ( 0x0B ) ;
278-
279- // Enable interrupts
280- self_int_en. write ( 0x01 ) ;
281- }
282- }
283-
284- fn line_sts ( & mut self ) -> LineStsFlags {
285- unsafe { LineStsFlags :: from_bits_truncate ( * self . line_sts . load ( Ordering :: Relaxed ) ) }
286- }
287-
288- /// Sends a byte on the serial port.
289- pub fn send ( & mut self , data : u8 ) {
290- let self_data = self . data . load ( Ordering :: Relaxed ) ;
291- unsafe {
292- match data {
293- 8 | 0x7F => {
294- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
295- self_data. write ( 8 ) ;
296- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
297- self_data. write ( b' ' ) ;
298- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
299- self_data. write ( 8 )
300- } ,
301- _ => {
302- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: OUTPUT_EMPTY ) ) ;
303- self_data. write ( data) ;
304- } ,
305- }
306- }
307- }
308-
309- /// Receives a byte on the serial port.
310- pub fn receive ( & mut self ) -> u8 {
311- let self_data = self . data . load ( Ordering :: Relaxed ) ;
312- unsafe {
313- wait_for ! ( self . line_sts( ) . contains( LineStsFlags :: INPUT_FULL ) ) ;
314- self_data. read ( )
315- }
316- }
317- }
318-
319- impl fmt:: Write for SerialPort {
320- fn write_str ( & mut self , s : & str ) -> fmt:: Result {
321- for byte in s. bytes ( ) {
322- self . send ( byte) ;
323- }
324- Ok ( ( ) )
325- }
326- }
0 commit comments