dmrconfig/src/serial.rs

260 lines
6.7 KiB
Rust

use std::ffi::{CStr};
use libc::{c_char, c_int, c_uchar, c_void};
use std::process::exit;
use std::time::Duration;
//hack to handle the serial device path state
pub static mut device_path: Option<String> = None;
extern {
fn find_path(vid: libc::c_int, pid: libc::c_int) -> *const c_char;
fn serial_identify(s: *const c_char) -> *const c_char;
fn serial_close();
}
/// Connect to the specified device.
/// Initiate the programming session.
pub fn serial_init(vid: u32, pid: u32, trace_flag: bool) -> Option<String> {
let dev_path = unsafe { find_path(vid as i32, pid as i32) };
if dev_path.is_null() {
if trace_flag {
eprintln!("Cannot find USB device: {:#x}{:#x}", vid, pid);
}
return None;
}
let dev_path = unsafe { CStr::from_ptr(dev_path).to_str().unwrap().to_string() };
println!("Serial port: {}", dev_path);
Some(dev_path)
}
fn send_receive(port: &mut dyn serial::SerialPort, command: &[u8], reply_len: usize, trace: bool) -> Vec<u8> {
if trace {
eprintln!("----Send [{}] {:?}", command.len(), command);
}
match port.write(&command) {
Ok(n) => {
if trace {
eprintln!("Wrote {} bytes over serial", n);
}
},
Err(e) => {
eprintln!("Serial write error: {}", e);
exit(-1);
}
};
let mut buf: Vec<u8> = vec![0; reply_len];
match port.read(&mut buf) {
Ok(n) if n == reply_len => {
if trace {
eprintln!("----Received [{}] {:?}", reply_len, buf);
}
},
Ok(n) => {
eprintln!("Serial: read {} bytes, expected {}", n, reply_len);
}
Err(e) => {
eprintln!("Serial read error: {}", e);
exit(-1);
}
};
buf
}
#[no_mangle]
pub extern "C" fn serial_read_region(addr: c_int, data: *mut c_uchar, nbytes: c_int) {
use serial::prelude::*;
let dev_path = unsafe {
match device_path {
Some(ref s) => s.clone(),
None => {
eprintln!("No serial device path set, exiting");
exit(-1);
}
}
};
let mut port = match serial::open(&dev_path) {
Ok(port) => port,
Err(err) => {
println!("{}", err);
exit(-1);
}
};
port.reconfigure(&|settings: &mut dyn SerialPortSettings| {
settings.set_baud_rate(serial::BaudRate::Baud115200)?;
settings.set_char_size(serial::CharSize::Bits8);
Ok(())
}).unwrap();
port.set_timeout(Duration::new(1,0)).unwrap();
const DATA_SIZE: usize = 64;
let num_bytes = nbytes as usize;
let addr = addr as usize;
let mut output_data = vec![];
let mut n = 0;
while n < num_bytes {
// Serial read command: 'R' aa aa aa aa 0x10
let cmd: [u8; 6] = [
0x52, // ASCII 'R'
((addr + n) >> 24) as u8,
((addr + n) >> 16) as u8,
((addr + n) >> 8) as u8,
(addr + n) as u8,
64,
];
let reply_len = 8 + DATA_SIZE;
eprintln!("Here in serial_read_region");
let reply = send_receive(&mut port, &cmd, reply_len, true);
if reply[0] != b'W' || reply[7+DATA_SIZE] != 0x6 {
eprintln!("serial_read_region: wrong read reply: {:?} shit: {}", reply, reply[7+DATA_SIZE]);
exit(-1);
}
//Compute checksum
let mut checksum: u8 = reply[1];
for idx in 2..6+DATA_SIZE {
checksum = checksum.overflowing_add(reply[idx]).0;
}
let expected_checksum = reply[6+DATA_SIZE];
if checksum != expected_checksum {
eprintln!("serial_read_region: wrong read checksum {}, expected {}", checksum, expected_checksum);
exit(-1);
}
output_data.extend(reply.into_iter());
n += DATA_SIZE;
}
let n = output_data.len();
let ptr = output_data.as_ptr() as *const c_void;
unsafe {
libc::memcpy(data as *mut c_void, ptr, n);
}
}
#[no_mangle]
pub extern "C" fn serial_write_region(addr: c_int, data: *mut c_uchar, nbytes: c_int) {
use serial::prelude::*;
let dev_path = unsafe {
match device_path {
Some(ref s) => s.clone(),
None => {
eprintln!("No serial device path set, exiting");
exit(-1);
}
}
};
let mut port = match serial::open(&dev_path) {
Ok(port) => port,
Err(err) => {
println!("{}", err);
exit(-1);
}
};
port.reconfigure(&|settings: &mut dyn SerialPortSettings| {
settings.set_baud_rate(serial::BaudRate::Baud115200)?;
settings.set_char_size(serial::CharSize::Bits8);
Ok(())
}).unwrap();
port.set_timeout(Duration::new(1,0)).unwrap();
const DATA_SIZE: usize = 16;
let num_bytes = nbytes as usize;
let addr = addr as usize;
let mut n = 0;
while n < num_bytes {
let mut cmd: [u8; 8 + DATA_SIZE] = [0; 8 + DATA_SIZE];
cmd[0] = 0x57; // ASCII 'W'
cmd[1] = ((addr + n) >> 24) as u8;
cmd[2] = ((addr + n) >> 16) as u8;
cmd[3] = ((addr + n) >> 8) as u8;
cmd[4] = (addr + n) as u8;
cmd[5] = DATA_SIZE as u8;
let ptr = unsafe {
cmd.as_mut_ptr().offset(6) as *mut c_void
};
unsafe {
libc::memcpy(ptr, data.offset(n as isize) as *const c_void, DATA_SIZE);
}
//Compute checksum
let mut checksum: u8 = cmd[1];
for idx in 2..(6+DATA_SIZE) {
checksum = checksum.overflowing_add(cmd[idx]).0;
}
cmd[DATA_SIZE+6] = checksum;
cmd[DATA_SIZE+7] = b'\x06'; // CMD_ACK byte
let reply = send_receive(&mut port, &cmd, 1, true);
if reply[0] != b'\x06' {
eprintln!("serial_write_region: wrong acknowledge {:?}, expected: {:?}", reply, b'\x06');
exit(-1);
}
n += DATA_SIZE as usize;
}
}
/// Query and return the device identification string.
/// On error, return None.
pub fn identify(dev_path: &str) -> Option<String> {
use serial::prelude::*;
let mut port = match serial::open(dev_path) {
Ok(port) => port,
Err(err) => {
println!("{}", err);
exit(-1);
}
};
port.reconfigure(&|settings: &mut dyn SerialPortSettings| {
settings.set_baud_rate(serial::BaudRate::Baud115200)?;
settings.set_char_size(serial::CharSize::Bits8);
Ok(())
}).unwrap();
port.set_timeout(Duration::new(1,0)).unwrap();
let program_cmd = b"PROGRAM";
let program_cmd_2 = b"\x02";
let program_ack = b"QX\x06";
let command_ack: u8 = b'\x06';
let output = send_receive(&mut port, program_cmd, 3, true);
if output != program_ack {
eprintln!("serial identify: wrong PRG acknowledge {:?}, expected {:?}", output, program_ack);
}
// Expected output should be something like:
// 49 44 38 36 38 55 56 45 00 56 31 30 32 00 00 06
// I D 8 6 8 U V E V 1 0 2
let output = send_receive(&mut port, program_cmd_2, 16, true);
if (output[0] != 'I' as u8) || (output[15] != command_ack) {
eprintln!("serial identify: wrong PRG2 reply {:?} expected 'I'...'\\6'", output);
}
return Some(String::from_utf8(output[1..7].to_vec()).unwrap())
}
/// Close the serial port.
pub fn close() {
unsafe {
serial_close();
}
}