use core::slice;
use heterob::{
bit_numbering::Lsb,
endianness::{Le, LeBytesTryInto},
Seq, P2, P3, P6,
};
use snafu::Snafu;
use super::ExtendedCapabilityHeader;
#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
pub enum ResizableBarError {
#[snafu(display("should have at least one entry"))]
FirstEntry,
#[snafu(display("should have 1..=6 number of entries"))]
NumberOfResizableBars { value: usize },
#[snafu(display("entries data too short"))]
ShortData,
}
#[derive(Debug, Clone)]
pub struct ResizableBar<'a>(pub slice::Chunks<'a, u8>);
impl<'a> ResizableBar<'a> {
pub const ENTRY_SIZE: usize = 4 + 4;
}
impl<'a> PartialEq for ResizableBar<'a> {
fn eq(&self, other: &Self) -> bool {
self.0.clone().eq(other.0.clone())
}
}
impl<'a> Eq for ResizableBar<'a> {}
impl<'a> TryFrom<&'a [u8]> for ResizableBar<'a> {
type Error = ResizableBarError;
fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
let Seq { head, .. } = slice
.get(ExtendedCapabilityHeader::SIZE..)
.unwrap_or_default()
.le_bytes_try_into()
.map_err(|_| ResizableBarError::FirstEntry)?;
let Lsb(((), num_bars, ())) = P3::<u64, 37, 3, 24>(head).into();
let _: usize = num_bars;
if let 1..=6 = num_bars {
let start = ExtendedCapabilityHeader::SIZE;
let end = start + num_bars * ResizableBar::ENTRY_SIZE;
let chunks = slice
.get(start..end)
.ok_or(ResizableBarError::ShortData)?
.chunks(ResizableBar::ENTRY_SIZE);
Ok(ResizableBar(chunks))
} else {
Err(ResizableBarError::NumberOfResizableBars { value: num_bars })
}
}
}
impl<'a> Iterator for ResizableBar<'a> {
type Item = ResizableBarEntry;
fn next(&mut self) -> Option<Self::Item> {
let chunk = self.0.next()?;
let Seq {
head: Le((cap, ctrl)),
..
} = P2(chunk).try_into().ok()?;
Some(ResizableBarEntry {
capability: From::<u32>::from(cap),
control: From::<u32>::from(ctrl),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResizableBarEntry {
pub capability: ResizableBarCapability,
pub control: ResizableBarControl,
}
impl ResizableBarEntry {
pub const BAR_SIZES: [&'static str; 44] = [
"1MB", "2MB", "4MB", "8MB", "16MB", "32MB", "64MB", "128MB", "256MB", "512MB",
"1GB", "2GB", "4GB", "8GB", "16GB", "32GB", "64GB", "128GB", "256GB", "512GB",
"1TB", "2TB", "4TB", "8TB", "16TB", "32TB", "64TB", "128TB", "256TB", "512TB",
"1PB", "2PB", "4PB", "8PB", "16PB", "32PB", "64PB", "128PB", "256PB", "512PB",
"1EB", "2EB", "4EB", "8EB",
];
pub fn is_function_supports_power_of_two(&self, power: usize) -> bool {
match power {
20..=47 => self.capability.support_map_from_1mb_to_128tb & (1 << (power - 16)) != 0,
48..=63 => self.control.support_map_from_256tb_to_8eb & (1 << (power - 48)) != 0,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResizableBarCapability {
pub support_map_from_1mb_to_128tb: u32,
}
impl From<u32> for ResizableBarCapability {
fn from(dword: u32) -> Self {
Self {
support_map_from_1mb_to_128tb: dword,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResizableBarControl {
pub bar_index: u8,
pub number_of_resizable_bars: u8,
pub bar_size: u8,
pub support_map_from_256tb_to_8eb: u16,
}
impl From<u32> for ResizableBarControl {
fn from(dword: u32) -> Self {
let Lsb((
bar_index,
(),
number_of_resizable_bars,
bar_size,
(),
support_map_from_256tb_to_8eb,
)) = P6::<_, 3, 2, 3, 6, 2, 16>(dword).into();
Self {
bar_index,
number_of_resizable_bars,
bar_size,
support_map_from_256tb_to_8eb,
}
}
}