Compare commits
5 Commits
9015d9f8e0
...
c1913d9943
Author | SHA1 | Date | |
---|---|---|---|
|
c1913d9943 | ||
|
b76a115054 | ||
|
c56fa32b2c | ||
|
963dc072c9 | ||
|
23fc703abd |
@ -2,7 +2,7 @@
|
|||||||
name = "dmrconfig"
|
name = "dmrconfig"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Greg Shuflin <greg.shuflin@protonmail.com>"]
|
authors = ["Greg Shuflin <greg.shuflin@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
@ -13,6 +13,7 @@ crate-type = ["staticlib"]
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
serial = "0.4"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = "0.53.1"
|
bindgen = "0.53.1"
|
||||||
|
4
serial.c
4
serial.c
@ -703,7 +703,7 @@ again:
|
|||||||
return (char*)&reply[1];
|
return (char*)&reply[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
void serial_read_region(int addr, unsigned char *data, int nbytes)
|
void c_serial_read_region(int addr, unsigned char *data, int nbytes)
|
||||||
{
|
{
|
||||||
static const int DATASZ = 64;
|
static const int DATASZ = 64;
|
||||||
unsigned char cmd[6], reply[8 + DATASZ];
|
unsigned char cmd[6], reply[8 + DATASZ];
|
||||||
@ -741,7 +741,7 @@ again:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void serial_write_region(int addr, unsigned char *data, int nbytes)
|
void c_serial_write_region(int addr, unsigned char *data, int nbytes)
|
||||||
{
|
{
|
||||||
//static const int DATASZ = 64;
|
//static const int DATASZ = 64;
|
||||||
static const int DATASZ = 16;
|
static const int DATASZ = 16;
|
||||||
|
29
src/radio.rs
29
src/radio.rs
@ -44,9 +44,6 @@ extern {
|
|||||||
fn hid_init(vid: c_int, pid: c_int) -> c_int;
|
fn hid_init(vid: c_int, pid: c_int) -> c_int;
|
||||||
fn hid_identify() -> *const c_char;
|
fn hid_identify() -> *const c_char;
|
||||||
fn hid_close();
|
fn hid_close();
|
||||||
|
|
||||||
fn serial_identify(s: *const c_char) -> *const c_char;
|
|
||||||
fn serial_close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,23 +61,28 @@ unsafe fn set_active_device(d: *const radio_device_t) {
|
|||||||
/// Connect to the radio via the serial port.
|
/// Connect to the radio via the serial port.
|
||||||
/// and identify the type of device.
|
/// and identify the type of device.
|
||||||
pub fn connect() -> Radio {
|
pub fn connect() -> Radio {
|
||||||
unsafe {
|
|
||||||
|
let ident_str = (|| {
|
||||||
let mut ident: *const c_char;
|
let mut ident: *const c_char;
|
||||||
// Try TYT MD family.
|
// Try TYT MD family.
|
||||||
ident = dfu_init(0x0483, 0xdf11);
|
ident = unsafe { dfu_init(0x0483, 0xdf11) };
|
||||||
if ident.is_null() {
|
if ident.is_null() {
|
||||||
// Try RD-5R, DM-1801 and GD-77.
|
// Try RD-5R, DM-1801 and GD-77.
|
||||||
if hid_init(0x15a2, 0x0073) >= 0 {
|
if unsafe { hid_init(0x15a2, 0x0073) } >= 0 {
|
||||||
ident = hid_identify();
|
ident = unsafe { hid_identify() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ident.is_null() {
|
if ident.is_null() {
|
||||||
// Try AT-D868UV.
|
// Try AT-D868UV.
|
||||||
let trace_flag = false; //TODO fix
|
let trace_flag = false; //TODO fix
|
||||||
if let Some(device_path) = crate::serial::serial_init(0x28e9, 0x018a, trace_flag) {
|
if let Some(device_path) = crate::serial::serial_init(0x28e9, 0x018a, trace_flag) {
|
||||||
let ptr = device_path.as_ptr() as *mut c_char;
|
if let Some(identifier) = crate::serial::identify(&device_path) {
|
||||||
ident = serial_identify(ptr);
|
unsafe {
|
||||||
|
crate::serial::device_path = Some(device_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +92,10 @@ pub fn connect() -> Radio {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ident_str = CStr::from_ptr(ident).to_str().unwrap();
|
unsafe { CStr::from_ptr(ident).to_str().unwrap().to_string() }
|
||||||
|
})();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
|
||||||
let mut device: *const radio_device_t = std::ptr::null();
|
let mut device: *const radio_device_t = std::ptr::null();
|
||||||
|
|
||||||
@ -124,7 +129,7 @@ pub fn disconnect() {
|
|||||||
dfu_reboot();
|
dfu_reboot();
|
||||||
dfu_close();
|
dfu_close();
|
||||||
hid_close();
|
hid_close();
|
||||||
serial_close();
|
crate::serial::close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
236
src/serial.rs
236
src/serial.rs
@ -1,8 +1,15 @@
|
|||||||
use std::ffi::{CStr};
|
use std::ffi::{CStr};
|
||||||
use libc::{c_char};
|
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 {
|
extern {
|
||||||
fn find_path(vid: libc::c_int, pid: libc::c_int) -> *const c_char;
|
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.
|
/// Connect to the specified device.
|
||||||
@ -22,12 +29,231 @@ pub fn serial_init(vid: u32, pid: u32, trace_flag: bool) -> Option<String> {
|
|||||||
Some(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.
|
/// Query and return the device identification string.
|
||||||
/// On error, return None.
|
/// On error, return None.
|
||||||
//
|
pub fn identify(dev_path: &str) -> Option<String> {
|
||||||
pub fn serial_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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
Loading…
Reference in New Issue
Block a user