pull/3/head
sq5bpf 2023-05-12 19:08:41 +02:00
rodzic f65b5d9280
commit f2e5f2ab27
5 zmienionych plików z 1375 dodań i 0 usunięć

12
Makefile 100644
Wyświetl plik

@ -0,0 +1,12 @@
# Makefile for k5prog
CC=gcc
COPTS=-g
default: k5prog
k5prog: k5prog.c uvk5.h
$(CC) $(COPTS) k5prog.c -o k5prog
clean:
rm k5prog

133
README 100644
Wyświetl plik

@ -0,0 +1,133 @@
k5prog - Quansheng UV-K5 EEPROM programmer v0.1
(c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
This program can read and write the eeprom of Quansheng UV-K5.
It can read/write arbitrary data, and might be useful for making backups of
the configuration, mass programming of radios or reverse engineering of
the radio configuration. Please note that it is probably possible to break
your radio by writing a bad configuration to it, so please use at your own
risk.
Note that this program does not edit the contents of the eeprom. Use an
external hex editor.
The program is written to (hopefully) run on POSIX systems. Testing was done
on GNU/Linux, but MacOS X and windows under cygwin should work too.
For licensing see the file LICENSE.
---- Usage ----
For a basic usage use -r to read eeprom, -w to write eeprom. The -v option
gives more verbosity.
Read configuration:
sq5bpf@chronos:~/k5prog$ ./baoprog -r -v
Quansheng UV-K5 EEPROM programmer v0.1 (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
k5_prepare: try 0
****** Connected to firmware version: [k5_2.01.23]
Sucessfuly read eeprom
The eeprom contents are written to the file k5_eeprom.raw, this can be
changed with the -f option.
Write configuration from file k5_eeprom.raw:
sq5bpf@chronos:~/chirp/k5prog$ ./baoprog -w -v
Quansheng UV-K5 EEPROM programmer v0.1 (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
k5_prepare: try 0
****** Connected to firmware version: [k5_2.01.23]
Read file k5_eeprom.raw success
Sucessfuly wrote eeprom
The -w option writes only the memory blocks which are written by the original
radio software, in the same order. The -W ioption writes all memory, possibly
allowing overwriting of calibration data (if there is any) or other data which
may be critical to the proper functioning of your radio.
Other configuration options are:
Quansheng UV-K5 EEPROM programmer v0.1 (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
cmdline opts:
-f <file> filename that contains the eeprom dump (default: k5_eeprom.raw)
-r read eeprom
-w write eeprom like the original software does (warning: may brick your radio)
-W write all eeprom (even bigger warning: this is sure to brick your radio!)
-p <port> device name (default: /dev/ttyUSB0)
-s <speed> serial speed (default: 38400, the UV-K5 doesn't accept any other speed)
-h print this help
-v be verbose, use multiple times for more verbosity
---- Compiling ----
This software was tested to compile using gcc on GNU/Linux systems, using a
simple makefile:
sq5bpf@dellix:~/k5prog-0.1$ make
gcc -O2 k5prog.c -o k5prog
Other POSIX platforms should work also, including MacOS X.
The software compiles under Cygwin/Microsoft Windows, but has not been tested.
According to the cygwin documentation you should use /dev/comX to use port comX
(for example using com6: k5prog.exe -v -r -p /dev/com6)
If port this to another platform, or do anything interesting with this
software, tell me about it.
---- Other uses ----
The file uvk5_original_eeprom.raw contains an eeprom downloaded from a UV-K5
radio. Maybe it can be used to resurrect another radio of the same type
if it was broken (perhaps by the use of this software :).
---- Protocol ----
The programming protocol used by this software has been reverse engineered
by observing communications between the radio and the original programming
software. It is not a variation of the typical Baofeng-like protocol.
The format of the datagram sent to the radio is:
0xAB 0xCD len 0x00 <data bytes> <2 bytes CRC> 0xDC 0xBA
The length is the length od the data bytes.
The data is protected by a typical CRC-16 xmodem algorithm.
The data bytes and the CRC are obfuscated by xor-in it with an 8-byte
sequence.
Fortunately the eeprom data contains a lot of 0xFF and 0x00 bytes, so the XOR
sequence is easy to find by observing the traffic.
The datagram sent from the radio is the same, but the CRC field is set to
0xFFFF. This shows that the CRC is not for data integrity, but for further
obfuscation (same as the XOR).
I intend to publish a further description of the protocol, and the eeprom
contents, meanwhile the sources can be used as documentation.
VY 73
Jacek / SQ5BPF

791
k5prog.c 100644
Wyświetl plik

@ -0,0 +1,791 @@
/* Quansheng UV-K5 EEPROM programmer v0.1
* (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
*
* This program can read and write the eeprom of Quansheng UVK5 Mark II
* and probably other similar radios via the serial port.
*
* It can read/write arbitrary data, and might be useful for reverse
* engineering the radio configuration.
*
* Use at your own risk.
*
*
* This program is licensed under the GNU GENERAL PUBLIC LICENSE v3
* License text avaliable at: http://www.gnu.org/copyleft/gpl.html
*/
/*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
#include <ctype.h>
#include <stdint.h>
#include "uvk5.h"
#define VERSION "Quansheng UV-K5 EEPROM programmer v0.1 (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>"
#define MODE_NONE 0
#define MODE_READ 1
#define MODE_WRITE 2
#define MODE_WRITE_ALL 3
#define UVK5_EEPROM_SIZE 0x2000
#define UVK5_EEPROM_BLOCKSIZE 0x80
#define UVK5_PREPARE_TRIES 10
#define DEFAULT_SERIAL_PORT "/dev/ttyUSB0"
#define DEFAULT_FILE_NAME "k5_eeprom.raw"
/* globals */
speed_t ser_speed=B38400;
char *ser_port=DEFAULT_SERIAL_PORT;
int verbose=0;
int mode=MODE_NONE;
char *file=DEFAULT_FILE_NAME;
struct k5_command {
unsigned char *cmd;
int len;
unsigned char *obfuscated_cmd;
int obfuscated_len;
int crcok;
};
/**** commands ********/
unsigned char uvk5_hello2[]={0x14, 0x05, 0x04, 0x00, 0x9f, 0x25, 0x5a, 0x64};
/* commands:
* 0x14 - hello
* 0x1b - read eeprom
* 0x1d - write eeprom
* 0xdd - reset radio
*/
/* the last 6 bytes have to be the same for each "session" */
unsigned char uvk5_hello[]={ 0x14, 0x5, 0x4, 0x0, 0x6a, 0x39, 0x57, 0x64 };
unsigned char uvk5_readmem1[]={ 0x1b, 0x5, 0x8, 0x0, 0x80, 0xe, 0x80, 0x0, 0x6a, 0x39, 0x57, 0x64 }; /* byte6 - length (max 0x80), byte 4 (lsb) ,5 (msb) address */
unsigned char uvk5_writemem1[]={ 0x1d, 0x5, 0x18, 0x0, 0x50, 0xf, 0x10, 0x0, 0x14, 0xad, 0x5c, 0x64, 0x43, 0x48, 0x30, 0x30, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; /* byte3 - command length, byte6 - data to be written length, byte4 - (lsb) byte5( msb) address, byte12-end data */
unsigned char uvk5_reset[]={ 0xdd, 0x5, 0x0, 0x0 };
/* terrible hexdump ripped from some old code, please don't look */
void hdump(unsigned char *buf,int len)
{
int tmp1;
char adump[80];
int tmp2=0;
int tmp3=0;
unsigned char sss;
char hexz[]="0123456789abcdef";
int lasttmp;
printf("\n0x%6.6x |0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |a |b |c |d |e |f |\n",len);
printf("---------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+------------\n");
memset(&adump,' ',78);
adump[78]=0;
for (tmp1=0; tmp1<len; tmp1++)
{
tmp2=tmp1%16;
if (tmp2==0) {
if (tmp1!=0) { printf("0x%6.6x: %.69s\n",tmp3,adump); lasttmp=tmp1; }
memset(&adump,' ',78);
adump[78]=0;
tmp3=tmp1;
}
sss=buf[tmp1];
adump[tmp2*3]=hexz[sss/16];
adump[tmp2*3+1]=hexz[sss%16];
if isprint(sss) { adump[tmp2+50]=sss; } else adump[tmp2+50]='.';
}
//if (((tmp1%16)!=0)||(len==16)) printf("0x%6.6x: %.69s\n",tmp3,adump);
if (lasttmp!=tmp1) printf("0x%6.6x: %.69s\n",tmp3,adump);
}
int openport(char *port,speed_t speed)
{
int fd;
struct termios my_termios;
fd = open(port, O_RDWR | O_NOCTTY);
if (fd < 0)
{
printf("open error %d %s\n", errno, strerror(errno));
return(-1);
}
if (tcgetattr(fd, &my_termios))
{
printf("tcgetattr error %d %s\n", errno, strerror(errno));
return(-1);
}
if (tcflush(fd, TCIFLUSH))
{
printf("tcgetattr error %d %s\n", errno, strerror(errno));
return(-1);
}
my_termios.c_cflag = CS8 |CREAD | CLOCAL | HUPCL;
cfmakeraw(&my_termios);
cfsetospeed(&my_termios, speed);
if ( tcsetattr(fd, TCSANOW, &my_termios))
{
printf("tcsetattr error %d %s\n", errno, strerror(errno));
return(-1);
}
return(fd);
}
/* read with timeout */
int read_timeout(int fd, unsigned char *buf, int maxlen, int timeout)
{
fd_set rfd;
int len=0;
int ret;
struct timeval tv;
int nr;
unsigned char *buf2;
buf2=buf;
FD_ZERO(&rfd);
while(1) {
FD_SET(fd,&rfd);
tv.tv_sec=timeout/1000;
tv.tv_usec=(timeout%1000)/1000;
ret=select(fd+1,&rfd,0,0,&tv);
if (FD_ISSET(fd,&rfd)) {
nr=read(fd,buf,maxlen);
len=len+nr;
buf=buf+nr;
if (nr>=0) maxlen=maxlen-nr;
if (maxlen==0) break;
}
if (ret==0) {
fprintf(stderr,"read_timeout\n");
/* error albo timeout */
break;
}
}
if (verbose>2) {
printf("RXRXRX:\n");
hdump(buf2,len);
}
return(len);
}
void destroy_k5_struct(struct k5_command *cmd)
{
if (cmd->cmd) { free(cmd->cmd); }
if (cmd->obfuscated_cmd) { free(cmd->obfuscated_cmd); }
free(cmd);
}
/* ripped from https://mdfs.net/Info/Comp/Comms/CRC16.htm */
uint16_t crc16xmodem(char *addr, int num, int crc)
{
#define poly 0x1021
int i;
for (; num>0; num--) /* Step through bytes in memory */
{
crc = crc ^ (*addr++ << 8); /* Fetch byte from memory, XOR into CRC top byte*/
for (i=0; i<8; i++) /* Prepare to rotate 8 bits */
{
crc = crc << 1; /* rotate */
if (crc & 0x10000) /* bit 15 was set (now bit 16)... */
crc = (crc ^ poly) & 0xFFFF; /* XOR with XMODEM polynomic */
/* and ensure CRC remains 16-bit value */
} /* Loop for 8 bits */
} /* Loop until num=0 */
return(crc); /* Return updated CRC */
}
/* (de)obfuscate the string using xor */
void xorarr(unsigned char *inarr,int len)
{
int len2=0;
unsigned char k5_xor_array[16]= {
0x16 , 0x6c , 0x14 , 0xe6 , 0x2e , 0x91 , 0x0d , 0x40 ,
0x21 , 0x35 , 0xd5 , 0x40 , 0x13 , 0x03 , 0xe9 , 0x80 };
while (len2<len) {
*inarr=*inarr^k5_xor_array[len2%sizeof(k5_xor_array)];
len2++;
inarr++;
}
}
/* hexdump a k5_command struct */
void k5_hexdump(struct k5_command *cmd) {
printf ("******** k5 command hexdump [obf_len:%i clear_len:%i crc_ok:%i **********\n",cmd->obfuscated_len,cmd->len,cmd->crcok);
if (cmd->obfuscated_cmd) {
printf("## obfuscated ##\n");
hdump(cmd->obfuscated_cmd,cmd->obfuscated_len);
}
if (cmd->cmd) {
printf("## cleartext ##\n");
hdump(cmd->cmd,cmd->len);
}
printf("*****************\n");
}
/* obfuscate a k5 datagram */
int k5_obfuscate(struct k5_command *cmd)
{
uint16_t c;
if (!cmd->cmd) return(0);
if (cmd->obfuscated_cmd) { free (cmd->obfuscated_cmd); }
cmd->obfuscated_len=cmd->len+8; /* header + length + data + crc + footer */
cmd->obfuscated_cmd=calloc(cmd->obfuscated_len,1);
cmd->obfuscated_cmd[0]=0xab;
cmd->obfuscated_cmd[1]=0xcd;
cmd->obfuscated_cmd[2]=cmd->len;
cmd->obfuscated_cmd[3]=0; /* or maybe more significant byte of length? */
memcpy((cmd->obfuscated_cmd)+4,cmd->cmd,cmd->len);
c=crc16xmodem((cmd->obfuscated_cmd)+4,cmd->len,0);
cmd->obfuscated_cmd[cmd->len+4]=c&0xff;
cmd->obfuscated_cmd[cmd->len+5]=(c>>8)&0xff;
xorarr((cmd->obfuscated_cmd)+4,cmd->len+2);
cmd->obfuscated_cmd[cmd->len+6]=0xdc;
cmd->obfuscated_cmd[cmd->len+7]=0xba;
cmd->crcok=1;
return(1);
}
/* deobfuscate a k5 datagram and verify it */
int k5_deobfuscate(struct k5_command *cmd)
{
uint16_t c,d;
if (!cmd->obfuscated_cmd) return(0);
if (cmd->cmd) { free (cmd->cmd); }
/* check the obfuscated datagram */
if ((cmd->obfuscated_cmd[0]!=0xab)||(cmd->obfuscated_cmd[1]!=0xcd)) {
//bad header
if (verbose>2) { printf("bad header\n"); k5_hexdump(cmd); }
return(0);
}
if ((cmd->obfuscated_cmd[cmd->obfuscated_len-2]!=0xdc)||(cmd->obfuscated_cmd[cmd->obfuscated_len-1]!=0xba)) {
//bad footer
if (verbose>2) { printf("bad footer\n"); k5_hexdump(cmd); }
return(0);
}
cmd->len=cmd->obfuscated_len-6; /* header + length + data + crc + footer */
cmd->cmd=calloc(cmd->len,1);
memcpy(cmd->cmd,cmd->obfuscated_cmd+4,cmd->len);
xorarr(cmd->cmd,cmd->len);
c=crc16xmodem(cmd->cmd,cmd->len-2,0);
d=(cmd->cmd[cmd->len-2])|(cmd->cmd[cmd->len-1]<<8);
//if ((*cmd->cmd[*cmd->cmd-2]==(c&0xff))&&(*cmd->cmd[*cmd->cmd-2]==((c<<8)&0xff)))
/* the protocol looks like it would use crc from the radio to the pc, but instead the radio sends 0xffff */
if (d==0xffff)
{
cmd->crcok=1;
cmd->len=cmd->len-2; /* skip crc */
} else {
if (d==c) {
printf("** the protocol actually uses proper crc on datagrams from the radio, please inform the author of the radio/firmware version\n");
k5_hexdump(cmd);
}
cmd->crcok=0;
if (verbose>2) { printf("bad crc 0x%4.4x (should be 0x%4.4x)\n",d,c); k5_hexdump(cmd); }
cmd->len=cmd->len-2; /* skip crc */
return(0);
}
return(1);
}
/* obfuscate a command, send it */
int k5_send_cmd(int fd,struct k5_command *cmd) {
int l;
if (!k5_obfuscate(cmd)) {
fprintf(stderr,"obfuscate error!\n");
return(0);
}
if (verbose>1) k5_hexdump(cmd);
l=write(fd,cmd->obfuscated_cmd,cmd->obfuscated_len);
if (verbose>2) printf("write %i\n",l);
return(1);
}
int k5_send_buf(int fd,unsigned char *buf,int len) {
int l;
struct k5_command *cmd;
cmd=calloc(sizeof(struct k5_command),1);
cmd->len=len;
cmd->cmd=malloc(cmd->len);
memcpy(cmd->cmd,buf,len);
l=k5_send_cmd(fd,cmd);
destroy_k5_struct(cmd);
return(l);
}
/* receive a response, deobfuscate it */
struct k5_command *k5_receive(int fd) {
unsigned char buf[4];
unsigned char buf2[2048];
struct k5_command *cmd;
int len;
len=read_timeout(fd,(unsigned char *)&buf,sizeof(buf),10000); /* wait 500ms */
if (len>0) {
if (verbose>2) { printf("magic:\n"); hdump((unsigned char *)&buf,len); }
} else
{
fprintf(stderr,"k5_receive: err read1\n");
return(0);
}
if ((buf[0]!=0xab)||(buf[1]!=0xcd)) {
fprintf(stderr,"k5_receive: bad magic number\n");
return(0);
}
if (buf[3]!=0) {
fprintf(stderr,"k5_receive: it seems that byte 3 can be something else than 0, please notify the author\n");
return(0);
}
cmd=calloc(sizeof(struct k5_command),1);
cmd->obfuscated_len=buf[2]+8;
cmd->obfuscated_cmd=calloc(cmd->obfuscated_len,1);
memcpy(cmd->obfuscated_cmd,buf,4);
len=read_timeout(fd,cmd->obfuscated_cmd+4,buf[2]+4,10000); /* wait 500ms */
if ((len+4)!=(cmd->obfuscated_len)) {
fprintf(stderr,"k5_receive err read1 len=%i wanted=%i\n",len,cmd->obfuscated_len);
return(0);
}
/* deobfuscate */
k5_deobfuscate(cmd);
if (verbose>2) k5_hexdump(cmd);
return(cmd);
}
int k5_readmem(int fd, unsigned char *buf, unsigned char maxlen, int offset)
{
int l;
unsigned char readmem[sizeof(uvk5_readmem1)];
int r;
struct k5_command *cmd;
if (verbose>1) printf("@@@@@@@@@@@@@@@@@@ readmem offset=0x%4.4x len=0x%2.2x\n",offset,maxlen);
/* byte6 - length (max 0x80), byte 4 (lsb) ,5 (msb) address */
memcpy(readmem,uvk5_readmem1,sizeof(uvk5_readmem1));
readmem[6]=maxlen;
readmem[4]=offset&0xff;
readmem[5]=(offset>>8)&0xff;
r=k5_send_buf(fd,readmem,sizeof(readmem));
if (!r) return(0);
cmd=k5_receive(fd);
if (!cmd) return(0);
if (verbose>2) k5_hexdump(cmd);
memcpy(buf,cmd->cmd+8,cmd->len-8);
destroy_k5_struct(cmd);
return(1);
}
int k5_writemem(int fd, unsigned char *buf, unsigned char len, int offset)
{
int l;
unsigned char writemem[512];
int r;
struct k5_command *cmd;
if (verbose>1) printf("@@@@@@@@@@@@@@@@@@ writemem offset=0x%4.4x len=0x%2.2x\n",offset,len);
/* byte6 - length (max 0x80), byte 4 (lsb) ,5 (msb) address */
writemem[0]=0x1d;
writemem[1]=0x5;
writemem[2]=len+8;
writemem[3]=0;
writemem[4]=offset&0xff;
writemem[5]=(offset>>8)&0xff;
writemem[6]=len;
writemem[7]=1;
writemem[8]=0x6a;
writemem[9]=0x39;
writemem[10]=0x57;
writemem[11]=0x64;
memcpy((void *)&writemem+12,buf,len);
r=k5_send_buf(fd,writemem,len+12);
if (!r) return(0);
cmd=k5_receive(fd);
if (!cmd) return(0);
if (verbose>2) k5_hexdump(cmd);
if (((cmd->cmd[0])!=0x1e)||((cmd->cmd[4])!=writemem[4])||((cmd->cmd[5])!=writemem[5])) {
fprintf(stderr,"bad write confirmation\n");
destroy_k5_struct(cmd);
return(0);
}
destroy_k5_struct(cmd);
return(1);
}
/* reset the radio */
int k5_reset(int fd)
{
int l;
int r;
struct k5_command *cmd;
if (verbose>1) printf("@@@@@@@@@@@@@@@@@@ reset\n");
r=k5_send_buf(fd,uvk5_reset,sizeof(uvk5_reset));
return(r);
}
void helpme()
{
printf(
"cmdline opts:\n"
"-f <file>\tfilename that contains the eeprom dump (default: " DEFAULT_FILE_NAME ")\n"
"-r \tread eeprom\n"
"-w \twrite eeprom like the original software does (warning: may brick your radio)\n"
"-W \twrite all eeprom (even bigger warning: this is sure to brick your radio!)\n"
"-p <port>\tdevice name (default: " DEFAULT_SERIAL_PORT ")\n"
"-s <speed>\tserial speed (default: 38400, the UV-K5 doesn't accept any other speed)\n"
"-h \tprint this help\n"
"-v \tbe verbose, use multiple times for more verbosity\n"
);
}
static speed_t baud_to_speed_t(int baud)
{
switch (baud) {
case 0:
return B0;
case 50:
return B50;
case 75:
return B75;
case 110:
return B110;
case 150:
return B150;
case 200:
return B200;
case 300:
return B300;
case 600:
return B600;
case 1200:
return B1200;
case 1800:
return B1800;
case 2400:
return B2400;
case 4800:
return B4800;
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
default:
return B0;
}
}
void parse_cmdline(int argc, char **argv)
{
int opt;
/* cmdline opts:
* -f <file>
* -r (read)
* -w (write)
* -p <port>
* -s <speed>
* -h (help)
* -v (verbose)
*/
while ((opt=getopt(argc,argv,"f:rwWp:s:hv"))!=EOF)
{
switch (opt)
{
case 'h':
helpme();
exit(0);
break;
case 'v':
verbose++;
break;
case 'r':
mode=MODE_READ;
break;
case 'w':
mode=MODE_WRITE;
break;
case 'W':
mode=MODE_WRITE_ALL;
break;
case 'f':
file=optarg;
break;
case 'p':
ser_port=optarg;
break;
case 's':
ser_speed=baud_to_speed_t(atoi(optarg));
if (ser_speed==B0) {
fprintf(stderr,"ERROR, unknown speed %s\n",optarg);
exit(1);
break;
default:
fprintf(stderr,"Unknown command line option %s\n",optarg);
exit(1);
break;
}
}
}
}
int write_file(char *name, unsigned char *buffer, int len)
{
int fd;
int l;
fd=open(name,O_WRONLY|O_CREAT|O_TRUNC,0600);
if (fd<0) {
printf("open %s error %d %s\n", name,errno, strerror(errno));
return(-1);
}
l=write(fd,buffer,len);
if (l!=len) {
printf("short write (%i) error %d %s\n", l,errno, strerror(errno));
return(-1);
}
close(fd);
return(1);
}
int k5_prepare(int fd) {
int r;
struct k5_command *cmd;
r=k5_send_buf(fd,uvk5_hello,sizeof(uvk5_hello));
if (!r) return(0);
cmd=k5_receive(fd);
if (!cmd) return(0);
printf("****** Connected to firmware version: [%s]\n",(cmd->cmd)+4);
destroy_k5_struct(cmd);
return(1);
}
int main(int argc,char **argv)
{
int fd,ffd;
unsigned char eeprom[UVK5_EEPROM_SIZE];
int i;
int r;
int j;
printf (VERSION "\n\n");
//debtest();
parse_cmdline(argc,argv);
if (mode==MODE_NONE) {
fprintf(stderr,"No operating mode selected, use -w or -r\n");
helpme();
exit(1);
}
fd=openport(ser_port,ser_speed);
if (fd<0) {
fprintf(stderr,"Open %s failed\n",ser_port);
exit(1);
}
for (i=0;i<UVK5_PREPARE_TRIES;i++)
{
if (verbose>0) { printf("k5_prepare: try %i\n",i); }
r=k5_prepare(fd);
if (r) break;
}
if (!r)
{
fprintf(stderr,"Failed to init radio\n");
exit(1);
}
switch(mode)
{
case MODE_READ:
for(i=0;i<UVK5_EEPROM_SIZE; i=i+UVK5_EEPROM_BLOCKSIZE) {
if (!k5_readmem(fd,(unsigned char *)&eeprom[i],UVK5_EEPROM_BLOCKSIZE,i))
{
fprintf(stderr,"Failed to read block 0x%4.4X\n",i);
exit(1);
}
if (verbose>0) {
printf("\rread block 0x%4.4X %i%%",i,(100*i/UVK5_EEPROM_SIZE));
fflush(stdout);
}
}
close(fd);
if (verbose>0) { printf("\rSucessfuly read eeprom\n"); }
if (verbose>2) { hdump((unsigned char *)&eeprom,UVK5_EEPROM_SIZE); }
write_file(file,(unsigned char *)&eeprom,UVK5_EEPROM_SIZE);
break;
case MODE_WRITE:
case MODE_WRITE_ALL:
/* read file */
ffd=open(file,O_RDONLY);
if (ffd<0) {
fprintf(stderr,"open %s error %d %s\n", file, errno, strerror(errno));
exit(1);
}
r=read(ffd,(unsigned char *)&eeprom[i],UVK5_EEPROM_SIZE);
if (r!=UVK5_EEPROM_SIZE) {
fprintf(stderr,"Failed to read whole eeprom from file %s, file too short?\n",file);
exit(1);
}
close(ffd);
if (verbose>0) { printf ("Read file %s success\n",file); }
if (mode==MODE_WRITE_ALL) {
/* write to radio */
for(i=0;i<UVK5_EEPROM_SIZE; i=i+UVK5_EEPROM_BLOCKSIZE) {
if (!k5_writemem(fd,(unsigned char *)&eeprom[i],UVK5_EEPROM_BLOCKSIZE,i))
{
fprintf(stderr,"Failed to write block 0x%4.4X\n",i);
exit(1);
}
if (verbose>0) {
printf("\rwrite block 0x%4.4X %i%%",i,(100*i/UVK5_EEPROM_SIZE));
fflush(stdout);
}
}
} else {
/* write to radio */
i=0;
while (uvk5_writes[i][1]) { i++; }
j=i;
i=0;
while (uvk5_writes[i][1]) {
if (!k5_writemem(fd,(unsigned char *)&eeprom[uvk5_writes[i][0]],uvk5_writes[i][1],uvk5_writes[i][0]))
{
fprintf(stderr,"Failed to write block 0x%4.4X length 0x%2.2x\n",uvk5_writes[i][0],uvk5_writes[i][1]);
exit(1);
}
if (verbose>0) {
printf("\rwrite block 0x%4.4X %i%%",i,(100*i/j));
fflush(stdout);
}
i++;
}
}
k5_reset(fd);
if (verbose>0) { printf("\rSucessfuly wrote eeprom\n"); }
break;
default:
fprintf(stderr,"this shouldn't happen :)\n");
break;
}
return(0); /* silence gcc */
}

439
uvk5.h 100644
Wyświetl plik

@ -0,0 +1,439 @@
/* UV-K5 eeprom programmer */
#ifndef UVK5_INCLUDE_H
#define UVK5_INCLUDE_H
int uvk5_writesx[][2]={
//{ 0x0e70 , 0x60 },
{ 0x0eb0 , 0x08 },
{0,0}
};
int uvk5_writes[][2]={
{ 0x0e70 , 0x60 },
{ 0x0000 , 0x10 },
{ 0x0f50 , 0x10 },
{ 0x0010 , 0x10 },
{ 0x0f60 , 0x10 },
{ 0x0020 , 0x10 },
{ 0x0f70 , 0x10 },
{ 0x0030 , 0x10 },
{ 0x0f80 , 0x10 },
{ 0x0040 , 0x10 },
{ 0x0f90 , 0x10 },
{ 0x0050 , 0x10 },
{ 0x0fa0 , 0x10 },
{ 0x0060 , 0x10 },
{ 0x0fb0 , 0x10 },
{ 0x0070 , 0x10 },
{ 0x0fc0 , 0x10 },
{ 0x0080 , 0x10 },
{ 0x0fd0 , 0x10 },
{ 0x0090 , 0x10 },
{ 0x0fe0 , 0x10 },
{ 0x00a0 , 0x10 },
{ 0x0ff0 , 0x10 },
{ 0x00b0 , 0x10 },
{ 0x1000 , 0x10 },
{ 0x00c0 , 0x10 },
{ 0x1010 , 0x10 },
{ 0x00d0 , 0x10 },
{ 0x1020 , 0x10 },
{ 0x00e0 , 0x10 },
{ 0x1030 , 0x10 },
{ 0x00f0 , 0x10 },
{ 0x1040 , 0x10 },
{ 0x0100 , 0x10 },
{ 0x1050 , 0x10 },
{ 0x0110 , 0x10 },
{ 0x1060 , 0x10 },
{ 0x0120 , 0x10 },
{ 0x1070 , 0x10 },
{ 0x0130 , 0x10 },
{ 0x1080 , 0x10 },
{ 0x0140 , 0x10 },
{ 0x1090 , 0x10 },
{ 0x0150 , 0x10 },
{ 0x10a0 , 0x10 },
{ 0x0160 , 0x10 },
{ 0x10b0 , 0x10 },
{ 0x0170 , 0x10 },
{ 0x10c0 , 0x10 },
{ 0x0180 , 0x10 },
{ 0x10d0 , 0x10 },
{ 0x0190 , 0x10 },
{ 0x10e0 , 0x10 },
{ 0x01a0 , 0x10 },
{ 0x10f0 , 0x10 },
{ 0x01b0 , 0x10 },
{ 0x1100 , 0x10 },
{ 0x01c0 , 0x10 },
{ 0x1110 , 0x10 },
{ 0x01d0 , 0x10 },
{ 0x1120 , 0x10 },
{ 0x01e0 , 0x10 },
{ 0x1130 , 0x10 },
{ 0x01f0 , 0x10 },
{ 0x1140 , 0x10 },
{ 0x0200 , 0x10 },
{ 0x1150 , 0x10 },
{ 0x0210 , 0x10 },
{ 0x1160 , 0x10 },
{ 0x0220 , 0x10 },
{ 0x1170 , 0x10 },
{ 0x0230 , 0x10 },
{ 0x1180 , 0x10 },
{ 0x0240 , 0x10 },
{ 0x1190 , 0x10 },
{ 0x0250 , 0x10 },
{ 0x11a0 , 0x10 },
{ 0x0260 , 0x10 },
{ 0x11b0 , 0x10 },
{ 0x0270 , 0x10 },
{ 0x11c0 , 0x10 },
{ 0x0280 , 0x10 },
{ 0x11d0 , 0x10 },
{ 0x0290 , 0x10 },
{ 0x11e0 , 0x10 },
{ 0x02a0 , 0x10 },
{ 0x11f0 , 0x10 },
{ 0x02b0 , 0x10 },
{ 0x1200 , 0x10 },
{ 0x02c0 , 0x10 },
{ 0x1210 , 0x10 },
{ 0x02d0 , 0x10 },
{ 0x1220 , 0x10 },
{ 0x02e0 , 0x10 },
{ 0x1230 , 0x10 },
{ 0x02f0 , 0x10 },
{ 0x1240 , 0x10 },
{ 0x0300 , 0x10 },
{ 0x1250 , 0x10 },
{ 0x0310 , 0x10 },
{ 0x1260 , 0x10 },
{ 0x0320 , 0x10 },
{ 0x1270 , 0x10 },
{ 0x0330 , 0x10 },
{ 0x1280 , 0x10 },
{ 0x0340 , 0x10 },
{ 0x1290 , 0x10 },
{ 0x0350 , 0x10 },
{ 0x12a0 , 0x10 },
{ 0x0360 , 0x10 },
{ 0x12b0 , 0x10 },
{ 0x0370 , 0x10 },
{ 0x12c0 , 0x10 },
{ 0x0380 , 0x10 },
{ 0x12d0 , 0x10 },
{ 0x0390 , 0x10 },
{ 0x12e0 , 0x10 },
{ 0x03a0 , 0x10 },
{ 0x12f0 , 0x10 },
{ 0x03b0 , 0x10 },
{ 0x1300 , 0x10 },
{ 0x03c0 , 0x10 },
{ 0x1310 , 0x10 },
{ 0x03d0 , 0x10 },
{ 0x1320 , 0x10 },
{ 0x03e0 , 0x10 },
{ 0x1330 , 0x10 },
{ 0x03f0 , 0x10 },
{ 0x1340 , 0x10 },
{ 0x0400 , 0x10 },
{ 0x1350 , 0x10 },
{ 0x0410 , 0x10 },
{ 0x1360 , 0x10 },
{ 0x0420 , 0x10 },
{ 0x1370 , 0x10 },
{ 0x0430 , 0x10 },
{ 0x1380 , 0x10 },
{ 0x0440 , 0x10 },
{ 0x1390 , 0x10 },
{ 0x0450 , 0x10 },
{ 0x13a0 , 0x10 },
{ 0x0460 , 0x10 },
{ 0x13b0 , 0x10 },
{ 0x0470 , 0x10 },
{ 0x13c0 , 0x10 },
{ 0x0480 , 0x10 },
{ 0x13d0 , 0x10 },
{ 0x0490 , 0x10 },
{ 0x13e0 , 0x10 },
{ 0x04a0 , 0x10 },
{ 0x13f0 , 0x10 },
{ 0x04b0 , 0x10 },
{ 0x1400 , 0x10 },
{ 0x04c0 , 0x10 },
{ 0x1410 , 0x10 },
{ 0x04d0 , 0x10 },
{ 0x1420 , 0x10 },
{ 0x04e0 , 0x10 },
{ 0x1430 , 0x10 },
{ 0x04f0 , 0x10 },
{ 0x1440 , 0x10 },
{ 0x0500 , 0x10 },
{ 0x1450 , 0x10 },
{ 0x0510 , 0x10 },
{ 0x1460 , 0x10 },
{ 0x0520 , 0x10 },
{ 0x1470 , 0x10 },
{ 0x0530 , 0x10 },
{ 0x1480 , 0x10 },
{ 0x0540 , 0x10 },
{ 0x1490 , 0x10 },
{ 0x0550 , 0x10 },
{ 0x14a0 , 0x10 },
{ 0x0560 , 0x10 },
{ 0x14b0 , 0x10 },
{ 0x0570 , 0x10 },
{ 0x14c0 , 0x10 },
{ 0x0580 , 0x10 },
{ 0x14d0 , 0x10 },
{ 0x0590 , 0x10 },
{ 0x14e0 , 0x10 },
{ 0x05a0 , 0x10 },
{ 0x14f0 , 0x10 },
{ 0x05b0 , 0x10 },
{ 0x1500 , 0x10 },
{ 0x05c0 , 0x10 },
{ 0x1510 , 0x10 },
{ 0x05d0 , 0x10 },
{ 0x1520 , 0x10 },
{ 0x05e0 , 0x10 },
{ 0x1530 , 0x10 },
{ 0x05f0 , 0x10 },
{ 0x1540 , 0x10 },
{ 0x0600 , 0x10 },
{ 0x1550 , 0x10 },
{ 0x0610 , 0x10 },
{ 0x1560 , 0x10 },
{ 0x0620 , 0x10 },
{ 0x1570 , 0x10 },
{ 0x0630 , 0x10 },
{ 0x1580 , 0x10 },
{ 0x0640 , 0x10 },
{ 0x1590 , 0x10 },
{ 0x0650 , 0x10 },
{ 0x15a0 , 0x10 },
{ 0x0660 , 0x10 },
{ 0x15b0 , 0x10 },
{ 0x0670 , 0x10 },
{ 0x15c0 , 0x10 },
{ 0x0680 , 0x10 },
{ 0x15d0 , 0x10 },
{ 0x0690 , 0x10 },
{ 0x15e0 , 0x10 },
{ 0x06a0 , 0x10 },
{ 0x15f0 , 0x10 },
{ 0x06b0 , 0x10 },
{ 0x1600 , 0x10 },
{ 0x06c0 , 0x10 },
{ 0x1610 , 0x10 },
{ 0x06d0 , 0x10 },
{ 0x1620 , 0x10 },
{ 0x06e0 , 0x10 },
{ 0x1630 , 0x10 },
{ 0x06f0 , 0x10 },
{ 0x1640 , 0x10 },
{ 0x0700 , 0x10 },
{ 0x1650 , 0x10 },
{ 0x0710 , 0x10 },
{ 0x1660 , 0x10 },
{ 0x0720 , 0x10 },
{ 0x1670 , 0x10 },
{ 0x0730 , 0x10 },
{ 0x1680 , 0x10 },
{ 0x0740 , 0x10 },
{ 0x1690 , 0x10 },
{ 0x0750 , 0x10 },
{ 0x16a0 , 0x10 },
{ 0x0760 , 0x10 },
{ 0x16b0 , 0x10 },
{ 0x0770 , 0x10 },
{ 0x16c0 , 0x10 },
{ 0x0780 , 0x10 },
{ 0x16d0 , 0x10 },
{ 0x0790 , 0x10 },
{ 0x16e0 , 0x10 },
{ 0x07a0 , 0x10 },
{ 0x16f0 , 0x10 },
{ 0x07b0 , 0x10 },
{ 0x1700 , 0x10 },
{ 0x07c0 , 0x10 },
{ 0x1710 , 0x10 },
{ 0x07d0 , 0x10 },
{ 0x1720 , 0x10 },
{ 0x07e0 , 0x10 },
{ 0x1730 , 0x10 },
{ 0x07f0 , 0x10 },
{ 0x1740 , 0x10 },
{ 0x0800 , 0x10 },
{ 0x1750 , 0x10 },
{ 0x0810 , 0x10 },
{ 0x1760 , 0x10 },
{ 0x0820 , 0x10 },
{ 0x1770 , 0x10 },
{ 0x0830 , 0x10 },
{ 0x1780 , 0x10 },
{ 0x0840 , 0x10 },
{ 0x1790 , 0x10 },
{ 0x0850 , 0x10 },
{ 0x17a0 , 0x10 },
{ 0x0860 , 0x10 },
{ 0x17b0 , 0x10 },
{ 0x0870 , 0x10 },
{ 0x17c0 , 0x10 },
{ 0x0880 , 0x10 },
{ 0x17d0 , 0x10 },
{ 0x0890 , 0x10 },
{ 0x17e0 , 0x10 },
{ 0x08a0 , 0x10 },
{ 0x17f0 , 0x10 },
{ 0x08b0 , 0x10 },
{ 0x1800 , 0x10 },
{ 0x08c0 , 0x10 },
{ 0x1810 , 0x10 },
{ 0x08d0 , 0x10 },
{ 0x1820 , 0x10 },
{ 0x08e0 , 0x10 },
{ 0x1830 , 0x10 },
{ 0x08f0 , 0x10 },
{ 0x1840 , 0x10 },
{ 0x0900 , 0x10 },
{ 0x1850 , 0x10 },
{ 0x0910 , 0x10 },
{ 0x1860 , 0x10 },
{ 0x0920 , 0x10 },
{ 0x1870 , 0x10 },
{ 0x0930 , 0x10 },
{ 0x1880 , 0x10 },
{ 0x0940 , 0x10 },
{ 0x1890 , 0x10 },
{ 0x0950 , 0x10 },
{ 0x18a0 , 0x10 },
{ 0x0960 , 0x10 },
{ 0x18b0 , 0x10 },
{ 0x0970 , 0x10 },
{ 0x18c0 , 0x10 },
{ 0x0980 , 0x10 },
{ 0x18d0 , 0x10 },
{ 0x0990 , 0x10 },
{ 0x18e0 , 0x10 },
{ 0x09a0 , 0x10 },
{ 0x18f0 , 0x10 },
{ 0x09b0 , 0x10 },
{ 0x1900 , 0x10 },
{ 0x09c0 , 0x10 },
{ 0x1910 , 0x10 },
{ 0x09d0 , 0x10 },
{ 0x1920 , 0x10 },
{ 0x09e0 , 0x10 },
{ 0x1930 , 0x10 },
{ 0x09f0 , 0x10 },
{ 0x1940 , 0x10 },
{ 0x0a00 , 0x10 },
{ 0x1950 , 0x10 },
{ 0x0a10 , 0x10 },
{ 0x1960 , 0x10 },
{ 0x0a20 , 0x10 },
{ 0x1970 , 0x10 },
{ 0x0a30 , 0x10 },
{ 0x1980 , 0x10 },
{ 0x0a40 , 0x10 },
{ 0x1990 , 0x10 },
{ 0x0a50 , 0x10 },
{ 0x19a0 , 0x10 },
{ 0x0a60 , 0x10 },
{ 0x19b0 , 0x10 },
{ 0x0a70 , 0x10 },
{ 0x19c0 , 0x10 },
{ 0x0a80 , 0x10 },
{ 0x19d0 , 0x10 },
{ 0x0a90 , 0x10 },
{ 0x19e0 , 0x10 },
{ 0x0aa0 , 0x10 },
{ 0x19f0 , 0x10 },
{ 0x0ab0 , 0x10 },
{ 0x1a00 , 0x10 },
{ 0x0ac0 , 0x10 },
{ 0x1a10 , 0x10 },
{ 0x0ad0 , 0x10 },
{ 0x1a20 , 0x10 },
{ 0x0ae0 , 0x10 },
{ 0x1a30 , 0x10 },
{ 0x0af0 , 0x10 },
{ 0x1a40 , 0x10 },
{ 0x0b00 , 0x10 },
{ 0x1a50 , 0x10 },
{ 0x0b10 , 0x10 },
{ 0x1a60 , 0x10 },
{ 0x0b20 , 0x10 },
{ 0x1a70 , 0x10 },
{ 0x0b30 , 0x10 },
{ 0x1a80 , 0x10 },
{ 0x0b40 , 0x10 },
{ 0x1a90 , 0x10 },
{ 0x0b50 , 0x10 },
{ 0x1aa0 , 0x10 },
{ 0x0b60 , 0x10 },
{ 0x1ab0 , 0x10 },
{ 0x0b70 , 0x10 },
{ 0x1ac0 , 0x10 },
{ 0x0b80 , 0x10 },
{ 0x1ad0 , 0x10 },
{ 0x0b90 , 0x10 },
{ 0x1ae0 , 0x10 },
{ 0x0ba0 , 0x10 },
{ 0x1af0 , 0x10 },
{ 0x0bb0 , 0x10 },
{ 0x1b00 , 0x10 },
{ 0x0bc0 , 0x10 },
{ 0x1b10 , 0x10 },
{ 0x0bd0 , 0x10 },
{ 0x1b20 , 0x10 },
{ 0x0be0 , 0x10 },
{ 0x1b30 , 0x10 },
{ 0x0bf0 , 0x10 },
{ 0x1b40 , 0x10 },
{ 0x0c00 , 0x10 },
{ 0x1b50 , 0x10 },
{ 0x0c10 , 0x10 },
{ 0x1b60 , 0x10 },
{ 0x0c20 , 0x10 },
{ 0x1b70 , 0x10 },
{ 0x0c30 , 0x10 },
{ 0x1b80 , 0x10 },
{ 0x0c40 , 0x10 },
{ 0x1b90 , 0x10 },
{ 0x0c50 , 0x10 },
{ 0x1ba0 , 0x10 },
{ 0x0c60 , 0x10 },
{ 0x1bb0 , 0x10 },
{ 0x0c70 , 0x10 },
{ 0x1bc0 , 0x10 },
{ 0x0c80 , 0x10 },
{ 0x0c90 , 0x10 },
{ 0x0ca0 , 0x10 },
{ 0x0cb0 , 0x10 },
{ 0x0cc0 , 0x10 },
{ 0x0cd0 , 0x10 },
{ 0x0ce0 , 0x10 },
{ 0x0cf0 , 0x10 },
{ 0x0d00 , 0x10 },
{ 0x0d10 , 0x10 },
{ 0x0d20 , 0x10 },
{ 0x0d30 , 0x10 },
{ 0x0d40 , 0x10 },
{ 0x0d50 , 0x10 },
{ 0x0d60 , 0x80 },
{ 0x0de0 , 0x50 },
{ 0x0e40 , 0x28 },
{ 0x0ed0 , 0x48 },
{ 0x1c00 , 0x80 },
{ 0x1c80 , 0x80 },
{ 0x0f18 , 0x08 },
{0,0}
};
#endif

Plik binarny nie jest wyświetlany.