use heterob::{
bit_numbering::Lsb,
endianness::{Le, LeBytesTryInto},
Seq, P13, P14, P3, P7,
};
use super::CapabilityDataError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AcceleratedGraphicsPort {
pub identifier: Identifier,
pub status: Status,
pub command: Command,
pub isochronous_status: Option<IsochronousStatus>,
pub control: Option<Control>,
pub aperture_size: Option<ApertureSize>,
pub enabled_aperture_page_size: Option<EnabledAperturePageSize>,
pub gart_pointer: Option<GartPointer>,
pub isochronous_command: Option<IsochronousCommand>,
}
impl TryFrom<&[u8]> for AcceleratedGraphicsPort {
type Error = CapabilityDataError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head: Le((identifier, status, command)),
tail,
} = P3(slice).try_into().map_err(|_| CapabilityDataError {
name: "AGP",
size: 10,
})?;
let status: Status = From::<u32>::from(status);
let mut isochronous_status = None;
let mut control = None;
let mut aperture_size = None;
let mut enabled_aperture_page_size = None;
let mut gartlo = None;
let mut garthi = None;
let isochronous_command = tail
.le_bytes_try_into()
.and_then(|seq| {
if status.isoch_support {
isochronous_status = Some(From::<u32>::from(seq.head));
}
seq.tail.le_bytes_try_into()
})
.and_then(|seq| {
control = Some(From::<u32>::from(seq.head));
seq.tail.le_bytes_try_into()
})
.and_then(|seq| {
aperture_size = Some(ApertureSize(seq.head));
seq.tail.le_bytes_try_into()
})
.and_then(|seq| {
enabled_aperture_page_size = Some(EnabledAperturePageSize(seq.head));
seq.tail.le_bytes_try_into()
})
.and_then(|seq| {
gartlo = Some(seq.head);
seq.tail.le_bytes_try_into()
})
.and_then(|seq| {
garthi = Some(seq.head);
seq.tail.le_bytes_try_into()
})
.map(|Seq { head, .. }| From::<u16>::from(head))
.ok()
.filter(|_| status.isoch_support);
let gart_pointer = match (gartlo, garthi) {
(Some(gartlo), Some(garthi)) => Some(GartPointer { gartlo, garthi }),
(Some(gartlo), None) => Some(GartPointer { gartlo, garthi: 0 }),
_ => None,
};
Ok(Self {
identifier: From::<u16>::from(identifier),
status,
command: From::<u32>::from(command),
isochronous_status,
control,
aperture_size,
enabled_aperture_page_size,
gart_pointer,
isochronous_command,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Identifier {
pub minor: u8,
pub major: u8,
}
impl From<u16> for Identifier {
fn from(word: u16) -> Self {
let Lsb((minor, major, ())) = P3::<u16, 4, 4, 8>(word).into();
Self { minor, major }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Status {
pub rq: u8,
pub isoch_support: bool,
pub reserved: bool,
pub arqsz: u8,
pub cal_cycle: CalCycle,
pub sba: bool,
pub ita_coh: bool,
pub gart64b: bool,
pub htrans: bool,
pub over4g: bool,
pub fw: bool,
pub agp_3_0_mode: bool,
pub rate: DataRateSupport,
}
impl From<u32> for Status {
fn from(dword: u32) -> Self {
let Lsb((
rate,
agp_3_0_mode,
fw,
over4g,
htrans,
gart64b,
ita_coh,
sba,
cal_cycle,
arqsz,
reserved,
isoch_support,
(),
rq,
)) = P14::<_, 3, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 6, 8>(dword).into();
Self {
rq,
isoch_support,
reserved,
arqsz,
cal_cycle: From::<u8>::from(cal_cycle),
sba,
ita_coh,
gart64b,
htrans,
over4g,
fw,
agp_3_0_mode,
rate: From::<u8>::from(rate),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CalCycle {
Period4ms,
Period16ms,
Period64ms,
Period256ms,
Reserved(u8),
CalibrationCycleNotNeeded,
}
impl From<u8> for CalCycle {
fn from(byte: u8) -> Self {
match byte {
0b000 => Self::Period4ms,
0b001 => Self::Period16ms,
0b010 => Self::Period64ms,
0b011 => Self::Period256ms,
0b111 => Self::CalibrationCycleNotNeeded,
v => Self::Reserved(v),
}
}
}
impl From<CalCycle> for u8 {
fn from(data: CalCycle) -> Self {
match data {
CalCycle::Period4ms => 0b000,
CalCycle::Period16ms => 0b001,
CalCycle::Period64ms => 0b010,
CalCycle::Period256ms => 0b011,
CalCycle::CalibrationCycleNotNeeded => 0b111,
CalCycle::Reserved(v) => v,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataRateSupport {
Speed4x,
Speed8x,
Speed4xAnd8x,
Reserved(u8),
}
impl From<u8> for DataRateSupport {
fn from(byte: u8) -> Self {
match byte {
0b001 => Self::Speed4x,
0b010 => Self::Speed8x,
0b011 => Self::Speed4xAnd8x,
v => Self::Reserved(v),
}
}
}
impl From<DataRateSupport> for u8 {
fn from(data: DataRateSupport) -> Self {
match data {
DataRateSupport::Speed4x => 0b001,
DataRateSupport::Speed8x => 0b010,
DataRateSupport::Speed4xAnd8x => 0b011,
DataRateSupport::Reserved(v) => v,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Command {
pub prq: u8,
pub parqsz: u8,
pub pcal_cycle: CalCycle,
pub sba_enable: bool,
pub agp_enable: bool,
pub gart64b: bool,
pub over4g: bool,
pub fw_enable: bool,
pub drate: DataRateEnabled,
}
impl From<u32> for Command {
fn from(dword: u32) -> Self {
let Lsb((
drate,
(),
fw_enable,
over4g,
(),
gart64b,
agp_enable,
sba_enable,
pcal_cycle,
parqsz,
(),
(),
prq,
)) = P13::<_, 3, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 7, 8>(dword).into();
Self {
prq,
parqsz,
pcal_cycle: From::<u8>::from(pcal_cycle),
sba_enable,
agp_enable,
gart64b,
over4g,
fw_enable,
drate: From::<u8>::from(drate),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataRateEnabled {
Speed4x,
Speed8x,
Reserved(u8),
}
impl From<u8> for DataRateEnabled {
fn from(byte: u8) -> Self {
match byte {
0b001 => Self::Speed4x,
0b010 => Self::Speed8x,
v => Self::Reserved(v),
}
}
}
impl From<DataRateEnabled> for u8 {
fn from(data: DataRateEnabled) -> Self {
match data {
DataRateEnabled::Speed4x => 0b001,
DataRateEnabled::Speed8x => 0b010,
DataRateEnabled::Reserved(v) => v,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IsochronousStatus {
pub maxbw: u8,
pub isoch_n: u8,
pub isoch_y: IsochY,
pub isoch_l: u8,
pub isoch_error_code: IsochErrorCode,
}
impl From<u32> for IsochronousStatus {
fn from(dword: u32) -> Self {
let Lsb((isoch_error_code, (), isoch_l, isoch_y, isoch_n, maxbw, ())) =
P7::<_, 2, 1, 3, 2, 8, 8, 8>(dword).into();
Self {
maxbw,
isoch_n,
isoch_y: IsochY(isoch_y),
isoch_l,
isoch_error_code: IsochErrorCode(isoch_error_code),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IsochY(pub u8);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IsochErrorCode(pub u8);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Control {
pub cal_cycle_dis: bool,
pub aperenb: bool,
pub gtlben: bool,
}
impl From<u32> for Control {
fn from(dword: u32) -> Self {
let Lsb(((), gtlben, aperenb, cal_cycle_dis, (), (), ())) =
P7::<_, 7, 1, 1, 1, 6, 8, 8>(dword).into();
Self {
cal_cycle_dis,
aperenb,
gtlben,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApertureSize(pub u16);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnabledAperturePageSize(pub u16);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GartPointer {
pub gartlo: u32,
pub garthi: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IsochronousCommand {
pub pisoch_n: u8,
pub pisoch_y: u8,
}
impl From<u16> for IsochronousCommand {
fn from(word: u16) -> Self {
let Lsb(((), pisoch_y, pisoch_n)) = P3::<_, 6, 2, 8>(word).into();
Self { pisoch_n, pisoch_y }
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn decode_all_bits_are_one() {
let data = [0xffu8; 48];
let result = data[2..].try_into().unwrap();
let sample = AcceleratedGraphicsPort {
identifier: Identifier {
minor: 15,
major: 15,
},
status: Status {
rq: 255,
isoch_support: true,
reserved: true,
arqsz: 7,
cal_cycle: CalCycle::CalibrationCycleNotNeeded,
sba: true,
ita_coh: true,
gart64b: true,
htrans: true,
over4g: true,
fw: true,
agp_3_0_mode: true,
rate: DataRateSupport::Reserved(7),
},
command: Command {
prq: 0xff,
parqsz: 7,
pcal_cycle: CalCycle::CalibrationCycleNotNeeded,
sba_enable: true,
agp_enable: true,
gart64b: true,
over4g: true,
fw_enable: true,
drate: DataRateEnabled::Reserved(7),
},
isochronous_status: Some(IsochronousStatus {
maxbw: 0xff,
isoch_n: 0xff,
isoch_y: IsochY(3),
isoch_l: 7,
isoch_error_code: IsochErrorCode(3),
}),
control: Some(Control {
cal_cycle_dis: true,
aperenb: true,
gtlben: true,
}),
aperture_size: Some(ApertureSize(0xffff)),
enabled_aperture_page_size: Some(EnabledAperturePageSize(0xffff)),
gart_pointer: Some(GartPointer {
gartlo: u32::MAX,
garthi: u32::MAX,
}),
isochronous_command: Some(IsochronousCommand {
pisoch_n: 0xff,
pisoch_y: 3,
}),
};
assert_eq!(sample, result);
}
}