use j2534_rust::FilterType::FLOW_CONTROL_FILTER;
use j2534_rust::*;
use libloading::Library;
use std::os::raw::c_char;
use std::sync::Arc;
use std::{ffi::*, fmt};
pub type PassthruResult<T> = Result<T, PassthruError>;
type PassThruOpenFn = unsafe extern "system" fn(name: *const c_void, device_id: *mut u32) -> i32;
type PassThruCloseFn = unsafe extern "system" fn(device_id: u32) -> i32;
type PassThruConnectFn = unsafe extern "system" fn(
device_id: u32,
protocol_id: u32,
flags: u32,
baudrate: u32,
channel_id: *mut u32,
) -> i32;
type PassThruDisconnectFn = unsafe extern "system" fn(channel_id: u32) -> i32;
type PassThruReadMsgsFn = unsafe extern "system" fn(
channel_id: u32,
msgs: *mut PASSTHRU_MSG,
num_msgs: *mut u32,
timeout: u32,
) -> i32;
type PassThruWriteMsgsFn = unsafe extern "system" fn(
channel_id: u32,
msgs: *mut PASSTHRU_MSG,
num_msgs: *mut u32,
timeout: u32,
) -> i32;
type PassThruStartPeriodicMsgFn = unsafe extern "system" fn(
channel_id: u32,
msg: *const PASSTHRU_MSG,
msg_id: *mut u32,
time_interval: u32,
) -> i32;
type PassThruStopPeriodicMsgFn = unsafe extern "system" fn(channel_id: u32, msg_id: u32) -> i32;
type PassThruStartMsgFilterFn = unsafe extern "system" fn(
channel_id: u32,
filter_type: u32,
m_msg: *const PASSTHRU_MSG,
p_msg: *const PASSTHRU_MSG,
fc_msg: *const PASSTHRU_MSG,
filter_id: *mut u32,
) -> i32;
type PassThruStopMsgFilterFn = unsafe extern "system" fn(channel_id: u32, filter_id: u32) -> i32;
type PassThruSetProgrammingVoltageFn =
unsafe extern "system" fn(device_id: u32, pin_number: u32, voltage: u32) -> i32;
type PassThruReadVersionFn = unsafe extern "system" fn(
device_id: u32,
firmware_version: *mut c_char,
dll_version: *mut c_char,
api_version: *mut c_char,
) -> i32;
type PassThruGetLastErrorFn = unsafe extern "system" fn(error_description: *mut c_char) -> i32;
type PassThruIoctlFn = unsafe extern "system" fn(
handle_id: u32,
ioctl_id: u32,
input: *mut c_void,
output: *mut c_void,
) -> i32;
#[derive(Debug)]
pub struct DrvVersion {
pub dll_version: String,
pub api_version: String,
pub fw_version: String,
}
#[derive(Clone)]
pub struct PassthruDrv {
lib: Arc<Library>,
is_connected: bool,
open_fn: PassThruOpenFn,
close_fn: PassThruCloseFn,
connect_fn: PassThruConnectFn,
disconnect_fn: PassThruDisconnectFn,
read_msg_fn: PassThruReadMsgsFn,
write_msg_fn: PassThruWriteMsgsFn,
start_periodic_fn: PassThruStartPeriodicMsgFn,
stop_periodic_fn: PassThruStopPeriodicMsgFn,
start_filter_fn: PassThruStartMsgFilterFn,
stop_filter_fn: PassThruStopMsgFilterFn,
set_prog_v_fn: PassThruSetProgrammingVoltageFn,
get_last_err_fn: PassThruGetLastErrorFn,
ioctl_fn: PassThruIoctlFn,
read_version_fn: PassThruReadVersionFn,
}
impl fmt::Debug for PassthruDrv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PassthruDrv")
.field("is_connected", &self.is_connected)
.field("library", &self.lib)
.finish()
}
}
#[inline(always)]
fn ret_res<T>(res: i32, ret: T) -> PassthruResult<T> {
match res {
0 => Ok(ret),
_ => {
log::error!("Function call failed with status {res}");
Err(PassthruError::try_from(res as u32).unwrap())
}
}
}
impl PassthruDrv {
pub fn load_lib(path: String) -> Result<PassthruDrv, libloading::Error> {
log::debug!("Opening function library {path}");
let lib = unsafe { Library::new(path)? };
unsafe {
let open_fn = *lib.get::<PassThruOpenFn>(b"PassThruOpen\0")?.into_raw();
let close_fn = *lib.get::<PassThruCloseFn>(b"PassThruClose\0")?.into_raw();
let connect_fn = *lib
.get::<PassThruConnectFn>(b"PassThruConnect\0")?
.into_raw();
let disconnect_fn = *lib
.get::<PassThruDisconnectFn>(b"PassThruDisconnect\0")?
.into_raw();
let read_msg_fn = *lib
.get::<PassThruReadMsgsFn>(b"PassThruReadMsgs\0")?
.into_raw();
let write_msg_fn = *lib
.get::<PassThruWriteMsgsFn>(b"PassThruWriteMsgs\0")?
.into_raw();
let start_periodic_fn = *lib
.get::<PassThruStartPeriodicMsgFn>(b"PassThruStartPeriodicMsg\0")?
.into_raw();
let stop_periodic_fn = *lib
.get::<PassThruStopPeriodicMsgFn>(b"PassThruStopPeriodicMsg\0")?
.into_raw();
let start_filter_fn = *lib
.get::<PassThruStartMsgFilterFn>(b"PassThruStartMsgFilter\0")?
.into_raw();
let stop_filter_fn = *lib
.get::<PassThruStopMsgFilterFn>(b"PassThruStopMsgFilter\0")?
.into_raw();
let set_prog_v_fn = *lib
.get::<PassThruSetProgrammingVoltageFn>(b"PassThruSetProgrammingVoltage\0")?
.into_raw();
let get_last_err_fn = *lib
.get::<PassThruGetLastErrorFn>(b"PassThruGetLastError\0")?
.into_raw();
let ioctl_fn = *lib.get::<PassThruIoctlFn>(b"PassThruIoctl\0")?.into_raw();
let read_version_fn = *lib
.get::<PassThruReadVersionFn>(b"PassThruReadVersion\0")?
.into_raw();
Ok(PassthruDrv {
lib: Arc::new(lib),
is_connected: false,
open_fn,
close_fn,
connect_fn,
disconnect_fn,
read_msg_fn,
write_msg_fn,
start_periodic_fn,
stop_periodic_fn,
start_filter_fn,
stop_filter_fn,
set_prog_v_fn,
get_last_err_fn,
ioctl_fn,
read_version_fn,
})
}
}
#[allow(dead_code)]
pub fn is_connected(&self) -> bool {
self.is_connected
}
pub fn open(&mut self) -> PassthruResult<u32> {
log::debug!("PT_OPEN called");
let mut id: u32 = 0;
let res = unsafe { (self.open_fn)(std::ptr::null(), &mut id) };
if res == 0x00 {
self.is_connected = true;
}
ret_res(res, id)
}
pub fn close(&mut self, dev_id: u32) -> PassthruResult<()> {
log::debug!("PT_CLOSE called. Device ID: {dev_id}");
let res = unsafe { (self.close_fn)(dev_id) };
if res == 0x00 {
self.is_connected = false;
}
ret_res(res, ())
}
#[allow(trivial_casts)]
pub fn write_messages(
&self,
channel_id: u32,
msgs: &mut [PASSTHRU_MSG],
timeout: u32,
) -> PassthruResult<usize> {
log::debug!(
"PT_WRITE_MSGS called. Channel ID: {}, {} msgs, Timeout {}",
channel_id,
msgs.len(),
timeout
);
if msgs.is_empty() {
return Ok(0);
}
let mut msg_count: u32 = msgs.len() as u32;
let res = unsafe {
(self.write_msg_fn)(
channel_id,
msgs.as_mut_ptr(),
&mut msg_count as *mut u32,
timeout,
)
};
ret_res(res, msg_count as usize)
}
pub fn read_messages(
&self,
channel_id: u32,
max_msgs: u32,
timeout: u32,
) -> PassthruResult<Vec<PASSTHRU_MSG>> {
let mut msg_count: u32 = max_msgs;
let mut write_array: Vec<PASSTHRU_MSG> = vec![
PASSTHRU_MSG {
protocol_id: 0,
rx_status: 0,
tx_flags: 0,
timestamp: 0,
data_size: 0,
extra_data_size: 0,
data: [0; 4128]
};
max_msgs as usize
];
let res = unsafe {
(self.read_msg_fn)(
channel_id,
write_array.as_mut_ptr(),
&mut msg_count,
timeout,
)
};
if res == PassthruError::ERR_BUFFER_EMPTY as i32 {
write_array.truncate(msg_count as usize);
return ret_res(0x00, write_array);
}
if res == PassthruError::ERR_TIMEOUT as i32 {
write_array.truncate(msg_count as usize);
return ret_res(0x00, write_array);
}
if msg_count != max_msgs {
write_array.truncate(msg_count as usize);
}
ret_res(res, write_array)
}
pub fn get_version(&self, dev_id: u32) -> PassthruResult<DrvVersion> {
log::debug!("PT_GET_VERSION called. Device ID {dev_id}");
let mut firmware_version: [u8; 80] = [0; 80];
let mut dll_version: [u8; 80] = [0; 80];
let mut api_version: [u8; 80] = [0; 80];
let res = unsafe {
(self.read_version_fn)(
dev_id,
firmware_version.as_mut_ptr() as *mut c_char,
dll_version.as_mut_ptr() as *mut c_char,
api_version.as_mut_ptr() as *mut c_char,
)
};
unsafe {
ret_res(
res,
DrvVersion {
api_version: CStr::from_ptr(api_version.as_ptr() as *const c_char)
.to_str()
.unwrap()
.to_string(),
dll_version: CStr::from_ptr(dll_version.as_ptr() as *const c_char)
.to_str()
.unwrap()
.to_string(),
fw_version: CStr::from_ptr(firmware_version.as_ptr() as *const c_char)
.to_str()
.unwrap()
.to_string(),
},
)
}
}
pub fn get_last_error(&self) -> PassthruResult<String> {
let mut err: [u8; 80] = [0; 80];
let res = unsafe { (self.get_last_err_fn)(err.as_mut_ptr() as *mut c_char) };
ret_res(res, String::from_utf8(err.to_vec()).unwrap())
}
pub fn ioctl(
&self,
handle_id: u32,
ioctl_id: IoctlID,
input: *mut c_void,
output: *mut c_void,
) -> PassthruResult<()> {
log::debug!("PT_IOCTL called. handle ID {handle_id}, IOCTL ID {ioctl_id}");
let res = unsafe { (self.ioctl_fn)(handle_id, ioctl_id as u32, input, output) };
ret_res(res, ())
}
pub fn connect(
&self,
dev_id: u32,
protocol: Protocol,
flags: u32,
baud: u32,
) -> PassthruResult<u32> {
log::debug!("PT_CONNECT called. Device ID {dev_id}, protocol {protocol}, flags: {flags:08X?}, baud: {baud}");
let mut channel_id: u32 = 0;
let res =
unsafe { (self.connect_fn)(dev_id, protocol as u32, flags, baud, &mut channel_id) };
ret_res(res, channel_id)
}
pub fn disconnect(&self, channel_id: u32) -> PassthruResult<()> {
log::debug!("PT_DISCONNECT called. Channel ID {channel_id}");
ret_res(unsafe { (self.disconnect_fn)(channel_id) }, ())
}
#[allow(dead_code)]
pub fn start_periodic_msg(
&self,
channel_id: u32,
msg: &PASSTHRU_MSG,
time_interval: u32,
) -> PassthruResult<u32> {
let mut msg_id: u32 = 0;
let res = unsafe { (self.start_periodic_fn)(channel_id, msg, &mut msg_id, time_interval) };
ret_res(res, msg_id)
}
#[allow(dead_code)]
pub fn stop_periodic_msg(&self, channel_id: u32, msg_id: u32) -> PassthruResult<()> {
ret_res(unsafe { (self.stop_periodic_fn)(channel_id, msg_id) }, ())
}
pub fn start_msg_filter(
&self,
channel_id: u32,
filter_type: FilterType,
mask: &PASSTHRU_MSG,
pattern: &PASSTHRU_MSG,
flow_control: Option<PASSTHRU_MSG>,
) -> PassthruResult<u32> {
log::debug!("PT_START_MSG_FILTER called. Channel ID {channel_id}");
let tmp = filter_type as u32;
if tmp == FLOW_CONTROL_FILTER as u32 && flow_control.is_none() {
return Err(PassthruError::ERR_INVALID_FILTER_ID);
}
let mut filter_id: u32 = 0;
let res = match flow_control.as_ref() {
None => unsafe {
(self.start_filter_fn)(
channel_id,
tmp,
mask,
pattern,
std::ptr::null(),
&mut filter_id,
)
},
Some(fc) => unsafe {
(self.start_filter_fn)(channel_id, tmp, mask, pattern, fc, &mut filter_id)
},
};
ret_res(res, filter_id)
}
pub fn stop_msg_filter(&self, channel_id: u32, filter_id: u32) -> PassthruResult<()> {
log::debug!("PT_STOP_MSG_FILTER called. Channel ID {channel_id}, Filter ID {filter_id}",);
let res = unsafe { (self.stop_filter_fn)(channel_id, filter_id) };
match res {
0 => Ok(()),
_ => Err(PassthruError::try_from(res as u32).unwrap()),
}
}
#[allow(dead_code)]
pub fn set_programming_voltage(
&self,
dev_id: u32,
pin: u32,
voltage: u32,
) -> PassthruResult<()> {
ret_res(unsafe { (self.set_prog_v_fn)(dev_id, pin, voltage) }, ())
}
}