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;