1use core::fmt::Write as _;
23use core::future::Future;
24
25use embassy_futures::select::select;
26use embassy_time::Instant;
27use embedded_can::{ExtendedId, Frame, Id, StandardId};
28use embedded_io_async::{Read, Write};
29use log::{debug, warn};
30
31const SLCAN_LINE_SZ: usize = 32;
33
34pub const ENCODED_FRAME_MAX: usize = 32;
38
39pub trait CanEncoder {
41 fn encode(&self, frame: &impl Frame, buf: &mut [u8]) -> usize;
43}
44
45pub trait CanDecoder {
47 fn decode(&self, buf: &[u8]) -> Option<CanFrame>;
50}
51
52pub trait BufferedCan: Sync {
58 fn read(&self, buf: &mut [u8]) -> impl Future<Output = usize>;
59 fn write(&self, buf: &[u8]) -> impl Future<Output = ()>;
60 fn check_dropped_frames(&self) -> usize;
61
62 fn reset_protocol(&self);
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct CanFrame {
71 pub id: CanId,
72 pub data: heapless::Vec<u8, 8>,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum CanId {
78 Standard(u16),
79 Extended(u32),
80}
81
82impl From<Id> for CanId {
83 fn from(id: Id) -> Self {
84 match id {
85 Id::Standard(s) => CanId::Standard(s.as_raw()),
86 Id::Extended(e) => CanId::Extended(e.as_raw()),
87 }
88 }
89}
90
91impl From<CanId> for Id {
92 fn from(id: CanId) -> Self {
93 match id {
94 CanId::Standard(v) => Id::Standard(StandardId::new(v).unwrap_or(StandardId::ZERO)),
95 CanId::Extended(v) => Id::Extended(ExtendedId::new(v).unwrap_or(ExtendedId::ZERO)),
96 }
97 }
98}
99
100impl Frame for CanFrame {
101 fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
102 Some(CanFrame {
103 id: id.into().into(),
104 data: heapless::Vec::from_slice(data).ok()?,
105 })
106 }
107
108 fn new_remote(_id: impl Into<Id>, _dlc: usize) -> Option<Self> {
110 None
111 }
112
113 fn is_extended(&self) -> bool {
114 matches!(self.id, CanId::Extended(_))
115 }
116
117 fn is_remote_frame(&self) -> bool {
118 false
119 }
120
121 fn id(&self) -> Id {
122 self.id.into()
123 }
124
125 fn dlc(&self) -> usize {
126 self.data.len()
127 }
128
129 fn data(&self) -> &[u8] {
130 &self.data
131 }
132}
133
134pub struct Slcan;
139
140impl CanEncoder for Slcan {
141 fn encode(&self, frame: &impl Frame, buf: &mut [u8]) -> usize {
142 let mut s = heapless::String::<64>::new();
143 match frame.id() {
144 Id::Standard(id) => {
145 let _ = write!(s, "t{:03X}{:1X}", id.as_raw(), frame.dlc());
146 }
147 Id::Extended(id) => {
148 let _ = write!(s, "T{:08X}{:1X}", id.as_raw(), frame.dlc());
149 }
150 }
151 let data = frame.data();
152 for &b in data {
153 let _ = write!(s, "{b:02X}");
154 }
155 let _ = s.push('\r');
156 let len = s.len().min(buf.len());
157 buf[..len].copy_from_slice(s.as_bytes());
158 len
159 }
160}
161
162impl CanDecoder for Slcan {
163 fn decode(&self, buf: &[u8]) -> Option<CanFrame> {
164 let s = core::str::from_utf8(buf).ok()?;
165 if !s.is_ascii() {
167 return None;
168 }
169 let s = s.trim_end_matches('\r');
170 let bytes = s.as_bytes();
171 match bytes.first()? {
172 b't' => {
173 if s.len() < 5 {
174 return None;
175 }
176 let id = u16::from_str_radix(&s[1..4], 16).ok()?;
177 let dlc = (bytes[4] as char).to_digit(16)? as usize;
178 if dlc > 8 {
179 return None;
180 }
181 let mut data = heapless::Vec::new();
182 let hex_data = &s[5..];
183 if hex_data.len() < dlc * 2 {
184 return None;
185 }
186 for i in 0..dlc {
187 let b = u8::from_str_radix(&hex_data[i * 2..i * 2 + 2], 16).ok()?;
188 let _ = data.push(b);
189 }
190 Some(CanFrame {
191 id: CanId::Standard(id),
192 data,
193 })
194 }
195 b'T' => {
196 if s.len() < 10 {
197 return None;
198 }
199 let id = u32::from_str_radix(&s[1..9], 16).ok()?;
200 let dlc = (bytes[9] as char).to_digit(16)? as usize;
201 if dlc > 8 {
202 return None;
203 }
204 let mut data = heapless::Vec::new();
205 let hex_data = &s[10..];
206 if hex_data.len() < dlc * 2 {
207 return None;
208 }
209 for i in 0..dlc {
210 let b = u8::from_str_radix(&hex_data[i * 2..i * 2 + 2], 16).ok()?;
211 let _ = data.push(b);
212 }
213 Some(CanFrame {
214 id: CanId::Extended(id),
215 data,
216 })
217 }
218 _ => None,
219 }
220 }
221}
222
223pub struct Gvret;
229
230impl CanEncoder for Gvret {
231 fn encode(&self, frame: &impl Frame, buf: &mut [u8]) -> usize {
232 let data = frame.data();
233 let n = 12 + data.len();
234 let Ok(dlc) = u8::try_from(data.len()) else {
235 return 0;
236 };
237 if dlc > 8 || buf.len() < n {
238 return 0;
239 }
240 let raw_id = match frame.id() {
241 Id::Standard(id) => u32::from(id.as_raw()),
242 Id::Extended(id) => id.as_raw() | 0x8000_0000,
243 };
244 buf[0] = 0xF1;
245 buf[1] = gvret::BUILD_CAN_FRAME;
246 buf[2..6].copy_from_slice(×tamp_us());
247 buf[6..10].copy_from_slice(&raw_id.to_le_bytes());
248 buf[10] = dlc; buf[11..11 + data.len()].copy_from_slice(data);
250 buf[11 + data.len()] = 0; n
252 }
253}
254
255fn timestamp_us() -> [u8; 4] {
258 #[allow(clippy::cast_possible_truncation)]
259 let t = Instant::now().as_micros() as u32;
260 t.to_le_bytes()
261}
262
263pub fn encode_frame(frame: &impl Frame, binary: bool, buf: &mut [u8]) -> usize {
266 if binary {
267 Gvret.encode(frame, buf)
268 } else {
269 Slcan.encode(frame, buf)
270 }
271}
272
273mod gvret {
275 pub const BUILD_CAN_FRAME: u8 = 0x00;
276 pub const TIME_SYNC: u8 = 0x01;
277 pub const GET_DIG_INPUTS: u8 = 0x02;
278 pub const GET_ANALOG_INPUTS: u8 = 0x03;
279 pub const SET_DIG_OUT: u8 = 0x04;
280 pub const SETUP_CANBUS: u8 = 0x05;
281 pub const GET_CANBUS_PARAMS: u8 = 0x06;
282 pub const GET_DEVICE_INFO: u8 = 0x07;
283 pub const SET_SINGLEWIRE_MODE: u8 = 0x08;
284 pub const KEEPALIVE: u8 = 0x09;
285 pub const SET_SYSTYPE: u8 = 0x0A;
286 pub const ECHO_CAN_FRAME: u8 = 0x0B;
287 pub const GET_NUM_BUSES: u8 = 0x0C;
288 pub const GET_EXT_BUSES: u8 = 0x0D;
289 pub const SET_EXT_BUSES: u8 = 0x0E;
290}
291
292pub enum CanAction {
294 Transmit(CanFrame),
296 Reply(heapless::Vec<u8, ENCODED_FRAME_MAX>),
298 EnableBinary,
300}
301
302#[derive(Clone, Copy)]
304enum GvretState {
305 Command,
307 Frame {
310 echo: bool,
311 buf: [u8; 15],
312 got: usize,
313 },
314 Consume(usize),
316}
317
318pub struct CanParser {
325 bitrate: u32,
327 line: heapless::Vec<u8, SLCAN_LINE_SZ>,
328 gvret: Option<GvretState>,
329}
330
331impl CanParser {
332 #[must_use]
333 pub fn new(bitrate: u32) -> Self {
334 CanParser {
335 bitrate,
336 line: heapless::Vec::new(),
337 gvret: None,
338 }
339 }
340
341 pub fn reset(&mut self) {
343 self.line.clear();
344 self.gvret = None;
345 }
346
347 pub fn feed(&mut self, byte: u8) -> Option<CanAction> {
349 if let Some(state) = self.gvret.take() {
350 return self.feed_gvret(state, byte);
351 }
352 match byte {
353 0xF1 => {
354 self.line.clear();
355 self.gvret = Some(GvretState::Command);
356 None
357 }
358 0xE7 => {
359 self.line.clear();
360 Some(CanAction::EnableBinary)
361 }
362 b'\r' | b'\n' => {
363 let frame = Slcan.decode(&self.line);
364 self.line.clear();
365 frame.map(CanAction::Transmit)
366 }
367 b => {
368 if self.line.push(b).is_err() {
371 self.line.clear();
372 }
373 None
374 }
375 }
376 }
377
378 fn feed_gvret(&mut self, state: GvretState, byte: u8) -> Option<CanAction> {
379 match state {
380 GvretState::Command => self.gvret_command(byte),
381 GvretState::Consume(n) => {
382 if n > 1 {
383 self.gvret = Some(GvretState::Consume(n - 1));
384 }
385 None
386 }
387 GvretState::Frame { echo, mut buf, got } => {
388 buf[got] = byte;
389 let got = got + 1;
390 if got >= 6 {
391 let dlc = usize::from(buf[5] & 0x0F).min(8);
392 if got == 6 + dlc + 1 {
395 return Self::finish_frame(echo, &buf, dlc);
396 }
397 }
398 self.gvret = Some(GvretState::Frame { echo, buf, got });
399 None
400 }
401 }
402 }
403
404 fn gvret_command(&mut self, cmd: u8) -> Option<CanAction> {
405 match cmd {
406 gvret::BUILD_CAN_FRAME | gvret::ECHO_CAN_FRAME => {
407 self.gvret = Some(GvretState::Frame {
408 echo: cmd == gvret::ECHO_CAN_FRAME,
409 buf: [0u8; 15],
410 got: 0,
411 });
412 None
413 }
414 gvret::TIME_SYNC => {
415 let mut b = [0u8; 6];
416 b[0] = 0xF1;
417 b[1] = gvret::TIME_SYNC;
418 b[2..6].copy_from_slice(×tamp_us());
419 reply(&b)
420 }
421 gvret::GET_DIG_INPUTS => reply(&[0xF1, gvret::GET_DIG_INPUTS, 0, 0]),
423 gvret::GET_ANALOG_INPUTS => {
424 reply(&[0xF1, gvret::GET_ANALOG_INPUTS, 0, 0, 0, 0, 0, 0, 0, 0, 0])
425 }
426 gvret::GET_CANBUS_PARAMS => {
427 let mut b = [0u8; 12];
429 b[0] = 0xF1;
430 b[1] = gvret::GET_CANBUS_PARAMS;
431 b[2] = 1;
432 b[3..7].copy_from_slice(&self.bitrate.to_le_bytes());
433 b[8..12].copy_from_slice(&self.bitrate.to_le_bytes());
434 reply(&b)
435 }
436 gvret::GET_DEVICE_INFO => {
437 reply(&[0xF1, gvret::GET_DEVICE_INFO, 0x6A, 0x02, 0, 0, 0, 0])
439 }
440 gvret::KEEPALIVE => reply(&[0xF1, gvret::KEEPALIVE, 0xDE, 0xAD]),
441 gvret::GET_NUM_BUSES => reply(&[0xF1, gvret::GET_NUM_BUSES, 1]),
442 gvret::GET_EXT_BUSES => {
443 let mut b = [0u8; 17];
445 b[0] = 0xF1;
446 b[1] = gvret::GET_EXT_BUSES;
447 reply(&b)
448 }
449 gvret::SET_DIG_OUT | gvret::SET_SINGLEWIRE_MODE | gvret::SET_SYSTYPE => {
452 self.gvret = Some(GvretState::Consume(1));
453 None
454 }
455 gvret::SETUP_CANBUS => {
456 self.gvret = Some(GvretState::Consume(8));
457 None
458 }
459 gvret::SET_EXT_BUSES => {
460 self.gvret = Some(GvretState::Consume(10));
461 None
462 }
463 _ => None,
464 }
465 }
466
467 fn finish_frame(echo: bool, buf: &[u8; 15], dlc: usize) -> Option<CanAction> {
468 let raw_id = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
469 let id = if raw_id & 0x8000_0000 != 0 {
470 CanId::Extended(raw_id & 0x1FFF_FFFF)
471 } else {
472 CanId::Standard((raw_id & 0x7FF) as u16)
473 };
474 let frame = CanFrame {
475 id,
476 data: heapless::Vec::from_slice(&buf[6..6 + dlc]).ok()?,
477 };
478 if echo {
479 let mut b = [0u8; ENCODED_FRAME_MAX];
480 let n = Gvret.encode(&frame, &mut b);
481 reply(&b[..n])
482 } else {
483 Some(CanAction::Transmit(frame))
484 }
485 }
486}
487
488fn reply(bytes: &[u8]) -> Option<CanAction> {
489 heapless::Vec::from_slice(bytes).ok().map(CanAction::Reply)
490}
491
492pub async fn can_bridge<C: BufferedCan + ?Sized>(
498 chan_read: impl Read<Error = sunset::Error>,
499 chan_write: impl Write<Error = sunset::Error>,
500 can: &C,
501) -> Result<(), sunset::Error> {
502 debug!("Starting CAN <--> SSH bridge");
503 can.reset_protocol();
504 select(can_to_ssh(can, chan_write), ssh_to_can(chan_read, can)).await;
505 debug!("Stopping CAN <--> SSH bridge");
506 Ok(())
507}
508
509async fn can_to_ssh<C: BufferedCan + ?Sized>(
510 can_buf: &C,
511 mut chan_write: impl Write<Error = sunset::Error>,
512) -> Result<(), sunset::Error> {
513 let mut ssh_tx_buf = [0u8; 128];
514 loop {
515 let dropped = can_buf.check_dropped_frames();
516 if dropped > 0 {
517 warn!("CAN RX dropped {dropped} frames");
518 }
519 let n = can_buf.read(&mut ssh_tx_buf).await;
520 chan_write.write_all(&ssh_tx_buf[..n]).await?;
521 }
522}
523
524async fn ssh_to_can<C: BufferedCan + ?Sized>(
525 mut chan_read: impl Read<Error = sunset::Error>,
526 can_buf: &C,
527) -> Result<(), sunset::Error> {
528 let mut can_tx_buf = [0u8; 64];
529 loop {
530 let n = chan_read.read(&mut can_tx_buf).await?;
531 if n == 0 {
532 return Err(sunset::Error::ChannelEOF);
533 }
534 can_buf.write(&can_tx_buf[..n]).await;
535 }
536}