use alloc::collections::VecDeque;
use alloc::rc::Rc;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::ptr::read_volatile;
use core::str::FromStr;
use core::sync::atomic::{fence, Ordering};
use crate::drivers::net::virtio_net::constants::{FeatureSet, Features, Status};
use crate::drivers::net::virtio_net::{CtrlQueue, NetDevCfg, RxQueues, TxQueues, VirtioNetDriver};
use crate::drivers::virtio::error::{VirtioError, VirtioNetError};
use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, MmioRegisterLayout, NotifCfg};
use crate::drivers::virtio::virtqueue::Virtq;
#[repr(C)]
pub struct NetDevCfgRaw {
config_generation: u32,
mac: [u8; 6],
status: u16,
max_virtqueue_pairs: u16,
mtu: u16,
}
impl NetDevCfgRaw {
pub fn get_mtu(&self) -> u16 {
unsafe {
loop {
let before = read_volatile(&self.config_generation);
fence(Ordering::SeqCst);
let mtu = read_volatile(&self.mtu);
fence(Ordering::SeqCst);
let after = read_volatile(&self.config_generation);
if before == after {
return mtu;
}
}
}
}
pub fn get_mac(&self) -> [u8; 6] {
let mut mac: [u8; 6] = [0u8; 6];
unsafe {
loop {
let before = read_volatile(&self.config_generation);
fence(Ordering::SeqCst);
let mut src = self.mac.iter();
mac.fill_with(|| read_volatile(src.next().unwrap()));
fence(Ordering::SeqCst);
let after = read_volatile(&self.config_generation);
if before == after {
return mac;
}
}
}
}
pub fn get_status(&self) -> u16 {
unsafe {
loop {
let before = read_volatile(&self.config_generation);
fence(Ordering::SeqCst);
let status = read_volatile(&self.status);
fence(Ordering::SeqCst);
let after = read_volatile(&self.config_generation);
if before == after {
return status;
}
}
}
}
pub fn get_max_virtqueue_pairs(&self) -> u16 {
unsafe {
loop {
let before = read_volatile(&self.config_generation);
fence(Ordering::SeqCst);
let max_pairs = read_volatile(&self.max_virtqueue_pairs);
fence(Ordering::SeqCst);
let after = read_volatile(&self.config_generation);
if before == after {
return max_pairs;
}
}
}
}
}
impl VirtioNetDriver {
pub fn new(
dev_id: u16,
registers: &'static mut MmioRegisterLayout,
irq: u8,
) -> Result<Self, VirtioNetError> {
let dev_cfg_raw: &'static NetDevCfgRaw =
unsafe { &*(((registers as *const _ as usize) + 0xFC) as *const NetDevCfgRaw) };
let dev_cfg = NetDevCfg {
raw: dev_cfg_raw,
dev_id,
features: FeatureSet::new(0),
};
let isr_stat = IsrStatus::new(registers);
let notif_cfg = NotifCfg::new(registers);
let mtu = if let Some(my_mtu) = hermit_var!("HERMIT_MTU") {
u16::from_str(&my_mtu).unwrap()
} else if dev_cfg.features.is_feature(Features::VIRTIO_NET_F_MTU) {
dev_cfg.raw.get_mtu()
} else {
1500
};
Ok(VirtioNetDriver {
dev_cfg,
com_cfg: ComCfg::new(registers, 1),
isr_stat,
notif_cfg,
ctrl_vq: CtrlQueue::new(None),
recv_vqs: RxQueues::new(
Vec::<Rc<Virtq>>::new(),
Rc::new(RefCell::new(VecDeque::new())),
false,
),
send_vqs: TxQueues::new(
Vec::<Rc<Virtq>>::new(),
Rc::new(RefCell::new(VecDeque::new())),
Vec::new(),
false,
),
num_vqs: 0,
irq,
mtu,
})
}
pub fn print_information(&mut self) {
self.com_cfg.print_information();
if self.dev_status() == u16::from(Status::VIRTIO_NET_S_LINK_UP) {
info!("The link of the network device is up!");
}
}
pub fn init(
dev_id: u16,
registers: &'static mut MmioRegisterLayout,
irq_no: u8,
) -> Result<VirtioNetDriver, VirtioError> {
if let Ok(mut drv) = VirtioNetDriver::new(dev_id, registers, irq_no) {
match drv.init_dev() {
Err(error_code) => Err(VirtioError::NetDriver(error_code)),
_ => {
drv.print_information();
Ok(drv)
}
}
} else {
error!("Unable to create Driver. Aborting!");
Err(VirtioError::Unknown)
}
}
}