]> git.ozlabs.org Git - ppp.git/blobdiff - linux/ppp.c
Apply patch for sync serial support from Paul Fulghum.
[ppp.git] / linux / ppp.c
index 32d5fa604aa1a9c6ea5054c9f31094ca8413e05e..777afa793ab73d84ffa43e243bdaa5144840bf91 100644 (file)
@@ -1,8 +1,20 @@
-/*
-   PPP for Linux
-
-   $Id: ppp.c,v 1.1 1994/05/27 00:55:25 paulus Exp $
-*/
+/*  PPP for Linux
+ *
+ *  Michael Callahan <callahan@maths.ox.ac.uk>
+ *  Al Longyear <longyear@netcom.com>
+ *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
+ *
+ *  ==FILEVERSION 990325==
+ *
+ *  NOTE TO MAINTAINERS:
+ *     If you modify this file at all, please set the number above to the
+ *     date of the modification as YYMMDD (year month day).
+ *     ppp.c is shipped with a PPP distribution as well as with the kernel;
+ *     if everyone increases the FILEVERSION number above, then scripts
+ *     can do the right thing when deciding whether to install a new ppp.c
+ *     file.  Don't change the format of that line otherwise, so the
+ *     installation script can recognize it.
+ */
 
 /*
    Sources:
 
    Flags for this module (any combination is acceptable for testing.):
 
-   NET02D            - Define if using Net-2-Debugged in kernels earler
-                       than v1.1.4.
-
-   NEW_TTY_DRIVERS    -        Define if using new Ted Ts'o's alpha TTY drivers
-                       from tsx-11.mit.edu. From Ted Ts'o.
-
-   OPTIMIZE_FLAG_TIME -        Number of jiffies to force sending of leading flag
+   OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag
                        character. This is normally set to ((HZ * 3) / 2).
-                       This is 1.5 seconds. If not defined then the leading
-                       flag is always sent.  
+                       This is 1.5 seconds. If zero then the leading
+                       flag is always sent.
+
+   CHECK_CHARACTERS   - Enable the checking on all received characters for
+                       8 data bits, no parity. This adds a small amount of
+                       processing for each received character.
 */
 
-/* #define NET02D                                      /* */
-/* #define NEW_TTY_DRIVERS                     /* */
-#define OPTIMIZE_FLAG_TIME  ((HZ * 3)/2)       /* */
+#define OPTIMIZE_FLAG_TIME     ((HZ * 3)/2)
+#define CHECK_CHARACTERS       1
+
+#define PPP_MAX_RCV_QLEN       32      /* max # frames we queue up for pppd */
 
+/* $Id: ppp.c,v 1.22 1999/03/30 06:33:07 paulus Exp $ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
+
+/* 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/ioport.h>
+#endif
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,23)
+#include <linux/poll.h>
+#endif
+
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/tty.h>
 #include <linux/errno.h>
-#include <linux/sched.h>   /* to get the struct task_struct */
-#include <linux/string.h>  /* used in new tty drivers */
-#include <linux/signal.h>  /* used in new tty drivers */
+#include <linux/sched.h>       /* to get the struct task_struct */
+#include <linux/string.h>      /* used in new tty drivers */
+#include <linux/signal.h>      /* used in new tty drivers */
 #include <asm/system.h>
 #include <asm/bitops.h>
-#include <asm/segment.h>
-
-#ifdef NET02D                          /* v1.1.4 net code and earlier */
-#include <dev.h>
-#include <skbuff.h>
-#define        skb_queue_head_init(buf)        *(buf) = NULL
-#else                                  /* v1.1.5 and later */
+#include <linux/if.h>
+#include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#endif
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <net/slhc_vj.h>
 
-#include <linux/ppp.h>
+#define fcstab ppp_crc16_table         /* Name of the table in the kernel */
+#include <linux/ppp_defs.h>
 
-#include <ip.h>
-#include <tcp.h>
-#include <inet.h>
-#include "slhc.h"
+#include <linux/socket.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppvar.h>
+#include <linux/ppp-comp.h>
 
-#include <linux/if_arp.h>
-#ifndef ARPHRD_PPP
-#define ARPHRD_PPP 0
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
 #endif
 
-#define PRINTK(p) printk p ;
-#define ASSERT(p) if (!p) PRINTK ((KERN_CRIT "assertion failed: " # p))
-#define PRINTKN(n,p) {if (ppp_debug >= n) PRINTK (p)}
-#define CHECK_PPP(a)  if (!ppp->inuse) { PRINTK ((ppp_warning, __LINE__)) return a;}
-#define CHECK_PPP_VOID()  if (!ppp->inuse) { PRINTK ((ppp_warning, __LINE__)) return;}
+#undef PPP_VERSION
+#define PPP_VERSION    "2.3.6"
 
-#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
-#define in_rmap(ppp,c) ((((unsigned int) (unsigned char) (c)) < 0x20) && \
-                       ppp->recv_async_map & (1 << (c)))
+#if LINUX_VERSION_CODE >= VERSION(2,1,4)
 
-#define bset(p,b)      ((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
+#if LINUX_VERSION_CODE >= VERSION(2,1,5)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
 
-int ppp_debug = 2;
-int ppp_debug_netpackets = 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
+
+#else  /* 2.0.x and 2.1.x before 2.1.4 */
+
+#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
 
-/* Define this string only once for all macro envocations */
-static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
+#if LINUX_VERSION_CODE < VERSION(2,1,25)
+#define net_device_stats       enet_statistics
+#endif
 
-int ppp_init(struct device *);
-static void ppp_init_ctrl_blk(struct ppp *);
-static int ppp_dev_open(struct device *);
-static int ppp_dev_close(struct device *);
-static void ppp_kick_tty(struct ppp *);
+#if LINUX_VERSION_CODE < VERSION(2,1,57)
+#define signal_pending(tsk)    ((tsk)->signal & ~(tsk)->blocked)
+#endif
 
-#ifdef NEW_TTY_DRIVERS
-#define ppp_find(tty) ((struct ppp *) tty->disc_data)
+#if LINUX_VERSION_CODE < VERSION(2,1,60)
+typedef int            rw_ret_t;
+typedef unsigned int   rw_count_t;
 #else
-static void ppp_output_done(void *);
-static void ppp_unesc(struct ppp *ppp, unsigned char *c, int n);
-static struct ppp *ppp_find(struct tty_struct *);
+typedef ssize_t                rw_ret_t;
+typedef size_t         rw_count_t;
 #endif
 
-static void ppp_doframe(struct ppp *);
-static int ppp_do_ip(struct ppp *, unsigned short, unsigned char *, int);
-static int ppp_us_queue(struct ppp *, unsigned short, unsigned char *, int);
-static int ppp_xmit(struct sk_buff *, struct device *);
-static unsigned short ppp_type_trans(struct sk_buff *, struct device *);
-
-#ifdef NET02D
-static int ppp_header(unsigned char *buff, struct device *dev,
-                     unsigned short type, unsigned long daddr,
-                     unsigned long saddr, unsigned len);
-static int ppp_rebuild_header(void *buff, struct device *dev);
-static void ppp_add_arp(unsigned long addr, struct sk_buff *skb,
-                       struct device *dev);
+#if LINUX_VERSION_CODE < VERSION(2,1,86)
+#define KFREE_SKB(s)   dev_kfree_skb((s), FREE_WRITE)
 #else
-static int ppp_header(unsigned char *, struct device *, unsigned short,
-                     void *, void *, unsigned, struct sk_buff *);
-static int ppp_rebuild_header(void *, struct device *, unsigned long,
-                             struct sk_buff *);
+#define KFREE_SKB(s)   kfree_skb(s)
 #endif
 
-static struct enet_statistics *ppp_get_stats (struct device *);
-static struct ppp *ppp_alloc(void);
-static int ppp_lock(struct ppp *);
-static void ppp_unlock(struct ppp *);
-static void ppp_add_fcs(struct ppp *);
-static int ppp_check_fcs(struct ppp *);
-static void ppp_print_buffer(const char *,char *,int,int);
-
-static int ppp_read(struct tty_struct *, struct file *, unsigned char *,
-                   unsigned int);
-static int ppp_write(struct tty_struct *, struct file *, unsigned char *,
-                    unsigned int);
-static int ppp_ioctl(struct tty_struct *, struct file *, unsigned int,
-                    unsigned long);
-static int ppp_select(struct tty_struct *tty, struct inode * inode,
-                     struct file * filp, int sel_type, select_table * wait);
-static int ppp_open(struct tty_struct *);
-static void ppp_close(struct tty_struct *);
-
-#ifdef NEW_TTY_DRIVERS
-static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
-                           char *fp, int count);
-static void ppp_write_wakeup(struct tty_struct *tty);
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+#define LIBERATE_SKB(s)        ((s)->free = 1)
 #else
-static void ppp_tty_input_ready(struct tty_struct *);
+#define LIBERATE_SKB(s)        do { } while (0)
 #endif
 
-/* FCS table from RFC1331 */
-
-static unsigned short fcstab[256] = {
-  0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
-  0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
-  0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
-  0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
-  0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
-  0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
-  0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
-  0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
-  0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
-  0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
-  0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
-  0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
-  0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
-  0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
-  0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
-  0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
-  0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
-  0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
-  0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
-  0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
-  0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
-  0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
-  0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
-  0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
-  0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
-  0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
-  0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
-  0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
-  0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
-  0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
-  0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
-  0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-  };
-
-struct tty_ldisc ppp_ldisc;
-
-static struct ppp ppp_ctrl[PPP_NRUNIT];
+#if LINUX_VERSION_CODE < VERSION(2,1,95)
+#define SUSER()                suser()
+#else
+#define SUSER()                capable(CAP_NET_ADMIN)
+#endif
 
-/*************************************************************
- * INITIALIZATION
- *************************************************************/
+/*
+ * Local functions
+ */
 
-static int first_time = 1;
+#ifdef CONFIG_MODULES
+static int ppp_register_compressor (struct compressor *cp);
+static void ppp_unregister_compressor (struct compressor *cp);
+#endif
 
-/* called at boot time for each ppp device */
+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 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 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);
+
+#ifndef OPTIMIZE_FLAG_TIME
+#define OPTIMIZE_FLAG_TIME     0
+#endif
 
-int
-ppp_init(struct device *dev)
-{
-  struct ppp *ppp;
-  int i;
+/*
+ * Parameters which may be changed via insmod.
+ */
 
-  ppp = &ppp_ctrl[dev->base_addr];
+static int  flag_time = OPTIMIZE_FLAG_TIME;
+#if LINUX_VERSION_CODE >= VERSION(2,1,19) 
+MODULE_PARM(flag_time, "i");
+#endif
 
-  if (first_time) {
-    first_time = 0;
+#define CHECK_PPP_MAGIC(ppp)   do { \
+       if (ppp->magic != PPP_MAGIC) { \
+               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__); \
+               return a; \
+       } \
+} while (0)
+#define CHECK_PPP_VOID() do { \
+       CHECK_PPP_MAGIC(ppp); \
+       if (!ppp->inuse) { \
+               printk(ppp_warning, __LINE__); \
+               return; \
+       } \
+} while (0)
+
+#define tty2ppp(tty)   ((struct ppp *) ((tty)->disc_data))
+#define dev2ppp(dev)   ((struct ppp *) ((dev)->priv))
+#define ppp2tty(ppp)   ((ppp)->tty)
+#define ppp2dev(ppp)   (&(ppp)->dev)
+
+static struct ppp *ppp_list = NULL;
+static struct ppp *ppp_last = NULL;
+
+/* 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";
 
-    printk (KERN_INFO "PPP: version %s (%d channels)"
-#ifdef NET02D
-          " NET02D"
-#endif
-#ifdef NEW_TTY_DRIVERS
-          " NEW_TTY_DRIVERS"
-#endif
-#ifdef OPTIMIZE_FLAG_TIME
-          " OPTIMIZE_FLAGS"
+static char szVersion[]                = PPP_VERSION;
+
+#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
-          "\n", PPP_VERSION, PPP_NRUNIT);
-
-    printk (KERN_INFO
-          "TCP compression code copyright 1989 Regents of the "
-          "University of California\n");
-
-    (void) memset(&ppp_ldisc, 0, sizeof(ppp_ldisc));
-    ppp_ldisc.open    = ppp_open;
-    ppp_ldisc.close   = ppp_close;
-    ppp_ldisc.read    = ppp_read;
-    ppp_ldisc.write   = ppp_write;
-    ppp_ldisc.ioctl   = ppp_ioctl;
-    ppp_ldisc.select  = ppp_select;
-
-#ifdef NEW_TTY_DRIVERS
-    ppp_ldisc.magic       = TTY_LDISC_MAGIC;
-    ppp_ldisc.receive_buf = ppp_receive_buf;
-    ppp_ldisc.write_wakeup = ppp_write_wakeup;
+
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ *    The following code implements the PPP line discipline
+ *    and supports using PPP on an async serial line.
+ *************************************************************/
+
+#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)))
+
+/*
+ * TTY callbacks
+ */
+
+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
-    ppp_ldisc.handler     = ppp_tty_input_ready;
+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] =
+{
+       0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+       0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+       0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+       0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+       0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+       0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+       0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+       0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+       0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+       0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+       0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+       0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+       0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+       0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+       0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+       0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+       0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+       0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+       0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+       0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+       0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+       0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+       0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+       0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+       0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+       0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+       0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+       0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+       0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+       0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+       0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+       0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
 
-    if ((i = tty_register_ldisc(N_PPP, &ppp_ldisc)) == 0)
-      printk(KERN_INFO "PPP line discipline registered.\n");
-    else
-      printk(KERN_ERR "error registering line discipline: %d\n", i);
-  }
-
-  /* initialize PPP control block */
-  ppp_init_ctrl_blk (ppp);
-  ppp->inuse = 0;
-  ppp->line  = dev->base_addr;
-  ppp->tty   = NULL;
-  ppp->dev   = dev;
-
-  /* clear statistics */
-  memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
-
-  /* device INFO */
-  dev->mtu             = PPP_MTU;
-  dev->hard_start_xmit = ppp_xmit;
-  dev->open            = ppp_dev_open;
-  dev->stop            = ppp_dev_close;
-  dev->get_stats       = ppp_get_stats;
-  dev->hard_header     = ppp_header;
-  dev->type_trans      = ppp_type_trans;
-  dev->rebuild_header  = ppp_rebuild_header;
-  dev->hard_header_len = 0;
-  dev->addr_len        = 0;
-  dev->type            = ARPHRD_PPP;
-
-#ifdef NET02D
-  dev->add_arp         = ppp_add_arp;
-  dev->queue_xmit      = dev_queue_xmit;
+#ifdef CHECK_CHARACTERS
+static __u32 paritytab[8] =
+{
+       0x96696996, 0x69969669, 0x69969669, 0x96696996,
+       0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
 #endif
 
-  for (i = 0; i < DEV_NUMBUFFS; i++)
-    skb_queue_head_init(&dev->buffs[i]);  /* = NULL if NET02D */
+/*
+ * This procedure is called at initialization time to register
+ * the PPP line discipline.
+ */
+static int
+ppp_first_time(void)
+{
+       static struct tty_ldisc ppp_ldisc;
+       int    status;
 
-  /* New-style flags */
-  dev->flags      = IFF_POINTOPOINT;
-  dev->family     = AF_INET;
-  dev->pa_addr    = 0;
-  dev->pa_brdaddr = 0;
-  dev->pa_mask    = 0;
-  dev->pa_alen    = sizeof(unsigned long);
+       printk(KERN_INFO
+              "PPP: version %s (demand dialling)"
+              "\n", szVersion);
 
-  return 0;
-}
+#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");
+#endif
 
-static void
-ppp_init_ctrl_blk(struct ppp *ppp)
-{
-  ppp->magic           = PPP_MAGIC;
-  ppp->sending         = 0;
-  ppp->toss            = 0xFE;
-  ppp->escape          = 0;
-
-  ppp->flags           = 0;
-  ppp->mtu             = PPP_MTU;
-  ppp->mru             = PPP_MRU;
-  ppp->fcs             = 0;
-
-  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->slcomp          = NULL;
-  ppp->rbuff           = NULL;
-  ppp->xbuff           = NULL;
-  ppp->cbuff           = NULL;
-
-  ppp->rhead           = NULL;
-  ppp->rend            = NULL;
-  ppp->rcount          = 0;
-  ppp->xhead           = NULL;
-  ppp->xtail           = NULL;
-
-  ppp->us_rbuff                = NULL;
-  ppp->us_rbuff_end    = NULL;
-  ppp->us_rbuff_head   = NULL;
-  ppp->us_rbuff_tail   = NULL;
-  ppp->read_wait       = NULL;
-  ppp->write_wait      = NULL;
-  ppp->us_rbuff_lock   = 0;
-  ppp->inp_sig         = 0;
-  ppp->inp_sig_pid     = 0;
-
-#ifdef OPTIMIZE_FLAG_TIME /* ensure flag will always be sent first time */
-  ppp->last_xmit       = jiffies - OPTIMIZE_FLAG_TIME;
+       /*
+        * 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;
+       ppp_ldisc.write         = ppp_tty_write;
+       ppp_ldisc.ioctl         = ppp_tty_ioctl;
+#if LINUX_VERSION_CODE < VERSION(2,1,23)
+       ppp_ldisc.select        = ppp_tty_select;
 #else
-  ppp->last_xmit       = 0;
+       ppp_ldisc.poll          = ppp_tty_poll;
 #endif
+       ppp_ldisc.receive_room  = ppp_tty_room;
+       ppp_ldisc.receive_buf   = ppp_tty_receive;
+       ppp_ldisc.write_wakeup  = ppp_tty_wakeup;
+
+       status = tty_register_ldisc (N_PPP, &ppp_ldisc);
+       if (status == 0)
+               printk(KERN_INFO "PPP line discipline registered.\n");
+       else
+               printk(KERN_ERR "error registering line discipline: %d\n",
+                      status);
+       return status;
+}
 
-  /* clear statistics */
-  memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
 
-  /* Reset the demand dial information */
-  ppp->ddinfo.ip_sjiffies  =
-  ppp->ddinfo.ip_rjiffies  =
-  ppp->ddinfo.nip_sjiffies =
-  ppp->ddinfo.nip_rjiffies = jiffies;
+#ifndef MODULE
+/*
+ * Called at boot time if the PPP driver is compiled into the kernel.
+ */
+int
+ppp_init(struct device *dev)
+{
+       static int first_time = 1;
+       int    answer = 0;
+
+       if (first_time) {
+               first_time = 0;
+               answer     = ppp_first_time();
+#if LINUX_VERSION_CODE < VERSION(2,1,18)
+               if (answer == 0)
+                       (void) register_symtab(&ppp_syms);
+#endif
+       }
+       if (answer == 0)
+               answer = -ENODEV;
+       return answer;
 }
+#endif
 
 /*
- * 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.
+ * Initialize the async-specific parts of the ppp structure.
  */
-   
 static void
-ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
+ppp_async_init(struct ppp *ppp)
 {
-  struct device *dev;
-  unsigned char *new_rbuff, *new_xbuff, *new_cbuff;
-  unsigned char *old_rbuff, *old_xbuff, *old_cbuff;
-  int mtu, mru;
-/*
- *  Allocate the buffer from the kernel for the data
- */
-  dev = ppp->dev;
-  mru = new_mru;
-  mtu = new_mtu;
+       ppp->escape = 0;
+       ppp->toss   = 0xE0;
+       ppp->tty_pushing = 0;
 
-  /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
-  if (mru < PPP_MRU)
-    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    = 0xffffffff;
 
-  mtu = (mtu * 2) + 20;
-  mru = (mru * 2) + 20;
+       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;
+}
 
-  PRINTKN (2,(KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
-             dev->name, new_mtu, new_mru));
-       
-  new_xbuff = (unsigned char *) kmalloc(mtu + 4, GFP_KERNEL);
-  new_rbuff = (unsigned char *) kmalloc(mru + 4, GFP_KERNEL);
-  new_cbuff = (unsigned char *) kmalloc(mru + 4, GFP_KERNEL);
 /*
- *  If the buffers failed to allocate then complain.
+ * Clean up the async-specific parts of the ppp structure.
  */
-  if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL)
-    {
-      PRINTKN (2,(KERN_ERR "ppp: failed to allocate new buffers\n"));
+static void
+ppp_async_release(struct ppp *ppp)
+{
+       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;
+}
+
 /*
- *  Release new buffer pointers if the updates were not performed
+ * TTY callback.
+ *
+ * Called when the tty discipline is switched to PPP.
  */
-      if (new_rbuff != NULL)
-       kfree (new_rbuff);
 
-      if (new_xbuff != NULL)
-       kfree (new_xbuff);
+static int
+ppp_tty_open (struct tty_struct *tty)
+{
+       struct ppp *ppp;
+
+       /*
+        * Allocate a ppp structure to use.
+        */
+       tty->disc_data = NULL;
+       ppp = ppp_find(current->pid);
+       if (ppp != NULL) {
+               /*
+                * If we are taking over a ppp unit which is currently
+                * connected to a loopback pty, there's not much to do.
+                */
+               CHECK_PPP(-EINVAL);
 
-      if (new_cbuff != NULL)
-       kfree (new_cbuff);
-    }
-/*
- *  Update the pointers to the new buffer structures.
- */
-  else
-    {
-      cli();
-      old_xbuff       = ppp->xbuff;
-      old_rbuff       = ppp->rbuff;
-      old_cbuff       = ppp->cbuff;
+       } else {
+               ppp = ppp_alloc();
+               if (ppp == NULL) {
+                       printk(KERN_ERR "ppp_alloc failed\n");
+                       return -ENFILE;
+               }
+
+               /*
+                * Initialize the control block
+                */
+               ppp_generic_init(ppp);
+               ppp_async_init(ppp);
+
+               MOD_INC_USE_COUNT;
+       }
 
-      ppp->xbuff      = new_xbuff;
-      ppp->rbuff      = new_rbuff;
-      ppp->cbuff      = new_cbuff;
+       tty->disc_data = ppp;
+       ppp->tty       = tty;
 
-      dev->mem_start  = (unsigned long) new_xbuff;
-      dev->mem_end    = (unsigned long) (dev->mem_start + mtu);
+       /*
+        * Flush any pending characters in the driver
+        */
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer (tty);
 
-      dev->rmem_start = (unsigned long) new_rbuff;
-      ppp->rend       = (unsigned char *)
-      dev->rmem_end   = (unsigned long) (dev->rmem_start + mru);
+       return ppp->line;
+}
 
-      ppp->rhead      = new_rbuff;
 /*
- *  Update the parameters for the new buffer sizes
+ * TTY callback.
+ *
+ * Called when the line discipline is changed to something
+ * else, the tty is closed, or the tty detects a hangup.
  */
-      ppp->toss                = 0xFE;
-      ppp->escape      = 0;
-      ppp->sending     = 0;
-      ppp->rcount      = 0;
 
-      ppp->mru         = new_mru;
+static void
+ppp_tty_close (struct tty_struct *tty)
+{
+       struct ppp *ppp = tty2ppp(tty);
 
-      ppp->mtu         =
-      dev->mtu         = new_mtu;
+       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);
+       } else {
+               ppp->tty = 0;
+               ppp->sc_xfer = 0;
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_INFO "ppp: channel %s closing.\n",
+                              ppp2dev(ppp)->name);
+
+               ppp_async_release(ppp);
+               ppp_release(ppp);
+               MOD_DEC_USE_COUNT;
+       }
+}
 
-      sti();
 /*
- *  Release old buffer pointers
+ * Read a PPP frame from the rcv_q list,
+ * waiting if necessary
  */
-      if (old_rbuff != NULL)
-       kfree (old_rbuff);
+static rw_ret_t
+ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
+            rw_count_t nr)
+{
+       struct ppp *ppp = tty2ppp (tty);
+       struct sk_buff *skb;
+       rw_ret_t len, err;
+
+       /*
+        * Validate the pointers
+        */
+       if (!ppp)
+               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.
+        */
+       err = verify_area(VERIFY_WRITE, buf, nr);
+       if (err != 0)
+               return (err);
+
+       /*
+        * 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;
+
+               skb = skb_dequeue(&ppp->rcv_q);
+               if (skb != 0)
+                       break;
+
+               /*
+                * If no frame is available, return -EAGAIN or wait.
+                */
+               err = -EAGAIN;
+               if (file->f_flags & O_NONBLOCK)
+                       break;
+
+               interruptible_sleep_on(&ppp->read_wait);
+               err = -EINTR;
+               if (signal_pending(current))
+                       break;
+       }
+       MOD_DEC_USE_COUNT;
+       if (skb == 0)
+               return err;
+
+       /*
+        * Ensure that the frame will fit within the caller's buffer.
+        * If not, just discard the frame.
+        */
+       len = skb->len;
+       if (len > nr) {
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_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;
+       }
 
-      if (old_xbuff != NULL)
-       kfree (old_xbuff);
+       /*
+        * Copy the received data from the buffer to the caller's area.
+        */
+       err = len;
+       if (COPY_TO_USER(buf, skb->data, len))
+               err = -EFAULT;
 
-      if (old_cbuff != NULL)
-       kfree (old_cbuff);
-    }
+out:
+       KFREE_SKB(skb);
+       return err;
 }
 
-/* called when we abandon the PPP line discipline */
-
-static void
-ppp_release(struct ppp *ppp)
+/*
+ * Writing to a tty in ppp line discipline sends a PPP frame.
+ * Used by pppd to send control packets (LCP, etc.).
+ */
+static rw_ret_t
+ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data,
+             rw_count_t count)
 {
-#ifdef NEW_TTY_DRIVERS
-  if (ppp->tty != NULL && ppp->tty->disc_data == ppp)
-    ppp->tty->disc_data = NULL; /* Break the tty->ppp link */
-#endif
+       struct ppp *ppp = tty2ppp (tty);
+       __u8 *new_data;
+       struct sk_buff *skb;
+
+       /*
+        * 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;
+       }
 
-  if (ppp->dev) {
-    ppp->dev->flags &= ~IFF_UP; /* down the device */
-    ppp->dev->flags |= IFF_POINTOPOINT;
-  }
+       /*
+        * Send the frame
+        */
+       ppp_send_ctrl(ppp, skb);
 
-  kfree (ppp->xbuff);
-  kfree (ppp->cbuff);
-  kfree (ppp->rbuff);
-  kfree (ppp->us_rbuff);
+       return (rw_ret_t) count;
+}
 
-  ppp->xbuff    =
-  ppp->cbuff    =
-  ppp->rbuff    =
-  ppp->us_rbuff = NULL;
+/*
+ * 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_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 = -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:
+               /*
+                * Retrieve the transmit async map
+                */
+               if (PUT_USER(ppp->xmit_async_map[0], (int *) param3))
+                       break;
+               error = 0;
+               break;
+
+       case PPPIOCSASYNCMAP:
+               /*
+                * Set the transmit async map
+                */
+               if (GET_USER(temp_i, (int *) param3))
+                       break;
+               ppp->xmit_async_map[0] = temp_i;
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_INFO
+                              "ppp_tty_ioctl: set xmit asyncmap %x\n",
+                              ppp->xmit_async_map[0]);
+               error = 0;
+               break;
+
+       case PPPIOCSRASYNCMAP:
+               /*
+                * Set the receive async map
+                */
+               if (GET_USER(temp_i, (int *) param3))
+                       break;
+               ppp->recv_async_map = temp_i;
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_INFO
+                              "ppp_tty_ioctl: set rcv asyncmap %x\n",
+                              ppp->recv_async_map);
+               error = 0;
+               break;
+
+       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;
+
+       case PPPIOCSXASYNCMAP:
+               /*
+                * Set the map of characters to be escaped on transmission.
+                */
+               {
+                       __u32 temp_tbl[8];
+
+                       if (COPY_FROM_USER(temp_tbl, (void *) param3,
+                                          sizeof (temp_tbl)))
+                               break;
+
+                       temp_tbl[1]  =  0x00000000;
+                       temp_tbl[2] &= ~0x40000000;
+                       temp_tbl[3] |=  0x60000000;
+
+                       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 FIONREAD:
+               /*
+                * Returns how many bytes are available for a read().
+                */
+               {
+                       unsigned long flags;
+                       struct sk_buff *skb;
+                       int count = 0;
+
+                       save_flags(flags);
+                       cli();
+                       skb = skb_peek(&ppp->rcv_q);
+                       if (skb != 0)
+                               count = skb->len;
+                       restore_flags(flags);
+                       if (PUT_USER(count, (int *) param3))
+                               break;
+                       error = 0;
+               }
+               break;
+
+       default:
+               /*
+                *  All other ioctl() events will come here.
+                */
+               error = ppp_ioctl(ppp, param2, param3);
+               break;
+       }
+       return error;
+}
 
-  if (ppp->slcomp) {
-    slhc_free(ppp->slcomp);
-    ppp->slcomp = NULL;
-  }
+/*
+ * TTY callback.
+ *
+ * Process the select() or poll() statement for the PPP device.
+ */
 
-  ppp->inuse = 0;
-  ppp->tty   = NULL;
+#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;
 }
 
-static void
-ppp_close(struct tty_struct *tty)
+#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 = ppp_find(tty);
+       struct ppp *ppp = tty2ppp(tty);
+       unsigned int mask = 0;
+
+       if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
+               CHECK_PPP(0);
 
-  if (ppp == NULL || ppp->magic != PPP_MAGIC) {
-    PRINTKN (1,(KERN_WARNING "ppp: trying to close unopened tty!\n"));
-  } else {
-    CHECK_PPP_VOID();
-    ppp_release (ppp);
+               poll_wait(filp, &ppp->read_wait, wait);
 
-    PRINTKN (2,(KERN_INFO "ppp: channel %s closing.\n", ppp->dev->name));
-  }
+               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 */
 
-/* called when PPP line discipline is selected on a tty */
-static int
-ppp_open(struct tty_struct *tty)
+/*
+ * This function is called by the tty driver when the transmit buffer has
+ * additional space. It is used by the ppp code to continue to transmit
+ * the current buffer should the buffer have been partially sent.
+ */
+static void
+ppp_tty_wakeup (struct tty_struct *tty)
 {
-  struct ppp *ppp = ppp_find(tty);
-
-  if (ppp) {
-    PRINTKN (1,(KERN_ERR "ppp_open: gack! tty already associated to %s!\n",
-               ppp->magic == PPP_MAGIC ? ppp->dev->name : "unknown"));
-    return -EEXIST;
-  }
-
-  ppp = ppp_alloc();
-  if (ppp == NULL) {
-    PRINTKN (1,(KERN_ERR "ppp_open: couldn't allocate ppp channel\n"));
-    return -ENFILE;
-  }
-
-  /* make sure the channel is actually open */
-  ppp_init_ctrl_blk (ppp);
-
-  ppp->tty = tty;
-
-#ifdef NEW_TTY_DRIVERS
-  tty->disc_data = ppp;
-  if (tty->driver.flush_buffer)
-    tty->driver.flush_buffer(tty);
-  if (tty->ldisc.flush_buffer)
-    tty->ldisc.flush_buffer(tty);
-#else
-  tty_read_flush (tty);
-  tty_write_flush (tty);
-#endif
+       struct ppp *ppp = tty2ppp (tty);
 
-  if ((ppp->slcomp = slhc_init(16, 16)) == NULL) {
-    PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
-    ppp_release (ppp);
-    return -ENOMEM;
-  }
-
-  /* Define the buffers for operation */
-  ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
-  if (ppp->rbuff == NULL) {
-    ppp_release (ppp);
-    return -ENOMEM;
-  }
-
-  /* Allocate a user-level receive buffer */
-  ppp->us_rbuff = kmalloc (RBUFSIZE, GFP_KERNEL);
-  if (ppp->us_rbuff == NULL) {
-    PRINTKN (1,(KERN_ERR "ppp: no space for user receive buffer\n"));
-    ppp_release (ppp);
-    return -ENOMEM;
-  }
-
-  ppp->us_rbuff_head =
-  ppp->us_rbuff_tail = ppp->us_rbuff;
-  ppp->us_rbuff_end  = ppp->us_rbuff + RBUFSIZE;
-
-  PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name));
-
-#ifdef NEW_TTY_DRIVERS
-  return (0);
-#else
-  return (ppp->line);
-#endif
+       tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+       if (!ppp)
+               return;
+       CHECK_PPP_VOID();
+       if (tty != ppp->tty)
+               return;
+
+       if (ppp_tty_push(ppp))
+               ppp_output_wakeup(ppp);
 }
 
-/* called when ppp interface goes "up".  here this just means we start
-   passing IP packets */
+/*
+ * 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.
+ */
 static int
-ppp_dev_open(struct device *dev)
+ppp_sync_send(struct ppp *ppp, struct sk_buff *skb)
 {
-  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
-
-  /* reset POINTOPOINT every time, since dev_close zaps it! */
-  dev->flags |= IFF_POINTOPOINT;
+       unsigned char *data;
+       int islcp;
+       
+       CHECK_PPP(0);
 
-  if (ppp->tty == NULL) {
-    PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n",
-               dev->name));
-    return -ENXIO;
-  }
+       if (ppp->tpkt != NULL)
+               return -1;
+       ppp->tpkt = skb;
 
-  PRINTKN (2,(KERN_INFO "ppp: channel %s going up for IP packets!\n",
-             dev->name));
+       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);
+       }
 
-  CHECK_PPP(-ENXIO);
-  return 0;
+       return ppp_tty_sync_push(ppp);
 }
 
+/*
+ * Push a synchronous frame out to the tty.
+ * Returns 1 if frame accepted (or discarded), 0 otherwise.
+ */
 static int
-ppp_dev_close(struct device *dev)
+ppp_tty_sync_push(struct ppp *ppp)
 {
-  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
-
-  if (ppp->tty == NULL) {
-    PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go down!\n",
-               dev->name));
-    return -ENXIO;
-  }
-
-  PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n",
-             dev->name));
-  CHECK_PPP(-ENXIO);
-  return 0;
+       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 loose */
+               /* 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;
 }
 
-/*************************************************************
- * TTY OUTPUT
- *    The following function delivers a fully-formed PPP
- *    frame in ppp->xbuff to the TTY for output.
- *************************************************************/
-
-#ifdef NEW_TTY_DRIVERS
-static inline void
-#else
-static void
-#endif
-ppp_output_done (void *ppp)
+/*
+ * Send a packet to the peer over an async tty line.
+ * Returns -1 iff the packet could not be accepted at present,
+ * 0 if the packet was accepted but we can't accept another yet, or
+ * 1 if we can accept another packet immediately.
+ * If this procedure returns 0, ppp_output_wakeup will be called
+ * exactly once.
+ */
+static int
+ppp_async_send(struct ppp *ppp, struct sk_buff *skb)
 {
-  /* unlock the transmitter queue */
-  ppp_unlock ((struct ppp *) ppp);
+       CHECK_PPP(0);
 
-  /* If the device is still up then enable the transmitter of the
-     next frame. */
-  if (((struct ppp *) ppp)->dev->flags & IFF_UP)
-    dev_tint (((struct ppp *) ppp)->dev);
+       ppp_tty_push(ppp);
 
-  /* enable any blocked process pending transmission */
-  wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
-}
+       if (ppp->tpkt != NULL)
+               return -1;
+       ppp->tpkt = skb;
+       ppp->tpkt_pos = 0;
 
-#ifndef NEW_TTY_DRIVERS
-static void
-ppp_kick_tty (struct ppp *ppp)
-{
-  register int count = ppp->xhead - ppp->xbuff;
-  register int answer;
-
-  ppp->stats.sbytes += count;
-
-  answer = tty_write_data (ppp->tty,
-                          ppp->xbuff,
-                          count,
-                          ppp_output_done,
-                          (void *) ppp);
-
-  if (answer == 0)
-    ppp_output_done (ppp);   /* Should not happen */
-  else
-    if (answer < 0) {
-      ppp->stats.serrors++;
-      ppp_output_done (ppp); /* unlock the transmitter */
-    }
+       return ppp_tty_push(ppp);
 }
 
-#else
-
-static void
-ppp_kick_tty (struct ppp *ppp)
+/*
+ * Push as much data as possible out to the tty.
+ * Returns 1 if we finished encoding the current frame, 0 otherwise.
+ */
+static int
+ppp_tty_push(struct ppp *ppp)
 {
-       register int count, actual;
+       int avail, sent, done = 0;
+       struct tty_struct *tty = ppp2tty(ppp);
        
-       count = ppp->xhead - ppp->xbuff;
-       
-       actual = ppp->tty->driver.write(ppp->tty, 0, ppp->xbuff, count);
-       ppp->stats.sbytes += actual;
-       if (actual == count) {
-               ppp_output_done(ppp);
-       } else {
-               ppp->xtail = ppp->xbuff + actual;
-               ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+       if ( ppp->flags & SC_SYNC ) 
+               return ppp_tty_sync_push(ppp);
+
+       CHECK_PPP(0);
+       if (ppp->tty_pushing)
+               return 0;
+       if (tty == NULL || tty->disc_data != (void *) ppp)
+               goto flush;
+       while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
+               ppp->tty_pushing = 1;
+               avail = ppp->olim - ppp->optr;
+               if (avail > 0) {
+                       tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+                       sent = tty->driver.write(tty, 0, ppp->optr, avail);
+                       if (sent < 0)
+                               goto flush;     /* error, e.g. loss of CD */
+                       ppp->stats.ppp_obytes += sent;
+                       ppp->optr += sent;
+                       if (sent < avail) {
+                               ppp->tty_pushing = 0;
+                               return done;
+                       }
+               }
+               if (ppp->tpkt != 0)
+                       done = ppp_async_encode(ppp);
+               ppp->tty_pushing = 0;
        }
+       return done;
+
+flush:
+       ppp->tty_pushing = 1;
+       ppp->stats.ppp_oerrors++;
+       if (ppp->tpkt != 0) {
+               KFREE_SKB(ppp->tpkt);
+               ppp->tpkt = 0;
+               done = 1;
+       }
+       ppp->optr = ppp->olim;
+       ppp->tty_pushing = 0;
+       return done;
 }
 
-static void ppp_write_wakeup(struct tty_struct *tty)
+/*
+ * Procedure to encode the data for async serial transmission.
+ * Does octet stuffing (escaping) and address/control
+ * and protocol compression.
+ * Assumes ppp->opkt != 0 on entry.
+ * Returns 1 if we finished the current frame, 0 otherwise.
+ */
+static int
+ppp_async_encode(struct ppp *ppp)
 {
-       register int count, actual;
-       struct ppp *ppp = ppp_find(tty);
+       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;
+       }
 
-       if (!ppp || ppp->magic != PPP_MAGIC) {
-               PRINTKN (1,
-                        (KERN_ERR "PPP: write_wakeup called but couldn't "
-                         "find PPP struct.\n"));
-               return;
+       /*
+        * 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 (!ppp->xtail || (ppp->flags & SC_XMIT_BUSY))
-               return;
+       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;
+       }
 
-       cli();
-       if (ppp->flags & SC_XMIT_BUSY)
+       /*
+        * Remember where we are up to in this packet.
+        */
+       ppp->olim = buf;
+       ppp->tpkt_pos = i;
+       ppp->tfcs = fcs;
+       return 0;
+}
+
+/*
+ * 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)
+{
+       return 65536;       /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Callback function when data is available at the tty driver.
+ */
+static void
+ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
+                char *flags, int count)
+{
+       register struct ppp *ppp = tty2ppp (tty);
+       struct sk_buff *skb;
+       int chr, flg;
+       unsigned char *p;
+
+       if (ppp != 0)
+               CHECK_PPP_VOID();
+       /*
+        * This can happen if stuff comes in on the backup tty.
+        */
+       if (ppp == 0 || tty != ppp->tty)
+               return;
+       /*
+        * Verify the table pointer and ensure that the line is
+        * still in PPP discipline.
+        */
+       if (ppp->magic != PPP_MAGIC) {
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_DEBUG
+                              "PPP: tty_receive called but couldn't find "
+                              "PPP struct.\n");
                return;
-       ppp->flags |= SC_XMIT_BUSY;
-       sti();
+       }
+       /*
+        * Print the buffer if desired
+        */
+       if (ppp->flags & SC_LOG_RAWIN)
+               ppp_print_buffer ("receive buffer", data, count);
+
+       ppp->stats.ppp_ibytes += count;
+       skb = ppp->rpkt;
        
-       count = ppp->xhead - ppp->xtail;
+       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;
+       }
        
-       actual = tty->driver.write(tty, 0, ppp->xtail, count);
-       ppp->stats.sbytes += actual;
-       if (actual == count) {
-               ppp->xtail = 0;
-               tty->flags &= ~TTY_DO_WRITE_WAKEUP;
+       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.
+                */
 
-               ppp_output_done(ppp);
-       } else {
-               ppp->xtail += actual;
+#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)))
+                       ppp->flags |= SC_RCV_ODDP;
+               else
+                       ppp->flags |= SC_RCV_EVNP;
+#endif
+
+               if (chr == PPP_FLAG) {
+                       /*
+                        * FLAG. This is the end of the block. If the block
+                        * ends with ESC FLAG, then the block is to be ignored.
+                        */
+                       if (ppp->escape)
+                               ppp->toss |= 0x80;
+                       /*
+                        * Process the frame if it was received correctly.
+                        * If there was an error, let the VJ decompressor know.
+                        * There are 4 cases here:
+                        * skb != NULL, toss != 0: error in frame
+                        * skb != NULL, toss == 0: frame ok
+                        * skb == NULL, toss != 0: very first frame,
+                        *      error on 1st char, or alloc_skb failed
+                        * skb == NULL, toss == 0: empty frame (~~)
+                        */
+                       if (ppp->toss || !ppp_receive_frame(ppp, skb)) {
+                               if (ppp->toss && (ppp->flags & SC_DEBUG))
+                                       printk(KERN_DEBUG
+                                              "ppp: tossing frame (%x)\n",
+                                              ppp->toss);
+                               if (skb != NULL)
+                                       KFREE_SKB(skb);
+                               if (!(ppp->toss == 0xE0 || ppp->toss == 0x80))
+                                       ++ppp->stats.ppp_ierrors;
+                               ppp_receive_error(ppp);
+                       }
+                       /*
+                        * Reset for the next frame.
+                        */
+                       skb = NULL;
+                       ppp->rfcs = PPP_INITFCS;
+                       ppp->escape = 0;
+                       ppp->toss = 0;
+                       continue;
+               }
+
+               /* If we're tossing, look no further. */
+               if (ppp->toss != 0)
+                       continue;
+
+               /* If this is a control char to be ignored, do so */
+               if (in_rmap(ppp, chr))
+                       continue;
+
+               /*
+                * 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;
+               }
+
+               /*
+                * Allocate an skbuff on the first character received.
+                * The 128 is room for VJ header expansion and FCS.
+                */
+               if (skb == NULL) {
+                       skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+                       if (skb == NULL) {
+                               if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_DEBUG "couldn't "
+                                              "alloc skb for recv\n");
+                               ppp->toss = 1;
+                               continue;
+                       }
+                       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=%ld 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);
        }
-       ppp->flags &= ~SC_XMIT_BUSY;
+       ppp->rpkt = skb;
 }
-#endif
 
 /*************************************************************
- * 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.
+ * 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.
  *************************************************************/
 
-/* stuff a single character into the receive buffer */
+/*
+ * Network device driver callback routines
+ */
+
+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
 
-inline void
-ppp_enqueue(struct ppp *ppp, unsigned char c)
-{
-  unsigned long flags;
-
-  save_flags(flags);
-  cli();
-  if (ppp->rhead < ppp->rend) {
-    *ppp->rhead = c;
-    ppp->rhead++;
-    ppp->rcount++;
-  } else
-    ppp->stats.roverrun++;
-  restore_flags(flags);
-}
+/*
+ * Information for the protocol decoder
+ */
 
-#ifdef CHECK_CHARACTERS
-static unsigned paritytab[8] = {
-    0x96696996, 0x69969669, 0x69969669, 0x96696996,
-    0x69969669, 0x96696996, 0x96696996, 0x69969669
+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 !!! */
 };
-#endif
 
-#ifndef NEW_TTY_DRIVERS
-static void
-ppp_dump_inqueue(struct tty_struct *tty)
+/*
+ * Called when the PPP network interface device is actually created.
+ */
+static int
+ppp_init_dev (struct device *dev)
 {
-  int  head = tty->read_q.head,
-       tail = tty->read_q.tail,
-       i, count;
-  char buffer[8];
-
-  PRINTK ((KERN_DEBUG "INQUEUE: head %d tail %d imode %x:\n", head, tail, 
-          (unsigned int) tty->termios->c_iflag))
-
-  i     = tail;
-  count = 0;
-
-  while (i != head) {
-    buffer [count] = tty->read_q.buf[i];
-    if (++count == 8) {
-      ppp_print_buffer (NULL, buffer, 8, KERNEL_DS);
-      count = 0;
-    }
-    i = (i + 1) & (TTY_BUF_SIZE - 1);
-  }
-  ppp_print_buffer (NULL, buffer, count, KERNEL_DS);
-}
+       dev->hard_header_len  = PPP_HDRLEN;
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+       dev->hard_header      = ppp_dev_header;
+       dev->rebuild_header   = ppp_dev_rebuild;
+#endif
 
-/* called by lower levels of TTY driver when data becomes available.
-   all incoming data comes through this function. */
+       /* 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
 
-void ppp_tty_input_ready(struct tty_struct *tty)
-{
-  struct ppp *ppp = ppp_find(tty);
-  int n, error;
-  unsigned char buff[128];
-
-/*  PRINTK( (KERN_DEBUG "PPP: handler called.\n") ) */
-  if (!ppp || ppp->magic != PPP_MAGIC) {
-    PRINTKN (1,
-            (KERN_ERR "PPP: handler called but couldn't find PPP struct.\n"));
-    return;
-  }
-
-  CHECK_PPP_VOID();
-
-  /* ZZZ */
-  if (ppp_debug >= 5)
-    ppp_dump_inqueue(ppp->tty);
-
-  do {
-    n = tty_read_raw_data(tty, buff, 128);
-    if ( n == 0 )              /* nothing there */
-      break;
-
-    if (ppp_debug >= 5)
-      ppp_print_buffer ("receive buffer", buff, n > 0 ? n : -n, KERNEL_DS);
-
-    if ( n < 0 ) {
-      /* Last character is error flag.
-        Process the previous characters, then set toss flag. */
-      n = (-n) - 1;
-      error = buff[n];
-    } else error = 0;
-    ppp->stats.rbytes += n;
-    ppp_unesc(ppp,buff,n);
-    if (error)
-      ppp->toss = error;
-  } while (1);
+       dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+
+       return 0;
 }
 
-/* recover frame by undoing PPP escape mechanism;
-   copies N chars of input data from C into PPP->rbuff
-   calls ppp_doframe to dispose of any frames it finds
-*/
+/*
+ * Callback from the network layer when the device goes up.
+ */
 
-static void
-ppp_unesc(struct ppp *ppp, unsigned char *c, int n)
+static int
+ppp_dev_open (struct device *dev)
 {
-  int i;
+       struct ppp *ppp = dev2ppp(dev);
 
-  for (i = 0; i < n; i++, c++) {
-    PRINTKN (6,(KERN_DEBUG "(%x)", (unsigned int) *c));
+       if (!ppp->inuse || ppp2tty(ppp) == NULL) {
+               printk(KERN_ERR "ppp: %s not active\n", dev->name);
+               return -ENXIO;
+       }
 
-#ifdef CHECK_CHARACTERS
-    if (*c & 0x80)
-       sc->sc_flags |= SC_RCV_B7_1;
-    else
-       sc->sc_flags |= SC_RCV_B7_0;
-
-    if (paritytab[*c >> 5] & (1 << (*c & 0x1F)))
-       sc->sc_flags |= SC_RCV_ODDP;
-    else
-       sc->sc_flags |= SC_RCV_EVNP;
-#endif
+       MOD_INC_USE_COUNT;
 
-    switch (*c) {
-    case PPP_ESC:              /* PPP_ESC: invert 0x20 in next character */
-      ppp->escape = PPP_TRANS;
-      break;
+       return 0;
+}
 
-    case PPP_FLAG:             /* PPP_FLAG: end of frame */
-      if (ppp->escape)         /* PPP_ESC just before PPP_FLAG is illegal */
-       ppp->toss = 0xFF;
+/*
+ * Callback from the network layer when the ppp device goes down.
+ */
 
-      if ((ppp->toss & 0x80) == 0)
-       ppp_doframe(ppp);       /* pass frame on to next layers */
-
-      ppp->rcount = 0;
-      ppp->rhead  = ppp->rbuff;
-      ppp->escape = 0;
-      ppp->toss   = 0;
-      break;
-
-    default:                   /* regular character */
-      if (!in_rmap (ppp, *c)) {
-       if (ppp->toss == 0)
-         ppp_enqueue (ppp, *c ^ ppp->escape);
-       ppp->escape = 0;
-      }
-      break;
-    }
-  }
-}
-
-#else
-static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
-                           char *fp, int count)
+static int
+ppp_dev_close (struct device *dev)
 {
-  register struct ppp *ppp = ppp_find (tty);
-  unsigned char c;
-/*  PRINTK( ("PPP: handler called.\n") ); */
-
-  if (!ppp || ppp->magic != PPP_MAGIC) {
-    PRINTKN (1,("PPP: handler called but couldn't find "
-               "PPP struct.\n"));
-    return;
-  }
-
-  CHECK_PPP_VOID();
-  if (ppp_debug >= 5) {
-    ppp_print_buffer ("receive buffer", cp, count, KERNEL_DS);
-  }
-  while (count-- > 0) {
-    c = *cp++;
-
-    if (fp) {
-      if (*fp && ppp->toss == 0)
-       ppp->toss = *fp;
-      fp++;
-    }
-
-#ifdef CHECK_CHARACTERS
-    if (c & 0x80)
-       sc->sc_flags |= SC_RCV_B7_1;
-    else
-       sc->sc_flags |= SC_RCV_B7_0;
-
-    if (paritytab[c >> 5] & (1 << (c & 0x1F)))
-       sc->sc_flags |= SC_RCV_ODDP;
-    else
-       sc->sc_flags |= SC_RCV_EVNP;
-#endif
-
-    switch (c) {
-    case PPP_ESC:              /* PPP_ESC: invert 0x20 in next character */
-      ppp->escape = PPP_TRANS;
-      break;
-
-    case PPP_FLAG:             /* PPP_FLAG: end of frame */
-      if (ppp->escape)         /* PPP_ESC just before PPP_FLAG is "cancel"*/
-       ppp->toss = 0xFF;
+       struct ppp *ppp = dev2ppp (dev);
 
-      if ((ppp->toss & 0x80) == 0)
-       ppp_doframe(ppp);       /* pass frame on to next layers */
+       CHECK_PPP_MAGIC(ppp);
 
-      ppp->rcount = 0;
-      ppp->rhead  = ppp->rbuff;
-      ppp->escape = 0;
-      ppp->toss   = 0;
-      break;
+       MOD_DEC_USE_COUNT;
 
-    default:                   /* regular character */
-      if (!in_rmap (ppp, c)) {
-       if (ppp->toss == 0)
-         ppp_enqueue (ppp, c ^ ppp->escape);
-       ppp->escape = 0;
-      }
-    }
-  }
+       return 0;
 }
-#endif
 
-/* on entry, a received frame is in ppp->rbuff
-   check it and dispose as appropriate */
-static void
-ppp_doframe(struct ppp *ppp)
+static inline void
+get_vj_stats(struct vjstat *vj, struct slcompress *slc)
 {
-  u_char *c = ppp->rbuff;
-  u_short proto;
-  int count = ppp->rcount;
-
-  /* forget it if we've already noticed an error */
-  if (ppp->toss) {
-    PRINTKN (1, (KERN_WARNING "ppp_toss: tossing frame, reason = %d\n",
-                ppp->toss));
-    ppp->stats.rerrors++;
-    return;
-  }
-
-  /* do this before printing buffer to avoid generating copious output */
-  if (count == 0)
-    return;
-
-  if (ppp_debug >= 3)
-    ppp_print_buffer ("receive frame", c, count, KERNEL_DS);
-
-  if (count < 4) {
-    PRINTKN (1,(KERN_WARNING "ppp: got runt ppp frame, %d chars\n", count));
-    ppp->stats.runts++;
-    return;
-  }
-
-  /* check PPP error detection field */
-  if (!ppp_check_fcs(ppp)) {
-    PRINTKN (1,(KERN_WARNING "ppp: frame with bad fcs\n"));
-    ppp->stats.rerrors++;
-    return;
-  }
-
-  count -= 2;                  /* ignore last two characters */
-
-  /* now we have a good frame */
-  /* figure out the protocol field */
-  if ((c[0] == PPP_ADDRESS) && (c[1] == PPP_CONTROL)) {
-    c = c + 2;                 /* ADDR/CTRL not compressed, so skip */
-    count -= 2;
-  }
-
-  proto = (u_short) *c++;              /* PROTO compressed */
-  if (proto & 1) {
-    count--;
-  } else {
-    proto = (proto << 8) | (u_short) *c++; /* PROTO uncompressed */
-    count -= 2;
-  }
-
-  /* Send the frame to the network if the ppp device is up */
-  if ((ppp->dev->flags & IFF_UP) && ppp_do_ip(ppp, proto, c, count)) {
-    ppp->ddinfo.ip_rjiffies = jiffies;
-    return;
-  }
-
-  /* If we got here, it has to go to a user process doing a read,
-     so queue it.
-
-     User process expects to get whole frame (for some reason), so
-     use count+2 so as to include FCS field. */
-
-  if (ppp_us_queue (ppp, proto, c, count+2)) {
-    ppp->ddinfo.nip_rjiffies = jiffies;
-    ppp->stats.rothers++;
-    return;
-  }
-
-  /* couldn't cope. */
-  PRINTKN (1,(KERN_WARNING
-             "ppp: dropping packet on the floor: nobody could take it.\n"));
-  ppp->stats.tossed++;
+       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;
 }
 
-/* Examine packet at C, attempt to pass up to net layer. 
-   PROTO is the protocol field from the PPP frame.
-   Return 1 if could handle it, 0 otherwise.  */
-
+/*
+ * Callback from the network layer to process the sockioctl functions.
+ */
 static int
-ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
-         int count)
+ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
 {
-  int flags, done;
-
-  PRINTKN (4,(KERN_DEBUG "ppp_do_ip: proto %x len %d first byte %x\n",
-             (int) proto, count, c[0]));
-
-  if (ppp_debug_netpackets) {
-    PRINTK (("KERN_DEBUG %s <-- proto %x len %d\n", ppp->dev->name,
-            (int) proto, count));
-  }
-    
-  if (proto == PROTO_IP) {
-    ppp->stats.runcomp++;
-    goto sendit;
-  }
-
-  if ((proto == PROTO_VJCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
-    /* get space for uncompressing the header */
-    done = 0;
-    save_flags (flags);
-    cli();
-    if ((ppp->rhead + 80) < ppp->rend) {
-      ppp->rhead += 80;
-      ppp->rcount += 80;
-      done = 1;
-    }
-    restore_flags(flags);
-
-    if (! done)        {
-      PRINTKN (1,(KERN_NOTICE
-                 "ppp: no space to decompress VJ compressed TCP header.\n"));
-      ppp->stats.roverrun++;
-      return 1;
-    }
-
-    count = slhc_uncompress(ppp->slcomp, c, count);
-    if (count <= 0) {
-      ppp->stats.rerrors++;
-      PRINTKN (1,(KERN_NOTICE "ppp: error in VJ decompression\n"));
-      return 1;
-    }
-    ppp->stats.rcomp++;
-    goto sendit;
-  }
-  
-  if ((proto == PROTO_VJUNCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
-    if (slhc_remember(ppp->slcomp, c, count) <= 0) {
-      ppp->stats.rerrors++;
-      PRINTKN (1,(KERN_NOTICE "ppp: error in VJ memorizing\n"));
-      return 1;
-    }
-    ppp->stats.runcomp++;
-    goto sendit;
-  }
-
-  /* not ours */
-  return 0;
-
- sendit:
-  if (ppp_debug_netpackets) {
-    struct iphdr *iph = (struct iphdr *) c;
-    PRINTK ((KERN_INFO "%s <--    src %lx dst %lx len %d\n", ppp->dev->name, 
-            iph->saddr, iph->daddr, count))
-  }
-
-  /* receive the frame through the network software */
-  while ((dev_rint(c, count, 0, ppp->dev) & ~1) != 0)
-    ;
-
-  return 1;
-}
+       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;
+       }
 
-/* stuff packet at BUF, length LEN, into the us_rbuff buffer
-   prepend PROTO information */
+       if (COPY_TO_USER((void *) ifr->ifr_ifru.ifru_data, &u, nb))
+               return -EFAULT;
+       return 0;
+}
 
-#define PUTC(c,label) *ppp->us_rbuff_head++ = c; \
-                if (ppp->us_rbuff_head == ppp->us_rbuff_end) \
-                     ppp->us_rbuff_head = ppp->us_rbuff; \
-                if (ppp->us_rbuff_head == ppp->us_rbuff_tail) \
-                     goto label;
-#define GETC(c) c = *ppp->us_rbuff_tail++; \
-                if (ppp->us_rbuff_tail == ppp->us_rbuff_end) \
-                     ppp->us_rbuff_tail = ppp->us_rbuff;
+/*
+ * Process the generic PPP ioctls, i.e. those which are not specific
+ * to any particular type of hardware link.
+ */
+static int
+ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
+{
+       register int temp_i = 0, oldflags;
+       int error = -EFAULT;
+       unsigned long flags;
+       struct ppp_idle cur_ddinfo;
+       struct npioctl npi;
+
+       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_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;
+
+       case PPPIOCGNPMODE:
+       case PPPIOCSNPMODE:
+               if (COPY_FROM_USER(&npi, (void *) param3, sizeof(npi)))
+                       break;
+
+               switch (npi.protocol) {
+               case PPP_IP:
+                       npi.protocol = NP_IP;
+                       break;
+               case PPP_IPX:
+                       npi.protocol = NP_IPX;
+                       break;
+               case PPP_AT:
+                       npi.protocol = NP_AT;
+                       break;
+               default:
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "pppioc[gs]npmode: "
+                                      "invalid proto %d\n", npi.protocol);
+                       error = -EINVAL;
+                       goto out;
+               }
+
+               if (param2 == PPPIOCGNPMODE) {
+                       npi.mode = ppp->sc_npmode[npi.protocol];
+                       if (COPY_TO_USER((void *) param3, &npi, sizeof(npi)))
+                               break;
+               } else {
+                       ppp->sc_npmode[npi.protocol] = npi.mode;
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "ppp: set np %d to %d\n",
+                                      npi.protocol, npi.mode);
+                       mark_bh(NET_BH);
+               }
+               error = 0;
+               break;
+
+       default:
+               /*
+                *  All other ioctl() events will come here.
+                */
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_ERR
+                              "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+                              param2, param3);
+
+               error = -ENOIOCTLCMD;
+               break;
+       }
+out:
+       return error;
+}
 
+/*
+ * Process the set-compression ioctl.
+ */
 static int
-ppp_us_queue(struct ppp *ppp, unsigned short proto, 
-            unsigned char *buf, int len)
+ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
 {
-  int totlen;
-  unsigned char *saved_head;
+       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;
 
-  totlen = len+2;              /* including protocol */
+       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 */
+       }
 
-  if (set_bit(1, &ppp->us_rbuff_lock)) {
-    PRINTKN (1, (KERN_NOTICE "ppp_us_queue: can't get lock\n"));
-    return 0;
-  }
-  saved_head = ppp->us_rbuff_head;
+       /*
+        * 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;
+}
 
-  PUTC((totlen & 0xff00) >> 8, failure);
-  PUTC(totlen & 0x00ff, failure);
-  PUTC((proto & 0xff00) >> 8, failure);
-  PUTC(proto & 0x00ff, failure);
+/*
+ * Handle a CCP packet.
+ *
+ * The CCP packet is passed along to the pppd process just like any
+ * other PPP frame. The difference is that some processing needs to be
+ * immediate or the compressors will become confused on the peer.
+ */
 
-  while (len-- > 0) {
-    PUTC(*buf++, failure);
-  }
+static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd)
+{
+       int slen    = CCP_LENGTH(dp);
+       __u8 *opt = dp   + CCP_HDRLEN;
+       int opt_len = slen - CCP_HDRLEN;
+       unsigned long flags;
 
-  PRINTKN (3, (KERN_INFO "ppp: successfully queued %d bytes\n", totlen));
-  clear_bit(1, &ppp->us_rbuff_lock);
-  wake_up_interruptible (&ppp->read_wait);
+       if (slen > len)
+               return;
 
-#ifdef NEW_TTY_DRIVERS
-  kill_fasync(ppp->tty->fasync, SIGIO);
-#endif
+       if (ppp->flags & SC_DEBUG)
+               printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n",
+                      rcvd, CCP_CODE(dp), ppp->flags);
+       save_flags(flags);
+       switch (CCP_CODE(dp)) {
+       case CCP_CONFREQ:
+       case CCP_TERMREQ:
+       case CCP_TERMACK:
+               /*
+                * CCP must be going down - disable compression
+                */
+               if (ppp->flags & SC_CCP_UP) {
+                       cli();
+                       ppp->flags &= ~(SC_CCP_UP   |
+                                       SC_COMP_RUN |
+                                       SC_DECOMP_RUN);
+               }
+               break;
+
+       case CCP_CONFACK:
+               if ((ppp->flags & SC_CCP_OPEN) == 0)
+                       break;
+               if (ppp->flags & SC_CCP_UP)
+                       break;
+               if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
+                       break;
+               if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
+                       break;
+               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,
+                            ppp->line, 0, ppp->flags & SC_DEBUG)) {
+                               if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_DEBUG "%s: comp running\n",
+                                              ppp->name);
+                               cli();
+                               ppp->flags |= SC_COMP_RUN;
+                       }
+                       break;
+               }
+
+               /*
+                * peer is agreeing to send compressed packets.
+                */
+               if (ppp->sc_rc_state == NULL)
+                       break;
+
+               if ((*ppp->sc_rcomp->decomp_init)
+                   (ppp->sc_rc_state,
+                    opt, opt_len,
+                    ppp->line, 0, ppp->mru, ppp->flags & SC_DEBUG)) {
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "%s: decomp running\n",
+                                      ppp->name);
+                       cli();
+                       ppp->flags |= SC_DECOMP_RUN;
+                       ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
+               }
+               break;
+
+       case CCP_RESETACK:
+               /*
+                * CCP Reset-ack resets compressors and decompressors
+                * as it passes through.
+                */
+               if ((ppp->flags & SC_CCP_UP) == 0)
+                       break;
+
+               if (!rcvd) {
+                       if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) {
+                               (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
+                               if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_DEBUG "%s: comp reset\n",
+                                              ppp->name);
+                       }
+               } else {
+                       if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
+                             (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
+                             if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_DEBUG "%s: decomp reset\n",
+                                              ppp->name);
+                             cli();
+                             ppp->flags &= ~SC_DC_ERROR;
+                       }
+               }
+               break;
+       }
+       restore_flags(flags);
+}
 
-  if (ppp->inp_sig && ppp->inp_sig_pid)
-    if (kill_proc (ppp->inp_sig_pid, ppp->inp_sig, 1) != 0) {
-      /* process is gone */
-      PRINTKN (2,(KERN_NOTICE
-                 "ppp: process that requested notification is gone\n"));
-      ppp->inp_sig = 0;
-      ppp->inp_sig_pid = 0;
-    }
-  return 1;
+/*
+ * CCP is down; free (de)compressor state if necessary.
+ */
 
- failure:
-  ppp->us_rbuff_head = saved_head;
-  clear_bit(1, &ppp->us_rbuff_lock);
+static void
+ppp_ccp_closed(struct ppp *ppp)
+{
+       unsigned long flags;
 
-  PRINTKN (1, (KERN_NOTICE "ppp_us_queue: ran out of buffer space.\n"));
+       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;
+       }
 
-  return 0;
+       if (ppp->sc_rc_state) {
+               (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
+               ppp->sc_rc_state = NULL;
+       }
 }
 
 /*************************************************************
- * 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.
+ * RECEIVE-SIDE ROUTINES
  *************************************************************/
 
-/* read a PPP frame from the us_rbuff circular buffer, 
-   waiting if necessary
-*/
-
+/*
+ * On entry, a received frame is in skb.
+ * Check it and dispose as appropriate.
+ */
 static int
-ppp_read(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
 {
-  struct ppp *ppp = ppp_find(tty);
-  unsigned char c;
-  int len, i;
-
-  if (!ppp || ppp->magic != PPP_MAGIC) {
-    PRINTKN (1,(KERN_ERR "ppp_read: cannnot find ppp channel\n"));
-    return -EIO;
-  }
-
-  CHECK_PPP(-ENXIO);
-
-  PRINTKN (4,(KERN_DEBUG "ppp_read: called %x num %u\n",
-             (unsigned int) buf,
-             nr));
-
-  do {
-    /* try to acquire read lock */
-    if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
-      /* got lock */
-      if (ppp->us_rbuff_head == ppp->us_rbuff_tail) {
-       /* no data */
-       PRINTKN (4,(KERN_DEBUG "ppp_read: no data\n"));
-       clear_bit(0, &ppp->us_rbuff_lock);
-        if (ppp->inp_sig) {
-         PRINTKN (4,(KERN_DEBUG "ppp_read: EWOULDBLOCK\n"));
-         return -EWOULDBLOCK;
-        } else goto wait;
-      }
-
-      /* reset the time of the last read operation */
-      ppp->ddinfo.nip_rjiffies = jiffies;
-
-      GETC (c); len = c << 8; GETC (c); len += c;
-
-      PRINTKN (4,(KERN_DEBUG "ppp_read: len = %d\n", len));
-
-      if (len + 2 > nr) {
-       /* frame too big; can't copy it, but do update us_rbuff_head */
-       PRINTKN (1,(KERN_DEBUG
-                   "ppp: read of %u bytes too small for %d frame\n",
-                   nr, len+2));
-       ppp->us_rbuff_head += len;
-       if (ppp->us_rbuff_head > ppp->us_rbuff_end)
-         ppp->us_rbuff_head += - (ppp->us_rbuff_end - ppp->us_rbuff);
-       clear_bit(0, &ppp->us_rbuff_lock);
-       wake_up_interruptible (&ppp->read_wait);
-       ppp->stats.rgiants++;
-       return -EOVERFLOW;              /* ZZZ; HACK! */
-      } else {
-       /* have the space: copy the packet, faking the first two bytes */
-       put_fs_byte (PPP_ADDRESS, buf++);
-       put_fs_byte (PPP_CONTROL, buf++);
-       i = len;
-       while (i-- > 0) {
-         GETC (c);
-         put_fs_byte (c, buf++);
-       }
-      }
-
-      clear_bit(0, &ppp->us_rbuff_lock);
-      PRINTKN (3,(KERN_DEBUG "ppp_read: passing %d bytes up\n", len + 2));
-      ppp->stats.rothers++;
-      return len + 2;
-    }
-
-    /* need to wait */
-  wait:
-    current->timeout = 0;
-    PRINTKN (3,(KERN_DEBUG "ppp_read: sleeping\n"));
-    interruptible_sleep_on (&ppp->read_wait);
-    if (current->signal & ~current->blocked)
-      return -EINTR;
-  } while (1);
+       __u8    *data;
+       int     count;
+       int     proto;
+       int     new_count;
+       struct sk_buff *new_skb;
+       ppp_proto_type  *proto_ptr;
+
+       /*
+        * 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;
+
+       /*
+        * 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;
+       }
+
+       if ( !(ppp->flags & SC_SYNC) ) { 
+               /*
+                * Verify the FCS of the frame and discard the FCS characters
+                * from the end of the buffer.
+                */
+               if (ppp->rfcs != PPP_GOODFCS) {
+                       if (ppp->flags & SC_DEBUG) {
+                               printk(KERN_DEBUG
+                                      "ppp: frame with bad fcs, length = %d\n",
+                                      count);
+                               ppp_print_buffer("bad frame", data, count);
+                       }
+                       ++ppp->estats.rx_crc_errors;
+                       return 0;
+               }
+               count -= 2;             /* ignore the fcs characters */
+               skb_trim(skb, count);
+       }
+       
+       /*
+        * Process the active decompressor.
+        */
+       if (ppp->sc_rc_state != NULL &&
+           (ppp->flags & SC_DECOMP_RUN) &&
+           ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
+               if (PPP_PROTOCOL(data) == PPP_COMP) {
+                       /*
+                        * If the frame is compressed then decompress it.
+                        */
+                       new_skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+                       if (new_skb == NULL) {
+                               printk(KERN_ERR "ppp_recv_frame: no memory\n");
+                               new_count = DECOMP_ERROR;
+                       } else {
+                               LIBERATE_SKB(new_skb);
+                               new_count = (*ppp->sc_rcomp->decompress)
+                                       (ppp->sc_rc_state, data, count,
+                                        new_skb->data, ppp->mru + PPP_HDRLEN);
+                       }
+                       if (new_count > 0) {
+                               /* Frame was decompressed OK */
+                               KFREE_SKB(skb);
+                               skb = new_skb;
+                               count = new_count;
+                               data = skb_put(skb, count);
+
+                       } else {
+                               /*
+                                * On a decompression error, we pass the
+                                * compressed frame up to pppd as an
+                                * error indication.
+                                */
+                               if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_INFO "%s: decomp err %d\n",
+                                              ppp->name, new_count);
+                               if (new_skb != 0)
+                                       KFREE_SKB(new_skb);
+                               if (ppp->slcomp != 0)
+                                       slhc_toss(ppp->slcomp);
+                               ++ppp->stats.ppp_ierrors;
+                               if (new_count == DECOMP_FATALERROR) {
+                                       ppp->flags |= SC_DC_FERROR;
+                               } else {
+                                       ppp->flags |= SC_DC_ERROR;
+                               }
+                       }
+
+
+               } else {
+                       /*
+                        * The frame is not compressed. Pass it to the
+                        * decompression code so it can update its
+                        * dictionary if necessary.
+                        */
+                       (*ppp->sc_rcomp->incomp)(ppp->sc_rc_state,
+                                                data, count);
+               }
+       }
+       else if (PPP_PROTOCOL(data) == PPP_COMP && (ppp->flags & SC_DEBUG))
+               printk(KERN_INFO "%s: not decomp, rc_state=%p flags=%x\n",
+                      ppp->name, ppp->sc_rc_state, ppp->flags);
+
+       /*
+        * Count the frame and print it
+        */
+       ++ppp->stats.ppp_ipackets;
+       ppp->stats.ppp_ioctects += count;
+       if (ppp->flags & SC_LOG_INPKT)
+               ppp_print_buffer ("receive frame", data, count);
+
+       /*
+        * Find the procedure to handle this protocol.
+        * The last one is marked as protocol 0 which is the 'catch-all'
+        * to feed it to the pppd daemon.
+        */
+       proto = PPP_PROTOCOL(data);
+       proto_ptr = proto_list;
+       while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
+               ++proto_ptr;
+
+       /*
+        * Update the appropriate statistic counter.
+        */
+       if (!(*proto_ptr->func)(ppp, skb)) {
+               KFREE_SKB(skb);
+               ++ppp->stats.ppp_discards;
+       }
+
+       return 1;
 }
 
-/* stuff a character into the transmit buffer, using PPP's way of escaping
-   special characters.
-   also, update ppp->fcs to take account of new character */
-static inline void
-ppp_stuff_char(struct ppp *ppp, unsigned char c)
+/*
+ * An input error has been detected, so we need to inform
+ * the VJ decompressor.
+ */
+static void
+ppp_receive_error(struct ppp *ppp)
 {
-  int curpt = ppp->xhead - ppp->xbuff;
-  if ((curpt < 0) || (curpt > 3000)) {
-    PRINTK ((KERN_DEBUG "ppp_stuff_char: %x %x %d\n",
-            (unsigned int) ppp->xbuff, (unsigned int) ppp->xhead, curpt))
-  }
-  if (in_xmap (ppp, c)) {
-    *ppp->xhead++ = PPP_ESC;
-    *ppp->xhead++ = c ^ PPP_TRANS;
-  } else
-    *ppp->xhead++ = c;
-  ppp->fcs = (ppp->fcs >> 8) ^ fcstab[(ppp->fcs ^ c) & 0xff];
-}
+       CHECK_PPP_VOID();
 
-/* write a frame with NR chars from BUF to TTY
-   we have to put the FCS field on ourselves
-*/
+       if (ppp->slcomp != 0)
+               slhc_toss(ppp->slcomp);
+}
 
+/*
+ * Put the input frame into the networking system for the indicated protocol
+ */
 static int
-ppp_write(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
+ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb)
 {
-  struct ppp *ppp = ppp_find(tty);
-  int i;
-
-  if (!ppp || ppp->magic != PPP_MAGIC) {
-    PRINTKN (1,(KERN_ERR "ppp_write: cannot find ppp unit\n"));
-    return -EIO;
-  }
-
-  CHECK_PPP(-ENXIO);
-  
-  if (ppp->mtu != ppp->dev->mtu)       /* Someone has been ifconfigging */
-    ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
-
-  if (nr > ppp->mtu) {
-    PRINTKN (1,(KERN_WARNING
-               "ppp_write: truncating user packet from %u to mtu %d\n",
-               nr, ppp->mtu));
-    nr = ppp->mtu;
-  }
-
-  if (ppp_debug >= 3)
-    ppp_print_buffer ("write frame", buf, nr, USER_DS);
-
-  /* lock this PPP unit so we will be the only writer;
-     sleep if necessary */
-  while ((ppp->sending == 1) || !ppp_lock(ppp)) {
-    current->timeout = 0;
-    PRINTKN (3,(KERN_DEBUG "ppp_write: sleeping\n"));
-    interruptible_sleep_on(&ppp->write_wait);
-    if (current->signal & ~current->blocked)
-      return -EINTR;
-  }
-
-  /* OK, locked.  Stuff the given bytes into the buffer. */
-
-  PRINTKN(4,(KERN_DEBUG "ppp_write: acquired write lock\n"));
-  ppp->xhead = ppp->xbuff;
-
-#ifdef OPTIMIZE_FLAG_TIME
-  if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
-    *ppp->xhead++ = PPP_FLAG;
-  ppp->last_xmit = jiffies;
-#else      
-  *ppp->xhead++ = PPP_FLAG;
-#endif
 
-  ppp->fcs = PPP_FCS_INIT;
-  i = nr;
-  while (i-- > 0)
-    ppp_stuff_char(ppp,get_fs_byte(buf++));
-
-  ppp_add_fcs(ppp);            /* concatenate FCS at end */
+       /*
+        * 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;
+}
 
-  *ppp->xhead++ = PPP_FLAG;
-  
-  /* reset the time of the last write operation */
-  ppp->ddinfo.nip_sjiffies = jiffies;
+/*
+ * Process the receipt of an IP frame
+ */
+static int
+rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP(0);
+       if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+           && ppp->sc_npmode[NP_IP] == NPMODE_PASS)
+               return ppp_rcv_rx(ppp, ETH_P_IP, skb);
+       return 0;
+}
 
-  if (ppp_debug >= 6)
-    ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
-  else {
-    PRINTKN (4,(KERN_DEBUG
-               "ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
-  }
+/*
+ * Process the receipt of an IPv6 frame
+ */
+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;
+}
 
-  /* packet is ready-to-go */
-  ++ppp->stats.sothers;
-  ppp_kick_tty(ppp);
+/*
+ * 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;
+}
 
-  return((int)nr);
+/*
+ * Process the receipt of an Appletalk frame
+ */
+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;
 }
+
+/*
+ * Process the receipt of an VJ Compressed frame
+ */
 static int
-ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
-         unsigned long l)
+rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb)
 {
-  struct ppp *ppp = ppp_find(tty);
-  register int temp_i = 0;
-  int error;
-
-  if (!ppp || ppp->magic != PPP_MAGIC) {
-    PRINTK ((KERN_ERR "ppp_ioctl: can't find PPP block from tty!\n"))
-    return -EBADF;
-  }
-
-  CHECK_PPP(-ENXIO);
-
-  /* This must be root user */
-  if (!suser())
-    return -EPERM;
-
-  switch (i) {
-  case PPPIOCSMRU:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %x\n", temp_i));
-      temp_i = (int) get_fs_long (l);
-      if (ppp->mru != temp_i)
-       ppp_changedmtu (ppp, ppp->mtu, temp_i);
-    }
-    break;
-
-  case PPPIOCGFLAGS:
-    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      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_fs_long ((long) temp_i, l);
-      PRINTKN (3,(KERN_DEBUG "ppp_ioctl: get flags: addr %lx flags %x\n",
-                 l,
-                 temp_i));
-    }
-    break;
-
-  case PPPIOCSFLAGS:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      temp_i      = (int) get_fs_long (l);
-      ppp->flags ^= ((ppp->flags ^ temp_i) & SC_MASK);
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: set flags to %x\n", temp_i));
-    }
-    break;
-
-  case PPPIOCGASYNCMAP:
-    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      put_fs_long (ppp->xmit_async_map[0], l);
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: get asyncmap: addr %lx asyncmap %lx\n",
-                 l, ppp->xmit_async_map[0]));
-    }
-    break;
-
-  case PPPIOCSASYNCMAP:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
-      ppp->xmit_async_map[0] = get_fs_long (l);
-      bset (ppp->xmit_async_map, PPP_FLAG);
-      bset (ppp->xmit_async_map, PPP_ESC);
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: set xmit asyncmap %lx\n",
-                 ppp->xmit_async_map[0]));
-    }
-    break;
-
-  case PPPIOCRASYNCMAP:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      ppp->recv_async_map = get_fs_long (l);
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: set recv asyncmap %lx\n",
-                 ppp->recv_async_map));
-    }
-    break;
-
-  case PPPIOCGUNIT:
-    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      put_fs_long (ppp->dev->base_addr, l);
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %d", ppp->dev->base_addr));
-    }
-    break;
-
-  case PPPIOCSINPSIG:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      ppp->inp_sig     = (int) get_fs_long (l);
-      ppp->inp_sig_pid = current->pid;
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: set input signal %d\n", ppp->inp_sig));
-    }
-    break;
-
-  case PPPIOCSDEBUG:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      ppp_debug = (int) get_fs_long (l);
-      ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8;
-      ppp_debug &= 0xff;
-      PRINTKN (1, (KERN_INFO "ppp_ioctl: set debug level %d, netpacket %d\n", 
-                  ppp_debug, ppp_debug_netpackets));
-    }
-    break;
-
-  case PPPIOCGDEBUG:
-    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      put_fs_long ((long) (ppp_debug | (ppp_debug_netpackets << 8)), l);
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: get debug level %d\n", 
-                 ppp_debug | (ppp_debug_netpackets << 8)));
-    }
-    break;
-
-  case PPPIOCGSTAT:
-    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_stats));
-    if (error == 0) {
-      memcpy_tofs ((void *) l, &ppp->stats, sizeof (struct ppp_stats));
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: read statistics\n"));
-    }
-    break;
-
-  case PPPIOCGTIME:
-    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_ddinfo));
-    if (error == 0) {
-      struct ppp_ddinfo cur_ddinfo;
-      unsigned long cur_jiffies = jiffies;
-
-      /* change absolute times to relative times. */
-      cur_ddinfo.ip_sjiffies  = cur_jiffies - ppp->ddinfo.ip_sjiffies;
-      cur_ddinfo.ip_rjiffies  = cur_jiffies - ppp->ddinfo.ip_rjiffies;
-      cur_ddinfo.nip_sjiffies = cur_jiffies - ppp->ddinfo.nip_sjiffies;
-      cur_ddinfo.nip_rjiffies = cur_jiffies - ppp->ddinfo.nip_rjiffies;
-      
-      memcpy_tofs ((void *) l, &cur_ddinfo, sizeof (struct ppp_ddinfo));
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: read demand dial info\n"));
-    }
-    break;
-
-  case PPPIOCGXASYNCMAP:
-    error = verify_area (VERIFY_WRITE,
-                        (void *) l,
-                        sizeof (ppp->xmit_async_map));
-    if (error == 0) {
-      memcpy_tofs ((void *) l,
-                  ppp->xmit_async_map,
-                  sizeof (ppp->xmit_async_map));
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: get xasyncmap: addr %lx\n", l));
-    }
-    break;
-
-  case PPPIOCSXASYNCMAP:
-    error = verify_area (VERIFY_READ, (void *) l,
-                        sizeof (ppp->xmit_async_map));
-    if (error == 0) {
-      unsigned long temp_tbl [8];
-
-      memcpy_fromfs (temp_tbl, (void *) l, sizeof (ppp->xmit_async_map));
-      temp_tbl[1]  =  0x00000000; /* must not escape 0x20 - 0x3f */
-      temp_tbl[2] &= ~0x40000000; /* must not escape 0x5e        */
-      temp_tbl[3] |=  0x60000000; /* must escape 0x7d and 0x7e   */
-
-      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));
-       PRINTKN (3,(KERN_INFO "ppp_ioctl: set xasyncmap\n"));
-      }
-    }
-    break;
-
-  case PPPIOCSMAXCID:
-    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
-    if (error == 0) {
-      temp_i = (int) get_fs_long (l) + 1;
-      PRINTKN (3,(KERN_INFO "ppp_ioctl: set maxcid to %d\n", temp_i));
-      if (ppp->slcomp != NULL)
-       slhc_free (ppp->slcomp);
-
-      ppp->slcomp = slhc_init (temp_i, temp_i);
-
-      if (ppp->slcomp == NULL) {
-       PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
-       ppp_release (ppp);
-       error = -ENOMEM;
-      }
-    }
-    break;
-
-#ifdef NEW_TTY_DRIVERS
-    /* Allow stty to read, but not set, the serial port */
-  case TCGETS:
-  case TCGETA:
-    error = n_tty_ioctl(tty, file, i, l);
-    break;
-#endif
+       int new_count;
+
+       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);
+}
 
 /*
- *  All other ioctl() events will come here.
+ * Process the receipt of an VJ Un-compressed frame
  */
+static int
+rcv_proto_vjc_uncomp(struct ppp *ppp, struct sk_buff *skb)
+{
+       CHECK_PPP(0);
+       if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
+               return 0;
+       if (slhc_remember(ppp->slcomp, skb->data + PPP_HDRLEN,
+                         skb->len - PPP_HDRLEN) <= 0) {
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_NOTICE "ppp: error in VJ memorizing\n");
+               return 0;
+       }
+       return rcv_proto_ip(ppp, skb);
+}
 
-  default:
-    PRINTKN (1,(KERN_ERR "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
-               i,
-               l));
-#ifdef NEW_TTY_DRIVERS
-    error = -ENOIOCTLCMD;
-#else
-    error = -EINVAL;
-#endif
-    break;
-  }
-  return error;
+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);
 }
 
+/*
+ * Receive all unclassified protocols.
+ */
 static int
-ppp_select (struct tty_struct *tty, struct inode * inode,
-           struct file * filp, int sel_type, select_table * wait)
+rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb)
 {
-  struct ppp *ppp = ppp_find (tty);
-  
-  if (!ppp || ppp->magic != PPP_MAGIC) {
-    PRINTK ((KERN_ERR "ppp_select: can't find PPP block from tty!\n"))
-    return -EBADF;
-  }
-  
-  /* If the PPP protocol is no longer active, return false */
-  CHECK_PPP (0);
-  
-  /* Process the request based upon the type desired */
-  switch (sel_type) {
-  case SEL_IN:
-    if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
-      /* Test for the presence of data in the queue */
-      if (ppp->us_rbuff_head != ppp->us_rbuff_tail) {
-       clear_bit (0, &ppp->us_rbuff_lock);
-       return 1;
-      }
-      clear_bit (0, &ppp->us_rbuff_lock);
-    } /* fall through */
-
-  case SEL_EX:
-    /* Is there a pending error condition? */
-    if (tty->packet && tty->link->ctrl_status)
-      return 1;
-    
-    /* closed? */
-    if (tty->flags & (1 << TTY_SLAVE_CLOSED))
-      return 1;
-    
-    /* If the tty is disconnected, then this is an exception too */
-    if (tty_hung_up_p(filp))
-      return 1;
-
-    select_wait (&ppp->read_wait, wait);
-    break;
-    
-  case SEL_OUT:
-    if (ppp_lock (ppp)) {
-      if (ppp->sending == 0) {
-       ppp_unlock (ppp);
+       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);
+       }
+
+       wake_up_interruptible (&ppp->read_wait);
+       if (ppp->tty->fasync != NULL)
+               kill_fasync (ppp->tty->fasync, SIGIO);
+
        return 1;
-      }
-      ppp_unlock (ppp);
-    }
-    select_wait (&ppp->write_wait, wait);
-    break;
-  }
-  return 0;
 }
 
 /*************************************************************
- * 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
  *************************************************************/
 
-int
-ppp_xmit(struct sk_buff *skb, struct device *dev)
+/* 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;
+}
+
+/*
+ * 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 void
+ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
 {
-  struct tty_struct *tty;
-  struct ppp *ppp;
-  unsigned char *p;
-  unsigned short proto;
-  int len;
-
-  /* just a little sanity check. */
-  if (skb == NULL) {
-    PRINTKN(3,(KERN_WARNING "ppp_xmit: null packet!\n"));
-    return 0;
-  }
-
-  /* Get pointers to the various components */
-  ppp   = &ppp_ctrl[dev->base_addr];
-  tty   = ppp->tty;
-  p     = (unsigned char *) (skb + 1);
-  len   = skb->len;
-  proto = PROTO_IP;
-
-  PRINTKN(4,(KERN_DEBUG "ppp_xmit [%s]: skb %lX busy %d\n", dev->name, 
-            (unsigned long int) skb, ppp->sending));
-
-  CHECK_PPP(0);
-
-  if (tty == NULL) {
-    PRINTKN(1,(KERN_ERR "ppp_xmit: %s not connected to a TTY!\n", dev->name));
-    goto done;
-  }
-
-  if (!(dev->flags & IFF_UP)) {
-    PRINTKN(1,(KERN_WARNING
-              "ppp_xmit: packet sent on interface %s, which is down for IP\n",
-              dev->name));
-    goto done;
-  }
-
-  /* get length from IP header as per Alan Cox bugfix for slip.c */
-  if (len < sizeof(struct iphdr)) {
-    PRINTKN(0,(KERN_ERR "ppp_xmit: given runt packet, ignoring\n"));
-    return 1;
-  }
-  len = ntohs( ((struct iphdr *)(skb->data)) -> tot_len );
-
-  /* If doing demand dial then divert the first frame to pppd. */
-  if (ppp->flags & SC_IP_DOWN) {
-    if (ppp->flags & SC_IP_FLUSH == 0) {
-      if (ppp_us_queue (ppp, proto, p, len))
-       ppp->flags |= SC_IP_FLUSH;
-    }
-    goto done;
-  }
-
-  /* Attempt to acquire send lock */
-  if (ppp->sending || !ppp_lock(ppp)) {
-    PRINTKN(3,(KERN_WARNING "ppp_xmit: busy\n"));
-    ppp->stats.sbusy++;
-    return 1;
-  }
-
-  ppp->xhead = ppp->xbuff;
-
-  /* try to compress, if VJ compression mode is on */
-  if (ppp->flags & SC_COMP_TCP) {
-    /* NOTE: last 0 argument says never to compress connection ID */
-    len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p, 0);
-    if (p[0] & SL_TYPE_COMPRESSED_TCP)
-      proto = PROTO_VJCOMP;
-    else {
-      if (p[0] >= SL_TYPE_UNCOMPRESSED_TCP) {
-       proto = PROTO_VJUNCOMP;
-       p[0] = (p[0] & 0x0f) | 0x40; 
-      }
-    }
-  }
-
-  /* increment appropriate counter */
-  if (proto == PROTO_VJCOMP)
-    ++ppp->stats.scomp;
-  else
-    ++ppp->stats.suncomp;
-      
-  if (ppp_debug_netpackets) {
-    struct iphdr *iph = (struct iphdr *) (skb + 1);
-    PRINTK ((KERN_DEBUG "%s ==> proto %x len %d src %x dst %x proto %d\n",
-           dev->name, (int) proto, (int) len, (int) iph->saddr,
-           (int) iph->daddr, (int) iph->protocol))
-  }
-
-  /* start of frame:   FLAG  ALL_STATIONS  CONTROL  <protohi> <protolo> */
-#ifdef OPTIMIZE_FLAG_TIME
-  if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
-    *ppp->xhead++ = PPP_FLAG;
-  ppp->last_xmit = jiffies;
-#else      
-  *ppp->xhead++ = PPP_FLAG;
-#endif
+       int     proto;
+       __u8    *data;
+       int     count;
+       __u8    *p;
+       int     ret;
+
+       CHECK_PPP_VOID();
+       data = skb->data;
+       count = skb->len;
+
+       /* dump the buffer */
+       if (ppp->flags & SC_LOG_OUTPKT)
+               ppp_print_buffer ("write frame", data, count);
+
+       /*
+        * Handle various types of protocol-specific compression
+        * and other processing, including:
+        * - VJ TCP header compression
+        * - updating LQR packets
+        * - updating CCP state on CCP packets
+        */
+       proto = PPP_PROTOCOL(data);
+       switch (proto) {
+       case PPP_IP:
+               if ((ppp->flags & SC_COMP_TCP) && ppp->slcomp != NULL)
+                       skb = ppp_vj_compress(ppp, skb);
+               break;
+
+       case 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;
+
+       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;
+
+       /*
+        * 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);
+
+               /* 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);
+               }
+       }
 
-  ppp->fcs = PPP_FCS_INIT;
-  if (!(ppp->flags & SC_COMP_AC)) { 
-    ppp_stuff_char(ppp, PPP_ADDRESS);
-    ppp_stuff_char(ppp, PPP_CONTROL);
-  }
-
-  if (!(ppp->flags & SC_COMP_PROT) || (proto & 0xff00))
-    ppp_stuff_char(ppp, proto>>8);
-  ppp_stuff_char(ppp, proto&0xff);
-
-  /* data part */
-  while (len-- > 0)
-    ppp_stuff_char(ppp, *p++);
-
-  /* fcs and flag */
-  ppp_add_fcs(ppp);
-  *ppp->xhead++ = PPP_FLAG;
-
-  /* update the time for demand dial function */
-  ppp->ddinfo.ip_sjiffies = jiffies;
-
-  /* send it! */
-  if (ppp_debug >= 6)
-    ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
-  else {
-    PRINTKN (4,(KERN_DEBUG
-               "ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
-  }
-
-  ppp_kick_tty(ppp);
-
- done:
-  if (skb->free) 
-    kfree_skb(skb, FREE_WRITE);
-  return 0;
+       /*
+        * 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");
+       }
 }
-  
-static unsigned short
-ppp_type_trans (struct sk_buff *skb, struct device *dev)
+
+/*
+ * Apply VJ TCP header compression to a packet.
+ */
+static struct sk_buff *
+ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
 {
-  return(htons(ETH_P_IP));
+       __u8 *orig_data, *data;
+       struct sk_buff *new_skb;
+       int len, proto;
+
+       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);
+
+       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);
+
+       if (data == orig_data) {
+               /* Couldn't compress the data */
+               KFREE_SKB(new_skb);
+               return skb;
+       }
+
+       /* 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];
+       }
+
+       data = skb_put(new_skb, len + PPP_HDRLEN);
+       data[0] = PPP_ALLSTATIONS;
+       data[1] = PPP_UI;
+       data[2] = 0;
+       data[3] = proto;
+
+       KFREE_SKB(skb);
+       return new_skb;
 }
 
-#ifdef NET02D
-static int
-ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
-          unsigned long daddr, unsigned long saddr, unsigned len)
+static inline void
+ppp_send_frames(struct ppp *ppp)
 {
-  return(0);
+       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);
+       }
 }
 
-static int
-ppp_rebuild_header(void *buff, struct device *dev)
+/*
+ * Called from the hardware (tty) layer when it can accept
+ * another packet.
+ */
+static void
+ppp_output_wakeup(struct ppp *ppp)
 {
-  return(0);
+       CHECK_PPP_VOID();
+
+       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);
 }
 
+/*
+ * Send a control frame (from pppd).
+ */
 static void
-ppp_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
+ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb)
 {
+       CHECK_PPP_VOID();
+
+       /*
+        * Put the packet on the queue, then send as many as we can.
+        */
+       skb_queue_tail(&ppp->xmt_q, skb);
+       ppp_send_frames(ppp);
 }
 
-#else
 
+/*************************************************************
+ * NETWORK OUTPUT
+ *    This routine accepts requests from the network layer
+ *    and attempts to deliver the packets.
+ *************************************************************/
+/*
+ * Send a frame to the peer.
+ * Returns 1 iff the frame was not accepted.
+ */
 static int
-ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
-          void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
+ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct ppp *ppp = dev2ppp(dev);
+       struct tty_struct *tty = ppp2tty(ppp);
+       enum NPmode npmode;
+       int proto;
+       unsigned char *hdr;
+
+       /* just a little sanity check. */
+       if (skb == NULL)
+               return 0;
+       if (skb->data == NULL) {
+               KFREE_SKB(skb);
+               return 0;
+       }
+
+       /*
+        * Avoid timing problem should tty hangup while data is
+        * queued to be sent.
+        */
+       if (!ppp->inuse) {
+               KFREE_SKB(skb);
+               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);
+               KFREE_SKB(skb);
+               return 0;
+       }
+
+       /*
+        * Work out the appropriate network-protocol mode for this packet.
+        */
+       npmode = NPMODE_PASS;   /* default */
+       switch (ntohs(skb->protocol)) {
+       case ETH_P_IP:
+               proto = PPP_IP;
+               npmode = ppp->sc_npmode[NP_IP];
+               break;
+       case ETH_P_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;
+       }
+
+       /*
+        * Drop, accept or reject the packet depending on the mode.
+        */
+       switch (npmode) {
+       case NPMODE_PASS:
+               break;
+
+       case NPMODE_QUEUE:
+               /*
+                * We may not send the packet now, so drop it.
+                * XXX It would be nice to be able to return it to the
+                * network system to be queued and retransmitted later.
+                */
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_DEBUG "%s: returning frame\n", ppp->name);
+               KFREE_SKB(skb);
+               return 0;
+
+       case NPMODE_ERROR:
+       case NPMODE_DROP:
+               if (ppp->flags & SC_DEBUG)
+                       printk(KERN_DEBUG
+                              "ppp_dev_xmit: dropping (npmode = %d) on %s\n",
+                              npmode, ppp->name);
+               KFREE_SKB(skb);
+               return 0;
+       }
+
+       /*
+        * 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;
+       }
+       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;
+       }
+
+       hdr = skb_push(skb, PPP_HDRLEN);
+       hdr[0] = PPP_ALLSTATIONS;
+       hdr[1] = PPP_UI;
+       hdr[2] = proto >> 8;
+       hdr[3] = proto;
+
+       ppp_send_frame(ppp, skb);
+       if (!ppp->xmit_busy)
+               ppp_send_frames(ppp);
+       return 0;
+}
+
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+/*
+ * Null hard_header and header_rebuild routines.
+ */
+static int ppp_dev_header(struct sk_buff *skb, struct device *dev,
+                         unsigned short type, void *daddr,
+                         void *saddr, unsigned int len)
 {
-  return(0);
+       return 0;
 }
 
-static int
-ppp_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
-                  struct sk_buff *skb)
+static int ppp_dev_rebuild(void *eth, struct device *dev,
+                          unsigned long raddr, struct sk_buff *skb)
 {
-  return(0);
+       return 0;
 }
-#endif
+#endif /* < 2.1.15 */
 
-static struct enet_statistics *
-ppp_get_stats (struct device *dev)
+/*
+ * Generate the statistic information for the /proc/net/dev listing.
+ */
+static struct net_device_stats *
+ppp_dev_stats (struct device *dev)
 {
-  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
-  static struct enet_statistics ppp_stats;
-
-  ppp_stats.rx_packets = ppp->stats.rcomp + ppp->stats.runcomp;
-  ppp_stats.rx_errors = ppp->stats.rerrors;
-  ppp_stats.rx_dropped = ppp->stats.tossed;
-  ppp_stats.rx_fifo_errors = 0;
-  ppp_stats.rx_length_errors = ppp->stats.runts;
-  ppp_stats.rx_over_errors = ppp->stats.roverrun;
-  ppp_stats.rx_crc_errors = 0;
-  ppp_stats.rx_frame_errors = 0;
-  ppp_stats.tx_packets = ppp->stats.scomp + ppp->stats.suncomp;
-  ppp_stats.tx_errors = ppp->stats.serrors;
-  ppp_stats.tx_dropped = 0;
-  ppp_stats.tx_fifo_errors = 0;
-  ppp_stats.collisions = ppp->stats.sbusy;
-  ppp_stats.tx_carrier_errors = 0;
-  ppp_stats.tx_aborted_errors = 0;
-  ppp_stats.tx_window_errors = 0;
-  ppp_stats.tx_heartbeat_errors = 0;
-
-  PRINTKN (3, (KERN_INFO "ppp_get_stats called"));
-  return &ppp_stats;
+       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;
+#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;
 }
 
 /*************************************************************
@@ -1844,155 +2926,386 @@ ppp_get_stats (struct device *dev)
  *    Miscellany called by various functions above.
  *************************************************************/
 
-#ifndef NEW_TTY_DRIVERS
-/* find a PPP channel given a TTY */
-struct ppp *
-ppp_find(struct tty_struct *tty)
+/* Locate the previous instance of the PPP channel */
+static struct ppp *
+ppp_find(int pid_value)
 {
-  int i;
-  for (i = 0; i < PPP_NRUNIT; i++)
-    if (ppp_ctrl[i].inuse && (ppp_ctrl[i].tty == tty)) return &ppp_ctrl[i];
-
-  return NULL;
+       struct ppp      *ppp;
+
+       /* try to find the device which this pid is already using */
+       for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
+               if (ppp->inuse && ppp->sc_xfer == pid_value) {
+                       ppp->sc_xfer = 0;
+                       break;
+               }
+       }
+       return ppp;
 }
-#endif
 
-/* allocate a PPP channel */
+/* allocate or create a PPP channel */
 static struct ppp *
 ppp_alloc(void)
 {
-  int i;
-  for (i = 0; i < PPP_NRUNIT; i++)
-    if (!set_bit(0, &ppp_ctrl[i].inuse)) return &ppp_ctrl[i];
+       int             if_num;
+       int             status;
+       struct device   *dev;
+       struct ppp      *ppp;
+
+       /* try to find an free device */
+       for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
+               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) {
+               printk(KERN_ERR "ppp: struct ppp allocation failed\n");
+               return 0;
+       }
+       memset(ppp, 0, sizeof(*ppp));
+
+       /* initialize channel control data */
+       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->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);
+       } else {
+               printk(KERN_ERR
+                      "ppp_alloc - register_netdev(%s) = %d failure.\n",
+                      dev->name, status);
+               kfree(ppp);
+               ppp = NULL;
+       }
+
+       /* link this unit into our list */
+       if (ppp_list == 0)
+               ppp_list = ppp;
+       else
+               ppp_last->next = ppp;
+       ppp_last = ppp;
 
-  return NULL;
+       return ppp;
 }
 
-/* marks a PPP interface 'busy'.  user processes will wait, if
-   they try to write, and the network code will refrain from sending
-   return nonzero if succeeded in acquiring lock
-*/
+/*
+ * Initialize the generic parts of the ppp structure.
+ */
+static void
+ppp_generic_init(struct ppp *ppp)
+{
+       int indx;
 
-static int
-ppp_lock(struct ppp *ppp)
+       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)
 {
-  int flags, locked;
-  save_flags(flags);
-  cli();
-  locked = ppp->sending;
-  ppp->sending = 1;
-  if (ppp->dev->flags & IFF_UP)
-    ppp->dev->tbusy = 1;
-  restore_flags(flags);
-  return locked == 0;
+       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_unlock(struct ppp *ppp)
+ppp_print_hex (register __u8 * out, const __u8 * in, int count)
 {
-  int flags;
-  save_flags(flags);
-  cli();
-  ppp->sending = 0;
-  if (ppp->dev->flags & IFF_UP)
-    ppp->dev->tbusy = 0;
-  restore_flags(flags);
+       register __u8 next_ch;
+       static char hex[] = "0123456789ABCDEF";
+
+       while (count-- > 0) {
+               next_ch = *in++;
+               *out++ = hex[(next_ch >> 4) & 0x0F];
+               *out++ = hex[next_ch & 0x0F];
+               ++out;
+       }
 }
 
-/* FCS support functions */
+static void
+ppp_print_char (register __u8 * out, const __u8 * in, int count)
+{
+       register __u8 next_ch;
+
+       while (count-- > 0) {
+               next_ch = *in++;
+
+               if (next_ch < 0x20 || next_ch > 0x7e)
+                       *out++ = '.';
+               else {
+                       *out++ = next_ch;
+                       if (next_ch == '%')   /* printk/syslogd has a bug !! */
+                               *out++ = '%';
+               }
+       }
+       *out = '\0';
+}
 
 static void
-ppp_add_fcs(struct ppp *ppp)
+ppp_print_buffer (const char *name, const __u8 *buf, int count)
 {
-  unsigned short fcs = ppp->fcs;
-
-  fcs ^= 0xffff;
-  ppp_stuff_char(ppp, fcs & 0x00ff);
-  ppp_stuff_char(ppp, (fcs & 0xff00) >> 8);
-  ASSERT (ppp->fcs == PPP_FCS_GOOD);
-  PRINTKN (4,(KERN_DEBUG "ppp_add_fcs: fcs is %lx\n",
-             (long) (unsigned long) fcs));
+       __u8 line[44];
+
+       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);
+               count -= 8;
+               buf += 8;
+       }
+
+       if (count > 0) {
+               memset (line, 32, 44);
+               ppp_print_hex (line, buf, count);
+               ppp_print_char (&line[8 * 3], buf, count);
+               printk(KERN_DEBUG "%s\n", line);
+       }
 }
 
-static int
-ppp_check_fcs(struct ppp *ppp)
+/*************************************************************
+ * Compressor module interface
+ *************************************************************/
+
+struct compressor_link {
+       struct compressor_link  *next;
+       struct compressor       *comp;
+};
+
+static struct compressor_link *ppp_compressors = (struct compressor_link *) 0;
+
+static struct compressor *find_compressor (int type)
 {
-  unsigned short fcs = PPP_FCS_INIT, msgfcs;
-  unsigned char *c = ppp->rbuff;
-  int i;
-
-  for (i = 0; i < ppp->rcount - 2; i++, c++)
-    fcs = (fcs >> 8) ^ fcstab[(fcs ^ *c) & 0xff];
-
-  fcs ^= 0xffff;
-  msgfcs = (c[1] << 8) + c[0];
-  PRINTKN (4,(KERN_INFO "ppp_check_fcs: got %lx want %lx\n",
-             (unsigned long) msgfcs, (unsigned long) fcs));
-  return fcs == msgfcs;
+       struct compressor_link *lnk;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+
+       lnk = ppp_compressors;
+       while (lnk != (struct compressor_link *) 0) {
+               if ((int) (__u8) lnk->comp->compress_proto == type) {
+                       restore_flags(flags);
+                       return lnk->comp;
+               }
+               lnk = lnk->next;
+       }
+
+       restore_flags(flags);
+       return (struct compressor *) 0;
 }
 
-static char hex[] = "0123456789ABCDEF";
+#ifdef CONFIG_MODULES
+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);
+
+       if (new == (struct compressor_link *) 0)
+               return 1;
+
+       save_flags(flags);
+       cli();
 
-inline void ppp_print_hex (register char *out, char *in, int count);
-inline void ppp_print_hex (register char *out, char *in, int count)
+       if (find_compressor (cp->compress_proto)) {
+               restore_flags(flags);
+               kfree (new);
+               return 0;
+       }
+
+       new->next       = ppp_compressors;
+       new->comp       = cp;
+       ppp_compressors = new;
+
+       restore_flags(flags);
+       return 0;
+}
+
+static void ppp_unregister_compressor (struct compressor *cp)
 {
-  register unsigned char next_ch;
+       struct compressor_link *prev = (struct compressor_link *) 0;
+       struct compressor_link *lnk;
+       unsigned long flags;
 
-  while (count-- > 0) {
-    next_ch = (unsigned char) get_fs_byte (in);
+       save_flags(flags);
+       cli();
 
-    *out++  = hex[(next_ch >> 4) & 0x0F];
-    *out++  = hex[next_ch        & 0x0F];
-    ++out;
-    ++in;
-  }
+       lnk  = ppp_compressors;
+       while (lnk != (struct compressor_link *) 0) {
+               if (lnk->comp == cp) {
+                       if (prev)
+                               prev->next = lnk->next;
+                       else
+                               ppp_compressors = lnk->next;
+                       kfree (lnk);
+                       break;
+               }
+               prev = lnk;
+               lnk  = lnk->next;
+       }
+       restore_flags(flags);
 }
+#endif
 
-inline void ppp_print_char (register char *out, char *in, int count);
-inline void ppp_print_char (register char *out, char *in, int count)
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+#ifdef MODULE
+int
+init_module(void)
 {
-  register unsigned char next_ch;
-
-  while (count-- > 0) {
-    next_ch = (unsigned char) get_fs_byte (in);
-
-    if (next_ch < 0x20 || next_ch > 0x7e)
-      *out++ = '.';
-    else {
-      *out++ = next_ch;
-      if (next_ch == '%')      /* printk/syslogd has a bug !! */
-       *out++ = '%';
-    }
-    ++in;
-  }
-  *out = '\0';
+       int status;
+
+       /* register our line disciplines */
+       status = ppp_first_time();
+       if (status != 0)
+               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;
 }
 
-static void ppp_print_buffer(const char *name, char *buf, int count, int seg)
+void
+cleanup_module(void)
 {
-  char line [44];
-  int  old_fs = get_fs();
-
-  set_fs (seg);
-
-  if (name != (char *) NULL)
-    PRINTK ((KERN_DEBUG "ppp: %s, count = %d\n", name, count));
-
-  while (count > 8) {
-    memset         (line, ' ', sizeof (line));
-    ppp_print_hex  (line, buf, 8);
-    ppp_print_char (&line[8 * 3], buf, 8);
-    PRINTK ((KERN_DEBUG "%s\n", line));
-    count -= 8;
-    buf   += 8;
-  }
-
-  if (count > 0) {
-    memset         (line, ' ', sizeof (line));
-    ppp_print_hex  (line, buf, count);
-    ppp_print_char (&line[8 * 3], buf, count);
-    PRINTK ((KERN_DEBUG "%s\n", line));
-  }
-
-  set_fs (old_fs);
+       int status;
+       struct ppp *ppp, *next_ppp;
+       int busy = 0;
+
+       /*
+        * Ensure that the devices are not in operation.
+        */
+       for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
+               CHECK_PPP_MAGIC(ppp);
+               if (ppp->inuse || (ppp->dev.flags & IFF_UP))
+                       ++busy;
+       }
+       if (busy)
+               printk(KERN_CRIT "PPP: removing despite %d units in use!\n",
+                      busy);
+
+       /*
+        * 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_ERR
+                      "PPP: Unable to unregister ppp line discipline "
+                      "(err = %d)\n", status);
+       else
+               printk(KERN_INFO
+                      "PPP: ppp line discipline successfully unregistered\n");
+
+       /*
+        * 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;
+               unregister_netdev(&ppp->dev);
+               kfree (ppp);
+       }
 }
+#endif