Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

CRC


September 1990/XMODEM, XMODEM-1K, And XMODEM/CRC/Listing 2

Listing 2

/*-----------------------------------------------
XMODEMR.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.
Jon Ward  7  Jun 90  Added more comments and a
            little cleanup.
-----------------------------------------------*/

#define XMODEM_LIB 1

#include <stdio.h>
#include "xmodem.h"


#define STATIC static  /* undef for debugging */

/*-----------------------------------------------
-----------------------------------------------*/
struct send_n_wait_st
  {
  char_char_to send;
  int retry_count;
  long ms_timeout;
  unsigned char *valid_responses;
  int num_valid_responses;
  };

STATIC unsigned char soh_stx_can [] =
  { SOH, STX, CAN };
STATIC unsigned char soh_stx_can_eot [] =
  { SOH, STX, CAN, EOT };

STATIC struct send_n_wait_st crc_req =
  {
  'C',
  CRC_RETRY_COUNT,
  CRC_TIMEOUT,
  soh_stx_can,
  sizeof (soh_stx_can)
  };

STATIC struct send_n_wait_st checksum_req =
  {
  NAK,
  NAK_RETRY_COUNT,
  NAK_TIMEOUT,
  soh_stx_can,
  sizeof (soh_stx_can)
  };

STATIC struct send_n_wait_st pack_nak =
  {
  NAK,
  NAK_RETRY_COUNT,
  NAK_TIMEOUT,
  soh_stx_can_eot,
  sizeof (soh_stx_can_eot)
  };

STATIC struct send_n_wait_st pack_ack =
  {
  ACK,
  ACK_RETRY_COUNT,
  ACK_TIMEOUT,
  soh_stx_can_eot,
  sizeof (soh_stx_can_eot)
  };

/*-----------------------------------------------
Error messages for error enums.
-----------------------------------------------*/
STATIC char *xmodem_errors [] =
  {
  "Transmission Successful",
  "NULL Transmit function pointer",
  "NULL Receive function pointer",
  "Receiver cancelled the transfer",
  "Sender cancelled the transfer",
  "User cancelled the transfer",
  "Error reading the flle",
  "Error writing the file",
  "Timed out waiting for data pack ACK",
  "Timed out waiting for initial NAK",
  "Timed out waiting for SOH",
  "Timed out waiting for data",
  "Timed out waiting for final ACK",
  "Invalid char waiting for SOH",
  "Block mismatch in packet header",
  "CRC is incorrect",
  "Checksum is incorrect",
  "Block out of sequence",
  "Received character error",
  "Modem is not online",
  };

/*-----------------------------------------------
      Local Function Prototypes
-----------------------------------------------*/
STATIC int xm_send_n_wait (
  const struct send_n_wait_st *req,    /* request structure */
  unsigned char *response,     /* response from sender */
  xfunc *xmf);             /* xmodem external functions */

STATIC int xm_block_start (
  xblock *xb,          /* xmodem block data */
  unsigned char block_start,   /* block start char from sender */
  xfunc *xmf);         /* xmodem external functions */

STATIC int xm_recv_block (
  xblock *xb,          /* xmodem block data */
  register xfunc *xmf);        /* xmodem external functions */

/*-----------------------------------------------
This function receives a file transferred via
XMODEM, XMODEM-1K or XMODEM/CRC. The f argument
represents the file to receive that has been
opened for writing.
-----------------------------------------------*/
int xmodem_recv (
  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 */
{
register int error;        /* gen purpose error var */
unsigned char start_char;  /* first char of block */
unsigned char next_block;  /* next block we expect */
unsigned char last_block;  /* last successful block */
xblock xb;       /* xmodem block data */
xfunc xmfuncs;       /* xmodem external functions */

/*-----------------------------------------------
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));

/*-----------------------------------------------
Initialize data for the first block and purge
all data from the receive buffer. Init the
number of bytes and blocks received and display
some useful info.
-----------------------------------------------*/
next_block = last_block = 1;

xb.total_block_count = 0L;
xb.total_byte_count = 0L;

(*xmfuncs.dispstat) (0L, 0L, "");

PURGE_RECEIVER(receive);

/*-----------------------------------------------
Attempt to transfer using CRC-16 error detection.
This involves sending the CRC begin character:
'C'.
-----------------------------------------------*/
xb.crc_used = 1;
error = xm_send_n_wait (&crc_req,
         &start_char,
         &xmfuncs);

/*-----------------------------------------------
If the sender did not respond to the CRC-16
transfer request, then attempt to transfer using
checksum error detection.
-----------------------------------------------*/
if (error == XERR_SOH_TIMEOUT)
  {
  xb.crc_used = 0;
  error = xm_send_n_wait (&checksum_req,
           &start_char,
           &xmfuncs);
  }

/*------------------------------------------------
If begin transfer request failed, return error.
------------------------------------------------*/
if (error != XERR_OK)
  return (error);

/*------------------------------------------------
If the starting character of the next block is
an EOT, then we have completed transferring the
file and we exit this loop. Otherwise, we init
the xmodem packet structure based on the first
character of the packet.
------------------------------------------------*/
while (start_char != EOT)
  {
  register int good_block; /* NZ if packet was OK */
  
  error = xm_block_start (&xb,
           start_char,
           &xmfuncs);
  
  if (error != XERR_OK)
    return (error);
  
  good_block = -1;  /* assume packet will be OK */

/*------------------------------------------------
Receive the packet. If there was an error, then
NAK it. Otherwise, the packet was received OK.
------------------------------------------------*/
  if (xm_recv_block (&xb, &xmfuncs) != XERR_OK)
    {
    good_block = 0;        /* bad block */
    }

/*------------------------------------------------
If this is the next expected packet, then append
it to the file and update the last and next
packet vars.
------------------------------------------------*/
  else if (xb.block_num == next_block)
    {
    int bytes_written; /* bytes written for this block */
  last_block = next_block;
  next_block = (next_block + 1) % 256;
  
  bytes_written = fwrite (xb.buffer, 1, xb.buflen, f);
  
  xb.total_block_count++;
  xb.total_byte_count += bytes_written;
  
  (*xmfuncs.dispstat) (xb.total_block_count,
         xb.total_byte_count,
         NULL);
  
  if (bytes_written != xb.buflen)
    {
    xm_send_cancel (transmit);
    return (xm_error (XERR_FILE_WRITE, &xmfuncs));
    }
  }

/*------------------------------------------------
If this is the previous packet, then the sender
did not receive our ACK to that packet and
resent it. This is OK. Just ACK the packet.

If the block number for this packet is completely
out of sequence, cancel the transmission and
return an error.
------------------------------------------------*/
  else if (xb.block_num != last_block)
    {
    xm_send_cancel (transmit);
    return (xm_perror (XERR_BLOCK_SEQUENCE, &xmfuncs));
    }

/*------------------------------------------------
Here, good_block is non-zero if the block was
received and processed with no problems. If it
was a good block, then we send an ACK. A NAK is
sent for bad blocks.
------------------------------------------------*/
  if (good_block)
    {
    error = xm_send_n_wait (&pack_ack,
             &start_char,
             &xmfuncs);
    }
  else
    {
    PURGE_RECEIVER(receive);
    error = xm_send_n_wait (&pack_nak,
             &start_char,
             &xmfuncs);
    }
  
  if (error != XERR_OK)
    return (error);
  }

/*------------------------------------------------
The whole file has been received, so attempt to
send an ACK to the final EOT.
------------------------------------------------*/
if ((*transmit) (ACK) != XMIT_OK)
  return (xm_perror (XERR_OFFLINE, &xmfuncs));

return (xm_perror (XERR_OK, &xmfuncs));
}

/*------------------------------------------------
Dummy function used in case caller did not supply
a display function.
------------------------------------------------*/
void xm_no_disp_func (
  long a,
  long b,
  const char *buf)
{
a = a;
b = b;
buf = buf; /* avoid compiler warnings */
}

/*------------------------------------------------
Dummy function used in case caller did not supply
a used abort function.
------------------------------------------------*/
int xm_no_abort_func (void)
{
return (0);
}

/*------------------------------------------------
This function transmits a character and waits for
a response. The req argument points to a
structure containing info about the char to
transmit, retry count, timeout, etc. The
response argument points to a storage place for
the received character. The xmf argument points
to a structure of caller supplied functions.

Any errors encountered are returned.
------------------------------------------------*/
STATIC int xm_send_n_wait (
  const struct send_n_wait_st *req,    /*request structure */
  unsigned char *response,     /* response from sender */
  register xfunc *xmf)         /* xmodem external functions */
{
int j;
unsigned int rerr;

for (j = 0; j < req->retry_count; j++)
  {
  register int rcvd_char;
  register int i;

/*------------------------------------------------
Check to see if the user aborted the transfer.
------------------------------------------------*/
  if ((*xmf->check_abort) ( ) != 0)
    {
    xm_send_cancel (xmf->transmit);
    return (xm_perror (XERR_USER_CANCEL, xmf));
    }

/*------------------------------------------------
Transmit the block response (or block start)
character.
------------------------------------------------*/
  if ((*xmf->transmit) (req->char_to_send) != XMIT_OK)
    return (xm_perror (XERR_OFFLINE, xmf));

/*------------------------------------------------
Wait for a response. If there isn't one or if a
parity or similar error occurred, continue with
next iteration of the retry loop.
------------------------------------------------*/
  rcvd_char = (*xmf->receive) (req->ms_timeout, &rerr);
  if (rcvd_char == RECV_TIMEOUT)
    continue;
  
  if (rerr != 0)
    return (xm_perror (XERR_ CHAR_ERROR, xmf));

/*------------------------------------------------
Initialize the response and check to see if it is
valid.
------------------------------------------------*/
  if (response != NULL)
    *response = (unsigned char) rcvd_char;
  
  for (i = 0; i < req->num_valid_responses; i++)
    if (rcvd_char == req->valid_responses [i])
      return (XERR_OK);
  }

return (xm_perror (XERR_SOH_TIMEOUT, xmf));
}

/*------------------------------------------------
This function analyzes valid block start
characters to determine block size or in the case
of CAN whether to abort the transmission.

Any errors encountered are returned.
------------------------------------------------*/
STATIC int xm_block_start (
  register xblock *xb,    /* xmodem block data */
  unsigned char block_start,  /* block start char from sender */
  register xfunc *xmf)    /* xmodem external functions */
{
switch (block_start)
  {
  case SOH:               /* NORMAL 128-byte block */
    xb->buflen = XMODEM_BLOCK_SIZE;
    return (XERR_OK);
  
  case STX:               /* 1024-byte block */
    xb->buflen = XMODEM_1K_BLOCK_SIZE;
    return (XERR_OK);
  
  case CAN:               /* Abort signal */
    if ((*xmf->receive) (CAN_TIMEOUT, NULL) == CAN)
      {
      xm_send_cancel (xmf->transmit);
      return (xm_perror (XERR_SEND_CANCEL, xmf));
      }
    break;
  }

return (xm_perror (XERR_INVALID_SOH, xmf));
}

/*------------------------------------------------

This function receives the block numbers, block
data, and block checksum or CRC. The received
data is stored in the structure pointed to by the
xb argument. The block numbers are compared, and
the checksum or CRC is verified.

Any errors encountered are returned.
------------------------------------------------*/
STATIC int xm_recv_block (
  xblock *xb,         /* xmodem block data */
  register xfunc *xmf)    /* xmodem external functions */
{
register int i;
unsigned int rerr;    /* receive error */

/*------------------------------------------------
Attempt to receive the block number. If the
receiver timesout or if there is a receive error,
ignore the rest of the packet.
------------------------------------------------*/
if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  return (xm_perror (XERR_DATA_TIMEOUT, xmf));

if (rerr != 0)
  return (xm_perror (XERR_CHAR_ERROR, xmf));

xb->block_num = (unsigned char) i;



/*------------------------------------------------
Attempt to receive the one's complement of the
block number.
------------------------------------------------*/
if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  return (xm_perror (XERR_DATA_TIMEOUT, xmf));

if (rerr != 0)
  return (xm_perror (XERR_CHAR_ERROR, xmf));

xb->not_block_num = (unsigned char) i;

*/------------------------------------------------
Make sure that the block number and one's
complemented block number agree.
------------------------------------------------*/
if ((255 - xb->block_num) != xb->not_block_num)
  return (xm_perror (XERR_INVALID_BLOCK_NUM, xmf));

/*------------------------------------------------
Clear the CRC and checksum accumulators and
receive the data block.
------------------------------------------------*/
xb->crc = 0;
xb->checksum = 0;

for (i = 0; i < xb->buflen; i++)
  {
  int rcvd_char;
  
  if ((rcvd_char = (*xmf->receive) (DATA_TIMEOUT, &rerr)) ==
      RECV_TIMEOUT)
    return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  
  if (rerr != 0)
    return (xm_perror (XERR_CHAR_ERROR, xmf));
  
  xb->buffer [i] = (unsigned char) rcvd_char;
  
  if (xb->crc_used != 0)
    xb->crc = xm_update_CRC (xb->crc, xb->buffer [i]);
  else
    xb->checksum += xb->buffer [i];


}

/*------------------------------------------------
Validate the CRC.
------------------------------------------------*/
if (xb->crc_used)
  {
  if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
    return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  
  if (rerr != 0)
    return (xm_perror (XERR_CHAR_ERROR, xmf));
  
  if ((unsigned char) i != (unsigned char ) (xb->crc >> 8))
    return (xm_perror (XERR_INVALID_CRC, xmf));
  
  if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
    return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  
  if (rerr != 0)
    return (xm_perror (XERR_CHAR_ERROR, xmf));
  
  if ((unsigned char) i != (unsigned char) (xb->crc & 0xFF))
    return (xm_perror (XERR_INVALID_CRC, xmf));
  }


/*------------------------------------------------
Validate the checksum.
------------------------------------------------*/
else
  {
  if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
    return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  
  if (rerr != 0)
    return (xm_perror (XERR_CHAR_ERROR, xmf));
  
  if ((unsigned char) i != xb->checksum)
    return (xm_perror (XERR_INVALID_CHECKSUM, xmf));
  }

return (XERR_OK);
}

/*------------------------------------------------
This function prints an XMODEM status message
using the caller supplied display function. The
error argument is returned.
------------------------------------------------*/
int xm_perror (
  int error,      /* error number */
  xfunc *xmf)     /* xmodem external functions */
{
xmf->dispstat (-1L, -1L, xmodem_errors [error]);
return (error);
}

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.