/*****************************************************************************
* aft_core_prot.c
*
* 		WANPIPE(tm) AFT CORE Hardware Support - Protocol/API
*
* Authors: 	Nenad Corbic <ncorbic@sangoma.com>
*
* Copyright:	(c) 2003-2008 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 "wanpipe_includes.h"
#include "wanpipe_defines.h"
#include "wanpipe.h"

#if defined (__LINUX__)
# include "wanproc.h"
# include "if_wanpipe.h"
#endif

#include "wanpipe_abstr.h"
#include "if_wanpipe_common.h"    /* Socket Driver common area */
#include "sdlapci.h"
#include "aft_core.h"
#include "wanpipe_iface.h"
#include "wanpipe_tdm_api.h"
#include "sdla_tdmv_dummy.h"



#if defined(AFT_XMTP2_API_SUPPORT)
/* This call back is not used by xmtp2. It is here for complete sake
 * It might be used in the future */
int wp_xmtp2_callback (void *prot_ptr, unsigned char *data, int len)
{
	private_area_t *chan = (private_area_t*)prot_ptr;
#if 0
	void * tx_skb;
	unsigned char *buff;
	int err;
#endif

	if (!chan || !data || len <= 0){
		if (WAN_NET_RATELIMIT()) {
		DEBUG_EVENT("%s:%d: Assert prot=%p  data=%p len=%d\n",
					__FUNCTION__,__LINE__,chan,data,len);
		}
		return -1;
	}

	DEBUG_EVENT("%s:%d: TX CALL BACK CALLED prot=%p  data=%p len=%d\n",
					__FUNCTION__,__LINE__,chan,data,len);

	return -1;
}
#endif



#if defined(AFT_RTP_SUPPORT)
enum {
	WAN_TDM_RTP_NO_CHANGE,
	WAN_TDM_RTP_CALL_START,
	WAN_TDM_RTP_CALL_STOP
};

void aft_rtp_unconfig(sdla_t *card)
{
	wan_rtp_chan_t *rtp_chan;
	int i;
	netskb_t *skb;

	card->wandev.rtp_len=0;

	card->rtp_conf.rtp_ip=0;
	card->rtp_conf.rtp_sample=0;

	if (card->wandev.rtp_dev) {
              	dev_put(card->wandev.rtp_dev);
		card->wandev.rtp_dev=NULL;
	}

	for (i=0;i<32;i++) {
		rtp_chan=&card->wandev.rtp_chan[i];
		if (rtp_chan->rx_skb) {
			wan_skb_free(rtp_chan->rx_skb);
			rtp_chan->rx_skb=NULL;
		}
		if (rtp_chan->tx_skb) {
			wan_skb_free(rtp_chan->tx_skb);
			rtp_chan->rx_skb=NULL;
		}
	}

	while ((skb=wan_skb_dequeue(&card->u.aft.rtp_tap_list))) {
		wan_skb_free(skb);
	}

}

int aft_rtp_config(sdla_t *card)
{
	netdevice_t *dev;
	card->wandev.rtp_tap=NULL;

	if (!card->rtp_conf.rtp_ip || !card->rtp_conf.rtp_sample) {
		return 1;
	}

	if (card->rtp_conf.rtp_sample < 10 || card->rtp_conf.rtp_sample > 150) {
		DEBUG_ERROR("%s: Error: Invalid RTP Sample %d [Min=10 Max=150ms]\n",
				card->devname,card->rtp_conf.rtp_sample);
		goto aft_rtp_init_exit;
	}

	DEBUG_EVENT("%s:    RTP TAP [ %d.%d.%d.%d:%d %dms %s %02X:%02X:%02X:%02X:%02X:%02X ]\n",
			card->devname,
			NIPQUAD(card->rtp_conf.rtp_ip),
			card->rtp_conf.rtp_port,
			card->rtp_conf.rtp_sample,
			card->rtp_conf.rtp_devname,
			card->rtp_conf.rtp_mac[0],
			card->rtp_conf.rtp_mac[1],
			card->rtp_conf.rtp_mac[2],
			card->rtp_conf.rtp_mac[3],
			card->rtp_conf.rtp_mac[4],
			card->rtp_conf.rtp_mac[5]);

	card->wandev.rtp_len = (card->rtp_conf.rtp_sample * 8) + sizeof(wan_rtp_pkt_t);

	if ((card->wandev.rtp_dev=wan_dev_get_by_name(card->rtp_conf.rtp_devname)) == NULL){
		DEBUG_EVENT("%s: Failed to open rtp tx device %s\n",
				card->devname,
				card->rtp_conf.rtp_devname);
        	goto aft_rtp_init_exit;
	}

	dev=(netdevice_t*)card->wandev.rtp_dev;

	memcpy(card->rtp_conf.rtp_local_mac,dev->dev_addr,dev->addr_len);
	card->rtp_conf.rtp_local_ip=wan_get_ip_address(card->wandev.rtp_dev,WAN_LOCAL_IP);
	if (card->rtp_conf.rtp_local_ip == 0) {
         	goto aft_rtp_init_exit;
	}

	card->wandev.rtp_tap=&aft_rtp_tap;

	memset(card->wandev.rtp_chan,0,sizeof(card->wandev.rtp_chan));

	return 0;

aft_rtp_init_exit:


	aft_rtp_unconfig(card);

	DEBUG_EVENT("%s: Failed to configure rtp tap!\n",card->devname);

	return -1;

}


__inline void aft_rtp_tap_chan(sdla_t *card, u8 *data, u32 len,
                                    netskb_t **skb_q,
				    u32 *timestamp,
				    u8 call_status,
				    u32 chan)
{
	wan_rtp_pkt_t *pkt;
	u8 *buf;
	netskb_t *skb;
	u32 ts;

	if ((skb=*skb_q) == NULL) {
		*skb_q=wan_skb_alloc(card->wandev.rtp_len+128);
		if (!*skb_q) {
                 	return;
		}
		skb=*skb_q;
		pkt = (wan_rtp_pkt_t*)wan_skb_put(skb,sizeof(wan_rtp_pkt_t));
		memset(pkt,0,sizeof(wan_rtp_pkt_t));
		pkt->rtp_hdr.version=2;
		if (IS_T1_CARD(card)) {
                	pkt->rtp_hdr.pt=0;
		} else {
                        pkt->rtp_hdr.pt=1;
		}
		DEBUG_TEST("%s: RTP(%d) SKB Allocated Len=%d \n",
				card->devname,chan,card->wandev.rtp_len);
	}

	pkt = (wan_rtp_pkt_t*)wan_skb_data(skb);

	if (call_status == WAN_TDM_RTP_CALL_START) {
        	pkt->rtp_hdr.seq=0;
		pkt->rtp_hdr.ts=0;
		*timestamp=0;
	}

	buf=wan_skb_put(skb,len);
	memcpy(buf,data,len);

	ts=htonl(pkt->rtp_hdr.ts);
	ts+=len;
	pkt->rtp_hdr.ts = htonl(ts);

        if (wan_skb_len(skb) >= card->wandev.rtp_len ||
	    call_status==WAN_TDM_RTP_CALL_STOP) {
	        netskb_t *nskb;
		u16 seq;

		pkt->rtp_hdr.ts = *timestamp;

		wan_ip_udp_setup(card,
				 &card->rtp_conf,
				 chan,
				 wan_skb_data(skb),
				 wan_skb_len(skb)-sizeof(wan_rtp_pkt_t));


		nskb=wan_skb_clone(skb);
		if (nskb) {
                	nskb->next = nskb->prev = NULL;
			nskb->dev = card->wandev.rtp_dev;
			nskb->protocol = htons(ETH_P_802_2);
			wan_skb_reset_mac_header(nskb);
			wan_skb_reset_network_header(nskb);

			wan_skb_queue_tail(&card->u.aft.rtp_tap_list,nskb);
			aft_core_taskq_trigger(card,AFT_RTP_TAP_Q);

			DEBUG_TEST("%s: RTP(%d) SKB Tx on dev %s \n",
				card->devname,chan,nskb->dev->name);
		}

		wan_skb_trim(skb,sizeof(wan_rtp_pkt_t));

		pkt = (wan_rtp_pkt_t*)wan_skb_data(skb);

		seq=htons(pkt->rtp_hdr.seq);
		seq++;
		pkt->rtp_hdr.seq=htons(seq);

		*timestamp = htonl(ts);
		DEBUG_TEST("Chan=%d Seq=%d TS=%d\n",chan,seq,ts);
		pkt->rtp_hdr.ts = htonl(ts);
	}
}

void aft_rtp_tap(void *card_ptr, u8 chan, u8* rx, u8* tx, u32 len)
{
	sdla_t *card = (sdla_t *)card_ptr;
	u8 call_status=WAN_TDM_RTP_NO_CHANGE;
	wan_rtp_chan_t *rtp_chan;
	u32 span;

	if (!card || !rx || !tx ) {
		if (WAN_NET_RATELIMIT()) {
			DEBUG_ERROR("%s: Internal Error: rtp tap invalid pointers chan %d\n",
					__FUNCTION__,chan);
		}
		return;
	}

	if (!card->rtp_conf.rtp_ip ||
	    !card->rtp_conf.rtp_sample ||
	    !card->wandev.rtp_len ||
	    !card->wandev.rtp_dev) {
		if (WAN_NET_RATELIMIT()) {
			DEBUG_EVENT("%s: RTP Tap Not configured %d\n",
					card->devname,chan);
		}
         	return;
	}

	if (chan >= 32) {
		if (WAN_NET_RATELIMIT()) {
			DEBUG_ERROR("%s: Internal Error: rtp tap chan out of range %d\n",
					card->devname,chan);
		}
		return;
	}

	span = card->tdmv_conf.span_no-1;
	rtp_chan = &card->wandev.rtp_chan[chan];

	if (wan_test_bit(chan,&card->wandev.rtp_tap_call_map)) {
		if (!wan_test_and_set_bit(chan,&card->wandev.rtp_tap_call_status)) {
        		/* Start of the call */
			call_status=WAN_TDM_RTP_CALL_START;
			DEBUG_TEST("%s: CALL Start on ch %d\n",
					card->devname,chan);
		}
	} else {
		if (!wan_test_bit(chan,&card->wandev.rtp_tap_call_status)) {
			/* Call not up */
			return;
		}
		wan_clear_bit(chan,&card->wandev.rtp_tap_call_status);
		call_status=WAN_TDM_RTP_CALL_STOP;
		DEBUG_TEST("%s: CALL Stop on ch %d\n",card->devname,chan);
	}


	aft_rtp_tap_chan(card, rx, len, &rtp_chan->rx_skb, &rtp_chan->rx_ts,
			 call_status, (span<<4|(chan+1)));
	aft_rtp_tap_chan(card, tx, len, &rtp_chan->tx_skb, &rtp_chan->tx_ts,
			 call_status, (span<<4|(chan+1))+2000);

}
#endif



/**SECTION*************************************************************
 *
 * 	Protocol API Support Functions
 *
 **********************************************************************/


int protocol_init (sdla_t *card, netdevice_t *dev,
		          private_area_t *chan,
			  wanif_conf_t* conf)
{

	chan->common.protocol = conf->protocol;

	DEBUG_TEST("%s: Protocol init 0x%X PPP=0x0%x FR=0x0%X\n",
			wan_netif_name(dev), chan->common.protocol,
			WANCONFIG_PPP,
			WANCONFIG_FR);

#ifndef CONFIG_PRODUCT_WANPIPE_GENERIC
	DEBUG_EVENT("%s: AFT Driver doesn't directly support any protocols!\n",
			chan->if_name);
	return -1;

#else
	if (chan->common.protocol == WANCONFIG_PPP ||
	    chan->common.protocol == WANCONFIG_CHDLC){

		struct ifreq		ifr;
		struct if_settings	ifsettings;

		wanpipe_generic_register(card, dev, wan_netif_name(dev));
		chan->common.prot_ptr = dev;

		if (chan->common.protocol == WANCONFIG_CHDLC){
			DEBUG_EVENT("%s: Starting Kernel CISCO HDLC protocol\n",
					chan->if_name);
			ifsettings.type = IF_PROTO_CISCO;
		}else{
			DEBUG_EVENT("%s: Starting Kernel Sync PPP protocol\n",
					chan->if_name);
			ifsettings.type = IF_PROTO_PPP;

		}
		ifr.ifr_data = (caddr_t)&ifsettings;
		if (wp_lite_set_proto(dev, &ifr)){
			wanpipe_generic_unregister(dev);
			return -EINVAL;
		}

	}else if (chan->common.protocol == WANCONFIG_GENERIC){
		chan->common.prot_ptr = dev;

	}else{
		DEBUG_EVENT("%s:%s: Unsupported protocol %d\n",
				card->devname,chan->if_name,chan->common.protocol);
		return -EPROTONOSUPPORT;
	}
#endif

	return 0;
}


int protocol_stop (sdla_t *card, netdevice_t *dev)
{
	private_area_t *chan=wan_netif_priv(dev);
	int err = 0;

	if (!chan)
		return 0;

	return err;
}

int protocol_shutdown (sdla_t *card, netdevice_t *dev)
{
	private_area_t *chan=wan_netif_priv(dev);

	if (!chan)
		return 0;

#ifndef CONFIG_PRODUCT_WANPIPE_GENERIC

	return 0;
#else

	if (chan->common.protocol == WANCONFIG_PPP ||
	    chan->common.protocol == WANCONFIG_CHDLC){

		chan->common.prot_ptr = NULL;
		wanpipe_generic_unregister(dev);

	}else if (chan->common.protocol == WANCONFIG_GENERIC){
		DEBUG_EVENT("%s:%s Protocol shutdown... \n",
				card->devname, chan->if_name);
	}
#endif
	return 0;
}

void protocol_recv(sdla_t *card, private_area_t *chan, netskb_t *skb)
{

#ifdef CONFIG_PRODUCT_WANPIPE_GENERIC
	if (chan->common.protocol == WANCONFIG_PPP ||
	    chan->common.protocol == WANCONFIG_CHDLC){
		wanpipe_generic_input(chan->common.dev, skb);
		return 0;
	}

#if defined(__LINUX__)
	if (chan->common.protocol == WANCONFIG_GENERIC){
		skb->protocol = htons(ETH_P_HDLC);
		skb->dev = chan->common.dev;
		wan_skb_reset_mac_header(skb);
		netif_rx(skb);
		return 0;
	}
#endif

#endif

#if defined(__LINUX__)

	skb->protocol = htons(ETH_P_IP);
	skb->dev = chan->common.dev;
	wan_skb_reset_mac_header(skb);
	netif_rx(skb);

#elif defined(__FreeBSD__)

	wan_skb_set_csum(skb,0);

	if (wan_iface.input && wan_iface.input(chan->common.dev, skb) != 0){
		WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common);	//++chan->if_stats.rx_dropped;
		wan_skb_free(skb);
		return;
	}

#else
	DEBUG_EVENT("%s: Action not supported (IP)!\n",
				card->devname);
	wan_skb_free(skb);
#endif

	return;
}



#ifdef CONFIG_PRODUCT_WANPIPE_ANNEXG
int bind_annexg(netdevice_t *dev, netdevice_t *annexg_dev)
{
	wan_smp_flag_t smp_flags=0;
	private_area_t* chan = wan_netif_priv(dev);
	sdla_t *card = chan->card;
	if (!chan)
		return -EINVAL;

	if (chan->common.usedby != ANNEXG)
		return -EPROTONOSUPPORT;

	if (chan->annexg_dev)
		return -EBUSY;

	wan_spin_lock_irq(&card->wandev.lock,&smp_flags);
	chan->annexg_dev = annexg_dev;
	wan_spin_unlock_irq(&card->wandev.lock,&smp_flags);
	return 0;
}


netdevice_t * un_bind_annexg(wan_device_t *wandev, netdevice_t *annexg_dev)
{
	struct wan_dev_le	*devle;
	netdevice_t *dev;
	wan_smp_flag_t smp_flags=0;
	sdla_t *card = wandev->priv;

	WAN_LIST_FOREACH(devle, &card->wandev.dev_head, dev_link){
		private_area_t* chan;

		dev = WAN_DEVLE2DEV(devle);
		if (dev == NULL || (chan = wan_netif_priv(dev)) == NULL)
			continue;

		if (!chan->annexg_dev || chan->common.usedby != ANNEXG)
			continue;

		if (chan->annexg_dev == annexg_dev){
			wan_spin_lock_irq(&card->wandev.lock,&smp_flags);
			chan->annexg_dev = NULL;
			wan_spin_unlock_irq(&card->wandev.lock,&smp_flags);
			return dev;
		}
	}
	return NULL;
}


void get_active_inactive(wan_device_t *wandev, netdevice_t *dev,
			       void *wp_stats_ptr)
{
	private_area_t* 	chan = wan_netif_priv(dev);
	wp_stack_stats_t *wp_stats = (wp_stack_stats_t *)wp_stats_ptr;

	if (chan->common.usedby == ANNEXG && chan->annexg_dev){
		if (IS_FUNC_CALL(lapb_protocol,lapb_get_active_inactive)){
			lapb_protocol.lapb_get_active_inactive(chan->annexg_dev,wp_stats);
		}
	}

	if (chan->common.state == WAN_CONNECTED){
		wp_stats->fr_active++;
	}else{
		wp_stats->fr_inactive++;
	}
}

int get_map(wan_device_t *wandev, netdevice_t *dev, struct seq_file* m, int* stop_cnt)
{
	private_area_t*	chan = wan_netif_priv(dev);

	if (!(dev->flags&IFF_UP)){
		return m->count;
	}

	if (chan->common.usedby == ANNEXG && chan->annexg_dev){
		if (IS_FUNC_CALL(lapb_protocol,lapb_get_map)){
			return lapb_protocol.lapb_get_map(chan->annexg_dev,
							 m);
		}
	}

	PROC_ADD_LINE(m,
		"%15s:%s:%c:%s:%c\n",
		chan->label,
		wandev->name,(wandev->state == WAN_CONNECTED) ? '*' : ' ',
		dev->name,(chan->common.state == WAN_CONNECTED) ? '*' : ' ');

	return m->count;
}

#endif


#ifdef AFT_TDM_API_SUPPORT

int aft_tdm_api_init(sdla_t *card, private_area_t *chan, wanif_conf_t *conf)
{
	int err=0;
	int chan_no=0;
	wanpipe_tdm_api_span_t *span;

	if (chan->common.usedby != TDM_VOICE_API	&&
	    chan->common.usedby != TDM_VOICE_DCHAN	&&
		!chan->wp_api_op_mode) {
	    	return 0;
	}

	if (chan->tdmv_zaptel_cfg) {
		return 0;
	}

	if (!card->tdm_api_span) {
		card->tdm_api_span=wan_malloc(sizeof(wanpipe_tdm_api_span_t));
		if (!card->tdm_api_span) {
			return -ENOMEM;
		}
		memset(card->tdm_api_span,0,sizeof(wanpipe_tdm_api_span_t));
	}

	span = (wanpipe_tdm_api_span_t*)card->tdm_api_span;
	
	if (IS_TE1_CARD(card)) {
		if (IS_T1_CARD(card)){
			chan_no=(u8)chan->first_time_slot+1;
		} else {
			chan_no=(u8)chan->first_time_slot;
		}
	} else if (IS_BRI_CARD(card)) {

		if (chan->dchan_time_slot >= 0) {
			chan_no = 3;
		} else {
			chan_no = (u8)(chan->first_time_slot % 2)+1;
		}
	} else if (IS_GSM_CARD(card)) {
		if (chan->dchan_time_slot >= 0) {
			chan_no = MAX_GSM_CHANNELS; /* d-chan is the last one */
		} else {
			/* funky math to calculate the chan number based on port number, -1 to remove the dchan from the tdm slot count */
			chan_no=(u8) (chan->first_time_slot + 1) - ((MAX_GSM_CHANNELS-1) * card->wandev.comm_port);
		}
	} else {
		chan_no=(u8)chan->first_time_slot+1;
	}


	/* hdlc_streaming indicates hw hdlc
	   sw_hdlc_mode indicates sw hdlc */
	if (conf->hdlc_streaming || chan->sw_hdlc_mode) {
		wan_clear_bit(chan_no, &span->timeslot_map);
	} else {
		wan_set_bit(chan_no, &span->timeslot_map);
	}

	DEBUG_TEST("%s: Setting Timeslot Map chan =%i  map=0x%X\n",chan->if_name,chan_no,span->timeslot_map);


	/* chan wp_tdm_api_dev is being initalized here: 
	   DO NOT USE wp_tdm_api_dev ABOVE THIS LINE */
	chan->wp_tdm_api_dev = &span->chans[chan_no];

	/* Initialize the channel for first time use */
	memset(chan->wp_tdm_api_dev,0,sizeof(span->chans[chan_no]));

	/* Initilaize TDM API Parameters */
	chan->wp_tdm_api_dev->chan = chan;
	chan->wp_tdm_api_dev->card = card;
	
	strncpy(chan->wp_tdm_api_dev->name,chan->if_name,WAN_IFNAME_SZ);

	if (conf->hdlc_streaming || chan->sw_hdlc_mode) {
		chan->wp_tdm_api_dev->hdlc_framing=1;
	} else {
		chan->wp_tdm_api_dev->hdlc_framing=0;
	}

	aft_core_tdmapi_event_init(chan);

	chan->wp_tdm_api_dev->cfg.rx_disable = 0;
	chan->wp_tdm_api_dev->cfg.tx_disable = 0;
	chan->wp_tdm_api_dev->cfg.tx_queue_sz = MAX_AFT_DMA_CHAINS;


	chan->wp_tdm_api_dev->tdm_chan = (u8)chan_no;

	if (IS_TE1_CARD(card)) {
		if (IS_T1_CARD(card)){
			chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_MULAW;
		}else{
			chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_ALAW;
		}

		if (IS_E1_CARD(card)){
			chan->wp_tdm_api_dev->active_ch = conf->active_ch;
		}else{
			chan->wp_tdm_api_dev->active_ch = conf->active_ch << 1;
		}

	} else if (IS_BRI_CARD(card)) {

		if (chan->dchan_time_slot >= 0) {
			wan_set_bit(chan->wp_tdm_api_dev->tdm_chan,&chan->wp_tdm_api_dev->active_ch);
		} else {
			wan_set_bit(chan->wp_tdm_api_dev->tdm_chan,&chan->wp_tdm_api_dev->active_ch);
			if (chan->num_of_time_slots == 2) {
				wan_set_bit(chan->wp_tdm_api_dev->tdm_chan+1,&chan->wp_tdm_api_dev->active_ch);
			}
		}

		if (card->fe.fe_cfg.tdmv_law == WAN_TDMV_MULAW){
		       	chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_MULAW;
		} else {
                chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_ALAW;
		}
	} else if (IS_GSM_CARD(card)) {
		DEBUG_EVENT("WANPIPE: IS_GSM_CARD\n");
		/* multiple ports share the same active_ch map, use the port number to decide the proper tdm slot */
		wan_set_bit((chan->wp_tdm_api_dev->tdm_chan + (card->wandev.comm_port * MAX_GSM_CHANNELS)),&chan->wp_tdm_api_dev->active_ch);
				chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_LIN16;
	} else {
		if (card->fe.fe_cfg.tdmv_law == WAN_TDMV_MULAW){
		    	chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_MULAW;
		} else {
            		chan->wp_tdm_api_dev->cfg.hw_tdm_coding=WP_ALAW;
		}
		chan->wp_tdm_api_dev->active_ch = conf->active_ch << 1;
	}

	DEBUG_TDMAPI("%s: TDM API ACTIVE CH 0x%08X  CHAN=%d\n",
		chan->if_name, chan->wp_tdm_api_dev->active_ch,chan->wp_tdm_api_dev->tdm_chan);


	chan->wp_tdm_api_dev->cfg.idle_flag = conf->u.aft.idle_flag;
	chan->wp_tdm_api_dev->cfg.rbs_tx_bits = conf->u.aft.rbs_cas_idle;

	chan->wp_tdm_api_dev->tdm_span = card->tdmv_conf.span_no;

	DEBUG_TDMAPI("%s: TDM API ACTIVE CH 0x%08X SPAN=%d CHAN=%d\n",
		chan->if_name, chan->wp_tdm_api_dev->active_ch,
		chan->wp_tdm_api_dev->tdm_span,chan->wp_tdm_api_dev->tdm_chan);

	chan->wp_tdm_api_dev->dtmfsupport = card->u.aft.tdmv_hw_tone;
	
	DEBUG_TDMAPI("%s: SPAN=%d, CHAN=%d Chunk=%d Period=%d Mtu=%d\n", chan->if_name,
		chan->wp_tdm_api_dev->tdm_span, chan->wp_tdm_api_dev->tdm_chan,  chan->tdm_api_chunk, chan->tdm_api_period, chan->mtu);

	DEBUG_TDMAPI("%s: conf->mtu=%d\n", chan->if_name, conf->mtu);

	DEBUG_TDMAPI("%s: tdm_api_chunk=%d, tdm_api_period=%d\n", chan->if_name,
		chan->tdm_api_chunk, chan->tdm_api_period);

	chan->wp_tdm_api_dev->operation_mode=chan->wp_api_op_mode;

	if (chan->wp_api_op_mode == WP_TDM_OPMODE_CHAN) {
		chan->wp_tdm_api_dev->operation_mode	= WP_TDM_OPMODE_CHAN;
		chan->wp_tdm_api_dev->cfg.hw_mtu_mru = 8;
		chan->wp_tdm_api_dev->cfg.usr_period = chan->tdm_api_period;
		chan->wp_tdm_api_dev->cfg.usr_mtu_mru = chan->tdm_api_chunk;
		if (chan->wp_tdm_api_dev->cfg.usr_mtu_mru < 160) {
			chan->tdm_api_period=20;
			chan->wp_tdm_api_dev->cfg.usr_period=20;
			chan->tdm_api_chunk=160;
         	chan->wp_tdm_api_dev->cfg.usr_mtu_mru=160;
		}
	} else {
		chan->wp_tdm_api_dev->operation_mode	= WP_TDM_OPMODE_SPAN;
		chan->wp_tdm_api_dev->cfg.hw_mtu_mru = 8;
		chan->wp_tdm_api_dev->cfg.usr_period = chan->tdm_api_period;
		chan->wp_tdm_api_dev->cfg.usr_mtu_mru = chan->mtu;

		/* Overwite the chan number based on group number */	
		chan->wp_tdm_api_dev->tdm_chan = (u8)chan->first_time_slot;
	}

	switch(chan->wp_tdm_api_dev->cfg.hw_tdm_coding)	{
		case WP_LIN16:
			chan->wp_tdm_api_dev->cfg.hw_mtu_mru = 16;
			break;
		default:
				chan->wp_tdm_api_dev->cfg.hw_mtu_mru = 8;
			break;
	};

	if (chan->usedby_cfg == MTP1_API) {
		chan->wp_tdm_api_dev->operation_mode = 	WP_TDM_OPMODE_MTP1;
	}
	
	/* Set the API interface mode.  Used on windows to
	 * indicate legacy API interface */
	chan->wp_tdm_api_dev->api_mode = chan->wp_api_iface_mode;

	if (chan->if_cnt == 1) {
		DEBUG_EVENT("%s:    Memory: TDM API %d \n",
				card->devname, sizeof(wanpipe_tdm_api_dev_t));
	}

	DEBUG_TDMAPI("%s: Chunk=%i, Period=%i, MTU=%i\n",
				 card->devname,chan->tdm_api_chunk,
				 chan->tdm_api_period,chan->wp_tdm_api_dev->cfg.usr_mtu_mru);

	err=wanpipe_tdm_api_reg(chan->wp_tdm_api_dev);
	if (err){
		if (span->usage == 0) {
			wan_free(span);
			card->tdm_api_span=NULL;
		}

		return err;
	}

	span->usage++;
	wan_set_bit(0,&chan->wp_tdm_api_dev->init);
	return err;
}

#endif

int aft_tdm_api_free(sdla_t *card, private_area_t *chan)
{
#ifdef AFT_TDM_API_SUPPORT
	int err=0;
	wanpipe_tdm_api_span_t *span;

	span = (wanpipe_tdm_api_span_t*)card->tdm_api_span;


	if (!span) {
		return 0;
	}

	if (chan->wp_tdm_api_dev && wan_test_bit(0,&chan->wp_tdm_api_dev->init)){
		wan_clear_bit(0,&chan->wp_tdm_api_dev->init);
		wan_clear_bit(chan->wp_tdm_api_dev->tdm_chan, &span->timeslot_map);

		err=wanpipe_tdm_api_unreg(chan->wp_tdm_api_dev);
		if (err){
			wan_set_bit(0,&chan->wp_tdm_api_dev->init);
			return err;
		}
		chan->wp_tdm_api_dev=NULL;

		span->usage--;
		if (span->usage < 0) {
			span->usage = 0;
		}

		if (span->usage == 0) {
			wan_free(span);
			card->tdm_api_span=NULL;
		}

	}
#endif
	return 0;
}




int aft_sw_hdlc_rx_data (void *priv_ptr, u8 *rx_data, int rx_len, uint32_t err_code)
{
	private_area_t *chan = (private_area_t*)priv_ptr;
	sdla_t *card=chan->card;
	netskb_t *skb;
	u8 *data;
	int err=0;

	//DEBUG_EVENT("%s: RX DATA Len=%i\n",chan->if_name,rx_len);
	if (card->u.aft.cfg.rx_crc_bytes == 0) {
		rx_len-=2;
	}
	

	/* FIXME: This will not work on WINDOWS */
	skb=wan_skb_alloc(rx_len+sizeof(wp_api_hdr_t));
	if (!skb) {
		WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common); /* ++chan->if_stats.rx_dropped; */
		WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_dropped);
		return -1;
	}

	data=wan_skb_put(skb,rx_len);
	memcpy(data,rx_data,rx_len);
	
	err=aft_bh_rx(chan, skb, (u8)err_code, rx_len);
	if (err) {
		/* FIXME: This will not work on WINDOWS */
		wan_skb_free(skb);
	}

	if (wp_mtp1_poll_check(chan->sw_hdlc_dev)) {
		WAN_TASKLET_SCHEDULE((&chan->common.bh_task));
	}
	
	return err;
}

int aft_sw_hdlc_rx_reject (void *priv_ptr, char *reason)
{
	/* Do nothing */
#if 0
	private_area_t *chan = (private_area_t*)priv_ptr;
	WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_crc_errors);
#endif
	return 0;
	
}

int aft_sw_hdlc_rx_suerm (void *priv_ptr)
{
	/* Pass up suerm event */
#if 0
	private_area_t *chan = (private_area_t*)priv_ptr;
	WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_crc_errors);
#endif
	return 0;
}

int aft_sw_hdlc_wakup(void *priv_ptr)
{
	private_area_t *chan = (private_area_t*)priv_ptr;
	wanpipe_wake_stack(chan);
	return 0;
}

int aft_sw_hdlc_trace(void *priv_ptr, u8 *data, int len, int dir)
{
	private_area_t *chan = (private_area_t*)priv_ptr;
	private_area_t *top_chan;
	int err;
	int channel=IS_E1_CARD(chan->card) ? chan->first_time_slot : chan->first_time_slot+1;
	
	top_chan=chan;
	if (chan->channelized_cfg) {
		top_chan=wan_netif_priv(chan->common.dev);
	} 
	
	if (dir) {
		err=wan_capture_trace_packet_buffer(chan->card, &top_chan->trace_info, data, len ,TRC_INCOMING_FRM, channel);
	} else {
		err=wan_capture_trace_packet_buffer(chan->card, &top_chan->trace_info, data, len ,TRC_OUTGOING_FRM, channel);
	}
	if (err) {
		WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common);	
	}

	return err;
}

