openlogi_core/hid/error.rs
1//! HID++ read/write error vocabulary — pure data, no I/O.
2//!
3//! The conversions from `hidpp`/`async_hid` error types into [`WriteError`]
4//! live in `openlogi_hid::write::error`, which this crate must never depend
5//! on.
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10/// Error returned by HID++ read/write operations.
11///
12/// Serializable + Clone so it can cross the agent↔GUI IPC unchanged: the GUI
13/// classifies a device read/write error as permanent (FeatureUnsupported /
14/// EmptyDpiList) vs transient, so the discriminating variant must survive the
15/// wire — stringifying it would collapse every case to "transient" and a device
16/// that genuinely lacks a feature would be re-probed forever. Variant order is
17/// therefore wire format: changes require a `PROTOCOL_VERSION` bump (guarded
18/// by `openlogi-ipc/tests/wire_format.rs`).
19#[derive(Debug, Clone, Error, Serialize, Deserialize)]
20pub enum WriteError {
21 /// HID transport error serialized as text.
22 ///
23 /// `openlogi_hid::BackendError` isn't `Serialize`, so carry its message as
24 /// text; the typed error is never matched on (only constructed +
25 /// displayed).
26 #[error("HID transport error: {0}")]
27 Hid(String),
28 /// No currently connected device matched the requested route.
29 #[error("no connected device matched the route")]
30 DeviceNotFound,
31 /// The HID node opened, but the target HID++ device index did not answer.
32 #[error("device at index {index:#04x} did not respond to HID++")]
33 DeviceUnreachable {
34 /// HID++ device index that failed to answer.
35 index: u8,
36 },
37 /// Device does not expose the requested HID++ feature.
38 #[error("device does not expose HID++ feature {feature_hex:#06x}")]
39 FeatureUnsupported {
40 /// HID++ feature ID that was not present.
41 feature_hex: u16,
42 },
43 /// Device reported no valid DPI values.
44 #[error("device returned no supported DPI values")]
45 EmptyDpiList,
46 /// Generic HID++ protocol error serialized as text.
47 #[error("HID++ protocol error: {0}")]
48 Hidpp(String),
49 /// HID++ feature error response.
50 #[error("HID++ feature error during {operation:?} for feature {feature_hex:#06x}: {kind:?}")]
51 HidppFeature {
52 /// Operation being performed.
53 operation: HidppOperation,
54 /// HID++ feature ID involved in the operation.
55 feature_hex: u16,
56 /// HID++ feature error kind.
57 kind: HidppFeatureErrorKind,
58 },
59 /// Device returned a structurally unsupported response.
60 #[error("HID++ unsupported response during {operation:?} for feature {feature_hex:#06x}")]
61 UnsupportedResponse {
62 /// Operation being performed.
63 operation: HidppOperation,
64 /// HID++ feature ID involved in the operation.
65 feature_hex: u16,
66 },
67 /// HID++ request timed out.
68 #[error("HID++ request timed out during {operation:?}")]
69 RequestTimedOut {
70 /// Operation that timed out.
71 operation: HidppOperation,
72 },
73 /// Tokio runtime could not be initialized for a sync caller.
74 #[error("tokio runtime init failed: {message}")]
75 RuntimeInit {
76 /// Runtime initialization error message.
77 message: String,
78 },
79 /// Background agent write path is unavailable.
80 #[error("background agent is unavailable")]
81 AgentUnavailable,
82 /// A standalone light value is outside the driver's supported range.
83 #[error("invalid light value for {control}: {value}")]
84 InvalidLightValue {
85 /// Semantic control name.
86 control: String,
87 /// Rejected value in the semantic/native unit supplied by the caller.
88 value: u16,
89 },
90 /// The selected light driver does not implement a requested control.
91 #[error("light control is unsupported: {control}")]
92 LightUnsupported {
93 /// Semantic control name.
94 control: String,
95 },
96 /// Multiple raw HID nodes matched one physical route.
97 #[error("multiple raw HID devices matched the route")]
98 AmbiguousRawDevice,
99}
100
101/// HID++ operation being performed when a device write/read failed.
102///
103/// Variant order is wire format because this travels inside [`WriteError`].
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
105pub enum HidppOperation {
106 /// Resolve a feature ID to its runtime feature index.
107 ResolveFeature,
108 /// Enumerate the device feature table.
109 DumpFeatures,
110 /// Read current DPI.
111 ReadDpi,
112 /// Read DPI capabilities.
113 ReadDpiCapabilities,
114 /// Write DPI.
115 WriteDpi,
116 /// Read SmartShift status.
117 ReadSmartShift,
118 /// Write SmartShift status.
119 WriteSmartShift,
120 /// Write keyboard lighting.
121 Lighting,
122 /// Read HiResWheel capabilities or the current wheel mode.
123 ReadWheelMode,
124 /// Write and verify the native HiResWheel mode.
125 WriteWheelMode,
126 /// Read the keyboard backlight config or info.
127 ReadBacklight,
128 /// Write the keyboard backlight config.
129 WriteBacklight,
130 /// Write keyboard Fn-lock (fn inversion).
131 WriteFnLock,
132 /// Write a standalone-light command. Appended last — variant order is wire format.
133 Light,
134 /// Play one haptic waveform. Appended last — variant order is wire format.
135 PlayHaptic,
136}
137
138/// HID++ feature error kind in a serializable wire-safe form.
139///
140/// Variant order is wire format because this travels inside [`WriteError`].
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
142pub enum HidppFeatureErrorKind {
143 /// HID++ `NoError` code.
144 NoError,
145 /// Unknown HID++ error.
146 Unknown,
147 /// Invalid argument.
148 InvalidArgument,
149 /// Argument out of range.
150 OutOfRange,
151 /// Hardware error.
152 HwError,
153 /// Logitech-internal firmware error.
154 LogitechInternal,
155 /// Invalid feature index.
156 InvalidFeatureIndex,
157 /// Invalid function ID.
158 InvalidFunctionId,
159 /// Device is busy.
160 Busy,
161 /// Operation is unsupported.
162 Unsupported,
163 /// Error code not modeled by OpenLogi.
164 Unrecognized,
165}