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
use std::string::FromUtf16Error;
/// Wrapper for problems when communicating with the MCP2221.
#[derive(Debug)]
pub enum Error {
/// A command issued to the MCP2221 via USB HID did not complete successfully.
///
/// The enclosed `u8` is the value returned by the MCP2221 in place of the success
/// code (0).
CommandFailed(u8),
/// An unsupported command was issued to the MCP2221.
///
/// This can occur when reading or writing the flash data. This error indicates
/// a bug in the `mcp2221-hal` library.
CommandNotSupported,
/// A disallowed command was issued to the MCP2221.
///
/// This can occur when writing the flash data, and appears to indicate that the
/// device is permanently locked after repeated failed password entries. See
/// section 3.1.4.1 in the datasheet.
CommandNotAllowed,
/// The I2C bus speed could not be changed because a transfer was in progress.
///
/// This can only occur when attempting to change the I2C bus speed.
I2cCouldNotChangeSpeed,
/// The command code echoed by the MCP2221 was not the command code written to it.
///
/// In practice this should not occur(!). Please report any occurrences.
MismatchedCommandCodeEcho {
/// Command code that was sent to the MCP2221.
sent: u8,
/// Command code echoed from the MCP2221.
received: u8,
},
/// String received from the MCP2221 was not valid UTF-16.
InvalidStringFromDevice(FromUtf16Error),
/// Invalid pin mode bit pattern received from the MCP2221.
InvalidPinModeFromDevice {
/// MCP2221 pin name, eg "GP0".
pin: &'static str,
/// Invalid bit pattern received for the pin's mode/designation.
mode: u8,
},
/// An error occurred when attempting to open the MCP2221 USB device.
HidApi(hidapi::HidError),
/// I2C engine could not handle the request because it was busy.
I2cEngineBusy,
/// I2C target data could not be read from the I2C engine.
I2cEngineReadError,
/// The number of bytes to read from or write to an I2C target was more than 65,535.
I2cTransferTooLong,
/// The number of bytes to write to or read from an I2C target was 0.
I2cTransferEmpty,
/// Exhausted retries for an I2C operation.
I2cOperationFailed,
/// I2C target address didn't acknowledge its address.
I2cAddressNack,
/// Attempt to perform an I2C transaction that is not possible via the MCP2221.
///
/// Specifically, a read occurs before a write in the slice of operations passed
/// to this library's implementation of [`embedded_hal::i2c::I2c::transaction`].
///
/// The embedded-hal I2C transaction contract cannot be fulfilled in its most
/// general form by the MCP2221, because it has no HID command to perform a read
/// without a final STOP condition.
///
/// This library does not attempt to "fake" the transaction because it would mean
/// introducing STOP conditions that would violate the documented contract, and
/// could be interpreted in an unexpected way by an I2C target.
///
/// If you need to perform a transaction where a read takes place before a write
/// without an STOP condition in between, you should use a device other than the
/// MCP2221.
I2cUnsupportedEmbeddedHalTransaction,
/// A GP pin's mode was changed while a mode-specific wrapper type was in use,
/// rendering it unable to perform its mode-specific functions.
PinModeChanged,
}
#[doc(hidden)]
impl From<hidapi::HidError> for Error {
fn from(value: hidapi::HidError) -> Self {
Self::HidApi(value)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::CommandFailed(e) => write!(
f,
"MCP2221 command did not complete successfully and returned error code {e:#X}.",
),
Error::CommandNotSupported => write!(
f,
"command rejected by the MCP2221 because it is unsupported"
),
Error::CommandNotAllowed => write!(
f,
"command rejected by the MCP2221 because it is not allowed"
),
Error::I2cCouldNotChangeSpeed => write!(
f,
"I2C bus speed could not be changed because a transfer is in progress",
),
Error::MismatchedCommandCodeEcho { sent, received } => write!(
f,
"incorrect command code echo from the MCP2221 (got {received:#X}, expected {sent:#X})",
),
Error::InvalidStringFromDevice(e) => {
write!(f, "invalid utf-16 string received from the MCP2221: {e}")
}
Error::InvalidPinModeFromDevice { pin, mode } => write!(
f,
"invalid pin mode bit pattern {mode:#b} received for {pin}"
),
Error::HidApi(hid_error) => write!(f, "HidApi error: {hid_error}"),
Error::I2cEngineBusy => write!(f, "I2C engine busy"),
Error::I2cEngineReadError => {
write!(f, "could not read I2C target data from the I2C engine")
}
Error::I2cTransferTooLong => {
write!(
f,
"attempt to transfer than 65,535 bytes to or from I2C target"
)
}
Error::I2cTransferEmpty => {
write!(f, "zero-length I2C transfers are not supported")
}
Error::I2cOperationFailed => {
write!(f, "all retries exhausted attempt to perform I2C operation")
}
Error::I2cAddressNack => {
write!(f, "I2C target didn't acknowledge its address")
}
Error::I2cUnsupportedEmbeddedHalTransaction => {
write!(f, "I2C transaction operations mixed in an unsupported way")
}
Error::PinModeChanged => {
write!(
f,
"pin mode was changed while a mode-specific wrapper was in use"
)
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::InvalidStringFromDevice(utf_error) => Some(utf_error),
Error::HidApi(hid_error) => Some(hid_error),
_ => None,
}
}
}