+/* 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;
+
+#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);
+
+ 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 "
+ "(EAGAIN)\n");
+ 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;
+ }
+/*
+ * 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.
+ */
+ 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 (PPP_ALLSTATIONS, buf++);
+ put_user (PPP_UI, buf++);
+
+ indx = len;
+/*
+ * Copy the received data from the buffer to the caller's area.
+ */
+ while (indx-- > 0) {
+ GETC (c);
+ put_user (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: %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 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;
+/*
+ * 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 = 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, __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) {
+ 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;
+ __u8 *ptr;
+ __u8 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 ((__u32) nb >= (__u32)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 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:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ temp_i = get_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_user (temp_i, (int *) 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_user ((int *) 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_user (ppp->xmit_async_map[0], (int *) param3);
+ if (ppp->flags & SC_DEBUG)
+ printk (KERN_INFO
+ "ppp_tty_ioctl: get asyncmap: addr "
+ "%lx asyncmap %x\n",
+ param3,
+ 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_user ((int *) param3);
+ 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:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ ppp->recv_async_map = get_user ((int *) param3);
+ 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:
+ error = verify_area (VERIFY_WRITE, (void *) param3,
+ sizeof (temp_i));
+ if (error == 0) {
+ put_user (ppp2dev (ppp)->base_addr, (int *) 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_user ((int *) 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_user (temp_i, (int *) 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;
+ __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;
+ 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_user ((int *) 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;
+
+ case PPPIOCXFERUNIT:
+ ppp_tty_close_local (tty, current->pid);
+ break;
+
+ case PPPIOCGNPMODE:
+ case PPPIOCSNPMODE:
+ error = verify_area (VERIFY_READ, (void *) param3,
+ sizeof (struct npioctl));
+ if (error == 0) {
+ struct npioctl npi;
+ memcpy_fromfs (&npi,
+ (void *) param3,
+ sizeof (npi));
+
+ switch (npi.protocol) {
+ case PPP_IP:
+ npi.protocol = NP_IP;
+ break;
+ default:
+ error = -EINVAL;
+ }
+
+ if (error != 0)
+ break;
+
+ if (param2 == PPPIOCGNPMODE) {
+ npi.mode = ppp->sc_npmode[npi.protocol];
+ error = verify_area (VERIFY_WRITE,
+ (void *) param3,
+ sizeof (npi));
+ if (error != 0)
+ break;
+
+ memcpy_tofs ((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:
+ error = verify_area (VERIFY_WRITE,
+ (void *) param3,
+ sizeof (int));
+ if (error == 0) {
+ int count = ppp->ubuf->tail - ppp->ubuf->head;
+ if (count < 0)
+ count += (ppp->ubuf->size + 1);
+
+ put_user (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;
+}
+
+/*
+ * TTY callback.
+ *
+ * Process the select() statement for the PPP device.
+ */