1#![deny(missing_docs)]
2#![doc(html_root_url = "https://docs.rs/ddc/0.3.0/")]
3
4extern crate mccs;
11
12use std::{error, fmt, iter, time::Duration};
13pub use {
14 self::{
15 commands::{Command, CommandResult, TimingMessage},
16 delay::Delay,
17 },
18 mccs::{FeatureCode, Value as VcpValue, ValueType as VcpValueType},
19};
20
21pub mod commands;
23mod delay;
24
25pub const I2C_ADDRESS_EDID: u16 = 0x50;
27
28pub const I2C_ADDRESS_EDID_SEGMENT: u16 = 0x30;
30
31pub const I2C_ADDRESS_DDC_CI: u16 = 0x37;
33
34pub const SUB_ADDRESS_DDC_CI: u8 = 0x51;
36
37pub const DELAY_COMMAND_FAILED_MS: u64 = 40;
39
40pub trait Edid {
43 type EdidError;
45
46 fn read_edid(&mut self, offset: u8, data: &mut [u8]) -> Result<usize, Self::EdidError>;
48}
49
50pub trait Eddc: Edid {
52 fn read_eddc_edid(&mut self, segment: u8, offset: u8, data: &mut [u8]) -> Result<usize, Self::EdidError>;
55}
56
57pub trait DdcHost {
59 type Error;
63
64 fn sleep(&mut self) {}
73}
74
75pub trait DdcCommandRaw: DdcHost {
77 fn execute_raw<'a>(
83 &mut self,
84 data: &[u8],
85 out: &'a mut [u8],
86 response_delay: Duration,
87 ) -> Result<&'a mut [u8], Self::Error>;
88}
89
90pub trait DdcCommandRawMarker: DdcCommandRaw
92where
93 Self::Error: From<ErrorCode>,
94{
95 fn set_sleep_delay(&mut self, delay: Delay);
98}
99
100pub trait DdcCommand: DdcHost {
105 fn execute<C: Command>(&mut self, command: C) -> Result<C::Ok, Self::Error>;
108
109 fn checksum<II: IntoIterator<Item = u8>>(iter: II) -> u8 {
111 iter.into_iter().fold(0u8, |sum, v| sum ^ v)
112 }
113
114 fn encode_command<'a>(data: &[u8], packet: &'a mut [u8]) -> &'a [u8] {
118 packet[0] = SUB_ADDRESS_DDC_CI;
119 packet[1] = 0x80 | data.len() as u8;
120 packet[2..2 + data.len()].copy_from_slice(data);
121 packet[2 + data.len()] =
122 Self::checksum(iter::once((I2C_ADDRESS_DDC_CI as u8) << 1).chain(packet[..2 + data.len()].iter().cloned()));
123
124 &packet[..3 + data.len()]
125 }
126}
127
128pub trait DdcCommandMarker: DdcCommand
131where
132 Self::Error: From<ErrorCode>,
133{
134}
135
136pub trait Ddc: DdcHost {
138 fn capabilities_string(&mut self) -> Result<Vec<u8>, Self::Error>;
142
143 fn get_vcp_feature(&mut self, code: FeatureCode) -> Result<VcpValue, Self::Error>;
145
146 fn set_vcp_feature(&mut self, code: FeatureCode, value: u16) -> Result<(), Self::Error>;
148
149 fn save_current_settings(&mut self) -> Result<(), Self::Error>;
151
152 fn get_timing_report(&mut self) -> Result<TimingMessage, Self::Error>;
154}
155
156pub trait DdcTable: DdcHost {
160 fn table_read(&mut self, code: FeatureCode) -> Result<Vec<u8>, Self::Error>;
162
163 fn table_write(&mut self, code: FeatureCode, offset: u16, value: &[u8]) -> Result<(), Self::Error>;
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
169pub enum ErrorCode {
170 InvalidOffset,
172 InvalidLength,
174 InvalidChecksum,
176 InvalidOpcode,
178 InvalidData,
180 Invalid(String),
182}
183
184impl error::Error for ErrorCode {}
185
186impl fmt::Display for ErrorCode {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 f.write_str(match *self {
189 ErrorCode::InvalidOffset => "invalid offset returned from DDC/CI",
190 ErrorCode::InvalidLength => "invalid DDC/CI length",
191 ErrorCode::InvalidChecksum => "DDC/CI checksum mismatch",
192 ErrorCode::InvalidOpcode => "DDC/CI VCP opcode mismatch",
193 ErrorCode::InvalidData => "invalid DDC/CI data",
194 ErrorCode::Invalid(ref s) => s,
195 })
196 }
197}
198
199impl<D: DdcCommandMarker> Ddc for D
200where
201 D::Error: From<ErrorCode>,
202{
203 fn capabilities_string(&mut self) -> Result<Vec<u8>, Self::Error> {
204 let mut string = Vec::new();
205 let mut offset = 0;
206 loop {
207 let caps = self.execute(commands::CapabilitiesRequest::new(offset))?;
208 if caps.offset != offset {
209 return Err(ErrorCode::InvalidOffset.into())
210 } else if caps.data.is_empty() {
211 break
212 }
213
214 string.extend(caps.data.iter());
215
216 offset += caps.data.len() as u16;
217 }
218
219 Ok(string)
220 }
221
222 fn get_vcp_feature(&mut self, code: FeatureCode) -> Result<VcpValue, Self::Error> {
223 self.execute(commands::GetVcpFeature::new(code))
224 }
225
226 fn set_vcp_feature(&mut self, code: FeatureCode, value: u16) -> Result<(), Self::Error> {
227 self.execute(commands::SetVcpFeature::new(code, value))
228 }
229
230 fn save_current_settings(&mut self) -> Result<(), Self::Error> {
231 self.execute(commands::SaveCurrentSettings)
232 }
233
234 fn get_timing_report(&mut self) -> Result<TimingMessage, Self::Error> {
235 self.execute(commands::GetTimingReport)
236 }
237}
238
239impl<D: DdcCommandMarker> DdcTable for D
240where
241 D::Error: From<ErrorCode>,
242{
243 fn table_read(&mut self, code: FeatureCode) -> Result<Vec<u8>, Self::Error> {
244 let mut value = Vec::new();
245 let mut offset = 0;
246 loop {
247 let table = self.execute(commands::TableRead::new(code, offset))?;
248 if table.offset != offset {
249 return Err(ErrorCode::InvalidOffset.into())
250 } else if table.bytes().is_empty() {
251 break
252 }
253
254 value.extend(table.bytes().iter());
255
256 offset += table.bytes().len() as u16;
257 }
258
259 Ok(value)
260 }
261
262 fn table_write(&mut self, code: FeatureCode, mut offset: u16, value: &[u8]) -> Result<(), Self::Error> {
263 for chunk in value.chunks(32) {
264 self.execute(commands::TableWrite::new(code, offset, chunk))?;
265 offset += chunk.len() as u16;
266 }
267
268 Ok(())
269 }
270}
271
272impl<D: DdcCommandRawMarker> DdcCommand for D
273where
274 D::Error: From<ErrorCode>,
275{
276 fn execute<C: Command>(&mut self, command: C) -> Result<C::Ok, Self::Error> {
277 let mut data = [0u8; 36];
280 command.encode(&mut data)?;
281
282 let mut out = [0u8; 36 + 3];
285 let out = if C::Ok::MAX_LEN > 0 {
286 &mut out[..C::Ok::MAX_LEN + 3]
287 } else {
288 &mut []
289 };
290 let res = self.execute_raw(
291 &data[..command.len()],
292 out,
293 Duration::from_millis(C::DELAY_RESPONSE_MS as _),
294 );
295 let res = match res {
296 Ok(res) => {
297 self.set_sleep_delay(Delay::new(Duration::from_millis(C::DELAY_COMMAND_MS)));
298 res
299 },
300 Err(e) => {
301 self.set_sleep_delay(Delay::new(Duration::from_millis(DELAY_COMMAND_FAILED_MS)));
302 return Err(e)
303 },
304 };
305
306 let res = C::Ok::decode(res);
307
308 if res.is_err() {
309 self.set_sleep_delay(Delay::new(Duration::from_millis(DELAY_COMMAND_FAILED_MS)));
310 }
311
312 res.map_err(From::from)
313 }
314}