Skip to main content

hdmi_hal_i2c_dev/
error.rs

1//! Error types for all fallible operations in this crate.
2
3use std::io;
4use std::path::PathBuf;
5
6/// The error type for all fallible operations in this crate.
7#[non_exhaustive]
8#[derive(Debug)]
9pub enum I2cDevError {
10    /// The specified DRM connector was not found in sysfs.
11    ConnectorNotFound {
12        /// The connector name that was looked up.
13        connector: String,
14    },
15    /// The connector exists in sysfs but has no `ddc` symlink.
16    /// Common for non-HDMI connectors and HDMI ports with no sink attached.
17    ConnectorHasNoDdcAdapter {
18        /// The connector name that was looked up.
19        connector: String,
20    },
21    /// The `ddc` symlink exists but its target does not contain a recognizable
22    /// adapter index. This indicates an unexpected sysfs layout.
23    DdcAdapterIndexUnparseable {
24        /// The full symlink target path that could not be parsed.
25        symlink_target: PathBuf,
26    },
27    /// The `/dev/i2c-N` device could not be opened.
28    DeviceOpenFailed {
29        /// The device path that could not be opened.
30        path: PathBuf,
31        /// The underlying I/O error.
32        source: io::Error,
33    },
34    /// An I²C transaction failed.
35    Transaction(I2cTransactionError),
36}
37
38/// The phase and kind of an I²C transaction failure.
39#[non_exhaustive]
40#[derive(Debug)]
41pub struct I2cTransactionError {
42    /// Which message in the compound transaction failed.
43    pub phase: MessagePhase,
44    /// The class of bus condition that caused the failure.
45    pub kind: I2cErrorKind,
46}
47
48/// Which message in the compound SCDC transaction failed.
49#[non_exhaustive]
50#[derive(Debug)]
51pub enum MessagePhase {
52    /// The write-phase message failed (register address byte, or write data).
53    Write {
54        /// The SCDC register address being accessed when the failure occurred.
55        register: u8,
56    },
57    /// The read-phase message failed (data retrieval).
58    Read {
59        /// The SCDC register address being accessed when the failure occurred.
60        register: u8,
61    },
62}
63
64/// The class of I²C bus condition that caused the failure.
65///
66/// Variants follow the conventions in `Documentation/i2c/fault-codes.rst`.
67/// See the `amdgpu` note in the crate documentation for current hardware
68/// limitations.
69#[non_exhaustive]
70#[derive(Debug)]
71pub enum I2cErrorKind {
72    /// The slave did not acknowledge its I²C address byte (`ENXIO`).
73    AddressNack,
74    /// The slave acknowledged its address but NACKed a data byte.
75    DataNack,
76    /// Arbitration was lost to another bus master (`EAGAIN`). Retry is appropriate.
77    ArbitrationLost,
78    /// The slave held SCL low beyond the adapter timeout threshold (`ETIMEDOUT`).
79    ClockStretchTimeout,
80    /// The bus was busy and the transaction could not be started (`EBUSY`).
81    BusBusy,
82    /// An unexpected START or STOP condition was observed on the bus (`EPROTO`).
83    BusProtocolError,
84    /// The I²C adapter was suspended or removed (`ESHUTDOWN`/`ENODEV`).
85    AdapterGone,
86    /// An errno that does not map to any known I²C condition.
87    Unknown {
88        /// The raw OS error number.
89        errno: i32,
90    },
91}
92
93impl std::fmt::Display for I2cDevError {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        match self {
96            Self::ConnectorNotFound { connector } => {
97                write!(f, "DRM connector not found in sysfs: {connector}")
98            }
99            Self::ConnectorHasNoDdcAdapter { connector } => {
100                write!(
101                    f,
102                    "connector {connector} has no DDC adapter (no sink attached, or not an HDMI port)"
103                )
104            }
105            Self::DdcAdapterIndexUnparseable { symlink_target } => {
106                write!(
107                    f,
108                    "DDC symlink target does not contain a recognizable adapter index: {}",
109                    symlink_target.display()
110                )
111            }
112            Self::DeviceOpenFailed { path, .. } => {
113                write!(f, "failed to open I²C device: {}", path.display())
114            }
115            Self::Transaction(e) => write!(f, "I²C transaction failed: {e}"),
116        }
117    }
118}
119
120impl std::error::Error for I2cDevError {
121    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
122        match self {
123            Self::DeviceOpenFailed { source, .. } => Some(source),
124            Self::Transaction(e) => Some(e),
125            _ => None,
126        }
127    }
128}
129
130impl std::fmt::Display for I2cTransactionError {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        let phase = match &self.phase {
133            MessagePhase::Write { register } => format!("write phase (register 0x{register:02x})"),
134            MessagePhase::Read { register } => format!("read phase (register 0x{register:02x})"),
135        };
136        write!(f, "{phase}: {:?}", self.kind)
137    }
138}
139
140impl std::error::Error for I2cTransactionError {}
141
142#[cfg(test)]
143#[path = "error_tests.rs"]
144mod tests;