+ 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;
+ if (ppp2dev (ppp) -> flags & IFF_UP) {
+ mark_bh (NET_BH);
+ }
+/*
+ * Wake up the transmission queue for all completion events.
+ */
+ wake_up_interruptible (&ppp->write_wait);
+/*
+ * Look at the priorities. Choose a daemon write over the device driver.
+ */
+ save_flags(flags);
+ cli();
+ xbuf = ppp->s1buf;
+ ppp->s1buf = NULL;
+ if (xbuf == NULL) {
+ xbuf = ppp->s2buf;
+ ppp->s2buf = NULL;
+ }
+/*
+ * If there is a pending buffer then transmit it now.
+ */
+ if (xbuf != NULL) {
+ ppp->flags &= ~SC_XMIT_BUSY;
+ ppp_kick_tty (ppp, xbuf);
+ restore_flags(flags);
+ return;
+ }
+ 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->ddinfo.recv_idle = 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)
+{
+ CHECK_PPP(0);
+ if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0))
+ return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
+ return 0;
+}
+
+/*
+ * Process the receipt of an VJ Compressed frame
+ */
+
+static int
+rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto,
+ __u8 *data, int count)
+{
+ CHECK_PPP(0);
+ if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
+ int new_count = slhc_uncompress (ppp->slcomp, data, count);
+ if (new_count >= 0) {
+ return rcv_proto_ip (ppp, PPP_IP, data, new_count);
+ }
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_NOTICE
+ "ppp: error in VJ decompression\n");
+ }
+ return 0;
+}
+
+/*
+ * Process the receipt of an VJ Un-compressed frame
+ */
+
+static int
+rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto,
+ __u8 *data, int count)
+{
+ CHECK_PPP(0);
+ if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
+ if (slhc_remember (ppp->slcomp, data, count) > 0) {
+ return rcv_proto_ip (ppp, PPP_IP, data, count);
+ }
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_NOTICE
+ "ppp: error in VJ memorizing\n");
+ }
+ return 0;
+}
+
+/*
+ * Receive all unclassified protocols.
+ */
+
+static int
+rcv_proto_unknown (struct ppp *ppp, __u16 proto,
+ __u8 *data, int len)
+{
+ int totlen;
+ register int current_idx;
+
+#define PUTC(c) \
+{ \
+ buf_base (ppp->ubuf) [current_idx++] = (__u8) (c); \
+ current_idx &= ppp->ubuf->size; \
+ if (current_idx == ppp->ubuf->tail) \
+ goto failure; \
+}
+
+ CHECK_PPP(0);
+/*
+ * The total length includes the protocol data.
+ * Lock the user information buffer.
+ */
+ if (test_and_set_bit (0, &ppp->ubuf->locked)) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp: rcv_proto_unknown: can't get lock\n");
+ } else {
+ CHECK_BUF_MAGIC(ppp->ubuf);
+ current_idx = ppp->ubuf->head;
+/*
+ * Insert the buffer length (not counted), the protocol, and the data
+ */
+ totlen = len + 2;
+ PUTC (totlen >> 8);
+ PUTC (totlen);
+
+ PUTC (proto >> 8);
+ PUTC (proto);
+
+ totlen -= 2;
+ while (totlen-- > 0) {
+ PUTC (*data++);
+ }
+#undef PUTC
+/*
+ * The frame is complete. Update the head pointer and wakeup the pppd
+ * process.
+ */
+ ppp->ubuf->head = current_idx;
+
+ clear_bit (0, &ppp->ubuf->locked);
+ wake_up_interruptible (&ppp->read_wait);
+ if (ppp->tty->fasync != NULL)
+ kill_fasync (ppp->tty->fasync, SIGIO);
+
+ return 1;
+/*
+ * The buffer is full. Unlock the header
+ */
+failure:
+ clear_bit (0, &ppp->ubuf->locked);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp: rcv_proto_unknown: buffer overflow\n");
+ }
+/*
+ * Discard the frame. There are no takers for this protocol.
+ */
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp: rcv_proto_unknown: dropping packet\n");
+ return 0;
+}
+
+/*
+ * Handle a CCP packet.
+ *
+ * The CCP packet is passed along to the pppd process just like any
+ * other PPP frame. The difference is that some processing needs to be
+ * immediate or the compressors will become confused on the peer.
+ */
+
+static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
+{
+ int slen = CCP_LENGTH(dp);
+ __u8 *opt = dp + CCP_HDRLEN;
+ int opt_len = slen - CCP_HDRLEN;
+ unsigned long flags;
+
+ if (slen > len)
+ return;
+
+ save_flags(flags);
+ switch (CCP_CODE(dp)) {
+ case CCP_CONFREQ:
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+/*
+ * CCP must be going down - disable compression
+ */
+ if (ppp->flags & SC_CCP_UP) {
+ cli();
+ ppp->flags &= ~(SC_CCP_UP |
+ SC_COMP_RUN |
+ SC_DECOMP_RUN);
+ }
+ break;
+
+ case CCP_CONFACK:
+ if ((ppp->flags & SC_CCP_OPEN) == 0)
+ break;
+ if (ppp->flags & SC_CCP_UP)
+ break;
+ if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
+ break;
+ if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
+ break;
+/*
+ * we're agreeing to send compressed packets.
+ */
+ if (!rcvd) {
+ if (ppp->sc_xc_state == NULL)
+ break;
+
+ if ((*ppp->sc_xcomp->comp_init)
+ (ppp->sc_xc_state,
+ opt,
+ opt_len,
+ ppp2dev (ppp)->base_addr,
+ 0,
+ ppp->flags & SC_DEBUG)) {
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "%s: comp running\n",
+ ppp->name);
+ cli();
+ ppp->flags |= SC_COMP_RUN;
+ }
+ break;
+ }
+/*
+ * peer is agreeing to send compressed packets.
+ */
+ if (ppp->sc_rc_state == NULL)
+ break;
+
+ if ((*ppp->sc_rcomp->decomp_init)
+ (ppp->sc_rc_state,
+ opt,
+ opt_len,
+ ppp2dev (ppp)->base_addr,
+ 0,
+ ppp->mru,
+ ppp->flags & SC_DEBUG)) {
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "%s: decomp running\n",
+ ppp->name);
+ cli();
+ ppp->flags |= SC_DECOMP_RUN;
+ ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
+ }
+ break;
+/*
+ * CCP Reset-ack resets compressors and decompressors as it passes through.
+ */
+ case CCP_RESETACK:
+ if ((ppp->flags & SC_CCP_UP) == 0)
+ break;
+
+ if (!rcvd) {
+ if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) {
+ (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "%s: comp reset\n",
+ ppp->name);
+ }
+ } else {
+ if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
+ (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "%s: decomp reset\n",
+ ppp->name);
+ cli();
+ ppp->flags &= ~SC_DC_ERROR;
+ }
+ }
+ break;
+ }
+ restore_flags(flags);
+}
+
+static int
+rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len)
+{
+ CHECK_PPP(0);
+ ppp_proto_ccp (ppp, dp, len, 1);
+ return rcv_proto_unknown (ppp, proto, dp, len);
+}
+
+/*
+ * Handle a LQR packet.
+ */
+
+static int
+rcv_proto_lqr (struct ppp *ppp, __u16 proto, __u8 * data, int len)
+{
+ return rcv_proto_unknown (ppp, proto, data, len);
+}
+
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ * The following functions form support user programs
+ * which read and write data on a TTY with the PPP line
+ * discipline. Reading is done from a circular queue,
+ * filled by the lower TTY levels.
+ *************************************************************/
+
+/* read a PPP frame from the us_rbuff circular buffer,
+ waiting if necessary
+*/
+
+static int
+ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
+ unsigned int nr)
+{
+ struct ppp *ppp = tty2ppp (tty);
+ __u8 c;
+ int len, ret;
+ int error;
+
+#define GETC(c) \
+{ \
+ c = buf_base (ppp->ubuf) [ppp->ubuf->tail++]; \
+ ppp->ubuf->tail &= ppp->ubuf->size; \
+}
+
+/*
+ * Validate the pointers
+ */
+ if (!ppp)
+ return -EIO;
+
+ if (ppp->magic != PPP_MAGIC)
+ return -EIO;
+
+ CHECK_PPP (-ENXIO);
+
+/*
+ * Before we attempt to write the frame to the user, ensure that the
+ * user has access to the pages for the total buffer length.
+ */
+ error = verify_area (VERIFY_WRITE, buf, nr);
+ if (error != 0)
+ return (error);
+
+/*
+ * Acquire the read lock.
+ */
+ for (;;) {
+ ppp = tty2ppp (tty);
+ if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
+ || tty != ppp->tty)
+ return 0;
+
+ if (test_and_set_bit (0, &ppp->ubuf->locked) != 0) {
+#if 0
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: sleeping(ubuf)\n");
+#endif
+ current->timeout = 0;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule ();
+
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ continue;
+ }
+
+/*
+ * Fetch the length of the buffer from the first two bytes.
+ */
+ if (ppp->ubuf->head == ppp->ubuf->tail)
+ len = 0;
+ else {
+ GETC (c);
+ len = c << 8;
+ GETC (c);
+ len += c;
+ if (len)
+ break;
+ }
+
+/*
+ * If there is no length then wait for the data to arrive.
+ */
+ /* no data */
+ clear_bit (0, &ppp->ubuf->locked);
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ current->timeout = 0;
+#if 0
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: sleeping(read_wait)\n");
+#endif
+ interruptible_sleep_on (&ppp->read_wait);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ }
+
+/*
+ * Ensure that the frame will fit within the caller's buffer. If not, then
+ * discard the frame from the input buffer.
+ */
+ if (len + 2 > nr) {
+ /* Can't copy it, update us_rbuff_head */
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp: read of %u bytes too small for %d "
+ "frame\n", nr, len + 2);
+ ppp->stats.ppp_ierrors++;
+ error = -EOVERFLOW;
+ goto out;
+ }
+
+/*
+ * Fake the insertion of the ADDRESS and CONTROL information because these
+ * were not saved in the buffer.
+ */
+ PUT_USER (error, (u_char) PPP_ALLSTATIONS, buf);
+ if (error)
+ goto out;
+ ++buf;
+ PUT_USER (error, (u_char) PPP_UI, buf);
+ if (error)
+ goto out;
+ ++buf;
+
+/*
+ * Copy the received data from the buffer to the caller's area.
+ */
+ ret = len + 2; /* Account for ADDRESS and CONTROL bytes */
+ while (len-- > 0) {
+ GETC (c);
+ PUT_USER (error, c, buf);
+ if (error)
+ goto out;
+ ++buf;
+ }
+
+ clear_bit (0, &ppp->ubuf->locked);
+#if 0
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG "ppp_tty_read: passing %d bytes up\n", ret);
+#endif
+ return ret;
+
+out:
+ ppp->ubuf->tail += len;
+ ppp->ubuf->tail &= ppp->ubuf->size;
+ clear_bit (0, &ppp->ubuf->locked);
+ return error;
+#undef GETC