Driver
Now let’s use the new Registers struct in our driver.
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
/// Driver for a PL011 UART.
#[derive(Debug)]
pub struct Uart {
registers: *mut Registers,
}
impl Uart {
/// Constructs a new instance of the UART driver for a PL011 device with the
/// given set of registers.
///
/// # Safety
///
/// The given pointer must point to the 8 MMIO control registers of a PL011
/// device, which must be mapped into the address space of the process as
/// device memory and not have any other aliases.
pub unsafe fn new(registers: *mut Registers) -> Self {
Self { registers }
}
/// Writes a single byte to the UART.
pub fn write_byte(&mut self, byte: u8) {
// Wait until there is room in the TX buffer.
while self.read_flag_register().contains(Flags::TXFF) {}
// SAFETY: We know that self.registers points to the control registers
// of a PL011 device which is appropriately mapped.
unsafe {
// Write to the TX buffer.
(&raw mut (*self.registers).dr).write_volatile(byte.into());
}
// Wait until the UART is no longer busy.
while self.read_flag_register().contains(Flags::BUSY) {}
}
/// Reads and returns a pending byte, or `None` if nothing has been
/// received.
pub fn read_byte(&mut self) -> Option<u8> {
if self.read_flag_register().contains(Flags::RXFE) {
None
} else {
// SAFETY: We know that self.registers points to the control
// registers of a PL011 device which is appropriately mapped.
let data = unsafe { (&raw const (*self.registers).dr).read_volatile() };
// TODO: Check for error conditions in bits 8-11.
Some(data as u8)
}
}
fn read_flag_register(&self) -> Flags {
// SAFETY: We know that self.registers points to the control registers
// of a PL011 device which is appropriately mapped.
unsafe { (&raw const (*self.registers).fr).read_volatile() }
}
}
- Note the use of
&raw const/&raw mutto get pointers to individual fields without creating an intermediate reference, which would be unsound. - The example isn’t included in the slides because it is very similar to the
safe-mmioexample which comes next. You can run it in QEMU withmake qemuundersrc/bare-metal/aps/examplesif you need to.