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

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

diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
@@ -1,8 +1,8 @@
-/* $Id: isdn_net.c,v 1.47 1997/06/21 10:52:05 fritz Exp $
+/* $Id: isdn_net.c,v 1.48.2.27 1998/11/05 22:11:53 fritz 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)
  *
@@ -21,6 +21,135 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.48.2.27  1998/11/05 22:11:53  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.48.2.26  1998/11/03 14:54:39  fritz
+ * Applied callback-patch fur bundled RAW-IP by gvz@popocate.hamburg.pop.de
+ *
+ * Revision 1.48.2.25  1998/11/03 14:31:05  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.48.2.24  1998/10/25 22:08:22  fritz
+ * Bugfix: Only first number was dialed.
+ *
+ * Revision 1.48.2.23  1998/10/25 17:42:18  fritz
+ * Bugfix: added missing reset of connect-flag.
+ *
+ * Revision 1.48.2.22  1998/10/25 15:48:13  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
+ * Revision 1.48.2.21  1998/10/23 10:14:02  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.48.2.20  1998/08/03 15:52:00  paul
+ * various changes from 2.0.3[45] kernel sources, as suggested by
+ * Oliver.Lauer@coburg.baynet.de
+ *
+ * Revision 1.48.2.19  1998/07/30 11:29: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.48.2.18  1998/06/29 17:08:20  cal
+ * applied small TimRu-patch by Oliver Lauer
+ *
+ * Revision 1.48.2.17  1998/06/26 22:00:47  keil
+ * tx_queue_len = 5 was too small
+ *
+ * Revision 1.48.2.16  1998/06/09 12:24:40  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.48.2.15  1998/06/07 13:47:51  fritz
+ * ABC cleanup
+ *
+ * Revision 1.48.2.13  1998/05/22 10:13:07  detabc
+ * in case of a icmp-unreach condition the tcp-keepalive-entrys
+ * will be dropped from the internal double-link-list (only abc-extension).
+ * send icmp unreach only if the skb->protocol == ETH_P_IP
+ *
+ * Revision 1.48.2.12  1998/05/21 09:23:56  detabc
+ * speedup abc-no-dchannel-redial
+ *
+ * Revision 1.48.2.11  1998/05/07 19:54:53  detabc
+ * bugfix in abc_delayed_hangup
+ * optimize keepalive-tests for abc_rawip
+ *
+ * Revision 1.48.2.10  1998/05/06 08:34:04  detabc
+ * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.)
+ * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.)
+ * drop all new packets and send ICMP_NET_UNREACH for
+ * min. dialwait to max. dialwait * 6 time. (only abc-ext.)
+ * change random-deliver of packets (small first) from all emcapsulation
+ * to only rawip with ABC-Router-Flag enabled.
+ *
+ * Revision 1.48.2.9  1998/05/03 17:48:22  detabc
+ * remove unused dev->tbusy = 1 line (only abc-extension)
+ *
+ * Revision 1.48.2.8  1998/04/28 15:11:55  detabc
+ * fixed the wrong #ifndef CONFIG_ISDN_WITH_ABC define
+ *
+ * Revision 1.48.2.7  1998/04/26 11:24:08  detabc
+ * add abc_delayed_hangup (only with a spezial udp-packet)
+ * move abc-compress and -crypt from start of transmit to the
+ * isdn_net_send_skb() function (better for TIMRU and the work is much easyer).
+ *
+ * added the abc_tx_queue's in the isdn_net_send_skb().
+ * give small-packets a high priority.
+ * transmit small packest first.
+ * NOTE: NOTE: NOTE:
+ * At now with the ABC-EXTENSION will be deliver the pakets in RANDOM-ORDER.
+ * Please let me know if this a problem.
+ *
+ * Revision 1.48.2.6  1998/04/18 17:55:09  detabc
+ * dropp packets if call's are disabled (only abc-extension)
+ * add secure callback (only abc-extension)
+ * this means: if you are the callback-out-side and the remote
+ * dont reject the call ?????
+ * in this case the connection is ok !!! but you pay the connection !!!!
+ * now this will be a configerror and the connection will be dropped .
+ * also a new call will be disabled for 4 hours.
+ * incouming-calls are still possible.
+ *
+ * Revision 1.48.2.5  1998/04/16 19:24:51  keil
+ * Fix from vger (tx max qlength)
+ *
+ * Revision 1.48.2.4  1998/03/20 12:17:27  detabc
+ * merge abc-extension with timru-time-rules
+ * christian please check my changes in the CONFIG_ISDN_TIMEOUT_RULES sources
+ * please ! think about:
+ * behind the function isdn_abc_net_start_xmit(), is the first one behind
+ * the kernel-driver, the paket will be compressed an/or crypted. In this
+ * case no information availible in the skb->data area.
+ *
+ * Fritz !! Please read my remarks in the funktion isdn_net_unreachable() !
+ *
+ * Revision 1.48.2.3  1998/03/16 09:55:51  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
+ * Revision 1.48.2.2  1998/03/07 23:35:09  detabc
+ * added the abc-extension to the linux isdn-kernel
+ * for kernel-version 2.0.xx
+ * DO NOT USE FOR HIGHER KERNELS-VERSIONS
+ * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
+ * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
+ *
+ * you need also a modified isdnctrl-source the switch on the
+ * features of the abc-extension
+ *
+ * please use carefully. more detail will be follow.
+ * thanks
+ *
+ * Revision 1.48.2.1  1997/08/21 15:56:07  fritz
+ * Synchronized 2.0.X branch with 2.0.31-pre7
+ *
+ * Revision 1.48  1997/06/22 11:57:15  fritz
+ * Added ability to adjust slave triggerlevel.
+ *
  * Revision 1.47  1997/06/21 10:52:05  fritz
  * Removed wrong SET_SKB_FREE in isdn_net_send_skb()
  *
@@ -30,6 +159,7 @@
  * Revision 1.45  1997/06/10 16:24:22  hipp
  * hard_header changes for syncPPP (now behaves like RAWIP)
  *
+
  * Revision 1.44  1997/05/27 15:17:26  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -208,7 +338,9 @@
 #include <net/icmp.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
 #include "isdn_ppp.h"
+#endif
 
 /* Prototypes */
 
@@ -218,7 +350,7 @@
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
 static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 
-char *isdn_net_revision = "$Revision: 1.47 $";
+char *isdn_net_revision = "$Revision: 1.48.2.27 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -227,14 +359,46 @@
 static void
 isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason)
 {
-	printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n",
-	       dev->name, reason);
-	if(skb->protocol==htons(ETH_P_IP))
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
+	int i;
+
+	if(skb != NULL) {
+	
+		u_short proto = ntohs(skb->protocol);
+
+		printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown",
+			   (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "" );
+
+		if(proto == ETH_P_IP) {
+
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN, 0
+#if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
+				,dev
+#endif
+				);
+		}
+	}
+	else {  /* dial not triggered by rawIP packet */
+		printk(KERN_DEBUG "isdn_net: %s: %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown");
+	}
+
+	for(i = 0; i < DEV_NUMBUFFS; i++) {
+		struct sk_buff *skb;
+
+		while((skb = skb_dequeue(&dev->buffs[i]))) {
+				if(ntohs(skb->protocol) == ETH_P_IP) {
+					icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
 #if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
-		  ,dev
+					, dev
 #endif
-	    );
+					);
+				}
+				dev_kfree_skb(skb, FREE_WRITE);
+        	}
+	}
 }
 
 static void
@@ -293,6 +457,21 @@
 	restore_flags(flags);
 }
 
+static inline void
+isdn_net_unbind_ptr_idx(int idx)
+{
+	if (idx != -1) {
+		dev->rx_netdev[idx] = NULL;
+		dev->st_netdev[idx] = NULL;
+	}
+}
+
+static inline void
+isdn_net_unbind_ptr(int drv, int ch)
+{
+	isdn_net_unbind_ptr_idx(isdn_dc2minor(drv, ch));
+}
+
 /*
  * unbind a net-interface (resets interface after an error)
  */
@@ -314,9 +493,10 @@
 	if (!lp->master)        /* purge only for master device */
 		dev_purge_queues(&lp->netdev->dev);
 	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;
-	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+	if (lp->isdn_device != -1 && lp->isdn_device != -1) {
+		isdn_net_unbind_ptr(lp->isdn_device, lp->isdn_channel);
+		isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+	}
 	lp->flags &= ~ISDN_NET_CONNECTED;
 	lp->isdn_device = -1;
 	lp->isdn_channel = -1;
@@ -358,7 +538,13 @@
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
 			anymore = 1;
 			l->huptimer++;
-			if ((l->onhtime) && (l->huptimer > l->onhtime))
+ 			/*
+ 			 * only do timeout-hangup
+ 			 * if interface is configured as AUTO
+ 			 */
+  			if ((ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_AUTO) &&
+ 			    (l->onhtime) &&
+ 			    (l->huptimer > l->onhtime))
 				if (l->hupflags & ISDN_MANCHARGE &&
 				    l->hupflags & ISDN_CHARGEHUP) {
 					while (jiffies - l->chargetime > l->chargeint)
@@ -382,6 +568,12 @@
 						isdn_net_hangup(&p->dev);
 				} else if (l->hupflags & ISDN_INHUP)
 					isdn_net_hangup(&p->dev);
+
+
+			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(p->local) == ISDN_NET_DM_OFF)) {
+				isdn_net_hangup(&p->dev);
+				break;
+			}
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -396,12 +588,14 @@
  * Return: 1 = Event handled, 0 = not for us or unknown Event.
  */
 int
-isdn_net_stat_callback(int idx, int cmd)
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
 {
 	isdn_net_dev *p = dev->st_netdev[idx];
 
 	if (p) {
 		isdn_net_local *lp = &(p->local);
+		int cmd = c->command;
+
 		switch (cmd) {
 			case ISDN_STAT_BSENT:
 				/* A packet has successfully been sent out */
@@ -434,35 +628,55 @@
 						lp->dialstate++;
 						return 1;
 					case 12:
+					case 13:
 						lp->dialstate = 5;
 						return 1;
 				}
 				break;
 			case ISDN_STAT_DHUP:
 				/* Either D-Channel-hangup or error during dialout */
-				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
-					lp->flags &= ~ISDN_NET_CONNECTED;
-					if (lp->first_skb) {
-						dev_kfree_skb(lp->first_skb, FREE_WRITE);
-						lp->first_skb = NULL;
-					}
-					if (lp->sav_skb) {
-						dev_kfree_skb(lp->sav_skb, FREE_WRITE);
-						lp->sav_skb = NULL;
-					}
-					isdn_free_channel(lp->isdn_device, lp->isdn_channel,
-							  ISDN_USAGE_NET);
+				if (lp->flags & ISDN_NET_CONNECTED) {
+					printk(KERN_INFO "%s: remote %s (%d)\n", lp->name,
+						lp->dialstate?"abort":"hangup", lp->dialstate);
+					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+						lp->charge);
+printk(KERN_DEBUG "idx=%d drv=%d ch=%d\n",idx, lp->isdn_device, lp->isdn_channel);
+					if (!lp->dialstate) {
+						isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 #ifdef CONFIG_ISDN_PPP
-					isdn_ppp_free(lp);
+						isdn_ppp_free(lp);
 #endif
-					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
-					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);
+					} else {
+						switch (lp->dialstate) {
+							case 4:
+							case 5:
+							case 6:
+							case 7:
+							case 8:
+							case 9:
+								lp->dialstate = 3;
+								break;
+							case 11:
+								break;
+							case 12:
+								{
+									isdn_ctrl cmd;
+									printk(KERN_INFO "%s: got reject, waiting for callback ...\n", p->local.name);
+
+									p->local.dtimer = 0;
+									p->local.dialstate = 13;
+									cmd.driver = p->local.isdn_device;
+									cmd.command = ISDN_CMD_HANGUP;
+									cmd.arg = p->local.isdn_channel;
+									isdn_command(&cmd);
+									isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
+								}
+								/* Fall through */
+							case 13:
+								break;
+						}
+					}
 					return 1;
 				}
 				break;
@@ -568,6 +782,7 @@
 		if (p->local.dialstate)
 			printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate);
 #endif
+
 		switch (p->local.dialstate) {
 			case 0:
 				/* Nothing to do for this interface */
@@ -587,6 +802,13 @@
 					break;
 				}
 				anymore = 1;
+
+				if(p->local.dialtimeout > 0)
+					if(p->local.dialstarted == 0 || jiffies > (p->local.dialstarted + p->local.dialtimeout + p->local.dialwait)) {
+						p->local.dialstarted = jiffies;
+						p->local.dialwait_timer = 0;
+					}
+
 				p->local.dialstate++;
 				/* Fall through */
 			case 2:
@@ -594,27 +816,37 @@
 				cmd.driver = p->local.isdn_device;
 				cmd.arg = p->local.isdn_channel;
 				cmd.command = ISDN_CMD_CLREAZ;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
 				cmd.command = ISDN_CMD_SETEAZ;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				p->local.dialretry = 0;
 				anymore = 1;
 				p->local.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(p->local) == 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, p->local.first_skb, s);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
 				cmd.driver = p->local.isdn_device;
 				cmd.command = ISDN_CMD_SETL2;
 				cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				cmd.driver = p->local.isdn_device;
 				cmd.command = ISDN_CMD_SETL3;
 				cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				cmd.driver = p->local.isdn_device;
 				cmd.arg = p->local.isdn_channel;
 				save_flags(flags);
@@ -631,6 +863,16 @@
 					p->local.dialstate = 4;
 					printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
 				} else {
+					if(p->local.dialtimeout > 0)
+						if(jiffies > (p->local.dialstarted + p->local.dialtimeout)) {
+							restore_flags(flags);
+							p->local.dialwait_timer = jiffies + p->local.dialwait;
+							p->local.dialstarted = 0;
+							isdn_net_unreachable(&p->dev, p->local.first_skb, "dial: timed out");
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+
 					sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num);
 					/*
 					 * Switch to next number or back to start if at end of list.
@@ -638,6 +880,17 @@
 					if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
 						p->local.dial = p->local.phone[1];
 						p->local.dialretry++;
+
+						if (p->local.dialretry > p->local.dialmax) {
+							restore_flags(flags);
+							if (p->local.dialtimeout == 0) {
+								p->local.dialwait_timer = jiffies + p->local.dialwait;
+								p->local.dialstarted = 0;
+								isdn_net_unreachable(&p->dev, p->local.first_skb, "dial: tried all numbers dialmax times");
+							}
+							isdn_net_hangup(&p->dev);
+							break;
+						}
 					}
 					restore_flags(flags);
 					cmd.command = ISDN_CMD_DIAL;
@@ -651,13 +904,14 @@
 						isdn_info_update();
 					}
 					printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
-					       p->local.dialretry - 1, cmd.parm.setup.phone);
+					       p->local.dialretry, cmd.parm.setup.phone);
 					p->local.dtimer = 0;
 #ifdef ISDN_DEBUG_NET_DIAL
 					printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
 					       p->local.isdn_channel);
 #endif
-					dev->drv[p->local.isdn_device]->interface->command(&cmd);
+					cmd.driver = p->local.isdn_device;
+					isdn_command(&cmd);
 				}
 				p->local.huptimer = 0;
 				p->local.outgoing = 1;
@@ -669,20 +923,19 @@
 					p->local.hupflags &= ~ISDN_HAVECHARGE;
 				}
 				anymore = 1;
-				p->local.dialstate =
-				    (p->local.cbdelay &&
-				     (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				if (p->local.flags & ISDN_NET_CBOUT) {
+					p->local.dialstate = (p->local.cbdelay) ? 12 : 13;
+				} else
+					p->local.dialstate = 4;
 				break;
 			case 4:
+			case 13:
 				/* 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 (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-					if (p->local.dialretry < p->local.dialmax) {
-						p->local.dialstate = 3;
-					} else
-						isdn_net_hangup(&p->dev);
+					p->local.dialstate = 3;
 				anymore = 1;
 				break;
 			case 5:
@@ -693,11 +946,12 @@
 				anymore = 1;
 				p->local.dtimer = 0;
 				p->local.dialstate++;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				break;
 			case 6:
 				/* Wait for B- or D-Channel-connect. If timeout,
 				 * switch back to state 3.
+				 * Dialmax-handling moved to state 3.
 				 */
 #ifdef ISDN_DEBUG_NET_DIAL
 				printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
@@ -716,11 +970,12 @@
 				cmd.driver = p->local.isdn_device;
 				cmd.command = ISDN_CMD_SETL2;
 				cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				cmd.driver = p->local.isdn_device;
 				cmd.command = ISDN_CMD_SETL3;
 				cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
+
 				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
 					isdn_net_hangup(&p->dev);
 				else {
@@ -733,7 +988,7 @@
 				cmd.driver = p->local.isdn_device;
 				cmd.arg = p->local.isdn_channel;
 				cmd.command = ISDN_CMD_ACCEPTB;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				anymore = 1;
 				p->local.dtimer = 0;
 				p->local.dialstate++;
@@ -744,7 +999,7 @@
 #ifdef ISDN_DEBUG_NET_DIAL
 				printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
 #endif
-				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT60)
 					isdn_net_hangup(&p->dev);
 				else
 					anymore = 1;
@@ -757,16 +1012,17 @@
 				break;
 			case 12:
 				/* Remote does callback. Hangup after cbdelay, then wait for incoming
-				 * call (in state 4).
+				 * call (in state 13).
 				 */
-				if (p->local.dtimer++ > p->local.cbdelay) {
+				if (p->local.dtimer++ > p->local.cbdelay) 
+				{
 					printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name);
 					p->local.dtimer = 0;
-					p->local.dialstate = 4;
+					p->local.dialstate = 13;
 					cmd.driver = p->local.isdn_device;
 					cmd.command = ISDN_CMD_HANGUP;
 					cmd.arg = p->local.isdn_channel;
-					(void) dev->drv[cmd.driver]->interface->command(&cmd);
+					isdn_command(&cmd);
 					isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
 				}
 				anymore = 1;
@@ -795,12 +1051,14 @@
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
 #endif
-		cmd.driver = lp->isdn_device;
-		cmd.command = ISDN_CMD_HANGUP;
-		cmd.arg = lp->isdn_channel;
-		(void) dev->drv[cmd.driver]->interface->command(&cmd);
+		if ((lp->isdn_device != -1) && (lp->isdn_channel != -1)) {
+			cmd.driver = lp->isdn_device;
+			cmd.command = ISDN_CMD_HANGUP;
+			cmd.arg = lp->isdn_channel;
+			isdn_command(&cmd);
+			isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+		}
 		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
-		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 	}
 	isdn_net_unbind_channel(lp);
 }
@@ -821,9 +1079,6 @@
 
 	addinfo[0] = '\0';
 	switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_SYNCPPP:
-                        p = &buf[IPPP_MAX_HEADER];
-                        break;
 		case ISDN_NET_ENCAP_IPTYP:
 			proto = ntohs(*(unsigned short *) &buf[0]);
 			p = &buf[2];
@@ -836,6 +1091,12 @@
 			proto = ntohs(*(unsigned short *) &buf[2]);
 			p = &buf[4];
 			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* jump over fake header. */
+			p = &buf[IPPP_MAX_HEADER];
+			break;
+#endif
 	}
 	data_ofs = ((p[0] & 15) * 4);
 	switch (proto) {
@@ -913,7 +1174,6 @@
 	return 1;
 }
 
-
 /*
  *  Helper function for isdn_net_start_xmit.
  *  When called, the connection is already established.
@@ -994,8 +1254,10 @@
 	if (ndev->tbusy) {
 		if (jiffies - ndev->trans_start < (2 * HZ))
 			return 1;
+
 		if (!lp->dialstate)
 			lp->stats.tx_errors++;
+
 		ndev->tbusy = 0;
 		ndev->trans_start = jiffies;
 	}
@@ -1015,17 +1277,53 @@
 #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, FREE_WRITE);
+				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) {
+/*
+printk("reject: jiffies=%ld, started=%ld, timeout=%d, wait=%ld, timer=%ld\n", jiffies, lp->dialstarted, lp->dialtimeout, lp->dialwait, lp->dialwait_timer);
+*/
+						/*
+						printk(KERN_WARNING "isdn_net: Dial rejected %s, packet dropped\n",
+							ndev->name);
+						*/
+						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+						dev_kfree_skb(skb, FREE_WRITE);
+						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);
 #if 0
 					printk(KERN_WARNING
@@ -1045,6 +1343,7 @@
 				/* Log packet, which triggered dialing */
 				if (dev->net_verbose)
 					isdn_net_log_packet(buf, lp);
+
 				lp->dialstate = 1;
 				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
@@ -1067,7 +1366,7 @@
 				 * when using encap ETHER
 				 */
 				if (lp->first_skb) {
-					printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
+					printk(KERN_DEBUG "isdn_net_start_xmit: First skb already set!\n");
 					dev_kfree_skb(lp->first_skb, FREE_WRITE);
 					lp->first_skb = NULL;
 				}
@@ -1268,7 +1567,8 @@
 			       lp->name);
 			kfree_skb(skb, FREE_READ);
 			return;
-	}
+ 	}
+
 	netif_rx(skb);
 	return;
 }
@@ -1346,11 +1646,11 @@
 	isdn_net_local *lp = dev->priv;
 	ushort len = 0;
 
-	skb->mac.raw = skb->data;
 	switch (lp->p_encap) {
 		case ISDN_NET_ENCAP_ETHER:
 			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
 			break;
+            break;
 #ifdef CONFIG_ISDN_PPP
 		case ISDN_NET_ENCAP_SYNCPPP:
 			/* stick on a fake header to keep fragmentation code happy. */
@@ -1493,6 +1793,9 @@
 	ndev->pa_mask = 0;
 	ndev->pa_alen = 4;
 
+	/* for clients with MPPP maybe higher values better */
+	ndev->tx_queue_len = 30;
+
 	for (i = 0; i < ETH_ALEN; i++)
 		ndev->broadcast[i] = 0xff;
 
@@ -1502,7 +1805,6 @@
 	/* The ISDN-specific entries in the device structure. */
 	ndev->open = &isdn_net_open;
 	ndev->hard_start_xmit = &isdn_net_start_xmit;
-
 	/*
 	 *  up till binding we ask the protocol layer to reserve as much
 	 *  as we might need for HL layer
@@ -1514,7 +1816,6 @@
 				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;
@@ -1648,9 +1949,11 @@
  *               4 = Wait cbdelay, then call back
  */
 int
-isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
+isdn_net_find_icall(int di, isdn_ctrl *c, int idx)
 {
 	char *eaz;
+	int ch = c->arg;
+	setup_parm *setup = &c->parm.setup;
 	int si1;
 	int si2;
 	int ematch;
@@ -1664,36 +1967,37 @@
 	/* Search name in netdev-chain */
 	save_flags(flags);
 	cli();
-	if (!setup.phone[0]) {
+	if (!setup->phone[0]) {
 		nr[0] = '0';
 		nr[1] = '\0';
 		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
 	} else
-		strcpy(nr, setup.phone);
-	si1 = (int) setup.si1;
-	si2 = (int) setup.si2;
-	if (!setup.eazmsn[0]) {
+		strcpy(nr, setup->phone);
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
 		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
 	} else
-		eaz = setup.eazmsn;
+		eaz = setup->eazmsn;
 	if (dev->net_verbose > 1)
 		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
 	/* Accept only calls with Si1 = 7 (Data-Transmission) */
 	if (si1 != 7) {
 		if (dev->net_verbose > 1)
 			printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
+		restore_flags(flags);
 		return 0;
 	}
 	n = (isdn_net_phone *) 0;
-	p = dev->netdev;
 	ematch = 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) {
+
+	for (p = dev->netdev; p; p = (isdn_net_dev *) p->next) {
 		/* If last check has triggered as binding-swap, revert it */
 		switch (swapped) {
 			case 2:
@@ -1704,18 +2008,20 @@
 				break;
 		}
 		swapped = 0;
-		if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
-			ematch = 1;
+		if (strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
+			continue;	/* next loop for next device */
+		ematch = 1;	/* EAZ matches! */
 #ifdef ISDN_DEBUG_NET_ICALL
 		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
 		       p->local.name, p->local.msn, p->local.flags, p->local.dialstate);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	/* EAZ is matching   */
-		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
-		      (USG_NONE(dev->usage[idx]))) ||	/* and ch. unused or */
-		     ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) &&	/* if dialing        */
-		       (!(p->local.flags & ISDN_NET_CALLBACK)))	/* but no callback   */
-		     ))) {
+		if ( (!(p->local.flags & ISDN_NET_CONNECTED) &&	/* not connected  */
+		      USG_NONE(dev->usage[idx]))		/* and ch. unused */
+ 		    ||						/* or */
+		     (((p->local.dialstate == 4) || (p->local.dialstate == 12) ||
+               (p->local.dialstate == 13)) &&	/* if dialing        */
+		       !(p->local.flags & ISDN_NET_CALLBACK))                 		/* but no callback   */
+		   ) /*if*/ {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
 			       p->local.pre_device, p->local.pre_channel);
@@ -1752,7 +2058,6 @@
 									swapped = 1;
 								} else {
 									/* ... else iterate next device */
-									p = (isdn_net_dev *) p->next;
 									continue;
 								}
 							} else {
@@ -1774,7 +2079,6 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 								printk(KERN_DEBUG "n_fi: final check failed\n");
 #endif
-								p = (isdn_net_dev *) p->next;
 								continue;
 							}
 						}
@@ -1783,7 +2087,6 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
 #endif
-						p = (isdn_net_dev *) p->next;
 						continue;
 					}
 				}
@@ -1804,12 +2107,26 @@
 #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' dialmode `off' -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/*
+				 * Is the interface up?
 				 * If not, reject the call actively.
 				 */
 				if (!p->dev.start) {
 					restore_flags(flags);
-					printk(KERN_INFO "%s: incoming call, if down -> rejected\n",
+					printk(KERN_INFO "incoming call, interface `%s' down -> rejected\n",
 					       lp->name);
 					return 3;
 				}
@@ -1820,7 +2137,7 @@
 					isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
 					printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
 					printk(KERN_DEBUG "master=%s\n", mlp->name);
-					if (mlp->flags & ISDN_NET_CONNECTED) {
+					if ((mlp->flags & ISDN_NET_CONNECTED) && (!mlp->dialstate)) {
 						printk(KERN_DEBUG "master online\n");
 						/* Master is online, find parent-slave (master if first slave) */
 						while (mlp->slave) {
@@ -1832,13 +2149,23 @@
 						printk(KERN_DEBUG "master offline\n");
 					/* Found parent, if it's offline iterate next device */
 					printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
-					if (!(mlp->flags & ISDN_NET_CONNECTED)) {
-						p = (isdn_net_dev *) p->next;
+					if (!(mlp->flags & ISDN_NET_CONNECTED) || mlp->dialstate) {
 						continue;
 					}
 				}
 				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_AUTO) {
+						restore_flags(flags);
+						printk(KERN_INFO "incoming call for callback, interface `%s' dialmode not `auto' -> 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]) {
@@ -1877,11 +2204,13 @@
 					       eaz);
 					/* if this interface is dialing, it does it probably on a different
 					   device, so free this device */
-					if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) {
+					if (p->local.dialstate &&
+						((p->local.isdn_device != di) || (p->local.isdn_channel != ch))) {
 #ifdef CONFIG_ISDN_PPP
 						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 							isdn_ppp_free(lp);
 #endif
+						isdn_net_unbind_ptr(lp->isdn_device, lp->isdn_channel);
 						isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
 							 ISDN_USAGE_NET);
 					}
@@ -1913,10 +2242,9 @@
 				}
 			}
 		}
-		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;
@@ -2074,10 +2402,15 @@
 	netdev->local.srobin = &netdev->dev;
 	netdev->local.hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
 	netdev->local.onhtime = 10;	/* Default hangup-time for saving costs
-	   of those who forget configuring this */
+	                                   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;
@@ -2225,6 +2558,8 @@
 		p->local.triggercps = cfg->triggercps;
 		p->local.slavedelay = cfg->slavedelay * HZ;
 		p->local.pppbind = cfg->pppbind;
+		p->local.dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+		p->local.dialwait    = cfg->dialwait * HZ;
 		if (cfg->secure)
 			p->local.flags |= ISDN_NET_SECURE;
 		else
@@ -2246,6 +2581,16 @@
 				p->local.flags &= ~ISDN_NET_CALLBACK;
 				break;
 		}
+ 		p->local.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");
+ 			p->local.flags |= ISDN_NET_DM_OFF; /* turn on 'off' bit */
+ 		}
+ 		else {
+ 			p->local.flags |= cfg->dialmode;   /* turn on selected bits */
+ 		}
 		if (cfg->chargehup)
 			p->local.hupflags |= ISDN_CHARGEHUP;
 		else
@@ -2259,7 +2604,6 @@
 			p->local.chargeint = cfg->chargeint * HZ;
 		}
 		if (cfg->p_encap != p->local.p_encap) {
-			/* FIXME: What if there are alias devices too? */
 			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
 				p->dev.hard_header = NULL;
 #if (LINUX_VERSION_CODE < 0x02010F)
@@ -2286,7 +2630,11 @@
 					p->dev.hard_header_cache = NULL;
 #endif
 					p->dev.header_cache_update = NULL;
-					p->dev.flags = IFF_NOARP | IFF_SOFTHEADERS;
+					/*
+					 * paul: IFF_SOFTHEADERS was added in
+					 * 2.0.33?? Does this make sense?
+					 */
+					p->dev.flags = IFF_NOARP /* | IFF_SOFTHEADERS */;
 				}
 			}
 		}
@@ -2324,15 +2672,18 @@
 		if (p->local.flags & ISDN_NET_CBOUT)
 			cfg->callback = 2;
 		cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0;
+ 		cfg->dialmode = p->local.flags & ISDN_NET_DIALMODE_MASK;
 		cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0;
 		cfg->ihup = (p->local.hupflags & 8) ? 1 : 0;
 		cfg->cbdelay = p->local.cbdelay;
 		cfg->dialmax = p->local.dialmax;
-		cfg->triggercps = p->local.triggercps;
+        cfg->triggercps = p->local.triggercps;
 		cfg->slavedelay = p->local.slavedelay / HZ;
 		cfg->chargeint = (p->local.hupflags & ISDN_CHARGEHUP) ?
 		    (p->local.chargeint / HZ) : 0;
 		cfg->pppbind = p->local.pppbind;
+		cfg->dialtimeout = p->local.dialtimeout >= 0 ? p->local.dialtimeout / HZ : -1;
+		cfg->dialwait = p->local.dialwait / HZ;
 		if (p->local.slave)
 			strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
 		else
@@ -2433,6 +2784,7 @@
 				else
 					p->local.phone[inout] = n->next;
 				kfree(n);
+				restore_flags(flags);
 				return 0;
 			}
 			m = n;

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