host_can/adapter/pcan/
mod.rs

1//! PCAN adapter module
2
3use std::ffi::c_int;
4use std::time::{Duration, Instant};
5use std::u32;
6
7use crate::adapter::Adapter;
8use crate::frame::{CanFrame, Frame};
9use crate::id;
10
11mod bind;
12use bind::{TPCANMsg, TPCANTimestamp, libPCBUSB};
13
14use super::{AdapterBaud, AdapterError};
15
16type PcanChannel = u16;
17type PcanBaud = u16;
18
19pub struct PcanAdapter {
20    lib: libPCBUSB,
21    channel: PcanChannel,
22    fd: c_int,
23}
24
25fn try_as_channel(name: &str) -> Result<PcanChannel, AdapterError> {
26    Ok(match name {
27        "" | "1" => bind::PCAN_USBBUS1 as PcanChannel,
28        "2" => bind::PCAN_USBBUS2 as PcanChannel,
29        "3" => bind::PCAN_USBBUS3 as PcanChannel,
30        "4" => bind::PCAN_USBBUS4 as PcanChannel,
31        "5" => bind::PCAN_USBBUS5 as PcanChannel,
32        "6" => bind::PCAN_USBBUS6 as PcanChannel,
33        "7" => bind::PCAN_USBBUS7 as PcanChannel,
34        "8" => bind::PCAN_USBBUS8 as PcanChannel,
35        _ => return Err(AdapterError::UnknownDevice),
36    })
37}
38
39fn try_as_baud(baud: u32) -> Result<PcanBaud, AdapterError> {
40    Ok(match baud {
41        10_000 => bind::PCAN_BAUD_10K as PcanBaud,
42        20_000 => bind::PCAN_BAUD_20K as PcanBaud,
43        50_000 => bind::PCAN_BAUD_50K as PcanBaud,
44        100_000 => bind::PCAN_BAUD_100K as PcanBaud,
45        125_000 => bind::PCAN_BAUD_125K as PcanBaud,
46        250_000 => bind::PCAN_BAUD_250K as PcanBaud,
47        500_000 => bind::PCAN_BAUD_500K as PcanBaud,
48        800_000 => bind::PCAN_BAUD_800K as PcanBaud,
49        1_000_000 => bind::PCAN_BAUD_1M as PcanBaud,
50        _ => return Err(AdapterError::UnsupportedBaud),
51    })
52}
53
54impl Default for TPCANMsg {
55    fn default() -> Self {
56        Self {
57            ID: 0,
58            MSGTYPE: 0,
59            LEN: 0,
60            DATA: [0; 8],
61        }
62    }
63}
64
65impl Default for TPCANTimestamp {
66    fn default() -> Self {
67        Self {
68            millis: 0,
69            millis_overflow: 0,
70            micros: 0,
71        }
72    }
73}
74
75impl PcanAdapter {
76    /// Create an instance of a PCAN adapter for the given channel and
77    /// baud-rate.  Note that the "channel" is numerical and corresponds
78    /// to `PCAN_USBBUS<#>` which is the only supported device-type.
79    pub fn new(
80        name: &str,
81        baud: AdapterBaud,
82    ) -> Result<Self, Box<dyn std::error::Error>> {
83        #[cfg(target_os = "macos")]
84        let lib = unsafe { libPCBUSB::new("libPCBUSB.dylib") }?;
85        #[cfg(target_os = "windows")]
86        let lib = unsafe { libPCBUSB::new("PCANBasic.dll") }?;
87
88        let channel = try_as_channel(name)?;
89
90        let status = unsafe {
91            lib.CAN_Initialize(
92                channel,
93                try_as_baud(baud)?,
94                bind::PCAN_USB as u8,
95                0,
96                0,
97            )
98        };
99        match status {
100            0 => {
101                let mut fd: c_int = 0;
102                let info_ptr: *mut c_int = &mut fd;
103                match unsafe {
104                    lib.CAN_GetValue(
105                        channel,
106                        bind::PCAN_RECEIVE_EVENT as u8,
107                        info_ptr as *mut ::std::os::raw::c_void,
108                        4,
109                    )
110                } {
111                    bind::PCAN_ERROR_OK => Ok(Self { lib, fd, channel }),
112                    _ => Err(Box::new(AdapterError::OpenFailed)),
113                }
114            }
115            _ => Err(Box::new(AdapterError::OpenFailed)),
116        }
117    }
118}
119
120impl Drop for PcanAdapter {
121    fn drop(&mut self) {
122        unsafe { self.lib.CAN_Uninitialize(self.channel.into()) };
123    }
124}
125
126impl Adapter for PcanAdapter {
127    fn send(&self, frame: &CanFrame) -> Result<(), Box<AdapterError>> {
128        let mut data: [u8; 8] = [0u8; 8];
129        data[..frame.dlc()].copy_from_slice(frame.data());
130
131        let mut msg = bind::TPCANMsg {
132            ID: id::raw_u32(frame.id()),
133            MSGTYPE: if frame.is_extended() {
134                bind::PCAN_MODE_EXTENDED
135            } else {
136                bind::PCAN_MODE_STANDARD
137            } as u8,
138            LEN: frame.dlc() as u8,
139            DATA: data,
140        };
141
142        let status = unsafe {
143            self.lib
144                .CAN_Write(self.channel.into(), &mut msg as *mut bind::TPCANMsg)
145        };
146        match status {
147            0 => Ok(()),
148            _ => Err(Box::new(AdapterError::WriteFailed)),
149        }
150    }
151
152    #[cfg(not(target_os = "windows"))]
153    fn recv(
154        &self,
155        timeout: Option<Duration>,
156    ) -> Result<CanFrame, Box<AdapterError>> {
157        use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
158        use std::os::unix::io::BorrowedFd;
159
160        let start_time = Instant::now();
161
162        let mut msg = bind::TPCANMsg::default();
163        let mut timestamp = TPCANTimestamp::default();
164
165        loop {
166            let status = unsafe {
167                self.lib.CAN_Read(
168                    self.channel.into(),
169                    &mut msg as *mut bind::TPCANMsg,
170                    &mut timestamp as *mut bind::TPCANTimestamp,
171                )
172            };
173
174            match status {
175                bind::PCAN_ERROR_OK => {
176                    let id = match msg.MSGTYPE as u32 {
177                        bind::PCAN_MODE_STANDARD => {
178                            id::new_standard(msg.ID as u16)
179                        }
180                        bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
181                        _ => return Err(Box::new(AdapterError::ReadFailed)),
182                    }
183                    .ok_or(Box::new(AdapterError::ReadFailed))?;
184
185                    let frame =
186                        CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
187                            .ok_or(Box::new(AdapterError::ReadFailed))?;
188                    return Ok(frame);
189                }
190
191                // No packet available: poll the event FD periodically,
192                // checking every 1s until the specified timeout (since
193                // the event FD does not appear to signal when there is
194                // a hardware error, e.g. unplugging the adapter, we need
195                // to call CAN_Read to get this)
196                bind::PCAN_ERROR_QRCVEMPTY => {
197                    let poll_interval = match timeout {
198                        Some(t) => {
199                            let now = Instant::now();
200                            let end = start_time + t;
201                            if now >= end {
202                                return Err(Box::new(
203                                    AdapterError::ReadTimeout,
204                                ));
205                            }
206
207                            let remaining = (end - now).as_millis();
208
209                            if remaining < 1000 {
210                                PollTimeout::from(remaining as u16)
211                            } else {
212                                PollTimeout::from(1000u16)
213                            }
214                        }
215                        None => PollTimeout::from(1000u16),
216                    };
217
218                    let pollfd = PollFd::new(
219                        unsafe { BorrowedFd::borrow_raw(self.fd) },
220                        PollFlags::POLLIN,
221                    );
222                    if poll(&mut [pollfd], poll_interval).is_err() {
223                        return Err(Box::new(AdapterError::ReadFailed));
224                    }
225                }
226
227                _ => return Err(Box::new(AdapterError::ReadFailed)),
228            }
229        }
230    }
231
232    #[cfg(target_os = "windows")]
233    fn recv(
234        &self,
235        timeout: Option<Duration>,
236    ) -> Result<CanFrame, Box<AdapterError>> {
237        let start_time = Instant::now();
238
239        let mut msg = bind::TPCANMsg::default();
240        let mut timestamp = TPCANTimestamp::default();
241
242        loop {
243            let status = unsafe {
244                self.lib.CAN_Read(
245                    self.channel.into(),
246                    &mut msg as *mut bind::TPCANMsg,
247                    &mut timestamp as *mut bind::TPCANTimestamp,
248                )
249            };
250
251            match status {
252                bind::PCAN_ERROR_OK => {
253                    let id = match msg.MSGTYPE as u32 {
254                        bind::PCAN_MODE_STANDARD => {
255                            id::new_standard(msg.ID as u16)
256                        }
257                        bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
258                        _ => return Err(Box::new(AdapterError::ReadFailed)),
259                    }
260                    .ok_or(Box::new(AdapterError::ReadFailed))?;
261
262                    let frame =
263                        CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
264                            .ok_or(Box::new(AdapterError::ReadFailed))?;
265                    return Ok(frame);
266                }
267
268                // No packet available: sleep a short amount and retry.  This
269                // should be done via PCAN_RECEIVE_EVENT to reduce CPU load.
270                bind::PCAN_ERROR_QRCVEMPTY => {
271                    if let Some(timeout) = timeout {
272                        std::thread::sleep(Duration::from_micros(10));
273                        if Instant::now() - start_time >= timeout {
274                            return Err(Box::new(AdapterError::ReadTimeout));
275                        }
276                    }
277                }
278
279                _ => return Err(Box::new(AdapterError::ReadFailed)),
280            }
281        }
282    }
283}