ドライバ
では、ドライバで新しい Registers 構造体を使ってみましょう。
// 著作権 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 use safe_mmio::{UniqueMmioPointer, field, field_shared}; /// Driver for a PL011 UART. #[derive(Debug)] pub struct Uart<'a> { registers: UniqueMmioPointer<'a, Registers>, } impl<'a> Uart<'a> { /// Constructs a new instance of the UART driver for a PL011 device with the /// given set of registers. pub fn new(registers: UniqueMmioPointer<'a, 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) {} // Write to the TX buffer. field!(self.registers, dr).write(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 { let data = field!(self.registers, dr).read(); // TODO: Check for error conditions in bits 8-11. Some(data as u8) } } fn read_flag_register(&self) -> Flags { field_shared!(self.registers, fr).read() } }
- このドライバでは、もはや unsafe コードは必要ありません!
UniqueMmioPointerは、MMIO デバイスまたはレジスタへの生ポインタをラップするものです。UniqueMmioPointer::newの呼び出し元は、それが指定されたライフタイムの間に有効で一意であることを保証するため、フィールドを安全に読み書きするメソッドを提供できます。Uart::newは現在では safe であり、代わりにUniqueMmioPointer::newが unsafe であることに注意してください。- これらの MMIO アクセスは一般に
read_volatileとwrite_volatileのラッパーですが、aarch64 では代わりにアセンブリで実装されています。これは、コンパイラが MMIO 仮想化を妨げる命令を出力してしまうバグを回避するためです。 field!およびfield_shared!マクロは内部で&raw mutと&raw constを使用して、中間参照を作成せずに個々のフィールドへのポインタを取得します。中間参照を作成すると不健全になるためです。field!はUniqueMmioPointerへの可変参照を必要とし、副作用を伴う読み取りと書き込みを許可するUniqueMmioPointerを返します。field_shared!は、UniqueMmioPointerまたはSharedMmioPointerのいずれかへの共有参照で動作します。これは、純粋な読み取りのみを許可するSharedMmioPointerを返します。