Files
riscv64-kernel/src/virtio/input.rs

255 lines
7.9 KiB
Rust

use core::{ptr::write_volatile, sync::atomic::Ordering};
use bytes_struct::VolatilePackedStruct;
use crate::{
println,
virtio::{DeviceStatus, QUEUE_SIZE, VirtioPciCommonCfg, Virtqueue},
};
pub struct VirtioPciDriver<S, F: Fn(&mut S, &VirtioInputEvent) = fn(&mut S, &VirtioInputEvent)> {
common_cfg: *mut VirtioPciCommonCfg,
notify_cfg: *mut u16,
isr_cfg: *mut u8,
queue: &'static mut Virtqueue,
event_pool: [VirtioInputEvent; QUEUE_SIZE],
last_used_idx: u16,
notify_off: u32, // Multiplier from PCI notify capability (used to compute notify address)
handle_event: F,
state: S,
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventType {
Sync = 0,
Key = 1,
Relative = 2,
Absolute = 3,
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeRelative {
X = 0,
Y = 1,
Wheel = 8,
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeValue {
Released = 0,
Pressed = 1,
AutomaticRepetition = 2,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct VirtioInputEvent {
pub event_type: EventType,
pub code: u16,
pub value: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct VirtioRelativeEvent {
pub event_type: EventType,
pub code: EventCodeRelative,
pub value: i32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct VirtioKeyEvent {
pub event_type: EventType,
pub code: u16,
pub value: EventCodeValue,
}
impl VirtioInputEvent {
pub fn is_relative(&self) -> bool {
self.event_type == EventType::Relative
}
pub unsafe fn as_relative_event(&self) -> &VirtioRelativeEvent {
unsafe { &*(self as *const Self as *const VirtioRelativeEvent) }
}
pub fn is_key(&self) -> bool {
self.event_type == EventType::Key
}
pub unsafe fn as_key_event(&self) -> &VirtioKeyEvent {
unsafe { &*(self as *const Self as *const VirtioKeyEvent) }
}
}
impl<S, F: Fn(&mut S, &VirtioInputEvent)> VirtioPciDriver<S, F> {
pub const unsafe fn new(handle_event: F, state: S, queue_mem: &'static mut Virtqueue) -> Self {
Self {
common_cfg: core::ptr::null_mut(),
notify_cfg: core::ptr::null_mut(),
isr_cfg: core::ptr::null_mut(),
queue: queue_mem,
event_pool: [VirtioInputEvent {
event_type: EventType::Sync,
code: 0,
value: 0,
}; QUEUE_SIZE],
last_used_idx: 0,
notify_off: 0,
handle_event,
state,
}
}
pub unsafe fn init(
&mut self,
common_cfg: *mut VirtioPciCommonCfg,
notify_cfg: *mut u16,
isr_cfg: *mut u8,
notify_mult: u32,
) {
self.common_cfg = common_cfg;
self.notify_cfg = notify_cfg;
self.isr_cfg = isr_cfg;
self.notify_off = notify_mult;
println!("{:x?}", self.common_cfg);
// --- 1. Reset & Status ---
self.common_cfg
.set_device_status(DeviceStatus::Reinitialized);
self.common_cfg
.set_device_status(DeviceStatus::Acknowledge | DeviceStatus::Driver);
// --- 2. Négociation Features (Bit 32 = Version 1) ---
self.common_cfg.set_driver_feature_select(1); // Page 1
let f1 = self.common_cfg.get_driver_feature();
self.common_cfg.set_driver_feature(f1 | 1); // bit 32 VIRTIO_F_VERSION_1
self.common_cfg.set_device_status(
DeviceStatus::Acknowledge | DeviceStatus::Driver | DeviceStatus::FeaturesOk,
);
if !self
.common_cfg
.get_device_status()
.contains(DeviceStatus::FeaturesOk)
{
panic!("PCI Virtio: Features rejected");
}
// --- 3. Configuration de la Queue 0 ---
self.common_cfg.set_queue_select(0);
self.common_cfg.set_queue_size(QUEUE_SIZE as u16);
// --- 4. Adresses 64 bits ---
let desc_addr = self.queue.descriptors.as_ptr() as u64;
println!("desc_addr: {:x}", desc_addr);
self.common_cfg.set_queue_desc(desc_addr);
let avail_addr = &raw const self.queue.available as u64;
self.common_cfg.set_queue_driver(avail_addr);
let used_addr = &raw const self.queue.used as u64;
self.common_cfg.set_queue_device(used_addr);
// --- 5. Remplissage initial ---
for i in 0..QUEUE_SIZE {
self.queue.descriptors[i].addr = &raw const self.event_pool[i] as u64;
self.queue.descriptors[i].len = core::mem::size_of::<VirtioInputEvent>() as u32;
self.queue.descriptors[i].flags = 2; // VRING_DESC_F_WRITE
self.queue.available.ring[i] = i as u16;
}
self.queue
.available
.idx
.store(QUEUE_SIZE as u16, Ordering::Release);
// --- 6. Activation ---
self.common_cfg.set_queue_enable(1);
if self.common_cfg.get_queue_enable() == 0 {
panic!("Le périphérique refuse d'activer la queue !");
}
// --- 7. Driver OK (must be set after queue is enabled) ---
self.common_cfg.set_device_status(
DeviceStatus::Acknowledge
| DeviceStatus::Driver
| DeviceStatus::DriverOk
| DeviceStatus::FeaturesOk,
);
unsafe {
// --- 8. PREMIER KICK ---
// Compute the notify address for queue 0 using the multiplier provided by PCI capability
let notify_addr = self.notify_cfg.byte_add(
self.notify_off as usize * self.common_cfg.get_queue_notify_off() as usize,
);
core::ptr::write_volatile(notify_addr, 0); // 0 = index de la queue
}
}
pub fn handle_interrupt(&mut self) {
// 1. Lire et acquitter l'ISR PCI (Indispensable pour baisser la ligne IRQ)
let isr = unsafe { self.isr_cfg.read_volatile() };
if isr & 1 == 0 {
// Not a queue interrupt
return;
}
let used_idx = self.queue.used.idx.load(Ordering::Acquire);
while self.last_used_idx != used_idx {
let ring_slot = self.last_used_idx as usize % QUEUE_SIZE;
let used_elem = &self.queue.used.ring[ring_slot];
let event = &self.event_pool[used_elem.id as usize];
(self.handle_event)(&mut self.state, event);
// Recyclage
let avail_head = self.queue.available.idx.load(Ordering::Relaxed) as usize % QUEUE_SIZE;
self.queue.available.ring[avail_head] = used_elem.id as u16;
self.queue.available.idx.fetch_add(1, Ordering::Release);
self.last_used_idx = self.last_used_idx.wrapping_add(1);
}
unsafe {
write_volatile(self.notify_cfg, 0);
}
}
}
pub const PLIC_BASE: usize = 0x0C00_0000;
pub const HANDLING_INTERRUPT: usize = PLIC_BASE + 0x20_0000;
pub const ENABLE_INTERRUPT: usize = PLIC_BASE + 0x2000;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, VolatilePackedStruct)]
pub struct PlicHandlingInterrupt {
pub priority_threshold: u32,
pub claim: u32,
}
/// See https://wiki.osdev.org/PLIC
pub fn init_plic_pci(irq: u32) {
let priority_ptr = (PLIC_BASE + (irq as usize * 4)) as *mut u32;
unsafe { priority_ptr.write_volatile(1) };
// Mettre le Threshold à 0 pour Hart 0 S-Mode (context 1)
let handling_interrupt = (HANDLING_INTERRUPT + 0x1000) as *mut PlicHandlingInterrupt;
handling_interrupt.set_priority_threshold(0);
// Activer l'IRQ pour le S-Mode (Hart 0) (context 1)
// Attention : l'enable bit pour l'IRQ 33 est le bit 1 du deuxième mot u32
let enable_ptr = (ENABLE_INTERRUPT + 0x80 + (irq / 32) as usize * 4) as *mut u32;
unsafe {
let current = enable_ptr.read_volatile();
enable_ptr.write_volatile(current | (1 << (irq % 32)));
}
}