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