Project

General

Profile

New Model #2311 ยป qs-tg-uv2.c

Mike Nix, 03/06/2015 08:21 AM

 
1

    
2
/*
3

    
4
   Quansheng TG-UV2 Utility by Mike Nix <mnix@wanm.com.au>
5

    
6
   Currently only reads the device memory, and displays the contents nicely formatted.
7
   
8
   
9
   Supports:
10
      All channel config, except CTSS/DCS settings (we know where they are, just haven't
11
      bothered to interpret them yet)
12

    
13
      All the settings configurable via Options in the original windows utility.
14

    
15
      Both of the known keypad dances for enabling/disabling bands are detected/displayed.
16
      and yes, we can do the same thing by updating the config.
17

    
18
      Saves the config read from the device to a file (tguv2.dat)
19
        **** THIS IS NOT THE SAME FORMAT USED BY THE WINDOWS UTILITY ****
20
      The windows utility saves in some proprietry format... we just dump
21
      a copy of the radio memory to disk :)
22

    
23
      Reads a file saved above, and uploads it to the radio.      
24
      
25
   TODO:
26
      There are at least 5 memory locations that do stuff I can't figure out (config->unk*)
27
      
28
      I never found a way to enable transmit on the 470-520 band
29
      (Australian UHF CB is at 476-477 Mhz).  Probably need a firmware hack for this :(
30
      
31
      Command-line options for settings so we can change the config easily.
32

    
33
      Find a copy of the firmware, and the update utility so we can really open it up.
34

    
35

    
36
   There is a command line option to manually set any byte in the config area for
37
   testing purposes, but this is not the way to configure your radio normally :)
38

    
39
   There is another command line option for sending arbitrary data to the radio in 
40
   programming mode...
41
   
42

    
43
   Other Info:
44
   -----------
45
   There are positive responses (ACK) to the following commands if followed
46
   by any other byte:
47
      G, O, P, Q  but have no idea what they do....
48
   
49
   The known commands for programming and getting the model number are:
50
   \002PnOGdAM	Enter Programming Mode
51
   M\002	Get Model Number? (Returns 'P5555' followed by F4 00 00)
52
   R		Read config memory
53
   W		Write config memory
54
   
55
   Any 1-byte after entering program mode followed by 0x02 will return the model number
56
   
57
   Other commands will not work until after you get the model number.
58

    
59
   R command is followed by addr_hi, addr_lo, length
60
   W command is followed by addr_hi, addr_lo, length then data bytes
61

    
62
   A null byte is sent by the radio every time the display changes... no idea what use that is.
63
   
64
   Further testing reveals that any 8 bytes written will do to start programming mode.
65
   ** maybe there is a special sequence for firmware mode?
66
   Actually, any sequence of more than 1 byte containing "M" seems to hit program mode.
67
     
68
*/
69

    
70

    
71
#include <string.h>
72
#include <stdio.h>
73
#include <stdlib.h>
74
#include <stdint.h>
75
#include <sys/types.h>
76
#include <sys/stat.h>
77
#include <sys/select.h>
78
#include <fcntl.h>
79
#include <termios.h>
80
#include <sys/ioctl.h>
81
#include <unistd.h>
82
#include <errno.h>
83

    
84
// Quansheng TG-UV2 uses 9600,N,8,2
85
#define RADIO_BAUD B9600
86

    
87
#define ACK 0x06
88

    
89
#define TGUV2_MEMSIZE 0x2000
90
#define TGUV2_CHANNELS 200
91
#define TGUV2_BANDS 8
92

    
93
// shorthand forms of integer types for convenience
94
typedef uint8_t  u8;
95
typedef uint16_t u16;
96
typedef uint32_t u32;
97
typedef uint64_t u64;
98

    
99
#pragma pack(1)
100
struct tguv2_channel {
101
   u32	freq; // frequency in 10s of Hz, stored in BCD
102
   u32	txofs;// offset to tx frequency, 10s of Hz, stored in BCD
103
   u8   rc_lo;
104
   u8	tc_lo;
105
   u8	code_type;  // low nibble is rc_hi, high nibble is tc_hi
106
                    // or could be type number (0-3)
107
   u8	flags1;
108
   u8	flags2;
109
   u8	flags3;    // always 0xFF ??
110
   u8	bandwidth; // channel step / bandwidth - 6=25, 9=100
111
   u8	tx_power;
112
};
113

    
114
struct tguv2_channel2 { // extra channel data
115
   u8   name[6];
116
   u8	data[10];
117
};
118

    
119
#define TXPOWER_LOW 2
120
#define TXPOWER_MID 1
121
#define TXPOWER_HI  0
122

    
123
#define CF1_SHIFT_POS	0x01
124
#define CF1_SHIFT_NEG	0x02
125
#define CF2_REVERSE_ON	0x01
126
#define CF2_SCRAMBLE_ON	0x02
127
#define CF2_WideFM	0x10
128

    
129
struct vfo {
130
   u8 current; // memory slot for this vfo: 0-199 = channel mode, 200-204=vfo band
131
   u8 chan;    // last used channel no
132
   u8 memno;   // last used band (as mem slot number 200-204)
133
};
134

    
135
struct tguv2_config {
136
   struct tguv2_channel	channels[TGUV2_CHANNELS];	// the channel memories
137
   struct tguv2_channel bands[TGUV2_BANDS];		// current / most recent settings for each band
138
   u8 cflags[TGUV2_CHANNELS];				// scan flags and band numbers
139
   u8 bandid[TGUV2_BANDS];				// band ids (F0 - F4)
140
   u8 reserved1[0x30];					// unused?, all 0xFF
141

    
142
   u8 unk1; // 00
143
   u8 squelch;						// 0 - 9
144
   u8 time_out_timer;					// 01 (minutes)
145
   u8 priority_channel;
146
   
147
   u8 keylock;						// 01=off, 00=on
148
   u8 busy_lockout;					// 01=off, 00=on
149
   u8 vox;						// 00=off, 1-9
150
   u8 unk2; // FF
151
   
152
   u8 keybeep;						// 00=on, 01=off
153
   u8 display;						// 00=Freq, 01=Channel, 02=Name
154
   u8 step; 						// FF=5, ??
155
   u8 unk3; // FF
156
   
157
   u8 unk4; // 00
158
   u8 mode;						// 00=DualWatch, 01=cross-band, other=off
159
   u8 end_tone;						// 0=on, 1=off
160
   u8 vfo_model;					// 0=VFO Disabled, other=VFO Enabled
161
   
162
   struct vfo vfo[2];
163
   
164
   u8 unk5; // 00
165
   u8 reserved2[9];					// Unused?, ALL 0xFF
166
   
167
   // band_restrict can be toggled by holding PTT+MON at Power on, until you hear a second beep
168
   u8 band_restrict;					// !=01 = all bands enabled
169
   
170
   // this can be toggled by holding BAND during power on until the display is ******
171
   // then enter 350390 on the keypad.
172
   u8 txen350390;					// FF=unset, 00=DIS, 01=EN (!=01 == DIS) 
173
   
174
   u8 reserved3[0xDE];					// Unused?, ALL 0xFF
175

    
176
   struct tguv2_channel2 channels2[TGUV2_CHANNELS];	// channel names etc
177
};
178

    
179
#define CFLAG_SCAN	0x80
180
#define CFLAG_BANDMASK	0x07
181

    
182
#pragma pack()
183

    
184
#define PMODE_NONE	-1
185
#define PMODE_PROGRAM	2
186
#define PMODE_FIRMWARE	1
187

    
188
double bandwidthmap[]={ 5.0, 6.25, 10.0, 12.5, 15, 20, 25, 30, 50, 100, 101, 102, 103, 104, 105, 5.0 };
189

    
190
// current radio state
191
int radio_mode=PMODE_NONE;
192

    
193
// Serial port options.
194
int debug=3;
195
int TX_enabled=0;
196

    
197
int TX_auto_on=1;
198
int RX_auto_on=1;
199

    
200

    
201
void hexdump(unsigned char *data, int len)
202
{
203
  int i;
204
  for (i=0; i<len; i++)
205
      printf(" %02x", data[i]);
206
  printf("  ");
207
  for (i=0; i<len; i++)
208
      printf("%c",  isprint(data[i]) ? data[i] : '.');
209
}
210

    
211
void TX_on(int fd)
212
{
213
  // raise RTS
214
  if (TX_auto_on) {
215
     // hardware works this out automatically so don't do anything
216
     }
217
  else {
218
     int status;
219
     if (debug>2) printf("RTS ON\r\n");
220
     ioctl(fd, TIOCMGET, &status);
221
     status |= TIOCM_RTS;
222
     if (ioctl(fd, TIOCMSET, &status)) {
223
        perror("ioctl");
224
        }
225
     }
226
  TX_enabled=1;
227
}
228

    
229
void TX_off(int fd)
230
{
231
  // lower RTS
232
  if (TX_auto_on) {
233
     // hardware does this automatically so don't do anything
234
     }
235
  else {
236
     int status;
237
     if (debug>2) printf("RTS OFF\r\n");
238
     tcdrain(fd);
239
     ioctl(fd, TIOCMGET, &status);
240
     status &= ~TIOCM_RTS;
241
     ioctl(fd, TIOCMSET, &status);
242
     }
243
  TX_enabled=0;
244
}
245

    
246
void RX_on(int fd)
247
{
248
  // raise DTR
249
  if (RX_auto_on) {
250
     }
251
  else {
252
     int status;
253
     if (debug>2) printf("DTR ON\r\n");
254
     ioctl(fd, TIOCMGET, &status);
255
     status |= TIOCM_DTR;
256
     if (ioctl(fd, TIOCMSET, &status)) {
257
        perror("ioctl");
258
        }
259
     }
260
}
261

    
262
void RX_off(int fd)
263
{
264
  // lower DTR
265
  if (RX_auto_on) {
266
     }
267
  else {
268
     int status;
269
     if (debug>2) printf("DTR OFF\r\n");
270
     ioctl(fd, TIOCMGET, &status);
271
     status &= ~TIOCM_DTR;
272
     ioctl(fd, TIOCMSET, &status);
273
     }
274
}
275

    
276

    
277
void TX(int fd, unsigned char *data, int len)
278
{
279
  if (!TX_enabled) TX_on(fd);
280
  
281
  if (debug>3) {
282
     printf("TX:");
283
     hexdump(data,len);
284
     printf("\r\n");
285
     }
286
  write(fd, data, len);
287
}
288

    
289
int RX(int fd, unsigned char *data, int len)
290
{
291
  int rc = read(fd, data, len);
292
   
293
  if (debug>3 && rc>0) {
294
     printf("RX:");
295
     hexdump(data,rc);
296
     printf("\r\n");
297
     }
298
  return rc;
299
}
300

    
301

    
302
#define TEST1
303

    
304
int start_pgm_mode(int fd, int pmode)
305
{
306
  u8 buf[8];
307

    
308
  if (pmode==PMODE_FIRMWARE) {
309
     buf[0]=0x02;
310
     buf[1]='F';
311
     buf[2]='i';
312
     buf[3]='r';
313
     buf[4]='m';
314
     buf[5]='w';
315
     buf[6]='a';
316
     buf[7]='r';
317
     }
318
  else {
319
#ifndef TEST1
320
     buf[0]=0x02;
321
     buf[1]=0x50;   // "PnOGdAM"
322
     buf[2]=0x6E;
323
     buf[3]=0x4F;
324
     buf[4]=0x47;
325
     buf[5]=0x64;
326
     buf[6]=0x41;
327
     buf[7]=0x4D;
328
#else
329
     buf[0]=0x02;
330
     buf[1]='P';   // "PnOGdAM"
331
     buf[2]='n';
332
     buf[3]='O';
333
     buf[4]='G';
334
     buf[5]='d';
335
     buf[6]='A';
336
     buf[7]='M';
337
#endif
338
     }
339
     
340
  TX(fd, buf, 8);
341
  
342
  if (RX(fd, buf, 1)==1) {
343
     if (buf[0]==ACK) {
344
        printf("Radio detected\n");
345
        radio_mode=pmode;
346
        return 0;
347
        }
348
     printf("Bad response from radio: 0x%02x\n", buf[0]);
349
     return -ENODEV;
350
     }
351
  printf("No response from radio\n");
352
  return -ENODEV;
353
}
354

    
355
int read_model(int fd, u8 *model) {
356
  u8 buf[8];
357
  int rc;
358
#ifndef TEST1
359
  buf[0]=0x4D; // 'M'
360
  buf[1]=0x02;
361
#else
362
  buf[0]='M'; // 'M'
363
  buf[1]=0x02;
364
#endif
365
  TX(fd, buf, 2);
366
  rc=RX(fd, model, 8);
367
  if (rc!=8) {
368
     printf("Error reading model data, got %d bytes:", rc);
369
     if (rc>0) hexdump(model,rc);
370
     printf("\n");
371
     return -1;
372
     }
373
  buf[0]=ACK;
374
  TX(fd, buf, 1);
375
  if (RX(fd, buf, 1)!=1) {
376
     printf("Didn't get expected ACK response\n");
377
     return -1;
378
     }
379
  if (buf[0]!=ACK) {
380
     printf("Expected ACK, got 0x%02x\n", buf[0]);
381
     }
382
  printf("Model: ");
383
  hexdump(model, 8);
384
  printf("\r\n");
385
  return 0;
386
}
387

    
388
int read_block(int fd, void *dest, u8 *buf, u16 addr, u8 len) {
389
  
390
  printf("Reading memory 0x%04x\r", addr);
391
  memset(buf, 0, 12);
392
  buf[0]='R';
393
  buf[1]=(addr >> 8) & 0xFF;
394
  buf[2]=addr & 0xFF;
395
  buf[3]=len;
396
  TX(fd, buf, 4);
397
  if (RX(fd, buf, len+4)==len+4) {
398
     if (buf[0]==0x57 && buf[3]==len &&
399
         (buf[1]<<8 | buf[2]) == addr
400
        ) {
401
        memcpy(dest, buf+4, len);
402
        return 1;
403
        }
404
     else {
405
        printf("\nunexpected data for address 0x%04x\n", addr);
406
        }
407
     } 
408
  else {
409
     printf("\nFailed read for address 0x%04x\n", addr);
410
     }
411
  return 0;
412
}
413

    
414
int read_block2(int fd, void *dest, u8 *buf, u32 addr, u8 len) {
415
  
416
  printf("Reading memory 0x%04x\r", addr);
417
  memset(buf, 0, 12);
418
  buf[0]='R';
419
  buf[1]=(addr >> 24) & 0xFF;
420
  buf[2]=(addr >> 16) & 0xFF;
421
  buf[3]=(addr >> 8) & 0xFF;
422
  buf[4]=addr & 0xFF;
423
  buf[5]=len;
424
  TX(fd, buf, 6);
425
  if (RX(fd, buf, len+4)==len+4) {
426
     if (buf[0]==0x57 && buf[5]==len &&
427
         (buf[3]<<8 | buf[4]) == addr
428
        ) {
429
        memcpy(dest, buf+4, len);
430
        return 1;
431
        }
432
     else {
433
        printf("\nunexpected data for address 0x%04x\n", addr);
434
        }
435
     } 
436
  else {
437
     printf("\nFailed read for address 0x%04x\n", addr);
438
     }
439
  return 0;
440
}
441

    
442
int write_block(int fd, u8 *src, u16 addr, u8 len) {
443

    
444
  u8 buf[4];
445
  
446
  printf("Writing memory 0x%04x\r", addr);
447
  buf[0]='W';
448
  buf[1]=(addr >> 8) & 0xFF;
449
  buf[2]=addr & 0xFF;
450
  buf[3]=len;
451
  TX(fd, buf, 4);
452
  TX(fd, src, len);
453
  if (RX(fd, buf, 1)==1) {
454
     if (buf[0]!=ACK)
455
        printf("Expected ACK, got 0x%02x for address 0x%04d\n", buf[0], addr);
456
     }
457
  else {
458
     printf("\nDid not receive ACK for address 0x%04x\n", addr);
459
     }
460
}
461

    
462

    
463
int test_cmd(int fd, unsigned char *cmd, u8 sendlen, u8 expectlen) {
464

    
465
  printf("Radio Mode: ");
466
  switch (radio_mode) {
467
     case PMODE_NONE:		printf("NONE");     break;
468
     case PMODE_PROGRAM:	printf("PROGRAM");  break;
469
     case PMODE_FIRMWARE:	printf("FIRMWARE"); break;
470
     default : printf("Unknown: %d", radio_mode);   break;
471
     }
472
  printf("\n");
473

    
474
  printf("Test Command: (%d bytes) ", sendlen);
475
  hexdump(cmd, sendlen);
476
  printf("\n");
477
  TX(fd, cmd, sendlen);
478

    
479
  int dbg=debug;
480
  debug=4;
481
  int rc=RX(fd, cmd, expectlen);
482
  debug=dbg;
483
  printf("Test Command: RX returned %d (expected %d bytes)\n", rc, expectlen);
484
  return 0;
485
}
486

    
487

    
488
int read_memory(int fd, struct tguv2_config *config, int setuponly) {
489
  int addr, end;
490
  u8 buf[12];
491
  u8 *data=(u8*)config;
492
  
493
  addr=0;
494
  end=TGUV2_MEMSIZE;
495
  if (setuponly) {
496
     addr=0x0C80;
497
     end =0x0F00;
498
     }
499
     
500
  for (; addr < end; addr += 8) {
501
      read_block(fd, data+addr, buf, addr, 8);
502
      }
503
  return TGUV2_MEMSIZE;
504
}
505

    
506

    
507
int write_memory(int fd, struct tguv2_config *config, int setuponly) {
508
  int addr, end;
509
  u8 *data=(u8*)config;
510
  
511
  addr=0;
512
  end=TGUV2_MEMSIZE;
513
  if (setuponly) {
514
     addr=0x0C80;
515
     end =0x0F00;
516
     }
517

    
518
  for (; addr < end; addr += 8) {
519
      write_block(fd, data+addr, addr, 8);
520
      }
521
  return TGUV2_MEMSIZE;
522
}
523

    
524
int read_channel(int fd, struct tguv2_config *config, u8 channel) {\
525
  int addr, end;
526
  u8 buf[12];
527
  u8 *data;
528

    
529
  if (radio_mode != PMODE_PROGRAM) {
530
     printf("read_channel called with radio not in PROGRAM mode\n");
531
     return;
532
     }
533
  
534
  // read the main info
535
  data=(u8*)(&config->channels[channel]);
536
  addr=data - (u8*)config;
537
  read_block(fd, data, buf, addr, 8);
538
  read_block(fd, data+8, buf, addr+8, 8);
539

    
540
  // read the extra info (name)
541
  data=(u8*)(&config->channels2[channel]);
542
  addr=data - (u8*)config;
543
  read_block(fd, data, buf, addr, 8);
544
  read_block(fd, data+8, buf, addr+8, 8);
545
  
546
}
547

    
548
int write_channel(int fd, struct tguv2_config *config, u8 channel) {\
549
  int addr, end;
550
  u8 *data;
551

    
552
  if (radio_mode != PMODE_PROGRAM) {
553
     printf("write_channel called with radio not in PROGRAM mode\n");
554
     return;
555
     }
556
  
557
  // read the main info
558
  data=(u8*)(&config->channels[channel]);
559
  addr=data - (u8*)config;
560
  write_block(fd, data, addr, 8);
561
  write_block(fd, data+8, addr+8, 8);
562

    
563
  // read the extra info (name)
564
  data=(u8*)(&config->channels2[channel]);
565
  addr=data - (u8*)config;
566
  write_block(fd, data, addr, 8);
567
  write_block(fd, data+8, addr+8, 8);
568
  
569
}
570

    
571
void save_memory(char *fname, struct tguv2_config *data)
572
{
573
  int fd=open(fname, O_CREAT | O_WRONLY, 0660);
574
  write(fd, data, TGUV2_MEMSIZE);
575
  close(fd);
576
}
577

    
578
void load_memory(char *fname, struct tguv2_config *data)
579
{
580
  int fd=open(fname, O_RDONLY, 0660);
581
  read(fd, data, TGUV2_MEMSIZE);
582
  close(fd);
583
}
584

    
585
u32 bcdtoint(u32 bcd)
586
{
587
  u32 i=0;
588
  int j;
589
  bcd = be32toh(bcd);
590
  for (j=7; j>=0; j--) {
591
      i+=(bcd >> (4*j)) & 0x0F;
592
      i*=10;
593
      }
594
  return i;
595
}
596

    
597
const char charmap[]="0123456789ABCDEFGHIJKLMNOPWRSTUVWXYZ |* +-\0";
598

    
599
char *channel_name(u8 *enc, u8 len)
600
{
601
  u8 i;
602
  static char buf[40];
603
  for (i=0; i<len; i++) {
604
      if (enc[i] <= 43) 
605
              buf[i]=charmap[enc[i]];
606
         else buf[i]='.';
607
      }
608
  buf[i++]=0;
609
  return buf;
610
}
611

    
612
void name_channel(u8 *enc, char *name, u8 len)
613
{
614
  int i,j;
615
  for (i=0; i<len && *name; i++, name++) {
616
      for (j=0; charmap[j] && charmap[j]!=*name; j++);
617
      if (charmap[j]) *enc++=j;
618
      }
619
}
620

    
621
void print_channel(struct tguv2_channel *chan)
622
{
623
  u32 rxf, txf;
624

    
625
  rxf = bcdtoint(chan->freq);
626
  txf = rxf;
627
  if (chan->flags1 & CF1_SHIFT_POS) 
628
     txf += bcdtoint(chan->txofs);
629
  if (chan->flags1 & CF1_SHIFT_NEG)
630
     txf -= bcdtoint(chan->txofs);
631

    
632
  printf("%10.6f  %10.6f  %6.2f ",
633
         rxf / 1000000.0,
634
         txf / 1000000.0,
635
         bandwidthmap[chan->bandwidth & 0x0F]);
636
     
637
  printf("%-3s %-3s %-4s  ",
638
         (chan->flags2 & CF2_SCRAMBLE_ON ? "OFF" : "ON"),
639
         (chan->flags2 & CF2_REVERSE_ON ? "OFF" : "ON"),
640
         (chan->flags2 & CF2_WideFM ? "Wide" : "Narr"));
641
         
642
  switch (chan->tx_power) {
643
         case TXPOWER_LOW: printf("LOW"); break;
644
         case TXPOWER_MID: printf("MID"); break;
645
         case TXPOWER_HI : printf("HI "); break;
646
         default: printf("???");
647
         }
648

    
649
}
650
    
651
void list_channels(struct tguv2_config *conf)
652
{
653
  int c;
654
  
655
  printf(" CH   RX          TX          Step  Scr REV Wide  Pow  SCN  BAND  CH_Name\n");
656
  for (c=0; c<TGUV2_CHANNELS; c++) {
657
      if (conf->channels[c].freq != 0xFFFFFFFF) {
658
         printf("%3d: ", c);
659
         print_channel(&(conf->channels[c]));
660

    
661
         printf("  %-3s   %02X",
662
            conf->cflags[c] & CFLAG_SCAN ? "Yes" : "No",
663
            conf->bandid[conf->cflags[c] & CFLAG_BANDMASK]);
664

    
665
         printf("  %s\n", channel_name(conf->channels2[c].name, 6));
666
 
667
         }
668
      }
669

    
670
}
671

    
672

    
673
void list_bands(struct tguv2_config *conf)
674
{
675
  int b;
676
  printf("          RX          TX          Step  Scr REV Wide  Pow\n");
677
  for (b=0; b<TGUV2_BANDS; b++) {
678
      if (conf->bands[b].freq != 0xFFFFFFFF) {
679
         printf("Band %02X: ", conf->bandid[b]);
680
         print_channel(&(conf->bands[b]));
681
         printf("\n");
682
         }
683
      }
684
}
685

    
686

    
687
void list_config(struct tguv2_config *conf)
688
{
689
  printf("Squelch         : %d\n", conf->squelch);
690
  printf("Time-Out-Timer  : %d seconds\n", conf->time_out_timer*60);
691
  printf("Priority Channel: ");
692
  if (conf->priority_channel < 200) printf("%d\n", conf->priority_channel);
693
                               else printf("OFF\n");
694
  printf("Busy Lockout    : %s  (%02x)\n", conf->busy_lockout==0x01 ? "OFF" : "ON", conf->busy_lockout);
695
  printf("VOX             : %d\n", conf->vox);
696
  printf("Key Lock        : %s\n", conf->keylock==0x01 ? "OFF" : "ON");
697
  printf("Key Beep        : %s\n", conf->keybeep==0x01 ? "OFF" : "ON");
698
  printf("Freq Step       : %1.2f  (%02x)\n", bandwidthmap[conf->step & 0x0F], conf->step & 0x0F, conf->step);
699
  printf("End Tone        : %s\n", conf->end_tone ? "OFF" : "ON");
700
  printf("VFO Model       : %s  (%02x)\n", conf->vfo_model==0x00 ? "No" : "Yes", conf->vfo_model);
701

    
702
  printf("Mode            : ");
703
  switch (conf->mode) {
704
     case 0x00: printf("Dual Watch"); break;
705
     case 0x01: printf("Cross-Band"); break;
706
     default:   printf("Normal (Channel/VFO)  (%02x)", conf->mode);
707
     }
708
  printf("\n");
709

    
710
  printf("Channel Display : ");
711
  switch (conf->display) {
712
     case 0x00: printf("Ch/Frequency"); break;
713
     case 0x01: printf("Channel");      break;
714
     case 0x02: printf("Ch/Name");      break;
715
     default:   printf("???  (%02x)", conf->display);
716
     }
717
  printf("\n");
718
  
719
  int v;
720
  for (v=0; v<2; v++) {
721
      if (conf->vfo[v].current < 200) {
722
         printf("VFO%d            : Channel %d\n", v, conf->vfo[v].chan);
723
         }
724
      else {
725
         printf("VFO%d            : Band %02X, Freq %10.6f\n",
726
                v,
727
                conf->bandid[conf->vfo[v].memno - 200],
728
                bcdtoint(conf->bands[conf->vfo[v].memno - 200].freq)/1000000.0);
729
         }
730
      }
731
      
732
  printf("Band Restrict   : %s\n", conf->band_restrict == 0x01 ? "Enabled" : "Disabled");
733
  printf("350-390 Mhz TX  : %s\n", conf->txen350390 == 0x01 ? "Enabled" : "Disabled");
734
  printf("test: %02x %02x\n", conf->band_restrict, conf->txen350390);
735
  
736
  printf("Unknown Bytes   : %02X %02X %02X %02X %02X\n",
737
        conf->unk1, conf->unk2, conf->unk3, conf->unk4, conf->unk5);
738
        
739
  printf("Reserved Bytes  :\n1: ");
740
  hexdump(conf->reserved1, sizeof(conf->reserved1));
741
  printf("\n2: ");
742
  hexdump(conf->reserved2, sizeof(conf->reserved2));
743
  printf("\n3: ");
744
  hexdump(conf->reserved3, sizeof(conf->reserved3));
745
  printf("\n");
746
}
747

    
748
int openserial(char *port)
749
{
750
  struct termios sp;
751
  if (debug>1) printf("Opening %s\r\n", port);
752
  
753
  // open non-blocking otherwise open can block until carrier appears
754
  int fd = open(port, O_RDWR | O_NONBLOCK);
755
  if (fd<0) return -ENODEV; 
756

    
757
  memset(&sp,0,sizeof(sp));
758
  sp.c_iflag=0;
759
  sp.c_oflag=0;
760
  sp.c_cflag=CS8|CREAD|CLOCAL|CSTOPB;
761
  sp.c_lflag=0;
762
  sp.c_cc[VMIN]=100;
763
  sp.c_cc[VTIME]=5;
764
  cfsetispeed(&sp,RADIO_BAUD);
765
  cfsetospeed(&sp,RADIO_BAUD);
766

    
767
  tcsetattr(fd, TCSANOW, &sp);
768
  if (debug>3) printf("tcflush()\r\n");
769

    
770
  // now switch to blocking IO because that's what we want
771
  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
772
  
773
  tcflush(fd, TCIOFLUSH);
774
  return fd;
775
}
776

    
777

    
778
void help()
779
{
780
  printf("Quansheng TG-UV2 Radio control tool 0.1\n");
781
  printf("Usage: radio <options>\n");
782
  printf("	-r f	read config from file f\n");
783
  printf("	-w f	write config to file f\n");
784
  printf("	-g	get config from radio\n");
785
  printf("	-p	put config to radio\n");
786
  printf("	-l	list current config\n");	
787
  printf("	-c	list current channels\n");
788
  printf("\n");
789
  printf("To select a port other than /dev/ttyUSB0 specify the name as the first option\n");
790
  
791
  printf("\n\n");
792
  printf("Debugging Tools: (DO NOT USE)\n");
793
  printf("	-s addr value		- set config byte at addr to value\n");
794
  printf("	-f addr value len	- fill len bytes from addr with value\n");
795
  printf("	-e len			- expect len byte reply to test command\n");
796
  printf("	-T <data>		- Test command in program mode\n");
797
  printf("	-t <data>		- Test command.\n");
798
  printf("	-F <data>		- Test command in firmware mode ** DANGEROUS **\n");
799
  printf("		<data> consists of a length byte, followed by length characters\n");
800
  printf("			or numeric values (escape digits with \\)\n");
801
  printf("	-D addr len		- dump a chunk of memory\n");
802
}
803

    
804

    
805
int init_radio(int *serial, char *port, u8 *model, int pmode) {
806
  if (*serial<0) {
807
     *serial=openserial(port);
808
     if (*serial < 0) {
809
        perror("Error opening port");
810
        return 0;
811
        }
812
     RX_on(*serial);
813

    
814
     if (start_pgm_mode(*serial, pmode)==0 && read_model(*serial, model)==0) {
815
        return 1;
816
        }
817
     close(*serial);
818
     *serial=-1;
819
     return 0;
820
     }
821
  if (radio_mode != pmode) {
822
     printf("Can't change programming mode during a run.\n");
823
     printf("You can not mix and match firmware updates and programming updates\n");
824
     return 0;
825
     }
826
  return 1;
827
}
828

    
829
main (int argc, char *argv[])
830
{
831
  struct timeval tv;
832
  fd_set rfds, tfds;
833
  int retval;
834
  int quit=0;
835
  int i;
836
  int setaddr=-1;
837
  int setvalue=0x00;
838
  int setlen=0;
839
  
840
  unsigned char cmd_buf[20];
841
  int cmd_len=0;
842
  int cmd_expect=8;
843
  
844
  char *port="/dev/ttyUSB0";
845
  int serial = -1;
846

    
847
  u8 model[20];
848
  int config_valid=0, channels_valid=0;
849
  struct tguv2_config *config;
850
  
851
  if (sizeof(*config) > TGUV2_MEMSIZE) {
852
     printf("Program Error: tguv2_config structure is too big for device memory!\n");
853
     printf("   sizeof(config) = %d, TGUV2_MEMSIZE=%d\n", sizeof(*config), TGUV2_MEMSIZE);
854
     exit(0);
855
     }
856
  
857
  config = malloc(TGUV2_MEMSIZE);
858

    
859
  i=1;
860
  while (i<argc) {
861
     if (strncmp(argv[i],"/dev/",5)==0) {
862
        port=argv[i];
863
        }
864
     else if (strcmp(argv[i],"-h")==0) {
865
        help();
866
        quit=1;
867
        }
868
     else if (strcmp(argv[i],"-q")==0) {
869
        quit=1;
870
        }
871
     else if (strcmp(argv[i],"-r")==0) { // read config from file
872
        i++;
873
        load_memory(argv[i], config);
874
        config_valid=1;
875
        channels_valid=1;
876
        }
877
     else if (strcmp(argv[i],"-w")==0) { // write config to file
878
        i++;
879
        if (!config_valid && channels_valid) {
880
           printf("Write config to file: load a config with -r or -g first\n");
881
           }
882
        else {
883
           save_memory(argv[i], config);
884
           }
885
        }
886
     else if (strcmp(argv[i],"-g")==0) { // get config from radio
887
        if (init_radio(&serial, port, model, PMODE_PROGRAM)) {
888
           read_memory(serial, config, 0);
889
           config_valid=1;
890
           channels_valid=1;
891
           }
892
        }
893
     else if (strcmp(argv[i],"-p")==0) { // put config to radio
894
        if (!config_valid && channels_valid) {
895
           printf("Put Config: load a config with -r or -g first\n");
896
           }
897
        else if (init_radio(&serial, port, model, PMODE_PROGRAM)) {
898
           write_memory(serial, config, 0);
899
           }
900
        }
901
     else if (strcmp(argv[i], "-c")==0) { // list channels in loaded config
902
        if (config_valid) {
903
           list_channels(config);
904
           }
905
        else {
906
           printf("List Channels: load a config with -r or -g first\n");
907
           }
908
        }
909
     else if (strcmp(argv[i], "-l")==0) { // list loaded config
910
        if (config_valid) {
911
           list_bands(config);
912
           list_config(config);
913
           }
914
        else {
915
           printf("List Config: load a config with -r or -g first\n");
916
           }
917
        }
918
     else if (strcmp(argv[i],"-s")==0) { // set config bytes
919
        i++;
920
        setaddr=strtol(argv[i], NULL, 0);
921
        i++;
922
        setvalue=strtol(argv[i], NULL, 0);
923
        setlen=1;
924

    
925
        if (setlen>0 && init_radio(&serial, port, model, PMODE_PROGRAM)) {
926
           if (!config_valid)  {
927
              read_memory(serial, config, 1); // only read the config area
928
              config_valid=1;
929
              }
930

    
931
           memset(((void *)config + setaddr), setvalue & 0xFF, setlen & 0xFF);
932

    
933
           write_memory(serial, config, 1); // only write the config area
934
           }
935
        }
936
     else if (strcmp(argv[i],"-f")==0) { // fill config bytes
937
        i++;
938
        setaddr=strtol(argv[i], NULL, 0);
939
        i++;
940
        setvalue=strtol(argv[i], NULL, 0);
941
        i++;
942
        setlen=strtol(argv[i], NULL, 0);
943

    
944
        if (setlen>0 && init_radio(&serial, port, model, PMODE_PROGRAM)) {
945
           if (!config_valid) {
946
              read_memory(serial, config, 1); // only read the config area
947
              config_valid=1;
948
              }
949
              
950
           memset(((void *)config + setaddr), setvalue & 0xFF, setlen & 0xFF);
951

    
952
           write_memory(serial, config, 1); // only write the config area
953
           }
954
        }
955
     else if (strcmp(argv[i],"-D")==0) { // dump a section of memory
956
        i++;
957
        setaddr=strtol(argv[i], NULL, 0);
958
        i++;
959
        setlen=strtol(argv[i], NULL, 0);
960
        if (setlen>0 && init_radio(&serial, port, model, radio_mode == PMODE_NONE ? PMODE_PROGRAM : radio_mode)) {
961
           char *data=malloc(setlen + 16);
962
           char buf[12];
963
           int p=0;
964
           
965
           // read_block = 16bit address, read_block2 = 32bit address
966
           while (p<setlen) {
967
              if (read_block(serial, data+p, buf, setaddr+p, 8) ||
968
                  read_block(serial, data+p+8, buf, setaddr+p+8, 8)
969
                 ) {
970
                 hexdump(data+p, 16);
971
                 printf("\n");
972
                 p+=16;
973
                 }
974
              else {
975
                 p=setlen;
976
                 }
977
              }
978
           free(data);
979
           }
980
        }
981
     else if (strcmp(argv[i],"-e")==0) { // set expected response
982
        i++;
983
        cmd_expect=strtol(argv[i],NULL,0);
984
        }
985
     else if ((strcmp(argv[i],"-t")==0)  ||
986
              (strcmp(argv[i],"-T")==0) ||
987
              (strcmp(argv[i],"-F")==0)
988
             )
989
        {				// test commands
990
        int from_program_mode=0;
991
        int j;
992
        char *ep;
993
        if (strcmp(argv[i],"-T")==0) from_program_mode=PMODE_PROGRAM;
994
        if (strcmp(argv[i],"-F")==0) from_program_mode=PMODE_FIRMWARE;
995
        
996
        i++;
997
        cmd_len=strtol(argv[i],NULL,0);
998
        if (cmd_len) {
999
           printf("%d byte command\n", cmd_len);
1000
           for (j=0; j<cmd_len; j++) {
1001
               i++;
1002
               ep=NULL;
1003
               cmd_buf[j] = strtol(argv[i], &ep, 0);
1004
               if (ep && !*ep) {
1005
                  printf("  0x%02x", cmd_buf[j]);
1006
                  }
1007
               else {
1008
                  if (*argv[i] == '\\') 
1009
                       cmd_buf[j]=argv[i][1];
1010
                  else cmd_buf[j]=argv[i][0];
1011
                  printf(" %c (0x%02x)", cmd_buf[j], cmd_buf[j]);
1012
                  }
1013
               }
1014
           printf("\n");
1015
           }
1016
        else {
1017
           i++;
1018
           printf("string command: '%s'\n", argv[i]);
1019
           strcpy(cmd_buf, argv[i]);
1020
           cmd_len=strlen(argv[i]);
1021
           }
1022
           
1023
        if (cmd_len) {
1024
           if (from_program_mode) {
1025
              if (init_radio(&serial, port, model, from_program_mode)) {
1026
                 test_cmd(serial, cmd_buf, cmd_len, cmd_expect);
1027
                 }
1028
              else {
1029
                 printf("Unable to enter program mode\n");
1030
                 }
1031
              }
1032
           else {
1033
              if (serial<0) {
1034
                 serial=openserial(port);
1035
                 if (serial < 0) {
1036
                    perror("Error opening port");
1037
                    }
1038
                 RX_on(serial);
1039
                 }
1040
              test_cmd(serial, cmd_buf, cmd_len, cmd_expect);
1041
              }
1042
           }
1043
        }
1044
     i++;
1045
     }
1046
     
1047
#if 0
1048
        // update a couple of channels - if you want to do more than a few
1049
        // it's probably easier to read/write the whole memory (but slower).
1050
        read_channel(serial, config, 1);
1051
        read_channel(serial, config, 2);
1052
        config->channels[1].tx_power=TXPOWER_HI;
1053
        config->channels[2].tx_power=TXPOWER_HI;
1054
        write_channel(serial, config, 1);
1055
        write_channel(serial, config, 2);
1056
#endif
1057

    
1058
  if (serial>0) {
1059
     tcdrain(serial);
1060
     usleep(200000);
1061
     close(serial);
1062
     serial=-1;
1063
     }
1064
}
    (1-1/1)