home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
019.lha
/
Aterm
/
XmodemCRC.c
< prev
Wrap
C/C++ Source or Header
|
1986-11-10
|
20KB
|
723 lines
/* XmodemCRC.c - Checksum/CRC Xmodem */
/*
* Adapted from Djj's Aterm1.3 version by Jeff Lydiatt, Vancouver, Canada
*
* Business first... This code is NOT to be used for commercial purposes.
* You are granted a limited license to distribute it on a not-for-profit
* basis by any means, including, but not limited to, paper, magnetic media,
* or telecommunications. We have no objection to its appearance on a
* commercial communications network provided it may be obtained for no
* cost over and above the standard connect time charges.
* In addition, we would appreciate it if anyone modifying this code
* would attempt to get the modifications back to us so that we can
* keep some semblance of continuity of versions.
*
* Jeff Lydiatt
* Larry Phillips, Compuserve, 76703,4322
*
* This version supports the following features:
* . Both Cyclic Redundancy Check and Checksum Xmodem are supported.
* . AutoChop logic swiped from Aterm Version 6.1
* . Timeouts for receive are 5 seconds within a block, and
* 20 seconds outside a block.
* . Timeouts use the timer, rather than polling the elapsed time
* in the standard date function.
* . In send mode, the last block is padded with the Amiga HUNKEND
* symbol (0x0f2) if I think you're sending a loadable program. This
* is detected by all of the conditions, a) last block has 4 or more
* characters, and b) is a even multiple of four bytes, and c) it
* already ends with a HUNKEND symbol.
* If it's not a loadable module, the padding character is a
* control-Z, unless it already ends with control-Z. In this case
* the send file is padded with nulls ('\0').
* . Also for sends, the program gives a theoretical time to transmit the
* file based on it's size and the baud rate.
* . 17Aug86 - by Jal. Chop modified to allow for files like Arc which
* end in hexadecimal 1A00.
* . 17Aug86 - by Jal. Send routine padding character changed to standard
* of '\0' instead of CPM_EOF.
* . 30Sep86 - by Jal. Fixed CRC/Checksum_Verify always returns OK.
* . 22Oct86 - Xmodem works better with buffered I/O.
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <stdio.h>
#include <fcntl.h>
#include <libraries/dos.h>
#include <functions.h>
#include "ConsoleIO.h"
#include "SerialIO.h"
#include "Timer.h"
/* things for xmodem send and recieve */
#define SECSIZ 128
#define BLOCKTIME 20 /* max seconds to timeout for a new block */
#define CHARTIME 5 /* max seconds to timeout within a block */
#define BufSize SECSIZ*2 /* Text buffer */
#define ERRORMAX 10 /* Max errors before abort */
#define RETRYMAX 10 /* Maximum retrys before abort */
#define SOH 1 /* Start of sector char */
#define EOT 4 /* end of transmission char */
#define ACK 6 /* acknowledge sector transmission */
#define NAK 21 /* error in transmission detected */
#define HUNKEND 0x3F2L /* Normal Load Module end marker. */
#define CAN 24 /* Cancel character -- abort transmission */
#define TIMEOUT -1 /* timeout during receive character */
#define ABORT -2 /* user aborted XMODEM transfer with escape key */
#define OK TRUE /* sector sent OK */
#define ESC '\x1b' /* Use ESC to abort Xmodem transfer */
#define BELL ('G'& 0x1F) /* Bell */
#define CPM_EOF 26
#define FOREVER for(;;)
/* Note: the fillbuf() routine depends upon buffer being longword aligned.
* Having bytes_xferred immediately preceding buffer will accomplish
* this for us.
*/
static long bytes_xferred;
static char buffer[BufSize];
static FILE *fd;
static int
sector;
static BOOL
cancel = FALSE,
timeout = FALSE;
static short crcflag; /* TRUE if crc mode, FALSE for checksum */
/* can be changed in send mode by remote receiver */
static unsigned short crc; /* 16 bit crc value */
static BOOL autoChop;
static unsigned char checksum;
static ULONG waitMask;
/*--------------------------------------------------------------*/
/* check_abort: return TRUE if ESC pressed. */
/*--------------------------------------------------------------*/
static BOOL check_abort()
{
if ( CheckKey() != ESC )
cancel = FALSE;
else
{
PutString( "Xmodem Cancelled!\n");
PutChar( BELL );
cancel = TRUE;
}
return cancel;
}
/*--------------------------------------------------------------*/
/* sendchar: send a character to the modem. */
/*--------------------------------------------------------------*/
static void sendchar(ch)
int ch;
{
SerIOWrite( (UBYTE)(ch & 0xFF) );
}
/*--------------------------------------------------------------*/
/* readchar: timed wait for char to arrive. */
/*--------------------------------------------------------------*/
/*--------------------------------------
readchar() waits for a character
timeout built in.
Timeout condition causes error return
'seconds' controls duration of wait.
'doabort' TRUE indicates that CAN recvd
will set cancel flag
----------------------------------------*/
static int readchar( seconds, doabort )
int doabort;
int seconds;
{
UBYTE ch;
BOOL timerOn = FALSE;
timeout = FALSE;
do
{
if ( CheckSerIO() )
{
ch = SerIORead();
if ( doabort && (ch == CAN) )
cancel = TRUE;
return (int)(ch);
}
else if ( (ch = CheckKey()) != '\0' )
if ( ch == ESC )
{
PutString( "Xmodem Cancelled!\n");
PutChar( BELL );
cancel = TRUE;
return ABORT;
}
else;
if ( !timerOn )
{
StartTimer( (long)seconds, 0L );
timerOn = TRUE;
}
Wait( waitMask );
} while( !TimerExpired() );
/* if not the above, the timer must have timed out */
PutString( " XMODEM Timeout.\n");
timeout = TRUE;
return TIMEOUT;
}
/*--------------------------------------------------------------*/
/* purge: purge the serial port buffer until timeout. */
/*--------------------------------------------------------------*/
static void purge( purgetime )
int purgetime;
{
timeout = FALSE;
FlushSerIO();
while ( timeout == FALSE )
(void)readchar ( purgetime, FALSE );
}
/*--------------------------------------------------------------*/
/* do_crc: computes CRC using the CCITT polynomial. */
/*--------------------------------------------------------------*/
static void do_crc( val )
unsigned char val;
{
register unsigned short shifter,flag;
if(crcflag)
for( shifter = 0x80 ; shifter ; shifter >>= 1 )
{
flag = (crc & 0x8000);
crc <<= 1;
crc |= ((shifter & val) ? 1 : 0);
if( flag )
crc ^= 0x1021;
}
else
checksum += val;
}
/*-----------------------------------------------------------*/
/* SetWaitMask: for Console, Serial Read or timer expired. */
/*-----------------------------------------------------------*/
static void SetWaitMask()
{
waitMask = ( 1L << GetConsoleSigBit() )
| ( 1L << GetSerialSigBit() )
| ( 1L << GetTimerSigBit() );
}
/*-------------------------------------------------------*/
/* writeBlock: write a sector to disk */
/*-------------------------------------------------------*/
static int writeBlock( fd, buffer, length )
FILE *fd;
register unsigned char *buffer;
int length;
{
register int j;
for ( j=0; j < length; ++j, ++buffer)
{
if ( putc( *buffer, fd ) == EOF )
return EOF;
}
return length;
}
/*-------------------------------------------------------*/
/* readBlock: read a sector from disk to buffer */
/*-------------------------------------------------------*/
static int readBlock( fd, buffer, maxlength )
FILE *fd;
register unsigned char *buffer;
int maxlength;
{
register int j;
unsigned char fgetc();
for ( j=0; j < maxlength; ++j)
{
if ( !feof( fd ) )
*buffer++ = fgetc( fd );
else
{
if ( j > 0 )
--j;
break;
}
}
return j;
}
/*--------------------------------------------------------------*/
/* CloseEmUp: write and chop the last block. */
/*--------------------------------------------------------------*/
static void CloseEmUp( sector )
int sector;
{
register int i;
int len;
ULONG filesize = 0;
register UBYTE c;
register char *buf;
if ( sector > 0 ) /* skip if no data at all */
{
buf = &buffer[ (sector & 1) * SECSIZ ];
i = SECSIZ - 1;
c = '\0';
if ( !autoChop )
{
c = 0xff;
PutString( "Chop Disabled: " );
}
else
{
c = buf[ i ]; /* c gets last char in block */
if ( (c == '\0') || (c == CPM_EOF) || (c == 0xFF) )
{
while ( i > 0 )
if ( buf[ --i ] != c )
break;
}
}
if ( c == '\0' && buf[ i ] == CPM_EOF )
/* It may be an Arc file which normally ends in 1A00 so ... */
i++;
i++;
if ( writeBlock( fd, buf, i) != i )
PutString("Error writing final block\n");
len = SECSIZ - i;
format( PutFormat, "%d bytes chopped from final block\n\xff", &len);
filesize = ( --sector ) * (long)(SECSIZ) + (long)( i );
format( PutFormat, "File size: %ld\n\xff", &filesize);
}
}
/*--------------------------------------------------------------*/
/* verify_checksum: return TRUE if checksum or crc ok. */
/*--------------------------------------------------------------*/
static BOOL verify_checksum() {
unsigned short sentCRC;
if( crcflag )
{
do_crc(0); do_crc(0);
sentCRC = ((unsigned short) readchar(CHARTIME,FALSE)) << 8;
sentCRC += ((unsigned short) readchar(CHARTIME,FALSE)) & 0xFF;
return (sentCRC == crc);
}
else
return ( checksum == readchar(CHARTIME,FALSE) );
}
/*--------------------------------------------------------------*/
/* getfile: Do the handshake, read a sector, until EOT. */
/*--------------------------------------------------------------*/
static BOOL getfile()
{
unsigned char ch, currsect, compsect, response;
int i, j;
register char *bptr; /* rx buffer index */
sector = 1;
response = (crcflag) ? 'C' : NAK;
cancel = FALSE;
FlushSerIO();
FOREVER /* sector receive loop */
{
/*--------------------------------------------------------*/
/* Handshake loop. */
/*--------------------------------------------------------*/
for( i=1 ; !cancel && (i <= RETRYMAX) ; i++ )
{
sendchar( response );
if (timeout && (sector == 1) && ( i == RETRYMAX / 2) )
{
crcflag = (crcflag == FALSE);
response = (crcflag) ? 'C' : NAK;
PutString( "Sender not responding, switching to ");
PutString( (crcflag) ? "CRC" : "Checksum");
PutString( " mode\n");
}
if( (ch = readchar(BLOCKTIME,TRUE)) == SOH )
break;
if( ch == EOT ) /* if EOT , we're done */
{
sendchar( ACK );
CloseEmUp( --sector );
return TRUE;
}
if( ch == CAN ) /* aborted */
{
purge( CHARTIME );
PutString( "Sender cancelled transmission.\n");
return FALSE;
}
if( i == RETRYMAX ) /* timed out */
{
PutString( "Sender not responding\nXMODEM Cancelled!\n" );
return FALSE;
}
} /* end for (i <= RETRYMAX) */
if ( cancel )
{
purge( CHARTIME );
sendchar( CAN );
return FALSE;
}
/*--------------------------------------------------------*/
/* Here Comes a Block */
/*--------------------------------------------------------*/
currsect = readchar(CHARTIME,FALSE);
if ( !cancel )
compsect = readchar(CHARTIME,FALSE);
checksum = crc = 0;
bptr = &buffer[ ( sector & 1 ) * SECSIZ ];
for( j = 0 ; !(cancel || timeout) && j < SECSIZ ; j++ )
{
*bptr = readchar( CHARTIME, FALSE );
do_crc( *bptr++ );
}
/*--------------------------------------------------------*/
/* Any errors in receiving the block? */
/*--------------------------------------------------------*/
if ( cancel || timeout )
continue;
response = NAK; /* default response */
if (!verify_checksum())
{
PutString( "Checksum error\n");
continue;
}
if ( (~currsect & 0xFF) != (compsect & 0xFF) )
continue;
if ( (currsect == (sector - 1) & 0xFF ) ) /* dup sector number? */
{
response = ACK;
continue; /* ignore it */
}
if ( currsect != (sector & 0xFF) ) /* sequence error? */
{
PutString( "Sector numbering error - Aborting." );
sendchar(CAN);
return FALSE;
}
if ( sector > 1 )
if( writeBlock(fd, &buffer[ ((sector-1) & 1) * SECSIZ ], SECSIZ)
!= SECSIZ)
{
PutString( "Error writing file\n");
sendchar(CAN);
return FALSE;
}
format( PutFormat, "\x0bReceived block # %d \n\xFF", §or );
response = ACK; /* sector received ok */
sector++;
} /* end FOREVER */
}
/*--------------------------------------------------------------*/
/* XMODEM_Read_File: the external entry point for readfile.*/
/*--------------------------------------------------------------*/
BOOL XMODEM_Read_File(file, xfermode, chopFlag)
char *file;
BOOL xfermode, chopFlag;
{
BOOL status;
cancel = FALSE;
crcflag = xfermode;
autoChop = chopFlag;
SetWaitMask();
if ((fd = fopen(file, "w")) == NULL)
{
PutString( "Cannot Open File\n");
sendchar(CAN);
return FALSE;
}
else
PutString( "Receiving File\n\n" );
status = getfile();
fclose( fd );
return status;
}
/*--------------------------------------------------------------*/
/* doTellSize: tell file size and how long to send it. */
/*--------------------------------------------------------------*/
static void doTellSize(file, baud)
char *file;
int baud;
{
struct parms
{
LONG size;
WORD sectors;
WORD minutes;
WORD seconds;
WORD baudrate;
} args;
struct FileInfoBlock *FBlock;
long *FLock;
if ( (FLock = (struct FileLock *) Lock(file, ACCESS_READ)) == NULL)
return;
if ( (FBlock = (struct FileInfoBlock *)
AllocMem( (long)sizeof(struct FileInfoBlock), (long)(MEMF_CHIP))) != NULL)
if ( Examine( FLock, FBlock) )
{
args.size = FBlock->fib_Size;
if (args.size != 0)
{
args.sectors = (args.size + SECSIZ - 1) / SECSIZ;
format( PutFormat,
"File contains %ld bytes, %d sectors.\n\xff", &args.size );
/* compute time for sending file */
args.baudrate = baud;
args.seconds = (ULONG)args.sectors * 133L * 11L / (ULONG)baud;
args.minutes = args.seconds / 60;
args.seconds %= 60;
format( PutFormat,
"Approximately %d minutes and %d seconds for transfer at %d bps.\n\xff",
&args.minutes );
}
FreeMem( FBlock, (long)sizeof(struct FileInfoBlock) );
}
UnLock( FLock );
return;
}
/*--------------------------------------------------------------*/
/* sendsector: send a sector with resend on error from receiver.*/
/*--------------------------------------------------------------*/
static int sendsector( buf )
unsigned char *buf;
{
int badblock;
register int i;
register unsigned char *bptr;
badblock = 0;
cancel = FALSE;
FOREVER
{
if( badblock == ERRORMAX )
return TIMEOUT;
if( cancel || check_abort() )
return ABORT;
FlushSerIO(); /* flush any garbage from line */
format( PutFormat, "\x0bSending sector %d\n\xFF", §or );
sendchar( SOH ); /* start of block */
sendchar( sector ); /* sector number */
sendchar( ~sector ); /* compliment of sector number */
checksum = crc = 0; /* clear CRC and checksum */
bptr = buf;
for( i = 0 ; i < SECSIZ ; i++ )
{
sendchar( *bptr );
do_crc( *bptr++ );
}
FlushSerIO(); /* flush garbage from receiver */
if( crcflag )
{
do_crc(0); do_crc(0); /* cycle CRC generator */
sendchar( crc >> 8 ); /* High byte */
sendchar( crc ); /* Low byte */
}
else
sendchar( checksum );
if( readchar(BLOCKTIME, TRUE ) == ACK)
break;
else
badblock++;
}
sector++; /* bump sector count */
return OK;
}
/*--------------------------------------------------------------*/
/* fillbuf: fill the buffer. Return TRUE data to be sent. */
/*--------------------------------------------------------------*/
static BOOL fillbuf()
{
register int bytes_read;
register long *iptr;
char padchar;
bytes_read = readBlock(fd, buffer, SECSIZ);
if (bytes_read <= 0) /* end of file */
{
if ( bytes_read < 0 )
PutString( "Error reading Send File. Xmodem Cancelled.\n" );
return FALSE;
}
else if( bytes_read == SECSIZ )
return TRUE;
/* Pad with Hunk ends if this is a load module */
if ( (bytes_read >= 4) && ( (bytes_read % 4) == 0 ) )
iptr = (long * ) ((long) buffer + bytes_read - 4);
else
iptr = NULL;
if ( (iptr != NULL) && (*iptr == HUNKEND) )
for ( iptr++; bytes_read < SECSIZ; bytes_read += 4 )
*iptr++ = HUNKEND;
/* Otherwise, make the padding character != last character of data */
else
{
if ( buffer[ bytes_read - 1 ] != '\0' )
padchar = '\0';
else
padchar = CPM_EOF;
for(; bytes_read < SECSIZ; bytes_read++)
buffer[bytes_read] = padchar;
}
return TRUE;
}
/*--------------------------------------------------------------*/
/* putfile: Handshake & send the file by sector. */
/*--------------------------------------------------------------*/
static BOOL putfile()
{
int timeoutcount, status;
UBYTE ch;
sector = 1; timeoutcount = 0;
FlushSerIO(); /* flush any garbage in receiver */
do /* start handshake with receiver */
{
ch = readchar(BLOCKTIME,TRUE);
if(timeout)
{
if(++timeoutcount == RETRYMAX)
{
PutString( "No response, aborting\n");
return FALSE;
}
}
if( cancel ) return FALSE;
} while (ch != NAK && ch != 'C');
status = OK;
crcflag = (ch == 'C');
PutString( crcflag ? "CRC" : "Checksum");
PutString( " mode requested\n\n");
while( fillbuf() && status == OK ) /* fill file buffer until EOF */
status = sendsector( buffer ); /* send sector */
cancel = FALSE;
timeoutcount = 0;
do
{
FlushSerIO();
sendchar( EOT ); /* end transmission */
if ( timeout )
{
if ( timeoutcount++ == RETRYMAX || cancel || check_abort())
return FALSE;
PutString( "Awaiting Acknowledge of EOT...\n" );
}
} while ( readchar( BLOCKTIME, TRUE ) != ACK);
if ( status == TIMEOUT || status == ABORT || timeoutcount >= RETRYMAX )
return FALSE;
else
return TRUE;
}
/*--------------------------------------------------------------*/
/* XMODEM_Send_File: External entry for Xmodem Send. */
/*--------------------------------------------------------------*/
XMODEM_Send_File(file, baudrate)
char *file;
int baudrate;
{
int status, timeoutcount;
unsigned char ch;
cancel = FALSE;
SetWaitMask();
doTellSize(file, baudrate);
if ((fd = fopen(file, "r")) == NULL)
{
PutString( "Cannot Open Send File\n");
sendchar(EOT);
return FALSE;
}
else
PutString( "Sending File\n \n \n");
status = putfile();
fclose( fd );
return status;
}