+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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; \
+}
+
+/*
+ * The total length includes the protocol data.
+ * Lock the user information buffer.
+ */
+ if (set_bit (0, &ppp->ubuf->locked)) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_us_queue: can't get lock\n");
+ } else {
+ 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_us_queue: ran out of buffer space.\n");
+ }
+/*
+ * Discard the frame. There are no takers for this protocol.
+ */
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp: dropping packet on the floor.\n");
+ slhc_toss (ppp->slcomp);
+ 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;
+
+ if (slen > len)
+ return;
+
+ 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) {
+ 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))
+ 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)) {
+ ppp->flags |= SC_DECOMP_RUN;
+ ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
+ }
+ break;
+/*
+ * The protocol sequence is complete at this end
+ */
+ 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);
+ } else {
+ if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
+ (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
+ ppp->flags &= ~SC_DC_ERROR;
+ }
+ }
+ break;
+ }
+}
+
+static int
+rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len)
+{
+ 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);
+}
+
+static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
+{
+ __u16 proto = PPP_PROTOCOL (data);
+ ppp_proto_type *proto_ptr;
+/*
+ * 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_HARD_HDR_LEN],
+ count - PPP_HARD_HDR_LEN))
+ ppp->stats.ppp_ioctects += count;
+ else
+ ++ppp->stats.ppp_discards;
+}
+
+/* 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 addr, ctrl, proto;
+ int new_count;
+ __u8 *new_data;
+/*
+ * 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_HARD_HDR_LEN) {
+ 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 */
+/*
+ * Ignore the leading ADDRESS and CONTROL fields in the frame.
+ */
+ addr = PPP_ALLSTATIONS;
+ ctrl = PPP_UI;
+
+ if ((data[0] == PPP_ALLSTATIONS) && (data[1] == PPP_UI)) {
+ data += 2;
+ count -= 2;
+ }
+/*
+ * Obtain the protocol from the frame
+ */
+ proto = (__u16) *data++;
+ if ((proto & 1) == 0) {
+ proto = (proto << 8) | (__u16) *data++;
+ --count;
+ }
+/*
+ * Rewrite the header with the full information. This may encroach upon
+ * the 'filler' area in the buffer header. This is the purpose for the
+ * filler.
+ */
+ *(--data) = proto;
+ *(--data) = proto >> 8;
+ *(--data) = ctrl;
+ *(--data) = addr;
+ count += 3;
+/*
+ * 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;
+}
+
+/*************************************************************
+ * 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, indx;
+ 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);
+/*
+ * Acquire the read lock.
+ */
+ for (;;) {
+ ppp = tty2ppp (tty);
+ if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
+ || tty != ppp->tty)
+ return 0;
+
+ if (set_bit (0, &ppp->ubuf->locked) != 0) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: sleeping(ubuf)\n");
+
+ current->timeout = 0;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule ();
+
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ continue;
+ }
+/*
+ * Before we attempt to write the frame to the user, ensure that the
+ * user has access to the pages for the total buffer length.
+ */
+ indx = verify_area (VERIFY_WRITE, buf, nr);
+ if (indx != 0)
+ return (indx);
+/*
+ * 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 there is no length then wait for the data to arrive.
+ */
+ if (len == 0) {
+ /* no data */
+ clear_bit (0, &ppp->ubuf->locked);
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ current->timeout = 0;
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: sleeping(read_wait)\n");
+
+ interruptible_sleep_on (&ppp->read_wait);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ continue;
+ }
+/*
+ * 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->ubuf->tail += len;
+ ppp->ubuf->tail &= ppp->ubuf->size;
+ clear_bit (0, &ppp->ubuf->locked);
+ ppp->stats.ppp_ierrors++;
+ return -EOVERFLOW;
+ }
+/*
+ * Before we attempt to write the frame to the user, ensure that the
+ * page tables are proper.
+ */
+ indx = verify_area (VERIFY_WRITE, buf, len + 2);
+ if (indx != 0) {
+ ppp->ubuf->tail += len;
+ ppp->ubuf->tail &= ppp->ubuf->size;
+ clear_bit (0, &ppp->ubuf->locked);
+ return (indx);
+ }
+/*
+ * 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);
+ ++buf;
+ PUT_USER (error, (u_char) PPP_UI, buf);
+ ++buf;
+
+ indx = len;
+/*
+ * Copy the received data from the buffer to the caller's area.
+ */
+ while (indx-- > 0) {
+ GETC (c);
+ PUT_USER (error, c, buf);
+ ++buf;
+ }
+
+ clear_bit (0, &ppp->ubuf->locked);
+ len += 2; /* Account for ADDRESS and CONTROL bytes */
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: passing %d bytes up\n", len);
+ return len;
+ }
+#undef GETC
+}
+
+/* stuff a character into the transmit buffer, using PPP's way of escaping
+ special characters.
+ also, update fcs to take account of new character */
+
+extern inline void
+ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
+ register __u8 chr)
+{
+/*
+ * The buffer should not be full.
+ */
+ if (ppp->flags & SC_DEBUG) {
+ if ((buf->count < 0) || (buf->count > 3000))
+ printk (KERN_DEBUG "ppp_stuff_char: %d %x\n",
+ (unsigned int) buf->count,
+ (unsigned int) chr);
+ }
+/*
+ * Update the FCS and if the character needs to be escaped, do it.
+ */
+ buf->fcs = PPP_FCS (buf->fcs, chr);
+ if (in_xmap (ppp, chr)) {
+ chr ^= PPP_TRANS;
+ ins_char (buf, PPP_ESCAPE);
+ }
+/*
+ * Add the character to the buffer.
+ */
+ ins_char (buf, chr);
+}
+
+/*
+ * Procedure to encode the data with the proper escaping and send the
+ * data to the remote system.
+ */
+
+static void
+ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
+ __u8 *data, int count, int non_ip)
+{
+ __u16 write_fcs;
+ int address, control;
+ int proto;
+
+ ++ppp->stats.ppp_opackets;
+ ppp->stats.ppp_ooctects += count;
+
+/*
+ * Insert the leading FLAG character
+ */
+ buf->count = 0;
+
+ if (non_ip || flag_time == 0)
+ ins_char (buf, PPP_FLAG);
+ else {
+ if (jiffies - ppp->last_xmit > flag_time)
+ ins_char (buf, PPP_FLAG);
+ }
+ ppp->last_xmit = jiffies;
+ buf->fcs = PPP_INITFCS;
+/*
+ * Emit the address/control information if needed
+ */
+ address = PPP_ADDRESS (data);
+ control = PPP_CONTROL (data);
+ proto = PPP_PROTOCOL (data);
+
+ if (address != PPP_ALLSTATIONS ||
+ control != PPP_UI ||
+ (ppp->flags & SC_COMP_AC) == 0) {
+ ppp_stuff_char (ppp, buf, address);
+ ppp_stuff_char (ppp, buf, control);
+ }
+/*
+ * Emit the protocol (compressed if possible)
+ */
+ if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00))
+ ppp_stuff_char (ppp, buf, proto >> 8);
+
+ ppp_stuff_char (ppp, buf, proto);
+/*
+ * Insert the data
+ */
+ data += 4;
+ count -= 4;
+
+ while (count-- > 0)
+ ppp_stuff_char (ppp, buf, *data++);
+/*
+ * Add the trailing CRC and the final flag character
+ */
+ write_fcs = buf->fcs ^ 0xFFFF;
+ ppp_stuff_char (ppp, buf, write_fcs);
+ ppp_stuff_char (ppp, buf, write_fcs >> 8);
+/*
+ * Add the trailing flag character
+ */
+ ins_char (buf, PPP_FLAG);
+/*
+ * Print the buffer
+ */
+ if (ppp->flags & SC_LOG_FLUSH)
+ ppp_print_buffer ("ppp flush", buf_base (buf),
+ buf->count);
+/*
+ * Send the block to the tty driver.
+ */
+ ppp->stats.ppp_obytes += buf->count;
+ ppp_kick_tty (ppp, buf);
+}
+
+/*
+ * Send an frame to the remote with the proper bsd compression.
+ *
+ * Return 0 if frame was queued for transmission.
+ * 1 if frame must be re-queued for later driver support.
+ */
+
+static int
+ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
+ __u8 *data, int count)
+{
+ int proto;
+ int address, control;
+ __u8 *new_data;
+ int new_count;
+/*
+ * Print the buffer
+ */
+ if (ppp->flags & SC_LOG_OUTPKT)
+ ppp_print_buffer ("write frame", data, count);
+/*
+ * Determine if the frame may be compressed. Attempt to compress the
+ * frame if possible.
+ */
+ proto = PPP_PROTOCOL (data);
+ address = PPP_ADDRESS (data);
+ control = PPP_CONTROL (data);
+
+ if (((ppp->flags & SC_COMP_RUN) != 0) &&
+ (ppp->sc_xc_state != (void *) 0) &&
+ (address == PPP_ALLSTATIONS) &&
+ (control == PPP_UI) &&
+ (proto != PPP_LCP) &&
+ (proto != PPP_CCP)) {
+ new_data = kmalloc (count, GFP_ATOMIC);
+ if (new_data == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_dev_xmit_frame: no memory\n");
+ return 1;
+ }
+
+ new_count = (*ppp->sc_xcomp->compress)
+ (ppp->sc_xc_state, data, new_data, count, count);
+
+ if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
+ ppp_dev_xmit_lower (ppp, buf, new_data, new_count, 0);
+ kfree (new_data);
+ return 0;
+ }
+/*
+ * The frame could not be compressed, or it could not be sent in
+ * compressed form because CCP is not yet up.
+ */
+ kfree (new_data);
+ }
+/*
+ * Go to the escape encoding
+ */
+ ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00));
+ return 0;
+}
+
+/*
+ * Revise the tty frame for specific protocols.
+ */
+
+static int
+send_revise_frame (register struct ppp *ppp, __u8 *data, int len)
+{
+ __u8 *p;
+
+ switch (PPP_PROTOCOL (data)) {
+/*
+ * Update the LQR frame with the current MIB information. This saves having
+ * the daemon read old MIB data from the driver.
+ */
+ case PPP_LQR:
+ len = 48; /* total size of this frame */
+ p = (__u8 *) &data [40]; /* Point to last two items. */
+ p = store_long (p, ppp->stats.ppp_opackets + 1);
+ p = store_long (p, ppp->stats.ppp_ooctects + len);
+ break;
+/*
+ * Outbound compression frames
+ */
+ case PPP_CCP:
+ ppp_proto_ccp (ppp,
+ data + PPP_HARD_HDR_LEN,
+ len - PPP_HARD_HDR_LEN,
+ 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return len;
+}
+
+/*
+ * write a frame with NR chars from BUF to TTY
+ * we have to put the FCS field on ourselves
+ */
+
+static int
+ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
+ unsigned int count)
+{
+ struct ppp *ppp = tty2ppp (tty);
+ __u8 *new_data;
+ int status;
+/*
+ * Verify the pointers.
+ */
+ if (!ppp)
+ return -EIO;
+
+ if (ppp->magic != PPP_MAGIC)
+ return -EIO;
+
+ CHECK_PPP (-ENXIO);
+/*
+ * Ensure that the caller does not wish to send too much.
+ */
+ if (count > PPP_MTU + PPP_HDRLEN) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_WARNING
+ "ppp_tty_write: truncating user packet "
+ "from %u to mtu %d\n", count,
+ PPP_MTU + PPP_HDRLEN);
+ count = PPP_MTU + PPP_HDRLEN;
+ }
+/*
+ * Allocate a buffer for the data and fetch it from the user space.
+ */
+ new_data = kmalloc (count, GFP_KERNEL);
+ if (new_data == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_tty_write: no memory\n");
+ return 0;
+ }
+/*
+ * lock this PPP unit so we will be the only writer;
+ * sleep if necessary
+ */
+ while (lock_buffer (ppp->tbuf) != 0) {
+ current->timeout = 0;
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG "ppp_tty_write: sleeping\n");
+ interruptible_sleep_on (&ppp->write_wait);
+
+ ppp = tty2ppp (tty);
+ if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
+ || tty != ppp->tty) {
+ kfree (new_data);
+ return 0;
+ }
+
+ if (current->signal & ~current->blocked) {
+ kfree (new_data);
+ return -EINTR;
+ }
+ }
+/*
+ * Retrieve the user's buffer
+ */
+ COPY_FROM_USER (status,
+ new_data,
+ data,
+ count);
+
+ if (status != 0) {
+ kfree (new_data);
+ ppp->tbuf->locked = 0;
+ return status;
+ }
+/*
+ * Change the LQR frame
+ */
+ count = send_revise_frame (ppp, new_data, count);
+/*
+ * Send the data
+ */
+ ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
+ kfree (new_data);
+ return (int) count;
+}
+
+/*
+ * Process the BSD compression IOCTL event for the tty device.
+ */
+
+static int
+ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
+{
+ struct compressor *cp;
+ struct ppp_option_data data;
+ int error;
+ int nb;
+ __u8 *ptr;
+ __u8 ccp_option[CCP_MAX_OPTION_LENGTH];
+/*
+ * Fetch the compression parameters
+ */
+ COPY_FROM_USER (error,
+ &data,
+ odp,
+ sizeof (data));
+
+ if (error != 0)
+ return error;
+
+ nb = data.length;
+ ptr = data.ptr;
+ if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH)
+ nb = CCP_MAX_OPTION_LENGTH;
+
+ COPY_FROM_USER (error,
+ ccp_option,
+ ptr,
+ nb);
+
+ if (error != 0)
+ return error;
+
+ if (ccp_option[1] < 2) /* preliminary check on the length byte */
+ return (-EINVAL);
+
+ cp = find_compressor ((int) (unsigned int) (__u8) ccp_option[0]);
+ if (cp != (struct compressor *) 0) {
+ /*
+ * Found a handler for the protocol - try to allocate
+ * a compressor or decompressor.
+ */
+ error = 0;
+ if (data.transmit) {
+ if (ppp->sc_xc_state != NULL)
+ (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
+
+ ppp->sc_xcomp = cp;
+ ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
+
+ if (ppp->sc_xc_state == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk("ppp%ld: comp_alloc failed\n",
+ ppp2dev (ppp)->base_addr);
+ error = -ENOBUFS;
+ }
+ ppp->flags &= ~SC_COMP_RUN;
+ } else {
+ if (ppp->sc_rc_state != NULL)
+ (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
+ ppp->sc_rcomp = cp;
+ ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
+ if (ppp->sc_rc_state == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk("ppp%ld: decomp_alloc failed\n",
+ ppp2dev (ppp)->base_addr);
+ error = ENOBUFS;
+ }
+ ppp->flags &= ~SC_DECOMP_RUN;
+ }
+ return (error);
+ }
+
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "ppp%ld: no compressor for [%x %x %x], %x\n",
+ ppp2dev (ppp)->base_addr, ccp_option[0], ccp_option[1],
+ ccp_option[2], nb);
+ return (-EINVAL); /* no handler found */
+}
+
+/*
+ * Process the IOCTL event for the tty device.
+ */
+
+static int
+ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
+ unsigned int param2, unsigned long param3)
+{
+ struct ppp *ppp = tty2ppp (tty);
+ register int temp_i = 0;
+ int error = 0;
+/*
+ * Verify the status of the PPP device.
+ */
+ if (!ppp)
+ return -EBADF;
+
+ if (ppp->magic != PPP_MAGIC)
+ return -EBADF;
+
+ CHECK_PPP (-ENXIO);
+/*
+ * The user must have an euid of root to do these requests.
+ */
+ if (!suser ())
+ return -EPERM;
+/*
+ * Set the MRU value
+ */
+ switch (param2) {
+ case PPPIOCSMRU:
+ GET_USER (error, temp_i, (int *) param3);
+ if (error == 0) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set mru to %x\n", temp_i);
+
+ if (ppp->mru != temp_i)
+ ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i);
+ }
+ break;
+/*
+ * Fetch the flags
+ */
+ case PPPIOCGFLAGS:
+ 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_USER (error, temp_i, (int *) param3);
+ break;
+/*
+ * Set the flags for the various options
+ */
+ case PPPIOCSFLAGS:
+ GET_USER (error, temp_i, (int *) param3);
+ if (error == 0) {
+ temp_i &= SC_MASK;
+ temp_i |= (ppp->flags & ~SC_MASK);
+
+ if ((ppp->flags & SC_CCP_OPEN) &&
+ (temp_i & SC_CCP_OPEN) == 0)
+ ppp_ccp_closed (ppp);
+
+ if ((ppp->flags | temp_i) & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set flags to %x\n", temp_i);
+ ppp->flags = temp_i;
+ }
+ break;
+/*
+ * Set the compression mode
+ */
+ case PPPIOCSCOMPRESS:
+ error = ppp_set_compression (ppp,
+ (struct ppp_option_data *) param3);
+ break;
+/*
+ * Retrieve the transmit async map
+ */
+ case PPPIOCGASYNCMAP:
+ PUT_USER (error, ppp->xmit_async_map[0], (int *) param3);
+ break;
+/*
+ * Set the transmit async map
+ */
+ case PPPIOCSASYNCMAP:
+ GET_USER (error, temp_i, (int *) param3);
+ if (error == 0) {
+ ppp->xmit_async_map[0] = temp_i;
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set xmit asyncmap %x\n",
+ ppp->xmit_async_map[0]);
+ }
+ break;
+/*
+ * Set the receive async map
+ */
+ case PPPIOCSRASYNCMAP:
+ GET_USER (error, temp_i, (int *) param3);
+ if (error == 0) {
+ ppp->recv_async_map = temp_i;
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set rcv asyncmap %x\n",
+ ppp->recv_async_map);
+ }
+ break;
+/*
+ * Obtain the unit number for this device.
+ */
+ case PPPIOCGUNIT:
+ PUT_USER (error, ppp2dev (ppp)->base_addr, (int *) param3);
+ if (error == 0) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: get unit: %ld\n",
+ ppp2dev (ppp)->base_addr);
+ }
+ break;
+/*
+ * Set the debug level
+ */
+ case PPPIOCSDEBUG:
+ GET_USER (error, temp_i, (int *) param3);
+ if (error == 0) {
+ temp_i = (temp_i & 0x1F) << 16;
+ temp_i |= (ppp->flags & ~0x1F0000);
+
+ if ((ppp->flags | temp_i) & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set flags to %x\n", temp_i);
+ ppp->flags = temp_i;
+ }
+ break;
+/*
+ * Get the debug level
+ */
+ case PPPIOCGDEBUG:
+ temp_i = (ppp->flags >> 16) & 0x1F;
+ PUT_USER (error, temp_i, (int *) param3);
+ break;
+/*
+ * Get the times since the last send/receive frame operation
+ */
+ case PPPIOCGIDLE:
+ {
+ struct ppp_idle cur_ddinfo;
+ __u32 cur_jiffies = jiffies;
+
+ /* change absolute times to relative times. */
+ cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ;
+ cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ;
+ COPY_TO_USER (error,
+ (void *) param3,
+ &cur_ddinfo,
+ sizeof (cur_ddinfo));
+ }
+ break;
+/*
+ * Retrieve the extended async map
+ */
+ case PPPIOCGXASYNCMAP:
+ COPY_TO_USER (error,
+ (void *) param3,
+ ppp->xmit_async_map,
+ sizeof (ppp->xmit_async_map));
+ break;
+/*
+ * Set the async extended map
+ */
+ case PPPIOCSXASYNCMAP:
+ {
+ __u32 temp_tbl[8];
+
+ COPY_FROM_USER (error,
+ temp_tbl,
+ (void *) param3,
+ sizeof (temp_tbl));
+
+ if (error == 0) {
+ temp_tbl[1] = 0x00000000;
+ temp_tbl[2] &= ~0x40000000;
+ temp_tbl[3] |= 0x60000000;
+
+ 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));
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set xasyncmap\n");
+ }
+ }
+ }
+ break;
+/*
+ * Set the maximum VJ header compression slot number.
+ */
+ case PPPIOCSMAXCID:
+ GET_USER (error, temp_i, (int *) param3);
+ if (error == 0) {
+ temp_i = (temp_i & 255) + 1;
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set maxcid to %d\n",
+ temp_i);
+ if (ppp->slcomp != NULL)
+ slhc_free (ppp->slcomp);
+ ppp->slcomp = slhc_init (16, temp_i);
+
+ if (ppp->slcomp == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp: no space for compression buffers!\n");
+ ppp_release (ppp);
+ error = -ENOMEM;
+ }
+ }
+ break;
+
+ case PPPIOCXFERUNIT:
+ ppp->backup_tty = tty;
+ ppp->sc_xfer = current->pid;
+ break;
+
+ case PPPIOCGNPMODE:
+ case PPPIOCSNPMODE:
+ {
+ struct npioctl npi;
+ COPY_FROM_USER (error,
+ &npi,
+ (void *) param3,
+ sizeof (npi));
+
+ if (error != 0)
+ break;
+
+ switch (npi.protocol) {
+ case PPP_IP:
+ npi.protocol = NP_IP;
+ break;
+ default:
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "pppioc[gs]npmode: "
+ "invalid proto %d\n", npi.protocol);
+ error = -EINVAL;
+ }
+
+ if (error != 0)
+ break;
+
+ if (param2 == PPPIOCGNPMODE) {
+ npi.mode = ppp->sc_npmode[npi.protocol];
+
+ COPY_TO_USER (error,
+ (void *) param3,
+ &npi,
+ sizeof (npi));
+ break;
+ }
+
+ if (npi.mode != ppp->sc_npmode[npi.protocol]) {
+ ppp->sc_npmode[npi.protocol] = npi.mode;
+ if (npi.mode != NPMODE_QUEUE) {
+ /* ppp_requeue(ppp); maybe needed */
+ ppp_tty_wakeup (ppp2tty(ppp));
+ }
+ }
+ }
+ break;
+/*
+ * Allow users to read, but not set, the serial port parameters
+ */
+ case TCGETS:
+ case TCGETA:
+ error = n_tty_ioctl (tty, file, param2, param3);
+ break;
+
+ case FIONREAD:
+ {
+ int count = ppp->ubuf->tail - ppp->ubuf->head;
+ if (count < 0)
+ count += (ppp->ubuf->size + 1);
+ PUT_USER (error, count, (int *) param3);
+ }
+ break;
+/*
+ * All other ioctl() events will come here.
+ */
+ default:
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_tty_ioctl: invalid ioctl: %x, addr %lx\n",
+ param2,
+ param3);
+
+ error = -ENOIOCTLCMD;
+ break;
+ }
+ return error;