diff --git a/dfu.c b/dfu.c index 22b8f7d..8399f58 100644 --- a/dfu.c +++ b/dfu.c @@ -261,7 +261,7 @@ static void set_address(uint32_t address) 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, (uint8_t)address, @@ -285,8 +285,10 @@ static void erase_block(uint32_t address) get_status(); wait_dfu_idle(); - fprintf(stderr, "#"); - fflush(stderr); + if (progress_flag) { + fprintf(stderr, "#"); + fflush(stderr); + } } 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. get_status(); @@ -377,25 +379,36 @@ void dfu_erase(int nbytes) md380_command(0x91, 0x01); usleep(100000); - erase_block(0x00000000); - erase_block(0x00010000); - erase_block(0x00020000); - erase_block(0x00030000); + if (start == 0) { + // Erase 256kbytes of configuration memory. + erase_block(0x00000000, 1); + erase_block(0x00010000, 1); + erase_block(0x00020000, 1); + erase_block(0x00030000, 1); - if (nbytes > 256*1024) { - erase_block(0x00110000); - erase_block(0x00120000); - erase_block(0x00130000); - erase_block(0x00140000); - erase_block(0x00150000); - erase_block(0x00160000); - erase_block(0x00170000); - erase_block(0x00180000); - erase_block(0x00190000); - erase_block(0x001a0000); - erase_block(0x001b0000); - erase_block(0x001c0000); - erase_block(0x001d0000); + if (finish > 256*1024) { + // Erase 768kbytes of extended configuration memory. + erase_block(0x00110000, 1); + erase_block(0x00120000, 1); + erase_block(0x00130000, 1); + erase_block(0x00140000, 1); + erase_block(0x00150000, 1); + erase_block(0x00160000, 1); + erase_block(0x00170000, 1); + erase_block(0x00180000, 1); + erase_block(0x00190000, 1); + erase_block(0x001a0000, 1); + erase_block(0x001b0000, 1); + erase_block(0x001c0000, 1); + erase_block(0x001d0000, 1); + } + } else { + // Erase callsign database. + int addr; + + for (addr=start; addr= 256) + if (bno >= 256 && bno < 2048) bno += 832; 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) { - if (bno >= 256) + if (bno >= 256 && bno < 2048) bno += 832; if (trace_flag) { diff --git a/main.c b/main.c index eb5068a..b46b77b 100644 --- a/main.c +++ b/main.c @@ -53,6 +53,8 @@ void usage() fprintf(stderr, _(" Store modified copy to a file 'device.img'.\n")); fprintf(stderr, _(" dmrconfig file.img\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, _(" -r Read codeplug from 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 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. setlocale(LC_ALL, ""); @@ -79,11 +81,12 @@ int main(int argc, char **argv) copyright = _("Copyright (C) 2018 Serge Vakulenko KK6ABQ"); trace_flag = 0; for (;;) { - switch (getopt(argc, argv, "tcwr")) { - case 't': ++trace_flag; continue; - case 'r': ++read_flag; continue; - case 'w': ++write_flag; continue; - case 'c': ++config_flag; continue; + switch (getopt(argc, argv, "tcwru")) { + case 't': ++trace_flag; continue; + case 'r': ++read_flag; continue; + case 'w': ++write_flag; continue; + case 'c': ++config_flag; continue; + case 'u': ++csv_flag; continue; default: usage(); case EOF: @@ -93,8 +96,8 @@ int main(int argc, char **argv) } argc -= optind; argv += optind; - if (write_flag + config_flag > 1) { - fprintf(stderr, "Only one of -w or -c options is allowed.\n"); + if (read_flag + write_flag + config_flag + csv_flag > 1) { + fprintf(stderr, "Only one of -r, -w, -c or -u options is allowed.\n"); usage(); } setvbuf(stdout, 0, _IOLBF, 0); @@ -156,6 +159,16 @@ int main(int argc, char **argv) } radio_print_config(conf, 1); 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 { if (argc != 1) usage(); diff --git a/md380.c b/md380.c index a0a3123..8b9b479 100644 --- a/md380.c +++ b/md380.c @@ -384,7 +384,7 @@ static void md380_upload(radio_device_t *radio, int cont_flag) { int bno; - dfu_erase(MEMSZ); + dfu_erase(0, MEMSZ); for (bno=0; bnoname); } // @@ -132,7 +132,7 @@ void radio_upload(int cont_flag) // // Read firmware image from the binary file. // -void radio_read_image(char *filename) +void radio_read_image(const char *filename) { FILE *img; struct stat st; @@ -171,7 +171,7 @@ void radio_read_image(char *filename) // // Save firmware image to the binary file. // -void radio_save_image(char *filename) +void radio_save_image(const char *filename) { FILE *img; @@ -188,7 +188,7 @@ void radio_save_image(char *filename) // // 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; char line [256], *p, *v; @@ -300,3 +300,26 @@ void radio_verify_config() 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); +} diff --git a/radio.h b/radio.h index 68bce67..474dd3c 100644 --- a/radio.h +++ b/radio.h @@ -60,22 +60,27 @@ void radio_print_config(FILE *out, int verbose); // // 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. // -void radio_save_image(char *filename); +void radio_save_image(const char *filename); // // 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. // -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. @@ -95,6 +100,7 @@ struct _radio_device_t { int (*parse_header)(radio_device_t *radio, 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 (*write_csv)(radio_device_t *radio, FILE *csv); int channel_count; }; diff --git a/util.h b/util.h index 5fe3c22..471f549 100644 --- a/util.h +++ b/util.h @@ -60,7 +60,7 @@ void print_hex(const unsigned char *data, int len); // const char *dfu_init(unsigned vid, unsigned pid); 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_write_block(int bno, unsigned char *data, int nbytes); void dfu_reboot(void); diff --git a/uv380.c b/uv380.c index 596d446..23cc114 100644 --- a/uv380.c +++ b/uv380.c @@ -52,6 +52,9 @@ #define OFFSET_CHANNELS 0x40000 #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_SETTINGS() ((general_settings_t*) &radio_mem[OFFSET_SETTINGS]) #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_GROUPLIST(i) ((grouplist_t*) &radio_mem[OFFSET_GLISTS + (i)*96]) #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_CHANNEL(ch) VALID_TEXT((ch)->name) @@ -343,6 +347,16 @@ typedef struct { uint16_t radio_name[16]; } 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 *BANDWIDTH[] = { "12.5", "20", "25", "25" }; 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; - dfu_erase(MEMSZ); + dfu_erase(0, MEMSZ); for (bno=0; bno '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 // @@ -2400,6 +2511,7 @@ radio_device_t radio_uv380 = { uv380_parse_header, uv380_parse_row, uv380_update_timestamp, + uv380_write_csv, }; //