1use std::string::String;
2use std::time::{Duration, SystemTime};
3
4use clap::Parser;
5
6use serde::Deserialize;
7
8pub use simplelog::{LevelFilter, TermLogger, TerminalMode};
9
10pub mod error;
11pub use error::HalError;
12
13#[cfg(all(feature = "hal-linux", target_os = "linux"))]
14pub mod linux;
15
16#[cfg(feature = "hal-cp2130")]
17pub mod cp2130;
18
19use crate::*;
20
21#[derive(Debug, Parser, Deserialize)]
23pub struct DeviceConfig {
24 #[clap(long, group = "spi-kind", env = "SPI_DEV")]
26 pub spi_dev: Option<String>,
27
28 #[clap(long, group = "spi-kind", env = "CP2130_DEV")]
30 pub cp2130_dev: Option<usize>,
31
32 #[clap(flatten)]
33 #[serde(flatten)]
34 pub spi: SpiConfig,
35
36 #[clap(flatten)]
37 #[serde(flatten)]
38 pub pins: PinConfig,
39}
40
41#[derive(Debug, Clone, Parser, Deserialize)]
43pub struct SpiConfig {
44 #[clap(long = "spi-baud", default_value = "1000000", env = "SPI_BAUD")]
46 pub baud: u32,
47
48 #[clap(long = "spi-mode", default_value = "0", env = "SPI_MODE")]
50 pub mode: u32,
51}
52
53#[derive(Debug, Clone, Parser, Deserialize)]
55pub struct PinConfig {
56 #[clap(long = "cs-pin", default_value = "16", env = "CS_PIN")]
58 pub chip_select: u64,
59
60 #[clap(long = "reset-pin", default_value = "17", env = "RESET_PIN")]
62 pub reset: u64,
63
64 #[clap(long = "busy-pin", env = "BUSY_PIN")]
66 pub busy: Option<u64>,
67
68 #[clap(long = "ready-pin", env = "READY_PIN")]
70 pub ready: Option<u64>,
71
72 #[clap(long = "led0-pin", env = "LED0_PIN")]
74 pub led0: Option<u64>,
75
76 #[clap(long = "led1-pin", env = "LED1_PIN")]
78 pub led1: Option<u64>,
79}
80
81#[derive(Debug, Parser)]
83pub struct LogConfig {
84 #[clap(long = "log-level", default_value = "info")]
85 level: LevelFilter,
87}
88
89impl LogConfig {
90 pub fn init(&self) {
92 TermLogger::init(
93 self.level,
94 simplelog::Config::default(),
95 TerminalMode::Mixed,
96 simplelog::ColorChoice::Auto,
97 )
98 .unwrap();
99 }
100}
101
102pub struct HalInst {
104 pub base: HalBase,
105 pub spi: HalSpi,
106 pub pins: HalPins,
107}
108impl HalInst {
109 pub fn load(config: &DeviceConfig) -> Result<HalInst, HalError> {
111 let hal = match (&config.spi_dev, &config.cp2130_dev) {
113 (Some(_), Some(_)) => {
114 error!("Only one of spi_dev and cp2130_dev may be specified");
115 return Err(HalError::InvalidConfig);
116 }
117 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
118 (Some(s), None) => {
119 debug!("Creating linux hal driver");
120 linux::LinuxDriver::new(s, &config.spi, &config.pins)?
121 }
122 #[cfg(all(feature = "hal-linux", not(target_os = "linux")))]
123 (Some(s), None) => {
124 error!("Linux HAL only supported on linux platforms");
125 return Err(HalError::InvalidConfig);
126 }
127 #[cfg(feature = "hal-cp2130")]
128 (None, Some(i)) => {
129 debug!("Creating cp2130 hal driver");
130 cp2130::Cp2130Driver::new(*i, &config.spi, &config.pins)?
131 }
132 _ => {
133 error!("No SPI configuration provided or no matching implementation found");
134 return Err(HalError::InvalidConfig);
135 }
136 };
137
138 Ok(hal)
139 }
140}
141
142pub enum HalBase {
144 #[cfg(feature = "hal-cp2130")]
145 Cp2130(driver_cp2130::Cp2130),
146 None,
147}
148
149#[non_exhaustive]
151pub enum HalSpi {
152 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
153 Linux(linux::LinuxSpiDevice),
154 #[cfg(feature = "hal-cp2130")]
155 Cp2130(driver_cp2130::Spi),
156}
157
158impl embedded_hal::spi::SpiDevice<u8> for HalSpi {
159 fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
160 match self {
161 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
162 HalSpi::Linux(i) => i.transaction(operations)?,
163 #[cfg(feature = "hal-cp2130")]
164 HalSpi::Cp2130(i) => i.transaction(operations)?,
165 #[allow(unreachable_patterns)]
166 _ => return Err(HalError::NoDriver),
167 }
168 Ok(())
169 }
170
171 fn write<'w>(&mut self, data: &[u8]) -> Result<(), Self::Error> {
172 match self {
173 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
174 HalSpi::Linux(i) => i.write(data)?,
175 #[cfg(feature = "hal-cp2130")]
176 HalSpi::Cp2130(i) => i.write(data)?,
177 #[allow(unreachable_patterns)]
178 _ => return Err(HalError::NoDriver),
179 }
180 Ok(())
181 }
182
183 fn transfer<'w>(&mut self, buff: &'w mut [u8], data: &'w [u8]) -> Result<(), Self::Error> {
184 match self {
185 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
186 HalSpi::Linux(i) => i.transfer(buff, data)?,
187 #[cfg(feature = "hal-cp2130")]
188 HalSpi::Cp2130(i) => i.transfer(buff, data)?,
189 #[allow(unreachable_patterns)]
190 _ => return Err(HalError::NoDriver),
191 }
192 Ok(())
193 }
194
195 fn transfer_in_place<'w>(&mut self, data: &'w mut [u8]) -> Result<(), Self::Error> {
196 match self {
197 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
198 HalSpi::Linux(i) => i.transfer_in_place(data)?,
199 #[cfg(feature = "hal-cp2130")]
200 HalSpi::Cp2130(i) => i.transfer_in_place(data)?,
201 #[allow(unreachable_patterns)]
202 _ => return Err(HalError::NoDriver),
203 }
204 Ok(())
205 }
206}
207
208impl embedded_hal::spi::ErrorType for HalSpi {
209 type Error = HalError;
210}
211
212#[non_exhaustive]
214pub enum HalInputPin {
215 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
216 Linux(linux_embedded_hal::SysfsPin),
217 #[cfg(feature = "hal-cp2130")]
218 Cp2130(driver_cp2130::InputPin),
219 None,
220}
221
222impl embedded_hal::digital::InputPin for HalInputPin {
223 fn is_high(&mut self) -> Result<bool, Self::Error> {
224 let r = match self {
225 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
226 HalInputPin::Linux(i) => i.is_high()?,
227
228 #[cfg(feature = "hal-cp2130")]
229 HalInputPin::Cp2130(i) => i.is_high()?,
230
231 #[allow(unreachable_patterns)]
232 _ => return Err(HalError::NoPin),
233 };
234
235 Ok(r)
236 }
237
238 fn is_low(&mut self) -> Result<bool, Self::Error> {
239 Ok(!self.is_high()?)
240 }
241}
242
243impl embedded_hal::digital::ErrorType for HalInputPin {
244 type Error = HalError;
245}
246
247#[non_exhaustive]
249pub enum HalOutputPin {
250 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
251 Linux(linux_embedded_hal::SysfsPin),
252 #[cfg(feature = "hal-cp2130")]
253 Cp2130(driver_cp2130::OutputPin),
254 None,
255}
256
257impl embedded_hal::digital::OutputPin for HalOutputPin {
258 fn set_high(&mut self) -> Result<(), Self::Error> {
259 match self {
260 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
261 HalOutputPin::Linux(i) => i.set_high()?,
262
263 #[cfg(feature = "hal-cp2130")]
264 HalOutputPin::Cp2130(i) => i.set_high()?,
265
266 #[allow(unreachable_patterns)]
267 _ => return Err(HalError::NoPin),
268 }
269 Ok(())
270 }
271
272 fn set_low(&mut self) -> Result<(), Self::Error> {
273 match self {
274 #[cfg(all(feature = "hal-linux", target_os = "linux"))]
275 HalOutputPin::Linux(i) => i.set_low()?,
276
277 #[cfg(feature = "hal-cp2130")]
278 HalOutputPin::Cp2130(i) => i.set_low()?,
279
280 #[allow(unreachable_patterns)]
281 _ => return Err(HalError::NoPin),
282 }
283 Ok(())
284 }
285}
286
287impl embedded_hal::digital::ErrorType for HalOutputPin {
288 type Error = HalError;
289}
290
291pub fn load_config<T>(file: &str) -> T
293where
294 T: serde::de::DeserializeOwned,
295{
296 let d = std::fs::read_to_string(file).expect("error reading file");
297 toml::from_str(&d).expect("error parsing toml file")
298}
299
300pub struct HalPins {
302 pub reset: HalOutputPin,
303 pub busy: HalInputPin,
304 pub ready: HalInputPin,
305 pub led0: HalOutputPin,
306 pub led1: HalOutputPin,
307}
308
309pub struct HalDelay;
311
312impl embedded_hal::delay::DelayNs for HalDelay {
313 fn delay_ns(&mut self, ns: u32) {
314 let n = SystemTime::now();
315 let d = Duration::from_nanos(ns as u64);
316 while n.elapsed().unwrap() < d {}
317 }
318}