use alloc::collections::VecDeque;
use alloc::rc::Rc;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::str::FromStr;
use crate::arch::pci::PciConfigRegion;
use crate::drivers::net::virtio_net::constants::{FeatureSet, Features};
use crate::drivers::net::virtio_net::{CtrlQueue, NetDevCfg, RxQueues, TxQueues, VirtioNetDriver};
use crate::drivers::pci::{PciCommand, PciDevice};
use crate::drivers::virtio::error::{self, VirtioError};
use crate::drivers::virtio::transport::pci;
use crate::drivers::virtio::transport::pci::{PciCap, UniCapsColl};
use crate::drivers::virtio::virtqueue::Virtq;
#[repr(C)]
pub(crate) struct NetDevCfgRaw {
mac: [u8; 6],
status: u16,
max_virtqueue_pairs: u16,
mtu: u16,
}
impl NetDevCfgRaw {
pub fn get_mtu(&self) -> u16 {
self.mtu
}
pub fn get_mac(&self) -> [u8; 6] {
self.mac
}
pub fn get_status(&self) -> u16 {
self.status
}
pub fn get_max_virtqueue_pairs(&self) -> u16 {
self.max_virtqueue_pairs
}
}
impl VirtioNetDriver {
fn map_cfg(cap: &PciCap) -> Option<NetDevCfg> {
let dev_cfg: &'static NetDevCfgRaw = match pci::map_dev_cfg::<NetDevCfgRaw>(cap) {
Some(cfg) => cfg,
None => return None,
};
Some(NetDevCfg {
raw: dev_cfg,
dev_id: cap.dev_id(),
features: FeatureSet::new(0),
})
}
pub(crate) fn new(
mut caps_coll: UniCapsColl,
device: &PciDevice<PciConfigRegion>,
) -> Result<Self, error::VirtioNetError> {
let device_id = device.device_id();
let com_cfg = match caps_coll.get_com_cfg() {
Some(com_cfg) => com_cfg,
None => {
error!("No common config. Aborting!");
return Err(error::VirtioNetError::NoComCfg(device_id));
}
};
let isr_stat = match caps_coll.get_isr_cfg() {
Some(isr_stat) => isr_stat,
None => {
error!("No ISR status config. Aborting!");
return Err(error::VirtioNetError::NoIsrCfg(device_id));
}
};
let notif_cfg = match caps_coll.get_notif_cfg() {
Some(notif_cfg) => notif_cfg,
None => {
error!("No notif config. Aborting!");
return Err(error::VirtioNetError::NoNotifCfg(device_id));
}
};
let dev_cfg = loop {
match caps_coll.get_dev_cfg() {
Some(cfg) => {
if let Some(dev_cfg) = VirtioNetDriver::map_cfg(&cfg) {
break dev_cfg;
}
}
None => {
error!("No dev config. Aborting!");
return Err(error::VirtioNetError::NoDevCfg(device_id));
}
}
};
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,
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: device.get_irq().unwrap(),
mtu,
})
}
pub(crate) fn init(
device: &PciDevice<PciConfigRegion>,
) -> Result<VirtioNetDriver, VirtioError> {
device.set_command(PciCommand::PCI_COMMAND_MASTER);
let mut drv = match pci::map_caps(device) {
Ok(caps) => match VirtioNetDriver::new(caps, device) {
Ok(driver) => driver,
Err(vnet_err) => {
error!("Initializing new network driver failed. Aborting!");
return Err(VirtioError::NetDriver(vnet_err));
}
},
Err(pci_error) => {
error!("Mapping capabilities failed. Aborting!");
return Err(VirtioError::FromPci(pci_error));
}
};
match drv.init_dev() {
Ok(_) => info!(
"Network device with id {:x}, has been initialized by driver!",
drv.get_dev_id()
),
Err(vnet_err) => {
drv.set_failed();
return Err(VirtioError::NetDriver(vnet_err));
}
}
if drv.is_link_up() {
info!("Virtio-net link is up after initialization.")
} else {
info!("Virtio-net link is down after initialization!")
}
Ok(drv)
}
}