home *** CD-ROM | disk | FTP | other *** search
- /* 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;
- }
-