+/* on entry, a received frame is in ppp->rbuf.bufr
+ check it and dispose as appropriate */
+
+static int
+ppp_doframe (struct ppp *ppp)
+{
+ u_char *data = buf_base (ppp->rbuf);
+ int count = ppp->rbuf->count;
+ int addr, ctrl, proto;
+ int new_count;
+ u_char *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)
+ printk (KERN_WARNING
+ "ppp_toss: tossing frame, reason = %d\n",
+ ppp->toss);
+ ppp->stats.ppp_ierrors++;
+ 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_WARNING
+ "ppp: got runt ppp frame, %d chars\n", count);
+ slhc_toss (ppp->slcomp);
+ ppp->stats.ppp_ierrors++;
+ return 1;
+ }
+/*
+ * 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_WARNING
+ "ppp: frame with bad fcs, excess = %x\n",
+ ppp->rbuf->fcs ^ PPP_GOODFCS);
+ ppp->stats.ppp_ierrors++;
+ 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 = (u_short) *data++;
+ if ((proto & 1) == 0) {
+ proto = (proto << 8) | (u_short) *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 + 4, GFP_ATOMIC);
+ if (new_data == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_doframe: no memory\n");
+ slhc_toss (ppp->slcomp);
+ (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
+ data,
+ count);
+ return 1;
+ }
+/*
+ * Decompress the frame
+ */
+ new_count = bsd_decompress (ppp->sc_rc_state,
+ data,
+ count,
+ new_data,
+ ppp->mru + 4);
+ switch (new_count) {
+ default:
+ ppp_doframe_lower (ppp, new_data, new_count);
+ kfree (new_data);
+ return 1;
+
+ case DECOMP_OK:
+ break;
+
+ case DECOMP_ERROR:
+ ppp->flags |= SC_DC_ERROR;
+ break;
+
+ case DECOMP_FATALERROR:
+ ppp->flags |= SC_DC_FERROR;
+ break;
+ }
+/*
+ * Log the error condition and discard the frame.
+ */
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_ERR
+ "ppp_proto_comp: "
+ "decompress err %d\n", new_count);
+ kfree (new_data);
+ slhc_toss (ppp->slcomp);
+ return 1;
+ }
+/*
+ * 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, u_char * buf,
+ unsigned int nr)
+{
+ struct ppp *ppp = tty2ppp (tty);
+ u_char c;
+ int len, indx;
+
+#define GETC(c) \
+{ \
+ c = buf_base (ppp->ubuf) [ppp->ubuf->tail++]; \
+ ppp->ubuf->tail &= ppp->ubuf->size; \
+}
+
+/*
+ * Validate the pointer to the PPP structure
+ */
+ if (!ppp)
+ return -EIO;
+
+ if (ppp->magic != PPP_MAGIC)
+ return -EIO;
+
+ CHECK_PPP (-ENXIO);
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: called buf=%p nr=%u\n",
+ buf, nr);
+/*
+ * Acquire the read lock.
+ */
+ for (;;) {
+ ppp = tty2ppp (tty);
+ if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
+ 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) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_read: no data "
+ "(EWOULDBLOCK)\n");
+ return -EWOULDBLOCK;
+ }
+ 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;
+ }
+/*
+ * Reset the time of the last read operation.
+ */
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG "ppp_tty_read: len = %d\n", len);
+/*
+ * Ensure that the frame will fit within the caller's buffer. If not, then
+ * discard the frame from the input buffer and return an error to the caller.
+ */
+ 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_byte_user (PPP_ALLSTATIONS, buf++);
+ put_byte_user (PPP_UI, buf++);
+
+ indx = len;
+/*
+ * Copy the received data from the buffer to the caller's area.
+ */
+ while (indx-- > 0) {
+ GETC (c);
+ put_byte_user (c, buf);
+ ++buf;
+ }
+/*
+ * Release the lock and return the character count in the buffer area.
+ */
+ 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 u_char 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: %x %d\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 escapement and send the
+ * data to the remote system.
+ */
+
+static void
+ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
+ u_char *data, int count, int non_ip)
+{
+ unsigned short int write_fcs;
+ int address, control;
+ int proto;
+/*
+ * 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);
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG "ppp_dev_xmit_lower: fcs is %hx\n",
+ write_fcs);
+/*
+ * 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);
+ else {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_dev_xmit: writing %d chars\n",
+ 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,
+ u_char *data, int count)
+{
+ int proto;
+ int address, control;
+ u_char *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 = bsd_compress (ppp->sc_xc_state,
+ data,
+ new_data,
+ count,
+ count);
+
+ if (new_count > 0) {
+ ++ppp->stats.ppp_opackets;
+ ppp->stats.ppp_ooctects += new_count;
+
+ ppp_dev_xmit_lower (ppp, buf, new_data,
+ new_count, 0);
+ kfree (new_data);
+ return 0;
+ }
+/*
+ * The frame could not be compressed.
+ */
+ kfree (new_data);
+ }
+/*
+ * The frame may not be compressed. Update the statistics before the
+ * count field is destroyed. The frame will be transmitted.
+ */
+ ++ppp->stats.ppp_opackets;
+ ppp->stats.ppp_ooctects += count;
+/*
+ * 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, u_char *data, int len)
+{
+ u_char *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 = (u_char *) &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;
+/*
+ * All other frame types
+ */
+ 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 u_char * data,
+ unsigned int count)
+{
+ struct ppp *ppp = tty2ppp (tty);
+ u_char *new_data;
+ int status;
+/*
+ * Verify the pointer to the PPP data and that the tty is still in PPP mode.
+ */
+ 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) {
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_WARNING
+ "ppp_tty_write: truncating user packet "
+ "from %u to mtu %d\n", count, PPP_MTU);
+ count = PPP_MTU;
+ }
+/*
+ * 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) {
+ kfree (new_data);
+ return 0;
+ }
+
+ if (current->signal & ~current->blocked) {
+ kfree (new_data);
+ return -EINTR;
+ }
+ }
+/*
+ * Ensure that the caller's buffer is valid.
+ */
+ status = verify_area (VERIFY_READ, data, count);
+ if (status != 0) {
+ kfree (new_data);
+ ppp->tbuf->locked = 0;
+ return status;
+ }
+
+ memcpy_fromfs (new_data, data, count);
+/*
+ * 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;
+ u_char *ptr;
+ u_char ccp_option[CCP_MAX_OPTION_LENGTH];
+/*
+ * Fetch the compression parameters
+ */
+ error = verify_area (VERIFY_READ, odp, sizeof (data));
+ if (error == 0) {
+ memcpy_fromfs (&data, odp, sizeof (data));
+ nb = data.length;
+ ptr = data.ptr;
+ if ((unsigned long) nb >= (unsigned long)CCP_MAX_OPTION_LENGTH)
+ nb = CCP_MAX_OPTION_LENGTH;
+
+ error = verify_area (VERIFY_READ, ptr, nb);
+ }
+
+ if (error != 0)
+ return error;
+
+ memcpy_fromfs (ccp_option, ptr, nb);
+
+ if (ccp_option[1] < 2) /* preliminary check on the length byte */
+ return (-EINVAL);
+
+ cp = find_compressor ((int) (unsigned) (unsigned char) 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;
+/*
+ * 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:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ temp_i = get_int_user ((int *) param3);
+ 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:
+ error = verify_area (VERIFY_WRITE, (void *) param3,
+ 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_long_user ((long) temp_i, param3);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_DEBUG
+ "ppp_tty_ioctl: get flags: addr %lx flags "
+ "%x\n", param3, temp_i);
+ }
+ break;
+/*
+ * Set the flags for the various options
+ */
+ case PPPIOCSFLAGS:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ temp_i = get_int_user (param3) & 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:
+ error = verify_area (VERIFY_WRITE, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ put_long_user (ppp->xmit_async_map[0], param3);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: get asyncmap: addr "
+ "%lx asyncmap %lx\n",
+ param3,
+ (unsigned long) ppp->xmit_async_map[0]);
+ }
+ break;
+/*
+ * Set the transmit async map
+ */
+ case PPPIOCSASYNCMAP:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ ppp->xmit_async_map[0] = get_long_user (param3);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set xmit asyncmap %lx\n",
+ (unsigned long) ppp->xmit_async_map[0]);
+ }
+ break;
+/*
+ * Set the receive async map
+ */
+ case PPPIOCSRASYNCMAP:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ ppp->recv_async_map = get_long_user (param3);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: set rcv asyncmap %lx\n",
+ (unsigned long) ppp->recv_async_map);
+ }
+ break;
+/*
+ * Obtain the unit number for this device.
+ */
+ case PPPIOCGUNIT:
+ error = verify_area (VERIFY_WRITE, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ put_long_user (ppp2dev (ppp)->base_addr, param3);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: get unit: %ld",
+ ppp2dev (ppp)->base_addr);
+ }
+ break;
+/*
+ * Set the debug level
+ */
+ case PPPIOCSDEBUG:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ temp_i = (get_int_user (param3) & 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:
+ error = verify_area (VERIFY_WRITE, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ temp_i = (ppp->flags >> 16) & 0x1F;
+ put_long_user ((long) temp_i, param3);
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: get debug level %d\n",
+ temp_i);
+ }
+ break;
+/*
+ * Get the times since the last send/receive frame operation
+ */
+ case PPPIOCGIDLE:
+ error = verify_area (VERIFY_WRITE, (void *) param3,
+ sizeof (struct ppp_idle));
+ if (error == 0) {
+ struct ppp_idle cur_ddinfo;
+ unsigned long 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;
+ memcpy_tofs ((void *) param3, &cur_ddinfo,
+ sizeof (cur_ddinfo));
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: read demand dial info\n");
+ }
+ break;
+/*
+ * Retrieve the extended async map
+ */
+ case PPPIOCGXASYNCMAP:
+ error = verify_area (VERIFY_WRITE,
+ (void *) param3,
+ sizeof (ppp->xmit_async_map));
+ if (error == 0) {
+ memcpy_tofs ((void *) param3,
+ ppp->xmit_async_map,
+ sizeof (ppp->xmit_async_map));
+
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: get xasyncmap: addr %lx\n",
+ param3);
+ }
+ break;
+/*
+ * Set the async extended map
+ */
+ case PPPIOCSXASYNCMAP:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (ppp->xmit_async_map));
+ if (error == 0) {
+ __u32 temp_tbl[8];
+
+ memcpy_fromfs (temp_tbl, (void *) param3,
+ sizeof (ppp->xmit_async_map));
+ 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:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ temp_i = get_int_user (param3) + 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;
+/*
+ * Allow users to read, but not set, the serial port parameters
+ */
+ case TCGETS:
+ case TCGETA:
+ error = n_tty_ioctl (tty, file, param2, 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;
+}
+
+/*
+ * TTY callback.
+ *
+ * Process the select() statement for the PPP device.
+ */
+
+static int
+ppp_tty_select (struct tty_struct *tty, struct inode *inode,
+ struct file *filp, int sel_type, select_table * wait)
+{
+ struct ppp *ppp = tty2ppp (tty);
+ int result = 1;
+/*
+ * Verify the status of the PPP device.
+ */
+ if (!ppp)
+ return -EBADF;
+
+ if (ppp->magic != PPP_MAGIC)
+ return -EBADF;
+
+ CHECK_PPP (0);
+/*
+ * Branch on the type of select mode. A read request must lock the user
+ * buffer area.
+ */
+ switch (sel_type) {
+ case SEL_IN:
+ if (set_bit (0, &ppp->ubuf->locked) == 0) {
+ /* Test for the presence of data in the queue */
+ if (ppp->ubuf->head != ppp->ubuf->tail) {
+ clear_bit (0, &ppp->ubuf->locked);
+ break;
+ }
+ clear_bit (0, &ppp->ubuf->locked);
+ } /* fall through */
+ /*
+ * Exceptions or read errors.
+ */
+ case SEL_EX:
+ /* Is this a pty link and the remote disconnected? */
+ if (tty->flags & (1 << TTY_SLAVE_CLOSED))
+ break;
+
+ /* Is this a local link and the modem disconnected? */
+ if (tty_hung_up_p (filp))
+ break;
+
+ select_wait (&ppp->read_wait, wait);
+ result = 0;
+ break;
+/*
+ * Write mode. A write is allowed if there is no current transmission.
+ */
+ case SEL_OUT:
+ if (ppp->tbuf->locked != 0) {
+ select_wait (&ppp->write_wait, wait);
+ result = 0;
+ }
+ break;
+ }
+ return result;
+}
+
+/*************************************************************
+ * 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...).
+ *************************************************************/
+
+/*
+ * Callback from the network layer when the device goes up.
+ */
+
+static int
+ppp_dev_open (struct device *dev)
+{
+ struct ppp *ppp = dev2ppp (dev);