Драйвер
Теперь используем новую структуру Registers в нашем драйвере.
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
/// Драйвер для UART PL011.
#[derive(Debug)]
pub struct Uart {
registers: *mut Registers,
}
impl Uart {
/// Создаёт новый экземпляр драйвера UART для устройства PL011 с
/// заданным набором регистров.
///
/// # Safety
///
/// Заданный указатель должен указывать на 8 управляющих регистров MMIO
/// устройства PL011, которые должны быть отображены в адресное пространство процесса как
/// память устройства и не иметь других алиасов.
pub unsafe fn new(registers: *mut Registers) -> Self {
Self { registers }
}
/// Записывает один байт в UART.
pub fn write_byte(&mut self, byte: u8) {
// Ждём, пока в буфере передачи (TX) не появится место.
while self.read_flag_register().contains(Flags::TXFF) {}
// SAFETY: Мы знаем, что self.registers указывает на управляющие регистры
// устройства PL011, которое корректно отображено.
unsafe {
// Записываем в буфер передачи (TX).
(&raw mut (*self.registers).dr).write_volatile(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 {
// SAFETY: Мы знаем, что self.registers указывает на управляющие
// регистры устройства PL011, которое корректно отображено.
let data = unsafe { (&raw const (*self.registers).dr).read_volatile() };
// TODO: Проверить условия ошибок в битах 8–11.
Some(data as u8)
}
}
fn read_flag_register(&self) -> Flags {
// SAFETY: Мы знаем, что self.registers указывает на управляющие регистры
// устройства PL011, которое корректно отображено.
unsafe { (&raw const (*self.registers).fr).read_volatile() }
}
}
- Обратите внимание на использование
&raw const/&raw mut, чтобы получить указатели на отдельные поля без создания промежуточной ссылки, что было бы некорректно. - Пример не включён в слайды, потому что он очень похож на пример
safe-mmio, который идёт следующим. При необходимости его можно запустить в QEMU командойmake qemuв каталогеsrc/bare-metal/aps/examples.