+ switch (param2) {
+ case PPPIOCSMRU:
+ /*
+ * Set the MRU value
+ */
+ if (GET_USER(temp_i, (int *) param3))
+ break;
+ if (temp_i < PPP_MRU)
+ temp_i = PPP_MRU;
+ ppp->mru = temp_i;
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_INFO
+ "ppp_ioctl: set mru to %x\n", temp_i);
+ error = 0;
+ break;
+
+ case PPPIOCGFLAGS:
+ /*
+ * Fetch the current flags
+ */
+ 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
+ if (PUT_USER(temp_i, (int *) param3))
+ break;
+ error = 0;
+ break;
+
+ case PPPIOCSFLAGS:
+ /*
+ * Set the flags for the various options
+ */
+ if (GET_USER(temp_i, (int *) param3))
+ break;
+
+ if (ppp->flags & ~temp_i & SC_CCP_OPEN)
+ ppp_ccp_closed(ppp);
+
+ save_flags(flags);
+ cli();
+ oldflags = ppp->flags;
+ temp_i = (temp_i & SC_MASK) | (oldflags & ~SC_MASK);
+ ppp->flags = temp_i;
+ restore_flags(flags);
+
+ if ((oldflags | temp_i) & SC_DEBUG)
+ printk(KERN_INFO
+ "ppp_ioctl: set flags to %x\n", temp_i);
+ error = 0;
+ break;
+
+ case PPPIOCSCOMPRESS:
+ /*
+ * Set the compression mode
+ */
+ error = ppp_set_compression
+ (ppp, (struct ppp_option_data *) param3);
+ break;
+
+ case PPPIOCGUNIT:
+ /*
+ * Obtain the unit number for this device.
+ */
+ if (PUT_USER(ppp->line, (int *) param3))
+ break;
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_INFO
+ "ppp_ioctl: get unit: %d\n", ppp->line);
+ error = 0;
+ break;
+
+ case PPPIOCSDEBUG:
+ /*
+ * Set the debug level
+ */
+ if (GET_USER(temp_i, (int *) param3))
+ break;
+ temp_i = (temp_i & 0x1F) << 16;
+
+ if ((ppp->flags | temp_i) & SC_DEBUG)
+ printk(KERN_INFO
+ "ppp_ioctl: set dbg flags to %x\n", temp_i);
+
+ save_flags(flags);
+ cli();
+ ppp->flags = (ppp->flags & ~0x1F0000) | temp_i;
+ restore_flags(flags);
+ error = 0;
+ break;
+
+ case PPPIOCGDEBUG:
+ /*
+ * Get the debug level
+ */
+ temp_i = (ppp->flags >> 16) & 0x1F;
+ if (PUT_USER(temp_i, (int *) param3))
+ break;
+ error = 0;
+ break;
+
+ case PPPIOCGIDLE:
+ /*
+ * Get the times since the last send/receive frame operation
+ */
+ /* change absolute times to relative times. */
+ cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+ cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ;
+ if (COPY_TO_USER((void *) param3, &cur_ddinfo,
+ sizeof (cur_ddinfo)))
+ break;
+ error = 0;
+ break;
+
+ case PPPIOCSMAXCID:
+ /*
+ * Set the maximum VJ header compression slot number.
+ */
+ if (GET_USER(temp_i, (int *) param3))
+ break;
+ error = -EINVAL;
+ if (temp_i < 2 || temp_i > 255)
+ break;
+ ++temp_i;
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_INFO "ppp_ioctl: set maxcid to %d\n",
+ temp_i);
+ if (ppp->slcomp != NULL)
+ slhc_free(ppp->slcomp);
+ ppp->slcomp = slhc_init(16, temp_i);
+
+ error = -ENOMEM;
+ if (ppp->slcomp == NULL) {
+ printk(KERN_ERR "ppp: no memory for VJ compression\n");
+ break;
+ }
+ error = 0;
+ break;
+
+ case PPPIOCGNPMODE:
+ case PPPIOCSNPMODE:
+ if (COPY_FROM_USER(&npi, (void *) param3, sizeof(npi)))
+ break;
+
+ switch (npi.protocol) {
+ case PPP_IP:
+ npi.protocol = NP_IP;
+ break;
+ case PPP_IPX:
+ npi.protocol = NP_IPX;
+ break;
+ case PPP_AT:
+ npi.protocol = NP_AT;
+ break;
+ default:
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "pppioc[gs]npmode: "
+ "invalid proto %d\n", npi.protocol);
+ error = -EINVAL;
+ goto out;
+ }
+
+ if (param2 == PPPIOCGNPMODE) {
+ npi.mode = ppp->sc_npmode[npi.protocol];
+ if (COPY_TO_USER((void *) param3, &npi, sizeof(npi)))
+ break;
+ } else {
+ ppp->sc_npmode[npi.protocol] = npi.mode;
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "ppp: set np %d to %d\n",
+ npi.protocol, npi.mode);
+ mark_bh(NET_BH);
+ }
+ error = 0;
+ break;
+
+ default:
+ /*
+ * All other ioctl() events will come here.
+ */
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_ERR
+ "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+ param2, param3);
+
+ error = -ENOIOCTLCMD;
+ break;
+ }
+out:
+ return error;
+}
+
+/*
+ * Process the set-compression ioctl.
+ */
+static int
+ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
+{
+ struct compressor *cp;
+ int error, nb;
+ unsigned long flags;
+ __u8 *ptr;
+ __u8 ccp_option[CCP_MAX_OPTION_LENGTH];
+ struct ppp_option_data data;
+
+ /*
+ * Fetch the compression parameters
+ */
+ error = -EFAULT;
+ if (COPY_FROM_USER(&data, odp, sizeof (data)))
+ goto out;
+
+ nb = data.length;
+ ptr = data.ptr;
+ if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH)
+ nb = CCP_MAX_OPTION_LENGTH;
+
+ if (COPY_FROM_USER(ccp_option, ptr, nb))
+ goto out;
+
+ error = -EINVAL;
+ if (ccp_option[1] < 2) /* preliminary check on the length byte */
+ goto out;
+
+ save_flags(flags);
+ cli();
+ ppp->flags &= ~(data.transmit? SC_COMP_RUN: SC_DECOMP_RUN);
+ restore_flags(flags);
+
+ cp = find_compressor (ccp_option[0]);
+#if defined(CONFIG_KMOD) || defined(CONFIG_KERNELD)
+ if (cp == NULL) {
+ char modname[32];
+ sprintf(modname, "ppp-compress-%d", ccp_option[0]);
+ request_module(modname);
+ cp = find_compressor(ccp_option[0]);
+ }
+#endif /* CONFIG_KMOD */
+
+ if (cp == NULL) {
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG
+ "%s: no compressor for [%x %x %x], %x\n",
+ ppp->name, ccp_option[0], ccp_option[1],
+ ccp_option[2], nb);
+ goto out; /* compressor not loaded */
+ }
+
+ /*
+ * 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_xc_state = NULL;
+
+ 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(KERN_DEBUG "%s: comp_alloc failed\n",
+ ppp->name);
+ error = -ENOBUFS;
+ }
+ } else {
+ if (ppp->sc_rc_state != NULL)
+ (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
+ ppp->sc_rc_state = NULL;
+
+ 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(KERN_DEBUG "%s: decomp_alloc failed\n",
+ ppp->name);
+ error = -ENOBUFS;
+ }
+ }
+out:
+ return error;
+}
+
+/*
+ * 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;
+
+ if (ppp->flags & SC_DEBUG)
+ printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n",
+ rcvd, CCP_CODE(dp), ppp->flags);
+ 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;