Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Драйвер

Теперь используем новую структуру 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, который позволяет только чистое чтение.