patch-2.4.4 linux/drivers/net/wan/sdla_fr.c

Next file: linux/drivers/net/wan/sdla_ft1.c
Previous file: linux/drivers/net/wan/sdla_chdlc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/net/wan/sdla_fr.c linux/drivers/net/wan/sdla_fr.c
@@ -4,14 +4,43 @@
 * Author(s):	Nenad Corbic  <ncorbic@sangoma.com>
 *		Gideon Hack
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2001 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.
 * ============================================================================
-* Feb 28, 2000  Jeff Garzik	o softnet updates
+* Nov 23, 2000  Nenad Corbic    o Added support for 2.4.X kernels
+* Nov 15, 2000  David Rokavarg  
+*               Nenad Corbic	o Added frame relay bridging support.
+* 				  Original code from Mark Wells and Kristian Hoffmann has
+* 				  been integrated into the frame relay driver.
+* Nov 13, 2000  Nenad Corbic    o Added true interface type encoding option.
+* 				  Tcpdump doesn't support Frame Relay inteface
+* 				  types, to fix this true type option will set
+* 				  the interface type to RAW IP mode.
+* Nov 07, 2000  Nenad Corbic	o Added security features for UDP debugging:
+*                                 Deny all and specify allowed requests.
+* Nov 06, 2000  Nenad Corbic	o Wanpipe interfaces conform to raw packet interfaces.  
+*                                 Moved the if_header into the if_send() routine.
+*                                 The if_header() was breaking the libpcap 
+*                                 support. i.e. support for tcpdump, ethereal ...
+* Oct 12. 2000  Nenad Corbic    o Added error message in fr_configure
+* Jul 31, 2000  Nenad Corbic	o Fixed the Router UP Time.
+* Apr 28, 2000  Nenad Corbic	o Added the option to shutdown an interface
+*                                 when the channel gets disconnected.
+* Apr 28, 2000  Nenad Corbic 	o Added M.Grants patch: disallow duplicate
+*                                 interface setups. 
+* Apr 25, 2000  Nenad Corbic	o Added M.Grants patch: dynamically add/remove 
+*                                 new dlcis/interfaces.
+* Mar 23, 2000  Nenad Corbic 	o Improved task queue, bh handling.
+* Mar 16, 2000	Nenad Corbic	o Added Inverse ARP support
+* Mar 13, 2000  Nenad Corbic	o Added new socket API support.
+* Mar 06, 2000  Nenad Corbic	o Bug Fix: corrupted mbox recovery.
+* Feb 24, 2000  Nenad Corbic    o Fixed up FT1 UDP debugging problem.
+* Dev 15, 1999  Nenad Corbic    o Fixed up header files for 2.0.X kernels
+*
 * Nov 08, 1999  Nenad Corbic    o Combined all debug UDP calls into one function
 *                               o Removed the ARP support. This has to be done
 *                                 in the next version.
@@ -109,12 +138,12 @@
 * Jan 02, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
-#include <linux/config.h>
+#include <linux/version.h>
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
@@ -123,14 +152,29 @@
 #include <linux/time.h>	 	/* for do_gettimeofday */	
 #include <linux/in.h>		/* sockaddr_in */
 #include <linux/inet.h>		/* in_ntoa(), etc... */
-#include <asm/uaccess.h>
-#include <linux/inetdevice.h>
+#include <asm/errno.h>
+
 #include <linux/ip.h>
-#include <net/route.h>          /* Dynamic Route Creation */
 #include <linux/if.h>
 
-#include <linux/sdla_fr.h>	/* frame relay firmware API definitions */
- 
+#include <linux/if_wanpipe_common.h>	/* Wanpipe Socket */
+#include <linux/if_wanpipe.h>	
+
+#include <linux/sdla_fr.h>		/* frame relay firmware API definitions */
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ #include <asm/uaccess.h>
+ #include <linux/inetdevice.h>
+ #include <linux/netdevice.h>
+
+#else
+ #include <asm/segment.h>
+#endif
+
+#include <net/route.h>          	/* Dynamic Route Creation */
+#include <linux/etherdevice.h>		/* eth_type_trans() used for bridging */
+#include <linux/random.h>
+
 /****** Defines & Macros ****************************************************/
 
 #define	MAX_CMD_RETRY	10		/* max number of firmware retries */
@@ -151,25 +195,20 @@
 #define CIR_ENABLED	0x00
 #define CIR_DISABLED	0x01
 
-#define WANPIPE 0x00
-#define API     0x01
 #define FRAME_RELAY_API 1
-
-#define TX_TIMEOUT	(5*HZ)
+#define MAX_BH_BUFF	10
 
 /* For handle_IPXWAN() */
 #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
  
 /****** Data Structures *****************************************************/
 
-/* This is an extention of the 'struct net_device' we create for each network
+/* This is an extention of the 'struct device' we create for each network
  * interface to keep the rest of channel-specific data.
  */
 typedef struct fr_channel
 {
-	/* This member must be first. */
-	struct net_device *slave;	/* WAN slave */
-
+	wanpipe_common_t common;
 	char name[WAN_IFNAME_SZ+1];	/* interface name, ASCIIZ */
 	unsigned dlci_configured  ;	/* check whether configured or not */
 	unsigned cir_status;		/* check whether CIR enabled or not */
@@ -183,7 +222,6 @@
 	unsigned long router_start_time;/* Router start time in seconds */
 	unsigned long tick_counter;	/* counter for transmit time out */
 	char dev_pending_devtint;	/* interface pending dev_tint() */
-	char state;			/* channel state */
 	void *dlci_int_interface;	/* pointer to the DLCI Interface */ 
 	unsigned long IB_addr;		/* physical address of Interface Byte */
 	unsigned long state_tick;	/* time of the last state change */
@@ -192,19 +230,52 @@
 	sdla_t *card;			/* -> owner */
 	unsigned route_flag;		/* Add/Rem dest addr in route tables */
 	unsigned inarp;			/* Inverse Arp Request status */ 
+	unsigned char inarp_ready;	/* Ready to send requests */
 	int inarp_interval;		/* Time between InArp Requests */
 	unsigned long inarp_tick;	/* InArp jiffies tick counter */
+	unsigned char interface_down;	/* Bring interface down on disconnect */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct net_device_stats ifstats;	/* interface statistics */
+      #else
+	struct enet_statistics ifstats;
+      #endif	
 	if_send_stat_t drvstats_if_send;
         rx_intr_stat_t drvstats_rx_intr;
         pipe_mgmt_stat_t drvstats_gen;
-
-	unsigned char usedby;  /* Used by WANPIPE or API */
-
 	unsigned long router_up_time;
 
 	unsigned short transmit_length;
-	char transmit_buffer[FR_MAX_NO_DATA_BYTES_IN_FRAME];
+	struct sk_buff *delay_skb;
+
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	bh_data_t *bh_head;	  	  /* Circular buffer for chdlc_bh */
+	unsigned long  tq_working;
+	volatile int  bh_write;
+	volatile int  bh_read;
+	atomic_t  bh_buff_used;
+      #endif
+
+	/* Polling task queue. Each interface
+         * has its own task queue, which is used
+         * to defer events from the interrupt */
+	struct tq_struct fr_poll_task;
+	struct timer_list fr_arp_timer;
+
+	u32 ip_local;
+	u32 ip_remote;
+	u8  config_dlci;
+	u32 unconfig_dlci;
+
+	/* Whether this interface should be setup as a gateway.
+	 * Used by dynamic route setup code */
+	u8  gateway;
+
+	/* True interface type */
+	u8 true_if_encoding;
+	u8 fr_header[FR_HEADER_LEN];
+	char fr_header_len;
+
 } fr_channel_t;
 
 /* Route Flag options */
@@ -212,6 +283,7 @@
 #define ADD_ROUTE 	0x01
 #define ROUTE_ADDED	0x02
 #define REMOVE_ROUTE 	0x03
+#define ARP_REQ		0x04
 
 /* inarp options */
 #define INARP_NONE		0x00
@@ -221,6 +293,10 @@
 /* reasons for enabling the timer interrupt on the adapter */
 #define TMR_INT_ENABLED_UDP   	0x01
 #define TMR_INT_ENABLED_UPDATE 	0x02
+#define TMR_INT_ENABLED_ARP	0x04
+#define TMR_INT_ENABLED_UPDATE_STATE 	0x08
+#define TMR_INT_ENABLED_CONFIG	0x10
+#define TMR_INT_ENABLED_UNCONFIG	0x20
 
 
 typedef struct dlci_status
@@ -248,7 +324,6 @@
 /* variable for keeping track of enabling/disabling FT1 monitor status */
 static int rCount = 0;
 
-extern int ip_rt_ioctl(unsigned int, void *);
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
@@ -256,27 +331,43 @@
  * interrupt test routine 
  */
 static int Intr_test_counter;
+
 /****** Function Prototypes *************************************************/
 
 /* WAN link driver entry points. These are called by the WAN router module. */
 static int update(wan_device_t *wandev);
-static int new_if(wan_device_t *wandev, struct net_device *dev, wanif_conf_t *conf);
-static int del_if(wan_device_t *wandev, struct net_device *dev);
+static int new_if(wan_device_t *wandev, netdevice_t *dev, wanif_conf_t *conf);
+static int del_if(wan_device_t *wandev, netdevice_t *dev);
+static void disable_comm (sdla_t *card);
 
 /* WANPIPE-specific entry points */
 static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data);
 
 /* Network device interface */
-static int if_init(struct net_device *dev);
-static int if_open(struct net_device *dev);
-static int if_close(struct net_device *dev);
-static int if_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
-static int if_rebuild_hdr(struct sk_buff *skb);
-static int if_send(struct sk_buff *skb, struct net_device *dev);
-static void if_tx_timeout (struct net_device *dev);
-static int chk_bcast_mcast_addr(sdla_t *card, struct net_device* dev,
+static int if_init(netdevice_t *dev);
+static int if_open(netdevice_t *dev);
+static int if_close(netdevice_t *dev);
+
+
+#ifdef LINUX_2_4
+static void if_tx_timeout (netdevice_t *dev);
+#endif
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static int if_rebuild_hdr (struct sk_buff *skb);
+#else
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+        struct sk_buff* skb);
+#endif
+
+static int if_send(struct sk_buff *skb, netdevice_t *dev);
+static int chk_bcast_mcast_addr(sdla_t *card, netdevice_t* dev,
                                 struct sk_buff *skb);
-static struct net_device_stats *if_stats(struct net_device *dev);
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats *if_stats(netdevice_t *dev);
+#else
+static struct enet_statistics* if_stats (netdevice_t* dev);
+#endif
 
 /* Interrupt handlers */
 static void fr_isr(sdla_t *card);
@@ -292,7 +383,7 @@
 static int fr_init_dlci (sdla_t *card, fr_channel_t *chan);
 static int fr_set_intr_mode (sdla_t *card, unsigned mode, unsigned mtu, unsigned short timeout);
 static int fr_comm_enable(sdla_t *card);
-static int fr_comm_disable(sdla_t *card);
+static void fr_comm_disable(sdla_t *card);
 static int fr_get_err_stats(sdla_t *card);
 static int fr_get_stats(sdla_t *card);
 static int fr_add_dlci(sdla_t *card, int dlci);
@@ -301,6 +392,13 @@
 static int fr_issue_isf(sdla_t *card, int isf);
 static int fr_send(sdla_t *card, int dlci, unsigned char attr, int len,
 	void *buf);
+static int fr_send_data_header(sdla_t *card, int dlci, unsigned char attr, int len,
+	void *buf,unsigned char hdr_len);
+static unsigned int fr_send_hdr(sdla_t *card, int dlci, unsigned int offset);
+
+static int check_dlci_config (sdla_t *card, fr_channel_t *chan);
+static void initialize_rx_tx_buffers (sdla_t *card);
+
 
 /* Firmware asynchronous event handlers */
 static int fr_event(sdla_t *card, int event, fr_mbox_t *mbox);
@@ -308,9 +406,9 @@
 static int fr_dlci_change(sdla_t *card, fr_mbox_t *mbox);
 
 /* Miscellaneous functions */
-static int update_chan_state(struct net_device *dev);
-static void set_chan_state(struct net_device *dev, int state);
-static struct net_device *find_channel(sdla_t *card, unsigned dlci);
+static int update_chan_state(netdevice_t *dev);
+static void set_chan_state(netdevice_t *dev, int state);
+static netdevice_t *find_channel(sdla_t *card, unsigned dlci);
 static int is_tx_ready(sdla_t *card, fr_channel_t *chan);
 static unsigned int dec_to_uint(unsigned char *str, int len);
 static int reply_udp( unsigned char *data, unsigned int mbox_len );
@@ -319,14 +417,38 @@
 static void init_chan_statistics( fr_channel_t* chan );
 static void init_global_statistics( sdla_t* card );
 static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan );
-static void setup_for_delayed_transmit(struct net_device* dev, void* buf,
-	unsigned len);
+static int setup_for_delayed_transmit(netdevice_t* dev, struct sk_buff *skb);
+
+netdevice_t * move_dev_to_next (sdla_t *, netdevice_t *);
+static int check_tx_status(sdla_t *, netdevice_t *);
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+/* Frame Relay Socket API */
+static void trigger_fr_bh (fr_channel_t *);
+static void fr_bh (netdevice_t *);
+static int fr_bh_cleanup (netdevice_t *);
+static int bh_enqueue (netdevice_t *, struct sk_buff *);
+#endif
+
+static void trigger_fr_poll (netdevice_t *);
+static void fr_poll (netdevice_t *);
+//static void add_gateway (netdevice_t *);
+
+static void trigger_unconfig_fr (netdevice_t *dev);
+static void unconfig_fr (sdla_t *);
+
+static void trigger_config_fr (sdla_t *);
+static void config_fr (sdla_t *);
 
 
 /* Inverse ARP and Dynamic routing functions */
-int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, struct net_device *dev);
+int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, netdevice_t *dev);
 int is_arp(void *buf);
-int send_inarp_request(sdla_t *card, struct net_device *dev);
+int send_inarp_request(sdla_t *card, netdevice_t *dev);
+
+static void trigger_fr_arp (netdevice_t *);
+static void fr_arp (unsigned long data);
+
 
 /* Udp management functions */
 static int process_udp_mgmt_pkt(sdla_t *card);
@@ -346,6 +468,7 @@
 void 	s508_s514_lock(sdla_t *card, unsigned long *smp_flags);
 
 unsigned short calc_checksum (char *, int);
+static int setup_fr_header(struct sk_buff** skb, netdevice_t* dev, char op_mode);
 
 
 /****** Public Functions ****************************************************/
@@ -366,15 +489,20 @@
 {
 
 	int err;
+	fr508_flags_t* flags;
 
 	union
 	{
 		char str[80];
 		fr_conf_t cfg;
 	} u;
+
 	fr_buf_info_t* buf_info;
 	int i;
 
+
+	printk(KERN_INFO "\n");
+
 	/* Verify configuration ID */
 	if (conf->config_id != WANCONFIG_FR) {
 		
@@ -403,6 +531,8 @@
 			return -EINVAL;
 	}
 
+	flags = card->flags;
+
 	/* Read firmware version.  Note that when adapter initializes, it
 	 * clears the mailbox, so it may appear that the first command was
 	 * executed successfully when in fact it was merely erased. To work
@@ -429,7 +559,7 @@
 	memset(card->u.f.dlci_to_dev_map, 0, sizeof(card->u.f.dlci_to_dev_map));
  	
 	/* Configure adapter firmware */
-	
+
 	u.cfg.mtu	= conf->mtu;
 	u.cfg.kbps	= conf->bps / 1000;
 
@@ -546,9 +676,7 @@
                         (void*)(buf_info->rse_base +
                         (buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) +
                         card->hw.dpmbase);
-	}
-
-	else {	
+	}else{	
 		buf_info = (void*)(card->hw.dpmbase + FR508_RXBC_OFFS);
 
 		card->rxmb = (void*)(buf_info->rse_next -
@@ -582,6 +710,8 @@
 	card->wandev.state	= WAN_DISCONNECTED;
 	card->wandev.ttl	= conf->ttl;
         card->wandev.udp_port 	= conf->udp_port;       
+	card->disable_comm	= &disable_comm;	
+	card->u.f.arp_dev 	= NULL;
 
 	/* Intialize global statistics for a card */
 	init_global_statistics( card );
@@ -593,11 +723,13 @@
 	card->intr_mode = INTR_TEST_MODE;
 	err = intr_test( card );
 
+	printk(KERN_INFO "%s: End of Interrupt Test rc=0x%x  count=%i\n",
+			card->devname,err,Intr_test_counter); 
+	
 	if (err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) {
-		printk( 
-			"%s: Interrupt Test Failed, Counter: %i\n", 
+		printk(KERN_ERR "%s: Interrupt Test Failed, Counter: %i\n", 
 			card->devname, Intr_test_counter);
-		printk( "Please choose another interrupt\n");
+		printk(KERN_ERR "Please choose another interrupt\n");
 		err = -EIO;
 		return err;
 	}
@@ -606,6 +738,32 @@
 			card->devname, Intr_test_counter);
 
 
+	/* Apr 28 2000. Nenad Corbic
+	 * Enable commnunications here, not in if_open or new_if, since
+         * interfaces come down when the link is disconnected. 
+         */
+	 
+	/* If you enable comms and then set ints, you get a Tx int as you
+	 * perform the SET_INT_TRIGGERS command. So, we only set int
+	 * triggers and then adjust the interrupt mask (to disable Tx ints)
+	 * before enabling comms. 
+	 */	
+        if (fr_set_intr_mode(card, (FR_INTR_RXRDY | FR_INTR_TXRDY |
+		FR_INTR_DLC | FR_INTR_TIMER | FR_INTR_TX_MULT_DLCIs) ,
+		card->wandev.mtu, 0)) {
+		return -EIO;
+	}
+
+	flags->imask &= ~(FR_INTR_TXRDY | FR_INTR_TIMER);
+ 
+	if (fr_comm_enable(card)) {
+		return -EIO;
+	}	
+	wanpipe_set_state(card, WAN_CONNECTED);
+	spin_lock_init(&card->u.f.if_send_lock);
+	
+	printk(KERN_INFO "\n");
+
         return 0;
 }
 
@@ -627,9 +785,6 @@
 	if (wandev->state == WAN_UNCONFIGURED)
 		return -ENODEV;
 
-	if (test_bit(1, (void*)&wandev->critical))
-		return -EAGAIN;
-
 	card = wandev->private;
 	flags = card->flags;
 
@@ -646,6 +801,7 @@
  			return -EAGAIN;
 		}
         }
+
 	return 0;
 }
 
@@ -661,16 +817,17 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
-static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* conf)
+static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf)
 {
 	sdla_t* card = wandev->private;
 	fr_channel_t* chan;
 	int dlci = 0;
 	int err = 0;
+
 	
 	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
 		
-		printk(KERN_INFO "%s: invalid interface name!\n",
+		printk(KERN_INFO "%s: Invalid interface name!\n",
 			card->devname);
 		return -EINVAL;
 	}
@@ -684,7 +841,7 @@
 	memset(chan, 0, sizeof(fr_channel_t));
 	strcpy(chan->name, conf->name);
 	chan->card = card;
- 
+
 	/* verify media address */
 	if (is_digit(conf->addr[0])) {
 
@@ -697,36 +854,89 @@
 		} else {
 		
 			printk(KERN_ERR
-				"%s: invalid DLCI %u on interface %s!\n",
+				"%s: Invalid DLCI %u on interface %s!\n",
 				wandev->name, dlci, chan->name);
 			err = -EINVAL;
 		}
 
 	} else {
 		printk(KERN_ERR
-			"%s: invalid media address on interface %s!\n",
+			"%s: Invalid media address on interface %s!\n",
 			wandev->name, chan->name);
 		err = -EINVAL;
 	}
 
-        /* Setup wanpipe as a router (WANPIPE) or as an API */
-        if(strcmp(conf->usedby, "WANPIPE") == 0){
-                printk(KERN_INFO "%s: Running in WANPIPE mode %s\n",
-			wandev->name, chan->name);
-                chan->usedby = WANPIPE;
+	if ((chan->true_if_encoding = conf->true_if_encoding) == WANOPT_YES){
+		printk(KERN_INFO 
+			"%s: Enabling, true interface type encoding.\n",
+			card->devname);
+	}
+	
 
-        } else if(strcmp(conf->usedby, "API") == 0){
 
-#ifdef FRAME_RELAY_API
-                chan->usedby = API;
-                printk(KERN_INFO "%s: Running in API mode %s\n",
-			wandev->name, chan->name);
+    /* Setup wanpipe as a router (WANPIPE) even if it is
+	 * a bridged DLCI, or as an API 
+	 */
+        if (strcmp(conf->usedby, "WANPIPE")  == 0  || 
+	    strcmp(conf->usedby, "BRIDGE")   == 0  ||
+	    strcmp(conf->usedby, "BRIDGE_N") == 0){
+		
+		if(strcmp(conf->usedby, "WANPIPE") == 0){
+			chan->common.usedby = WANPIPE;
+			
+	                printk(KERN_INFO "%s: Running in WANPIPE mode.\n", 
+					card->devname);
+			
+		}else if(strcmp(conf->usedby, "BRIDGE") == 0){
+			
+			chan->common.usedby = BRIDGE;
+			
+#if defined(LINUX_2_1) || defined(LINUX_2_4) 
+			printk(KERN_INFO "%s: Running in WANPIPE (BRIDGE) mode.\n", 
+					card->devname);
+#else
+			printk(KERN_INFO "%s: WANPIPE Bridging mode not supported in 2.0.X kernels.\n",
+					card->devname);
+			err = -EPROTONOSUPPORT;
+#endif
+		}else if( strcmp(conf->usedby, "BRIDGE_N") == 0 ){
+			
+			chan->common.usedby = BRIDGE_NODE;
+		
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+			printk(KERN_INFO "%s: Running in WANPIPE (BRIDGE_NODE) mode.\n", 
+					card->devname);
 #else
-                printk(KERN_INFO "%s: API Mode is not supported !\n",
+			printk(KERN_INFO "%s: WANPIPE Bridging mode not supported in 2.0.X kernels.\n",
+					card->devname);
+			err = -EPROTONOSUPPORT;
+#endif
+		}
+
+		if (!err){
+			/* Dynamic interface configuration option.
+			 * On disconnect, if the options is selected,
+			 * the interface will be brought down */
+			if (conf->if_down == WANOPT_YES){ 
+				set_bit(DYN_OPT_ON,&chan->interface_down);
+				printk(KERN_INFO 
+				    "%s: Dynamic interface configuration enabled.\n",
+					card->devname);
+			}
+		}
+
+        } else if(strcmp(conf->usedby, "API") == 0){
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4) 
+                chan->common.usedby = API;
+                printk(KERN_INFO "%s: Running in API mode.\n",
 			wandev->name);
-                printk(KERN_INFO
-			"%s: API patch can be obtained from Sangoma Tech.\n",
-                        	wandev->name);
+#else
+                printk(KERN_INFO "%s: The API Mode is not supported for"
+				 "kernels lower than 2.2.X !\n",
+					wandev->name);
+		printk(KERN_INFO "%s: Please upgrade to a 2.2.X kernel for the API support\n",
+					wandev->name);
                 err = -EINVAL;
 #endif
         }
@@ -737,8 +947,6 @@
 		return err;
 	}
 
-	card->u.f.dlci_to_dev_map[dlci] = dev;
- 
 	/* place cir,be,bc and other channel specific information into the
 	 * chan structure 
          */
@@ -774,20 +982,24 @@
 
 	chan->mc = conf->mc;
 
-	/* FIXME: ARP is not supported by this frame relay verson */
 	if (conf->inarp == WANOPT_YES){
-		printk(KERN_INFO "%s: ERROR - This version of WANPIPE doesn't support ARPs\n",
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+		printk(KERN_INFO "%s: Inverse ARP Support Enabled\n",card->devname);
+		chan->inarp = conf->inarp ? INARP_REQUEST : INARP_NONE;
+		chan->inarp_interval = conf->inarp_interval ? conf->inarp_interval : 10;
+#else
+		printk(KERN_INFO "%s: Warning, Inverse ARP Support not available for 2.0.X kernels!\n",
 				card->devname);
-	
-		//chan->inarp = conf->inarp ? INARP_REQUEST : INARP_NONE;
-		//chan->inarp_interval = conf->inarp_interval ? conf->inarp_interval : 10;
-		kfree(chan);
-		return -EINVAL;
+		chan->inarp = INARP_NONE;
+		chan->inarp_interval = 10;
+#endif
 	}else{
+		printk(KERN_INFO "%s: Inverse ARP Support Disabled\n",card->devname);
 		chan->inarp = INARP_NONE;
 		chan->inarp_interval = 10;
 	}
 
+
 	chan->dlci_configured = DLCI_NOT_CONFIGURED;	
 
 
@@ -814,64 +1026,107 @@
 	chan->transmit_length = 0;
 
 	/* prepare network device data space for registration */
-	strcpy(dev->name, chan->name);
+#ifdef LINUX_2_4
+	strcpy(dev->name,chan->name);
+#else
+	dev->name = (char *)kmalloc(strlen(chan->name) + 2, GFP_KERNEL); 
+	if(dev->name == NULL)
+	{
+		kfree(chan);
+		return -ENOMEM;
+	}
+	sprintf(dev->name, "%s", chan->name);
+#endif
+	
 	dev->init = &if_init;
 	dev->priv = chan;
 
-
-	/* Enable Interrupts and Communications */
-	if (!wandev->new_if_cnt){
-		fr508_flags_t* flags = card->flags;
-
-		wandev->new_if_cnt++;
-
-		/* 
-		If you enable comms and then set ints, you get a Tx int as you
-		perform the SET_INT_TRIGGERS command. So, we only set int
-		triggers and then adjust the interrupt mask (to disable Tx ints)
-		before enabling comms. 
-		*/	
-                if (fr_set_intr_mode(card, (FR_INTR_RXRDY | FR_INTR_TXRDY |
-			FR_INTR_DLC | FR_INTR_TIMER | FR_INTR_TX_MULT_DLCIs) ,
-			card->wandev.mtu, 0)) {
-			kfree(chan);
-			return -EIO;
-		}
-
-		flags->imask &= ~(FR_INTR_TXRDY | FR_INTR_TIMER);
- 
-		if (fr_comm_enable(card)) {
-			kfree(chan);
-			return -EIO;
-		}	
-		wanpipe_set_state(card, WAN_CONNECTED);
+	/* Initialize FR Polling Task Queue
+         * We need a poll routine for each network
+         * interface. 
+         */
+#ifndef LINUX_2_4
+	chan->fr_poll_task.next = NULL;
+#endif
+	chan->fr_poll_task.sync = 0;
+	chan->fr_poll_task.routine = (void *)(void *)fr_poll;
+	chan->fr_poll_task.data = dev;
+
+	init_timer(&chan->fr_arp_timer);
+	chan->fr_arp_timer.data=(unsigned long)dev;
+	chan->fr_arp_timer.function = fr_arp;
+
+	wandev->new_if_cnt++;
+
+	/* Tells us that if this interface is a
+         * gateway or not */
+	if ((chan->gateway = conf->gateway) == WANOPT_YES){
+		printk(KERN_INFO "%s: Interface %s is set as a gateway.\n",
+			card->devname,dev->name);
+	}
+
+	/* M. Grant Patch Apr 28 2000 
+         * Disallow duplicate dlci configurations. */
+	if (card->u.f.dlci_to_dev_map[chan->dlci] != NULL) {
+		kfree(chan);
+		return -EBUSY;
 	}
 
+	/* Configure this dlci at a later date, when
+         * the interface comes up. i.e. when if_open() 
+         * executes */
+	set_bit(0,&chan->config_dlci);
+	
+	printk(KERN_INFO "\n");
+
 	return 0;
 }
 
 /*============================================================================
  * Delete logical channel.
  */
-static int del_if (wan_device_t* wandev, struct net_device* dev)
+static int del_if (wan_device_t* wandev, netdevice_t* dev)
 {
-	sdla_t *card = wandev->private;
+	fr_channel_t* chan = dev->priv;
+	unsigned long smp_flags=0;
 
-	/* Execute shutdown very first time we enter del_if */
+	/* This interface is dead, make sure the 
+	 * ARP timer is stopped */
+	del_timer(&chan->fr_arp_timer);
+	
+	/* If we are a NODE, we must unconfigure this DLCI
+	 * Trigger an unconfigure command that will
+	 * be executed in timer interrupt. We must wait
+	 * for the command to complete. */
+	trigger_unconfig_fr(dev);
+
+	lock_adapter_irq(&wandev->lock, &smp_flags);
+	wandev->new_if_cnt--;
+	unlock_adapter_irq(&wandev->lock, &smp_flags);
 
-	if (!wandev->del_if_cnt) {
-		wandev->del_if_cnt++;
-		wanpipe_set_state(card, WAN_DISCONNECTED);
-		fr_set_intr_mode(card, 0, 0, 0);
-		fr_comm_disable(card);
-	}
+	return 0;
+}
 
-	if (dev->priv) {
-		kfree(dev->priv);
-		dev->priv = NULL;
-	}
 
-	return 0;
+/*=====================================================================
+ * disable_comm
+ *
+ * Description:
+ *	Disable communications.
+ * 	This code runs in shutdown (sdlamain.c)
+ *      under critical flag. Therefore it is not
+ *      necessary to set a critical flag here 
+ *
+ * Usage:
+ * 	Commnunications are disabled only on a card
+ *      shutdown.
+ */
+
+static void disable_comm (sdla_t *card)
+{
+	printk(KERN_INFO "%s: Disabling Communications!\n",
+			card->devname);
+	fr_comm_disable(card);
 }
 
 /****** WANPIPE-specific entry points ***************************************/
@@ -886,6 +1141,8 @@
 	int err, len;
 	fr_cmd_t cmd;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
 	if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
 		return -EFAULT;
 	
@@ -916,6 +1173,43 @@
 		return -EFAULT;
 	return 0;
 
+#else
+        if (!u_cmd || verify_area(VERIFY_WRITE, u_cmd, sizeof(fr_cmd_t)))
+                return -EFAULT;
+
+        memcpy_fromfs((void*)&cmd, u_cmd, sizeof(cmd));
+
+        if (cmd.length) {
+
+                if (!u_data || verify_area(VERIFY_READ, u_data, cmd.length))
+                        return -EFAULT;
+        }
+
+        /* execute command */
+        do
+        {
+                memcpy(&mbox->cmd, &cmd, sizeof(cmd));
+
+                if (cmd.length)
+                        memcpy_fromfs((void*)&mbox->data, u_data, cmd.length);
+
+                if (sdla_exec(mbox))
+                        err = mbox->cmd.result;
+
+                else return -EIO;
+        } while (err && retry-- && fr_event(card, err, mbox));
+
+        /* return result */
+        memcpy_tofs(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t));
+        len = mbox->cmd.length;
+
+        if (len && u_data && !verify_area(VERIFY_WRITE, u_data, len))
+                memcpy_tofs(u_data, (void*)&mbox->data, len);
+
+        return 0;
+
+#endif
+
 }
 
 /****** Network Device Interface ********************************************/
@@ -927,44 +1221,79 @@
  * interface registration.  Returning anything but zero will fail interface
  * registration.
  */
-static int if_init (struct net_device* dev)
+static int if_init (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	wan_device_t* wandev = &card->wandev;
+#ifdef LINUX_2_0
+	int i;
+#endif
 
 	/* Initialize device driver entry points */
 	dev->open		= &if_open;
 	dev->stop		= &if_close;
-	dev->hard_header	= &if_header;
+	dev->hard_header	= NULL;
 	dev->rebuild_header	= &if_rebuild_hdr;
 	dev->hard_start_xmit	= &if_send;
 	dev->get_stats		= &if_stats;
+#ifdef LINUX_2_4
 	dev->tx_timeout		= &if_tx_timeout;
 	dev->watchdog_timeo	= TX_TIMEOUT;
+#endif
+	
+	if (chan->common.usedby == WANPIPE || chan->common.usedby == API){
+#ifdef LINUX_2_0
+		dev->family = AF_INET;
+#endif
+		/* Initialize media-specific parameters */
+		if (chan->true_if_encoding){
+			dev->type 		= ARPHRD_DLCI;  /* This breaks tcpdump */
+		}else{
+			dev->type		= ARPHRD_PPP; 	/* ARP h/w type */
+		}
+		
+		dev->flags		|= IFF_POINTOPOINT;
+		dev->flags		|= IFF_NOARP;
 
-	/* Initialize media-specific parameters */
-	dev->type		= ARPHRD_DLCI;	/* ARP h/w type */
-	dev->flags		|= IFF_POINTOPOINT;	
-
-	/* Enable Multicast addressing */
-	if (chan->mc == WANOPT_YES){
-		dev->flags 	|= IFF_MULTICAST;
-	}
+		/* Enable Multicast addressing */
+		if (chan->mc == WANOPT_YES){
+			dev->flags 	|= IFF_MULTICAST;
+		}
 
-	dev->mtu		= wandev->mtu - FR_HEADER_LEN;
-	/* For an API, the maximum number of bytes that the stack will pass
-	   to the driver is (dev->mtu + dev->hard_header_len). So, adjust the
-	   mtu so that a frame of maximum size can be transmitted by the API. 
-	*/
-	if(chan->usedby == API) {
-		dev->mtu += (sizeof(api_tx_hdr_t) - FR_HEADER_LEN);
-	}
-	
-	dev->hard_header_len	= FR_HEADER_LEN;/* media header length */
-	dev->addr_len		= 2; 		/* hardware address length */
-	*(unsigned short*)dev->dev_addr = htons(chan->dlci);
+		dev->mtu		= wandev->mtu - FR_HEADER_LEN;
+		/* For an API, the maximum number of bytes that the stack will pass
+		   to the driver is (dev->mtu + dev->hard_header_len). So, adjust the
+		   mtu so that a frame of maximum size can be transmitted by the API. 
+		*/
+		if(chan->common.usedby == API) {
+			dev->mtu += (sizeof(api_tx_hdr_t) - FR_HEADER_LEN);
+		}
+		
+		dev->hard_header_len	= FR_HEADER_LEN;/* media header length */
+		dev->addr_len		= 2; 		/* hardware address length */
+		*(unsigned short*)dev->dev_addr = htons(chan->dlci);
+
+		/* Set transmit buffer queue length */
+        	dev->tx_queue_len = 100;
+
+			/* Initialize socket buffers */
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
+	        for (i = 0; i < DEV_NUMBUFFS; ++i)
+			skb_queue_head_init(&dev->buffs[i]);
+#endif
+	}else{
 
+		/* Setup the interface for Bridging */
+		int hw_addr=0;
+		ether_setup(dev);
+		
+		/* Use a random number to generate the MAC address */
+		memcpy(dev->dev_addr, "\xFE\xFC\x00\x00\x00\x00", 6);
+		get_random_bytes(&hw_addr, sizeof(hw_addr));
+		*(int *)(dev->dev_addr + 2) += hw_addr;
+	}
+		
 	/* Initialize hardware parameters (just for reference) */
 	dev->irq	= wandev->irq;
 	dev->dma	= wandev->dma;
@@ -972,13 +1301,6 @@
 	dev->mem_start	= wandev->maddr;
 	dev->mem_end	= wandev->maddr + wandev->msize - 1;
 
-        /* Set transmit buffer queue length */
-        dev->tx_queue_len = 100;
-   
-	/* Initialize socket buffers */
-	dev_init_buffers(dev);
-
-	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
 
@@ -989,60 +1311,51 @@
  *
  * Return 0 if O.k. or errno.
  */
-static int if_open (struct net_device* dev)
+static int if_open (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	int err = 0;
 	struct timeval tv;
 
-	if (netif_running(dev))
-		return -EBUSY;		/* only one open is allowed */
-
-	if (test_and_set_bit(1, (void*)&card->wandev.critical))
-		return -EAGAIN;
+	if (is_dev_running(dev))
+		return -EBUSY;
 	
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	/* Initialize the task queue */
+	chan->tq_working=0;
 
-	/* If signalling is set to NO, then setup 
-         * DLCI addresses right away.  Don't have to wait for
-	 * link to connect. 
-	 */
-	if (card->wandev.signalling == WANOPT_NO){
-		printk(KERN_INFO "%s: Signalling set to NO: Mapping DLCI's\n",
-					card->wandev.name);
-		if (fr_init_dlci(card,chan)){
-			return -EAGAIN;
-		}
-	}
-
-	if (card->wandev.station == WANOPT_CPE) {
-
-		/* CPE: issue full status enquiry */
-		fr_issue_isf(card, FR_ISF_FSE);
-
-	} else {	/* FR switch: activate DLCI(s) */
-		
-		/* For Switch emulation we have to ADD and ACTIVATE
-		 * the DLCI(s) that were configured with the SET_DLCI_
-		 * CONFIGURATION command. Add and Activate will fail if
-		 * DLCI specified is not included in the list.
-		 *
-		 * Also If_open is called once for each interface. But
-		 * it does not get in here for all the interface. So
-	 	 * we have to pass the entire list of DLCI(s) to add 
-		 * activate routines.  
-		 */ 
-	
-		fr_add_dlci(card, chan->dlci);
-		fr_activate_dlci(card, chan->dlci);
-	}
+#ifndef LINUX_2_4
+	chan->common.wanpipe_task.next = NULL;
+#endif
+	chan->common.wanpipe_task.sync = 0;
+	chan->common.wanpipe_task.routine = (void *)(void *)fr_bh;
+	chan->common.wanpipe_task.data = dev;
+
+	/* Allocate and initialize BH circular buffer */
+	chan->bh_head = kmalloc((sizeof(bh_data_t)*MAX_BH_BUFF),GFP_ATOMIC);
+	memset(chan->bh_head,0,(sizeof(bh_data_t)*MAX_BH_BUFF));
+	atomic_set(&chan->bh_buff_used, 0);
+#endif
 
+#ifdef LINUX_2_4
 	netif_start_queue(dev);
+#else	
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+#endif
+
 	wanpipe_open(card);
-	update_chan_state(dev);
 	do_gettimeofday( &tv );
 	chan->router_start_time = tv.tv_sec;
-        clear_bit(1, (void*)&card->wandev.critical);
+	
+	if (test_bit(0,&chan->config_dlci)){
+		trigger_config_fr (card);
+	}else if (chan->inarp == INARP_REQUEST){
+		trigger_fr_arp(dev);
+	}
+	
 	return err;
 }
 
@@ -1051,61 +1364,42 @@
  * o if this is the last open, then disable communications and interrupts.
  * o reset flags.
  */
-static int if_close (struct net_device* dev)
+static int if_close (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
-	if (test_and_set_bit(1, (void*)&card->wandev.critical))
-		return -EAGAIN;
+	if (chan->inarp == INARP_CONFIGURED) {
+		chan->inarp = INARP_REQUEST;
+	}
 
-	netif_stop_queue(dev);
+	stop_net_queue(dev);
+#ifndef LINUX_2_4
+	dev->start=0;
+#endif
 	wanpipe_close(card);
-	if (card->wandev.station == WANOPT_NODE) {
-		fr_delete_dlci (card,chan->dlci);
-	}
 
-        clear_bit(1, (void*)&card->wandev.critical);
 	return 0;
 }
 
 /*============================================================================
- * Build media header.
- * o encapsulate packet according to encapsulation type.
- *
- * The trick here is to put packet type (Ethertype) into 'protocol' field of
- * the socket buffer, so that we don't forget it.  If encapsulation fails,
- * set skb->protocol to 0 and discard packet later.
- *
- * Return:	media header length.
- */
-static int if_header (struct sk_buff* skb, struct net_device* dev,
-	unsigned short type, void* daddr, void* saddr, unsigned len)
-{
-	int hdr_len = 0;
-	
-	skb->protocol = type;
-	hdr_len = wanrouter_encapsulate(skb, dev);
-
-	if (hdr_len < 0) {
-		hdr_len = 0;
-		skb->protocol = 0;
-	}
-	skb_push(skb, 1);
-	skb->data[0] = Q922_UI;
-	++hdr_len;
-	return hdr_len;
-}
-
-/*============================================================================
  * Re-build media header.
  *
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 static int if_rebuild_hdr (struct sk_buff* skb)
 {
-	struct net_device *dev = skb->dev;
+#else
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+                           struct sk_buff* skb)
+{
+#endif
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4) 
+	netdevice_t *dev = skb->dev;
+#endif
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
@@ -1114,13 +1408,14 @@
 	return 1;
 }
 
-
+#ifdef LINUX_2_4
 /*============================================================================
  * Handle transmit timeout event from netif watchdog
  */
-static void if_tx_timeout (struct net_device *dev)
+static void if_tx_timeout (netdevice_t *dev)
 {
     	fr_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
 
 	/* If our device stays busy for at least 5 seconds then we will
 	 * kick start the device by making dev->tbusy = 0.  We expect
@@ -1131,12 +1426,13 @@
 	chan->drvstats_if_send.if_send_tbusy++;
 	++chan->ifstats.collisions;
 
-	printk (KERN_INFO "%s: Transmit timed out\n", chan->name);
+	printk (KERN_INFO "%s: Transmit timed out on %s\n", 
+			card->devname, dev->name);
 	chan->drvstats_if_send.if_send_tbusy_timeout++;
 	netif_wake_queue (dev);
 
 }
-
+#endif
 
 /*============================================================================
  * Send a packet on a network interface.
@@ -1154,23 +1450,28 @@
  * Notes:
  * 1. This routine is called either by the protocol stack or by the "net
  *    bottom half" (with interrupts enabled).
- * 2. Setting tbusy flag will inhibit further transmit requests from the
- *    protocol stack and can be used for flow control with protocol layer.
+ * 
+ * 2. Using the start_net_queue() and stop_net_queue() MACROS
+ *    will inhibit further transmit requests from the protocol stack 
+ *    and can be used for flow control with protocol layer.
  */
-static int if_send (struct sk_buff* skb, struct net_device* dev)
+static int if_send (struct sk_buff* skb, netdevice_t* dev)
 {
     	fr_channel_t* chan = dev->priv;
     	sdla_t* card = chan->card;
         int err;
     	unsigned char *sendpacket;
     	fr508_flags_t* adptr_flags = card->flags;
-	int udp_type, send_data;
+	int udp_type, delay_tx_queued=0;
 	unsigned long smp_flags=0;
-	void* data;
-	unsigned len;
+	unsigned char attr = 0;
 
 	chan->drvstats_if_send.if_send_entry++;
 
+#ifdef LINUX_2_4
+	netif_stop_queue(dev);
+#endif
+	
         if (skb == NULL) {             
 		/* if we get here, some higher layer thinks we've missed an
 		 * tx-done interrupt.
@@ -1178,7 +1479,19 @@
 		printk(KERN_INFO "%s: interface %s got kicked!\n", 
 			card->devname, dev->name);
 		chan->drvstats_if_send.if_send_skb_null ++;
-		netif_wake_queue(dev);
+
+		wake_net_dev(dev);
+		return 0;
+	}
+
+	/* If a peripheral task is running just drop packets */
+	if (test_bit(PERI_CRIT, &card->wandev.critical)){
+		
+		printk(KERN_INFO "%s: Critical in if_send(): Peripheral running!\n",
+				card->devname);
+		
+		wan_dev_kfree_skb(skb,FREE_WRITE);
+		start_net_queue(dev);
 		return 0;
 	}
 
@@ -1187,23 +1500,58 @@
 	   ensure that the transmit interrupt does not reset the 'tbusy' flag
 	   just before we set it, as this will result in a "transmit timeout".
 	*/
-	set_bit(2, (void*)&card->wandev.critical);
+	set_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical);
         if(chan->transmit_length) {
-		netif_stop_queue(dev);
-	        chan->tick_counter = jiffies;
- 		clear_bit(2, (void*)&card->wandev.critical);
+		stop_net_queue(dev);
+		chan->tick_counter = jiffies;
+ 		clear_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical);
 		return 1;
 	}
-       	clear_bit(2, (void*)&card->wandev.critical);
-  
-	data = skb->data;
+       	clear_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical);
+ 
+#ifndef LINUX_2_4
+    	if (dev->tbusy) {
+
+		/* If our device stays busy for at least 5 seconds then we will
+                 * kick start the device by making dev->tbusy = 0.  We expect
+                 * that our device never stays busy more than 5 seconds. So this                 
+		 * is only used as a last resort.
+                 */
+		
+		chan->drvstats_if_send.if_send_tbusy++;
+		++chan->ifstats.collisions;
+
+		if ((jiffies - chan->tick_counter) < (5 * HZ)) {
+			return 1;
+		}
+
+		printk(KERN_INFO "%s: Transmit timed out on %s\n", 
+				card->devname, chan->name);
+		chan->drvstats_if_send.if_send_tbusy_timeout ++;
+		dev->tbusy = 0;
+    	}
+#endif
+
+	
+	/* Move the if_header() code to here. By inserting frame
+	 * relay header in if_header() we would break the
+	 * tcpdump and other packet sniffers */
+	chan->fr_header_len = setup_fr_header(&skb,dev,chan->common.usedby);
+	if (chan->fr_header_len < 0 ){
+		++chan->ifstats.tx_dropped;
+		++card->wandev.stats.tx_dropped;
+		
+		wan_dev_kfree_skb(skb,FREE_WRITE);
+		start_net_queue(dev);	
+		return 0;
+	}
+
 	sendpacket = skb->data;
-        len = skb->len;
 
 	udp_type = udp_pkt_type(skb, card);
 
         if(udp_type != UDP_INVALID_TYPE) {
-                if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, skb,
+		if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, skb,
                         chan->dlci)) {
                         adptr_flags->imask |= FR_INTR_TIMER;
                         if (udp_type == UDP_FPIPE_TYPE){
@@ -1211,61 +1559,92 @@
 					if_send_PIPE_request ++;
 			}
                 }
+		start_net_queue(dev);
 		return 0;
 	}
 
-	if((chan->usedby == API) && (len <= sizeof(api_tx_hdr_t))) {
-		//FIXME: increment some error statistic
-                dev_kfree_skb(skb);
-                return 0;
- 	}
- 
 	//FIXME: can we do better than sendpacket[2]?
-  	if ((chan->usedby == WANPIPE) && (sendpacket[2] == 0x45)) {
+  	if ((chan->common.usedby == WANPIPE) && (sendpacket[2] == 0x45)) {
+		
                	/* check to see if the source IP address is a broadcast or */
                 /* multicast IP address */
-                if(chk_bcast_mcast_addr(card, dev, skb))
-                        return 0;
+                if(chk_bcast_mcast_addr(card, dev, skb)){
+            		++chan->ifstats.tx_dropped;
+			++card->wandev.stats.tx_dropped;
+                	wan_dev_kfree_skb(skb, FREE_WRITE);
+			start_net_queue(dev);
+			return 0;
+		}
 	}
 
-	/* Lock the 508 card: SMP Supported */
+	
+	/* Lock the S514/S508 card: SMP Supported */
     	s508_s514_lock(card,&smp_flags);
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+	if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
+		
 		chan->drvstats_if_send.if_send_critical_non_ISR ++;
 		chan->ifstats.tx_dropped ++;
-		printk(KERN_INFO "%s Critical in IF_SEND %02X\n", 
-				card->devname, card->wandev.critical);
-		dev_kfree_skb(skb);
-		/* Unlock the 508 card */
-               	s508_s514_unlock(card,&smp_flags);
-		return 0;
+		printk(KERN_INFO "%s Critical in IF_SEND: if_send() already running!\n", 
+				card->devname);
+		goto if_send_start_and_exit;
 	}
- 	
-	if (card->wandev.state != WAN_CONNECTED) {
-		chan->drvstats_if_send.if_send_wan_disconnected ++;
-		++chan->ifstats.tx_dropped;
-        	++card->wandev.stats.tx_dropped;
 	
-	} else if (chan->state != WAN_CONNECTED) {
-		chan->drvstats_if_send.if_send_dlci_disconnected ++;
-		/* Critical area on 514, since disabl_irq is not used
-                 * thus, interrupt would execute a command at
-		 * the same time as if_send.
-                 */
-     		set_bit(1, (void*)&card->wandev.critical);
-		update_chan_state(dev);
-		clear_bit(1, (void*)&card->wandev.critical);
-        	++chan->ifstats.tx_dropped;
+	/* API packet check: minimum packet size must be greater than 
+	 * 16 byte API header */
+	if((chan->common.usedby == API) && (skb->len <= sizeof(api_tx_hdr_t))) {
+		++chan->ifstats.tx_dropped;
+		++card->wandev.stats.tx_dropped;
+	    
+		
+		goto if_send_start_and_exit;
+
+ 	}else{
+		/* During API transmission, get rid of the API header */
+		if (chan->common.usedby == API) {
+			api_tx_hdr_t* api_tx_hdr;
+			api_tx_hdr = (api_tx_hdr_t*)&skb->data[0x00];
+			attr = api_tx_hdr->attr;
+			skb_pull(skb,sizeof(api_tx_hdr_t));
+		}
+	}
+
+	if (card->wandev.state != WAN_CONNECTED) {
+		chan->drvstats_if_send.if_send_wan_disconnected ++;
+		++chan->ifstats.tx_dropped;
         	++card->wandev.stats.tx_dropped;
+	
+	} else if (chan->common.state != WAN_CONNECTED) {
+		chan->drvstats_if_send.if_send_dlci_disconnected ++;
 
+		/* Update the DLCI state in timer interrupt */
+		card->u.f.timer_int_enabled |= TMR_INT_ENABLED_UPDATE_STATE;	
+		adptr_flags->imask |= FR_INTR_TIMER;
+
+        	++chan->ifstats.tx_dropped;
+        	++card->wandev.stats.tx_dropped;
+		
 	} else if (!is_tx_ready(card, chan)) {
-		setup_for_delayed_transmit(dev, data, len);
+		/* No tx buffers available, store for delayed transmit */
+		if (!setup_for_delayed_transmit(dev, skb)){
+			set_bit(1,&delay_tx_queued);
+		}
 		chan->drvstats_if_send.if_send_no_bfrs++;
- 	} else {
-		send_data = 1;
+		
+	} else if (!skb->protocol) {
+		/* No protocols drop packet */
+		chan->drvstats_if_send.if_send_protocol_error ++;
+		++card->wandev.stats.tx_errors;
+	
+	} else if (test_bit(ARP_CRIT,&card->wandev.critical)){
+		/* We are trying to send an ARP Packet, block IP data until
+		 * ARP is sent */
+		++chan->ifstats.tx_dropped;
+        	++card->wandev.stats.tx_dropped;
+		
+	} else {
 		//FIXME: IPX is not implemented in this version of Frame Relay ?
-		if((chan->usedby == WANPIPE) &&
+		if((chan->common.usedby == WANPIPE) &&
 		 	sendpacket[1] == 0x00 &&
 		    	sendpacket[2] == 0x80 &&
 		    	sendpacket[6] == 0x81 &&
@@ -1279,32 +1658,28 @@
 				printk(KERN_INFO 
 				"%s: WARNING: Unsupported IPX data in send, packet dropped\n",
 					card->devname);
-				send_data = 0;
-			}
-		}
-
-		if (send_data) {
-			unsigned char attr = 0;
-
-			/* For an API transmission, get rid of the API header */
-                        if (chan->usedby == API) {
-                                api_tx_hdr_t* api_tx_hdr;
-           			api_tx_hdr = (api_tx_hdr_t*)&skb->data[0x00];
-                      		attr = api_tx_hdr->attr;
-				data += sizeof(api_tx_hdr_t);
-				len -= sizeof(api_tx_hdr_t);
 			}
-
-        		err = fr_send(card, chan->dlci, attr, len, data);
+			
+		}else{
+        		err = fr_send_data_header(card, chan->dlci, attr, skb->len, skb->data, chan->fr_header_len);
 			if (err) {
 				switch(err) {
 				case FRRES_CIR_OVERFLOW:
 				case FRRES_BUFFER_OVERFLOW:
-                			setup_for_delayed_transmit(dev, data,
-						len);
+                			if (!setup_for_delayed_transmit(dev, skb)){
+						set_bit(1,&delay_tx_queued);
+					}
            				chan->drvstats_if_send.
 						if_send_adptr_bfrs_full ++;
-					break;	
+					break;
+					
+				case FRRES_TOO_LONG:
+					if (net_ratelimit()){
+						printk(KERN_INFO 
+						"%s: Error: Frame too long, transmission failed %i\n",
+						 card->devname, (unsigned int)skb->len);
+					}
+					/* Drop down to default */
 				default:
 					chan->drvstats_if_send.
 						if_send_dlci_disconnected ++;
@@ -1317,20 +1692,37 @@
 					if_send_bfr_passed_to_adptr++;
 				++chan->ifstats.tx_packets;
 				++card->wandev.stats.tx_packets;
-                                chan->ifstats.tx_bytes += len;
-                                card->wandev.stats.tx_bytes += len;
+				
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+                                chan->ifstats.tx_bytes += skb->len;
+                                card->wandev.stats.tx_bytes += skb->len;
+#endif
+#ifdef LINUX_2_4
+				dev->trans_start = jiffies;
+#endif
 			}
 		}
 	}
 
-        if (!netif_queue_stopped(dev))
-		dev_kfree_skb(skb);
+if_send_start_and_exit:
+
+	start_net_queue(dev);
+	
+	/* If we queued the packet for transmission, we must not
+	 * deallocate it. The packet is unlinked from the IP stack
+	 * not copied. Therefore, we must keep the original packet */
+	if (!test_bit(1,&delay_tx_queued)) {
+                wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+		adptr_flags->imask |= FR_INTR_TXRDY;
+		card->u.f.tx_interrupts_pending ++;
+	}
 
-        clear_bit(0, (void*)&card->wandev.critical);
+        clear_bit(SEND_CRIT, (void*)&card->wandev.critical);
 
 	s508_s514_unlock(card,&smp_flags);
 
-        return (netif_queue_stopped(dev));
+	return 0;
 }
 
 
@@ -1339,33 +1731,49 @@
  * Setup so that a frame can be transmitted on the occurence of a transmit
  * interrupt.
  */
-static void setup_for_delayed_transmit (struct net_device* dev, void* buf,
-	unsigned len)
+static int setup_for_delayed_transmit (netdevice_t* dev, struct sk_buff *skb)
 {
         fr_channel_t* chan = dev->priv;
         sdla_t* card = chan->card;
-        fr508_flags_t* adptr_flags = card->flags;
-        fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface;
+        fr_dlci_interface_t* dlci_interface;
+	int len = skb->len;
+
+	/* Check that the dlci is properly configured,
+         * before using tx interrupt */
+	if (!chan->dlci_int_interface){
+		if (net_ratelimit()){ 
+			printk(KERN_INFO 
+				"%s: ERROR on DLCI %i: Not configured properly !\n",
+					card->devname, chan->dlci);
+			printk(KERN_INFO "%s: Please contact Sangoma Technologies\n",
+					card->devname);
+		}
+		return 1;
+	}
+		
+	dlci_interface = chan->dlci_int_interface;
 
         if(chan->transmit_length) {
                 printk(KERN_INFO "%s: Big mess in setup_for_del...\n",
 				card->devname);
-                return;
+                return 1;
         }
 
 	if(len > FR_MAX_NO_DATA_BYTES_IN_FRAME) {
 		//FIXME: increment some statistic */
-		return;
+		return 1;
 	}
 
+	skb_unlink(skb);
+	
         chan->transmit_length = len;
-        memcpy(chan->transmit_buffer, buf, len);
-
+	chan->delay_skb = skb;
+        
         dlci_interface->gen_interrupt |= FR_INTR_TXRDY;
         dlci_interface->packet_length = len;
-        adptr_flags->imask |= FR_INTR_TXRDY;
 
-	card->u.f.tx_interrupts_pending ++;
+	/* Turn on TX interrupt at the end of if_send */
+	return 0;
 }
 
 
@@ -1375,18 +1783,21 @@
  * Return 0 if not broadcast/multicast address, otherwise return 1.
  */
 
-static int chk_bcast_mcast_addr(sdla_t *card, struct net_device* dev,
+static int chk_bcast_mcast_addr(sdla_t *card, netdevice_t* dev,
                                 struct sk_buff *skb)
 {
         u32 src_ip_addr;
         u32 broadcast_ip_addr = 0;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         struct in_device *in_dev;
+#endif
         fr_channel_t* chan = dev->priv;
  
         /* read the IP source address from the outgoing packet */
         src_ip_addr = *(u32 *)(skb->data + 14);
 
         /* read the IP broadcast address for the device */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         in_dev = dev->ip_ptr;
         if(in_dev != NULL) {
                 struct in_ifaddr *ifa= in_dev->ifa_list;
@@ -1395,13 +1806,15 @@
                 else
                         return 0;
         }
+#else
+        broadcast_ip_addr = dev->pa_brdaddr;
+#endif
 
         /* check if the IP Source Address is a Broadcast address */
-        if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) {                printk(KERN_INFO
+        if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) {
+                printk(KERN_INFO
                         "%s: Broadcast Source Address silently discarded\n",
                         card->devname);
-                dev_kfree_skb(skb);
-                ++ chan->ifstats.tx_dropped;
                 return 1;
         }
 
@@ -1411,8 +1824,6 @@
                 printk(KERN_INFO
                         "%s: Multicast Source Address silently discarded\n",
                         card->devname);
-                dev_kfree_skb(skb);
-                ++ chan->ifstats.tx_dropped;
                 return 1;
         }
 
@@ -1433,7 +1844,7 @@
 	fr_udp_pkt_t *fr_udp_pkt = (fr_udp_pkt_t *)data; 
 
 	/* Set length of packet */
-	len = sizeof(fr_encap_hdr_t)+
+	len = //sizeof(fr_encap_hdr_t)+
 	      sizeof(ip_pkt_t)+ 
 	      sizeof(udp_pkt_t)+
 	      sizeof(wp_mgmt_t)+
@@ -1481,7 +1892,7 @@
 	fr_udp_pkt->udp_pkt.udp_checksum = 0;
 
 	fr_udp_pkt->udp_pkt.udp_checksum = 
-		calc_checksum(&data[UDP_OFFSET+sizeof(fr_encap_hdr_t)],
+		calc_checksum(&data[UDP_OFFSET/*+sizeof(fr_encap_hdr_t)*/],
 			      udp_length+UDP_OFFSET);
 
 	/* fill in IP length */
@@ -1499,7 +1910,7 @@
 	/* fill in IP checksum */
 	fr_udp_pkt->ip_pkt.hdr_checksum = 0;
 	fr_udp_pkt->ip_pkt.hdr_checksum = 
-		calc_checksum(&data[sizeof(fr_encap_hdr_t)],
+		calc_checksum(&data[/*sizeof(fr_encap_hdr_t)*/0],
 		      	      sizeof(ip_pkt_t));
 
 	return len;
@@ -1588,9 +1999,13 @@
 
 /*============================================================================
  * Get ethernet-style interface statistics.
- * Return a pointer to struct net_device_stats.
+ * Return a pointer to struct enet_statistics.
  */
-static struct net_device_stats* if_stats (struct net_device* dev)
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats *if_stats(netdevice_t *dev)
+#else
+static struct enet_statistics* if_stats (netdevice_t* dev)
+#endif
 {
 	fr_channel_t* chan = dev->priv;
 	
@@ -1603,7 +2018,12 @@
 /****** Interrupt Handlers **************************************************/
 
 /*============================================================================
- * S508 frame relay interrupt service routine.
+ * fr_isr:	S508 frame relay interrupt service routine.
+ *
+ * Description:
+ *	Frame relay main interrupt service route. This
+ *      function check the interrupt type and takes
+ *      the appropriate action.
  */
 static void fr_isr (sdla_t* card)
 {
@@ -1613,26 +2033,25 @@
 	fr_mbox_t* mbox = card->mbox;
 
 	/* This flag prevents nesting of interrupts.  See sdla_isr() routine
-         * in sdlamain.c. 
-	 */
+         * in sdlamain.c.  */
 	card->in_isr = 1;
 	
 	++card->statistics.isr_entry;
 
-	if(test_bit(1, (void*)&card->wandev.critical)) {
-		card->wandev.critical = 0;
-		flags->iflag = 0;
-		card->in_isr = 0;
-		return;
-	}
 
+	/* All peripheral (configuraiton, re-configuration) events
+	 * take presidence over the ISR.  Thus, retrigger */
+	if (test_bit(PERI_CRIT, (void*)&card->wandev.critical)) {
+		++card->statistics.isr_already_critical;
+		goto fr_isr_exit;
+	}
+	
         if(card->hw.type != SDLA_S514) {
-		if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
-                        printk(KERN_INFO "%s: Critical while in ISR (0x%02X)\n",
-                                card->devname, card->wandev.critical);
+		if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
+                        printk(KERN_INFO "%s: Critical while in ISR: If Send Running!\n",
+                                card->devname);
 			++card->statistics.isr_already_critical;
-			card->in_isr = 0;
-			return;
+			goto fr_isr_exit;
 		}
 	}
 
@@ -1649,12 +2068,12 @@
 			tx_intr(card); 
             		break;
 
-                case FR_INTR_READY:
+                case FR_INTR_READY:  	
 	    		Intr_test_counter++;
 			++card->statistics.isr_intr_test;
 	    		break;	
 
-                case FR_INTR_DLC: /* Event interrupt occurred */
+                case FR_INTR_DLC: /* Event interrupt occured */
 			mbox->cmd.command = FR_READ_STATUS;
 			mbox->cmd.length = 0;
 			err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
@@ -1680,27 +2099,31 @@
 			break;
     	}
 
+fr_isr_exit:
+	
 	card->in_isr = 0;
 	flags->iflag = 0;
-        if(card->hw.type != SDLA_S514)
-                clear_bit(0, (void*)&card->wandev.critical);
+	return;
 }
 
 
 
-/*============================================================================
- * Receive interrupt handler.
- * When a receive interrupt occurs do the following:
- *	1- Find the structure for the dlci that the interrupt occurred on
- *      2- If it doesn't exist then print appropriate msg and goto step 8. 
- * 	3- If it exist then copy data to a skb.
- * 	4- If skb contains Sangoma UDP data then process them
- *	5- If skb contains IPXWAN data then send IPXWAN reply packets
- *	6- If skb contains Inverse Arp data then send Inv Arp replies
- *	7- If skb contains any other data then decapsulate the packet and
- *	   send it to the stack.
- * 	8- Release the receive element and update receive pointers on the board 
+/*===========================================================
+ * rx_intr	Receive interrupt handler.
+ *
+ * Description
+ * 	Upon receiveing an interrupt: 
+ *	1. Check that the firmware is in sync with 
+ *     	   the driver. 
+ *      2. Find an appropriate network interface
+ *         based on the received dlci number.
+ *	3. Check that the netowrk interface exists
+ *         and that it's setup properly.
+ *	4. Copy the data into an skb buffer.
+ *	5. Check the packet type and take
+ *         appropriate acton: UPD, API, ARP or Data.
  */
+
 static void rx_intr (sdla_t* card)
 {
 	fr_rx_buf_ctl_t* frbuf = card->rxmb;
@@ -1708,11 +2131,13 @@
 	fr_channel_t* chan;
 	char *ptr = &flags->iflag;
 	struct sk_buff* skb;
-	struct net_device* dev;
+	netdevice_t* dev;
 	void* buf;
 	unsigned dlci, len, offs, len_incl_hdr;
 	int i, udp_type;	
 
+
+	/* Check that firmware buffers are in sync */
 	if (frbuf->flag != 0x01) {
 
 		printk(KERN_INFO 
@@ -1725,6 +2150,16 @@
 		printk(KERN_INFO "\n");
 	
 		++card->statistics.rx_intr_corrupt_rx_bfr;
+
+		/* Bug Fix: Mar 6 2000
+                 * If we get a corrupted mailbox, it means that driver 
+                 * is out of sync with the firmware. There is no recovery.
+                 * If we don't turn off all interrupts for this card
+                 * the machine will crash. 
+                 */
+		printk(KERN_INFO "%s: Critical router failure ...!!!\n", card->devname);
+		printk(KERN_INFO "Please contact Sangoma Technologies !\n");
+		fr_set_intr_mode(card, 0, 0, 0);	
 		return;
 	}
 
@@ -1732,134 +2167,206 @@
 	dlci = frbuf->dlci;
 	offs = frbuf->offset;
 
-	/* Find network interface for this packet */
+	/* Find the network interface for this packet */
 	dev = find_channel(card, dlci);
-        
-	if (dev == NULL) {
+   
 
-		/* unconfigured DLCI, so discard packet */
-   		printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n",
+	/* Check that the network interface is active and
+         * properly setup */
+	if (dev == NULL) {
+   		if( net_ratelimit()) { 
+			printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n",
                                                 card->devname, dlci);
+		}
 		++card->statistics.rx_intr_on_orphaned_DLCI; 
+		++card->wandev.stats.rx_dropped;
+		goto rx_done;
+	}
 
-       	} else {
-		chan = dev->priv;
+	if ((chan = dev->priv) == NULL){
+		if( net_ratelimit()) { 
+			printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n",
+                                                card->devname, dlci);
+		}
+		++card->statistics.rx_intr_on_orphaned_DLCI; 
+		++card->wandev.stats.rx_dropped;
+		goto rx_done;
+	}
 
-   		skb = dev_alloc_skb(len); 
+	skb = dev_alloc_skb(len); 
 
-		if (!netif_running(dev) || (skb == NULL)) { 
-			++chan->ifstats.rx_dropped;
-		
-			if(netif_running(dev)) {
+	if (!is_dev_running(dev) || (skb == NULL)){
 
+		++chan->ifstats.rx_dropped;
+	
+		if(skb == NULL) {
+			if (net_ratelimit()) { 
 				printk(KERN_INFO 
-				"%s: no socket buffers available!\n", 
-				card->devname);
-	    			chan->drvstats_rx_intr.rx_intr_no_socket ++;
-                		
-			} else
-				chan->drvstats_rx_intr.
-					rx_intr_dev_not_started ++;
-		} else {
-			/* Copy data to the socket buffer */
-			if ((offs + len) > card->u.f.rx_top + 1) {
-				unsigned tmp = card->u.f.rx_top - offs + 1;
-
-				buf = skb_put(skb, tmp);
-				sdla_peek(&card->hw, offs, buf, tmp);
-				offs = card->u.f.rx_base;
-               			len -= tmp;
-               		}
-
-			buf = skb_put(skb, len);
-			sdla_peek(&card->hw, offs, buf, len);
-
-			udp_type = udp_pkt_type( skb, card );
-
-                        if(udp_type != UDP_INVALID_TYPE) {
-                                if(store_udp_mgmt_pkt(udp_type,
-                                        UDP_PKT_FRM_NETWORK, card, skb, dlci)) {
-                                        flags->imask |= FR_INTR_TIMER;
-                                        if (udp_type == UDP_FPIPE_TYPE){
-                                                chan->drvstats_rx_intr.
-						    rx_intr_PIPE_request ++;
-					}
-				}			
+					"%s: no socket buffers available!\n", 
+						card->devname);
 			}
+			chan->drvstats_rx_intr.rx_intr_no_socket ++;
+		} 
 
-			else if (chan->usedby == API) {
-				api_rx_hdr_t* api_rx_hdr;
-				chan->drvstats_rx_intr.
-                                	rx_intr_bfr_passed_to_stack ++;
-				chan->ifstats.rx_packets ++;
-                                card->wandev.stats.rx_packets ++;
-				chan->ifstats.rx_bytes += skb->len;
-				card->wandev.stats.rx_bytes += skb->len;
-
-				skb_push(skb, sizeof(api_rx_hdr_t));
-				api_rx_hdr = (api_rx_hdr_t*)&skb->data[0x00];
-				api_rx_hdr->attr = frbuf->attr;
-				api_rx_hdr->time_stamp = frbuf->tmstamp;
-				skb->protocol = htons(0x16);
-				skb->pkt_type = PACKET_HOST;
-                                /* Pass it up the protocol stack */
-                                skb->dev = dev;
-                                skb->mac.raw  = skb->data;
-                                netif_rx(skb);
-
-			} else if (handle_IPXWAN(skb->data,chan->name,
-				chan->enable_IPX, chan->network_number)) {
-				if (chan->enable_IPX) {
-                                        fr_send(card, dlci, 0, skb->len,
-						skb->data);
-				}
-				dev_kfree_skb(skb);
+		if (!is_dev_running(dev)){
+			chan->drvstats_rx_intr.
+				rx_intr_dev_not_started ++;
+			if (skb){
+				wan_dev_kfree_skb(skb, FREE_READ);
+			}
+		}
+		goto rx_done;
+	}
 
-/*FIXME: Fix the ARPS in next release
+	/* Copy data from the board into the socket buffer */
+	if ((offs + len) > card->u.f.rx_top + 1) {
+		unsigned tmp = card->u.f.rx_top - offs + 1;
 
-			} else if (is_arp(skb->data)) {
-				if (process_ARP((arphdr_1490_t *)skb->data, card, dev)) {
-					printk (KERN_INFO "%s: Error processing ARP Packet.\n", card->devname);
-				}
-				dev_kfree_skb(skb);
-*/
+		buf = skb_put(skb, tmp);
+		sdla_peek(&card->hw, offs, buf, tmp);
+		offs = card->u.f.rx_base;
+		len -= tmp;
+	}
 
-			} else if ( skb->data[0] != 0x03) {
-				printk(KERN_INFO "%s: Non IETF packet discarded.\n", card->devname);
-				dev_kfree_skb(skb);
+	buf = skb_put(skb, len);
+	sdla_peek(&card->hw, offs, buf, len);
 
-			} else {
 
-				len_incl_hdr = skb->len;
-                    		/* Decapsulate packet and pass it up the
-			   	   protocol stack */
-				skb->dev = dev;
+	/* We got the packet from the bard. 
+         * Check the packet type and take appropriate action */
 
-				/* remove hardware header */
-				buf = skb_pull(skb, 1); 
+	udp_type = udp_pkt_type( skb, card );
 
-				if (!wanrouter_type_trans(skb, dev)) {
-					
-					/* can't decapsulate packet */
-					dev_kfree_skb(skb);
-					chan->drvstats_rx_intr.
-					     rx_intr_bfr_not_passed_to_stack ++;
- 					++ chan->ifstats.rx_errors;
-		                       	++ card->wandev.stats.rx_errors;
-					
-				} else {
-					netif_rx(skb);
-					chan->drvstats_rx_intr.
-						rx_intr_bfr_passed_to_stack ++;
-					++ chan->ifstats.rx_packets;
-					++ card->wandev.stats.rx_packets;
-                                        chan->ifstats.rx_bytes += len_incl_hdr;
-                                        card->wandev.stats.rx_bytes += 
-						len_incl_hdr;
+	if(udp_type != UDP_INVALID_TYPE) {
+
+		/* UDP Debug packet received, store the
+		 * packet and handle it in timer interrupt */
+
+		skb_pull(skb, 1); 
+		if (wanrouter_type_trans(skb, dev)){ 
+			if(store_udp_mgmt_pkt(udp_type,UDP_PKT_FRM_NETWORK,card,skb,dlci)){
+
+				flags->imask |= FR_INTR_TIMER;
+
+				if (udp_type == UDP_FPIPE_TYPE){
+					++chan->drvstats_rx_intr.rx_intr_PIPE_request;
 				}
-                	}
-            	}
-       	}
+			}
+		}
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	}else if (chan->common.usedby == API) {
+
+		/* We are in API mode. 
+                 * Add an API header to the RAW packet
+                 * and queue it into a circular buffer.
+                 * Then kick the fr_bh() bottom half handler */
+
+		api_rx_hdr_t* api_rx_hdr;
+		chan->drvstats_rx_intr.rx_intr_bfr_passed_to_stack ++;
+		chan->ifstats.rx_packets ++;
+		card->wandev.stats.rx_packets ++;
+
+		chan->ifstats.rx_bytes += skb->len;
+		card->wandev.stats.rx_bytes += skb->len;
+
+		skb_push(skb, sizeof(api_rx_hdr_t));
+		api_rx_hdr = (api_rx_hdr_t*)&skb->data[0x00];
+		api_rx_hdr->attr = frbuf->attr;
+		api_rx_hdr->time_stamp = frbuf->tmstamp;
+
+		skb->protocol = htons(ETH_P_IP);
+		skb->mac.raw  = skb->data;
+		skb->dev      = dev;
+		skb->pkt_type = WAN_PACKET_DATA;
+
+		bh_enqueue(dev, skb);
+
+		trigger_fr_bh(chan);
+	#endif
+
+	}else if (handle_IPXWAN(skb->data,chan->name,chan->enable_IPX, chan->network_number)){
+
+		//FIXME: Frame Relay IPX is not supported, Yet !
+		//if (chan->enable_IPX) {
+		//	fr_send(card, dlci, 0, skb->len,skb->data);
+		//}
+		wan_dev_kfree_skb(skb, FREE_READ);
+
+	} else if (is_arp(skb->data)) {
+
+		/* ARP support enabled Mar 16 2000 
+		 * Process incoming ARP reply/request, setup
+		 * dynamic routes. */ 
+
+		if (process_ARP((arphdr_1490_t *)skb->data, card, dev)) {
+			if (net_ratelimit()){  
+				printk (KERN_INFO 
+				   "%s: Error processing ARP Packet.\n", 
+					card->devname);
+			}
+		}
+		wan_dev_kfree_skb(skb, FREE_READ);
+
+	} else if (skb->data[0] != 0x03) {
+
+		if (net_ratelimit()) { 
+			printk(KERN_INFO "%s: Non IETF packet discarded.\n", 
+				card->devname);
+		}
+		wan_dev_kfree_skb(skb, FREE_READ);
+
+	} else {
+
+		len_incl_hdr = skb->len;
+		/* Decapsulate packet and pass it up the
+		   protocol stack */
+		skb->dev = dev;
+		
+		if (chan->common.usedby == BRIDGE || chan->common.usedby == BRIDGE_NODE){
+		
+			/* Make sure it's an Ethernet frame, otherwise drop it */
+			if (!memcmp(skb->data, "\x03\x00\x80\x00\x80\xC2\x00\x07", 8)) {
+				skb_pull(skb, 8);
+				skb->protocol=eth_type_trans(skb,dev);
+			}else{
+				++chan->drvstats_rx_intr.rx_intr_bfr_not_passed_to_stack;
+				++chan->ifstats.rx_errors;
+				++card->wandev.stats.rx_errors;
+				goto rx_done;
+			}
+		}else{
+		
+			/* remove hardware header */
+			buf = skb_pull(skb, 1); 
+			
+			if (!wanrouter_type_trans(skb, dev)) {
+				
+				/* can't decapsulate packet */
+				wan_dev_kfree_skb(skb, FREE_READ);
+
+				++chan->drvstats_rx_intr.rx_intr_bfr_not_passed_to_stack;
+				++chan->ifstats.rx_errors;
+				++card->wandev.stats.rx_errors;
+				goto rx_done;	
+			}
+			skb->mac.raw = skb->data;
+		} 
+		
+
+		/* Send a packed up the IP stack */
+		netif_rx(skb);
+		++chan->drvstats_rx_intr.rx_intr_bfr_passed_to_stack;
+		++chan->ifstats.rx_packets;
+		++card->wandev.stats.rx_packets;
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+		chan->ifstats.rx_bytes += len_incl_hdr;
+		card->wandev.stats.rx_bytes += len_incl_hdr;
+#endif
+	}
+
+rx_done:
 
        	/* Release buffer element and calculate a pointer to the next one */ 
        	frbuf->flag = 0;
@@ -1869,14 +2376,36 @@
 
 }
 
-/*============================================================================
- * Transmit interrupt handler.
+/*==================================================================
+ * tx_intr:	Transmit interrupt handler.
+ *
+ * Rationale:
+ *      If the board is busy transmitting, if_send() will
+ *      buffers a single packet and turn on
+ *      the tx interrupt. Tx interrupt will be called
+ *      by the board, once the firmware can send more
+ *      data. Thus, no polling is required.	 
+ *
+ * Description:
+ *	Tx interrupt is called for each 
+ *      configured dlci channel. Thus: 
+ * 	1. Obtain the netowrk interface based on the
+ *         dlci number.
+ *      2. Check that network interface is up and
+ *         properly setup.
+ * 	3. Check for a buffered packed.
+ *      4. Transmit the packed.
+ *	5. If we are in WANPIPE mode, mark the 
+ *         NET_BH handler. 
+ *      6. If we are in API mode, kick
+ *         the AF_WANPIPE socket for more data. 
+ *	   
  */
 static void tx_intr(sdla_t *card)
 {
         fr508_flags_t* flags = card->flags;
         fr_tx_buf_ctl_t* bctl;
-        struct net_device* dev = card->wandev.dev;
+        netdevice_t* dev;
         fr_channel_t* chan;
 
         if(card->hw.type == SDLA_S514){
@@ -1888,61 +2417,112 @@
 
         /* Find the structure and make it unbusy */
         dev = find_channel(card, flags->dlci);
-        chan = dev->priv;
+	if (dev == NULL){
+		printk(KERN_INFO "NO DEV IN TX Interrupt\n");	
+		goto end_of_tx_intr;
+	}
+
+        if ((chan = dev->priv) == NULL){
+		printk(KERN_INFO "NO CHAN IN TX Interrupt\n");	
+		goto end_of_tx_intr;
+	}
 
-        if(!chan->transmit_length) {
+        if(!chan->transmit_length || !chan->delay_skb) {
                 printk(KERN_INFO "%s: tx int error - transmit length zero\n",
 				card->wandev.name);
-                return;
+                goto end_of_tx_intr;
         }
 
 	/* If the 'if_send()' procedure is currently checking the 'tbusy'
 	   status, then we cannot transmit. Instead, we configure the microcode
 	   so as to re-issue this transmit interrupt at a later stage. 
 	*/
-	if (test_bit(2, (void*)&card->wandev.critical)) {
+	if (test_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical)) {
+
 		fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface;
 		bctl->flag = 0xA0;
 		dlci_interface->gen_interrupt |= FR_INTR_TXRDY;
-		printk(KERN_INFO "%s: TX Interrupt Detected busy if_send\n",card->devname);
+		return;
 
- 	} else {
+ 	}else{
         	bctl->dlci = flags->dlci;
-	        bctl->length = chan->transmit_length;
-        	sdla_poke(&card->hw, bctl->offset, chan->transmit_buffer,
-                	chan->transmit_length);
+	        bctl->length = chan->transmit_length+chan->fr_header_len;
+        	sdla_poke(&card->hw, 
+		          fr_send_hdr(card,bctl->dlci,bctl->offset), 
+			  chan->delay_skb->data,
+ 	              	  chan->delay_skb->len);
 	        bctl->flag = 0xC0;
 
 		++chan->ifstats.tx_packets;
 		++card->wandev.stats.tx_packets;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 		chan->ifstats.tx_bytes += chan->transmit_length;
 		card->wandev.stats.tx_bytes += chan->transmit_length;
+#endif
+
+		/* We must free an sk buffer, which we used
+		 * for delayed transmission; Otherwise, the sock
+		 * will run out of memory */
+                wan_dev_kfree_skb(chan->delay_skb, FREE_WRITE);
+
+		chan->delay_skb = NULL;				
         	chan->transmit_length = 0;
 
-	        /* if any other interfaces have transmit interrupts pending, */
-		/* do not disable the global transmit interrupt */
-		if(!(-- card->u.f.tx_interrupts_pending))
-        	        flags->imask &= ~FR_INTR_TXRDY;
+#ifdef LINUX_2_4
+		dev->trans_start = jiffies;
+#endif
 
-		netif_wake_queue (dev);
+#ifdef LINUX_2_0
+		wake_net_dev(dev);
+#else
+		if (is_queue_stopped(dev)){
+			/* If using API, than wakeup socket BH handler */
+			if (chan->common.usedby == API){
+				start_net_queue(dev);
+				wakeup_sk_bh(dev);
+			}else{
+				wake_net_dev(dev);
+			}
+		}
+#endif
 	}
+
+end_of_tx_intr:
+
+ 	/* if any other interfaces have transmit interrupts pending, 
+	 * do not disable the global transmit interrupt */
+	if(!(-- card->u.f.tx_interrupts_pending))
+       	        flags->imask &= ~FR_INTR_TXRDY;
+
+
 }
 
 
 /*============================================================================
- * Timer interrupt handler.
- FIXME: update comments as we modify the code
- * The timer interrupt is used for three purposes:
- *    1) Processing udp calls from 'fpipemon'.
- *    2) Processing update calls from /proc file system
- *    2) Reading board-level statistics for updating the proc file system.
- *    3) Sending inverse ARP request packets.
+ * timer_intr:	Timer interrupt handler.
+ *
+ * Rationale:
+ *	All commans must be executed within the timer
+ *      interrupt since no two commands should execute
+ *      at the same time.
+ *
+ * Description:
+ *	The timer interrupt is used to:
+ *    	1. Processing udp calls from 'fpipemon'.
+ *    	2. Processing update calls from /proc file system
+ *   	3. Reading board-level statistics for 
+ *         updating the proc file system.
+ *    	4. Sending inverse ARP request packets.
+ *	5. Configure a dlci/channel.
+ *	6. Unconfigure a dlci/channel. (Node only)
  */
+
 static void timer_intr(sdla_t *card)
 {
 	fr508_flags_t* flags = card->flags;
 
-        if(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UDP) {
+	/* UDP Debuging: fpipemon call */
+        if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UDP) {
 		if(card->u.f.udp_type == UDP_FPIPE_TYPE) {
                     	if(process_udp_mgmt_pkt(card)) {
 		                card->u.f.timer_int_enabled &=
@@ -1951,81 +2531,132 @@
 		}
         }
 
-	if(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE) {
+	/* /proc update call : triggered from update() */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE) {
 		fr_get_err_stats(card);
 		fr_get_stats(card);
 		card->u.f.update_comms_stats = 0;
 		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
 	}
 
+	/* Update the channel state call.  This is call is
+         * triggered by if_send() function */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE_STATE){
+		netdevice_t *dev;
+		if (card->wandev.state == WAN_CONNECTED){
+			for (dev=card->wandev.dev; dev; dev = *((netdevice_t **)dev->priv)){
+				fr_channel_t *chan = dev->priv;	
+				if (chan->common.state != WAN_CONNECTED){
+					update_chan_state(dev);
+				}
+			}
+		}
+		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE_STATE;
+	}
+
+	/* configure a dlci/channel */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_CONFIG){
+		config_fr(card);
+		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_CONFIG;
+	}
 
-//FIXME: Fix the dynamic IP addressing
-/*
-goto L4;
+	/* unconfigure a dlci/channel */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UNCONFIG){
+		unconfig_fr(card);
+		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UNCONFIG;
+	}
 
-	// Used to send inarp request at given interval 
-	if (card->wandev.state == WAN_CONNECTED) {
-        	int num_remaining = 0;
-
-		dev = card->wandev.dev;
-		while (dev) {
-                	fr_channel_t *chan = dev->priv;
-
-                        if (chan->inarp == INARP_REQUEST &&
-                        	chan->state == WAN_CONNECTED) {
-                                num_remaining++;
-
-                                if ((jiffies - chan->inarp_tick) > (chan->inarp_interval * HZ)) {
-                                	send_inarp_request(card,dev);
-                                	chan->inarp_tick = jiffies;
-                                }
+	
+	/* Transmit ARP packets */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_ARP){
+		int i=0;
+		netdevice_t *dev;
+
+		if (card->u.f.arp_dev == NULL)
+			card->u.f.arp_dev = card->wandev.dev;
+
+		dev = card->u.f.arp_dev;
+
+		for (;;){ 
+
+			fr_channel_t *chan = dev->priv;
+
+			/* If the interface is brought down cancel sending In-ARPs */
+			if (!(dev->flags&IFF_UP)){
+				clear_bit(0,&chan->inarp_ready);	
+			}
+
+			if (test_bit(0,&chan->inarp_ready)){
+
+				if (check_tx_status(card,dev)){
+					set_bit(ARP_CRIT,&card->wandev.critical);
+					break;
+				}
+
+				if (!send_inarp_request(card,dev)){
+					trigger_fr_arp(dev);
+					chan->inarp_tick = jiffies;
+				}
+
+				clear_bit(0,&chan->inarp_ready);
+				dev = move_dev_to_next(card,dev);
+				break;
+			}
+			dev = move_dev_to_next(card,dev);
+
+			if (++i == card->wandev.new_if_cnt){
+				card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_ARP;
+				break;
 			}
-			dev = chan->slave;
-		}
-		if (!num_remaining) {   // no more to process 
-                        flags->imask &= ~FR_INTR_TIMER;
 		}
+		card->u.f.arp_dev = dev;
 	}
-L4:
-	;
-*/
+
         if(!card->u.f.timer_int_enabled)
                 flags->imask &= ~FR_INTR_TIMER;
 }
 
 
 /*============================================================================
- * Spurious interrupt handler.
- * o print a warning
- * o 
+ * spur_intr:	Spurious interrupt handler.
+ * 
+ * Description:
+ *  	We don't know this interrupt.
+ *      Print a warning.
  */
+
 static void spur_intr (sdla_t* card)
 {
-	printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
+	if (net_ratelimit()){ 
+		printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
+	}
 }
 
+
 //FIXME: Fix the IPX in next version
 /*===========================================================================
  *  Return 0 for non-IPXWAN packet
  *         1 for IPXWAN packet or IPX is not enabled!
  *  FIXME: Use a IPX structure here not offsets
  */
-static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number)
+static int handle_IPXWAN(unsigned char *sendpacket, 
+			 char *devname, unsigned char enable_IPX, 
+			 unsigned long network_number)
 {
 	int i;
 
-	if( sendpacket[1] == 0x00 &&
-	    sendpacket[2] == 0x80 &&
-	    sendpacket[6] == 0x81 &&
-	    sendpacket[7] == 0x37) { 
+	if( sendpacket[1] == 0x00 && sendpacket[2] == 0x80 &&
+	    sendpacket[6] == 0x81 && sendpacket[7] == 0x37) { 
 
 		/* It's an IPX packet */
-		if(!enable_IPX) {
+		if (!enable_IPX){
 			/* Return 1 so we don't pass it up the stack. */
 			//FIXME: Take this out when IPX is fixed
-			printk (KERN_INFO 
+			if (net_ratelimit()){ 
+				printk (KERN_INFO 
 				"%s: WARNING: Unsupported IPX packet received and dropped\n",
 					devname);
+			}
 			return 1;
 		}
 	} else {
@@ -2033,39 +2664,33 @@
 		return 0;
 	}
 
-	if( sendpacket[24] == 0x90 &&
-	    sendpacket[25] == 0x04)
-	{
+	if( sendpacket[24] == 0x90 && sendpacket[25] == 0x04){
 		/* It's IPXWAN */
 
-		if( sendpacket[10] == 0x02 &&
-		    sendpacket[42] == 0x00)
-		{
+		if( sendpacket[10] == 0x02 && sendpacket[42] == 0x00){
+
 			/* It's a timer request packet */
-			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",
+					devname);
 
 			/* Go through the routing options and answer no to every
 			 * option except Unnumbered RIP/SAP
 			 */
-			for(i = 49; sendpacket[i] == 0x00; i += 5)
-			{
+			for(i = 49; sendpacket[i] == 0x00; i += 5){
 				/* 0x02 is the option for Unnumbered RIP/SAP */
-				if( sendpacket[i + 4] != 0x02)
-				{
+				if( sendpacket[i + 4] != 0x02){
 					sendpacket[i + 1] = 0;
 				}
 			}
 
 			/* Skip over the extended Node ID option */
-			if( sendpacket[i] == 0x04 )
-			{
+			if( sendpacket[i] == 0x04 ){
 				i += 8;
 			}
 
 			/* We also want to turn off all header compression opt.
 			 */
-			for(; sendpacket[i] == 0x80 ;)
-			{
+			for(; sendpacket[i] == 0x80 ;){
 				sendpacket[i + 1] = 0;
 				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
 			}
@@ -2073,12 +2698,15 @@
 			/* Set the packet type to timer response */
 			sendpacket[42] = 0x01;
 
-			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
-		}
-		else if( sendpacket[42] == 0x02 )
-		{
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",
+					devname);
+
+		} else if( sendpacket[42] == 0x02 ){
+
 			/* This is an information request packet */
-			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
+			printk(KERN_INFO 
+				"%s: Received IPXWAN Information Request packet\n",
+						devname);
 
 			/* Set the packet type to information response */
 			sendpacket[42] = 0x03;
@@ -2103,10 +2731,10 @@
 				sendpacket[i] = 0;
 			}
 
-			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
-		}
-		else
-		{
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",
+					devname);
+		} else {
+
 			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
 			return 0;
 		}
@@ -2128,103 +2756,183 @@
 	return 0;
 }
 /*============================================================================
- * Process Route.
- * This routine is called as a polling routine to dynamically add/delete routes
- * negotiated by inverse ARP.  It is in this "task" because we don't want routes
- * to be added while in interrupt context.
-*/
+ * process_route
+ * 
+ * Rationale:
+ *	If the interface goes down, or we receive an ARP request,
+ *      we have to change the network interface ip addresses.
+ * 	This cannot be done within the interrupt.
+ *
+ * Description:
+ *
+ * 	This routine is called as a polling routine to dynamically 
+ *	add/delete routes negotiated by inverse ARP.  It is in this 
+ *    	"task" because we don't want routes to be added while in 
+ *      interrupt context.
+ *
+ * Usage:
+ *	This function is called by fr_poll() polling funtion.
+ */
 
-static void process_route (sdla_t* card)
+static void process_route (netdevice_t *dev)
 {
-	struct net_device* dev;
-	struct in_device *in_dev;
-    	struct rtentry route;
-        int err = 0;
-        mm_segment_t fs;
+	fr_channel_t *chan = dev->priv;
+	sdla_t *card = chan->card;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 
-	/* Dynamic Route adding/removing */
-	dev = card->wandev.dev;
-	while (dev) {
-		fr_channel_t *chan = dev->priv;
-
-		if (chan->route_flag == ADD_ROUTE   ||
-		    chan->route_flag == REMOVE_ROUTE ) {
-			fs = get_fs();
-		
-			in_dev = dev->ip_ptr;
-
-			if( in_dev != NULL && in_dev->ifa_list != NULL) {
-				memset(&route, 0, sizeof(route));
-				route.rt_dev   = dev->name;
-				route.rt_flags = 0;
-
-				((struct sockaddr_in *) &(route.rt_dst)) ->
-					sin_addr.s_addr=in_dev->ifa_list->ifa_address;
-				((struct sockaddr_in *) &(route.rt_dst)) ->
-					sin_family = AF_INET;
-				((struct sockaddr_in *) &(route.rt_genmask)) ->
-					sin_addr.s_addr = 0xFFFFFFFF;
-				((struct sockaddr_in *) &(route.rt_genmask)) ->
-					sin_family = AF_INET;
-
-				switch(chan->route_flag) {
-
-				case ADD_ROUTE:
-					set_fs(get_ds());     /* get user space block */
-					err = ip_rt_ioctl( SIOCADDRT, &route);	
-					set_fs(fs);	      /* restore old block */
-
-					if (err) {
-						printk(KERN_INFO "%s: Adding of route failed.  Error: %d\n", card->devname,err);
-						printk(KERN_INFO "%s: Address: %s\n",
-						       chan->name,
-						       in_ntoa(in_dev->ifa_list->ifa_address) );
-					} else {
-						chan->route_flag = ROUTE_ADDED;
-					}
-					break;
+	struct ifreq if_info;
+	struct sockaddr_in *if_data;
+	mm_segment_t fs = get_fs();
+	u32 ip_tmp;
+	int err;
 
-				case REMOVE_ROUTE:
-					set_fs(get_ds());     /* get user space block */
-					err = ip_rt_ioctl( SIOCDELRT, &route);
-					set_fs(fs);	      /* restore old block */
-
-					if (err) {
-						printk(KERN_INFO "%s: Deleting of route failed.  Error: %d\n", card->devname,err);
-						printk(KERN_INFO "%s: Address: %s\n",
-						       dev->name,in_ntoa(in_dev->ifa_list->ifa_address) );
-					} else {
-						printk(KERN_INFO "%s: Removed route.\n",
-						       chan->name);
-						chan->route_flag = NO_ROUTE;
-					}
-					break;
-				} /* Case Statement */
-			}
-		} /* If ADD/DELETE ROUTE */
 
-		dev = chan->slave;
-	}  /* Device 'While' Loop */
+	switch(chan->route_flag){
 
-	card->poll = NULL;
-}
+	case ADD_ROUTE:
+				
+		/* Set remote addresses */
+		memset(&if_info, 0, sizeof(if_info));
+		strcpy(if_info.ifr_name, dev->name);
 
+		set_fs(get_ds());     /* get user space block */ 
+		
+		if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
+		if_data->sin_addr.s_addr = chan->ip_remote;
+		if_data->sin_family = AF_INET;
+		err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
 
+		set_fs(fs);           /* restore old block */
 
-/****** Frame Relay Firmware-Specific Functions *****************************/
+		if (err) {
 
-/*============================================================================
- * Read firmware code version.
- * o fill string str with firmware version info. 
- */
-static int fr_read_version (sdla_t* card, char* str)
-{
-	fr_mbox_t* mbox = card->mbox;
-	int retry = MAX_CMD_RETRY;
-	int err;
+			printk(KERN_INFO 
+				"%s: Route Add failed.  Error: %d\n", 
+					card->devname,err);
+			printk(KERN_INFO "%s: Address: %s\n",
+				chan->name, in_ntoa(chan->ip_remote));
+
+		}else {
+			printk(KERN_INFO "%s: Route Added Successfully: %s\n",
+				card->devname,in_ntoa(chan->ip_remote));
+			chan->route_flag = ROUTE_ADDED;
+		}
+		break;
 
-	do
+	case REMOVE_ROUTE:
+
+		/* Set remote addresses */
+		memset(&if_info, 0, sizeof(if_info));
+		strcpy(if_info.ifr_name, dev->name);
+
+		ip_tmp = get_ip_address(dev,WAN_POINTOPOINT_IP);	
+
+		set_fs(get_ds());     /* get user space block */ 
+		
+		if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
+		if_data->sin_addr.s_addr = 0;
+		if_data->sin_family = AF_INET;
+		err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+
+		set_fs(fs);    
+		
+		if (err) {
+
+			printk(KERN_INFO 
+				"%s: Deleting of route failed.  Error: %d\n", 
+					card->devname,err);
+			printk(KERN_INFO "%s: Address: %s\n",
+				dev->name,in_ntoa(chan->ip_remote) );
+
+		} else {
+
+			printk(KERN_INFO "%s: Route Removed Sucessfuly: %s\n", 
+				card->devname,in_ntoa(ip_tmp));
+			chan->route_flag = NO_ROUTE;
+		}
+		break;
+
+	} /* Case Statement */
+
+#else
+        /* Dynamic Route adding/removing */
+	struct rtentry route;
+        int err = 0;
+        unsigned long fs = get_fs();
+
+	memset(&route, 0, sizeof(route));
+	route.rt_dev   = dev->name;
+	route.rt_flags = 0;
+
+	((struct sockaddr_in *) &(route.rt_dst)) ->
+					sin_addr.s_addr=dev->pa_dstaddr;
+	((struct sockaddr_in *) &(route.rt_dst)) ->
+						sin_family = AF_INET;
+	((struct sockaddr_in *) &(route.rt_genmask)) ->
+				sin_addr.s_addr = 0xFFFFFFFF;
+	((struct sockaddr_in *) &(route.rt_genmask)) ->
+						sin_family = AF_INET;
+	switch(chan->route_flag) {
+
+	case ADD_ROUTE:
+
+		set_fs(get_ds());     /* get user space block */
+		err = ip_rt_new(&route);
+		set_fs(fs);           /* restore old block */
+
+		if (err) {
+			printk(KERN_INFO "%s: Adding of route failed.  Error: %d\n", 
+						card->devname,err);
+			printk(KERN_INFO "%s: Address: %s\n",
+				chan->name, in_ntoa(dev->pa_dstaddr) );
+			}
+		else {
+			chan->route_flag = ROUTE_ADDED;
+		}
+		break;
+
+	case REMOVE_ROUTE:
+
+		set_fs(get_ds());     /* get user space block */
+		err = ip_rt_kill(&route);
+		set_fs(fs);           /* restore old block */
+
+		if (err) {
+
+		     printk(KERN_INFO "%s: Deleting of route failed.  Error: %d\n", 
+				card->devname,err);
+		     printk(KERN_INFO "%s: Address: %s\n",
+				dev->name,in_ntoa(dev->pa_dstaddr) );
+		} else {
+
+		     printk(KERN_INFO "%s: Removed route.\n",
+			((fr_channel_t*)dev->priv)->name);
+		     chan->route_flag = NO_ROUTE;
+
+		}
+		break;
+	}
+
+#endif	
+
+}
+
+
+
+/****** Frame Relay Firmware-Specific Functions *****************************/
+
+/*============================================================================
+ * Read firmware code version.
+ * o fill string str with firmware version info. 
+ */
+static int fr_read_version (sdla_t* card, char* str)
+{
+	fr_mbox_t* mbox = card->mbox;
+	int retry = MAX_CMD_RETRY;
+	int err;
+
+	do
 	{
 		mbox->cmd.command = FR_READ_CODE_VERSION;
 		mbox->cmd.length = 0;
@@ -2264,6 +2972,12 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	
 	} while (err && retry-- && fr_event(card, err, mbox));
+
+	/*NC Oct 12 2000 */
+	if (err != CMD_OK){
+		printk(KERN_ERR "%s: Frame Relay Configuration Failed: rc=0x%x\n",
+				card->devname,err);
+	}
 	
 	return err;
 }
@@ -2338,33 +3052,39 @@
 }
 
 /*============================================================================
- * Disable communications. 
+ * fr_comm_disable 
+ *
+ * Warning: This functin is called by the shutdown() procedure. It is void
+ *          since dev->priv are has already been deallocated and no
+ *          error checking is possible using fr_event() function.
  */
-static int fr_comm_disable (sdla_t* card)
+static void fr_comm_disable (sdla_t* card)
 {
 	fr_mbox_t* mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
 
-	do
-	{
-		mbox->cmd.command = FR_COMM_DISABLE;
-		mbox->cmd.length = 0;
-		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && fr_event(card, err, mbox));
-
-	retry = MAX_CMD_RETRY;
-
 	do {
 	mbox->cmd.command = FR_SET_MODEM_STATUS;
 	mbox->cmd.length = 1;
 	mbox->data[0] = 0;
 	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && fr_event(card, err, mbox));
+	} while (err && retry--);
 	
-	return err;
+	retry = MAX_CMD_RETRY;
+	
+	do
+	{
+		mbox->cmd.command = FR_COMM_DISABLE;
+		mbox->cmd.length = 0;
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	} while (err && retry--);
+
+	return;
 }
 
+
+
 /*============================================================================
  * Get communications error statistics. 
  */
@@ -2518,9 +3238,58 @@
 	return err;
 }
 
+
+static unsigned int fr_send_hdr (sdla_t*card, int dlci, unsigned int offset)
+{
+	netdevice_t *dev = find_channel(card,dlci);	
+	fr_channel_t *chan;
+
+	if (!dev || !(chan=dev->priv))
+		return offset;
+	
+	if (chan->fr_header_len){
+		sdla_poke(&card->hw, offset, chan->fr_header, chan->fr_header_len);
+	}
+	
+	return offset+chan->fr_header_len;
+}
+
 /*============================================================================
  * Send a frame on a selected DLCI.  
  */
+static int fr_send_data_header (sdla_t* card, int dlci, unsigned char attr, int len,
+	void *buf, unsigned char hdr_len)
+{
+	fr_mbox_t* mbox = card->mbox + 0x800;
+	int retry = MAX_CMD_RETRY;
+	int err;
+
+	do
+	{
+		mbox->cmd.dlci    = dlci;
+		mbox->cmd.attr    = attr;
+		mbox->cmd.length  = len+hdr_len;
+		mbox->cmd.command = FR_WRITE;
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	} while (err && retry-- && fr_event(card, err, mbox));
+
+	if (!err) {
+		fr_tx_buf_ctl_t* frbuf;
+ 
+               	if(card->hw.type == SDLA_S514)
+			frbuf = (void*)(*(unsigned long*)mbox->data +
+                        	card->hw.dpmbase);
+		else
+			frbuf = (void*)(*(unsigned long*)mbox->data -
+                        	FR_MB_VECTOR + card->hw.dpmbase);
+
+		sdla_poke(&card->hw, fr_send_hdr(card,dlci,frbuf->offset), buf, len);
+		frbuf->flag = 0x01;
+	}
+
+	return err;
+}
+
 static int fr_send (sdla_t* card, int dlci, unsigned char attr, int len,
 	void *buf)
 {
@@ -2538,7 +3307,6 @@
 	} while (err && retry-- && fr_event(card, err, mbox));
 
 	if (!err) {
-
 		fr_tx_buf_ctl_t* frbuf;
  
                	if(card->hw.type == SDLA_S514)
@@ -2555,6 +3323,7 @@
 	return err;
 }
 
+
 /****** Firmware Asynchronous Event Handlers ********************************/
 
 /*============================================================================
@@ -2577,46 +3346,41 @@
 
 		case FRRES_CHANNEL_DOWN:
 			{
-			struct net_device *dev;
+			netdevice_t *dev;
 
 			/* Remove all routes from associated DLCI's */
-			dev = card->wandev.dev;
-			while (dev) {
+			for (dev = card->wandev.dev; dev; dev = *((netdevice_t **)dev->priv)) {
 				fr_channel_t *chan = dev->priv;
 				if (chan->route_flag == ROUTE_ADDED) {
 					chan->route_flag = REMOVE_ROUTE;
-					card->poll = &process_route;
 				}
 
 				if (chan->inarp == INARP_CONFIGURED) {
 					chan->inarp = INARP_REQUEST;
 				}
 
-				dev = chan->slave;
+				/* If the link becomes disconnected then,
+                                 * all channels will be disconnected
+                                 * as well.
+                                 */
+				set_chan_state(dev,WAN_DISCONNECTED);
 			}
-
+				
 			wanpipe_set_state(card, WAN_DISCONNECTED);
 			return 1;
 			}
 
 		case FRRES_CHANNEL_UP:
 			{
-			struct net_device *dev;
-			int num_requests = 0;
+			netdevice_t *dev;
 
-			/* Remove all routes from associated DLCI's */
-			dev = card->wandev.dev;
-			while (dev) {
-				fr_channel_t *chan = dev->priv;	
-				if( chan->inarp == INARP_REQUEST ){
-					num_requests++;
-					chan->inarp_tick = jiffies;
-				}
-				dev = chan->slave;
+			/* FIXME: Only startup devices that are on the list */
+			
+			for (dev = card->wandev.dev; dev; dev = *((netdevice_t **)dev->priv)) {
+				
+				set_chan_state(dev,WAN_CONNECTED);
 			}
 
-			/* Allow timer interrupts */
-			if (num_requests) flags->imask |= 0x20;	
 			wanpipe_set_state(card, WAN_CONNECTED);
 			return 1;
 			}
@@ -2644,8 +3408,10 @@
  
 		case FRRES_CIR_OVERFLOW:
 			break;
+			
 		case FRRES_BUFFER_OVERFLOW:
 			break; 
+			
 		default:
 			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n"
 				, card->devname, mbox->cmd.command, event);
@@ -2684,13 +3450,13 @@
 	dlci_status_t* status = (void*)mbox->data;
 	int cnt = mbox->cmd.length / sizeof(dlci_status_t);
 	fr_channel_t *chan;
-	struct net_device* dev2;
+	netdevice_t* dev2;
 	
 
 	for (; cnt; --cnt, ++status) {
 
 		unsigned short dlci= status->dlci;
-		struct net_device* dev = find_channel(card, dlci);
+		netdevice_t* dev = find_channel(card, dlci);
 		
 		if (dev == NULL){
 			printk(KERN_INFO 
@@ -2707,7 +3473,7 @@
 					"%s: DLCI %u is inactive!\n",
 					card->devname, dlci);
 
-				if (dev && netif_running(dev))
+				if (dev && is_dev_running(dev))
 					set_chan_state(dev, WAN_DISCONNECTED);
 			}
 	
@@ -2717,12 +3483,14 @@
 					"%s: DLCI %u has been deleted!\n",
 					card->devname, dlci);
 
-				if (dev && netif_running(dev)) {
+				if (dev && is_dev_running(dev)){
+
 					fr_channel_t *chan = dev->priv;
 
 					if (chan->route_flag == ROUTE_ADDED) {
 						chan->route_flag = REMOVE_ROUTE;
-						card->poll = &process_route;
+						/* The state change will trigger
+                                                 * the fr polling routine */
 					}
 
 					if (chan->inarp == INARP_CONFIGURED) {
@@ -2740,16 +3508,15 @@
 				   DLCI(s) when they become active.
 			 	*/ 
 				chan->dlci_configured = DLCI_CONFIG_PENDING;
-			
-				if (dev && netif_running(dev))
-					set_chan_state(dev, WAN_CONNECTED);
+	
+				set_chan_state(dev, WAN_CONNECTED);
 		
 			}
 		}
 	}
 	
-	dev2 = card->wandev.dev;
-	while (dev2) {
+	for (dev2 =card->wandev.dev; dev2; dev2 = *((netdevice_t **)dev2->priv)){
+		
 		chan = dev2->priv;
 	
 		if (chan->dlci_configured == DLCI_CONFIG_PENDING) {
@@ -2758,7 +3525,6 @@
 			}
 		}
 
-		dev2 = chan->slave;
 	}
 	return 1;
 }
@@ -2767,8 +3533,7 @@
 static int fr_init_dlci (sdla_t *card, fr_channel_t *chan)
 {
 	fr_dlc_conf_t cfg;
-	fr508_flags_t* flags = card->flags;
-
+	
 	memset(&cfg, 0, sizeof(cfg));
 
 	if ( chan->cir_status == CIR_DISABLED) {
@@ -2786,22 +3551,16 @@
 	}
 	
 	if (fr_dlci_configure( card, &cfg , chan->dlci)){
-	printk(KERN_INFO 
-		"%s: DLCI Configure failed for %d\n",
-			card->devname, chan->dlci);
+		printk(KERN_INFO 
+			"%s: DLCI Configure failed for %d\n",
+				card->devname, chan->dlci);
 		return 1;	
 	}
 	
 	chan->dlci_configured = DLCI_CONFIGURED;
 
-	/* Allow timer interrupts */
-	if( chan->inarp == INARP_REQUEST && card->wandev.state == WAN_CONNECTED) {
-		chan->inarp_tick = jiffies;
-		flags->imask |= 0x20;	
-	}	
-
 	/* Read the interface byte mapping into the channel 
-	   structure.
+	 * structure.
 	 */
 	read_DLCI_IB_mapping( card, chan );
 
@@ -2812,7 +3571,7 @@
 /*============================================================================
  * Update channel state. 
  */
-static int update_chan_state (struct net_device* dev)
+static int update_chan_state (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
@@ -2828,14 +3587,25 @@
 	} while (err && retry-- && fr_event(card, err, mbox));
 
 	if (!err) {
-
+		
 		unsigned short* list = (void*)mbox->data;
 		int cnt = mbox->cmd.length / sizeof(short);
-
+		
+		err=1;
+		
 		for (; cnt; --cnt, ++list) {
 
 			if (*list == chan->dlci) {
  				set_chan_state(dev, WAN_CONNECTED);
+
+
+				/* May 23 2000. NC
+				 * When a dlci is added or restarted,
+                                 * the dlci_int_interface pointer must
+				 * be reinitialized.  */
+				if (!chan->dlci_int_interface){
+					err=fr_init_dlci (card,chan);
+				}
 				break;
 			}
 		}
@@ -2847,16 +3617,12 @@
 /*============================================================================
  * Set channel state.
  */
-static void set_chan_state (struct net_device* dev, int state)
+static void set_chan_state (netdevice_t* dev, int state)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
 
-	if (chan->state != state) {
+	if (chan->common.state != state) {
 
 		switch (state) {
 
@@ -2864,6 +3630,12 @@
 				printk(KERN_INFO
 					"%s: Interface %s: DLCI %d connected\n",
 					card->devname, dev->name, chan->dlci);
+
+				/* If the interface was previoulsy down,
+                                 * bring it up, since the channel is active */
+
+				trigger_fr_poll (dev);
+				trigger_fr_arp  (dev);
 				break;
 
 			case WAN_CONNECTING:
@@ -2876,20 +3648,29 @@
 				printk (KERN_INFO 
 				    "%s: Interface %s: DLCI %d disconnected!\n",
 					card->devname, dev->name, chan->dlci);
+			
+				/* If the interface is up, bring it down,
+                                 * since the channel is now disconnected */
+				trigger_fr_poll (dev);
 				break;
 		}
 
-		chan->state = state;
+		chan->common.state = state;
 	}
 
 	chan->state_tick = jiffies;
-	restore_flags(flags);
 }
 
 /*============================================================================
  * Find network device by its channel number.
+ *
+ * We need this critical flag because we change
+ * the dlci_to_dev_map outside the interrupt.
+ *
+ * NOTE: del_if() functions updates this array, it uses
+ *       the spin locks to avoid corruption.
  */
-static struct net_device* find_channel (sdla_t* card, unsigned dlci)
+static netdevice_t* find_channel (sdla_t* card, unsigned dlci)
 {
 	if(dlci > HIGHEST_VALID_DLCI)
 		return NULL;
@@ -2945,9 +3726,15 @@
                                 struct sk_buff *skb, int dlci)
 {
         int udp_pkt_stored = 0;
-
-        if(!card->u.f.udp_pkt_lgth &&(skb->len <= MAX_LGTH_UDP_MGNT_PKT)){
-                card->u.f.udp_pkt_lgth = skb->len;
+	
+	netdevice_t *dev=find_channel(card,dlci);
+	fr_channel_t *chan;
+	
+	if (!dev || !(chan=dev->priv))
+		return 1;
+	
+        if(!card->u.f.udp_pkt_lgth && (skb->len <= MAX_LGTH_UDP_MGNT_PKT)){
+                card->u.f.udp_pkt_lgth = skb->len + chan->fr_header_len;
                 card->u.f.udp_type = udp_type;
                 card->u.f.udp_pkt_src = udp_pkt_src;
                 card->u.f.udp_dlci = dlci;
@@ -2960,8 +3747,12 @@
 							dlci);
 	}
 
-        dev_kfree_skb(skb);
-
+        if(udp_pkt_src == UDP_PKT_FRM_STACK){
+                wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+                wan_dev_kfree_skb(skb, FREE_READ);
+	}
+		
         return(udp_pkt_stored);
 }
 
@@ -2982,7 +3773,7 @@
 	int err;
 	struct timeval tv;
 	int udp_mgmt_req_valid = 1;
-        struct net_device* dev;
+        netdevice_t* dev;
         fr_channel_t* chan;
         fr_udp_pkt_t *fr_udp_pkt;
 	unsigned short num_trc_els;
@@ -2995,7 +3786,14 @@
 
 	/* Find network interface for this packet */
 	dev = find_channel(card, dlci);
-        chan = dev->priv;
+	if (!dev){
+		card->u.f.udp_pkt_lgth = 0;
+		return 1;
+	}
+        if ((chan = dev->priv) == NULL){
+		card->u.f.udp_pkt_lgth = 0;
+		return 1;
+	}
 
 	/* If the UDP packet is from the network, we are going to have to 
 	   transmit a response. Before doing so, we must check to see that
@@ -3003,39 +3801,49 @@
 	   that we are not already in a 'delayed transmit' state.
 	*/
 	if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-		if (test_bit(0, (void*)&card->wandev.critical) ||
-			test_bit(2, (void*)&card->wandev.critical)) {
-			return 0;
-  		}
-		if((netif_queue_stopped(dev)) || (card->u.f.tx_interrupts_pending)) {
-			return 0;
+		if (check_tx_status(card,dev)){
+			card->u.f.udp_pkt_lgth = 0;
+			return 1;
 		}
         }
 
         fr_udp_pkt = (fr_udp_pkt_t *)card->u.f.udp_pkt_data;
 
-	switch(fr_udp_pkt->cblock.command) {
+	if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+	
+		switch(fr_udp_pkt->cblock.command) {
 
-		case FPIPE_ENABLE_TRACING:
-		case FPIPE_DISABLE_TRACING:
-		case FPIPE_GET_TRACE_INFO:
-		case FR_SET_FT1_MODE:
-			if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-                    		chan->drvstats_gen.
-					UDP_PIPE_mgmt_direction_err ++;
+			case FR_READ_MODEM_STATUS:
+			case FR_READ_STATUS:
+			case FPIPE_ROUTER_UP_TIME:
+			case FR_READ_ERROR_STATS:
+			case FPIPE_DRIVER_STAT_GEN:
+			case FR_READ_STATISTICS:
+			case FR_READ_ADD_DLC_STATS:
+			case FR_READ_CONFIG:
+			case FR_READ_CODE_VERSION:
+				udp_mgmt_req_valid = 1;
+				break;
+			default:
 				udp_mgmt_req_valid = 0;
 				break;
-			}
-
-		default:
-			break;
-       	}
+		}
+	}
 
 	if(!udp_mgmt_req_valid) {
 		/* set length to 0 */
 		fr_udp_pkt->cblock.length = 0;
 		/* set return code */
 		fr_udp_pkt->cblock.result = 0xCD; 
+		
+		chan->drvstats_gen.UDP_PIPE_mgmt_direction_err ++;
+
+		if (net_ratelimit()){	
+			printk(KERN_INFO 
+			"%s: Warning, Illegal UDP command attempted from network: %x\n",
+			card->devname,fr_udp_pkt->cblock.command);
+		}
+		
 	} else {   
            
 		switch(fr_udp_pkt->cblock.command) {
@@ -3074,7 +3882,7 @@
 				/* Calculate the maximum trace data area in */
 				/* the UDP packet */
 				card->u.f.trc_bfr_space=(MAX_LGTH_UDP_MGNT_PKT -
-					sizeof(fr_encap_hdr_t) -
+					//sizeof(fr_encap_hdr_t) -
 					sizeof(ip_pkt_t) -
 					sizeof(udp_pkt_t) -
 					sizeof(wp_mgmt_t) -
@@ -3204,7 +4012,7 @@
                 case FPIPE_FT1_READ_STATUS:
 			sdla_peek(&card->hw, 0xF020,
 				&fr_udp_pkt->data[0x00] , 2);
-			fr_udp_pkt->cblock.length = 2;
+			fr_udp_pkt->cblock.length = mbox->cmd.length = 2;
 			fr_udp_pkt->cblock.result = 0;
 			break;
 
@@ -3220,44 +4028,32 @@
 						chan->router_start_time;
     	                *(unsigned long *)&fr_udp_pkt->data =
     				chan->router_up_time;	
-			mbox->cmd.length = 4;
+			mbox->cmd.length = fr_udp_pkt->cblock.length = 4;
+			fr_udp_pkt->cblock.result = 0;
 			break;
 
-
-		case FR_FT1_STATUS_CTRL:
-			if(fr_udp_pkt->data[0] == 1) {
-				if(rCount++ != 0 ){
-					fr_udp_pkt->cblock.result = 0;
-					mbox->cmd.length = 1;
-					break;
-				} 
-			}
-           
-			/* Disable FT1 MONITOR STATUS */
-                        if(fr_udp_pkt->data[0] == 0) {
-				if( --rCount != 0) {
-                                        fr_udp_pkt->cblock.result = 0;
-					mbox->cmd.length = 1;
-					break;
-				} 
-			}  
-
 		case FPIPE_DRIVER_STAT_IFSEND:
 			memcpy(fr_udp_pkt->data,
 				&chan->drvstats_if_send.if_send_entry,
 				sizeof(if_send_stat_t));
-			mbox->cmd.length = sizeof(if_send_stat_t);	
+			mbox->cmd.length = fr_udp_pkt->cblock.length =sizeof(if_send_stat_t);	
+			fr_udp_pkt->cblock.result = 0;
 			break;
 	
 		case FPIPE_DRIVER_STAT_INTR:
+
 			memcpy(fr_udp_pkt->data,
                                 &card->statistics.isr_entry,
                                 sizeof(global_stats_t));
+
                         memcpy(&fr_udp_pkt->data[sizeof(global_stats_t)],
                                 &chan->drvstats_rx_intr.rx_intr_no_socket,
                                 sizeof(rx_intr_stat_t));
-			mbox->cmd.length = sizeof(global_stats_t) +
+
+			mbox->cmd.length = fr_udp_pkt->cblock.length = 
+					sizeof(global_stats_t) +
 					sizeof(rx_intr_stat_t);
+			fr_udp_pkt->cblock.result = 0;
 			break;
 
 		case FPIPE_DRIVER_STAT_GEN:
@@ -3268,13 +4064,34 @@
                         memcpy(&fr_udp_pkt->data[sizeof(pipe_mgmt_stat_t)],
                                &card->statistics, sizeof(global_stats_t));
 
-                        fr_udp_pkt->cblock.result = 0;
-                        fr_udp_pkt->cblock.length = sizeof(global_stats_t)+
+                        mbox->cmd.length = fr_udp_pkt->cblock.length = sizeof(global_stats_t)+
                                                      sizeof(rx_intr_stat_t);
-                        mbox->cmd.length = fr_udp_pkt->cblock.length;
+			fr_udp_pkt->cblock.result = 0;
                         break;
-		
+
+
+		case FR_FT1_STATUS_CTRL:
+			if(fr_udp_pkt->data[0] == 1) {
+				if(rCount++ != 0 ){
+					fr_udp_pkt->cblock.result = 0;
+					mbox->cmd.length = 1;
+					break;
+				} 
+			}
+           
+			/* Disable FT1 MONITOR STATUS */
+                        if(fr_udp_pkt->data[0] == 0) {
+				if( --rCount != 0) {
+                                        fr_udp_pkt->cblock.result = 0;
+					mbox->cmd.length = 1;
+					break;
+				} 
+			}  
+			goto udp_mgmt_dflt;
+
+			
 		default:
+udp_mgmt_dflt:
  			do {
 				memcpy(&mbox->cmd,
 					&fr_udp_pkt->cblock.command,
@@ -3312,12 +4129,19 @@
         len = reply_udp(card->u.f.udp_pkt_data, mbox->cmd.length);
 
         if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-		
-                err = fr_send(card, dlci, 0, len, card->u.f.udp_pkt_data);
-		if (err) 
+
+		chan->fr_header_len=2;
+		chan->fr_header[0]=Q922_UI;
+		chan->fr_header[1]=NLPID_IP;
+			
+		err = fr_send_data_header(card, dlci, 0, len, 
+			card->u.f.udp_pkt_data,chan->fr_header_len);
+		if (err){ 
 			chan->drvstats_gen.UDP_PIPE_mgmt_adptr_send_passed ++;
-		else
+		}else{
 			chan->drvstats_gen.UDP_PIPE_mgmt_adptr_send_failed ++;
+		}
+		
 	} else {
 		/* Allocate socket buffer */
 		if((new_skb = dev_alloc_skb(len)) != NULL) {
@@ -3326,22 +4150,12 @@
 			buf = skb_put(new_skb, len);
 			memcpy(buf, card->u.f.udp_pkt_data, len);
         
-			/* Decapsulate packet and pass it up the protocol 
-			   stack */
+			chan->drvstats_gen.
+				UDP_PIPE_mgmt_passed_to_stack ++;
 			new_skb->dev = dev;
-			buf = skb_pull(new_skb, 1);  /* remove hardware header*/
-			
-			if(!wanrouter_type_trans(new_skb, dev)) {
-
-				chan->drvstats_gen.
-					UDP_PIPE_mgmt_not_passed_to_stack ++;
-				/* can't decapsulate packet */
-				dev_kfree_skb(new_skb);
-			} else {
-				chan->drvstats_gen.
-					UDP_PIPE_mgmt_passed_to_stack ++;
-				netif_rx(new_skb);
-                	}
+			new_skb->protocol = htons(ETH_P_IP);
+			new_skb->mac.raw = new_skb->data;
+			netif_rx(new_skb);
             	
 		} else {
 			chan->drvstats_gen.UDP_PIPE_mgmt_no_socket ++;
@@ -3360,15 +4174,19 @@
  * Send Inverse ARP Request
  */
 
-int send_inarp_request(sdla_t *card, struct net_device *dev)
+int send_inarp_request(sdla_t *card, netdevice_t *dev)
 {
+	int err=0;
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
 	arphdr_1490_t *ArpPacket;
 	arphdr_fr_t *arphdr;
 	fr_channel_t *chan = dev->priv;
 	struct in_device *in_dev;
 
 	in_dev = dev->ip_ptr;
-	
+
 	if(in_dev != NULL ) {	
 
 		ArpPacket = kmalloc(sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t), GFP_ATOMIC);
@@ -3396,16 +4214,66 @@
 		arphdr->ar_tha = 0; 		/* dst HW DLCI - Doesn't matter */
 		arphdr->ar_tip = 0;		/* Remote Address -- what we want */
 
-		printk(KERN_INFO "%s: Sending InARP request on DLCI %d.\n", card->devname, chan->dlci);
-                fr_send(card, chan->dlci, 0,
-		   sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t),
-		   (void *)ArpPacket);
+		err = fr_send(card, chan->dlci, 0, sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t),
+		   			(void *)ArpPacket);
+
+		if (!err){
+			printk(KERN_INFO "\n%s: Sending InARP request on DLCI %d.\n", 
+				card->devname, chan->dlci);
+			clear_bit(ARP_CRIT,&card->wandev.critical);
+		}
+
 		kfree(ArpPacket);
+	}else{
+		printk(KERN_INFO "%s: INARP ERROR: %s doesn't have a local IP address!\n",
+				card->devname,dev->name);
+		return 1;
 	}
 
-	return 1;
-}
-	
+#else
+        arphdr_1490_t *ArpPacket;
+        arphdr_fr_t *arphdr;
+        fr_channel_t *chan = dev->priv;
+
+        ArpPacket = kmalloc(sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t), GFP_ATOMIC);
+        /* SNAP Header indicating ARP */
+        ArpPacket->control      = 0x03;
+        ArpPacket->pad          = 0x00;
+        ArpPacket->NLPID        = 0x80;
+        ArpPacket->OUI[0]       = 0;
+        ArpPacket->OUI[1]       = 0;
+        ArpPacket->OUI[2]       = 0;
+        ArpPacket->PID          = 0x0608;
+
+        arphdr = (arphdr_fr_t *)(ArpPacket + 1); // Go to ARP Packet
+       /* InARP request */
+        arphdr->ar_hrd = 0x0F00;        /* Frame Relay HW type */
+        arphdr->ar_pro = 0x0008;        /* IP Protocol         */
+        arphdr->ar_hln = 2;             /* HW addr length      */
+        arphdr->ar_pln = 4;             /* IP addr length      */
+        arphdr->ar_op = htons(0x08);    /* InARP Request       */
+        arphdr->ar_sha = 0;             /* src HW DLCI - Doesn't matter */
+        arphdr->ar_sip = dev->pa_addr;  /* Local Address       */
+        arphdr->ar_tha = 0;             /* dst HW DLCI - Doesn't matter */
+        arphdr->ar_tip = 0;             /* Remote Address -- what we want */
+
+        printk(KERN_INFO "%s: Sending InARP request on DLCI %d.\n", card->devname, chan->dlci);
+        err = fr_send(card, chan->dlci, 0,
+                   sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t),
+                   (void *)ArpPacket);
+	
+	if (!err){
+         	printk(KERN_INFO "\n%s: Sending InARP request on DLCI %d.\n",
+                        card->devname, chan->dlci);
+                clear_bit(ARP_CRIT,&card->wandev.critical);
+        }
+
+        kfree(ArpPacket);
+#endif
+
+	return 0;
+}
+	
 
 /*==============================================================================
  * Check packet for ARP Type
@@ -3426,123 +4294,289 @@
  * Process ARP Packet Type
  */
 
-int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, struct net_device* dev)
+int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, netdevice_t* dev)
 {
 
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	arphdr_fr_t *arphdr = (arphdr_fr_t *)(ArpPacket + 1); /* Skip header */
 	fr_rx_buf_ctl_t* frbuf = card->rxmb;
 	struct in_device *in_dev;
+	fr_channel_t *chan = dev->priv;		
+#else
+  	arphdr_fr_t *arphdr = (arphdr_fr_t *)(ArpPacket + 1); /* Skip header */
+        fr_rx_buf_ctl_t* frbuf = card->rxmb;
+#endif
+	
+	/* Before we transmit ARP packet, we must check 
+	 * to see that we are not currently transmitting a 
+	 * frame (in 'if_send()') and that we are not 
+	 * already in a 'delayed transmit' state. */
+	if (check_tx_status(card,dev)){
+		if (net_ratelimit()){ 	
+			printk(KERN_INFO "%s: Disabling comminication to process ARP\n",
+					card->devname);
+		}
+		set_bit(ARP_CRIT,&card->wandev.critical);
+		return 0;
+	}
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 
 	in_dev = dev->ip_ptr;
-	if( in_dev != NULL && in_dev->ifa_list != NULL) {
-		switch (ntohs(arphdr->ar_op)) {
 
-		case 0x08:  // Inverse ARP request  -- Send Reply, add route.
+	/* Check that IP addresses exist for our network address */
+	if (in_dev == NULL || in_dev->ifa_list == NULL) 
+		return -1;
+
+	switch (ntohs(arphdr->ar_op)) {
+
+	case 0x08:  // Inverse ARP request  -- Send Reply, add route.
 			
-			/* Check for valid Address */
-			printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Req\n", ((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+		/* Check for valid Address */
+		printk(KERN_INFO "%s: Recvd PtP addr -InArp Req: %s\n", 
+			card->devname, in_ntoa(arphdr->ar_sip));
 
-			if ((in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) {
-				printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", card->devname);
-				printk(KERN_INFO "mask %X\n", in_dev->ifa_list->ifa_mask);
-				printk(KERN_INFO "local %X\n", in_dev->ifa_list->ifa_local);
-				return -1;
-			}
-		
-			if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) {
-				printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", card->devname);
-				return -1;
-			}
+
+		/* Check that the network address is the same as ours, only
+                 * if the netowrk mask is not 255.255.255.255. Otherwise
+                 * this check would not make sense */
+
+		if (in_dev->ifa_list->ifa_mask != 0xFFFFFFFF && 
+		    (in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != 
+		    (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)){
+
+			printk(KERN_INFO 
+				"%s: Invalid PtP address. %s  InARP ignored.\n", 
+					card->devname,in_ntoa(arphdr->ar_sip));
+
+			printk(KERN_INFO "%s: mask %s\n", 
+				card->devname, in_ntoa(in_dev->ifa_list->ifa_mask));
+				printk(KERN_INFO "%s: local %s\n", 
+				card->devname,in_ntoa(in_dev->ifa_list->ifa_local));
+			return -1;
+		}
+
+		if (in_dev->ifa_list->ifa_local == arphdr->ar_sip){
+			printk(KERN_INFO 
+				"%s: Local addr = PtP addr.  InARP ignored.\n", 
+					card->devname);
+			return -1;
+		}
 	
-			arphdr->ar_op = htons(0x09);	/* InARP Reply */
+		arphdr->ar_op = htons(0x09);	/* InARP Reply */
 
-			/* Set addresses */
-			arphdr->ar_tip = arphdr->ar_sip;
-			arphdr->ar_sip = in_dev->ifa_list->ifa_local;
+		/* Set addresses */
+		arphdr->ar_tip = arphdr->ar_sip;
+		arphdr->ar_sip = in_dev->ifa_list->ifa_local;
 
-                        fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket);
+		chan->ip_local = in_dev->ifa_list->ifa_local;
+		chan->ip_remote = arphdr->ar_sip;
 
-			/* Modify Point-to-Point Address */
-			{
-			struct ifreq if_info;
-			struct sockaddr_in *if_data;
-			mm_segment_t fs = get_fs();
-			int err;
-
-			/* Set remote addresses */
-			memset(&if_info, 0, sizeof(if_info));
-			strcpy(if_info.ifr_name, dev->name);
-
-          		set_fs(get_ds());     /* get user space block */ 
-	
-			if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
-			if_data->sin_addr.s_addr = arphdr->ar_tip;
-			if_data->sin_family = AF_INET;
-			err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+		fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket);
 
-                	set_fs(fs);           /* restore old block */
+		if (test_bit(ARP_CRIT,&card->wandev.critical)){
+			if (net_ratelimit()){ 	
+				printk(KERN_INFO "%s: ARP Processed Enabling Communication!\n",
+					card->devname);
 			}
-				
-			/* Add Route Flag */
-			/* The route will be added in the polling routine so
-			   that it is not interrupt context. */
+		}
+		clear_bit(ARP_CRIT,&card->wandev.critical);
+		
+		chan->ip_local = in_dev->ifa_list->ifa_local;
+		chan->ip_remote = arphdr->ar_sip;
 
-			((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
-			card->poll	= &process_route;
+		/* Add Route Flag */
+		/* The route will be added in the polling routine so
+		   that it is not interrupt context. */
 
-			break;
+		chan->route_flag = ADD_ROUTE;
+		trigger_fr_poll (dev);
 
-		case 0x09:  // Inverse ARP reply
+		break;
 
-			/* Check for valid Address */
-			printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", ((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+	case 0x09:  // Inverse ARP reply
 
-			if ((in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) {
-				printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", card->devname);
-				return -1;
-			}
+		/* Check for valid Address */
+		printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", 
+				card->devname, in_ntoa(arphdr->ar_sip));
 
-			if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) {
-				printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", card->devname);
-				return -1;
-			}			
 
-			/* Modify Point-to-Point Address */
-			{
-			struct ifreq if_info;
-			struct sockaddr_in *if_data;
-			mm_segment_t fs = get_fs();
-			int err;
+		/* Compare network addresses, only if network mask
+                 * is not 255.255.255.255  It would not make sense
+                 * to perform this test if the mask was all 1's */
 
-			/* Set remote addresses */
-			memset(&if_info, 0, sizeof(if_info));
-			strcpy(if_info.ifr_name, dev->name);
+		if (in_dev->ifa_list->ifa_mask != 0xffffffff &&
+		    (in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != 
+			(in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) {
 
-          		set_fs(get_ds());     /* get user space block */ 
-	
-			if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
-			if_data->sin_addr.s_addr = arphdr->ar_sip;
-			if_data->sin_family = AF_INET;
-			err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+			printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", 
+					card->devname);
+			return -1;
+		}
 
-                	set_fs(fs);           /* restore old block */
-			}
+		/* Make sure that the received IP address is not
+                 * the same as our own local address */
+		if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) {
+			printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", 
+				card->devname);
+			return -1;
+		}			
 
-			/* Add Route Flag */
-			/* The route will be added in the polling routine so
-			   that it is not interrupt context. */
+		chan->ip_local  = in_dev->ifa_list->ifa_local;
+		chan->ip_remote = arphdr->ar_sip;
 
-			((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
-			((fr_channel_t *) dev->priv)->inarp = INARP_CONFIGURED;
-			card->poll	= &process_route;
-			
-			break;
-		default:  // ARP's and RARP's -- Shouldn't happen.
-		}
+		/* Add Route Flag */
+		/* The route will be added in the polling routine so
+		   that it is not interrupt context. */
+
+		chan->route_flag = ADD_ROUTE;
+		chan->inarp = INARP_CONFIGURED;
+		trigger_fr_poll(dev);
+		
+		break;
+	default:  // ARP's and RARP's -- Shouldn't happen.
 	}
 
 	return 0;	
+#else
+
+        switch (ntohs(arphdr->ar_op)) {
+
+                case 0x08:  // Inverse ARP request  -- Send Reply, add route.
+
+                        /* Check for valid Address */
+                        printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Req\n", 
+				((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+
+
+			if (dev->pa_mask != 0xFFFFFFFF){
+
+	                        if ((dev->pa_mask & arphdr->ar_sip) != (dev->pa_mask & dev->pa_addr)) {
+        	                        printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", 
+							card->devname);
+                        	        return -1;
+                        	}
+			}
+
+                        if (dev->pa_addr == arphdr->ar_sip) {
+                                printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", 
+						card->devname);
+                                return -1;
+                        }
+
+                        arphdr->ar_op = htons(0x09);    /* InARP Reply */
+
+                        /* Set addresses */
+                        arphdr->ar_tip = arphdr->ar_sip;
+                        arphdr->ar_sip = dev->pa_addr;
+
+                        fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket);
+
+			clear_bit(ARP_CRIT,&card->wandev.critical);
+
+                        /* Modify Point-to-Point Address */
+                        dev->pa_dstaddr = arphdr->ar_tip;
+
+                        /* Add Route Flag */
+                        /* The route will be added in the polling routine so
+                           that it is not interrupt context. */
+                        ((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
+			trigger_fr_poll(dev);
+
+                        break;
+                case 0x09:  // Inverse ARP reply
+
+                        /* Check for valid Address */
+                        printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", 
+				((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+
+                        if ((dev->pa_mask & arphdr->ar_sip) != (dev->pa_mask & dev->pa_addr)) {
+                                printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", 
+					card->devname);
+                                return -1;
+                        }
+
+                        if (dev->pa_addr == arphdr->ar_sip) {
+                                printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", 
+					card->devname);
+                                return -1;
+                        }
+
+                        /* Modify Point-to-Point Address */
+                        dev->pa_dstaddr = arphdr->ar_sip;
+                        /* Add Route Flag */
+                        /* The route will be added in the polling routine so
+                           that it is not interrupt context. */
+
+                        ((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
+                        ((fr_channel_t *) dev->priv)->inarp = INARP_CONFIGURED;
+			trigger_fr_poll(dev);
+
+                        break;
+                default:  // ARP's and RARP's -- Shouldn't happen.
+        }
+
+        return 0;
+
+#endif
+}
+
+
+/*============================================================
+ * trigger_fr_arp
+ *
+ * Description:
+ * 	Add an fr_arp() task into a arp
+ *      timer handler for a specific dlci/interface.  
+ *      This will kick the fr_arp() routine 
+ *      within the specified time interval. 
+ *
+ * Usage:
+ * 	This timer is used to send ARP requests at
+ *      certain time intervals. 
+ * 	Called by an interrupt to request an action
+ *      at a later date.
+ */	
+
+static void trigger_fr_arp (netdevice_t *dev)
+{
+	fr_channel_t* chan = dev->priv;
+
+	del_timer(&chan->fr_arp_timer);
+	chan->fr_arp_timer.expires = jiffies + (chan->inarp_interval * HZ);
+	add_timer(&chan->fr_arp_timer);
+	return;
+}
+
+
+
+/*==============================================================================
+ * ARP Request Action
+ *
+ *	This funciton is called by timer interrupt to send an arp request
+ *      to the remote end.
+ */
+
+static void fr_arp (unsigned long data)
+{
+	netdevice_t *dev = (netdevice_t *)data;
+	fr_channel_t *chan = dev->priv;
+	volatile sdla_t *card = chan->card;
+	fr508_flags_t* flags = card->flags;
+
+	/* Send ARP packets for all devs' until
+         * ARP state changes to CONFIGURED */
+
+	if (chan->inarp == INARP_REQUEST &&
+	    chan->common.state == WAN_CONNECTED && 
+	    card->wandev.state == WAN_CONNECTED){
+		set_bit(0,&chan->inarp_ready);
+		card->u.f.timer_int_enabled |= TMR_INT_ENABLED_ARP;
+		flags->imask |= FR_INTR_TIMER;	
+	}
+ 
+	return;
 }
 	
 
@@ -3555,11 +4589,6 @@
 	fr_mbox_t* mb = card->mbox;
 	int err,i;
 
-	/* The critical flag is unset here because we want to get into the
-	   ISR without the flag already set. The If_open sets the flag.
-	*/
-        clear_bit(1, (void*)&card->wandev.critical);
-
         err = fr_set_intr_mode(card, FR_INTR_READY, card->wandev.mtu, 0 );
 	
 	if (err == CMD_OK) {
@@ -3582,7 +4611,6 @@
 	if( err != CMD_OK ) 
 		return err;
 
-	set_bit(1, (void*)&card->wandev.critical);
 	return 0;
 }
 
@@ -3593,6 +4621,9 @@
 {
 	fr_udp_pkt_t *fr_udp_pkt = (fr_udp_pkt_t *)skb->data;
 
+	/* Quick HACK */
+	
+	
         if((fr_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) &&
 		(fr_udp_pkt->ip_pkt.ver_inet_hdr_length == 0x45) &&
 		(fr_udp_pkt->udp_pkt.udp_dst_port == 
@@ -3600,10 +4631,10 @@
 		(fr_udp_pkt->wp_mgmt.request_reply == 
 		UDPMGMT_REQUEST)) {
                         if(!strncmp(fr_udp_pkt->wp_mgmt.signature,
-                                UDPMGMT_FPIPE_SIGNATURE, 8))
+                                UDPMGMT_FPIPE_SIGNATURE, 8)){
                                 return UDP_FPIPE_TYPE;
+			}
 	}
-
         return UDP_INVALID_TYPE;
 }
 
@@ -3674,38 +4705,772 @@
 		card->devname, chan->dlci);
 }
 
+
+
 void s508_s514_lock(sdla_t *card, unsigned long *smp_flags)
 {
-
 	if (card->hw.type != SDLA_S514){
-#ifdef CONFIG_SMP
-		spin_lock_irqsave(&card->lock, *smp_flags);
+
+#if defined(__SMP__) || defined(LINUX_2_4)
+		spin_lock_irqsave(&card->wandev.lock, *smp_flags);
 #else
-    		disable_irq(card->hw.irq);
+          	disable_irq(card->hw.irq);
 #endif
-	}
-#ifdef CONFIG_SMP
-	else{
-		spin_lock(&card->lock);
-	}	
+	}else{
+#if defined(__SMP__) || defined(LINUX_2_4)
+		spin_lock(&card->u.f.if_send_lock);
 #endif
+	}
+	return;
 }
 
+
 void s508_s514_unlock(sdla_t *card, unsigned long *smp_flags)
 {
 	if (card->hw.type != SDLA_S514){
-#ifdef CONFIG_SMP
-		spin_unlock_irqrestore(&card->lock, *smp_flags);
+
+#if defined(__SMP__) || defined(LINUX_2_4)
+		spin_unlock_irqrestore (&card->wandev.lock, *smp_flags);
 #else
-    		enable_irq(card->hw.irq);
+          	enable_irq(card->hw.irq);
+#endif
+	}else{
+#if defined(__SMP__) || defined(LINUX_2_4)
+		spin_unlock(&card->u.f.if_send_lock);
 #endif
 	}
-#ifdef CONFIG_SMP
-	else{
-		spin_unlock(&card->lock);
+	return;
+}
+
+
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
+/*----------------------------------------------------------------------
+                  RECEIVE INTERRUPT: BOTTOM HALF HANDLERS 
+ ----------------------------------------------------------------------*/
+
+
+/*========================================================
+ * bh_enqueue
+ *
+ * Description:
+ *	Insert a received packed into a circular
+ *      rx queue.  This packed will be picked up 
+ *      by fr_bh() and sent up the stack to the
+ *      user.
+ *       	
+ * Usage: 
+ *	This function is called by rx interrupt,
+ *      in API mode.
+ *
+ */
+
+static int bh_enqueue (netdevice_t *dev, struct sk_buff *skb)
+{
+	/* Check for full */
+	fr_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+
+
+	if (atomic_read(&chan->bh_buff_used) == MAX_BH_BUFF){
+		++card->wandev.stats.rx_dropped;
+		wan_dev_kfree_skb(skb, FREE_READ);
+		return 1; 
+	}
+
+	((bh_data_t *)&chan->bh_head[chan->bh_write])->skb = skb;
+
+	if (chan->bh_write == (MAX_BH_BUFF-1)){
+		chan->bh_write=0;
+	}else{
+		++chan->bh_write;
+	}
+
+	atomic_inc(&chan->bh_buff_used);
+
+	return 0;
+}
+
+
+/*========================================================
+ * trigger_fr_bh
+ *
+ * Description:
+ * 	Kick the fr_bh() handler
+ *
+ * Usage:
+ *	rx interrupt calls this function during
+ *      the API mode. 
+ */
+
+static void trigger_fr_bh (fr_channel_t *chan)
+{
+	if (!test_and_set_bit(0,&chan->tq_working)){
+		wanpipe_queue_tq(&chan->common.wanpipe_task);
+		wanpipe_mark_bh();
+	}
+}
+
+
+/*========================================================
+ * fr_bh
+ *
+ * Description:
+ *	Frame relay receive BH handler. 
+ *	Dequeue data from the BH circular 
+ *	buffer and pass it up the API sock.
+ *       	
+ * Rationale: 
+ *	This fuction is used to offload the 
+ *	rx_interrupt during API operation mode.  
+ *	The fr_bh() function executes for each 
+ *	dlci/interface.  
+ * 
+ *      Once receive interrupt copies data from the
+ *      card into an skb buffer, the skb buffer
+ *  	is appended to a circular BH buffer.
+ *  	Then the interrupt kicks fr_bh() to finish the
+ *      job at a later time (no within the interrupt).
+ *       
+ * Usage:
+ * 	Interrupts use this to defer a taks to 
+ *      a polling routine.
+ *
+ */	
+
+static void fr_bh (netdevice_t * dev)
+{
+	fr_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	struct sk_buff *skb;
+
+	if (atomic_read(&chan->bh_buff_used) == 0){
+		clear_bit(0, &chan->tq_working);
+		return;
+	}
+
+	while (atomic_read(&chan->bh_buff_used)){
+
+		if (chan->common.sk == NULL || chan->common.func == NULL){
+			clear_bit(0, &chan->tq_working);
+			return;
+		}
+
+		skb  = ((bh_data_t *)&chan->bh_head[chan->bh_read])->skb;
+
+		if (skb != NULL){
+
+			if (chan->common.sk == NULL || chan->common.func == NULL){
+				++card->wandev.stats.rx_dropped;
+				++chan->ifstats.rx_dropped;
+				wan_dev_kfree_skb(skb, FREE_READ);
+				fr_bh_cleanup(dev);
+				continue;
+			}
+
+			if (chan->common.func(skb,dev,chan->common.sk) != 0){
+				/* Sock full cannot send, queue us for
+                                 * another try */
+				atomic_set(&chan->common.receive_block,1);
+				return;
+			}else{
+				fr_bh_cleanup(dev);
+			}
+		}else{
+			fr_bh_cleanup(dev);
+		}
 	}	
+	clear_bit(0, &chan->tq_working);
+
+	return;
+}
+
+static int fr_bh_cleanup (netdevice_t *dev)
+{
+	fr_channel_t* chan = dev->priv;
+
+	((bh_data_t *)&chan->bh_head[chan->bh_read])->skb = NULL;
+
+	if (chan->bh_read == (MAX_BH_BUFF-1)){
+		chan->bh_read=0;
+	}else{
+		++chan->bh_read;	
+	}
+
+	atomic_dec(&chan->bh_buff_used);
+	return 0;
+}
+#endif
+
+
+/*----------------------------------------------------------------------
+               POLL BH HANDLERS AND KICK ROUTINES 
+ ----------------------------------------------------------------------*/
+
+/*============================================================
+ * trigger_fr_poll
+ *
+ * Description:
+ * 	Add a fr_poll() task into a tq_scheduler bh handler
+ *      for a specific dlci/interface.  This will kick
+ *      the fr_poll() routine at a later time. 
+ *
+ * Usage:
+ * 	Interrupts use this to defer a taks to 
+ *      a polling routine.
+ *
+ */	
+static void trigger_fr_poll (netdevice_t *dev)
+{
+	fr_channel_t* chan = dev->priv;
+#ifdef LINUX_2_4
+	schedule_task(&chan->fr_poll_task);
+#else
+	queue_task(&chan->fr_poll_task, &tq_scheduler);
+#endif
+	return;
+}
+
+
+/*============================================================
+ * fr_poll
+ *	
+ * Rationale:
+ * 	We cannot manipulate the routing tables, or
+ *      ip addresses withing the interrupt. Therefore
+ *      we must perform such actons outside an interrupt 
+ *      at a later time. 
+ *
+ * Description:	
+ *	Frame relay polling routine, responsible for 
+ *     	shutting down interfaces upon disconnect
+ *     	and adding/removing routes. 
+ *      
+ * Usage:        
+ * 	This function is executed for each frame relay
+ * 	dlci/interface through a tq_schedule bottom half.
+ *      
+ *      trigger_fr_poll() function is used to kick
+ *      the fr_poll routine.  
+ */
+
+static void fr_poll (netdevice_t *dev)
+{
+
+	fr_channel_t* chan;
+	sdla_t *card;
+	u8 check_gateway=0;
+
+	if (!dev || (chan = dev->priv) == NULL)
+		return;
+
+	card = chan->card;
+	
+	/* (Re)Configuraiton is in progress, stop what you are 
+	 * doing and get out */
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		return;
+	}
+
+	switch (chan->common.state){
+
+	case WAN_DISCONNECTED:
+
+		if (test_bit(DYN_OPT_ON,&chan->interface_down) &&
+		    !test_bit(DEV_DOWN, &chan->interface_down) &&
+		    dev->flags&IFF_UP){
+
+			printk(KERN_INFO "%s: Interface %s is Down.\n", 
+				card->devname,dev->name);
+			change_dev_flags(dev,dev->flags&~IFF_UP);
+			set_bit(DEV_DOWN, &chan->interface_down);
+			chan->route_flag = NO_ROUTE;
+			
+		}else{
+			if (chan->inarp != INARP_NONE)
+				process_route(dev);	
+		}
+		break;
+
+	case WAN_CONNECTED:
+
+		if (test_bit(DYN_OPT_ON,&chan->interface_down) &&
+		    test_bit(DEV_DOWN, &chan->interface_down) &&
+		    !(dev->flags&IFF_UP)){
+
+			printk(KERN_INFO "%s: Interface %s is Up.\n", 
+					card->devname,dev->name);
+
+			change_dev_flags(dev,dev->flags|IFF_UP);
+			clear_bit(DEV_DOWN, &chan->interface_down);
+			check_gateway=1;
+		}
+
+		if (chan->inarp != INARP_NONE){
+			process_route(dev);
+			check_gateway=1;
+		}
+
+		if (chan->gateway && check_gateway)
+			add_gateway(card,dev);
+
+		break;
+
+	}
+
+	return;	
+}
+
+/*==============================================================
+ * check_tx_status
+ *
+ * Rationale:
+ *	We cannot transmit from an interrupt while
+ *      the if_send is transmitting data.  Therefore,
+ *      we must check whether the tx buffers are
+ *      begin used, before we transmit from an
+ *      interrupt.	
+ * 
+ * Description:	
+ *	Checks whether it's safe to use the transmit 
+ *      buffers. 
+ *
+ * Usage:
+ * 	ARP and UDP handling routines use this function
+ *      because, they need to transmit data during
+ *      an interrupt.
+ */
+
+static int check_tx_status(sdla_t *card, netdevice_t *dev)
+{
+
+	if (card->hw.type == SDLA_S514){
+		if (test_bit(SEND_CRIT, (void*)&card->wandev.critical) ||
+			test_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical)) {
+			return 1;
+		}
+	}
+
+	if (is_queue_stopped(dev) || (card->u.f.tx_interrupts_pending))
+     		return 1; 
+
+	return 0;
+}
+
+/*===============================================================
+ * move_dev_to_next
+ *  
+ * Description:
+ *	Move the dev pointer to the next location in the
+ *      link list.  Check if we are at the end of the 
+ *      list, if so start from the begining.
+ *
+ * Usage:
+ * 	Timer interrupt uses this function to efficiently
+ *      step through the devices that need to send ARP data.
+ *
+ */
+
+netdevice_t * move_dev_to_next (sdla_t *card, netdevice_t *dev)
+{
+	if (card->wandev.new_if_cnt != 1){
+		if (*((netdevice_t **)dev->priv) == NULL){
+			return card->wandev.dev;
+		}else{
+			return *((netdevice_t **)dev->priv);
+		}
+	}
+	return dev;
+}
+
+/*==============================================================
+ * trigger_config_fr
+ *
+ * Rationale:
+ *	All commands must be performed inside of a  
+ *      interrupt.   
+ *
+ * Description:
+ *	Kick the config_fr() routine throught the
+ *      timer interrupt.
+ */
+
+
+static void trigger_config_fr (sdla_t *card)
+{
+	fr508_flags_t* flags = card->flags;
+
+	card->u.f.timer_int_enabled |= TMR_INT_ENABLED_CONFIG;
+	flags->imask |= FR_INTR_TIMER;
+}
+
+
+/*==============================================================
+ * config_fr
+ *
+ * Rationale:
+ * 	All commands must be performed inside of a  
+ *      interrupt.  
+ &
+ * Description:	
+ * 	Configure a DLCI. This function is executed
+ *      by a timer_interrupt.  The if_open() function
+ *      triggers it.
+ *
+ * Usage:
+ *	new_if() collects all data necessary to
+ *      configure the DLCI. It sets the chan->dlci_ready 
+ *      bit.  When the if_open() function is executed
+ *      it checks this bit, and if its set it triggers
+ *      the timer interrupt to execute the config_fr()
+ *      function.
+ */
+
+static void config_fr (sdla_t *card)
+{
+	netdevice_t *dev;
+	fr_channel_t *chan;
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t **)dev->priv)){
+	
+		if ((chan=dev->priv) == NULL)
+			continue;
+		
+		if (!test_bit(0,&chan->config_dlci))
+			continue;
+
+		clear_bit(0,&chan->config_dlci);
+
+		/* If signalling is set to NO, then setup 
+        	 * DLCI addresses right away.  Don't have to wait for
+		 * link to connect. 
+		 */
+		if (card->wandev.signalling == WANOPT_NO){
+			printk(KERN_INFO "%s: Signalling set to NO: Mapping DLCI's\n",
+					card->wandev.name);
+			if (fr_init_dlci(card,chan)){
+				printk(KERN_INFO "%s: ERROR: Failed to configure DLCI %i !\n",
+					card->devname, chan->dlci);
+				return;
+			}
+		}
+
+		if (card->wandev.station == WANOPT_CPE) {
+	
+			update_chan_state(dev);	
+			
+			/* CPE: issue full status enquiry */
+			fr_issue_isf(card, FR_ISF_FSE);
+
+		} else {	
+			/* FR switch: activate DLCI(s) */
+	
+			/* For Switch emulation we have to ADD and ACTIVATE
+			 * the DLCI(s) that were configured with the SET_DLCI_
+			 * CONFIGURATION command. Add and Activate will fail if
+			 * DLCI specified is not included in the list.
+			 *
+			 * Also If_open is called once for each interface. But
+			 * it does not get in here for all the interface. So
+		 	 * we have to pass the entire list of DLCI(s) to add 
+			 * activate routines.  
+			 */ 
+			
+			if (!check_dlci_config (card, chan)){
+				fr_add_dlci(card, chan->dlci);
+				fr_activate_dlci(card, chan->dlci);
+			}
+		}
+
+		card->u.f.dlci_to_dev_map[chan->dlci] = dev;
+	}
+	return;
+}
+
+
+/*==============================================================
+ * config_fr
+ *
+ * Rationale:
+ *	All commands must be executed during an interrupt.
+ * 
+ * Description:	
+ *	Trigger uncofig_fr() function through 
+ *      the timer interrupt.
+ *
+ */
+
+static void trigger_unconfig_fr (netdevice_t *dev)
+{
+	fr_channel_t *chan = dev->priv;
+	volatile sdla_t *card = chan->card;
+	u32 timeout;
+	fr508_flags_t* flags = card->flags;
+	int reset_critical=0;
+	
+	if (test_bit(PERI_CRIT,(void*)&card->wandev.critical)){
+		clear_bit(PERI_CRIT,(void*)&card->wandev.critical);
+		reset_critical=1;
+	}
+		
+	/* run unconfig_dlci() function 
+         * throught the timer interrupt */
+	set_bit(0,(void*)&chan->unconfig_dlci);
+	card->u.f.timer_int_enabled |= TMR_INT_ENABLED_UNCONFIG;
+	flags->imask |= FR_INTR_TIMER;
+
+	/* Wait for the command to complete */
+	timeout = jiffies;
+     	for(;;) {
+
+		if(!(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UNCONFIG))
+			break;
+
+             	if ((jiffies - timeout) > (1 * HZ)){
+    			card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UNCONFIG;
+			printk(KERN_INFO "%s: Failed to delete DLCI %i\n",
+				card->devname,chan->dlci);
+ 			break;
+		}
+	}
+
+	if (reset_critical){
+		set_bit(PERI_CRIT,(void*)&card->wandev.critical);
+	}
+}
+
+/*==============================================================
+ * unconfig_fr
+ *
+ * Rationale:
+ *	All commands must be executed during an interrupt.
+ * 
+ * Description:	
+ *	Remove the dlci from firmware.
+ *	This funciton is used in NODE shutdown.
+ */
+
+static void unconfig_fr (sdla_t *card)
+{
+	netdevice_t *dev;
+	fr_channel_t *chan;
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t **)dev->priv)){
+	
+		if ((chan=dev->priv) == NULL)
+			continue;
+		
+		if (!test_bit(0,&chan->unconfig_dlci))
+			continue;
+
+		clear_bit(0,&chan->unconfig_dlci);
+
+		if (card->wandev.station == WANOPT_NODE){
+			printk(KERN_INFO "%s: Unconfiguring DLCI %i\n",
+					card->devname,chan->dlci);
+			fr_delete_dlci(card,chan->dlci);
+		}
+		card->u.f.dlci_to_dev_map[chan->dlci] = NULL;
+	}
+}
+
+static int setup_fr_header(struct sk_buff ** skb_orig, netdevice_t* dev, char op_mode)
+{
+	struct sk_buff *skb = *skb_orig;
+	fr_channel_t *chan=dev->priv;
+
+	if (op_mode == WANPIPE){
+
+		chan->fr_header[0]=Q922_UI;
+		
+		switch (htons(skb->protocol)){
+			
+		case ETH_P_IP:
+			chan->fr_header[1]=NLPID_IP;
+			break;
+		default:
+			return -EINVAL;
+		}
+			
+		return 2;
+	}
+
+	/* If we are in bridging mode, we must apply
+	 * an Ethernet header */
+	if (op_mode == BRIDGE || op_mode == BRIDGE_NODE){
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
+		/* Encapsulate the packet as a bridged Ethernet frame. */
+#ifdef DEBUG
+		printk(KERN_INFO "%s: encapsulating skb for frame relay\n", 
+			dev->name);
+#endif
+		
+		chan->fr_header[0] = 0x03;
+		chan->fr_header[1] = 0x00;
+		chan->fr_header[2] = 0x80;
+		chan->fr_header[3] = 0x00;
+		chan->fr_header[4] = 0x80;
+		chan->fr_header[5] = 0xC2;
+		chan->fr_header[6] = 0x00;
+		chan->fr_header[7] = 0x07;
+
+		/* Yuck. */
+		skb->protocol = ETH_P_802_3;
+		return 8;
+
+#else
+
+		/* BRIDGING is not supported in 2.0.X */
+		return -EINVAL;
+
 #endif
+	}
+		
+	return 0;
+}
+
+
+static int check_dlci_config (sdla_t *card, fr_channel_t *chan)
+{
+	fr_mbox_t* mbox = card->mbox;
+	int err=0;
+	fr_conf_t *conf=NULL;
+	unsigned short dlci_num = chan->dlci;
+	int dlci_offset=0;
+	netdevice_t *dev=NULL;
+	
+	mbox->cmd.command = FR_READ_CONFIG;
+	mbox->cmd.length = 0;
+	mbox->cmd.dlci = dlci_num; 	
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	
+	if (err == CMD_OK){
+		return 0;
+	}
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		set_chan_state(dev,WAN_DISCONNECTED);
+	}
+	
+	printk(KERN_INFO "DLCI %i Not configured, configuring\n",dlci_num);
+	
+	mbox->cmd.command = FR_COMM_DISABLE;
+	mbox->cmd.length = 0;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err != CMD_OK){
+		fr_event(card, err, mbox);
+		return 2;
+	}
+
+	printk(KERN_INFO "Disabled Communications \n");
+	
+	mbox->cmd.command = FR_READ_CONFIG;
+	mbox->cmd.length = 0;
+	mbox->cmd.dlci = 0; 	
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	
+	if (err != CMD_OK){
+		fr_event(card, err, mbox);
+		return 2;
+	}
+	
+	conf = (fr_conf_t *)mbox->data;
+
+	dlci_offset=0;
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		fr_channel_t *chan_tmp = dev->priv;
+		conf->dlci[dlci_offset] = chan_tmp->dlci;		
+		dlci_offset++;
+	}
+	
+	printk(KERN_INFO "Got Fr configuration Buffer Length is %x Dlci %i Dlci Off %i\n",
+		mbox->cmd.length,
+		mbox->cmd.length > 0x20 ? conf->dlci[0] : -1, 
+		dlci_offset );
+	
+	mbox->cmd.length = 0x20 + dlci_offset*2;
+
+	mbox->cmd.command = FR_SET_CONFIG;
+	mbox->cmd.dlci = 0; 
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+
+	if (err != CMD_OK){
+		fr_event(card, err, mbox);
+		return 2;
+	}
+
+	initialize_rx_tx_buffers (card);
+
+	
+	printk(KERN_INFO "Configuraiton Succeded for new DLCI %i\n",dlci_num);
+
+	if (fr_comm_enable (card)){
+		return 2;
+	}
+
+	printk(KERN_INFO "Enabling Communications \n");
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		fr_channel_t *chan_tmp = dev->priv;
+		fr_init_dlci(card,chan_tmp);
+		fr_add_dlci(card, chan_tmp->dlci);
+		fr_activate_dlci(card, chan_tmp->dlci);
+	}
 
+	printk(KERN_INFO "END OF CONFIGURAITON %i\n",dlci_num);
+	
+	return 1;
 }
+
+static void initialize_rx_tx_buffers (sdla_t *card)
+{
+	fr_buf_info_t* buf_info;
+	
+	if (card->hw.type == SDLA_S514) {
+	
+                buf_info = (void*)(card->hw.dpmbase + FR_MB_VECTOR +
+			FR508_RXBC_OFFS);
+
+                card->rxmb = (void*)(buf_info->rse_next + card->hw.dpmbase);
+
+                card->u.f.rxmb_base =
+                        (void*)(buf_info->rse_base + card->hw.dpmbase); 
+
+                card->u.f.rxmb_last =
+                        (void*)(buf_info->rse_base +
+                        (buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) +
+                        card->hw.dpmbase);
+	}else{	
+		buf_info = (void*)(card->hw.dpmbase + FR508_RXBC_OFFS);
+
+		card->rxmb = (void*)(buf_info->rse_next -
+			FR_MB_VECTOR + card->hw.dpmbase);
+		
+		card->u.f.rxmb_base =
+			(void*)(buf_info->rse_base -
+			FR_MB_VECTOR + card->hw.dpmbase);
+		
+		card->u.f.rxmb_last =
+			(void*)(buf_info->rse_base +
+			(buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) -
+			FR_MB_VECTOR + card->hw.dpmbase);
+	}
+
+	card->u.f.rx_base = buf_info->buf_base;
+	card->u.f.rx_top  = buf_info->buf_top;
+
+	card->u.f.tx_interrupts_pending = 0;
+
+	return;
+}
+
+	
+
 
 /****** End *****************************************************************/

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)