273 lines
6.6 KiB
Rust
273 lines
6.6 KiB
Rust
use std::ffi::{CStr, CString};
|
|
use libc::{c_char, c_int, c_uint};
|
|
use std::os::unix::io::AsRawFd;
|
|
use std::process::exit;
|
|
|
|
use libc::FILE;
|
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
|
pub struct Radio {
|
|
ptr: *const radio_device_t
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct radio_tab_t {
|
|
ident: *const c_char,
|
|
device: *mut radio_device_t,
|
|
}
|
|
|
|
extern {
|
|
|
|
fn set_active_device(device: *const radio_device_t);
|
|
|
|
fn radio_print_config(device: *const radio_device_t, file: *const libc::FILE, verbose: c_int);
|
|
fn radio_read_image(filename: *const c_char) -> *const radio_device_t;
|
|
fn radio_parse_config(device: *const radio_device_t, filename: *const c_char);
|
|
fn radio_write_csv(device: *const radio_device_t, filename: *const c_char);
|
|
|
|
fn dfu_init(vid: c_uint, pid: c_uint) -> *const c_char;
|
|
fn dfu_reboot();
|
|
fn dfu_close();
|
|
|
|
fn hid_init(vid: c_int, pid: c_int) -> c_int;
|
|
fn hid_identify() -> *const c_char;
|
|
fn hid_close();
|
|
|
|
fn serial_init(vid: c_int, pid: c_int) -> c_int;
|
|
fn serial_identify() -> *const c_char;
|
|
fn serial_close();
|
|
|
|
fn get_radio_tab() -> *const radio_tab_t;
|
|
}
|
|
|
|
/// Connect to the radio via the serial port.
|
|
/// and identify the type of device.
|
|
pub fn connect() -> Radio {
|
|
unsafe {
|
|
let mut ident: *const c_char;
|
|
// Try TYT MD family.
|
|
ident = dfu_init(0x0483, 0xdf11);
|
|
if ident.is_null() {
|
|
// Try RD-5R, DM-1801 and GD-77.
|
|
if hid_init(0x15a2, 0x0073) >= 0 {
|
|
ident = hid_identify();
|
|
}
|
|
}
|
|
|
|
if ident.is_null() {
|
|
// Try AT-D868UV.
|
|
if serial_init(0x28e9, 0x018a) >= 0 {
|
|
ident = serial_identify();
|
|
}
|
|
}
|
|
|
|
if ident.is_null() {
|
|
eprintln!("No radio detected.");
|
|
eprintln!("Check your USB cable!");
|
|
exit(-1);
|
|
}
|
|
|
|
let ident_str = CStr::from_ptr(ident).to_str().unwrap();
|
|
|
|
let mut device: *const radio_device_t = std::ptr::null();
|
|
let mut radio_tab_ptr = get_radio_tab();
|
|
loop {
|
|
let ident_ptr = (*radio_tab_ptr).ident;
|
|
if ident_ptr.is_null() {
|
|
break;
|
|
}
|
|
|
|
let table_ident = CStr::from_ptr(ident_ptr).to_str().unwrap();
|
|
if ident_str.eq_ignore_ascii_case(table_ident) {
|
|
device = (*radio_tab_ptr).device;
|
|
break;
|
|
}
|
|
|
|
radio_tab_ptr = radio_tab_ptr.offset(1);
|
|
}
|
|
|
|
if device.is_null() {
|
|
eprintln!("Unrecognized radio '{}'.", ident_str);
|
|
exit(-1);
|
|
}
|
|
|
|
let name_ptr = (*device).name;
|
|
let name = CStr::from_ptr(name_ptr).to_str().unwrap();
|
|
println!("Connect to {}", name);
|
|
|
|
set_active_device(device);
|
|
return Radio { ptr: device as *mut radio_device_t };
|
|
}
|
|
}
|
|
|
|
/// Close the serial port.
|
|
pub fn disconnect() {
|
|
|
|
eprintln!("Close device.");
|
|
unsafe {
|
|
// Restore the normal radio mode.
|
|
dfu_reboot();
|
|
dfu_close();
|
|
hid_close();
|
|
serial_close();
|
|
}
|
|
}
|
|
|
|
/// Read firmware image from the device
|
|
pub fn download(radio: &Radio, trace: bool) {
|
|
|
|
let device = radio.ptr as *mut radio_device_t;
|
|
|
|
if !trace {
|
|
eprint!("Read device: ");
|
|
}
|
|
|
|
unsafe {
|
|
let download_fn = (*device).download.unwrap();
|
|
download_fn(device);
|
|
}
|
|
|
|
if !trace {
|
|
eprintln!(" done.");
|
|
}
|
|
}
|
|
|
|
/// Write firmware image to the device.
|
|
pub fn upload(radio: &Radio, cont_flag: c_int, trace: bool) {
|
|
let device = radio.ptr as *mut radio_device_t;
|
|
unsafe {
|
|
let is_compatible_fn = (*device).is_compatible.unwrap();
|
|
if is_compatible_fn(device) == 0 {
|
|
eprintln!("Incompatible image - cannot upload.");
|
|
exit(-1);
|
|
}
|
|
|
|
if !trace {
|
|
eprint!("Write device: ");
|
|
}
|
|
|
|
let upload_fn = (*device).upload.unwrap();
|
|
upload_fn(device, cont_flag);
|
|
|
|
if !trace {
|
|
eprintln!(" done.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// List all supported radios.
|
|
pub fn list() {
|
|
|
|
println!("Supported radios:");
|
|
|
|
unsafe {
|
|
let mut ptr = get_radio_tab();
|
|
loop {
|
|
let ident_ptr = (*ptr).ident;
|
|
if ident_ptr.is_null() {
|
|
break;
|
|
}
|
|
let name_ptr = (*(*ptr).device).name;
|
|
|
|
let name = CStr::from_ptr(name_ptr).to_str().unwrap().to_string();
|
|
println!(" {}", name);
|
|
|
|
ptr = ptr.offset(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check that the configuration is correct.
|
|
pub fn verify_config(radio: &Radio) {
|
|
let device = radio.ptr as *mut radio_device_t;
|
|
unsafe {
|
|
let verify_fn = (*device).verify_config.unwrap();
|
|
if verify_fn(device) == 0 {
|
|
// Message should be already printed.
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Read firmware image from the binary file.
|
|
pub fn read_image(filename: &str) -> Radio {
|
|
let filename = CString::new(filename.to_string()).unwrap();
|
|
let ptr = unsafe {
|
|
radio_read_image(filename.as_ptr())
|
|
};
|
|
Radio { ptr: ptr as *mut radio_device_t }
|
|
}
|
|
|
|
/// Save firmware image to the binary file.
|
|
pub fn save_image(radio: &Radio, filename: &str) {
|
|
|
|
eprintln!("Write codeplug to file '{}'.", filename);
|
|
|
|
let device = radio.ptr as *mut radio_device_t;
|
|
let file = std::fs::File::create(filename).unwrap();
|
|
let fd = file.as_raw_fd();
|
|
let mode = CString::new("wb").unwrap();
|
|
|
|
unsafe {
|
|
let file = libc::fdopen(fd, mode.as_ptr());
|
|
let save_image_fn = (*device).save_image.unwrap();
|
|
save_image_fn(device, file);
|
|
libc::fclose(file);
|
|
}
|
|
}
|
|
|
|
/// Read the configuration from text file, and modify the firmware.
|
|
pub fn parse_config(radio: &Radio, filename: &str) {
|
|
let device = radio.ptr;
|
|
let filename = CString::new(filename.to_string()).unwrap();
|
|
unsafe {
|
|
radio_parse_config(device, filename.as_ptr())
|
|
}
|
|
}
|
|
|
|
/// Print full information about the device configuration.
|
|
pub fn print_config(radio: &Radio, filename: &str) {
|
|
let device = radio.ptr;
|
|
let file = std::fs::File::create(filename).unwrap();
|
|
let fd = file.as_raw_fd();
|
|
let mode = CString::new("w").unwrap();
|
|
unsafe {
|
|
let file = libc::fdopen(fd, mode.as_ptr());
|
|
radio_print_config(device, file, 1);
|
|
libc::fclose(file);
|
|
}
|
|
}
|
|
|
|
pub fn print_config_to_stdout(radio: &Radio) {
|
|
let device = radio.ptr;
|
|
let mode = CString::new("w").unwrap();
|
|
unsafe {
|
|
let stdout = libc::fdopen(libc::STDOUT_FILENO, mode.as_ptr());
|
|
let verbosity = if libc::isatty(libc::STDOUT_FILENO) == 1 {
|
|
0
|
|
} else {
|
|
1
|
|
};
|
|
radio_print_config(device, stdout, verbosity);
|
|
}
|
|
}
|
|
|
|
/// Print generic information about the device.
|
|
pub fn print_version(radio: &Radio) {
|
|
let device = radio.ptr as *mut radio_device_t;
|
|
let mode = CString::new("w").unwrap();
|
|
unsafe {
|
|
let print_version_fn = (*device).print_version.unwrap();
|
|
print_version_fn(device, libc::fdopen(libc::STDOUT_FILENO, mode.as_ptr()));
|
|
}
|
|
}
|
|
|
|
/// Update CSV contacts database.
|
|
pub fn write_csv(radio: &Radio, filename: &str) {
|
|
let device = radio.ptr;
|
|
let filename = CString::new(filename.to_string()).unwrap();
|
|
unsafe {
|
|
radio_write_csv(device, filename.as_ptr());
|
|
}
|
|
}
|