Better virtual file system, keyboard through MMIO&VirtIO
This commit is contained in:
192
src/virtio/input.rs
Normal file
192
src/virtio/input.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
use core::{
|
||||
ptr::{read_volatile, write_volatile},
|
||||
sync::atomic::Ordering,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
println,
|
||||
uart::write_char_uart,
|
||||
virtio::{
|
||||
QUEUE_SIZE, STATUS_ACKNOWLEDGE, STATUS_DRIVER, STATUS_DRIVER_OK, VIRTIO_MMIO_INTERRUPT_ACK,
|
||||
VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN,
|
||||
VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VirtioInputEvent, Virtqueue,
|
||||
},
|
||||
};
|
||||
pub struct VirtioInputDriver {
|
||||
base_addr: usize,
|
||||
queue: &'static mut Virtqueue,
|
||||
event_pool: [VirtioInputEvent; QUEUE_SIZE],
|
||||
last_used_idx: u16,
|
||||
}
|
||||
|
||||
impl VirtioInputDriver {
|
||||
pub const unsafe fn new(base_addr: usize, queue_mem: &'static mut Virtqueue) -> Self {
|
||||
Self {
|
||||
base_addr,
|
||||
queue: queue_mem,
|
||||
event_pool: [VirtioInputEvent {
|
||||
event_type: 0,
|
||||
code: 0,
|
||||
value: 0,
|
||||
}; QUEUE_SIZE],
|
||||
last_used_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
unsafe {
|
||||
// 1. Reset & Status (Ack + Driver)
|
||||
self.write_reg(0x070, 0);
|
||||
self.write_reg(0x070, 1 | 2);
|
||||
|
||||
// 2. Négociation Features (Obligatoire en Modern pour débloquer les queues)
|
||||
self.write_reg(0x024, 1); // Select Page 1
|
||||
let f1 = self.read_reg(0x010);
|
||||
self.write_reg(0x020, f1 | 1); // On accepte VERSION_1 (Bit 32 global)
|
||||
|
||||
self.write_reg(0x070, 1 | 2 | 8); // STATUS_FEATURES_OK
|
||||
if (self.read_reg(0x070) & 8) == 0 {
|
||||
panic!("Features rejected");
|
||||
}
|
||||
|
||||
// 3. Configuration de la Queue
|
||||
self.write_reg(0x030, 0); // Select Queue 0
|
||||
let max = self.read_reg(0x034); // QUEUE_NUM_MAX
|
||||
self.write_reg(0x038, QUEUE_SIZE as u32);
|
||||
|
||||
// 4. Envoi des adresses 64 bits (Plus besoin de PFN !)
|
||||
let desc_addr = &self.queue.descriptors as *const _ as u64;
|
||||
self.write_reg(0x080, (desc_addr & 0xffffffff) as u32);
|
||||
self.write_reg(0x084, (desc_addr >> 32) as u32);
|
||||
|
||||
let avail_addr = &self.queue.available as *const _ as u64;
|
||||
self.write_reg(0x090, (avail_addr & 0xffffffff) as u32);
|
||||
self.write_reg(0x094, (avail_addr >> 32) as u32);
|
||||
|
||||
let used_addr = &self.queue.used as *const _ as u64;
|
||||
self.write_reg(0x0a0, (used_addr & 0xffffffff) as u32);
|
||||
self.write_reg(0x0a4, (used_addr >> 32) as u32);
|
||||
|
||||
// 5. REMPLISSAGE INITIAL
|
||||
for i in 0..QUEUE_SIZE {
|
||||
self.queue.descriptors[i].addr = &self.event_pool[i] as *const _ as u64;
|
||||
self.queue.descriptors[i].len = core::mem::size_of::<VirtioInputEvent>() as u32;
|
||||
self.queue.descriptors[i].flags = 2; // Writeable
|
||||
self.queue.available.ring[i] = i as u16;
|
||||
}
|
||||
self.queue
|
||||
.available
|
||||
.idx
|
||||
.store(QUEUE_SIZE as u16, Ordering::Release);
|
||||
|
||||
// 6. ACTIVATION (L'étape que tout le monde oublie en Modern)
|
||||
self.write_reg(0x044, 1); // QUEUE_READY
|
||||
|
||||
// 7. DRIVER_OK
|
||||
self.write_reg(0x070, 1 | 2 | 4 | 8);
|
||||
|
||||
self.activate_queue();
|
||||
}
|
||||
}
|
||||
unsafe fn activate_queue(&mut self) {
|
||||
unsafe {
|
||||
// 1. Sélectionner la queue
|
||||
self.write_reg(0x030, 0);
|
||||
|
||||
// 2. Écrire la taille (doit correspondre à tes structures Rust)
|
||||
self.write_reg(0x038, QUEUE_SIZE as u32);
|
||||
|
||||
// 3. Écrire les adresses (ORDRE CRITIQUE : LOW puis HIGH)
|
||||
let desc_addr = &self.queue.descriptors as *const _ as u64;
|
||||
self.write_reg(0x080, desc_addr as u32); // DescLow
|
||||
self.write_reg(0x084, (desc_addr >> 32) as u32); // DescHigh
|
||||
|
||||
let avail_addr = &self.queue.available as *const _ as u64;
|
||||
self.write_reg(0x090, avail_addr as u32); // AvailLow
|
||||
self.write_reg(0x094, (avail_addr >> 32) as u32); // AvailHigh
|
||||
|
||||
let used_addr = &self.queue.used as *const _ as u64;
|
||||
self.write_reg(0x0a0, used_addr as u32); // UsedLow
|
||||
self.write_reg(0x0a4, (used_addr >> 32) as u32); // UsedHigh
|
||||
|
||||
// 4. LE KICK : Activer la queue
|
||||
self.write_reg(0x044, 1); // QUEUE_READY = 1
|
||||
|
||||
// 5. SYNC : On s'assure que le périphérique a bien pris le READY
|
||||
if self.read_reg(0x044) == 0 {
|
||||
panic!("La queue refuse de passer en READY. Vérifiez les adresses !");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Appelé lors d'une interruption clavier
|
||||
pub fn handle_interrupt(&mut self) {
|
||||
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];
|
||||
|
||||
if event.event_type == 1 {
|
||||
// EV_KEY
|
||||
self.on_key(event.code, event.value);
|
||||
}
|
||||
|
||||
// Recyclage du descripteur : on le rend disponible à nouveau
|
||||
let avail_idx = self.queue.available.idx.load(Ordering::Relaxed) as usize % QUEUE_SIZE;
|
||||
self.queue.available.ring[avail_idx] = 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);
|
||||
}
|
||||
|
||||
// Acquitter l'interruption
|
||||
unsafe {
|
||||
self.write_reg(VIRTIO_MMIO_INTERRUPT_ACK, 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_key(&self, code: u16, value: u32) {
|
||||
let state = match value {
|
||||
1 => "Pressed",
|
||||
0 => "Released",
|
||||
2 => "Repeat",
|
||||
_ => "Unknown",
|
||||
};
|
||||
// write_char_uart((b'0' + (code / 10) as u8 % 10) as char);
|
||||
// write_char_uart((b'0' + (code % 10) as u8) as char);
|
||||
write_char_uart(code as u8 as char);
|
||||
write_char_uart('\n');
|
||||
write_char_uart('\r');
|
||||
// Ici, implémentez votre conversion Scancode -> ASCII
|
||||
// println!("Key Code: {} - State: {}", code, state);
|
||||
}
|
||||
|
||||
unsafe fn write_reg(&self, offset: usize, val: u32) {
|
||||
unsafe { write_volatile((self.base_addr + offset) as *mut u32, val) };
|
||||
}
|
||||
|
||||
unsafe fn read_reg(&self, offset: usize) -> u32 {
|
||||
unsafe { read_volatile((self.base_addr + offset) as *const u32) }
|
||||
}
|
||||
}
|
||||
|
||||
pub const PLIC_BASE: usize = 0x0c00_0000;
|
||||
pub const IRQ_VIRTIO: u32 = 1;
|
||||
|
||||
pub const S_MODE_CLAIM_COMPLETE: *mut u32 = 0x0c20_1004 as *mut u32;
|
||||
pub unsafe fn init_plic_m_mode() {
|
||||
// 1. Priority : identique pour tous les modes
|
||||
let priority_ptr = (PLIC_BASE + 4 * IRQ_VIRTIO as usize) as *mut u32;
|
||||
unsafe { priority_ptr.write_volatile(1) };
|
||||
|
||||
// 2. Enable : Pour Hart 0 M-Mode, l'offset est 0x2000
|
||||
let enable_ptr = (PLIC_BASE + 0x2080) as *mut u32;
|
||||
unsafe { enable_ptr.write_volatile(1 << IRQ_VIRTIO) };
|
||||
|
||||
// 3. Threshold : Pour Hart 0 M-Mode, l'offset est 0x200000
|
||||
let threshold_ptr = (PLIC_BASE + 0x201000) as *mut u32;
|
||||
unsafe { threshold_ptr.write_volatile(0) };
|
||||
}
|
||||
Reference in New Issue
Block a user