/***********************************************************************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]];
}
}
}