ddc_i2c/
lib.rs

1#![deny(missing_docs)]
2#![doc(html_root_url = "https://docs.rs/ddc-i2c/0.2.2/")]
3
4//! Implementation of DDC/CI traits over I2C.
5//!
6//! # Example
7//!
8//! ```rust,no_run
9//! extern crate ddc_i2c;
10//! extern crate ddc;
11//!
12//! # fn main() {
13//! use ddc::Ddc;
14//!
15//! # #[cfg(all(target_os = "linux", feature = "i2c-linux"))] fn ddc() {
16//! let mut ddc = ddc_i2c::from_i2c_device("/dev/i2c-4").unwrap();
17//! let mccs_version = ddc.get_vcp_feature(0xdf).unwrap();
18//! println!("MCCS version: {:04x}", mccs_version.maximum());
19//! # }
20//! # }
21//! ```
22
23extern crate ddc;
24extern crate i2c;
25#[cfg(all(target_os = "linux", feature = "i2c-linux"))]
26extern crate i2c_linux;
27extern crate resize_slice;
28
29use {
30    ddc::{DdcCommand, DdcCommandMarker, DdcCommandRaw, DdcCommandRawMarker, DdcHost, Delay, Eddc, Edid, ErrorCode},
31    resize_slice::ResizeSlice,
32    std::{cmp, error, fmt, io, iter, thread::sleep, time::Duration},
33};
34
35#[cfg(all(target_os = "linux", feature = "with-linux-enumerate"))]
36mod enumerate;
37#[cfg(all(target_os = "linux", feature = "with-linux-enumerate"))]
38pub use enumerate::Enumerator as I2cDeviceEnumerator;
39
40/// A handle to provide DDC/CI operations on an I2C device.
41#[derive(Clone, Debug)]
42pub struct I2cDdc<I> {
43    inner: I,
44    delay: Delay,
45}
46
47/// DDC/CI on Linux i2c-dev
48#[cfg(all(target_os = "linux", feature = "i2c-linux"))]
49pub type I2cDeviceDdc = I2cDdc<::i2c_linux::I2c<::std::fs::File>>;
50
51/// Open a new DDC/CI handle with the specified I2C device node path
52#[cfg(all(target_os = "linux", feature = "i2c-linux"))]
53pub fn from_i2c_device<P: AsRef<::std::path::Path>>(p: P) -> ::std::io::Result<I2cDeviceDdc> {
54    Ok(I2cDdc::new(::i2c_linux::I2c::from_path(p)?))
55}
56
57impl<I> I2cDdc<I> {
58    /// Create a new DDC/CI handle with an existing open device.
59    pub fn new(i2c: I) -> Self {
60        I2cDdc {
61            inner: i2c,
62            delay: Default::default(),
63        }
64    }
65
66    /// Consume the handle to return the inner device.
67    pub fn into_inner(self) -> I {
68        self.inner
69    }
70
71    /// Borrow the inner device.
72    pub fn inner_ref(&self) -> &I {
73        &self.inner
74    }
75
76    /// Mutably borrow the inner device.
77    pub fn inner_mut(&mut self) -> &mut I {
78        &mut self.inner
79    }
80}
81
82// TODO: impl with BulkTransfer?
83impl<I: i2c::Address + i2c::BlockTransfer> Edid for I2cDdc<I> {
84    type EdidError = I::Error;
85
86    fn read_edid(&mut self, mut offset: u8, mut data: &mut [u8]) -> Result<usize, I::Error> {
87        self.inner.set_slave_address(ddc::I2C_ADDRESS_EDID, false)?;
88
89        let mut len = 0;
90        while !data.is_empty() {
91            let datalen = cmp::min(0x80, data.len());
92            let read = self.inner.i2c_read_block_data(offset, &mut data[..datalen])?;
93            if read == 0 {
94                break
95            }
96            len += read;
97            offset = if let Some(offset) = offset.checked_add(read as u8) {
98                offset
99            } else {
100                break
101            };
102            data.resize_from(read);
103        }
104
105        Ok(len)
106    }
107}
108
109// TODO: these address/block bounds shouldn't be necessary, but might need
110// specialization to impl Edid with BulkTransfer :<
111impl<I: i2c::Address + i2c::BlockTransfer + i2c::BulkTransfer> Eddc for I2cDdc<I> {
112    fn read_eddc_edid(&mut self, segment: u8, offset: u8, data: &mut [u8]) -> Result<usize, I::Error> {
113        let len = {
114            let mut msgs = [
115                i2c::Message::Write {
116                    address: ddc::I2C_ADDRESS_EDID_SEGMENT,
117                    data: &[segment],
118                    flags: Default::default(),
119                },
120                i2c::Message::Write {
121                    address: ddc::I2C_ADDRESS_EDID,
122                    data: &[offset],
123                    flags: Default::default(),
124                },
125                i2c::Message::Read {
126                    address: ddc::I2C_ADDRESS_EDID,
127                    data,
128                    flags: Default::default(),
129                },
130            ];
131            self.inner.i2c_transfer(&mut msgs)?;
132            msgs[2].len()
133        };
134
135        Ok(len)
136    }
137}
138
139impl<I: i2c::Master> ::DdcHost for I2cDdc<I> {
140    type Error = Error<I::Error>;
141
142    fn sleep(&mut self) {
143        self.delay.sleep()
144    }
145}
146
147impl<I: i2c::Address + i2c::ReadWrite> DdcCommandRaw for I2cDdc<I> {
148    fn execute_raw<'a>(
149        &mut self,
150        data: &[u8],
151        out: &'a mut [u8],
152        response_delay: Duration,
153    ) -> Result<&'a mut [u8], Error<I::Error>> {
154        assert!(data.len() <= 36);
155
156        let mut packet = [0u8; 36 + 3];
157        let packet = Self::encode_command(data, &mut packet);
158        self.inner
159            .set_slave_address(ddc::I2C_ADDRESS_DDC_CI, false)
160            .map_err(Error::I2c)?;
161
162        let full_len = {
163            self.sleep();
164            self.inner.i2c_write(packet).map_err(Error::I2c)?;
165            if !out.is_empty() {
166                sleep(response_delay);
167                self.inner.i2c_read(out).map_err(Error::I2c)?
168            } else {
169                return Ok(out)
170            }
171        };
172
173        if full_len < 2 {
174            return Err(Error::Ddc(ErrorCode::InvalidLength))
175        }
176
177        let len = (out[1] & 0x7f) as usize;
178
179        if out[1] & 0x80 == 0 {
180            // TODO: the meaning of this bit is unclear?
181            return Err(Error::Ddc(ErrorCode::Invalid("Expected DDC/CI length bit".into())))
182        }
183
184        if full_len < len + 2 {
185            return Err(Error::Ddc(ErrorCode::InvalidLength))
186        }
187
188        let checksum = Self::checksum(
189            iter::once(((ddc::I2C_ADDRESS_DDC_CI as u8) << 1) | 1)
190                .chain(iter::once(ddc::SUB_ADDRESS_DDC_CI))
191                .chain(out[1..2 + len].iter().cloned()),
192        );
193
194        if out[2 + len] != checksum {
195            return Err(Error::Ddc(ErrorCode::InvalidChecksum))
196        }
197
198        Ok(&mut out[2..2 + len])
199    }
200}
201
202impl<I: i2c::Address + i2c::ReadWrite> DdcCommandMarker for I2cDdc<I> {}
203
204impl<I: i2c::Address + i2c::ReadWrite> DdcCommandRawMarker for I2cDdc<I> {
205    fn set_sleep_delay(&mut self, delay: Delay) {
206        self.delay = delay;
207    }
208}
209
210/// An error that can occur during DDC/CI communication.
211///
212/// This error is generic over the underlying I2C communication.
213#[derive(Debug, Clone)]
214pub enum Error<I> {
215    /// Internal I2C communication error
216    I2c(I),
217    /// DDC/CI protocol error or transmission corruption
218    Ddc(ErrorCode),
219}
220
221impl<I> From<ErrorCode> for Error<I> {
222    fn from(e: ErrorCode) -> Self {
223        Error::Ddc(e)
224    }
225}
226
227impl<I: error::Error + Send + Sync + 'static> From<Error<I>> for io::Error {
228    fn from(e: Error<I>) -> io::Error {
229        match e {
230            Error::I2c(e) => io::Error::new(io::ErrorKind::Other, e),
231            Error::Ddc(e) => io::Error::new(io::ErrorKind::InvalidData, e),
232        }
233    }
234}
235
236impl<I: error::Error> error::Error for Error<I> {
237    fn description(&self) -> &str {
238        match *self {
239            Error::I2c(ref e) => error::Error::description(e),
240            Error::Ddc(ref e) => error::Error::description(e),
241        }
242    }
243
244    fn cause(&self) -> Option<&error::Error> {
245        match *self {
246            Error::I2c(ref e) => error::Error::cause(e),
247            Error::Ddc(ref e) => error::Error::cause(e),
248        }
249    }
250}
251
252impl<I: fmt::Display> fmt::Display for Error<I> {
253    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
254        match *self {
255            Error::I2c(ref e) => write!(f, "DDC/CI I2C error: {}", e),
256            Error::Ddc(ref e) => write!(f, "DDC/CI error: {}", e),
257        }
258    }
259}