kopia lustrzana https://github.com/sq5bpf/k5prog
Improve & simplify align-to-magic-word
This commit implements a byte-by-byte alignment approach. It consists on a state machine that consumes the UART output byte-by-byte, and that will only accept input of the form: `0xAB 0xCB, 0xXX, 0x00` . Additionally: * Corrects several memory issues (segmentation faults & leaks). * Reduces timeout lengths as they hinder the retry-realign process. Tested on Linux with bootloaders * 4.00.01 (UV-5R Plus), and * 2.00.06 (UV-K5) and adaptors based chipsets * CH341, and * PL2303pull/22/head
rodzic
241ab18b61
commit
c66207cab6
160
k5prog.c
160
k5prog.c
|
@ -35,6 +35,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/select.h>
|
||||
|
@ -255,11 +256,16 @@ int read_timeout(int fd, unsigned char *buf, int maxlen, int timeout)
|
|||
|
||||
|
||||
|
||||
void destroy_k5_struct(struct k5_command *cmd)
|
||||
void destroy_k5_struct(struct k5_command **cmd)
|
||||
{
|
||||
if (cmd->cmd) { free(cmd->cmd); }
|
||||
if (cmd->obfuscated_cmd) { free(cmd->obfuscated_cmd); }
|
||||
free(cmd);
|
||||
if (!*cmd) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*cmd)->cmd) { free((*cmd)->cmd); }
|
||||
if ((*cmd)->obfuscated_cmd) { free((*cmd)->obfuscated_cmd); }
|
||||
free(*cmd);
|
||||
*cmd = NULL;
|
||||
}
|
||||
|
||||
/* ripped from https://mdfs.net/Info/Comp/Comms/CRC16.htm */
|
||||
|
@ -406,42 +412,71 @@ int k5_send_buf(int fd,unsigned char *buf,int len) {
|
|||
cmd->cmd=malloc(cmd->len);
|
||||
memcpy(cmd->cmd,buf,len);
|
||||
l=k5_send_cmd(fd,cmd);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
return(l);
|
||||
}
|
||||
|
||||
|
||||
int align_and_read_magic_number_0xAB_0xCD(int fd, unsigned char *buf, size_t buf_len, int retries)
|
||||
{
|
||||
int alignment_step = 0;
|
||||
|
||||
while (1) {
|
||||
const int len = read_timeout(fd, &buf[alignment_step], 1, 1000);
|
||||
|
||||
if (len == 0) {
|
||||
fprintf(stderr,"k5_receive: err read1\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (verbose > 1) {
|
||||
printf("Alignment step: %d byte read: %x\n", alignment_step, buf[alignment_step]);
|
||||
printf("magic:"); hdump((unsigned char *)&buf,len);
|
||||
}
|
||||
|
||||
if (alignment_step == 0 && buf[alignment_step] == 0xab) {
|
||||
alignment_step++;
|
||||
} else if (alignment_step == 1 && buf[alignment_step] == 0xcd) {
|
||||
alignment_step++;
|
||||
} else if (alignment_step == 2) {
|
||||
alignment_step++;
|
||||
} else if (alignment_step == 3) {
|
||||
if (buf[alignment_step] != 0) {
|
||||
fprintf(stderr, "align_to_magic_number_0xAB_0xCD: it seems that byte 3 can be something else than 0, please notify the author\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// aligned
|
||||
break;
|
||||
} else {
|
||||
if (!(retries--)) {
|
||||
fprintf(stderr,"k5_receive: err read1\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tcflush(fd, TCIFLUSH);
|
||||
fprintf(stderr,"Alignment failed. Restarting (step: %d, byte: 0x%x\n", alignment_step, buf[alignment_step]);
|
||||
fprintf(stderr,"k5_receive: got %d expected %ld\n", len, sizeof(buf));
|
||||
alignment_step = 0;
|
||||
memset (buf, 0, buf_len);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose > 1) {
|
||||
printf("Magic received: 0x%#02x, 0x%#02x, 0x%#02x, 0x%#02x\n", buf[0], buf[1], buf[2], buf[3]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* receive a response, deobfuscate it */
|
||||
struct k5_command *k5_receive(int fd, int tmout) {
|
||||
unsigned char buf[4];
|
||||
unsigned char buf[4] = { 0 };
|
||||
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);
|
||||
}
|
||||
|
||||
/* During plugging in etc we can receive a single byte.
|
||||
* Handle this case here. */
|
||||
if (len != sizeof(buf))
|
||||
{
|
||||
fprintf(stderr,"k5_receive: got %d expected %ld\n", len, sizeof(buf));
|
||||
return(0);
|
||||
}
|
||||
|
||||
if ((buf[0]!=0xab)||(buf[1]!=0xcd)) {
|
||||
fprintf(stderr,"k5_receive: bad magic number\n");
|
||||
/* Assume we are out of sync and flush rx buffer by reading everything.
|
||||
* This works because the boot message is repeated. */
|
||||
while (len>0)
|
||||
len =read_timeout(fd,(unsigned char *)&buf,sizeof(buf),10000);
|
||||
return(0);
|
||||
}
|
||||
align_and_read_magic_number_0xAB_0xCD(fd,(unsigned char *)&buf,sizeof(buf), 100);
|
||||
|
||||
if (buf[3]!=0) {
|
||||
fprintf(stderr,"k5_receive: it seems that byte 3 can be something else than 0, please notify the author\n");
|
||||
|
@ -452,9 +487,10 @@ struct k5_command *k5_receive(int fd,int tmout) {
|
|||
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,tmout); /* wait 500ms */
|
||||
len=read_timeout(fd,cmd->obfuscated_cmd+4,buf[2]+4,tmout);
|
||||
if ((len+4)!=(cmd->obfuscated_len)) {
|
||||
fprintf(stderr,"k5_receive err read1 len=%i wanted=%i\n",len,cmd->obfuscated_len);
|
||||
destroy_k5_struct(&cmd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
@ -485,14 +521,17 @@ int k5_readmem(int fd, unsigned char *buf, unsigned char maxlen, int offset)
|
|||
|
||||
r=k5_send_buf(fd,readmem,sizeof(readmem));
|
||||
if (!r) return(0);
|
||||
cmd=k5_receive(fd,10000);
|
||||
if (!cmd) return(0);
|
||||
cmd=k5_receive(fd,1000);
|
||||
if (!cmd || !cmd->cmd){
|
||||
destroy_k5_struct(&cmd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
if (verbose>2) k5_hexdump(cmd);
|
||||
|
||||
memcpy(buf,cmd->cmd+8,cmd->len-8);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
return(1);
|
||||
|
||||
}
|
||||
|
@ -528,18 +567,21 @@ int k5_writemem(int fd, unsigned char *buf, unsigned char len, int offset)
|
|||
r=k5_send_buf(fd,writemem,len+12);
|
||||
if (!r) return(0);
|
||||
|
||||
cmd=k5_receive(fd,10000);
|
||||
if (!cmd) return(0);
|
||||
cmd=k5_receive(fd,1000);
|
||||
if (!cmd || !cmd->cmd){
|
||||
destroy_k5_struct(&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);
|
||||
destroy_k5_struct(&cmd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
@ -571,7 +613,7 @@ int wait_flash_message(int fd,int ntimes) {
|
|||
|
||||
if (verbose>1) { printf("wait_flash_message try %i\n",ntimes); }
|
||||
|
||||
cmd=k5_receive(fd,10000);
|
||||
cmd=k5_receive(fd,1000);
|
||||
|
||||
if (!cmd) {
|
||||
/* No need to print, k5_receive already printed why it failed */
|
||||
|
@ -583,20 +625,20 @@ int wait_flash_message(int fd,int ntimes) {
|
|||
|
||||
if (!cmd->cmd) {
|
||||
printf("wait_flash_message: received malformed packet\n");
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cmd->cmd[0]!=0x18)&&(cmd->cmd[1]!=0x05)) {
|
||||
printf("wait_flash_message: got unexpected command type 0x%2.2x 0x%2.2x\n",cmd->cmd[1],cmd->cmd[0]);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
continue;
|
||||
}
|
||||
/* 36 is normal length, 22 is sent by some LSENG UV-K5 clone,
|
||||
* 20 is sent by some other version, so just use an arbitrarily chosen range */
|
||||
if ((cmd->len<18)||(cmd->len>50)) {
|
||||
printf("wait_flash_message: got unexpected command length %i\n",cmd->len);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -631,7 +673,7 @@ int wait_flash_message(int fd,int ntimes) {
|
|||
}
|
||||
buf[i]=0;
|
||||
printf("Flasher version is: [%s]\n",buf);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
@ -654,11 +696,11 @@ int k5_send_flash_version_message(int fd,char *version_string) {
|
|||
if (!r) return(0);
|
||||
|
||||
/* check if we're still getting packets, usually this is a 0x18 type packet, but not sure what else the radio can send */
|
||||
cmd=k5_receive(fd,10000);
|
||||
cmd=k5_receive(fd,1000);
|
||||
if (!cmd) return(0);
|
||||
|
||||
if (verbose>1) k5_hexdump(cmd);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
@ -708,9 +750,12 @@ int k5_writeflash(int fd, unsigned char *buf, int len, int offset,int max_flash
|
|||
/* wait for a reply packet */
|
||||
l=5;
|
||||
while(l) {
|
||||
cmd=k5_receive(fd,10000);
|
||||
cmd=k5_receive(fd,1000);
|
||||
l--;
|
||||
if (!cmd) {
|
||||
if (!cmd || !cmd->cmd) {
|
||||
if (cmd) {
|
||||
destroy_k5_struct(&cmd);
|
||||
}
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
|
@ -722,7 +767,7 @@ int k5_writeflash(int fd, unsigned char *buf, int len, int offset,int max_flash
|
|||
/* we're still getting "i'm in flash mode packets", can happen after the first flash command, ignore it */
|
||||
if ((cmd->cmd[0]==0x18)&&(cmd->cmd[1]==0x05)&&(cmd->cmd[2]==0x20)&&(cmd->cmd[3]==0x0)&&(cmd->cmd[4]==0x1)&&(cmd->cmd[5]==0x2)&&(cmd->cmd[6]==0x2)) {
|
||||
if (verbose>1) printf("&&&&| ignoring \"i'm in flash mode\" packet\n");
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -732,11 +777,11 @@ int k5_writeflash(int fd, unsigned char *buf, int len, int offset,int max_flash
|
|||
*/
|
||||
if (((cmd->cmd[0])!=0x1a)||((cmd->cmd[8])!=writeflash[8])||((cmd->cmd[9])!=writeflash[9])) {
|
||||
fprintf(stderr,"bad write confirmation\n");
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
continue;
|
||||
}
|
||||
ok=1;
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -932,19 +977,24 @@ int k5_prepare(int fd) {
|
|||
|
||||
r=k5_send_buf(fd,uvk5_hello,sizeof(uvk5_hello));
|
||||
if (!r) return(0);
|
||||
cmd=k5_receive(fd,10000);
|
||||
if (!cmd) return(0);
|
||||
cmd=k5_receive(fd,1000);
|
||||
if (!cmd || !cmd->cmd){
|
||||
destroy_k5_struct(&cmd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* this is a bit problem with people trying to read the radio config in firmware flash mode,
|
||||
* don't know why people do this, but they do it quite often */
|
||||
if ((cmd->cmd[0]==0x18)&&(cmd->cmd[1]==0x05)) {
|
||||
fprintf(stderr,"\nWARNING: this radio is in firmware flash mode (PTT + turn on).\n"
|
||||
"Please have the radio in normal mode to read the EEPROM\n\n");
|
||||
destroy_k5_struct(&cmd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
printf ("cmd: %2.2x %2.2x ok:%i\n",cmd->cmd[0],cmd->cmd[1],cmd->crcok);
|
||||
printf("****** Connected to firmware version: [%s]\n",(cmd->cmd)+4);
|
||||
destroy_k5_struct(cmd);
|
||||
destroy_k5_struct(&cmd);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue