1#![deny(clippy::all)]
2
3use bitflags::bitflags;
45pub use embedded_can::{ExtendedId, Id, StandardId};
46use libc::{
47 bind, c_int, c_short, c_void, close, fcntl, read, setsockopt, sockaddr, socket, write, F_GETFL,
48 F_SETFL, O_NONBLOCK, SOCK_DGRAM,
49};
50use nix::net::if_::if_nametoindex;
51use std::convert::TryFrom;
52use std::convert::TryInto;
53use std::io;
54use std::mem::size_of;
55use std::num::TryFromIntError;
56use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
57use std::time::Duration;
58use thiserror::Error;
59
60pub const AF_CAN: c_short = 29;
62
63pub const PF_CAN: c_int = 29;
65
66pub const CAN_ISOTP: c_int = 6;
68
69pub const SOL_CAN_BASE: c_int = 100;
71
72pub const SOL_CAN_ISOTP: c_int = SOL_CAN_BASE + CAN_ISOTP;
74
75pub const CAN_ISOTP_OPTS: c_int = 1;
77
78pub const CAN_ISOTP_RECV_FC: c_int = 2;
80
81pub const CAN_ISOTP_TX_STMIN: c_int = 3;
85
86pub const CAN_ISOTP_RX_STMIN: c_int = 4;
90
91pub const CAN_ISOTP_LL_OPTS: c_int = 5;
93
94pub const CAN_MAX_DLEN: u8 = 8;
96
97const RECV_BUFFER_SIZE: usize = 4096;
99
100const SIZE_OF_CAN_FRAME: u8 = 16;
103
104const FLOW_CONTROL_OPTIONS_SIZE: usize = size_of::<FlowControlOptions>();
105
106const ISOTP_OPTIONS_SIZE: usize = size_of::<IsoTpOptions>();
107
108const LINK_LAYER_OPTIONS_SIZE: usize = size_of::<LinkLayerOptions>();
109
110const CAN_ISOTP_DEFAULT_RECV_BS: u8 = 0;
111
112const CAN_ISOTP_DEFAULT_RECV_STMIN: u8 = 0x00;
113
114const CAN_ISOTP_DEFAULT_RECV_WFTMAX: u8 = 0;
115
116bitflags! {
117 pub struct IsoTpBehaviour: u32 {
118 const CAN_ISOTP_LISTEN_MODE = 0x001;
120 const CAN_ISOTP_EXTEND_ADDR = 0x002;
122 const CAN_ISOTP_TX_PADDING = 0x004;
124 const CAN_ISOTP_RX_PADDING = 0x008;
126 const CAN_ISOTP_CHK_PAD_LEN = 0x010;
128 const CAN_ISOTP_CHK_PAD_DATA = 0x020;
130 const CAN_ISOTP_HALF_DUPLEX = 0x040;
132 const CAN_ISOTP_FORCE_TXSTMIN = 0x080;
134 const CAN_ISOTP_FORCE_RXSTMIN = 0x100;
136 const CAN_ISOTP_RX_EXT_ADDR = 0x200;
138 }
139}
140
141pub const EFF_FLAG: u32 = 0x8000_0000;
143
144pub const RTR_FLAG: u32 = 0x4000_0000;
146
147pub const ERR_FLAG: u32 = 0x2000_0000;
149
150pub const SFF_MASK: u32 = 0x0000_07ff;
152
153pub const EFF_MASK: u32 = 0x1fff_ffff;
155
156pub const ERR_MASK: u32 = 0x1fff_ffff;
158
159pub const ERR_MASK_ALL: u32 = ERR_MASK;
161
162pub const ERR_MASK_NONE: u32 = 0;
164
165#[derive(Debug)]
166#[repr(C)]
167struct CanAddr {
168 _af_can: c_short,
169 if_index: c_int, rx_id: u32,
172 tx_id: u32,
174 _pgn: u32,
175 _addr: u8,
176}
177
178#[repr(C)]
180pub struct IsoTpOptions {
181 flags: u32,
183 frame_txtime: u32,
186 ext_address: u8,
188 txpad_content: u8,
190 rxpad_content: u8,
192 rx_ext_address: u8,
194}
195
196impl IsoTpOptions {
197 pub fn new(
198 flags: IsoTpBehaviour,
199 frame_txtime: Duration,
200 ext_address: u8,
201 txpad_content: u8,
202 rxpad_content: u8,
203 rx_ext_address: u8,
204 ) -> Result<Self, TryFromIntError> {
205 let flags = flags.bits();
206 let frame_txtime = u32::try_from(frame_txtime.as_nanos())?;
207
208 Ok(Self {
209 flags,
210 frame_txtime,
211 ext_address,
212 txpad_content,
213 rxpad_content,
214 rx_ext_address,
215 })
216 }
217
218 pub fn get_flags(&self) -> Option<IsoTpBehaviour> {
220 IsoTpBehaviour::from_bits(self.flags)
221 }
222
223 pub fn set_flags(&mut self, flags: IsoTpBehaviour) {
225 self.flags = flags.bits();
226 }
227
228 pub fn get_frame_txtime(&self) -> Duration {
230 Duration::from_nanos(self.frame_txtime.into())
231 }
232
233 pub fn set_frame_txtime(&mut self, frame_txtime: Duration) -> Result<(), TryFromIntError> {
235 self.frame_txtime = u32::try_from(frame_txtime.as_nanos())?;
236 Ok(())
237 }
238
239 pub fn get_ext_address(&self) -> u8 {
241 self.ext_address
242 }
243
244 pub fn set_ext_address(&mut self, ext_address: u8) {
246 self.ext_address = ext_address;
247 }
248
249 pub fn get_txpad_content(&self) -> u8 {
251 self.txpad_content
252 }
253
254 pub fn set_txpad_content(&mut self, txpad_content: u8) {
256 self.txpad_content = txpad_content;
257 }
258
259 pub fn get_rxpad_content(&self) -> u8 {
261 self.rxpad_content
262 }
263
264 pub fn set_rxpad_content(&mut self, rxpad_content: u8) {
266 self.rxpad_content = rxpad_content;
267 }
268
269 pub fn get_rx_ext_address(&self) -> u8 {
271 self.rx_ext_address
272 }
273
274 pub fn set_rx_ext_address(&mut self, rx_ext_address: u8) {
276 self.rx_ext_address = rx_ext_address;
277 }
278}
279
280impl Default for IsoTpOptions {
281 fn default() -> Self {
282 Self {
284 flags: 0x00,
285 frame_txtime: 0x00,
286 ext_address: 0x00,
287 txpad_content: 0xCC,
288 rxpad_content: 0xCC,
289 rx_ext_address: 0x00,
290 }
291 }
292}
293
294#[repr(C)]
296pub struct FlowControlOptions {
297 bs: u8,
300 stmin: u8,
307 wftmax: u8,
310}
311
312impl Default for FlowControlOptions {
313 fn default() -> Self {
314 Self {
315 bs: CAN_ISOTP_DEFAULT_RECV_BS,
316 stmin: CAN_ISOTP_DEFAULT_RECV_STMIN,
317 wftmax: CAN_ISOTP_DEFAULT_RECV_WFTMAX,
318 }
319 }
320}
321
322impl FlowControlOptions {
323 pub fn new(bs: u8, stmin: u8, wftmax: u8) -> Self {
332 Self { bs, stmin, wftmax }
333 }
334}
335
336bitflags! {
337 pub struct TxFlags: u8 {
338 const CANFD_BRS = 0x01;
340 const CANFD_ESI = 0x02;
342 }
343}
344
345#[repr(C)]
347pub struct LinkLayerOptions {
348 mtu: u8,
352 tx_dl: u8,
357 tx_flags: u8,
362}
363
364impl LinkLayerOptions {
365 pub fn new(mtu: u8, tx_dl: u8, tx_flags: TxFlags) -> Self {
366 let tx_flags = tx_flags.bits();
367 Self {
368 mtu,
369 tx_dl,
370 tx_flags,
371 }
372 }
373}
374
375impl Default for LinkLayerOptions {
376 fn default() -> Self {
377 Self {
378 mtu: SIZE_OF_CAN_FRAME,
380 tx_dl: CAN_MAX_DLEN,
382 tx_flags: 0x00,
384 }
385 }
386}
387
388#[derive(Error, Debug)]
389pub enum Error {
391 #[error("Failed to find can device: {source:?}")]
393 Lookup {
394 #[from]
395 source: nix::Error,
396 },
397
398 #[error("IO error: {source:?}")]
400 Io {
401 #[from]
402 source: io::Error,
403 },
404}
405pub struct IsoTpSocket {
410 fd: c_int,
411 recv_buffer: [u8; RECV_BUFFER_SIZE],
412}
413
414impl IsoTpSocket {
415 pub fn open(ifname: &str, rx_id: impl Into<Id>, tx_id: impl Into<Id>) -> Result<Self, Error> {
420 Self::open_with_opts(
421 ifname,
422 rx_id,
423 tx_id,
424 Some(IsoTpOptions::default()),
425 Some(FlowControlOptions::default()),
426 Some(LinkLayerOptions::default()),
427 )
428 }
429
430 pub fn open_with_opts(
435 ifname: &str,
436 rx_id: impl Into<Id>,
437 tx_id: impl Into<Id>,
438 isotp_options: Option<IsoTpOptions>,
439 rx_flow_control_options: Option<FlowControlOptions>,
440 link_layer_options: Option<LinkLayerOptions>,
441 ) -> Result<Self, Error> {
442 let if_index = if_nametoindex(ifname)?;
443 Self::open_if_with_opts(
444 if_index.try_into().unwrap(),
445 rx_id,
446 tx_id,
447 isotp_options,
448 rx_flow_control_options,
449 link_layer_options,
450 )
451 }
452
453 pub fn open_if(
457 if_index: c_int,
458 rx_id: impl Into<Id>,
459 tx_id: impl Into<Id>,
460 ) -> Result<Self, Error> {
461 Self::open_if_with_opts(
462 if_index,
463 rx_id,
464 tx_id,
465 Some(IsoTpOptions::default()),
466 Some(FlowControlOptions::default()),
467 Some(LinkLayerOptions::default()),
468 )
469 }
470
471 pub fn open_if_with_opts(
475 if_index: c_int,
476 rx_id: impl Into<Id>,
477 tx_id: impl Into<Id>,
478 isotp_options: Option<IsoTpOptions>,
479 rx_flow_control_options: Option<FlowControlOptions>,
480 link_layer_options: Option<LinkLayerOptions>,
481 ) -> Result<Self, Error> {
482 let rx_id = match rx_id.into() {
483 Id::Standard(standard_id) => standard_id.as_raw() as u32,
484 Id::Extended(extended_id) => extended_id.as_raw() | EFF_FLAG,
485 };
486 let tx_id = match tx_id.into() {
487 Id::Standard(standard_id) => standard_id.as_raw() as u32,
488 Id::Extended(extended_id) => extended_id.as_raw() | EFF_FLAG,
489 };
490 let addr = CanAddr {
491 _af_can: AF_CAN,
492 if_index,
493 rx_id,
494 tx_id,
495 _pgn: 0,
496 _addr: 0,
497 };
498
499 let sock_fd;
501 unsafe {
502 sock_fd = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP);
503 }
504
505 if sock_fd == -1 {
506 return Err(Error::from(io::Error::last_os_error()));
507 }
508
509 if let Some(isotp_options) = isotp_options {
511 let isotp_options_ptr: *const c_void = &isotp_options as *const _ as *const c_void;
512 let err = unsafe {
513 setsockopt(
514 sock_fd,
515 SOL_CAN_ISOTP,
516 CAN_ISOTP_OPTS,
517 isotp_options_ptr,
518 ISOTP_OPTIONS_SIZE.try_into().unwrap(),
519 )
520 };
521 if err == -1 {
522 return Err(Error::from(io::Error::last_os_error()));
523 }
524 }
525
526 if let Some(rx_flow_control_options) = rx_flow_control_options {
528 let rx_flow_control_options_ptr: *const c_void =
529 &rx_flow_control_options as *const _ as *const c_void;
530 let err = unsafe {
531 setsockopt(
532 sock_fd,
533 SOL_CAN_ISOTP,
534 CAN_ISOTP_RECV_FC,
535 rx_flow_control_options_ptr,
536 FLOW_CONTROL_OPTIONS_SIZE.try_into().unwrap(),
537 )
538 };
539 if err == -1 {
540 return Err(Error::from(io::Error::last_os_error()));
541 }
542 }
543
544 if let Some(link_layer_options) = link_layer_options {
546 let link_layer_options_ptr: *const c_void =
547 &link_layer_options as *const _ as *const c_void;
548 let err = unsafe {
549 setsockopt(
550 sock_fd,
551 SOL_CAN_ISOTP,
552 CAN_ISOTP_LL_OPTS,
553 link_layer_options_ptr,
554 LINK_LAYER_OPTIONS_SIZE.try_into().unwrap(),
555 )
556 };
557 if err == -1 {
558 return Err(Error::from(io::Error::last_os_error()));
559 }
560 }
561
562 let bind_rv;
564 unsafe {
565 let sockaddr_ptr = &addr as *const CanAddr;
566 bind_rv = bind(
567 sock_fd,
568 sockaddr_ptr as *const sockaddr,
569 size_of::<CanAddr>().try_into().unwrap(),
570 );
571 }
572
573 if bind_rv == -1 {
575 let e = io::Error::last_os_error();
576 unsafe {
577 close(sock_fd);
578 }
579 return Err(Error::from(e));
580 }
581
582 Ok(Self {
583 fd: sock_fd,
584 recv_buffer: [0x00; RECV_BUFFER_SIZE],
585 })
586 }
587
588 fn close(&mut self) -> io::Result<()> {
589 unsafe {
590 let rv = close(self.fd);
591 if rv != -1 {
592 return Err(io::Error::last_os_error());
593 }
594 }
595 Ok(())
596 }
597
598 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
600 let oldfl = unsafe { fcntl(self.fd, F_GETFL) };
602
603 if oldfl == -1 {
604 return Err(io::Error::last_os_error());
605 }
606
607 let newfl = if nonblocking {
608 oldfl | O_NONBLOCK
609 } else {
610 oldfl & !O_NONBLOCK
611 };
612
613 let rv = unsafe { fcntl(self.fd, F_SETFL, newfl) };
614
615 if rv != 0 {
616 return Err(io::Error::last_os_error());
617 }
618 Ok(())
619 }
620
621 pub fn read(&mut self) -> io::Result<&[u8]> {
623 let buffer_ptr = &mut self.recv_buffer as *mut _ as *mut c_void;
624
625 let read_rv = unsafe { read(self.fd, buffer_ptr, RECV_BUFFER_SIZE) };
626
627 if read_rv < 0 {
628 return Err(io::Error::last_os_error());
629 }
630
631 Ok(&self.recv_buffer[0..read_rv.try_into().unwrap()])
632 }
633
634 pub fn write(&self, buffer: &[u8]) -> io::Result<()> {
636 let write_rv = unsafe {
637 let buffer_ptr = buffer as *const _ as *const c_void;
638 write(self.fd, buffer_ptr, buffer.len())
639 };
640
641 if write_rv != buffer.len().try_into().unwrap() {
642 return Err(io::Error::last_os_error());
643 }
644
645 Ok(())
646 }
647}
648
649impl AsRawFd for IsoTpSocket {
650 fn as_raw_fd(&self) -> RawFd {
651 self.fd
652 }
653}
654
655impl FromRawFd for IsoTpSocket {
656 unsafe fn from_raw_fd(fd: RawFd) -> Self {
657 Self {
658 fd,
659 recv_buffer: [0x00; RECV_BUFFER_SIZE],
660 }
661 }
662}
663
664impl IntoRawFd for IsoTpSocket {
665 fn into_raw_fd(self) -> RawFd {
666 self.fd
667 }
668}
669
670impl Drop for IsoTpSocket {
671 fn drop(&mut self) {
672 self.close().ok(); }
674}