x86_q35/
vga_uart_driver.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! VgaUart` — a **synchronous, write-only** façade that lets capsules
6//! use a `hil::uart::Uart`-style interface while we actually write to
7//! the VGA text buffer, not a serial port.
8//!
9//! ## Key design points
10//!
11//! - Implements the *minimum subset* of `Transmit` required by `MuxUart`.
12//!   All writes copy bytes to the global `VGA_TEXT` then schedule a deferred
13//!   call to invoke the transmit callback (split-phase contract).
14//! - **Receive / abort / re-configure** operations just return
15//!   `ErrorCode::NOSUPPORT` — VGA is output-only.
16
17use crate::vga::Vga;
18use core::{cell::Cell, cmp};
19use kernel::deferred_call::{DeferredCall, DeferredCallClient};
20use kernel::hil::uart::{Configure, Parameters, Receive, ReceiveClient, Transmit, TransmitClient};
21use kernel::utilities::cells::TakeCell;
22use kernel::ErrorCode;
23use tock_cells::optional_cell::OptionalCell;
24
25/// UART-compatible wrapper around the VGA text writer.
26pub struct VgaText<'a> {
27    vga_buffer: Vga,
28    tx_client: OptionalCell<&'a dyn TransmitClient>,
29    rx_client: OptionalCell<&'a dyn ReceiveClient>,
30    deferred_call: DeferredCall,
31    pending_buf: TakeCell<'static, [u8]>,
32    pending_len: Cell<usize>,
33}
34
35impl VgaText<'_> {
36    pub fn new() -> Self {
37        Self {
38            vga_buffer: Vga::new(),
39            tx_client: OptionalCell::empty(),
40            rx_client: OptionalCell::empty(),
41            deferred_call: DeferredCall::new(),
42            pending_buf: TakeCell::empty(),
43            pending_len: Cell::new(0),
44        }
45    }
46
47    fn fire_tx_callback(&self, buf: &'static mut [u8], len: usize) {
48        self.tx_client.map(|client| {
49            client.transmitted_buffer(buf, len, Ok(()));
50        });
51    }
52}
53
54// DeferredCallClient implementation
55impl DeferredCallClient for VgaText<'_> {
56    fn handle_deferred_call(&self) {
57        if let Some(buf) = self.pending_buf.take() {
58            let len = self.pending_len.get();
59            self.fire_tx_callback(buf, len);
60        }
61    }
62
63    fn register(&'static self) {
64        self.deferred_call.register(self);
65    }
66}
67
68// Transmit for Vga
69impl<'a> Transmit<'a> for VgaText<'a> {
70    fn set_transmit_client(&self, client: &'a dyn TransmitClient) {
71        self.tx_client.set(client);
72    }
73
74    fn transmit_buffer(
75        &self,
76        buffer: &'static mut [u8],
77        len: usize,
78    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
79        let write_len = cmp::min(len, buffer.len());
80        for &byte in &buffer[..write_len] {
81            self.vga_buffer.write_byte(byte);
82        }
83        self.pending_buf.replace(buffer);
84        self.pending_len.set(len);
85        self.deferred_call.set();
86        Ok(())
87    }
88
89    fn transmit_word(&self, _word: u32) -> Result<(), ErrorCode> {
90        Err(ErrorCode::NOSUPPORT)
91    }
92
93    fn transmit_abort(&self) -> Result<(), ErrorCode> {
94        Err(ErrorCode::NOSUPPORT)
95    }
96}
97
98// Receive for Vga
99impl<'a> Receive<'a> for VgaText<'a> {
100    fn set_receive_client(&self, client: &'a dyn ReceiveClient) {
101        self.rx_client.set(client);
102    }
103
104    fn receive_buffer(
105        &self,
106        buffer: &'static mut [u8],
107        _len: usize,
108    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
109        Err((ErrorCode::NOSUPPORT, buffer))
110    }
111
112    fn receive_word(&self) -> Result<(), ErrorCode> {
113        Err(ErrorCode::NOSUPPORT)
114    }
115
116    fn receive_abort(&self) -> Result<(), ErrorCode> {
117        Err(ErrorCode::NOSUPPORT)
118    }
119}
120
121// Configure for Vga
122impl Configure for VgaText<'_> {
123    fn configure(&self, _params: Parameters) -> Result<(), ErrorCode> {
124        Ok(())
125    }
126}