dmrconfig/radio.c

377 lines
10 KiB
C
Raw Normal View History

2018-08-20 22:47:13 -07:00
/*
* Configuration Utility for DMR radios.
*
* Copyright (C) 2018 Serge Vakulenko, KK6ABQ
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include "radio.h"
#include "util.h"
2021-02-28 19:41:21 -08:00
typedef struct { char* ident; radio_device_t* device; } radio_tab_t;
static radio_tab_t radio_tab[] = {
{ "DR780", &radio_md380 }, // TYT MD-380, Retevis RT3, RT8
2018-11-27 19:11:57 -08:00
{ "MD390", &radio_md390 }, // TYT MD-390
{ "MD-UV380", &radio_uv380 }, // TYT MD-UV380
{ "MD-UV390", &radio_uv390 }, // TYT MD-UV390, Retevis RT3S
{ "2017", &radio_md2017 }, // TYT MD-2017, Retevis RT82
{ "MD9600", &radio_md9600 }, // TYT MD-9600
{ "BF-5R", &radio_rd5r }, // Baofeng RD-5R, TD-5R
{ "1801", &radio_dm1801 }, // Baofeng DM-1801
{ "DM-1701", &radio_rt84 }, // Baofeng DM-1701, Retevis RT84
{ "MD-760P", &radio_gd77 }, // Radioddity GD-77, version 3.1.1 and later
2018-10-13 21:16:03 -07:00
{ "D868UVE", &radio_d868uv }, // Anytone AT-D868UV
2018-12-10 09:59:34 -08:00
{ "D878UV", &radio_d878uv }, // Anytone AT-D878UV
2018-11-09 17:46:05 -08:00
{ "D6X2UV", &radio_dmr6x2 }, // BTECH DMR-6x2
{ "ZD3688", &radio_d900 }, // Zastone D900
{ "TP660", &radio_dp880 }, // Zastone DP880
{ "ZN><:", &radio_rt27d }, // Radtel RT-27D
{ 0, 0 }
};
2021-02-28 19:41:21 -08:00
radio_tab_t* get_radio_tab() {
return radio_tab;
}
2021-02-28 23:52:03 -08:00
static radio_device_t *active_device; // Device-dependent interface
radio_device_t* get_active_device() {
return active_device;
}
void set_active_device(radio_device_t* d) {
active_device = d;
}
unsigned char radio_mem [1024*1024*2]; // Radio memory contents, up to 2 Mbytes
2018-08-20 22:47:13 -07:00
//
// Print a generic information about the device.
//
void radio_print_version(radio_device_t* dev, FILE *out)
2018-08-20 22:47:13 -07:00
{
dev->print_version(dev, out);
2018-08-20 22:47:13 -07:00
}
//
// Read firmware image from the device.
//
void radio_download(radio_device_t* dev)
2018-08-20 22:47:13 -07:00
{
if (! trace_flag) {
2018-08-20 22:47:13 -07:00
fprintf(stderr, "Read device: ");
fflush(stderr);
}
2018-08-20 22:47:13 -07:00
dev->download(dev);
2018-08-20 22:47:13 -07:00
2018-08-31 11:56:21 -07:00
if (! trace_flag)
2018-08-20 22:47:13 -07:00
fprintf(stderr, " done.\n");
}
//
// Write firmware image to the device.
//
void radio_upload(radio_device_t* dev, int cont_flag)
2018-08-20 22:47:13 -07:00
{
// Check for compatibility.
if (! dev->is_compatible(dev)) {
2018-08-20 22:47:13 -07:00
fprintf(stderr, "Incompatible image - cannot upload.\n");
exit(-1);
}
2018-08-31 11:56:21 -07:00
if (! trace_flag) {
2018-08-20 22:47:13 -07:00
fprintf(stderr, "Write device: ");
2018-08-26 23:48:20 -07:00
fflush(stderr);
}
dev->upload(dev, cont_flag);
2018-08-20 22:47:13 -07:00
2018-08-31 11:56:21 -07:00
if (! trace_flag)
2018-08-20 22:47:13 -07:00
fprintf(stderr, " done.\n");
}
//
// Read firmware image from the binary file.
//
radio_device_t* radio_read_image(const char *filename)
2018-08-20 22:47:13 -07:00
{
FILE *img;
struct stat st;
2018-09-15 18:23:38 -07:00
char ident[8];
radio_device_t * dev = NULL;
2018-08-20 22:47:13 -07:00
2018-08-31 13:15:35 -07:00
fprintf(stderr, "Read codeplug from file '%s'.\n", filename);
2018-09-15 18:23:38 -07:00
img = fopen(filename, "rb");
if (! img) {
perror(filename);
exit(-1);
}
2018-08-20 22:47:13 -07:00
// Guess device type by file size.
if (stat(filename, &st) < 0) {
perror(filename);
exit(-1);
}
switch (st.st_size) {
case 851968:
2018-08-24 17:12:55 -07:00
case 852533:
dev = &radio_uv380;
2018-08-20 22:47:13 -07:00
break;
2018-08-24 20:13:27 -07:00
case 262144:
case 262709:
dev = &radio_md380;
2018-08-24 20:13:27 -07:00
break;
case 1606528:
2018-11-09 17:46:05 -08:00
if (fread(ident, 1, 8, img) != 8) {
fprintf(stderr, "%s: Cannot read header.\n", filename);
exit(-1);
}
fseek(img, 0, SEEK_SET);
if (memcmp(ident, "D868UVE", 7) == 0) {
dev = &radio_d868uv;
2018-12-10 09:59:34 -08:00
} else if (memcmp(ident, "D878UV", 6) == 0) {
dev = &radio_d878uv;
2018-11-09 17:46:05 -08:00
} else if (memcmp(ident, "D6X2UV", 6) == 0) {
dev = &radio_dmr6x2;
2018-11-09 17:46:05 -08:00
} else {
fprintf(stderr, "%s: Unrecognized header '%.6s'\n",
filename, ident);
exit(-1);
}
2018-10-15 13:46:38 -07:00
break;
2018-09-15 15:43:23 -07:00
case 131072:
2018-09-15 18:23:38 -07:00
if (fread(ident, 1, 8, img) != 8) {
fprintf(stderr, "%s: Cannot read header.\n", filename);
exit(-1);
}
if (memcmp(ident, "BF-5R", 5) == 0) {
dev = &radio_rd5r;
2018-09-15 18:23:38 -07:00
} else if (memcmp(ident, "MD-760P", 7) == 0) {
dev = &radio_gd77;
} else if (memcmp(ident, "1801", 4) == 0) {
dev = &radio_dm1801;
2018-09-17 17:28:21 -07:00
} else if (memcmp(ident, "MD-760", 6) == 0) {
fprintf(stderr, "Old Radioddity GD-77 v2.6 image not supported!\n");
exit(-1);
2018-09-15 18:23:38 -07:00
} else {
fprintf(stderr, "%s: Unrecognized header '%.6s'\n",
filename, ident);
exit(-1);
}
fseek(img, 0, SEEK_SET);
2018-09-15 15:43:23 -07:00
break;
2018-08-20 22:47:13 -07:00
default:
fprintf(stderr, "%s: Unrecognized file size %u bytes.\n",
filename, (int) st.st_size);
exit(-1);
}
dev->read_image(dev, img);
2018-08-20 22:47:13 -07:00
fclose(img);
2021-02-28 23:52:03 -08:00
set_active_device(dev);
return dev;
2018-08-20 22:47:13 -07:00
}
//
// Save firmware image to the binary file.
//
void radio_save_image(radio_device_t* device, const char *filename)
2018-08-20 22:47:13 -07:00
{
FILE *img;
2018-08-31 13:15:35 -07:00
fprintf(stderr, "Write codeplug to file '%s'.\n", filename);
2018-09-07 23:38:52 -07:00
img = fopen(filename, "wb");
2018-08-20 22:47:13 -07:00
if (! img) {
perror(filename);
exit(-1);
}
2018-08-29 17:44:46 -07:00
device->save_image(device, img);
2018-08-20 22:47:13 -07:00
fclose(img);
}
//
// Read the configuration from text file, and modify the firmware.
//
void radio_parse_config(radio_device_t* device, const char *filename)
2018-08-20 22:47:13 -07:00
{
FILE *conf;
char line [256], *p, *v;
int table_id = 0, table_dirty = 0;
fprintf(stderr, "Read configuration from file '%s'.\n", filename);
conf = fopen(filename, "r");
if (! conf) {
perror(filename);
exit(-1);
}
2018-08-30 00:45:29 -07:00
device->channel_count = 0;
2018-08-20 22:47:13 -07:00
while (fgets(line, sizeof(line), conf)) {
line[sizeof(line)-1] = 0;
// Strip comments.
v = strchr(line, '#');
if (v)
*v = 0;
// Strip trailing spaces and newline.
v = line + strlen(line) - 1;
while (v >= line && (*v=='\n' || *v=='\r' || *v==' ' || *v=='\t'))
*v-- = 0;
// Ignore comments and empty lines.
p = line;
if (*p == 0)
continue;
if (*p != ' ') {
// Table finished.
table_id = 0;
// Find the value.
v = strchr(p, ':');
if (! v) {
// Table header: get table type.
2018-08-29 17:44:46 -07:00
table_id = device->parse_header(device, p);
2018-08-20 22:47:13 -07:00
if (! table_id) {
badline: fprintf(stderr, "Invalid line: '%s'\n", line);
exit(-1);
}
table_dirty = 0;
continue;
}
// Parameter.
*v++ = 0;
// Skip spaces.
while (*v == ' ' || *v == '\t')
v++;
2018-08-29 17:44:46 -07:00
device->parse_parameter(device, p, v);
2018-08-20 22:47:13 -07:00
} else {
// Table row or comment.
// Skip spaces.
// Ignore comments and empty lines.
while (*p == ' ' || *p == '\t')
p++;
if (*p == '#' || *p == 0)
continue;
if (! table_id) {
goto badline;
}
2018-08-29 17:44:46 -07:00
if (! device->parse_row(device, table_id, ! table_dirty, p)) {
2018-08-20 22:47:13 -07:00
goto badline;
}
table_dirty = 1;
}
}
fclose(conf);
2018-08-29 21:04:13 -07:00
device->update_timestamp(device);
2018-08-20 22:47:13 -07:00
}
//
// Print full information about the device configuration.
//
void radio_print_config(radio_device_t* device, FILE *out, int verbose)
2018-08-20 22:47:13 -07:00
{
if (verbose) {
char buf [40];
time_t t;
struct tm *tmp;
t = time(NULL);
tmp = localtime(&t);
if (! tmp || ! strftime(buf, sizeof(buf), "%Y/%m/%d ", tmp))
buf[0] = 0;
fprintf(out, "#\n");
2018-11-12 23:35:09 -08:00
fprintf(out, "# Configuration generated %sby dmrconfig, version %s\n",
buf, version);
2018-08-20 22:47:13 -07:00
fprintf(out, "#\n");
}
2018-08-29 17:44:46 -07:00
device->print_config(device, out, verbose);
2018-08-20 22:47:13 -07:00
}
//
// Check the configuration is correct.
//
void radio_verify_config(radio_device_t* device)
{
if (!device->verify_config(device)) {
// Message should be already printed.
exit(-1);
}
}
//
// Update contacts database on the device.
//
void radio_write_csv(radio_device_t* device, const char *filename)
{
FILE *csv;
if (!device->write_csv) {
fprintf(stderr, "%s does not support CSV database.\n", device->name);
return;
}
2018-09-07 23:38:52 -07:00
csv = fopen(filename, "r");
if (! csv) {
perror(filename);
return;
}
fprintf(stderr, "Read file '%s'.\n", filename);
device->write_csv(device, csv);
fclose(csv);
}
//
// Check for compatible radio model.
//
int radio_is_compatible(const char *name)
{
2021-02-28 23:52:03 -08:00
radio_device_t* dev = get_active_device();
int i;
for (i=0; radio_tab[i].ident; i++) {
// Radio is compatible when it has the same parse routine.
2021-02-28 23:52:03 -08:00
if (dev->parse_parameter == radio_tab[i].device->parse_parameter &&
strcasecmp(name, radio_tab[i].device->name) == 0) {
return 1;
}
}
return 0;
}