Skip to content
Snippets Groups Projects
Commit fd26b324 authored by rswindell's avatar rswindell
Browse files

Add official support for Xmodem-G (aka Qmodem-G)

xmodem_get_ack() now returns an int (ACK on success)
Use 3-second block 0/1 timeout to follow ymodem.doc example
Fix the use of xm.max_errors
Support (configurable) fallback from Ymodem-send to Xmodem-send
sexyz.ini can be used to disable 1024-recv, CRC-send and G-send support
Document more sexyz command-line options to enable legacy x/ymodem modes
parent 53091ac5
No related branches found
No related tags found
No related merge requests found
......@@ -844,9 +844,9 @@ static int send_files(char** fname, uint fnames)
success=FALSE;
startfile=time(NULL);
lprintf(LOG_INFO,"Sending %s (%lu KB) via %s"
lprintf(LOG_INFO,"Sending %s (%lu KB) via %cMODEM"
,path,fsize/1024
,mode&XMODEM ? "Xmodem" : mode&YMODEM ? "Ymodem" : "Zmodem");
,mode&XMODEM ? 'X' : mode&YMODEM ? 'Y' : 'Z');
if(mode&ZMODEM)
success=zmodem_send_file(&zm, path, fp, /* ZRQINIT? */fnum==0, &startfile, &sent_bytes);
......@@ -924,11 +924,11 @@ static int send_files(char** fname, uint fnames)
if(xmodem_get_mode(&xm)) {
lprintf(LOG_INFO,"Sending Ymodem termination block");
lprintf(LOG_INFO,"Sending YMODEM termination block");
memset(block,0,XMODEM_MIN_BLOCK_SIZE); /* send short block for terminator */
xmodem_put_block(&xm, block, XMODEM_MIN_BLOCK_SIZE /* block_size */, 0 /* block_num */);
if(!xmodem_get_ack(&xm,6,0)) {
if(xmodem_get_ack(&xm, /* tries: */6, /* block_num: */0) != ACK) {
lprintf(LOG_WARNING,"Failed to receive ACK after terminating block");
}
}
......@@ -977,23 +977,25 @@ static int receive_files(char** fname_list, int fnames)
else {
if(mode&YMODEM) {
lprintf(LOG_INFO,"Fetching Ymodem header block");
lprintf(LOG_INFO,"Fetching YMODEM header block");
for(errors=0;errors<=xm.max_errors && !xm.cancelled;errors++) {
if(errors>(xm.max_errors/2) && mode&CRC && !(mode&GMODE))
mode&=~CRC;
xmodem_put_nak(&xm, /* expected_block: */ 0);
if(xmodem_get_block(&xm, block, /* expected_block: */ 0) == 0) {
if(xmodem_get_block(&xm, block, /* expected_block: */ 0) == SUCCESS) {
send_byte(NULL,ACK,10);
break;
}
if(errors+1>xm.max_errors/3 && mode&CRC && !(mode&GMODE)) {
lprintf(LOG_NOTICE,"Falling back to 8-bit Checksum mode");
mode&=~CRC;
}
}
if(errors>=xm.max_errors || xm.cancelled) {
lprintf(LOG_ERR,"Error fetching Ymodem header block");
if(errors>xm.max_errors || xm.cancelled) {
lprintf(LOG_ERR,"Error fetching YMODEM header block");
xmodem_cancel(&xm);
return(1);
}
if(!block[0]) {
lprintf(LOG_INFO,"Received Ymodem termination block");
lprintf(LOG_INFO,"Received YMODEM termination block");
return(0);
}
file_bytes=ftime=total_files=total_bytes=0;
......@@ -1005,11 +1007,11 @@ static int receive_files(char** fname_list, int fnames)
,&total_files /* remaining files to be sent */
,&total_bytes /* remaining bytes to be sent */
);
lprintf(LOG_DEBUG,"Ymodem header (%u fields): %s", i, block+strlen(block)+1);
lprintf(LOG_DEBUG,"YMODEM header (%u fields): %s", i, block+strlen(block)+1);
SAFECOPY(fname,block);
} else { /* Zmodem */
lprintf(LOG_INFO,"Waiting for Zmodem sender...");
lprintf(LOG_INFO,"Waiting for ZMODEM sender...");
i=zmodem_recv_init(&zm);
......@@ -1105,14 +1107,15 @@ static int receive_files(char** fname_list, int fnames)
}
if(mode&XMODEM)
lprintf(LOG_INFO,"Receiving %s via Xmodem %s"
lprintf(LOG_INFO,"Receiving %s via XMODEM%s %s"
,str
,mode&GMODE ? "-G" : ""
,mode&CRC ? "CRC-16":"Checksum");
else
lprintf(LOG_INFO,"Receiving %s (%lu KB) via %s %s"
,str
,file_bytes/1024
,mode&YMODEM ? mode&GMODE ? "Ymodem-G" : "Ymodem" :"Zmodem"
,mode&YMODEM ? mode&GMODE ? "YMODEM-G" : "YMODEM" :"ZMODEM"
,mode&ZMODEM ? "" : (mode&CRC ? "CRC-16" : "Checksum"));
startfile=time(NULL);
......@@ -1138,7 +1141,7 @@ static int receive_files(char** fname_list, int fnames)
xmodem_progress(NULL,block_num,ftell(fp),file_bytes,startfile);
i=xmodem_get_block(&xm, block, block_num);
if(i!=0) {
if(i!=SUCCESS) {
if(i==EOT) { /* end of transfer */
success=TRUE;
xmodem_put_ack(&xm);
......@@ -1152,20 +1155,23 @@ static int receive_files(char** fname_list, int fnames)
if(mode&GMODE)
return(-1);
if(++errors>=xm.max_errors) {
if(++errors>xm.max_errors) {
lprintf(LOG_ERR,"Too many errors (%u)",errors);
xmodem_cancel(&xm);
break;
}
if(block_num==1 && errors>(xm.max_errors/2) && mode&CRC && !(mode&GMODE))
if(i!=NOT_XMODEM
&& block_num==1 && errors>xm.max_errors/3 && mode&CRC && !(mode&GMODE)) {
lprintf(LOG_NOTICE,"Falling back to 8-bit Checksum mode (error=%d)", i);
mode&=~CRC;
}
xmodem_put_nak(&xm, block_num);
continue;
}
if(!(mode&GMODE))
send_byte(NULL,ACK,10);
if(file_bytes_left<=0L) { /* No more bytes to send */
lprintf(LOG_WARNING,"Attempt to send more byte specified in header");
if(file_bytes_left<=0L) { /* No more bytes to receive */
lprintf(LOG_WARNING,"Sender attempted to send more bytes than were specified in header");
break;
}
wr=xm.block_size;
......@@ -1264,9 +1270,12 @@ static const char* usage=
"socket = TCP socket descriptor\n"
#endif
"\n"
"opts = -y to overwrite files when receiving\n"
"opts = -y allow overwriting of existing files when receiving\n"
" -o disable Zmodem CRC-32 mode (use CRC-16)\n"
" -s disable Zmodem streaming (Slow Zmodem)\n"
" -k enable X/Ymodem-1K send mode\n"
" -c enable Xmodem-CRC receive mode\n"
" -g enable X/Ymodem-G receive mode (no error recovery)\n"
" -2 set maximum Zmodem block size to 2K\n"
" -4 set maximum Zmodem block size to 4K\n"
" -8 set maximum Zmodem block size to 8K (ZedZap)\n"
......@@ -1275,11 +1284,11 @@ static const char* usage=
" -rlogin or -ssh or -raw to disable Telnet mode\n"
"\n"
"cmd = v to display detailed version information\n"
" sx to send Xmodem rx to recv Xmodem\n"
" sX to send Xmodem-1K rc to recv Xmodem-CRC\n"
" sy to send Ymodem ry to recv Ymodem\n"
" sY to send Ymodem-1K rg to recv Ymodem-G\n"
" sz to send Zmodem rz to recv Zmodem\n"
" sx to send Xmodem rx to receive Xmodem\n"
" sX to send Xmodem-1K rc to receive Xmodem-CRC\n"
" sy to send Ymodem ry to receive Ymodem\n"
" sY to send Ymodem-1K rg to receive Ymodem-G\n"
" sz to send Zmodem rz to receive Zmodem\n"
"\n"
"file = filename to send or receive\n"
"path = directory to receive files into\n"
......@@ -1316,7 +1325,7 @@ int main(int argc, char **argv)
sscanf("$Revision$", "%*s %s", revision);
fprintf(statfp,"\nSynchronet External X/Y/Zmodem v%s-%s"
fprintf(statfp,"\nSynchronet External X/Y/ZMODEM v%s-%s"
" Copyright %s Rob Swindell\n\n"
,revision
,PLATFORM_DESC
......@@ -1364,9 +1373,14 @@ int main(int argc, char **argv)
xm.recv_timeout =iniReadInteger(fp,"Xmodem","RecvTimeout",xm.recv_timeout); /* seconds */
xm.byte_timeout =iniReadInteger(fp,"Xmodem","ByteTimeout",xm.byte_timeout); /* seconds */
xm.ack_timeout =iniReadInteger(fp,"Xmodem","AckTimeout",xm.ack_timeout); /* seconds */
xm.block_size =iniReadInteger(fp,"Xmodem","BlockSize",xm.block_size); /* 128 or 1024 */
xm.block_size =iniReadInteger(fp,"Xmodem","BlockSize",xm.block_size); /* 128 or 1024 */
xm.max_block_size =iniReadInteger(fp,"Xmodem","MaxBlockSize",xm.max_block_size); /* 128 or 1024 */
xm.max_errors =iniReadInteger(fp,"Xmodem","MaxErrors",xm.max_errors);
xm.g_delay =iniReadInteger(fp,"Xmodem","G_Delay",xm.g_delay);
xm.crc_mode_supported =iniReadBool(fp,"Xmodem","SendCRC",xm.crc_mode_supported);
xm.g_mode_supported =iniReadBool(fp,"Xmodem","SendG",xm.g_mode_supported);
xm.fallback_to_xmodem =iniReadInteger(fp,"Ymodem","FallbackToXmodem", xm.fallback_to_xmodem);
zm.init_timeout =iniReadInteger(fp,"Zmodem","InitTimeout",zm.init_timeout); /* seconds */
zm.send_timeout =iniReadInteger(fp,"Zmodem","SendTimeout",zm.send_timeout); /* seconds */
......@@ -1445,6 +1459,9 @@ int main(int argc, char **argv)
case 'Y':
mode|=(YMODEM|CRC);
break;
case 'k': /* Ymodem-Checksum for debug/test purposes only */
mode|=YMODEM;
break;
case 'g':
case 'G':
mode|=(YMODEM|CRC|GMODE);
......@@ -1516,8 +1533,8 @@ int main(int argc, char **argv)
case 'S': /* disable Zmodem streaming */
zm.no_streaming=TRUE;
break;
case 'G': /* Ymodem-G */
mode|=GMODE;
case 'G': /* Ymodem-G or Xmodem-G (a.k.a. Qmodem-G) */
mode|=(GMODE|CRC);
break;
case 'Y':
mode|=OVERWRITE;
......
......@@ -8,7 +8,7 @@
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html *
* Copyright 2008 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
......@@ -46,7 +46,7 @@
#define YMODEM (1<<3) /* Use Ymodem */
#define ZMODEM (1<<4) /* Use Zmodem */
#define CRC (1<<5) /* Use CRC error correction */
#define GMODE (1<<6) /* For Qmodem-G and Ymodem-G */
#define GMODE (1<<6) /* For Xmodem-G and Ymodem-G */
#define RECVDIR (1<<7) /* Directory specified to download to */
#define OVERWRITE (1<<9) /* Overwrite receiving files */
......@@ -54,5 +54,8 @@
#define CTRL_ESC (1<<0) /* Escape all control chars */
#define VAR_HDRS (1<<1) /* Use variable headers */
#define NOINP -1 /* input buffer empty (incom only) */
#define NOT_YMODEM -3 /* Expected block zero, got block one */
#define SUCCESS 0
#define NOINP -1 /* input buffer empty (incom only) */
#define FAILURE -2 /* xmodem_get_block() failure */
#define NOT_YMODEM -3 /* Expected block zero, got block one */
#define NOT_XMODEM -4 /* Expected block one, got block zero */
......@@ -116,7 +116,7 @@ int xmodem_put_nak(xmodem_t* xm, unsigned block_num)
; /* wait for any trailing data */
if(block_num<=1) {
if(*(xm->mode)&GMODE) { /* G for Ymodem-G */
if(*(xm->mode)&GMODE) { /* G for X/Ymodem-G */
lprintf(xm,LOG_INFO,"Requesting mode: Streaming, 16-bit CRC");
return putcom('G');
} else if(*(xm->mode)&CRC) { /* C for CRC */
......@@ -145,7 +145,7 @@ int xmodem_cancel(xmodem_t* xm)
xm->cancelled=TRUE;
}
return 0;
return SUCCESS;
}
/****************************************************************************/
......@@ -162,7 +162,7 @@ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) {
i=getcom(expected_block_num<=1 ? 5 : 10);
i=getcom(expected_block_num<=1 ? 3 : 10);
if(eot && i!=EOT && i!=NOINP)
eot=0;
if(can && i!=CAN)
......@@ -172,10 +172,12 @@ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
xm->block_size=XMODEM_MIN_BLOCK_SIZE;
break;
case STX: /* 1024 byte blocks */
if(xm->max_block_size < XMODEM_MAX_BLOCK_SIZE)
return FAILURE;
xm->block_size=XMODEM_MAX_BLOCK_SIZE;
break;
case EOT:
lprintf(xm,LOG_DEBUG,"EOT");
lprintf(xm,LOG_DEBUG,"Block %u: EOT received", expected_block_num);
if(/*((*xm->mode)&(YMODEM|GMODE))==YMODEM &&*/ !eot) {
lprintf(xm,LOG_INFO,"NAKing first EOT");
eot=1;
......@@ -186,13 +188,15 @@ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
case CAN:
if(!can) { /* must get two CANs in a row */
can=1;
lprintf(xm,LOG_WARNING,"Received CAN Expected SOH, STX, or EOT");
lprintf(xm,LOG_WARNING,"Block %u: Received CAN Expected SOH, STX, or EOT"
,expected_block_num);
continue;
}
lprintf(xm,LOG_WARNING,"Cancelled remotely");
lprintf(xm,LOG_WARNING,"Block %u: Cancelled remotely", expected_block_num);
return(CAN);
default:
lprintf(xm,LOG_WARNING,"Received %s Expected SOH, STX, or EOT",chr((uchar)i));
lprintf(xm,LOG_WARNING,"Block %u: Received %s Expected SOH, STX, or EOT"
,expected_block_num, chr((uchar)i));
case NOINP: /* Nothing came in */
if(eot)
return(EOT);
......@@ -226,8 +230,8 @@ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
chksum=getcom(xm->byte_timeout);
if(block_num!=(uchar)~block_inv) {
lprintf(xm,LOG_WARNING,"Block number bit error (0x%02X vs 0x%02x)"
,block_num,(uchar)~block_inv);
lprintf(xm,LOG_WARNING,"Block %u: Block number bit error (0x%02X vs 0x%02x)"
,expected_block_num, block_num,(uchar)~block_inv);
break;
}
......@@ -248,6 +252,8 @@ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
if(block_num!=(uchar)(expected_block_num&0xff)) {
lprintf(xm,LOG_WARNING,"Block number error (%u received, expected %u)"
,block_num,expected_block_num&0xff);
if((*xm->mode)&XMODEM && expected_block_num==1 && block_num==0)
return(NOT_XMODEM);
if(expected_block_num==0 && block_num==1)
return(NOT_YMODEM);
if(expected_block_num && block_num==(uchar)((expected_block_num-1)&0xff))
......@@ -255,10 +261,10 @@ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
break;
}
return(0); /* Success */
return SUCCESS; /* Success */
}
return(-2); /* Failure */
return FAILURE; /* Failure */
}
/*****************/
......@@ -303,35 +309,35 @@ int xmodem_put_block(xmodem_t* xm, uchar* block, unsigned block_size, unsigned b
/************************************************************/
/* Gets an acknowledgement - usually after sending a block */
/* Returns 1 if ack received, 0 otherwise. */
/* Returns ACK if ack received */
/************************************************************/
BOOL xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num)
int xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num)
{
int i,can=0;
int i=NOINP,can=0;
unsigned errors;
for(errors=0;errors<tries && is_connected(xm);errors++) {
for(errors=0;errors<tries && is_connected(xm);) {
if((*xm->mode)&GMODE) { /* Don't wait for ACK on Ymodem-G */
if((*xm->mode)&GMODE) { /* Don't wait for ACK on X/Ymodem-G */
SLEEP(xm->g_delay);
if(getcom(0)==CAN) {
lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num);
xmodem_cancel(xm);
return(FALSE);
return(CAN);
}
return(TRUE);
return(ACK);
}
i=getcom(xm->ack_timeout);
if(can && i!=CAN)
can=0;
if(i==ACK)
return(TRUE);
break;
if(i==CAN) {
if(can) {
if(can) { /* 2 CANs in a row */
lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num);
xmodem_cancel(xm);
return(FALSE);
return(CAN);
}
can=1;
}
......@@ -339,11 +345,13 @@ BOOL xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num)
lprintf(xm,LOG_WARNING,"Block %u: !Received %s Expected ACK"
,block_num, chr((uchar)i));
if(i!=CAN)
return(FALSE);
}
return(i);
}
if(i!=CAN)
errors++;
}
return(FALSE);
return(i);
}
BOOL xmodem_get_mode(xmodem_t* xm)
......@@ -365,10 +373,14 @@ BOOL xmodem_get_mode(xmodem_t* xm)
return(TRUE);
case 'C':
lprintf(xm,LOG_INFO,"Receiver requested mode: 16-bit CRC");
if(!xm->crc_mode_supported)
continue;
*(xm->mode)|=CRC;
return(TRUE);
case 'G':
lprintf(xm,LOG_INFO,"Receiver requested mode: Streaming, 16-bit CRC");
if(!xm->crc_mode_supported || !xm->g_mode_supported)
continue;
*(xm->mode)|=(GMODE|CRC);
return(TRUE);
case CAN:
......@@ -462,17 +474,24 @@ BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start,
,xm->total_files-xm->sent_files
,xm->total_bytes-xm->sent_bytes);
lprintf(xm,LOG_INFO,"Sending Ymodem header block: '%s'",block+strlen(block)+1);
lprintf(xm,LOG_INFO,"Sending YMODEM header block: '%s'",block+strlen(block)+1);
block_len=strlen(block)+1+i;
for(xm->errors=0;xm->errors<=xm->max_errors && !is_cancelled(xm) && is_connected(xm);xm->errors++) {
xmodem_put_block(xm, block, block_len <=XMODEM_MIN_BLOCK_SIZE ? XMODEM_MIN_BLOCK_SIZE:XMODEM_MAX_BLOCK_SIZE, 0 /* block_num */);
if(xmodem_get_ack(xm,1,0)) {
if((i=xmodem_get_ack(xm,/* tries: */1, /* block_num: */0)) == ACK) {
sent_header=TRUE;
break;
}
if((i==NAK || i=='C' || i=='G')
&& xm->fallback_to_xmodem && xm->errors+1 == xm->fallback_to_xmodem) {
lprintf(xm,LOG_NOTICE,"Falling back to XMODEM mode after %u attempts"
,xm->fallback_to_xmodem);
*(xm->mode)&=~YMODEM;
break;
}
}
if(xm->errors>=xm->max_errors || is_cancelled(xm)) {
if(xm->errors>xm->max_errors || is_cancelled(xm)) {
lprintf(xm,LOG_ERR,"Failed to send header block");
break;
}
......@@ -511,7 +530,7 @@ BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start,
if(xm->progress!=NULL)
xm->progress(xm->cbdata,block_num,ftell(fp),st.st_size,startfile);
xmodem_put_block(xm, block, xm->block_size, block_num);
if(!xmodem_get_ack(xm,5,block_num)) {
if(xmodem_get_ack(xm, /* tries: */5,block_num) != ACK) {
xm->errors++;
lprintf(xm,LOG_WARNING,"Error #%d at offset %ld"
,xm->errors,ftell(fp)-xm->block_size);
......@@ -576,11 +595,14 @@ void xmodem_init(xmodem_t* xm, void* cbdata, long* mode
xm->ack_timeout=10; /* seconds */
xm->block_size=XMODEM_MAX_BLOCK_SIZE;
xm->max_block_size=XMODEM_MAX_BLOCK_SIZE;
xm->max_errors=9;
xm->g_delay=1;
xm->cbdata=cbdata;
xm->mode=mode;
xm->g_mode_supported=TRUE;
xm->crc_mode_supported=TRUE;
xm->lputs=lputs;
xm->progress=progress;
xm->send_byte=send_byte;
......
......@@ -49,13 +49,17 @@ typedef struct {
void* cbdata;
long* mode;
BOOL cancelled;
BOOL crc_mode_supported; /* for send */
BOOL g_mode_supported; /* for send */
unsigned block_size;
unsigned max_block_size; /* for recv */
unsigned ack_timeout;
unsigned byte_timeout;
unsigned send_timeout;
unsigned recv_timeout;
unsigned errors;
unsigned max_errors;
unsigned fallback_to_xmodem; /* fallback to Xmodem after this many Ymodem send attempts */
unsigned g_delay;
unsigned total_files;
unsigned total_bytes;
......@@ -82,7 +86,7 @@ void xmodem_init(xmodem_t*, void* cbdata, long* mode
char* xmodem_ver(char *buf);
const char* xmodem_source(void);
int xmodem_cancel(xmodem_t*);
BOOL xmodem_get_ack(xmodem_t*, unsigned tries, unsigned block_num);
int xmodem_get_ack(xmodem_t*, unsigned tries, unsigned block_num);
BOOL xmodem_get_mode(xmodem_t*);
BOOL xmodem_put_eot(xmodem_t*);
int xmodem_put_ack(xmodem_t*);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment