]> git.ozlabs.org Git - ppp.git/blobdiff - linux/ppp.c
update for 2.3.9
[ppp.git] / linux / ppp.c
index 38472f90e2de50e2dd4c904ae9caf7b4494c2bd7..f5f9151f5cc3e1744ae9412dc5787f39790037d4 100644 (file)
@@ -2,11 +2,9 @@
  *
  *  Michael Callahan <callahan@maths.ox.ac.uk>
  *  Al Longyear <longyear@netcom.com>
+ *  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>
- *
- *  ==FILEVERSION 970626==
+ *  ==FILEVERSION 990510==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
 */
 
 #define OPTIMIZE_FLAG_TIME     ((HZ * 3)/2)
-
 #define CHECK_CHARACTERS       1
-#define PPP_COMPRESS           1
 
-#ifndef PPP_MAX_DEV
-#define PPP_MAX_DEV    256
-#endif
+#define PPP_MAX_RCV_QLEN       32      /* max # frames we queue up for pppd */
 
-/* $Id: ppp.c,v 1.13 1997/07/14 03:50:50 paulus Exp $
- * Added dynamic allocation of channels to eliminate
- *   compiled-in limits on the number of channels.
- *
- * Dynamic channel allocation code Copyright 1995 Caldera, Inc.,
- *   released under the GNU General Public License Version 2.
- */
+/* $Id: ppp.c,v 1.30 1999/07/23 07:07:34 paulus Exp $ */
 
 #include <linux/version.h>
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -68,8 +57,7 @@
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
 
-#undef VERSION
-/* a nice define to generate linux version numbers */
+/* a macro to generate linux version number codes */
 #define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
 
 #if LINUX_VERSION_CODE < VERSION(2,1,14)
 #include <linux/skbuff.h>
 #include <linux/inet.h>
 #include <linux/ioctl.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>
@@ -112,187 +96,156 @@ typedef struct sk_buff        sk_buff;
 #include <linux/if_pppvar.h>
 #include <linux/ppp-comp.h>
 
-#ifndef PPP_IPX
-#define PPP_IPX 0x2b  /* IPX protocol over PPP */
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
-
-#ifndef PPP_LQR
-#define PPP_LQR 0xc025 /* Link Quality Reporting Protocol */
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
 #endif
 
+#undef PPP_VERSION
+#define PPP_VERSION    "2.3.9"
+
 #if LINUX_VERSION_CODE >= VERSION(2,1,4)
-#include <asm/segment.h>
-#define GET_USER(error,value,addr) error = get_user(value,addr)
-#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
-#define PUT_USER(error,value,addr) error = put_user(value,addr)
-#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
 
 #if LINUX_VERSION_CODE >= VERSION(2,1,5)
 #include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
 #endif
 
-#else  /* 2.0.x and 2.1.x before 2.1.4 */
-
-#define GET_USER(error,value,addr)                                       \
-do {                                                                     \
-       error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \
-       if (error == 0)                                                   \
-               value = get_user(addr);                                   \
-} while (0)
-
-#define COPY_FROM_USER(error,dest,src,size)                              \
-do {                                                                     \
-       error = verify_area (VERIFY_READ, (void *) src, size);            \
-       if (error == 0)                                                   \
-               memcpy_fromfs (dest, src, size);                          \
-} while (0)
-
-#define PUT_USER(error,value,addr)                                        \
-do {                                                                      \
-       error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \
-       if (error == 0)                                                    \
-               put_user (value, addr);                                    \
-} while (0)
+#define GET_USER       get_user
+#define PUT_USER       put_user
+#define COPY_FROM_USER copy_from_user
+#define COPY_TO_USER   copy_to_user
 
-#define COPY_TO_USER(error,dest,src,size)                                \
-do {                                                                     \
-       error = verify_area (VERIFY_WRITE, (void *) dest, size);                  \
-       if (error == 0)                                                   \
-               memcpy_tofs (dest, src, size);                            \
-} while (0)
+#else  /* 2.0.x and 2.1.x before 2.1.4 */
 
-#endif
+#define GET_USER(val, src)     \
+       (verify_area(VERIFY_READ, src, sizeof(*src))? -EFAULT: \
+        ((val) = get_user(src), 0))
+#define PUT_USER(val, dst)     \
+       (verify_area(VERIFY_WRITE, dst, sizeof(*dst))? -EFAULT: \
+        (put_user(val, dst), 0))
+#define COPY_FROM_USER(dst, src, size) \
+       (verify_area(VERIFY_READ, src, size)? -EFAULT: \
+        (memcpy_fromfs(dst, src, size), 0))
+#define COPY_TO_USER(dst, src, size)   \
+       (verify_area(VERIFY_WRITE, dst, size)? -EFAULT: \
+        (memcpy_tofs(dst, src, size), 0))
+
+#endif /* < 2.1.4 */
 
 #if LINUX_VERSION_CODE < VERSION(2,1,37)
 #define test_and_set_bit(nr, addr)     set_bit(nr, addr)
 #endif
 
-static int ppp_register_compressor (struct compressor *cp);
-static void ppp_unregister_compressor (struct compressor *cp);
-
-/*
- * Local functions
- */
+#if LINUX_VERSION_CODE < VERSION(2,1,25)
+#define net_device_stats       enet_statistics
+#endif
 
-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);
-
-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))
+#if LINUX_VERSION_CODE < VERSION(2,1,57)
+#define signal_pending(tsk)    ((tsk)->signal & ~(tsk)->blocked)
+#endif
 
-#ifndef OPTIMIZE_FLAG_TIME
-#define OPTIMIZE_FLAG_TIME     0
+#if LINUX_VERSION_CODE < VERSION(2,1,60)
+typedef int            rw_ret_t;
+typedef unsigned int   rw_count_t;
+#else
+typedef ssize_t                rw_ret_t;
+typedef size_t         rw_count_t;
 #endif
 
-#ifndef PPP_MAX_DEV
-#define PPP_MAX_DEV 256
+#if LINUX_VERSION_CODE < VERSION(2,1,86)
+#define KFREE_SKB(s)   dev_kfree_skb((s), FREE_WRITE)
+#else
+#define KFREE_SKB(s)   kfree_skb(s)
 #endif
 
-/*
- * Parameters which may be changed via insmod.
- */
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+#define LIBERATE_SKB(s)        ((s)->free = 1)
+#else
+#define LIBERATE_SKB(s)        do { } while (0)
+#endif
 
-static int  flag_time = OPTIMIZE_FLAG_TIME;
-static int  max_dev   = PPP_MAX_DEV;
+#if LINUX_VERSION_CODE < VERSION(2,1,95)
+#define SUSER()                suser()
+#else
+#define SUSER()                capable(CAP_NET_ADMIN)
+#endif
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,19) 
-MODULE_PARM(flag_time, "i");
-MODULE_PARM(max_dev, "i");
+#if LINUX_VERSION_CODE < VERSION(2,2,0)
+#define wmb()          mb()
 #endif
 
 /*
- * The "main" procedure to the ppp device
+ * Local functions
  */
 
-int ppp_init (struct device *);
+static int ppp_register_compressor (struct compressor *cp);
+static void ppp_unregister_compressor (struct compressor *cp);
 
-/*
- * Network device driver callback routines
- */
+static void ppp_async_init(struct ppp *ppp);
+static void ppp_async_release(struct ppp *ppp);
+static int ppp_tty_sync_push(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_sync_send(struct ppp *, struct sk_buff *);
+static void ppp_tty_flush_output(struct ppp *);
+
+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 void ppp_send_frames(struct ppp *ppp);
+static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb);
 
-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 enet_statistics *ppp_dev_stats (struct device *);
+static struct ppp *ppp_find (int pid_value);
+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);
 
-#if LINUX_VERSION_CODE < VERSION(2,1,15)
-static int ppp_dev_header (sk_buff *, struct device *, __u16,
-                          void *, void *, unsigned int);
-static int ppp_dev_rebuild (void *eth, struct device *dev,
-                           unsigned long raddr, struct sk_buff *skb);
+#ifndef OPTIMIZE_FLAG_TIME
+#define OPTIMIZE_FLAG_TIME     0
 #endif
 
 /*
- * TTY callbacks
+ * Parameters which may be changed via insmod.
  */
 
-static int ppp_tty_read (struct tty_struct *, struct file *, __u8 *,
-                        unsigned int);
-static int ppp_tty_write (struct tty_struct *, struct file *, const __u8 *,
-                         unsigned int);
-static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
-                         unsigned long);
-#if LINUX_VERSION_CODE < VERSION(2,1,23)
-static int ppp_tty_select (struct tty_struct *tty, struct inode *inode,
-                     struct file *filp, int sel_type, select_table * wait);
-#else
-static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait);
+static int  flag_time = OPTIMIZE_FLAG_TIME;
+#if LINUX_VERSION_CODE >= VERSION(2,1,19) 
+MODULE_PARM(flag_time, "i");
 #endif
-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)
@@ -301,39 +254,57 @@ static void ppp_tty_wakeup (struct tty_struct *tty);
 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
- */
+#if LINUX_VERSION_CODE < VERSION(2,1,18)
+static struct symbol_table ppp_syms = {
+#include <linux/symtab_begin.h>
+       X(ppp_register_compressor),
+       X(ppp_unregister_compressor),
+#include <linux/symtab_end.h>
+};
+#else
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+#endif
+
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ *    The following code implements the PPP line discipline
+ *    and supports using PPP on an async serial line.
+ *************************************************************/
 
-typedef int (*pfn_proto)  (struct ppp *, __u16, __u8 *, int);
+#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)))
 
-typedef struct ppp_proto_struct {
-       int             proto;
-       pfn_proto       func;
-} ppp_proto_type;
+/*
+ * TTY callbacks
+ */
 
-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 !!! */
-};
+static rw_ret_t ppp_tty_read(struct tty_struct *, struct file *, __u8 *,
+                            rw_count_t);
+static rw_ret_t ppp_tty_write(struct tty_struct *, struct file *, const __u8 *,
+                             rw_count_t);
+static int ppp_tty_ioctl(struct tty_struct *, struct file *, unsigned int,
+                        unsigned long);
+#if LINUX_VERSION_CODE < VERSION(2,1,23)
+static int ppp_tty_select(struct tty_struct *tty, struct inode *inode,
+                       struct file *filp, int sel_type, select_table * wait);
+#else
+static unsigned int ppp_tty_poll(struct tty_struct *tty, struct file *filp,
+                                poll_table * wait);
+#endif
+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] =
 {
@@ -370,6 +341,9 @@ __u16 ppp_crc16_table[256] =
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
+#if LINUX_VERSION_CODE >= VERSION(2,1,18)
+EXPORT_SYMBOL(ppp_crc16_table);
+#endif
 
 #ifdef CHECK_CHARACTERS
 static __u32 paritytab[8] =
@@ -379,45 +353,34 @@ static __u32 paritytab[8] =
 };
 #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.
  */
-
 static int
-ppp_first_time (void)
+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;
+#if LINUX_VERSION_CODE >= VERSION(2,1,28)
+       ppp_ldisc.name          = "ppp";
+#endif
        ppp_ldisc.open          = ppp_tty_open;
        ppp_ldisc.close         = ppp_tty_close;
        ppp_ldisc.read          = ppp_tty_read;
@@ -434,123 +397,20 @@ ppp_first_time (void)
 
        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)
-{
-#if LINUX_VERSION_CODE < VERSION(2,1,15)
-       dev->hard_header      = ppp_dev_header;
-       dev->rebuild_header   = ppp_dev_rebuild;
-#endif
-
-       dev->hard_header_len  = PPP_HARD_HDR_LEN;
-
-       /* 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;
-
-#if LINUX_VERSION_CODE < VERSION(2,1,20)
-       {
-               int    indx;
-
-               for (indx = 0; indx < DEV_NUMBUFFS; indx++)
-                       skb_queue_head_init (&dev->buffs[indx]);
-       }
-#else
-       dev_init_buffers(dev);
-#endif
-
-       /* New-style flags */
-       dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
-       dev->family     = AF_INET;
-       dev->pa_addr    = 0;
-       dev->pa_brdaddr = 0;
-       dev->pa_mask    = 0;
-       dev->pa_alen    = 4; /* sizeof (__u32) */
-
-       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->read_wait  = NULL;
-       ppp->write_wait = NULL;
-       ppp->last_xmit  = jiffies - flag_time;
-
-       /* clear statistics */
-       memset(&ppp->stats, 0, sizeof (struct pppstat));
-       memset(&ppp->estats, 0, sizeof(struct enet_statistics));
-
-       /* Reset the demand dial information */
-       ppp->ddinfo.xmit_idle=         /* time since last NP packet sent */
-       ppp->ddinfo.recv_idle=jiffies; /* time since last NP packet received */
-
-       /* PPP compression data */
-       ppp->sc_xc_state =
-       ppp->sc_rc_state = NULL;
-}
-
-#if LINUX_VERSION_CODE < VERSION(2,1,18)
-static struct symbol_table ppp_syms = {
-#include <linux/symtab_begin.h>
-       X(ppp_register_compressor),
-       X(ppp_unregister_compressor),
-       X(ppp_crc16_table),
-#include <linux/symtab_end.h>
-};
-#else
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
-EXPORT_SYMBOL(ppp_crc16_table);
-#endif
-
-/* 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;
@@ -560,7 +420,7 @@ ppp_init (struct device *dev)
                answer     = ppp_first_time();
 #if LINUX_VERSION_CODE < VERSION(2,1,18)
                if (answer == 0)
-                       (void) register_symtab (&ppp_syms);
+                       (void) register_symtab(&ppp_syms);
 #endif
        }
        if (answer == 0)
@@ -569,610 +429,910 @@ ppp_init (struct device *dev)
 }
 #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;
-
-       buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer),
-                                            GFP_ATOMIC);
-
-       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->escape = 0;
+       ppp->toss   = 0xE0;
+       ppp->tty_pushing = 0;
 
-/*
- * Routine to release the allocated buffer.
- */
+       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;
 
-static void
-ppp_free_buf (struct ppp_buffer *ptr)
-{
-       if (ptr != NULL) {
-               CHECK_BUF_MAGIC(ptr);
-               kfree (ptr);
-       }
+       ppp->tpkt = NULL;
+       ppp->tfcs = PPP_INITFCS;
+       ppp->optr = ppp->obuf;
+       ppp->olim = ppp->obuf;
+
+       ppp->rpkt = NULL;
+       ppp->rfcs = PPP_INITFCS;
+
+       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)
 {
-       register int 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;
-
-       restore_flags (flags);
-       return (state);
+       struct sk_buff *skb;
+
+       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_buffer *new_rbuf;
-       struct ppp_buffer *new_wbuf;
-       struct ppp_buffer *new_cbuf;
-       struct ppp_buffer *new_tbuf;
+       struct ppp *ppp;
 
-       struct ppp_buffer *old_rbuf;
-       struct ppp_buffer *old_wbuf;
-       struct ppp_buffer *old_cbuf;
-       struct ppp_buffer *old_tbuf;
-
-       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) {
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_ERR
-                               "ppp: failed to allocate new buffers\n");
+       /*
+        * 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);
 
-               ppp_free_buf (new_wbuf);
-               ppp_free_buf (new_tbuf);
-               ppp_free_buf (new_rbuf);
-               ppp_free_buf (new_cbuf);
-               return 0;
-       }
-/*
- *  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 */
+       } else {
+               ppp = ppp_alloc();
+               if (ppp == NULL) {
+                       printk(KERN_ERR "ppp_alloc failed\n");
+                       return -ENFILE;
+               }
 
-       dev->mtu    =
-       ppp->mtu    = new_mtu;
-       ppp->mru    = new_mru;
+               /*
+                * Initialize the control block
+                */
+               ppp_generic_init(ppp);
+               ppp_async_init(ppp);
 
-       ppp->s1buf  = NULL;
-       ppp->s2buf  = NULL;
-       ppp->xbuf   = NULL;
+               MOD_INC_USE_COUNT;
+       }
 
-       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;
+               if (ppp_tty_push(ppp))
+                       ppp_output_wakeup(ppp);
+               wake_up_interruptible(&ppp->read_wait);
+       } 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);
+               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 rw_ret_t
+ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
+            rw_count_t nr)
 {
-       struct tty_struct *tty;
-       struct device *dev;
+       struct ppp *ppp = tty2ppp (tty);
+       struct sk_buff *skb;
+       rw_ret_t len, err;
 
-       CHECK_PPP_MAGIC(ppp);
-       tty = ppp2tty (ppp);
-       dev = ppp2dev (ppp);
+       /*
+        * Validate the pointers
+        */
+       if (!ppp)
+               return -EIO;
+       CHECK_PPP(-ENXIO);
 
-       if (ppp->flags & SC_DEBUG)
-               printk(KERN_DEBUG "ppp%s released\n", ppp->name);
+       /*
+        * 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);
 
-       ppp_ccp_closed (ppp);
+       /*
+        * 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;
 
-       /* Ensure that the pppd process is not hanging on select()/poll() */
-       wake_up_interruptible (&ppp->read_wait);
-       wake_up_interruptible (&ppp->write_wait);
+               skb = skb_dequeue(&ppp->rcv_q);
+               if (skb != 0)
+                       break;
 
-       if (tty != NULL && tty->disc_data == ppp)
-               tty->disc_data = NULL;  /* Break the tty->ppp link */
+               /*
+                * If no frame is available, return -EAGAIN or wait.
+                */
+               err = -EAGAIN;
+               if (file->f_flags & O_NONBLOCK)
+                       break;
 
-       if (dev && dev->flags & IFF_UP) {
-               dev->flags &= ~IFF_UP;   /* prevent recursion */
-               dev_close (dev); /* close the device properly */
+               interruptible_sleep_on(&ppp->read_wait);
+               err = -EINTR;
+               if (signal_pending(current))
+                       break;
        }
+       MOD_DEC_USE_COUNT;
+       if (skb == 0)
+               return err;
 
-       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;
+       /*
+        * 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_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;
        }
 
-       ppp->inuse = 0;
-       ppp->tty   = NULL;
-       ppp->backup_tty = NULL;
+       /*
+        * Copy the received data from the buffer to the caller's area.
+        */
+       err = len;
+       if (COPY_TO_USER(buf, skb->data, len))
+               err = -EFAULT;
+
+out:
+       KFREE_SKB(skb);
+       return err;
 }
 
 /*
- * TTY callback.
- *
- * Called when the line discipline is changed to something
- * else, the tty is closed, or the tty detects a hangup.
+ * 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_close (struct tty_struct *tty)
+static rw_ret_t
+ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data,
+             rw_count_t count)
 {
        struct ppp *ppp = tty2ppp (tty);
+       __u8 *new_data;
+       struct sk_buff *skb;
 
-       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;
-               }
+       /*
+        * Verify the pointers.
+        */
+       if (!ppp)
+               return -EIO;
+
+       if (ppp->magic != PPP_MAGIC)
+               return -EIO;
+
+       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.
+        */
+       skb = alloc_skb(count, GFP_KERNEL);
+       if (skb == NULL) {
+               printk(KERN_ERR "ppp_tty_write: no memory\n");
+               return 0;
+       }
+       LIBERATE_SKB(skb);
+       new_data = skb_put(skb, count);
+
+       /*
+        * Retrieve the user's buffer
+        */
+       if (COPY_FROM_USER(new_data, data, count)) {
+               KFREE_SKB(skb);
+               return -EFAULT;
        }
+
+       /*
+        * Send the frame
+        */
+       ppp_send_ctrl(ppp, skb);
+
+       return (rw_ret_t) count;
 }
 
 /*
- * TTY callback.
- *
- * Called when the tty discipline is switched to PPP.
+ * 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 int
-ppp_tty_open (struct tty_struct *tty)
+ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
+               unsigned int param2, unsigned long param3)
 {
        struct ppp *ppp = tty2ppp (tty);
-       int indx;
-/*
- * There should not be an existing table for this slot.
- */
-       if (ppp) {
-               if (ppp->flags & SC_DEBUG)
-                       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) {
+       register int temp_i = 0;
+       int error = -EFAULT;
+
+       /*
+        * Verify the status of the PPP device.
+        */
+       if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
+               return -ENXIO;
+
+       /*
+        * The user must have an euid of root to do these requests.
+        */
+       if (!SUSER())
+               return -EPERM;
+
+       switch (param2) {
+       case PPPIOCGASYNCMAP:
                /*
-                * If we are taking over a ppp unit which is currently
-                * connected to a loopback pty, there's not much to do.
+                * Retrieve the transmit async map
                 */
-               CHECK_PPP(-EINVAL);
-               tty->disc_data = ppp;
-               ppp->tty       = tty;
+               if (PUT_USER(ppp->xmit_async_map[0], (int *) param3))
+                       break;
+               error = 0;
+               break;
 
-       } else {
-               ppp = ppp_alloc();
-               if (ppp == NULL) {
-                       if (ppp->flags & SC_DEBUG)
-                               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) {
-                       if (ppp->flags & SC_DEBUG)
-                               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) {
-                       if (ppp->flags & SC_DEBUG)
-                               printk (KERN_ERR "ppp_tty_open: "
-                                       "no space for user receive buffer\n");
-                       ppp_release (ppp);
-                       return -ENOMEM;
-               }
+       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;
 
+       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: channel %s open\n",
-                               ppp2dev(ppp)->name);
+                       printk(KERN_INFO
+                              "ppp_tty_ioctl: set rcv asyncmap %x\n",
+                              ppp->recv_async_map);
+               error = 0;
+               break;
 
-               for (indx = 0; indx < NUM_NP; ++indx)
-                       ppp->sc_npmode[indx] = NPMODE_PASS;
+       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;
+               error = 0;
+               break;
 
-               MOD_INC_USE_COUNT;
-       }
-/*
- * Flush any pending characters in the driver and discipline.
- */
-       if (tty->ldisc.flush_buffer)
-               tty->ldisc.flush_buffer (tty);
+       case PPPIOCSXASYNCMAP:
+               /*
+                * Set the map of characters to be escaped on transmission.
+                */
+               {
+                       __u32 temp_tbl[8];
 
-       if (tty->driver.flush_buffer)
-               tty->driver.flush_buffer (tty);
-       return (ppp->line);
-}
+                       if (COPY_FROM_USER(temp_tbl, (void *) param3,
+                                          sizeof (temp_tbl)))
+                               break;
 
-/*
- * 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.
- */
+                       temp_tbl[1]  =  0x00000000;
+                       temp_tbl[2] &= ~0x40000000;
+                       temp_tbl[3] |=  0x60000000;
 
-static void
-ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty,
-                    struct ppp_buffer *xbuf)
-{
-       register int count, actual;
-       unsigned long flags;
+                       memcpy(ppp->xmit_async_map, temp_tbl,
+                              sizeof (ppp->xmit_async_map));
+
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_INFO
+                                      "ppp_tty_ioctl: set xasyncmap\n");
+                       error = 0;
+               }
+               break;
+
+       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;
+
+       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 TCFLSH:
+               /*
+                * Flush our buffers, then call the generic code to
+                * flush the serial port's buffer.
+                */
+               if (param3 == TCIFLUSH || param3 == TCIOFLUSH) {
+                       struct sk_buff *skb;
+                       while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
+                               KFREE_SKB(skb);
+               }
+               if (param3 == TCIOFLUSH || param3 == TCOFLUSH)
+                       ppp_tty_flush_output(ppp);
+               error = n_tty_ioctl (tty, file, param2, param3);
+               break;
+
+       case FIONREAD:
+               /*
+                * Returns how many bytes are available for a read().
+                */
+               {
+                       unsigned long flags;
+                       struct sk_buff *skb;
+                       int count = 0;
 
-       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;
-                       if (ppp2dev (ppp) -> flags & IFF_UP) {
-                               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;
-                       }
+                       skb = skb_peek(&ppp->rcv_q);
+                       if (skb != 0)
+                               count = skb->len;
                        restore_flags(flags);
+                       if (PUT_USER(count, (int *) param3))
+                               break;
+                       error = 0;
                }
+               break;
+
+       default:
+               /*
+                *  All other ioctl() events will come here.
+                */
+               error = ppp_ioctl(ppp, param2, param3);
+               break;
        }
+       return error;
+}
+
 /*
- * Clear the re-entry flag
+ * TTY callback.
+ *
+ * Process the select() or poll() statement for the PPP device.
  */
-       save_flags(flags);      /* &=~ may not be atomic */
-       cli ();
-       ppp->flags &= ~SC_XMIT_BUSY;
-       restore_flags(flags);
+
+#if LINUX_VERSION_CODE < VERSION(2,1,23)
+static int
+ppp_tty_select(struct tty_struct *tty, struct inode *inode,
+              struct file *filp, int sel_type, select_table * wait)
+{
+       struct ppp *ppp = tty2ppp(tty);
+       int result = 1;
+
+       /*
+        * Verify the status of the PPP device.
+        */
+       if (!ppp || tty != ppp->tty)
+               return -EBADF;
+
+       CHECK_PPP(-EBADF);
+
+       switch (sel_type) {
+       case SEL_IN:
+               /* The fd is readable if the receive queue isn't empty. */
+               if (skb_peek(&ppp->rcv_q) != NULL)
+                       break;
+               /* fall through */
+       case SEL_EX:
+               /* Check for exceptions or read errors. */
+               /* Is this a pty link and the remote disconnected? */
+               if (tty->flags & (1 << TTY_OTHER_CLOSED))
+                       break;
+
+               /* Is this a local link and the modem disconnected? */
+               if (tty_hung_up_p (filp))
+                       break;
+
+               select_wait(&ppp->read_wait, wait);
+               result = 0;
+               break;
+
+       case SEL_OUT:
+               /* The fd is always writable. */
+               break;
+       }
+       return result;
+}
+
+#else  /* 2.1.23 or later */
+
+static unsigned int
+ppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait)
+{
+       struct ppp *ppp = tty2ppp(tty);
+       unsigned int mask = 0;
+
+       if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
+               CHECK_PPP(0);
+
+               poll_wait(filp, &ppp->read_wait, wait);
+
+               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;
+       }
+       return mask;
 }
+#endif /* >= 2.1.23 */
 
 /*
  * 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.
  */
-
 static void
 ppp_tty_wakeup (struct tty_struct *tty)
 {
-       struct ppp_buffer *xbuf;
        struct ppp *ppp = tty2ppp (tty);
 
+       tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
        if (!ppp)
                return;
        CHECK_PPP_VOID();
-
-       if (tty != ppp->tty) {
-               tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+       if (tty != ppp->tty)
                return;
-       }
+
+       if (ppp_tty_push(ppp))
+               ppp_output_wakeup(ppp);
+}
+
 /*
- * Ensure that there is a transmission pending. Clear the re-entry flag if
- * there is no pending buffer. Otherwise, send the buffer.
+ * Send a packet to the peer over a synchronous tty line.
+ * All encoding and FCS are handled by hardware.
+ * Addr/Ctrl and Protocol field compression implemented.
+ * 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.
  */
-       xbuf = ppp->xbuf;
-       if (xbuf == NULL)
-               tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-       else
-               ppp_tty_wakeup_code (ppp, tty, xbuf);
+static int
+ppp_sync_send(struct ppp *ppp, struct sk_buff *skb)
+{
+       unsigned char *data;
+       int islcp;
+       
+       CHECK_PPP(0);
+
+       if (ppp->tpkt != NULL)
+               return -1;
+       ppp->tpkt = skb;
+
+       data = ppp->tpkt->data;
+       
+       /*
+        * 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;
+
+       /* only reset idle time for data packets */
+       if (PPP_PROTOCOL(data) < 0x8000)
+               ppp->last_xmit = jiffies;
+       ++ppp->stats.ppp_opackets;
+       ppp->stats.ppp_ooctects += ppp->tpkt->len;
+
+       if ( !(data[2]) && (ppp->flags & SC_COMP_PROT) ) {
+               /* compress protocol field */
+               data[2] = data[1];
+               data[1] = data[0];
+               skb_pull(ppp->tpkt,1);
+               data = ppp->tpkt->data;
+       }
+       
+       /*
+        * Do address/control compression
+        */
+       if ((ppp->flags & SC_COMP_AC) && !islcp
+           && PPP_ADDRESS(data) == PPP_ALLSTATIONS
+           && PPP_CONTROL(data) == PPP_UI) {
+               /* strip addr and control field */
+               skb_pull(ppp->tpkt,2);
+       }
+
+       return ppp_tty_sync_push(ppp);
 }
 
 /*
- * 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.
+ * Push a synchronous frame out to the tty.
+ * Returns 1 if frame accepted (or discarded), 0 otherwise.
  */
-
-static void
-ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
+static int
+ppp_tty_sync_push(struct ppp *ppp)
 {
+       int sent;
+       struct tty_struct *tty = ppp2tty(ppp);
        unsigned long flags;
+               
+       CHECK_PPP(0);
+
+       if (ppp->tpkt == NULL)
+               return 0;
+               
+       /* prevent reentrancy with tty_pushing flag */          
+       save_flags(flags);
+       cli();
+       if (ppp->tty_pushing) {
+               /* record wakeup attempt so we don't lose */
+               /* a wakeup call while doing push processing */
+               ppp->woke_up=1;
+               restore_flags(flags);
+               return 0;
+       }
+       ppp->tty_pushing = 1;
+       restore_flags(flags);
+       
+       if (tty == NULL || tty->disc_data != (void *) ppp)
+               goto flush;
+               
+       for(;;){
+               ppp->woke_up=0;
+               
+               /* Note: Sync driver accepts complete frame or nothing */
+               tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+               sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len);
+               if (sent < 0) {
+                       /* write error (possible loss of CD) */
+                       /* record error and discard current packet */
+                       ppp->stats.ppp_oerrors++;
+                       break;
+               }
+               ppp->stats.ppp_obytes += sent;
+               if (sent < ppp->tpkt->len) {
+                       /* driver unable to accept frame just yet */
+                       save_flags(flags);
+                       cli();
+                       if (ppp->woke_up) {
+                               /* wake up called while processing */
+                               /* try to send the frame again */
+                               restore_flags(flags);
+                               continue;
+                       }
+                       /* wait for wakeup callback to try send again */
+                       ppp->tty_pushing = 0;
+                       restore_flags(flags);
+                       return 0;
+               }
+               break;
+       }
+flush: 
+       /* done with current packet (sent or discarded) */
+       KFREE_SKB(ppp->tpkt);
+       ppp->tpkt = 0;
+       ppp->tty_pushing = 0;
+       return 1;
+}
 
-       CHECK_PPP_VOID();
-       CHECK_BUF_MAGIC(xbuf);
-/*
- * Hold interrupts.
- */
-       save_flags (flags);
-       cli ();
 /*
- * Control the flags which are best performed with the interrupts masked.
+ * 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.
  */
-       xbuf->locked     = 1;
-       xbuf->tail       = 0;
+static int
+ppp_async_send(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP(0);
+
+       ppp_tty_push(ppp);
+
+       if (ppp->tpkt != NULL)
+               return -1;
+       ppp->tpkt = skb;
+       ppp->tpkt_pos = 0;
+
+       return ppp_tty_push(ppp);
+}
+
 /*
- * If the transmitter is busy then place the buffer on the appropriate
- * priority queue.
+ * Push as much data as possible out to the tty.
+ * Returns 1 if we finished encoding the current frame, 0 otherwise.
  */
-       if (ppp->xbuf != NULL) {
-               if (xbuf->type == BUFFER_TYPE_TTY_WR)
-                       ppp->s1buf = xbuf;
-               else
-                       ppp->s2buf = xbuf;
-               restore_flags (flags);
-               return;
+static int
+ppp_tty_push(struct ppp *ppp)
+{
+       int avail, sent, done = 0;
+       struct tty_struct *tty = ppp2tty(ppp);
+       
+       if (ppp->flags & SC_SYNC) 
+               return ppp_tty_sync_push(ppp);
+
+       CHECK_PPP(0);
+       if (ppp->tty_pushing) {
+               ppp->woke_up = 1;
+               return 0;
        }
+       if (tty == NULL || tty->disc_data != (void *) ppp)
+               goto flush;
+       while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
+               ppp->tty_pushing = 1;
+               mb();
+               ppp->woke_up = 0;
+               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) {
+                               wmb();
+                               ppp->tty_pushing = 0;
+                               mb();
+                               if (ppp->woke_up)
+                                       continue;
+                               return done;
+                       }
+               }
+               if (ppp->tpkt != 0)
+                       done = ppp_async_encode(ppp);
+               wmb();
+               ppp->tty_pushing = 0;
+       }
+       return done;
+
+flush:
+       ppp->tty_pushing = 1;
+       mb();
+       ppp->stats.ppp_oerrors++;
+       if (ppp->tpkt != 0) {
+               KFREE_SKB(ppp->tpkt);
+               ppp->tpkt = 0;
+               done = 1;
+       }
+       ppp->optr = ppp->olim;
+       wmb();
+       ppp->tty_pushing = 0;
+       return done;
+}
+
 /*
- * If the transmitter is not busy then this is the highest priority 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.
  */
-       ppp->flags      &= ~SC_XMIT_BUSY;
-       ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-       ppp->xbuf        = xbuf;
-       restore_flags (flags);
+static int
+ppp_async_encode(struct ppp *ppp)
+{
+       int fcs, i, count, c;
+       unsigned char *buf, *buflim;
+       unsigned char *data;
+       int islcp;
+
+       CHECK_PPP(0);
+
+       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;
+               }
+               *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;
+}
+
 /*
- * Do the "tty wakeup_code" to actually send this buffer.
+ * Flush output from our internal buffers.
+ * Called for the TCFLSH ioctl.
  */
-       ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf);
+static void
+ppp_tty_flush_output(struct ppp *ppp)
+{
+       struct sk_buff *skb;
+       int done = 0;
+
+       while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
+               KFREE_SKB(skb);
+       ppp->tty_pushing = 1;
+       mb();
+       ppp->optr = ppp->olim;
+       if (ppp->tpkt != NULL) {
+               KFREE_SKB(ppp->tpkt);
+               ppp->tpkt = 0;
+               done = 1;
+       }
+       wmb();
+       ppp->tty_pushing = 0;
+       if (done)
+               ppp_output_wakeup(ppp);
 }
 
-/*************************************************************
- * 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.
- *************************************************************/
-
 /*
  * 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
 ppp_tty_room (struct tty_struct *tty)
 {
@@ -1187,8 +1347,9 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
                 char *flags, int count)
 {
        register struct ppp *ppp = tty2ppp (tty);
-       register struct ppp_buffer *buf = NULL;
-       __u8 chr;
+       struct sk_buff *skb;
+       int chr, flg;
+       unsigned char *p;
 
        if (ppp != 0)
                CHECK_PPP_VOID();
@@ -1197,61 +1358,128 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
         */
        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.
- */
+       /*
+        * 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");
+                       printk(KERN_DEBUG
+                              "PPP: tty_receive called but couldn't find "
+                              "PPP struct.\n");
                return;
        }
-       CHECK_PPP_VOID ();
-/*
- * Print the buffer if desired
- */
+       /*
+        * Print the buffer if desired
+        */
        if (ppp->flags & SC_LOG_RAWIN)
                ppp_print_buffer ("receive buffer", data, count);
-/*
- * 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;
-               }
-/*
- * 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.
- */
 
-#ifdef CHECK_CHARACTERS
-               if (chr & 0x80)
-                       ppp->flags |= SC_RCV_B7_1;
-               else
+       ppp->stats.ppp_ibytes += count;
+       skb = ppp->rpkt;
+       
+       if ( ppp->flags & SC_SYNC ) {
+               /* synchronous mode */
+               
+               if (ppp->toss==0xE0) {
+                       /* this is the 1st frame, reset vj comp */
+                       ppp_receive_error(ppp);
+                       ppp->toss = 0;
+               }
+               
+               /*
+                * Allocate an skbuff for frame.
+                * The 128 is room for VJ header expansion.
+                */
+               
+               if (skb == NULL)
+                       skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+                       
+               if (skb == NULL) {
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "couldn't "
+                                      "alloc skb for recv\n");
+               } else {
+                       LIBERATE_SKB(skb);
+                       /*
+                        * Decompress A/C and protocol compression here.
+                        */
+                       p = skb_put(skb, 2);
+                       p[0] = PPP_ALLSTATIONS;
+                       p[1] = PPP_UI;
+                       if (*data == PPP_ALLSTATIONS) {
+                               data += 2;
+                               count -= 2;
+                       }
+                       if ((*data & 1) != 0) {
+                               p = skb_put(skb, 1);
+                               p[0] = 0;
+                       }
+
+                       /* copy frame to socket buffer */
+                       p = skb_put(skb, count);
+                       memcpy(p,data,count);
+                       
+                       /*
+                        * Check if we've overflowed the MRU
+                        */
+                       if (skb->len >= ppp->mru + PPP_HDRLEN + 2
+                           || skb_tailroom(skb) <= 0) {
+                               ++ppp->estats.rx_length_errors;
+                               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));
+                       } else {
+                               if (!ppp_receive_frame(ppp, skb)) {
+                                       KFREE_SKB(skb);
+                                       ppp_receive_error(ppp);
+                               }
+                       }
+               
+                       /* Reset for the next frame */
+                       skb = NULL;
+               }
+               ppp->rpkt = skb;
+               return;
+       }
+       
+       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;
+                       }
+               }
+
+               /*
+                * 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.
+                */
+
+#ifdef CHECK_CHARACTERS
+               if (chr & 0x80)
+                       ppp->flags |= SC_RCV_B7_1;
+               else
                        ppp->flags |= SC_RCV_B7_0;
 
                if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
@@ -1259,427 +1487,619 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
                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 (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 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;
-                       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;
-
-                       /* If this is a control char to be ignored, do so */
-                       if (in_rmap (ppp, chr))
-                               break;
-
                        /*
-                        * Modify the next character if preceded by escape.
-                        * The escape character (0x7d) could be an escaped
-                        * 0x5d, if it follows an escape :-)
+                        * 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->escape) {
-                               chr ^= PPP_TRANS;
-                               ppp->escape = 0;
-                       } else if (chr == PPP_ESCAPE) {
-                               ppp->escape = PPP_TRANS;
-                               break;
+                       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);
                        }
-
                        /*
-                        * Decompress A/C and protocol compression here.
+                        * Reset for the next frame.
                         */
-                       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;
+                       skb = NULL;
+                       ppp->rfcs = PPP_INITFCS;
+                       ppp->escape = 0;
+                       ppp->toss = 0;
+                       continue;
                }
-       }
-}
 
-/* on entry, a received frame is in ppp->rbuf.bufr
-   check it and dispose as appropriate */
+               /* If we're tossing, look no further. */
+               if (ppp->toss != 0)
+                       continue;
 
-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 this is a control char to be ignored, do so */
+               if (in_rmap(ppp, chr))
+                       continue;
 
-       CHECK_PPP(0);
-       CHECK_BUF_MAGIC(ppp->rbuf);
-/*
- * 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);
+               /*
+                * 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;
                }
-               ++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) {
-                               if (ppp->flags & SC_DEBUG)
-                                       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 DECOMP_ERROR:
-                               ppp->flags |= SC_DC_ERROR;
-                               break;
 
-                       case DECOMP_FATALERROR:
-                               ppp->flags |= SC_DC_FERROR;
+               /*
+                * 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_ERR "ppp: fatal decomp error\n");
-                               break;
+                                       printk(KERN_DEBUG "couldn't "
+                                              "alloc skb for recv\n");
+                               ppp->toss = 1;
+                               continue;
                        }
-/*
- * 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);
+                       LIBERATE_SKB(skb);
+               }
+
+               /*
+                * 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;
+               }
+
+               /*
+                * 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;
                }
+
+               /*
+                * Store the character and update the FCS.
+                */
+               p = skb_put(skb, 1);
+               *p = chr;
+               ppp->rfcs = PPP_FCS(ppp->rfcs, chr);
        }
-/*
- * Process the uncompressed frame.
- */
-       ppp_doframe_lower (ppp, data, count);
-       return 1;
+       ppp->rpkt = skb;
 }
 
-static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
-{
-       __u16           proto = PPP_PROTOCOL (data);
-       ppp_proto_type  *proto_ptr;
+/*************************************************************
+ * 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.
+ *************************************************************/
 
-       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.
+ * Network device driver callback routines
  */
-       proto_ptr = proto_list;
-       while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
-               ++proto_ptr;
+
+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 *);
+
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+static int ppp_dev_header(struct sk_buff *, struct device *, __u16,
+                         void *, void *, unsigned int);
+static int ppp_dev_rebuild(void *eth, struct device *dev,
+                          unsigned long raddr, struct sk_buff *skb);
+#endif
+
 /*
- * Update the appropriate statistic counter.
+ * Information for the protocol decoder
  */
-       if ((*proto_ptr->func) (ppp, proto,
-                               &data[PPP_HDRLEN],
-                               count - PPP_HDRLEN))
-               ppp->stats.ppp_ioctects += count;
-       else
-               ++ppp->stats.ppp_discards;
-}
+
+typedef int (*pfn_proto)  (struct ppp *, struct sk_buff *);
+
+typedef struct ppp_proto_struct {
+       int             proto;
+       pfn_proto       func;
+} ppp_proto_type;
+
+static int rcv_proto_ip                (struct ppp *, struct sk_buff *);
+static int rcv_proto_ipv6      (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 *);
+
+static
+ppp_proto_type proto_list[] = {
+       { PPP_IP,         rcv_proto_ip         },
+       { PPP_IPV6,       rcv_proto_ipv6       },
+       { 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 !!! */
+};
 
 /*
- * Put the input frame into the networking system for the indicated protocol
+ * Called when the PPP network interface device is actually created.
  */
-
 static int
-ppp_rcv_rx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+ppp_init_dev (struct device *dev)
 {
-       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;
-       }
-/*
- * 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
- */
+       dev->hard_header_len  = PPP_HDRLEN;
 #if LINUX_VERSION_CODE < VERSION(2,1,15)
-       skb->free = 1;
+       dev->hard_header      = ppp_dev_header;
+       dev->rebuild_header   = ppp_dev_rebuild;
 #endif
 
-       ppp->ddinfo.recv_idle = jiffies;
-       netif_rx (skb);
-       return 1;
-}
+       /* 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;
 
-/*
- * Process the receipt of an IP frame
- */
+#if LINUX_VERSION_CODE < VERSION(2,1,20)
+       {
+               int    indx;
+
+               for (indx = 0; indx < DEV_NUMBUFFS; indx++)
+                       skb_queue_head_init (&dev->buffs[indx]);
+       }
+#else
+       dev_init_buffers(dev);
+#endif
+
+       dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
 
-static int
-rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count)
-{
-       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;
 }
 
 /*
- * Process the receipt of an IPX frame
+ * Callback from the network layer when the device goes up.
  */
 
 static int
-rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+ppp_dev_open (struct device *dev)
 {
-       CHECK_PPP(0);
-       if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0))
-               return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
+       struct ppp *ppp = dev2ppp(dev);
+
+       if (!ppp->inuse || ppp2tty(ppp) == NULL) {
+               printk(KERN_ERR "ppp: %s not active\n", dev->name);
+               return -ENXIO;
+       }
+
+       MOD_INC_USE_COUNT;
+
        return 0;
 }
 
 /*
- * Process the receipt of an VJ Compressed frame
+ * Callback from the network layer when the ppp device goes down.
  */
 
 static int
-rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto,
-                   __u8 *data, int count)
+ppp_dev_close (struct device *dev)
 {
-       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->flags & SC_DEBUG)
-                       printk (KERN_NOTICE
-                               "ppp: error in VJ decompression\n");
-       }
+       struct ppp *ppp = dev2ppp (dev);
+
+       CHECK_PPP_MAGIC(ppp);
+
+       MOD_DEC_USE_COUNT;
+
        return 0;
 }
 
+static inline void
+get_vj_stats(struct vjstat *vj, struct slcompress *slc)
+{
+       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;
+}
+
 /*
- * Process the receipt of an VJ Un-compressed frame
+ * Callback from the network layer to process the sockioctl functions.
  */
-
 static int
-rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto,
-                     __u8 *data, int count)
+ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
 {
-       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);
-               }
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_NOTICE
-                               "ppp: error in VJ memorizing\n");
+       struct ppp *ppp = dev2ppp(dev);
+       int nb;
+       union {
+               struct ppp_stats stats;
+               struct ppp_comp_stats cstats;
+               char vers[32];
+       } u;
+
+       CHECK_PPP_MAGIC(ppp);
+
+       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;
+
+       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;
+
+       case SIOCGPPPVER:
+               strcpy(u.vers, szVersion);
+               nb = strlen(u.vers) + 1;
+               break;
+
+       default:
+               return -EINVAL;
        }
+
+       if (COPY_TO_USER((void *) ifr->ifr_ifru.ifru_data, &u, nb))
+               return -EFAULT;
        return 0;
 }
 
 /*
- * Receive all unclassified protocols.
+ * Process the generic PPP ioctls, i.e. those which are not specific
+ * to any particular type of hardware link.
  */
-
 static int
-rcv_proto_unknown (struct ppp *ppp, __u16 proto,
-                  __u8 *data, int len)
+ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
 {
-       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;                                \
-}
+       register int temp_i = 0, oldflags;
+       int error = -EFAULT;
+       unsigned long flags;
+       struct ppp_idle cur_ddinfo;
+       struct npioctl npi;
 
-       CHECK_PPP(0);
-/*
- * The total length includes the protocol data.
- * Lock the user information buffer.
- */
-       if (test_and_set_bit (0, &ppp->ubuf->locked)) {
+       CHECK_PPP(-ENXIO);
+
+       /*
+        * The user must have an euid of root to do these requests.
+        */
+       if (!SUSER())
+               return -EPERM;
+
+       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;
+               ppp->mru = temp_i;
                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
- */
-               totlen = len + 2;
-               PUTC (totlen >> 8);
-               PUTC (totlen);
+                       printk(KERN_INFO
+                              "ppp_ioctl: set mru to %x\n", temp_i);
+               error = 0;
+               break;
+
+       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;
+
+       case PPPIOCSFLAGS:
+               /*
+                * Set the flags for the various options
+                */
+               if (GET_USER(temp_i, (int *) param3))
+                       break;
+
+               if (ppp->flags & ~temp_i & SC_CCP_OPEN)
+                       ppp_ccp_closed(ppp);
+
+               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;
+
+       case PPPIOCSCOMPRESS:
+               /*
+                * Set the compression mode
+                */
+               error = ppp_set_compression
+                       (ppp, (struct ppp_option_data *) param3);
+               break;
+
+       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;
+
+       case PPPIOCSDEBUG:
+               /*
+                * Set the debug level
+                */
+               if (GET_USER(temp_i, (int *) param3))
+                       break;
+               temp_i = (temp_i & 0x1F) << 16;
+
+               if ((ppp->flags | temp_i) & SC_DEBUG)
+                       printk(KERN_INFO
+                              "ppp_ioctl: set dbg flags to %x\n", temp_i);
+
+               save_flags(flags);
+               cli();
+               ppp->flags = (ppp->flags & ~0x1F0000) | temp_i;
+               restore_flags(flags);
+               error = 0;
+               break;
+
+       case PPPIOCGDEBUG:
+               /*
+                * Get the debug level
+                */
+               temp_i = (ppp->flags >> 16) & 0x1F;
+               if (PUT_USER(temp_i, (int *) param3))
+                       break;
+               error = 0;
+               break;
+
+       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;
+
+       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);
+
+               error = -ENOMEM;
+               if (ppp->slcomp == NULL) {
+                       printk(KERN_ERR "ppp: no memory for VJ compression\n");
+                       break;
+               }
+               error = 0;
+               break;
 
-               PUTC (proto >> 8);
-               PUTC (proto);
+       case PPPIOCGNPMODE:
+       case PPPIOCSNPMODE:
+               if (COPY_FROM_USER(&npi, (void *) param3, sizeof(npi)))
+                       break;
 
-               totlen -= 2;
-               while (totlen-- > 0) {
-                       PUTC (*data++);
+               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;
                }
-#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);
+               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 = 0;
+               break;
 
-               return 1;
-/*
- * The buffer is full. Unlock the header
- */
-failure:
-               clear_bit (0, &ppp->ubuf->locked);
+       default:
+               /*
+                *  All other ioctl() events will come here.
+                */
                if (ppp->flags & SC_DEBUG)
-                       printk (KERN_DEBUG
-                               "ppp: rcv_proto_unknown: buffer overflow\n");
+                       printk(KERN_ERR
+                              "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+                              param2, param3);
+
+               error = -ENOIOCTLCMD;
+               break;
        }
+out:
+       return error;
+}
+
 /*
- * Discard the frame. There are no takers for this protocol.
+ * Process the set-compression ioctl.
  */
-       if (ppp->flags & SC_DEBUG)
-               printk (KERN_DEBUG
-                       "ppp: rcv_proto_unknown: dropping packet\n");
-       return 0;
+static int
+ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
+{
+       struct compressor *cp;
+       int error, nb;
+       unsigned long flags;
+       __u8 *ptr;
+       __u8 ccp_option[CCP_MAX_OPTION_LENGTH];
+       struct ppp_option_data data;
+
+       /*
+        * Fetch the compression parameters
+        */
+       error = -EFAULT;
+       if (COPY_FROM_USER(&data, odp, sizeof (data)))
+               goto out;
+
+       nb  = data.length;
+       ptr = data.ptr;
+       if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH)
+               nb = CCP_MAX_OPTION_LENGTH;
+
+       if (COPY_FROM_USER(ccp_option, ptr, nb))
+               goto out;
+
+       error = -EINVAL;
+       if (ccp_option[1] < 2)  /* preliminary check on the length byte */
+               goto out;
+
+       save_flags(flags);
+       cli();
+       ppp->flags &= ~(data.transmit? SC_COMP_RUN: SC_DECOMP_RUN);
+       restore_flags(flags);
+
+       cp = find_compressor (ccp_option[0]);
+#if defined(CONFIG_KMOD) || defined(CONFIG_KERNELD)
+       if (cp == NULL) {
+               char modname[32];
+               sprintf(modname, "ppp-compress-%d", ccp_option[0]);
+               request_module(modname);
+               cp = find_compressor(ccp_option[0]);
+       }
+#endif /* CONFIG_KMOD */
+
+       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.
+        */
+       error = 0;
+       if (data.transmit) {
+               if (ppp->sc_xc_state != NULL)
+                       (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
+               ppp->sc_xc_state = NULL;
+
+               ppp->sc_xcomp    = cp;
+               ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
+               if (ppp->sc_xc_state == NULL) {
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "%s: comp_alloc failed\n",
+                                      ppp->name);
+                       error = -ENOBUFS;
+               }
+       } else {
+               if (ppp->sc_rc_state != NULL)
+                       (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
+               ppp->sc_rc_state = NULL;
+
+               ppp->sc_rcomp    = cp;
+               ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
+               if (ppp->sc_rc_state == NULL) {
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "%s: decomp_alloc failed\n",
+                                      ppp->name);
+                       error = -ENOBUFS;
+               }
+       }
+out:
+       return error;
 }
 
 /*
@@ -1690,7 +2110,7 @@ failure:
  * immediate or the compressors will become confused on the peer.
  */
 
-static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
+static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd)
 {
        int slen    = CCP_LENGTH(dp);
        __u8 *opt = dp   + CCP_HDRLEN;
@@ -1700,14 +2120,17 @@ static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
        if (slen > len)
                return;
 
+       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
- */
+               /*
               * CCP must be going down - disable compression
               */
                if (ppp->flags & SC_CCP_UP) {
                        cli();
                        ppp->flags &= ~(SC_CCP_UP   |
@@ -1725,20 +2148,17 @@ static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
                        break;
                if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
                        break;
-/*
- * we're agreeing to send compressed packets.
- */
                if (!rcvd) {
+                       /*
+                        * we're agreeing to send compressed packets.
+                        */
                        if (ppp->sc_xc_state == NULL)
                                break;
 
                        if ((*ppp->sc_xcomp->comp_init)
                            (ppp->sc_xc_state,
-                            opt,
-                            opt_len,
-                            ppp2dev (ppp)->base_addr,
-                            0,
-                            ppp->flags & SC_DEBUG)) {
+                            opt, opt_len,
+                            ppp->line, 0, ppp->flags & SC_DEBUG)) {
                                if (ppp->flags & SC_DEBUG)
                                        printk(KERN_DEBUG "%s: comp running\n",
                                               ppp->name);
@@ -1747,20 +2167,17 @@ static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
                        }
                        break;
                }
-/*
- * peer is agreeing to send compressed packets.
- */
+
+               /*
+                * 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)) {
+                    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);
@@ -1769,10 +2186,12 @@ static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
                        ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
                }
                break;
-/*
- * CCP Reset-ack resets compressors and decompressors as it passes through.
- */
+
        case CCP_RESETACK:
+               /*
+                * CCP Reset-ack resets compressors and decompressors
+                * as it passes through.
+                */
                if ((ppp->flags & SC_CCP_UP) == 0)
                        break;
 
@@ -1798,1200 +2217,658 @@ static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
        restore_flags(flags);
 }
 
-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);
-}
-
 /*
- * Handle a LQR packet.
+ * CCP is down; free (de)compressor state if necessary.
  */
 
-static int
-rcv_proto_lqr (struct ppp *ppp, __u16 proto, __u8 * data, int len)
+static void
+ppp_ccp_closed(struct ppp *ppp)
 {
-       return rcv_proto_unknown (ppp, proto, data, len);
-}
-
-/*************************************************************
- * 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.
- *************************************************************/
-
-/* read a PPP frame from the us_rbuff circular buffer,
-   waiting if necessary
-*/
+       unsigned long flags;
 
-static int
-ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
-             unsigned int nr)
-{
-       struct ppp *ppp = tty2ppp (tty);
-       __u8 c;
-       int len, ret;
-       int error;
-
-#define GETC(c)                                                \
-{                                                      \
-       c = buf_base (ppp->ubuf) [ppp->ubuf->tail++];   \
-       ppp->ubuf->tail &= ppp->ubuf->size;             \
-}
-
-/*
- * Validate the pointers
- */
-       if (!ppp)
-               return -EIO;
-
-       if (ppp->magic != PPP_MAGIC)
-               return -EIO;
-
-       CHECK_PPP (-ENXIO);
-
-/*
- * Before we attempt to write the frame to the user, ensure that the
- * user has access to the pages for the total buffer length.
- */
-       error = verify_area (VERIFY_WRITE, buf, nr);
-       if (error != 0)
-               return (error);
-
-/*
- * Acquire the read lock.
- */
-       for (;;) {
-               ppp = tty2ppp (tty);
-               if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
-                   || tty != ppp->tty)
-                       return 0;
-
-               if (test_and_set_bit (0, &ppp->ubuf->locked) != 0) {
-#if 0
-                       if (ppp->flags & SC_DEBUG)
-                               printk (KERN_DEBUG
-                                    "ppp_tty_read: sleeping(ubuf)\n");
-#endif
-                       current->timeout = 0;
-                       current->state   = TASK_INTERRUPTIBLE;
-                       schedule ();
-
-                       if (current->signal & ~current->blocked)
-                               return -EINTR;
-                       continue;
-               }
-
-/*
- * 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;
-               }
-
-/*
- * If there is no length then wait for the data to arrive.
- */
-               /* no data */
-               clear_bit (0, &ppp->ubuf->locked);
-               if (file->f_flags & O_NONBLOCK)
-                       return -EAGAIN;
-               current->timeout = 0;
-#if 0
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_DEBUG
-                               "ppp_tty_read: sleeping(read_wait)\n");
-#endif
-               interruptible_sleep_on (&ppp->read_wait);
-               if (current->signal & ~current->blocked)
-                       return -EINTR;
-       }
-
-/*
- * 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 */
-
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_DEBUG
-                               "ppp: read of %u bytes too small for %d "
-                               "frame\n", nr, len + 2);
-               ppp->stats.ppp_ierrors++;
-               error = -EOVERFLOW;
-               goto out;
-       }
-
-/*
- * Fake the insertion of the ADDRESS and CONTROL information because these
- * were not saved in the buffer.
- */
-       PUT_USER (error, (u_char) PPP_ALLSTATIONS, buf);
-       if (error)
-               goto out;
-       ++buf;
-       PUT_USER (error, (u_char) PPP_UI, buf);
-       if (error)
-               goto out;
-       ++buf;
-
-/*
- * Copy the received data from the buffer to the caller's area.
- */
-       ret = len + 2;  /* Account for ADDRESS and CONTROL bytes */
-       while (len-- > 0) {
-               GETC (c);
-               PUT_USER (error, c, buf);
-               if (error)
-                       goto out;
-               ++buf;
-       }
-
-       clear_bit (0, &ppp->ubuf->locked);
-#if 0
+       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 "ppp_tty_read: passing %d bytes up\n", ret);
-#endif
-       return ret;
-
-out:
-       ppp->ubuf->tail += len;
-       ppp->ubuf->tail &= ppp->ubuf->size;
-       clear_bit (0, &ppp->ubuf->locked);
-       return error;
-#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 */
-
-extern inline void
-ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
-               register __u8 chr)
-{
-/*
- * 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);
-}
-
-/*
- * Procedure to encode the data with the proper escaping and send the
- * data to the remote system.
- */
-
-static void
-ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
-                   __u8 *data, int count, int non_ip)
-{
-       __u16   write_fcs;
-       int     address, control;
-       int     proto;
-
-       CHECK_PPP_VOID();
-       CHECK_BUF_MAGIC(buf);
-       ++ppp->stats.ppp_opackets;
-       ppp->stats.ppp_ooctects += count;
-
-/*
- * Insert the leading FLAG character
- */
-       buf->count = 0;
-
-       if (non_ip || flag_time == 0)
-               ins_char (buf, PPP_FLAG);
-       else {
-               if (jiffies - ppp->last_xmit > flag_time)
-                       ins_char (buf, PPP_FLAG);
+               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;
        }
-       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);
 
-       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);
+       if (ppp->sc_rc_state) {
+               (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
+               ppp->sc_rc_state = NULL;
        }
-/*
- * Emit the protocol (compressed if possible)
- */
-       if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00))
-               ppp_stuff_char (ppp, buf, proto >> 8);
-
-       ppp_stuff_char (ppp, buf, proto);
-/*
- * Insert the data
- */
-       data  += 4;
-       count -= 4;
-
-       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);
 }
 
+/*************************************************************
+ * RECEIVE-SIDE ROUTINES
+ *************************************************************/
+
 /*
- * 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.
+ * On entry, a received frame is in skb.
+ * Check it and dispose as appropriate.
  */
-
 static int
-ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
-                   __u8 *data, int count)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
 {
+       __u8    *data;
+       int     count;
        int     proto;
-       int     address, control;
-       __u8 *new_data;
        int     new_count;
+       struct sk_buff *new_skb;
+       ppp_proto_type  *proto_ptr;
 
-       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);
-
-       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, GFP_ATOMIC);
-               if (new_data == NULL) {
-                       if (ppp->flags & SC_DEBUG)
-                               printk (KERN_ERR
-                                       "ppp_dev_xmit_frame: no memory\n");
-                       return 1;
-               }
-
-               new_count = (*ppp->sc_xcomp->compress)
-                   (ppp->sc_xc_state, data, new_data, count, ppp->mtu);
-
-               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;
-}
-
-/*
- * Revise the tty frame for specific protocols.
- */
-
-static int
-send_revise_frame (register struct ppp *ppp, __u8 *data, int len)
-{
-       __u8 *p;
-
-       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);
-               break;
-/*
- * Outbound compression frames
- */
-       case PPP_CCP:
-               ppp_proto_ccp (ppp,
-                              data + PPP_HDRLEN,
-                              len  - PPP_HDRLEN,
-                              0);
-               break;
-
-       default:
-               break;
+       /*
+        * 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;
 
-       return len;
-}
-
-/*
- * write a frame with NR chars from BUF to TTY
- * we have to put the FCS field on ourselves
- */
-
-static int
-ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
-              unsigned int count)
-{
-       struct ppp *ppp = tty2ppp (tty);
-       __u8 *new_data;
-       int proto;
-
-/*
- * Verify the pointers.
- */
-       if (!ppp)
-               return -EIO;
-
-       if (ppp->magic != PPP_MAGIC)
-               return -EIO;
-
-       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 %u to mtu %d\n", 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) {
+       /*
+        * Generate an error if the frame is too small.
+        */
+       if (count < PPP_HDRLEN + 2) {
                if (ppp->flags & SC_DEBUG)
-                       printk (KERN_ERR
-                               "ppp_tty_write: no memory\n");
+                       printk(KERN_DEBUG
+                              "ppp: got runt ppp frame, %d chars\n", count);
+               ++ppp->estats.rx_length_errors;
                return 0;
        }
-/*
- * Retrieve the user's buffer
- */
-       if (copy_from_user (new_data, data, count)) {
-               kfree (new_data);
-               return -EFAULT;
-       }
-/*
- * lock this PPP unit so we will be the only writer;
- * sleep if necessary
- */
-       while (lock_buffer (ppp->tbuf) != 0) {
-               current->timeout = 0;
-#if 0
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_DEBUG "ppp_tty_write: sleeping\n");
-#endif
-               interruptible_sleep_on (&ppp->write_wait);
-
-               ppp = tty2ppp (tty);
-               if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
-                   || tty != ppp->tty) {
-                       kfree (new_data);
-                       return 0;
-               }
 
-               if (current->signal & ~current->blocked) {
-                       kfree (new_data);
-                       return -EINTR;
-               }
-       }
-/*
- * Change the LQR frame
- */
-       count = send_revise_frame (ppp, new_data, count);
-/*
- * Send the data
- */
-       if (proto == PPP_IP) {
+       if ( !(ppp->flags & SC_SYNC) ) { 
                /*
-                * 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.
+                * Verify the FCS of the frame and discard the FCS characters
+                * from the end of the buffer.
                 */
-               ppp_dev_xmit_ip(ppp, ppp->tbuf, new_data + PPP_HDRLEN,
-                               count - PPP_HDRLEN);
-
-       } else {
-               ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
+               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);
        }
-
-       kfree (new_data);
-       return (int) count;
-}
-
-/*
- * Process the BSD compression IOCTL event for the tty device.
- */
-
-static int
-ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
-{
-       struct compressor *cp;
-       struct ppp_option_data data;
-       int error;
-       int nb;
-       __u8 *ptr;
-       __u8 ccp_option[CCP_MAX_OPTION_LENGTH];
-       unsigned long flags;
-
-/*
- * Fetch the compression parameters
- */
-       COPY_FROM_USER (error,
-                       &data,
-                       odp,
-                       sizeof (data));
-
-       if (error != 0)
-               return error;
-
-       nb  = data.length;
-       ptr = data.ptr;
-       if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH)
-               nb = CCP_MAX_OPTION_LENGTH;
-
-       COPY_FROM_USER (error,
-                       ccp_option,
-                       ptr,
-                       nb);
-
-       if (error != 0)
-               return error;
-
-       if (ccp_option[1] < 2)  /* preliminary check on the length byte */
-               return (-EINVAL);
-
-       save_flags(flags);
-       cli();
-       ppp->flags &= ~(SC_COMP_RUN | SC_DECOMP_RUN);
-       restore_flags(flags);
-
-       cp = find_compressor (ccp_option[0]);
-       if (cp != (struct compressor *) 0) {
-               /*
-                * Found a handler for the protocol - try to allocate
-                * a compressor or decompressor.
-                */
-               error = 0;
-               if (data.transmit) {
-                       if (ppp->sc_xc_state != NULL)
-                               (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
-
-                       ppp->sc_xcomp    = cp;
-                       ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
-
-                       if (ppp->sc_xc_state == NULL) {
-                               if (ppp->flags & SC_DEBUG)
-                                       printk(KERN_DEBUG "%s: comp_alloc failed\n",
-                                              ppp->name);
-                               error = -ENOBUFS;
+       
+       /*
+        * 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 {
-                               if (ppp->flags & SC_DEBUG)
-                                       printk(KERN_DEBUG "%s: comp_alloc -> %p\n",
-                                              ppp->name, ppp->sc_xc_state);
+                               LIBERATE_SKB(new_skb);
+                               new_count = (*ppp->sc_rcomp->decompress)
+                                       (ppp->sc_rc_state, data, count,
+                                        new_skb->data, ppp->mru + PPP_HDRLEN);
                        }
-               } else {
-                       if (ppp->sc_rc_state != NULL)
-                               (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
-                       ppp->sc_rcomp    = cp;
-                       ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
-                       if (ppp->sc_rc_state == NULL) {
-                               if (ppp->flags & SC_DEBUG)
-                                       printk(KERN_DEBUG "%s: decomp_alloc failed\n",
-                                              ppp->name);
-                               error = -ENOBUFS;
+                       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_DEBUG "%s: decomp_alloc -> %p\n",
-                                              ppp->name, ppp->sc_rc_state);
+                                       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;
+                               }
                        }
-               }
-               return (error);
-       }
-
-       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);
-       return (-EINVAL);       /* no handler found */
-}
-
-/*
- * Process the IOCTL event for the tty device.
- */
-
-static int
-ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
-               unsigned int param2, unsigned long param3)
-{
-       struct ppp *ppp = tty2ppp (tty);
-       register int temp_i = 0;
-       int error = 0;
-/*
- * 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 (!suser ())
-               return -EPERM;
-/*
- * Set the MRU value
- */
-       switch (param2) {
-       case PPPIOCSMRU:
-               GET_USER (error, 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 (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
-               PUT_USER (error, temp_i, (int *) param3);
-               break;
-/*
- * Set the flags for the various options
- */
-       case PPPIOCSFLAGS:
-               GET_USER (error, temp_i, (int *) param3);
-               if (error != 0)
-                       break;
-               temp_i &= SC_MASK;
-               temp_i |= (ppp->flags & ~SC_MASK);
-
-               if ((ppp->flags & SC_CCP_OPEN) &&
-                   (temp_i & SC_CCP_OPEN) == 0)
-                       ppp_ccp_closed (ppp);
 
-               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;
-/*
- * Set the compression mode
- */
-       case PPPIOCSCOMPRESS:
-               error = ppp_set_compression (ppp,
-                                           (struct ppp_option_data *) param3);
-               break;
-/*
- * Retrieve the transmit async map
- */
-       case PPPIOCGASYNCMAP:
-               PUT_USER (error, ppp->xmit_async_map[0], (int *) param3);
-               break;
-/*
- * Set the transmit async map
- */
-       case PPPIOCSASYNCMAP:
-               GET_USER (error, temp_i, (int *) param3);
-               if (error != 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:
-               GET_USER (error, temp_i, (int *) param3);
-               if (error != 0)
-                       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:
-               PUT_USER (error, ppp2dev (ppp)->base_addr, (int *) param3);
-               if (error != 0)
-                       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:
-               GET_USER (error, temp_i, (int *) param3);
-               if (error != 0)
-                       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;
-               PUT_USER (error, temp_i, (int *) param3);
-               break;
-/*
- * Get the times since the last send/receive frame operation
- */
-       case PPPIOCGIDLE:
-               {
-                       struct ppp_idle cur_ddinfo;
-                       __u32 cur_jiffies = jiffies;
-
-                       /* change absolute times to relative times. */
-                       cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ;
-                       cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ;
-                       COPY_TO_USER (error,
-                                     (void *) param3,
-                                     &cur_ddinfo,
-                                     sizeof (cur_ddinfo));
-               }
-               break;
-/*
- * Retrieve the extended async map
- */
-       case PPPIOCGXASYNCMAP:
-               COPY_TO_USER (error,
-                             (void *) param3,
-                             ppp->xmit_async_map,
-                             sizeof (ppp->xmit_async_map));
-               break;
-/*
- * Set the async extended map
- */
-       case PPPIOCSXASYNCMAP:
-               {
-                       __u32 temp_tbl[8];
-
-                       COPY_FROM_USER (error,
-                                       temp_tbl,
-                                       (void *) param3,
-                                       sizeof (temp_tbl));
-
-                       if (error != 0)
-                               break;
-                       temp_tbl[1]  =  0x00000000;
-                       temp_tbl[2] &= ~0x40000000;
-                       temp_tbl[3] |=  0x60000000;
-
-                       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->flags & SC_DEBUG)
-                                       printk (KERN_INFO
-                                               "ppp_tty_ioctl: set xasyncmap\n");
-                       }
-               }
-               break;
-/*
- * Set the maximum VJ header compression slot number.
- */
-       case PPPIOCSMAXCID:
-               GET_USER (error, temp_i, (int *) param3);
-               if (error != 0)
-                       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 = slhc_init (16, temp_i);
-
-               if (ppp->slcomp == NULL) {
-                       if (ppp->flags & SC_DEBUG)
-                               printk (KERN_ERR
-                                       "ppp: no space for compression buffers!\n");
-                       ppp_release (ppp);
-                       error = -ENOMEM;
+               } 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);
                }
-               break;
-
-    case PPPIOCXFERUNIT:
-               ppp->backup_tty = tty;
-               ppp->sc_xfer = current->pid;
-               break;
-
-    case PPPIOCGNPMODE:
-    case PPPIOCSNPMODE:
-               {
-                       struct npioctl npi;
-
-                       COPY_FROM_USER (error,
-                                       &npi,
-                                       (void *) param3,
-                                       sizeof (npi));
-                       if (error != 0)
-                               break;
+       }
+       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);
 
-                       switch (npi.protocol) {
-                       case PPP_IP:
-                               npi.protocol = NP_IP;
-                               break;
-                       default:
-                               if (ppp->flags & SC_DEBUG)
-                                       printk(KERN_DEBUG "pppioc[gs]npmode: "
-                                              "invalid proto %d\n", npi.protocol);
-                               error = -EINVAL;
-                       }
+       /*
+        * 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);
 
-                       if (error != 0)
-                               break;
+       /*
+        * 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;
 
-                       if (param2 == PPPIOCGNPMODE) {
-                               npi.mode = ppp->sc_npmode[npi.protocol];
+       /*
+        * Update the appropriate statistic counter.
+        */
+       if (!(*proto_ptr->func)(ppp, skb)) {
+               KFREE_SKB(skb);
+               ++ppp->stats.ppp_discards;
+       }
 
-                               COPY_TO_USER (error,
-                                             (void *) param3,
-                                             &npi,
-                                             sizeof (npi));
-                               break;
-                       }
+       return 1;
+}
 
-                       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);
-               }
-               break;
 /*
- * Allow users to read, but not set, the serial port parameters
+ * An input error has been detected, so we need to inform
+ * the VJ decompressor.
  */
-       case TCGETS:
-       case TCGETA:
-               error = n_tty_ioctl (tty, file, param2, param3);
-               break;
+static void
+ppp_receive_error(struct ppp *ppp)
+{
+       CHECK_PPP_VOID();
+
+       if (ppp->slcomp != 0)
+               slhc_toss(ppp->slcomp);
+}
 
-       case FIONREAD:
-               {
-                       int count = ppp->ubuf->tail - ppp->ubuf->head;
-                       if (count < 0)
-                               count += (ppp->ubuf->size + 1);
-                       PUT_USER (error, count, (int *) param3);
-               }
-               break;
 /*
- *  All other ioctl() events will come here.
+ * Put the input frame into the networking system for the indicated protocol
  */
-       default:
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_ERR
-                               "ppp_tty_ioctl: invalid ioctl: %x, addr %lx\n",
-                               param2,
-                               param3);
+static int
+ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb)
+{
 
-               error = -ENOIOCTLCMD;
-               break;
-       }
-       return error;
+       /*
+        * 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_pull(skb, PPP_HDRLEN);      /* pull off ppp header */
+       skb->mac.raw   = skb->data;
+       ppp->last_recv = jiffies;
+       netif_rx (skb);
+       return 1;
 }
 
 /*
- * TTY callback.
- *
- * Process the select() (or poll()) statement for the PPP device.
+ * Process the receipt of an IP frame
  */
-
-#if LINUX_VERSION_CODE < VERSION(2,1,23)
 static int
-ppp_tty_select (struct tty_struct *tty, struct inode *inode,
-               struct file *filp, int sel_type, select_table * wait)
+rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb)
 {
-       struct ppp *ppp = tty2ppp (tty);
-       int result = 1;
+       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;
+}
+
 /*
- * Verify the status of the PPP device.
+ * Process the receipt of an IPv6 frame
  */
-       if (!ppp)
-               return -EBADF;
+static int
+rcv_proto_ipv6(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP(0);
+       if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+           && ppp->sc_npmode[NP_IPV6] == NPMODE_PASS)
+               return ppp_rcv_rx(ppp, ETH_P_IPV6, skb);
+       return 0;
+}
 
-       if (ppp->magic != PPP_MAGIC || tty != ppp->tty)
-               return -EBADF;
+/*
+ * Process the receipt of an IPX frame
+ */
+static int
+rcv_proto_ipx(struct ppp *ppp, struct sk_buff *skb)
+{
+       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;
+}
 
-       CHECK_PPP (0);
 /*
- * Branch on the type of select mode. A read request must lock the user
- * buffer area.
+ * Process the receipt of an Appletalk frame
  */
-       switch (sel_type) {
-       case SEL_IN:
-               if (test_and_set_bit (0, &ppp->ubuf->locked) == 0) {
-                       /* Test for the presence of data in the queue */
-                       if (ppp->ubuf->head != ppp->ubuf->tail) {
-                               clear_bit (0, &ppp->ubuf->locked);
-                               break;
-                       }
-                       clear_bit (0, &ppp->ubuf->locked);
-               }               /* fall through */
+static int
+rcv_proto_at(struct ppp *ppp, struct sk_buff *skb)
+{
+       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;
+}
+
 /*
- * Exceptions or read errors.
+ * Process the receipt of an VJ Compressed frame
  */
-       case SEL_EX:
-               /* Is this a pty link and the remote disconnected? */
-               if (tty->flags & (1 << TTY_OTHER_CLOSED))
-                       break;
+static int
+rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb)
+{
+       int new_count;
 
-               /* Is this a local link and the modem disconnected? */
-               if (tty_hung_up_p (filp))
-                       break;
+       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;
+       }
+       new_count += PPP_HDRLEN;
+       if (new_count > skb->len)
+               skb_put(skb, new_count - skb->len);
+       else
+               skb_trim(skb, new_count);
+       return rcv_proto_ip(ppp, skb);
+}
 
-               select_wait (&ppp->read_wait, wait);
-               result = 0;
-               break;
 /*
- * Write mode. A write is allowed if there is no current transmission.
+ * Process the receipt of an VJ Un-compressed frame
  */
-       case SEL_OUT:
-               if (ppp->tbuf->locked != 0) {
-                       select_wait (&ppp->write_wait, wait);
-                       result = 0;
-               }
-               break;
+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 result;
+       return rcv_proto_ip(ppp, skb);
 }
 
-#else  /* 2.1.23 or later */
+static int
+rcv_proto_ccp(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP(0);
+       ppp_proto_ccp (ppp, skb->data + PPP_HDRLEN, skb->len - PPP_HDRLEN, 1);
+       return rcv_proto_unknown(ppp, skb);
+}
 
-static unsigned int
-ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait)
+/*
+ * Receive all unclassified protocols.
+ */
+static int
+rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb)
 {
-       struct ppp *ppp = tty2ppp (tty);
-       unsigned int mask = 0;
+       CHECK_PPP(0);
 
-       if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
-               CHECK_PPP (0);
+       /*
+        * 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);
+       }
 
-               poll_wait(&ppp->read_wait, wait);
-               poll_wait(&ppp->write_wait, wait);
+       wake_up_interruptible (&ppp->read_wait);
+       if (ppp->tty->fasync != NULL)
+               kill_fasync (ppp->tty->fasync, SIGIO);
 
-               /* Must lock the user buffer area while checking. */
-               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;
-       }
-       return mask;
+       return 1;
 }
 
-#endif
-
 /*************************************************************
- * 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...).
+ * TRANSMIT-SIDE ROUTINES
  *************************************************************/
 
+/* 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;
+}
+
 /*
- * Callback from the network layer when the device goes up.
+ * Compress and send an frame to the peer.
+ * Should be called with xmit_busy == 1, having been set by the caller.
+ * That is, we use xmit_busy as a lock to prevent reentry of this
+ * procedure.
  */
-
-static int
-ppp_dev_open (struct device *dev)
+static void
+ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
 {
-       struct ppp *ppp = dev2ppp (dev);
+       int     proto;
+       __u8    *data;
+       int     count;
+       __u8    *p;
+       int     ret;
 
-       /* reset POINTOPOINT every time, since dev_close zaps it! */
-       dev->flags |= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+       CHECK_PPP_VOID();
+       data = skb->data;
+       count = skb->len;
 
-       if (ppp2tty (ppp) == NULL) {
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_ERR
-                       "ppp: %s not connected to a TTY! can't go open!\n",
-                       dev->name);
-               return -ENXIO;
-       }
+       /* dump the buffer */
+       if (ppp->flags & SC_LOG_OUTPKT)
+               ppp_print_buffer ("write frame", data, count);
 
-       if (ppp->flags & SC_DEBUG)
-               printk (KERN_INFO
-                       "ppp: channel %s going up for IP packets!\n",
-                       dev->name);
+       /*
+        * 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;
 
-       CHECK_PPP (-ENXIO);
-       return 0;
-}
+       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;
 
-/*
- * Callback from the network layer when the ppp device goes down.
- */
+       case PPP_CCP:
+               /*
+                * Outbound compression control frames
+                */
+               ppp_proto_ccp(ppp, data + PPP_HDRLEN, count - PPP_HDRLEN, 0);
+               break;
+       }
+       data = skb->data;
+       count = skb->len;
 
-static int
-ppp_dev_close (struct device *dev)
-{
-       struct ppp *ppp = dev2ppp (dev);
+       /*
+        * 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->xmit_busy = 0;
+                       return;
+               }
+               LIBERATE_SKB(new_skb);
 
-       if (ppp2tty (ppp) == NULL) {
-               return -ENXIO;
+               /* 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
+        */
+       if ( ppp->flags & SC_SYNC ) 
+               ret = ppp_sync_send(ppp, skb);
+       else
+               ret = ppp_async_send(ppp, skb);
+       if (ret > 0) {
+               /* we can release the lock */
+               ppp->xmit_busy = 0;
+       } else if (ret < 0) {
+               /* can't happen, since the caller got the xmit_busy lock */
+               printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n");
        }
-/*
- * We don't do anything about the device going down. It is not important
- * for us.
- */
-       if (ppp->flags & SC_DEBUG)
-               printk (KERN_INFO
-                       "ppp: channel %s going down for IP packets!\n",
-                       dev->name);
-       CHECK_PPP (-ENXIO);
-       return 0;
 }
 
 /*
- * IOCTL operation to read the version of the driver.
+ * Apply VJ TCP header compression to a packet.
  */
-
-static int
-ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr)
+static struct sk_buff *
+ppp_vj_compress(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
- */
-       COPY_TO_USER (error,
-                     result,
-                     szVersion,
-                     len);
+       __u8 *orig_data, *data;
+       struct sk_buff *new_skb;
+       int len, proto;
 
-       return error;
-}
+       new_skb = alloc_skb(skb->len, GFP_ATOMIC);
+       if (new_skb == NULL) {
+               printk(KERN_ERR "ppp: no memory for vj compression\n");
+               return skb;
+       }
+       LIBERATE_SKB(new_skb);
 
-/*
- * IOCTL to read the statistics for the pppstats program.
- */
+       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);
 
-static int
-ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
-{
-       struct ppp_stats *result, temp;
-       int    error;
-/*
- * Supply the information for the caller. First move the version data
- * then move the ppp stats; and finally the vj stats.
- */
-       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;
-               }
+       if (data == orig_data) {
+               /* Couldn't compress the data */
+               KFREE_SKB(new_skb);
+               return skb;
        }
 
-       result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+       /* 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];
+       }
 
-       COPY_TO_USER (error,
-                     result,
-                     &temp,
-                     sizeof (temp));
+       data = skb_put(new_skb, len + PPP_HDRLEN);
+       data[0] = PPP_ALLSTATIONS;
+       data[1] = PPP_UI;
+       data[2] = 0;
+       data[3] = proto;
 
-       return error;
+       KFREE_SKB(skb);
+       return new_skb;
 }
 
-/*
- * IOCTL to read the compression statistics for the pppstats program.
- */
-
-static int
-ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+static inline void
+ppp_send_frames(struct ppp *ppp)
 {
-       struct ppp_comp_stats *result, temp;
-       int    error;
+       struct sk_buff *skb;
+
+       while (!test_and_set_bit(0, &ppp->xmit_busy)) {
+               skb = skb_dequeue(&ppp->xmt_q);
+               if (skb == NULL) {
+                       ppp->xmit_busy = 0;
+                       break;
+               }
+               ppp_send_frame(ppp, skb);
+       }
+       if (!ppp->xmit_busy && ppp->dev.tbusy) {
+               ppp->dev.tbusy = 0;
+               mark_bh(NET_BH);
+       }
+}
+
 /*
- * Supply the information for the caller.
+ * Called from the hardware (tty) layer when it can accept
+ * another packet.
  */
-       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 void
+ppp_output_wakeup(struct ppp *ppp)
+{
+       CHECK_PPP_VOID();
 
-               if (ppp->sc_rc_state != NULL)
-                       (*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state,
-                                                      &temp.d);
+       if (!ppp->xmit_busy) {
+               printk(KERN_ERR "ppp_output_wakeup called but xmit_busy==0\n");
+               return;
        }
+       ppp->xmit_busy = 0;
+       ppp_send_frames(ppp);
+}
+
 /*
- * Move the data to the caller's buffer
+ * Send a control frame (from pppd).
  */
-       result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data;
-
-       COPY_TO_USER (error,
-                     result,
-                     &temp,
-                     sizeof (temp));
+static void
+ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP_VOID();
 
-       return error;
+       /*
+        * 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.
+ *************************************************************/
 /*
- * Callback from the network layer to process the sockioctl functions.
+ * Send a frame to the peer.
+ * Returns 1 iff the frame was not accepted.
  */
-
 static int
-ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
+ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
 {
-       struct ppp *ppp = dev2ppp (dev);
-       int error;
+       struct ppp *ppp = dev2ppp(dev);
+       struct tty_struct *tty = ppp2tty(ppp);
+       enum NPmode npmode;
+       int proto;
+       unsigned char *hdr;
 
-       CHECK_PPP_MAGIC(ppp);
-/*
- * Process the requests
- */
-       switch (cmd) {
-       case SIOCGPPPSTATS:
-               error = ppp_dev_ioctl_stats (ppp, ifr, dev);
-               break;
+       /* just a little sanity check. */
+       if (skb == NULL)
+               return 0;
+       if (skb->data == NULL) {
+               KFREE_SKB(skb);
+               return 0;
+       }
 
-       case SIOCGPPPCSTATS:
-               error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev);
-               break;
+       /*
+        * Avoid timing problem should tty hangup while data is
+        * queued to be sent.
+        */
+       if (!ppp->inuse) {
+               KFREE_SKB(skb);
+               return 0;
+       }
 
-       case SIOCGPPPVER:
-               error = ppp_dev_ioctl_version (ppp, ifr);
-               break;
+       /*
+        * 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);
+               KFREE_SKB(skb);
+               return 0;
+       }
 
-       default:
-               error = -EINVAL;
+       /*
+        * 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_IPV6:
+               proto = PPP_IPV6;
+               npmode = ppp->sc_npmode[NP_IPV6];
+               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_INFO "%s: packet for unknown proto %x\n",
+                              ppp->name, ntohs(skb->protocol));
+               KFREE_SKB(skb);
+               return 0;
        }
-       return error;
-}
-
-/*
- * 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.
- */
 
-static int
-ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf,
-                __u8 *data, int len)
-{
-       int     proto = PPP_IP;
-       __u8    *hdr;
-/*
- * Branch on the type of processing for the IP frame.
- */
-       switch (ppp->sc_npmode[NP_IP]) {
+       /*
+        * Drop, accept or reject the packet depending on the mode.
+        */
+       switch (npmode) {
        case NPMODE_PASS:
                break;
 
@@ -3002,228 +2879,107 @@ ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf,
                 * network system to be queued and retransmitted later.
                 */
                if (ppp->flags & SC_DEBUG)
-                       printk(KERN_DEBUG "%s: returning frame\n",
-                              ppp->name);
-               return -1;
+                       printk(KERN_DEBUG "%s: returning frame\n", ppp->name);
+               KFREE_SKB(skb);
+               return 0;
 
        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;
+                       printk(KERN_DEBUG
+                              "ppp_dev_xmit: dropping (npmode = %d) on %s\n",
+                              npmode, ppp->name);
+               KFREE_SKB(skb);
+               return 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;
+       /*
+        * 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.
+        * We deliberately queue as little as possible inside
+        * the ppp driver in order to minimize the latency
+        * for high-priority packets.
+        */
+       if (test_and_set_bit(0, &ppp->xmit_busy)) {
+               dev->tbusy = 1; /* can't take it now */
+               return 1;
        }
-/*
- * 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;
+       dev->tbusy = 0;
+
+       /*
+        * 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);
+                       KFREE_SKB(skb);
+                       ppp->xmit_busy = 0;
+                       ppp_send_frames(ppp);
+                       return 0;
                }
+               LIBERATE_SKB(new_skb);
+               skb_reserve(new_skb, PPP_HDRLEN);
+               memcpy(skb_put(new_skb, skb->len), skb->data, skb->len);
+               KFREE_SKB(skb);
+               skb = new_skb;
        }
-/*
- * Send the frame
- */
-       len  += PPP_HDRLEN;
-       hdr   = data - PPP_HDRLEN;
 
+       hdr = skb_push(skb, PPP_HDRLEN);
        hdr[0] = PPP_ALLSTATIONS;
        hdr[1] = PPP_UI;
-       hdr[2] = 0;
+       hdr[2] = proto >> 8;
        hdr[3] = proto;
 
-       return ppp_dev_xmit_frame (ppp, ppp->wbuf, hdr, len);
+       ppp_send_frame(ppp, skb);
+       if (!ppp->xmit_busy)
+               ppp_send_frames(ppp);
+       return 0;
 }
 
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
 /*
- * 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.
+ * Null hard_header and header_rebuild routines.
  */
-static int
-ppp_dev_xmit_other (struct device *dev, struct ppp *ppp,
-                 __u8 *data, int len, int proto)
+static int ppp_dev_header(struct sk_buff *skb, struct device *dev,
+                         unsigned short type, void *daddr,
+                         void *saddr, unsigned int len)
 {
-       __u8    *hdr;
-/*
- * Send the frame
- */
-       len += PPP_HDRLEN;
-       hdr = data - PPP_HDRLEN;
-
-       hdr[0] = PPP_ALLSTATIONS;
-       hdr[1] = PPP_UI;
-       hdr[2] = proto >> 8;
-       hdr[3] = proto;
-
-       return ppp_dev_xmit_frame (ppp, ppp->wbuf, hdr, len);
+       return 0;
 }
 
-/*
- * Send a frame to the remote.
- */
-
-static int
-ppp_dev_xmit (sk_buff *skb, struct device *dev)
+static int ppp_dev_rebuild(void *eth, struct device *dev,
+                          unsigned long raddr, struct sk_buff *skb)
 {
-       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");
-               return 0;
-       }
-/*
- * Avoid timing problem should tty hangup while data is queued to be sent
- */
-       if (!ppp->inuse) {
-               dev_kfree_skb (skb, FREE_WRITE);
-               return 0;
-       }
-/*
- * 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, FREE_WRITE);
-               return 0;
-       }
-/*
- * Fetch the pointer to the data
- */
-       len   = skb->len;
-       data  = skb_data(skb);
-
-       if (data == (__u8 *) 0) {
-               if (ppp->flags & SC_DEBUG)
-                       printk (KERN_CRIT "ppp_dev_xmit: %s Null skb data\n",
-                               dev->name);
-               dev_kfree_skb (skb, FREE_WRITE);
-               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=%d w=%d\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);
-               break;
-
-       default: /* All others have no support at this time. */
-               dev_kfree_skb (skb, FREE_WRITE);
-               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, FREE_WRITE);
-               ppp->ddinfo.xmit_idle = jiffies;
-       } else {
-               ppp->wbuf->locked = 0;
-               if (answer < 0) {
-                       /* packet should be dropped */
-                       dev_kfree_skb (skb, FREE_WRITE);
-                       answer = 0;
-               } else {
-                       /* packet should be queued for later */
-                       dev->tbusy = 1;
-               }
-       }
-       return answer;
+       return 0;
 }
+#endif /* < 2.1.15 */
 
 /*
  * Generate the statistic information for the /proc/net/dev listing.
  */
-
-static struct enet_statistics *
+static struct net_device_stats *
 ppp_dev_stats (struct device *dev)
 {
        struct ppp *ppp = dev2ppp (dev);
 
-       ppp->estats.rx_packets        = ppp->stats.ppp_ipackets;
-       ppp->estats.rx_errors         = ppp->stats.ppp_ierrors;
-       ppp->estats.tx_packets        = ppp->stats.ppp_opackets;
-       ppp->estats.tx_errors         = ppp->stats.ppp_oerrors;
+       ppp->estats.rx_packets = ppp->stats.ppp_ipackets;
+       ppp->estats.rx_errors  = ppp->stats.ppp_ierrors;
+       ppp->estats.tx_packets = ppp->stats.ppp_opackets;
+       ppp->estats.tx_errors  = ppp->stats.ppp_oerrors;
+#if LINUX_VERSION_CODE >= VERSION(2,1,25)
+       ppp->estats.rx_bytes   = ppp->stats.ppp_ibytes;
+       ppp->estats.tx_bytes   = ppp->stats.ppp_obytes;
+#endif
 
        return &ppp->estats;
 }
 
-#if LINUX_VERSION_CODE < VERSION(2,1,15)
-static int ppp_dev_header (sk_buff *skb, struct device *dev,
-                          __u16 type, void *daddr,
-                          void *saddr, unsigned int len)
-{
-       return (0);
-}
-
-static int
-ppp_dev_rebuild (void *eth, struct device *dev,
-                unsigned long raddr, struct sk_buff *skb)
-{
-       return (0);
-}
-#endif
-
 /*************************************************************
  * UTILITIES
  *    Miscellany called by various functions above.
@@ -3231,7 +2987,7 @@ ppp_dev_rebuild (void *eth, struct device *dev,
 
 /* Locate the previous instance of the PPP channel */
 static struct ppp *
-ppp_find (int pid_value)
+ppp_find(int pid_value)
 {
        struct ppp      *ppp;
 
@@ -3247,7 +3003,7 @@ ppp_find (int pid_value)
 
 /* allocate or create a PPP channel */
 static struct ppp *
-ppp_alloc (void)
+ppp_alloc(void)
 {
        int             if_num;
        int             status;
@@ -3255,60 +3011,154 @@ ppp_alloc (void)
        struct ppp      *ppp;
 
        /* try to find an free device */
-       if_num = 0;
        for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-               if (!set_bit(0, &ppp->inuse))
-                       return ppp;
-               ++if_num;
+               if (!test_and_set_bit(0, &ppp->inuse)) {
+                       dev = ppp2dev(ppp);
+                       if (dev->flags & IFF_UP) {
+                               clear_bit(0, &ppp->inuse);
+                               continue;
+                       }
+                       /* 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 */
+               }
        }
-/*
- * 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->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 LINUX_VERSION_CODE < VERSION(2,1,31)
+       if_num = (ppp_list == 0)? 0: ppp_last->line + 1;
+       sprintf(ppp->name, "ppp%d", if_num);
+#else
+       if_num = dev_alloc_name(dev, "ppp%d");
+#endif
+       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;
+       ppp->xmit_busy  = 0;
+
+       /* 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);
 
+       ppp->inuse = 0;
+       if (ppp->dev.tbusy) {
+               ppp->dev.tbusy = 0;
+               mark_bh(NET_BH);
+       }
+}
+
+/*
+ * Utility procedures to print a buffer in hex/ascii
+ */
 static void
 ppp_print_hex (register __u8 * out, const __u8 * in, int count)
 {
@@ -3343,18 +3193,18 @@ ppp_print_char (register __u8 * out, const __u8 * in, int count)
 }
 
 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;
        }
@@ -3363,7 +3213,7 @@ ppp_print_buffer (const __u8 * name, const __u8 * buf, int count)
                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);
        }
 }
 
@@ -3404,7 +3254,8 @@ static int ppp_register_compressor (struct compressor *cp)
        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;
@@ -3464,67 +3315,53 @@ init_module(void)
        /* register our line disciplines */
        status = ppp_first_time();
        if (status != 0)
-               printk (KERN_INFO
-                      "PPP: ppp_init() failure %d\n", status);
+               printk(KERN_INFO "PPP: ppp_init() failure %d\n", status);
 #if LINUX_VERSION_CODE < VERSION(2,1,18)
        else
                (void) register_symtab (&ppp_syms);
 #endif
-       return (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 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;
        }
-/*
- * Ensure that there are no compressor modules registered
- */
-       if (ppp_compressors != NULL)
-               busy_flag = 1;
+       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);
        }
 }