diff -u --recursive --new-file linux-2.1.32/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- linux-2.1.32/arch/i386/kernel/i386_ksyms.c	Fri Apr  4 11:30:09 1997
+++ linux/arch/i386/kernel/i386_ksyms.c	Fri Apr  4 16:10:19 1997
@@ -39,6 +39,10 @@
 EXPORT_SYMBOL(kernel_flag);
 EXPORT_SYMBOL(active_kernel_processor);
 EXPORT_SYMBOL(smp_invalidate_needed);
+EXPORT_SYMBOL(__global_cli);
+EXPORT_SYMBOL(__global_sti);
+EXPORT_SYMBOL(__global_save_flags);
+EXPORT_SYMBOL(__global_restore_flags);
 EXPORT_SYMBOL_NOVERS(__lock_kernel);
 #endif
 
diff -u --recursive --new-file linux-2.1.32/drivers/net/Config.in linux/drivers/net/Config.in
--- linux-2.1.32/drivers/net/Config.in	Fri Apr  4 11:30:20 1997
+++ linux/drivers/net/Config.in	Fri Apr  4 18:08:51 1997
@@ -177,9 +177,13 @@
     fi
   fi
 fi
-
+#
+# X.25 network drivers
+#
 if [ "$CONFIG_X25" != "n" ]; then
-  tristate 'LAPB over Ethernet driver' CONFIG_LAPBETHER
-  tristate 'X.25 async driver' CONFIG_X25_ASY
+if [ "$CONFIG_LAPB" != "n" ]; then
+    dep_tristate 'LAPB over Ethernet driver' CONFIG_LAPBETHER $CONFIG_LAPB
+    dep_tristate 'X.25 async driver' CONFIG_X25_ASY $CONFIG_LAPB
+fi
 fi
 
diff -u --recursive --new-file linux-2.1.32/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- linux-2.1.32/drivers/net/bpqether.c	Fri Apr  4 11:30:21 1997
+++ linux/drivers/net/bpqether.c	Fri Apr  4 11:32:59 1997
@@ -1,10 +1,6 @@
 /*
  *	G8BPQ compatible "AX.25 via ethernet" driver release 003
  *
- *	This is ALPHA test software. This code may break your machine, randomly
- *	fail to work with new releases, misbehave and/or generally screw up.
- *	It might even work.
- *
  *	This code REQUIRES 2.0.0 or higher/ NET3.029
  *
  *	This module:
@@ -267,6 +263,7 @@
  */
 static int bpq_xmit(struct sk_buff *skb, struct device *dev)
 {
+	struct sk_buff *newskb;
 	unsigned char *ptr;
 	struct bpqdev *bpq;
 	int size;
@@ -277,7 +274,7 @@
 	 */
 	if (!dev->start) {
 		bpq_check_devices(dev);
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		return -ENODEV;
 	}
 
@@ -289,21 +286,16 @@
 	 * sendto() does not.
 	 */
 	if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {	/* Ough! */
-		struct sk_buff *newskb = alloc_skb(skb->len + AX25_BPQ_HEADER_LEN, GFP_ATOMIC);
-
-		if (newskb == NULL) {			/* Argh! */
-			printk(KERN_WARNING "bpq_xmit: not enough space to add BPQ Ether header\n");
-			dev_kfree_skb(skb, FREE_WRITE);
+		if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) {
+			printk(KERN_WARNING "bpqether: out of memory\n");
+			kfree_skb(skb, FREE_WRITE);
 			return -ENOMEM;
 		}
 
-		newskb->arp  = 1;
-		if (skb->sk)
+		if (skb->sk != NULL)
 			skb_set_owner_w(newskb, skb->sk);
 
-		skb_reserve(newskb, AX25_BPQ_HEADER_LEN);
-		memcpy(skb_put(newskb, size), skb->data, size);
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		skb = newskb;
 	}
 
@@ -318,7 +310,7 @@
 
 	if ((dev = bpq_get_ether_dev(dev)) == NULL) {
 		bpq->stats.tx_dropped++;
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		return -ENODEV;
 	}
 
@@ -508,7 +500,7 @@
 	unsigned char *buf;
 	struct bpqdev *bpq, *bpq2;
 
-	if ((bpq = (struct bpqdev *)kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
+	if ((bpq = kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
 		return -ENOMEM;
 
 	memset(bpq, 0, sizeof(struct bpqdev));
@@ -522,7 +514,7 @@
 	memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
 
 	dev = &bpq->axdev;
-	buf = (unsigned char *)kmalloc(14, GFP_KERNEL);
+	buf = kmalloc(14, GFP_KERNEL);
 
 	for (k = 0; k < MAXBPQDEV; k++) {
 		struct device *odev;
@@ -561,12 +553,13 @@
 
 	/* preset with reasonable values */
 
-#if CONFIG_INET
 	dev->flags      = 0;
 	dev->family     = AF_INET;
-	dev->pa_addr    = 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask    = 0;
+
+#ifdef CONFIG_INET
+	dev->pa_addr    = in_aton("192.168.0.1");
+	dev->pa_brdaddr = in_aton("192.168.0.255");
+	dev->pa_mask    = in_aton("255.255.255.0");
 	dev->pa_alen    = 4;
 #endif
 
@@ -662,6 +655,9 @@
 
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@lykos.oche.de>");
+MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
 
 int init_module(void)
 {
diff -u --recursive --new-file linux-2.1.32/drivers/net/lapbether.c linux/drivers/net/lapbether.c
--- linux-2.1.32/drivers/net/lapbether.c	Thu Mar 27 19:43:07 1997
+++ linux/drivers/net/lapbether.c	Mon Mar 31 14:29:36 1997
@@ -1,10 +1,6 @@
 /*
  *	"LAPB via ethernet" driver release 001
  *
- *	This is ALPHA test software. This code may break your machine, randomly 
- *	fail to work with new releases, misbehave and/or generally screw up. 
- *	It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -225,7 +221,7 @@
 	 */
 	if (!dev->start) {
 		lapbeth_check_devices(dev);
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		return -ENODEV;
 	}
 
@@ -426,7 +422,7 @@
 	unsigned char *buf;
 	struct lapbethdev *lapbeth, *lapbeth2;
 	
-	if ((lapbeth = (struct lapbethdev *)kmalloc(sizeof(struct lapbethdev), GFP_KERNEL)) == NULL)
+	if ((lapbeth = kmalloc(sizeof(struct lapbethdev), GFP_KERNEL)) == NULL)
 		return -ENOMEM;
 		
 	memset(lapbeth, 0, sizeof(struct lapbethdev));
@@ -437,7 +433,7 @@
 	strncpy(lapbeth->ethname, dev->name, sizeof(lapbeth->ethname)-1);
 
 	dev = &lapbeth->axdev;
-	buf = (unsigned char *)kmalloc(14, GFP_KERNEL);
+	buf = kmalloc(14, GFP_KERNEL);
 
 	for (k = 0; k < MAXLAPBDEV; k++) {
 		struct device *odev;
@@ -473,12 +469,13 @@
 
 	/* preset with reasonable values */
 
-#if CONFIG_INET
 	dev->flags      = 0;
 	dev->family     = AF_INET;
-	dev->pa_addr    = 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask    = 0;
+
+#ifdef CONFIG_INET
+	dev->pa_addr    = in_aton("192.168.0.1");
+	dev->pa_brdaddr = in_aton("192.168.0.255");
+	dev->pa_mask    = in_aton("255.255.255.0");
 	dev->pa_alen    = 4;
 #endif
 
@@ -560,6 +557,9 @@
 
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The unofficial LAPB over Ethernet driver");
 
 int init_module(void)
 {
diff -u --recursive --new-file linux-2.1.32/drivers/net/mkiss.c linux/drivers/net/mkiss.c
--- linux-2.1.32/drivers/net/mkiss.c	Thu Mar 27 19:43:08 1997
+++ linux/drivers/net/mkiss.c	Tue Apr  1 13:29:26 1997
@@ -15,7 +15,7 @@
  *		This method clears the way to implement other kiss protocols
  *		like mkiss smack g8bpq ..... so far only mkiss is implemented.
  *
- * Hans Alblas Hansa@cuci.nl
+ * Hans Alblas <hans@esrac.ele.tue.nl>
  *
  *	History
  *	Jonathan (G4KLX)	Fixed to match Linux networking changes - 2.1.15.
@@ -31,6 +31,7 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
+#include <linux/inet.h>
 #include <linux/tty.h>
 #include <linux/errno.h>
 #include <linux/netdevice.h>
@@ -74,8 +75,8 @@
 } ax25_ctrl_t;
 
 static ax25_ctrl_t **ax25_ctrls = NULL;
+
 int ax25_maxdev = AX25_MAXDEV;		/* Can be overridden with insmod! */
-MODULE_PARM(ax25_maxdev, "i");
 
 static struct tty_ldisc	ax_ldisc;
 static struct tty_driver mkiss_driver;
@@ -117,7 +118,7 @@
 		return NULL;
 
 	/* If no channels are available, allocate one */
-	if (axp == NULL && (ax25_ctrls[i] = (ax25_ctrl_t *)kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
+	if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
 		axp = ax25_ctrls[i];
 		memset(axp, 0, sizeof(ax25_ctrl_t));
 
@@ -147,7 +148,7 @@
 			return &axp->ctrl;
 		} else {
 			clear_bit(AXF_INUSE,&axp->ctrl.flags);
-			printk(KERN_ERR "ax_alloc() - register_netdev() failure.\n");
+			printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n");
 		}
 	}
 
@@ -165,7 +166,7 @@
 		kfree(ax->xbuff);
 	ax->xbuff = NULL;
 	if (!clear_bit(AXF_INUSE, &ax->flags))
-		printk(KERN_ERR "%s: ax_free for already free unit.\n", ax->dev->name);
+		printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name);
 }
 
 static void ax_changedmtu(struct ax_disp *ax)
@@ -185,11 +186,11 @@
 	if (len < 576 * 2)
 		len = 576 * 2;
 
-	xbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC);
-	rbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC);
+	xbuff = kmalloc(len + 4, GFP_ATOMIC);
+	rbuff = kmalloc(len + 4, GFP_ATOMIC);
 
 	if (xbuff == NULL || rbuff == NULL)  {
-		printk(KERN_ERR "%s: unable to grow ax25 buffers, MTU change cancelled.\n",
+		printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n",
 		       ax->dev->name);
 		dev->mtu = ax->mtu;
 		if (xbuff != NULL)
@@ -244,7 +245,7 @@
 static inline void ax_lock(struct ax_disp *ax)
 {
 	if (set_bit(0, (void *)&ax->dev->tbusy))
-		printk(KERN_ERR "%s: trying to lock already locked device!\n", ax->dev->name);
+		printk(KERN_ERR "mkiss: %s: trying to lock already locked device!\n", ax->dev->name);
 }
 
 
@@ -252,7 +253,7 @@
 static inline void ax_unlock(struct ax_disp *ax)
 {
 	if (!clear_bit(0, (void *)&ax->dev->tbusy))
-		printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", ax->dev->name);
+		printk(KERN_ERR "mkiss: %s: trying to unlock already unlocked device!\n", ax->dev->name);
 }
 
 /* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
@@ -276,7 +277,7 @@
 	count = ax->rcount;
 
 	if ((skb = dev_alloc_skb(count)) == NULL) {
-		printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", ax->dev->name);
+		printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name);
 		ax->rx_dropped++;
 		return;
 	}
@@ -301,7 +302,7 @@
 
 	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
 		len = ax->mtu;
-		printk(KERN_ERR "%s: truncating oversized transmit packet!\n", ax->dev->name);
+		printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
 		ax->tx_dropped++;
 		ax_unlock(ax);
 		return;
@@ -379,7 +380,7 @@
 	}
 
 	if (!dev->start)  {
-		printk(KERN_ERR "%s: xmit call when iface is down\n", dev->name);
+		printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
 		return 1;
 	}
 
@@ -403,7 +404,7 @@
 			return 1;
 		}
 
-		printk(KERN_ERR "%s: transmit timed out, %s?\n", dev->name,
+		printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
 		       (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
 		       "bad line quality" : "driver error");
 
@@ -418,7 +419,7 @@
 		if (tmp_ax != NULL)
 			ax_lock(tmp_ax);
 		ax_encaps(ax, skb->data, skb->len);
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 	}
 
 	return 0;
@@ -475,10 +476,10 @@
 	if (len < 576 * 2)
 		len = 576 * 2;
 
-	if ((ax->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL)
+	if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
 		goto norbuff;
 
-	if ((ax->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL)
+	if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
 		goto noxbuff;
 
 	ax->mtu	     = dev->mtu + 73;
@@ -822,7 +823,7 @@
 
 	if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */
 
-	if ((ax25_ctrls = (ax25_ctrl_t **)kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
+	if ((ax25_ctrls = kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
 		printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array !  No mkiss available\n");
 		return -ENOMEM;
 	}
@@ -909,10 +910,13 @@
 	/* New-style flags. */
 	dev->flags      = 0;
 	dev->family     = AF_INET;
-	dev->pa_addr    = 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask    = 0;
+
+#ifdef CONFIG_INET
+	dev->pa_addr    = in_aton("192.168.0.1");
+	dev->pa_brdaddr = in_aton("192.168.0.255");
+	dev->pa_mask    = in_aton("255.255.255.0");
 	dev->pa_alen    = 4;
+#endif
 
 	return 0;
 }
@@ -1079,7 +1083,7 @@
 	mkiss_driver.put_char     = mkiss_dummy2;
 
 	if (tty_register_driver(&mkiss_driver)) {
-		printk(KERN_ERR "Couldn't register Mkiss device\n");
+		printk(KERN_ERR "mkiss: couldn't register Mkiss device\n");
 		return -EIO;
 	}
 
@@ -1090,6 +1094,12 @@
 
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
+
+MODULE_PARM(ax25_maxdev, "i");
+MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
+
+MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>");
+MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
 
 int init_module(void)
 {
diff -u --recursive --new-file linux-2.1.32/drivers/net/pi2.c linux/drivers/net/pi2.c
--- linux-2.1.32/drivers/net/pi2.c	Fri Apr  4 11:30:24 1997
+++ linux/drivers/net/pi2.c	Fri Apr  4 16:10:51 1997
@@ -331,11 +331,6 @@
     wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT);
 }
 
-static void free_p(struct sk_buff *skb)
-{
-	dev_kfree_skb(skb, FREE_WRITE);
-}
-
 static void a_txint(struct pi_local *lp)
 {
     int cmd;
@@ -412,7 +407,7 @@
     }
     switch (lp->tstate) {
     case ACTIVE:
-	free_p(lp->sndbuf);
+	kfree_skb(lp->sndbuf, FREE_WRITE);
 	lp->sndbuf = NULL;
 	lp->tstate = FLAGOUT;
 	tdelay(lp, lp->squeldelay);
@@ -732,7 +727,7 @@
 	    /* stuffing a char satisfies Interrupt condition */
 	} else {
 	    /* No more to send */
-	    free_p(lp->sndbuf);
+	    kfree_skb(lp->sndbuf, FREE_WRITE);
 	    lp->sndbuf = NULL;
 	    if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) {
 		/* Did we underrun? */
@@ -784,7 +779,7 @@
 
     switch (lp->tstate) {
     case ACTIVE:		/* Unexpected underrun */
-	free_p(lp->sndbuf);
+	kfree_skb(lp->sndbuf, FREE_WRITE);
 	lp->sndbuf = NULL;
 	wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
 	lp->tstate = FLAGOUT;
@@ -1567,7 +1562,7 @@
 
     /* Free any buffers left in the hardware transmit queue */
     while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
-	free_p(ptr);
+	kfree_skb(ptr, FREE_WRITE);
 
     restore_flags(flags);
 
@@ -1667,6 +1662,9 @@
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
 
+MODULE_AUTHOR("David Perry <dp@hydra.carleton.ca>");
+MODULE_DESCRIPTION("AX.25 driver for the Ottawa PI and PI/2 HDLC cards");
+
 int init_module(void)
 {
     return pi_init();
@@ -1687,12 +1685,3 @@
     unregister_netdev(&pi0b);
 }
 #endif
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
- *  version-control: t
- *  kept-new-versions: 5
- *  tab-width: 4
- * End:
- */
diff -u --recursive --new-file linux-2.1.32/drivers/net/pt.c linux/drivers/net/pt.c
--- linux-2.1.32/drivers/net/pt.c	Fri Apr  4 11:30:25 1997
+++ linux/drivers/net/pt.c	Fri Apr  4 11:33:03 1997
@@ -26,7 +26,7 @@
  * 03/03/95 cs  Painfully found out (after 3 days) SERIAL_CFG is write only
  *              created image of it and DMA_CFG
  * 21/06/95 cs  Upgraded to suit PI driver 0.8 ALPHA
- * 22/08/95	cs	Changed it all around to make it like pi driver
+ * 22/08/95 cs	Changed it all around to make it like pi driver
  * 23/08/95 cs  It now works, got caught again by TMR2 and we must have
  *				auto-enables for daughter boards.
  * 07/10/95 cs  Fixed for 1.3.30 (hopefully)
@@ -317,12 +317,6 @@
     restore_flags(flags);
 }
 
-static void free_p(struct sk_buff *skb)
-{
-    dev_kfree_skb(skb, FREE_WRITE);
-}
-
-
 /*
  * This sets up all the registers in the SCC for the given channel
  * based upon tsync_hwint()
@@ -978,7 +972,7 @@
 
 	/* Free any buffers left in the hardware transmit queue */
 	while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
-		free_p(ptr);
+		kfree_skb(ptr, FREE_WRITE);
 
 	restore_flags(flags);
 
@@ -1196,7 +1190,7 @@
 	        /* stuffing a char satisfies interrupt condition */
 	    } else {
 	        /* No more to send */
-	        free_p(lp->sndbuf);
+	        kfree_skb(lp->sndbuf, FREE_WRITE);
 	        lp->sndbuf = NULL;
 	        if ((rdscc(lp->cardbase, cmd, R0) & TxEOM))
 	        {
@@ -1552,7 +1546,7 @@
 #ifdef PT_DEBUG
 	printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");
 #endif
-        free_p(lp->sndbuf);
+        kfree_skb(lp->sndbuf, FREE_WRITE);
         lp->sndbuf = NULL;
         if (!lp->dmachan)
         {
@@ -1765,6 +1759,9 @@
 
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Craig Small VK2XLZ <vk2xlz@vk2xlz.ampr.org>");
+MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card");
 
 int init_module(void)
 {
diff -u --recursive --new-file linux-2.1.32/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c
--- linux-2.1.32/drivers/net/soundmodem/sm.c	Fri Apr  4 11:30:26 1997
+++ linux/drivers/net/soundmodem/sm.c	Fri Apr  4 11:33:04 1997
@@ -157,6 +157,7 @@
 static const struct hardware_info *sm_hardware_table[] = {
 #ifdef CONFIG_SOUNDMODEM_SBC
 	&sm_hw_sbc,
+	&sm_hw_sbcfdx,
 #endif /* CONFIG_SOUNDMODEM_SBC */
 #ifdef CONFIG_SOUNDMODEM_WSS
 	&sm_hw_wss,
diff -u --recursive --new-file linux-2.1.32/drivers/net/soundmodem/sm.h linux/drivers/net/soundmodem/sm.h
--- linux-2.1.32/drivers/net/soundmodem/sm.h	Fri Mar  7 14:49:04 1997
+++ linux/drivers/net/soundmodem/sm.h	Wed Mar 26 15:05:01 1997
@@ -325,6 +325,7 @@
 extern const struct modem_rx_info sm_fsk9600_5_rx;
 
 extern const struct hardware_info sm_hw_sbc;
+extern const struct hardware_info sm_hw_sbcfdx;
 extern const struct hardware_info sm_hw_wss;
 extern const struct hardware_info sm_hw_wssfdx;
 
diff -u --recursive --new-file linux-2.1.32/drivers/net/soundmodem/sm_afsk1200.c linux/drivers/net/soundmodem/sm_afsk1200.c
--- linux-2.1.32/drivers/net/soundmodem/sm_afsk1200.c	Thu Mar 27 19:43:10 1997
+++ linux/drivers/net/soundmodem/sm_afsk1200.c	Thu Mar 27 20:02:45 1997
@@ -1,7 +1,7 @@
 /*****************************************************************************/
 
 /*
- *	sm_afsk1200.h  -- soundcard radio modem driver, 1200 baud AFSK modem
+ *	sm_afsk1200.c  -- soundcard radio modem driver, 1200 baud AFSK modem
  *
  *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
diff -u --recursive --new-file linux-2.1.32/drivers/net/soundmodem/sm_fsk9600.c linux/drivers/net/soundmodem/sm_fsk9600.c
--- linux-2.1.32/drivers/net/soundmodem/sm_fsk9600.c	Fri Mar  7 14:49:04 1997
+++ linux/drivers/net/soundmodem/sm_fsk9600.c	Thu Mar 27 20:03:57 1997
@@ -1,7 +1,7 @@
 /*****************************************************************************/
 
 /*
- *	sm_fsk9600.h  --  soundcard radio modem driver, 
+ *	sm_fsk9600.c  --  soundcard radio modem driver, 
  *                        9600 baud G3RUH compatible FSK modem
  *
  *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
diff -u --recursive --new-file linux-2.1.32/drivers/net/soundmodem/sm_hapn4800.c linux/drivers/net/soundmodem/sm_hapn4800.c
--- linux-2.1.32/drivers/net/soundmodem/sm_hapn4800.c	Fri Mar  7 14:49:05 1997
+++ linux/drivers/net/soundmodem/sm_hapn4800.c	Thu Mar 27 20:03:40 1997
@@ -1,7 +1,7 @@
 /*****************************************************************************/
 
 /*
- *	sm_hapn4800.h  -- soundcard radio modem driver, 4800 baud HAPN modem
+ *	sm_hapn4800.c  -- soundcard radio modem driver, 4800 baud HAPN modem
  *
  *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
diff -u --recursive --new-file linux-2.1.32/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c
--- linux-2.1.32/drivers/net/soundmodem/sm_sbc.c	Thu Mar 27 19:43:10 1997
+++ linux/drivers/net/soundmodem/sm_sbc.c	Wed Mar 26 15:08:54 1997
@@ -1,7 +1,7 @@
 /*****************************************************************************/
 
 /*
- *	sm_sbc.h  -- soundcard radio modem driver soundblaster hardware driver
+ *	sm_sbc.c  -- soundcard radio modem driver soundblaster hardware driver
  *
  *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
@@ -80,9 +80,12 @@
 struct sc_state_sbc {
 	unsigned char revhi, revlo;
 	unsigned char fmt[2];
+	unsigned int sr[2];
 	unsigned int dmabuflen;
 	unsigned char *dmabuf;
+	unsigned char *dmabuf2;
 	unsigned char dmabufidx;
+	unsigned char dma2bufidx;
 	unsigned char ptt;
 };
 
@@ -99,6 +102,7 @@
 #define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
 #define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
 #define DSP_MIXER_DATA(iobase)   (iobase+0x5)
+#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
 #define SBC_EXTENT               16
 
 /* --------------------------------------------------------------------- */
@@ -122,6 +126,8 @@
 #define SBC_DMA_ON             0xd0
 #define SBC_DMA_OFF            0xd4
 #define SBC_SAMPLE_RATE        0x40
+#define SBC_SAMPLE_RATE_OUT    0x41
+#define SBC_SAMPLE_RATE_IN     0x42
 #define SBC_MONO_8BIT          0xa0
 #define SBC_MONO_16BIT         0xa4
 #define SBC_STEREO_8BIT        0xa8
@@ -131,6 +137,9 @@
 #define SBC4_IN8_AI            0xce
 #define SBC4_MODE_UNS_MONO     0x00
 
+#define SBC4_OUT16_AI          0xb6
+#define SBC4_IN16_AI           0xbe
+
 /* --------------------------------------------------------------------- */
 
 static int inline reset_dsp(struct device *dev)
@@ -179,13 +188,102 @@
 
 /* --------------------------------------------------------------------- */
 
-static void inline sbc_int_ack(struct device *dev)
+static int config_resources(struct device *dev, struct sm_state *sm, int fdx)
+{
+	unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+	unsigned long flags;
+
+	switch (dev->irq) {
+	case 2:
+	case 9:
+		irqreg |= 0x01;
+		break;
+
+	case 5:
+		irqreg |= 0x02;
+		break;
+
+	case 7:
+		irqreg |= 0x04;
+		break;
+
+	case 10:
+		irqreg |= 0x08;
+		break;
+		
+	default:
+		return -ENODEV;
+	}
+
+	switch (dev->dma) {
+	case 0:
+		dmareg |= 0x01;
+		break;
+
+	case 1:
+		dmareg |= 0x02;
+		break;
+
+	case 3:
+		dmareg |= 0x08;
+		break;
+
+	default:
+		return -ENODEV;
+	}
+		
+	if (fdx) {
+		switch (sm->hdrv.ptt_out.dma2) {
+		case 5:
+			dmareg |= 0x20;
+			break;
+			
+		case 6:
+			dmareg |= 0x40;
+			break;
+			
+		case 7:
+			dmareg |= 0x80;
+			break;
+			
+		default:
+			return -ENODEV;
+		}
+	}
+	save_flags(flags);
+	cli();
+	outb(0x80, DSP_MIXER_ADDR(dev->base_addr));
+	outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
+	realirq = inb(DSP_MIXER_DATA(dev->base_addr));
+	outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
+	outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
+	realdma = inb(DSP_MIXER_DATA(dev->base_addr));
+	restore_flags(flags);
+	if ((~realirq) & irqreg || (~realdma) & dmareg) {
+		printk(KERN_ERR "%s: sbc resource registers cannot be set; "
+		       "PnP device and IRQ/DMA specified wrongly?\n",
+		       sm_drvname);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_8bit(struct device *dev)
 {
 	inb(DSP_DATA_AVAIL(dev->base_addr));
 }
 
 /* --------------------------------------------------------------------- */
 
+static void inline sbc_int_ack_16bit(struct device *dev)
+{
+	inb(DSP_INTACK_16BIT(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
 static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
 {
         unsigned long flags;
@@ -209,7 +307,7 @@
                 panic("sm: DMA buffer violates DMA boundary!");
         save_flags(flags);
         cli();
-        sbc_int_ack(dev);
+        sbc_int_ack_8bit(dev);
         write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
         write_dsp(dev, SCSTATE->fmt[send]);
         write_dsp(dev, sbcskr[send]); 
@@ -219,7 +317,7 @@
         set_dma_addr(dev->dma, dmabufaddr);
         set_dma_count(dev->dma, SCSTATE->dmabuflen);
         enable_dma(dev->dma);
-        sbc_int_ack(dev);
+        sbc_int_ack_8bit(dev);
 	if (SCSTATE->revhi >= 4) {
 		write_dsp(dev, sbc4mode[send]);
 		write_dsp(dev, SBC4_MODE_UNS_MONO);
@@ -247,7 +345,7 @@
 	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
 		return;
 	new_ptt = hdlcdrv_ptt(&sm->hdrv);
- 	sbc_int_ack(dev);
+ 	sbc_int_ack_8bit(dev);
  	buf = SCSTATE->dmabuf;
 	if (SCSTATE->dmabufidx)
 		buf += SCSTATE->dmabuflen/2;
@@ -294,6 +392,8 @@
 
 static int sbc_open(struct device *dev, struct sm_state *sm) 
 {
+	int err;
+
 	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
 		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
 		       sizeof(struct sc_state_sbc), sizeof(sm->m));
@@ -327,6 +427,11 @@
 		       dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
 		return -ENODEV;
 	}
+	if (SCSTATE->revhi >= 4 && 
+	    (err = config_resources(dev, sm, 0))) {
+		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+		return err;
+	}
 	/*
 	 * initialize some variables
 	 */
@@ -521,6 +626,282 @@
 const struct hardware_info sm_hw_sbc = {
 	"sbc", sizeof(struct sc_state_sbc), 
 	sbc_open, sbc_close, sbc_ioctl, sbc_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm)
+{
+        unsigned long flags;
+	unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf);
+	unsigned long dmabuf2addr = virt_to_bus(SCSTATE->dmabuf2);
+
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+                return;
+        }
+        if (((dmabufaddr & 0xffff) + SCSTATE->dmabuflen > 0x10000) ||
+	    ((dmabuf2addr & 0xffff) + 2*(SCSTATE->dmabuflen) > 0x10000))
+		panic("sm: DMA buffer violates DMA boundary!");
+        save_flags(flags);
+        cli();
+        sbc_int_ack_8bit(dev);
+        sbc_int_ack_16bit(dev);
+	/* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
+        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+        write_dsp(dev, SCSTATE->fmt[0]);
+        write_dsp(dev, SBC_SPEAKER_ON);
+	/*
+	 * DMA channel 1 (8bit) does input (capture),
+	 * DMA channel 2 (16bit) does output (playback)
+	 */
+        disable_dma(dev->dma);
+        disable_dma(sm->hdrv.ptt_out.dma2);
+        clear_dma_ff(dev->dma);
+        set_dma_mode(dev->dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->dma, dmabufaddr);
+        set_dma_count(dev->dma, SCSTATE->dmabuflen);
+        clear_dma_ff(sm->hdrv.ptt_out.dma2);
+        set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+        set_dma_addr(sm->hdrv.ptt_out.dma2, dmabuf2addr);
+        set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen);
+        enable_dma(dev->dma);
+        enable_dma(sm->hdrv.ptt_out.dma2);
+        sbc_int_ack_8bit(dev);
+        sbc_int_ack_16bit(dev);
+	write_dsp(dev, SBC4_IN8_AI);
+	write_dsp(dev, SBC4_MODE_UNS_MONO);
+	write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+	write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+	write_dsp(dev, SBC4_OUT16_AI);
+	write_dsp(dev, SBC4_MODE_UNS_MONO);
+	write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+	write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned char *buf;
+	unsigned char *buf2;
+	unsigned char intsrc;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	buf = SCSTATE->dmabuf;
+	buf2 = SCSTATE->dmabuf2;
+	outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
+	intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
+	if (intsrc & 0x01) {
+		sbc_int_ack_8bit(dev);
+		if (SCSTATE->dmabufidx)
+			buf += SCSTATE->dmabuflen/2;
+		SCSTATE->dmabufidx = !SCSTATE->dmabufidx;
+	}
+	if (intsrc & 0x02) {
+		sbc_int_ack_16bit(dev);
+		if (SCSTATE->dma2bufidx)
+			buf2 += SCSTATE->dmabuflen/2;
+		SCSTATE->dma2bufidx = !SCSTATE->dma2bufidx;
+	}
+	sm_int_freq(sm);
+	sti();
+	if (intsrc & 0x02) {
+		if ((SCSTATE->ptt = hdlcdrv_ptt(&sm->hdrv))) 
+			time_exec(sm->debug_vals.mod_cyc, 
+				  sm->mode_tx->modulator(sm, buf2, SCSTATE->dmabuflen/2));
+		else
+			time_exec(sm->debug_vals.mod_cyc, 
+				  memset(buf2, 0x80, SCSTATE->dmabuflen/2));
+	}
+	if (intsrc & 0x01) {
+		time_exec(sm->debug_vals.demod_cyc, 
+			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+	}
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_open(struct device *dev, struct sm_state *sm) 
+{
+	int err;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
+		       sizeof(struct sc_state_sbc), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev))
+		return -ENODEV;
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &SCSTATE->revhi) || 
+	    !read_dsp(dev, &SCSTATE->revlo))
+		return -ENODEV;
+	if (SCSTATE->revhi < 4) {
+		printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
+		return -ENODEV;
+	}
+	if ((err = config_resources(dev, sm, 1))) {
+		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+		return err;
+	}
+	/*
+	 * initialize some variables
+	 */
+	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(SCSTATE->dmabuf2 = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		return -ENOMEM;
+	}
+	SCSTATE->dmabufidx = SCSTATE->dma2bufidx = SCSTATE->ptt = 0;
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+		free_dma(dev->dma);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+		free_dma(dev->dma);
+		free_dma(sm->hdrv.ptt_out.dma2);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_fdx_dsp(dev, sm);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(sm->hdrv.ptt_out.dma2);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+	kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int dv;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
+			    (*mrp)->srate == (*mtp)->srate) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->sr[0] = sm->mode_rx->srate;
+				SCSTATE->sr[1] = sm->mode_tx->srate;
+				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
+					 sm->mode_rx->dmabuflenmodulo);
+				if (dv & 1)
+					dv <<= 1; /* dmabuflen must be a multiple of 4 */
+				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
+				SCSTATE->dmabuflen /= dv;
+				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	return sbc_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbcfdx = {
+	"sbcfdx", sizeof(struct sc_state_sbc), 
+	sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
 };
 
 /* --------------------------------------------------------------------- */
diff -u --recursive --new-file linux-2.1.32/include/linux/ax25.h linux/include/linux/ax25.h
--- linux-2.1.32/include/linux/ax25.h	Fri Mar  7 14:49:07 1997
+++ linux/include/linux/ax25.h	Tue Mar 25 09:55:42 1997
@@ -16,7 +16,7 @@
 #define AX25_T2		5
 #define	AX25_BACKOFF	6
 #define	AX25_EXTSEQ	7
-#define	AX25_HDRINCL	8
+#define	AX25_PIDINCL	8
 #define AX25_IDLE	9
 #define AX25_PACLEN	10
 #define AX25_IAMDIGI	12
diff -u --recursive --new-file linux-2.1.32/include/linux/netrom.h linux/include/linux/netrom.h
--- linux-2.1.32/include/linux/netrom.h	Fri Mar  7 14:49:08 1997
+++ linux/include/linux/netrom.h	Tue Mar 25 09:55:48 1997
@@ -13,7 +13,6 @@
 #define NETROM_T1	1
 #define NETROM_T2	2
 #define NETROM_N2	3
-#define	NETROM_HDRINCL	4
 #define	NETROM_T4	6
 #define	NETROM_IDLE	7
 
diff -u --recursive --new-file linux-2.1.32/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-2.1.32/include/linux/proc_fs.h	Thu Mar 27 19:43:21 1997
+++ linux/include/linux/proc_fs.h	Fri Apr  4 13:04:33 1997
@@ -129,7 +129,6 @@
 	PROC_NET_RS_ROUTES,
 	PROC_NET_RS,
 	PROC_NET_CL2LLC,
-	PROC_NET_X25_LINKS,
 	PROC_NET_X25_ROUTES,
 	PROC_NET_X25,
 	PROC_NET_TR_RIF,
diff -u --recursive --new-file linux-2.1.32/include/linux/rose.h linux/include/linux/rose.h
--- linux-2.1.32/include/linux/rose.h	Fri Mar  7 14:48:14 1997
+++ linux/include/linux/rose.h	Tue Mar 25 09:55:57 1997
@@ -14,7 +14,7 @@
 #define	ROSE_T2		3
 #define	ROSE_T3		4
 #define	ROSE_IDLE	5
-#define	ROSE_HDRINCL	6
+#define	ROSE_QBITINCL	6
 #define	ROSE_HOLDBACK	7
 
 #define	ROSE_KILL	99
diff -u --recursive --new-file linux-2.1.32/include/net/ax25.h linux/include/net/ax25.h
--- linux-2.1.32/include/net/ax25.h	Thu Mar 27 19:43:22 1997
+++ linux/include/net/ax25.h	Fri Apr  4 13:16:47 1997
@@ -46,32 +46,12 @@
 #define	AX25_COND_REJECT		0x02
 #define	AX25_COND_PEER_RX_BUSY		0x04
 #define	AX25_COND_OWN_RX_BUSY		0x08
+#define	AX25_COND_DAMA_MODE		0x10
 
 #ifndef _LINUX_NETDEVICE_H
 #include <linux/netdevice.h>
 #endif
 
-/*
- * These headers are taken from the KA9Q package by Phil Karn. These specific
- * files have been placed under the GPL (not the whole package) by Phil.
- *
- *
- * Copyright 1991 Phil Karn, KA9Q
- *
- * 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; version 2 dated June, 1991.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program;  if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
- */
-
 /* Upper sub-layer (LAPB) definitions */
 
 /* Control field templates */
@@ -149,7 +129,7 @@
 #define	AX25_DEF_T2		(3 * AX25_SLOWHZ)	/* T2=3s  */
 #define	AX25_DEF_T3		(300 * AX25_SLOWHZ)	/* T3=300s */
 #define	AX25_DEF_N2		10			/* N2=10 */
-#define AX25_DEF_IDLE		(20 * 60 * AX25_SLOWHZ)	/* Idle=20 mins */		
+#define AX25_DEF_IDLE		(0 * 60 * AX25_SLOWHZ)	/* Idle=None */
 #define AX25_DEF_PACLEN		256			/* Paclen=256 */
 #define	AX25_DEF_PROTOCOL	AX25_PROTO_STD_SIMPLEX	/* Standard AX.25 */
 #define AX25_DEF_DS_TIMEOUT	(3 * 60 * AX25_SLOWHZ)  /* DAMA timeout 3 minutes */
@@ -202,17 +182,13 @@
 	ax25_digi		*digipeat;
 	ax25_dev		*ax25_dev;
 	unsigned char		iamdigi;
-	unsigned char		state, modulus, hdrincl;
+	unsigned char		state, modulus, pidincl;
 	unsigned short		vs, vr, va;
 	unsigned char		condition, backoff;
 	unsigned char		n2, n2count;
-#ifdef CONFIG_AX25_DAMA_SLAVE
-	unsigned char		dama_slave;
-#endif
 	unsigned short		t1, t2, t3, idle, rtt;
 	unsigned short		t1timer, t2timer, t3timer, idletimer;
-	unsigned short		paclen;
-	unsigned short		fragno, fraglen;
+	unsigned short		paclen, fragno, fraglen;
 	struct sk_buff_head	write_queue;
 	struct sk_buff_head	reseq_queue;
 	struct sk_buff_head	ack_queue;
@@ -313,7 +289,7 @@
 extern int  ax25_rt_get_info(char *, char **, off_t, int, int);
 extern int  ax25_rt_autobind(ax25_cb *, ax25_address *);
 extern void ax25_rt_build_path(ax25_cb *, ax25_address *, struct device *);
-extern void ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *);
+extern struct sk_buff *ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *);
 extern char ax25_ip_mode_get(ax25_address *, struct device *);
 extern void ax25_rt_free(void);
 
diff -u --recursive --new-file linux-2.1.32/include/net/netrom.h linux/include/net/netrom.h
--- linux-2.1.32/include/net/netrom.h	Fri Mar  7 14:49:10 1997
+++ linux/include/net/netrom.h	Tue Mar 25 09:56:27 1997
@@ -61,7 +61,7 @@
 	struct device		*device;
 	unsigned char		my_index,   my_id;
 	unsigned char		your_index, your_id;
-	unsigned char		state, condition, bpqext, hdrincl, window;
+	unsigned char		state, condition, bpqext, window;
 	unsigned short		vs, vr, va, vl;
 	unsigned char		n2, n2count;
 	unsigned short		t1, t2, t4, idle;
diff -u --recursive --new-file linux-2.1.32/include/net/rose.h linux/include/net/rose.h
--- linux-2.1.32/include/net/rose.h	Fri Mar  7 14:49:10 1997
+++ linux/include/net/rose.h	Tue Mar 25 09:56:35 1997
@@ -77,31 +77,41 @@
 #define	FAC_CCITT_SRC_NSAP		0xCB
 
 struct rose_neigh {
-	struct rose_neigh *next;
-	ax25_address      callsign;
-	ax25_digi         *digipeat;
-	struct device     *dev;
-	unsigned short    count;
-	unsigned int      number;
-	int               restarted;
-	struct sk_buff_head queue;
-	unsigned short    t0timer, ftimer;
-	struct timer_list timer;
+	struct rose_neigh	*next;
+	ax25_address		callsign;
+	ax25_digi		*digipeat;
+	struct device		*dev;
+	unsigned short		count;
+	unsigned int		number;
+	int			restarted;
+	struct sk_buff_head	queue;
+	unsigned short		t0timer, ftimer;
+	struct timer_list	timer;
 };
 
 struct rose_node {
-	struct rose_node  *next;
-	rose_address      address;
-	unsigned short    mask;
-	unsigned char     count;
-	struct rose_neigh *neighbour[3];
+	struct rose_node	*next;
+	rose_address		address;
+	unsigned short		mask;
+	unsigned char		count;
+	struct rose_neigh	*neighbour[3];
 };
 
 struct rose_route {
-	struct rose_route *next;
-	unsigned int	  lci1,    lci2;
-	struct rose_neigh *neigh1, *neigh2;
-	unsigned int      rand;
+	struct rose_route	*next;
+	unsigned int		lci1, lci2;
+	rose_address		src_addr, dest_addr;
+	ax25_address		src_call, dest_call;
+	struct rose_neigh 	*neigh1, *neigh2;
+	unsigned int		rand;
+};
+
+struct rose_facilities {
+	rose_address		source_addr,   dest_addr;
+	ax25_address		source_call,   dest_call;
+	unsigned char		source_ndigis, dest_ndigis;
+	ax25_address		source_digi,   dest_digi;
+	unsigned int		rand;
 };
 
 typedef struct {
@@ -112,7 +122,7 @@
 	struct rose_neigh	*neighbour;
 	struct device		*device;
 	unsigned int		lci, rand;
-	unsigned char		state, condition, hdrincl;
+	unsigned char		state, condition, qbitincl;
 	unsigned short		vs, vr, va, vl;
 	unsigned short		timer;
 	unsigned short		t1, t2, t3, hb, idle;
@@ -135,6 +145,7 @@
 extern int  rosecmpm(rose_address *, rose_address *, unsigned short);
 extern char *rose2asc(rose_address *);
 extern struct sock *rose_find_socket(unsigned int, struct device *);
+extern void rose_kill_by_neigh(struct rose_neigh *);
 extern unsigned int rose_new_lci(struct device *);
 extern int  rose_rx_call_request(struct sk_buff *, struct device *, struct rose_neigh *, unsigned int);
 extern void rose_destroy_socket(struct sock *);
@@ -183,7 +194,7 @@
 extern int  rose_validate_nr(struct sock *, unsigned short);
 extern void rose_write_internal(struct sock *, int);
 extern int  rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
-extern int  rose_parse_facilities(struct sk_buff *, rose_cb *);
+extern int  rose_parse_facilities(struct sk_buff *, struct rose_facilities *);
 extern int  rose_create_facilities(unsigned char *, rose_cb *);
 
 /* rose_timer.c */
diff -u --recursive --new-file linux-2.1.32/include/net/sock.h linux/include/net/sock.h
--- linux-2.1.32/include/net/sock.h	Fri Apr  4 11:30:40 1997
+++ linux/include/net/sock.h	Fri Apr  4 13:16:47 1997
@@ -271,20 +271,23 @@
  *		sockmem [mem, proto, callbacks]
  *
  *		union or struct {
- *			netrom;
- *			ax_25;
+ *			ax25;
  *		} ll_pinfo;
  *	
  *		union {
  *			ipv4;
  *			ipv6;
  *			ipx;
+ *			netrom;
+ *			rose;
+ * 			x25;
  *		} net_pinfo;
  *
  *		union {
  *			tcp;
  *			udp;
  *			spx;
+ *			netrom;
  *		} tp_pinfo;
  *
  *	}
diff -u --recursive --new-file linux-2.1.32/include/net/x25.h linux/include/net/x25.h
--- linux-2.1.32/include/net/x25.h	Fri Mar  7 14:49:10 1997
+++ linux/include/net/x25.h	Tue Mar 11 17:37:49 1997
@@ -157,6 +157,11 @@
 extern void x25_establish_link(struct x25_neigh *);
 extern void x25_terminate_link(struct x25_neigh *);
 
+/* x25_facilities.c */
+extern int  x25_parse_facilities(struct sk_buff *, struct x25_facilities *);
+extern int  x25_create_facilities(unsigned char *, struct x25_facilities *);
+extern int  x25_negotiate_facilities(struct sk_buff *, struct sock *, struct x25_facilities *);
+
 /* x25_in.c */
 extern int  x25_process_rx_frame(struct sock *, struct sk_buff *);
 
@@ -173,7 +178,6 @@
 extern void x25_transmit_link(struct sk_buff *, struct x25_neigh *);
 extern int  x25_subscr_ioctl(unsigned int, void *);
 extern struct x25_neigh *x25_get_neigh(struct device *);
-extern int  x25_link_get_info(char *, char **, off_t, int, int);
 extern void x25_link_free(void);
 
 /* x25_out.c */
@@ -195,8 +199,6 @@
 extern int  x25_validate_nr(struct sock *, unsigned short);
 extern void x25_write_internal(struct sock *, int);
 extern int  x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *);
-extern int  x25_parse_facilities(struct sk_buff *, struct x25_facilities *);
-extern int  x25_create_facilities(unsigned char *, struct x25_facilities *);
 
 /* x25_timer.c */
 extern void x25_set_timer(struct sock *);
diff -u --recursive --new-file linux-2.1.32/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- linux-2.1.32/net/ax25/af_ax25.c	Sun Apr  6 10:54:16 1997
+++ linux/net/ax25/af_ax25.c	Sun Apr  6 10:53:36 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
- *	releases, misbehave and/or generally screw up. It might even work.
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -140,11 +137,11 @@
 void ax25_free_cb(ax25_cb *ax25)
 {
 	if (ax25->digipeat != NULL) {
-		kfree_s(ax25->digipeat, sizeof(ax25_digi));
+		kfree(ax25->digipeat);
 		ax25->digipeat = NULL;
 	}
 
-	kfree_s(ax25, sizeof(ax25_cb));
+	kfree(ax25);
 
 	MOD_DEC_USE_COUNT;
 }
@@ -467,7 +464,6 @@
 			if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
 				ax25_dama_off(ax25);
 #endif
-
 			if (ax25->sk != NULL) {
 				ax25->sk->state     = TCP_CLOSE;
 				ax25->sk->err       = ENETRESET;
@@ -476,9 +472,8 @@
 					ax25->sk->state_change(ax25->sk);
 				ax25->sk->dead  = 1;
 			}
-
 			ax25_set_timer(ax25);
-	  		break;
+			break;
 
 	  	case AX25_WINDOW:
 	  		if (ax25->modulus == AX25_MODULUS) {
@@ -693,8 +688,8 @@
 			sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
 			return 0;
 
-		case AX25_HDRINCL:
-			sk->protinfo.ax25->hdrincl = opt ? 1 : 0;
+		case AX25_PIDINCL:
+			sk->protinfo.ax25->pidincl = opt ? 1 : 0;
 			return 0;
 
 		case AX25_IAMDIGI:
@@ -757,8 +752,8 @@
 			val = (sk->protinfo.ax25->modulus == AX25_EMODULUS);
 			break;
 
-		case AX25_HDRINCL:
-			val = sk->protinfo.ax25->hdrincl;
+		case AX25_PIDINCL:
+			val = sk->protinfo.ax25->pidincl;
 			break;
 
 		case AX25_IAMDIGI:
@@ -905,7 +900,7 @@
 
 	ax25->modulus = osk->protinfo.ax25->modulus;
 	ax25->backoff = osk->protinfo.ax25->backoff;
-	ax25->hdrincl = osk->protinfo.ax25->hdrincl;
+	ax25->pidincl = osk->protinfo.ax25->pidincl;
 	ax25->iamdigi = osk->protinfo.ax25->iamdigi;
 	ax25->rtt     = osk->protinfo.ax25->rtt;
 	ax25->t1      = osk->protinfo.ax25->t1;
@@ -1178,6 +1173,8 @@
 
 #ifdef CONFIG_AX25_DAMA_SLAVE
 		case AX25_PROTO_DAMA_SLAVE:
+			ax25->modulus = AX25_MODULUS;
+			ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
 			if (sk->protinfo.ax25->ax25_dev->dama.slave)
 				ax25_ds_establish_data_link(sk->protinfo.ax25);
 			else
@@ -1401,9 +1398,11 @@
 	/* User data follows immediately after the AX.25 data */
 	memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
 
-	/* Add the PID, usually AX25_TEXT */
-	asmptr  = skb_push(skb, 1);
-	*asmptr = sk->protocol;
+	/* Add the PID if one is not supplied by the user in the skb */
+	if (!sk->protinfo.ax25->pidincl) {
+		asmptr  = skb_push(skb, 1);
+		*asmptr = sk->protocol;
+	}
 
 	SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n");
 
@@ -1450,10 +1449,9 @@
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
-	int copied, length;
+	int copied;
 	struct sk_buff *skb;
 	int er;
-	int dama;
 
 	/*
 	 * 	This works for seqpacket too. The receiver has ordered the
@@ -1466,16 +1464,11 @@
 	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
-	if (sk->protinfo.ax25->hdrincl) {
-		length = skb->len + (skb->data - skb->h.raw);
-	} else {
-		if (sk->type == SOCK_SEQPACKET)
-			skb_pull(skb, 1);		/* Remove PID */
-		length     = skb->len;
-		skb->h.raw = skb->data;
-	}
+	if (!sk->protinfo.ax25->pidincl)
+		skb_pull(skb, 1);		/* Remove PID */
 
-	copied = length;
+	skb->h.raw = skb->data;
+	copied     = skb->len;
 
 	if (copied > size) {
 		copied = size;
@@ -1487,6 +1480,7 @@
 	if (sax != NULL) {
 		ax25_digi digi;
 		ax25_address dest;
+		int dama;
 
 		ax25_addr_parse(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
 
@@ -1811,6 +1805,9 @@
 }
 
 #ifdef MODULE
+MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol");
+
 int init_module(void)
 {
 	ax25_proto_init(NULL);
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_addr.c linux/net/ax25/ax25_addr.c
--- linux-2.1.32/net/ax25/ax25_addr.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_addr.c	Sat Mar 15 20:12:00 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_dev.c linux/net/ax25/ax25_dev.c
--- linux-2.1.32/net/ax25/ax25_dev.c	Sun Apr  6 10:54:16 1997
+++ linux/net/ax25/ax25_dev.c	Sun Apr  6 10:58:40 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -77,7 +74,7 @@
 	unsigned long flags;
 
 	if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
-		printk(KERN_ERR "ax25_dev_device_up out of memory\n");
+		printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
 		return;
 	}
 
@@ -102,11 +99,7 @@
 	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE;
 	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
 	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
-#ifdef CONFIG_AX25_DAMA_SLAVE
-	ax25_dev->values[AX25_VALUES_PROTOCOL]	= AX25_PROTO_DAMA_SLAVE;
-#else
 	ax25_dev->values[AX25_VALUES_PROTOCOL]  = AX25_DEF_PROTOCOL;
-#endif
 	ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
 
 	save_flags(flags); cli();
@@ -147,7 +140,7 @@
 	if ((s = ax25_dev_list) == ax25_dev) {
 		ax25_dev_list = s->next;
 		restore_flags(flags);
-		kfree_s(ax25_dev, sizeof(ax25_dev));
+		kfree(ax25_dev);
 #ifdef CONFIG_SYSCTL
 		ax25_register_sysctl();
 #endif
@@ -158,7 +151,7 @@
 		if (s->next == ax25_dev) {
 			s->next = ax25_dev->next;
 			restore_flags(flags);
-			kfree_s(ax25_dev, sizeof(ax25_dev));
+			kfree(ax25_dev);
 #ifdef CONFIG_SYSCTL
 			ax25_register_sysctl();
 #endif
@@ -229,7 +222,7 @@
 		s        = ax25_dev;
 		ax25_dev = ax25_dev->next;
 
-		kfree_s(s, sizeof(ax25_dev));
+		kfree(s);
 	}
 }
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_ds_in.c linux/net/ax25/ax25_ds_in.c
--- linux-2.1.32/net/ax25/ax25_ds_in.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_ds_in.c	Thu Mar 27 20:30:23 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -61,12 +58,6 @@
 			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			break;
 
-		case AX25_SABME:
-			ax25->modulus = AX25_EMODULUS;
-			ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			break;
-
 		case AX25_DISC:
 			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
 			break;
@@ -100,20 +91,15 @@
 
 		case AX25_DM:
 			if (pf) {
-				if (ax25->modulus == AX25_MODULUS) {
-					ax25_clear_queues(ax25);
-					ax25->state = AX25_STATE_0;
-					if (ax25->sk != NULL) {
-						ax25->sk->state     = TCP_CLOSE;
-						ax25->sk->err       = ECONNREFUSED;
-						ax25->sk->shutdown |= SEND_SHUTDOWN;
-						if (!ax25->sk->dead)
-							ax25->sk->state_change(ax25->sk);
-						ax25->sk->dead      = 1;
-					}
-				} else {
-					ax25->modulus = AX25_MODULUS;
-					ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+				ax25_clear_queues(ax25);
+				ax25->state = AX25_STATE_0;
+				if (ax25->sk != NULL) {
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = ECONNREFUSED;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
 				}
 			}
 			break;
@@ -136,9 +122,7 @@
 {
 	switch (frametype) {
 		case AX25_SABM:
-		case AX25_SABME:
 			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-			ax25->dama_slave = 0;
 			ax25_dama_off(ax25);
 			break;
 
@@ -146,7 +130,6 @@
 			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			ax25->state = AX25_STATE_0;
 			ax25_dama_off(ax25);
-
 			if (ax25->sk != NULL) {
 				ax25->sk->state     = TCP_CLOSE;
 				ax25->sk->err       = 0;
@@ -162,7 +145,6 @@
 			if (pf) {
 				ax25->state = AX25_STATE_0;
 				ax25_dama_off(ax25);
-
 				if (ax25->sk != NULL) {
 					ax25->sk->state     = TCP_CLOSE;
 					ax25->sk->err       = 0;
@@ -180,7 +162,6 @@
 		case AX25_RR:
 			if (pf) {
 				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-				ax25->dama_slave = 0;
 				ax25_dama_off(ax25);
 			}
 			break;
@@ -203,14 +184,8 @@
 
 	switch (frametype) {
 		case AX25_SABM:
-		case AX25_SABME:
-			if (frametype == AX25_SABM) {
-				ax25->modulus = AX25_MODULUS;
-				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
-			} else {
-				ax25->modulus = AX25_EMODULUS;
-				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
-			}
+			ax25->modulus   = AX25_MODULUS;
+			ax25->window    = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
 			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			ax25->condition = 0x00;
 			ax25->t1timer   = 0;
@@ -229,7 +204,6 @@
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
 			ax25_dama_off(ax25);
-
 			if (ax25->sk != NULL) {
 				ax25->sk->state     = TCP_CLOSE;
 				ax25->sk->err       = 0;
@@ -245,7 +219,6 @@
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
 			ax25_dama_off(ax25);
-
 			if (ax25->sk != NULL) {
 				ax25->sk->state     = TCP_CLOSE;
 				ax25->sk->err       = ECONNRESET;
@@ -306,193 +279,7 @@
 				break;
 			}
 			if (ns == ax25->vr) {
-				ax25->vr = (ax25->vr + 1) % ax25->modulus;
-				queued = ax25_rx_iframe(ax25, skb);
-				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
-					ax25->vr = ns;	/* ax25->vr - 1 */
-					if (pf) ax25_ds_enquiry_response(ax25);
-					break;
-				}
-				ax25->condition &= ~AX25_COND_REJECT;
-				if (pf) {
-					ax25_ds_enquiry_response(ax25);
-				} else {
-					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
-						ax25->t2timer = ax25->t2;
-						ax25->condition |= AX25_COND_ACK_PENDING;
-					}
-				}
-			} else {
-				if (ax25->condition & AX25_COND_REJECT) {
-					if (pf) ax25_ds_enquiry_response(ax25);
-				} else {
-					ax25->condition |= AX25_COND_REJECT;
-					ax25_ds_enquiry_response(ax25);
-					ax25->condition &= ~AX25_COND_ACK_PENDING;
-				}
-			}
-			break;
-
-		case AX25_FRMR:
-		case AX25_ILLEGAL:
-			ax25_ds_establish_data_link(ax25);
-			ax25->state = AX25_STATE_1;
-			break;
-
-		default:
-			break;
-	}
-
-	return queued;
-}
-
-/*
- *	State machine for state 4, Timer Recovery State.
- *	The handling of the timer(s) is in file ax25_timer.c
- *	Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
-	int queued = 0;
-
-	switch (frametype) {
-		case AX25_SABM:
-		case AX25_SABME:
-			if (frametype == AX25_SABM) {
-				ax25->modulus = AX25_MODULUS;
-				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
-			} else {
-				ax25->modulus = AX25_EMODULUS;
-				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
-			}
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			ax25->condition = 0x00;
-			ax25->t1timer   = 0;
-			ax25->t3timer   = ax25->t3;
-			ax25->idletimer = ax25->idle;
-			ax25->vs        = 0;
-			ax25->va        = 0;
-			ax25->vr        = 0;
-			ax25->state     = AX25_STATE_3;
-			ax25->n2count   = 0;
-			ax25_requeue_frames(ax25);
-			ax25_dama_on(ax25);
-			break;
-
-		case AX25_DISC:
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-
-			if (ax25->sk != NULL) {
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = 0;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-			break;
-
-		case AX25_DM:
-			ax25_clear_queues(ax25);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-
-			if (ax25->sk != NULL) {
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = ECONNRESET;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-			break;
-
-		case AX25_RR:
-		case AX25_RNR:
-			if (frametype == AX25_RR)
-				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-			else
-				ax25->condition |= AX25_COND_PEER_RX_BUSY;
-			if (type == AX25_RESPONSE && pf) {
-				ax25->t1timer = 0;
-				if (ax25_validate_nr(ax25, nr)) {
-					ax25_frames_acked(ax25, nr);
-					ax25->n2count = 0;
-					if (ax25->vs == ax25->va) {
-						ax25->t3timer = ax25->t3;
-						ax25->state   = AX25_STATE_3;
-					} else {
-						ax25_requeue_frames(ax25);
-					}
-				} else {
-					ax25_ds_nr_error_recovery(ax25);
-					ax25->state = AX25_STATE_1;
-				}
-				break;
-			}
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_frames_acked(ax25, nr);
-				ax25->n2count = 0;
-				if (type == AX25_COMMAND && pf)
-					ax25_ds_enquiry_response(ax25);
-			} else {
-				ax25_ds_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
-
-		case AX25_REJ:
-			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-			if (pf) {
-				ax25->t1timer = 0;
-				if (ax25_validate_nr(ax25, nr)) {
-					ax25_frames_acked(ax25, nr);
-					ax25->n2count = 0;
-					if (ax25->vs == ax25->va) {
-						ax25->t3timer = ax25->t3;
-						ax25->state   = AX25_STATE_3;
-					} else {
-						ax25_requeue_frames(ax25);
-					}
-					if (type == AX25_COMMAND && pf)
-						ax25_ds_enquiry_response(ax25);
-				} else {
-					ax25_ds_nr_error_recovery(ax25);
-					ax25->state = AX25_STATE_1;
-				}
-				break;
-			}
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_frames_acked(ax25, nr);
-				ax25->n2count = 0;
-				ax25_requeue_frames(ax25);
-				if (type == AX25_COMMAND && pf)
-					ax25_ds_enquiry_response(ax25);
-			} else {
-				ax25_ds_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
-
-		case AX25_I:
-			if (!ax25_validate_nr(ax25, nr)) {
-				ax25_ds_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-				break;
-			}
-			ax25_frames_acked(ax25, nr);
-			ax25->n2count = 0;
-			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
-				if (pf) ax25_ds_enquiry_response(ax25);
-				break;
-			}
-			if (ns == ax25->vr) {
-				ax25->vr = (ax25->vr + 1) % ax25->modulus;
+				ax25->vr = (ax25->vr + 1) % AX25_MODULUS;
 				queued = ax25_rx_iframe(ax25, skb);
 				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
 					ax25->vr = ns;	/* ax25->vr - 1 */
@@ -550,9 +337,6 @@
 			break;
 		case AX25_STATE_3:
 			queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
-			break;
-		case AX25_STATE_4:
-			queued = ax25_ds_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
 			break;
 	}
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_ds_subr.c linux/net/ax25/ax25_ds_subr.c
--- linux-2.1.32/net/ax25/ax25_ds_subr.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_ds_subr.c	Thu Mar 27 20:30:46 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -109,9 +106,7 @@
 			continue;
 		}
 
-		if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && 
-		    (ax25o->state == AX25_STATE_3 || 
-		    (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
+		if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) {
 			ax25_requeue_frames(ax25o);
 			ax25_kick(ax25o);
 		}
@@ -125,12 +120,11 @@
 
 void ax25_ds_establish_data_link(ax25_cb *ax25)
 {
-	ax25->condition = 0x00;
-	ax25->n2count   = 0;
-
-	ax25->t3timer = ax25->t3;
-	ax25->t2timer = 0;
-	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+	ax25->condition &= AX25_COND_DAMA_MODE;
+	ax25->n2count    = 0;
+	ax25->t3timer    = ax25->t3;
+	ax25->t2timer    = 0;
+	ax25->t1timer    = ax25->t1 = ax25_calculate_t1(ax25);
 }
 
 /*
@@ -139,7 +133,6 @@
  *	We need a driver level  request to switch duplex mode, that does 
  *	either SCC changing, PI config or KISS as required. Currently
  *	this request isn't reliable.
- *
  */
 static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param)
 {
@@ -173,13 +166,12 @@
  *	ax25->dama_slave=1 and look on every disconnect if still slave
  *	connections exist.
  */
-
 static int ax25_check_dama_slave(ax25_dev *ax25_dev)
 {
 	ax25_cb *ax25;
 
 	for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next)
-		if (ax25->ax25_dev == ax25_dev && ax25->dama_slave && ax25->state > AX25_STATE_1)
+		if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1)
 			return 1;
 
 	return 0;
@@ -202,8 +194,7 @@
 	if (ax25_dev == NULL)
 		return;
 
-	if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev))
-	{
+	if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) {
 		ax25_kiss_cmd(ax25_dev, 5, 0);
 		ax25_dev->dama.slave = 0;
 		ax25_ds_del_timer(ax25_dev);
@@ -213,13 +204,13 @@
 void ax25_dama_on(ax25_cb *ax25)
 {
 	ax25_dev_dama_on(ax25->ax25_dev);
-	ax25->dama_slave = 1;
+	ax25->condition |= AX25_COND_DAMA_MODE;
 }
 
 void ax25_dama_off(ax25_cb *ax25)
 {
 	ax25_dev_dama_off(ax25->ax25_dev);
-	ax25->dama_slave = 0;
+	ax25->condition &= ~AX25_COND_DAMA_MODE;
 }
 
 #endif
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_ds_timer.c linux/net/ax25/ax25_ds_timer.c
--- linux-2.1.32/net/ax25/ax25_ds_timer.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_ds_timer.c	Thu Mar 27 20:31:06 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -87,15 +84,13 @@
 	if (ax25_dev == NULL || !ax25_dev->dama.slave)
 		return;			/* Yikes! */
 	
-	if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout)
-	{
+	if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) {
 		ax25_ds_set_timer(ax25_dev);
 		return;
 	}
 	
-	for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next)
-	{
-		if (ax25->ax25_dev != ax25_dev || !ax25->dama_slave)
+	for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) {
+		if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
 			continue;
 
 		ax25_link_failed(&ax25->dest_addr, ax25_dev->dev);
@@ -103,8 +98,7 @@
 		ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 		ax25->state = AX25_STATE_0;
 
-		if (ax25->sk != NULL)
-		{
+		if (ax25->sk != NULL) {
 			SOCK_DEBUG(ax25->sk, "AX.25 DAMA Slave Timeout\n");
 			ax25->sk->state     = TCP_CLOSE;
 			ax25->sk->err       = ETIMEDOUT;
@@ -120,7 +114,6 @@
 	ax25_dev_dama_off(ax25_dev);
 }
 
-
 /*
  *	AX.25 TIMER 
  *
@@ -141,7 +134,6 @@
 			break;
 
 		case AX25_STATE_3:
-		case AX25_STATE_4:
 			/*
 			 * Check the state of the receive buffer.
 			 */
@@ -279,12 +271,7 @@
 			}
 			break;
 
-		case AX25_STATE_3: 
-			ax25->n2count = 1;
-			ax25->state   = AX25_STATE_4;
-			break;
-
-		case AX25_STATE_4:
+		case AX25_STATE_3:
 			if (ax25->n2count == ax25->n2) {
 				ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
 				ax25_clear_queues(ax25);
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_iface.c linux/net/ax25/ax25_iface.c
--- linux-2.1.32/net/ax25/ax25_iface.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_iface.c	Sun Mar 16 11:08:32 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -98,7 +95,7 @@
 	if (protocol->pid == pid) {
 		protocol_list = protocol->next;
 		restore_flags(flags);
-		kfree_s(protocol, sizeof(struct protocol_struct));
+		kfree(protocol);
 		return;
 	}
 
@@ -107,7 +104,7 @@
 			s = protocol->next;
 			protocol->next = protocol->next->next;
 			restore_flags(flags);
-			kfree_s(s, sizeof(struct protocol_struct));
+			kfree(s);
 			return;
 		}
 
@@ -152,7 +149,7 @@
 	if (linkfail->func == func) {
 		linkfail_list = linkfail->next;
 		restore_flags(flags);
-		kfree_s(linkfail, sizeof(struct linkfail_struct));
+		kfree(linkfail);
 		return;
 	}
 
@@ -161,7 +158,7 @@
 			s = linkfail->next;
 			linkfail->next = linkfail->next->next;
 			restore_flags(flags);
-			kfree_s(s, sizeof(struct linkfail_struct));
+			kfree(s);
 			return;
 		}
 
@@ -210,7 +207,7 @@
 	if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
 		listen_list = listen->next;
 		restore_flags(flags);
-		kfree_s(listen, sizeof(struct listen_struct));
+		kfree(listen);
 		return;
 	}
 
@@ -219,7 +216,7 @@
 			s = listen->next;
 			listen->next = listen->next->next;
 			restore_flags(flags);
-			kfree_s(s, sizeof(struct listen_struct));
+			kfree(s);
 			return;
 		}
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- linux-2.1.32/net/ax25/ax25_in.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_in.c	Mon Mar 31 12:01:55 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -73,7 +70,6 @@
 static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
 {
 	struct sk_buff *skbn, *skbo;
-	int hdrlen, nhdrlen;
 
 	if (ax25->fragno != 0) {
 		if (!(*skb->data & AX25_SEG_FIRST)) {
@@ -92,29 +88,16 @@
 						return 1;
 					}
 
-					skbn->dev = ax25->ax25_dev->dev;
-
 					skb_reserve(skbn, AX25_MAX_HEADER_LEN);
 
-					/* Get first fragment from queue */
-					skbo = skb_dequeue(&ax25->frag_queue);
-					hdrlen  = skbo->data - skbo->h.raw;
-					nhdrlen = hdrlen - 2;
-
-					skb_push(skbo, hdrlen);
-					skb_push(skbn, nhdrlen);
+					skbn->dev   = ax25->ax25_dev->dev;
 					skbn->h.raw = skbn->data;
 
-					/* Copy AX.25 headers */
-					memcpy(skbn->data, skbo->data, nhdrlen);
-					skb_pull(skbn, nhdrlen);
-					skb_pull(skbo, hdrlen);
-
 					/* Copy data from the fragments */
-					do {
+					while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) {
 						memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
 						kfree_skb(skbo, FREE_READ);
-					} while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL);
+					}
 
 					ax25->fraglen = 0;
 
@@ -188,11 +171,13 @@
 		return (*func)(skb, ax25);
 	}
 
-	if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2 && ax25->sk->protocol == pid) {
-		if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
-			queued = 1;
-		else
-			ax25->condition |= AX25_COND_OWN_RX_BUSY;
+	if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) {
+		if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) {
+			if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
+				queued = 1;
+			else
+				ax25->condition |= AX25_COND_OWN_RX_BUSY;
+		}
 	}
 
 	return queued;
@@ -256,7 +241,7 @@
 		return 0;
 	}
 
-	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL,&skb) != FW_ACCEPT) {
+	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL, &skb) != FW_ACCEPT) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -445,7 +430,7 @@
 
 	if (dp.ndigi == 0) {
 		if (ax25->digipeat != NULL) {
-			kfree_s(ax25->digipeat, sizeof(ax25_digi));
+			kfree(ax25->digipeat);
 			ax25->digipeat = NULL;
 		}
 	} else {
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_ip.c linux/net/ax25/ax25_ip.c
--- linux-2.1.32/net/ax25/ax25_ip.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_ip.c	Tue Mar 18 20:18:52 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
- *	releases, misbehave and/or generally screw up. It might even work.
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -94,7 +91,7 @@
   			*buff++ = AX25_P_ARP;
   			break;
   		default:
-  			printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
+  			printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%x2.2\n", type);
   			*buff++ = 0;
   			break;
  	}
@@ -138,7 +135,7 @@
 			 *	gets fixed.
 			 */
 			if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
-				dev_kfree_skb(skb, FREE_WRITE);
+				kfree_skb(skb, FREE_WRITE);
 				return 1;
 			}
 
@@ -163,12 +160,15 @@
   	bp[14] |= AX25_EBIT;
   	bp[14] |= AX25_SSSID_SPARE;
 
-	ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
+	if ((ourskb = ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev)) == NULL) {
+		kfree_skb(skb, FREE_WRITE);
+		return 1;
+	}
 
-	skb->dev      = dev;
-	skb->priority = SOPRI_NORMAL;
+	ourskb->dev      = dev;
+	ourskb->priority = SOPRI_NORMAL;
 
-	ax25_queue_xmit(skb);
+	ax25_queue_xmit(ourskb);
 
   	return 1;
 }
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- linux-2.1.32/net/ax25/ax25_out.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_out.c	Thu Mar 27 20:28:06 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -157,7 +154,7 @@
 
 			if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
 				restore_flags(flags);
-				printk(KERN_DEBUG "ax25_output: alloc_skb returned NULL\n");
+				printk(KERN_CRIT "AX.25: ax25_output - out of memory\n");
 				return;
 			}
 
@@ -314,7 +311,9 @@
 
 void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
 {
+	struct sk_buff *skbn;
 	unsigned char *ptr;
+	int headroom;
 
 	if (ax25->ax25_dev == NULL) {
 		if (ax25->sk != NULL) {
@@ -328,13 +327,24 @@
 		return;
 	}
 
-	if (skb_headroom(skb) < ax25_addr_size(ax25->digipeat)) {
-		printk(KERN_CRIT "ax25_transmit_buffer: not enough room for digi-peaters\n");
+	headroom = ax25_addr_size(ax25->digipeat);
+
+	if (skb_headroom(skb) < headroom) {
+		if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) {
+			printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n");
+			kfree_skb(skb, FREE_WRITE);
+			return;
+		}
+
+		if (skb->sk != NULL)
+			skb_set_owner_w(skbn, skb->sk);
+
 		kfree_skb(skb, FREE_WRITE);
-		return;
+		skb = skbn;
 	}
 
-	ptr = skb_push(skb, ax25_addr_size(ax25->digipeat));
+	ptr = skb_push(skb, headroom);
+
 	ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
 
 	skb->dev      = ax25->ax25_dev->dev;
@@ -352,7 +362,7 @@
 	unsigned char *ptr;
 
 	if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- linux-2.1.32/net/ax25/ax25_route.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_route.c	Tue Mar 18 20:20:24 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -99,15 +96,15 @@
 			if (ax25_route_list == s) {
 				ax25_route_list = s->next;
 				if (s->digipeat != NULL)
-					kfree_s(s->digipeat, sizeof(ax25_digi));
-				kfree_s(s, sizeof(ax25_route));
+					kfree(s->digipeat);
+				kfree(s);
 			} else {
 				for (t = ax25_route_list; t != NULL; t = t->next) {
 					if (t->next == s) {
 						t->next = s->next;
 						if (s->digipeat != NULL)
-							kfree_s(s->digipeat, sizeof(ax25_digi));
-						kfree_s(s, sizeof(ax25_route));
+							kfree(s->digipeat);
+						kfree(s);
 						break;
 					}
 				}
@@ -137,7 +134,7 @@
 			for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 				if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) {
 					if (ax25_rt->digipeat != NULL) {
-						kfree_s(ax25_rt->digipeat, sizeof(ax25_digi));
+						kfree(ax25_rt->digipeat);
 						ax25_rt->digipeat = NULL;
 					}
 					if (route.digi_count != 0) {
@@ -161,7 +158,7 @@
 			ax25_rt->ip_mode      = ' ';
 			if (route.digi_count != 0) {
 				if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-					kfree_s(ax25_rt, sizeof(ax25_route));
+					kfree(ax25_rt);
 					return -ENOMEM;
 				}
 				ax25_rt->digipeat->lastrepeat = -1;
@@ -191,15 +188,15 @@
 					if (ax25_route_list == s) {
 						ax25_route_list = s->next;
 						if (s->digipeat != NULL)
-							kfree_s(s->digipeat, sizeof(ax25_digi));
-						kfree_s(s, sizeof(ax25_route));
+							kfree(s->digipeat);
+						kfree(s);
 					} else {
 						for (t = ax25_route_list; t != NULL; t = t->next) {
 							if (t->next == s) {
 								t->next = s->next;
 								if (s->digipeat != NULL)
-									kfree_s(s->digipeat, sizeof(ax25_digi));
-								kfree_s(s, sizeof(ax25_route));
+									kfree(s->digipeat);
+								kfree(s);
 								break;
 							}
 						}				
@@ -413,8 +410,9 @@
 	ax25_adjust_path(addr, ax25->digipeat);
 }
 
-void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
+struct sk_buff *ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
 {
+	struct sk_buff *skbn;
 	ax25_route *ax25_rt;
 	ax25_digi digipeat;
 	ax25_address src, dest;
@@ -424,10 +422,10 @@
 	skb_pull(skb, 1);	/* skip KISS command */
 
 	if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
-		return;
+		return skb;
 
 	if (ax25_rt->digipeat == NULL)
-		return;
+		return skb;
 
 	digipeat = *ax25_rt->digipeat;
 
@@ -436,8 +434,17 @@
 	len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN;
 
 	if (skb_headroom(skb) < len) {
-		printk(KERN_CRIT "ax25_dg_build_path: not enough headroom for digis in skb\n");
-		return;
+		if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
+			printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
+			return NULL;
+		}
+
+		if (skb->sk != NULL)
+			skb_set_owner_w(skbn, skb->sk);
+
+		kfree_skb(skb, FREE_WRITE);
+
+		skb = skbn;
 	}
 
 	memcpy(&dest, skb->data + 0, AX25_ADDR_LEN);
@@ -446,6 +453,8 @@
 	bp = skb_push(skb, len);
 
 	ax25_addr_build(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS);
+
+	return skb;
 }
 
 /*
@@ -476,9 +485,9 @@
 		ax25_rt = ax25_rt->next;
 
 		if (s->digipeat != NULL)
-			kfree_s(s->digipeat, sizeof(ax25_digi));
+			kfree(s->digipeat);
 
-		kfree_s(s, sizeof(ax25_route));
+		kfree(s);
 	}
 }
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_std_in.c linux/net/ax25/ax25_std_in.c
--- linux-2.1.32/net/ax25/ax25_std_in.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_std_in.c	Sat Mar 15 20:13:36 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_std_subr.c linux/net/ax25/ax25_std_subr.c
--- linux-2.1.32/net/ax25/ax25_std_subr.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_std_subr.c	Sat Mar 15 20:13:25 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_std_timer.c linux/net/ax25/ax25_std_timer.c
--- linux-2.1.32/net/ax25/ax25_std_timer.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_std_timer.c	Sat Mar 15 20:13:08 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- linux-2.1.32/net/ax25/ax25_subr.c	Fri Mar  7 14:49:12 1997
+++ linux/net/ax25/ax25_subr.c	Sat Mar 15 20:12:55 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -289,6 +286,9 @@
  */
 void ax25_calculate_rtt(ax25_cb *ax25)
 {
+	if (ax25->backoff == 0)
+		return;
+
 	if (ax25->t1timer > 0 && ax25->n2count == 0)
 		ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10;
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- linux-2.1.32/net/ax25/ax25_timer.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/ax25_timer.c	Sat Mar 15 20:12:44 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/ax25/ax25_uid.c linux/net/ax25/ax25_uid.c
--- linux-2.1.32/net/ax25/ax25_uid.c	Fri Mar  7 14:49:13 1997
+++ linux/net/ax25/ax25_uid.c	Sun Mar 16 11:05:02 1997
@@ -1,9 +1,6 @@
 /*
  *	AX.25 release 036
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
- *	releases, misbehave and/or generally screw up. It might even work.
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -110,14 +107,14 @@
 			if ((s = ax25_uid_list) == ax25_uid) {
 				ax25_uid_list = s->next;
 				restore_flags(flags);
-				kfree_s(ax25_uid, sizeof(ax25_uid_assoc));
+				kfree(ax25_uid);
 				return 0;
 			}
 			while (s != NULL && s->next != NULL) {
 				if (s->next == ax25_uid) {
 					s->next = ax25_uid->next;
 					restore_flags(flags);
-					kfree_s(ax25_uid, sizeof(ax25_uid_assoc));
+					kfree(ax25_uid);
 					return 0;
 				}
 				s = s->next;
@@ -180,7 +177,7 @@
 		s        = ax25_uid;
 		ax25_uid = ax25_uid->next;
 
-		kfree_s(s, sizeof(ax25_uid_assoc));
+		kfree(s);
 	}
 }
 
diff -u --recursive --new-file linux-2.1.32/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- linux-2.1.32/net/ax25/sysctl_net_ax25.c	Thu Mar 27 19:43:26 1997
+++ linux/net/ax25/sysctl_net_ax25.c	Thu Mar 27 20:29:36 1997
@@ -5,6 +5,7 @@
  * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS]
  */
 
+#include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/sysctl.h>
 #include <net/ax25.h>
@@ -122,7 +123,7 @@
 		ax25_table[n].proc_handler = NULL;
 
 		memcpy(ax25_dev->systable, ax25_param_table, sizeof(ax25_dev->systable));
-		
+
 #ifndef CONFIG_AX25_DAMA_SLAVE
 		/* 
 		 * We do not wish to have a representation of this parameter
@@ -150,7 +151,7 @@
 {
 	unregister_sysctl_table(ax25_table_header);
 
-	kfree_s(ax25_table, ax25_table_size);
+	kfree(ax25_table);
 
 	ax25_dir_table[0].child = NULL;
 }
diff -u --recursive --new-file linux-2.1.32/net/lapb/lapb_iface.c linux/net/lapb/lapb_iface.c
--- linux-2.1.32/net/lapb/lapb_iface.c	Fri Mar  7 14:49:16 1997
+++ linux/net/lapb/lapb_iface.c	Thu Mar 20 19:26:28 1997
@@ -49,7 +49,7 @@
  */
 static void lapb_free_cb(lapb_cb *lapb)
 {
-	kfree_s(lapb, sizeof(lapb_cb));
+	kfree(lapb);
 
 	MOD_DEC_USE_COUNT;
 }
@@ -122,7 +122,7 @@
 {
 	lapb_cb *lapb;
 
-	if ((lapb = (lapb_cb *)kmalloc(sizeof(*lapb), GFP_ATOMIC)) == NULL)
+	if ((lapb = kmalloc(sizeof(*lapb), GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	MOD_INC_USE_COUNT;
@@ -403,6 +403,9 @@
 }
 
 #ifdef MODULE
+MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The X.25 Link Access Procedure B link layer protocol");
+
 int init_module(void)
 {
 	lapb_proto_init(NULL);
diff -u --recursive --new-file linux-2.1.32/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- linux-2.1.32/net/netrom/af_netrom.c	Sun Apr  6 10:54:16 1997
+++ linux/net/netrom/af_netrom.c	Sun Apr  6 10:53:37 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -30,6 +27,7 @@
  *	NET/ROM 005	Jonathan(G4KLX) Linux 2.1
  *			Alan(GW4PTS)	Started POSIXisms
  *	NET/ROM 006	Alan(GW4PTS)	Brought in line with the ANK changes
+ *			Jonathan(G4KLX)	Removed hdrincl.
  */
 
 #include <linux/config.h>
@@ -65,6 +63,8 @@
 #include <net/arp.h>
 #include <linux/if_arp.h>
 
+int nr_ndevs = 4;
+
 int sysctl_netrom_default_path_quality            = NR_DEFAULT_QUAL;
 int sysctl_netrom_obsolescence_count_initialiser  = NR_DEFAULT_OBS;
 int sysctl_netrom_network_ttl_initialiser         = NR_DEFAULT_TTL;
@@ -85,7 +85,7 @@
 
 static void nr_free_sock(struct sock *sk)
 {
-	kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
+	kfree(sk->protinfo.nr);
 
 	sk_free(sk);
 
@@ -100,7 +100,7 @@
 	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return NULL;
 
-	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
+	if ((nr = kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
 		sk_free(sk);
 		return NULL;
 	}
@@ -460,10 +460,6 @@
 			sk->protinfo.nr->idle = opt * 60 * NR_SLOWHZ;
 			return 0;
 
-		case NETROM_HDRINCL:
-			sk->protinfo.nr->hdrincl = opt ? 1 : 0;
-			return 0;
-
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -503,10 +499,6 @@
 			val = sk->protinfo.nr->idle / (NR_SLOWHZ * 60);
 			break;
 
-		case NETROM_HDRINCL:
-			val = sk->protinfo.nr->hdrincl;
-			break;
-
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -612,7 +604,6 @@
 
 	nr->device  = osk->protinfo.nr->device;
 	nr->bpqext  = osk->protinfo.nr->bpqext;
-	nr->hdrincl = osk->protinfo.nr->hdrincl;
 
 	return sk;
 }
@@ -1120,7 +1111,6 @@
 	return len;
 }
 
-
 static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, 
 	int flags, struct scm_cookie *scm)
 {
@@ -1142,12 +1132,8 @@
 	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
-	if (!sk->protinfo.nr->hdrincl) {
-		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
-		skb->h.raw = skb->data;
-	}
-
-	copied = skb->len;
+	skb->h.raw = skb->data;
+	copied     = skb->len;
 
 	if (copied > size) {
 		copied = size;
@@ -1353,28 +1339,32 @@
 };
 #endif	
 
-static struct device dev_nr[] = {
-	{"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
-	{"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
-	{"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
-	{"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
-};
+static struct device *dev_nr;
 
 void nr_proto_init(struct net_proto *pro)
 {
 	int i;
 
+	if ((dev_nr = kmalloc(nr_ndevs * sizeof(struct device), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n");
+		return;
+	}
+
+	memset(dev_nr, 0x00, nr_ndevs * sizeof(struct device));
+
+	for (i = 0; i < nr_ndevs; i++) {
+		dev_nr[i].name = kmalloc(20, GFP_KERNEL);
+		sprintf(dev_nr[i].name, "nr%d", i);
+		dev_nr[i].init = nr_init;
+		register_netdev(&dev_nr[i]);
+	}
+
 	sock_register(&nr_family_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
 	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.035 Linux 2.1\n");
 
-	if (!ax25_protocol_register(AX25_P_NETROM, nr_route_frame))
-		printk(KERN_ERR "NET/ROM unable to register protocol with AX.25\n");
-	if (!ax25_linkfail_register(nr_link_failed))
-		printk(KERN_ERR "NET/ROM unable to register linkfail handler with AX.25\n");
-
-	for (i = 0; i < 4; i++)
-		register_netdev(&dev_nr[i]);
+	ax25_protocol_register(AX25_P_NETROM, nr_route_frame);
+	ax25_linkfail_register(nr_link_failed);
 
 #ifdef CONFIG_SYSCTL
 	nr_register_sysctl();
@@ -1390,6 +1380,12 @@
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
 
+MODULE_PARM(nr_ndevs, "i");
+MODULE_PARM_DESC(nr_ndevs, "number of NET/ROM devices");
+
+MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The amateur radio NET/ROM network and transport layer protocol");
+
 int init_module(void)
 {
 	nr_proto_init(NULL);
@@ -1418,13 +1414,16 @@
 #endif
 	sock_unregister(AF_NETROM);
 
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < nr_ndevs; i++) {
 		if (dev_nr[i].priv != NULL) {
 			kfree(dev_nr[i].priv);
 			dev_nr[i].priv = NULL;
 			unregister_netdev(&dev_nr[i]);
 		}
+		kfree(dev_nr[i].name);
 	}
+
+	kfree(dev_nr);
 }
 
 #endif
diff -u --recursive --new-file linux-2.1.32/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- linux-2.1.32/net/netrom/nr_dev.c	Thu Mar 27 19:43:33 1997
+++ linux/net/netrom/nr_dev.c	Mon Mar 31 14:31:01 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
- *	releases, misbehave and/or generally screw up. It might even work.
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -123,7 +120,7 @@
 	struct sk_buff *skbn;
 	unsigned char *bp = skb->data;
 
-	if (!arp_find(bp + 7, skb)) {
+	if (arp_find(bp + 7, skb)) {
 		kfree_skb(skb, FREE_WRITE);
 		return 1;
 	}
@@ -203,7 +200,7 @@
 		return 0;
 
 	if (!dev->start) {
-		printk(KERN_ERR "netrom: xmit call when iface is down\n");
+		printk(KERN_ERR "NET/ROM: nr_xmit - called when iface is down\n");
 		return 1;
 	}
 
@@ -254,10 +251,12 @@
 	dev->flags		= 0;
 	dev->family		= AF_INET;
 
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr		= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= sizeof(unsigned long);
+#ifdef CONFIG_INET
+	dev->pa_addr		= in_aton("192.168.0.1");
+	dev->pa_brdaddr		= in_aton("192.168.0.255");
+	dev->pa_mask		= in_aton("255.255.255.0");
+	dev->pa_alen		= 4;
+#endif
 
 	if ((dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL)) == NULL)
 		return -ENOMEM;
diff -u --recursive --new-file linux-2.1.32/net/netrom/nr_in.c linux/net/netrom/nr_in.c
--- linux-2.1.32/net/netrom/nr_in.c	Fri Mar  7 14:48:20 1997
+++ linux/net/netrom/nr_in.c	Tue Mar 25 16:32:29 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -23,6 +20,7 @@
  *	NET/ROM 003	Jonathan(G4KLX)	Added NET/ROM fragment reception.
  *			Darryl(G7LED)	Added missing INFO with NAK case, optimized
  *					INFOACK handling, removed reconnect on error.
+ *	NET/ROM 006	Jonathan(G4KLX)	Hdrincl removal changes.
  */
 
 #include <linux/config.h>
@@ -54,6 +52,8 @@
 {
 	struct sk_buff *skbo, *skbn = skb;
 
+	skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+
 	if (more) {
 		sk->protinfo.nr->fraglen += skb->len;
 		skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
@@ -69,12 +69,7 @@
 
 		skbn->h.raw = skbn->data;
 
-		skbo = skb_dequeue(&sk->protinfo.nr->frag_queue);
-		memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
-		kfree_skb(skbo, FREE_READ);
-
 		while ((skbo = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) {
-			skb_pull(skbo, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
 			kfree_skb(skbo, FREE_READ);
 		}
diff -u --recursive --new-file linux-2.1.32/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- linux-2.1.32/net/netrom/nr_out.c	Fri Mar  7 14:48:20 1997
+++ linux/net/netrom/nr_out.c	Sat Mar 15 20:17:00 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- linux-2.1.32/net/netrom/nr_route.c	Thu Mar 27 19:43:33 1997
+++ linux/net/netrom/nr_route.c	Thu Mar 27 20:18:59 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -91,7 +88,7 @@
 		return 0;
 
 	if (nr_neigh == NULL) {
-		if ((nr_neigh = (struct nr_neigh *)kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
+		if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
 
 		nr_neigh->callsign = *ax25;
@@ -105,7 +102,7 @@
 
 		if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
 			if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
-				kfree_s(nr_neigh, sizeof(*nr_neigh));
+				kfree(nr_neigh);
 				return -ENOMEM;
 			}
 			*nr_neigh->digipeat = *ax25_digi;
@@ -124,7 +121,7 @@
 		nr_neigh->quality = quality;
 
 	if (nr_node == NULL) {
-		if ((nr_node = (struct nr_node *)kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL)
+		if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
 
 		nr_node->callsign = *nr;
@@ -252,7 +249,7 @@
 	if ((s = nr_node_list) == nr_node) {
 		nr_node_list = nr_node->next;
 		restore_flags(flags);
-		kfree_s(nr_node, sizeof(struct nr_node));
+		kfree(nr_node);
 		return;
 	}
 
@@ -260,7 +257,7 @@
 		if (s->next == nr_node) {
 			s->next = nr_node->next;
 			restore_flags(flags);
-			kfree_s(nr_node, sizeof(struct nr_node));
+			kfree(nr_node);
 			return;
 		}
 
@@ -282,8 +279,8 @@
 		nr_neigh_list = nr_neigh->next;
 		restore_flags(flags);
 		if (nr_neigh->digipeat != NULL)
-			kfree_s(nr_neigh->digipeat, sizeof(ax25_digi));
-		kfree_s(nr_neigh, sizeof(struct nr_neigh));
+			kfree(nr_neigh->digipeat);
+		kfree(nr_neigh);
 		return;
 	}
 
@@ -292,8 +289,8 @@
 			s->next = nr_neigh->next;
 			restore_flags(flags);
 			if (nr_neigh->digipeat != NULL)
-				kfree_s(nr_neigh->digipeat, sizeof(ax25_digi));
-			kfree_s(nr_neigh, sizeof(struct nr_neigh));
+				kfree(nr_neigh->digipeat);
+			kfree(nr_neigh);
 			return;
 		}
 
@@ -370,7 +367,7 @@
 		}
 	}
 
-	if ((nr_neigh = (struct nr_neigh *)kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
+	if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
 	nr_neigh->callsign = *callsign;
@@ -384,7 +381,7 @@
 
 	if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
 		if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
-			kfree_s(nr_neigh, sizeof(*nr_neigh));
+			kfree(nr_neigh);
 			return -ENOMEM;
 		}
 		*nr_neigh->digipeat = *ax25_digi;
@@ -688,6 +685,7 @@
 
 	if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
+
 	if (ax25 == NULL && call_out_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
 
diff -u --recursive --new-file linux-2.1.32/net/netrom/nr_subr.c linux/net/netrom/nr_subr.c
--- linux-2.1.32/net/netrom/nr_subr.c	Fri Mar  7 14:48:20 1997
+++ linux/net/netrom/nr_subr.c	Tue Mar 18 20:14:14 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
@@ -158,7 +155,7 @@
 		case NR_INFOACK:
 			break;
 		default:
-			printk(KERN_ERR "nr_write_internal: invalid frame type %d\n", frametype);
+			printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype);
 			return;
 	}
 
diff -u --recursive --new-file linux-2.1.32/net/netrom/nr_timer.c linux/net/netrom/nr_timer.c
--- linux-2.1.32/net/netrom/nr_timer.c	Thu Mar 27 19:43:33 1997
+++ linux/net/netrom/nr_timer.c	Sat Mar 15 20:17:29 1997
@@ -1,9 +1,6 @@
 /*
  *	NET/ROM release 006
  *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
- *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
diff -u --recursive --new-file linux-2.1.32/net/netsyms.c linux/net/netsyms.c
--- linux-2.1.32/net/netsyms.c	Sun Apr  6 10:54:16 1997
+++ linux/net/netsyms.c	Sun Apr  6 10:53:38 1997
@@ -28,6 +28,7 @@
 #include <net/route.h>
 #include <net/scm.h>
 #include <net/inet_common.h>
+#include <linux/inet.h>
 #include <linux/net_alias.h>
 #include <linux/mroute.h>
 
@@ -166,6 +167,8 @@
 EXPORT_SYMBOL(ip_fragment);
 EXPORT_SYMBOL(ip_dev_find_tunnel);
 EXPORT_SYMBOL(inet_family_ops);
+EXPORT_SYMBOL(in_aton);
+EXPORT_SYMBOL(in_ntoa);
 
 #ifdef CONFIG_IPV6_MODULE
 /* inet functions common to v4 and v6 */
diff -u --recursive --new-file linux-2.1.32/net/rose/af_rose.c linux/net/rose/af_rose.c
--- linux-2.1.32/net/rose/af_rose.c	Sun Apr  6 10:54:17 1997
+++ linux/net/rose/af_rose.c	Sun Apr  6 10:53:38 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,10 +10,13 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from af_netrom.c.
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from af_netrom.c.
  *			Alan(GW4PTS)	Hacked up for newer API stuff
  *			Terry (VK2KTJ)	Added support for variable length
  * 					address masks.
+ *	ROSE 002	Jonathan(G4KLX)	Changed hdrincl to qbitincl.
+ *					Added random number facilities entry.
+ *					Variable number of ROSE devices.
  */
 
 #include <linux/config.h>
@@ -53,6 +53,8 @@
 #include <net/arp.h>
 #include <linux/if_arp.h>
 
+int rose_ndevs = 6;
+
 int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0;
 int sysctl_rose_call_request_timeout    = ROSE_DEFAULT_T1;
 int sysctl_rose_reset_request_timeout   = ROSE_DEFAULT_T2;
@@ -71,7 +73,7 @@
 ax25_address rose_callsign;
 
 /*
- *	Convert a Rose address into text.
+ *	Convert a ROSE address into text.
  */
 char *rose2asc(rose_address *addr)
 {
@@ -93,7 +95,7 @@
 }
 
 /*
- *	Compare two Rose addresses, 0 == equal.
+ *	Compare two ROSE addresses, 0 == equal.
  */
 int rosecmp(rose_address *addr1, rose_address *addr2)
 {
@@ -107,7 +109,7 @@
 }
 
 /*
- *	Compare two Rose addresses for only mask digits, 0 == equal.
+ *	Compare two ROSE addresses for only mask digits, 0 == equal.
  */
 int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask)
 {
@@ -133,7 +135,7 @@
 
 static void rose_free_sock(struct sock *sk)
 {
-	kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
+	kfree(sk->protinfo.rose);
 
 	sk_free(sk);
 
@@ -148,7 +150,7 @@
 	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return NULL;
 
-	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+	if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
 		sk_free(sk);
 		return NULL;
 	}
@@ -194,6 +196,27 @@
 }
 
 /*
+ *	Kill all bound sockets on a broken link layer connection to a
+ *	particular neighbour.
+ */
+void rose_kill_by_neigh(struct rose_neigh *neigh)
+{
+	struct sock *s;
+	
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->neighbour == neigh) {
+			s->protinfo.rose->state     = ROSE_STATE_0;
+			s->protinfo.rose->neighbour = NULL;
+			s->state                    = TCP_CLOSE;
+			s->err                      = ENETUNREACH;
+			s->shutdown                |= SEND_SHUTDOWN;
+			s->state_change(s);
+			s->dead                     = 1;
+		}
+	}
+}
+
+/*
  *	Kill all bound sockets on a dropped device.
  */
 static void rose_kill_by_device(struct device *dev)
@@ -223,9 +246,15 @@
 	if (event != NETDEV_DOWN)
 		return NOTIFY_DONE;
 
-	rose_kill_by_device(dev);
-	rose_rt_device_down(dev);
-	rose_link_device_down(dev);
+	switch (dev->type) {
+		case ARPHRD_ROSE:
+			rose_kill_by_device(dev);
+			break;
+		case ARPHRD_AX25:
+			rose_link_device_down(dev);
+			rose_rt_device_down(dev);
+			break;
+	}
 
 	return NOTIFY_DONE;
 }
@@ -250,7 +279,7 @@
  *	Find a socket that wants to accept the Call Request we just
  *	received.
  */
-static struct sock *rose_find_listener(ax25_address *call)
+static struct sock *rose_find_listener(rose_address *addr, ax25_address *call)
 {
 	unsigned long flags;
 	struct sock *s;
@@ -259,14 +288,14 @@
 	cli();
 
 	for (s = rose_list; s != NULL; s = s->next) {
-		if (ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) {
+		if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) {
 			restore_flags(flags);
 			return s;
 		}
 	}
 
 	for (s = rose_list; s != NULL; s = s->next) {
-		if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) {
+		if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) {
 			restore_flags(flags);
 			return s;
 		}
@@ -277,7 +306,7 @@
 }
 
 /*
- *	Find a connected Rose socket given my LCI and device.
+ *	Find a connected ROSE socket given my LCI and device.
  */
 struct sock *rose_find_socket(unsigned int lci, struct device *dev)
 {
@@ -372,11 +401,11 @@
 
 /*
  *	Handling for system calls applied via the various interfaces to a
- *	Rose socket object.
+ *	ROSE socket object.
  */
 
 /*
- * dl1bke 960311: set parameters for existing Rose connections,
+ * dl1bke 960311: set parameters for existing ROSE connections,
  *		  includes a KILL command to abort any connection.
  *		  VERY useful for debugging ;-)
  */
@@ -506,8 +535,8 @@
 			sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ;
 			return 0;
 
-		case ROSE_HDRINCL:
-			sk->protinfo.rose->hdrincl = opt ? 1 : 0;
+		case ROSE_QBITINCL:
+			sk->protinfo.rose->qbitincl = opt ? 1 : 0;
 			return 0;
 
 		default:
@@ -549,8 +578,8 @@
 			val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60);
 			break;
 
-		case ROSE_HDRINCL:
-			val = sk->protinfo.rose->hdrincl;
+		case ROSE_QBITINCL:
+			val = sk->protinfo.rose->qbitincl;
 			break;
 
 		default:
@@ -652,8 +681,8 @@
 	rose->hb      = osk->protinfo.rose->hb;
 	rose->idle    = osk->protinfo.rose->idle;
 
-	rose->device  = osk->protinfo.rose->device;
-	rose->hdrincl = osk->protinfo.rose->hdrincl;
+	rose->device   = osk->protinfo.rose->device;
+	rose->qbitincl = osk->protinfo.rose->qbitincl;
 
 	return sk;
 }
@@ -734,7 +763,7 @@
 		return -EINVAL;
 
 	if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
-		SOCK_DEBUG(sk, "Rose: bind failed: invalid address\n");
+		SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
 		return -EADDRNOTAVAIL;
 	}
 
@@ -758,7 +787,7 @@
 	rose_insert_socket(sk);
 
 	sk->zapped = 0;
-	SOCK_DEBUG(sk, "Rose: socket is bound\n");
+	SOCK_DEBUG(sk, "ROSE: socket is bound\n");
 	return 0;
 }
 
@@ -817,6 +846,7 @@
 		sk->protinfo.rose->dest_digi   = addr->srose_digi;
 	}
 	sk->protinfo.rose->lci       = rose_new_lci(sk->protinfo.rose->neighbour->dev);
+	sk->protinfo.rose->rand      = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
 
 	/* Move to connecting socket, start sending Connect Requests */
 	sock->state   = SS_CONNECTING;
@@ -952,7 +982,7 @@
 {
 	struct sock *sk;
 	struct sock *make;
-	rose_cb rose;
+	struct rose_facilities facilities;
 
 	skb->sk = NULL;		/* Initially we don't know who it's for */
 
@@ -963,11 +993,11 @@
 	/*
 	 * XXX This is an error.
 	 */
-	if (!rose_parse_facilities(skb, &rose)) {
+	if (!rose_parse_facilities(skb, &facilities)) {
 		return 0;
 	}
 
-	sk = rose_find_listener(&rose.source_call);
+	sk = rose_find_listener(&facilities.source_addr, &facilities.source_call);
 
 	/*
 	 * We can't accept the Call Request.
@@ -981,14 +1011,14 @@
 	make->state = TCP_ESTABLISHED;
 
 	make->protinfo.rose->lci           = lci;
-	make->protinfo.rose->dest_addr     = rose.dest_addr;
-	make->protinfo.rose->dest_call     = rose.dest_call;
-	make->protinfo.rose->dest_ndigis   = rose.dest_ndigis;
-	make->protinfo.rose->dest_digi     = rose.dest_digi;
-	make->protinfo.rose->source_addr   = rose.source_addr;
-	make->protinfo.rose->source_call   = rose.source_call;
-	make->protinfo.rose->source_ndigis = rose.source_ndigis;
-	make->protinfo.rose->source_digi   = rose.source_digi;
+	make->protinfo.rose->dest_addr     = facilities.dest_addr;
+	make->protinfo.rose->dest_call     = facilities.dest_call;
+	make->protinfo.rose->dest_ndigis   = facilities.dest_ndigis;
+	make->protinfo.rose->dest_digi     = facilities.dest_digi;
+	make->protinfo.rose->source_addr   = facilities.source_addr;
+	make->protinfo.rose->source_call   = facilities.source_call;
+	make->protinfo.rose->source_ndigis = facilities.source_ndigis;
+	make->protinfo.rose->source_digi   = facilities.source_digi;
 	make->protinfo.rose->neighbour     = neigh;
 	make->protinfo.rose->device        = dev;
 
@@ -1024,7 +1054,7 @@
 	struct sockaddr_rose srose;
 	struct sk_buff *skb;
 	unsigned char *asmptr;
-	int size;
+	int size, qbit = 0;
 
 	if (msg->msg_flags & ~MSG_DONTWAIT)
 		return -EINVAL;
@@ -1037,7 +1067,7 @@
 		return -EPIPE;
 	}
 
-	if (sk->protinfo.rose->device == NULL)
+	if (sk->protinfo.rose->neighbour == NULL || sk->protinfo.rose->device == NULL)
 		return -ENETUNREACH;
 
 	if (usrose != NULL) {
@@ -1067,44 +1097,55 @@
 			srose.srose_digi   = sk->protinfo.rose->dest_digi;
 		}
 	}
-	SOCK_DEBUG(sk, "Rose: sendto: Addresses built.\n");
+
+	SOCK_DEBUG(sk, "ROSE: sendto: Addresses built.\n");
 
 	/* Build a packet */
-	SOCK_DEBUG(sk, "Rose: sendto: building packet.\n");
+	SOCK_DEBUG(sk, "ROSE: sendto: building packet.\n");
 	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
 
 	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
-	skb_reserve(skb, size - len);
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN);
 
 	/*
-	 *	Push down the Rose header
+	 *	Put the data on the end
 	 */
+	SOCK_DEBUG(sk, "ROSE: Appending user data\n");
 
-	asmptr = skb_push(skb, ROSE_MIN_LEN);
-	SOCK_DEBUG(sk, "Building Rose Header.\n");
+	asmptr = skb->h.raw = skb_put(skb, len);
 
-	/* Build a Rose Transport header */
+	memcpy_fromiovec(asmptr, msg->msg_iov, len);
 
-	*asmptr++ = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI;
-	*asmptr++ = (sk->protinfo.rose->lci >> 0) & 0xFF;
-	*asmptr++ = ROSE_DATA;
-	SOCK_DEBUG(sk, "Built header.\n");
+	/*
+	 *	If the Q BIT Include socket option is in force, the first
+	 *	byte of the user data is the logical value of the Q Bit.
+	 */
+	if (sk->protinfo.rose->qbitincl) {
+		qbit = skb->data[0];
+		skb_pull(skb, 1);
+	}
 
 	/*
-	 *	Put the data on the end
+	 *	Push down the ROSE header
 	 */
+	asmptr = skb_push(skb, ROSE_MIN_LEN);
 
-	skb->h.raw = skb_put(skb, len);
+	SOCK_DEBUG(sk, "ROSE: Building Network Header.\n");
 
-	asmptr = skb->h.raw;
-	SOCK_DEBUG(sk, "Rose: Appending user data\n");
+	/* Build a ROSE Network header */
+	asmptr[0] = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI;
+	asmptr[1] = (sk->protinfo.rose->lci >> 0) & 0xFF;
+	asmptr[2] = ROSE_DATA;
 
-	/* User data follows immediately after the Rose transport header */
-	memcpy_fromiovec(asmptr, msg->msg_iov, len);
-	SOCK_DEBUG(sk, "Rose: Transmitting buffer\n");
+	if (qbit)
+		asmptr[0] |= ROSE_Q_BIT;
+
+	SOCK_DEBUG(sk, "ROSE: Built header.\n");
 
+	SOCK_DEBUG(sk, "ROSE: Transmitting buffer\n");
+	
 	if (sk->state != TCP_ESTABLISHED) {
 		kfree_skb(skb, FREE_WRITE);
 		return -ENOTCONN;
@@ -1121,7 +1162,8 @@
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
-	int copied;
+	int copied, qbit;
+	unsigned char *asmptr;
 	struct sk_buff *skb;
 	int er;
 
@@ -1136,12 +1178,17 @@
 	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
-	if (!sk->protinfo.rose->hdrincl) {
-		skb_pull(skb, ROSE_MIN_LEN);
-		skb->h.raw = skb->data;
+	qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
+
+	skb_pull(skb, ROSE_MIN_LEN);
+
+	if (sk->protinfo.rose->qbitincl) {
+		asmptr  = skb_push(skb, 1);
+		*asmptr = qbit;
 	}
 
-	copied = skb->len;
+	skb->h.raw = skb->data;
+	copied     = skb->len;
 
 	if (copied > size) {
 		copied = size;
@@ -1371,14 +1418,7 @@
 };
 #endif	
 
-static struct device dev_rose[] = {
-	{"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
-};
+static struct device *dev_rose;
 
 void rose_proto_init(struct net_proto *pro)
 {
@@ -1386,17 +1426,26 @@
 
 	rose_callsign = null_ax25_address;
 
-	sock_register(&rose_family_ops);
-	register_netdevice_notifier(&rose_dev_notifier);
-	printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.035 Linux 2.1\n");
+	if ((dev_rose = kmalloc(rose_ndevs * sizeof(struct device), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n");
+		return;
+	}
 
-	if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame))
-		printk(KERN_ERR "Rose unable to register protocol with AX.25\n");
-	if (!ax25_linkfail_register(rose_link_failed))
-		printk(KERN_ERR "Rose unable to register linkfail handler with AX.25\n");
+	memset(dev_rose, 0x00, rose_ndevs * sizeof(struct device));
 
-	for (i = 0; i < 6; i++)
+	for (i = 0; i < rose_ndevs; i++) {
+		dev_rose[i].name = kmalloc(20, GFP_KERNEL);
+		sprintf(dev_rose[i].name, "rose%d", i);
+		dev_rose[i].init = rose_init;
 		register_netdev(&dev_rose[i]);
+	}
+
+	sock_register(&rose_family_ops);
+	register_netdevice_notifier(&rose_dev_notifier);
+	printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.2 for AX25.035 Linux 2.1\n");
+
+	ax25_protocol_register(AX25_P_ROSE, rose_route_frame);
+	ax25_linkfail_register(rose_link_failed);
 
 #ifdef CONFIG_SYSCTL
 	rose_register_sysctl();
@@ -1413,6 +1462,12 @@
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
 
+MODULE_PARM(rose_ndevs, "i");
+MODULE_PARM_DESC(rose_ndevs, "number of ROSE devices");
+
+MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The amateur radio ROSE network layer protocol");
+
 int init_module(void)
 {
 	rose_proto_init(NULL);
@@ -1445,13 +1500,16 @@
 
 	sock_unregister(AF_ROSE);
 
-	for (i = 0; i < 6; i++) {
+	for (i = 0; i < rose_ndevs; i++) {
 		if (dev_rose[i].priv != NULL) {
 			kfree(dev_rose[i].priv);
 			dev_rose[i].priv = NULL;
 			unregister_netdev(&dev_rose[i]);
 		}
+		kfree(dev_rose[i].name);
 	}
+
+	kfree(dev_rose);
 }
 
 #endif
diff -u --recursive --new-file linux-2.1.32/net/rose/af_rose.cneighbour linux/net/rose/af_rose.cneighbour
--- linux-2.1.32/net/rose/af_rose.cneighbour	Thu Jan  1 01:00:00 1970
+++ linux/net/rose/af_rose.cneighbour	Tue Apr  1 12:31:10 1997
@@ -0,0 +1,1514 @@
+/*
+ *	ROSE release 002
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from af_netrom.c.
+ *			Alan(GW4PTS)	Hacked up for newer API stuff
+ *			Terry (VK2KTJ)	Added support for variable length
+ * 					address masks.
+ *	ROSE 002	Jonathan(G4KLX)	Changed hdrincl to qbitincl.
+ *					Added random number facilities entry.
+ *					Variable number of ROSE devices.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/stat.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/rose.h>
+#include <linux/proc_fs.h>
+#include <net/ip.h>
+#include <net/arp.h>
+#include <linux/if_arp.h>
+
+int rose_ndevs = 6;
+
+int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0;
+int sysctl_rose_call_request_timeout    = ROSE_DEFAULT_T1;
+int sysctl_rose_reset_request_timeout   = ROSE_DEFAULT_T2;
+int sysctl_rose_clear_request_timeout   = ROSE_DEFAULT_T3;
+int sysctl_rose_no_activity_timeout     = ROSE_DEFAULT_IDLE;
+int sysctl_rose_ack_hold_back_timeout   = ROSE_DEFAULT_HB;
+int sysctl_rose_routing_control         = ROSE_DEFAULT_ROUTING;
+int sysctl_rose_link_fail_timeout       = ROSE_DEFAULT_FAIL_TIMEOUT;
+
+static unsigned int lci = 1;
+
+static struct sock *volatile rose_list = NULL;
+
+static struct proto_ops rose_proto_ops;
+
+ax25_address rose_callsign;
+
+/*
+ *	Convert a ROSE address into text.
+ */
+char *rose2asc(rose_address *addr)
+{
+	static char buffer[11];
+
+	if (addr->rose_addr[0] == 0x00 && addr->rose_addr[1] == 0x00 &&
+	    addr->rose_addr[2] == 0x00 && addr->rose_addr[3] == 0x00 &&
+	    addr->rose_addr[4] == 0x00) {
+		strcpy(buffer, "*");
+	} else {
+		sprintf(buffer, "%02X%02X%02X%02X%02X", addr->rose_addr[0] & 0xFF,
+						addr->rose_addr[1] & 0xFF,
+						addr->rose_addr[2] & 0xFF,
+						addr->rose_addr[3] & 0xFF,
+						addr->rose_addr[4] & 0xFF);
+	}
+
+	return buffer;
+}
+
+/*
+ *	Compare two ROSE addresses, 0 == equal.
+ */
+int rosecmp(rose_address *addr1, rose_address *addr2)
+{
+	int i;
+
+	for (i = 0; i < 5; i++)
+		if (addr1->rose_addr[i] != addr2->rose_addr[i])
+			return 1;
+
+	return 0;
+}
+
+/*
+ *	Compare two ROSE addresses for only mask digits, 0 == equal.
+ */
+int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask)
+{
+	int i, j;
+
+	if (mask > 10)
+		return 1;
+
+	for (i = 0; i < mask; i++) {
+		j = i / 2;
+
+		if ((i % 2) != 0) {
+			if ((addr1->rose_addr[j] & 0x0F) != (addr2->rose_addr[j] & 0x0F))
+				return 1;
+		} else {
+			if ((addr1->rose_addr[j] & 0xF0) != (addr2->rose_addr[j] & 0xF0))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void rose_free_sock(struct sock *sk)
+{
+	kfree(sk->protinfo.rose);
+
+	sk_free(sk);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static struct sock *rose_alloc_sock(void)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	memset(rose, 0x00, sizeof(*rose));
+
+	sk->protinfo.rose = rose;
+	rose->sk          = sk;
+
+	return sk;
+}
+
+/*
+ *	Socket removal during an interrupt is now safe.
+ */
+static void rose_remove_socket(struct sock *sk)
+{
+	struct sock *s;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_list) == sk) {
+		rose_list = s->next;
+		restore_flags(flags);
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == sk) {
+			s->next = sk->next;
+			restore_flags(flags);
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Kill all bound sockets on a broken link layer connection to a
+ *	particular neighbour.
+ */
+void rose_kill_by_neigh(struct rose_neigh *neigh)
+{
+	struct sock *s;
+	
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->neighbour == neigh) {
+			s->protinfo.rose->state     = ROSE_STATE_0;
+			s->protinfo.rose->neighbour = NULL;
+			s->state                    = TCP_CLOSE;
+			s->err                      = ENETUNREACH;
+			s->shutdown                |= SEND_SHUTDOWN;
+			s->state_change(s);
+			s->dead                     = 1;
+		}
+	}
+}
+
+/*
+ *	Kill all bound sockets on a dropped device.
+ */
+static void rose_kill_by_device(struct device *dev)
+{
+	struct sock *s;
+	
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->device == dev) {
+			s->protinfo.rose->state  = ROSE_STATE_0;
+			s->protinfo.rose->device = NULL;
+			s->state                 = TCP_CLOSE;
+			s->err                   = ENETUNREACH;
+			s->shutdown             |= SEND_SHUTDOWN;
+			s->state_change(s);
+			s->dead                  = 1;
+		}
+	}
+}
+
+/*
+ *	Handle device status changes.
+ */
+static int rose_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+
+	if (event != NETDEV_DOWN)
+		return NOTIFY_DONE;
+
+	switch (dev->type) {
+		case ARPHRD_ROSE:
+			rose_kill_by_device(dev);
+			break;
+		case ARPHRD_AX25:
+			rose_link_device_down(dev);
+			rose_rt_device_down(dev);
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+/*
+ *	Add a socket to the bound sockets list.
+ */
+static void rose_insert_socket(struct sock *sk)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	sk->next  = rose_list;
+	rose_list = sk;
+
+	restore_flags(flags);
+}
+
+/*
+ *	Find a socket that wants to accept the Call Request we just
+ *	received.
+ */
+static struct sock *rose_find_listener(rose_address *addr, ax25_address *call)
+{
+	unsigned long flags;
+	struct sock *s;
+
+	save_flags(flags);
+	cli();
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	restore_flags(flags);
+	return NULL;
+}
+
+/*
+ *	Find a connected ROSE socket given my LCI and device.
+ */
+struct sock *rose_find_socket(unsigned int lci, struct device *dev)
+{
+	struct sock *s;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour->dev == dev) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	restore_flags(flags);
+
+	return NULL;
+}
+
+/*
+ *	Find a unique LCI for a given device.
+ */
+unsigned int rose_new_lci(struct device *dev)
+{
+	lci++;
+	if (lci > 4095) lci = 1;
+
+	while (rose_find_socket(lci, dev) != NULL) {
+		lci++;
+		if (lci > 4095) lci = 1;
+	}
+
+	return lci;
+}
+
+/*
+ *	Deferred destroy.
+ */
+void rose_destroy_socket(struct sock *);
+
+/*
+ *	Handler for deferred kills.
+ */
+static void rose_destroy_timer(unsigned long data)
+{
+	rose_destroy_socket((struct sock *)data);
+}
+
+/*
+ *	This is called from user mode and the timers. Thus it protects itself against
+ *	interrupt users but doesn't worry about being called during work.
+ *	Once it is removed from the queue no interrupt or bottom half will
+ *	touch it and we are (fairly 8-) ) safe.
+ */
+void rose_destroy_socket(struct sock *sk)	/* Not static as it's used by the timer */
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	del_timer(&sk->timer);
+
+	rose_remove_socket(sk);
+	rose_clear_queues(sk);		/* Flush the queues */
+
+	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+		if (skb->sk != sk) {			/* A pending connection */
+			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
+			rose_set_timer(skb->sk);
+			skb->sk->protinfo.rose->state = ROSE_STATE_0;
+		}
+
+		kfree_skb(skb, FREE_READ);
+	}
+
+	if (sk->wmem_alloc != 0 || sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
+		init_timer(&sk->timer);
+		sk->timer.expires  = jiffies + 10 * HZ;
+		sk->timer.function = rose_destroy_timer;
+		sk->timer.data     = (unsigned long)sk;
+		add_timer(&sk->timer);
+	} else {
+		rose_free_sock(sk);
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Handling for system calls applied via the various interfaces to a
+ *	ROSE socket object.
+ */
+
+/*
+ * dl1bke 960311: set parameters for existing ROSE connections,
+ *		  includes a KILL command to abort any connection.
+ *		  VERY useful for debugging ;-)
+ */
+static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
+{
+	struct rose_ctl_struct rose_ctl;
+	struct sock *sk;
+	unsigned long flags;
+	struct device *dev;
+	int err;
+	
+	if ((err = verify_area(VERIFY_READ, arg, sizeof(rose_ctl))) != 0)
+		return err;
+
+	copy_from_user(&rose_ctl, arg, sizeof(rose_ctl));
+
+	if ((dev = rose_ax25_dev_get(rose_ctl.dev)) == NULL)
+		return -EINVAL;
+	
+	if ((sk = rose_find_socket(rose_ctl.lci, dev)) == NULL)
+		return -ENOTCONN;
+
+	switch (rose_ctl.cmd) {
+		case ROSE_KILL:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = ENETRESET;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			rose_set_timer(sk);
+	  		break;
+
+	  	case ROSE_T1:
+  			if (rose_ctl.arg < 1) 
+  				return -EINVAL;
+  			save_flags(flags); cli();
+  			sk->protinfo.rose->t1 = rose_ctl.arg * ROSE_SLOWHZ;
+  			restore_flags(flags);
+  			break;
+
+	  	case ROSE_T2:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->t2 = rose_ctl.arg * ROSE_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	case ROSE_T3:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->t3 = rose_ctl.arg * ROSE_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	case ROSE_HOLDBACK:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->hb = rose_ctl.arg * ROSE_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	case ROSE_IDLE:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->idle = rose_ctl.arg * 60 * ROSE_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	default:
+	  		return -EINVAL;
+	  }
+	  
+	  return 0;
+}
+
+static int rose_setsockopt(struct socket *sock, int level, int optname,
+	char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	int opt;
+
+	if (level != SOL_ROSE)
+		return -ENOPROTOOPT;
+
+	if (optlen < sizeof(int))
+		return -EINVAL;
+
+	if (get_user(opt, (int *)optval))
+		return -EFAULT;
+
+	switch (optname) {
+		case ROSE_T1:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t1 = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_T2:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t2 = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_T3:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t3 = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_HOLDBACK:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->hb = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_IDLE:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_QBITINCL:
+			sk->protinfo.rose->qbitincl = opt ? 1 : 0;
+			return 0;
+
+		default:
+			return -ENOPROTOOPT;
+	}
+}
+
+static int rose_getsockopt(struct socket *sock, int level, int optname,
+	char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	int val = 0;
+	int len;
+
+	if (level != SOL_ROSE)
+		return -ENOPROTOOPT;
+		
+	if (get_user(len, optlen))
+		return -EFAULT;
+	
+	switch (optname) {
+		case ROSE_T1:
+			val = sk->protinfo.rose->t1 / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_T2:
+			val = sk->protinfo.rose->t2 / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_T3:
+			val = sk->protinfo.rose->t3 / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_HOLDBACK:
+			val = sk->protinfo.rose->hb / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_IDLE:
+			val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60);
+			break;
+
+		case ROSE_QBITINCL:
+			val = sk->protinfo.rose->qbitincl;
+			break;
+
+		default:
+			return -ENOPROTOOPT;
+	}
+
+	len = min(len, sizeof(int));
+
+	if (put_user(len, optlen))
+		return -EFAULT;
+
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int rose_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk->state != TCP_LISTEN) {
+		sk->protinfo.rose->dest_ndigis = 0;
+		memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN);
+		memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN);
+		memset(&sk->protinfo.rose->dest_digi, '\0', AX25_ADDR_LEN);
+		sk->max_ack_backlog = backlog;
+		sk->state           = TCP_LISTEN;
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int rose_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if (sock->type != SOCK_SEQPACKET || protocol != 0)
+		return -ESOCKTNOSUPPORT;
+
+	if ((sk = rose_alloc_sock()) == NULL)
+		return -ENOMEM;
+
+	rose = sk->protinfo.rose;
+
+	sock_init_data(sock, sk);
+	
+	sock->ops    = &rose_proto_ops;
+	sk->protocol = protocol;
+	sk->mtu      = ROSE_MTU;	/* 128 */
+
+	skb_queue_head_init(&rose->frag_queue);
+
+	rose->t1   = sysctl_rose_call_request_timeout;
+	rose->t2   = sysctl_rose_reset_request_timeout;
+	rose->t3   = sysctl_rose_clear_request_timeout;
+	rose->hb   = sysctl_rose_ack_hold_back_timeout;
+	rose->idle = sysctl_rose_no_activity_timeout;
+
+	rose->state = ROSE_STATE_0;
+
+	return 0;
+}
+
+static struct sock *rose_make_new(struct sock *osk)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if (osk->type != SOCK_SEQPACKET)
+		return NULL;
+
+	if ((sk = rose_alloc_sock()) == NULL)
+		return NULL;
+
+	rose = sk->protinfo.rose;
+
+	sock_init_data(NULL, sk);
+
+	sk->type     = osk->type;
+	sk->socket   = osk->socket;
+	sk->priority = osk->priority;
+	sk->protocol = osk->protocol;
+	sk->rcvbuf   = osk->rcvbuf;
+	sk->sndbuf   = osk->sndbuf;
+	sk->debug    = osk->debug;
+	sk->state    = TCP_ESTABLISHED;
+	sk->mtu      = osk->mtu;
+	sk->sleep    = osk->sleep;
+	sk->zapped   = osk->zapped;
+
+	skb_queue_head_init(&rose->frag_queue);
+
+	rose->t1      = osk->protinfo.rose->t1;
+	rose->t2      = osk->protinfo.rose->t2;
+	rose->t3      = osk->protinfo.rose->t3;
+	rose->hb      = osk->protinfo.rose->hb;
+	rose->idle    = osk->protinfo.rose->idle;
+
+	rose->device   = osk->protinfo.rose->device;
+	rose->qbitincl = osk->protinfo.rose->qbitincl;
+
+	return sk;
+}
+
+static int rose_dup(struct socket *newsock, struct socket *oldsock)
+{
+	struct sock *sk = oldsock->sk;
+
+	if (sk == NULL || newsock == NULL)
+		return -EINVAL;
+
+	return rose_create(newsock, sk->protocol);
+}
+
+static int rose_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk == NULL) return 0;
+
+	switch (sk->protinfo.rose->state) {
+
+		case ROSE_STATE_0:
+			sk->state     = TCP_CLOSE;
+			sk->shutdown |= SEND_SHUTDOWN;
+			sk->state_change(sk);
+			sk->dead      = 1;
+			rose_destroy_socket(sk);
+			break;
+
+		case ROSE_STATE_2:
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			rose_destroy_socket(sk);
+			break;			
+
+		case ROSE_STATE_1:
+		case ROSE_STATE_3:
+		case ROSE_STATE_4:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+			sk->protinfo.rose->state = ROSE_STATE_2;
+			sk->state                = TCP_CLOSE;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			sk->destroy              = 1;
+			break;
+
+		default:
+			break;
+	}
+
+	sock->sk = NULL;	
+	sk->socket = NULL;	/* Not used, but we should do this. **/
+
+	return 0;
+}
+
+static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+	struct device *dev;
+	ax25_address *user, *source;
+
+	if (sk->zapped == 0)
+		return -EINVAL;
+
+	if (addr_len != sizeof(struct sockaddr_rose))
+		return -EINVAL;
+
+	if (addr->srose_family != AF_ROSE)
+		return -EINVAL;
+
+	if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
+		SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	source = &addr->srose_call;
+
+	if ((user = ax25_findbyuid(current->euid)) == NULL) {
+		if (ax25_uid_policy && !suser())
+			return -EACCES;
+		user = source;
+	}
+
+	sk->protinfo.rose->source_addr = addr->srose_addr;
+	sk->protinfo.rose->source_call = *user;
+	sk->protinfo.rose->device      = dev;
+
+	if (addr->srose_ndigis == 1) {
+		sk->protinfo.rose->source_ndigis = 1;
+		sk->protinfo.rose->source_digi   = addr->srose_digi;
+	}
+
+	rose_insert_socket(sk);
+
+	sk->zapped = 0;
+	SOCK_DEBUG(sk, "ROSE: socket is bound\n");
+	return 0;
+}
+
+static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+	ax25_address *user;
+	struct device *dev;
+
+	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
+		sock->state = SS_CONNECTED;
+		return 0;	/* Connect completed during a ERESTARTSYS event */
+	}
+
+	if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
+		sock->state = SS_UNCONNECTED;
+		return -ECONNREFUSED;
+	}
+
+	if (sk->state == TCP_ESTABLISHED)
+		return -EISCONN;	/* No reconnect on a seqpacket socket */
+
+	sk->state   = TCP_CLOSE;
+	sock->state = SS_UNCONNECTED;
+
+	if (addr_len != sizeof(struct sockaddr_rose))
+		return -EINVAL;
+
+	if (addr->srose_family != AF_ROSE)
+		return -EINVAL;
+
+	if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL)	
+		return -ENETUNREACH;
+
+	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
+		sk->zapped = 0;
+
+		if ((dev = rose_dev_first()) == NULL)
+			return -ENETUNREACH;
+
+		if ((user = ax25_findbyuid(current->euid)) == NULL)
+			return -EINVAL;
+
+		memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN);
+		sk->protinfo.rose->source_call = *user;
+		sk->protinfo.rose->device      = dev;
+
+		rose_insert_socket(sk);		/* Finish the bind */
+	}
+
+	sk->protinfo.rose->dest_addr = addr->srose_addr;
+	sk->protinfo.rose->dest_call = addr->srose_call;
+	if (addr->srose_ndigis == 1) {
+		sk->protinfo.rose->dest_ndigis = 1;
+		sk->protinfo.rose->dest_digi   = addr->srose_digi;
+	}
+	sk->protinfo.rose->lci       = rose_new_lci(sk->protinfo.rose->neighbour->dev);
+	sk->protinfo.rose->rand      = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
+
+	/* Move to connecting socket, start sending Connect Requests */
+	sock->state   = SS_CONNECTING;
+	sk->state     = TCP_SYN_SENT;
+
+	sk->protinfo.rose->state = ROSE_STATE_1;
+	sk->protinfo.rose->timer = sk->protinfo.rose->t1;
+	rose_write_internal(sk, ROSE_CALL_REQUEST);
+
+	rose_set_timer(sk);
+
+	/* Now the loop */
+	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+		return -EINPROGRESS;
+
+	cli();	/* To avoid races on the sleep */
+
+	/*
+	 * A Connect Ack with Choke or timeout or failed routing will go to closed.
+	 */
+	while (sk->state == TCP_SYN_SENT) {
+		interruptible_sleep_on(sk->sleep);
+		if (current->signal & ~current->blocked) {
+			sti();
+			return -ERESTARTSYS;
+		}
+	}
+
+	if (sk->state != TCP_ESTABLISHED) {
+		sti();
+		sock->state = SS_UNCONNECTED;
+		return sock_error(sk);	/* Always set at this point */
+	}
+
+	sock->state = SS_CONNECTED;
+
+	sti();
+
+	return 0;
+}
+
+static int rose_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk;
+	struct sock *newsk;
+	struct sk_buff *skb;
+
+	if (newsock->sk != NULL)
+		rose_destroy_socket(newsock->sk);
+
+	newsock->sk = NULL;
+
+	if ((sk = sock->sk) == NULL)
+		return -EINVAL;
+
+	if (sk->type != SOCK_SEQPACKET)
+		return -EOPNOTSUPP;
+
+	if (sk->state != TCP_LISTEN)
+		return -EINVAL;
+
+	/*
+	 *	The write queue this time is holding sockets ready to use
+	 *	hooked into the SABM we saved
+	 */
+	do {
+		cli();
+		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
+			if (flags & O_NONBLOCK) {
+				sti();
+				return -EWOULDBLOCK;
+			}
+			interruptible_sleep_on(sk->sleep);
+			if (current->signal & ~current->blocked) {
+				sti();
+				return -ERESTARTSYS;
+			}
+		}
+	} while (skb == NULL);
+
+	newsk = skb->sk;
+	newsk->pair = NULL;
+	sti();
+
+	/* Now attach up the new socket */
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_READ);
+	sk->ack_backlog--;
+	newsock->sk = newsk;
+
+	return 0;
+}
+
+static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
+	int *uaddr_len, int peer)
+{
+	struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
+	struct sock *sk = sock->sk;
+
+	if (peer != 0) {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+		srose->srose_family = AF_ROSE;
+		srose->srose_ndigis = 0;
+		srose->srose_addr   = sk->protinfo.rose->dest_addr;
+		srose->srose_call   = sk->protinfo.rose->dest_call;
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+		*uaddr_len = sizeof(struct sockaddr_rose);
+	} else {
+		srose->srose_family = AF_ROSE;
+		srose->srose_ndigis = 0;
+		srose->srose_addr   = sk->protinfo.rose->source_addr;
+		srose->srose_call   = sk->protinfo.rose->source_call;
+		if (sk->protinfo.rose->source_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->source_digi;
+		}
+		*uaddr_len = sizeof(struct sockaddr_rose);
+	}
+
+	return 0;
+}
+
+int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_neigh *neigh, unsigned int lci)
+{
+	struct sock *sk;
+	struct sock *make;
+	struct rose_facilities facilities;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+
+	/*
+	 *	skb->data points to the rose frame start
+	 */
+
+	/*
+	 * XXX This is an error.
+	 */
+	if (!rose_parse_facilities(skb, &facilities)) {
+		return 0;
+	}
+
+	sk = rose_find_listener(&facilities.source_addr, &facilities.source_call);
+
+	/*
+	 * We can't accept the Call Request.
+	 */
+	if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) {
+		rose_transmit_clear_request(neigh, lci, 0x01);
+		return 0;
+	}
+
+	skb->sk     = make;
+	make->state = TCP_ESTABLISHED;
+
+	make->protinfo.rose->lci           = lci;
+	make->protinfo.rose->dest_addr     = facilities.dest_addr;
+	make->protinfo.rose->dest_call     = facilities.dest_call;
+	make->protinfo.rose->dest_ndigis   = facilities.dest_ndigis;
+	make->protinfo.rose->dest_digi     = facilities.dest_digi;
+	make->protinfo.rose->source_addr   = facilities.source_addr;
+	make->protinfo.rose->source_call   = facilities.source_call;
+	make->protinfo.rose->source_ndigis = facilities.source_ndigis;
+	make->protinfo.rose->source_digi   = facilities.source_digi;
+	make->protinfo.rose->neighbour     = neigh;
+	make->protinfo.rose->device        = dev;
+
+	rose_write_internal(make, ROSE_CALL_ACCEPTED);
+
+	make->protinfo.rose->condition = 0x00;
+	make->protinfo.rose->vs        = 0;
+	make->protinfo.rose->va        = 0;
+	make->protinfo.rose->vr        = 0;
+	make->protinfo.rose->vl        = 0;
+	make->protinfo.rose->state     = ROSE_STATE_3;
+	sk->ack_backlog++;
+	make->pair = sk;
+
+	rose_insert_socket(make);
+
+	skb_queue_head(&sk->receive_queue, skb);
+
+	rose_set_timer(make);
+
+	if (!sk->dead)
+		sk->data_ready(sk, skb->len);
+
+	return 1;
+}
+
+static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, 
+				struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
+	int err;
+	struct sockaddr_rose srose;
+	struct sk_buff *skb;
+	unsigned char *asmptr;
+	int size, qbit = 0;
+
+	if (msg->msg_flags & ~MSG_DONTWAIT)
+		return -EINVAL;
+
+	if (sk->zapped)
+		return -EADDRNOTAVAIL;
+
+	if (sk->shutdown & SEND_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		return -EPIPE;
+	}
+
+	if (sk->protinfo.rose->neighbour == NULL)
+		return -ENETUNREACH;
+
+	if (usrose != NULL) {
+		if (msg->msg_namelen < sizeof(srose))
+			return -EINVAL;
+		srose = *usrose;
+		if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 ||
+		    ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0)
+			return -EISCONN;
+		if (srose.srose_ndigis == 1 && sk->protinfo.rose->dest_ndigis == 1) {
+			if (ax25cmp(&sk->protinfo.rose->dest_digi, &srose.srose_digi) != 0)
+				return -EISCONN;
+		}
+		if (srose.srose_family != AF_ROSE)
+			return -EINVAL;
+	} else {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+
+		srose.srose_family = AF_ROSE;
+		srose.srose_addr   = sk->protinfo.rose->dest_addr;
+		srose.srose_call   = sk->protinfo.rose->dest_call;
+		srose.srose_ndigis = 0;
+
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose.srose_ndigis = 1;
+			srose.srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+	}
+
+	SOCK_DEBUG(sk, "ROSE: sendto: Addresses built.\n");
+
+	/* Build a packet */
+	SOCK_DEBUG(sk, "ROSE: sendto: building packet.\n");
+	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
+
+	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+		return err;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN);
+
+	/*
+	 *	Put the data on the end
+	 */
+	SOCK_DEBUG(sk, "ROSE: Appending user data\n");
+
+	asmptr = skb->h.raw = skb_put(skb, len);
+
+	memcpy_fromiovec(asmptr, msg->msg_iov, len);
+
+	/*
+	 *	If the Q BIT Include socket option is in force, the first
+	 *	byte of the user data is the logical value of the Q Bit.
+	 */
+	if (sk->protinfo.rose->qbitincl) {
+		qbit = skb->data[0];
+		skb_pull(skb, 1);
+	}
+
+	/*
+	 *	Push down the ROSE header
+	 */
+	asmptr = skb_push(skb, ROSE_MIN_LEN);
+
+	SOCK_DEBUG(sk, "ROSE: Building Network Header.\n");
+
+	/* Build a ROSE Network header */
+	asmptr[0] = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI;
+	asmptr[1] = (sk->protinfo.rose->lci >> 0) & 0xFF;
+	asmptr[2] = ROSE_DATA;
+
+	if (qbit)
+		asmptr[0] |= ROSE_Q_BIT;
+
+	SOCK_DEBUG(sk, "ROSE: Built header.\n");
+
+	SOCK_DEBUG(sk, "ROSE: Transmitting buffer\n");
+	
+	if (sk->state != TCP_ESTABLISHED) {
+		kfree_skb(skb, FREE_WRITE);
+		return -ENOTCONN;
+	}
+
+	rose_output(sk, skb);	/* Shove it onto the queue */
+
+	return len;
+}
+
+
+static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, 
+		   int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
+	int copied, qbit;
+	unsigned char *asmptr;
+	struct sk_buff *skb;
+	int er;
+
+	/*
+	 * This works for seqpacket too. The receiver has ordered the queue for
+	 * us! We do one quick check first though
+	 */
+	if (sk->state != TCP_ESTABLISHED)
+		return -ENOTCONN;
+
+	/* Now we can treat all alike */
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
+		return er;
+
+	qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
+
+	skb_pull(skb, ROSE_MIN_LEN);
+
+	if (sk->protinfo.rose->qbitincl) {
+		asmptr  = skb_push(skb, 1);
+		*asmptr = qbit;
+	}
+
+	skb->h.raw = skb->data;
+	copied     = skb->len;
+
+	if (copied > size) {
+		copied = size;
+		msg->msg_flags |= MSG_TRUNC;
+	}
+
+	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+	if (srose != NULL) {
+		srose->srose_family = AF_ROSE;
+		srose->srose_addr   = sk->protinfo.rose->dest_addr;
+		srose->srose_call   = sk->protinfo.rose->dest_call;
+		srose->srose_ndigis = 0;
+
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+	}
+
+	msg->msg_namelen = sizeof(struct sockaddr_rose);
+
+	skb_free_datagram(sk, skb);
+
+	return copied;
+}
+
+static int rose_shutdown(struct socket *sk, int how)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err;
+	long amount = 0;
+
+	switch (cmd) {
+		case TIOCOUTQ:
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
+				return err;
+			amount = sk->sndbuf - sk->wmem_alloc;
+			if (amount < 0)
+				amount = 0;
+			put_user(amount, (unsigned int *)arg);
+			return 0;
+
+		case TIOCINQ: {
+			struct sk_buff *skb;
+			/* These two are safe on a single CPU system as only user tasks fiddle here */
+			if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+				amount = skb->len - 20;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
+				return err;
+			put_user(amount, (unsigned int *)arg);
+			return 0;
+		}
+
+		case SIOCGSTAMP:
+			if (sk != NULL) {
+				if (sk->stamp.tv_sec==0)
+					return -ENOENT;
+				if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
+					return err;
+				copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+				return 0;
+			}
+			return -EINVAL;
+
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFMETRIC:
+		case SIOCSIFMETRIC:
+			return -EINVAL;
+
+		case SIOCADDRT:
+		case SIOCDELRT:
+			if (!suser()) return -EPERM;
+			return rose_rt_ioctl(cmd, (void *)arg);
+
+		case SIOCRSCTLCON:
+ 			if (!suser()) return -EPERM;
+ 			return rose_ctl_ioctl(cmd, (void *)arg);
+
+		case SIOCRSL2CALL:
+			if (!suser()) return -EPERM;
+			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0)
+				return err;
+			if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+				ax25_listen_release(&rose_callsign, NULL);
+			copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address));
+			if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+				ax25_listen_register(&rose_callsign, NULL);
+			return 0;
+
+		default:
+			return dev_ioctl(cmd, (void *)arg);
+	}
+
+	/*NOTREACHED*/
+	return 0;
+}
+
+static int rose_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct sock *s;
+	struct device *dev;
+	const char *devname, *callsign;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "dest_addr  dest_call src_addr   src_call  dev   lci st vs vr va   t  t1  t2  t3  hb  Snd-Q Rcv-Q\n");
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if ((dev = s->protinfo.rose->device) == NULL)
+			devname = "???";
+		else
+			devname = dev->name;
+
+		len += sprintf(buffer + len, "%-10s %-9s ",
+			rose2asc(&s->protinfo.rose->dest_addr),
+			ax2asc(&s->protinfo.rose->dest_call));
+
+		if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0)
+			callsign = "??????-?";
+		else
+			callsign = ax2asc(&s->protinfo.rose->source_call);
+
+		len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X  %d  %d  %d  %d %3d %3d %3d %3d %3d %5d %5d\n",
+			rose2asc(&s->protinfo.rose->source_addr), callsign,
+			devname,  s->protinfo.rose->lci & 0x0FFF,
+			s->protinfo.rose->state,
+			s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va,
+			s->protinfo.rose->timer / ROSE_SLOWHZ,
+			s->protinfo.rose->t1    / ROSE_SLOWHZ,
+			s->protinfo.rose->t2    / ROSE_SLOWHZ,
+			s->protinfo.rose->t3    / ROSE_SLOWHZ,
+			s->protinfo.rose->hb    / ROSE_SLOWHZ,
+			s->wmem_alloc, s->rmem_alloc);
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return(len);
+} 
+
+static struct net_proto_family rose_family_ops = {
+	AF_ROSE,
+	rose_create
+};
+
+static struct proto_ops rose_proto_ops = {
+	AF_ROSE,
+
+	rose_dup,
+	rose_release,
+	rose_bind,
+	rose_connect,
+	rose_socketpair,
+	rose_accept,
+	rose_getname,
+	datagram_poll,
+	rose_ioctl,
+	rose_listen,
+	rose_shutdown,
+	rose_setsockopt,
+	rose_getsockopt,
+	sock_no_fcntl,
+	rose_sendmsg,
+	rose_recvmsg
+};
+
+static struct notifier_block rose_dev_notifier = {
+	rose_device_event,
+	0
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_rose = {
+	PROC_NET_RS, 4, "rose",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_get_info
+};
+static struct proc_dir_entry proc_net_rose_neigh = {
+	PROC_NET_RS_NEIGH, 10, "rose_neigh",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_neigh_get_info
+};
+static struct proc_dir_entry proc_net_rose_nodes = {
+	PROC_NET_RS_NODES, 10, "rose_nodes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_nodes_get_info
+};
+static struct proc_dir_entry proc_net_rose_routes = {
+	PROC_NET_RS_ROUTES, 11, "rose_routes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_routes_get_info
+};
+#endif	
+
+static struct device *dev_rose;
+
+void rose_proto_init(struct net_proto *pro)
+{
+	int i;
+
+	rose_callsign = null_ax25_address;
+
+	if ((dev_rose = kmalloc(rose_ndevs * sizeof(struct device), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n");
+		return;
+	}
+
+	memset(dev_rose, 0x00, rose_ndevs * sizeof(struct device));
+
+	for (i = 0; i < rose_ndevs; i++) {
+		dev_rose[i].name = kmalloc(20, GFP_KERNEL);
+		sprintf(dev_rose[i].name, "rose%d", i);
+		dev_rose[i].init = rose_init;
+		register_netdev(&dev_rose[i]);
+	}
+
+	sock_register(&rose_family_ops);
+	register_netdevice_notifier(&rose_dev_notifier);
+	printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.2 for AX25.035 Linux 2.1\n");
+
+	ax25_protocol_register(AX25_P_ROSE, rose_route_frame);
+	ax25_linkfail_register(rose_link_failed);
+
+	rose_register_sysctl();
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_rose);
+	proc_net_register(&proc_net_rose_neigh);
+	proc_net_register(&proc_net_rose_nodes);
+	proc_net_register(&proc_net_rose_routes);
+#endif
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_PARM(rose_ndevs, "i");
+MODULE_PARM_DESC(rose_ndevs, "number of ROSE devices");
+
+MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The amateur radio ROSE network layer protocol");
+
+int init_module(void)
+{
+	rose_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_RS);
+	proc_net_unregister(PROC_NET_RS_NEIGH);
+	proc_net_unregister(PROC_NET_RS_NODES);
+	proc_net_unregister(PROC_NET_RS_ROUTES);
+#endif
+	rose_rt_free();
+
+	ax25_protocol_release(AX25_P_ROSE);
+	ax25_linkfail_release(rose_link_failed);
+
+	if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+		ax25_listen_release(&rose_callsign, NULL);
+
+	rose_unregister_sysctl();
+
+	unregister_netdevice_notifier(&rose_dev_notifier);
+
+	sock_unregister(AF_ROSE);
+
+	for (i = 0; i < rose_ndevs; i++) {
+		if (dev_rose[i].priv != NULL) {
+			kfree(dev_rose[i].priv);
+			dev_rose[i].priv = NULL;
+			unregister_netdev(&dev_rose[i]);
+		}
+		kfree(dev_rose[i].name);
+	}
+
+	kfree(dev_rose);
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- linux-2.1.32/net/rose/rose_dev.c	Thu Mar 27 19:43:34 1997
+++ linux/net/rose/rose_dev.c	Mon Mar 31 14:32:07 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
- *	releases, misbehave and/or generally screw up. It might even work.
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,7 +10,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from nr_dev.c.
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_dev.c.
  *			Hans(PE1AYX)	Fixed interface to IP layer.
  */
 
@@ -52,7 +49,7 @@
 #include <net/rose.h>
 
 /*
- *	Only allow IP over Rose frames through if the netrom device is up.
+ *	Only allow IP over ROSE frames through if the netrom device is up.
  */
 
 int rose_rx_ip(struct sk_buff *skb, struct device *dev)
@@ -175,7 +172,7 @@
 		return 0;
 
 	if (!dev->start) {
-		printk(KERN_ERR "rose: xmit call when iface is down\n");
+		printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n");
 		return 1;
 	}
 
@@ -226,10 +223,12 @@
 	dev->flags		= 0;
 	dev->family		= AF_INET;
 
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr		= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= sizeof(unsigned long);
+#ifdef CONFIG_INET
+	dev->pa_addr		= in_aton("192.168.0.1");
+	dev->pa_brdaddr		= in_aton("192.168.0.255");
+	dev->pa_mask		= in_aton("255.255.255.0");
+	dev->pa_alen		= 4;
+#endif
 
 	if ((dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL)) == NULL)
 		return -ENOMEM;
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_in.c linux/net/rose/rose_in.c
--- linux-2.1.32/net/rose/rose_in.c	Fri Mar  7 14:49:17 1997
+++ linux/net/rose/rose_in.c	Tue Mar 25 10:52:25 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
- *	releases, misbehave and/or generally screw up. It might even work.
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -19,7 +16,7 @@
  *	easy to break;
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from nr_in.c
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_in.c
  */
 
 #include <linux/config.h>
@@ -116,8 +113,7 @@
 			sk->dead                 = 1;
 			break;
 
-		default:		/* XXX */
-			printk(KERN_WARNING "rose: unknown %02X in state 1\n", frametype);
+		default:
 			break;
 	}
 
@@ -146,8 +142,7 @@
 			sk->dead                 = 1;
 			break;
 
-		default:		/* XXX */
-			printk(KERN_WARNING "rose: unknown %02X in state 2\n", frametype);
+		default:
 			break;
 	}
 
@@ -256,7 +251,7 @@
 			break;
 
 		default:
-			printk(KERN_WARNING "rose: unknown %02X in state 3\n", frametype);
+			printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype);
 			break;
 	}
 
@@ -297,8 +292,7 @@
 			sk->dead                 = 1;
 			break;
 
-		default:		/* XXX */
-			printk(KERN_WARNING "rose: unknown %02X in state 4\n", frametype);
+		default:
 			break;
 	}
 
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_link.c linux/net/rose/rose_link.c
--- linux-2.1.32/net/rose/rose_link.c	Thu Mar 27 19:43:34 1997
+++ linux/net/rose/rose_link.c	Thu Mar 27 20:15:32 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,7 +10,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from rose_timer.c
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from rose_timer.c
  */
 
 #include <linux/config.h>
@@ -62,7 +59,7 @@
 }
 
 /*
- *	Rose Link Timer
+ *	ROSE Link Timer
  *
  *	This routine is called every 100ms. Decrement timer by this
  *	amount - if expired then process the event.
@@ -145,11 +142,11 @@
 			break;
 
 		case ROSE_DIAGNOSTIC:
-			printk(KERN_WARNING "rose: diagnostic #%d\n", skb->data[3]);
+			printk(KERN_WARNING "ROSE: received diagnostic #%d\n", skb->data[3]);
 			break;
 
 		default:
-			printk(KERN_WARNING "rose: received unknown %02X with LCI 000\n", frametype);
+			printk(KERN_WARNING "ROSE: received unknown %02X with LCI 000\n", frametype);
 			break;
 	}
 
@@ -278,8 +275,10 @@
 {
 	unsigned char *dptr;
 
-	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL,&skb) != FW_ACCEPT)
+	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
+		kfree_skb(skb, FREE_WRITE);
 		return;
+	}
 
 	if (!rose_link_up(neigh))
 		neigh->restarted = 0;
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_out.c linux/net/rose/rose_out.c
--- linux-2.1.32/net/rose/rose_out.c	Fri Mar  7 14:48:21 1997
+++ linux/net/rose/rose_out.c	Tue Mar 25 10:58:06 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,7 +10,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from nr_out.c
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_out.c
  */
 
 #include <linux/config.h>
@@ -41,7 +38,7 @@
 #include <net/rose.h>
 
 /*
- *	This is where all Rose frames pass;
+ *	This is where all ROSE frames pass;
  */
 void rose_output(struct sock *sk, struct sk_buff *skb)
 {
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_route.c linux/net/rose/rose_route.c
--- linux-2.1.32/net/rose/rose_route.c	Thu Mar 27 19:43:34 1997
+++ linux/net/rose/rose_route.c	Thu Mar 27 19:46:30 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,9 +10,11 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from nr_route.c.
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_route.c.
  *			Terry(VK2KTJ)	Added support for variable length
  *					address masks.
+ *	ROSE 002	Jonathan(G4KLX)	Uprated through routing of packets.
+ *					Routing loop detection.
  */
 
 #include <linux/config.h>
@@ -76,7 +75,7 @@
 			break;
 
 	if (rose_neigh == NULL) {
-		if ((rose_neigh = (struct rose_neigh *)kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL)
+		if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
 
 		rose_neigh->callsign  = rose_route->neighbour;
@@ -92,7 +91,7 @@
 
 		if (rose_route->ndigis != 0) {
 			if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
-				kfree_s(rose_neigh, sizeof(*rose_neigh));
+				kfree(rose_neigh);
 				return -ENOMEM;
 			}
 			rose_neigh->digipeat->ndigi = rose_route->ndigis;
@@ -127,7 +126,7 @@
 		}
 
 		/* create new node */
-		if ((rose_node = (struct rose_node *)kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
+		if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
 
 		rose_node->address = rose_route->address;
@@ -183,7 +182,7 @@
 	if ((s = rose_node_list) == rose_node) {
 		rose_node_list = rose_node->next;
 		restore_flags(flags);
-		kfree_s(rose_node, sizeof(struct rose_node));
+		kfree(rose_node);
 		return;
 	}
 
@@ -191,7 +190,7 @@
 		if (s->next == rose_node) {
 			s->next = rose_node->next;
 			restore_flags(flags);
-			kfree_s(rose_node, sizeof(struct rose_node));
+			kfree(rose_node);
 			return;
 		}
 
@@ -219,8 +218,8 @@
 		rose_neigh_list = rose_neigh->next;
 		restore_flags(flags);
 		if (rose_neigh->digipeat != NULL)
-			kfree_s(rose_neigh->digipeat, sizeof(ax25_digi));
-		kfree_s(rose_neigh, sizeof(struct rose_neigh));
+			kfree(rose_neigh->digipeat);
+		kfree(rose_neigh);
 		return;
 	}
 
@@ -229,8 +228,8 @@
 			s->next = rose_neigh->next;
 			restore_flags(flags);
 			if (rose_neigh->digipeat != NULL)
-				kfree_s(rose_neigh->digipeat, sizeof(ax25_digi));
-			kfree_s(rose_neigh, sizeof(struct rose_neigh));
+				kfree(rose_neigh->digipeat);
+			kfree(rose_neigh);
 			return;
 		}
 
@@ -251,7 +250,7 @@
 	if ((s = rose_route_list) == rose_route) {
 		rose_route_list = rose_route->next;
 		restore_flags(flags);
-		kfree_s(rose_route, sizeof(struct rose_route));
+		kfree(rose_route);
 		return;
 	}
 
@@ -259,7 +258,7 @@
 		if (s->next == rose_route) {
 			s->next = rose_route->next;
 			restore_flags(flags);
-			kfree_s(rose_route, sizeof(struct rose_route));
+			kfree(rose_route);
 			return;
 		}
 
@@ -372,7 +371,7 @@
 	struct rose_route *s, *rose_route = rose_route_list;
 
 	while (rose_route != NULL) {
-		s         = rose_route;
+		s          = rose_route;
 		rose_route = rose_route->next;
 
 		if (s->neigh1->dev == dev || s->neigh2->dev == dev)
@@ -397,7 +396,7 @@
 }
 
 /*
- *	Find the first active Rose device, usually "rose0".
+ *	Find the first active ROSE device, usually "rose0".
  */
 struct device *rose_dev_first(void)
 {
@@ -412,7 +411,7 @@
 }
 
 /*
- *	Find the Rose device for the given address.
+ *	Find the ROSE device for the given address.
  */
 struct device *rose_dev_get(rose_address *addr)
 {
@@ -426,22 +425,21 @@
 }
 
 /*
- *	Find a neighbour given a Rose address.
+ *	Find a neighbour given a ROSE address.
  */
 struct rose_neigh *rose_get_neigh(rose_address *addr)
 {
 	struct rose_node *node;
 	int i;
 
-	for (node = rose_node_list; node != NULL; node = node->next)
-		if (rosecmpm(addr, &node->address, node->mask) == 0)
-			break;
-
-	if (node == NULL) return NULL;
-
-	for (i = 0; i < node->count; i++)
-		if (node->neighbour[i]->ftimer == 0)
-			return node->neighbour[i];
+	for (node = rose_node_list; node != NULL; node = node->next) {
+		if (rosecmpm(addr, &node->address, node->mask) == 0) {
+			for (i = 0; i < node->count; i++) {
+				if (node->neighbour[i]->ftimer == 0)
+					return node->neighbour[i];
+			}
+		}
+	}
 
 	return NULL;
 }
@@ -485,14 +483,54 @@
 	return 0;
 }
 
+static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
+{
+	struct rose_route *rose_route, *s;
+	struct sk_buff    *skb;
+
+	rose_neigh->restarted = 0;
+	rose_neigh->t0timer   = 0;
+	rose_neigh->ftimer    = sysctl_rose_link_fail_timeout;
+
+	rose_link_set_timer(rose_neigh);
+
+	while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+
+	rose_route = rose_route_list;
+
+	while (rose_route != NULL) {
+		if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) ||
+		    (rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL)       ||
+		    (rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) {
+			s = rose_route->next;
+			rose_remove_route(rose_route);
+			rose_route = s;
+			continue;
+		}
+
+		if (rose_route->neigh1 == rose_neigh) {
+			rose_route->neigh1 = NULL;
+			rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, 0x0D);
+		}
+
+		if (rose_route->neigh2 == rose_neigh) {
+			rose_route->neigh2 = NULL;
+			rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, 0x0D);
+		}
+
+		rose_route = rose_route->next;
+	}
+}
+
 /*
  * 	A level 2 link has timed out, therefore it appears to be a poor link,
- *	then don't use that neighbour until it is reset. XXX others.
+ *	then don't use that neighbour until it is reset. Blow away all through
+ *	routes and connections using this route.
  */
 void rose_link_failed(ax25_address *callsign, struct device *dev)
 {
 	struct rose_neigh *rose_neigh;
-	struct sk_buff    *skb;
 
 	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
 		if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev)
@@ -500,33 +538,22 @@
 
 	if (rose_neigh == NULL) return;
 
-	rose_neigh->restarted = 0;
-	rose_neigh->t0timer   = 0;
-	rose_neigh->ftimer    = sysctl_rose_link_fail_timeout;
-
-	rose_link_set_timer(rose_neigh);
-
-	while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
-		kfree_skb(skb, FREE_WRITE);
+	rose_del_route_by_neigh(rose_neigh);
+	rose_kill_by_neigh(rose_neigh);
 }
 
 /*
- * 	A device has been "downed" remove its link status. XXX others.
+ * 	A device has been "downed" remove its link status. Blow away all
+ *	through routes and connections that use this device.
  */
 void rose_link_device_down(struct device *dev)
 {
 	struct rose_neigh *rose_neigh;
-	struct sk_buff    *skb;
 
 	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
 		if (rose_neigh->dev == dev) {
-			rose_neigh->restarted = 0;
-			rose_neigh->t0timer   = 0;
-			rose_neigh->ftimer    = 0;
-			del_timer(&rose_neigh->timer);
-
-			while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
-				kfree_skb(skb, FREE_WRITE);
+			rose_del_route_by_neigh(rose_neigh);
+			rose_kill_by_neigh(rose_neigh);
 		}
 	}
 }
@@ -538,6 +565,7 @@
 {
 	struct rose_neigh *rose_neigh, *new_neigh;
 	struct rose_route *rose_route;
+	struct rose_facilities facilities;
 	rose_address *src_addr, *dest_addr;
 	struct sock *sk;
 	unsigned short frametype;
@@ -597,29 +625,36 @@
 	/*
 	 *	Route it to the next in line if we have an entry for it.
 	 */
-
-	/*
-	 *	We should check for the random number in the facilities
-	 *	here. XXX.
-	 */
 	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
 		if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) {
-			skb->data[0] &= 0xF0;
-			skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
-			skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
-			rose_transmit_link(skb, rose_route->neigh2);
-			if (frametype == ROSE_CLEAR_CONFIRMATION)
-				rose_remove_route(rose_route);
-			return 1;
+			if (rose_route->neigh2 != NULL) {
+				skb->data[0] &= 0xF0;
+				skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
+				skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
+				rose_transmit_link(skb, rose_route->neigh2);
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 1;
+			} else {
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 0;
+			}
 		}
 		if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) {
-			skb->data[0] &= 0xF0;
-			skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F;
-			skb->data[1]  = (rose_route->lci1 >> 0) & 0xFF;
-			rose_transmit_link(skb, rose_route->neigh1);
-			if (frametype == ROSE_CLEAR_CONFIRMATION)
-				rose_remove_route(rose_route);
-			return 1;
+			if (rose_route->neigh1 != NULL) {
+				skb->data[0] &= 0xF0;
+				skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F;
+				skb->data[1]  = (rose_route->lci1 >> 0) & 0xFF;
+				rose_transmit_link(skb, rose_route->neigh1);
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 1;
+			} else {
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 0;
+			}
 		}
 	}
 
@@ -631,20 +666,40 @@
 	if (frametype != ROSE_CALL_REQUEST)	/* XXX */
 		return 0;
 
+	rose_parse_facilities(skb, &facilities);
+
+	/*
+	 *	Check for routing loops.
+	 */
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
+		if (rose_route->rand != 0x00 && rose_route->rand == facilities.rand) {
+			printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr));
+			printk(KERN_DEBUG "ROSE:                to %s\n", rose2asc(dest_addr));
+			rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+			return 0;
+		}
+	}
+
 	if ((new_neigh = rose_get_neigh(dest_addr)) == NULL) {
+		printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr));
 		rose_transmit_clear_request(rose_neigh, lci, 0x0D);
 		return 0;
 	}
 
-	if ((rose_route = (struct rose_route *)kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
+	if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
 		rose_transmit_clear_request(rose_neigh, lci, 0x0D);
 		return 0;
 	}
 
-	rose_route->lci1   = lci;
-	rose_route->neigh1 = rose_neigh;
-	rose_route->lci2   = rose_new_lci(new_neigh->dev);
-	rose_route->neigh2 = new_neigh;
+	rose_route->lci1      = lci;
+	rose_route->src_addr  = *src_addr;
+	rose_route->dest_addr = *dest_addr;
+	rose_route->src_call  = facilities.dest_call;
+	rose_route->dest_call = facilities.source_call;
+	rose_route->rand      = facilities.rand;
+	rose_route->neigh1    = rose_neigh;
+	rose_route->lci2      = rose_new_lci(new_neigh->dev);
+	rose_route->neigh2    = new_neigh;
 
 	save_flags(flags); cli();
 	rose_route->next = rose_route_list;
@@ -759,17 +814,28 @@
 
 	cli();
 
-	len += sprintf(buffer, "lci  callsign  dev  <-> lci  callsign  dev\n");
+	len += sprintf(buffer, "lci  address     callsign   neigh  <-> lci  address     callsign   neigh\n");
 
 	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
-		len += sprintf(buffer + len, "%3.3X %-9s %-4s     ",
-			rose_route->lci1,
-			ax2asc(&rose_route->neigh1->callsign),
-			rose_route->neigh1->dev ? rose_route->neigh1->dev->name : "???");
-		len += sprintf(buffer + len, "%3.3X %-9s %-4s\n",
-			rose_route->lci2,
-			ax2asc(&rose_route->neigh2->callsign),
-			rose_route->neigh2->dev ? rose_route->neigh2->dev->name : "???");
+		if (rose_route->neigh1 != NULL) {
+			len += sprintf(buffer + len, "%3.3X  %-10s  %-9s  %05d      ",
+				rose_route->lci1,
+				rose2asc(&rose_route->src_addr),
+				ax2asc(&rose_route->src_call),
+				rose_route->neigh1->number);
+		} else {
+			len += sprintf(buffer + len, "000  *           *          00000      ");
+		}
+
+		if (rose_route->neigh2 != NULL) {
+			len += sprintf(buffer + len, "%3.3X  %-10s  %-9s  %05d\n",
+				rose_route->lci2,
+				rose2asc(&rose_route->dest_addr),
+				ax2asc(&rose_route->dest_call),
+				rose_route->neigh2->number);
+		} else {
+			len += sprintf(buffer + len, "000  *           *          00000\n");
+		}
 
 		pos = begin + len;
 
@@ -795,7 +861,7 @@
 #ifdef MODULE
 
 /*
- *	Release all memory associated with Rose routing structures.
+ *	Release all memory associated with ROSE routing structures.
  */
 void rose_rt_free(void)
 {
@@ -811,7 +877,7 @@
 	}
 
 	while (rose_node != NULL) {
-		t          = rose_node;
+		t         = rose_node;
 		rose_node = rose_node->next;
 
 		rose_remove_node(t);
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- linux-2.1.32/net/rose/rose_subr.c	Fri Jan 24 10:27:39 1997
+++ linux/net/rose/rose_subr.c	Tue Mar 25 10:58:34 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,7 +10,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from nr_subr.c
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_subr.c
  */
 
 #include <linux/config.h>
@@ -173,7 +170,7 @@
 			break;
 
 		default:
-			printk(KERN_ERR "rose_write_internal: invalid frametype %02X\n", frametype);
+			printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype);
 			kfree_skb(skb, FREE_WRITE);
 			return;
 	}
@@ -227,7 +224,7 @@
 	return ROSE_ILLEGAL;
 }
 
-static int rose_parse_national(unsigned char *p, rose_cb *rose, int len)
+static int rose_parse_national(unsigned char *p, struct rose_facilities *facilities, int len)
 {
 	unsigned char l, n = 0;
 
@@ -241,7 +238,7 @@
 
 			case 0x40:
 				if (*p == FAC_NATIONAL_RAND)
-					rose->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
+					facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
 				p   += 3;
 				n   += 3;
 				len -= 3;
@@ -256,12 +253,12 @@
 			case 0xC0:
 				l = p[1];
 				if (*p == FAC_NATIONAL_DEST_DIGI) {
-					memcpy(&rose->source_digi, p + 2, AX25_ADDR_LEN);
-					rose->source_ndigis = 1;
+					memcpy(&facilities->source_digi, p + 2, AX25_ADDR_LEN);
+					facilities->source_ndigis = 1;
 				}
 				if (*p == FAC_NATIONAL_SRC_DIGI) {
-					memcpy(&rose->dest_digi,   p + 2, AX25_ADDR_LEN);
-					rose->dest_ndigis = 1;
+					memcpy(&facilities->dest_digi,   p + 2, AX25_ADDR_LEN);
+					facilities->dest_ndigis = 1;
 				}
 				p   += l + 2;
 				n   += l + 2;
@@ -273,7 +270,7 @@
 	return n;
 }
 
-static int rose_parse_ccitt(unsigned char *p, rose_cb *rose, int len)
+static int rose_parse_ccitt(unsigned char *p, struct rose_facilities *facilities, int len)
 {
 	unsigned char l, n = 0;
 	char callsign[11];
@@ -301,16 +298,16 @@
 			case 0xC0:
 				l = p[1];
 				if (*p == FAC_CCITT_DEST_NSAP) {
-					memcpy(&rose->source_addr, p + 7, ROSE_ADDR_LEN);
+					memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);
 					memcpy(callsign, p + 12,   l - 10);
 					callsign[l - 10] = '\0';
-					rose->source_call = *asc2ax(callsign);
+					facilities->source_call = *asc2ax(callsign);
 				}
 				if (*p == FAC_CCITT_SRC_NSAP) {
-					memcpy(&rose->dest_addr, p + 7, ROSE_ADDR_LEN);
+					memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN);
 					memcpy(callsign, p + 12, l - 10);
 					callsign[l - 10] = '\0';
-					rose->dest_call = *asc2ax(callsign);
+					facilities->dest_call = *asc2ax(callsign);
 				}
 				p   += l + 2;
 				n   += l + 2;
@@ -322,12 +319,12 @@
 	return n;
 }
 
-int rose_parse_facilities(struct sk_buff *skb, rose_cb *rose)
+int rose_parse_facilities(struct sk_buff *skb, struct rose_facilities *facilities)
 {
 	int facilities_len, len;
 	unsigned char *p;
 
-	memset(rose, 0x00, sizeof(rose_cb));
+	memset(facilities, 0x00, sizeof(struct rose_facilities));
 
 	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
 	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
@@ -346,19 +343,19 @@
 
 			switch (*p) {
 				case FAC_NATIONAL:		/* National */
-					len = rose_parse_national(p + 1, rose, facilities_len - 1);
+					len = rose_parse_national(p + 1, facilities, facilities_len - 1);
 					facilities_len -= len + 1;
 					p += len + 1;
 					break;
 
 				case FAC_CCITT:		/* CCITT */
-					len = rose_parse_ccitt(p + 1, rose, facilities_len - 1);
+					len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1);
 					facilities_len -= len + 1;
 					p += len + 1;
 					break;
 
 				default:
-					printk(KERN_DEBUG "rose_parse_facilities: unknown facilities family %02X\n", *p);
+					printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p);
 					facilities_len--;
 					p++;
 					break;
diff -u --recursive --new-file linux-2.1.32/net/rose/rose_timer.c linux/net/rose/rose_timer.c
--- linux-2.1.32/net/rose/rose_timer.c	Thu Mar 27 19:43:34 1997
+++ linux/net/rose/rose_timer.c	Tue Mar 25 10:58:45 1997
@@ -1,8 +1,5 @@
 /*
- *	Rose release 001
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	ROSE release 002
  *
  *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
@@ -13,7 +10,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	Rose 001	Jonathan(G4KLX)	Cloned from nr_timer.c
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_timer.c
  */
 
 #include <linux/config.h>
@@ -61,7 +58,7 @@
 }
 
 /*
- *	Rose Timer
+ *	ROSE Timer
  *
  *	This routine is called every 100ms. Decrement timer by this
  *	amount - if expired then process the event.
diff -u --recursive --new-file linux-2.1.32/net/rose/sysctl_net_rose.c linux/net/rose/sysctl_net_rose.c
--- linux-2.1.32/net/rose/sysctl_net_rose.c	Fri Mar  7 14:48:21 1997
+++ linux/net/rose/sysctl_net_rose.c	Tue Mar 25 10:58:54 1997
@@ -1,5 +1,5 @@
 /* -*- linux-c -*-
- * sysctl_net_rose.c: sysctl interface to net Rose subsystem.
+ * sysctl_net_rose.c: sysctl interface to net ROSE subsystem.
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/rose directory entry (empty =) ). [MS]
diff -u --recursive --new-file linux-2.1.32/net/x25/Makefile linux/net/x25/Makefile
--- linux-2.1.32/net/x25/Makefile	Sun Apr  6 10:54:18 1997
+++ linux/net/x25/Makefile	Sun Apr  6 10:56:50 1997
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := x25.o
-O_OBJS	 := af_x25.o x25_dev.o x25_in.o x25_link.o x25_out.o x25_route.o x25_subr.o x25_timer.o
+O_OBJS	 := af_x25.o x25_dev.o x25_facilities.o x25_in.o x25_link.o x25_out.o \
+	    x25_route.o x25_subr.o x25_timer.o
 M_OBJS   := $(O_TARGET)
 
 ifeq ($(CONFIG_SYSCTL),y)
diff -u --recursive --new-file linux-2.1.32/net/x25/af_x25.c linux/net/x25/af_x25.c
--- linux-2.1.32/net/x25/af_x25.c	Sun Apr  6 10:54:18 1997
+++ linux/net/x25/af_x25.c	Sun Apr  6 10:53:40 1997
@@ -59,6 +59,8 @@
 
 static struct proto_ops x25_proto_ops;
 
+static x25_address null_x25_address = {"               "};
+
 int x25_addr_ntoa(unsigned char *p, x25_address *called_addr, x25_address *calling_addr)
 {
 	int called_len, calling_len;
@@ -238,7 +240,9 @@
 	cli();
 
 	for (s = x25_list; s != NULL; s = s->next) {
-		if (strcmp(s->protinfo.x25->source_addr.x25_addr, addr->x25_addr) == 0 && s->state == TCP_LISTEN) {
+		if ((strcmp(addr->x25_addr, s->protinfo.x25->source_addr.x25_addr) == 0 ||
+		     strcmp(addr->x25_addr, null_x25_address.x25_addr) == 0) &&
+		     s->state == TCP_LISTEN) {
 			restore_flags(flags);
 			return s;
 		}
@@ -335,7 +339,7 @@
 		sk->timer.data     = (unsigned long)sk;
 		add_timer(&sk->timer);
 	} else {
-		kfree_s(sk->protinfo.x25, sizeof(*sk->protinfo.x25));
+		kfree(sk->protinfo.x25);
 		sk_free(sk);
 		MOD_DEC_USE_COUNT;
 	}
@@ -428,7 +432,7 @@
 	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return NULL;
 
-	if ((x25 = (x25_cb *)kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) {
+	if ((x25 = kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) {
 		sk_free(sk);
 		return NULL;
 	}
@@ -599,7 +603,9 @@
 	x25_insert_socket(sk);
 
 	sk->zapped = 0;
+
 	SOCK_DEBUG(sk, "x25_bind: socket is bound\n");
+
 	return 0;
 }
 
@@ -640,6 +646,9 @@
 	if (sk->zapped)		/* Must bind first - autobinding does not work */
 		return -EINVAL;
 
+	if (strcmp(sk->protinfo.x25->source_addr.x25_addr, null_x25_address.x25_addr) == 0)
+		memset(&sk->protinfo.x25->source_addr, '\0', X25_ADDR_LEN);
+
 	sk->protinfo.x25->dest_addr = addr->sx25_addr;
 	sk->protinfo.x25->lci       = x25_new_lci();
 
@@ -660,7 +669,7 @@
 	cli();	/* To avoid races on the sleep */
 
 	/*
-	 * A Connect Ack with Choke or timeout or failed routing will go to closed.
+	 * A Clear Request or timeout or failed routing will go to closed.
 	 */
 	while (sk->state == TCP_SYN_SENT) {
 		interruptible_sleep_on(sk->sleep);
@@ -765,6 +774,7 @@
 	struct sock *make;
 	x25_address source_addr, dest_addr;
 	struct x25_facilities facilities;
+	int len;
 
 	/*
 	 *	Remove the LCI and frame type.
@@ -785,16 +795,31 @@
 	/*
 	 *	We can't accept the Call Request.
 	 */
-	if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = x25_make_new(sk)) == NULL) {
+	if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog) {
+		x25_transmit_clear_request(neigh, lci, 0x01);
+		return 0;
+	}
+
+	/*
+	 *	Try to reach a compromise on the requested facilities.
+	 */
+	if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1) {
+		x25_transmit_clear_request(neigh, lci, 0x01);
+		return 0;
+	}
+
+	/*
+	 *	Try to create a new socket.
+	 */
+	if ((make = x25_make_new(sk)) == NULL) {
 		x25_transmit_clear_request(neigh, lci, 0x01);
 		return 0;
 	}
 
 	/*
-	 *	Parse the facilities, and remove them, leaving any Call User
-	 *	Data.
+	 *	Remove the facilities, leaving any Call User Data.
 	 */
-	skb_pull(skb, x25_parse_facilities(skb, &facilities));
+	skb_pull(skb, len);
 
 	skb->sk     = make;
 	make->state = TCP_ESTABLISHED;
@@ -803,11 +828,6 @@
 	make->protinfo.x25->dest_addr     = dest_addr;
 	make->protinfo.x25->source_addr   = source_addr;
 	make->protinfo.x25->neighbour     = neigh;
-
-	/*
-	 *	This implies that we accept all the incoming facilities
-	 *	values (if any). This needs fixing. XXX
-	 */
 	make->protinfo.x25->facilities    = facilities;
 
 	x25_write_internal(make, X25_CALL_ACCEPTED);
@@ -881,10 +901,12 @@
 		sx25.sx25_family = AF_X25;
 		sx25.sx25_addr   = sk->protinfo.x25->dest_addr;
 	}
+
 	SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n");
 
 	/* Build a packet */
 	SOCK_DEBUG(sk, "x25_sendmsg: sendto: building packet.\n");
+
 	if ((msg->msg_flags & MSG_OOB) && len > 32)
 		len = 32;
 
@@ -899,8 +921,8 @@
 	 *	Put the data on the end
 	 */
 	SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n");
-	skb->h.raw = skb_put(skb, len);
-	asmptr = skb->h.raw;
+
+	asmptr = skb->h.raw = skb_put(skb, len);
 
 	memcpy_fromiovec(asmptr, msg->msg_iov, len);
 
@@ -917,6 +939,7 @@
 	 *	Push down the X.25 header
 	 */
 	SOCK_DEBUG(sk, "x25_sendmsg: Building X.25 Header.\n");
+
 	if (msg->msg_flags & MSG_OOB) {
 		if (sk->protinfo.x25->neighbour->extended) {
 			asmptr    = skb_push(skb, X25_STD_MIN_LEN);
@@ -948,6 +971,7 @@
 		if (qbit)
 			skb->data[0] |= X25_Q_BIT;
 	}
+
 	SOCK_DEBUG(sk, "x25_sendmsg: Built header.\n");
 	SOCK_DEBUG(sk, "x25_sendmsg: Transmitting buffer\n");
 
@@ -1076,7 +1100,7 @@
 
 		case SIOCGSTAMP:
 			if (sk != NULL) {
-				if (sk->stamp.tv_sec==0)
+				if (sk->stamp.tv_sec == 0)
 					return -ENOENT;
 				if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
 					return err;
@@ -1268,12 +1292,6 @@
 	0, &proc_net_inode_operations, 
 	x25_get_info
 };
-static struct proc_dir_entry proc_net_x25_links = {
-	PROC_NET_X25_LINKS, 9, "x25_links",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations, 
-	x25_link_get_info
-};
 static struct proc_dir_entry proc_net_x25_routes = {
 	PROC_NET_X25_ROUTES, 10, "x25_routes",
 	S_IFREG | S_IRUGO, 1, 0, 0,
@@ -1299,7 +1317,6 @@
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_x25);
-	proc_net_register(&proc_net_x25_links);
 	proc_net_register(&proc_net_x25_routes);
 #endif	
 }
@@ -1307,6 +1324,9 @@
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
 
+MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The X.25 Packet Layer network layer protocol");
+
 int init_module(void)
 {
 	struct device *dev;
@@ -1332,7 +1352,6 @@
 
 #ifdef CONFIG_PROC_FS
 	proc_net_unregister(PROC_NET_X25);
-	proc_net_unregister(PROC_NET_X25_LINKS);
 	proc_net_unregister(PROC_NET_X25_ROUTES);
 #endif
 
diff -u --recursive --new-file linux-2.1.32/net/x25/x25_facilities.c linux/net/x25/x25_facilities.c
--- linux-2.1.32/net/x25/x25_facilities.c	Thu Jan  1 01:00:00 1970
+++ linux/net/x25/x25_facilities.c	Tue Mar 11 17:18:07 1997
@@ -0,0 +1,203 @@
+/*
+ *	X.25 Packet Layer release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	X.25 001	Split from x25_subr.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/x25.h>
+
+/*
+ *	Parse a set of facilities into the facilities structure. Unrecognised
+ *	facilities are written to the debug log file.
+ */
+int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities)
+{
+	unsigned int len;
+	unsigned char *p = skb->data;
+
+	len = *p++;
+
+	while (len > 0) {
+		switch (*p & X25_FAC_CLASS_MASK) {
+			case X25_FAC_CLASS_A:
+				switch (*p) {
+					case X25_FAC_REVERSE:
+						facilities->reverse = (p[1] & 0x01);
+						break;
+					case X25_FAC_THROUGHPUT:
+						facilities->throughput = p[1];
+						break;
+					default:
+						printk(KERN_DEBUG "X.25: unknown facility %02X, value %02X\n", p[0], p[1]);
+						break;
+				}
+				p   += 2;
+				len -= 2;
+				break;
+
+			case X25_FAC_CLASS_B:
+				switch (*p) {
+					case X25_FAC_PACKET_SIZE:
+						facilities->pacsize_in  = p[1];
+						facilities->pacsize_out = p[2];
+						break;
+					case X25_FAC_WINDOW_SIZE:
+						facilities->winsize_in  = p[1];
+						facilities->winsize_out = p[2];
+						break;
+					default:
+						printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X\n", p[0], p[1], p[2]);
+						break;
+				}
+				p   += 3;
+				len -= 3;
+				break;
+
+			case X25_FAC_CLASS_C:
+				printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X, %02X\n", p[0], p[1], p[2], p[3]);
+				p   += 4;
+				len -= 4;
+				break;
+
+			case X25_FAC_CLASS_D:
+				printk(KERN_DEBUG "X.25: unknown facility %02X, length %d, values %02X, %02X, %02X, %02X\n", p[0], p[1], p[2], p[3], p[4], p[5]);
+				p   += p[1] + 2;
+				len -= p[1] + 2;
+				break;
+		}
+	}
+
+	return p - skb->data;
+}
+
+/*
+ *	Create a set of facilities.
+ */
+int x25_create_facilities(unsigned char *buffer, struct x25_facilities *facilities)
+{
+	unsigned char *p = buffer + 1;
+	int len;
+
+	if (facilities->reverse != 0) {
+		*p++ = X25_FAC_REVERSE;
+		*p++ = (facilities->reverse) ? 0x01 : 0x00;
+	}
+
+	if (facilities->throughput != 0) {
+		*p++ = X25_FAC_THROUGHPUT;
+		*p++ = facilities->throughput;
+	}
+
+	if (facilities->pacsize_in != 0 || facilities->pacsize_out != 0) {
+		*p++ = X25_FAC_PACKET_SIZE;
+		*p++ = (facilities->pacsize_in  == 0) ? facilities->pacsize_out : facilities->pacsize_in;
+		*p++ = (facilities->pacsize_out == 0) ? facilities->pacsize_in  : facilities->pacsize_out;
+	}
+
+	if (facilities->winsize_in != 0 || facilities->winsize_out != 0) {
+		*p++ = X25_FAC_WINDOW_SIZE;
+		*p++ = (facilities->winsize_in  == 0) ? facilities->winsize_out : facilities->winsize_in;
+		*p++ = (facilities->winsize_out == 0) ? facilities->winsize_in  : facilities->winsize_out;
+	}
+
+	len       = p - buffer;
+	buffer[0] = len - 1;
+
+	return len;
+}
+
+/*
+ *	Try to reach a compromise on a set of facilities.
+ *
+ *	The only real problem is with reverse charging.
+ */
+int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, struct x25_facilities *new)
+{
+	struct x25_facilities *ours;
+	struct x25_facilities theirs;
+	int len;
+
+	memset(&theirs, 0x00, sizeof(struct x25_facilities));
+
+	ours = &sk->protinfo.x25->facilities;
+
+	*new = *ours;
+
+	len = x25_parse_facilities(skb, &theirs);
+
+	/*
+	 *	They want reverse charging, we won't accept it.
+	 */
+	if (theirs.reverse != 0 && ours->reverse == 0) {
+		SOCK_DEBUG(sk, "X.25: rejecting reverse charging request");
+		return -1;
+	}
+
+	new->reverse = theirs.reverse;
+
+	if (theirs.throughput != 0) {
+		if (theirs.throughput < ours->throughput) {
+			SOCK_DEBUG(sk, "X.25: throughput negotiated down");
+			new->throughput = theirs.throughput;
+		}
+	}
+
+	if (theirs.pacsize_in != 0 && theirs.pacsize_out != 0) {
+		if (theirs.pacsize_in < ours->pacsize_in) {
+			SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down");
+			new->pacsize_in = theirs.pacsize_in;
+		}
+		if (theirs.pacsize_out < ours->pacsize_out) {
+			SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down");
+			new->pacsize_out = theirs.pacsize_out;
+		}
+	}
+
+	if (theirs.winsize_in != 0 && theirs.winsize_out != 0) {
+		if (theirs.winsize_in < ours->winsize_in) {
+			SOCK_DEBUG(sk, "X.25: window size inwards negotiated down");
+			new->winsize_in = theirs.winsize_in;
+		}
+		if (theirs.winsize_out < ours->winsize_out) {
+			SOCK_DEBUG(sk, "X.25: window size outwards negotiated down");
+			new->winsize_out = theirs.winsize_out;
+		}
+	}
+
+	return len;
+}
+
+#endif
diff -u --recursive --new-file linux-2.1.32/net/x25/x25_in.c linux/net/x25/x25_in.c
--- linux-2.1.32/net/x25/x25_in.c	Fri Mar  7 14:49:18 1997
+++ linux/net/x25/x25_in.c	Tue Mar 11 17:16:33 1997
@@ -84,7 +84,6 @@
 static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
 {
 	x25_address source_addr, dest_addr;
-	struct x25_facilities facilities;
 
 	switch (frametype) {
 
@@ -102,9 +101,8 @@
 			 */
 			skb_pull(skb, X25_STD_MIN_LEN);
 			skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
-			skb_pull(skb, x25_parse_facilities(skb, &facilities));
+			skb_pull(skb, x25_parse_facilities(skb, &sk->protinfo.x25->facilities));
 			/*
-			 *	Facilities XXX
 			 *	Copy any Call User Data.
 			 */
 			if (skb->len >= 0) {
@@ -128,7 +126,6 @@
 			break;
 
 		default:
-			printk(KERN_WARNING "x25: unknown %02X in state 1\n", frametype);
 			break;
 	}
 
@@ -158,7 +155,6 @@
 			break;
 
 		default:
-			printk(KERN_WARNING "x25: unknown %02X in state 2\n", frametype);
 			break;
 	}
 
@@ -334,7 +330,6 @@
 			break;
 
 		default:
-			printk(KERN_WARNING "x25: unknown %02X in state 4\n", frametype);
 			break;
 	}
 
diff -u --recursive --new-file linux-2.1.32/net/x25/x25_link.c linux/net/x25/x25_link.c
--- linux-2.1.32/net/x25/x25_link.c	Thu Mar 27 19:43:34 1997
+++ linux/net/x25/x25_link.c	Thu Mar 27 20:16:53 1997
@@ -240,8 +240,10 @@
 
 void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh)
 {
-	if (call_fw_firewall(PF_X25, skb->dev, skb->data, NULL,&skb) != FW_ACCEPT)
+	if (call_fw_firewall(PF_X25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
+		kfree_skb(skb, FREE_WRITE);
 		return;
+	}
 
 	switch (neigh->state) {
 		case X25_LINK_STATE_0:
@@ -294,7 +296,7 @@
 	struct x25_neigh *x25_neigh;
 	unsigned long flags;
 
-	if ((x25_neigh = (struct x25_neigh *)kmalloc(sizeof(*x25_neigh), GFP_ATOMIC)) == NULL)
+	if ((x25_neigh = kmalloc(sizeof(*x25_neigh), GFP_ATOMIC)) == NULL)
 		return;
 
 	skb_queue_head_init(&x25_neigh->queue);
@@ -329,7 +331,7 @@
 	if ((s = x25_neigh_list) == x25_neigh) {
 		x25_neigh_list = x25_neigh->next;
 		restore_flags(flags);
-		kfree_s(x25_neigh, sizeof(struct x25_neigh));
+		kfree(x25_neigh);
 		return;
 	}
 
@@ -337,7 +339,7 @@
 		if (s->next == x25_neigh) {
 			s->next = x25_neigh->next;
 			restore_flags(flags);
-			kfree_s(x25_neigh, sizeof(struct x25_neigh));
+			kfree(x25_neigh);
 			return;
 		}
 
@@ -419,46 +421,6 @@
 
 	return 0;
 }
-
-int x25_link_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct x25_neigh *x25_neigh;
-	int len     = 0;
-	off_t pos   = 0;
-	off_t begin = 0;
-
-	cli();
-
-	len += sprintf(buffer, "device  st    t20    ext\n");
-
-	for (x25_neigh = x25_neigh_list; x25_neigh != NULL; x25_neigh = x25_neigh->next) {
-		len += sprintf(buffer + len, "%-6s  %2d  %3d/%03d  %d\n",
-			x25_neigh->dev->name,
-			x25_neigh->state,
-			x25_neigh->t20timer / X25_SLOWHZ,
-			x25_neigh->t20      / X25_SLOWHZ,
-			x25_neigh->extended);
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-
-		if (pos > offset + length)
-			break;
-	}
-
-	sti();
-
-	*start = buffer + (offset - begin);
-	len   -= (offset - begin);
-
-	if (len > length) len = length;
-
-	return len;
-} 
 
 #ifdef MODULE
 
diff -u --recursive --new-file linux-2.1.32/net/x25/x25_route.c linux/net/x25/x25_route.c
--- linux-2.1.32/net/x25/x25_route.c	Fri Jan 24 10:27:40 1997
+++ linux/net/x25/x25_route.c	Sun Mar 16 11:09:50 1997
@@ -59,7 +59,7 @@
 		if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits)
 			return -EINVAL;
 
-	if ((x25_route = (struct x25_route *)kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL)
+	if ((x25_route = kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
 	strcpy(x25_route->address.x25_addr, "000000000000000");
@@ -87,7 +87,7 @@
 	if ((s = x25_route_list) == x25_route) {
 		x25_route_list = x25_route->next;
 		restore_flags(flags);
-		kfree_s(x25_route, sizeof(struct x25_route));
+		kfree(x25_route);
 		return;
 	}
 
@@ -95,7 +95,7 @@
 		if (s->next == x25_route) {
 			s->next = x25_route->next;
 			restore_flags(flags);
-			kfree_s(x25_route, sizeof(struct x25_route));
+			kfree(x25_route);
 			return;
 		}
 
diff -u --recursive --new-file linux-2.1.32/net/x25/x25_subr.c linux/net/x25/x25_subr.c
--- linux-2.1.32/net/x25/x25_subr.c	Fri Jan 24 10:27:40 1997
+++ linux/net/x25/x25_subr.c	Sat Mar  8 21:07:34 1997
@@ -281,106 +281,4 @@
 	return X25_ILLEGAL;
 }
 
-/*
- *	Parse a set of facilities into the facilities structure. Unrecognised
- *	facilities are written to the debug log file.
- */
-int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities)
-{
-	unsigned int len;
-	unsigned char *p = skb->data;
-
-	memset(facilities, 0x00, sizeof(struct x25_facilities));
-
-	len = *p++;
-
-	while (len > 0) {
-		switch (*p & X25_FAC_CLASS_MASK) {
-			case X25_FAC_CLASS_A:
-				switch (*p) {
-					case X25_FAC_REVERSE:
-						facilities->reverse = (p[1] & 0x01);
-						break;
-					case X25_FAC_THROUGHPUT:
-						facilities->throughput = p[1];
-						break;
-					default:
-						printk(KERN_DEBUG "X.25: unknown facility %02X, value %02X\n", p[0], p[1]);
-						break;
-				}
-				p   += 2;
-				len -= 2;
-				break;
-
-			case X25_FAC_CLASS_B:
-				switch (*p) {
-					case X25_FAC_PACKET_SIZE:
-						facilities->pacsize_in  = p[1];
-						facilities->pacsize_out = p[2];
-						break;
-					case X25_FAC_WINDOW_SIZE:
-						facilities->winsize_in  = p[1];
-						facilities->winsize_out = p[2];
-						break;
-					default:
-						printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X\n", p[0], p[1], p[2]);
-						break;
-				}
-				p   += 3;
-				len -= 3;
-				break;
-
-			case X25_FAC_CLASS_C:
-				printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X, %02X\n", p[0], p[1], p[2], p[3]);
-				p   += 4;
-				len -= 4;
-				break;
-
-			case X25_FAC_CLASS_D:
-				printk(KERN_DEBUG "X.25: unknown facility %02X, length %d, values %02X, %02X, %02X, %02X\n", p[0], p[1], p[2], p[3], p[4], p[5]);
-				p   += p[1] + 2;
-				len -= p[1] + 2;
-				break;
-		}
-	}
-
-	return p - skb->data;
-}
-
-/*
- *	Create a set of facilities.
- */
-int x25_create_facilities(unsigned char *buffer, struct x25_facilities *facilities)
-{
-	unsigned char *p = buffer + 1;
-	int len;
-
-	if (facilities->reverse != 0) {
-		*p++ = X25_FAC_REVERSE;
-		*p++ = (facilities->reverse) ? 0x01 : 0x00;
-	}
-
-	if (facilities->throughput != 0) {
-		*p++ = X25_FAC_THROUGHPUT;
-		*p++ = facilities->throughput;
-	}
-
-	if (facilities->pacsize_in != 0 || facilities->pacsize_out != 0) {
-		*p++ = X25_FAC_PACKET_SIZE;
-		*p++ = (facilities->pacsize_in  == 0) ? facilities->pacsize_out : facilities->pacsize_in;
-		*p++ = (facilities->pacsize_out == 0) ? facilities->pacsize_in  : facilities->pacsize_out;
-	}
-
-	if (facilities->winsize_in != 0 || facilities->winsize_out != 0) {
-		*p++ = X25_FAC_WINDOW_SIZE;
-		*p++ = (facilities->winsize_in  == 0) ? facilities->winsize_out : facilities->winsize_in;
-		*p++ = (facilities->winsize_out == 0) ? facilities->winsize_in  : facilities->winsize_out;
-	}
-
-	len       = p - buffer;
-	buffer[0] = len - 1;
-
-	return len;
-}
-
 #endif
