patch-2.0.1 linux/drivers/isdn/isdn_ppp.c

Next file: linux/drivers/isdn/isdn_tty.c
Previous file: linux/drivers/isdn/isdn_net.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.0/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.9 1996/05/18 01:37:01 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.12 1996/06/24 17:42:03 fritz Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,16 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.12  1996/06/24 17:42:03  fritz
+ * Minor bugfixes.
+ *
+ * Revision 1.11  1996/06/16 17:46:05  tsbogend
+ * changed unsigned long to u32 to make Alpha people happy
+ *
+ * Revision 1.10  1996/06/11 14:50:29  hipp
+ * Lot of changes and bugfixes.
+ * New scheme to resend packets to busy LL devices.
+ *
  * Revision 1.9  1996/05/18 01:37:01  fritz
  * Added spelling corrections and some minor changes
  * to stay in sync with kernel.
@@ -69,7 +79,7 @@
 #endif
  
 /* Prototypes */
-static int isdn_ppp_fill_rq(char *buf, int len,int proto, int minor);
+static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int minor);
 static int isdn_ppp_closewait(int);
 static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
 			struct sk_buff *skb, int proto);
@@ -83,7 +93,7 @@
 		int BEbyte, int *sqno, int min_sqno);
 #endif
 
-char *isdn_ppp_revision              = "$Revision: 1.9 $";
+char *isdn_ppp_revision              = "$Revision: 1.12 $";
 struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
 extern int isdn_net_force_dial_lp(isdn_net_local *);
@@ -95,10 +105,13 @@
 int isdn_ppp_free(isdn_net_local *lp)
 {
 	isdn_net_local *master_lp=lp;
+	unsigned long flags;
 
 	if (lp->ppp_minor < 0)
 		return 0;
 
+	save_flags(flags);
+	cli();
 #ifdef CONFIG_ISDN_MPP
 	if(lp->master)
 		master_lp = (isdn_net_local *) lp->master->priv;
@@ -123,7 +136,7 @@
 
 	ippp_table[lp->ppp_minor]->lp = NULL;	/* link is down .. set lp to NULL */
 	lp->ppp_minor = -1;			/* is this OK ?? */
-
+	restore_flags(flags);
 	return 0;
 }
 
@@ -286,6 +299,7 @@
 
 #ifdef CONFIG_ISDN_PPP_VJ
 	slhc_free(ippp_table[minor]->slcomp);
+	ippp_table[minor]->slcomp = NULL;
 	kfree(ippp_table[minor]->cbuf);
 #endif
 
@@ -394,7 +408,25 @@
 	case PPPIOCSMAXCID:	/* set the maximum compression slot id */
 		if ((r = get_arg((void *) arg, &val)))
 			return r;
-		ippp_table[minor]->maxcid = val;
+		val++;
+		if(ippp_table[minor]->maxcid != val) {
+#ifdef CONFIG_ISDN_PPP_VJ
+			struct slcompress *sltmp;
+#endif
+			if(ippp_table[minor]->debug & 0x1)
+				printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val);
+			ippp_table[minor]->maxcid = val;
+#ifdef CONFIG_ISDN_PPP_VJ
+			sltmp = slhc_init(16,val);
+			if(!sltmp) {
+				printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+				return -ENOMEM;
+			}	
+			if(ippp_table[minor]->slcomp)
+				slhc_free(ippp_table[minor]->slcomp);
+			ippp_table[minor]->slcomp = sltmp;
+#endif
+		}
 		break;
 	case PPPIOCGDEBUG:
 		if ((r = set_arg((void *) arg, ippp_table[minor]->debug)))
@@ -453,10 +485,11 @@
  *  fill up isdn_ppp_read() queue ..
  */
 
-static int isdn_ppp_fill_rq(char *buf, int len,int proto, int minor)
+static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int minor)
 {
 	struct ippp_buf_queue *bf, *bl;
 	unsigned long flags;
+	unsigned char *nbuf;
 
 	if (minor < 0 || minor >= ISDN_MAX_CHANNELS) {
 		printk(KERN_WARNING "ippp: illegal minor.\n");
@@ -466,6 +499,18 @@
 		printk(KERN_DEBUG "ippp: device not activated.\n");
 		return 0;
 	}
+
+	nbuf = (unsigned char *) kmalloc(len+4, GFP_ATOMIC);
+	if(!nbuf) {
+		printk(KERN_WARNING "ippp: Can't alloc buf\n");
+		return 0;
+	}
+	nbuf[0] = PPP_ALLSTATIONS;
+	nbuf[1] = PPP_UI;
+	nbuf[2] = proto >> 8;
+	nbuf[3] = proto & 0xff;
+	memcpy(nbuf+4, buf, len);
+
 	save_flags(flags);
 	cli();
 
@@ -478,20 +523,9 @@
 		kfree(bf->buf);
 		ippp_table[minor]->first = bf;
 	}
-	bl->buf = (char *) kmalloc(len+4, GFP_ATOMIC);
-	if (!bl->buf) {
-		printk(KERN_WARNING "ippp: Can't alloc buf\n");
-		restore_flags(flags);
-		return 0;
-	}
+	bl->buf = (char *) nbuf;
 	bl->len = len+4;
 
-	bl->buf[0] = PPP_ALLSTATIONS;
-	bl->buf[1] = PPP_UI;
-	bl->buf[2] = proto >> 8;
-	bl->buf[3] = proto & 0xff;
-	memcpy(bl->buf+4, buf, len);
-
 	ippp_table[minor]->last = bl->next;
 	restore_flags(flags);
 
@@ -561,9 +595,23 @@
 			return 0;
 
 		if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
-		    (lp->flags & ISDN_NET_CONNECTED))
-			isdn_writebuf_stub(lp->isdn_device,lp->isdn_channel,
-                                           buf, count, 1);
+		    (lp->flags & ISDN_NET_CONNECTED)) {
+			struct sk_buff *skb;
+			skb = dev_alloc_skb(count);
+			if(!skb) {
+				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
+				return count;
+			}
+			skb->free = 1;
+			memcpy_fromfs(skb_put(skb, count), buf, count);
+			if(isdn_writebuf_skb_stub(lp->isdn_device,lp->isdn_channel,skb) != count) {
+				if(lp->sav_skb) {
+					dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb!\n");
+				}
+				lp->sav_skb = skb;
+			}
+		}
 	}
 
 	return count;
@@ -617,6 +665,11 @@
 	if(ippp_table[lp->ppp_minor]->debug & 0x4)
 		printk(KERN_DEBUG "recv skb, len: %ld\n",skb->len);
 
+	if(net_dev->local.master) {
+		printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
+		net_dev = ((isdn_net_local*) net_dev->local.master->priv)->netdev;
+	}
+
 	if(skb->data[0] == 0xff && skb->data[1] == 0x03)
 		skb_pull(skb,2);
 	else if (ippp_table[lp->ppp_minor]->pppcfg & SC_REJ_COMP_AC) {
@@ -819,8 +872,13 @@
 			skb->mac.raw = skb->data;
 			pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_minor]->slcomp,
 						  skb->data, skb_old->len);
-			skb_trim(skb, pkt_len);
 			dev_kfree_skb(skb_old,FREE_WRITE);
+			if(pkt_len < 0) {
+				dev_kfree_skb(skb,FREE_WRITE);
+				lp->stats.rx_dropped++;
+				return;
+			}
+			skb_trim(skb, pkt_len);
 			skb->protocol = htons(ETH_P_IP);
 		}
 #else
@@ -848,6 +906,7 @@
  * send ppp frame .. we expect a PIDCOMPressable proto -- 
  *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
  */
+
 int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct device *mdev = ((isdn_net_local *) (dev->priv) )->master;	/* get master (for redundancy) */
@@ -861,32 +920,43 @@
 	else
 		mlp = (isdn_net_local *) (dev->priv);
 	nd = mlp->netdev;	/* get master lp */
-	lp = nd->queue;		/* get lp on top of queue */
-	ipt = ippp_table[lp->ppp_minor];
 	ipts = ippp_table[mlp->ppp_minor];
 
-	if (!(ipt->pppcfg & SC_ENABLE_IP)) {    /* PPP connected ? */
-		printk(KERN_INFO "isdn, xmit: Packet blocked: %d %d\n", lp->isdn_device, lp->isdn_channel);
+	if (!(ipts->pppcfg & SC_ENABLE_IP)) {    /* PPP connected ? */
+		printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
 		return 1;
         }
+
+	lp = nd->queue;		/* get lp on top of queue */
+	if(lp->sav_skb) {	/* find a non-busy device */
+		isdn_net_local *nlp = lp->next;
+		while(lp->sav_skb) {
+			if(lp == nlp)
+				return 1;
+			nlp = nd->queue = nd->queue->next;
+		}
+		lp = nlp;
+	}
+	ipt = ippp_table[lp->ppp_minor];
+
         lp->huptimer = 0;
  
         /* If packet is to be resent, it has already been processed and
          * therefore its first bytes are already initialized. In this case
          * send it immediately ...
          */
-        if (*((unsigned long *)skb->data) != 0)
+        if (*((u32 *)skb->data) != 0) {
+	  printk(KERN_ERR "%s: Whoops .. packet resend should no longer happen!\n",dev->name);
           return (isdn_net_send_skb(dev , lp , skb));
+        }
 
         /* ... else packet needs processing. */
 
-/* future: step to next 'lp' when this lp is 'tbusy' */
-
 	if(ippp_table[lp->ppp_minor]->debug & 0x4)
 		printk(KERN_DEBUG  "xmit skb, len %ld\n",skb->len);
 
 #ifdef CONFIG_ISDN_PPP_VJ
-	if (ipt->pppcfg & SC_COMP_TCP) {	/* ipt or ipts ? -> check this again! */
+	if (ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but check again */
 		u_char *buf = skb->data;
 		int pktlen;
 		int len = 4;
@@ -947,7 +1017,15 @@
 	skb->data[3] = proto & 0xff;
 
         /* tx-stats are now updated via BSENT-callback */
-	return (isdn_net_send_skb(dev , lp , skb));
+	if(isdn_net_send_skb(dev , lp , skb)) {	
+		if(lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
+			printk(KERN_ERR "%s: whoops .. there is another stored skb!\n!",dev->name);
+			dev_kfree_skb(skb,FREE_WRITE);
+		}
+		else
+			lp->sav_skb = skb;
+	}
+	return 0;
 }
 
 void isdn_ppp_free_sqqueue(isdn_net_dev * p) 
@@ -1165,6 +1243,9 @@
 
 static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min_sqno)
 {
+#ifdef CONFIG_ISDN_PPP_VJ
+	int toss = 0;
+#endif
 /* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
    eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
    wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
@@ -1183,6 +1264,9 @@
 					ql = q->last;
 					dev_kfree_skb(q->skb,FREE_WRITE);
 					kfree(q);
+#ifdef CONFIG_ISDN_PPP_VJ
+					toss = 1;
+#endif
 					q = ql;
 				}
 				q = dev->mp_last;
@@ -1191,6 +1275,11 @@
 		} else
 			break;
 	}
+#ifdef CONFIG_ISDN_PPP_VJ
+	/* did we free a stale frame ? */
+	if(toss)
+		slhc_toss(ippp_table[dev->local.ppp_minor]->slcomp);
+#endif
 }
 
 /*
@@ -1207,7 +1296,7 @@
 
 	while (net_dev) {
 		isdn_net_local *lp = &net_dev->local;
-		if (net_dev->ib.modify)	{	/* interface locked? */
+		if (net_dev->ib.modify || lp->master)	{	/* interface locked or slave?*/
                         net_dev = net_dev->next;
 			continue;
                 }
@@ -1215,6 +1304,13 @@
 		q = net_dev->ib.sq;
 		while (q) {
 			if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
+
+#ifdef CONFIG_ISDN_PPP_VJ
+				/* did we step over a missing frame ? */
+				if(q->sqno_start != net_dev->ib.next_num)
+					slhc_toss(ippp_table[lp->ppp_minor]->slcomp);
+#endif
+
 				ql = net_dev->ib.sq;
 				net_dev->ib.sq = q->next;
 				net_dev->ib.next_num = q->sqno_end + 1;
@@ -1234,6 +1330,49 @@
 #endif
 }
 
+/*
+ * network device ioctl handlers
+ */
+
+static int isdn_ppp_dev_ioctl_stats(int minor,struct ifreq *ifr,struct device *dev)
+{
+	struct ppp_stats *res, t;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	int err;
+
+        res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+        err = verify_area (VERIFY_WRITE, res,sizeof(struct ppp_stats));
+
+	if(err)
+		return err;
+
+	/* build a temporary stat struct and copy it to user space */
+
+	memset (&t, 0, sizeof(struct ppp_stats));
+	if(dev->flags & IFF_UP) {
+		t.p.ppp_ipackets = lp->stats.rx_packets;
+		t.p.ppp_ierrors = lp->stats.rx_errors;
+		t.p.ppp_opackets = lp->stats.tx_packets;
+		t.p.ppp_oerrors = lp->stats.tx_errors;
+#ifdef CONFIG_ISDN_PPP_VJ
+		if(minor >= 0 && ippp_table[minor]->slcomp) {
+			struct slcompress *slcomp = ippp_table[minor]->slcomp;
+			t.vj.vjs_packets  = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed;
+			t.vj.vjs_compressed = slcomp->sls_o_compressed;
+			t.vj.vjs_searches = slcomp->sls_o_searches;
+			t.vj.vjs_misses   = slcomp->sls_o_misses;
+			t.vj.vjs_errorin  = slcomp->sls_i_error;
+			t.vj.vjs_tossed   = slcomp->sls_i_tossed;
+			t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
+			t.vj.vjs_compressedin = slcomp->sls_i_compressed;
+		}
+#endif
+	}
+	memcpy_tofs (res, &t, sizeof (struct ppp_stats));
+	return 0;
+
+}
+
 int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {
 	int error;
@@ -1241,19 +1380,27 @@
 	int len;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 
+#if 1
+	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_minor);
+#endif
+
 	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
 		return -EINVAL;
 
 	switch (cmd) {
-	case SIOCGPPPVER:
-		r = (char *) ifr->ifr_ifru.ifru_data;
-		len = strlen(PPP_VERSION) + 1;
-		error = verify_area(VERIFY_WRITE, r, len);
-		if (!error)
-			memcpy_tofs(r, PPP_VERSION, len);
-		break;
-	default:
-		error = -EINVAL;
+		case SIOCGPPPVER:
+			r = (char *) ifr->ifr_ifru.ifru_data;
+			len = strlen(PPP_VERSION) + 1;
+			error = verify_area(VERIFY_WRITE, r, len);
+			if (!error)
+				memcpy_tofs(r, PPP_VERSION, len);
+			break;
+		case SIOCGPPPSTATS:
+			error = isdn_ppp_dev_ioctl_stats (lp->ppp_minor, ifr, dev);
+			break;
+		default:
+			error = -EINVAL;
+			break;
 	}
 	return error;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov