/*****************************************************************************
* af_wanpipe_datascope.c
* 		
* 		WANPIPE(tm) Datascope Socket Layer.
*
* Author:	Nenad Corbic	<ncorbic@sangoma.com>
*
* Copyright:	(c) 2003 Sangoma Technologies Inc.
*
*		This program is free software; you can redistribute it and/or
*		modify it under the terms of the GNU General Public License
*		as published by the Free Software Foundation; either version
*		2 of the License, or (at your option) any later version.
* ============================================================================
*/

#include <linux/wanpipe_includes.h>
#include <linux/wanpipe_defines.h>
#include <linux/wanpipe.h>
#include <linux/if_wanpipe.h>
#include <linux/if_wanpipe_common.h>
#include <linux/if_wanpipe_kernel.h>

/*===================================================================
 * 
 * DEFINES
 * 
 *==================================================================*/

#define INC_CRC_CNT(a)   if (++a >= MAX_SOCK_CRC_QUEUE) a=0;
#define GET_FIN_CRC_CNT(a)  { if (--a < 0) a=MAX_SOCK_CRC_QUEUE-1; \
		              if (--a < 0) a=MAX_SOCK_CRC_QUEUE-1; }

#define FLIP_CRC(a,b)  { b=0; \
			 b |= (a&0x000F)<<12 ; \
			 b |= (a&0x00F0) << 4; \
			 b |= (a&0x0F00) >> 4; \
			 b |= (a&0xF000) >> 12; }

#define DECODE_CRC(a) { a=( (((~a)&0x000F)<<4) | \
		            (((~a)&0x00F0)>>4) | \
			    (((~a)&0x0F00)<<4) | \
			    (((~a)&0xF000)>>4) ); }
#define BITSINBYTE 8

#define NO_FLAG 	0
#define OPEN_FLAG 	1
#define CLOSING_FLAG 	2


/*===================================================================
 * 
 * GLOBAL VARIABLES
 * 
 *==================================================================*/

static const int MagicNums[8] = { 0x1189, 0x2312, 0x4624, 0x8C48, 0x1081, 0x2102, 0x4204, 0x8408 };
static unsigned short CRC_TABLE[256];
static unsigned long init_crc_g=0;
static const char FLAG[]={ 0x7E, 0xFC, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3F };


/*===================================================================
 * 
 * FUNCTION PROTOTYPES
 * 
 *==================================================================*/

#ifdef LINUX_2_6
HLIST_HEAD(wanpipe_parent_sklist);
#else
struct sock * wanpipe_parent_sklist = NULL;
#endif
static rwlock_t wanpipe_parent_sklist_lock = RW_LOCK_UNLOCKED;

static int wanpipe_check_prot_options(struct sock *sk);
void wanpipe_unbind_sk_from_parent(struct sock *sk);
static int wanpipe_attach_sk_to_parent (struct sock *parent_sk, struct sock *sk);
static int wanpipe_unattach_sk_from_parent (struct sock *parent_sk, struct sock *sk);
static wanpipe_hdlc_engine_t *
	wanpipe_get_hdlc_engine (struct sock *parent_sk,unsigned long active_ch);
static void free_hdlc_engine(wanpipe_hdlc_engine_t *hdlc_eng);
	
static int bind_sk_to_parent_prot(void **,unsigned char*,struct sock *sk);
static int unbind_sk_from_parent_prot(void **, unsigned char *,struct sock *sk);
static int bind_hdlc_into_timeslots(struct sock *parent_sk,
		                     wanpipe_hdlc_engine_t *hdlc_eng);
static void unbind_hdlc_from_timeslots(struct sock *parent_sk, wanpipe_hdlc_engine_t *hdlc_eng);

static int decode_byte (wanpipe_hdlc_engine_t *hdlc_eng, 
		        wanpipe_hdlc_decoder_t *chan,
			unsigned char *byte,
			unsigned char txdir);
static void init_hdlc_decoder(wanpipe_hdlc_decoder_t *chan);

static void init_crc(void);
static void calc_rx_crc(wanpipe_hdlc_decoder_t *chan);
static void tx_up_decode_pkt(wanpipe_hdlc_engine_t *,wanpipe_hdlc_decoder_t *,unsigned char);
static int wanpipe_hdlc_decode (wanpipe_hdlc_engine_t *hdlc_eng, unsigned char, struct sk_buff *skb);

static int wanpipe_sk_ss7_monitor_rx(void**map, struct sk_buff **skb_ptr);
static int wanpipe_skb_timeslot_parse(struct sock *parent_sk, struct sk_buff *skb, unsigned char dir);
static int wanpipe_handle_hdlc_data(struct sock *parent_sk);
static int wanpipe_rx_prot_data(void **map, unsigned short protocol, struct sk_buff *skb);
//static void print_hdlc_list(wanpipe_hdlc_engine_t **list);
static void wanpipe_free_parent_sock(struct sock *sk);

static void wanpipe_sk_parent_rx_bh (unsigned long data);

extern struct sock *wanpipe_make_new(struct sock *);
extern atomic_t wanpipe_socks_nr;




/*============================================================
 *  wanpipe_bind_sk_to_parent
 *
 *===========================================================*/

int wanpipe_bind_sk_to_parent(struct sock *sk, netdevice_t *dev, struct wan_sockaddr_ll *sll)
{
	int err=0;
	struct sock *parent_sk;
	unsigned long flags;
	struct ifreq ifr;
	void *buf;

	memset(&ifr,0,sizeof(struct ifreq));

	if (sll->sll_protocol){
		SK_PRIV(sk)->num = sll->sll_protocol;
	}

	buf = wan_malloc(sizeof(wanpipe_datascope_t));
	if (!buf){
		DEBUG_EVENT("%s: Wanpipe Socket failed to allocate datascope struct!\n",
				dev->name);
		return -ENOMEM;
	}

	DATA_SC_INIT(sk,buf);

	memset(DATA_SC(sk),0,sizeof(wanpipe_datascope_t));

	DATA_SC(sk)->prot_type = sll->sll_prot;
	DATA_SC(sk)->active_ch = sll->sll_active_ch;

	/* Find the parent socket for this interface */
	parent_sk=NULL;
	write_lock_irqsave(&wanpipe_parent_sklist_lock,flags);
#ifdef LINUX_2_6
	{
	struct hlist_node *node;
	sk_for_each(parent_sk, node, &wanpipe_parent_sklist) {
		if (SK_PRIV((parent_sk))->dev == dev) {
			break;
		}
	}
	}
#else
	{
	struct sock **skp;	
	for (skp = &wanpipe_parent_sklist; *skp; skp = &(*skp)->next) {
		if (SK_PRIV((*skp))->dev == dev) {
			parent_sk=*skp;
			break;
		}
	}
	}
#endif
	write_unlock_irqrestore(&wanpipe_parent_sklist_lock,flags);

	DEBUG_EVENT("%s: Dev name %s parensk=%p\n",__FUNCTION__,dev->name, parent_sk);
	
	if (parent_sk == NULL){
	
		init_crc();

		/* Device is not being used
		 *
		 * Create a parent socket that will be used
		 * to interface to network device */
		
		parent_sk=wanpipe_make_new(sk);	
		if (!parent_sk){
			DEBUG_EVENT("wansock: Failed to allocate new ss7 monitor sock!\n");
			return -ENOMEM;
		}	
		atomic_inc(&wanpipe_socks_nr);

		buf = wan_malloc(sizeof(wanpipe_datascope_t));
		if (!buf){
			DEBUG_EVENT("%s: Wanpipe Socket failed to allocate datascope struct!\n",
					dev->name);
			wanpipe_free_parent_sock(parent_sk);
			return -ENOMEM;
		}

		DATA_SC_INIT(parent_sk,buf);
		memset(DATA_SC(parent_sk),0,sizeof(wanpipe_datascope_t));

		buf=wan_malloc(sizeof(wanpipe_parent_t));
		if (!buf){
			DEBUG_EVENT("%s: Wanpipe Socket failed to allocate parent struct!\n",
					dev->name);
			wan_free(DATA_SC(parent_sk));
			wanpipe_free_parent_sock(parent_sk);
			return -ENOMEM;
		}

		PPRIV_INIT(parent_sk,buf);
		memset(PPRIV(parent_sk),0,sizeof(wanpipe_parent_t));

		PPRIV(parent_sk)->lock = RW_LOCK_UNLOCKED; 

		SK_PRIV(parent_sk)->dev=dev;

		tasklet_init((&PPRIV(parent_sk)->rx_task), 
			      wanpipe_sk_parent_rx_bh, (unsigned long)parent_sk);
		skb_queue_head_init(&PPRIV(parent_sk)->rx_queue);
		
		wansk_reset_zapped(parent_sk);
		DATA_SC(parent_sk)->parent=1;

		if (DATA_SC(sk)->active_ch){
			PPRIV(parent_sk)->hdlc_enabled=1;
		}
		
		/* Bind the parent socket to network device */
		
		ifr.ifr_data = (void*)parent_sk;
		err=-EINVAL;
		if (dev->do_ioctl)
			err=dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_BIND_SK);

		if (err != 0){
			DEBUG_EVENT("%s: Error: Dev busy with another protocol!\n",
					dev->name);
			wanpipe_free_parent_sock(parent_sk);
			return err;
		}	

		
		DEBUG_EVENT("%s: Parent sock registered to dev %s\n",
					dev->name,dev->name);

		PPRIV(parent_sk)->time_slots = 24;
		PPRIV(parent_sk)->media = WAN_MEDIA_T1;
		PPRIV(parent_sk)->seven_bit_hdlc = sll->sll_seven_bit_hdlc;

		DEBUG_EVENT("%s: Parent hdlc engine %s  Bits %d\n",
					dev->name,
					PPRIV(parent_sk)->hdlc_enabled?
						"Enabled":"Disabled",
					PPRIV(parent_sk)->seven_bit_hdlc?7:8);

		PPRIV(parent_sk)->time_slots = 
		                      dev->do_ioctl(dev,NULL,SIOC_WANPIPE_GET_TIME_SLOTS);
		if (PPRIV(parent_sk)->time_slots < 0){
			DEBUG_EVENT("%s: Error, failed to obtain time slots from driver!\n",
					dev->name);
			wanpipe_free_parent_sock(parent_sk);
			return -EINVAL;
		}
		
		PPRIV(parent_sk)->media      =
		                      dev->do_ioctl(dev,NULL,SIOC_WANPIPE_GET_MEDIA_TYPE);
		if (PPRIV(parent_sk)->media < 0){
			DEBUG_EVENT("%s: Error, failed to obtain media type from driver!\n",
					dev->name);
			wanpipe_free_parent_sock(parent_sk);
			return -EINVAL;
		}

		if (PPRIV(parent_sk)->media==WAN_MEDIA_NONE){
			PPRIV(parent_sk)->time_slots=0;
			PPRIV(parent_sk)->hdlc_enabled=1;
			DEBUG_EVENT("%s: Parent connected to Serial Device!\n",
					dev->name);
		}else{
			DEBUG_EVENT("%s: Parent connected to %s Timeslots=%i!\n",
					dev->name,
					PPRIV(parent_sk)->media==WAN_MEDIA_T1?"T1":"E1",
					PPRIV(parent_sk)->time_slots);
		}

		DEBUG_EVENT("%s: Parent hdlc engine %s  Bits %d\n",
					dev->name,
					PPRIV(parent_sk)->hdlc_enabled?
						"Enabled":"Disabled",
					PPRIV(parent_sk)->seven_bit_hdlc?7:8);
		
		/* Insert parent socket into global parent socket list */
		write_lock_irqsave(&wanpipe_parent_sklist_lock,flags);
#ifdef LINUX_2_6
		sk_add_node(parent_sk,&wanpipe_parent_sklist);
#else
		parent_sk->next = wanpipe_parent_sklist;
		wanpipe_parent_sklist = parent_sk;
		wp_sock_hold(parent_sk);
#endif
		write_unlock_irqrestore(&wanpipe_parent_sklist_lock,flags);

		err=dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_DEV_STATE);
		if (err == WANSOCK_CONNECTED){
			parent_sk->sk_state = WANSOCK_CONNECTED;
		}else{
			parent_sk->sk_state = WANSOCK_DISCONNECTED;
		}
		
		parent_sk->sk_bound_dev_if = dev->ifindex; 
		SK_PRIV(parent_sk)->dev = dev;
		wansk_set_zapped(parent_sk);

	}else{
		if (SK_PRIV(parent_sk)->num != SK_PRIV(sk)->num){
			DEBUG_EVENT("%s: Error parent prot 0x%X doesn't match sk prot 0x%X!\n",
					dev->name,SK_PRIV(parent_sk)->num,SK_PRIV(sk)->num);
			return -EFAULT;
		}
	}

	/* Initialize the sk parameters */
	DATA_SC(sk)->parent_sk=parent_sk;
	wp_sock_hold(parent_sk);

	if (PPRIV(parent_sk)->hdlc_enabled){
		
		if (PPRIV(parent_sk)->media != WAN_MEDIA_NONE && !DATA_SC(sk)->active_ch){
			DEBUG_EVENT("%s: Error invalid sk active ch selection: 0x%X!\n",
						dev->name,DATA_SC(sk)->active_ch);
			DEBUG_EVENT("%s: Parent hdlc engine enabled!\n",
						dev->name);
			wanpipe_unbind_sk_from_parent(sk);
			return -EINVAL;
		}
		if (PPRIV(parent_sk)->media == WAN_MEDIA_NONE){
			DATA_SC(sk)->active_ch=0;
		}
	}

	if (!PPRIV(parent_sk)->hdlc_enabled && DATA_SC(sk)->active_ch){
		DEBUG_EVENT("%s: Error invalid sk active ch selection: 0x%X!\n",
					dev->name,DATA_SC(sk)->active_ch);
		DEBUG_EVENT("%s: Parent hdlc engine disabled!\n",
					dev->name);
		wanpipe_unbind_sk_from_parent(sk);
		return -EINVAL;
	}


	DATA_SC(sk)->delta= sll->sll_prot_opt & DELTA_PROT_OPT;
	if (DATA_SC(sk)->delta){
		DEBUG_EVENT("%s: Datascope enabling DELTA parsing\n",dev->name);
	}
	
	if (sll->sll_prot_opt & MULTI_PROT_OPT){
		DATA_SC(sk)->max_mult_cnt=sll->sll_mult_cnt;
		DEBUG_EVENT("%s: Datascope enabling MULTI parsing: Max mult cnt = %i\n",
				dev->name,DATA_SC(sk)->max_mult_cnt);
	}

	if (wanpipe_check_prot_options(sk) != 0){
		DEBUG_EVENT("%s: Error invalid sk protocol specified: 0x%lX!\n",
					dev->name,DATA_SC(sk)->prot_type);
		wanpipe_unbind_sk_from_parent(sk);
		return -EINVAL;
	}
	

	err=wanpipe_attach_sk_to_parent(parent_sk,sk);
	if (err!=0){
		wanpipe_unbind_sk_from_parent(sk);
		return err;
	}

	SK_PRIV(sk)->dev=dev;
	sk->sk_bound_dev_if = dev->ifindex;
	sk->sk_state = parent_sk->sk_state;
	wansk_set_zapped(sk);

	return 0;
}

/*============================================================
 *  wanpipe_unbind_sk_from_parent
 *
 *===========================================================*/

void wanpipe_unbind_sk_from_parent(struct sock *sk)
{
	
	struct sock *parent_sk;
	unsigned long flags;
	int sk_child_exists=0;

	if (!DATA_SC(sk)){
		return;
	}
	
	parent_sk=DATA_SC(sk)->parent_sk;
	if (!parent_sk){
		wan_free(DATA_SC(sk));
		DATA_SC_INIT(sk,NULL);
		return;
	}	

	sk_child_exists = wanpipe_unattach_sk_from_parent(parent_sk,sk);

	/* If this is the last socket in the parents list
	 * remove the parent as well */
	if (!sk_child_exists){

#ifdef LINUX_2_6
		sock_set_flag(parent_sk, SOCK_DEAD);
#else
		set_bit(0,&parent_sk->dead);
#endif
		tasklet_kill(&PPRIV(parent_sk)->rx_task);
		skb_queue_purge(&PPRIV(parent_sk)->rx_queue);
		
		DEBUG_EVENT("%s: Parent sock unregistered from dev %s\n",
				SK_PRIV(parent_sk)->dev->name,
				SK_PRIV(parent_sk)->dev->name);

		/* Remove parent from the global parent list */
		write_lock_irqsave(&wanpipe_parent_sklist_lock,flags);
#ifdef LINUX_2_6
		sk_del_node_init(parent_sk);
#else
		{
		struct sock **skp;
		for (skp = &wanpipe_parent_sklist; *skp; skp = &(*skp)->next) {
			if (*skp == parent_sk) {
				*skp = parent_sk->next;
				wp_sock_put(parent_sk);
				break;
			}
		}
		}
#endif
		wanpipe_free_parent_sock(parent_sk);	
		parent_sk=NULL;

		write_unlock_irqrestore(&wanpipe_parent_sklist_lock,flags);

	}

	wansk_reset_zapped(sk);
	wan_free(DATA_SC(sk));
	DATA_SC_INIT(sk,NULL);
	
	return;
}


void wanpipe_wakup_sk_attached_to_parent(struct sock *parent_sk)
{
	struct sock *sk;
	int i;

	if (!DATA_SC(parent_sk) || !DATA_SC(parent_sk)->parent){
		return;
	}

	for (i=0;i<MAX_PARENT_PROT_NUM;i++){
		if ((sk=PPRIV(parent_sk)->sk_to_prot_map[i]) != NULL){
			sk->sk_state = parent_sk->sk_state;
			sk->sk_data_ready(sk,0);
		}
	}
}

static void wanpipe_sk_parent_rx_bh (unsigned long data)
{
	struct sock *parent_sk = (struct sock *)data;
	struct sk_buff *skb;
	unsigned char txdir=0;
	int err;

#ifdef LINUX_2_6
	if (sock_flag(parent_sk, SOCK_DEAD)){
#else
	if (test_bit(0,&parent_sk->dead)){
#endif
		return;
	}
	
	read_lock(&PPRIV(parent_sk)->lock);

	while ((skb=skb_dequeue(&PPRIV(parent_sk)->rx_queue))){
#ifdef LINUX_2_6
		if (sock_flag(parent_sk, SOCK_DEAD)){
#else		
		if (test_bit(0,&parent_sk->dead)){
#endif
			read_unlock(&PPRIV(parent_sk)->lock);
			return;
		}

		/* Protocol specific here */
		if (PPRIV(parent_sk)->hdlc_enabled){
			
			wp_sock_rx_hdr_t *hdr=(wp_sock_rx_hdr_t *)skb->data;
			txdir=hdr->direction;

			skb_pull(skb,sizeof(wp_sock_rx_hdr_t));
			
			if (wanpipe_skb_timeslot_parse(parent_sk,skb,txdir)){
				/* There is data to be passed up
				 * check all hdlc engines */
				wanpipe_handle_hdlc_data(parent_sk);
			}
			wan_skb_free(skb);
			err = 0;
		}else{
			err = wanpipe_rx_prot_data(PPRIV(parent_sk)->sk_to_prot_map,
						    SK_PRIV(parent_sk)->num,
						    skb);
		}
	}
	
	read_unlock(&PPRIV(parent_sk)->lock);

	return;
}


int wanpipe_sk_parent_rx(struct sock *parent_sk, struct sk_buff *skb)
{
	unsigned char txdir=0;
	int err;
	
	if (skb->len <= WANPIPE_HEADER_SZ){
		wan_skb_free(skb);
		return 0;
	}

	if (!PPRIV(parent_sk)){
		wan_skb_free(skb);
		return 0;
	}

#ifdef LINUX_2_6
	if (sock_flag(parent_sk, SOCK_DEAD)){
#else
	if (test_bit(0,&parent_sk->dead)){
#endif
		wan_skb_free(skb);
		return 0;
	}

	skb_queue_tail(&PPRIV(parent_sk)->rx_queue,skb);
	tasklet_hi_schedule(&PPRIV(parent_sk)->rx_task);

	return 0;
	
	read_lock(&PPRIV(parent_sk)->lock);
	
	/* Protocol specific here */
	if (PPRIV(parent_sk)->hdlc_enabled){
		
		wp_sock_rx_hdr_t *hdr=(wp_sock_rx_hdr_t *)skb->data;
		txdir=hdr->direction;

		skb_pull(skb,sizeof(wp_sock_rx_hdr_t));
		
		if (wanpipe_skb_timeslot_parse(parent_sk,skb,txdir)){
			/* There is data to be passed up
			 * check all hdlc engines */
			wanpipe_handle_hdlc_data(parent_sk);
		}
		wan_skb_free(skb);
		err = 0;
	}else{
		err = wanpipe_rx_prot_data(PPRIV(parent_sk)->sk_to_prot_map,
				            SK_PRIV(parent_sk)->num,
					    skb);
	}
	
	read_unlock(&PPRIV(parent_sk)->lock);
	
	return err;
}


static int wanpipe_rx_prot_data(void **map, unsigned short protocol, struct sk_buff *skb)
{
	int sk_id=MAX_PARENT_PROT_NUM;
	struct sock *sk;
	int err;
	
	/* Protocol specific here */

	if (protocol == htons(SS7_MONITOR_PROT)){
		sk_id=wanpipe_sk_ss7_monitor_rx(map,&skb);
	}else{
		if (net_ratelimit()){
			DEBUG_EVENT("%s: Invalid sk prot configuration 0x%X\n",__FUNCTION__,
				protocol);
		}
		wan_skb_free(skb);
		return 0;
	}

	if (sk_id < 0){
		/* The packet was intentionaly dropped by
		 * the protocol layer.  The packet was
		 * handled by the protocol layer thus,
		 * no deallocation is needed */
		return 0;
	}
	
	if (sk_id >= MAX_PARENT_PROT_NUM){
		if (net_ratelimit()){
			DEBUG_EVENT("%s: Invalid skb pkt len %i id=%i\n",
					__FUNCTION__,
					skb->len,sk_id);
		}
		wan_skb_free(skb);
		return 0;
	}
	
	if ((sk=map[sk_id]) == NULL){
		if (net_ratelimit()){
			DEBUG_EVENT("%s: No sk for prot %s\n",
					__FUNCTION__,
					DECODE_SS7_PROT(sk_id+1));
		}
		wan_skb_free(skb);
		return 0;
	}

	if ((err=sock_queue_rcv_skb(sk,skb))<0){
		sk->sk_data_ready(sk,0);
		if (net_ratelimit()){
			DEBUG_EVENT("%s: Sock failed to rx on prot %s: sock full: err %i truesize %i sktotal %i skblen %i!\n",
					__FUNCTION__,
					DECODE_SS7_PROT(DATA_SC(sk)->prot_type),
					err,skb->truesize,
					sk->sk_rcvbuf,
					skb->len);
		}

		//FIXME: skb ptr can be mangled at this poing
		//       we should never get here
		kfree(skb);
		return 0;
	}

	return 0;
}

static int wanpipe_handle_hdlc_data(struct sock *parent_sk)
{
	wanpipe_hdlc_engine_t *hdlc_eng;	
	struct sk_buff *skb;
	
	for (hdlc_eng = PPRIV(parent_sk)->hdlc_eng_list;
	     hdlc_eng;
	     hdlc_eng=hdlc_eng->next){
		while ((skb=wan_skb_dequeue(&hdlc_eng->data_q)) != NULL){
			wanpipe_rx_prot_data(hdlc_eng->sk_to_prot_map,
				            SK_PRIV(parent_sk)->num,
					    skb);
		}
	}

	return 0;
}

/*******************************************************************
 *
 * PRIVATE FUNCTIONS
 *
 *******************************************************************/


static inline unsigned short wanpipe_skb_get_checksum(struct sk_buff *skb)
{
	return *((unsigned short*)&skb->data[skb->len-2]);
}

static int wanpipe_skb_mult_check(struct sk_buff **orig_skb, 
	                          unsigned short *orig_csum, 
	 	                  struct sk_buff **up_skb, 
				  unsigned short max_mult_cnt)
{
	unsigned short csum;
	struct sk_buff *cur_skb=*orig_skb;
	struct sk_buff *skb=*up_skb;
	unsigned short *cur_skb_cnt,*skb_cnt;

	csum=wanpipe_skb_get_checksum(skb);
	skb_cnt=(unsigned short*)&skb->data[0];

	if (cur_skb == NULL){
		/* This is the first incoming packet for this
		 * protocol */
		*orig_skb = skb;
		*orig_csum = csum;
		*skb_cnt=1;

		return -1;
		
	}

	cur_skb_cnt=(unsigned short*)&cur_skb->data[0];

	if (csum == *orig_csum){			
		/* Same as already stored packet,
		 * increment the packet count */
			
		if (++(*cur_skb_cnt) >= max_mult_cnt){
			/* We have reached the maximum number of
			 * multiple packets, send the packet up
			 * the stack */
	
			DEBUG_EVENT("REACHED MAX MULT COUNT %i\n",
					max_mult_cnt);
			
			*skb_cnt = *cur_skb_cnt;
					
			kfree_skb(cur_skb);
			*orig_skb=NULL;
			*orig_csum=0;
						
		}else{
			/* We incremented the count thus drop
			 * the packet */
			return 1;
		}
	}else{
		/* Different packet recevied, we must pass previous
		 * packet up the stack and store current one */
		*up_skb = cur_skb;

		DEBUG_EVENT("GOT DIFF PKT sending with cnt %i\n",
				*cur_skb_cnt);

		/* Store the incoming packet and incremnet its
		 * rx count */
		*orig_skb=skb;
		*orig_csum=csum;
		*skb_cnt=1;
	}

	return 0;
}



static int wanpipe_sk_ss7_monitor_rx(void **map, struct sk_buff **skb_ptr)
{
	struct sk_buff *skb=*skb_ptr;
	int len=skb->len-WANPIPE_HEADER_SZ;
	
	wp_sock_rx_hdr_t *hdr=(wp_sock_rx_hdr_t *)skb->data;
	int txdir=hdr->direction;
	
	unsigned short csum;
	struct sock *sk;
	int err;

	if ((sk=map[HDLC_BIT_MAP]) != NULL){
		return HDLC_BIT_MAP;
	}
	
	if (len == SS7_FISU_LEN){

		if ((sk=map[SS7_FISU_BIT_MAP]) == NULL){
			goto ss7_drop_pkt;
		}
		
		if (DATA_SC(sk)->delta){

			csum=wanpipe_skb_get_checksum(skb);
			if (txdir){
				if (csum == DATA_SC(sk)->u.ss7.tx_fisu_skb_csum){
					goto ss7_drop_pkt;
				}
				DATA_SC(sk)->u.ss7.tx_fisu_skb_csum=csum;
			}else{
				if (csum == DATA_SC(sk)->u.ss7.rx_fisu_skb_csum){
					goto ss7_drop_pkt;
				}
				DATA_SC(sk)->u.ss7.rx_fisu_skb_csum=csum;
			}

		}else if (DATA_SC(sk)->max_mult_cnt){

			if (txdir){
				err=wanpipe_skb_mult_check((struct sk_buff **)&DATA_SC(sk)->u.ss7.tx_fisu_skb,
							   &DATA_SC(sk)->u.ss7.tx_fisu_skb_csum,
							   skb_ptr,
							   DATA_SC(sk)->max_mult_cnt);
				
			}else{
				err=wanpipe_skb_mult_check((struct sk_buff **)&DATA_SC(sk)->u.ss7.rx_fisu_skb,
							   &DATA_SC(sk)->u.ss7.rx_fisu_skb_csum,
							   skb_ptr,
							   DATA_SC(sk)->max_mult_cnt);
			}
			
			if (err < 0){
				goto ss7_skip_pkt;
			}else if ( err > 0){
				goto ss7_drop_pkt;
			}
		}

		return (SS7_FISU_BIT_MAP);

		
	}else if (len > SS7_FISU_LEN && len < SS7_MSU_LEN){
		
		if ((sk=map[SS7_LSSU_BIT_MAP]) == NULL){
			goto ss7_drop_pkt;
		}

		if (DATA_SC(sk)->delta){

			csum=wanpipe_skb_get_checksum(skb);
			if (txdir){
				if (csum == DATA_SC(sk)->u.ss7.tx_lssu_skb_csum){
					goto ss7_drop_pkt;
				}
				
				DATA_SC(sk)->u.ss7.tx_lssu_skb_csum=csum;
				
			}else{
				if (csum == DATA_SC(sk)->u.ss7.rx_lssu_skb_csum){
					goto ss7_drop_pkt;
				}
				
				DATA_SC(sk)->u.ss7.rx_lssu_skb_csum=csum;
			}
			
		}else if (DATA_SC(sk)->max_mult_cnt){

			if (txdir){
				err=wanpipe_skb_mult_check((struct sk_buff **)&DATA_SC(sk)->u.ss7.tx_lssu_skb,
						   &DATA_SC(sk)->u.ss7.tx_lssu_skb_csum,
						   skb_ptr,
						   DATA_SC(sk)->max_mult_cnt);
			}else{
				err=wanpipe_skb_mult_check((struct sk_buff **)&DATA_SC(sk)->u.ss7.rx_lssu_skb,
						   &DATA_SC(sk)->u.ss7.rx_lssu_skb_csum,
						   skb_ptr,
						   DATA_SC(sk)->max_mult_cnt);
			}
				
			if (err < 0){
				goto ss7_skip_pkt;
			}else if ( err > 0){
				goto ss7_drop_pkt;
			}
		}
		
		return (SS7_LSSU_BIT_MAP);

	}else if (len >= SS7_MSU_LEN && len <= SS7_MSU_END_LEN){
	
		if ((sk=map[SS7_MSU_BIT_MAP]) == NULL){
			goto ss7_drop_pkt;
		}
		
		return (SS7_MSU_BIT_MAP);
	}

#if 1
	if (net_ratelimit()){
		DEBUG_EVENT("%s: Error: Illegal ss7 frame len %i\n",
				__FUNCTION__,len);
	}
#endif

	/* Invalid protocol or ss7 packet */
	return MAX_PARENT_PROT_NUM; 

ss7_drop_pkt:	
	kfree_skb(skb);
	*skb_ptr=NULL;

ss7_skip_pkt:
	return -1;
}




static int wanpipe_check_prot_options(struct sock *sk)
{
	if (SK_PRIV(sk)->num == htons(SS7_MONITOR_PROT)){

		if ((DATA_SC(sk)->prot_type & (1<<HDLC_BIT_MAP))){
			return 0;
		}
		
		if ((DATA_SC(sk)->prot_type & SS7_ALL_PROT) == 0){
			return -EINVAL;
		}
		return 0;
	}

	return -EINVAL;
}

static int wanpipe_attach_sk_to_parent (struct sock *parent_sk, struct sock *sk)
{
	unsigned long flags;

	if (PPRIV(parent_sk)->hdlc_enabled){

		wanpipe_hdlc_engine_t *hdlc_eng;

		hdlc_eng = wanpipe_get_hdlc_engine(parent_sk,DATA_SC(sk)->active_ch);
		if (!hdlc_eng){
			return -ENOMEM;
		}

		DEBUG_INIT("%s: (DEBUG) GOT HDLC ENGINE %p Bound = %d\n",
			SK_PRIV(parent_sk)->dev->name,hdlc_eng,hdlc_eng->bound);

		write_lock_irqsave(&PPRIV(parent_sk)->lock,flags);

		if (bind_sk_to_parent_prot(hdlc_eng->sk_to_prot_map,
					   SK_PRIV(parent_sk)->dev->name,sk) != 0){
			write_unlock_irqrestore(&PPRIV(parent_sk)->lock,flags);
			if (!hdlc_eng->bound){
				free_hdlc_engine(hdlc_eng);
			}
			return -EEXIST;
		}

		/* Insert the HDLC engine in parent hdlc
		 * engine list */
		if (!hdlc_eng->bound){
			int err;
			
			DEBUG_INIT("%s: (DEBUG) BOUND HDLC ENGINE %p to Parent\n",
				SK_PRIV(parent_sk)->dev->name,hdlc_eng);
		
			hdlc_eng->next = PPRIV(parent_sk)->hdlc_eng_list;
			PPRIV(parent_sk)->hdlc_eng_list = hdlc_eng;
			wp_dev_hold(hdlc_eng);

			err=bind_hdlc_into_timeslots(parent_sk,hdlc_eng);
			if (err!=0){
				unbind_hdlc_from_timeslots(parent_sk,hdlc_eng);
				unbind_sk_from_parent_prot(hdlc_eng->sk_to_prot_map,
							   SK_PRIV(parent_sk)->dev->name,
							   sk);
				write_unlock_irqrestore(&PPRIV(parent_sk)->lock,flags);
				free_hdlc_engine(hdlc_eng);
				return -ENOMEM;
			}
		
			hdlc_eng->bound=1;
		}

		DATA_SC(sk)->hdlc_eng = hdlc_eng;
		wp_dev_hold(hdlc_eng);

		write_unlock_irqrestore(&PPRIV(parent_sk)->lock,flags);

	}else{

		write_lock_irqsave(&PPRIV(parent_sk)->lock,flags);

		if (bind_sk_to_parent_prot(PPRIV(parent_sk)->sk_to_prot_map,
					   SK_PRIV(parent_sk)->dev->name,sk) != 0){
			write_unlock_irqrestore(&PPRIV(parent_sk)->lock,flags);
			return -EEXIST;
		}
	
		write_unlock_irqrestore(&PPRIV(parent_sk)->lock,flags);	
	}

	return 0;
}



int wanpipe_unattach_sk_from_parent (struct sock *parent_sk, struct sock *sk)
{
	unsigned long flags;
	int sk_child_exists=0;

	/* Unbind sk from parents list */
	write_lock_irqsave(&PPRIV(parent_sk)->lock,flags);

	if (PPRIV(parent_sk)->hdlc_enabled){

		wanpipe_hdlc_engine_t *hdlc_eng = DATA_SC(sk)->hdlc_eng;
		if (hdlc_eng){
			sk_child_exists = 
				unbind_sk_from_parent_prot(hdlc_eng->sk_to_prot_map,
							   SK_PRIV(parent_sk)->dev->name,
							   sk);
			if (!sk_child_exists){
				unbind_hdlc_from_timeslots(parent_sk,DATA_SC(sk)->hdlc_eng);	
				
				hdlc_eng->bound=0;
				wp_dev_put(hdlc_eng);
				DATA_SC(sk)->hdlc_eng=NULL;

				free_hdlc_engine(hdlc_eng);

			}else{
				wp_dev_put(DATA_SC(sk)->hdlc_eng);
				DATA_SC(sk)->hdlc_eng=NULL;	
			}
		}

		sk_child_exists = (PPRIV(parent_sk)->hdlc_eng_list == NULL) ? 0:1;
	
	}else{
		sk_child_exists = 
			unbind_sk_from_parent_prot(PPRIV(parent_sk)->sk_to_prot_map,
			 	   		   SK_PRIV(parent_sk)->dev->name,
					           sk);
	}
	
	wp_sock_put(DATA_SC(sk)->parent_sk);
	DATA_SC(sk)->parent_sk=NULL;

	write_unlock_irqrestore(&PPRIV(parent_sk)->lock,flags);

	return sk_child_exists;
}


/**************************************************************
 *
 * HDLC ENGINE
 * 
 **************************************************************/

static int wanpipe_skb_timeslot_parse(struct sock *parent_sk, 
		                      struct sk_buff *skb, 
				      unsigned char txdir)
{
	wanpipe_hdlc_engine_t *chan;
	wanpipe_hdlc_list_t *list_el;
	unsigned int ch=0,stream_full=0;
	unsigned char *buf;
	int len=skb->len;

//	read_lock(&PPRIV(parent_sk)->lock);

	if (PPRIV(parent_sk)->media == WAN_MEDIA_NONE){
		list_el=PPRIV(parent_sk)->time_slot_hdlc_map[0];
		if (list_el){
			chan=list_el->hdlc;	

			if (!chan->skb_decode_size){

				chan->skb_decode_size=len;

				if (chan->skb_decode_size > MAX_SOCK_HDLC_LIMIT){
					chan->skb_decode_size=MAX_SOCK_HDLC_LIMIT;
				}
				
				DEBUG_EVENT("%s: Autocfg serial hdlc tx up size to %d\n",
						SK_PRIV(parent_sk)->dev->name,
						chan->skb_decode_size);
			}

			if (wanpipe_hdlc_decode(chan,txdir,skb)){
				stream_full=1;
			}
			return stream_full;
		}
	}
	
	for (;;){

		for (list_el=PPRIV(parent_sk)->time_slot_hdlc_map[ch];
		     list_el;
		     list_el = list_el->next){

			chan=list_el->hdlc;

			if (!chan->skb_decode_size){

				chan->skb_decode_size=len;

				if (chan->skb_decode_size > MAX_SOCK_HDLC_LIMIT){
					chan->skb_decode_size=MAX_SOCK_HDLC_LIMIT;
				}
				
				if (chan->skb_decode_size % chan->timeslots){
					chan->skb_decode_size-=
							chan->skb_decode_size % 
							chan->timeslots;
					chan->skb_decode_size+=chan->timeslots;
				}

				DEBUG_EVENT("%s: Autocfg hdlc tx up size to %d\n",
						SK_PRIV(parent_sk)->dev->name,
						chan->skb_decode_size);
			}


			if (txdir){

				if (chan->raw_tx_skb){
					buf=skb_put(chan->raw_tx_skb,1);
					buf[0]=skb->data[0];

					if (chan->raw_tx_skb->len >= chan->skb_decode_size){
						if (wanpipe_hdlc_decode(chan,txdir,chan->raw_tx_skb)){
							stream_full=1;
						}
						wan_skb_init(chan->raw_tx_skb,16);
						wan_skb_trim(chan->raw_tx_skb,0);
					}
					
				}else{
					DEBUG_EVENT("%s:%s: Critical Error no hdlc raw_tx_skb ptr!\n",
							SK_PRIV(parent_sk)->dev->name,
							__FUNCTION__);
				}
			}else{
				if (chan->raw_rx_skb){
					buf=skb_put(chan->raw_rx_skb,1);
					buf[0]=skb->data[0];

					if (chan->raw_rx_skb->len >= chan->skb_decode_size){
						if (wanpipe_hdlc_decode(chan,txdir,chan->raw_rx_skb)){
							stream_full=1;
						}
						wan_skb_init(chan->raw_rx_skb,16);
						wan_skb_trim(chan->raw_rx_skb,0);
					}
					
				}else{
					DEBUG_EVENT("%s:%s: Critical Error no hdlc raw_rx_skb ptr!\n",
							SK_PRIV(parent_sk)->dev->name,
							__FUNCTION__);
				}
			}
		}
		skb_pull(skb,1);

		if (skb->len < 1){
			break;
		}
	
		if (++ch >= PPRIV(parent_sk)->time_slots){
			ch=0;
		}
	
	}//for(;;)

//	read_unlock(&PPRIV(parent_sk)->lock);
	
	return stream_full;
}

/* HDLC Bitstream Decode Functions */
static int wanpipe_hdlc_decode (wanpipe_hdlc_engine_t *hdlc_eng, unsigned char txdir, struct sk_buff *skb)
{
	int i;
	int word_len;
	int found=0;
	unsigned char *buf;
	int len;
	int gotdata=0;

	buf = skb->data;
	len = skb->len;
	
	word_len=len-(len%4);
	/* Before decoding the packet, make sure that the current
	 * bit stream contains data. Decoding is very expensive,
	 * thus perform word (32bit) comparision test */
	
	for (i=0;i<word_len;i+=4){
		if ((*(unsigned int*)&buf[i]) != *(unsigned int*)buf){
			found=1;
			break;
		}
	}

	if ((len%4) && !found){
		for (i=word_len;i<len;i++){
			if (buf[i]!=buf[0]){
				found=1;
				break;
			}
		}
	}
	
	/* Data found proceed to decode
	 * the bitstream and pull out data packets */
	if (found){
		wanpipe_hdlc_decoder_t *hdlc_decoder;
	
		if (txdir){
			hdlc_decoder = 	&hdlc_eng->tx_decoder;
		}else{
			hdlc_decoder = 	&hdlc_eng->rx_decoder;
		}

		for (i=0; i<len; i++){
			if (decode_byte(hdlc_eng,hdlc_decoder,&buf[i],txdir)){
				gotdata=1;
			}
		}

		if (hdlc_decoder->rx_decode_len >= MAX_SOCK_HDLC_LIMIT){
			DEBUG_EVENT ("ERROR Rx decode len > max\n");		
			init_hdlc_decoder(hdlc_decoder);
		}
	}
	
	return gotdata;
}

static int decode_byte (wanpipe_hdlc_engine_t *hdlc_eng, 
		        wanpipe_hdlc_decoder_t *chan,
			unsigned char *byte_ptr,
			unsigned char txdir)
{
	int i;
	int gotdata=0;
	unsigned long byte=*byte_ptr;

	/* Test each bit in an incoming bitstream byte.  Search
	 * for an hdlc flag 0x7E, six 1s in a row.  Once the
	 * flag is obtained, construct the data packets. 
	 * The complete data packets are sent up the API stack */
	
	for (i=0; i<BITSINBYTE; i++){

		if (hdlc_eng->seven_bit_hdlc && i == 7){
			continue;
		}
		
		if (test_bit(i,&byte)){
			/* Got a 1 */
			
			++chan->rx_decode_onecnt;
			
			/* Make sure that we received a valid flag,
			 * before we start decoding incoming data */
			if (!test_bit(NO_FLAG,&chan->hdlc_flag)){ 
				chan->rx_decode_buf[chan->rx_decode_len] |= (1 << chan->rx_decode_bit_cnt);
				
				if (++chan->rx_decode_bit_cnt >= BITSINBYTE){

					/* Completed a byte of data, update the
					 * crc count, and start on the next 
					 * byte.  */
					calc_rx_crc(chan);
#ifdef PRINT_PKT
					printk(" %02X", data);
#endif
					++chan->rx_decode_len;
					if (chan->rx_decode_len > MAX_SOCK_HDLC_BUF){
						init_hdlc_decoder(&hdlc_eng->tx_decoder);	
					}else{
						chan->rx_decode_buf[chan->rx_decode_len]=0;
						chan->rx_decode_bit_cnt=0;
						chan->hdlc_flag=0;
						set_bit(CLOSING_FLAG,&chan->hdlc_flag);
					}
				}
			}
		}else{
			/* Got a zero */
			if (chan->rx_decode_onecnt == 5){
				
				/* bit stuffed zero detected,
				 * do not increment our decode_bit_count.
				 * thus, ignore this bit*/
				
			
			}else if (chan->rx_decode_onecnt == 6){
				
				/* Got a Flag */
				if (test_bit(CLOSING_FLAG,&chan->hdlc_flag)){
				
					/* Got a closing flag, thus asemble
					 * the packet and send it up the 
					 * stack */
					chan->hdlc_flag=0;
					set_bit(OPEN_FLAG,&chan->hdlc_flag);
				
					if (chan->rx_decode_len >= 3){
						
						GET_FIN_CRC_CNT(chan->crc_cur);
						FLIP_CRC(chan->rx_crc[chan->crc_cur],chan->crc_fin);
						DECODE_CRC(chan->crc_fin);
				
						/* Check CRC error before passing data up
						 * the API socket */
						if (chan->crc_fin==chan->rx_orig_crc){
							tx_up_decode_pkt(hdlc_eng,chan,txdir);
							gotdata=1;
						}else{
							//++chan->ifstats.rx_crc_errors;
							//CRC Error; initialize hdlc eng
							init_hdlc_decoder(&hdlc_eng->tx_decoder);
						}
					}else{
						//Abort
						//++chan->ifstats.rx_frame_errors;
					}

				}else if (test_bit(NO_FLAG,&chan->hdlc_flag)){
					/* Got a very first flag */
					chan->hdlc_flag=0;	
					set_bit(OPEN_FLAG,&chan->hdlc_flag);
				}

				/* After a flag, initialize the decode and
				 * crc buffers and get ready for the next 
				 * data packet */
				chan->rx_decode_len=0;
				chan->rx_decode_buf[chan->rx_decode_len]=0;
				chan->rx_decode_bit_cnt=0;
				chan->rx_crc[0]=-1;
				chan->rx_crc[1]=-1;
				chan->rx_crc[2]=-1;
				chan->crc_cur=0; 
				chan->crc_prv=0;
			}else{
				/* Got a valid zero, thus increment the
				 * rx_decode_bit_cnt, as a result of which
				 * a zero is left in the consturcted
				 * byte.  NOTE: we must have a valid flag */
				
				if (!test_bit(NO_FLAG,&chan->hdlc_flag)){ 	
					if (++chan->rx_decode_bit_cnt >= BITSINBYTE){
						calc_rx_crc(chan);
#ifdef PRINT_PKT
						printk(" %02X", data);
#endif
						++chan->rx_decode_len;
						if (chan->rx_decode_len > MAX_SOCK_HDLC_BUF){
							init_hdlc_decoder(&hdlc_eng->tx_decoder);
						}else{
							chan->rx_decode_buf[chan->rx_decode_len]=0;
							chan->rx_decode_bit_cnt=0;
							chan->hdlc_flag=0;
							set_bit(CLOSING_FLAG,&chan->hdlc_flag);
						}
					}
				}
			}
			chan->rx_decode_onecnt=0;
		}
	}
	
	return gotdata;
}


static void init_crc(void)
{
	int i,j;

	if (test_and_set_bit(0,&init_crc_g)){
		return;
	}
	
	for(i=0;i<256;i++){
		CRC_TABLE[i]=0;
		for (j=0;j<BITSINBYTE;j++){
			if (i & (1<<j)){
				CRC_TABLE[i] ^= MagicNums[j];
			}
		}
	}
}

static void calc_rx_crc(wanpipe_hdlc_decoder_t *chan)
{
	INC_CRC_CNT(chan->crc_cur);

	/* Save the incoming CRC value, so it can be checked
	 * against the calculated one */
	chan->rx_orig_crc = (((chan->rx_orig_crc<<8)&0xFF00) | chan->rx_decode_buf[chan->rx_decode_len]);
	
	chan->rx_crc_tmp = (chan->rx_decode_buf[chan->rx_decode_len] ^ chan->rx_crc[chan->crc_prv]) & 0xFF;
	chan->rx_crc[chan->crc_cur] =  chan->rx_crc[chan->crc_prv] >> 8;
	chan->rx_crc[chan->crc_cur] &= 0x00FF;
	chan->rx_crc[chan->crc_cur] ^= CRC_TABLE[chan->rx_crc_tmp];
	chan->rx_crc[chan->crc_cur] &= 0xFFFF;
	INC_CRC_CNT(chan->crc_prv);
}

static void tx_up_decode_pkt(wanpipe_hdlc_engine_t *hdlc_eng, 
		             wanpipe_hdlc_decoder_t *chan, 
			     unsigned char txdir)
{
	wp_sock_rx_hdr_t *hdr;
	unsigned char *buf;

	struct sk_buff *skb = wan_skb_alloc(chan->rx_decode_len+sizeof(wp_sock_rx_hdr_t));
	if (!skb){
		printk(KERN_INFO "wansock: %s: HDLC Tx up: failed to allocate memory!\n",
				__FUNCTION__);
		return;
	}

	hdr=(wp_sock_rx_hdr_t *)skb_put(skb,sizeof(wp_sock_rx_hdr_t));
	memset(hdr, 0, sizeof(wp_sock_rx_hdr_t));
	hdr->direction=txdir;

	buf = skb_put(skb,chan->rx_decode_len);
	memcpy(buf, 
	       chan->rx_decode_buf, 
	       chan->rx_decode_len);

	DEBUG_TX("Tx UP Dir=%d, decod size %i hdr %i skblen=%i\n",
			txdir,chan->rx_decode_len,
			sizeof(wp_sock_rx_hdr_t),
			skb->len);

	wan_skb_queue_tail(&hdlc_eng->data_q,skb);
	return;

}

/**************************************************************
 *
 * UTILITY FUNCTIONS
 * 
 **************************************************************/



static int bind_hdlc_into_timeslots(struct sock *parent_sk,
		                     wanpipe_hdlc_engine_t *hdlc_eng)
{
	int i=0;
	wanpipe_hdlc_list_t *hdlc_list;

	if (PPRIV(parent_sk)->media == WAN_MEDIA_NONE){
		hdlc_list=wan_malloc(sizeof(wanpipe_hdlc_list_t));
		if (!hdlc_list){
			DEBUG_EVENT("%s: Error failed to allocate list memory!\n",
					SK_PRIV(parent_sk)->dev->name);
			return -ENOMEM;
		}
		memset(hdlc_list,0,sizeof(sizeof(wanpipe_hdlc_list_t)));

		++hdlc_eng->timeslots;
		hdlc_list->hdlc=hdlc_eng;
		wp_dev_hold(hdlc_eng);
			
		hdlc_list->next = PPRIV(parent_sk)->time_slot_hdlc_map[i];
		PPRIV(parent_sk)->time_slot_hdlc_map[i] = hdlc_list;

		DEBUG_EVENT("%s: Binding HDLC engine to Serial\n",
				SK_PRIV(parent_sk)->dev->name);
		return 0;
		
	}

	
	for (i=0;i<PPRIV(parent_sk)->time_slots;i++){
		if (test_bit(i,&hdlc_eng->active_ch)){

			hdlc_list=wan_malloc(sizeof(wanpipe_hdlc_list_t));
			if (!hdlc_list){
				DEBUG_EVENT("%s: Error failed to allocate list memory!\n",
						SK_PRIV(parent_sk)->dev->name);
				return -ENOMEM;
			}
			memset(hdlc_list,0,sizeof(sizeof(wanpipe_hdlc_list_t)));

			++hdlc_eng->timeslots;
			hdlc_list->hdlc=hdlc_eng;
			wp_dev_hold(hdlc_eng);
			
			if (PPRIV(parent_sk)->media == WAN_MEDIA_E1){
				hdlc_list->next = PPRIV(parent_sk)->time_slot_hdlc_map[i+1];
				PPRIV(parent_sk)->time_slot_hdlc_map[i+1] = hdlc_list;
			}else{
				hdlc_list->next = PPRIV(parent_sk)->time_slot_hdlc_map[i];
				PPRIV(parent_sk)->time_slot_hdlc_map[i] = hdlc_list;
			}

			DEBUG_EVENT("%s: Binding HDLC engine to timeslot %i\n",
					SK_PRIV(parent_sk)->dev->name,i+1);
		}
	}
	
	return 0;
}

static int remove_hdlc_from_list(wanpipe_hdlc_engine_t **list, wanpipe_hdlc_engine_t *hdlc_eng)
{
	wanpipe_hdlc_engine_t *tmp,*prev;

	if ((tmp=*list) == hdlc_eng){
		*list = hdlc_eng->next;
		DEBUG_TEST("DEBUG Removing first hdlc_eng %p from hdlc list: list=%p\n",
				hdlc_eng,*list);
		wp_dev_put(hdlc_eng);
		return 0;
	}
	
	while (tmp != NULL && tmp->next != NULL){
		
		prev=tmp;
		tmp=tmp->next;

		if (tmp == hdlc_eng){
			prev->next=tmp->next;	
			DEBUG_TEST("DEBUG Removing hdlc_eng %p from hdlc list: list=%p\n",
				hdlc_eng,*list);
			wp_dev_put(hdlc_eng);
			return 0;
		}
	}

	return -ENODEV;
}

static int remove_hdlc_from_ts_list(wanpipe_hdlc_list_t **list, wanpipe_hdlc_engine_t *hdlc_eng)
{
	wanpipe_hdlc_list_t *tmp,*prev;

	if ((tmp=*list)){

		if (tmp->hdlc == hdlc_eng){
		
			*list = tmp->next;
		
			DEBUG_TEST("DEBUG Removing first hdlc_eng %p from ts list: list=%p\n",
				hdlc_eng,*list);
			wp_dev_put(hdlc_eng);

			wan_free(tmp);
			return 0;
		}
	}
	
	while (tmp != NULL && tmp->next != NULL){
		
		prev=tmp;
		tmp=tmp->next;

		if (tmp->hdlc == hdlc_eng){
			prev->next=tmp->next;
			DEBUG_TEST("DEBUG Removing hdlc_eng %p from list: list=%p\n",
				hdlc_eng,*list);
			wp_dev_put(hdlc_eng);

			wan_free(tmp);
			return 0;
		}
	}

	return -ENODEV;
}



static void unbind_hdlc_from_timeslots(struct sock *parent_sk, wanpipe_hdlc_engine_t *hdlc_eng)
{
	int i;
	remove_hdlc_from_list(&PPRIV(parent_sk)->hdlc_eng_list,hdlc_eng);

	for (i=0;i<MAX_SOCK_CHANNELS;i++){
		if (PPRIV(parent_sk)->time_slot_hdlc_map[i]){
			remove_hdlc_from_ts_list(&PPRIV(parent_sk)->time_slot_hdlc_map[i],
					         hdlc_eng);
		}
	}

	return;
}


static wanpipe_hdlc_engine_t *wanpipe_get_hdlc_engine (struct sock *parent_sk,
		 				       unsigned long active_ch)
{
	wanpipe_hdlc_engine_t *hdlc_eng;
	
	for (hdlc_eng = PPRIV(parent_sk)->hdlc_eng_list; 
	     hdlc_eng;
	     hdlc_eng = hdlc_eng->next){
		if (hdlc_eng->active_ch == active_ch){
			return hdlc_eng;
		}	
	}

	hdlc_eng = wan_malloc(sizeof(wanpipe_hdlc_engine_t));
	if (!hdlc_eng){
		return NULL;
	}	

	memset(hdlc_eng,0,sizeof(wanpipe_hdlc_engine_t));

	hdlc_eng->raw_rx_skb = wan_skb_alloc(MAX_SOCK_HDLC_BUF);
	if (!hdlc_eng->raw_rx_skb){
		wan_free(hdlc_eng);
		return NULL;
	}

	hdlc_eng->raw_tx_skb = wan_skb_alloc(MAX_SOCK_HDLC_BUF);
	if (!hdlc_eng->raw_tx_skb){
		wan_skb_free(hdlc_eng->raw_rx_skb);
		wan_free(hdlc_eng);
		return NULL;
	}

	hdlc_eng->active_ch=active_ch;
	hdlc_eng->seven_bit_hdlc=PPRIV(parent_sk)->seven_bit_hdlc;

	init_hdlc_decoder(&hdlc_eng->rx_decoder);
	init_hdlc_decoder(&hdlc_eng->tx_decoder);
	
	skb_queue_head_init(&hdlc_eng->data_q);

	wp_dev_hold(hdlc_eng);

	return hdlc_eng;	
}

static void init_hdlc_decoder(wanpipe_hdlc_decoder_t *hdlc_decoder)
{
	hdlc_decoder->hdlc_flag=0;
	set_bit(NO_FLAG,&hdlc_decoder->hdlc_flag);
	
	hdlc_decoder->rx_decode_len=0;
	hdlc_decoder->rx_decode_buf[hdlc_decoder->rx_decode_len]=0;
	hdlc_decoder->rx_decode_bit_cnt=0;
	hdlc_decoder->rx_decode_onecnt=0;
	hdlc_decoder->rx_crc[0]=-1;
	hdlc_decoder->rx_crc[1]=-1;
	hdlc_decoder->rx_crc[2]=-1;
	hdlc_decoder->crc_cur=0; 
	hdlc_decoder->crc_prv=0;
}


static void free_hdlc_engine(wanpipe_hdlc_engine_t *hdlc_eng)
{
	skb_queue_purge(&hdlc_eng->data_q);
	if (hdlc_eng->raw_tx_skb){
		wan_skb_free(hdlc_eng->raw_tx_skb);
		hdlc_eng->raw_tx_skb=NULL;
	}
	if (hdlc_eng->raw_rx_skb){
		wan_skb_free(hdlc_eng->raw_rx_skb);
		hdlc_eng->raw_rx_skb=NULL;
	}
	wp_dev_put(hdlc_eng);
}

static int bind_sk_to_parent_prot(void **map, unsigned char *devname,struct sock *sk)
{
	int i;

	for (i=0;i<MAX_PARENT_PROT_NUM;i++){
		if (test_bit(i,&DATA_SC(sk)->prot_type)){
			if (map[i]){
				DEBUG_EVENT("%s: Error SS7 prot %s exists!\n",
					devname,DECODE_SS7_PROT((1<<i)));

				return -EEXIST;
			}else{

				DEBUG_EVENT("%s: Sock prot %s registered\n",
					devname,
					DECODE_SS7_PROT((1<<i)));

				map[i]=sk;	
				wp_sock_hold(sk);
			}
		}
	}
	return 0;
}

static int unbind_sk_from_parent_prot(void**map,unsigned char *devname,struct sock *sk)
{
	int i;
	int sk_child_exists=0;

	for (i=0;i<MAX_PARENT_PROT_NUM;i++){
		if (map[i] == sk){
			DEBUG_EVENT("%s: Sock prot %s unregistered\n",
					devname,
					DECODE_SS7_PROT((1<<i)));
			map[i]=NULL;	
			wp_sock_put(sk);
		}

		if (map[i] != NULL){
			sk_child_exists=1;
		}
	}

	return sk_child_exists;
}

/*============================================================
 *  wanpipe_free_parent_sock
 *
 *	This is a function which performs actual killing
 *      of the sock.  It releases socket resources,
 *      and unlinks the sock from the driver. 
 *===========================================================*/
static void wanpipe_free_parent_sock(struct sock *sk)
{
	unsigned long flags;
	netdevice_t *dev;
	
#ifndef LINUX_2_4
	struct sk_buff *skb;
#endif

	if (!sk)
		return;

	write_lock_irqsave(&PPRIV(sk)->lock,flags);

	dev=SK_PRIV(sk)->dev;
	if (dev && dev->do_ioctl){
		struct ifreq ifr;
		memset(&ifr,0,sizeof(struct ifreq));
		ifr.ifr_data = (void*)sk;
		DEBUG_TEST("%s: UNBINDING SK dev=%s\n",__FUNCTION__,dev->name);
		dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_UNBIND_SK);
	}

	sk->sk_socket = NULL;

	/* Purge queues */
#ifdef LINUX_2_4
	skb_queue_purge(&sk->sk_receive_queue);
	skb_queue_purge(&sk->sk_write_queue);
	skb_queue_purge(&sk->sk_error_queue);
#else	
	while ((skb=skb_dequeue(&sk->sk_receive_queue)) != NULL){
		kfree_skb(skb);
	}
	while ((skb=skb_dequeue(&sk->sk_write_queue)) != NULL) {
		kfree_skb(skb);
	}
	while ((skb=skb_dequeue(&sk->sk_error_queue)) != NULL){
		kfree_skb(skb);
	}
#endif

	write_unlock_irqrestore(&PPRIV(sk)->lock,flags);

	
	if (PPRIV(sk)){
		wan_free(PPRIV(sk));
		PPRIV_INIT(sk,NULL);
	}
	
	if (DATA_SC(sk)){
		wan_free(DATA_SC(sk));
		DATA_SC_INIT(sk,NULL);
	}
	
	if (SK_PRIV(sk)){
		kfree(sk->sk_user_data);
		sk->sk_user_data=NULL;
	}

      #ifdef LINUX_2_4
	if (atomic_read(&sk->refcnt) != 1){
		atomic_set(&sk->refcnt,1);
		DEBUG_EVENT("%s:%d: Error, wrong reference count: %i ! :timer.\n",
					__FUNCTION__,__LINE__,atomic_read(&sk->refcnt));
	}
	wp_sock_put(sk);
      #else
	sk_free(sk);
      #endif
	atomic_dec(&wanpipe_socks_nr);
#ifndef LINUX_2_6
	MOD_DEC_USE_COUNT;
#endif
	return;
}


#if 0
static void print_hdlc_list(wanpipe_hdlc_engine_t **list, int ts_list)
{
	wanpipe_hdlc_engine_t *tmp;

	DEBUG_EVENT("\n");
	if (ts_list){
		DEBUG_EVENT("TS LIST: ");
		for (tmp=*list;tmp;tmp=tmp->ts_next){
		__DEBUG_EVENT("%p ->"); 
	
	}else{
		DEBUG_EVENT("LIST: ");
		for (tmp=*list;tmp;tmp=tmp->next){
			__DEBUG_EVENT("%p ->"); 
		}
	}
	__DEBUG_EVENT("NULL \n");
	DEBUG_EVENT("\n");
	
}
#endif
