1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
//! A full serial driver with more advanced I/O support, e.g., interrupt-based data receival.
//!
//! This crate builds on  [`serial_port_basic`], which provides the lower-level types
//! and functions that enable simple interactions with serial ports. 
//! This crate extends that functionality to provide interrupt handlers for receiving data
//! and handling data access in a deferred, asynchronous manner.
//! It also implements additional higher-level I/O traits for serial ports,
//! namely [`core2::io::Read`] and [`core2::io::Write`].
//!
//! # Notes
//! Typically, drivers do not need to be designed in this split manner. 
//! However, the serial port is the very earliest device to be initialized and used
//! in Theseus, as it acts as the backend output stream for Theseus's logger.

#![no_std]
#![feature(abi_x86_interrupt)]

extern crate alloc;

use log::{info, error, warn};
use alloc::format;

use deferred_interrupt_tasks::InterruptRegistrationError;
pub use serial_port_basic::{
    SerialPortAddress,
    SerialPortInterruptEvent,
    SerialPort as SerialPortBasic,
    take_serial_port as take_serial_port_basic,
};

use alloc::{boxed::Box, sync::Arc};
use core::{fmt, ops::{Deref, DerefMut}};
use sync_irq::IrqSafeMutex;
use spin::Once;
use interrupts::{InterruptHandler, EoiBehaviour, InterruptNumber, interrupt_handler};

#[cfg(target_arch = "aarch64")]
use interrupts::{PL011_RX_SPI, init_pl011_rx_interrupt};

// Dependencies below here are temporary and will be removed
// after we have support for separate interrupt handling tasks.
extern crate sync_channel;
use sync_channel::Sender;

/// A temporary hack to allow the serial port interrupt handler
/// to inform a listener on the other end of this channel
/// that a new connection has been detected on one of the serial ports,
/// i.e., that it received some data on a serial port that 
/// didn't expect it or wasn't yet set up to handle incoming data.
pub fn set_connection_listener(
    sender: Sender<SerialPortAddress>
) -> &'static Sender<SerialPortAddress> {
    NEW_CONNECTION_NOTIFIER.call_once(|| sender)
}
static NEW_CONNECTION_NOTIFIER: Once<Sender<SerialPortAddress>> = Once::new();


// Serial ports cannot be reliably probed (discovered dynamically), thus,
// we ensure they are exposed safely as singletons through the below static instances.
static COM1_SERIAL_PORT: Once<Arc<IrqSafeMutex<SerialPort>>> = Once::new();
static COM2_SERIAL_PORT: Once<Arc<IrqSafeMutex<SerialPort>>> = Once::new();
static COM3_SERIAL_PORT: Once<Arc<IrqSafeMutex<SerialPort>>> = Once::new();
static COM4_SERIAL_PORT: Once<Arc<IrqSafeMutex<SerialPort>>> = Once::new();


/// Obtains a reference to the [`SerialPort`] specified by the given [`SerialPortAddress`],
/// if it has been initialized (see [`init_serial_port()`]).
pub fn get_serial_port(
    serial_port_address: SerialPortAddress
) -> Option<&'static Arc<IrqSafeMutex<SerialPort>>> {
    static_port_of(&serial_port_address).get()
}

/// Initializes the [`SerialPort`] specified by the given [`SerialPortAddress`].
///
/// This function also registers the interrupt handler for this serial port
/// such that it can receive data using interrupts instead of busy-waiting or polling.
///
/// If the given serial port has already been initialized, this does nothing
/// and simply returns a reference to the already-initialized serial port.
pub fn init_serial_port(
    serial_port_address: SerialPortAddress,
    serial_port: SerialPortBasic,
) -> Option<&'static Arc<IrqSafeMutex<SerialPort>>> {
    // Note: if we're called by device_manager, we cannot log (as we're modifying the logger config)

    #[cfg(target_arch = "aarch64")]
    if serial_port_address != SerialPortAddress::COM1 {
        return None;
    }

    Some(static_port_of(&serial_port_address).call_once(|| {
        let sp = Arc::new(IrqSafeMutex::new(SerialPort::new(serial_port)));

        #[cfg(target_arch = "x86_64")]
        let (int_num, int_handler) = interrupt_number_handler(&serial_port_address);

        #[cfg(target_arch = "aarch64")]
        let (int_num, int_handler) = (PL011_RX_SPI, primary_serial_port_interrupt_handler);

        SerialPort::register_interrupt_handler(sp.clone(), int_num, int_handler).unwrap();

        #[cfg(target_arch = "aarch64")]
        init_pl011_rx_interrupt().unwrap();

        sp
    }))
}

/// Returns a reference to the static instance of this serial port.
fn static_port_of(
    serial_port_address: &SerialPortAddress
) -> &'static Once<Arc<IrqSafeMutex<SerialPort>>> {
    match serial_port_address {
        SerialPortAddress::COM1 => &COM1_SERIAL_PORT,
        SerialPortAddress::COM2 => &COM2_SERIAL_PORT,
        SerialPortAddress::COM3 => &COM3_SERIAL_PORT,
        SerialPortAddress::COM4 => &COM4_SERIAL_PORT,
    }
}

/// Returns the interrupt number (IRQ vector)
/// and the interrupt handler function for this serial port.
#[cfg(target_arch = "x86_64")]
fn interrupt_number_handler(
    serial_port_address: &SerialPortAddress
) -> (InterruptNumber, InterruptHandler) {
    use interrupts::IRQ_BASE_OFFSET;
    match serial_port_address {
        SerialPortAddress::COM1 | SerialPortAddress::COM3 => (IRQ_BASE_OFFSET + 0x04, primary_serial_port_interrupt_handler),
        SerialPortAddress::COM2 | SerialPortAddress::COM4 => (IRQ_BASE_OFFSET + 0x03, secondary_serial_port_interrupt_handler),
    }
}


/// A serial port abstraction with support for interrupt-based data receival.
pub struct SerialPort {
    /// The basic interface used to access this serial port.
    inner: SerialPortBasic,
    /// The channel endpoint to which data received on this serial port will be pushed.
    /// If `None`, received data will be ignored and a warning printed.
    /// 
    /// The format of data sent via this channel is effectively a slice of bytes,
    /// but is represented without using references as a tuple:
    ///  * the number of bytes actually being transmitted, to be used as an index into the array,
    ///  * an array of bytes holding the actual data, up to 
    data_sender: Option<Sender<DataChunk>>,
}
impl Deref for SerialPort {
    type Target = SerialPortBasic;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}
impl DerefMut for SerialPort {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

impl SerialPort {
    /// Initialize this serial port by giving it ownership and control of
    /// the given basic `serial_port`.
    pub fn new(serial_port: SerialPortBasic) -> SerialPort {
        SerialPort {
            inner: serial_port,
            data_sender: None,
        }
    }

    /// Register the interrupt handler for this serial port
    /// and spawn a deferrent interrupt task to handle its data receival. 
    pub fn register_interrupt_handler(
        serial_port: Arc<IrqSafeMutex<SerialPort>>,
        interrupt_number: InterruptNumber,
        interrupt_handler: InterruptHandler,
    ) -> Result<(), &'static str> {
        let base_port = { 
            let sp = serial_port.lock();
            sp.base_port_address()
        };

        // Register the interrupt handler for this serial port. 
        let registration_result = deferred_interrupt_tasks::register_interrupt_handler(
            interrupt_number,
            interrupt_handler,
            serial_port_receive_deferred,
            serial_port,
            Some(format!("serial_port_deferred_task_irq_{interrupt_number:#X}")),
        );

        match registration_result {
            Ok(deferred_task) => {
                // Now that we successfully registered the interrupt and spawned 
                // a deferred interrupt task, save some information for the 
                // immediate interrupt handler to use when it fires
                // such that it triggers the deferred task to act. 
                info!("Registered interrupt handler at IRQ {:#X} for serial port {:?}.",
                    interrupt_number, base_port,
                );
                match base_port {
                    SerialPortAddress::COM1 | SerialPortAddress::COM3 => {
                        INTERRUPT_ACTION_COM1_COM3.call_once(|| 
                            Box::new(move || {
                                deferred_task.unblock()
                                    .expect("BUG: com_1_com3_interrupt_handler: couldn't unblock deferred task");
                            })
                        );
                    }
                    SerialPortAddress::COM2 | SerialPortAddress::COM4 => {
                        INTERRUPT_ACTION_COM2_COM4.call_once(|| 
                            Box::new(move || {
                                deferred_task.unblock()
                                    .expect("BUG: com_2_com4_interrupt_handler: couldn't unblock deferred task");
                            })
                        );
                    }
                };                
            }
            Err(InterruptRegistrationError::IrqInUse { irq, existing_handler_address }) => {
                if existing_handler_address != interrupt_handler as usize {
                    error!("Failed to register interrupt handler at IRQ {:#X} for serial port {:?}. \
                        Existing interrupt handler was a different handler, at address {:#X}.",
                        irq, base_port, existing_handler_address,
                    );
                }
            }
            Err(InterruptRegistrationError::SpawnError(e)) => return Err(e),
        }

        Ok(())
    }


    /// Tells this `SerialPort` to push received data bytes
    /// onto the given `sender` channel.
    ///
    /// If a sender already exists for this serial port,
    /// the existing sender is *not* replaced and an error is returned.
    pub fn set_data_sender(
        &mut self,
        sender: Sender<DataChunk>
    ) -> Result<(), DataSenderAlreadyExists> {
        if self.data_sender.is_some() { 
            Err(DataSenderAlreadyExists)
        } else {
            self.data_sender = Some(sender);
            Ok(())
        }
    }

}

/// An empty error type indicating that a data sender could not be set
/// for a serial port because a sender had already been set for it.
#[derive(Debug)]
pub struct DataSenderAlreadyExists;


/// A non-blocking implementation of [`core2::io::Read`] that will read bytes into the given `buf`
/// so long as more bytes are available.
/// The read operation will be completed when there are no more bytes to be read,
/// or when the `buf` is filled, whichever comes first.
///
/// Because it's non-blocking, a [`core2::io::ErrorKind::WouldBlock`] error is returned
/// if there are no bytes available to be read, indicating that the read would block.
impl core2::io::Read for SerialPort {
    fn read(&mut self, buf: &mut [u8]) -> core2::io::Result<usize> {
        if !self.data_available() {
            return Err(core2::io::ErrorKind::WouldBlock.into());
        }
        Ok(self.in_bytes(buf))
    }
}

/// A blocking implementation of [`core2::io::Write`] that will write bytes from the given `buf`
/// to the `SerialPort`, waiting until it is ready to transfer all bytes. 
///
/// The `flush()` function is a no-op, since the `SerialPort` does not have buffering. 
impl core2::io::Write for SerialPort {
    fn write(&mut self, buf: &[u8]) -> core2::io::Result<usize> {
        self.out_bytes(buf);
        Ok(buf.len())
    }

    fn flush(&mut self) -> core2::io::Result<()> {
        Ok(())
    }    
}

/// Forward the implementation of [`core::fmt::Write`] to the inner [`SerialPortBasic`].
impl fmt::Write for SerialPort {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.inner.write_str(s) 
    }
}


/// This function is invoked from the serial port's deferred interrupt task,
/// and runs asynchronously after a serial port interrupt has occurred. 
///
/// Currently, we only use interrupts for receiving data on a serial port.
///
/// This is responsible for actually reading the received data from the serial port
/// and doing something with that data.
/// On the other hand, the interrupt handler itself merely notifies the system 
/// that it's time to invoke this function soon.
fn serial_port_receive_deferred(
    serial_port: &Arc<IrqSafeMutex<SerialPort>>
) -> Result<(), ()> {
    let mut buf = DataChunk::empty();
    let bytes_read;
    let base_port;

    let mut input_was_ignored = false;
    let mut send_result = Ok(());

    // We shouldn't hold the serial port lock for long periods of time,
    // and we cannot hold it at all while issuing a log statement.
    { 
        let mut sp = serial_port.lock();
        base_port = sp.base_port_address();
        bytes_read = sp.in_bytes(&mut buf.data);
        if bytes_read > 0 {
            if let Some(ref sender) = sp.data_sender {
                buf.len = bytes_read as u8;
                send_result = sender.try_send(buf);
            } else {
                input_was_ignored = true;
            }
        } else {
            // Ignore this interrupt, as it was caused by a `SerialPortInterruptEvent` 
            // other than data being received, which is the only one we currently care about.
            return Ok(());
        }
    }

    if let Err(e) = send_result {
        error!("Failed to send data received for serial port at {:?}: {:?}.", base_port, e.1);
    }

    if input_was_ignored {
        if let Some(sender) = NEW_CONNECTION_NOTIFIER.get() {
            // info!("Requesting new console to be spawned for this serial port ({:#X})", base_port);
            if let Err(err) = sender.try_send(base_port) {
                error!("Error sending request for new console to be spawned for this serial port ({:?}): {:?}",
                    base_port, err
                );
            }
        } else {
            warn!("Warning: no connection detector; ignoring {}-byte input read from serial port {:?}.",
                bytes_read, base_port
            );
        }
    }

    Ok(())
}

/// A chunk of data read from a serial port that will be transmitted to a receiver.
///
/// For performance, this type is sized to and aligned to 64-byte boundaries 
/// such that it fits in a cache line. 
#[repr(align(64))]
pub struct DataChunk {
    pub len: u8,
    pub data: [u8; 64 - 1],
}
const _: () = assert!(core::mem::size_of::<DataChunk>() == 64);
const _: () = assert!(core::mem::align_of::<DataChunk>() == 64);

impl DataChunk {
    /// Returns a new `DataChunk` filled with zeroes that can be written into.
    pub const fn empty() -> Self {
        DataChunk { len: 0, data: [0; (64 - 1)] }
    }
}

/// A closure specifying the action that will be taken when a serial port interrupt occurs
/// (for COM1 or COM3 interrupts, since they share an IRQ line).
static INTERRUPT_ACTION_COM1_COM3: Once<Box<dyn Fn() + Send + Sync>> = Once::new();
/// A closure specifying the action that will be taken when a serial port interrupt occurs
/// (for COM2 or COM4 interrupts, since they share an IRQ line).
static INTERRUPT_ACTION_COM2_COM4: Once<Box<dyn Fn() + Send + Sync>> = Once::new();


// Cross-platform interrupt handler for the primary serial port.
//
// * On x86_64, this is IRQ 0x24, used for COM1 and COM3 serial ports.
// * On aarch64, this is interrupt 0x21, used for the PL011 UART serial port.
interrupt_handler!(primary_serial_port_interrupt_handler, interrupts::IRQ_BASE_OFFSET + 0x4, _stack_frame, {
    // log::trace!("COM1/COM3 serial handler");

    #[cfg(target_arch = "aarch64")] {
        let mut sp = COM1_SERIAL_PORT.get().unwrap().as_ref().lock();
        sp.acknowledge_interrupt(SerialPortInterruptEvent::DataReceived);
    }

    if let Some(func) = INTERRUPT_ACTION_COM1_COM3.get() {
        func()
    }

    // log::trace!("COM1/COM3 serial handler done");
    EoiBehaviour::HandlerDidNotSendEoi
});

// Cross-platform interrupt handler, only used on x86_64 for COM2 and COM4 (IRQ 0x23).
interrupt_handler!(secondary_serial_port_interrupt_handler, interrupts::IRQ_BASE_OFFSET + 0x3, _stack_frame, {
    // trace!("COM2/COM4 serial handler");
    if let Some(func) = INTERRUPT_ACTION_COM2_COM4.get() {
        func()
    }
    EoiBehaviour::HandlerDidNotSendEoi
});