patch-2.1.109 linux/drivers/net/ppp.c

Next file: linux/drivers/pci/oldproc.c
Previous file: linux/drivers/net/ne2k-pci.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.108/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
@@ -2,15 +2,9 @@
  *
  *  Michael Callahan <callahan@maths.ox.ac.uk>
  *  Al Longyear <longyear@netcom.com>
- *  Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
- *  Cyrus Durgin <cider@speakeasy.org> (changes for kmod)
+ *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
- *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
- *
- *  Machine hang caused by NULLing a live wait queue fix. <Alan.Cox@linux.org>
- *
- *  ==FILEVERSION 980608==
+ *  ==FILEVERSION 980704==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -47,21 +41,22 @@
 */
 
 #define OPTIMIZE_FLAG_TIME	((HZ * 3)/2)
-
 #define CHECK_CHARACTERS	1
-#define PPP_COMPRESS		1
 
-/* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */
+#define PPP_MAX_RCV_QLEN	32	/* max # frames we queue up for pppd */
+
+/* $Id: ppp.c,v 1.19 1998/07/07 04:27:37 paulus Exp $ */
 
-#include <linux/config.h> /* for CONFIG_KMOD */
+#include <linux/version.h>
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
-#include <linux/poll.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
+#include <linux/poll.h>
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/tty.h>
@@ -72,18 +67,13 @@
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
+
 #include <linux/if.h>
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/inet.h>
 #include <linux/ioctl.h>
-#include <linux/init.h>
-
-typedef struct sk_buff	     sk_buff;
-#define skb_data(skb)	     ((__u8 *) (skb)->data)
-
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/if_arp.h>
@@ -101,49 +91,38 @@
 #include <linux/kmod.h>
 #endif
 
-#ifndef PPP_IPX
-#define PPP_IPX 0x2b  /* IPX protocol over PPP */
-#endif
-
-#ifndef PPP_LQR
-#define PPP_LQR 0xc025	/* Link Quality Reporting Protocol */
-#endif
+/*
+ * Local functions
+ */
 
 #ifdef CONFIG_MODULES
 static int ppp_register_compressor (struct compressor *cp);
 static void ppp_unregister_compressor (struct compressor *cp);
 #endif
 
-/*
- * Local functions
- */
+static void ppp_async_init(struct ppp *ppp);
+static void ppp_async_release(struct ppp *ppp);
+static int ppp_tty_push(struct ppp *ppp);
+static int ppp_async_encode(struct ppp *ppp);
+static int ppp_async_send(struct ppp *, struct sk_buff *);
+
+static int ppp_ioctl(struct ppp *, unsigned int, unsigned long);
+static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp);
+static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd);
+static void ppp_ccp_closed(struct ppp *ppp);
+static int ppp_receive_frame(struct ppp *, struct sk_buff *);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_output_wakeup(struct ppp *ppp);
+static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb);
 
-static struct compressor *find_compressor (int type);
-static void ppp_init_ctrl_blk (register struct ppp *);
-static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr);
-static struct ppp *ppp_alloc (void);
 static struct ppp *ppp_find (int pid_value);
-static void ppp_print_buffer (const __u8 *, const __u8 *, int);
-extern inline void ppp_stuff_char (struct ppp *ppp,
-				   register struct ppp_buffer *buf,
-				   register __u8 chr);
-extern inline int lock_buffer (register struct ppp_buffer *buf);
-static int ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf,
-			    __u8 *data, int len, enum NPmode npmode);
-
-static int rcv_proto_ip		(struct ppp *, __u16, __u8 *, int);
-static int rcv_proto_ipx	(struct ppp *, __u16, __u8 *, int);
-static int rcv_proto_vjc_comp	(struct ppp *, __u16, __u8 *, int);
-static int rcv_proto_vjc_uncomp (struct ppp *, __u16, __u8 *, int);
-static int rcv_proto_unknown	(struct ppp *, __u16, __u8 *, int);
-static int rcv_proto_lqr	(struct ppp *, __u16, __u8 *, int);
-static void ppp_doframe_lower	(struct ppp *, __u8 *, int);
-static int ppp_doframe		(struct ppp *);
-
-static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd);
-static int  rcv_proto_ccp (struct ppp *, __u16, __u8 *, int);
-
-#define ins_char(pbuf,c) (buf_base(pbuf) [(pbuf)->count++] = (__u8)(c))
+static struct ppp *ppp_alloc (void);
+static void ppp_generic_init(struct ppp *ppp);
+static void ppp_release(struct ppp *ppp);
+static void ppp_print_buffer (const char *, const __u8 *, int);
+static struct compressor *find_compressor (int type);
 
 #ifndef OPTIMIZE_FLAG_TIME
 #define OPTIMIZE_FLAG_TIME	0
@@ -156,68 +135,26 @@
 static int  flag_time = OPTIMIZE_FLAG_TIME;
 MODULE_PARM(flag_time, "i");
 
-/*
- * The "main" procedure to the ppp device
- */
-
-int ppp_init (struct device *);
-
-/*
- * Network device driver callback routines
- */
-
-static int ppp_dev_open (struct device *);
-static int ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd);
-static int ppp_dev_close (struct device *);
-static int ppp_dev_xmit (sk_buff *, struct device *);
-static struct net_device_stats *ppp_dev_stats (struct device *);
-
-/*
- * TTY callbacks
- */
-
-static ssize_t ppp_tty_read (struct tty_struct *, struct file *, __u8 *,
-			     size_t);
-static ssize_t ppp_tty_write (struct tty_struct *, struct file *,
-			      const __u8 *, size_t);
-static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
-			  unsigned long);
-static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp,
-				  poll_table * wait);
-static int ppp_tty_open (struct tty_struct *);
-static void ppp_tty_close (struct tty_struct *);
-static int ppp_tty_room (struct tty_struct *tty);
-static void ppp_tty_receive (struct tty_struct *tty, const __u8 * cp,
-			     char *fp, int count);
-static void ppp_tty_wakeup (struct tty_struct *tty);
-
 #define CHECK_PPP_MAGIC(ppp)	do { \
 	if (ppp->magic != PPP_MAGIC) { \
-		printk(KERN_WARNING "bad magic for ppp %p at %s:%d\n", \
-		       ppp, __FILE__, __LINE__); \
+		printk(ppp_magic_warn, ppp, __FILE__, __LINE__); \
 	} \
 } while (0)
 #define CHECK_PPP(a)	do { \
 	CHECK_PPP_MAGIC(ppp); \
 	if (!ppp->inuse) { \
-		printk (ppp_warning, __LINE__); \
+		printk(ppp_warning, __LINE__); \
 		return a; \
 	} \
 } while (0)
 #define CHECK_PPP_VOID() do { \
 	CHECK_PPP_MAGIC(ppp); \
 	if (!ppp->inuse) { \
-		printk (ppp_warning, __LINE__); \
+		printk(ppp_warning, __LINE__); \
 		return; \
 	} \
 } while (0)
 
-#define in_xmap(ppp,c)	(ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
-#define in_rmap(ppp,c)	((((unsigned int) (__u8) (c)) < 0x20) && \
-			ppp->recv_async_map & (1 << (c)))
-
-#define bset(p,b)	((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
-
 #define tty2ppp(tty)	((struct ppp *) ((tty)->disc_data))
 #define dev2ppp(dev)	((struct ppp *) ((dev)->priv))
 #define ppp2tty(ppp)	((ppp)->tty)
@@ -226,39 +163,43 @@
 static struct ppp *ppp_list = NULL;
 static struct ppp *ppp_last = NULL;
 
-/* Buffer types */
-#define BUFFER_TYPE_DEV_RD	0  /* ppp read buffer	    */
-#define BUFFER_TYPE_TTY_WR	1  /* tty write buffer	    */
-#define BUFFER_TYPE_DEV_WR	2  /* ppp write buffer	    */
-#define BUFFER_TYPE_TTY_RD	3  /* tty read buffer	    */
-#define BUFFER_TYPE_VJ		4  /* vj compression buffer */
-
-/* Define this string only once for all macro invocations */
+/* Define these strings only once for all macro invocations */
 static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
+static char ppp_magic_warn[] = KERN_WARNING "bad magic for ppp %p at %s:%d\n";
 
 static char szVersion[]		= PPP_VERSION;
 
-/*
- * Information for the protocol decoder
- */
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
 
-typedef int (*pfn_proto)  (struct ppp *, __u16, __u8 *, int);
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ *    The following code implements the PPP line discipline
+ *    and supports using PPP on an async serial line.
+ *************************************************************/
 
-typedef struct ppp_proto_struct {
-	int		proto;
-	pfn_proto	func;
-} ppp_proto_type;
+#define in_xmap(ppp,c)	(ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
+#define in_rmap(ppp,c)	((((unsigned int) (__u8) (c)) < 0x20) && \
+			ppp->recv_async_map & (1 << (c)))
 
-static
-ppp_proto_type proto_list[] = {
-	{ PPP_IP,	  rcv_proto_ip	       },
-	{ PPP_IPX,	  rcv_proto_ipx	       },
-	{ PPP_VJC_COMP,	  rcv_proto_vjc_comp   },
-	{ PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
-	{ PPP_LQR,	  rcv_proto_lqr	       },
-	{ PPP_CCP,	  rcv_proto_ccp	       },
-	{ 0,		  rcv_proto_unknown    }  /* !!! MUST BE LAST !!! */
-};
+/*
+ * TTY callbacks
+ */
+
+static ssize_t ppp_tty_read(struct tty_struct *, struct file *, __u8 *,
+			    size_t);
+static ssize_t ppp_tty_write(struct tty_struct *, struct file *, const __u8 *,
+			     size_t);
+static int ppp_tty_ioctl(struct tty_struct *, struct file *, unsigned int,
+			 unsigned long);
+static unsigned int ppp_tty_poll(struct tty_struct *tty, struct file *filp,
+				 poll_table * wait);
+static int ppp_tty_open (struct tty_struct *);
+static void ppp_tty_close (struct tty_struct *);
+static int ppp_tty_room (struct tty_struct *tty);
+static void ppp_tty_receive (struct tty_struct *tty, const __u8 * cp,
+			     char *fp, int count);
+static void ppp_tty_wakeup (struct tty_struct *tty);
 
 __u16 ppp_crc16_table[256] =
 {
@@ -295,6 +236,7 @@
 	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
 	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
+EXPORT_SYMBOL(ppp_crc16_table);
 
 #ifdef CHECK_CHARACTERS
 static __u32 paritytab[8] =
@@ -304,43 +246,29 @@
 };
 #endif
 
-/* local function to store a value into the LQR frame */
-extern inline __u8 * store_long (register __u8 *p, register int value) {
-	*p++ = (__u8) (value >> 24);
-	*p++ = (__u8) (value >> 16);
-	*p++ = (__u8) (value >>	 8);
-	*p++ = (__u8) value;
-	return p;
-}
-
-/*************************************************************
- * INITIALIZATION
- *************************************************************/
-
-/* This procedure is called once and once only to define who we are to
- * the operating system and the various procedures that it may use in
- * accessing the ppp protocol.
+/*
+ * This procedure is called at initialization time to register
+ * the PPP line discipline.
  */
-
-__initfunc(static int
-ppp_first_time (void))
+static int
+ppp_first_time(void)
 {
 	static struct tty_ldisc	ppp_ldisc;
 	int    status;
 
-	printk (KERN_INFO
-		"PPP: version %s (demand dialling)"
-		"\n", szVersion);
+	printk(KERN_INFO
+	       "PPP: version %s (demand dialling)"
+	       "\n", szVersion);
 
 #ifndef MODULE /* slhc module logic has its own copyright announcement */
-	printk (KERN_INFO
-		"TCP compression code copyright 1989 Regents of the "
-		"University of California\n");
+	printk(KERN_INFO
+	       "TCP compression code copyright 1989 Regents of the "
+	       "University of California\n");
 #endif
 
-/*
- * Register the tty discipline
- */
+	/*
+	 * Register the tty discipline
+	 */
 	(void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
 	ppp_ldisc.magic		= TTY_LDISC_MAGIC;
 	ppp_ldisc.name          = "ppp";
@@ -356,89 +284,20 @@
 
 	status = tty_register_ldisc (N_PPP, &ppp_ldisc);
 	if (status == 0)
-		printk (KERN_INFO "PPP line discipline registered.\n");
+		printk(KERN_INFO "PPP line discipline registered.\n");
 	else
-		printk (KERN_ERR "error registering line discipline: %d\n",
-			status);
+		printk(KERN_ERR "error registering line discipline: %d\n",
+		       status);
 	return status;
 }
 
-/*************************************************************
- * INITIALIZATION
- *************************************************************/
-
-/* called when the device is actually created */
-
-static int
-ppp_init_dev (struct device *dev)
-{
-	dev->hard_header_len  = PPP_HDRLEN;
-
-	/* device INFO */
-	dev->mtu	      = PPP_MTU;
-	dev->hard_start_xmit  = ppp_dev_xmit;
-	dev->open	      = ppp_dev_open;
-	dev->stop	      = ppp_dev_close;
-	dev->get_stats	      = ppp_dev_stats;
-	dev->do_ioctl	      = ppp_dev_ioctl;
-	dev->addr_len	      = 0;
-	dev->tx_queue_len     = 10;
-	dev->type	      = ARPHRD_PPP;
-
-	dev_init_buffers(dev);
-
-	/* New-style flags */
-	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
-
-	return 0;
-}
 
+#ifndef MODULE
 /*
- * Local procedure to initialize the ppp structure
+ * Called at boot time if the PPP driver is compiled into the kernel.
  */
-
-static void
-ppp_init_ctrl_blk (register struct ppp *ppp)
-{
-	ppp->magic  = PPP_MAGIC;
-	ppp->toss   = 0xE0;
-	ppp->escape = 0;
-
-	ppp->flags  = 0;
-	ppp->mtu    = PPP_MTU;
-	ppp->mru    = PPP_MRU;
-
-	memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
-	ppp->xmit_async_map[0] = 0xffffffff;
-	ppp->xmit_async_map[3] = 0x60000000;
-	ppp->recv_async_map    = 0x00000000;
-
-	ppp->rbuf	= NULL;
-	ppp->wbuf	= NULL;
-	ppp->ubuf	= NULL;
-	ppp->cbuf	= NULL;
-	ppp->slcomp	= NULL;
-	ppp->last_xmit	= jiffies - flag_time;
-	ppp->last_recv  = jiffies;
-
-	/* clear statistics */
-	memset(&ppp->stats, 0, sizeof (struct pppstat));
-	memset(&ppp->estats, 0, sizeof(ppp->estats));
-
-	/* PPP compression data */
-	ppp->sc_xc_state =
-	ppp->sc_rc_state = NULL;
-}
-
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
-EXPORT_SYMBOL(ppp_crc16_table);
-
-/* called at boot/load time for each ppp device defined in the kernel */
-
-#ifndef MODULE
 int
-ppp_init (struct device *dev)
+ppp_init(struct device *dev)
 {
 	static int first_time = 1;
 	int    answer = 0;
@@ -453,1712 +312,1259 @@
 }
 #endif
 
-#define BUFFER_MAGIC	0x1d10
-#define CHECK_BUF_MAGIC(buf)	do { \
-	if (buf->magic != BUFFER_MAGIC) { \
-		printk(KERN_WARNING "bad magic for ppp buffer %p at %s:%d\n", \
-		       buf, __FILE__, __LINE__); \
-	} \
-} while (0)
-
 /*
- * Routine to allocate a buffer for later use by the driver.
+ * Initialize the async-specific parts of the ppp structure.
  */
-
-static struct ppp_buffer *
-ppp_alloc_buf (int size, int type)
+static void
+ppp_async_init(struct ppp *ppp)
 {
-	struct ppp_buffer *buf;
+	ppp->escape = 0;
+	ppp->toss   = 0xE0;
+	ppp->tty_pushing = 0;
 
-	buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer),
-					     GFP_ATOMIC);
+	memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
+	ppp->xmit_async_map[0] = 0xffffffff;
+	ppp->xmit_async_map[3] = 0x60000000;
+	ppp->recv_async_map    = 0xffffffff;
 
-	if (buf != NULL) {
-		buf->size   = size - 1;	/* Mask for the buffer size */
-		buf->type   = type;
-		buf->locked = 0;
-		buf->count  = 0;
-		buf->head   = 0;
-		buf->tail   = 0;
-		buf->fcs    = PPP_INITFCS;
-		buf->magic  = BUFFER_MAGIC;
-	}
-	return (buf);
-}
+	ppp->tpkt = NULL;
+	ppp->tfcs = PPP_INITFCS;
+	ppp->optr = ppp->obuf;
+	ppp->olim = ppp->obuf;
 
-/*
- * Routine to release the allocated buffer.
- */
+	ppp->rpkt = NULL;
+	ppp->rfcs = PPP_INITFCS;
 
-static void
-ppp_free_buf (struct ppp_buffer *ptr)
-{
-	if (ptr != NULL) {
-		CHECK_BUF_MAGIC(ptr);
-		kfree (ptr);
-	}
+	ppp->tty  = NULL;
+	ppp->backup_tty = NULL;
+
+	ppp->bytes_sent = 0;
+	ppp->bytes_rcvd = 0;
 }
 
 /*
- * Lock the indicated transmit buffer
+ * Clean up the async-specific parts of the ppp structure.
  */
-
-extern inline int
-lock_buffer (register struct ppp_buffer *buf)
+static void
+ppp_async_release(struct ppp *ppp)
 {
-	unsigned long state;
-	unsigned long flags;
-/*
- * Save the current state and if free then set it to the "busy" state
- */
-	CHECK_BUF_MAGIC(buf);
-	save_flags (flags);
-	cli ();
-	state = buf->locked;
-	if (state == 0)
-		buf->locked = 2;
+	struct sk_buff *skb;
 
-	restore_flags (flags);
-	return (state);
+	if ((skb = ppp->rpkt) != NULL)
+		kfree_skb(skb);
+	ppp->rpkt = NULL;
+	if ((skb = ppp->tpkt) != NULL)
+		kfree_skb(skb);
+	ppp->tpkt = NULL;
 }
 
 /*
- * MTU has been changed by the IP layer. Unfortunately we are not told
- * about this, but we spot it ourselves and fix things up. We could be
- * in an upcall from the tty driver, or in an ip packet queue.
+ * TTY callback.
+ *
+ * Called when the tty discipline is switched to PPP.
  */
 
 static int
-ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
+ppp_tty_open (struct tty_struct *tty)
 {
-	struct device *dev;
-	unsigned long flags;
+	struct ppp *ppp;
+
+	/*
+	 * Allocate a ppp structure to use.
+	 */
+	tty->disc_data = NULL;
+	ppp = ppp_find(current->pid);
+	if (ppp != NULL) {
+		/*
+		 * If we are taking over a ppp unit which is currently
+		 * connected to a loopback pty, there's not much to do.
+		 */
+		CHECK_PPP(-EINVAL);
 
-	struct ppp_buffer *new_rbuf;
-	struct ppp_buffer *new_wbuf;
-	struct ppp_buffer *new_cbuf;
-	struct ppp_buffer *new_tbuf;
-
-	struct ppp_buffer *old_rbuf;
-	struct ppp_buffer *old_wbuf;
-	struct ppp_buffer *old_cbuf;
-	struct ppp_buffer *old_tbuf;
+	} else {
+		ppp = ppp_alloc();
+		if (ppp == NULL) {
+			printk(KERN_ERR "ppp_alloc failed\n");
+			return -ENFILE;
+		}
 
-	int mtu, mru;
-/*
- *  Allocate the buffer from the kernel for the data
- */
-	CHECK_PPP(0);
-	dev = ppp2dev (ppp);
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: changedmtu %d %d\n", ppp->name,
-		       new_mtu, new_mru);
-	mru = new_mru;
-	/* allow for possible escaping of every character */
-	mtu = (new_mtu * 2) + 20;
-
-	/* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
-	if (mru < PPP_MRU)
-		mru = PPP_MRU;
-
-	mru += 10;
-
-	new_wbuf = ppp_alloc_buf (mtu+PPP_HDRLEN,	BUFFER_TYPE_DEV_WR);
-	new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24,	BUFFER_TYPE_TTY_WR);
-	new_rbuf = ppp_alloc_buf (mru + 84,		BUFFER_TYPE_DEV_RD);
-	new_cbuf = ppp_alloc_buf (mru+PPP_HDRLEN,	BUFFER_TYPE_VJ);
-/*
- *  If the buffers failed to allocate then complain and release the partial
- *  allocations.
- */
-	if (new_wbuf == NULL || new_tbuf == NULL ||
-	    new_rbuf == NULL || new_cbuf == NULL) {
-		printk (KERN_ERR "ppp: failed to allocate new buffers\n");
-
-		ppp_free_buf (new_wbuf);
-		ppp_free_buf (new_tbuf);
-		ppp_free_buf (new_rbuf);
-		ppp_free_buf (new_cbuf);
-		return 0;
+		/*
+		 * Initialize the control block
+		 */
+		ppp_generic_init(ppp);
+		ppp_async_init(ppp);
+
+		MOD_INC_USE_COUNT;
 	}
-/*
- *  Update the pointers to the new buffer structures.
- */
-	save_flags(flags);
-	cli ();
-	old_wbuf = ppp->wbuf;
-	old_rbuf = ppp->rbuf;
-	old_cbuf = ppp->cbuf;
-	old_tbuf = ppp->tbuf;
-
-	ppp->wbuf = new_wbuf;
-	ppp->rbuf = new_rbuf;
-	ppp->cbuf = new_cbuf;
-	ppp->tbuf = new_tbuf;
-
-	if (old_wbuf)
-		new_wbuf->locked = old_wbuf->locked;
-
-	ppp->rbuf->size -= 80;	/* reserve space for vj header expansion */
-
-	dev->mem_start	= (unsigned long) buf_base (new_wbuf);
-	dev->mem_end	= (unsigned long) (dev->mem_start + mtu);
-	dev->rmem_start = (unsigned long) buf_base (new_rbuf);
-	dev->rmem_end	= (unsigned long) (dev->rmem_start + mru);
-/*
- *  Update the parameters for the new buffer sizes
- */
-	ppp->toss   = 0xE0;	/* To ignore characters until new FLAG */
-	ppp->escape = 0;	/* No pending escape character */
-
-	dev->mtu    =
-	ppp->mtu    = new_mtu;
-	ppp->mru    = new_mru;
-
-	ppp->s1buf  = NULL;
-	ppp->s2buf  = NULL;
-	ppp->xbuf   = NULL;
 
-	ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-	ppp->flags	&= ~SC_XMIT_BUSY;
+	tty->disc_data = ppp;
+	ppp->tty       = tty;
 
-	restore_flags(flags);
-/*
- *  Release old buffer pointers
- */
-	ppp_free_buf (old_rbuf);
-	ppp_free_buf (old_wbuf);
-	ppp_free_buf (old_cbuf);
-	ppp_free_buf (old_tbuf);
-	return 1;
+	/*
+	 * Flush any pending characters in the driver
+	 */
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer (tty);
+
+	return ppp->line;
 }
 
 /*
- * CCP is down; free (de)compressor state if necessary.
+ * TTY callback.
+ *
+ * Called when the line discipline is changed to something
+ * else, the tty is closed, or the tty detects a hangup.
  */
 
 static void
-ppp_ccp_closed (struct ppp *ppp)
+ppp_tty_close (struct tty_struct *tty)
 {
-	unsigned long flags;
+	struct ppp *ppp = tty2ppp(tty);
 
-	save_flags(flags);
-	cli();
-	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
-	restore_flags(flags);
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: ccp closed\n", ppp->name);
-	if (ppp->sc_xc_state) {
-		(*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
-		ppp->sc_xc_state = NULL;
+	if (ppp == NULL)
+		return;
+	tty->disc_data = NULL;
+	if (ppp->magic != PPP_MAGIC) {
+		printk(KERN_WARNING "ppp_tty_close: bogus\n");
+		return;
+	}
+	if (!ppp->inuse) {
+		printk(KERN_WARNING "ppp_tty_close: not inuse\n");
+		ppp->tty = ppp->backup_tty = 0;
+		return;
 	}
+	if (tty == ppp->backup_tty)
+		ppp->backup_tty = 0;
+	if (tty != ppp->tty)
+		return;
+	if (ppp->backup_tty) {
+		ppp->tty = ppp->backup_tty;
+	} else {
+		ppp->tty = 0;
+		ppp->sc_xfer = 0;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO "ppp: channel %s closing.\n",
+			       ppp2dev(ppp)->name);
 
-	if (ppp->sc_rc_state) {
-		(*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
-		ppp->sc_rc_state = NULL;
+		ppp_async_release(ppp);
+		ppp_release(ppp);
+		ppp->inuse = 0;
+		MOD_DEC_USE_COUNT;
 	}
 }
 
 /*
- * Called to release all of the information in the current PPP structure.
- *
- * It is called when the ppp device goes down or if it is unable to go
- * up.
+ * Read a PPP frame from the rcv_q list,
+ * waiting if necessary
  */
-
-static void
-ppp_release (struct ppp *ppp)
+static ssize_t
+ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
+	     size_t nr)
 {
-	struct tty_struct *tty;
-	struct device *dev;
-
-	CHECK_PPP_MAGIC(ppp);
-	tty = ppp2tty (ppp);
-	dev = ppp2dev (ppp);
-
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s released\n", ppp->name);
-
-	ppp_ccp_closed (ppp);
-
-	/* Ensure that the pppd process is not hanging on poll() */
-	wake_up_interruptible (&ppp->read_wait);
-	wake_up_interruptible (&ppp->write_wait);
-
-	if (tty != NULL && tty->disc_data == ppp)
-		tty->disc_data = NULL;	/* Break the tty->ppp link */
-
-	ppp_free_buf (ppp->rbuf);
-	ppp_free_buf (ppp->wbuf);
-	ppp_free_buf (ppp->cbuf);
-	ppp_free_buf (ppp->ubuf);
-	ppp_free_buf (ppp->tbuf);
-
-	ppp->rbuf  =
-	ppp->wbuf  =
-	ppp->cbuf  =
-	ppp->tbuf  =
-	ppp->xbuf  =
-	ppp->s1buf =
-	ppp->s2buf =
-	ppp->ubuf  = NULL;
-
-	if (ppp->slcomp) {
-		slhc_free (ppp->slcomp);
-		ppp->slcomp = NULL;
-	}
-
-	ppp->inuse = 0;
-	ppp->tty   = NULL;
-	ppp->backup_tty = NULL;
-}
+	struct ppp *ppp = tty2ppp (tty);
+	struct sk_buff *skb;
+	ssize_t len, err;
 
-/*
- * TTY callback.
- *
- * Called when the line discipline is changed to something
- * else, the tty is closed, or the tty detects a hangup.
- */
+	/*
+	 * Validate the pointers
+	 */
+	if (!ppp)
+		return -EIO;
+	CHECK_PPP(-ENXIO);
 
-static void
-ppp_tty_close (struct tty_struct *tty)
-{
-	struct ppp *ppp = tty2ppp (tty);
+	/*
+	 * Before we attempt to write the frame to the user, ensure that the
+	 * user has access to the pages for the total buffer length.
+	 */
+	err = verify_area(VERIFY_WRITE, buf, nr);
+	if (err != 0)
+		return (err);
 
-	if (ppp != NULL) {
-		if (ppp->magic != PPP_MAGIC) {
-			if (ppp->flags & SC_DEBUG)
-				printk (KERN_WARNING
-				       "ppp: trying to close unopened tty!\n");
-			return;
-		}
-		CHECK_PPP_VOID();
-		tty->disc_data = NULL;
-		if (tty == ppp->backup_tty)
-			ppp->backup_tty = 0;
-		if (tty != ppp->tty)
-			return;
-		if (ppp->backup_tty) {
-			ppp->tty = ppp->backup_tty;
-		} else {
-			ppp->sc_xfer = 0;
-			if (ppp->flags & SC_DEBUG)
-				printk (KERN_INFO "ppp: channel %s closing.\n",
-					ppp2dev(ppp)->name);
-			ppp_release (ppp);
-			MOD_DEC_USE_COUNT;
-		}
-	}
-}
+	/*
+	 * Wait for a frame to arrive if necessary.
+	 * We increment the module use count so that the module
+	 * can't go away while we're sleeping.
+	 */
+	MOD_INC_USE_COUNT;
+	skb = NULL;
+	for (;;) {
+		ppp = tty2ppp(tty);
+		err = 0;
+		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
+		    || tty != ppp->tty)
+			break;
 
-/*
- * TTY callback.
- *
- * Called when the tty discipline is switched to PPP.
- */
+		skb = skb_dequeue(&ppp->rcv_q);
+		if (skb != 0)
+			break;
 
-static int
-ppp_tty_open (struct tty_struct *tty)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	int indx;
-/*
- * There should not be an existing table for this slot.
- */
-	if (ppp) {
-		printk (KERN_ERR
-			"ppp_tty_open: gack! tty already associated to %s!\n",
-			ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name
-						: "unknown");
-		return -EEXIST;
-	}
-/*
- * Allocate the structure from the system
- */
-	ppp = ppp_find(current->pid);
-	if (ppp != NULL) {
 		/*
-		 * If we are taking over a ppp unit which is currently
-		 * connected to a loopback pty, there's not much to do.
+		 * If no frame is available, return -EAGAIN or wait.
 		 */
-		CHECK_PPP(-EINVAL);
-		tty->disc_data = ppp;
-		ppp->tty       = tty;
+		err = -EAGAIN;
+		if (file->f_flags & O_NONBLOCK)
+			break;
 
-	} else {
-		ppp = ppp_alloc();
-		if (ppp == NULL) {
-			printk (KERN_ERR "ppp_alloc failed\n");
-			return -ENFILE;
-		}
-/*
- * Initialize the control block
- */
-		ppp_init_ctrl_blk (ppp);
-		tty->disc_data = ppp;
-		ppp->tty       = tty;
-/*
- * Allocate space for the default VJ header compression slots
- */
-		ppp->slcomp = slhc_init (16, 16);
-		if (ppp->slcomp == NULL) {
-			printk (KERN_ERR "ppp_tty_open: "
-				"no space for compression buffers!\n");
-			ppp_release (ppp);
-			return -ENOMEM;
-		}
-/*
- * Allocate space for the MTU and MRU buffers
- */
-		if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) {
-			ppp_release (ppp);
-			return -ENOMEM;
-		}
-/*
- * Allocate space for a user level buffer
- */
-		ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD);
-		if (ppp->ubuf == NULL) {
-			printk (KERN_ERR "ppp_tty_open: "
-				"no space for user receive buffer\n");
-			ppp_release (ppp);
-			return -ENOMEM;
-		}
+		current->timeout = 0;
+		interruptible_sleep_on(&ppp->read_wait);
+		err = -EINTR;
+		if (signal_pending(current))
+			break;
+	}
+	MOD_DEC_USE_COUNT;
+	if (skb == 0)
+		return err;
 
+	/*
+	 * Ensure that the frame will fit within the caller's buffer.
+	 * If not, just discard the frame.
+	 */
+	len = skb->len;
+	if (len > nr) {
 		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO "ppp: channel %s open\n",
-				ppp2dev(ppp)->name);
-
-		for (indx = 0; indx < NUM_NP; ++indx)
-			ppp->sc_npmode[indx] = NPMODE_PASS;
-
-		MOD_INC_USE_COUNT;
+			printk(KERN_DEBUG
+			       "ppp: read of %lu bytes too small for %ld "
+			       "frame\n", (unsigned long) nr, (long) len);
+		ppp->stats.ppp_ierrors++;
+		err = -EOVERFLOW;
+		goto out;
 	}
-/*
- * Flush any pending characters in the driver and discipline.
- */
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer (tty);
 
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer (tty);
-	return (ppp->line);
-}
-
-/*
- * Local function to send the next portion of the buffer.
- *
- * Called by the tty driver's tty_wakeup function should it be entered
- * because the partial buffer was transmitted.
- *
- * Called by kick_tty to send the initial portion of the buffer.
- *
- * Completion processing of the buffer transmission is handled here.
- */
-
-static void
-ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty,
-		     struct ppp_buffer *xbuf)
-{
-	register int count, actual;
-	unsigned long flags;
+	/*
+	 * Copy the received data from the buffer to the caller's area.
+	 */
+	err = len;
+	if (copy_to_user(buf, skb->data, len))
+		err = -EFAULT;
 
-	CHECK_PPP_VOID();
-	CHECK_BUF_MAGIC(xbuf);
-/*
- * Prevent re-entrancy by ensuring that this routine is called only once.
- */
-	save_flags(flags);
-	cli ();
-	if (ppp->flags & SC_XMIT_BUSY) {
-		restore_flags(flags);
-		return;
-	}
-	ppp->flags |= SC_XMIT_BUSY;
-	restore_flags(flags);
-/*
- * Send the next block of data to the modem
- */
-	count = xbuf->count - xbuf->tail;
-	actual = tty->driver.write (tty, 0,
-				    buf_base (xbuf) + xbuf->tail, count);
-/*
- * Terminate transmission of any block which may have an error.
- * This could occur should the carrier drop.
- */
-	if (actual < 0) {
-		ppp->stats.ppp_oerrors++;
-		actual = count;
-	} else
-		ppp->bytes_sent += actual;
-/*
- * If the buffer has been transmitted then clear the indicators.
- */
-	xbuf->tail += actual;
-	if (actual == count) {
-		xbuf = NULL;
-		ppp->flags &= ~SC_XMIT_BUSY;
-/*
- * Complete the transmission on the current buffer.
- */
-		xbuf = ppp->xbuf;
-		if (xbuf != NULL) {
-			tty->flags  &= ~(1 << TTY_DO_WRITE_WAKEUP);
-			xbuf->locked = 0;
-			ppp->xbuf    = NULL;
-/*
- * If the completed buffer came from the device write, then complete the
- * transmission block.
- */
-			ppp2dev (ppp)->tbusy = 0;
-			mark_bh (NET_BH);
-/*
- * Wake up the transmission queue for all completion events.
- */
-			wake_up_interruptible (&ppp->write_wait);
-/*
- * Look at the priorities. Choose a daemon write over the device driver.
- */
-			save_flags(flags);
-			cli();
-			xbuf = ppp->s1buf;
-			ppp->s1buf = NULL;
-			if (xbuf == NULL) {
-				xbuf = ppp->s2buf;
-				ppp->s2buf = NULL;
-			}
-/*
- * If there is a pending buffer then transmit it now.
- */
-			if (xbuf != NULL) {
-				ppp->flags &= ~SC_XMIT_BUSY;
-				ppp_kick_tty (ppp, xbuf);
-				restore_flags(flags);
-				return;
-			}
-			restore_flags(flags);
-		}
-	}
-/*
- * Clear the re-entry flag
- */
-	save_flags(flags);	/* &=~ may not be atomic */
-	cli ();
-	ppp->flags &= ~SC_XMIT_BUSY;
-	restore_flags(flags);
+out:
+	kfree_skb(skb);
+	return err;
 }
 
 /*
- * This function is called by the tty driver when the transmit buffer has
- * additional space. It is used by the ppp code to continue to transmit
- * the current buffer should the buffer have been partially sent.
- *
- * In addition, it is used to send the first part of the buffer since the
- * logic and the inter-locking would be identical.
+ * Writing to a tty in ppp line discipline sends a PPP frame.
+ * Used by pppd to send control packets (LCP, etc.).
  */
-
-static void
-ppp_tty_wakeup (struct tty_struct *tty)
+static ssize_t
+ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data,
+	      size_t count)
 {
-	struct ppp_buffer *xbuf;
 	struct ppp *ppp = tty2ppp (tty);
+	__u8 *new_data;
+	struct sk_buff *skb;
 
+	/*
+	 * Verify the pointers.
+	 */
 	if (!ppp)
-		return;
-	CHECK_PPP_VOID();
+		return -EIO;
 
-	if (tty != ppp->tty) {
-		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-		return;
-	}
-/*
- * Ensure that there is a transmission pending. Clear the re-entry flag if
- * there is no pending buffer. Otherwise, send the buffer.
- */
-	xbuf = ppp->xbuf;
-	if (xbuf == NULL)
-		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-	else
-		ppp_tty_wakeup_code (ppp, tty, xbuf);
-}
+	if (ppp->magic != PPP_MAGIC)
+		return -EIO;
 
-/*
- * This function is called to transmit a buffer to the remote. The buffer
- * is placed on the pending queue if there is presently a buffer being
- * sent or it is transmitted with the aid of ppp_tty_wakeup.
- */
+	CHECK_PPP(-ENXIO);
 
-static void
-ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
-{
-	unsigned long flags;
+	/*
+	 * Ensure that the caller does not wish to send too much.
+	 */
+	if (count > PPP_MTU + PPP_HDRLEN) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_WARNING
+			       "ppp_tty_write: truncating user packet "
+			       "from %lu to mtu %d\n", (unsigned long) count,
+			       PPP_MTU + PPP_HDRLEN);
+		count = PPP_MTU + PPP_HDRLEN;
+	}
 
-	CHECK_PPP_VOID();
-	CHECK_BUF_MAGIC(xbuf);
-/*
- * Hold interrupts.
- */
-	save_flags (flags);
-	cli ();
-/*
- * Control the flags which are best performed with the interrupts masked.
- */
-	xbuf->locked	 = 1;
-	xbuf->tail	 = 0;
-/*
- * If the transmitter is busy then place the buffer on the appropriate
- * priority queue.
- */
-	if (ppp->xbuf != NULL) {
-		if (xbuf->type == BUFFER_TYPE_TTY_WR)
-			ppp->s1buf = xbuf;
-		else
-			ppp->s2buf = xbuf;
-		restore_flags (flags);
-		return;
+	/*
+	 * Allocate a buffer for the data and fetch it from the user space.
+	 */
+	skb = alloc_skb(count, GFP_KERNEL);
+	if (skb == NULL) {
+		printk(KERN_ERR "ppp_tty_write: no memory\n");
+		return 0;
 	}
-/*
- * If the transmitter is not busy then this is the highest priority frame
- */
-	ppp->flags	&= ~SC_XMIT_BUSY;
-	ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-	ppp->xbuf	 = xbuf;
-	restore_flags (flags);
-/*
- * Do the "tty wakeup_code" to actually send this buffer.
- */
-	ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf);
-}
+	new_data = skb_put(skb, count);
 
-/*************************************************************
- * TTY INPUT
- *    The following functions handle input that arrives from
- *    the TTY.	It recognizes PPP frames and either hands them
- *    to the network layer or queues them for delivery to a
- *    user process reading this TTY.
- *************************************************************/
+	/*
+	 * Retrieve the user's buffer
+	 */
+	if (copy_from_user(new_data, data, count)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
 
-/*
- * Callback function from tty driver. Return the amount of space left
- * in the receiver's buffer to decide if remote transmitter is to be
- * throttled.
- */
+	/*
+	 * Send the frame
+	 */
+	ppp_send_ctrl(ppp, skb);
 
-static int
-ppp_tty_room (struct tty_struct *tty)
-{
-	return 65536;	    /* We can handle an infinite amount of data. :-) */
+	return (ssize_t) count;
 }
 
 /*
- * Callback function when data is available at the tty driver.
+ * Process the IOCTL call for the tty device.
+ * Only the ioctls that relate to using ppp on async serial lines
+ * are processed here; the rest are handled by ppp_ioctl.
  */
-static void
-ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
-		 char *flags, int count)
+static int
+ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
+               unsigned int param2, unsigned long param3)
 {
-	register struct ppp *ppp = tty2ppp (tty);
-	register struct ppp_buffer *buf = NULL;
-	__u8 chr;
+	struct ppp *ppp = tty2ppp (tty);
+	register int temp_i = 0;
+	int error = -EFAULT;
 
-	if (ppp != 0)
-		CHECK_PPP_VOID();
 	/*
-	 * This can happen if stuff comes in on the backup tty.
+	 * Verify the status of the PPP device.
 	 */
-	if (ppp == 0 || tty != ppp->tty)
-		return;
-/*
- * Fetch the pointer to the buffer. Be careful about race conditions.
- */
-	buf = ppp->rbuf;
-	if (buf == NULL)
-		return;
-/*
- * Verify the table pointer and ensure that the line is
- * still in PPP discipline.
- */
-	if (ppp->magic != PPP_MAGIC) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"PPP: tty_receive called but couldn't find "
-				"PPP struct.\n");
-		return;
-	}
-	CHECK_PPP_VOID ();
-/*
- * Print the buffer if desired
- */
-	if (ppp->flags & SC_LOG_RAWIN)
-		ppp_print_buffer ("receive buffer", data, count);
+	if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
+		return -ENXIO;
 
-/*
- * Collect the character and error condition for the character. Set the toss
- * flag for the first character error.
- */
-	while (count-- > 0) {
-		ppp->bytes_rcvd++;
-		chr = *data++;
-		if (flags) {
-			if (*flags && ppp->toss == 0) {
-				ppp->toss = *flags;
-				switch (ppp->toss) {
-				case TTY_OVERRUN:
-					++ppp->estats.rx_fifo_errors;
-					break;
-				case TTY_FRAME:
-				case TTY_BREAK:
-					++ppp->estats.rx_frame_errors;
-					break;
-				}
-			}
-			++flags;
-		}
+	/*
+	 * The user must have an euid of root to do these requests.
+	 */
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
 
-/*
- * Set the flags for d7 being 0/1 and parity being even/odd so that
- * the normal processing would have all flags set at the end of the
- * session.  A missing flag bit indicates an error condition.
- */
+	switch (param2) {
+	case PPPIOCGASYNCMAP:
+		/*
+		 * Retrieve the transmit async map
+		 */
+		if (put_user(ppp->xmit_async_map[0], (int *) param3))
+			break;
+		error = 0;
+		break;
+
+	case PPPIOCSASYNCMAP:
+		/*
+		 * Set the transmit async map
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		ppp->xmit_async_map[0] = temp_i;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_tty_ioctl: set xmit asyncmap %x\n",
+			       ppp->xmit_async_map[0]);
+		error = 0;
+		break;
 
-#ifdef CHECK_CHARACTERS
-		if (chr & 0x80)
-			ppp->flags |= SC_RCV_B7_1;
-		else
-			ppp->flags |= SC_RCV_B7_0;
+	case PPPIOCSRASYNCMAP:
+		/*
+		 * Set the receive async map
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		ppp->recv_async_map = temp_i;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_tty_ioctl: set rcv asyncmap %x\n",
+			       ppp->recv_async_map);
+		error = 0;
+		break;
 
-		if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
-			ppp->flags |= SC_RCV_ODDP;
-		else
-			ppp->flags |= SC_RCV_EVNP;
-#endif
-/*
- * Branch on the character.
- */
-		switch (chr) {
-/*
- * FLAG. This is the end of the block. If the block terminated by ESC FLAG,
- * then the block is to be ignored. In addition, characters before the very
- * first FLAG are also tossed by this procedure.
- */
-		case PPP_FLAG:	/* PPP_FLAG: end of frame */
-			ppp->stats.ppp_ibytes += ppp->rbuf->count;
-			if (ppp->escape)
-				ppp->toss |= 0x80;
-/*
- * Process frames which are not to be ignored. If the processing failed,
- * then clean up the VJ tables.
- */
-			if (ppp_doframe (ppp) == 0) {
-				++ppp->stats.ppp_ierrors;
-				slhc_toss (ppp->slcomp);
-			}
-/*
- * Reset all indicators for the new frame to follow.
- */
-			buf->count  = 0;
-			buf->fcs    = PPP_INITFCS;
-			ppp->escape = 0;
-			ppp->toss   = 0;
+	case PPPIOCGXASYNCMAP:
+		/*
+		 * Get the map of characters to be escaped on transmission.
+		 */
+		if (copy_to_user((void *) param3, ppp->xmit_async_map,
+				 sizeof (ppp->xmit_async_map)))
 			break;
-/*
- * All other characters in the data come here. If the character is in the
- * receive mask then ignore the character.
- */
-		default:
-			/* If we're tossing, look no further. */
-			if (ppp->toss != 0)
-				break;
+		error = 0;
+		break;
 
-			/* If this is a control char to be ignored, do so */
-			if (in_rmap (ppp, chr))
-				break;
+	case PPPIOCSXASYNCMAP:
+		/*
+		 * Set the map of characters to be escaped on transmission.
+		 */
+		{
+			__u32 temp_tbl[8];
 
-			/*
-			 * Modify the next character if preceded by escape.
-			 * The escape character (0x7d) could be an escaped
-			 * 0x5d, if it follows an escape :-)
-			 */
-			if (ppp->escape) {
-				chr ^= PPP_TRANS;
-				ppp->escape = 0;
-			} else if (chr == PPP_ESCAPE) {
-				ppp->escape = PPP_TRANS;
+			if (copy_from_user(temp_tbl, (void *) param3,
+					   sizeof (temp_tbl)))
 				break;
-			}
 
-			/*
-			 * Decompress A/C and protocol compression here.
-			 */
-			if (buf->count == 0 && chr != PPP_ALLSTATIONS) {
-				buf_base(buf)[0] = PPP_ALLSTATIONS;
-				buf_base(buf)[1] = PPP_UI;
-				buf->count = 2;
-			}
-			if (buf->count == 2 && (chr & 1) != 0) {
-				buf_base(buf)[2] = 0;
-				buf->count = 3;
-			}
-/*
- * If the count sent is within reason then store the character, bump the
- * count, and update the FCS for the character.
- */
-			if (buf->count < buf->size) {
-				buf_base (buf)[buf->count++] = chr;
-				buf->fcs = PPP_FCS (buf->fcs, chr);
-				break;
-			}
-/*
- * The peer sent too much data. Set the flags to discard the current frame
- * and wait for the re-synchronization FLAG to be sent.
- */
-			++ppp->estats.rx_length_errors;
-			ppp->toss |= 0xC0;
-			break;
-		}
-	}
-}
+			temp_tbl[1]  =	0x00000000;
+			temp_tbl[2] &= ~0x40000000;
+			temp_tbl[3] |=	0x60000000;
 
-/* on entry, a received frame is in ppp->rbuf.bufr
-   check it and dispose as appropriate */
+			memcpy(ppp->xmit_async_map, temp_tbl,
+			       sizeof (ppp->xmit_async_map));
 
-static int
-ppp_doframe (struct ppp *ppp)
-{
-	__u8	*data = buf_base (ppp->rbuf);
-	int	count = ppp->rbuf->count;
-	int	proto;
-	int	new_count;
-	__u8 *new_data;
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_INFO
+				       "ppp_tty_ioctl: set xasyncmap\n");
+			error = 0;
+		}
+		break;
 
-	CHECK_PPP(0);
-	CHECK_BUF_MAGIC(ppp->rbuf);
+	case PPPIOCXFERUNIT:
+		/*
+		 * Set up this PPP unit to be used next time this
+		 * process sets a tty to PPP line discipline.
+		 */
+		ppp->backup_tty = tty;
+		ppp->sc_xfer = current->pid;
+		error = 0;
+		break;
 
-/*
- * If there is a pending error from the receiver then log it and discard
- * the damaged frame.
- */
-	if (ppp->toss) {
-		if ((ppp->flags & SC_DEBUG) && count > 0)
-			printk (KERN_DEBUG
-				"ppp_toss: tossing frame, reason = %x\n",
-				ppp->toss);
-		return 0;
-	}
-/*
- * An empty frame is ignored. This occurs if the FLAG sequence precedes and
- * follows each frame.
- */
-	if (count == 0)
-		return 1;
-/*
- * Generate an error if the frame is too small.
- */
-	if (count < PPP_HDRLEN + 2) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: got runt ppp frame, %d chars\n", count);
-		++ppp->estats.rx_length_errors;
-		return 0;
-	}
-/*
- * Verify the CRC of the frame and discard the CRC characters from the
- * end of the buffer.
- */
-	if (ppp->rbuf->fcs != PPP_GOODFCS) {
-		if (ppp->flags & SC_DEBUG) {
-			printk (KERN_DEBUG
-				"ppp: frame with bad fcs, length = %d\n",
-				count);
-			ppp_print_buffer("bad frame", data, count);
-		}
-		++ppp->estats.rx_crc_errors;
-		return 0;
-	}
-	count -= 2;		/* ignore the fcs characters */
-/*
- * Obtain the protocol from the frame
- */
-	proto = PPP_PROTOCOL(data);
-/*
- * Process the active decompressor.
- */
-	if ((ppp->sc_rc_state != (void *) 0) &&
-	    (ppp->flags & SC_DECOMP_RUN)     &&
-	    ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
-		if (proto == PPP_COMP) {
-/*
- * If the frame is compressed then decompress it.
- */
-			new_data = kmalloc (ppp->mru + PPP_HDRLEN, GFP_ATOMIC);
-			if (new_data == NULL) {
-				printk (KERN_ERR "ppp_doframe: no memory\n");
-				new_count = DECOMP_ERROR;
-			} else {
-				new_count = (*ppp->sc_rcomp->decompress)
-					(ppp->sc_rc_state, data, count,
-					 new_data, ppp->mru + PPP_HDRLEN);
-			}
-			switch (new_count) {
-			default:
-				ppp_doframe_lower (ppp, new_data, new_count);
-				kfree (new_data);
-				return 1;
+	case TCGETS:
+	case TCGETA:
+		/*
+		 * Allow users to read, but not set, the serial port parameters
+		 */
+		error = n_tty_ioctl (tty, file, param2, param3);
+		break;
 
-			case DECOMP_ERROR:
-				ppp->flags |= SC_DC_ERROR;
-				break;
+	case FIONREAD:
+		/*
+		 * Returns how many bytes are available for a read().
+		 */
+		{
+			unsigned long flags;
+			struct sk_buff *skb;
+			int count = 0;
 
-			case DECOMP_FATALERROR:
-				ppp->flags |= SC_DC_FERROR;
-				printk(KERN_ERR "ppp: fatal decomp error\n");
+			save_flags(flags);
+			cli();
+			skb = skb_peek(&ppp->rcv_q);
+			if (skb != 0)
+				count = skb->len;
+			restore_flags(flags);
+			if (put_user(count, (int *) param3))
 				break;
-			}
-/*
- * Log the error condition and discard the frame.
- */
-			if (new_data != 0)
-				kfree (new_data);
-			slhc_toss (ppp->slcomp);
-			++ppp->stats.ppp_ierrors;
-		} else {
-/*
- * The frame is not special. Pass it through the compressor without
- * actually compressing the data
- */
-			(*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
-						  data, count);
+			error = 0;
 		}
-	} else if (proto == PPP_COMP && (ppp->flags & SC_DEBUG)) {
-		printk(KERN_DEBUG "ppp: frame not decompressed: "
-		       "flags=%x, count=%d, sc_rc_state=%p\n",
-		       ppp->flags, count, ppp->sc_rc_state);
+		break;
+
+	default:
+		/*
+		 *  All other ioctl() events will come here.
+		 */
+		error = ppp_ioctl(ppp, param2, param3);
+		break;
 	}
+	return error;
+}
+
 /*
- * Process the uncompressed frame.
+ * TTY callback.
+ *
+ * Process the poll() statement for the PPP device.
  */
-	ppp_doframe_lower (ppp, data, count);
-	return 1;
-}
 
-static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
+static unsigned int
+ppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait)
 {
-	__u16		proto = PPP_PROTOCOL (data);
-	ppp_proto_type	*proto_ptr;
+	struct ppp *ppp = tty2ppp(tty);
+	unsigned int mask = 0;
 
-	CHECK_PPP_VOID();
-/*
- * Ignore empty frames
- */
-	if (count <= PPP_HDRLEN)
-		return;
-/*
- * Count the frame and print it
- */
-	++ppp->stats.ppp_ipackets;
-	if (ppp->flags & SC_LOG_INPKT)
-		ppp_print_buffer ("receive frame", data, count);
-/*
- * Find the procedure to handle this protocol. The last one is marked
- * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon.
- */
-	proto_ptr = proto_list;
-	while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
-		++proto_ptr;
-/*
- * Update the appropriate statistic counter.
- */
-	if ((*proto_ptr->func) (ppp, proto,
-				&data[PPP_HDRLEN],
-				count - PPP_HDRLEN))
-		ppp->stats.ppp_ioctects += count;
-	else
-		++ppp->stats.ppp_discards;
-}
+	if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
+		CHECK_PPP(0);
 
-/*
- * Put the input frame into the networking system for the indicated protocol
- */
+		poll_wait(filp, &ppp->read_wait, wait);
 
-static int
-ppp_rcv_rx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
-{
-	sk_buff *skb = dev_alloc_skb (count);
-/*
- * Generate a skb buffer for the new frame.
- */
-	if (skb == NULL) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_ERR
-			 "ppp_do_ip: packet dropped on %s (no memory)!\n",
-			 ppp2dev (ppp)->name);
-		return 0;
+		if (skb_peek(&ppp->rcv_q) != NULL)
+			mask |= POLLIN | POLLRDNORM;
+		if (tty->flags & (1 << TTY_OTHER_CLOSED)
+		    || tty_hung_up_p(filp))
+			mask |= POLLHUP;
+		mask |= POLLOUT | POLLWRNORM;
 	}
-/*
- * Move the received data from the input buffer to the skb buffer.
- */
-	skb->dev      = ppp2dev (ppp);	/* We are the device */
-	skb->protocol = proto;
-	skb->mac.raw  = skb_data(skb);
-	memcpy (skb_put(skb,count), data, count);	/* move data */
-/*
- * Tag the frame and kick it to the proper receive routine
- */
-	ppp->last_recv = jiffies;
-	netif_rx (skb);
-	return 1;
+	return mask;
 }
 
 /*
- * Process the receipt of an IP frame
+ * This function is called by the tty driver when the transmit buffer has
+ * additional space. It is used by the ppp code to continue to transmit
+ * the current buffer should the buffer have been partially sent.
  */
-
-static int
-rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+static void
+ppp_tty_wakeup (struct tty_struct *tty)
 {
-	CHECK_PPP(0);
-	if ((ppp2dev (ppp)->flags & IFF_UP) && (count > 0))
-		if (ppp->sc_npmode[NP_IP] == NPMODE_PASS)
-			return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count);
-	return 0;
+	struct ppp *ppp = tty2ppp (tty);
+
+	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	if (!ppp)
+		return;
+	CHECK_PPP_VOID();
+	if (tty != ppp->tty)
+		return;
+
+	if (ppp_tty_push(ppp))
+		ppp_output_wakeup(ppp);
 }
 
 /*
- * Process the receipt of an IPX frame
+ * Send a packet to the peer over an async tty line.
+ * Returns -1 iff the packet could not be accepted at present,
+ * 0 if the packet was accepted but we can't accept another yet, or
+ * 1 if we can accept another packet immediately.
+ * If this procedure returns 0, ppp_output_wakeup will be called
+ * exactly once.
  */
-
 static int
-rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+ppp_async_send(struct ppp *ppp, struct sk_buff *skb)
 {
 	CHECK_PPP(0);
-	if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0))
-		return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
-	return 0;
+
+	ppp_tty_push(ppp);
+
+	if (ppp->tpkt != NULL)
+		return -1;
+	ppp->tpkt = skb;
+	ppp->tpkt_pos = 0;
+
+	return ppp_tty_push(ppp);
 }
 
 /*
- * Process the receipt of an VJ Compressed frame
+ * Push as much data as possible out to the tty.
+ * Returns 1 if we finished encoding the current frame, 0 otherwise.
  */
-
 static int
-rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto,
-		    __u8 *data, int count)
+ppp_tty_push(struct ppp *ppp)
 {
+	int avail, sent, done = 0;
+	struct tty_struct *tty = ppp2tty(ppp);
+
 	CHECK_PPP(0);
-	if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
-		int new_count = slhc_uncompress (ppp->slcomp, data, count);
-		if (new_count >= 0) {
-			return rcv_proto_ip (ppp, PPP_IP, data, new_count);
+	if (ppp->tty_pushing)
+		return 0;
+	if (tty == NULL || tty->disc_data != (void *) ppp)
+		goto flush;
+	while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
+		ppp->tty_pushing = 1;
+		avail = ppp->olim - ppp->optr;
+		if (avail > 0) {
+			tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+			sent = tty->driver.write(tty, 0, ppp->optr, avail);
+			if (sent < 0)
+				goto flush;	/* error, e.g. loss of CD */
+			ppp->stats.ppp_obytes += sent;
+			ppp->optr += sent;
+			if (sent < avail) {
+				ppp->tty_pushing = 0;
+				return done;
+			}
 		}
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_NOTICE
-				"ppp: error in VJ decompression\n");
+		if (ppp->tpkt != 0)
+			done = ppp_async_encode(ppp);
+		ppp->tty_pushing = 0;
 	}
-	return 0;
+	return done;
+
+flush:
+	ppp->tty_pushing = 1;
+	ppp->stats.ppp_oerrors++;
+	if (ppp->tpkt != 0) {
+		kfree_skb(ppp->tpkt);
+		ppp->tpkt = 0;
+		done = 1;
+	}
+	ppp->optr = ppp->olim;
+	ppp->tty_pushing = 0;
+	return done;
 }
 
 /*
- * Process the receipt of an VJ Un-compressed frame
+ * Procedure to encode the data for async serial transmission.
+ * Does octet stuffing (escaping) and address/control
+ * and protocol compression.
+ * Assumes ppp->opkt != 0 on entry.
+ * Returns 1 if we finished the current frame, 0 otherwise.
  */
-
 static int
-rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto,
-		      __u8 *data, int count)
+ppp_async_encode(struct ppp *ppp)
 {
+	int fcs, i, count, c;
+	unsigned char *buf, *buflim;
+	unsigned char *data;
+	int islcp;
+
 	CHECK_PPP(0);
-	if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
-		if (slhc_remember (ppp->slcomp, data, count) > 0) {
-			return rcv_proto_ip (ppp, PPP_IP, data, count);
+
+	buf = ppp->obuf;
+	ppp->olim = buf;
+	ppp->optr = buf;
+	i = ppp->tpkt_pos;
+	data = ppp->tpkt->data;
+	count = ppp->tpkt->len;
+	fcs = ppp->tfcs;
+
+	/*
+	 * LCP packets with code values between 1 (configure-reqest)
+	 * and 7 (code-reject) must be sent as though no options
+	 * had been negotiated.
+	 */
+	islcp = PPP_PROTOCOL(data) == PPP_LCP
+		&& 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7;
+
+	if (i == 0) {
+		/*
+		 * Start of a new packet - insert the leading FLAG
+		 * character if necessary.
+		 */
+		if (islcp || flag_time == 0
+		    || jiffies - ppp->last_xmit >= flag_time)
+			*buf++ = PPP_FLAG;
+		/* only reset idle time for data packets */
+		if (PPP_PROTOCOL(data) < 0x8000)
+			ppp->last_xmit = jiffies;
+		fcs = PPP_INITFCS;
+		++ppp->stats.ppp_opackets;
+		ppp->stats.ppp_ooctects += count;
+
+		/*
+		 * Do address/control compression
+		 */
+		if ((ppp->flags & SC_COMP_AC) != 0 && !islcp
+		    && PPP_ADDRESS(data) == PPP_ALLSTATIONS
+		    && PPP_CONTROL(data) == PPP_UI)
+			i += 2;
+	}
+
+	/*
+	 * Once we put in the last byte, we need to put in the FCS
+	 * and closing flag, so make sure there is at least 7 bytes
+	 * of free space in the output buffer.
+	 */
+	buflim = buf + OBUFSIZE - 6;
+	while (i < count && buf < buflim) {
+		c = data[i++];
+		if (i == 3 && c == 0 && (ppp->flags & SC_COMP_PROT))
+			continue;	/* compress protocol field */
+		fcs = PPP_FCS(fcs, c);
+		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
+			*buf++ = PPP_ESCAPE;
+			c ^= 0x20;
 		}
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_NOTICE
-				"ppp: error in VJ memorizing\n");
+		*buf++ = c;
+	}
+
+	if (i == count) {
+		/*
+		 * We have finished the packet.  Add the FCS and flag.
+		 */
+		fcs = ~fcs;
+		c = fcs & 0xff;
+		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
+			*buf++ = PPP_ESCAPE;
+			c ^= 0x20;
+		}
+		*buf++ = c;
+		c = (fcs >> 8) & 0xff;
+		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
+			*buf++ = PPP_ESCAPE;
+			c ^= 0x20;
+		}
+		*buf++ = c;
+		*buf++ = PPP_FLAG;
+		ppp->olim = buf;
+
+		kfree_skb(ppp->tpkt);
+		ppp->tpkt = 0;
+		return 1;
 	}
+
+	/*
+	 * Remember where we are up to in this packet.
+	 */
+	ppp->olim = buf;
+	ppp->tpkt_pos = i;
+	ppp->tfcs = fcs;
 	return 0;
 }
 
 /*
- * Receive all unclassified protocols.
+ * Callback function from tty driver. Return the amount of space left
+ * in the receiver's buffer to decide if remote transmitter is to be
+ * throttled.
  */
-
 static int
-rcv_proto_unknown (struct ppp *ppp, __u16 proto,
-		   __u8 *data, int len)
+ppp_tty_room (struct tty_struct *tty)
 {
-	int totlen;
-	register int current_idx;
-
-#define PUTC(c)						 \
-{							 \
-    buf_base (ppp->ubuf) [current_idx++] = (__u8) (c); \
-    current_idx &= ppp->ubuf->size;			 \
-    if (current_idx == ppp->ubuf->tail)			 \
-	    goto failure;				 \
+	return 65536;	    /* We can handle an infinite amount of data. :-) */
 }
 
-	CHECK_PPP(0);
-/*
- * The total length includes the protocol data.
- * Lock the user information buffer.
- */
-	if (test_and_set_bit (0, &ppp->ubuf->locked)) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: rcv_proto_unknown: can't get lock\n");
-	} else {
-		CHECK_BUF_MAGIC(ppp->ubuf);
-		current_idx = ppp->ubuf->head;
 /*
- * Insert the buffer length (not counted), the protocol, and the data
+ * Callback function when data is available at the tty driver.
  */
-		totlen = len + 2;
-		PUTC (totlen >> 8);
-		PUTC (totlen);
+static void
+ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
+		 char *flags, int count)
+{
+	register struct ppp *ppp = tty2ppp (tty);
+	struct sk_buff *skb;
+	int chr, flg;
+	unsigned char *p;
 
-		PUTC (proto >> 8);
-		PUTC (proto);
+	if (ppp != 0)
+		CHECK_PPP_VOID();
+	/*
+	 * This can happen if stuff comes in on the backup tty.
+	 */
+	if (ppp == 0 || tty != ppp->tty)
+		return;
+	/*
+	 * Verify the table pointer and ensure that the line is
+	 * still in PPP discipline.
+	 */
+	if (ppp->magic != PPP_MAGIC) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "PPP: tty_receive called but couldn't find "
+			       "PPP struct.\n");
+		return;
+	}
+	/*
+	 * Print the buffer if desired
+	 */
+	if (ppp->flags & SC_LOG_RAWIN)
+		ppp_print_buffer ("receive buffer", data, count);
 
-		totlen -= 2;
-		while (totlen-- > 0) {
-			PUTC (*data++);
+	ppp->stats.ppp_ibytes += count;
+	skb = ppp->rpkt;
+	while (count-- > 0) {
+		/*
+		 * Collect the character and error condition for the character.
+		 * Set the toss flag for the first character error.
+		 */
+		chr = *data++;
+		if (flags) {
+			flg = *flags++;
+			if (flg) {
+				if (ppp->toss == 0)
+					ppp->toss = flg;
+				switch (flg) {
+				case TTY_OVERRUN:
+					++ppp->estats.rx_fifo_errors;
+					break;
+				case TTY_FRAME:
+				case TTY_BREAK:
+					++ppp->estats.rx_frame_errors;
+					break;
+				}
+				continue;
+			}
 		}
-#undef PUTC
-/*
- * The frame is complete. Update the head pointer and wakeup the pppd
- * process.
- */
-		ppp->ubuf->head = current_idx;
 
-		clear_bit (0, &ppp->ubuf->locked);
-		wake_up_interruptible (&ppp->read_wait);
-		if (ppp->tty->fasync != NULL)
-			kill_fasync (ppp->tty->fasync, SIGIO);
+		/*
+		 * Set the flags for d7 being 0/1 and parity being
+		 * even/odd so that the normal processing would have
+		 * all flags set at the end of the session.  A
+		 * missing flag bit indicates an error condition.
+		 */
 
-		return 1;
-/*
- * The buffer is full. Unlock the header
- */
-failure:
-		clear_bit (0, &ppp->ubuf->locked);
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: rcv_proto_unknown: buffer overflow\n");
-	}
-/*
- * Discard the frame. There are no takers for this protocol.
- */
-	if (ppp->flags & SC_DEBUG)
-		printk (KERN_DEBUG
-			"ppp: rcv_proto_unknown: dropping packet\n");
-	return 0;
-}
+#ifdef CHECK_CHARACTERS
+		if (chr & 0x80)
+			ppp->flags |= SC_RCV_B7_1;
+		else
+			ppp->flags |= SC_RCV_B7_0;
 
-/*
- * Handle a CCP packet.
- *
- * The CCP packet is passed along to the pppd process just like any
- * other PPP frame. The difference is that some processing needs to be
- * immediate or the compressors will become confused on the peer.
- */
+		if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
+			ppp->flags |= SC_RCV_ODDP;
+		else
+			ppp->flags |= SC_RCV_EVNP;
+#endif
 
-static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
-{
-	int slen    = CCP_LENGTH(dp);
-	__u8 *opt = dp	 + CCP_HDRLEN;
-	int opt_len = slen - CCP_HDRLEN;
-	unsigned long flags;
+		if (chr == PPP_FLAG) {
+			/*
+			 * FLAG. This is the end of the block. If the block
+			 * ends with ESC FLAG, then the block is to be ignored.
+			 */
+			if (ppp->escape)
+				ppp->toss |= 0x80;
+			/*
+			 * Process the frame if it was received correctly.
+			 * If there was an error, let the VJ decompressor know.
+			 * There are 4 cases here:
+			 * skb != NULL, toss != 0: error in frame
+			 * skb != NULL, toss == 0: frame ok
+			 * skb == NULL, toss != 0: very first frame,
+			 *	error on 1st char, or alloc_skb failed
+			 * skb == NULL, toss == 0: empty frame (~~)
+			 */
+			if (ppp->toss || !ppp_receive_frame(ppp, skb)) {
+				if (ppp->toss && (ppp->flags & SC_DEBUG))
+					printk(KERN_DEBUG
+					       "ppp: tossing frame (%x)\n",
+					       ppp->toss);
+				if (skb != NULL)
+					kfree_skb(skb);
+				if (!(ppp->toss == 0xE0 || ppp->toss == 0x80))
+					++ppp->stats.ppp_ierrors;
+				ppp_receive_error(ppp);
+			}
+			/*
+			 * Reset for the next frame.
+			 */
+			skb = NULL;
+			ppp->rfcs = PPP_INITFCS;
+			ppp->escape = 0;
+			ppp->toss = 0;
+			continue;
+		}
 
-	if (slen > len)
-		return;
+		/* If we're tossing, look no further. */
+		if (ppp->toss != 0)
+			continue;
 
-	save_flags(flags);
-	switch (CCP_CODE(dp)) {
-	case CCP_CONFREQ:
-	case CCP_TERMREQ:
-	case CCP_TERMACK:
-/*
- * CCP must be going down - disable compression
- */
-		if (ppp->flags & SC_CCP_UP) {
-			cli();
-			ppp->flags &= ~(SC_CCP_UP   |
-					SC_COMP_RUN |
-					SC_DECOMP_RUN);
-		}
-		break;
+		/* If this is a control char to be ignored, do so */
+		if (in_rmap(ppp, chr))
+			continue;
 
-	case CCP_CONFACK:
-		if ((ppp->flags & SC_CCP_OPEN) == 0)
-			break;
-		if (ppp->flags & SC_CCP_UP)
-			break;
-		if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
-			break;
-		if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
-			break;
-/*
- * we're agreeing to send compressed packets.
- */
-		if (!rcvd) {
-			if (ppp->sc_xc_state == NULL)
-				break;
+		/*
+		 * Modify the next character if preceded by escape.
+		 * The escape character (0x7d) could be an escaped
+		 * 0x5d, if it follows an escape :-)
+		 */
+		if (ppp->escape) {
+			chr ^= PPP_TRANS;
+			ppp->escape = 0;
+		} else if (chr == PPP_ESCAPE) {
+			ppp->escape = PPP_TRANS;
+			continue;
+		}
 
-			if ((*ppp->sc_xcomp->comp_init)
-			    (ppp->sc_xc_state,
-			     opt,
-			     opt_len,
-			     ppp2dev (ppp)->base_addr,
-			     0,
-			     ppp->flags & SC_DEBUG)) {
+		/*
+		 * Allocate an skbuff on the first character received.
+		 * The 128 is room for VJ header expansion and FCS.
+		 */
+		if (skb == NULL) {
+			skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+			if (skb == NULL) {
 				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: comp running\n",
-					       ppp->name);
-				cli();
-				ppp->flags |= SC_COMP_RUN;
+					printk(KERN_DEBUG "couldn't "
+					       "alloc skb for recv\n");
+				ppp->toss = 1;
+				continue;
 			}
-			break;
 		}
-/*
- * peer is agreeing to send compressed packets.
- */
-		if (ppp->sc_rc_state == NULL)
-			break;
 
-		if ((*ppp->sc_rcomp->decomp_init)
-		    (ppp->sc_rc_state,
-		     opt,
-		     opt_len,
-		     ppp2dev (ppp)->base_addr,
-		     0,
-		     ppp->mru,
-		     ppp->flags & SC_DEBUG)) {
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "%s: decomp running\n",
-				       ppp->name);
-			cli();
-			ppp->flags |= SC_DECOMP_RUN;
-			ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
+		/*
+		 * Decompress A/C and protocol compression here.
+		 */
+		if (skb->len == 0 && chr != PPP_ALLSTATIONS) {
+			p = skb_put(skb, 2);
+			p[0] = PPP_ALLSTATIONS;
+			p[1] = PPP_UI;
+		}
+		if (skb->len == 2 && (chr & 1) != 0) {
+			p = skb_put(skb, 1);
+			p[0] = 0;
 		}
-		break;
-/*
- * CCP Reset-ack resets compressors and decompressors as it passes through.
- */
-	case CCP_RESETACK:
-		if ((ppp->flags & SC_CCP_UP) == 0)
-			break;
 
-		if (!rcvd) {
-			if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) {
-				(*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: comp reset\n",
-					       ppp->name);
-			}
-		} else {
-			if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
-			      (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
-			      if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: decomp reset\n",
-					       ppp->name);
-			      cli();
-			      ppp->flags &= ~SC_DC_ERROR;
-			}
+		/*
+		 * Check if we've overflowed the MRU
+		 */
+		if (skb->len >= ppp->mru + PPP_HDRLEN + 2
+		    || skb_tailroom(skb) <= 0) {
+			++ppp->estats.rx_length_errors;
+			ppp->toss = 0xC0;
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "rcv frame too long: "
+				       "len=%d mru=%d hroom=%d troom=%d\n",
+				       skb->len, ppp->mru, skb_headroom(skb),
+				       skb_tailroom(skb));
+			continue;
 		}
-		break;
+
+		/*
+		 * Store the character and update the FCS.
+		 */
+		p = skb_put(skb, 1);
+		*p = chr;
+		ppp->rfcs = PPP_FCS(ppp->rfcs, chr);
 	}
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "ppp_proto_ccp: %s code %d, flags=%x\n",
-		       (rcvd? "rcvd": "sent"), CCP_CODE(dp), ppp->flags);
-	restore_flags(flags);
+	ppp->rpkt = skb;
 }
 
-static int
-rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len)
-{
-	CHECK_PPP(0);
-	ppp_proto_ccp (ppp, dp, len, 1);
-	return rcv_proto_unknown (ppp, proto, dp, len);
-}
+/*************************************************************
+ * PPP NETWORK INTERFACE SUPPORT
+ *	The following code implements the PPP network
+ *	interface device and handles those parts of
+ *	the PPP processing which are independent of the
+ *	type of hardware link being used, including
+ *	VJ and packet compression.
+ *************************************************************/
 
 /*
- * Handle a LQR packet.
+ * Network device driver callback routines
  */
 
-static int
-rcv_proto_lqr (struct ppp *ppp, __u16 proto, __u8 * data, int len)
-{
-	return rcv_proto_unknown (ppp, proto, data, len);
-}
+static int ppp_init_dev(struct device *dev);
+static int ppp_dev_open(struct device *);
+static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int ppp_dev_close(struct device *);
+static int ppp_dev_xmit(struct sk_buff *, struct device *);
+static struct net_device_stats *ppp_dev_stats (struct device *);
 
-/*************************************************************
- * LINE DISCIPLINE SUPPORT
- *    The following functions form support user programs
- *    which read and write data on a TTY with the PPP line
- *    discipline.  Reading is done from a circular queue,
- *    filled by the lower TTY levels.
- *************************************************************/
+/*
+ * Information for the protocol decoder
+ */
 
-/* read a PPP frame from the us_rbuff circular buffer,
-   waiting if necessary
-*/
+typedef int (*pfn_proto)  (struct ppp *, struct sk_buff *);
 
-static ssize_t
-ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf, size_t nr)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	__u8 c;
-	ssize_t len, ret;
+typedef struct ppp_proto_struct {
+	int		proto;
+	pfn_proto	func;
+} ppp_proto_type;
 
-#define GETC(c)						\
-{							\
-	c = buf_base (ppp->ubuf) [ppp->ubuf->tail++];	\
-	ppp->ubuf->tail &= ppp->ubuf->size;		\
-}
+static int rcv_proto_ip		(struct ppp *, struct sk_buff *);
+static int rcv_proto_ipx	(struct ppp *, struct sk_buff *);
+static int rcv_proto_at		(struct ppp *, struct sk_buff *);
+static int rcv_proto_vjc_comp	(struct ppp *, struct sk_buff *);
+static int rcv_proto_vjc_uncomp (struct ppp *, struct sk_buff *);
+static int rcv_proto_ccp	(struct ppp *, struct sk_buff *);
+static int rcv_proto_unknown	(struct ppp *, struct sk_buff *);
 
-/*
- * Validate the pointers
- */
-	if (!ppp)
-		return -EIO;
-	CHECK_PPP (-ENXIO);
+static
+ppp_proto_type proto_list[] = {
+	{ PPP_IP,	  rcv_proto_ip	       },
+	{ PPP_IPX,	  rcv_proto_ipx	       },
+	{ PPP_AT,	  rcv_proto_at	       },
+	{ PPP_VJC_COMP,	  rcv_proto_vjc_comp   },
+	{ PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
+	{ PPP_CCP,	  rcv_proto_ccp	       },
+	{ 0,		  rcv_proto_unknown    }  /* !!! MUST BE LAST !!! */
+};
 
 /*
- * Before we attempt to write the frame to the user, ensure that the
- * user has access to the pages for the total buffer length.
+ * Called when the PPP network interface device is actually created.
  */
-	if (verify_area (VERIFY_WRITE, buf, nr))
-		return -EFAULT;
+static int
+ppp_init_dev (struct device *dev)
+{
+	dev->hard_header_len  = PPP_HDRLEN;
 
-/*
- * Increment the module use count so that the module can't get unloaded
- * while we're sleeping below.  The problem is that ppp_tty_close() can
- * get called (as a result of a hangup from the tty) while we're sleeping.
- */
-	MOD_INC_USE_COUNT;
+	/* device INFO */
+	dev->mtu	      = PPP_MTU;
+	dev->hard_start_xmit  = ppp_dev_xmit;
+	dev->open	      = ppp_dev_open;
+	dev->stop	      = ppp_dev_close;
+	dev->get_stats	      = ppp_dev_stats;
+	dev->do_ioctl	      = ppp_dev_ioctl;
+	dev->addr_len	      = 0;
+	dev->tx_queue_len     = 10;
+	dev->type	      = ARPHRD_PPP;
 
-/*
- * Acquire the read lock.
- */
-	for (;;) {
-		ret = 0;
-		ppp = tty2ppp (tty);
-		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
-		    || tty != ppp->tty)
-			goto done;
+	dev_init_buffers(dev);
 
-		if (test_and_set_bit (0, &ppp->ubuf->locked) != 0) {
-			current->timeout = 0;
-			current->state	 = TASK_INTERRUPTIBLE;
-			schedule();
-
-			ret = -EINTR;
-			if (signal_pending(current))
-				goto done;
-			continue;
-		}
+	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
 
-/*
- * Fetch the length of the buffer from the first two bytes.
- */
-		if (ppp->ubuf->head == ppp->ubuf->tail)
-			len = 0;
-		else {
-			GETC (c);
-			len = c << 8;
-			GETC (c);
-			len += c;
-			if (len)
-				break;
-		}
+	return 0;
+}
 
 /*
- * If there is no length then wait for the data to arrive.
+ * Callback from the network layer when the device goes up.
  */
-		/* no data */
-		clear_bit (0, &ppp->ubuf->locked);
-		ret = -EAGAIN;
-		if (file->f_flags & O_NONBLOCK)
-			goto done;
-		current->timeout = 0;
-		interruptible_sleep_on (&ppp->read_wait);
-		ret = -EINTR;
-		if (signal_pending(current))
-			goto done;
-	}
 
-/*
- * Ensure that the frame will fit within the caller's buffer. If not, then
- * discard the frame from the input buffer.
- */
-	if (len + 2 > nr) {
-		/* Can't copy it, update us_rbuff_head */
+static int
+ppp_dev_open (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp(dev);
 
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: read of %lu bytes too small for %ld "
-				"frame\n", (unsigned long) nr, (long) len + 2);
-		ppp->stats.ppp_ierrors++;
-		ret = -EOVERFLOW;
-		goto out;
+	if (!ppp->inuse || ppp2tty(ppp) == NULL) {
+		printk(KERN_ERR "ppp: %s not active\n", dev->name);
+		return -ENXIO;
 	}
 
-/*
- * Fake the insertion of the ADDRESS and CONTROL information because these
- * were not saved in the buffer.
- */
-	ret = -EFAULT;
-	if (put_user((u_char) PPP_ALLSTATIONS, buf)
-	    || put_user((u_char) PPP_UI, buf+1))
-		goto out;
-	buf += 2;
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
 
 /*
- * Copy the received data from the buffer to the caller's area.
+ * Callback from the network layer when the ppp device goes down.
  */
-	nr = len + 2; 	/* Account for ADDRESS and CONTROL bytes */
-	while (len-- > 0) {
-		GETC (c);
-		if (put_user(c, buf))
-			goto out;
-		++buf;
-	}
-	ret = nr;
 
-out:
-	if (len > 0)
-		ppp->ubuf->tail = (ppp->ubuf->tail + len) & ppp->ubuf->size;
-	clear_bit(0, &ppp->ubuf->locked);
+static int
+ppp_dev_close (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp (dev);
+
+	CHECK_PPP_MAGIC(ppp);
 
-done:
 	MOD_DEC_USE_COUNT;
-	return ret;
-#undef GETC
-}
 
-/* stuff a character into the transmit buffer, using PPP's way of escaping
-   special characters.
-   also, update fcs to take account of new character */
+	return 0;
+}
 
-extern inline void
-ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
-		register __u8 chr)
+static inline void
+get_vj_stats(struct vjstat *vj, struct slcompress *slc)
 {
-/*
- * The buffer should not be full.
- */
-	if (ppp->flags & SC_DEBUG) {
-		if ((buf->count < 0) || (buf->count > 3000))
-			printk (KERN_DEBUG "ppp_stuff_char: %d %x\n",
-				(unsigned int) buf->count,
-				(unsigned int) chr);
-	}
-/*
- * Update the FCS and if the character needs to be escaped, do it.
- */
-	buf->fcs = PPP_FCS (buf->fcs, chr);
-	if (in_xmap (ppp, chr)) {
-		chr ^= PPP_TRANS;
-		ins_char (buf, PPP_ESCAPE);
-	}
-/*
- * Add the character to the buffer.
- */
-	ins_char (buf, chr);
+	vj->vjs_packets    = slc->sls_o_compressed + slc->sls_o_uncompressed;
+	vj->vjs_compressed = slc->sls_o_compressed;
+	vj->vjs_searches   = slc->sls_o_searches;
+	vj->vjs_misses     = slc->sls_o_misses;
+	vj->vjs_errorin    = slc->sls_i_error;
+	vj->vjs_tossed     = slc->sls_i_tossed;
+	vj->vjs_uncompressedin = slc->sls_i_uncompressed;
+	vj->vjs_compressedin   = slc->sls_i_compressed;
 }
 
 /*
- * Procedure to encode the data with the proper escaping and send the
- * data to the remote system.
+ * Callback from the network layer to process the sockioctl functions.
  */
-
-static void
-ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
-		    __u8 *data, int count, int non_ip)
+static int
+ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
 {
-	__u16   write_fcs;
-	int	address, control;
-	int	proto;
+	struct ppp *ppp = dev2ppp(dev);
+	int nb;
+	union {
+		struct ppp_stats stats;
+		struct ppp_comp_stats cstats;
+		char vers[32];
+	} u;
 
-	CHECK_PPP_VOID();
-	CHECK_BUF_MAGIC(buf);
-	++ppp->stats.ppp_opackets;
-	ppp->stats.ppp_ooctects += count;
+	CHECK_PPP_MAGIC(ppp);
 
-/*
- * Insert the leading FLAG character
- */
-	buf->count = 0;
+	memset(&u, 0, sizeof(u));
+	switch (cmd) {
+	case SIOCGPPPSTATS:
+		u.stats.p = ppp->stats;
+		if (ppp->slcomp != NULL)
+			get_vj_stats(&u.stats.vj, ppp->slcomp);
+		nb = sizeof(u.stats);
+		break;
 
-	if (non_ip || flag_time == 0)
-		ins_char (buf, PPP_FLAG);
-	else {
-		if (jiffies - ppp->last_xmit >= flag_time)
-			ins_char (buf, PPP_FLAG);
-	}
-	ppp->last_xmit = jiffies;
-	buf->fcs       = PPP_INITFCS;
-/*
- * Emit the address/control information if needed
- */
-	address = PPP_ADDRESS  (data);
-	control = PPP_CONTROL  (data);
-	proto	= PPP_PROTOCOL (data);
+	case SIOCGPPPCSTATS:
+		if (ppp->sc_xc_state != NULL)
+			(*ppp->sc_xcomp->comp_stat)
+				(ppp->sc_xc_state, &u.cstats.c);
+		if (ppp->sc_rc_state != NULL)
+			(*ppp->sc_rcomp->decomp_stat)
+				(ppp->sc_rc_state, &u.cstats.d);
+		nb = sizeof(u.cstats);
+		break;
 
-	if (address != PPP_ALLSTATIONS ||
-	    control != PPP_UI ||
-	    (ppp->flags & SC_COMP_AC) == 0) {
-		ppp_stuff_char (ppp, buf, address);
-		ppp_stuff_char (ppp, buf, control);
-	}
-/*
- * Emit the protocol (compressed if possible)
- */
-	if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00))
-		ppp_stuff_char (ppp, buf, proto >> 8);
+	case SIOCGPPPVER:
+		strcpy(u.vers, szVersion);
+		nb = strlen(u.vers) + 1;
+		break;
 
-	ppp_stuff_char (ppp, buf, proto);
-/*
- * Insert the data
- */
-	data  += 4;
-	count -= 4;
+	default:
+		return -EINVAL;
+	}
 
-	while (count-- > 0)
-		ppp_stuff_char (ppp, buf, *data++);
-/*
- * Add the trailing CRC and the final flag character
- */
-	write_fcs = buf->fcs ^ 0xFFFF;
-	ppp_stuff_char (ppp, buf, write_fcs);
-	ppp_stuff_char (ppp, buf, write_fcs >> 8);
-/*
- * Add the trailing flag character
- */
-	ins_char (buf, PPP_FLAG);
-/*
- * Send the block to the tty driver.
- */
-	ppp->stats.ppp_obytes += buf->count;
-	ppp_kick_tty (ppp, buf);
+	if (copy_to_user((void *) ifr->ifr_ifru.ifru_data, &u, nb))
+		return -EFAULT;
+	return 0;
 }
 
 /*
- * Compress and send an frame to the peer.
- *
- * Return 0 if frame was queued for transmission.
- *	  1 if frame must be re-queued for later driver support.
+ * Process the generic PPP ioctls, i.e. those which are not specific
+ * to any particular type of hardware link.
  */
-
 static int
-ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
-		    __u8 *data, int count)
+ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
 {
-	int	proto;
-	int	address, control;
-	__u8 *new_data;
-	int	new_count;
+	register int temp_i = 0, oldflags;
+	int error = -EFAULT;
+	unsigned long flags;
+	struct ppp_idle cur_ddinfo;
+	struct npioctl npi;
 
-	CHECK_PPP(0);
-	CHECK_BUF_MAGIC(buf);
-/*
- * Print the buffer
- */
-	if (ppp->flags & SC_LOG_OUTPKT)
-		ppp_print_buffer ("write frame", data, count);
-/*
- * Determine if the frame may be compressed. Attempt to compress the
- * frame if possible.
- */
-	proto	= PPP_PROTOCOL (data);
-	address = PPP_ADDRESS  (data);
-	control = PPP_CONTROL  (data);
+	CHECK_PPP(-ENXIO);
 
-	if (((ppp->flags & SC_COMP_RUN) != 0)	&&
-	    (ppp->sc_xc_state != (void *) 0)	&&
-	    (address == PPP_ALLSTATIONS)	&&
-	    (control == PPP_UI)			&&
-	    (proto != PPP_LCP)			&&
-	    (proto != PPP_CCP)) {
-		new_data = kmalloc (ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
-		if (new_data == NULL) {
-			printk (KERN_ERR "ppp_dev_xmit_frame: no memory\n");
-			return 1;
-		}
+	/*
+	 * The user must have an euid of root to do these requests.
+	 */
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
 
-		new_count = (*ppp->sc_xcomp->compress)
-		    (ppp->sc_xc_state, data, new_data, count,
-		     ppp->mtu + PPP_HDRLEN);
+	switch (param2) {
+	case PPPIOCSMRU:
+		/*
+		 * Set the MRU value
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		if (temp_i < PPP_MRU)
+			temp_i = PPP_MRU;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: set mru to %x\n", temp_i);
+		error = 0;
+		break;
 
-		if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
-			ppp_dev_xmit_lower (ppp, buf, new_data, new_count, 0);
-			kfree (new_data);
-			return 0;
-		}
-/*
- * The frame could not be compressed, or it could not be sent in
- * compressed form because CCP is not yet up.
- */
-		kfree (new_data);
-	}
-/*
- * Go to the escape encoding
- */
-	ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00));
-	return 0;
-}
+	case PPPIOCGFLAGS:
+		/*
+		 * Fetch the current flags
+		 */
+		temp_i = ppp->flags & SC_MASK;
+#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
+		temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 |
+			  SC_RCV_ODDP | SC_RCV_EVNP;
+#endif
+		if (put_user(temp_i, (int *) param3))
+			break;
+		error = 0;
+		break;
 
-/*
- * Revise the tty frame for specific protocols.
- */
+	case PPPIOCSFLAGS:
+		/*
+		 * Set the flags for the various options
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
 
-static int
-send_revise_frame (register struct ppp *ppp, __u8 *data, int len)
-{
-	__u8 *p;
+		if (ppp->flags & ~temp_i & SC_CCP_OPEN)
+			ppp_ccp_closed(ppp);
 
-	switch (PPP_PROTOCOL (data)) {
-/*
- * Update the LQR frame with the current MIB information. This saves having
- * the daemon read old MIB data from the driver.
- */
-	case PPP_LQR:
-		len = 48;			/* total size of this frame */
-		p   = (__u8 *) &data [40];	/* Point to last two items. */
-		p   = store_long (p, ppp->stats.ppp_opackets + 1);
-		p   = store_long (p, ppp->stats.ppp_ooctects + len);
+		save_flags(flags);
+		cli();
+		oldflags = ppp->flags;
+		temp_i = (temp_i & SC_MASK) | (oldflags & ~SC_MASK);
+		ppp->flags = temp_i;
+		restore_flags(flags);
+
+		if ((oldflags | temp_i) & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: set flags to %x\n", temp_i);
+		error = 0;
 		break;
-/*
- * Outbound compression frames
- */
-	case PPP_CCP:
-		ppp_proto_ccp (ppp,
-			       data + PPP_HDRLEN,
-			       len  - PPP_HDRLEN,
-			       0);
+
+	case PPPIOCSCOMPRESS:
+		/*
+		 * Set the compression mode
+		 */
+		error = ppp_set_compression
+			(ppp, (struct ppp_option_data *) param3);
 		break;
 
-	default:
+	case PPPIOCGUNIT:
+		/*
+		 * Obtain the unit number for this device.
+		 */
+		if (put_user(ppp->line, (int *) param3))
+			break;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: get unit: %d\n", ppp->line);
+		error = 0;
 		break;
-	}
 
-	return len;
-}
+	case PPPIOCSDEBUG:
+		/*
+		 * Set the debug level
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		temp_i = (temp_i & 0x1F) << 16;
 
-/*
- * write a frame with NR chars from BUF to TTY
- * we have to put the FCS field on ourselves
- */
+		if ((ppp->flags | temp_i) & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: set dbg flags to %x\n", temp_i);
 
-static ssize_t
-ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
-	       size_t count)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	__u8 *new_data;
-	int error;
-	struct wait_queue wait = {current, NULL};
+		save_flags(flags);
+		cli();
+		ppp->flags = (ppp->flags & ~0x1F0000) | temp_i;
+		restore_flags(flags);
+		error = 0;
+		break;
 
-/*
- * Verify the pointers.
- */
-	error = -EIO;
-	if (!ppp)
-		goto out;
-	if (ppp->magic != PPP_MAGIC)
-		goto out;
+	case PPPIOCGDEBUG:
+		/*
+		 * Get the debug level
+		 */
+		temp_i = (ppp->flags >> 16) & 0x1F;
+		if (put_user(temp_i, (int *) param3))
+			break;
+		error = 0;
+		break;
 
-	CHECK_PPP (-ENXIO);
-/*
- * Ensure that the caller does not wish to send too much.
- */
-	if (count > PPP_MTU + PPP_HDRLEN) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING
-				"ppp_tty_write: truncating user packet "
-				"from %lu to mtu %d\n", (unsigned long) count,
-				PPP_MTU + PPP_HDRLEN);
-		count = PPP_MTU + PPP_HDRLEN;
-	}
-/*
- * Allocate a buffer for the data and fetch it from the user space.
- */
-	new_data = kmalloc (count, GFP_KERNEL);
-	if (new_data == NULL) {
-		printk (KERN_ERR "ppp_tty_write: no memory\n");
-		return 0;
-	}
-/*
- * Retrieve the user's buffer
- */
-	error = -EFAULT;
-	if (copy_from_user(new_data, data, count))
-		goto out_free;
+	case PPPIOCGIDLE:
+		/*
+		 * Get the times since the last send/receive frame operation
+		 */
+		/* change absolute times to relative times. */
+		cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+		cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ;
+		if (copy_to_user((void *) param3, &cur_ddinfo,
+				 sizeof (cur_ddinfo)))
+			break;
+		error = 0;
+		break;
 
-/*
- * Increment the module use count so that the module can't get unloaded
- * while we're sleeping below.  The problem is that ppp_tty_close() can
- * get called (as a result of a hangup from the tty) while we're sleeping.
- */
-	MOD_INC_USE_COUNT;
+	case PPPIOCSMAXCID:
+		/*
+		 * Set the maximum VJ header compression slot number.
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		error = -EINVAL;
+		if (temp_i < 2 || temp_i > 255)
+			break;
+		++temp_i;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO "ppp_ioctl: set maxcid to %d\n",
+			       temp_i);
+		if (ppp->slcomp != NULL)
+			slhc_free(ppp->slcomp);
+		ppp->slcomp = slhc_init(16, temp_i);
 
-/*
- * Lock this PPP unit so we will be the only writer,
- * sleeping if necessary.
- *
- * Note that we add our task to the wait queue before
- * attempting to lock, as the lock flag may be cleared
- * from an interrupt.
- */
-	add_wait_queue(&ppp->write_wait, &wait);
-	while (1) {
+		error = -ENOMEM;
+		if (ppp->slcomp == NULL) {
+			printk(KERN_ERR "ppp: no memory for VJ compression\n");
+			break;
+		}
 		error = 0;
-		current->timeout = 0;
-		current->state = TASK_INTERRUPTIBLE;
-		if (lock_buffer(ppp->tbuf) == 0)
+		break;
+
+	case PPPIOCGNPMODE:
+	case PPPIOCSNPMODE:
+		if (copy_from_user(&npi, (void *) param3, sizeof(npi)))
 			break;
-		schedule();
 
-		error = -EINVAL;
-		ppp = tty2ppp (tty);
-		if (!ppp || ppp->magic != PPP_MAGIC || 
-		    !ppp->inuse || tty != ppp->tty) {
-			printk("ppp_tty_write: %p invalid after wait!\n", ppp);
+		switch (npi.protocol) {
+		case PPP_IP:
+			npi.protocol = NP_IP;
+			break;
+		case PPP_IPX:
+			npi.protocol = NP_IPX;
 			break;
+		case PPP_AT:
+			npi.protocol = NP_AT;
+			break;
+		default:
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "pppioc[gs]npmode: "
+				       "invalid proto %d\n", npi.protocol);
+			error = -EINVAL;
+			goto out;
+		}
+
+		if (param2 == PPPIOCGNPMODE) {
+			npi.mode = ppp->sc_npmode[npi.protocol];
+			if (copy_to_user((void *) param3, &npi, sizeof(npi)))
+				break;
+		} else {
+			ppp->sc_npmode[npi.protocol] = npi.mode;
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "ppp: set np %d to %d\n",
+				       npi.protocol, npi.mode);
+			mark_bh(NET_BH);
 		}
-		error = -EINTR;
-		if (signal_pending(current))
-			break;
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&ppp->write_wait, &wait);
-	if (error)
-		goto out_free_dec;
+		error = 0;
+		break;
 
-/*
- * Change the LQR frame
- */
-	count = send_revise_frame (ppp, new_data, count);
-/*
- * Send the data
- */
-	if (PPP_PROTOCOL(new_data) == PPP_IP) {
+	default:
 		/*
-		 * IP frames can be sent by pppd when we're doing
-		 * demand-dialling.  We send them via ppp_dev_xmit_ip
-		 * to make sure that VJ compression happens properly.
+		 *  All other ioctl() events will come here.
 		 */
-		ppp_dev_xmit_ip(ppp, ppp->tbuf, new_data + PPP_HDRLEN,
-				count - PPP_HDRLEN, NPMODE_PASS);
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_ERR
+			       "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+			       param2, param3);
 
-	} else {
-		ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
+		error = -ENOIOCTLCMD;
+		break;
 	}
-	error = count;
-
-out_free_dec:
-	MOD_DEC_USE_COUNT;
-out_free:
-	kfree (new_data);
 out:
 	return error;
 }
@@ -2166,7 +1572,6 @@
 /*
  * Process the set-compression ioctl.
  */
-
 static int
 ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
 {
@@ -2177,16 +1582,16 @@
 	__u8 ccp_option[CCP_MAX_OPTION_LENGTH];
 	struct ppp_option_data data;
 
-/*
- * Fetch the compression parameters
- */
+	/*
+	 * Fetch the compression parameters
+	 */
 	error = -EFAULT;
 	if (copy_from_user(&data, odp, sizeof (data)))
 		goto out;
 
 	nb  = data.length;
 	ptr = data.ptr;
-	if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH)
+	if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH)
 		nb = CCP_MAX_OPTION_LENGTH;
 
 	if (copy_from_user(ccp_option, ptr, nb))
@@ -2211,8 +1616,15 @@
 	}
 #endif /* CONFIG_KMOD */
 
-	if (cp == NULL)
-		goto out_no_comp;
+	if (cp == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "%s: no compressor for [%x %x %x], %x\n",
+			       ppp->name, ccp_option[0], ccp_option[1],
+			       ccp_option[2], nb);
+		goto out;		/* compressor not loaded */
+	}
+
 	/*
 	 * Found a handler for the protocol - try to allocate
 	 * a compressor or decompressor.
@@ -2226,12 +1638,11 @@
 		ppp->sc_xcomp	 = cp;
 		ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
 		if (ppp->sc_xc_state == NULL) {
-			printk(KERN_WARNING "%s: comp_alloc failed\n",
-				ppp->name);
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "%s: comp_alloc failed\n",
+				       ppp->name);
 			error = -ENOBUFS;
-		} else if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: comp_alloc -> %p\n",
-			       ppp->name, ppp->sc_xc_state);
+		}
 	} else {
 		if (ppp->sc_rc_state != NULL)
 			(*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
@@ -2240,761 +1651,817 @@
 		ppp->sc_rcomp	 = cp;
 		ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
 		if (ppp->sc_rc_state == NULL) {
-			printk(KERN_WARNING "%s: decomp_alloc failed\n",
-				ppp->name);
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "%s: decomp_alloc failed\n",
+				       ppp->name);
 			error = -ENOBUFS;
-		} else if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: decomp_alloc -> %p\n",
-			       ppp->name, ppp->sc_rc_state);
+		}
 	}
 out:
 	return error;
-
-out_no_comp:
-	error = -EINVAL;	/* no handler found */
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: no compressor for [%x %x %x], %x\n",
-			ppp->name, ccp_option[0], ccp_option[1],
-			ccp_option[2], nb);
-	goto out;
 }
 
 /*
- * Process the IOCTL event for the tty device.
+ * Handle a CCP packet.
+ *
+ * The CCP packet is passed along to the pppd process just like any
+ * other PPP frame. The difference is that some processing needs to be
+ * immediate or the compressors will become confused on the peer.
  */
 
-static int
-ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
-               unsigned int param2, unsigned long param3)
+static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd)
 {
-	struct ppp *ppp = tty2ppp (tty);
-	register int temp_i = 0, oldflags;
-	int error = 0;
+	int slen    = CCP_LENGTH(dp);
+	__u8 *opt = dp	 + CCP_HDRLEN;
+	int opt_len = slen - CCP_HDRLEN;
 	unsigned long flags;
-/*
- * Verify the status of the PPP device.
- */
-	if (!ppp)
-		return -EBADF;
 
-	if (ppp->magic != PPP_MAGIC)
-		return -EBADF;
-
-	CHECK_PPP (-ENXIO);
-/*
- * The user must have an euid of root to do these requests.
- */
-	if (!capable(CAP_NET_ADMIN))
-		return -EPERM;
-/*
- * Set the MRU value
- */
-	switch (param2) {
-	case PPPIOCSMRU:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
-			break;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set mru to %x\n", temp_i);
+	if (slen > len)
+		return;
 
-		if (ppp->mru != temp_i)
-			ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i);
-		break;
-/*
- * Fetch the flags
- */
-	case PPPIOCGFLAGS:
-		temp_i = (ppp->flags & SC_MASK);
-#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
-		temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 |
-			  SC_RCV_ODDP | SC_RCV_EVNP;
-#endif
-		error = put_user(temp_i, (int *) param3);
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n",
+		       rcvd, CCP_CODE(dp), ppp->flags);
+	save_flags(flags);
+	switch (CCP_CODE(dp)) {
+	case CCP_CONFREQ:
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		/*
+		 * CCP must be going down - disable compression
+		 */
+		if (ppp->flags & SC_CCP_UP) {
+			cli();
+			ppp->flags &= ~(SC_CCP_UP   |
+					SC_COMP_RUN |
+					SC_DECOMP_RUN);
+		}
 		break;
-/*
- * Set the flags for the various options
- */
-	case PPPIOCSFLAGS:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
-			break;
-		temp_i &= SC_MASK;
 
-		if ((ppp->flags & SC_CCP_OPEN) && (temp_i & SC_CCP_OPEN) == 0)
-			ppp_ccp_closed(ppp);
-
-		save_flags(flags);
-		cli();
-		oldflags = ppp->flags;
-		ppp->flags = temp_i |= (ppp->flags & ~SC_MASK);
-		restore_flags(flags);
-		if ((oldflags | temp_i) & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set flags to %x\n", temp_i);
-		break;
-/*
- * Set the compression mode
- */
-	case PPPIOCSCOMPRESS:
-		error = ppp_set_compression (ppp,
-					    (struct ppp_option_data *) param3);
-		break;
-/*
- * Retrieve the transmit async map
- */
-	case PPPIOCGASYNCMAP:
-		error = put_user(ppp->xmit_async_map[0], (int *) param3);
-		break;
-/*
- * Set the transmit async map
- */
-	case PPPIOCSASYNCMAP:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+	case CCP_CONFACK:
+		if ((ppp->flags & SC_CCP_OPEN) == 0)
 			break;
-		ppp->xmit_async_map[0] = temp_i;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set xmit asyncmap %x\n",
-				ppp->xmit_async_map[0]);
-		break;
-/*
- * Set the receive async map
- */
-	case PPPIOCSRASYNCMAP:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+		if (ppp->flags & SC_CCP_UP)
 			break;
-		ppp->recv_async_map = temp_i;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set rcv asyncmap %x\n",
-				ppp->recv_async_map);
-		break;
-/*
- * Obtain the unit number for this device.
- */
-	case PPPIOCGUNIT:
-		error = put_user(ppp2dev (ppp)->base_addr, (int *) param3);
-		if (error != 0)
+		if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
 			break;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: get unit: %ld\n",
-				ppp2dev (ppp)->base_addr);
-		break;
-/*
- * Set the debug level
- */
-	case PPPIOCSDEBUG:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+		if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
 			break;
-		temp_i	= (temp_i & 0x1F) << 16;
-		temp_i |= (ppp->flags & ~0x1F0000);
-
-		if ((ppp->flags | temp_i) & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set flags to %x\n", temp_i);
-		ppp->flags = temp_i;
-		break;
-/*
- * Get the debug level
- */
-	case PPPIOCGDEBUG:
-		temp_i = (ppp->flags >> 16) & 0x1F;
-		error = put_user(temp_i, (int *) param3);
-		break;
-/*
- * Get the times since the last send/receive frame operation
- */
-	case PPPIOCGIDLE:
-		{
-			struct ppp_idle cur_ddinfo;
-
-			/* change absolute times to relative times. */
-			cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-			cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ;
-			error = -EFAULT;
-			if (!copy_to_user((void *) param3, &cur_ddinfo,
-					     sizeof (cur_ddinfo)))
-				error = 0;
-		}
-		break;
-/*
- * Retrieve the extended async map
- */
-	case PPPIOCGXASYNCMAP:
-		error = -EFAULT;
-		if (!copy_to_user((void *) param3, ppp->xmit_async_map,
-				     sizeof (ppp->xmit_async_map)))
-			error = 0;
-		break;
-/*
- * Set the async extended map
- */
-	case PPPIOCSXASYNCMAP:
-		{
-			__u32 temp_tbl[8];
-
-			error = -EFAULT;
-			if (copy_from_user(temp_tbl, (void *) param3,
-					       sizeof (temp_tbl)))
+		if (!rcvd) {
+			/*
+			 * we're agreeing to send compressed packets.
+			 */
+			if (ppp->sc_xc_state == NULL)
 				break;
 
-			temp_tbl[1]  =	0x00000000;
-			temp_tbl[2] &= ~0x40000000;
-			temp_tbl[3] |=	0x60000000;
-
-			error = 0;
-			if ((temp_tbl[2] & temp_tbl[3]) != 0 ||
-			    (temp_tbl[4] & temp_tbl[5]) != 0 ||
-			    (temp_tbl[6] & temp_tbl[7]) != 0)
-				error = -EINVAL;
-			else {
-				memcpy (ppp->xmit_async_map,
-					temp_tbl,
-					sizeof (ppp->xmit_async_map));
-
+			if ((*ppp->sc_xcomp->comp_init)
+			    (ppp->sc_xc_state,
+			     opt, opt_len,
+			     ppp->line, 0, ppp->flags & SC_DEBUG)) {
 				if (ppp->flags & SC_DEBUG)
-					printk (KERN_INFO
-						"ppp_tty_ioctl: set xasyncmap\n");
+					printk(KERN_DEBUG "%s: comp running\n",
+					       ppp->name);
+				cli();
+				ppp->flags |= SC_COMP_RUN;
 			}
+			break;
 		}
-		break;
-/*
- * Set the maximum VJ header compression slot number.
- */
-	case PPPIOCSMAXCID:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+
+		/*
+		 * peer is agreeing to send compressed packets.
+		 */
+		if (ppp->sc_rc_state == NULL)
 			break;
-		temp_i = (temp_i & 255) + 1;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set maxcid to %d\n", temp_i);
-		if (ppp->slcomp != NULL)
-			slhc_free (ppp->slcomp);
-		ppp->slcomp = NULL;
 
-		ppp->slcomp = slhc_init (16, temp_i);
-		if (ppp->slcomp == NULL) {
-			printk (KERN_ERR "ppp_tty_ioctl: "
-				"no space for compression buffers!\n");
-			ppp_release (ppp);
-			error = -ENOMEM;
+		if ((*ppp->sc_rcomp->decomp_init)
+		    (ppp->sc_rc_state,
+		     opt, opt_len,
+		     ppp->line, 0, ppp->mru, ppp->flags & SC_DEBUG)) {
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "%s: decomp running\n",
+				       ppp->name);
+			cli();
+			ppp->flags |= SC_DECOMP_RUN;
+			ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
 		}
 		break;
 
-    case PPPIOCXFERUNIT:
-		ppp->backup_tty = tty;
-		ppp->sc_xfer = current->pid;
-		break;
-
-    case PPPIOCGNPMODE:
-    case PPPIOCSNPMODE:
-		{
-			struct npioctl npi;
-
-			error = -EFAULT;
-			if (copy_from_user(&npi, (void *) param3, sizeof(npi)))
-				break;
+	case CCP_RESETACK:
+		/*
+		 * CCP Reset-ack resets compressors and decompressors
+		 * as it passes through.
+		 */
+		if ((ppp->flags & SC_CCP_UP) == 0)
+			break;
 
-			if (npi.protocol != PPP_IP) {
+		if (!rcvd) {
+			if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) {
+				(*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
 				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "pppioc[gs]npmode: "
-					       "invalid protocol %d\n",
-						npi.protocol);
-				error = -EINVAL;
-				break;
+					printk(KERN_DEBUG "%s: comp reset\n",
+					       ppp->name);
 			}
-			npi.protocol = NP_IP;
-
-			if (param2 == PPPIOCGNPMODE) {
-				npi.mode = ppp->sc_npmode[npi.protocol];
-				if (copy_to_user((void *) param3, &npi,
-						sizeof (npi)))
-					break;
+		} else {
+			if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
+			      (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
+			      if (ppp->flags & SC_DEBUG)
+					printk(KERN_DEBUG "%s: decomp reset\n",
+					       ppp->name);
+			      cli();
+			      ppp->flags &= ~SC_DC_ERROR;
 			}
-
-			ppp->sc_npmode[npi.protocol] = npi.mode;
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "ppp: set np %d to %d\n",
-				       npi.protocol, npi.mode);
-			/* N.B. Why is the busy flag cleared here? */
-			ppp2dev(ppp)->tbusy = 0;
-			mark_bh(NET_BH);
-			error = 0;
-		}
-		break;
-/*
- * Allow users to read, but not set, the serial port parameters
- */
-	case TCGETS:
-	case TCGETA:
-		error = n_tty_ioctl (tty, file, param2, param3);
-		break;
-
-	case FIONREAD:
-		{
-			int count = ppp->ubuf->tail - ppp->ubuf->head;
-			if (count < 0)
-				count += (ppp->ubuf->size + 1);
-			error = put_user(count, (int *) param3);
 		}
 		break;
-/*
- *  All other ioctl() events will come here.
- */
-	default:
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING "ppp_tty_ioctl: "
-				"invalid ioctl=%x, addr=%lx\n", param2, param3);
-		error = -ENOIOCTLCMD;
-		break;
 	}
-	return error;
+	restore_flags(flags);
 }
 
 /*
- * TTY callback.
- *
- * Process the poll() statement for the PPP device.
+ * CCP is down; free (de)compressor state if necessary.
  */
 
-static unsigned int
-ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait)
+static void
+ppp_ccp_closed(struct ppp *ppp)
 {
-	struct ppp *ppp = tty2ppp (tty);
-	unsigned int mask = 0;
-
-	if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
-		CHECK_PPP (0);
+	unsigned long flags;
 
-		poll_wait(filp, &ppp->read_wait, wait);
-		poll_wait(filp, &ppp->write_wait, wait);
+	save_flags(flags);
+	cli();
+	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
+	restore_flags(flags);
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "%s: ccp closed\n", ppp->name);
+	if (ppp->sc_xc_state) {
+		(*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
+		ppp->sc_xc_state = NULL;
+	}
 
-		/* Must lock the user buffer area while checking. */
-		CHECK_BUF_MAGIC(ppp->ubuf);
-		if(test_and_set_bit(0, &ppp->ubuf->locked) == 0) {
-			if(ppp->ubuf->head != ppp->ubuf->tail)
-				mask |= POLLIN | POLLRDNORM;
-			clear_bit(0, &ppp->ubuf->locked);
-		}
-		if(tty->flags & (1 << TTY_OTHER_CLOSED))
-			mask |= POLLHUP;
-		if(tty_hung_up_p(filp))
-			mask |= POLLHUP;
-		if(ppp->tbuf->locked == 0)
-			mask |= POLLOUT | POLLWRNORM;
+	if (ppp->sc_rc_state) {
+		(*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
+		ppp->sc_rc_state = NULL;
 	}
-	return mask;
 }
 
 /*************************************************************
- * NETWORK OUTPUT
- *    This routine accepts requests from the network layer
- *    and attempts to deliver the packets.
- *    It also includes various routines we are compelled to
- *    have to make the network layer work (arp, etc...).
+ * RECEIVE-SIDE ROUTINES
  *************************************************************/
 
 /*
- * Callback from the network layer when the device goes up.
+ * On entry, a received frame is in skb.
+ * Check it and dispose as appropriate.
  */
-
 static int
-ppp_dev_open (struct device *dev)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
 {
-	struct ppp *ppp = dev2ppp (dev);
+	__u8	*data;
+	int	count;
+	int	proto;
+	int	new_count;
+	struct sk_buff *new_skb;
+	ppp_proto_type	*proto_ptr;
 
-	if (ppp2tty (ppp) == NULL) {
-		printk (KERN_ERR
-			"ppp: %s not connected to a TTY! can't go open!\n",
-			dev->name);
-		return -ENXIO;
+	/*
+	 * An empty frame is ignored. This occurs if the FLAG sequence
+	 * precedes and follows each frame.
+	 */
+	if (skb == NULL)
+		return 1;
+	if (skb->len == 0) {
+		kfree_skb(skb);
+		return 1;
 	}
+	data = skb->data;
+	count = skb->len;
 
-	if (ppp->flags & SC_DEBUG)
-		printk (KERN_INFO
-			"ppp: channel %s going up for IP packets!\n",
-			dev->name);
+	/*
+	 * Generate an error if the frame is too small.
+	 */
+	if (count < PPP_HDRLEN + 2) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "ppp: got runt ppp frame, %d chars\n", count);
+		++ppp->estats.rx_length_errors;
+		return 0;
+	}
 
-	CHECK_PPP (-ENXIO);
-	MOD_INC_USE_COUNT;
-	return 0;
+	/*
+	 * Verify the FCS of the frame and discard the FCS characters
+	 * from the end of the buffer.
+	 */
+	if (ppp->rfcs != PPP_GOODFCS) {
+		if (ppp->flags & SC_DEBUG) {
+			printk(KERN_DEBUG
+			       "ppp: frame with bad fcs, length = %d\n",
+			       count);
+			ppp_print_buffer("bad frame", data, count);
+		}
+		++ppp->estats.rx_crc_errors;
+		return 0;
+	}
+	count -= 2;		/* ignore the fcs characters */
+	skb_trim(skb, count);
+
+	/*
+	 * Process the active decompressor.
+	 */
+	if (ppp->sc_rc_state != NULL &&
+	    (ppp->flags & SC_DECOMP_RUN) &&
+	    ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
+		if (PPP_PROTOCOL(data) == PPP_COMP) {
+			/*
+			 * If the frame is compressed then decompress it.
+			 */
+			new_skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+			if (new_skb == NULL) {
+				printk(KERN_ERR "ppp_recv_frame: no memory\n");
+				new_count = DECOMP_ERROR;
+			} else {
+				new_count = (*ppp->sc_rcomp->decompress)
+					(ppp->sc_rc_state, data, count,
+					 new_skb->data, ppp->mru + PPP_HDRLEN);
+			}
+			if (new_count > 0) {
+				/* Frame was decompressed OK */
+				kfree_skb(skb);
+				skb = new_skb;
+				count = new_count;
+				data = skb_put(skb, count);
+
+			} else {
+				/*
+				 * On a decompression error, we pass the
+				 * compressed frame up to pppd as an
+				 * error indication.
+				 */
+				if (ppp->flags & SC_DEBUG)
+					printk(KERN_INFO "%s: decomp err %d\n",
+					       ppp->name, new_count);
+				if (new_skb != 0)
+					kfree_skb(new_skb);
+				if (ppp->slcomp != 0)
+					slhc_toss(ppp->slcomp);
+				++ppp->stats.ppp_ierrors;
+				if (new_count == DECOMP_FATALERROR) {
+					ppp->flags |= SC_DC_FERROR;
+				} else {
+					ppp->flags |= SC_DC_ERROR;
+				}
+			}
+
+
+		} else {
+			/*
+			 * The frame is not compressed. Pass it to the
+			 * decompression code so it can update its
+			 * dictionary if necessary.
+			 */
+			(*ppp->sc_rcomp->incomp)(ppp->sc_rc_state,
+						 data, count);
+		}
+	}
+	else if (PPP_PROTOCOL(data) == PPP_COMP && (ppp->flags & SC_DEBUG))
+		printk(KERN_INFO "%s: not decomp, rc_state=%p flags=%x\n",
+		       ppp->name, ppp->sc_rc_state, ppp->flags);
+
+	/*
+	 * Count the frame and print it
+	 */
+	++ppp->stats.ppp_ipackets;
+	ppp->stats.ppp_ioctects += count;
+	if (ppp->flags & SC_LOG_INPKT)
+		ppp_print_buffer ("receive frame", data, count);
+
+	/*
+	 * Find the procedure to handle this protocol.
+	 * The last one is marked as protocol 0 which is the 'catch-all'
+	 * to feed it to the pppd daemon.
+	 */
+	proto = PPP_PROTOCOL(data);
+	proto_ptr = proto_list;
+	while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
+		++proto_ptr;
+
+	/*
+	 * Update the appropriate statistic counter.
+	 */
+	if (!(*proto_ptr->func)(ppp, skb)) {
+		kfree_skb(skb);
+		++ppp->stats.ppp_discards;
+	}
+
+	return 1;
 }
 
 /*
- * Callback from the network layer when the ppp device goes down.
+ * An input error has been detected, so we need to inform
+ * the VJ decompressor.
  */
+static void
+ppp_receive_error(struct ppp *ppp)
+{
+	CHECK_PPP_VOID();
 
+	if (ppp->slcomp != 0)
+		slhc_toss(ppp->slcomp);
+}
+
+/*
+ * Put the input frame into the networking system for the indicated protocol
+ */
 static int
-ppp_dev_close (struct device *dev)
+ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb)
 {
-	struct ppp *ppp = dev2ppp (dev);
 
-	MOD_DEC_USE_COUNT;
-	if (ppp2tty (ppp) == NULL) {
-		return -ENXIO;
-	}
+	/*
+	 * Fill in a few fields of the skb and give it to netif_rx().
+	 */
+	skb->dev      = ppp2dev(ppp);	/* We are the device */
+	skb->protocol = htons(proto);
+	skb->mac.raw  = skb->data;
+	skb_pull(skb, PPP_HDRLEN);	/* pull off ppp header */
+	ppp->last_recv = jiffies;
+	netif_rx (skb);
+	return 1;
+}
+
 /*
- * We don't do anything about the device going down. It is not important
- * for us.
+ * Process the receipt of an IP frame
  */
-	if (ppp->flags & SC_DEBUG)
-		printk (KERN_INFO
-			"ppp: channel %s going down for IP packets!\n",
-			dev->name);
-	CHECK_PPP (-ENXIO);
+static int
+rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
+	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_IP] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_IP, skb);
 	return 0;
 }
 
 /*
- * IOCTL operation to read the version of the driver.
+ * Process the receipt of an IPX frame
  */
-
 static int
-ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr)
+rcv_proto_ipx(struct ppp *ppp, struct sk_buff *skb)
 {
-	int error;
-	char *result  = (char *) ifr->ifr_ifru.ifru_data;
-	int  len      = strlen (szVersion) + 1;
-/*
- * Move the version data
- */
-	error = -EFAULT;
-	if (!copy_to_user(result, szVersion, len))
-		error = 0;
-	return error;
+	CHECK_PPP(0);
+	if (((ppp2dev(ppp)->flags & IFF_UP) != 0) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_IPX] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_IPX, skb);
+	return 0;
 }
 
 /*
- * IOCTL to read the statistics for the pppstats program.
+ * Process the receipt of an Appletalk frame
  */
-
 static int
-ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+rcv_proto_at(struct ppp *ppp, struct sk_buff *skb)
 {
-	struct ppp_stats *result, temp;
-	int    error;
+	CHECK_PPP(0);
+	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_AT] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_PPPTALK, skb);
+	return 0;
+}
+
 /*
- * Supply the information for the caller. First move the version data
- * then move the ppp stats; and finally the vj stats.
+ * Process the receipt of an VJ Compressed frame
  */
-	memset (&temp, 0, sizeof(temp));
-	if (dev->flags & IFF_UP) {
-		memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat));
-		if (ppp->slcomp != NULL) {
-			temp.vj.vjs_packets    = ppp->slcomp->sls_o_compressed+
-						 ppp->slcomp->sls_o_uncompressed;
-			temp.vj.vjs_compressed = ppp->slcomp->sls_o_compressed;
-			temp.vj.vjs_searches   = ppp->slcomp->sls_o_searches;
-			temp.vj.vjs_misses     = ppp->slcomp->sls_o_misses;
-			temp.vj.vjs_errorin    = ppp->slcomp->sls_i_error;
-			temp.vj.vjs_tossed     = ppp->slcomp->sls_i_tossed;
-			temp.vj.vjs_uncompressedin = ppp->slcomp->sls_i_uncompressed;
-			temp.vj.vjs_compressedin   = ppp->slcomp->sls_i_compressed;
-		}
-	}
-
-	result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+static int
+rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb)
+{
+	int new_count;
 
-	error = -EFAULT;
-	if (!copy_to_user(result, &temp, sizeof (temp)))
-		error = 0;
-	return error;
+	CHECK_PPP(0);
+	if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
+		return 0;
+	new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN,
+				    skb->len - PPP_HDRLEN);
+	if (new_count < 0) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_NOTICE
+			       "ppp: error in VJ decompression\n");
+		return 0;
+	}
+	skb_put(skb, new_count + PPP_HDRLEN - skb->len);
+	return rcv_proto_ip(ppp, skb);
 }
 
 /*
- * IOCTL to read the compression statistics for the pppstats program.
+ * Process the receipt of an VJ Un-compressed frame
  */
+static int
+rcv_proto_vjc_uncomp(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
+	if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
+		return 0;
+	if (slhc_remember(ppp->slcomp, skb->data + PPP_HDRLEN,
+			  skb->len - PPP_HDRLEN) <= 0) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_NOTICE "ppp: error in VJ memorizing\n");
+		return 0;
+	}
+	return rcv_proto_ip(ppp, skb);
+}
 
 static int
-ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+rcv_proto_ccp(struct ppp *ppp, struct sk_buff *skb)
 {
-	struct ppp_comp_stats *result, temp;
-	int    error;
+	CHECK_PPP(0);
+	ppp_proto_ccp (ppp, skb->data + PPP_HDRLEN, skb->len - PPP_HDRLEN, 1);
+	return rcv_proto_unknown(ppp, skb);
+}
+
 /*
- * Supply the information for the caller.
+ * Receive all unclassified protocols.
  */
-	memset (&temp, 0, sizeof(temp));
-	if (dev->flags & IFF_UP) {
-		if (ppp->sc_xc_state != NULL)
-			(*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state,
-						     &temp.c);
+static int
+rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
 
-		if (ppp->sc_rc_state != NULL)
-			(*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state,
-						       &temp.d);
+	/*
+	 * Limit queue length by dropping old frames.
+	 */
+	skb_queue_tail(&ppp->rcv_q, skb);
+	while (ppp->rcv_q.qlen > PPP_MAX_RCV_QLEN) {
+		struct sk_buff *skb = skb_dequeue(&ppp->rcv_q);
+		if (skb)
+			kfree_skb(skb);
 	}
-/*
- * Move the data to the caller's buffer
- */
-	result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data;
 
-	error = -EFAULT;
-	if (!copy_to_user(result, &temp, sizeof (temp)))
-		error = 0;
-	return error;
+	wake_up_interruptible (&ppp->read_wait);
+	if (ppp->tty->fasync != NULL)
+		kill_fasync (ppp->tty->fasync, SIGIO);
+
+	return 1;
 }
 
-/*
- * Callback from the network layer to process the sockioctl functions.
- */
+/*************************************************************
+ * TRANSMIT-SIDE ROUTINES
+ *************************************************************/
 
-static int
-ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
-{
-	struct ppp *ppp = dev2ppp (dev);
-	int error;
+/* local function to store a value into the LQR frame */
+extern inline __u8 * store_long (register __u8 *p, register int value) {
+	*p++ = (__u8) (value >> 24);
+	*p++ = (__u8) (value >> 16);
+	*p++ = (__u8) (value >>	 8);
+	*p++ = (__u8) value;
+	return p;
+}
 
-	CHECK_PPP_MAGIC(ppp);
 /*
- * Process the requests
+ * Compress and send an frame to the peer.
+ * Should be called with dev->tbusy == 1, having been set by the caller.
+ * That is, we use dev->tbusy as a lock to prevent reentry of this
+ * procedure.
  */
-	switch (cmd) {
-	case SIOCGPPPSTATS:
-		error = ppp_dev_ioctl_stats (ppp, ifr, dev);
-		break;
+static void
+ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+	int	proto;
+	__u8	*data;
+	int	count;
+	__u8	*p;
+	int	ret;
 
-	case SIOCGPPPCSTATS:
-		error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev);
+	CHECK_PPP_VOID();
+	data = skb->data;
+	count = skb->len;
+
+	/* dump the buffer */
+	if (ppp->flags & SC_LOG_OUTPKT)
+		ppp_print_buffer ("write frame", data, count);
+
+	/*
+	 * Handle various types of protocol-specific compression
+	 * and other processing, including:
+	 * - VJ TCP header compression
+	 * - updating LQR packets
+	 * - updating CCP state on CCP packets
+	 */
+	proto = PPP_PROTOCOL(data);
+	switch (proto) {
+	case PPP_IP:
+		if ((ppp->flags & SC_COMP_TCP) && ppp->slcomp != NULL)
+			skb = ppp_vj_compress(ppp, skb);
 		break;
 
-	case SIOCGPPPVER:
-		error = ppp_dev_ioctl_version (ppp, ifr);
+	case PPP_LQR:
+		/*
+		 * Update the LQR frame with the current MIB information.
+		 * This way the information is accurate and up-to-date.
+		 */
+		if (count < 48)
+			break;
+		p = data + 40;	/* Point to last two items. */
+		p = store_long(p, ppp->stats.ppp_opackets + 1);
+		p = store_long(p, ppp->stats.ppp_ooctects + count);
+		++ppp->stats.ppp_olqrs;
 		break;
 
-	default:
-		error = -EINVAL;
+	case PPP_CCP:
+		/*
+		 * Outbound compression control frames
+		 */
+		ppp_proto_ccp(ppp, data + PPP_HDRLEN, count - PPP_HDRLEN, 0);
 		break;
 	}
-	return error;
+	data = skb->data;
+	count = skb->len;
+
+	/*
+	 * Compress the whole frame if possible.
+	 */
+	if (((ppp->flags & SC_COMP_RUN) != 0)	&&
+	    (ppp->sc_xc_state != (void *) 0)	&&
+	    (proto != PPP_LCP)			&&
+	    (proto != PPP_CCP)) {
+		struct sk_buff *new_skb;
+		int new_count;
+
+		/* Allocate an skb for the compressed frame. */
+		new_skb = alloc_skb(ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
+		if (new_skb == NULL) {
+			printk(KERN_ERR "ppp_send_frame: no memory\n");
+			kfree_skb(skb);
+			ppp->dev.tbusy = 0;
+			return;
+		}
+
+		/* Compress the frame. */
+		new_count = (*ppp->sc_xcomp->compress)
+			(ppp->sc_xc_state, data, new_skb->data,
+			 count, ppp->mtu + PPP_HDRLEN);
+
+		/* Did it compress? */
+		if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
+			skb_put(new_skb, new_count);
+			kfree_skb(skb);
+			skb = new_skb;
+		} else {
+			/*
+			 * The frame could not be compressed, or it could not
+			 * be sent in compressed form because CCP is down.
+			 */
+			kfree_skb(new_skb);
+		}
+	}
+
+	/*
+	 * Send the frame
+	 */
+	ret = ppp_async_send(ppp, skb);
+	if (ret > 0) {
+		/* we can release the lock */
+		ppp->dev.tbusy = 0;
+	} else if (ret < 0) {
+		/* this can't happen, since the caller got the tbusy lock */
+		printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n");
+	}
 }
 
 /*
- * Send an IP frame to the remote with vj header compression.
- *
- * Return 0 if frame was queued for transmission.
- *	  1 if frame must be re-queued for later driver support.
- *	  -1 if frame should be dropped.
+ * Apply VJ TCP header compression to a packet.
  */
-
-static int
-ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf,
-		 __u8 *data, int len, enum NPmode npmode)
+static struct sk_buff *
+ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
 {
-	int	proto = PPP_IP;
-	__u8	*hdr;
-/*
- * Branch on the type of processing for the IP frame.
- */
-	switch (npmode) {
-	case NPMODE_PASS:
-		break;
+	__u8 *orig_data, *data;
+	struct sk_buff *new_skb;
+	int len, proto;
 
-	case NPMODE_QUEUE:
-		/*
-		 * We may not send the packet now, so drop it.
-		 * XXX It would be nice to be able to return it to the
-		 * network system to be queued and retransmitted later.
-		 */
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: returning frame\n",
-			       ppp->name);
-		return -1;
+	new_skb = alloc_skb(skb->len, GFP_ATOMIC);
+	if (new_skb == NULL) {
+		printk(KERN_ERR "ppp: no memory for vj compression\n");
+		return skb;
+	}
 
-	case NPMODE_ERROR:
-	case NPMODE_DROP:
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp_dev_xmit: npmode = %d on %s\n",
-				ppp->sc_npmode[NP_IP], ppp->name);
-		return -1;
+	orig_data = data = skb->data + PPP_HDRLEN;
+	len = slhc_compress(ppp->slcomp, data, skb->len - PPP_HDRLEN,
+			    new_skb->data + PPP_HDRLEN, &data,
+			    (ppp->flags & SC_NO_TCP_CCID) == 0);
 
-	default:
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING
-				"ppp_dev_xmit: unknown npmode %d on %s\n",
-				ppp->sc_npmode[NP_IP], ppp->name);
-		return -1;
+	if (data == orig_data) {
+		/* Couldn't compress the data */
+		kfree_skb(new_skb);
+		return skb;
 	}
-/*
- * At this point, the buffer will be transmitted. There is no other exit.
- *
- * Try to compress the header.
- */
-	if (ppp->flags & SC_COMP_TCP) {
-		len = slhc_compress (ppp->slcomp, data, len,
-				     buf_base (ppp->cbuf) + PPP_HDRLEN,
-				     &data,
-				     (ppp->flags & SC_NO_TCP_CCID) == 0);
-
-		if (data[0] & SL_TYPE_COMPRESSED_TCP) {
-			proto	 = PPP_VJC_COMP;
-			data[0] ^= SL_TYPE_COMPRESSED_TCP;
-		} else {
-			if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
-				proto = PPP_VJC_UNCOMP;
-			data[0] = (data[0] & 0x0f) | 0x40;
-		}
+
+	/* The data has been changed */
+	if (data[0] & SL_TYPE_COMPRESSED_TCP) {
+		proto = PPP_VJC_COMP;
+		data[0] ^= SL_TYPE_COMPRESSED_TCP;
+	} else {
+		if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+			proto = PPP_VJC_UNCOMP;
+		else
+			proto = PPP_IP;
+		data[0] = orig_data[0];
 	}
-/*
- * Send the frame
- */
-	len  += PPP_HDRLEN;
-	hdr   = data - PPP_HDRLEN;
 
-	hdr[0] = PPP_ALLSTATIONS;
-	hdr[1] = PPP_UI;
-	hdr[2] = 0;
-	hdr[3] = proto;
+	data = skb_put(new_skb, len + PPP_HDRLEN);
+	data[0] = PPP_ALLSTATIONS;
+	data[1] = PPP_UI;
+	data[2] = 0;
+	data[3] = proto;
 
-	return ppp_dev_xmit_frame (ppp, buf, hdr, len);
+	kfree_skb(skb);
+	return new_skb;
+}
+
+static inline void
+ppp_send_frames(struct ppp *ppp)
+{
+	struct sk_buff *skb;
+
+	while (!test_and_set_bit(0, &ppp->dev.tbusy)) {
+		skb = skb_dequeue(&ppp->xmt_q);
+		if (skb == NULL) {
+			ppp->dev.tbusy = 0;
+			mark_bh(NET_BH);
+			break;
+		}
+		ppp_send_frame(ppp, skb);
+	}
 }
 
 /*
- * Send a non-IP data frame (such as an IPX frame) to the remote.
- *
- * Return 0 if frame was queued for transmission.
- *	  1 if frame must be re-queued for later driver support.
+ * Called from the hardware (tty) layer when it can accept
+ * another packet.
  */
-static int
-ppp_dev_xmit_other (struct device *dev, struct ppp *ppp,
-		  __u8 *data, int len, int proto)
+static void
+ppp_output_wakeup(struct ppp *ppp)
 {
-	__u8	*hdr;
+	CHECK_PPP_VOID();
+
+	if (!ppp->dev.tbusy) {
+		printk(KERN_ERR "ppp_output_wakeup called but tbusy==0\n");
+		return;
+	}
+	ppp->dev.tbusy = 0;
+	ppp_send_frames(ppp);
+}
+
 /*
- * Send the frame
+ * Send a control frame (from pppd).
  */
-	len += PPP_HDRLEN;
-	hdr = data - PPP_HDRLEN;
-
-	hdr[0] = PPP_ALLSTATIONS;
-	hdr[1] = PPP_UI;
-	hdr[2] = proto >> 8;
-	hdr[3] = proto;
+static void
+ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP_VOID();
 
-	return ppp_dev_xmit_frame (ppp, ppp->wbuf, hdr, len);
+	/*
+	 * Put the packet on the queue, then send as many as we can.
+	 */
+	skb_queue_tail(&ppp->xmt_q, skb);
+	ppp_send_frames(ppp);
 }
 
+
+/*************************************************************
+ * NETWORK OUTPUT
+ *    This routine accepts requests from the network layer
+ *    and attempts to deliver the packets.
+ *************************************************************/
 /*
- * Send a frame to the remote.
+ * Send a frame to the peer.
+ * Returns 1 iff the frame was not accepted.
  */
-
 static int
-ppp_dev_xmit (sk_buff *skb, struct device *dev)
+ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
 {
-	int answer, len;
-	__u8		  *data;
-	struct ppp	  *ppp = dev2ppp (dev);
-	struct tty_struct *tty = ppp2tty (ppp);
-/*
- * just a little sanity check.
- */
-	if (skb == NULL) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING "ppp_dev_xmit: null packet!\n");
+	struct ppp *ppp = dev2ppp(dev);
+	struct tty_struct *tty = ppp2tty(ppp);
+	enum NPmode npmode;
+	int proto;
+	unsigned char *hdr;
+
+	/* just a little sanity check. */
+	if (skb == NULL)
+		return 0;
+	if (skb->data == NULL) {
+		kfree_skb(skb);
 		return 0;
 	}
-/*
- * Avoid timing problem should tty hangup while data is queued to be sent
- */
+
+	/*
+	 * Avoid timing problem should tty hangup while data is
+	 * queued to be sent.
+	 */
 	if (!ppp->inuse) {
-		dev_kfree_skb (skb);
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * Validate the tty interface
- */
+
+	/*
+	 * Validate the tty interface
+	 */
 	if (tty == NULL) {
 		if (ppp->flags & SC_DEBUG)
-			printk (KERN_ERR
-				"ppp_dev_xmit: %s not connected to a TTY!\n",
-				dev->name);
-		dev_kfree_skb (skb);
+			printk(KERN_ERR
+			       "ppp_dev_xmit: %s not connected to a TTY!\n",
+			       dev->name);
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * Fetch the pointer to the data
- */
-	len   = skb->len;
-	data  = skb_data(skb);
 
-	if (data == (__u8 *) 0) {
+	/*
+	 * Work out the appropriate network-protocol mode for this packet.
+	 */
+	npmode = NPMODE_PASS;	/* default */
+	switch (ntohs(skb->protocol)) {
+	case ETH_P_IP:
+		proto = PPP_IP;
+		npmode = ppp->sc_npmode[NP_IP];
+		break;
+	case ETH_P_IPX:
+		proto = PPP_IPX;
+		npmode = ppp->sc_npmode[NP_IPX];
+		break;
+	case ETH_P_PPPTALK:
+	case ETH_P_ATALK:
+		proto = PPP_AT;
+		npmode = ppp->sc_npmode[NP_AT];
+		break;
+	default:
 		if (ppp->flags & SC_DEBUG)
-			printk (KERN_CRIT "ppp_dev_xmit: %s Null skb data\n",
-				dev->name);
-		dev_kfree_skb (skb);
+			printk(KERN_INFO "%s: packet for unknown proto %x\n",
+			       ppp->name, ntohs(skb->protocol));
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * Detect a change in the transfer size
- */
-	if (ppp->mtu != ppp2dev (ppp)->mtu) {
-		ppp_changedmtu (ppp,
-				ppp2dev (ppp)->mtu,
-				ppp->mru);
-	}
-/*
- * Acquire the lock on the transmission buffer. If the buffer was busy then
- * mark the device as busy.
- * We also require that ppp->tbuf be unlocked, in order to serialize
- * calls to ppp_dev_xmit_frame (which does compression) and the output
- * of frames w.r.t. tty writes from pppd.
- */
-	CHECK_BUF_MAGIC(ppp->wbuf);
-	if (ppp->tbuf->locked || lock_buffer (ppp->wbuf) != 0) {
-		dev->tbusy = 1;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "dev_xmit blocked, t=%lu w=%lu\n",
-			       ppp->tbuf->locked, ppp->wbuf->locked);
-		return 1;
-	}
-/*
- * Look at the protocol in the skb to determine the difference between
- * an IP frame and an IPX frame.
- */
-	switch (ntohs (skb->protocol)) {
-	case ETH_P_IPX:
-		answer = ppp_dev_xmit_other (dev, ppp, data, len, PPP_IPX);
-		break;
 
-	case ETH_P_IP:
-		answer = ppp_dev_xmit_ip (ppp, ppp->wbuf, data, len,
-					  ppp->sc_npmode[NP_IP]);
+	/*
+	 * Drop, accept or reject the packet depending on the mode.
+	 */
+	switch (npmode) {
+	case NPMODE_PASS:
 		break;
 
-	default: /* All others have no support at this time. */
-		dev_kfree_skb (skb);
+	case NPMODE_QUEUE:
+		/*
+		 * We may not send the packet now, so drop it.
+		 * XXX It would be nice to be able to return it to the
+		 * network system to be queued and retransmitted later.
+		 */
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG "%s: returning frame\n", ppp->name);
+		dev_kfree_skb(skb);
+		return 0;
+
+	case NPMODE_ERROR:
+	case NPMODE_DROP:
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "ppp_dev_xmit: dropping (npmode = %d) on %s\n",
+			       npmode, ppp->name);
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * This is the end of the transmission. Release the buffer if it was sent.
- */
-	if (answer == 0) {
-		/* packet queued OK */
-		dev_kfree_skb (skb);
-	} else {
-		ppp->wbuf->locked = 0;
-		if (answer < 0) {
-			/* packet should be dropped */
-			dev_kfree_skb (skb);
-			answer = 0;
-		} else {
-			/* packet should be queued for later */
-			dev->tbusy = 1;
+
+	/*
+	 * The dev->tbusy field acts as a lock to allow only
+	 * one packet to be processed at a time.  If we can't
+	 * get the lock, try again later.
+	 */
+	if (test_and_set_bit(0, &dev->tbusy))
+		return 1;
+
+	/*
+	 * Put the 4-byte PPP header on the packet.
+	 * If there isn't room for it, we have to copy the packet.
+	 */
+	if (skb_headroom(skb) < PPP_HDRLEN) {
+		struct sk_buff *new_skb;
+
+		new_skb = alloc_skb(skb->len + PPP_HDRLEN, GFP_ATOMIC);
+		if (new_skb == NULL) {
+			printk(KERN_ERR "%s: skb hdr alloc failed\n",
+			       ppp->name);
+			dev_kfree_skb(skb);
+			dev->tbusy = 0;
+			return 0;
 		}
+		skb_reserve(new_skb, PPP_HDRLEN);
+		memcpy(skb_put(new_skb, skb->len), skb->data, skb->len);
+		dev_kfree_skb(skb);
+		skb = new_skb;
 	}
-	return answer;
+
+	hdr = skb_push(skb, PPP_HDRLEN);
+	hdr[0] = PPP_ALLSTATIONS;
+	hdr[1] = PPP_UI;
+	hdr[2] = proto >> 8;
+	hdr[3] = proto;
+
+	ppp_send_frame(ppp, skb);
+	return 0;
 }
 
 /*
  * Generate the statistic information for the /proc/net/dev listing.
  */
-
 static struct net_device_stats *
 ppp_dev_stats (struct device *dev)
 {
@@ -3017,7 +2484,7 @@
 
 /* Locate the previous instance of the PPP channel */
 static struct ppp *
-ppp_find (int pid_value)
+ppp_find(int pid_value)
 {
 	struct ppp	*ppp;
 
@@ -3031,104 +2498,152 @@
 	return ppp;
 }
 
-/* Collect hanged up channels */
-
-static void ppp_sync(void)
-{
-	struct device	*dev;
-	struct ppp	*ppp;
-
-	rtnl_lock();
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (!ppp->inuse) {
-			dev = ppp2dev(ppp);
-			if (dev->flags&IFF_UP)
-				dev_close(dev);
-		}
-	}
-	rtnl_unlock();
-}
-
-
 /* allocate or create a PPP channel */
 static struct ppp *
-ppp_alloc (void)
+ppp_alloc(void)
 {
 	int		if_num;
 	int		status;
 	struct device	*dev;
 	struct ppp	*ppp;
 
-	ppp_sync();
-
 	/* try to find an free device */
-	if_num = 0;
 	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
 		if (!test_and_set_bit(0, &ppp->inuse)) {
-
-			/* Reregister device */
-
 			dev = ppp2dev(ppp);
-			unregister_netdev (dev);
-
-			if (register_netdev (dev)) {
-				printk(KERN_DEBUG "cannot reregister ppp device\n");
-				return NULL;
+			if (dev->flags & IFF_UP) {
+				clear_bit(0, &ppp->inuse);
+				continue;
 			}
-			return ppp;
+			/* Reregister device */
+			unregister_netdev(dev);
+			if (register_netdev(dev) == 0)
+				return ppp;
+			printk(KERN_DEBUG "could not reregister ppp device\n");
+			/* leave inuse set in this case */
 		}
-		++if_num;
 	}
-/*
- * There are no available units, so make a new one.
- */
-	ppp = (struct ppp *) kmalloc (sizeof(struct ppp), GFP_KERNEL);
-	if (ppp == 0)
+
+	/*
+	 * There are no available units, so make a new one.
+	 */
+	ppp = (struct ppp *) kmalloc(sizeof(struct ppp), GFP_KERNEL);
+	if (ppp == 0) {
+		printk(KERN_ERR "ppp: struct ppp allocation failed\n");
 		return 0;
+	}
 	memset(ppp, 0, sizeof(*ppp));
 
 	/* initialize channel control data */
-	set_bit(0, &ppp->inuse);
-
-	ppp->line      = if_num;
-	ppp->tty       = NULL;
-	ppp->backup_tty = NULL;
-	if (ppp_last == 0)
-		ppp_list = ppp;
-	else
-		ppp_last->next = ppp;
-	ppp_last = ppp;
-	ppp->next = 0;
-	ppp->read_wait	= NULL;
-	ppp->write_wait = NULL;
+	ppp->magic = PPP_MAGIC;
+	ppp->next = NULL;
+	ppp->inuse = 1;
+	ppp->read_wait = NULL;
 
+	/*
+	 * Make up a suitable name for this device
+	 */
 	dev = ppp2dev(ppp);
-	dev->next      = NULL;
-	dev->init      = ppp_init_dev;
-	dev->name      = ppp->name;
-	sprintf(dev->name, "ppp%d", if_num);
-	dev->base_addr = (__u32) if_num;
-	dev->priv      = (void *) ppp;
+	dev->name = ppp->name;
+	if_num = dev_alloc_name(dev, "ppp%d");
+	if (if_num < 0) {
+		printk(KERN_ERR "ppp: dev_alloc_name failed (%d)\n", if_num);
+		kfree(ppp);
+		return 0;
+	}
+	ppp->line = if_num;
+	ppp->slcomp = NULL;
+
+	dev->next = NULL;
+	dev->init = ppp_init_dev;
+	dev->name = ppp->name;
+	dev->priv = (void *) ppp;
 
 	/* register device so that we can be ifconfig'd */
 	/* ppp_init_dev() will be called as a side-effect */
 	status = register_netdev (dev);
 	if (status == 0) {
-		printk (KERN_INFO "registered device %s\n", dev->name);
+		printk(KERN_INFO "registered device %s\n", dev->name);
 	} else {
-		printk (KERN_ERR
+		printk(KERN_ERR
 		       "ppp_alloc - register_netdev(%s) = %d failure.\n",
-			dev->name, status);
+		       dev->name, status);
+		kfree(ppp);
 		ppp = NULL;
-		/* This one will forever be busy as it is not initialized */
 	}
+
+	/* link this unit into our list */
+	if (ppp_list == 0)
+		ppp_list = ppp;
+	else
+		ppp_last->next = ppp;
+	ppp_last = ppp;
+
 	return ppp;
 }
 
 /*
- * Utility procedures to print a buffer in hex/ascii
+ * Initialize the generic parts of the ppp structure.
+ */
+static void
+ppp_generic_init(struct ppp *ppp)
+{
+	int indx;
+
+	ppp->flags  = 0;
+	ppp->mtu    = PPP_MTU;
+	ppp->mru    = PPP_MRU;
+
+	skb_queue_head_init(&ppp->xmt_q);
+	skb_queue_head_init(&ppp->rcv_q);
+
+	ppp->last_xmit	= jiffies;
+	ppp->last_recv  = jiffies;
+
+	/* clear statistics */
+	memset(&ppp->stats, 0, sizeof (struct pppstat));
+	memset(&ppp->estats, 0, sizeof(struct net_device_stats));
+
+	/* PPP compression data */
+	ppp->sc_xc_state = NULL;
+	ppp->sc_rc_state = NULL;
+
+	for (indx = 0; indx < NUM_NP; ++indx)
+		ppp->sc_npmode[indx] = NPMODE_PASS;
+}
+
+/*
+ * Called to clean up the generic parts of the ppp structure.
  */
+static void
+ppp_release(struct ppp *ppp)
+{
+	struct sk_buff *skb;
+
+	CHECK_PPP_MAGIC(ppp);
+
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "%s released\n", ppp->name);
+
+	ppp_ccp_closed(ppp);
+
+        /* Ensure that the pppd process is not hanging on select()/poll() */
+        wake_up_interruptible(&ppp->read_wait);
+
+	if (ppp->slcomp) {
+		slhc_free(ppp->slcomp);
+		ppp->slcomp = NULL;
+	}
+
+	while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
+		kfree_skb(skb);
+	while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
+		kfree_skb(skb);
+}
 
+/*
+ * Utility procedures to print a buffer in hex/ascii
+ */
 static void
 ppp_print_hex (register __u8 * out, const __u8 * in, int count)
 {
@@ -3163,18 +2678,18 @@
 }
 
 static void
-ppp_print_buffer (const __u8 * name, const __u8 * buf, int count)
+ppp_print_buffer (const char *name, const __u8 *buf, int count)
 {
 	__u8 line[44];
 
-	if (name != (__u8 *) NULL)
-		printk (KERN_DEBUG "ppp: %s, count = %d\n", name, count);
+	if (name != NULL)
+		printk(KERN_DEBUG "ppp: %s, count = %d\n", name, count);
 
 	while (count > 8) {
 		memset (line, 32, 44);
 		ppp_print_hex (line, buf, 8);
 		ppp_print_char (&line[8 * 3], buf, 8);
-		printk (KERN_DEBUG "%s\n", line);
+		printk(KERN_DEBUG "%s\n", line);
 		count -= 8;
 		buf += 8;
 	}
@@ -3183,7 +2698,7 @@
 		memset (line, 32, 44);
 		ppp_print_hex (line, buf, count);
 		ppp_print_char (&line[8 * 3], buf, count);
-		printk (KERN_DEBUG "%s\n", line);
+		printk(KERN_DEBUG "%s\n", line);
 	}
 }
 
@@ -3225,7 +2740,8 @@
 	struct compressor_link *new;
 	unsigned long flags;
 
-	new = (struct compressor_link *) kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
+	new = (struct compressor_link *)
+		kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
 
 	if (new == (struct compressor_link *) 0)
 		return 1;
@@ -3286,63 +2802,49 @@
 	/* register our line disciplines */
 	status = ppp_first_time();
 	if (status != 0)
-		printk (KERN_INFO
-		       "PPP: ppp_init() failure %d\n", status);
-	return (status);
+		printk(KERN_INFO "PPP: ppp_init() failure %d\n", status);
+
+	return status;
 }
 
 void
 cleanup_module(void)
 {
 	int status;
-	struct device *dev;
 	struct ppp *ppp, *next_ppp;
-	int busy_flag = 0;
-/*
- * Ensure that the devices are not in operation.
- */
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (ppp->inuse && ppp->tty != NULL) {
-			busy_flag = 1;
-			break;
-		}
+	int busy = 0;
 
-		dev = ppp2dev (ppp);
-		if (dev->start || dev->flags & IFF_UP) {
-			busy_flag = 1;
-			break;
-		}
-	}
-/*
- * Ensure that there are no compressor modules registered
- */
-	if (ppp_compressors != NULL)
-		busy_flag = 1;
+	/*
+	 * Ensure that the devices are not in operation.
+	 */
+	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
+		CHECK_PPP_MAGIC(ppp);
+		if (ppp->inuse || (ppp->dev.flags & IFF_UP))
+			++busy;
+	}
+	if (busy)
+		printk(KERN_CRIT "PPP: removing despite %d units in use!\n",
+		       busy);
 
-	if (busy_flag) {
-		printk (KERN_INFO
-			"PPP: device busy, remove delayed\n");
-		return;
-	}
-/*
- * Release the tty registration of the line discipline so that no new entries
- * may be created.
- */
+	/*
+	 * Release the tty registration of the line discipline so that
+	 * ttys can no longer be put into PPP line discipline.
+	 */
 	status = tty_register_ldisc (N_PPP, NULL);
 	if (status != 0)
-		printk (KERN_INFO
-			"PPP: Unable to unregister ppp line discipline "
-			"(err = %d)\n", status);
+		printk(KERN_ERR
+		       "PPP: Unable to unregister ppp line discipline "
+		       "(err = %d)\n", status);
 	else
-		printk (KERN_INFO
+		printk(KERN_INFO
 		       "PPP: ppp line discipline successfully unregistered\n");
-/*
- * De-register the devices so that there is no problem with them
- */
+
+	/*
+	 * De-register the devices so that there is no problem with them
+	 */
 	for (ppp = ppp_list; ppp != 0; ppp = next_ppp) {
 		next_ppp = ppp->next;
-		ppp_release (ppp);
-		unregister_netdev (&ppp->dev);
+		unregister_netdev(&ppp->dev);
 		kfree (ppp);
 	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov