Implement -u option: update ContactsCSV database.

This commit is contained in:
Serge Vakulenko 2018-09-05 21:03:03 -07:00
parent 24b6bafcaf
commit 5144316fb9
7 changed files with 210 additions and 43 deletions

57
dfu.c
View File

@ -261,7 +261,7 @@ static void set_address(uint32_t address)
wait_dfu_idle(); wait_dfu_idle();
} }
static void erase_block(uint32_t address) static void erase_block(uint32_t address, int progress_flag)
{ {
unsigned char cmd[5] = { 0x41, unsigned char cmd[5] = { 0x41,
(uint8_t)address, (uint8_t)address,
@ -285,9 +285,11 @@ static void erase_block(uint32_t address)
get_status(); get_status();
wait_dfu_idle(); wait_dfu_idle();
if (progress_flag) {
fprintf(stderr, "#"); fprintf(stderr, "#");
fflush(stderr); fflush(stderr);
} }
}
static const char *identify() static const char *identify()
{ {
@ -369,7 +371,7 @@ void dfu_close()
} }
} }
void dfu_erase(int nbytes) void dfu_erase(unsigned start, unsigned finish)
{ {
// Enter Programming Mode. // Enter Programming Mode.
get_status(); get_status();
@ -377,25 +379,36 @@ void dfu_erase(int nbytes)
md380_command(0x91, 0x01); md380_command(0x91, 0x01);
usleep(100000); usleep(100000);
erase_block(0x00000000); if (start == 0) {
erase_block(0x00010000); // Erase 256kbytes of configuration memory.
erase_block(0x00020000); erase_block(0x00000000, 1);
erase_block(0x00030000); erase_block(0x00010000, 1);
erase_block(0x00020000, 1);
erase_block(0x00030000, 1);
if (nbytes > 256*1024) { if (finish > 256*1024) {
erase_block(0x00110000); // Erase 768kbytes of extended configuration memory.
erase_block(0x00120000); erase_block(0x00110000, 1);
erase_block(0x00130000); erase_block(0x00120000, 1);
erase_block(0x00140000); erase_block(0x00130000, 1);
erase_block(0x00150000); erase_block(0x00140000, 1);
erase_block(0x00160000); erase_block(0x00150000, 1);
erase_block(0x00170000); erase_block(0x00160000, 1);
erase_block(0x00180000); erase_block(0x00170000, 1);
erase_block(0x00190000); erase_block(0x00180000, 1);
erase_block(0x001a0000); erase_block(0x00190000, 1);
erase_block(0x001b0000); erase_block(0x001a0000, 1);
erase_block(0x001c0000); erase_block(0x001b0000, 1);
erase_block(0x001d0000); erase_block(0x001c0000, 1);
erase_block(0x001d0000, 1);
}
} else {
// Erase callsign database.
int addr;
for (addr=start; addr<finish; addr+=0x00010000) {
erase_block(addr, (addr & 0x00070000) == 0x00070000);
}
} }
// Zero address. // Zero address.
@ -404,7 +417,7 @@ void dfu_erase(int nbytes)
void dfu_read_block(int bno, uint8_t *data, int nbytes) void dfu_read_block(int bno, uint8_t *data, int nbytes)
{ {
if (bno >= 256) if (bno >= 256 && bno < 2048)
bno += 832; bno += 832;
if (trace_flag) { if (trace_flag) {
@ -427,7 +440,7 @@ void dfu_read_block(int bno, uint8_t *data, int nbytes)
void dfu_write_block(int bno, uint8_t *data, int nbytes) void dfu_write_block(int bno, uint8_t *data, int nbytes)
{ {
if (bno >= 256) if (bno >= 256 && bno < 2048)
bno += 832; bno += 832;
if (trace_flag) { if (trace_flag) {

21
main.c
View File

@ -53,6 +53,8 @@ void usage()
fprintf(stderr, _(" Store modified copy to a file 'device.img'.\n")); fprintf(stderr, _(" Store modified copy to a file 'device.img'.\n"));
fprintf(stderr, _(" dmrconfig file.img\n")); fprintf(stderr, _(" dmrconfig file.img\n"));
fprintf(stderr, _(" Display configuration from the codeplug image.\n")); fprintf(stderr, _(" Display configuration from the codeplug image.\n"));
fprintf(stderr, _(" dmrconfig -u [-t] file.csv\n"));
fprintf(stderr, _(" Update contacts database.\n"));
fprintf(stderr, _("Options:\n")); fprintf(stderr, _("Options:\n"));
fprintf(stderr, _(" -r Read codeplug from the radio.\n")); fprintf(stderr, _(" -r Read codeplug from the radio.\n"));
fprintf(stderr, _(" -w Write codeplug to the radio.\n")); fprintf(stderr, _(" -w Write codeplug to the radio.\n"));
@ -63,7 +65,7 @@ void usage()
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int read_flag = 0, write_flag = 0, config_flag = 0; int read_flag = 0, write_flag = 0, config_flag = 0, csv_flag = 0;
// Set locale and message catalogs. // Set locale and message catalogs.
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
@ -79,11 +81,12 @@ int main(int argc, char **argv)
copyright = _("Copyright (C) 2018 Serge Vakulenko KK6ABQ"); copyright = _("Copyright (C) 2018 Serge Vakulenko KK6ABQ");
trace_flag = 0; trace_flag = 0;
for (;;) { for (;;) {
switch (getopt(argc, argv, "tcwr")) { switch (getopt(argc, argv, "tcwru")) {
case 't': ++trace_flag; continue; case 't': ++trace_flag; continue;
case 'r': ++read_flag; continue; case 'r': ++read_flag; continue;
case 'w': ++write_flag; continue; case 'w': ++write_flag; continue;
case 'c': ++config_flag; continue; case 'c': ++config_flag; continue;
case 'u': ++csv_flag; continue;
default: default:
usage(); usage();
case EOF: case EOF:
@ -93,8 +96,8 @@ int main(int argc, char **argv)
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (write_flag + config_flag > 1) { if (read_flag + write_flag + config_flag + csv_flag > 1) {
fprintf(stderr, "Only one of -w or -c options is allowed.\n"); fprintf(stderr, "Only one of -r, -w, -c or -u options is allowed.\n");
usage(); usage();
} }
setvbuf(stdout, 0, _IOLBF, 0); setvbuf(stdout, 0, _IOLBF, 0);
@ -156,6 +159,16 @@ int main(int argc, char **argv)
} }
radio_print_config(conf, 1); radio_print_config(conf, 1);
fclose(conf); fclose(conf);
} else if (csv_flag) {
// Update contacts database on the device.
if (argc != 1)
usage();
radio_connect();
radio_write_csv(argv[0]);
radio_disconnect();
} else { } else {
if (argc != 1) if (argc != 1)
usage(); usage();

View File

@ -384,7 +384,7 @@ static void md380_upload(radio_device_t *radio, int cont_flag)
{ {
int bno; int bno;
dfu_erase(MEMSZ); dfu_erase(0, MEMSZ);
for (bno=0; bno<MEMSZ/1024; bno++) { for (bno=0; bno<MEMSZ/1024; bno++) {
dfu_write_block(bno, &radio_mem[bno*1024], 1024); dfu_write_block(bno, &radio_mem[bno*1024], 1024);

31
radio.c
View File

@ -67,7 +67,6 @@ void radio_connect()
{ {
// Only TYT MD family for now. // Only TYT MD family for now.
const char *ident = dfu_init(0x0483, 0xdf11); const char *ident = dfu_init(0x0483, 0xdf11);
fprintf(stderr, "Connect to %s.\n", ident);
if (strcasecmp(ident, "DR780") == 0) { // TYT MD-380, Retevis RT3, RT8 if (strcasecmp(ident, "DR780") == 0) { // TYT MD-380, Retevis RT3, RT8
device = &radio_md380; device = &radio_md380;
@ -91,6 +90,7 @@ void radio_connect()
ident); ident);
exit(-1); exit(-1);
} }
fprintf(stderr, "Connect to %s.\n", device->name);
} }
// //
@ -132,7 +132,7 @@ void radio_upload(int cont_flag)
// //
// Read firmware image from the binary file. // Read firmware image from the binary file.
// //
void radio_read_image(char *filename) void radio_read_image(const char *filename)
{ {
FILE *img; FILE *img;
struct stat st; struct stat st;
@ -171,7 +171,7 @@ void radio_read_image(char *filename)
// //
// Save firmware image to the binary file. // Save firmware image to the binary file.
// //
void radio_save_image(char *filename) void radio_save_image(const char *filename)
{ {
FILE *img; FILE *img;
@ -188,7 +188,7 @@ void radio_save_image(char *filename)
// //
// Read the configuration from text file, and modify the firmware. // Read the configuration from text file, and modify the firmware.
// //
void radio_parse_config(char *filename) void radio_parse_config(const char *filename)
{ {
FILE *conf; FILE *conf;
char line [256], *p, *v; char line [256], *p, *v;
@ -300,3 +300,26 @@ void radio_verify_config()
exit(-1); exit(-1);
} }
} }
//
// Update contacts database on the device.
//
void radio_write_csv(const char *filename)
{
FILE *csv;
if (!device->write_csv) {
fprintf(stderr, "%s does not support CSV database.\n", device->name);
exit(-1);
}
csv = fopen(filename, "rb");
if (! csv) {
perror(filename);
exit(-1);
}
fprintf(stderr, "Read file '%s'.\n", filename);
device->write_csv(device, csv);
fclose(csv);
}

14
radio.h
View File

@ -60,22 +60,27 @@ void radio_print_config(FILE *out, int verbose);
// //
// Read firmware image from the binary file. // Read firmware image from the binary file.
// //
void radio_read_image(char *filename); void radio_read_image(const char *filename);
// //
// Save firmware image to the binary file. // Save firmware image to the binary file.
// //
void radio_save_image(char *filename); void radio_save_image(const char *filename);
// //
// Read the configuration from text file, and modify the firmware. // Read the configuration from text file, and modify the firmware.
// //
void radio_parse_config(char *filename); void radio_parse_config(const char *filename);
// //
// Check the configuration. // Check the configuration.
// //
void radio_verify_config(); void radio_verify_config(void);
//
// Update CSV contacts database.
//
void radio_write_csv(const char *filename);
// //
// Device-dependent interface to the radio. // Device-dependent interface to the radio.
@ -95,6 +100,7 @@ struct _radio_device_t {
int (*parse_header)(radio_device_t *radio, char *line); int (*parse_header)(radio_device_t *radio, char *line);
int (*parse_row)(radio_device_t *radio, int table_id, int first_row, char *line); int (*parse_row)(radio_device_t *radio, int table_id, int first_row, char *line);
void (*update_timestamp)(radio_device_t *radio); void (*update_timestamp)(radio_device_t *radio);
void (*write_csv)(radio_device_t *radio, FILE *csv);
int channel_count; int channel_count;
}; };

2
util.h
View File

@ -60,7 +60,7 @@ void print_hex(const unsigned char *data, int len);
// //
const char *dfu_init(unsigned vid, unsigned pid); const char *dfu_init(unsigned vid, unsigned pid);
void dfu_close(void); void dfu_close(void);
void dfu_erase(int nbytes); void dfu_erase(unsigned start, unsigned finish);
void dfu_read_block(int bno, unsigned char *data, int nbytes); void dfu_read_block(int bno, unsigned char *data, int nbytes);
void dfu_write_block(int bno, unsigned char *data, int nbytes); void dfu_write_block(int bno, unsigned char *data, int nbytes);
void dfu_reboot(void); void dfu_reboot(void);

114
uv380.c
View File

@ -52,6 +52,9 @@
#define OFFSET_CHANNELS 0x40000 #define OFFSET_CHANNELS 0x40000
#define OFFSET_CONTACTS 0x70000 #define OFFSET_CONTACTS 0x70000
#define CALLSIGN_START 0x00200000 // Start of callsign database
#define CALLSIGN_FINISH 0x01000000 // End of callsign database
#define GET_TIMESTAMP() (&radio_mem[OFFSET_TIMESTMP]) #define GET_TIMESTAMP() (&radio_mem[OFFSET_TIMESTMP])
#define GET_SETTINGS() ((general_settings_t*) &radio_mem[OFFSET_SETTINGS]) #define GET_SETTINGS() ((general_settings_t*) &radio_mem[OFFSET_SETTINGS])
#define GET_CHANNEL(i) ((channel_t*) &radio_mem[OFFSET_CHANNELS + (i)*64]) #define GET_CHANNEL(i) ((channel_t*) &radio_mem[OFFSET_CHANNELS + (i)*64])
@ -61,6 +64,7 @@
#define GET_CONTACT(i) ((contact_t*) &radio_mem[OFFSET_CONTACTS + (i)*36]) #define GET_CONTACT(i) ((contact_t*) &radio_mem[OFFSET_CONTACTS + (i)*36])
#define GET_GROUPLIST(i) ((grouplist_t*) &radio_mem[OFFSET_GLISTS + (i)*96]) #define GET_GROUPLIST(i) ((grouplist_t*) &radio_mem[OFFSET_GLISTS + (i)*96])
#define GET_MESSAGE(i) ((uint16_t*) &radio_mem[OFFSET_MSG + (i)*288]) #define GET_MESSAGE(i) ((uint16_t*) &radio_mem[OFFSET_MSG + (i)*288])
#define GET_CALLSIGN(m,i) ((callsign_t*) ((m) + 0x4003 + (i)*120))
#define VALID_TEXT(txt) (*(txt) != 0 && *(txt) != 0xffff) #define VALID_TEXT(txt) (*(txt) != 0 && *(txt) != 0xffff)
#define VALID_CHANNEL(ch) VALID_TEXT((ch)->name) #define VALID_CHANNEL(ch) VALID_TEXT((ch)->name)
@ -343,6 +347,16 @@ typedef struct {
uint16_t radio_name[16]; uint16_t radio_name[16];
} general_settings_t; } general_settings_t;
//
// Callsign database (CSV).
//
typedef struct {
unsigned dmrid : 24; // DMR id
unsigned _unused : 8; // 0xff
char callsign[16]; // ascii zero terminated
char name[100]; // name, nickname, city, state, country
} callsign_t;
static const char *POWER_NAME[] = { "Low", "Low", "Mid", "High" }; static const char *POWER_NAME[] = { "Low", "Low", "Mid", "High" };
static const char *BANDWIDTH[] = { "12.5", "20", "25", "25" }; static const char *BANDWIDTH[] = { "12.5", "20", "25", "25" };
static const char *CONTACT_TYPE[] = { "-", "Group", "Private", "All" }; static const char *CONTACT_TYPE[] = { "-", "Group", "Private", "All" };
@ -402,7 +416,7 @@ static void uv380_upload(radio_device_t *radio, int cont_flag)
{ {
int bno; int bno;
dfu_erase(MEMSZ); dfu_erase(0, MEMSZ);
for (bno=0; bno<MEMSZ/1024; bno++) { for (bno=0; bno<MEMSZ/1024; bno++) {
dfu_write_block(bno, &radio_mem[bno*1024], 1024); dfu_write_block(bno, &radio_mem[bno*1024], 1024);
@ -2383,6 +2397,103 @@ static int uv380_verify_config(radio_device_t *radio)
return 1; return 1;
} }
//
// Write CSV file to contacts database.
//
static void uv380_write_csv(radio_device_t *radio, FILE *csv)
{
uint8_t *mem;
char line[256], *callsign, *name;
int id, bno, nbytes, nrecords = 0;
callsign_t *cs;
// Allocate 14Mbytes of memory.
nbytes = CALLSIGN_FINISH - CALLSIGN_START;
mem = malloc(nbytes);
if (!mem) {
fprintf(stderr, "Out of memory!\n");
return;
}
memset(mem, 0xff, nbytes);
// Parse CSV file.
while (fgets(line, sizeof(line), csv)) {
if (line[0] < '0' || line[0] > '9') {
// Skip header.
continue;
}
id = strtoul(line, 0, 10);
if (id < 1 || id > 0xffffff) {
fprintf(stderr, "Bad id: %d\n", id);
fprintf(stderr, "Line: '%s'\n", line);
return;
}
callsign = strchr(line, ',');
if (! callsign) {
fprintf(stderr, "Cannot find callsign!\n");
fprintf(stderr, "Line: '%s'\n", line);
return;
}
*callsign++ = 0;
name = strchr(callsign, ',');
if (! name) {
fprintf(stderr, "Cannot find name!\n");
fprintf(stderr, "Line: '%s,%s'\n", line, callsign);
return;
}
*name++ = 0;
//printf("%-10d%-10s%s", id, callsign, name);
cs = GET_CALLSIGN(mem, nrecords);
nrecords++;
// Fill callsign structure.
cs->dmrid = id;
strncpy(cs->callsign, callsign, sizeof(cs->callsign));
strncpy(cs->name, name, sizeof(cs->name));
}
fprintf(stderr, "Total %d contacts.\n", nrecords);
// Preserve 1kbyte at 0x0200000-0x02003ff.
//dfu_read_block(CALLSIGN_START/1024, &mem[0], 1024);
// Number of contacts.
mem[0] = nrecords >> 16;
mem[1] = nrecords >> 8;
mem[2] = nrecords;
radio_progress = 0;
if (! trace_flag) {
fprintf(stderr, "Erase contacts: ");
fflush(stderr);
}
// Erase whole region.
dfu_erase(CALLSIGN_START, CALLSIGN_FINISH);
if (! trace_flag) {
fprintf(stderr, " done.\n");
fprintf(stderr, "Write contacts: ");
fflush(stderr);
}
// Write callsigns.
for (bno = CALLSIGN_START/1024; bno < CALLSIGN_FINISH/1024; bno++) {
dfu_write_block(bno, &mem[bno*1024 - CALLSIGN_START], 1024);
++radio_progress;
if (radio_progress % 512 == 0) {
fprintf(stderr, "#");
fflush(stderr);
}
}
if (! trace_flag)
fprintf(stderr, " done.\n");
free(mem);
}
// //
// TYT MD-UV380 // TYT MD-UV380
// //
@ -2400,6 +2511,7 @@ radio_device_t radio_uv380 = {
uv380_parse_header, uv380_parse_header,
uv380_parse_row, uv380_parse_row,
uv380_update_timestamp, uv380_update_timestamp,
uv380_write_csv,
}; };
// //