kopia lustrzana https://github.com/sq5bpf/k5prog
initial commit
rodzic
f65b5d9280
commit
f2e5f2ab27
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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 */
|
||||
}
|
|
@ -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.
Ładowanie…
Reference in New Issue