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        // NOTE: MacOS only at the moment; can #[cfg] other platforms
84        // here
85        let lib = unsafe { libPCBUSB::new("libPCBUSB.dylib") }?;
86
87        let channel = try_as_channel(name)?;
88
89        let status = unsafe {
90            lib.CAN_Initialize(
91                channel,
92                try_as_baud(baud)?,
93                bind::PCAN_USB as u8,
94                0,
95                0,
96            )
97        };
98        match status {
99            0 => {
100                let mut fd: c_int = 0;
101                let info_ptr: *mut c_int = &mut fd;
102                match unsafe {
103                    lib.CAN_GetValue(
104                        channel,
105                        bind::PCAN_RECEIVE_EVENT as u8,
106                        info_ptr as *mut ::std::os::raw::c_void,
107                        4,
108                    )
109                } {
110                    bind::PCAN_ERROR_OK => Ok(Self { lib, fd, channel }),
111                    _ => Err(Box::new(AdapterError::OpenFailed)),
112                }
113            }
114            _ => Err(Box::new(AdapterError::OpenFailed)),
115        }
116    }
117}
118
119impl Drop for PcanAdapter {
120    fn drop(&mut self) {
121        unsafe { self.lib.CAN_Uninitialize(self.channel.into()) };
122    }
123}
124
125impl Adapter for PcanAdapter {
126    fn send(&self, frame: &CanFrame) -> Result<(), Box<AdapterError>> {
127        let mut data: [u8; 8] = [0u8; 8];
128        data[..frame.dlc()].copy_from_slice(frame.data());
129
130        let mut msg = bind::TPCANMsg {
131            ID: id::raw_u32(frame.id()),
132            MSGTYPE: if frame.is_extended() {
133                bind::PCAN_MODE_EXTENDED
134            } else {
135                bind::PCAN_MODE_STANDARD
136            } as u8,
137            LEN: frame.dlc() as u8,
138            DATA: data,
139        };
140
141        let status = unsafe {
142            self.lib
143                .CAN_Write(self.channel.into(), &mut msg as *mut bind::TPCANMsg)
144        };
145        match status {
146            0 => Ok(()),
147            _ => Err(Box::new(AdapterError::WriteFailed)),
148        }
149    }
150
151    fn recv(
152        &self,
153        timeout: Option<Duration>,
154    ) -> Result<CanFrame, Box<AdapterError>> {
155        use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
156        use std::os::unix::io::BorrowedFd;
157
158        let start_time = Instant::now();
159
160        let mut msg = bind::TPCANMsg::default();
161        let mut timestamp = TPCANTimestamp::default();
162
163        loop {
164            let status = unsafe {
165                self.lib.CAN_Read(
166                    self.channel.into(),
167                    &mut msg as *mut bind::TPCANMsg,
168                    &mut timestamp as *mut bind::TPCANTimestamp,
169                )
170            };
171
172            match status {
173                bind::PCAN_ERROR_OK => {
174                    let id = match msg.MSGTYPE as u32 {
175                        bind::PCAN_MODE_STANDARD => {
176                            id::new_standard(msg.ID as u16)
177                        }
178                        bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
179                        _ => return Err(Box::new(AdapterError::ReadFailed)),
180                    }
181                    .ok_or(Box::new(AdapterError::ReadFailed))?;
182
183                    let frame =
184                        CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
185                            .ok_or(Box::new(AdapterError::ReadFailed))?;
186                    return Ok(frame);
187                }
188
189                // No packet available: poll the event FD periodically,
190                // checking every 1s until the specified timeout (since
191                // the event FD does not appear to signal when there is
192                // a hardware error, e.g. unplugging the adapter, we need
193                // to call CAN_Read to get this)
194                bind::PCAN_ERROR_QRCVEMPTY => {
195                    let poll_interval = match timeout {
196                        Some(t) => {
197                            let now = Instant::now();
198                            let end = start_time + t;
199                            if now >= end {
200                                return Err(Box::new(
201                                    AdapterError::ReadTimeout,
202                                ));
203                            }
204
205                            let remaining = (end - now).as_millis();
206
207                            if remaining < 1000 {
208                                PollTimeout::from(remaining as u16)
209                            } else {
210                                PollTimeout::from(1000u16)
211                            }
212                        }
213                        None => PollTimeout::from(1000u16),
214                    };
215
216                    let pollfd = PollFd::new(
217                        unsafe { BorrowedFd::borrow_raw(self.fd) },
218                        PollFlags::POLLIN,
219                    );
220                    if poll(&mut [pollfd], poll_interval).is_err() {
221                        return Err(Box::new(AdapterError::ReadFailed));
222                    }
223                }
224
225                _ => return Err(Box::new(AdapterError::ReadFailed)),
226            }
227        }
228    }
229}