crc.c 10.6 KB
/***********************************************************************CMOD**
 * NAME:      crc.c
 *
 * PURPOSE:   Provides functions for calculating Cyclic Redundancy Checks
 *            (CRCs).
 *
 *****************************************************************************/

#define CRC_DEFINE_VARS
#include "crc.h"

/***********************************************************************INFN**
 * NAME:      crc_reflect
 *
 * PURPOSE:   Reverses the bit order of a CRC value, if is_rev is CRC_TRUE
 *
 * PARAMS:    crc            Address of the value to reverse
 *            width          Width (in bits) of the value
 *            is_rev         Set to TRUE to reverse the bit order; FALSE is
 *                           no operation
 *
 * RETURNS:   None
 *
 * OPERATION: Loop through each bit, shifting new value left, moving LSB of
 *            old value to LSB of new value, shifting old value right.
 *
 *****************************************************************************/
void crc_reflect(CRC_VAL *crc, int width, int is_rev)
{
  CRC_VAL rev = 0;
  int ii;

  if (is_rev)
  {
    for (ii = 1; ii <= width; ii++)
    {
      rev <<= 1;
      rev |= ((*crc) & 1L);
      (*crc) >>= 1;
    }
    (*crc) = rev;
  }
}


/***********************************************************************EXFN**
 * NAME:      crc_mk_std_tbl
 *
 * PURPOSE:   Initialises a CRC_TBL structure to make it calculate a
 *            standard type of crc
 *
 * PARAMS:    tbl            Address of the structure to initialise
 *            std_type       The type of CRC, one of the following constants:
 *                           CRC_STD_CRC16, CRC_STD_CRC32, CRC_STD_JAM,
 *                           CRC_STD_XMODEM, CRC_STD_ZMODEM
 *
 * RETURNS:   TRUE           Structure successfully initialised
 *            FALSE          Error
 *
 * OPERATION: Calls crc_mk_tbl with hard coded values based on std_type
 *
 *****************************************************************************/
CRC_BOOL crc_mk_std_tbl(CRC_TBL *tbl, int std_type)
{
  CRC_BOOL rc;
  switch (std_type)
  {
    case CRC_STD_CRC16:
      rc = crc_mk_tbl(tbl, 16, 0x1021, 0xFFFF, 0x0000, CRC_FALSE);
      break;
    case CRC_DNP_CRC16:
      rc = crc_mk_tbl(tbl, 16, 0x13D65, 0x0000, 0xFFFF, CRC_TRUE);
      break;
    case CRC_MOD_CRC16:
      rc = crc_mk_tbl(tbl, 16, 0x8005, 0xFFFF, 0x0000, CRC_TRUE);
      break;
    case CRC_STD_XMODEM:
      rc = crc_mk_tbl(tbl, 16, 0x8408, 0x0000, 0x0000, CRC_TRUE);
      break;
    case CRC_STD_ZMODEM16:
      rc = crc_mk_tbl(tbl, 16, 0x1021, 0x0000, 0x0000, CRC_FALSE);
      break;
    case CRC_STD_CRC32:
      rc = crc_mk_tbl(tbl, 32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, CRC_TRUE);
      break;
    case CRC_STD_JAM:
      rc = crc_mk_tbl(tbl, 32, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, CRC_TRUE);
      break;
    default:
      rc = CRC_FALSE;
      break;
  }
  return rc;
}
#define CRC_STD_ZMODEM CRC_STD_CRC32


/***********************************************************************EXFN**
 * NAME:      crc_mk_tbl
 *
 * PURPOSE:   Initialises a CRC_TBL structure for CRC calculation
 *
 * PARAMS:    tbl            Address of structure to initialize
 *            width          Register width. Must be 8, 16, 24 or 32
 *            poly           Calculation polynomial
 *            init_val       Initial value of register
 *            xor_out        Value to XOR register width before outputting
 *            is_rev         Reversed bit order mode? TRUE/FALSE
 *
 * RETURNS:   TRUE           Structure successfully initialised
 *            FALSE          Error
 *
 * OPERATION: Stores all parameters in the structure, and the calculates
 *            a table entry for each input character.
 *
 *****************************************************************************/
CRC_BOOL crc_mk_tbl(CRC_TBL *tbl, int width, CRC_VAL poly, CRC_VAL init_val,
                                                   CRC_VAL xor_out, int is_rev)
{
  CRC_VAL pre_calc;
  int is_carry;
  int ii;
  int jj;
  int rc = CRC_TRUE;
  
// add freefrug
  CRC_VAL  mask = 0xffff;
  switch( width )
  {
  case 8  : mask = 0xff;       break;
  case 24 : mask = 0xffffff;   break;
  case 32 : mask = 0xffffffff; break;
  }
// ---

  /***************************************************************************
   * Store parameters
   ***************************************************************************/
  tbl->width = width;
  tbl->poly = poly;
  tbl->init_val = init_val;
  tbl->xor_out = xor_out;
  tbl->is_rev = is_rev;

  /***************************************************************************
   * Check that register width is valid
   ***************************************************************************/
  if ( ((width % CRC_BITS_BYTE) != 0) ||
       (width > (sizeof(CRC_VAL) * CRC_BITS_BYTE)) )
  {
    rc = CRC_FALSE;
    goto ERROR;
  }

  /***************************************************************************
   * Calculate a table entry for each possible input character. Each entry is
   * stats as with the input byte occupying the most significant byte of the
   * register. This is then repeatedly shifted left by one bit, until the
   * all the original bits have been moved out. After each shift, if the bit
   * just removed is 1 then the register is XORed with the polynomial.
   ***************************************************************************/
  for (ii = 0; ii <= CRC_UCHAR_MAX; ii++)
  {
    pre_calc = (CRC_VAL) ii;
    crc_reflect(&pre_calc, CRC_BITS_BYTE, is_rev);
    pre_calc <<= (width - CRC_BITS_BYTE);
    for (jj = 0; jj < CRC_BITS_BYTE; jj++)
    {
      is_carry = pre_calc & (1 << (width - 1));
      pre_calc <<= 1;
      if (is_carry)
      {
        pre_calc ^= poly;
      }
    }
    crc_reflect(&pre_calc, width, is_rev);
    tbl->pre_calc[ii] = pre_calc & mask;  // adjust freefrug
  }

ERROR:
  return rc;
}


/***********************************************************************EXFN**
 * NAME:      crc_init
 *
 * PURPOSE:   Initialise a CRC register, so data can be applied to it
 *
 * PARAMS:    crc            Address of the CRC register
 *            tbl            Address of the table to use for calculation
 *
 * RETURNS:   None
 *
 * OPERATION: Sets the CRC register to initial value and reflects it if
 *            necessary
 *
 *****************************************************************************/
void crc_init(CRC_VAL *crc, CRC_TBL *tbl)
{
  (*crc) = tbl->init_val;
  crc_reflect(crc, tbl->width, tbl->is_rev);
}


/***********************************************************************EXFN**
 * NAME:      crc_final
 *
 * PURPOSE:   Finalise calculation of CRC register, so it can be used
 *
 * PARAMS:    crc            Address of the CRC register
 *            tbl            Address of the table to use for calculation
 *
 * RETURNS:   Usable CRC
 *
 * OPERATION: XOR the CRC register with mask, and then strip off unused bits.
 *            Leave register unmodified; return modified value.
 *
 *****************************************************************************/
CRC_VAL crc_final(CRC_VAL *crc, CRC_TBL *tbl)
{
  return ((*crc) ^ tbl->xor_out); /* & ~(0xFFFFFFFF << tbl->width); */
}


/***********************************************************************EXFN**
 * NAME:      crc_add_chr
 *
 * PURPOSE:   Apply row of one or more characters to the CRC register
 *
 * PARAMS:    crc            Address of the CRC register
 *            tbl            Address of the table to use for calculation
 *            chr            Character to apply
 *            cnt            Number of time to apply it
 *
 * RETURNS:   None
 *
 * OPERATION: Shift the register along by a byte, then XOR with the
 *            appropriate value in the table
 *
 *****************************************************************************/
void crc_add_chr(CRC_VAL *crc, CRC_TBL *tbl, unsigned char chr, int cnt)
{
  if(tbl->is_rev)
  {
    for(; cnt > 0; cnt--)
    {
      (*crc) = ((*crc) >> CRC_BITS_BYTE) ^
                                 tbl->pre_calc[((*crc) & CRC_BYTE_MASK) ^ chr];
    }
  }
  else
  {
    for(; cnt > 0; cnt--)
    {
      (*crc) = ((*crc) << CRC_BITS_BYTE) ^ tbl->pre_calc[(((*crc) >>
                         (tbl->width - CRC_BITS_BYTE)) & CRC_BYTE_MASK) ^ chr];
    }
  }
}


/***********************************************************************EXFN**
 * NAME:      crc_add_str
 *
 * PURPOSE:   Apply a null-terminated string to the CRC register
 *
 * PARAMS:    crc            Address of the CRC register
 *            tbl            Address of the table to use for calculation
 *            str            Address of null-terminated string to apply
 *
 * RETURNS:   None
 *
 * OPERATION: Loop through the string until the null-terminator is found,
 *            applying the action for crc_add_chr on each character
 *
 *****************************************************************************/
void crc_add_str(CRC_VAL *crc, CRC_TBL *tbl, unsigned char *str)
{
  if(tbl->is_rev)
  {
    while(*str)
    {
      (*crc) = ((*crc) >> CRC_BITS_BYTE) ^
                              tbl->pre_calc[((*crc) & CRC_BYTE_MASK) ^ (*str)];
      str++;
    }
  }
  else
  {
    while(*str)
    {
      (*crc) = ((*crc) << CRC_BITS_BYTE) ^ tbl->pre_calc[(((*crc) >>
                      (tbl->width - CRC_BITS_BYTE)) & CRC_BYTE_MASK) ^ (*str)];
      str++;
    }
  }

}


/***********************************************************************EXFN**
 * NAME:      crc_add_data
 *
 * PURPOSE:   Apply data with specified length to the CRC register
 *
 * PARAMS:    crc            Address of the CRC register
 *            tbl            Address of the table to use for calculation
 *            data           Address of beginning of data
 *            len            Length of data
 *
 * RETURNS:   None
 *
 * OPERATION: Loop through the data buffer applying the action for
 *            crc_add_chr on each character
 *
 *****************************************************************************/
void crc_add_data(CRC_VAL *crc, CRC_TBL *tbl, unsigned char *data, int len)
{
  int ii;

  if(tbl->is_rev)
  {
    for(ii = 0; ii < len; ii++)
    {
      (*crc) = ((*crc) >> CRC_BITS_BYTE) ^
                            tbl->pre_calc[((*crc) & CRC_BYTE_MASK) ^ data[ii]];
    }
  }
  else
  {
    for(ii = 0; ii < len; ii++)
    {
      (*crc) = ((*crc) << CRC_BITS_BYTE) ^ tbl->pre_calc[(((*crc) >>
                    (tbl->width - CRC_BITS_BYTE)) & CRC_BYTE_MASK) ^ data[ii]];
    }
  }

}