j2534/
lib.rs

1//! SAE J2534 PassThru defines a standard library interface for communicating with vehicle control modules.
2//! All automakers in the US are required to provide J2534-compatible service software.
3//! J2534 provides access to the communication layers required for accessing vehicle diagnostics services as
4//! well as downloading and reflashing control modules.
5//!
6//! ### Example
7//! ```rust
8//! use j2534::{Interface, PassThruMsg, Protocol, ConnectFlags, RxStatus, TxFlags};
9//!
10//! fn main() -> j2534::Result<()> {
11//!     // Open the library and connect to a device
12//!     let interface = Interface::new("C:\\device.dll")?;
13//!     let device = interface.open_any()?;
14//!
15//!     // Create a CAN channel
16//!     let channel = device
17//!         .connect(Protocol::CAN, ConnectFlags::NONE, 500000)
18//!         .unwrap();
19//!
20//!     // Create a new message with an arbitration id of `8` and payload of `[0, 1, 2, 3]`.
21//!     let message = PassThruMsg::new_can(8, &[0, 1, 2, 3]);
22//!     channel.write(&mut [message], 1000)?;
23//!     Ok(())
24//! }
25//! ```
26
27#[macro_use]
28extern crate bitflags;
29
30use std::ffi;
31use std::ffi::OsStr;
32use std::fmt;
33use std::fmt::Debug;
34use std::io;
35use std::marker::PhantomData;
36use std::path::Path;
37use std::str::Utf8Error;
38
39use bitflags::_core::fmt::Formatter;
40use libloading::{Library, Symbol};
41
42#[cfg(windows)]
43use winreg::{enums::*, RegKey};
44
45#[derive(thiserror::Error, Debug)]
46pub enum Error {
47    #[error(transparent)]
48    Utf8Error(#[from] Utf8Error),
49    #[error(transparent)]
50    Io(#[from] io::Error),
51
52    #[error("success")]
53    NoError,
54    #[error("option not supported')")]
55    NotSupported,
56    #[error("invalid channel id")]
57    InvalidChannelId,
58    #[error("invalid protocol id")]
59    InvalidProtocolId,
60    #[error("NULL was incorrectly passed as a parameter")]
61    NullParameter,
62    #[error("invalid ioctl parameter")]
63    InvalidIoctlValue,
64    #[error("invalid flags")]
65    InvalidFlags,
66    #[error("unspecified error")]
67    Failed,
68    #[error("a PassThru device is not connected")]
69    DeviceNotConnected,
70    #[error("timed out")]
71    Timeout,
72    #[error("invalid message")]
73    InvalidMessage,
74    #[error("invalid time interval")]
75    InvalidTimeInterval,
76    #[error("exceeded message limit")]
77    ExceededLimit,
78    #[error("invalid message id")]
79    InvalidMessageId,
80    #[error("device in use")]
81    DeviceInUse,
82    #[error("invalid ioctl id")]
83    InvalidIoctlId,
84    /// Could not read any messages from the vehicle network
85    #[error("could not read messages")]
86    BufferEmpty,
87    #[error("transmit queue full")]
88    BufferFull,
89    /// Receive buffer overflowed and messages were lost
90    #[error("receive buffer overflowed")]
91    BufferOverflow,
92    #[error("unknown pin number or resource in use")]
93    PinInvalid,
94    #[error("channel already in use")]
95    ChannelInUse,
96    #[error("message protocol does not match channel protocol")]
97    MessageProtocolId,
98    #[error("invalid filter id")]
99    InvalidFilterId,
100    #[error("no flow control filter matches outgoing message")]
101    NoFlowControl,
102    #[error("filter already exists")]
103    NotUnique,
104    #[error("unsupported baudrate")]
105    InvalidBaudrate,
106    #[error("invalid device id")]
107    InvalidDeviceId,
108    #[error("unknown j2534 error code {0}")]
109    Unknown(i32),
110}
111
112impl Error {
113    pub fn from_code(code: i32) -> Error {
114        match code {
115            0x00 => Error::NoError,
116            0x01 => Error::NotSupported,
117            0x02 => Error::InvalidChannelId,
118            0x03 => Error::InvalidProtocolId,
119            0x04 => Error::NullParameter,
120            0x05 => Error::InvalidIoctlValue,
121            0x06 => Error::InvalidFlags,
122            0x07 => Error::Failed,
123            0x08 => Error::DeviceNotConnected,
124            0x09 => Error::Timeout,
125            0x0A => Error::InvalidMessage,
126            0x0B => Error::InvalidTimeInterval,
127            0x0C => Error::ExceededLimit,
128            0x0D => Error::InvalidMessageId,
129            0x0E => Error::DeviceInUse,
130            0x0F => Error::InvalidIoctlId,
131            0x10 => Error::BufferEmpty,
132            0x11 => Error::BufferFull,
133            0x12 => Error::BufferOverflow,
134            0x13 => Error::PinInvalid,
135            0x14 => Error::ChannelInUse,
136            0x15 => Error::MessageProtocolId,
137            0x16 => Error::InvalidFilterId,
138            0x17 => Error::NoFlowControl,
139            0x18 => Error::NotUnique,
140            0x19 => Error::InvalidBaudrate,
141            0x1A => Error::InvalidDeviceId,
142            other => Error::Unknown(other),
143        }
144    }
145}
146
147type PassThruOpenFn =
148    unsafe extern "stdcall" fn(name: *const libc::c_void, device_id: *mut u32) -> i32;
149type PassThruCloseFn = unsafe extern "stdcall" fn(device_id: u32) -> i32;
150type PassThruConnectFn = unsafe extern "stdcall" fn(
151    device_id: u32,
152    protocol_id: u32,
153    flags: u32,
154    baudrate: u32,
155    channel_id: *mut u32,
156) -> i32;
157type PassThruDisconnectFn = unsafe extern "stdcall" fn(channel_id: u32) -> i32;
158type PassThruReadMsgsFn = unsafe extern "stdcall" fn(
159    channel_id: u32,
160    msgs: *mut PassThruMsg,
161    num_msgs: *mut u32,
162    timeout: u32,
163) -> i32;
164type PassThruWriteMsgsFn = unsafe extern "stdcall" fn(
165    channel_id: u32,
166    msgs: *mut PassThruMsg,
167    num_msgs: *mut u32,
168    timeout: u32,
169) -> i32;
170type PassThruStartPeriodicMsgFn = unsafe extern "stdcall" fn(
171    channel_id: u32,
172    msg: *const PassThruMsg,
173    msg_id: *mut u32,
174    time_interval: u32,
175) -> i32;
176type PassThruStopPeriodicMsgFn = unsafe extern "stdcall" fn(channel_id: u32, msg_id: u32) -> i32;
177type PassThruStartMsgFilterFn = unsafe extern "stdcall" fn(
178    channel_id: u32,
179    filter_type: u32,
180    msg_mask: *const PassThruMsg,
181    pattern_msg: *const PassThruMsg,
182    flow_control_msg: *const PassThruMsg,
183    filter_id: *mut u32,
184) -> i32;
185type PassThruStopMsgFilterFn = unsafe extern "stdcall" fn(channel_id: u32, filter_id: u32) -> i32;
186type PassThruSetProgrammingVoltageFn =
187    unsafe extern "stdcall" fn(device_id: u32, pin_number: u32, voltage: u32) -> i32;
188type PassThruReadVersionFn = unsafe extern "stdcall" fn(
189    device_id: u32,
190    firmware_version: *mut libc::c_char,
191    dll_version: *mut libc::c_char,
192    api_version: *mut libc::c_char,
193) -> i32;
194type PassThruGetLastErrorFn =
195    unsafe extern "stdcall" fn(error_description: *mut libc::c_char) -> i32;
196type PassThruIoctlFn = unsafe extern "stdcall" fn(
197    handle_id: u32,
198    ioctl_id: u32,
199    input: *mut libc::c_void,
200    output: *mut libc::c_void,
201) -> i32;
202
203// Much of the descriptions and APIs used here were taken from http://www.drewtech.com/support/passthru.html
204
205#[derive(Copy, Clone)]
206#[repr(C, packed(1))]
207/// A message sent a received from the device
208pub struct PassThruMsg {
209    pub protocol_id: u32,
210    pub rx_status: u32,
211    pub tx_flags: u32,
212    pub timestamp: u32,
213    pub data_size: u32,
214    pub extra_data_index: u32,
215    pub data: [u8; 4128],
216}
217
218#[repr(C, packed(1))]
219struct SConfig {
220    parameter: u32,
221    value: u32,
222}
223
224#[repr(C, packed(1))]
225struct SConfigList {
226    size: u32,
227    config_ptr: *const SConfig,
228}
229
230#[repr(C, packed(1))]
231struct SByteArray {
232    size: u32,
233    byte_ptr: *const u8,
234}
235
236impl PassThruMsg {
237    pub fn new_raw(
238        protocol: Protocol,
239        rx_status: RxStatus,
240        tx_flags: TxFlags,
241        timestamp: u32,
242        data_size: u32,
243        extra_data_index: u32,
244        data: [u8; 4128],
245    ) -> PassThruMsg {
246        PassThruMsg {
247            protocol_id: protocol as u32,
248            rx_status: rx_status.bits,
249            tx_flags: tx_flags.bits,
250            timestamp,
251            data_size,
252            extra_data_index,
253            data,
254        }
255    }
256
257    pub fn new(protocol: Protocol) -> PassThruMsg {
258        PassThruMsg {
259            protocol_id: protocol as u32,
260            rx_status: 0,
261            tx_flags: 0,
262            timestamp: 0,
263            data_size: 0,
264            extra_data_index: 0,
265            data: [0; 4128],
266        }
267    }
268
269    /// Creates a new CAN message.
270    /// The data size must be less than or equal to 8 bytes.
271    pub fn new_can(id: u32, data: &[u8]) -> PassThruMsg {
272        let mut msg_data = [0_u8; 4128];
273        // Copy arbitration ID
274        msg_data[0] = ((id >> 24) & 0xFF) as u8;
275        msg_data[1] = ((id >> 16) & 0xFF) as u8;
276        msg_data[2] = ((id >> 8) & 0xFF) as u8;
277        msg_data[3] = (id & 0xFF) as u8;
278
279        // Copy the message
280        &mut msg_data[4..data.len() + 4].copy_from_slice(data);
281
282        PassThruMsg {
283            data: msg_data,
284            data_size: (data.len() + 4) as u32,
285            ..Self::new(Protocol::CAN)
286        }
287    }
288
289    /// Creates a new ISO 15765-2 (ISO-TP) message.
290    /// ISO-TP is a transport-layer protocol that uses multiple CAN messages to transmit
291    /// up to 4095 bytes per packet.
292    /// The data size must be less than or equal to 4095.
293    pub fn new_isotp(id: u32, data: &[u8]) -> PassThruMsg {
294        let mut msg_data = [0_u8; 4128];
295        // Copy arbitration ID
296        msg_data[0] = ((id >> 24) & 0xFF) as u8;
297        msg_data[1] = ((id >> 16) & 0xFF) as u8;
298        msg_data[2] = ((id >> 8) & 0xFF) as u8;
299        msg_data[3] = (id & 0xFF) as u8;
300
301        // Copy the message
302        &mut msg_data[4..data.len() + 4].copy_from_slice(data);
303
304        PassThruMsg {
305            data: msg_data,
306            data_size: (data.len() + 4) as u32,
307            ..Self::new(Protocol::ISO15765)
308        }
309    }
310
311    /// Returns the CAN ID and payload. Also use this method for reading ISO-TP messages.
312    /// Returns `None` if the protocol is not CAN or ISO15765 or if the message is too short.
313    pub fn can_message(&self) -> Option<(u32, &[u8])> {
314        if (self.protocol_id == (Protocol::CAN as u32)
315            || self.protocol_id == (Protocol::ISO15765 as u32))
316            && self.data_size >= 4
317        {
318            let id = ((self.data[0] as u32) << 24)
319                | ((self.data[1] as u32) << 16)
320                | ((self.data[2] as u32) << 8)
321                | (self.data[3] as u32);
322            Some((id, &self.data[4..self.data_size as usize]))
323        } else {
324            None
325        }
326    }
327
328    /// Alias to [`PassThruMsg::can_message`]
329    #[inline]
330    pub fn isotp_message(&self) -> Option<(u32, &[u8])> {
331        self.can_message()
332    }
333
334    /// Sets the rx status of the message
335    pub fn rx_status(mut self, rx_status: RxStatus) -> Self {
336        self.rx_status = rx_status.bits;
337        self
338    }
339
340    /// Sets the transmit flags of the message
341    pub fn tx_flags(mut self, tx_flags: TxFlags) -> Self {
342        self.tx_flags = tx_flags.bits;
343        self
344    }
345
346    /// Returns true if this is an echo of a message transmitted by
347    /// the PassThru device.
348    pub fn transmitted(&self) -> bool {
349        self.rx_status & (RxStatus::TX_MSG_TYPE).bits() != 0
350    }
351
352    /// Returns true if this message indicates the first frame of a ISO15765 packet
353    /// has been received.
354    pub fn first_frame(&self) -> bool {
355        self.rx_status & (RxStatus::ISO15765_FIRST_FRAME).bits() != 0
356    }
357}
358
359impl Default for PassThruMsg {
360    fn default() -> PassThruMsg {
361        PassThruMsg {
362            protocol_id: 0,
363            rx_status: 0,
364            tx_flags: 0,
365            timestamp: 0,
366            data_size: 0,
367            extra_data_index: 0,
368            data: [0; 4128],
369        }
370    }
371}
372
373impl Debug for PassThruMsg {
374    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
375        unsafe {
376            f.debug_struct("PassThruMsg")
377                .field("protocol_id", &self.protocol_id)
378                .field("rx_status", &self.rx_status)
379                .field("tx_flags", &self.tx_flags)
380                .field("timestamp", &self.timestamp)
381                .field("extra_data_index", &self.extra_data_index)
382                .field("data", &&self.data[..self.data_size as usize])
383                .finish()
384        }
385    }
386}
387
388/// Vehicle communication channel ID
389#[derive(Copy, Clone, Debug)]
390struct ChannelId(u32);
391
392/// Device ID
393#[derive(Copy, Clone, Debug)]
394struct DeviceId(u32);
395
396/// Periodic message ID
397#[derive(Copy, Clone, Debug)]
398pub struct MessageId(u32);
399
400/// Message filter ID
401#[derive(Copy, Clone, Debug)]
402pub struct FilterId(u32);
403
404/// A J2534 library
405pub struct Interface {
406    library: Library,
407
408    c_pass_thru_open: PassThruOpenFn,
409    c_pass_thru_close: PassThruCloseFn,
410    c_pass_thru_connect: PassThruConnectFn,
411    c_pass_thru_disconnect: PassThruDisconnectFn,
412    c_pass_thru_read_version: PassThruReadVersionFn,
413    c_pass_thru_get_last_error: PassThruGetLastErrorFn,
414    c_pass_thru_read_msgs: PassThruReadMsgsFn,
415    c_pass_thru_start_msg_filter: PassThruStartMsgFilterFn,
416    c_pass_thru_stop_msg_filter: PassThruStopMsgFilterFn,
417    c_pass_thru_write_msgs: PassThruWriteMsgsFn,
418    c_pass_thru_start_periodic_msg: PassThruStartPeriodicMsgFn,
419    c_pass_thru_stop_periodic_msg: PassThruStopPeriodicMsgFn,
420    c_pass_thru_set_programming_voltage: PassThruSetProgrammingVoltageFn,
421    c_pass_thru_ioctl: PassThruIoctlFn,
422
423    _marker: PhantomData<*mut ()>,
424}
425
426/// A device created with [`Interface::open`]
427pub struct Device<'a> {
428    interface: &'a Interface,
429    id: DeviceId,
430}
431
432/// A communication channel
433pub struct Channel<'a> {
434    device: &'a Device<'a>,
435    id: ChannelId,
436    protocol: Protocol,
437}
438
439impl Interface {
440    /// Returns a J2534 library given the path
441    ///
442    /// # Arguments
443    ///
444    /// * `path` - The absolute path to the J2534 shared library
445    ///
446    /// # Example
447    /// ```no_run
448    /// use j2534::Interface;
449    /// let interface = Interface::new("C:\\j2534_driver.dll").unwrap();
450    /// ```
451    pub fn new<S: AsRef<OsStr>>(path: S) -> Result<Interface, libloading::Error> {
452        let library = Library::new(path)?;
453
454        let interface = unsafe {
455            let c_pass_thru_open: Symbol<PassThruOpenFn> = library.get(b"PassThruOpen\0")?;
456            let c_pass_thru_close: Symbol<PassThruCloseFn> = library.get(b"PassThruClose\0")?;
457            let c_pass_thru_connect: Symbol<PassThruConnectFn> =
458                library.get(b"PassThruConnect\0")?;
459            let c_pass_thru_disconnect: Symbol<PassThruDisconnectFn> =
460                library.get(b"PassThruDisconnect\0")?;
461            let c_pass_thru_read_version: Symbol<PassThruReadVersionFn> =
462                library.get(b"PassThruReadVersion\0")?;
463            let c_pass_thru_get_last_error: Symbol<PassThruGetLastErrorFn> =
464                library.get(b"PassThruGetLastError\0")?;
465            let c_pass_thru_read_msgs: Symbol<PassThruReadMsgsFn> =
466                library.get(b"PassThruReadMsgs\0")?;
467            let c_pass_thru_start_msg_filter: Symbol<PassThruStartMsgFilterFn> =
468                library.get(b"PassThruStartMsgFilter\0")?;
469            let c_pass_thru_stop_msg_filter: Symbol<PassThruStopMsgFilterFn> =
470                library.get(b"PassThruStopMsgFilter\0")?;
471            let c_pass_thru_write_msgs: Symbol<PassThruWriteMsgsFn> =
472                library.get(b"PassThruWriteMsgs\0")?;
473            let c_pass_thru_start_periodic_msg: Symbol<PassThruStartPeriodicMsgFn> =
474                library.get(b"PassThruStartPeriodicMsg\0")?;
475            let c_pass_thru_stop_periodic_msg: Symbol<PassThruStopPeriodicMsgFn> =
476                library.get(b"PassThruStopPeriodicMsg\0")?;
477            let c_pass_thru_set_programming_voltage: Symbol<PassThruSetProgrammingVoltageFn> =
478                library.get(b"PassThruSetProgrammingVoltage\0")?;
479            let c_pass_thru_ioctl: Symbol<PassThruIoctlFn> = library.get(b"PassThruIoctl\0")?;
480            Interface {
481                c_pass_thru_open: *c_pass_thru_open.into_raw(),
482                c_pass_thru_close: *c_pass_thru_close.into_raw(),
483                c_pass_thru_connect: *c_pass_thru_connect.into_raw(),
484                c_pass_thru_disconnect: *c_pass_thru_disconnect.into_raw(),
485                c_pass_thru_read_version: *c_pass_thru_read_version.into_raw(),
486                c_pass_thru_get_last_error: *c_pass_thru_get_last_error.into_raw(),
487                c_pass_thru_read_msgs: *c_pass_thru_read_msgs.into_raw(),
488                c_pass_thru_start_msg_filter: *c_pass_thru_start_msg_filter.into_raw(),
489                c_pass_thru_stop_msg_filter: *c_pass_thru_stop_msg_filter.into_raw(),
490                c_pass_thru_write_msgs: *c_pass_thru_write_msgs.into_raw(),
491                c_pass_thru_start_periodic_msg: *c_pass_thru_start_periodic_msg.into_raw(),
492                c_pass_thru_stop_periodic_msg: *c_pass_thru_stop_periodic_msg.into_raw(),
493                c_pass_thru_set_programming_voltage: *c_pass_thru_set_programming_voltage
494                    .into_raw(),
495                c_pass_thru_ioctl: *c_pass_thru_ioctl.into_raw(),
496                library,
497
498                _marker: PhantomData,
499            }
500        };
501
502        // OpenPort 2.0 debug mode: unsafe { (&interface.c_pass_thru_ioctl)(0, 0x70001, 0x1 as *mut libc::c_void, std::ptr::null_mut() as *mut libc::c_void); }
503
504        Ok(interface)
505    }
506
507    /// Returns a text description of the most recent error
508    pub fn get_last_error(&self) -> Result<String, Error> {
509        let mut error: [u8; 80] = [0; 80];
510        let res =
511            unsafe { (&self.c_pass_thru_get_last_error)(error.as_mut_ptr() as *mut libc::c_char) };
512        if res != 0 {
513            return Err(Error::from_code(res));
514        }
515
516        unsafe {
517            Ok(String::from(
518                ffi::CStr::from_ptr(error.as_mut_ptr() as *mut libc::c_char).to_str()?,
519            ))
520        }
521    }
522
523    /// Creates a `Device` from the PassThru interface connected to the given port
524    ///
525    /// # Arguments
526    ///
527    /// * `port` - The port to search for a J2534 device
528    ///
529    /// # Example
530    /// ```no_run
531    /// use j2534::Interface;
532    /// let interface = Interface::new("C:\\j2534_driver.dll").unwrap();
533    /// let device = interface.open("COM2").unwrap();
534    /// ```
535    pub fn open<S: Into<Vec<u8>>>(&self, port: S) -> Result<Device, Error> {
536        let s = ffi::CString::new(port).unwrap();
537        let raw = s.as_ptr() as *const libc::c_void;
538        let mut id = 0;
539
540        let res = unsafe { (&self.c_pass_thru_open)(raw, &mut id as *mut u32) };
541        if res != 0 {
542            return Err(Error::from_code(res));
543        }
544
545        Ok(Device {
546            interface: self,
547            id: DeviceId(id),
548        })
549    }
550
551    /// Creates a `Device` from any connected PassThru devices
552    ///
553    /// # Example
554    /// ```
555    /// use j2534::Interface;
556    /// let interface = Interface::new("C:\\j2534_driver.dll").unwrap();
557    /// let device = interface.open_any().unwrap();
558    /// ```
559    pub fn open_any(&self) -> Result<Device, Error> {
560        let raw = std::ptr::null() as *const libc::c_void;
561        let mut id = 0;
562        let res = unsafe { (&self.c_pass_thru_open)(raw, &mut id as *mut u32) };
563        if res != 0 {
564            return Err(Error::from_code(res));
565        }
566
567        Ok(Device {
568            interface: self,
569            id: DeviceId(id),
570        })
571    }
572
573    /// General purpose I/O control for modifying device or channel characteristics.
574    pub unsafe fn ioctl(
575        &self,
576        handle: u32,
577        id: IoctlId,
578        input: *mut libc::c_void,
579        output: *mut libc::c_void,
580    ) -> Result<i32, Error> {
581        let res = (&self.c_pass_thru_ioctl)(handle, id as u32, input, output);
582        if res != 0 {
583            return Err(Error::from_code(res));
584        }
585        Ok(res)
586    }
587}
588
589#[derive(Copy, Clone)]
590pub enum Protocol {
591    J1850VPW = 1,
592    J1850PWM = 2,
593    ISO9141 = 3,
594    ISO14230 = 4,
595    CAN = 5,
596    ISO15765 = 6,
597    SCI_A_ENGINE = 7,
598    SCI_A_TRANS = 8,
599    SCI_B_ENGINE = 9,
600    SCI_B_TRANS = 10,
601}
602
603bitflags! {
604    /// Flags used when creating a communication channel
605    pub struct ConnectFlags: u32 {
606        const NONE = 0;
607        const CAN_29_BIT_ID = 0x100;
608        const ISO9141_NO_CHECKSUM = 0x200;
609        const CAN_ID_BOTH = 0x800;
610        const ISO9141_K_LINE_ONLY = 0x1000;
611    }
612}
613
614bitflags! {
615    /// Transmit status flags
616    pub struct TxFlags: u32 {
617        const NONE = 0;
618        // 0 = no padding
619        // 1 = pad all flow controlled messages to a full CAN frame using zeroes
620        const ISO15765_FRAME_PAD = 0x00000040;
621
622        const ISO15765_ADDR_TYPE = 0x00000080;
623        const CAN_29BIT_ID =  0x00000100;
624
625        // 0 = Interface message timing as specified in ISO 14230
626        // 1 = After a response is received for a physical request, the wait time shall be reduced to P3_MIN
627        // Does not affect timing on responses to functional requests
628        const WAIT_P3_MIN_ONLY = 0x00000200;
629
630        const SW_CAN_HV_TX = 0x00000400;
631
632        // 0 = Transmit using SCI Full duplex mode
633        // 1 = Transmit using SCI Half duplex mode
634        const SCI_MODE = 0x00400000;
635
636        // 0 = no voltage after message transmit
637        // 1 = apply 20V after message transmit
638        const SCI_TX_VOLTAGE = 0x00800000;
639
640        const DT_PERIODIC_UPDATE = 0x10000000;
641    }
642}
643
644bitflags! {
645    /// Receive status flags
646    pub struct RxStatus: u32 {
647        const NONE = 0;
648        // 0 = received
649        // 1 = transmitted
650        const TX_MSG_TYPE  = 0x00000001;
651
652        // 0 = Not a start of message indication
653        // 1 = First byte or frame received
654        const START_OF_MESSAGE = 0x00000002;
655        const ISO15765_FIRST_FRAME = 0x00000002  ;
656
657        const ISO15765_EXT_ADDR = 0x00000080;
658
659        // 0 = No break received
660        // 1 = Break received
661        const RX_BREAK = 0x00000004;
662
663        // 0 = No TxDone
664        // 1 = TxDone
665        const TX_INDICATION   = 0x00000008;
666        const TX_DONE = 0x00000008;
667
668        // 0 = No Error
669        // 1 = Padding Error
670        const ISO15765_PADDING_ERROR = 0x00000010;
671
672        // 0 = no extended address,
673        // 1 = extended address is first byte after the CAN ID
674        const ISO15765_ADDR_TYPE = 0x00000080;
675
676        const CAN_29BIT_ID = 0x00000100;
677
678        const SW_CAN_NS_RX = 0x00040000;
679        const SW_CAN_HS_RX = 0x00020000;
680        const SW_CAN_HV_RX = 0x00010000;
681    }
682}
683
684#[derive(Copy, Clone)]
685pub enum IoctlId {
686    GET_CONFIG = 0x01,
687    SET_CONFIG = 0x02,
688    READ_VBATT = 0x03,
689    FIVE_BAUD_INIT = 0x04,
690    FAST_INIT = 0x05,
691    // unused 0x06
692    CLEAR_TX_BUFFER = 0x07,
693    CLEAR_RX_BUFFER = 0x08,
694    CLEAR_PERIODIC_MSGS = 0x09,
695    CLEAR_MSG_FILTERS = 0x0A,
696    CLEAR_FUNCT_MSG_LOOKUP_TABLE = 0x0B,
697    ADD_TO_FUNCT_MSG_LOOKUP_TABLE = 0x0C,
698    DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE = 0x0D,
699    READ_PROG_VOLTAGE = 0x0E,
700
701    SW_CAN_HS = 0x8000,
702    SW_CAN_NS = 0x8001,
703    SET_POLL_RESPONSE = 0x8002,
704    BECOME_MASTER = 0x8003,
705}
706
707#[derive(Copy, Clone)]
708/// Channel configuration parameters. Use with [`Channel::get_config`] and [`Channel::set_config`]
709pub enum ConfigId {
710    DATA_RATE = 0x01,
711    LOOPBACK = 0x03,
712    NODE_ADDRESS = 0x04,
713    NETWORK_LINE = 0x05,
714    P1_MIN = 0x06,
715    P1_MAX = 0x07,
716    P2_MIN = 0x08,
717    P2_MAX = 0x09,
718    P3_MIN = 0x0A,
719    P3_MAX = 0x0B,
720    P4_MIN = 0x0C,
721    P4_MAX = 0x0D,
722
723    W1 = 0x0E,
724    W2 = 0x0F,
725    W3 = 0x10,
726    W4 = 0x11,
727    W5 = 0x12,
728    TIDLE = 0x13,
729    TINIL = 0x14,
730    TWUP = 0x15,
731    PARITY = 0x16,
732    BIT_SAMPLE_POINT = 0x17,
733    SYNC_JUMP_WIDTH = 0x18,
734    W0 = 0x19,
735    T1_MAX = 0x1A,
736    T2_MAX = 0x1B,
737
738    T4_MAX = 0x1C,
739    T5_MAX = 0x1D,
740    ISO15765_BS = 0x1E,
741    ISO15765_STMIN = 0x1F,
742    DATA_BITS = 0x20,
743    FIVE_BAUD_MOD = 0x21,
744    BS_TX = 0x22,
745    STMIN_TX = 0x23,
746    T3_MAX = 0x24,
747    ISO15765_WFT_MAX = 0x25,
748
749    CAN_MIXED_FORMAT = 0x8000,
750
751    J1962_PINS = 0x8001,
752
753    SW_CAN_HS_DATA_RATE = 0x8010,
754    SW_CAN_SPEEDCHANGE_ENABLE = 0x8011,
755    SW_CAN_RES_SWITCH = 0x8012,
756    ACTIVE_CHANNELS = 0x8020,
757    SAMPLE_RATE = 0x8021,
758    SAMPLES_PER_READING = 0x8022,
759    READINGS_PER_MSG = 0x8023,
760    AVERAGING_METHOD = 0x8024,
761    SAMPLE_RESOLUTION = 0x8025,
762    INPUT_RANGE_LOW = 0x8026,
763    INPUT_RANGE_HIGH = 0x8027,
764}
765
766#[derive(Copy, Clone)]
767pub enum FilterType {
768    /// Allows matching messages into the receive queue. This filter type is only valid on non-ISO 15765 channels
769    Pass = 1,
770    /// Keeps matching messages out of the receive queue. This filter type is only valid on non-ISO 15765 channels
771    Block = 2,
772    /// Allows matching messages into the receive queue and defines an outgoing flow control message to support
773    /// the ISO 15765-2 flow control mechanism. This filter type is only valid on ISO 15765 channels.
774    FlowControl = 3,
775}
776
777#[derive(Debug)]
778/// Information about a device's version.
779pub struct VersionInfo {
780    pub firmware_version: String,
781    pub dll_version: String,
782    pub api_version: String,
783}
784
785pub const SHORT_TO_GROUND: u32 = 0xFFFFFFFE;
786pub const VOLTAGE_OFF: u32 = 0xFFFFFFFF;
787
788impl<'a> Device<'a> {
789    /// Reads the version info of the device
790    pub fn read_version(&self) -> Result<VersionInfo, Error> {
791        let mut firmware_version: [u8; 80] = [0; 80];
792        let mut dll_version: [u8; 80] = [0; 80];
793        let mut api_version: [u8; 80] = [0; 80];
794        let res = unsafe {
795            (&self.interface.c_pass_thru_read_version)(
796                self.id.0,
797                firmware_version.as_mut_ptr() as *mut libc::c_char,
798                dll_version.as_mut_ptr() as *mut libc::c_char,
799                api_version.as_mut_ptr() as *mut libc::c_char,
800            )
801        };
802        if res != 0 {
803            return Err(Error::from_code(res));
804        }
805        unsafe {
806            Ok(VersionInfo {
807                firmware_version: String::from(
808                    ffi::CStr::from_ptr(firmware_version.as_mut_ptr() as *mut libc::c_char)
809                        .to_str()?,
810                ),
811                api_version: String::from(
812                    ffi::CStr::from_ptr(api_version.as_mut_ptr() as *mut libc::c_char).to_str()?,
813                ),
814                dll_version: String::from(
815                    ffi::CStr::from_ptr(dll_version.as_mut_ptr() as *mut libc::c_char).to_str()?,
816                ),
817            })
818        }
819    }
820
821    /// Outputs a programmable voltage on the specified J1962 connector pin.
822    /// Only one pin can have a specified voltage applied at a time. The only exception: it is permissible to program pin 15 for SHORT_TO_GROUND, and another pin to a voltage level.
823    /// When switching pins, the user application must disable the first voltage (VOLTAGE_OFF option) before enabling the second.
824    ///
825    /// # Arguments
826    ///
827    /// * `pin_number` - The J1962 connector pin to which the PassThru device will apply the specified voltage
828    /// * `voltage` - The voltage value (in millivolts) that will be applied to the specified pin
829    pub fn set_programming_voltage(&self, pin_number: u32, voltage: u32) -> Result<(), Error> {
830        let res = unsafe {
831            (&self.interface.c_pass_thru_set_programming_voltage)(self.id.0, pin_number, voltage)
832        };
833        if res != 0 {
834            return Err(Error::from_code(res));
835        }
836        Ok(())
837    }
838
839    /// Creates a channel
840    ///
841    /// # Arguments
842    ///
843    /// * protocol - Protocol to use with the channel
844    /// * flags - Protocol-specific flags. This is usually set to zero
845    /// * baudrate - Initial baud rate for the channel
846    pub fn connect(
847        &self,
848        protocol: Protocol,
849        flags: ConnectFlags,
850        baudrate: u32,
851    ) -> Result<Channel, Error> {
852        let mut id: u32 = 0;
853        let res = unsafe {
854            (&self.interface.c_pass_thru_connect)(
855                self.id.0,
856                protocol as u32,
857                flags.bits,
858                baudrate,
859                &mut id as *mut u32,
860            )
861        };
862        if res != 0 {
863            return Err(Error::from_code(res));
864        }
865        Ok(Channel {
866            device: self,
867            id: ChannelId(id),
868            protocol: protocol as Protocol,
869        })
870    }
871
872    /// Returns the battery voltage in millivolts read from Pin 16 on the J1962 connector.
873    pub fn read_battery_voltage(&self) -> Result<u32, Error> {
874        let mut voltage: u32 = 0;
875        unsafe {
876            self.interface.ioctl(
877                self.id.0,
878                IoctlId::READ_VBATT,
879                std::ptr::null_mut::<libc::c_void>(),
880                (&mut voltage) as *mut _ as *mut libc::c_void,
881            )
882        }?;
883        Ok(voltage)
884    }
885
886    /// Returns the current output voltage for ECU reprogramming in millivolts.
887    pub fn read_programming_voltage(&self) -> Result<u32, Error> {
888        let mut voltage: u32 = 0;
889        unsafe {
890            self.interface.ioctl(
891                self.id.0,
892                IoctlId::READ_PROG_VOLTAGE,
893                std::ptr::null_mut::<libc::c_void>(),
894                (&mut voltage) as *mut _ as *mut libc::c_void,
895            )
896        }?;
897        Ok(voltage)
898    }
899}
900
901impl<'a> Drop for Device<'a> {
902    fn drop(&mut self) {
903        unsafe { (&self.interface.c_pass_thru_close)(self.id.0) };
904    }
905}
906
907impl<'a> Channel<'a> {
908    /// Fills `msgs` with messages until timing out or until `msgs` is filled. Returns the slice of messages read.
909    ///
910    /// # Arguments
911    ///
912    /// * `msgs` - The array of messages to fill.
913    /// * `timeout` - The amount of time in milliseconds to wait. If set to zero, reads buffered messages and returns immediately
914    ///
915    /// Returns the amount of messages read.
916    pub fn read(&self, buf: &mut [PassThruMsg], timeout: u32) -> Result<usize, Error> {
917        for msg in buf.iter_mut() {
918            msg.protocol_id = self.protocol as u32;
919        }
920        let mut num_msgs: u32 = buf.len() as u32;
921        let res = unsafe {
922            (&self.device.interface.c_pass_thru_read_msgs)(
923                self.id.0,
924                buf.as_mut_ptr(),
925                &mut num_msgs as *mut u32,
926                timeout,
927            )
928        };
929        if res != 0 {
930            return Err(Error::from_code(res));
931        }
932        Ok(num_msgs as usize)
933    }
934
935    /// Reads a single message
936    pub fn read_once(&self, timeout: u32) -> Result<PassThruMsg, Error> {
937        let mut msg = PassThruMsg::new(self.protocol);
938
939        let mut num_msgs = 1 as u32;
940        let res = unsafe {
941            (&self.device.interface.c_pass_thru_read_msgs)(
942                self.id.0,
943                &mut msg as *mut PassThruMsg,
944                &mut num_msgs as *mut u32,
945                timeout,
946            )
947        };
948        if num_msgs == 0 {
949            // This should never happen, but just in case...
950            Err(Error::BufferEmpty)
951        } else if res != 0 {
952            Err(Error::from_code(res))
953        } else {
954            Ok(msg)
955        }
956    }
957
958    /// Writes `msgs` to the device until all messages have been written or until the timeout has been reached. Returns the amount of message written.
959    ///
960    /// # Arguments
961    ///
962    /// * msgs - The array of messages to send.
963    /// * timeout - The amount of time in milliseconds to wait. If set to zero, queues as many messages as possible and returns immediately.
964    pub fn write(&self, msgs: &mut [PassThruMsg], timeout: u32) -> Result<usize, Error> {
965        let mut num_msgs: u32 = msgs.len() as u32;
966        let res = unsafe {
967            (&self.device.interface.c_pass_thru_write_msgs)(
968                self.id.0,
969                msgs.as_mut_ptr(),
970                &mut num_msgs as *mut u32,
971                timeout,
972            )
973        };
974        if res != 0 {
975            return Err(Error::from_code(res));
976        }
977        Ok(num_msgs as usize)
978    }
979
980    /// Sets up a network protocol filter to filter messages received by the PassThru device. There is a limit of ten filters per network layer protocol.
981    /// The device blocks all receive frames by default when no filters are defined.
982    ///
983    /// Returns filter ID
984    /// http://www.drewtech.com/support/passthru/startmsgfilter.html
985    pub fn start_message_filter(
986        &self,
987        filter_type: FilterType,
988        mask_msg: Option<&PassThruMsg>,
989        pattern_msg: Option<&PassThruMsg>,
990        flow_control_msg: Option<&PassThruMsg>,
991    ) -> Result<FilterId, Error> {
992        let mut msg_id: u32 = 0;
993
994        let mask_ptr = match mask_msg {
995            Some(msg) => msg as *const PassThruMsg,
996            None => std::ptr::null() as *const PassThruMsg,
997        };
998
999        let pattern_ptr = match pattern_msg {
1000            Some(msg) => msg as *const PassThruMsg,
1001            None => std::ptr::null() as *const PassThruMsg,
1002        };
1003
1004        let flow_control_ptr = match flow_control_msg {
1005            Some(msg) => msg as *const PassThruMsg,
1006            None => std::ptr::null() as *const PassThruMsg,
1007        };
1008
1009        let res = unsafe {
1010            (&self.device.interface.c_pass_thru_start_msg_filter)(
1011                self.id.0,
1012                filter_type as u32,
1013                mask_ptr,
1014                pattern_ptr,
1015                flow_control_ptr,
1016                &mut msg_id as *mut u32,
1017            )
1018        };
1019        if res != 0 {
1020            return Err(Error::from_code(res));
1021        }
1022        Ok(FilterId(msg_id))
1023    }
1024
1025    /// Removes a message filter started with `Channel::start_msg_filter`
1026    ///
1027    /// # Arguments
1028    ///
1029    /// * `msg_id` - The id of the message returned from `Channel::start_msg_filter`
1030    pub fn stop_message_filter(&self, filter_id: FilterId) -> Result<(), Error> {
1031        let res =
1032            unsafe { (&self.device.interface.c_pass_thru_stop_msg_filter)(self.id.0, filter_id.0) };
1033        if res != 0 {
1034            return Err(Error::from_code(res));
1035        }
1036        Ok(())
1037    }
1038
1039    /// Repetitively transmit network protocol messages at the specified time interval over an existing logical communication channel. There is a limit of ten periodic messages per network layer protocol.
1040    /// Returns a handle for the periodic message used in `Channel::stop_periodic_msg`
1041    ///
1042    /// # Arguments
1043    ///
1044    /// * `msg` - The message to send
1045    /// * `time_interval` - The time in milliseconds to wait between sending messages. The acceptable range is between 5 and 65,535 milliseconds.
1046    pub fn start_periodic_message(
1047        &self,
1048        msg: &PassThruMsg,
1049        time_interval: u32,
1050    ) -> Result<MessageId, Error> {
1051        let mut msg_id = 0;
1052        let res = unsafe {
1053            (&self.device.interface.c_pass_thru_start_periodic_msg)(
1054                self.id.0,
1055                msg as *const PassThruMsg,
1056                &mut msg_id as *mut u32,
1057                time_interval,
1058            )
1059        };
1060        if res != 0 {
1061            return Err(Error::from_code(res));
1062        }
1063        Ok(MessageId(msg_id))
1064    }
1065
1066    /// Stops a periodic mesage started with `Channel::start_periodic_msg`
1067    ///
1068    /// # Arguments
1069    ///
1070    /// * msg_id = the id of the periodic message returned from `Channel::start_periodiC_msg`
1071    pub fn stop_periodic_message(&self, msg_id: MessageId) -> Result<(), Error> {
1072        let res =
1073            unsafe { (&self.device.interface.c_pass_thru_stop_periodic_msg)(self.id.0, msg_id.0) };
1074        if res != 0 {
1075            return Err(Error::from_code(res));
1076        }
1077        Ok(())
1078    }
1079
1080    /// Clear transmit message queue
1081    pub fn clear_transmit_buffer(&self) -> Result<(), Error> {
1082        unsafe {
1083            self.device.interface.ioctl(
1084                self.id.0,
1085                IoctlId::CLEAR_TX_BUFFER,
1086                std::ptr::null_mut::<libc::c_void>(),
1087                std::ptr::null_mut::<libc::c_void>(),
1088            )
1089        }?;
1090        Ok(())
1091    }
1092
1093    /// Halt continuous messages
1094    pub fn clear_periodic_messages(&self) -> Result<(), Error> {
1095        unsafe {
1096            self.device.interface.ioctl(
1097                self.id.0,
1098                IoctlId::CLEAR_PERIODIC_MSGS,
1099                std::ptr::null_mut::<libc::c_void>(),
1100                std::ptr::null_mut::<libc::c_void>(),
1101            )
1102        }?;
1103        Ok(())
1104    }
1105
1106    /// Clear receive message queue
1107    pub fn clear_receive_buffer(&self) -> Result<(), Error> {
1108        unsafe {
1109            self.device.interface.ioctl(
1110                self.id.0,
1111                IoctlId::CLEAR_RX_BUFFER,
1112                std::ptr::null_mut::<libc::c_void>(),
1113                std::ptr::null_mut::<libc::c_void>(),
1114            )
1115        }?;
1116        Ok(())
1117    }
1118
1119    /// Removes all message filters
1120    pub fn clear_message_filters(&self) -> Result<(), Error> {
1121        unsafe {
1122            self.device.interface.ioctl(
1123                self.id.0,
1124                IoctlId::CLEAR_MSG_FILTERS,
1125                std::ptr::null_mut::<libc::c_void>(),
1126                std::ptr::null_mut::<libc::c_void>(),
1127            )
1128        }?;
1129        Ok(())
1130    }
1131
1132    /// Gets a single configuration parameter.
1133    pub fn get_config(&self, id: ConfigId) -> Result<u32, Error> {
1134        let mut item = SConfig {
1135            parameter: id as u32,
1136            value: 0,
1137        };
1138        let mut input = SConfigList {
1139            size: 1,
1140            config_ptr: &mut item as *mut SConfig,
1141        };
1142        unsafe {
1143            self.device.interface.ioctl(
1144                self.id.0,
1145                IoctlId::GET_CONFIG,
1146                &mut input as *mut _ as *mut libc::c_void,
1147                std::ptr::null_mut::<libc::c_void>(),
1148            )
1149        }?;
1150        Ok(item.value)
1151    }
1152
1153    /// Sets a single configuration parameter.
1154    pub fn set_config(&self, id: ConfigId, value: u32) -> Result<(), Error> {
1155        let mut item = SConfig {
1156            parameter: id as u32,
1157            value,
1158        };
1159        let mut input = SConfigList {
1160            size: 1,
1161            config_ptr: &mut item as *mut SConfig,
1162        };
1163        unsafe {
1164            self.device.interface.ioctl(
1165                self.id.0,
1166                IoctlId::SET_CONFIG,
1167                &mut input as *mut _ as *mut libc::c_void,
1168                std::ptr::null_mut::<libc::c_void>(),
1169            )
1170        }?;
1171        Ok(())
1172    }
1173}
1174
1175impl<'a> Drop for Channel<'a> {
1176    fn drop(&mut self) {
1177        unsafe { (&self.device.interface.c_pass_thru_disconnect)(self.id.0) };
1178    }
1179}
1180
1181/// Information about an installed PassThru driver
1182#[derive(Debug)]
1183pub struct Driver {
1184    pub name: String,
1185    pub vendor: String,
1186    pub path: String,
1187}
1188
1189#[cfg(windows)]
1190/// Returns a list of all installed PassThru drivers
1191pub fn drivers() -> io::Result<Vec<Driver>> {
1192    let passthru = match RegKey::predef(HKEY_LOCAL_MACHINE)
1193        .open_subkey(Path::new("SOFTWARE").join("PassThruSupport.04.04"))
1194    {
1195        Err(err) if err.kind() == io::ErrorKind::NotFound => {
1196            return Ok(Vec::new());
1197        }
1198        other => other,
1199    }?;
1200    let mut listings = Vec::new();
1201
1202    for name in passthru.enum_keys() {
1203        let name = name?;
1204        let key = passthru.open_subkey(name)?;
1205
1206        let device_name: String = key.get_value("Name")?;
1207        let vendor: String = key.get_value("Vendor")?;
1208        let path: String = key.get_value("FunctionLibrary")?;
1209
1210        listings.push(Driver {
1211            name: device_name,
1212            vendor,
1213            path,
1214        });
1215    }
1216
1217    Ok(listings)
1218}
1219
1220#[cfg(not(windows))]
1221/// Returns a list of all installed PassThru drivers
1222pub fn drivers() -> io::Result<Vec<Driver>> {
1223    Ok(Vec::new())
1224}