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 { /* eprintln!"Read codeplug from file '{}'.", filename); let img = std::fs::File::open(filename).unwrap(); let metadata = std::fs::metadata(filename).unwrap(); let file_size = metadata.len(); match file_size { 851968 | 852533 => { } 262144 | 262709 => { } 1606528 => { } 131072 => { } size => { eprintln!("{}: Unrecognized file size {} bytes.", filename, size); exit(-1); } }; */ 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()); } }