patch-2.2.11 linux/drivers/isdn/isdn_net.c

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

diff -u --recursive --new-file v2.2.10/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
@@ -1,8 +1,8 @@
-/* $Id: isdn_net.c,v 1.55 1998/02/23 19:38:22 fritz Exp $
+/* $Id: isdn_net.c,v 1.88 1999/07/07 10:13:31 detabc Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -20,11 +20,105 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn_net.c,v $
+ * Revision 1.88  1999/07/07 10:13:31  detabc
+ * remove unused messages
+ *
+ * Revision 1.87  1999/07/06 07:53:53  calle
+ * calls to dev_alloc_skb waste 16 bytes of memory, if we calculate the
+ * right header space for the lowlevel driver. using alloc_skb instead.
+ *
+ * Revision 1.86  1999/06/09 10:12:05  paul
+ * thinko in previous patch
+ *
+ * Revision 1.85  1999/06/07 19:42:39  paul
+ * isdn_net_getpeer() fixed to return correct `outgoing' flag
+ *
+ * Revision 1.84  1999/04/18 14:06:55  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.83  1999/04/12 12:33:23  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.82  1999/01/17 00:55:58  he
+ * added mark_bh in BCONN statcallb and cleaned up some dead code
+ *
+ * Revision 1.81  1999/01/15 16:36:52  he
+ * replaced icmp_send() by dst_link_failure()
+ *
+ * Revision 1.80  1998/12/01 13:06:22  paul
+ * Also huptimeout with dialmode == manual
+ *
+ * Revision 1.79  1998/10/30 17:55:27  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.78  1998/10/26 18:20:46  he
+ * re-inserted p=p->next in isdn_net_find_icall() (fixes kernel lock up
+ * on incoming call not matching the first interface)
+ *
+ * Revision 1.77  1998/10/23 10:18:44  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.76  1998/09/07 22:00:05  he
+ * flush method for 2.1.118 and above
+ * updated IIOCTLNETGPN
+ *
+ * Revision 1.75  1998/08/31 21:09:50  he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ *     peer phone number)
+ *
+ * Revision 1.74  1998/07/30 11:28:32  paul
+ * printk message only appeared when status is off and interface is rawIP,
+ * which is confusing for people who don't know about "isdnctrl status <if> on".
+ *
+ * Revision 1.73  1998/06/26 22:01:37  keil
+ * tx_queue_len = 5 was too small
+ *
+ * Revision 1.72  1998/06/26 15:12:31  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.71  1998/06/18 22:43:08  fritz
+ * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at cleanup.
+ *
+ * Revision 1.70  1998/06/17 19:50:49  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.69  1998/06/09 12:27:37  cal
+ * Changed default of local netdev flags: ISDN_NET_STOPPED is default now,
+ * so autodial is suppressed for that device until it is switched on using
+ * 'isdnctrl status dev-name on'.
+ *
+ *
+ *
+ * Revision 1.66  1998/05/26 22:39:24  he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
+ * Revision 1.61  1998/04/16 19:19:42  keil
+ * Fix from vger (tx max qlength)
+ *
+ * Revision 1.60  1998/04/14 16:28:49  he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.59  1998/03/07 22:37:33  fritz
+ * Bugfix: restore_flags missing.
+ *
+ * Revision 1.58  1998/03/07 18:21:05  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.57  1998/02/25 18:31:13  fritz
+ * Added debugging output in adjust_header.
+ *
+ * Revision 1.56  1998/02/25 17:49:42  he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
  * Revision 1.55  1998/02/23 19:38:22  fritz
  * Corrected check for modified feature-flags.
  *
@@ -248,9 +342,7 @@
 #include <linux/isdn.h>
 #include <net/arp.h>
 #include <net/dst.h>
-#ifndef DEV_NUMBUFFS
 #include <net/pkt_sched.h>
-#endif
 #include <linux/inetdevice.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
@@ -265,14 +357,10 @@
 /* Prototypes */
 
 int isdn_net_force_dial_lp(isdn_net_local *);
-static int isdn_net_wildmat(char *s, char *p);
 static int isdn_net_start_xmit(struct sk_buff *, struct device *);
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
-#ifdef DEV_NUMBUFFS
-static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
-#endif
 
-char *isdn_net_revision = "$Revision: 1.55 $";
+char *isdn_net_revision = "$Revision: 1.88 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -293,16 +381,21 @@
 		
 		dst_link_failure(skb);
 	}
+	else {  /* dial not triggered by rawIP packet */
+		printk(KERN_DEBUG "isdn_net: %s: %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown");
+	}
 }
 
 static void
 isdn_net_reset(struct device *dev)
 {
 #ifdef CONFIG_ISDN_X25
-	struct concap_device_ops * dops = 
+	struct concap_device_ops * dops =
 		( (isdn_net_local *) dev->priv ) -> dops;
-	struct concap_proto * cprot = 
-		( (isdn_net_local *) dev->priv ) -> netdev -> cprot; 
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
 #endif
 	ulong flags;
 
@@ -311,7 +404,7 @@
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 #ifdef CONFIG_ISDN_X25
-	if( cprot && cprot -> pops && dops ) 
+	if( cprot && cprot -> pops && dops )
 		cprot -> pops -> restart ( cprot, dev, dops );
 #endif
 	restore_flags(flags);
@@ -338,7 +431,7 @@
 		if (ifa != NULL)
 			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
 	}
- 
+
 	/* If this interface has slaves, start them also */
 
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
@@ -362,6 +455,7 @@
 
 	save_flags(flags);
 	cli();
+	lp->flags |= ISDN_NET_CONNECTED;
 	lp->isdn_device = dev->drvmap[idx];
 	lp->isdn_channel = dev->chanmap[idx];
 	dev->rx_netdev[idx] = lp->netdev;
@@ -387,10 +481,6 @@
 		dev_kfree_skb(lp->sav_skb);
 		lp->sav_skb = NULL;
 	}
-#ifdef DEV_NUMBUFFS
-	if (!lp->master)        /* purge only for master device */
-		dev_purge_queues(&lp->netdev->dev);
-#else
 	if (!lp->master) {	/* reset only master device */
 		/* Moral equivalent of dev_purge_queues():
 		   BEWARE! This chunk of code cannot be called from hardware
@@ -398,7 +488,6 @@
 		 */
 		qdisc_reset(lp->netdev->dev.qdisc);
 	}
-#endif
 	lp->dialstate = 0;
 	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
 	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
@@ -444,7 +533,13 @@
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
 			anymore = 1;
 			l->huptimer++;
-			if ((l->onhtime) && (l->huptimer > l->onhtime)) {
+			/*
+			 * if there is some dialmode where timeout-hangup
+			 * should _not_ be done, check for that here
+			 */
+			if ((l->onhtime) &&
+			    (l->huptimer > l->onhtime))
+			{
 				if (l->hupflags & ISDN_MANCHARGE &&
 				    l->hupflags & ISDN_CHARGEHUP) {
 					while (jiffies - l->chargetime > l->chargeint)
@@ -469,6 +564,11 @@
 				} else if (l->hupflags & ISDN_INHUP)
 					isdn_net_hangup(&p->dev);
 			}
+
+			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
+				isdn_net_hangup(&p->dev);
+				break;
+			}
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -487,7 +587,7 @@
 {
 	isdn_net_dev *p = dev->st_netdev[idx];
 	int cmd = c->command;
-	
+
 	if (p) {
 		isdn_net_local *lp = p->local;
 #ifdef CONFIG_ISDN_X25
@@ -538,23 +638,12 @@
 				   failed. If there are generic encap protocol
 				   receiver routines signal the closure of
 				   the link*/
-				
-				if( !(lp->flags & ISDN_NET_CONNECTED) 
+
+				if( !(lp->flags & ISDN_NET_CONNECTED)
 				    && pops && pops -> disconn_ind )
 					pops -> disconn_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
-					lp->flags &= ~ISDN_NET_CONNECTED;
-					if (lp->first_skb) {
-						dev_kfree_skb(lp->first_skb);
-						lp->first_skb = NULL;
-					}
-					if (lp->sav_skb) {
-						dev_kfree_skb(lp->sav_skb);
-						lp->sav_skb = NULL;
-					}
-					isdn_free_channel(lp->isdn_device, lp->isdn_channel,
-							  ISDN_USAGE_NET);
 #ifdef CONFIG_ISDN_PPP
 					isdn_ppp_free(lp);
 #endif
@@ -562,10 +651,7 @@
 					printk(KERN_INFO "%s: remote hangup\n", lp->name);
 					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
 					       lp->charge);
-					lp->isdn_device = -1;
-					lp->isdn_channel = -1;
-					dev->st_netdev[idx] = NULL;
-					dev->rx_netdev[idx] = NULL;
+					isdn_net_unbind_channel(lp);
 					return 1;
 				}
 				break;
@@ -607,6 +693,11 @@
 						lp->chargetime = jiffies;
 						printk(KERN_DEBUG "isdn_net: chargetime of %s now %d\n",
 						lp->name, lp->chargetime);
+
+						/* reset dial-timeout */
+						lp->dialstarted = 0;
+						lp->dialwait_timer = 0;
+
 						/* Immediately send first skb to speed up arp */
 #ifdef CONFIG_ISDN_PPP
 						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
@@ -617,13 +708,13 @@
 						if( pops )
 							if( pops->connect_ind)
 								pops->connect_ind(cprot);
-
 #endif /* CONFIG_ISDN_X25 */
 						if (lp->first_skb) {
-							
+
 							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
 								lp->first_skb = NULL;
-						} else {
+						}
+						else {
 							/*
 							 * dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb).
 							 * With an empty lp->first_skb, we need to do this ourselves
@@ -721,6 +812,13 @@
 					break;
 				}
 				anymore = 1;
+
+				if(lp->dialtimeout > 0)
+					if(lp->dialstarted == 0 || jiffies > (lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
+						lp->dialstarted = jiffies;
+						lp->dialwait_timer = 0;
+					}
+
 				lp->dialstate++;
 				/* Fall through */
 			case 2:
@@ -735,12 +833,22 @@
 				lp->dialretry = 0;
 				anymore = 1;
 				lp->dialstate++;
-				/* Falls through */
+				/* Fall through */
 			case 3:
 				/* Setup interface, dial current phone-number, switch to next number.
 				 * If list of phone-numbers is exhausted, increment
 				 * retry-counter.
 				 */
+				if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
+					char *s;
+					if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+						s = "dial suppressed: isdn system stopped";
+					else
+						s = "dial suppressed: dialmode `off'";
+					isdn_net_unreachable(&p->dev, lp->first_skb, s);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
 				cmd.driver = lp->isdn_device;
 				cmd.command = ISDN_CMD_SETL2;
 				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
@@ -765,6 +873,16 @@
 					lp->dialstate = 4;
 					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
 				} else {
+					if(lp->dialtimeout > 0)
+						if(jiffies > (lp->dialstarted + lp->dialtimeout)) {
+							restore_flags(flags);
+							lp->dialwait_timer = jiffies + lp->dialwait;
+							lp->dialstarted = 0;
+							isdn_net_unreachable(&p->dev, lp->first_skb, "dial: timed out");
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+
 					sprintf(cmd.parm.setup.phone, "%s", lp->dial->num);
 					/*
 					 * Switch to next number or back to start if at end of list.
@@ -772,6 +890,17 @@
 					if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
 						lp->dial = lp->phone[1];
 						lp->dialretry++;
+
+						if (lp->dialretry > lp->dialmax) {
+							restore_flags(flags);
+							if (lp->dialtimeout == 0) {
+								lp->dialwait_timer = jiffies + lp->dialwait;
+								lp->dialstarted = 0;
+								isdn_net_unreachable(&p->dev, lp->first_skb, "dial: tried all numbers dialmax times");
+							}
+							isdn_net_hangup(&p->dev);
+							break;
+						}
 					}
 					restore_flags(flags);
 					cmd.driver = lp->isdn_device;
@@ -786,7 +915,7 @@
 						isdn_info_update();
 					}
 					printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
-					       lp->dialretry - 1, cmd.parm.setup.phone);
+					       lp->dialretry, cmd.parm.setup.phone);
 					lp->dtimer = 0;
 #ifdef ISDN_DEBUG_NET_DIAL
 					printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
@@ -810,15 +939,11 @@
 				break;
 			case 4:
 				/* Wait for D-Channel-connect.
-				 * If timeout and max retries not
-				 * reached, switch back to state 3.
+				 * If timeout, switch back to state 3.
+				 * Dialmax-handling moved to state 3.
 				 */
-				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) {
-					if (lp->dialretry < lp->dialmax) {
-						lp->dialstate = 3;
-					} else
-						isdn_net_hangup(&p->dev);
-				}
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
 				anymore = 1;
 				break;
 			case 5:
@@ -895,7 +1020,8 @@
 				/* Remote does callback. Hangup after cbdelay, then wait for incoming
 				 * call (in state 4).
 				 */
-				if (lp->dtimer++ > lp->cbdelay) {
+				if (lp->dtimer++ > lp->cbdelay)
+				{
 					printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
 					lp->dtimer = 0;
 					lp->dialstate = 4;
@@ -930,7 +1056,6 @@
 #endif
 
 	if (lp->flags & ISDN_NET_CONNECTED) {
-		lp->flags &= ~ISDN_NET_CONNECTED;
 		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
@@ -938,7 +1063,7 @@
 #ifdef CONFIG_ISDN_X25
 		/* try if there are generic encap protocol
 		   receiver routines and signal the closure of
-		   the link */		
+		   the link */
 		if( pops && pops -> disconn_ind )
 		  pops -> disconn_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
@@ -968,7 +1093,7 @@
 	char addinfo[100];
 
 	addinfo[0] = '\0';
-	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */ 
+	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
 	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
 		/* fall back to old isdn_net_log_packet method() */
 		char * buf = skb->data;
@@ -1062,20 +1187,17 @@
 	if (ret == len) {
 		lp->transcount += len;
 		clear_bit(0, (void *) &(ndev->tbusy));
-		mark_bh(NET_BH);
 		return 0;
 	}
 	if (ret < 0) {
 		dev_kfree_skb(skb);
 		lp->stats.tx_errors++;
 		clear_bit(0, (void *) &(ndev->tbusy));
-		mark_bh(NET_BH);
 		return 0;
 	}
 	return 1;
 }
 
-
 /*
  *  Helper function for isdn_net_start_xmit.
  *  When called, the connection is already established.
@@ -1150,9 +1272,11 @@
 	if (!skb)
 		return;
 	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-		ulong pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
-		if (pullsize)
+		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+		if (pullsize > 0) {
+			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
 			skb_pull(skb, pullsize);
+		}
 	}
 }
 
@@ -1166,7 +1290,7 @@
 {
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
 #ifdef CONFIG_ISDN_X25
-	struct concap_proto * cprot = lp -> netdev -> cprot; 
+	struct concap_proto * cprot = lp -> netdev -> cprot;
 #endif
 
 	if (ndev->tbusy) {
@@ -1179,7 +1303,7 @@
 	ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */
 #ifdef CONFIG_ISDN_X25
 /* At this point hard_start_xmit() passes control to the encapsulation
-   protocol (if present). 
+   protocol (if present).
    For X.25 auto-dialing is completly bypassed because:
    - It does not conform with the semantics of a reliable datalink
      service as needed by X.25 PLP.
@@ -1205,17 +1329,46 @@
 #endif
 		if (!(lp->flags & ISDN_NET_CONNECTED)) {
 			int chi;
+			/* only do autodial if allowed by config */
+			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+				dev_kfree_skb(skb);
+				ndev->tbusy = 0;
+				return 0;
+			}
 			if (lp->phone[1]) {
 				ulong flags;
 				save_flags(flags);
 				cli();
+
+				if(lp->dialwait_timer <= 0)
+					if(lp->dialstarted > 0 && lp->dialtimeout > 0 && jiffies < lp->dialstarted + lp->dialtimeout + lp->dialwait)
+						lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+				if(lp->dialwait_timer > 0) {
+					if(jiffies < lp->dialwait_timer) {
+						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+						dev_kfree_skb(skb);
+						ndev->tbusy = 0;
+						restore_flags(flags);
+						return 0;
+					} else
+						lp->dialwait_timer = 0;
+				}
+
 				/* Grab a free ISDN-Channel */
-				if ((chi =
+				if (((chi =
 				     isdn_get_free_channel(ISDN_USAGE_NET,
 							   lp->l2_proto,
 							   lp->l3_proto,
 							   lp->pre_device,
-						 lp->pre_channel)) < 0) {
+						 lp->pre_channel)) < 0) &&
+					((chi =
+				     isdn_get_free_channel(ISDN_USAGE_NET,
+							   lp->l2_proto,
+							   lp->l3_proto,
+							   lp->pre_device,
+						 lp->pre_channel^1)) < 0)) {
 					restore_flags(flags);
 					isdn_net_unreachable(ndev, skb,
 							   "No channel");
@@ -1227,7 +1380,6 @@
 				if (dev->net_verbose)
 					isdn_net_log_skb(skb, lp);
 				lp->dialstate = 1;
-				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
 				isdn_net_bind_channel(lp, chi);
 #ifdef CONFIG_ISDN_PPP
@@ -1290,8 +1442,8 @@
 {
 	struct device *p;
 #ifdef CONFIG_ISDN_X25
-	struct concap_proto * cprot = 
-		( (isdn_net_local *) dev->priv ) -> netdev -> cprot; 
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
 	/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
 #endif
 
@@ -1304,9 +1456,9 @@
 		/* If this interface has slaves, stop them also */
 		while (p) {
 #ifdef CONFIG_ISDN_X25
-			cprot = ( (isdn_net_local *) p->priv ) 
-				-> netdev -> cprot; 
-			if( cprot && cprot -> pops ) 
+			cprot = ( (isdn_net_local *) p->priv )
+				-> netdev -> cprot;
+			if( cprot && cprot -> pops )
 				cprot -> pops -> close( cprot );
 #endif
 			isdn_net_hangup(p);
@@ -1387,12 +1539,12 @@
 isdn_net_slarp_send(isdn_net_local *lp, int is_reply)
 {
 	unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
-	struct sk_buff *skb = dev_alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp));
+	struct sk_buff *skb = alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp), GFP_ATOMIC);
 	unsigned long t = (jiffies / HZ * 1000000);
 	int len;
 	cisco_hdr *ch;
 	cisco_slarp *s;
-	
+
 	if (!skb) {
 		printk(KERN_WARNING
 		       "%s: Could not allocate SLARP reply\n", lp->name);
@@ -1406,7 +1558,7 @@
 	s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp));
 	if (is_reply) {
 		s->code = htonl(CISCO_SLARP_REPLY);
- 		memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
+		memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
 		memset(&s->slarp.reply.netmask, 0, sizeof(__u32));
 	} else {
 		lp->cisco_myseq++;
@@ -1589,7 +1741,7 @@
 		default:
 #ifdef CONFIG_ISDN_X25
 		  /* try if there are generic sync_device receiver routines */
-			if(cprot) if(cprot -> pops) 
+			if(cprot) if(cprot -> pops)
 				if( cprot -> pops -> data_ind){
 					cprot -> pops -> data_ind(cprot,skb);
 					return;
@@ -1600,6 +1752,7 @@
 			kfree_skb(skb);
 			return;
 	}
+
 	netif_rx(skb);
 	return;
 }
@@ -1788,16 +1941,12 @@
 	ndev->type = ARPHRD_ETHER;
 	ndev->addr_len = ETH_ALEN;
 
-	ndev->tx_queue_len = 10; /* for clients without MPPP 5 is better.  */
+	/* for clients with MPPP maybe higher values better */
+	ndev->tx_queue_len = 30;
 
 	for (i = 0; i < ETH_ALEN; i++)
 		ndev->broadcast[i] = 0xff;
 
-#ifdef DEV_NUMBUFFS
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&ndev->buffs[i]);
-#endif
-
 	/* The ISDN-specific entries in the device structure. */
 	ndev->open = &isdn_net_open;
 	ndev->hard_start_xmit = &isdn_net_start_xmit;
@@ -1813,86 +1962,15 @@
 				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
 
 	ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
-
 	ndev->stop = &isdn_net_close;
 	ndev->get_stats = &isdn_net_get_stats;
 	ndev->rebuild_header = &isdn_net_rebuild_header;
-
 #ifdef CONFIG_ISDN_PPP
 	ndev->do_ioctl = isdn_ppp_dev_ioctl;
 #endif
 	return 0;
 }
 
-/*
- * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
- * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
- */
-
-static int
-isdn_net_Star(char *s, char *p)
-{
-	while (isdn_net_wildmat(s, p) == 0)
-		if (*++s == '\0')
-			return (0);
-	return (1);
-}
-
-/*
- * Shell-type Pattern-matching for incoming caller-Ids
- * This function gets a string in s and checks, if it matches the pattern
- * given in p. It returns 1 on success, 0 otherwise.
- *
- * Possible Patterns:
- *
- * '?'     matches one character
- * '*'     matches zero or more characters
- * [xyz]   matches the set of characters in brackets.
- * [^xyz]  matches any single character not in the set of characters
- */
-
-static int
-isdn_net_wildmat(char *s, char *p)
-{
-	register int last;
-	register int matched;
-	register int reverse;
-
-	for (; *p; s++, p++)
-		switch (*p) {
-			case '\\':
-				/*
-				 * Literal match with following character,
-				 * fall through.
-				 */
-				p++;
-			default:
-				if (*s != *p)
-					return (0);
-				continue;
-			case '?':
-				/* Match anything. */
-				if (*s == '\0')
-					return (0);
-				continue;
-			case '*':
-				/* Trailing star matches everything. */
-				return (*++p ? isdn_net_Star(s, p) : 1);
-			case '[':
-				/* [^....] means inverse character class. */
-				if ((reverse = (p[1] == '^')))
-					p++;
-				for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
-					/* This next line requires a good C compiler. */
-					if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
-						matched = 1;
-				if (matched == reverse)
-					return (0);
-				continue;
-		}
-	return (*s == '\0');
-}
-
 static void
 isdn_net_swapbind(int drvidx)
 {
@@ -1945,6 +2023,8 @@
  *               2 = Reject call, wait cbdelay, then call back
  *               3 = Reject call
  *               4 = Wait cbdelay, then call back
+ *               5 = No appropriate interface for this call,
+ *                   would eventually match if CID was longer.
  */
 int
 isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
@@ -1953,6 +2033,7 @@
 	int si1;
 	int si2;
 	int ematch;
+	int wret;
 	int swapped;
 	int sidx = 0;
 	isdn_net_dev *p;
@@ -1987,13 +2068,13 @@
 	}
 	n = (isdn_net_phone *) 0;
 	p = dev->netdev;
-	ematch = 0;
+	ematch = wret = swapped = 0;
 #ifdef ISDN_DEBUG_NET_ICALL
 	printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
 	       dev->usage[idx]);
 #endif
-	swapped = 0;
 	while (p) {
+		int matchret;
 		isdn_net_local *lp = p->local;
 
 		/* If last check has triggered as binding-swap, revert it */
@@ -2006,18 +2087,22 @@
 				break;
 		}
 		swapped = 0;
-		if (!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz))
+		if (!(matchret = isdn_wildmat(eaz, isdn_map_eaz2msn(lp->msn, di))))
 			ematch = 1;
+		/* Remember if more numbers eventually can match */
+		if (matchret > wret)
+			wret = matchret;
 #ifdef ISDN_DEBUG_NET_ICALL
 		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
 		       lp->name, lp->msn, lp->flags, lp->dialstate);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) &&	/* EAZ is matching   */
-		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
-		      (USG_NONE(dev->usage[idx]))) ||	/* and ch. unused or */
-		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) &&	/* if dialing        */
-		       (!(lp->flags & ISDN_NET_CALLBACK)))	/* but no callback   */
-		     ))) {
+		if ((!matchret) &&                                        /* EAZ is matching   */
+		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&              /* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||                     /* and ch. unused or */
+		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing        */
+		       (!(lp->flags & ISDN_NET_CALLBACK)))                /* but no callback   */
+		     )))
+			 {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
 			       lp->pre_device, lp->pre_channel);
@@ -2085,8 +2170,6 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
 #endif
-						p = (isdn_net_dev *) p->next;
-						continue;
 					}
 				}
 			}
@@ -2096,7 +2179,7 @@
 			n = lp->phone[0];
 			if (lp->flags & ISDN_NET_SECURE) {
 				while (n) {
-					if (isdn_net_wildmat(nr, n->num))
+					if (!isdn_wildmat(nr, n->num))
 						break;
 					n = (isdn_net_phone *) n->next;
 				}
@@ -2105,7 +2188,21 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 				printk(KERN_DEBUG "n_fi: match3\n");
 #endif
-				/* Here we got an interface matched, now see if it is up.
+				/* matching interface found */
+
+				/*
+				 * Is the state STOPPED?
+				 * If so, no dialin is allowed,
+				 * so reject actively.
+				 * */
+				if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+					restore_flags(flags);
+					printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/*
+				 * Is the interface up?
 				 * If not, reject the call actively.
 				 */
 				if (!p->dev.start) {
@@ -2140,6 +2237,17 @@
 				}
 				if (lp->flags & ISDN_NET_CALLBACK) {
 					int chi;
+					/*
+					 * Is the state MANUAL?
+					 * If so, no callback can be made,
+					 * so reject actively.
+					 * */
+					if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+						restore_flags(flags);
+						printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+						       lp->name);
+						return 3;
+					}
 					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
 					       lp->name, nr, eaz);
 					if (lp->phone[1]) {
@@ -2155,7 +2263,6 @@
 						/* Setup dialstate. */
 						lp->dtimer = 0;
 						lp->dialstate = 11;
-						lp->flags |= ISDN_NET_CONNECTED;
 						/* Connect interface with channel */
 						isdn_net_bind_channel(lp, chi);
 #ifdef CONFIG_ISDN_PPP
@@ -2217,10 +2324,10 @@
 		p = (isdn_net_dev *) p->next;
 	}
 	/* If none of configured EAZ/MSN matched and not verbose, be silent */
-	if (ematch || dev->net_verbose)
+	if (!ematch || dev->net_verbose)
 		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
 	restore_flags(flags);
-	return 0;
+	return (wret == 2)?5:0;
 }
 
 /*
@@ -2253,6 +2360,7 @@
 			ulong flags;
 			save_flags(flags);
 			cli();
+
 			/* Grab a free ISDN-Channel */
 			if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
 							 lp->l3_proto,
@@ -2263,7 +2371,6 @@
 				return -EAGAIN;
 			}
 			lp->dialstate = 1;
-			lp->flags |= ISDN_NET_CONNECTED;
 			/* Connect interface with channel */
 			isdn_net_bind_channel(lp, chi);
 #ifdef CONFIG_ISDN_PPP
@@ -2285,6 +2392,20 @@
 }
 
 /*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_local * lp)
+{
+	/* is there a better error code? */
+	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
+
+	return isdn_net_force_dial_lp(lp);
+}
+
+/*
  * Force a net-interface to dial out.
  * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
  */
@@ -2383,8 +2504,13 @@
 	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
 	   of those who forget configuring this */
 	netdev->local->dialmax = 1;
-	netdev->local->flags = ISDN_NET_CBHUP;	/* Hangup before Callback */
+	netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;	/* Hangup before Callback, manual dial */
 	netdev->local->cbdelay = 25;	/* Wait 5 secs before Callback */
+	netdev->local->dialtimeout = -1;  /* Infinite Dial-Timeout */
+	netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+	netdev->local->dialstarted = 0;   /* Jiffies of last dial-start */
+	netdev->local->dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
+
 	/* Put into to netdev-chain */
 	netdev->next = (void *) dev->netdev;
 	dev->netdev = netdev;
@@ -2465,7 +2591,7 @@
 			save_flags(flags);
 			cli(); /* avoid races with incoming events trying to
 				  call cprot->pops methods */
-			if( cprot && cprot -> pops ) 
+			if( cprot && cprot -> pops )
 				cprot -> pops -> proto_del ( cprot );
 			p -> cprot = NULL;
 			lp -> dops = NULL;
@@ -2479,7 +2605,7 @@
 			p -> cprot = isdn_concap_new( cfg -> p_encap );
 			/* p -> cprot == NULL now if p_encap is not supported
 			   by means of the concap_proto mechanism */
-			/* the protocol is not configured yet; this will 
+			/* the protocol is not configured yet; this will
 			   happen later when isdn_net_reset() is called */
 #endif
 		}
@@ -2508,7 +2634,7 @@
 			if( cfg->p_encap >= 0 &&
 			    cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
 				break;
-			printk(KERN_WARNING 
+			printk(KERN_WARNING
 			       "%s: encapsulation protocol %d not supported\n",
 			       p->local->name, cfg->p_encap);
 			return -EINVAL;
@@ -2583,6 +2709,8 @@
 		lp->triggercps = cfg->triggercps;
 		lp->slavedelay = cfg->slavedelay * HZ;
 		lp->pppbind = cfg->pppbind;
+		lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+		lp->dialwait = cfg->dialwait * HZ;
 		if (cfg->secure)
 			lp->flags |= ISDN_NET_SECURE;
 		else
@@ -2604,6 +2732,16 @@
 				lp->flags &= ~ISDN_NET_CALLBACK;
 				break;
 		}
+		lp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
+		if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+			/* old isdnctrl version, where only 0 or 1 is given */
+			printk(KERN_WARNING
+			     "Old isdnctrl version detected! Please update.\n");
+			lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
+		}
+		else {
+			lp->flags |= cfg->dialmode;  /* turn on selected bits */
+		}
 		if (cfg->chargehup)
 			lp->hupflags |= ISDN_CHARGEHUP;
 		else
@@ -2671,6 +2809,7 @@
 		if (lp->flags & ISDN_NET_CBOUT)
 			cfg->callback = 2;
 		cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+		cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
 		cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
 		cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
 		cfg->cbdelay = lp->cbdelay;
@@ -2680,6 +2819,8 @@
 		cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
 		    (lp->chargeint / HZ) : 0;
 		cfg->pppbind = lp->pppbind;
+		cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
+		cfg->dialwait = lp->dialwait / HZ;
 		if (lp->slave)
 			strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
 		else
@@ -2749,9 +2890,37 @@
 }
 
 /*
- * Delete a phone-number from an interface.
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
  */
+int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int ch, dv, idx;
 
+	if (!p) return -ENODEV;
+	/*
+	 * Theoretical race: while this executes, the remote number might
+	 * become invalid (hang up) or change (new connection), resulting
+         * in (partially) wrong number copied to user. This race
+	 * currently ignored.
+	 */
+	ch = p->local->isdn_channel;
+	dv = p->local->isdn_device;
+	if(ch<0 && dv<0) return -ENOTCONN;
+	idx = isdn_dc2minor(dv, ch);
+	if (idx<0) return -ENODEV;
+	/* for pre-bound channels, we need this extra check */
+	if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN;
+	strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN);
+	phone->outgoing=USG_OUTGOING(dev->usage[idx]);
+	if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
+	return 0;
+}
+/*
+ * Delete a phone-number from an interface.
+ */
 int
 isdn_net_delphone(isdn_net_ioctl_phone * phone)
 {
@@ -2957,21 +3126,3 @@
 	restore_flags(flags);
 	return 0;
 }
-
-#ifdef DEV_NUMBUFFS
-/*
- * helper function to flush device queues
- * the better place would be net/core/dev.c
- */
-static void
-dev_purge_queues(struct device *dev)
-{
-	int i;
-	for (i = 0; i < DEV_NUMBUFFS; i++) {
-		struct sk_buff *skb;
-		while ((skb = skb_dequeue(&dev->buffs[i])))
-			dev_kfree_skb(skb);
-	}
-
-}
-#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)