]> git.ozlabs.org Git - ppp.git/blobdiff - linux/ppp.c
update for 2.3.9
[ppp.git] / linux / ppp.c
index 333c7fa95ea705b8ede5c297e018bfffe2f088b1..f5f9151f5cc3e1744ae9412dc5787f39790037d4 100644 (file)
@@ -4,7 +4,7 @@
  *  Al Longyear <longyear@netcom.com>
  *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  ==FILEVERSION 990114==
+ *  ==FILEVERSION 990510==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -45,7 +45,7 @@
 
 #define PPP_MAX_RCV_QLEN       32      /* max # frames we queue up for pppd */
 
-/* $Id: ppp.c,v 1.21 1999/02/26 06:48:21 paulus Exp $ */
+/* $Id: ppp.c,v 1.30 1999/07/23 07:07:34 paulus Exp $ */
 
 #include <linux/version.h>
 #include <linux/config.h>
 #endif
 
 #undef PPP_VERSION
-#define PPP_VERSION    "2.3.6"
+#define PPP_VERSION    "2.3.9"
 
 #if LINUX_VERSION_CODE >= VERSION(2,1,4)
 
@@ -174,20 +174,25 @@ typedef size_t            rw_count_t;
 #define SUSER()                capable(CAP_NET_ADMIN)
 #endif
 
+#if LINUX_VERSION_CODE < VERSION(2,2,0)
+#define wmb()          mb()
+#endif
+
 /*
  * Local functions
  */
 
-#ifdef CONFIG_MODULES
 static int ppp_register_compressor (struct compressor *cp);
 static void ppp_unregister_compressor (struct compressor *cp);
-#endif
 
 static void ppp_async_init(struct ppp *ppp);
 static void ppp_async_release(struct ppp *ppp);
+static int ppp_tty_sync_push(struct ppp *ppp);
 static int ppp_tty_push(struct ppp *ppp);
 static int ppp_async_encode(struct ppp *ppp);
 static int ppp_async_send(struct ppp *, struct sk_buff *);
+static int ppp_sync_send(struct ppp *, struct sk_buff *);
+static void ppp_tty_flush_output(struct ppp *);
 
 static int ppp_ioctl(struct ppp *, unsigned int, unsigned long);
 static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp);
@@ -336,6 +341,9 @@ __u16 ppp_crc16_table[256] =
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
+#if LINUX_VERSION_CODE >= VERSION(2,1,18)
+EXPORT_SYMBOL(ppp_crc16_table);
+#endif
 
 #ifdef CHECK_CHARACTERS
 static __u32 paritytab[8] =
@@ -550,6 +558,7 @@ ppp_tty_close (struct tty_struct *tty)
                ppp->tty = ppp->backup_tty;
                if (ppp_tty_push(ppp))
                        ppp_output_wakeup(ppp);
+               wake_up_interruptible(&ppp->read_wait);
        } else {
                ppp->tty = 0;
                ppp->sc_xfer = 0;
@@ -829,6 +838,21 @@ ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
                error = n_tty_ioctl (tty, file, param2, param3);
                break;
 
+       case TCFLSH:
+               /*
+                * Flush our buffers, then call the generic code to
+                * flush the serial port's buffer.
+                */
+               if (param3 == TCIFLUSH || param3 == TCIOFLUSH) {
+                       struct sk_buff *skb;
+                       while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
+                               KFREE_SKB(skb);
+               }
+               if (param3 == TCIOFLUSH || param3 == TCOFLUSH)
+                       ppp_tty_flush_output(ppp);
+               error = n_tty_ioctl (tty, file, param2, param3);
+               break;
+
        case FIONREAD:
                /*
                 * Returns how many bytes are available for a read().
@@ -954,6 +978,135 @@ ppp_tty_wakeup (struct tty_struct *tty)
                ppp_output_wakeup(ppp);
 }
 
+/*
+ * 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_sync_send(struct ppp *ppp, struct sk_buff *skb)
+{
+       unsigned char *data;
+       int islcp;
+       
+       CHECK_PPP(0);
+
+       if (ppp->tpkt != NULL)
+               return -1;
+       ppp->tpkt = skb;
+
+       data = ppp->tpkt->data;
+       
+       /*
+        * LCP packets with code values between 1 (configure-reqest)
+        * and 7 (code-reject) must be sent as though no options
+        * had been negotiated.
+        */
+       islcp = PPP_PROTOCOL(data) == PPP_LCP
+               && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7;
+
+       /* only reset idle time for data packets */
+       if (PPP_PROTOCOL(data) < 0x8000)
+               ppp->last_xmit = jiffies;
+       ++ppp->stats.ppp_opackets;
+       ppp->stats.ppp_ooctects += ppp->tpkt->len;
+
+       if ( !(data[2]) && (ppp->flags & SC_COMP_PROT) ) {
+               /* compress protocol field */
+               data[2] = data[1];
+               data[1] = data[0];
+               skb_pull(ppp->tpkt,1);
+               data = ppp->tpkt->data;
+       }
+       
+       /*
+        * Do address/control compression
+        */
+       if ((ppp->flags & SC_COMP_AC) && !islcp
+           && PPP_ADDRESS(data) == PPP_ALLSTATIONS
+           && PPP_CONTROL(data) == PPP_UI) {
+               /* strip addr and control field */
+               skb_pull(ppp->tpkt,2);
+       }
+
+       return ppp_tty_sync_push(ppp);
+}
+
+/*
+ * Push a synchronous frame out to the tty.
+ * Returns 1 if frame accepted (or discarded), 0 otherwise.
+ */
+static int
+ppp_tty_sync_push(struct ppp *ppp)
+{
+       int sent;
+       struct tty_struct *tty = ppp2tty(ppp);
+       unsigned long flags;
+               
+       CHECK_PPP(0);
+
+       if (ppp->tpkt == NULL)
+               return 0;
+               
+       /* prevent reentrancy with tty_pushing flag */          
+       save_flags(flags);
+       cli();
+       if (ppp->tty_pushing) {
+               /* record wakeup attempt so we don't lose */
+               /* a wakeup call while doing push processing */
+               ppp->woke_up=1;
+               restore_flags(flags);
+               return 0;
+       }
+       ppp->tty_pushing = 1;
+       restore_flags(flags);
+       
+       if (tty == NULL || tty->disc_data != (void *) ppp)
+               goto flush;
+               
+       for(;;){
+               ppp->woke_up=0;
+               
+               /* Note: Sync driver accepts complete frame or nothing */
+               tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+               sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len);
+               if (sent < 0) {
+                       /* write error (possible loss of CD) */
+                       /* record error and discard current packet */
+                       ppp->stats.ppp_oerrors++;
+                       break;
+               }
+               ppp->stats.ppp_obytes += sent;
+               if (sent < ppp->tpkt->len) {
+                       /* driver unable to accept frame just yet */
+                       save_flags(flags);
+                       cli();
+                       if (ppp->woke_up) {
+                               /* wake up called while processing */
+                               /* try to send the frame again */
+                               restore_flags(flags);
+                               continue;
+                       }
+                       /* wait for wakeup callback to try send again */
+                       ppp->tty_pushing = 0;
+                       restore_flags(flags);
+                       return 0;
+               }
+               break;
+       }
+flush: 
+       /* done with current packet (sent or discarded) */
+       KFREE_SKB(ppp->tpkt);
+       ppp->tpkt = 0;
+       ppp->tty_pushing = 0;
+       return 1;
+}
+
 /*
  * Send a packet to the peer over an async tty line.
  * Returns -1 iff the packet could not be accepted at present,
@@ -986,14 +1139,21 @@ ppp_tty_push(struct ppp *ppp)
 {
        int avail, sent, done = 0;
        struct tty_struct *tty = ppp2tty(ppp);
+       
+       if (ppp->flags & SC_SYNC) 
+               return ppp_tty_sync_push(ppp);
 
        CHECK_PPP(0);
-       if (ppp->tty_pushing)
+       if (ppp->tty_pushing) {
+               ppp->woke_up = 1;
                return 0;
+       }
        if (tty == NULL || tty->disc_data != (void *) ppp)
                goto flush;
        while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
                ppp->tty_pushing = 1;
+               mb();
+               ppp->woke_up = 0;
                avail = ppp->olim - ppp->optr;
                if (avail > 0) {
                        tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
@@ -1003,18 +1163,24 @@ ppp_tty_push(struct ppp *ppp)
                        ppp->stats.ppp_obytes += sent;
                        ppp->optr += sent;
                        if (sent < avail) {
+                               wmb();
                                ppp->tty_pushing = 0;
+                               mb();
+                               if (ppp->woke_up)
+                                       continue;
                                return done;
                        }
                }
                if (ppp->tpkt != 0)
                        done = ppp_async_encode(ppp);
+               wmb();
                ppp->tty_pushing = 0;
        }
        return done;
 
 flush:
        ppp->tty_pushing = 1;
+       mb();
        ppp->stats.ppp_oerrors++;
        if (ppp->tpkt != 0) {
                KFREE_SKB(ppp->tpkt);
@@ -1022,6 +1188,7 @@ flush:
                done = 1;
        }
        ppp->optr = ppp->olim;
+       wmb();
        ppp->tty_pushing = 0;
        return done;
 }
@@ -1135,6 +1302,32 @@ ppp_async_encode(struct ppp *ppp)
        return 0;
 }
 
+/*
+ * Flush output from our internal buffers.
+ * Called for the TCFLSH ioctl.
+ */
+static void
+ppp_tty_flush_output(struct ppp *ppp)
+{
+       struct sk_buff *skb;
+       int done = 0;
+
+       while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
+               KFREE_SKB(skb);
+       ppp->tty_pushing = 1;
+       mb();
+       ppp->optr = ppp->olim;
+       if (ppp->tpkt != NULL) {
+               KFREE_SKB(ppp->tpkt);
+               ppp->tpkt = 0;
+               done = 1;
+       }
+       wmb();
+       ppp->tty_pushing = 0;
+       if (done)
+               ppp_output_wakeup(ppp);
+}
+
 /*
  * Callback function from tty driver. Return the amount of space left
  * in the receiver's buffer to decide if remote transmitter is to be
@@ -1184,6 +1377,74 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
 
        ppp->stats.ppp_ibytes += count;
        skb = ppp->rpkt;
+       
+       if ( ppp->flags & SC_SYNC ) {
+               /* synchronous mode */
+               
+               if (ppp->toss==0xE0) {
+                       /* this is the 1st frame, reset vj comp */
+                       ppp_receive_error(ppp);
+                       ppp->toss = 0;
+               }
+               
+               /*
+                * Allocate an skbuff for frame.
+                * The 128 is room for VJ header expansion.
+                */
+               
+               if (skb == NULL)
+                       skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+                       
+               if (skb == NULL) {
+                       if (ppp->flags & SC_DEBUG)
+                               printk(KERN_DEBUG "couldn't "
+                                      "alloc skb for recv\n");
+               } else {
+                       LIBERATE_SKB(skb);
+                       /*
+                        * Decompress A/C and protocol compression here.
+                        */
+                       p = skb_put(skb, 2);
+                       p[0] = PPP_ALLSTATIONS;
+                       p[1] = PPP_UI;
+                       if (*data == PPP_ALLSTATIONS) {
+                               data += 2;
+                               count -= 2;
+                       }
+                       if ((*data & 1) != 0) {
+                               p = skb_put(skb, 1);
+                               p[0] = 0;
+                       }
+
+                       /* copy frame to socket buffer */
+                       p = skb_put(skb, count);
+                       memcpy(p,data,count);
+                       
+                       /*
+                        * Check if we've overflowed the MRU
+                        */
+                       if (skb->len >= ppp->mru + PPP_HDRLEN + 2
+                           || skb_tailroom(skb) <= 0) {
+                               ++ppp->estats.rx_length_errors;
+                               if (ppp->flags & SC_DEBUG)
+                                       printk(KERN_DEBUG "rcv frame too long: "
+                                              "len=%d mru=%d hroom=%d troom=%d\n",
+                                              skb->len, ppp->mru, skb_headroom(skb),
+                                              skb_tailroom(skb));
+                       } else {
+                               if (!ppp_receive_frame(ppp, skb)) {
+                                       KFREE_SKB(skb);
+                                       ppp_receive_error(ppp);
+                               }
+                       }
+               
+                       /* Reset for the next frame */
+                       skb = NULL;
+               }
+               ppp->rpkt = skb;
+               return;
+       }
+       
        while (count-- > 0) {
                /*
                 * Collect the character and error condition for the character.
@@ -1324,7 +1585,7 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
                        ppp->toss = 0xC0;
                        if (ppp->flags & SC_DEBUG)
                                printk(KERN_DEBUG "rcv frame too long: "
-                                      "len=%ld mru=%d hroom=%d troom=%d\n",
+                                      "len=%d mru=%d hroom=%d troom=%d\n",
                                       skb->len, ppp->mru, skb_headroom(skb),
                                       skb_tailroom(skb));
                        continue;
@@ -2024,23 +2285,25 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
                return 0;
        }
 
-       /*
-        * Verify the FCS of the frame and discard the FCS characters
-        * from the end of the buffer.
-        */
-       if (ppp->rfcs != PPP_GOODFCS) {
-               if (ppp->flags & SC_DEBUG) {
-                       printk(KERN_DEBUG
-                              "ppp: frame with bad fcs, length = %d\n",
-                              count);
-                       ppp_print_buffer("bad frame", data, count);
+       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;
                }
-               ++ppp->estats.rx_crc_errors;
-               return 0;
+               count -= 2;             /* ignore the fcs characters */
+               skb_trim(skb, count);
        }
-       count -= 2;             /* ignore the fcs characters */
-       skb_trim(skb, count);
-
+       
        /*
         * Process the active decompressor.
         */
@@ -2410,7 +2673,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
        /*
         * Send the frame
         */
-       ret = ppp_async_send(ppp, skb);
+       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;
@@ -2983,7 +3249,6 @@ static struct compressor *find_compressor (int type)
        return (struct compressor *) 0;
 }
 
-#ifdef CONFIG_MODULES
 static int ppp_register_compressor (struct compressor *cp)
 {
        struct compressor_link *new;
@@ -3036,7 +3301,6 @@ static void ppp_unregister_compressor (struct compressor *cp)
        }
        restore_flags(flags);
 }
-#endif
 
 /*************************************************************
  * Module support routines