/*
 * Copyright (c) 2004-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagcrc.c 15597 2012-03-28 22:21:32Z jomi.gregory $
 */

/* File header. */
#include "dagcrc.h"

/* C Standard Library headers. */
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>

void generate_crc8_crc10_table(void)
/* generate a table of CRC-8 syndromes for all possible input bytes, CRC-10 is not used */
{
	dagcrc_gen_table(DAGCRC_ATM_HEC);
}

uint8_t 
dagcrc_atm_hec8( uint8_t crc_accum, const char *pdata, int len )
{
	return (uint8_t)dagcrc_do_crc(crc_accum, pdata, len, DAGCRC_ATM_HEC, DAGCRC_MODE_FULL);
}

void
dagcrc_make_ethernet_table_r(void)
{
	dagcrc_gen_table(DAGCRC_ETH);	
}

uint32_t
dagcrc_ethernet_crc32_r(uint32_t crc, const uint8_t* buf, int len)
{
	return dagcrc_do_crc(crc, buf, len, DAGCRC_ETH, DAGCRC_MODE_FULL);
}


void
dagcrc_make_ppp_fcs16_table(void)
{
	dagcrc_gen_table(DAGCRC_PPP16);
}


/* Users calling this fn on a full packet must pass fcs = PPPINITFCS16 on start */
uint16_t
dagcrc_ppp_fcs16(uint16_t fcs, const uint8_t* cp, int len)
{
	return (uint16_t)dagcrc_do_crc(fcs, cp, len, DAGCRC_PPP16, DAGCRC_MODE_CONT);
}

/* Note no legacy dagcrc_make_ppp_fcs32_table() function*/


/* Users calling this fn on a full packet must pass fcs = PPPINITFCS32 on start */
uint32_t
dagcrc_ppp_fcs32(uint32_t fcs, const uint8_t* cp, int len)
{
	return dagcrc_do_crc(fcs, cp, len, DAGCRC_PPP32, DAGCRC_MODE_CONT);
}


void
dagcrc_make_aal5_table(void)
{
	dagcrc_gen_table(DAGCRC_AAL5);
}


/* Users calling this fn on a full packet must pass crc_accum = CRC_INIT on start */
uint32_t
dagcrc_aal5_crc(uint32_t crc_accum, const char *data_blk_ptr, int data_blk_size)
{
	return dagcrc_do_crc(crc_accum, data_blk_ptr, data_blk_size, DAGCRC_AAL5, DAGCRC_MODE_CONT);
}


void
dagcrc_make_infiniband_vcrc_table_r(void)
{
	dagcrc_gen_table(DAGCRC_IB_VAR);
}


uint16_t
dagcrc_infiniband_vcrc16_r(uint32_t crc, const uint8_t* buf, int len)
{
	return (uint16_t)dagcrc_do_crc(crc, buf, len, DAGCRC_IB_VAR, DAGCRC_MODE_FULL);	
}


/* Function:     dagcrc_infiniband_icrc32
 * Description:  calculate ICRC(32 bits ) for the infiniband packet
 * Inputs:       	crc:- Initial value
*			buf1 - pointer to masked buffer (header)
*			len1 - length of the buf1
*			buf2 - pointer rest of the packet
*			len2 - length of the buf2
* Outputs:      calculated crc32 of the given packet 
 */
uint32_t
dagcrc_infiniband_icrc32(uint32_t crc, const uint8_t* buf1, int len1, const uint8_t* buf2, int len2)
{
	crc =  dagcrc_do_crc(crc, buf1, len1, DAGCRC_IB_INV, DAGCRC_MODE_START);	
	return dagcrc_do_crc(crc, buf2, len2, DAGCRC_IB_INV, DAGCRC_MODE_END);	
}

typedef struct dagcrc_def {
	uint32_t poly;
	int bits;
	int reflected;
	uint32_t xor_start;
	uint32_t xor_end;
	uint32_t *table;
} dagcrc_def_t;

static dagcrc_def_t dagcrc_defs[] = {
	/* DAGCRC_ETH Ethernet and DAGCRC_IB_INV InfiniBand Invariant-CRC 32-bit Reflected */
	{
		0xedb88320, /* 0x04C11DB7 */
		32,
		1,
		0xffffffff,
		0xffffffff,
		NULL
	},
	/* DAGCRC_AAL5 ATM AAL5 32-bit */
	{
		0x04c11db7,
		32,
		0,
		0xffffffff,
		0,
		NULL
	},
	/* DAGCRC_PPP32 PPP 32-bit Reflected */
	{
		0xedb88320, /* 0x04C11DB7 */
		32,
		1,
		0xffffffff,
		0,
		NULL
	},
	/* DAGCRC_CRC32C Castagnoli iSCSI 32-bit Reflected */
	{
		0x82F63B78, /* 0x1EDC6F41 */
		32,
		1,
		0xffffffff,
		0xffffffff,
		NULL
	},
	/* DAGCRC_PPP16 PPP (CRC-16-CCITT) 16-bit Reflected */
	{
		0x8408, /* 0x1021 */
		16,
		1,
		0xffff,
		0,
		NULL
	},
	/* DAGCRC_IB_VAR InfiniBand Variant-CRC 16-bit Reflected */
	{
		0xd008, /* 0x100b */
		16,
		1,
		0xffff,
		0xffff,
		NULL
	},
	/* DAGCRC_ATM_HEC ATM HEC (CRC-8-CCITT) 8-bit */
	{
		0x07,
		8,
		0,
		0,
		0x55,
		NULL
	},
};

int
dagcrc_gen_table(dagcrc_crcs_t crc_type)
{
	dagcrc_def_t *def=&dagcrc_defs[crc_type];
	uint32_t crc;
	int i, j;

	if (crc_type > DAGCRC_CRC_MAX)
		return -1;

	/* Check if table is already constructed */
	if (def->table)
		return 0;
	
	/* Not constructed, allocate */
	if ( (def->table = calloc(256, sizeof(uint32_t))) == NULL) {
		return -1;
	}

	for (i = 0; i < 256; i++) {
		if (def->reflected) {
			crc = i;
			for (j = 8; j > 0; j--)	{
				if (crc & 1) {
					crc = (crc >> 1) ^ def->poly;
				} else {
					crc >>= 1;
				}
			}
		} else {
			crc = i << (def->bits - 8);
			for (j = 8; j > 0; j--)	{
				if (crc & (1 << (def->bits - 1))) {
					crc = (crc << 1) ^ def->poly;
				} else {
					crc <<= 1;
				}
			}
			crc &= (0xffffffff >> (32 - def->bits));
		}
		(def->table)[i] = crc;
	}

	return 0;
}

uint32_t
dagcrc_do_crc(uint32_t crc, const void *buf, unsigned int length, dagcrc_crcs_t crc_type, dagcrc_mode_t mode)
{
	int retval = 0;
	unsigned int term;
	dagcrc_def_t *def=&dagcrc_defs[crc_type];
	uint8_t *cbuf = (uint8_t *)buf;

	if ( (!cbuf) || (crc_type > DAGCRC_CRC_MAX))
		return 0;

	if (!(def->table)) {
		if ( (retval = dagcrc_gen_table(crc_type)) < 0)
			return retval;
	}

	if (mode & DAGCRC_MODE_START)
		crc ^= def->xor_start;

	while (length--) {
		if (def->reflected) {
			term = (crc ^ (*cbuf++)) & 0xff;
			crc = (def->table)[term] ^ (crc >> 8);
		} else {
			term = ((crc >> (def->bits - 8)) ^ (*cbuf++)) & 0xff;
			crc = (def->table)[term] ^ (crc << 8);
		}
		crc &= (0xffffffff >> (32 - def->bits));			
	}

	if (mode & DAGCRC_MODE_END)
		return (crc ^ def->xor_end);

	return crc;
}
