跳转到内容

添加驱动

本指南介绍如何为 OpenOS 添加新的设备驱动。

Terminal window
mkdir kernel/src/drivers/my_driver
touch kernel/src/drivers/my_driver/mod.rs
kernel/src/drivers/my_driver/mod.rs
use x86_64::instructions::port::Port;
use spin::Mutex;
/// I/O 端口常量
const DATA_PORT: u16 = 0x100;
const STATUS_PORT: u16 = 0x101;
const COMMAND_PORT: u16 = 0x102;
/// 驱动状态
pub struct MyDriver {
data_port: Port<u8>,
status_port: Port<u8>,
command_port: Port<u8>,
initialized: bool,
}
/// 全局驱动实例
pub static MY_DRIVER: Mutex<MyDriver> = Mutex::new(MyDriver::new());
impl MyDriver {
pub const fn new() -> Self {
MyDriver {
data_port: Port::new(DATA_PORT),
status_port: Port::new(STATUS_PORT),
command_port: Port::new(COMMAND_PORT),
initialized: false,
}
}
pub fn init(&mut self) {
unsafe {
self.command_port.write(0xFF);
while self.status_port.read() & 0x01 == 0 {
core::hint::spin_loop();
}
self.command_port.write(0x01);
}
self.initialized = true;
}
pub fn read_data(&mut self) -> Option<u8> {
if !self.initialized {
return None;
}
unsafe {
if self.status_port.read() & 0x01 != 0 {
Some(self.data_port.read())
} else {
None
}
}
}
pub fn write_data(&mut self, data: u8) -> Result<(), Error> {
if !self.initialized {
return Err(Error::NotInitialized);
}
unsafe {
while self.status_port.read() & 0x02 == 0 {
core::hint::spin_loop();
}
self.data_port.write(data);
}
Ok(())
}
}
kernel/src/drivers/mod.rs
pub mod my_driver;
/// 初始化所有驱动
pub fn init_all() {
my_driver::MY_DRIVER.lock().init();
}
kernel/src/arch/x86_64/interrupts.rs
extern "x86-interrupt" fn my_driver_handler(_stack_frame: InterruptStackFrame) {
drivers::my_driver::MY_DRIVER.lock().handle_interrupt();
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::MyDriver.as_u8());
}
}
// 在 IDT 初始化中注册
pub fn init() {
let mut idt = IDT.lock();
idt[InterruptIndex::MyDriver.as_usize()].set_handler_fn(my_driver_handler);
}
kernel/src/main.rs
#[no_mangle]
pub extern "C" fn _start() -> ! {
// ... 其他初始化 ...
// 初始化驱动
drivers::init_all();
// ... 其他初始化 ...
}

处理字符流的设备(串口、键盘):

pub trait CharDevice {
fn read_byte(&mut self) -> Option<u8>;
fn write_byte(&mut self, byte: u8) -> Result<(), Error>;
fn has_data(&self) -> bool;
}

处理数据块的设备(磁盘、SSD):

pub trait BlockDevice {
fn read_block(&mut self, block: u64, buf: &mut [u8]) -> Result<(), Error>;
fn write_block(&mut self, block: u64, buf: &[u8]) -> Result<(), Error>;
fn block_size(&self) -> usize;
}
pub fn register_irq_handler(
irq: u8,
handler: extern "x86-interrupt" fn(InterruptStackFrame),
) {
let mut idt = IDT.lock();
idt[(PIC_1_OFFSET + irq) as usize].set_handler_fn(handler);
}
  1. 快速处理 - 中断处理函数应该尽快完成
  2. 避免阻塞 - 不要等待 I/O 或获取可能被持有的锁
  3. 发送 EOI - 必须发送中断结束信号
  4. 使用下半部 - 将复杂处理推迟到线程
use x86_64::instructions::port::Port;
pub unsafe fn port_read(port: u16) -> u8 {
let mut port = Port::new(port);
port.read()
}
pub unsafe fn port_write(port: u16, value: u8) {
let mut port = Port::new(port);
port.write(value);
}
serial_println!("[DRIVER] Initializing...");
serial_println!("[DRIVER] Port: 0x{:X}", port_base);
serial_println!("[DRIVER] IRQ: {}", irq);

问题:中断不触发

  • 检查 PIC 初始化
  • 检查 IRQ 使能
  • 检查 IDT 注册

问题:数据丢失

  • 检查 FIFO 缓冲区
  • 增加中断处理速度
  • 使用 DMA

问题:死锁

  • 避免在中断处理中获取锁
  • 使用 without_interrupts() 保护共享数据