patch-pre2.0.6 linux/drivers/isdn/isdn_ppp.c

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

diff -u --recursive --new-file pre2.0.5/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.5 1996/04/20 16:32:32 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.9 1996/05/18 01:37:01 fritz Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,19 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.9  1996/05/18 01:37:01  fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.8  1996/05/06 11:34:55  hipp
+ * fixed a few bugs
+ *
+ * Revision 1.7  1996/04/30 11:07:42  fritz
+ * Added Michael's ippp-bind patch.
+ *
+ * Revision 1.6  1996/04/30 09:33:09  fritz
+ * Removed compatibility-macros.
+ *
  * Revision 1.5  1996/04/20 16:32:32  fritz
  * Changed ippp_table to an array of pointers, allocating each part
  * separately.
@@ -43,9 +56,7 @@
 
 /* TODO: right tbusy handling when using MP */
 
-#ifndef STANDALONE
 #include <linux/config.h>
-#endif
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/isdn.h>
@@ -58,8 +69,8 @@
 #endif
  
 /* Prototypes */
-static int isdn_ppp_fill_rq(char *buf, int len, int minor);
-static int isdn_ppp_hangup(int);
+static int isdn_ppp_fill_rq(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);
 static int isdn_ppp_if_get_unit(char **namebuf);
@@ -72,47 +83,53 @@
 		int BEbyte, int *sqno, int min_sqno);
 #endif
 
-char *isdn_ppp_revision              = "$Revision: 1.5 $";
+char *isdn_ppp_revision              = "$Revision: 1.9 $";
 struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
 extern int isdn_net_force_dial_lp(isdn_net_local *);
 
-int isdn_ppp_free(isdn_net_local * lp)
+/*
+ * unbind isdn_net_local <=> ippp-device 
+ * note: it can happen, that we hangup/free the master before the slaves
+ */
+int isdn_ppp_free(isdn_net_local *lp)
 {
+	isdn_net_local *master_lp=lp;
+
 	if (lp->ppp_minor < 0)
 		return 0;
 
 #ifdef CONFIG_ISDN_MPP
 	if(lp->master)
-	{
-		isdn_net_dev *p = dev->netdev;
-		lp->last->next = lp->next;
-		lp->next->last = lp->last;
-		if(lp->netdev->queue == lp)
-			lp->netdev->queue = lp->next;
-                lp->next = lp->last = lp;
-		while(p) {
-			if(lp == &p->local) {
-				lp->netdev = p;
-				break;
-			}
-			p=p->next;
+		master_lp = (isdn_net_local *) lp->master->priv;
+
+	lp->last->next = lp->next;
+	lp->next->last = lp->last;
+	if(master_lp->netdev->queue == lp) {
+		master_lp->netdev->queue = lp->next;
+		if(lp->next == lp) {	/* last link in queue? */
+               		master_lp->netdev->ib.bundled = 0;
+			isdn_ppp_free_mpqueue(master_lp->netdev);
+			isdn_ppp_free_sqqueue(master_lp->netdev);
 		}
-	} else {
-                lp->netdev->ib.bundled = 0;
-		/* last link: free mpqueue, free sqqueue ? */
 	}
-
+	lp->next = lp->last = lp;	/* (re)set own pointers */
 #endif
 
-	isdn_ppp_hangup(lp->ppp_minor);
-#if 0
-	printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_minor, (long) lp,(long) ippp_table[lp->ppp_minor]->lp);
-#endif
-	ippp_table[lp->ppp_minor]->lp = NULL;
+	isdn_ppp_closewait(lp->ppp_minor);	/* force wakeup on ippp device */
+
+	if(ippp_table[lp->ppp_minor]->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_minor, (long) lp,(long) ippp_table[lp->ppp_minor]->lp);
+
+	ippp_table[lp->ppp_minor]->lp = NULL;	/* link is down .. set lp to NULL */
+	lp->ppp_minor = -1;			/* is this OK ?? */
+
 	return 0;
 }
 
+/*
+ * bind isdn_net_local <=> ippp-device
+ */
 int isdn_ppp_bind(isdn_net_local * lp)
 {
 	int i;
@@ -126,16 +143,31 @@
 	save_flags(flags);
 	cli();
 
-        /* 
-         * search a free device 
-         */
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		if (ippp_table[i]->state == IPPP_OPEN) {		/* OPEN, but not connected! */
-#if 0
-			printk(KERN_DEBUG "find_minor, %d lp: %08lx\n", i, (long) lp);
-#endif
-			break;
+	if(lp->pppbind < 0)	/* device bounded to ippp device ? */
+	{
+ 		isdn_net_dev *net_dev = dev->netdev;
+		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
+		memset(exclusive,0,ISDN_MAX_CHANNELS);
+		while (net_dev) {	/* step through net devices to find exclusive minors */
+			isdn_net_local *lp = &net_dev->local;
+			if(lp->pppbind >= 0)
+				exclusive[lp->pppbind] = 1;
+			net_dev = net_dev->next;
 		}
+		/*
+		 * search a free device 
+		 */
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[i]) { /* OPEN, but not connected! */
+				break;
+			}
+		}
+	}
+	else {
+		if (ippp_table[lp->pppbind]->state == IPPP_OPEN) /* OPEN, but not connected! */
+			i = lp->pppbind;
+		else
+			i = ISDN_MAX_CHANNELS;	/* trigger error */
 	}
 
 	if (i >= ISDN_MAX_CHANNELS) {
@@ -163,7 +195,12 @@
 	return lp->ppp_minor;
 }
 
-static int isdn_ppp_hangup(int minor)
+/*
+ * there was a hangup on the netdevice
+ * force wakeup of the ippp device 
+ * go into 'device waits for release' state
+ */
+static int isdn_ppp_closewait(int minor)
 {
 	if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
 		return 0;
@@ -181,9 +218,8 @@
 
 int isdn_ppp_open(int minor, struct file *file)
 {
-#if 0
-	printk(KERN_DEBUG "ippp, open, minor: %d state: %04x\n", minor,ippp_table[minor]->state);
-#endif
+	if(ippp_table[minor]->debug & 0x1)
+		printk(KERN_DEBUG "ippp, open, minor: %d state: %04x\n", minor,ippp_table[minor]->state);
 	if (ippp_table[minor]->state)
 		return -EBUSY;
 
@@ -219,6 +255,9 @@
 	return 0;
 }
 
+/*
+ * release ippp device
+ */
 void isdn_ppp_release(int minor, struct file *file)
 {
 	int i;
@@ -226,29 +265,24 @@
 	if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
 		return;
 
-#if 0
-	printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) ippp_table[minor]->lp);
-#endif
+	if(ippp_table[minor]->debug & 0x1)
+		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) ippp_table[minor]->lp);
 
 	if (ippp_table[minor]->lp) {	/* a lp address says: this link is still up */
 		isdn_net_dev *p = dev->netdev;
-		while(p) {	/* find interface for our lp; */
-			if(&p->local == ippp_table[minor]->lp)
-				break;
-			p = p->next;
-		}
-		if(!p) {
-			printk(KERN_ERR "isdn_ppp_release: Can't find device for net_local\n");
-			p = ippp_table[minor]->lp->netdev;
-		}
+		p = ippp_table[minor]->lp->netdev;
 		ippp_table[minor]->lp->ppp_minor = -1;
-		isdn_net_hangup(&p->dev); /* lp->ppp_minor==-1 => no calling of isdn_ppp_hangup() */
+		isdn_net_hangup(&p->dev); /* lp->ppp_minor==-1 => no calling of isdn_ppp_closewait() */
 		ippp_table[minor]->lp = NULL;
 	}
 	for (i = 0; i < NUM_RCV_BUFFS; i++) {
-		if (ippp_table[minor]->rq[i].buf)
+		if (ippp_table[minor]->rq[i].buf) {
 			kfree(ippp_table[minor]->rq[i].buf);
+			ippp_table[minor]->rq[i].buf = NULL;
+		}
 	}
+        ippp_table[minor]->first = ippp_table[minor]->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+        ippp_table[minor]->last = ippp_table[minor]->rq;
 
 #ifdef CONFIG_ISDN_PPP_VJ
 	slhc_free(ippp_table[minor]->slcomp);
@@ -258,6 +292,9 @@
 	ippp_table[minor]->state = 0;
 }
 
+/*
+ * get_arg .. ioctl helper
+ */
 static int get_arg(void *b, unsigned long *val)
 {
 	int r;
@@ -267,6 +304,9 @@
 	return 0;
 }
 
+/*
+ * set arg .. ioctl helper
+ */
 static int set_arg(void *b, unsigned long val)
 {
 	int r;
@@ -276,15 +316,17 @@
 	return 0;
 }
 
+/*
+ * ippp device ioctl 
+ */
 int isdn_ppp_ioctl(int minor, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
 	int r;
 
-#if 0
-	printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x",minor,cmd);
-	printk(KERN_DEBUG " state: %x\n",ippp_table[minor]->state);
-#endif
+	if(ippp_table[minor]->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",
+			minor,cmd,ippp_table[minor]->state);
 
 	if (!(ippp_table[minor]->state & IPPP_OPEN))
 		return -EINVAL;
@@ -328,8 +370,9 @@
 			return r;
 		}
 		if (val & SC_ENABLE_IP && !(ippp_table[minor]->pppcfg & SC_ENABLE_IP)) {
-			ippp_table[minor]->lp->netdev->dev.tbusy = 0;
-			mark_bh(NET_BH); /* OK .. we are ready to send the first buffer */
+			isdn_net_local *lp = ippp_table[minor]->lp;
+			lp->netdev->dev.tbusy = 0;
+			mark_bh(NET_BH); /* OK .. we are ready to send buffers */
 		}
 		ippp_table[minor]->pppcfg = val;
 		break;
@@ -354,8 +397,13 @@
 		ippp_table[minor]->maxcid = val;
 		break;
 	case PPPIOCGDEBUG:
+		if ((r = set_arg((void *) arg, ippp_table[minor]->debug)))
+			return r;
 		break;
 	case PPPIOCSDEBUG:
+		if ((r = get_arg((void *) arg, &val)))
+			return r;
+		ippp_table[minor]->debug = val;
 		break;
 	default:
 		break;
@@ -368,9 +416,8 @@
 	struct ippp_buf_queue *bf, *bl;
 	unsigned long flags;
 
-#if 0
-	printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",minor,type);
-#endif
+	if(ippp_table[minor]->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",minor,type);
 
 	if (!(ippp_table[minor]->state & IPPP_OPEN))
 		return -EINVAL;
@@ -381,6 +428,9 @@
 		cli();
 		bl = ippp_table[minor]->last;
 		bf = ippp_table[minor]->first;
+		/* 
+		 * if IPPP_NOBLOCK is set we return even if we have nothing to read 
+		 */
 		if (bf->next == bl && !(ippp_table[minor]->state & IPPP_NOBLOCK)) {
 			select_wait(&ippp_table[minor]->wq, st);
 			restore_flags(flags);
@@ -403,7 +453,7 @@
  *  fill up isdn_ppp_read() queue ..
  */
 
-static int isdn_ppp_fill_rq(char *buf, int len, int minor)
+static int isdn_ppp_fill_rq(char *buf, int len,int proto, int minor)
 {
 	struct ippp_buf_queue *bf, *bl;
 	unsigned long flags;
@@ -428,15 +478,19 @@
 		kfree(bf->buf);
 		ippp_table[minor]->first = bf;
 	}
-	bl->buf = (char *) kmalloc(len, GFP_ATOMIC);
+	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->len = len;
+	bl->len = len+4;
 
-	memcpy(bl->buf, buf, len);
+	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);
@@ -502,6 +556,7 @@
 	if (!lp)
 		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
 	else {
+		lp->huptimer = 0;
 		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
 			return 0;
 
@@ -526,11 +581,11 @@
 	        if (!(ippp_table[i] = (struct ippp_struct *)
 	                kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
 		        printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
-                        for (j = 0; j < i; j++)
-                          kfree(ippp_table[i]);
+			for (j = 0; j < i; j++)
+				kfree(ippp_table[i]);
 		        return -1;
 	        }
-                memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
+		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
 		ippp_table[i]->state = 0;
 		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
 		ippp_table[i]->last = ippp_table[i]->rq;
@@ -559,14 +614,15 @@
 
 void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
 {
-#if 0
-	printk(KERN_DEBUG "recv, skb %d\n",skb->len);
-#endif
+	if(ippp_table[lp->ppp_minor]->debug & 0x4)
+		printk(KERN_DEBUG "recv skb, len: %ld\n",skb->len);
 
 	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)
+	else if (ippp_table[lp->ppp_minor]->pppcfg & SC_REJ_COMP_AC) {
+		dev_kfree_skb(skb,FREE_WRITE);
 		return;		/* discard it silently */
+	}
 
 #ifdef CONFIG_ISDN_MPP
 	if (!(ippp_table[lp->ppp_minor]->mpppcfg & SC_REJ_MP_PROT)) {
@@ -583,11 +639,10 @@
 			isdn_net_local *lpq;
 			int sqno, min_sqno, tseq;
 			u_char BEbyte = skb->data[0];
-#if 0
-			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_minor, proto ,
-				(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
-				(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
-#endif
+			if(ippp_table[lp->ppp_minor]->debug & 0x8)
+	 			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_minor, proto ,
+					(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
+					(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
 			if (!(ippp_table[lp->ppp_minor]->mpppcfg & SC_IN_SHORT_SEQ)) {
 				sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
 				skb_pull(skb,4);
@@ -657,8 +712,9 @@
 
 				q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
 				if (!q) {
-					printk(KERN_WARNING "ippp: err, no memory !!\n");
 					net_dev->ib.modify = 0;
+					printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
+					dev_kfree_skb(skb,FREE_WRITE);
 					return;		/* discard */
 				}
 				q->skb = skb;
@@ -726,9 +782,8 @@
 		}
 	}
 
-#if 0
-	printk(KERN_DEBUG "push, skb %d %04x\n",skb->len,proto);
-#endif
+	if(ippp_table[lp->ppp_minor]->debug & 0x10)
+		printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto);
 
 	switch (proto) {
 	case PPP_IPX: /* untested */
@@ -755,6 +810,7 @@
 			if (!skb) {
 				printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
 				net_dev->local.stats.rx_dropped++;
+				dev_kfree_skb(skb_old,FREE_WRITE);
 				return;
 			}
 			skb->dev = dev;
@@ -770,16 +826,12 @@
 #else
 		printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
 		lp->stats.rx_dropped++;
+		dev_kfree_skb(skb,FREE_WRITE);
 		return;
 #endif
 		break;
 	default:
-		skb_push(skb,4);
-		skb->data[0] = 0xff;
-		skb->data[1] = 0x03;
-		skb->data[2] = (proto>>8);
-		skb->data[3] = proto & 0xff;
-		isdn_ppp_fill_rq(skb->data, skb->len, lp->ppp_minor);	/* push data to pppd device */
+		isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_minor);	/* push data to pppd device */
 		dev_kfree_skb(skb,FREE_WRITE);
 		return;
 	}
@@ -793,19 +845,32 @@
 }
 
 /*
- * send ppp frame .. we expect a PIDCOMPable proto -- 
+ * 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)
 {
-	isdn_net_dev *nd = ((isdn_net_local *) dev->priv)->netdev;
-	isdn_net_local *lp = nd->queue;
+	struct device *mdev = ((isdn_net_local *) (dev->priv) )->master;	/* get master (for redundancy) */
+	isdn_net_local *lp,*mlp;
+	isdn_net_dev *nd;
 	int proto = PPP_IP;	/* 0x21 */
-	struct ippp_struct *ipt = ippp_table[lp->ppp_minor];
-#if defined(CONFIG_ISDN_PPP_VJ) || defined(CONFIG_ISDN_MPP)
-	struct ippp_struct *ipts = ippp_table[lp->netdev->local.ppp_minor];
-#endif
+	struct ippp_struct *ipt,*ipts;
 
+	if(mdev)
+		mlp = (isdn_net_local *) (mdev->priv); 
+	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);
+		return 1;
+        }
+        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 ...
@@ -817,17 +882,16 @@
 
 /* future: step to next 'lp' when this lp is 'tbusy' */
 
-#if 0
-	printk(KERN_DEBUG  "xmit, skb %d\n",skb->len);
-#endif
+	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) {
+	if (ipt->pppcfg & SC_COMP_TCP) {	/* ipt or ipts ? -> check this again! */
 		u_char *buf = skb->data;
 		int pktlen;
 		int len = 4;
 #ifdef CONFIG_ISDN_MPP
-		if (ipt->mpppcfg & SC_MP_PROT) /* sigh */ 
+		if (ipt->mpppcfg & SC_MP_PROT) /* sigh */ 	/* ipt or ipts ?? */
 			if (ipt->mpppcfg & SC_OUT_SHORT_SEQ)
 				len += 3;
 			else
@@ -851,9 +915,8 @@
 	}
 #endif
 
-#if 0
-	printk(KERN_DEBUG  "xmit, skb %d %04x\n",skb->len,proto);
-#endif
+        if(ippp_table[lp->ppp_minor]->debug & 0x24)
+ 		printk(KERN_DEBUG  "xmit2 skb, len %ld, proto %04x\n",skb->len,proto);
 
 #ifdef CONFIG_ISDN_MPP
 	if (ipt->mpppcfg & SC_MP_PROT) {
@@ -883,15 +946,25 @@
 	skb->data[2] = proto >> 8;
 	skb->data[3] = proto & 0xff;
 
-	lp->huptimer = 0;
-	if (!(ipt->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
-		printk(KERN_INFO "isdn, xmit: Packet blocked: %d %d\n", lp->isdn_device, lp->isdn_channel);
-		return 1;
-	}
         /* tx-stats are now updated via BSENT-callback */
 	return (isdn_net_send_skb(dev , lp , skb));
 }
 
+void isdn_ppp_free_sqqueue(isdn_net_dev * p) 
+{
+	struct sqqueue *q = p->ib.sq;
+
+	p->ib.sq = NULL;
+	while(q) {
+		struct sqqueue *qn = q->next;
+		if(q->skb)
+			dev_kfree_skb(q->skb,FREE_WRITE);
+		kfree(q);
+		q = qn;
+	}
+
+}
+
 void isdn_ppp_free_mpqueue(isdn_net_dev * p)
 {
 	struct mpqueue *ql, *q = p->mp_last;
@@ -932,8 +1005,6 @@
 	nlp->next = lp;
 	p->queue = nlp;
 
-	nlp->netdev = lp->netdev;
-
 	ippp_table[nlp->ppp_minor]->unit = ippp_table[lp->ppp_minor]->unit;
 /* maybe also SC_CCP stuff */
 	ippp_table[nlp->ppp_minor]->pppcfg |= ippp_table[lp->ppp_minor]->pppcfg &
@@ -1239,4 +1310,34 @@
 #endif
 }
 
+int isdn_ppp_hangup_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+        isdn_net_dev *ndev;
+        isdn_net_local *lp;
+        struct device *sdev;
+
+	if(!(ndev = isdn_net_findif(name)))
+		return 1;
+	lp = &ndev->local;
+	if(!(lp->flags & ISDN_NET_CONNECTED))
+		return 5;
+
+	sdev = lp->slave;
+	while(sdev)
+	{
+		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+		if((mlp->flags & ISDN_NET_CONNECTED))
+			break;
+		sdev = mlp->slave;
+	}
+	if(!sdev)
+		return 2;
+
+	isdn_net_hangup(sdev);
+	return 0;
+#else
+	return -1;
+#endif
+}
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this