tokio_socketcan_isotp/
lib.rs

1#![deny(clippy::all)]
2#![allow(dead_code)]
3//! # tokio-socketcan-isotp
4//!
5//! This library creates asynchronous wrapper around [socketcan-isotp](https://github.com/marcelbuesing/socketcan-isotp)
6//!
7//! Currently not dependent on the original, but rather on the modified of the socketcan-isotp libary, which adds read_to_vec method, in order to prevent mutable borrowing of the socket, when reading.
8//!
9//! You may experience busy waiting, or soft locks when two consecutive locks. This is due to the error in Linux Kernel, which is now [solved](https://lore.kernel.org/linux-can/20230818114345.142983-1-lukas.magel@posteo.net/), but the maintainer of your Linux kernel might not have shipped it yet. I tested it on the mainline Kernel 6.6.5 and everything worked correctly. If you experience mentioned errors and cannot upgrade to newer kernel, refer to the src/lib.rs file to the poll method of the IsoTpWriteFuture, where a edit is suggested that might help with your situation.
10//!
11//! The API is provided by the struct [IsoTpSocket]
12//!
13//! Example of basic echoing server on vcan0:
14//!
15//! ```rust
16//! use tokio_socketcan_isotp::{IsoTpSocket, StandardId, Error};
17//!
18//! #[tokio::main]
19//! async fn main() -> Result<(), Error> {
20//!    let mut socket = IsoTpSocket::open(
21//!        "vcan0",
22//!        StandardId::new(0x123).expect("Invalid src id"),
23//!        StandardId::new(0x321).expect("Invalid src id")
24//!            )?;
25//!            
26//!     while let Ok(packet) = socket.read_packet()?.await {
27//!         println!("{:?}", packet);
28//!         let rx = socket.write_packet(packet)?.await;
29//!     }
30//! }
31//! ```
32//!
33//! To setup vcan0 run following commands:
34//!
35//! ```bash
36//! sudo modprobe can
37//! sudo ip link add dev vcan0 type vcan
38//! sudo ip link set up vcan0
39//! ```
40
41mod socketcan_isotp;
42
43pub use crate::socketcan_isotp::{
44    Error, ExtendedId, FlowControlOptions, Id, IsoTpBehaviour, IsoTpOptions, LinkLayerOptions,
45    StandardId, TxFlags, AF_CAN, CAN_ISOTP, CAN_ISOTP_LL_OPTS, CAN_ISOTP_OPTS, CAN_ISOTP_RECV_FC,
46    CAN_ISOTP_RX_STMIN, CAN_ISOTP_TX_STMIN, CAN_MAX_DLEN, EFF_FLAG, EFF_MASK, ERR_FLAG, ERR_MASK,
47    ERR_MASK_ALL, ERR_MASK_NONE, PF_CAN, RECV_BUFFER_SIZE, RTR_FLAG, SFF_MASK, SOL_CAN_BASE,
48    SOL_CAN_ISOTP,
49};
50use futures::prelude::*;
51use futures::ready;
52use std::io;
53use std::os::raw::c_int;
54use std::pin::Pin;
55use std::task::{Context, Poll};
56use tokio::io::unix::AsyncFd;
57
58/// Future for writing data to IsoTpSocket
59pub struct IsoTpWriteFuture<'a> {
60    socket: &'a IsoTpSocket,
61    packet: &'a [u8],
62}
63
64impl Future for IsoTpWriteFuture<'_> {
65    type Output = io::Result<()>;
66
67    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
68        loop {
69            let mut guard = ready!(self.socket.0.poll_write_ready(cx))?;
70            match self.socket.0.get_ref().write(self.packet) {
71                Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
72                    guard.clear_ready(); // Comment this line if you are on older Kernel and the communication soft-locks
73                    continue;
74                }
75                Ok(_) => return Poll::Ready(Ok(())),
76                Err(err) => return Poll::Ready(Err(err)),
77            }
78        }
79    }
80}
81
82/// Future for reading data from IsoTpSocket
83pub struct IsoTpReadFuture<'a> {
84    socket: &'a IsoTpSocket,
85}
86
87impl Future for IsoTpReadFuture<'_> {
88    type Output = io::Result<Vec<u8>>;
89
90    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
91        loop {
92            let mut ready_guard = ready!(self.socket.0.poll_read_ready(cx))?;
93            match ready_guard.try_io(|inner| inner.get_ref().read_to_vec()) {
94                Ok(result) => return Poll::Ready(result),
95                Err(_would_block) => continue,
96            }
97        }
98    }
99}
100
101/// An asynchronous I/O wrapped socketcan_isotp::IsoTpSocket
102/// For reading and writting to the socket use [IsoTpSocket::read_packet] and [IsoTpSocket::write_packet] respectively.
103pub struct IsoTpSocket(AsyncFd<socketcan_isotp::IsoTpSocket>);
104#[allow(dead_code)]
105impl IsoTpSocket {
106    /// Open a named CAN device such as "vcan0"
107    pub fn open(
108        ifname: &str,
109        src: impl Into<Id>,
110        dst: impl Into<Id>,
111    ) -> Result<IsoTpSocket, socketcan_isotp::Error> {
112        let sock = socketcan_isotp::IsoTpSocket::open(ifname, src, dst)?;
113        sock.set_nonblocking(true)?;
114        Ok(IsoTpSocket(AsyncFd::new(sock)?))
115    }
116
117    pub fn open_with_opts(
118        ifname: &str,
119        src: impl Into<Id>,
120        dst: impl Into<Id>,
121        isotp_options: Option<IsoTpOptions>,
122        rx_flow_control_options: Option<FlowControlOptions>,
123        link_layer_options: Option<LinkLayerOptions>,
124    ) -> Result<IsoTpSocket, socketcan_isotp::Error> {
125        let sock = socketcan_isotp::IsoTpSocket::open_with_opts(
126            ifname,
127            src,
128            dst,
129            isotp_options,
130            rx_flow_control_options,
131            link_layer_options,
132        )?;
133        sock.set_nonblocking(true)?;
134        Ok(IsoTpSocket(AsyncFd::new(sock)?))
135    }
136
137    /// Open by kernel interface number
138    pub fn open_if(
139        if_index: c_int,
140        src: impl Into<Id>,
141        dst: impl Into<Id>,
142    ) -> Result<IsoTpSocket, socketcan_isotp::Error> {
143        let sock = socketcan_isotp::IsoTpSocket::open_if(if_index, src, dst)?;
144        sock.set_nonblocking(true)?;
145        Ok(IsoTpSocket(AsyncFd::new(sock)?))
146    }
147
148    pub fn open_if_with_opts(
149        if_index: c_int,
150        src: impl Into<Id>,
151        dst: impl Into<Id>,
152        isotp_options: Option<IsoTpOptions>,
153        rx_flow_control_options: Option<FlowControlOptions>,
154        link_layer_options: Option<LinkLayerOptions>,
155    ) -> Result<IsoTpSocket, socketcan_isotp::Error> {
156        let sock = socketcan_isotp::IsoTpSocket::open_if_with_opts(
157            if_index,
158            src,
159            dst,
160            isotp_options,
161            rx_flow_control_options,
162            link_layer_options,
163        )?;
164        sock.set_nonblocking(true)?;
165        Ok(IsoTpSocket(AsyncFd::new(sock)?))
166    }
167
168    pub fn write_packet<'a>(&'a self, packet: &'a [u8]) -> IsoTpWriteFuture {
169        IsoTpWriteFuture {
170            socket: self,
171            packet,
172        }
173    }
174
175    pub fn read_packet(&self) -> IsoTpReadFuture {
176        IsoTpReadFuture { socket: self }
177    }
178}