use alloc::vec::Vec;
pub const VIRTIO_PCI_CAP_VENDOR_SPECIFIC: u8 = 0x09;
pub const VIRTIO_PCI_CONFIG_EFFECT_ID: u16 = 1;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum VirtioPciCapabilityType {
Common = 1,
Notify = 2,
Isr = 3,
Device = 4,
PciConfig = 5,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VirtioPciCapability {
cfg_type: VirtioPciCapabilityType,
bar: u8,
offset: u32,
length: u32,
notify_off_multiplier: u32,
}
impl VirtioPciCapability {
pub const fn new(cfg_type: VirtioPciCapabilityType, bar: u8, offset: u32, length: u32) -> Self {
Self {
cfg_type,
bar,
offset,
length,
notify_off_multiplier: 0,
}
}
pub const fn with_notify_multiplier(mut self, multiplier: u32) -> Self {
self.notify_off_multiplier = multiplier;
self
}
pub const fn cfg_type(self) -> VirtioPciCapabilityType {
self.cfg_type
}
pub const fn bar(self) -> u8 {
self.bar
}
pub const fn offset(self) -> u32 {
self.offset
}
pub const fn length(self) -> u32 {
self.length
}
pub const fn notify_off_multiplier(self) -> u32 {
self.notify_off_multiplier
}
pub const fn serialized_length(self) -> u8 {
match self.cfg_type {
VirtioPciCapabilityType::Notify | VirtioPciCapabilityType::PciConfig => 20,
_ => 16,
}
}
pub fn body(self) -> Vec<u8> {
let mut body = alloc::vec![0; usize::from(self.serialized_length()) - 2];
body[0] = self.serialized_length();
body[1] = self.cfg_type as u8;
body[2] = self.bar;
body[6..10].copy_from_slice(&self.offset.to_le_bytes());
body[10..14].copy_from_slice(&self.length.to_le_bytes());
if matches!(
self.cfg_type,
VirtioPciCapabilityType::Notify | VirtioPciCapabilityType::PciConfig
) {
body[14..18].copy_from_slice(&self.notify_off_multiplier.to_le_bytes());
}
body
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VirtioPciCapabilitySet {
capabilities: Vec<VirtioPciCapability>,
}
impl VirtioPciCapabilitySet {
pub fn new(device_config_size: u32) -> Self {
Self {
capabilities: alloc::vec![
VirtioPciCapability::new(VirtioPciCapabilityType::Common, 0, 0x000, 0x38),
VirtioPciCapability::new(VirtioPciCapabilityType::Notify, 0, 0x100, 0x04)
.with_notify_multiplier(4),
VirtioPciCapability::new(VirtioPciCapabilityType::Isr, 0, 0x200, 0x01),
VirtioPciCapability::new(
VirtioPciCapabilityType::Device,
0,
0x300,
device_config_size,
),
VirtioPciCapability::new(VirtioPciCapabilityType::PciConfig, 0, 0, 0),
],
}
}
pub fn as_slice(&self) -> &[VirtioPciCapability] {
&self.capabilities
}
}