1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//! Error type shared by every layer of the crate.
//!
//! [`Error`] carries the detail (USB error, transfer status, NIOS packet
//! problem, ...); [`ErrorKind`] is the stable coarse category applications
//! match on. Both enums are `#[non_exhaustive]`.
use crate::protocol::nios::NiosPacketError;
use std::fmt;
/// Result type alias for this crate.
pub type Result<T> = std::result::Result<T, Error>;
/// Stable, coarse category of an [`Error`], returned by [`Error::kind`].
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ErrorKind {
/// USB device, descriptor, endpoint or transfer failure.
Usb,
/// Malformed NIOS request or response packet.
Protocol,
/// An operation did not complete within its deadline.
Timeout,
/// A non-blocking operation has nothing to return yet.
WouldBlock,
/// No matching device was found.
NotFound,
/// A caller-supplied value is out of range or otherwise invalid.
InvalidArgument,
/// The feature is not available on this hardware or configuration.
Unsupported,
/// The operation is not valid in the current device or stream state.
State,
/// The device reported an unexpected or inconsistent hardware state.
Hardware,
/// A calibration routine did not converge.
Calibration,
/// Flash contents could not be verified or decoded.
Flash,
/// File or serialization failure outside the device.
Io,
/// An internal invariant was violated; please report this.
Internal,
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Self::Usb => "usb",
Self::Protocol => "protocol",
Self::Timeout => "timeout",
Self::WouldBlock => "would block",
Self::NotFound => "not found",
Self::InvalidArgument => "invalid argument",
Self::Unsupported => "unsupported",
Self::State => "invalid state",
Self::Hardware => "hardware",
Self::Calibration => "calibration",
Self::Flash => "flash",
Self::Io => "io",
Self::Internal => "internal",
};
f.write_str(name)
}
}
/// Error type for all operations in this crate.
#[non_exhaustive]
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// An I/O error occurred (e.g. reading a DC calibration table from disk).
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
/// A JSON deserialization error (e.g. malformed DC calibration table file).
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
/// A USB error from the nusb transport layer.
#[error("USB error: {0}")]
Nusb(#[from] nusb::Error),
/// A USB transfer error (submission or completion failure).
#[error("USB transfer error: {0}")]
Transfer(#[from] nusb::transfer::TransferError),
/// A USB descriptor query error.
#[error("USB descriptor error: {0}")]
Descriptor(#[from] nusb::GetDescriptorError),
/// An invalid argument was supplied (e.g. out-of-range frequency or gain).
#[error("invalid argument: {0}")]
Argument(String),
/// The device does not support the required USB speed (must be High or above).
#[error("device requires High speed or above")]
UnsupportedSpeed,
/// No BladeRF device was found matching the search criteria.
#[error("device not found")]
NotFound,
/// A USB operation or device readiness check timed out.
#[error("operation timed out")]
Timeout,
/// A NIOS packet encode/decode error (malformed request or response).
#[error("NIOS packet error: {0}")]
NiosPacket(#[from] NiosPacketError),
/// A USB endpoint is already claimed or busy.
#[error("endpoint busy")]
EndpointBusy(#[source] nusb::Error),
/// The requested USB endpoint is not available in the current alt setting.
#[error("endpoint not available")]
EndpointNotAvailable,
/// FPGA-assisted tuning failed (VCOCAP did not converge).
#[error("FPGA tuning failed")]
TuningFailed,
/// The FPGA retune queue is full; cannot schedule another retune.
#[error("FPGA retune queue is full")]
RetuneQueueFull,
/// The requested feature is not supported on this hardware or configuration.
#[error("unsupported feature: {0}")]
Unsupported(&'static str),
/// A DC calibration sub-module failed to converge.
#[error("calibration failed: {0}")]
CalibrationFailed(&'static str),
/// The device reported an unexpected or inconsistent hardware state
/// (e.g. an invalid register value or a VCO that did not settle).
#[error("unexpected hardware state: {0}")]
BoardState(&'static str),
/// The board has not been initialized; call `RfLinkSession::initialize`.
#[error("device not initialized")]
NotInitialized,
/// A USB control transfer returned fewer bytes than expected.
#[error("USB control response too short: expected {expected} bytes, got {actual}")]
UsbControlResponseTooShort {
/// Number of bytes the command is defined to return.
expected: usize,
/// Number of bytes actually received.
actual: usize,
},
/// The requested sample rate is invalid for the current configuration.
#[error("invalid sample rate: {0}")]
InvalidSampleRate(&'static str),
/// A non-blocking operation would block; no data is available yet.
#[error("operation would block")]
WouldBlock,
/// Flash verification failed after a write operation.
#[error(
"flash verification failed at byte {byte_offset}: expected 0x{expected:02x}, got 0x{actual:02x}"
)]
FlashVerificationFailed {
/// Offset of the first mismatching byte.
byte_offset: usize,
/// Byte value that was written.
expected: u8,
/// Byte value that was read back.
actual: u8,
},
/// Data stored in flash (calibration region, BINKV fields) is malformed.
#[error("malformed flash data: {0}")]
FlashData(&'static str),
/// The stream has already been closed or was never opened.
#[error("stream already closed")]
StreamClosed,
/// The stream must be started before data can be transferred.
#[error("stream not started")]
StreamNotStarted,
/// `start()` was called on a stream that is already running.
#[error("stream already started")]
StreamAlreadyStarted,
/// Nothing can complete: the caller holds every buffer of the pool (RX)
/// or no buffer is available and none is in flight (TX).
#[error("no transfers in flight; recycle buffers first")]
NoTransfersInFlight,
/// Cannot switch USB alt setting while streams are active.
#[error("cannot switch mode while streams are active")]
StreamsActive,
/// The trigger must be armed before it can be fired or disarmed.
#[error("trigger not armed")]
TriggerNotArmed,
/// Only the trigger master may fire the trigger.
#[error("only the trigger master can fire the trigger")]
TriggerNotMaster,
/// An internal invariant was violated (e.g. a static table is
/// incomplete or a computed register value overflowed).
#[error("internal error: {0}")]
Internal(&'static str),
}
impl Error {
/// Returns the coarse category of this error.
pub fn kind(&self) -> ErrorKind {
match self {
Self::Io(_) | Self::Json(_) => ErrorKind::Io,
Self::Nusb(_)
| Self::Transfer(_)
| Self::Descriptor(_)
| Self::EndpointBusy(_)
| Self::EndpointNotAvailable
| Self::UnsupportedSpeed
| Self::UsbControlResponseTooShort { .. } => ErrorKind::Usb,
Self::NiosPacket(_) => ErrorKind::Protocol,
Self::Timeout => ErrorKind::Timeout,
Self::WouldBlock => ErrorKind::WouldBlock,
Self::NotFound => ErrorKind::NotFound,
Self::Argument(_) | Self::InvalidSampleRate(_) => ErrorKind::InvalidArgument,
Self::Unsupported(_) => ErrorKind::Unsupported,
Self::NotInitialized
| Self::StreamClosed
| Self::StreamNotStarted
| Self::StreamAlreadyStarted
| Self::NoTransfersInFlight
| Self::StreamsActive
| Self::TriggerNotArmed
| Self::TriggerNotMaster => ErrorKind::State,
Self::BoardState(_) | Self::TuningFailed | Self::RetuneQueueFull => ErrorKind::Hardware,
Self::CalibrationFailed(_) => ErrorKind::Calibration,
Self::FlashVerificationFailed { .. } | Self::FlashData(_) => ErrorKind::Flash,
Self::Internal(_) => ErrorKind::Internal,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kinds_are_stable() {
assert_eq!(Error::Timeout.kind(), ErrorKind::Timeout);
assert_eq!(Error::NotInitialized.kind(), ErrorKind::State);
assert_eq!(Error::StreamNotStarted.kind(), ErrorKind::State);
assert_eq!(Error::BoardState("x").kind(), ErrorKind::Hardware);
assert_eq!(
Error::Argument("x".into()).kind(),
ErrorKind::InvalidArgument
);
assert_eq!(Error::FlashData("x").kind(), ErrorKind::Flash);
assert_eq!(Error::Internal("x").kind(), ErrorKind::Internal);
assert_eq!(ErrorKind::State.to_string(), "invalid state");
}
}