home *** CD-ROM | disk | FTP | other *** search
-
- /*------------------------------------------------
- XMODEMS.C
-
- Author Date Description
- -------------------------------------------
- Jon Ward 22 Apr 90 Initial Revision.
- Jon Ward 23 Apr 90 Cleanup and modify for
- XMODEM-1K and XMODEM-CRC.
- Jon Ward 26 Apr 90 Corrected implementation of
- XMODEM-CRC.
- ------------------------------------------------*/
- #define XMODEM_LIB 1
-
- #include <stdio.h>
- #include "xmodem.h"
-
-
- #define STATIC static /* undef for debugging */
-
- /*------------------------------------------------
- Local Function Prototypes
- ------------------------------------------------*/
- STATIC int xm_send_file (
- FILE *f, /* file pointer */
- xblock *xb, /* pointer to block data */
- xfunc *xmf); /* xmodem external functions */
-
- STATIC int xm_send_block (
- xblock *xb, /* pointer to block data */
- xfunc *xmf); /* xmodem external functions */
-
-
- /*------------------------------------------------
- This function sends a file via XMODEM, XMODEM-1K
- or XMODEM/CRC. The f argument represents the
- file to send that has been opened for reading.
- ------------------------------------------------*/
- int xmodem_send (
- int block_size, /* maximum block size */
- FILE *f, /* file to write to */
- int (*transmit) (char), /* xmit function */
- int (*receive) (long, unsigned int *), /* recv function */
- void (*dispstat) (long, long, const char *), /* display function */
- int (*check_abort) (void)) /* manual abort function */
- {
- xblock xb; /* block data */
- xfunc xmfuncs; /* xmodem external functions */
- register int error_count; /* counter for errors */
- register int can_count; /* cancel counter */
- int error; /* gen error var */
- unsigned int rerr; /* received char error */
-
- /*------------------------------------------------
- Initialize the function pointer structure.
- ------------------------------------------------*/
- if ((xmfuncs.dispstat = dispstat) == NULL)
- xmfuncs.dispstat = xm_no_disp_func;
-
- if ((xmfuncs.check_abort = check_abort) == NULL)
- xmfuncs.check_abort = xm_no_abort_func;
-
- if ((xmfuncs.transmit = transmit) == NULL)
- return (xm_perror (XERR_XMIT_FUNC, &xmfuncs));
-
- if ((xmfuncs.receive = receive) == NULL)
- return (xm_perror (XERR_RCVR_FUNC, &xmfuncs));
-
-
- /*------------------------------------------------
- ------------------------------------------------*/
- xb.total_block_count = 0L;
- xb.total_byte_count = 0L;
-
- (*xmfuncs.dispstat) (0L, 0L, "");
-
- PURGE_RECEIVER(receive);
-
-
- /*------------------------------------------------
- Purge all data from the receive buffer. Then we
- wait for 1 full minute. If we receive no
- characters during that time, then we return
- indicating the file was not transferred. If we
- receive a NAK, then we begin transmission. If
- we receive an excessive number of garbage
- characters, then we return indicating no file
- transfer.
- ------------------------------------------------*/
- for (error_count = 0, can_count = 0; 1; )
- {
- int rcvd_char;
-
- if (error_count >= START_XMIT_RETRY_COUNT)
- return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
-
- /* try for 1 minute */
- rcvd_char = (*xmfuncs.receive) (START_XMIT_TIMEOUT, &rerr);
-
- if (rerr != 0)
- {
- xm_send_cancel (xmfuncs.transmit);
- return (xm_perror (XERR_CHAR_ERROR, &xmfuncs));
- }
-
-
- switch (rcvd_char)
- {
- case RECV_TIMEOUT:
- xm_send_cancel (xmfuncs.transmit);
- return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
-
- case 'C':
- xb.crc_used = 1;
- break;
-
- case NAK:
- xb.crc_used = 0;
- break;
-
- case CAN:
- if (++can_count >= CAN_COUNT_ABORT)
- {
- xm_send_cancel (xmfuncs.transmit);
- return (xm_perror (XERR_RCVR_CANCEL, &xmfuncs));
- }
- continue;
-
- default:
- can_count = 0;
- error_count++;
- continue;
- }
-
- break;
- }
-
-
- /*------------------------------------------------
- Setup the block size and the start of block
- character and send the file.
- ------------------------------------------------*/
- if (block_size == XMODEM_1K_BLOCK_SIZE)
- {
- xb.buflen = XMODEM_1K_BLOCK_SIZE;
- xb.start_char = STX;
- }
- else
- {
- xb.buflen = XMODEM_BLOCK_SIZE;
- xb.start_char = SOH;
- }
-
- if ((error = xm_send_file (f, &xb, &xmfuncs)) != XERR_OK)
- return (error);
-
-
- /*------------------------------------------------
- Now, we send an EOT to the receiver and we
- expect to get an ACK within 1 minute. If we
- don't then we timeout and report an error. If
- we get any character other than an ACK, we
- retransmit the EOT. If we get an ACK, then we
- exit with hopeful success.
- ------------------------------------------------*/
- while (1)
- {
- if ((*xmfuncs.transmit) (EOT) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, &xmfuncs));
-
- switch ((*receive) (START_XMIT_TIMEOUT, &rerr))
- {
- case RECV_TIMEOUT:
- return (xm_perror (XERR_LAST_ACK_TIMEOUT, &xmfuncs));
-
- case ACK:
- if (rerr != 0)
- continue;
-
- break;
-
- default:
- continue;
- }
-
- break;
- }
-
- return (xm_perror (XERR_OK, &xmfuncs));
- }
-
-
- /*------------------------------------------------
- This function transmits the file associated with
- the file pointer f according to the requirements
- of the XMODEM transfer protocol.
- ------------------------------------------------*/
- STATIC int xm_send_file (
- FILE *f, /* file pointer */
- register xblock *xb, /* pointer to block data */
- register xfunc *xmf) /* xmodem external functions */
- {
- int done;
-
- /*------------------------------------------------
- Repeat until we have sent the whole file.
- ------------------------------------------------*/
- for (done = 0, xb->block_num = 1; !done; xb->block_num++)
- {
- size_t bytes_read;
- int error;
-
-
-
- /*------------------------------------------------
- Read a block of data. If it was not a full
- block, then check for a file error. If it was a
- file error, cancel the transfer and return an
- error. Otherwise, pad the remainder of the
- block with CPM EOFs.
- ------------------------------------------------*/
- bytes_read = fread (xb->buffer, 1, xb->buflen, f);
-
- if (bytes_read != xb->buflen)
- {
- register int i;
-
- if (ferror (f))
- {
- xm_send_cancel (xmf->transmit);
- return (xm_perror (XERR_FILE_READ, xmf));
- }
-
- for (i = bytes_read; i < xb->buflen; i++)
- xb->buffer [i] = CPMEOF;
-
- done = -1;
- }
-
- /*------------------------------------------------
- Calculate the one's complement block number and
- send the block.
- ------------------------------------------------*/
- xb->not_block_num = (unsigned char) (255 - xb->block_num);
-
- if ((error = xm_send_block (xb, xmf)) != XERR_OK)
- return (error);
- }
-
- return (XERR_OK);
- }
-
-
- /*------------------------------------------------
- This function transmits the block described by the
- structure pointed to by the xb argument. The
- transmit and receive arguments point to functions
- that transmit and receive data to and from the
- modem. A value of 0 is returned to indicate that
- the block was successfully sent. A non-zero
- return value indicates an offline condition.
- ------------------------------------------------*/
- STATIC int xm_send_block (
- register xblock *xb, /* pointer to block data */
- register xfunc *xmf) /* xmodem external functions */
- {
- int error_count;
-
- error_count = 0;
-
- while (1)
- {
- int i;
- int can_count; /* number of CANS received */
- int block_resp; /* response to block */
- unsigned rerr; /* received char error */
-
-
- /*------------------------------------------------
- If there were too many errors, then exit with
- error.
- ------------------------------------------------*/
- if (error_count > BLOCK_RETRY_COUNT)
- {
- xm_send_cancel (xmf->transmit);
- return (xm_perror (XERR_ACK_TIMEOUT, xmf));
- }
-
-
- /*------------------------------------------------
- Check for user abort and process it if
- applicable.
- ------------------------------------------------*/
- if ((*xmf->check_abort) () != 0)
- {
- xm_send_cancel (xmf->transmit);
- return (xm_perror (XERR_USER_CANCEL, xmf));
- }
-
-
- /*------------------------------------------------
- Transmit the block start character (it's
- different depending on whether we're sending
- 1024-BYTE or 128-BYTE packets.
-
- Send the block number.
-
- Send the one's complement of the block number.
- ------------------------------------------------*/
- if ((*xmf->transmit) (xb->start_char) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
-
- if ((*xmf->transmit) (xb->block_num) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
-
- if ((*xmf->transmit) (xb->not_block_num) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
-
-
- /*------------------------------------------------
- Clear the CRC and checksum and send the data
- block while building the CRC or checksum.
- ------------------------------------------------*/
- xb->crc = 0;
- xb->checksum = 0;
-
- for (i = 0; i < xb->buflen; i++)
- {
- if ((*xmf->transmit) (xb->buffer [i]) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
-
- if (xb->crc_used != 0)
- xb->crc = xm_update_CRC (xb->crc, xb->buffer [i]);
- else
- xb->checksum += xb->buffer [i];
- }
-
-
- /*------------------------------------------------
- Send the CRC or checksum. If we send the CRC,
- we must send the High BYTE first.
- ------------------------------------------------*/
- if (xb->crc_used == 0)
- {
- if ((*xmf->transmit) (xb->checksum) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
- }
- else
- {
- if ((*xmf->transmit) ((unsigned char) (xb->crc >> 8)) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
-
- if ((*xmf->transmit) ((unsigned char) (xb->crc & 0xFF)) != XMIT_OK)
- return (xm_perror (XERR_OFFLINE, xmf));
- }
-
-
- /*------------------------------------------------
- Wait for the receiver to respond with an ACK or
- a NAK. If we timeout waiting, then return an
- error code. If we get a NAK, retransmit the
- block. If we gat an ACK, then return with
- status OK. If we receive 2 consecutive CANs,
- then we abort the transmission.
- ------------------------------------------------*/
- can_count = 0;
-
- GET_BLOCK_RESPONSE:
- block_resp = (*xmf->receive) (BLOCK_RESP_TIMEOUT, &rerr);
-
- if (rerr != 0)
- {
- xm_perror (XERR_CHAR_ERROR, xmf);
- goto GET_BLOCK_RESPONSE;
- }
-
- switch (block_resp)
- {
- case ACK:
- error_count = 0;
-
- xb->total_block_count++;
- xb->total_byte_count += xb->buflen;
-
- (*xmf->dispstat) (xb->total_block_count,
- xb->total_byte_count,
- NULL);
- break;
-
- case CAN:
- if (++can_count >= CAN_COUNT_ABORT)
- {
- xm_send_cancel (xmf->transmit);
- return (xm_perror (XERR_RCVR_CANCEL, xmf));
- }
- goto GET_BLOCK_RESPONSE;
-
-
- /*------------------------------------------------
- I have seen 2 ways of handling this problem. One
- is to return an error indicating that the sender
- never received a packet ACK. Another method
- retransmits the packet just sent in hopes that
- the receiver will get its ACK together. The
- second one is what I did.
- ------------------------------------------------*/
- case RECV_TIMEOUT:
- error_count++;
- #if 0
- return (xm_perror (XERR_ACK_TIMEOUT, xmf));
- #else
- xm_perror (XERR_ACK_TIMEOUT, xmf);
- continue;
- #endif
-
- case NAK:
- xm_perror (XERR_BLOCK_NAK, xmf);
- error_count++;
- continue;
-
- default:
- goto GET_BLOCK_RESPONSE;
- }
-
- break;
- }
-
- return (XERR_OK);
- }
-
- /*------------------------------------------------
- This function updates a CRC accumulator for xmodem
- CCITT CRC.
- ------------------------------------------------*/
- unsigned int xm_update_CRC (
- register unsigned int crc, /* current CRC */
- unsigned int c) /* character to add to CRC */
- {
- static unsigned int crc_polynomial = 0x1021;
- register int i;
-
- c <<= 8;
-
- for (i = 0; i < 8; i++)
- {
- if ((c ^ crc) & 0x8000)
- crc = (crc << 1) ^ crc_polynomial;
- else
- crc <<= 1;
-
- c <<= 1;
- }
-
- return (crc);
- }
-
-
- /*------------------------------------------------
- This function sends 8 CANs and 8 BSs as done by
- YAM. This is used to cancel an X or Y Modem send
- or receive.
- ------------------------------------------------*/
- void xm_send_cancel (
- int (*transmit) (char)) /* transmit function */
- {
- register int i;
-
- for (i = 0; i < 8; i++)
- (*transmit) (CAN);
-
- for (i = 0; i < 8; i++)
- (*transmit) (BACKSPACE);
- }
-
- /*------------------------------------------------
- ------------------------------------------------*/
-
-
-
-