use hdmi_hal_async::scdc::ScdcTransport;
use plumbob_async::ScdcClient;
use crate::{Config0, ScdcError};
use super::Scdc;
#[cfg_attr(docsrs, doc(cfg(feature = "plumbob-async")))]
impl<T: ScdcTransport> ScdcClient for Scdc<T> {
type Error = ScdcError<T::Error>;
async fn read_flt_ready(&mut self) -> Result<bool, Self::Error> {
Ok(Scdc::read_status_flags(self).await?.flt_ready)
}
async fn read_update_flags(&mut self) -> Result<plumbob_async::UpdateFlags, Self::Error> {
Scdc::read_update_flags(self).await.map(Into::into)
}
async fn clear_update_flags(
&mut self,
flags: plumbob_async::ClearableUpdateFlags,
) -> Result<(), Self::Error> {
Scdc::clear_update_flags(self, flags.into()).await
}
async fn read_ltp_requests(&mut self) -> Result<plumbob_async::LtpRequests, Self::Error> {
Scdc::read_ltp_requests(self).await.map(Into::into)
}
async fn read_source_test_config(
&mut self,
) -> Result<plumbob_async::SourceTestConfig, Self::Error> {
Scdc::read_source_test_config(self).await.map(Into::into)
}
async fn write_config_0_defaults(&mut self) -> Result<(), Self::Error> {
Scdc::write_config_0(self, Config0::default()).await
}
async fn write_frl_config(
&mut self,
config: plumbob_async::FrlConfig,
) -> Result<(), Self::Error> {
Scdc::write_frl_config(self, config.into()).await
}
async fn read_ced(&mut self) -> Result<plumbob_async::CedCounters, Self::Error> {
Scdc::read_ced(self).await.map(Into::into)
}
}
#[cfg(test)]
mod tests {
use super::super::Scdc;
use super::super::test_transport::TestTransport;
use crate::{ProtocolError, ScdcError};
use display_types::HdmiForumFrl;
use plumbob_async::{
ClearableUpdateFlags, FfeLevels, FrlConfig, LtpReq, LtpRequests, ScdcClient,
SourceTestConfig, UpdateFlags,
};
fn scdc_with(regs: &[(u8, u8)]) -> Scdc<TestTransport> {
let mut t = TestTransport::new();
for &(addr, value) in regs {
t.set(addr, value);
}
Scdc::new(t)
}
#[pollster::test]
async fn read_flt_ready_reads_status_flags_0_bit_6() {
assert!(
ScdcClient::read_flt_ready(&mut scdc_with(&[(0x40, 0x40)]))
.await
.unwrap()
);
assert!(
!ScdcClient::read_flt_ready(&mut scdc_with(&[(0x40, 0xBF)]))
.await
.unwrap()
);
}
#[pollster::test]
async fn read_update_flags_maps_every_flag() {
let read = |byte| {
pollster::block_on(ScdcClient::read_update_flags(&mut scdc_with(&[(
0x10, byte,
)])))
.unwrap()
};
let flag = |n: usize| {
let b = |i| i == n;
UpdateFlags::new(b(0), b(1), b(2), b(3), b(4), b(5), b(6))
};
for n in 0..7 {
assert_eq!(read(1 << n), flag(n), "bit {n}");
}
assert_eq!(
read(0x7F),
UpdateFlags::new(true, true, true, true, true, true, true)
);
assert_eq!(read(0x80), UpdateFlags::default());
}
#[pollster::test]
async fn clear_update_flags_writes_each_given_bit() {
let clear = |flags| {
let mut scdc = Scdc::new(TestTransport::new());
pollster::block_on(ScdcClient::clear_update_flags(&mut scdc, flags)).unwrap();
scdc.into_transport().get(0x10)
};
for (n, bit) in [0, 1, 3, 4, 5, 6].into_iter().enumerate() {
let b = |i| i == n;
let flags = ClearableUpdateFlags::new(b(0), b(1), b(2), b(3), b(4), b(5));
assert_eq!(clear(flags), 1 << bit, "bit {bit}");
}
assert_eq!(clear(ClearableUpdateFlags::default()), 0x00);
}
#[pollster::test]
async fn read_ltp_requests_maps_each_lane() {
let mut scdc = scdc_with(&[(0x41, 0x65), (0x42, 0x87)]);
assert_eq!(
ScdcClient::read_ltp_requests(&mut scdc).await.unwrap(),
LtpRequests::new(LtpReq::Lfsr0, LtpReq::Lfsr1, LtpReq::Lfsr2, LtpReq::Lfsr3)
);
}
#[pollster::test]
async fn read_ltp_requests_maps_every_request_value() {
let cases = [
(0x0, LtpReq::None),
(0x1, LtpReq::AllOnes),
(0x2, LtpReq::AllZeros),
(0x3, LtpReq::NyquistClock),
(0x4, LtpReq::DdeCompliance),
(0x5, LtpReq::Lfsr0),
(0x6, LtpReq::Lfsr1),
(0x7, LtpReq::Lfsr2),
(0x8, LtpReq::Lfsr3),
(0xE, LtpReq::FfeChange),
(0xF, LtpReq::RateChange),
];
for (nibble, expected) in cases {
let requests = ScdcClient::read_ltp_requests(&mut scdc_with(&[(0x41, nibble)]))
.await
.unwrap();
assert_eq!(requests.lane0, expected, "{nibble:#x}");
}
}
#[pollster::test]
async fn read_ltp_requests_passes_undefined_values_on() {
let requests = ScdcClient::read_ltp_requests(&mut scdc_with(&[(0x42, 0x90)]))
.await
.unwrap();
assert_eq!(requests.lane3, LtpReq::Reserved(0x9));
assert_eq!(requests.lane2, LtpReq::None);
}
#[pollster::test]
async fn read_source_test_config_maps_every_bit() {
let read = |byte| {
pollster::block_on(ScdcClient::read_source_test_config(&mut scdc_with(&[(
0x35, byte,
)])))
.unwrap()
};
for (n, bit) in [1, 2, 3, 5, 6, 7].into_iter().enumerate() {
let b = |i| i == n;
let expected = SourceTestConfig::new(b(0), b(1), b(2), b(3), b(4), b(5));
assert_eq!(read(1 << bit), expected, "bit {bit}");
}
assert_eq!(read(0x11), SourceTestConfig::default());
}
#[pollster::test]
async fn write_config_0_defaults_clears_config_0() {
let mut scdc = scdc_with(&[(0x30, 0xFF)]);
ScdcClient::write_config_0_defaults(&mut scdc)
.await
.unwrap();
assert_eq!(scdc.into_transport().get(0x30), 0x00);
}
#[pollster::test]
async fn write_frl_config_encodes_rate_and_ffe_levels() {
let mut scdc = Scdc::new(TestTransport::new());
ScdcClient::write_frl_config(
&mut scdc,
FrlConfig::new(HdmiForumFrl::Rate12Gbps4Lanes, FfeLevels::new(3).unwrap()),
)
.await
.unwrap();
assert_eq!(scdc.into_transport().get(0x31), 0x36);
}
#[pollster::test]
async fn write_frl_config_turns_frl_off() {
let mut scdc = scdc_with(&[(0x31, 0x36)]);
ScdcClient::write_frl_config(
&mut scdc,
FrlConfig::new(HdmiForumFrl::NotSupported, FfeLevels::default()),
)
.await
.unwrap();
assert_eq!(scdc.into_transport().get(0x31), 0x00);
}
#[pollster::test]
async fn write_frl_config_keeps_culverts_rate_check() {
let mut scdc = Scdc::new(TestTransport::new());
let result = ScdcClient::write_frl_config(
&mut scdc,
FrlConfig::new(HdmiForumFrl::Rate6Gbps4Lanes, FfeLevels::MAX),
)
.await;
assert!(matches!(
result,
Err(ScdcError::Protocol(ProtocolError::FfeLevelsOutOfRange {
levels: 7,
..
}))
));
}
#[pollster::test]
async fn read_ced_maps_each_lane() {
let mut scdc = scdc_with(&[
(0x50, 0x34),
(0x51, 0x92),
(0x52, 0x01),
(0x53, 0x00),
(0x54, 0xFF),
(0x55, 0xFF),
(0x57, 0x00),
(0x58, 0x80),
]);
let ced = ScdcClient::read_ced(&mut scdc).await.unwrap();
assert_eq!(ced.lane0.map(|c| c.value()), Some(0x1234));
assert_eq!(ced.lane1, None);
assert_eq!(ced.lane2.map(|c| c.value()), Some(0x7FFF));
assert_eq!(ced.lane3.map(|c| c.value()), Some(0));
}
#[pollster::test]
async fn transport_errors_are_returned() {
let fails = || Scdc::new(TestTransport::failing_after(0));
assert!(matches!(
ScdcClient::read_flt_ready(&mut fails()).await,
Err(ScdcError::Transport(()))
));
assert!(
ScdcClient::read_update_flags(&mut fails())
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
assert!(
ScdcClient::clear_update_flags(&mut fails(), ClearableUpdateFlags::default())
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
assert!(
ScdcClient::read_ltp_requests(&mut fails())
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
assert!(
ScdcClient::read_source_test_config(&mut fails())
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
assert!(
ScdcClient::write_config_0_defaults(&mut fails())
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
assert!(
ScdcClient::write_frl_config(
&mut fails(),
FrlConfig::new(HdmiForumFrl::Rate6Gbps4Lanes, FfeLevels::default())
)
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
assert!(
ScdcClient::read_ced(&mut fails())
.await
.is_err_and(|error| matches!(error, ScdcError::Transport(())))
);
}
}