pub mod ci;
pub mod edid;
pub mod eddc;
#[cfg(target_os = "linux")]
pub mod linux;
#[cfg(target_os = "macos")]
mod mac_os;
#[cfg(target_os = "windows")]
mod windows;
use thiserror::Error;
use self::{
ci::{parse_feature_reply, DdcCiMessage, DdcCiProtocolError, DdcOpcode, ResultCode},
edid::{Edid, EdidParseError},
};
use crate::mccs::{
capabilities::{parse_capabilities, Capabilities},
features::VcpValue,
};
pub const I2C_DDC_RECV_BUFFER_SIZE: usize = 64;
#[derive(Error, Debug)]
pub enum DdcError {
#[error("Error Reading Data {0}")]
ReadDataError(#[from] std::io::Error),
#[error("Error Parsing Edid Data {0}")]
EdidParseError(#[from] EdidParseError),
#[error("Internal Displays do not support DDC/CI")]
InternalDisplay,
#[error("Communication Error")]
CommunicationError(#[from] DdcCiError),
#[error("Unsupported Vcp Feature")]
UnsupportedVcpFeature,
}
#[derive(Debug, Error)]
pub enum DdcCiError {
#[error("Error sending DDC data: {0}")]
TransmitError(anyhow::Error),
#[error("Error receiving DDC data: {0}")]
ReceiveError(anyhow::Error),
#[error("DDC/CI Protocol Error! {0}")]
ProtocolError(#[from] DdcCiProtocolError),
#[error("DDC/CI unexpected ReplyCode")]
UnexpectedReplyCode,
}
pub trait DdcCommunicationBase {
fn transmit(&mut self, addr: u8, data: &[u8]) -> Result<(), DdcCiError>;
fn receive(&mut self, addr: u8) -> Result<[u8; I2C_DDC_RECV_BUFFER_SIZE], DdcCiError>;
fn delay(&self, delay_ms: u64);
}
pub trait DeriveDdcCiDevice: DdcCommunicationBase {}
pub trait DdcCiDevice {
fn read_capabilities(&mut self) -> Result<Capabilities, DdcError>;
fn get_vcp_feature<V: VcpValue>(&mut self) -> Result<V, DdcError>;
fn set_vcp_feature<V: VcpValue>(&mut self, vcp_value: V) -> Result<(), DdcError>;
fn save_current_settings(&mut self) -> Result<(), DdcError>;
}
impl<X> DdcCiDevice for X
where
X: DeriveDdcCiDevice,
{
fn read_capabilities(&mut self) -> Result<Capabilities, DdcError> {
let mut capabilities_request =
DdcCiMessage::from_opcode(ci::DdcOpcode::CapabilitiesRequest).set_offset(0x0);
self.transmit(
capabilities_request.addr(),
&capabilities_request.transmit_buffer(),
)?;
self.delay(50);
let mut capabilities_reply =
DdcCiMessage::parse_buffer(&self.receive(capabilities_request.addr())?)
.map_err(|err| DdcCiError::ProtocolError(err))?;
let mut capabilities_buffer = Vec::new();
while capabilities_reply.get_data_len() != 0 {
capabilities_buffer.extend_from_slice(capabilities_reply.get_data());
capabilities_request =
capabilities_request.add_offset(capabilities_reply.get_data_len());
self.transmit(
capabilities_request.addr(),
&capabilities_request.transmit_buffer(),
)?;
self.delay(50);
capabilities_reply =
DdcCiMessage::parse_buffer(&self.receive(capabilities_request.addr())?)
.map_err(|err| DdcCiError::ProtocolError(err))?;
}
let cap_str = String::from_utf8(capabilities_buffer).unwrap();
let capabilities: Capabilities = parse_capabilities(&cap_str)?;
Ok(capabilities)
}
fn get_vcp_feature<V: VcpValue>(&mut self) -> Result<V, DdcError> {
let get_vcp_request =
DdcCiMessage::from_opcode(ci::DdcOpcode::VcpRequest).set_vcp_feature(V::vcp_feature());
self.transmit(get_vcp_request.addr(), &get_vcp_request.transmit_buffer())?;
self.delay(40);
let mut get_vcp_reply = DdcCiMessage::parse_buffer(&self.receive(get_vcp_request.addr())?)
.map_err(|err| DdcCiError::ProtocolError(err))?;
let mut retry = 3;
while retry > 0 && get_vcp_reply == DdcCiMessage::NullResponse() {
self.transmit(get_vcp_request.addr(), &get_vcp_request.transmit_buffer())?;
self.delay(40);
get_vcp_reply = DdcCiMessage::parse_buffer(&self.receive(get_vcp_request.addr())?)
.map_err(|err| DdcCiError::ProtocolError(err))?;
retry -= 1;
}
if get_vcp_reply
.get_opcode()
.is_some_and(|opcode| *opcode == DdcOpcode::VcpReply)
{
let (_, vcp_resp) = parse_feature_reply(get_vcp_reply.get_data()).map_err(|err| {
println!("{get_vcp_reply:#x?}");
DdcCiError::ProtocolError(err.into())
})?;
if *vcp_resp.result_code() == ResultCode::UnsupportedCode {
Err(DdcError::UnsupportedVcpFeature)
} else {
Ok(vcp_resp.vcp_data().into())
}
} else {
Err(DdcCiError::UnexpectedReplyCode.into())
}
}
fn set_vcp_feature<V: VcpValue>(&mut self, vcp_value: V) -> Result<(), DdcError> {
let set_vcp_request = DdcCiMessage::from_opcode(ci::DdcOpcode::SetVcp)
.set_vcp_feature(V::vcp_feature())
.set_data(&[vcp_value.vh(), vcp_value.vl()])
.map_err(|err| DdcCiError::ProtocolError(err))?;
self.transmit(set_vcp_request.addr(), &set_vcp_request.transmit_buffer())?;
self.delay(50);
Ok(())
}
fn save_current_settings(&mut self) -> Result<(), DdcError> {
let save_request = DdcCiMessage::from_opcode(ci::DdcOpcode::SaveCurrentSettings);
self.transmit(save_request.addr(), &save_request.transmit_buffer())?;
Ok(())
}
}
pub trait DdcDevice {
fn name(&self) -> String;
fn read_edid(&mut self) -> Result<Edid, DdcError>;
}
pub trait Ddc: DdcDevice + DdcCiDevice {}