Драйвер
Теперь используем новую структуру Registers в нашем драйвере.
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
use safe_mmio::{UniqueMmioPointer, field, field_shared};
/// Драйвер для UART PL011.
#[derive(Debug)]
pub struct Uart<'a> {
registers: UniqueMmioPointer<'a, Registers>,
}
impl<'a> Uart<'a> {
/// Создаёт новый экземпляр драйвера UART для устройства PL011 с
/// заданным набором регистров.
pub fn new(registers: UniqueMmioPointer<'a, Registers>) -> Self {
Self { registers }
}
/// Записывает один байт в UART.
pub fn write_byte(&mut self, byte: u8) {
// Ждём, пока в буфере передачи (TX) не появится место.
while self.read_flag_register().contains(Flags::TXFF) {}
// Записываем в буфер передачи (TX).
field!(self.registers, dr).write(byte.into());
// Ждём, пока UART больше не занят.
while self.read_flag_register().contains(Flags::BUSY) {}
}
/// Читает и возвращает ожидающий байт или `None`, если ничего
/// не получено.
pub fn read_byte(&mut self) -> Option<u8> {
if self.read_flag_register().contains(Flags::RXFE) {
None
} else {
let data = field!(self.registers, dr).read();
// TODO: Проверить условия ошибок в битах 8–11.
Some(data as u8)
}
}
fn read_flag_register(&self) -> Flags {
field_shared!(self.registers, fr).read()
}
}
- Драйверу больше не нужен никакой небезопасный код!
UniqueMmioPointer— обёртка вокруг сырого указателя на устройство MMIO или регистр. ВызывающийUniqueMmioPointer::newобещает, что указатель допустим и уникален на заданное время жизни, поэтому можно предоставить безопасные методы для чтения и записи полей.- Обратите внимание, что
Uart::newтеперь безопасна; вместо этого небезопаснаUniqueMmioPointer::new. - Эти обращения MMIO обычно являются обёрткой над
read_volatileиwrite_volatile, хотя на aarch64 они реализованы на ассемблере, чтобы обойти ошибку, из-за которой компилятор может выдавать инструкции, мешающие виртуализации MMIO. - Макросы
field!иfield_shared!внутри используют&raw mutи&raw const, чтобы получить указатели на отдельные поля без создания промежуточной ссылки, что было бы некорректно. field!требует изменяемую ссылку наUniqueMmioPointerи возвращаетUniqueMmioPointer, который позволяет читать с побочными эффектами и записывать.field_shared!работает с разделяемой ссылкой либо наUniqueMmioPointer, либо наSharedMmioPointer. Он возвращаетSharedMmioPointer, который позволяет только чистое чтение.