From b07a2a0e2055cb2ba2b3addd5c50f2c9682a0777 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 27 May 1994 00:55:25 +0000 Subject: [PATCH] Initial revision --- linux/ppp.c | 1998 +++++++++++++++++++++++++++++++++++++++++++++++++++ linux/ppp.h | 235 ++++++ 2 files changed, 2233 insertions(+) create mode 100644 linux/ppp.c create mode 100644 linux/ppp.h diff --git a/linux/ppp.c b/linux/ppp.c new file mode 100644 index 0000000..32d5fa6 --- /dev/null +++ b/linux/ppp.c @@ -0,0 +1,1998 @@ +/* + PPP for Linux + + $Id: ppp.c,v 1.1 1994/05/27 00:55:25 paulus Exp $ +*/ + +/* + Sources: + + slip.c + + RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of + Multi-protocol Datagrams over Point-to-Point Links + + RFC1332: IPCP + + ppp-2.0 + + 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 + character. This is normally set to ((HZ * 3) / 2). + This is 1.5 seconds. If not defined then the leading + flag is always sent. +*/ + +/* #define NET02D /* */ +/* #define NEW_TTY_DRIVERS /* */ +#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) /* */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* to get the struct task_struct */ +#include /* used in new tty drivers */ +#include /* used in new tty drivers */ +#include +#include +#include + +#ifdef NET02D /* v1.1.4 net code and earlier */ +#include +#include +#define skb_queue_head_init(buf) *(buf) = NULL +#else /* v1.1.5 and later */ +#include +#include +#endif + +#include + +#include +#include +#include +#include "slhc.h" + +#include +#ifndef ARPHRD_PPP +#define ARPHRD_PPP 0 +#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;} + +#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))) + +#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) + +int ppp_debug = 2; +int ppp_debug_netpackets = 0; + +/* Define this string only once for all macro envocations */ +static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; + +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 *); + +#ifdef NEW_TTY_DRIVERS +#define ppp_find(tty) ((struct ppp *) tty->disc_data) +#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 *); +#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); +#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 *); +#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); +#else +static void ppp_tty_input_ready(struct tty_struct *); +#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]; + +/************************************************************* + * INITIALIZATION + *************************************************************/ + +static int first_time = 1; + +/* called at boot time for each ppp device */ + +int +ppp_init(struct device *dev) +{ + struct ppp *ppp; + int i; + + ppp = &ppp_ctrl[dev->base_addr]; + + if (first_time) { + first_time = 0; + + 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" +#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; +#else + ppp_ldisc.handler = ppp_tty_input_ready; +#endif + + 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; +#endif + + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); /* = NULL if NET02D */ + + /* 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); + + return 0; +} + +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; +#else + ppp->last_xmit = 0; +#endif + + /* 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; +} + +/* + * 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. + */ + +static void +ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru) +{ + 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; + + /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */ + if (mru < PPP_MRU) + mru = PPP_MRU; + + mtu = (mtu * 2) + 20; + mru = (mru * 2) + 20; + + 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. + */ + if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL) + { + PRINTKN (2,(KERN_ERR "ppp: failed to allocate new buffers\n")); +/* + * Release new buffer pointers if the updates were not performed + */ + if (new_rbuff != NULL) + kfree (new_rbuff); + + if (new_xbuff != NULL) + kfree (new_xbuff); + + 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; + + ppp->xbuff = new_xbuff; + ppp->rbuff = new_rbuff; + ppp->cbuff = new_cbuff; + + dev->mem_start = (unsigned long) new_xbuff; + dev->mem_end = (unsigned long) (dev->mem_start + mtu); + + dev->rmem_start = (unsigned long) new_rbuff; + ppp->rend = (unsigned char *) + dev->rmem_end = (unsigned long) (dev->rmem_start + mru); + + ppp->rhead = new_rbuff; +/* + * Update the parameters for the new buffer sizes + */ + ppp->toss = 0xFE; + ppp->escape = 0; + ppp->sending = 0; + ppp->rcount = 0; + + ppp->mru = new_mru; + + ppp->mtu = + dev->mtu = new_mtu; + + sti(); +/* + * Release old buffer pointers + */ + if (old_rbuff != NULL) + kfree (old_rbuff); + + if (old_xbuff != NULL) + kfree (old_xbuff); + + if (old_cbuff != NULL) + kfree (old_cbuff); + } +} + +/* called when we abandon the PPP line discipline */ + +static void +ppp_release(struct ppp *ppp) +{ +#ifdef NEW_TTY_DRIVERS + if (ppp->tty != NULL && ppp->tty->disc_data == ppp) + ppp->tty->disc_data = NULL; /* Break the tty->ppp link */ +#endif + + if (ppp->dev) { + ppp->dev->flags &= ~IFF_UP; /* down the device */ + ppp->dev->flags |= IFF_POINTOPOINT; + } + + kfree (ppp->xbuff); + kfree (ppp->cbuff); + kfree (ppp->rbuff); + kfree (ppp->us_rbuff); + + ppp->xbuff = + ppp->cbuff = + ppp->rbuff = + ppp->us_rbuff = NULL; + + if (ppp->slcomp) { + slhc_free(ppp->slcomp); + ppp->slcomp = NULL; + } + + ppp->inuse = 0; + ppp->tty = NULL; +} + +static void +ppp_close(struct tty_struct *tty) +{ + struct ppp *ppp = ppp_find(tty); + + 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); + + PRINTKN (2,(KERN_INFO "ppp: channel %s closing.\n", ppp->dev->name)); + } +} + +/* called when PPP line discipline is selected on a tty */ +static int +ppp_open(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 + + 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 +} + +/* called when ppp interface goes "up". here this just means we start + passing IP packets */ +static int +ppp_dev_open(struct device *dev) +{ + struct ppp *ppp = &ppp_ctrl[dev->base_addr]; + + /* reset POINTOPOINT every time, since dev_close zaps it! */ + dev->flags |= IFF_POINTOPOINT; + + if (ppp->tty == NULL) { + PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n", + dev->name)); + return -ENXIO; + } + + PRINTKN (2,(KERN_INFO "ppp: channel %s going up for IP packets!\n", + dev->name)); + + CHECK_PPP(-ENXIO); + return 0; +} + +static int +ppp_dev_close(struct device *dev) +{ + 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; +} + +/************************************************************* + * 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) +{ + /* unlock the transmitter queue */ + ppp_unlock ((struct ppp *) ppp); + + /* 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); + + /* enable any blocked process pending transmission */ + wake_up_interruptible (&((struct ppp *) ppp)->write_wait); +} + +#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 */ + } +} + +#else + +static void +ppp_kick_tty (struct ppp *ppp) +{ + register int count, actual; + + 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); + } +} + +static void ppp_write_wakeup(struct tty_struct *tty) +{ + register int count, actual; + struct ppp *ppp = ppp_find(tty); + + if (!ppp || ppp->magic != PPP_MAGIC) { + PRINTKN (1, + (KERN_ERR "PPP: write_wakeup called but couldn't " + "find PPP struct.\n")); + return; + } + + if (!ppp->xtail || (ppp->flags & SC_XMIT_BUSY)) + return; + + cli(); + if (ppp->flags & SC_XMIT_BUSY) + return; + ppp->flags |= SC_XMIT_BUSY; + sti(); + + count = ppp->xhead - ppp->xtail; + + 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; + + ppp_output_done(ppp); + } else { + ppp->xtail += actual; + } + ppp->flags &= ~SC_XMIT_BUSY; +} +#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. + *************************************************************/ + +/* stuff a single character into the receive buffer */ + +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); +} + +#ifdef CHECK_CHARACTERS +static unsigned paritytab[8] = { + 0x96696996, 0x69969669, 0x69969669, 0x96696996, + 0x69969669, 0x96696996, 0x96696996, 0x69969669 +}; +#endif + +#ifndef NEW_TTY_DRIVERS +static void +ppp_dump_inqueue(struct tty_struct *tty) +{ + 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); +} + +/* called by lower levels of TTY driver when data becomes available. + all incoming data comes through this function. */ + +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); +} + +/* 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 +*/ + +static void +ppp_unesc(struct ppp *ppp, unsigned char *c, int n) +{ + int i; + + for (i = 0; i < n; i++, c++) { + PRINTKN (6,(KERN_DEBUG "(%x)", (unsigned int) *c)); + +#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 illegal */ + ppp->toss = 0xFF; + + 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) +{ + 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; + + 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; + } + } + } +} +#endif + +/* on entry, a received frame is in ppp->rbuff + check it and dispose as appropriate */ +static void +ppp_doframe(struct ppp *ppp) +{ + 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++; +} + +/* 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. */ + +static int +ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c, + int count) +{ + 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; +} + +/* stuff packet at BUF, length LEN, into the us_rbuff buffer + prepend PROTO information */ + +#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; + +static int +ppp_us_queue(struct ppp *ppp, unsigned short proto, + unsigned char *buf, int len) +{ + int totlen; + unsigned char *saved_head; + + totlen = len+2; /* including protocol */ + + 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; + + PUTC((totlen & 0xff00) >> 8, failure); + PUTC(totlen & 0x00ff, failure); + PUTC((proto & 0xff00) >> 8, failure); + PUTC(proto & 0x00ff, failure); + + while (len-- > 0) { + PUTC(*buf++, failure); + } + + PRINTKN (3, (KERN_INFO "ppp: successfully queued %d bytes\n", totlen)); + clear_bit(1, &ppp->us_rbuff_lock); + wake_up_interruptible (&ppp->read_wait); + +#ifdef NEW_TTY_DRIVERS + kill_fasync(ppp->tty->fasync, SIGIO); +#endif + + 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; + + failure: + ppp->us_rbuff_head = saved_head; + clear_bit(1, &ppp->us_rbuff_lock); + + PRINTKN (1, (KERN_NOTICE "ppp_us_queue: ran out of buffer space.\n")); + + return 0; +} + +/************************************************************* + * LINE DISCIPLINE SUPPORT + * The following functions form support user programs + * which read and write data on a TTY with the PPP line + * discipline. Reading is done from a circular queue, + * filled by the lower TTY levels. + *************************************************************/ + +/* read a PPP frame from the us_rbuff circular buffer, + waiting if necessary +*/ + +static int +ppp_read(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr) +{ + 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); +} + +/* 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) +{ + 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]; +} + +/* write a frame with NR chars from BUF to TTY + we have to put the FCS field on ourselves +*/ + +static int +ppp_write(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr) +{ + 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 */ + + *ppp->xhead++ = PPP_FLAG; + + /* reset the time of the last write operation */ + ppp->ddinfo.nip_sjiffies = jiffies; + + 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)); + } + + /* packet is ready-to-go */ + ++ppp->stats.sothers; + ppp_kick_tty(ppp); + + return((int)nr); +} + +static int +ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i, + unsigned long l) +{ + 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 + +/* + * All other ioctl() events will come here. + */ + + 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 +ppp_select (struct tty_struct *tty, struct inode * inode, + struct file * filp, int sel_type, select_table * wait) +{ + 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); + 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...). + *************************************************************/ + +int +ppp_xmit(struct sk_buff *skb, struct device *dev) +{ + 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 */ +#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; + 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; +} + +static unsigned short +ppp_type_trans (struct sk_buff *skb, struct device *dev) +{ + return(htons(ETH_P_IP)); +} + +#ifdef NET02D +static int +ppp_header(unsigned char *buff, struct device *dev, unsigned short type, + unsigned long daddr, unsigned long saddr, unsigned len) +{ + return(0); +} + +static int +ppp_rebuild_header(void *buff, struct device *dev) +{ + return(0); +} + +static void +ppp_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev) +{ +} + +#else + +static int +ppp_header(unsigned char *buff, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len, struct sk_buff *skb) +{ + return(0); +} + +static int +ppp_rebuild_header(void *buff, struct device *dev, unsigned long raddr, + struct sk_buff *skb) +{ + return(0); +} +#endif + +static struct enet_statistics * +ppp_get_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; +} + +/************************************************************* + * UTILITIES + * 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) +{ + 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; +} +#endif + +/* allocate 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]; + + return NULL; +} + +/* 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 +*/ + +static int +ppp_lock(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; +} + +static void +ppp_unlock(struct ppp *ppp) +{ + int flags; + save_flags(flags); + cli(); + ppp->sending = 0; + if (ppp->dev->flags & IFF_UP) + ppp->dev->tbusy = 0; + restore_flags(flags); +} + +/* FCS support functions */ + +static void +ppp_add_fcs(struct ppp *ppp) +{ + 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)); +} + +static int +ppp_check_fcs(struct ppp *ppp) +{ + 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; +} + +static char hex[] = "0123456789ABCDEF"; + +inline void ppp_print_hex (register char *out, char *in, int count); +inline void ppp_print_hex (register char *out, char *in, int count) +{ + register unsigned char next_ch; + + while (count-- > 0) { + next_ch = (unsigned char) get_fs_byte (in); + + *out++ = hex[(next_ch >> 4) & 0x0F]; + *out++ = hex[next_ch & 0x0F]; + ++out; + ++in; + } +} + +inline void ppp_print_char (register char *out, char *in, int count); +inline void ppp_print_char (register char *out, char *in, int count) +{ + 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'; +} + +static void ppp_print_buffer(const char *name, char *buf, int count, int seg) +{ + 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); +} diff --git a/linux/ppp.h b/linux/ppp.h new file mode 100644 index 0000000..153d959 --- /dev/null +++ b/linux/ppp.h @@ -0,0 +1,235 @@ +#ifndef _LINUX_PPP_H +#define _LINUX_PPP_H + +/* definitions for kernel PPP module + Michael Callahan + Nov. 4 1993 */ + +/* $Id: ppp.h,v 1.1 1994/05/27 00:55:25 paulus Exp $ */ + +/* how many PPP units? */ +#define PPP_NRUNIT 4 + +#define PPP_VERSION "0.2.7" + +/* line discipline number */ +#define N_PPP 3 + +/* Magic value for the ppp structure */ +#define PPP_MAGIC 0x5002 + +#define PPPIOCGFLAGS 0x5490 /* get configuration flags */ +#define PPPIOCSFLAGS 0x5491 /* set configuration flags */ +#define PPPIOCGASYNCMAP 0x5492 /* get async map */ +#define PPPIOCSASYNCMAP 0x5493 /* set async map */ +#define PPPIOCGUNIT 0x5494 /* get ppp unit number */ +#define PPPIOCSINPSIG 0x5495 /* set input ready signal */ +#define PPPIOCSDEBUG 0x5497 /* set debug level */ +#define PPPIOCGDEBUG 0x5498 /* get debug level */ +#define PPPIOCGSTAT 0x5499 /* read PPP statistic information */ +#define PPPIOCGTIME 0x549A /* read time delta information */ +#define PPPIOCGXASYNCMAP 0x549B /* get async table */ +#define PPPIOCSXASYNCMAP 0x549C /* set async table */ +#define PPPIOCSMRU 0x549D /* set receive unit size for PPP */ +#define PPPIOCRASYNCMAP 0x549E /* set receive async map */ +#define PPPIOCSMAXCID 0x549F /* set the maximum compression slot id */ + +/* special characters in the framing protocol */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7E /* frame delimiter -- marks frame boundaries */ +#define PPP_ADDRESS 0xFF /* first character of frame <-- (may be */ +#define PPP_CONTROL 0x03 /* second character of frame <-- compressed)*/ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ +#define PPP_ESC 0x7d /* escape charecter -- next character is + data, and the PPP_TRANS bit should be + toggled. PPP_ESC PPP_FLAG is illegal */ + +/* protocol numbers */ +#define PROTO_IP 0x0021 +#define PROTO_VJCOMP 0x002d +#define PROTO_VJUNCOMP 0x002f + +/* FCS support */ +#define PPP_FCS_INIT 0xffff +#define PPP_FCS_GOOD 0xf0b8 + +/* initial MTU */ +#define PPP_MTU 1500 + +/* initial MRU */ +#define PPP_MRU PPP_MTU + +/* flags */ +#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ +#define SC_COMP_AC 0x00000002 /* header compression (output) */ +#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ +#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ +#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ +#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ +#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */ +#define SC_IP_DOWN 0x00000200 /* give ip frames to pppd */ +#define SC_IP_FLUSH 0x00000400 /* "next time" flag for IP_DOWN */ +#define SC_DEBUG 0x00010000 /* enable debug messages */ +#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */ +#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */ +#define SC_LOG_RAWIN 0x00080000 /* log all chars received */ +#define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */ + +/* Flag bits to determine state of input characters */ +#define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */ +#define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 0 */ +#define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */ +#define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */ + +#define SC_MASK 0x0fffffff /* bits that user can change */ + +/* flag for doing transmitter lockout */ +#define SC_XMIT_BUSY 0x10000000 /* ppp_write_wakeup is active */ + +/* + * This is the format of the data buffer of a LQP packet. The packet data + * is sent/received to the peer. + */ + +struct ppp_lqp_packet_hdr { + unsigned long LastOutLQRs; /* Copied from PeerOutLQRs */ + unsigned long LastOutPackets; /* Copied from PeerOutPackets */ + unsigned long LastOutOctets; /* Copied from PeerOutOctets */ + unsigned long PeerInLQRs; /* Copied from SavedInLQRs */ + unsigned long PeerInPackets; /* Copied from SavedInPackets */ + unsigned long PeerInDiscards; /* Copied from SavedInDiscards */ + unsigned long PeerInErrors; /* Copied from SavedInErrors */ + unsigned long PeerInOctets; /* Copeid from SavedInOctets */ + unsigned long PeerOutLQRs; /* Copied from OutLQRs, plus 1 */ + unsigned long PeerOutPackets; /* Current ifOutUniPackets, + 1 */ + unsigned long PeerOutOctets; /* Current ifOutOctets + LQR */ + }; + +/* + * This data is not sent to the remote. It is updated by the driver when + * a packet is received. + */ + +struct ppp_lqp_packet_trailer { + unsigned long SaveInLQRs; /* Current InLQRs on receiption */ + unsigned long SaveInPackets; /* Current ifInUniPackets */ + unsigned long SaveInDiscards; /* Current ifInDiscards */ + unsigned long SaveInErrors; /* Current ifInErrors */ + unsigned long SaveInOctets; /* Current ifInOctects */ +}; + +/* + * PPP LQP packet. The packet is changed by the driver immediately prior + * to transmission and updated upon receiption with the current values. + * So, it must be known to the driver as well as the pppd software. + */ + +struct ppp_lpq_packet { + unsigned long magic; /* current magic value */ + struct ppp_lqp_packet_hdr hdr; /* Header fields for structure */ + struct ppp_lqp_packet_trailer tail; /* Trailer fields (not sent) */ +}; + +/* + * PPP interface statistics. (used by LQP / pppstats) + */ + +struct ppp_stats { + unsigned long rbytes; /* bytes received */ + unsigned long rcomp; /* compressed packets received */ + unsigned long runcomp; /* uncompressed packets received */ + unsigned long rothers; /* non-ip frames received */ + unsigned long rerrors; /* received errors */ + unsigned long roverrun; /* "buffer overrun" counter */ + unsigned long tossed; /* packets discarded */ + unsigned long runts; /* frames too short to process */ + unsigned long rgiants; /* frames too large to process */ + unsigned long sbytes; /* bytes sent */ + unsigned long scomp; /* compressed packets sent */ + unsigned long suncomp; /* uncompressed packets sent */ + unsigned long sothers; /* non-ip frames sent */ + unsigned long serrors; /* transmitter errors */ + unsigned long sbusy; /* "transmitter busy" counter */ +}; + +/* + * Demand dial fields + */ + +struct ppp_ddinfo { + unsigned long ip_sjiffies; /* time when last IP frame sent */ + unsigned long ip_rjiffies; /* time when last IP frame recvd*/ + unsigned long nip_sjiffies; /* time when last NON-IP sent */ + unsigned long nip_rjiffies; /* time when last NON-IP recvd */ +}; + +#ifdef __KERNEL__ + +struct ppp { + int magic; /* magic value for structure */ + + /* Bitmapped flag fields. */ + char inuse; /* are we allocated? */ + char sending; /* "channel busy" indicator */ + char escape; /* 0x20 if prev char was PPP_ESC*/ + char toss; /* toss this frame */ + + unsigned int flags; /* miscellany */ + + unsigned long xmit_async_map[8]; /* 1 bit means that given control + character is quoted on output*/ + + unsigned long recv_async_map; /* 1 bit means that given control + character is ignored on input*/ + int mtu; /* maximum xmit frame size */ + int mru; /* maximum receive frame size */ + unsigned short fcs; /* FCS field of current frame */ + + /* Various fields. */ + int line; /* PPP channel number */ + struct tty_struct *tty; /* ptr to TTY structure */ + struct device *dev; /* easy for intr handling */ + struct slcompress *slcomp; /* for header compression */ + unsigned long last_xmit; /* time of last transmission */ + + /* These are pointers to the malloc()ed frame buffers. + These buffers are used while processing a packet. If a packet + has to hang around for the user process to read it, it lingers in + the user buffers below. */ + unsigned char *rbuff; /* receiver buffer */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *cbuff; /* compression buffer */ + + /* These are the various pointers into the buffers. */ + unsigned char *rhead; /* RECV buffer pointer (head) */ + unsigned char *rend; /* RECV buffer pointer (end) */ + int rcount; /* PPP receive counter */ + unsigned char *xhead; /* XMIT buffer pointer (head) */ + unsigned char *xtail; /* XMIT buffer pointer (end) */ + + /* Structures for interfacing with the user process. */ +#define RBUFSIZE 4000 + unsigned char *us_rbuff; /* circular incoming packet buf.*/ + unsigned char *us_rbuff_end; /* end of allocated space */ + unsigned char *us_rbuff_head; /* head of waiting packets */ + unsigned char *us_rbuff_tail; /* tail of waiting packets */ + unsigned char us_rbuff_lock; /* lock: bit 0 head bit 1 tail */ + int inp_sig; /* input ready signal for pgrp */ + int inp_sig_pid; /* process to get notified */ + + /* items to support the select() function */ + struct wait_queue *write_wait; /* queue for reading processes */ + struct wait_queue *read_wait; /* queue for writing processes */ + + /* PPP interface statistics. */ + struct ppp_stats stats; /* statistic information */ + + /* PPP demand dial information. */ + struct ppp_ddinfo ddinfo; /* demand dial information */ +}; + +#endif /* __KERNEL__ */ +#endif /* _LINUX_PPP_H */ + + -- 2.39.2