+ struct ppp *ppp = tty2ppp (tty);
+ int indx;
+/*
+ * There should not be an existing table for this slot.
+ */
+ if (ppp) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_tty_open: gack! tty already associated to %s!\n",
+ ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name
+ : "unknown");
+ return -EEXIST;
+ }
+/*
+ * Allocate the structure from the system
+ */
+ ppp = ppp_find(current->pid);
+ if (ppp != NULL) {
+ /*
+ * 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);
+ tty->disc_data = ppp;
+ ppp->tty = tty;
+
+ } else {
+ ppp = ppp_alloc();
+ if (ppp == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR "ppp_alloc failed\n");
+ return -ENFILE;
+ }
+/*
+ * Initialize the control block
+ */
+ ppp_init_ctrl_blk (ppp);
+ tty->disc_data = ppp;
+ ppp->tty = tty;
+/*
+ * Allocate space for the default VJ header compression slots
+ */
+ ppp->slcomp = slhc_init (16, 16);
+ if (ppp->slcomp == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR "ppp_tty_open: "
+ "no space for compression buffers!\n");
+ ppp_release (ppp);
+ return -ENOMEM;
+ }
+/*
+ * Allocate space for the MTU and MRU buffers
+ */
+ if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) {
+ ppp_release (ppp);
+ return -ENOMEM;
+ }
+/*
+ * Allocate space for a user level buffer
+ */
+ ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD);
+ if (ppp->ubuf == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR "ppp_tty_open: "
+ "no space for user receive buffer\n");
+ ppp_release (ppp);
+ return -ENOMEM;
+ }
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO "ppp: channel %s open\n",
+ ppp2dev(ppp)->name);
+
+ for (indx = 0; indx < NUM_NP; ++indx)
+ ppp->sc_npmode[indx] = NPMODE_PASS;
+
+ MOD_INC_USE_COUNT;
+ }
+/*
+ * Flush any pending characters in the driver and discipline.
+ */
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer (tty);
+
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer (tty);
+ return (ppp->line);
+}
+
+/*
+ * Local function to send the next portion of the buffer.
+ *
+ * Called by the tty driver's tty_wakeup function should it be entered
+ * because the partial buffer was transmitted.
+ *
+ * Called by kick_tty to send the initial portion of the buffer.
+ *
+ * Completion processing of the buffer transmission is handled here.
+ */
+
+static void
+ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty,
+ struct ppp_buffer *xbuf)
+{
+ register int count, actual;
+ unsigned long flags;
+
+ CHECK_PPP_VOID();
+ CHECK_BUF_MAGIC(xbuf);
+/*
+ * Prevent re-entrancy by ensuring that this routine is called only once.
+ */
+ save_flags(flags);
+ cli ();
+ if (ppp->flags & SC_XMIT_BUSY) {
+ restore_flags(flags);
+ return;
+ }
+ ppp->flags |= SC_XMIT_BUSY;
+ restore_flags(flags);
+/*
+ * Send the next block of data to the modem
+ */
+ count = xbuf->count - xbuf->tail;
+ actual = tty->driver.write (tty, 0,
+ buf_base (xbuf) + xbuf->tail, count);
+/*
+ * Terminate transmission of any block which may have an error.
+ * This could occur should the carrier drop.
+ */
+ if (actual < 0) {
+ ppp->stats.ppp_oerrors++;
+ actual = count;
+ } else
+ ppp->bytes_sent += actual;
+/*
+ * If the buffer has been transmitted then clear the indicators.
+ */
+ xbuf->tail += actual;
+ if (actual == count) {
+ xbuf = NULL;
+ ppp->flags &= ~SC_XMIT_BUSY;
+/*
+ * Complete the transmission on the current buffer.
+ */
+ xbuf = ppp->xbuf;
+ if (xbuf != NULL) {
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ xbuf->locked = 0;
+ ppp->xbuf = NULL;
+/*
+ * If the completed buffer came from the device write, then complete the
+ * transmission block.
+ */
+ ppp2dev (ppp)->tbusy = 0;
+ mark_bh (NET_BH);
+/*
+ * Wake up the transmission queue for all completion events.
+ */
+ wake_up_interruptible (&ppp->write_wait);
+/*
+ * Look at the priorities. Choose a daemon write over the device driver.
+ */
+ save_flags(flags);
+ cli();
+ xbuf = ppp->s1buf;
+ ppp->s1buf = NULL;
+ if (xbuf == NULL) {
+ xbuf = ppp->s2buf;
+ ppp->s2buf = NULL;
+ }
+/*
+ * If there is a pending buffer then transmit it now.
+ */
+ if (xbuf != NULL) {
+ ppp->flags &= ~SC_XMIT_BUSY;
+ ppp_kick_tty (ppp, xbuf);
+ restore_flags(flags);
+ return;
+ }
+ restore_flags(flags);
+ }
+ }
+/*
+ * Clear the re-entry flag
+ */
+ save_flags(flags); /* &=~ may not be atomic */
+ cli ();
+ ppp->flags &= ~SC_XMIT_BUSY;
+ restore_flags(flags);
+}
+
+/*
+ * This function is called by the tty driver when the transmit buffer has
+ * additional space. It is used by the ppp code to continue to transmit
+ * the current buffer should the buffer have been partially sent.
+ *
+ * In addition, it is used to send the first part of the buffer since the
+ * logic and the inter-locking would be identical.
+ */
+
+static void
+ppp_tty_wakeup (struct tty_struct *tty)
+{
+ struct ppp_buffer *xbuf;
+ struct ppp *ppp = tty2ppp (tty);
+
+ if (!ppp)
+ return;
+ CHECK_PPP_VOID();
+
+ if (tty != ppp->tty) {
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ return;
+ }
+/*
+ * Ensure that there is a transmission pending. Clear the re-entry flag if
+ * there is no pending buffer. Otherwise, send the buffer.
+ */
+ xbuf = ppp->xbuf;
+ if (xbuf == NULL)
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ else
+ ppp_tty_wakeup_code (ppp, tty, xbuf);
+}
+
+/*
+ * This function is called to transmit a buffer to the remote. The buffer
+ * is placed on the pending queue if there is presently a buffer being
+ * sent or it is transmitted with the aid of ppp_tty_wakeup.
+ */
+
+static void
+ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
+{
+ unsigned long flags;
+
+ CHECK_PPP_VOID();
+ CHECK_BUF_MAGIC(xbuf);
+/*
+ * Hold interrupts.
+ */
+ save_flags (flags);
+ cli ();
+/*
+ * Control the flags which are best performed with the interrupts masked.
+ */
+ xbuf->locked = 1;
+ xbuf->tail = 0;
+/*
+ * If the transmitter is busy then place the buffer on the appropriate
+ * priority queue.
+ */
+ if (ppp->xbuf != NULL) {
+ if (xbuf->type == BUFFER_TYPE_TTY_WR)
+ ppp->s1buf = xbuf;
+ else
+ ppp->s2buf = xbuf;
+ restore_flags (flags);
+ return;
+ }
+/*
+ * If the transmitter is not busy then this is the highest priority frame
+ */
+ ppp->flags &= ~SC_XMIT_BUSY;
+ ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ ppp->xbuf = xbuf;
+ restore_flags (flags);
+/*
+ * Do the "tty wakeup_code" to actually send this buffer.
+ */
+ ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf);
+}
+
+/*************************************************************
+ * TTY INPUT
+ * The following functions handle input that arrives from
+ * the TTY. It recognizes PPP frames and either hands them
+ * to the network layer or queues them for delivery to a
+ * user process reading this TTY.
+ *************************************************************/
+
+/*
+ * Callback function from tty driver. Return the amount of space left
+ * in the receiver's buffer to decide if remote transmitter is to be
+ * throttled.
+ */
+
+static int
+ppp_tty_room (struct tty_struct *tty)
+{
+ 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);
+ register struct ppp_buffer *buf = NULL;
+ __u8 chr;
+
+ if (ppp != 0)
+ CHECK_PPP_VOID();
+ /*
+ * This can happen if stuff comes in on the backup tty.
+ */
+ if (ppp == 0 || tty != ppp->tty)
+ return;
+/*
+ * Fetch the pointer to the buffer. Be careful about race conditions.
+ */
+ buf = ppp->rbuf;
+ if (buf == NULL)
+ return;
+/*
+ * Verify the table pointer and ensure that the line is
+ * still in PPP discipline.
+ */
+ if (ppp->magic != PPP_MAGIC) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "PPP: tty_receive called but couldn't find "
+ "PPP struct.\n");
+ return;
+ }
+ CHECK_PPP_VOID ();
+/*
+ * Print the buffer if desired
+ */
+ if (ppp->flags & SC_LOG_RAWIN)
+ ppp_print_buffer ("receive buffer", data, count);
+
+/*
+ * Collect the character and error condition for the character. Set the toss
+ * flag for the first character error.
+ */
+ while (count-- > 0) {
+ ppp->bytes_rcvd++;
+ chr = *data++;
+ if (flags) {
+ if (*flags && ppp->toss == 0) {
+ ppp->toss = *flags;
+ switch (ppp->toss) {
+ case TTY_OVERRUN:
+ ++ppp->estats.rx_fifo_errors;
+ break;
+ case TTY_FRAME:
+ case TTY_BREAK:
+ ++ppp->estats.rx_frame_errors;
+ break;
+ }
+ }
+ ++flags;
+ }
+
+/*
+ * Set the flags for d7 being 0/1 and parity being even/odd so that
+ * the normal processing would have all flags set at the end of the
+ * session. A missing flag bit indicates an error condition.
+ */
+
+#ifdef CHECK_CHARACTERS
+ if (chr & 0x80)
+ ppp->flags |= SC_RCV_B7_1;
+ else
+ ppp->flags |= SC_RCV_B7_0;
+
+ if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
+ ppp->flags |= SC_RCV_ODDP;
+ else
+ ppp->flags |= SC_RCV_EVNP;
+#endif
+/*
+ * Branch on the character.
+ */
+ switch (chr) {
+/*
+ * FLAG. This is the end of the block. If the block terminated by ESC FLAG,
+ * then the block is to be ignored. In addition, characters before the very
+ * first FLAG are also tossed by this procedure.
+ */
+ case PPP_FLAG: /* PPP_FLAG: end of frame */
+ ppp->stats.ppp_ibytes += ppp->rbuf->count;
+ if (ppp->escape)
+ ppp->toss |= 0x80;
+/*
+ * Process frames which are not to be ignored. If the processing failed,
+ * then clean up the VJ tables.
+ */
+ if (ppp_doframe (ppp) == 0) {
+ ++ppp->stats.ppp_ierrors;
+ slhc_toss (ppp->slcomp);
+ }
+/*
+ * Reset all indicators for the new frame to follow.
+ */
+ buf->count = 0;
+ buf->fcs = PPP_INITFCS;
+ ppp->escape = 0;
+ ppp->toss = 0;
+ break;
+/*
+ * All other characters in the data come here. If the character is in the
+ * receive mask then ignore the character.
+ */
+ default:
+ /* If we're tossing, look no further. */
+ if (ppp->toss != 0)
+ break;
+
+ /* If this is a control char to be ignored, do so */
+ if (in_rmap (ppp, chr))
+ break;
+
+ /*
+ * Modify the next character if preceded by escape.
+ * The escape character (0x7d) could be an escaped
+ * 0x5d, if it follows an escape :-)
+ */
+ if (ppp->escape) {
+ chr ^= PPP_TRANS;
+ ppp->escape = 0;
+ } else if (chr == PPP_ESCAPE) {
+ ppp->escape = PPP_TRANS;
+ break;
+ }
+
+ /*
+ * Decompress A/C and protocol compression here.
+ */
+ if (buf->count == 0 && chr != PPP_ALLSTATIONS) {
+ buf_base(buf)[0] = PPP_ALLSTATIONS;
+ buf_base(buf)[1] = PPP_UI;
+ buf->count = 2;
+ }
+ if (buf->count == 2 && (chr & 1) != 0) {
+ buf_base(buf)[2] = 0;
+ buf->count = 3;
+ }
+/*
+ * If the count sent is within reason then store the character, bump the
+ * count, and update the FCS for the character.
+ */
+ if (buf->count < buf->size) {
+ buf_base (buf)[buf->count++] = chr;
+ buf->fcs = PPP_FCS (buf->fcs, chr);
+ break;
+ }
+/*
+ * The peer sent too much data. Set the flags to discard the current frame
+ * and wait for the re-synchronization FLAG to be sent.
+ */
+ ++ppp->estats.rx_length_errors;
+ ppp->toss |= 0xC0;
+ break;
+ }
+ }
+}
+
+/* on entry, a received frame is in ppp->rbuf.bufr
+ check it and dispose as appropriate */
+
+static int
+ppp_doframe (struct ppp *ppp)
+{
+ __u8 *data = buf_base (ppp->rbuf);
+ int count = ppp->rbuf->count;
+ int proto;
+ int new_count;
+ __u8 *new_data;
+
+ CHECK_PPP(0);
+ CHECK_BUF_MAGIC(ppp->rbuf);
+
+/*
+ * If there is a pending error from the receiver then log it and discard
+ * the damaged frame.
+ */
+ if (ppp->toss) {
+ if ((ppp->flags & SC_DEBUG) && count > 0)
+ printk (KERN_DEBUG
+ "ppp_toss: tossing frame, reason = %x\n",
+ ppp->toss);
+ return 0;
+ }
+/*
+ * An empty frame is ignored. This occurs if the FLAG sequence precedes and
+ * follows each frame.
+ */
+ if (count == 0)
+ return 1;
+/*
+ * Generate an error if the frame is too small.
+ */
+ if (count < PPP_HDRLEN + 2) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp: got runt ppp frame, %d chars\n", count);
+ ++ppp->estats.rx_length_errors;
+ return 0;
+ }
+/*
+ * Verify the CRC of the frame and discard the CRC characters from the
+ * end of the buffer.
+ */
+ if (ppp->rbuf->fcs != PPP_GOODFCS) {
+ if (ppp->flags & SC_DEBUG) {
+ printk (KERN_DEBUG
+ "ppp: frame with bad fcs, length = %d\n",
+ count);
+ ppp_print_buffer("bad frame", data, count);
+ }
+ ++ppp->estats.rx_crc_errors;
+ return 0;
+ }
+ count -= 2; /* ignore the fcs characters */
+/*
+ * Obtain the protocol from the frame
+ */
+ proto = PPP_PROTOCOL(data);
+/*
+ * Process the active decompressor.
+ */
+ if ((ppp->sc_rc_state != (void *) 0) &&
+ (ppp->flags & SC_DECOMP_RUN) &&
+ ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
+ if (proto == PPP_COMP) {
+/*
+ * If the frame is compressed then decompress it.
+ */
+ new_data = kmalloc (ppp->mru + PPP_HDRLEN, GFP_ATOMIC);
+ if (new_data == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_doframe: no memory\n");
+ new_count = DECOMP_ERROR;
+ } else {
+ new_count = (*ppp->sc_rcomp->decompress)
+ (ppp->sc_rc_state, data, count,
+ new_data, ppp->mru + PPP_HDRLEN);
+ }
+ switch (new_count) {
+ default:
+ ppp_doframe_lower (ppp, new_data, new_count);
+ kfree (new_data);
+ return 1;
+
+ case DECOMP_ERROR:
+ ppp->flags |= SC_DC_ERROR;
+ break;
+
+ case DECOMP_FATALERROR:
+ ppp->flags |= SC_DC_FERROR;
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_ERR "ppp: fatal decomp error\n");
+ break;
+ }
+/*
+ * Log the error condition and discard the frame.
+ */
+ if (new_data != 0)
+ kfree (new_data);
+ slhc_toss (ppp->slcomp);
+ ++ppp->stats.ppp_ierrors;
+ } else {
+/*
+ * The frame is not special. Pass it through the compressor without
+ * actually compressing the data
+ */
+ (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
+ data, count);
+ }
+ }
+/*
+ * Process the uncompressed frame.
+ */
+ ppp_doframe_lower (ppp, data, count);
+ return 1;
+}
+
+static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
+{
+ __u16 proto = PPP_PROTOCOL (data);
+ ppp_proto_type *proto_ptr;
+
+ CHECK_PPP_VOID();
+/*
+ * Ignore empty frames
+ */
+ if (count <= PPP_HDRLEN)
+ return;
+/*
+ * Count the frame and print it
+ */
+ ++ppp->stats.ppp_ipackets;
+ if (ppp->flags & SC_LOG_INPKT)
+ ppp_print_buffer ("receive frame", data, count);
+/*
+ * Find the procedure to handle this protocol. The last one is marked
+ * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon.
+ */
+ proto_ptr = proto_list;
+ while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
+ ++proto_ptr;
+/*
+ * Update the appropriate statistic counter.
+ */
+ if ((*proto_ptr->func) (ppp, proto,
+ &data[PPP_HDRLEN],
+ count - PPP_HDRLEN))
+ ppp->stats.ppp_ioctects += count;
+ else
+ ++ppp->stats.ppp_discards;
+}
+
+/*
+ * Put the input frame into the networking system for the indicated protocol
+ */
+
+static int
+ppp_rcv_rx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+{
+ sk_buff *skb = dev_alloc_skb (count);
+/*
+ * Generate a skb buffer for the new frame.
+ */
+ if (skb == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_do_ip: packet dropped on %s (no memory)!\n",
+ ppp2dev (ppp)->name);
+ return 0;
+ }
+/*
+ * Move the received data from the input buffer to the skb buffer.
+ */
+ skb->dev = ppp2dev (ppp); /* We are the device */
+ skb->protocol = proto;
+ skb->mac.raw = skb_data(skb);
+ memcpy (skb_put(skb,count), data, count); /* move data */
+/*
+ * Tag the frame and kick it to the proper receive routine
+ */
+#if LINUX_VERSION_CODE < VERSION(2,1,15)
+ skb->free = 1;
+#endif
+
+ ppp->last_recv = jiffies;
+ netif_rx (skb);
+ return 1;
+}
+
+/*
+ * Process the receipt of an IP frame
+ */
+
+static int
+rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+{
+ CHECK_PPP(0);
+ if ((ppp2dev (ppp)->flags & IFF_UP) && (count > 0))
+ if (ppp->sc_npmode[NP_IP] == NPMODE_PASS)
+ return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count);
+ return 0;
+}
+
+/*
+ * Process the receipt of an IPX frame
+ */
+
+static int
+rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count)