#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/ddc/0.3.0/")]
extern crate mccs;
use std::{error, fmt, iter, time::Duration};
pub use {
self::{
commands::{Command, CommandResult, TimingMessage},
delay::Delay,
},
mccs::{FeatureCode, Value as VcpValue, ValueType as VcpValueType},
};
pub mod commands;
mod delay;
pub const I2C_ADDRESS_EDID: u16 = 0x50;
pub const I2C_ADDRESS_EDID_SEGMENT: u16 = 0x30;
pub const I2C_ADDRESS_DDC_CI: u16 = 0x37;
pub const SUB_ADDRESS_DDC_CI: u8 = 0x51;
pub const DELAY_COMMAND_FAILED_MS: u64 = 40;
pub trait Edid {
type EdidError;
fn read_edid(&mut self, offset: u8, data: &mut [u8]) -> Result<usize, Self::EdidError>;
}
pub trait Eddc: Edid {
fn read_eddc_edid(&mut self, segment: u8, offset: u8, data: &mut [u8]) -> Result<usize, Self::EdidError>;
}
pub trait DdcHost {
type Error;
fn sleep(&mut self) {}
}
pub trait DdcCommandRaw: DdcHost {
fn execute_raw<'a>(
&mut self,
data: &[u8],
out: &'a mut [u8],
response_delay: Duration,
) -> Result<&'a mut [u8], Self::Error>;
}
pub trait DdcCommandRawMarker: DdcCommandRaw
where
Self::Error: From<ErrorCode>,
{
fn set_sleep_delay(&mut self, delay: Delay);
}
pub trait DdcCommand: DdcHost {
fn execute<C: Command>(&mut self, command: C) -> Result<C::Ok, Self::Error>;
fn checksum<II: IntoIterator<Item = u8>>(iter: II) -> u8 {
iter.into_iter().fold(0u8, |sum, v| sum ^ v)
}
fn encode_command<'a>(data: &[u8], packet: &'a mut [u8]) -> &'a [u8] {
packet[0] = SUB_ADDRESS_DDC_CI;
packet[1] = 0x80 | data.len() as u8;
packet[2..2 + data.len()].copy_from_slice(data);
packet[2 + data.len()] =
Self::checksum(iter::once((I2C_ADDRESS_DDC_CI as u8) << 1).chain(packet[..2 + data.len()].iter().cloned()));
&packet[..3 + data.len()]
}
}
pub trait DdcCommandMarker: DdcCommand
where
Self::Error: From<ErrorCode>,
{
}
pub trait Ddc: DdcHost {
fn capabilities_string(&mut self) -> Result<Vec<u8>, Self::Error>;
fn get_vcp_feature(&mut self, code: FeatureCode) -> Result<VcpValue, Self::Error>;
fn set_vcp_feature(&mut self, code: FeatureCode, value: u16) -> Result<(), Self::Error>;
fn save_current_settings(&mut self) -> Result<(), Self::Error>;
fn get_timing_report(&mut self) -> Result<TimingMessage, Self::Error>;
}
pub trait DdcTable: DdcHost {
fn table_read(&mut self, code: FeatureCode) -> Result<Vec<u8>, Self::Error>;
fn table_write(&mut self, code: FeatureCode, offset: u16, value: &[u8]) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ErrorCode {
InvalidOffset,
InvalidLength,
InvalidChecksum,
InvalidOpcode,
InvalidData,
Invalid(String),
}
impl error::Error for ErrorCode {}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
ErrorCode::InvalidOffset => "invalid offset returned from DDC/CI",
ErrorCode::InvalidLength => "invalid DDC/CI length",
ErrorCode::InvalidChecksum => "DDC/CI checksum mismatch",
ErrorCode::InvalidOpcode => "DDC/CI VCP opcode mismatch",
ErrorCode::InvalidData => "invalid DDC/CI data",
ErrorCode::Invalid(ref s) => s,
})
}
}
impl<D: DdcCommandMarker> Ddc for D
where
D::Error: From<ErrorCode>,
{
fn capabilities_string(&mut self) -> Result<Vec<u8>, Self::Error> {
let mut string = Vec::new();
let mut offset = 0;
loop {
let caps = self.execute(commands::CapabilitiesRequest::new(offset))?;
if caps.offset != offset {
return Err(ErrorCode::InvalidOffset.into())
} else if caps.data.is_empty() {
break
}
string.extend(caps.data.iter());
offset += caps.data.len() as u16;
}
Ok(string)
}
fn get_vcp_feature(&mut self, code: FeatureCode) -> Result<VcpValue, Self::Error> {
self.execute(commands::GetVcpFeature::new(code))
}
fn set_vcp_feature(&mut self, code: FeatureCode, value: u16) -> Result<(), Self::Error> {
self.execute(commands::SetVcpFeature::new(code, value))
}
fn save_current_settings(&mut self) -> Result<(), Self::Error> {
self.execute(commands::SaveCurrentSettings)
}
fn get_timing_report(&mut self) -> Result<TimingMessage, Self::Error> {
self.execute(commands::GetTimingReport)
}
}
impl<D: DdcCommandMarker> DdcTable for D
where
D::Error: From<ErrorCode>,
{
fn table_read(&mut self, code: FeatureCode) -> Result<Vec<u8>, Self::Error> {
let mut value = Vec::new();
let mut offset = 0;
loop {
let table = self.execute(commands::TableRead::new(code, offset))?;
if table.offset != offset {
return Err(ErrorCode::InvalidOffset.into())
} else if table.bytes().is_empty() {
break
}
value.extend(table.bytes().iter());
offset += table.bytes().len() as u16;
}
Ok(value)
}
fn table_write(&mut self, code: FeatureCode, mut offset: u16, value: &[u8]) -> Result<(), Self::Error> {
for chunk in value.chunks(32) {
self.execute(commands::TableWrite::new(code, offset, chunk))?;
offset += chunk.len() as u16;
}
Ok(())
}
}
impl<D: DdcCommandRawMarker> DdcCommand for D
where
D::Error: From<ErrorCode>,
{
fn execute<C: Command>(&mut self, command: C) -> Result<C::Ok, Self::Error> {
let mut data = [0u8; 36];
command.encode(&mut data)?;
let mut out = [0u8; 36 + 3];
let out = if C::Ok::MAX_LEN > 0 {
&mut out[..C::Ok::MAX_LEN + 3]
} else {
&mut []
};
let res = self.execute_raw(
&data[..command.len()],
out,
Duration::from_millis(C::DELAY_RESPONSE_MS as _),
);
let res = match res {
Ok(res) => {
self.set_sleep_delay(Delay::new(Duration::from_millis(C::DELAY_COMMAND_MS)));
res
},
Err(e) => {
self.set_sleep_delay(Delay::new(Duration::from_millis(DELAY_COMMAND_FAILED_MS)));
return Err(e)
},
};
let res = C::Ok::decode(res);
if res.is_err() {
self.set_sleep_delay(Delay::new(Duration::from_millis(DELAY_COMMAND_FAILED_MS)));
}
res.map_err(From::from)
}
}