/* $Id: tpam_crcpc.c,v 1.1.2.2 2001/09/23 22:25:03 kai Exp $
 *
 * Turbo PAM ISDN driver for Linux. (Kernel Driver - CRC encoding)
 *
 * Copyright 1998-2000 AUVERTECH Télécom
 * Copyright 2001 Stelian Pop <stelian.pop@fr.alcove.com>, Alcôve
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 * For all support questions please contact: <support@auvertech.fr>
 *
 */

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Module Name:

    crcpc.c

Abstract:

    Modem HDLC coding
    Software HDLC coding / decoding

Revision History:

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

#include <linux/crc16.h>
#include "tpam.h"

#define  HDLC_CTRL_CHAR_CMPL_MASK	0x20	/* HDLC control character complement mask */
#define  HDLC_FLAG			0x7E	/* HDLC flag */
#define  HDLC_CTRL_ESC			0x7D	/* HDLC control escapr character */
#define  HDLC_LIKE_FCS_INIT_VAL		0xFFFF	/* FCS initial value (0xFFFF for new equipment or 0) */
#define  HDLC_FCS_OK			0xF0B8	/* This value is the only valid value of FCS */

#define TRUE	1
#define FALSE	0

static u8 ap_t_ctrl_char_complemented[256]; /* list of characters to complement */

static void ap_hdlc_like_ctrl_char_list (u32 ctrl_char) {
	int i;

	for (i = 0; i < 256; ++i)
		ap_t_ctrl_char_complemented[i] = FALSE;
	for (i = 0; i < 32; ++i)
		if ((ctrl_char >> i) & 0x0001)
			ap_t_ctrl_char_complemented [i] = TRUE;
	ap_t_ctrl_char_complemented[HDLC_FLAG] = TRUE;
	ap_t_ctrl_char_complemented[HDLC_CTRL_ESC] = TRUE;
		
}

void init_CRC() {
	ap_hdlc_like_ctrl_char_list(0xffffffff);
}

void hdlc_encode_modem(u8 *buffer_in, u32 lng_in,
		       u8 *buffer_out, u32 *lng_out) {
	u16 fcs;
	register u8 data;
	register u8 *p_data_out = buffer_out;

	fcs = HDLC_LIKE_FCS_INIT_VAL;

	/*
	 *   Insert HDLC flag at the beginning of the frame
	 */
	*p_data_out++ = HDLC_FLAG;

#define ESCAPE_CHAR(data_out, data) \
	if (ap_t_ctrl_char_complemented[data]) { \
		*data_out++ = HDLC_CTRL_ESC; \
		*data_out++ = data ^ 0x20; \
	} \
	else \
		*data_out++ = data;

	while (lng_in--) {
		data = *buffer_in++;

		/*
		 *   FCS calculation
		 */
		fcs = crc16_byte(fcs, data);

		ESCAPE_CHAR(p_data_out, data);
	}

	/*
	 *  Add FCS and closing flag
	 */
	fcs ^= 0xFFFF;  // Complement

	data = (u8)(fcs & 0xff);	/* LSB */
	ESCAPE_CHAR(p_data_out, data);

	data = (u8)((fcs >> 8));	/* MSB */
	ESCAPE_CHAR(p_data_out, data);
#undef ESCAPE_CHAR

	*p_data_out++ = HDLC_FLAG;

	*lng_out = (u32)(p_data_out - buffer_out);
}

void hdlc_no_accm_encode(u8 *buffer_in, u32 lng_in, 
			 u8 *buffer_out, u32 *lng_out) {
	u16 fcs;
	register u8 data;
	register u8 *p_data_out = buffer_out;

	/*
	 *   Insert HDLC flag at the beginning of the frame
	 */
	fcs = HDLC_LIKE_FCS_INIT_VAL;

	while (lng_in--) {
		data = *buffer_in++;
		/* calculate FCS */
		fcs = crc16_byte(fcs, data);
		*p_data_out++ = data;
	}

	/*
	 *  Add FCS and closing flag
	 */
	fcs ^= 0xFFFF;  // Complement
	data = (u8)(fcs);    
	*p_data_out++ = data;

	data =(u8)((fcs >> 8));   // revense MSB / LSB
	*p_data_out++ = data;
 
	*lng_out = (u32)(p_data_out - buffer_out);
}

u32 hdlc_no_accm_decode(u8 *buffer_in, u32 lng_in) {
	u16 fcs;
	u32 lng = lng_in;
	register u8 data;

	/*
	 *   Insert HDLC flag at the beginning of the frame
	 */
	fcs = HDLC_LIKE_FCS_INIT_VAL;

	while (lng_in--) {
		data = *buffer_in++;
		/* calculate FCS */
		fcs = crc16_byte(fcs, data);
	}

	if (fcs == HDLC_FCS_OK) 
		return (lng-2);
	else 
		return 0;
}

