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

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

diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
@@ -1,8 +1,8 @@
-/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $
+/* $Id: isdn_common.c,v 1.44.2.9 1998/11/06 00:07:25 fritz Exp $
 
  * Linux ISDN subsystem, common used 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    Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -21,6 +21,43 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.44.2.9  1998/11/06 00:07:25  fritz
+ * Bugfix: loading additional driver while /dev/isdnctrl opened resulted in
+ *         wrong module usage count after closing /dev/isdnctrl.
+ *
+ * Revision 1.44.2.8  1998/11/05 22:11:43  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.44.2.7  1998/11/04 17:22:40  fritz
+ * Replaced broken lowlevel-driver locking.
+ *
+ * Revision 1.44.2.6  1998/11/03 14:30:56  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.44.2.5  1998/10/25 15:48:04  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
+ * Revision 1.44.2.4  1998/06/07 13:47:44  fritz
+ * ABC cleanup
+ *
+ * Revision 1.44.2.2  1998/03/16 09:55:44  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
+ * Revision 1.44.2.1  1998/03/07 23:35:03  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.44  1997/05/27 15:17:23  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -218,7 +255,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.44 $";
+static char *isdn_revision = "$Revision: 1.44.2.9 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -238,16 +275,39 @@
 void
 isdn_MOD_INC_USE_COUNT(void)
 {
+	int i;
+
 	MOD_INC_USE_COUNT;
+	for (i = 0; i < dev->drivers; i++) {
+		isdn_ctrl cmd;
+
+		cmd.driver = i;
+		cmd.arg = 0;
+		cmd.command = ISDN_CMD_LOCK;
+		isdn_command(&cmd);
+		dev->drv[i]->locks++;
+	}
 }
 
 void
 isdn_MOD_DEC_USE_COUNT(void)
 {
+	int i;
+
 	MOD_DEC_USE_COUNT;
+	for (i = 0; i < dev->drivers; i++)
+		if (dev->drv[i]->locks > 0) {
+			isdn_ctrl cmd;
+
+			cmd.driver = i;
+			cmd.arg = 0;
+			cmd.command = ISDN_CMD_UNLOCK;
+			isdn_command(&cmd);
+			dev->drv[i]->locks--;
+		}
 }
 
-#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) || defined(CONFIG_ISDN_TIMEOUT_RULES)
 void
 isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
 {
@@ -329,6 +389,7 @@
 #endif
 		}
 	}
+
 	if (tf) {
 		int flags;
 
@@ -392,6 +453,15 @@
 		isdn_trash_skb(skb, FREE_READ);
 }
 
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+	return dev->drv[cmd->driver]->interface->command(cmd);
+}
+
 void
 isdn_all_eaz(int di, int ch)
 {
@@ -403,7 +473,7 @@
 	cmd.arg = ch;
 	cmd.command = ISDN_CMD_SETEAZ;
 	cmd.parm.num[0] = '\0';
-	(void) dev->drv[di]->interface->command(&cmd);
+	isdn_command(&cmd);
 }
 
 static int
@@ -424,7 +494,7 @@
 				return -1;
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				return 0;
 			if (isdn_tty_stat_callback(i, c))
 				return 0;
@@ -438,13 +508,13 @@
 			wake_up_interruptible(&dev->drv[di]->st_waitq);
 			break;
 		case ISDN_STAT_RUN:
-			dev->drv[di]->running = 1;
+			dev->drv[di]->flags |= DRV_FLAG_RUNNING;
 			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 				if (dev->drvmap[i] == di)
 					isdn_all_eaz(di, dev->chanmap[i]);
 			break;
 		case ISDN_STAT_STOP:
-			dev->drv[di]->running = 0;
+			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
 			break;
 		case ISDN_STAT_ICALL:
 			if (i < 0)
@@ -456,27 +526,23 @@
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_HANGUP;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_command(&cmd);
 				return 0;
 			}
 			/* Try to find a network-interface which will accept incoming call */
-			cmd.driver = di;
-			cmd.arg = c->arg;
-			cmd.command = ISDN_CMD_LOCK;
-			dev->drv[di]->interface->command(&cmd);
-			r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
+			r = isdn_net_find_icall(di, c, i);
 			switch (r) {
 				case 0:
 					/* No network-device replies.
 					 * Try ttyI's
 					 */
-					if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0)
+					if (isdn_tty_find_icall(di, c) >= 0)
 						retval = 1;
-					else if (dev->drv[di]->reject_bus) {
+					else if (dev->drv[di]->flags & DRV_FLAG_REJBUS) {
 						cmd.driver = di;
 						cmd.arg = c->arg;
 						cmd.command = ISDN_CMD_HANGUP;
-						dev->drv[di]->interface->command(&cmd);
+						isdn_command(&cmd);
 						retval = 2;
 					}
 					break;
@@ -486,7 +552,7 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_ACCEPTD;
-					dev->drv[di]->interface->command(&cmd);
+					isdn_command(&cmd);
 					retval = 1;
 					break;
 				case 2:	/* For calling back, first reject incoming call ... */
@@ -496,7 +562,7 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_HANGUP;
-					dev->drv[di]->interface->command(&cmd);
+					isdn_command(&cmd);
 					if (r == 3)
 						break;
 					/* Fall through */
@@ -505,12 +571,6 @@
 					isdn_net_dial();
 					break;
 			}
-			if (retval != 1) {
-				cmd.driver = di;
-				cmd.arg = c->arg;
-				cmd.command = ISDN_CMD_UNLOCK;
-				dev->drv[di]->interface->command(&cmd);
-			}
 			return retval;
 			break;
 		case ISDN_STAT_CINF:
@@ -522,7 +582,7 @@
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
 			if (strcmp(c->parm.num, "0"))
-				isdn_net_stat_callback(i, c->command);
+				isdn_net_stat_callback(i, c);
 			break;
 		case ISDN_STAT_CAUSE:
 #ifdef ISDN_DEBUG_STATCALLB
@@ -541,14 +601,14 @@
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
 			/* Find any net-device, waiting for D-channel setup */
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			/* Find any ttyI, waiting for D-channel setup */
 			if (isdn_tty_stat_callback(i, c)) {
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_ACCEPTB;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_command(&cmd);
 				break;
 			}
 			break;
@@ -560,10 +620,10 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags &= ~(1 << (c->arg));
+			dev->drv[di]->online &= ~(1 << (c->arg));
 			isdn_info_update();
 			/* Signal hangup to network-devices */
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -577,9 +637,9 @@
 			/* Signal B-channel-connect to network-devices */
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags |= (1 << (c->arg));
+			dev->drv[di]->online |= (1 << (c->arg));
 			isdn_info_update();
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -592,7 +652,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags &= ~(1 << (c->arg));
+			dev->drv[di]->online &= ~(1 << (c->arg));
 			isdn_info_update();
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -605,7 +665,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -817,7 +877,7 @@
 	p = istatbuf + strlen(istatbuf);
 	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
 		if (dev->drv[i]) {
-			sprintf(p, "%ld ", dev->drv[i]->flags);
+			sprintf(p, "%ld ", dev->drv[i]->online);
 			p = istatbuf + strlen(istatbuf);
 		} else {
 			sprintf(p, "? ");
@@ -880,7 +940,7 @@
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
 		save_flags(flags);
@@ -943,7 +1003,7 @@
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
 		while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
@@ -957,7 +1017,7 @@
 		/*
 		 * We want to use the isdnctrl device to load the firmware
 		 *
-		 if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 		 return -ENODEV;
 		 */
 		if (dev->drv[drvidx]->interface->writecmd)
@@ -1044,136 +1104,13 @@
 }
 #endif
 
-static int
-isdn_set_allcfg(char *src)
-{
-	int ret;
-	int i;
-	ulong flags;
-	isdn_net_ioctl_cfg cfg;
+typedef union {
+	char name[10];
+	char bname[22];
+	isdn_ioctl_struct iocts;
 	isdn_net_ioctl_phone phone;
-
-	if ((ret = isdn_net_rmall()))
-		return ret;
-	if ((ret = copy_from_user((char *) &i, src, sizeof(int))))
-		return ret;
-	save_flags(flags);
-	cli();
-	src += sizeof(int);
-	while (i) {
-		int phone_len;
-		int out_flag;
-
-		if ((ret = copy_from_user((char *) &cfg, src, sizeof(cfg)))) {
-			restore_flags(flags);
-			return ret;
-		}
-		src += sizeof(cfg);
-		if (!isdn_net_new(cfg.name, NULL)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if ((ret = isdn_net_setcfg(&cfg))) {
-			restore_flags(flags);
-			return ret;
-		}
-		phone_len = out_flag = 0;
-		while (out_flag < 2) {
-			if ((ret = verify_area(VERIFY_READ, src, 1))) {
-				restore_flags(flags);
-				return ret;
-			}
-			GET_USER(phone.phone[phone_len], src++);
-			if ((phone.phone[phone_len] == ' ') ||
-			    (phone.phone[phone_len] == '\0')) {
-				if (phone_len) {
-					phone.phone[phone_len] = '\0';
-					strcpy(phone.name, cfg.name);
-					phone.outgoing = out_flag;
-					if ((ret = isdn_net_addphone(&phone))) {
-						restore_flags(flags);
-						return ret;
-					}
-				} else
-					out_flag++;
-				phone_len = 0;
-			}
-			if (++phone_len >= sizeof(phone.phone))
-				printk(KERN_WARNING
-				       "%s: IIOCSETSET phone number too long, ignored\n",
-				       cfg.name);
-		}
-		i--;
-	}
-	restore_flags(flags);
-	return 0;
-}
-
-static int
-isdn_get_allcfg(char *dest)
-{
 	isdn_net_ioctl_cfg cfg;
-	isdn_net_ioctl_phone phone;
-	isdn_net_dev *p;
-	ulong flags;
-	int ret;
-
-	/* Walk through netdev-chain */
-	save_flags(flags);
-	cli();
-	p = dev->netdev;
-	while (p) {
-		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) {
-			restore_flags(flags);
-			return ret;
-		}
-		strcpy(cfg.eaz, p->local.msn);
-		cfg.exclusive = p->local.exclusive;
-		if (p->local.pre_device >= 0) {
-			sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device],
-				p->local.pre_channel);
-		} else
-			cfg.drvid[0] = '\0';
-		cfg.onhtime = p->local.onhtime;
-		cfg.charge = p->local.charge;
-		cfg.l2_proto = p->local.l2_proto;
-		cfg.l3_proto = p->local.l3_proto;
-		cfg.p_encap = p->local.p_encap;
-		cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
-		cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
-		cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0;
-		cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0;
-		cfg.chargeint = p->local.chargeint;
-		if (copy_to_user(dest, p->local.name, 10)) {
-			restore_flags(flags);
-			return -EFAULT;
-		}
-		dest += 10;
-		if (copy_to_user(dest, (char *) &cfg, sizeof(cfg))) {
-			restore_flags(flags);
-			return -EFAULT;
-		}
-		dest += sizeof(cfg);
-		strcpy(phone.name, p->local.name);
-		phone.outgoing = 0;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
-			restore_flags(flags);
-			return ret;
-		} else
-			dest += ret;
-		strcpy(phone.name, p->local.name);
-		phone.outgoing = 1;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
-			restore_flags(flags);
-			return ret;
-		} else
-			dest += ret;
-		put_user(0, dest);
-		p = p->next;
-	}
-	restore_flags(flags);
-	return 0;
-}
+} iocpar_t;
 
 static int
 isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
@@ -1186,19 +1123,15 @@
 	int i;
 	char *p;
 	char *s;
-	union iocpar {
-		char name[10];
-		char bname[22];
-		isdn_ioctl_struct iocts;
-		isdn_net_ioctl_phone phone;
-		isdn_net_ioctl_cfg cfg;
-	} iocpar;
-
-#define name  iocpar.name
-#define bname iocpar.bname
-#define iocts iocpar.iocts
-#define phone iocpar.phone
-#define cfg   iocpar.cfg
+	iocpar_t *iocpar;
+
+#define name  iocpar->name
+#define bname iocpar->bname
+#define iocts iocpar->iocts
+#define phone iocpar->phone
+#define cfg   iocpar->cfg
+
+#define RETURN(r) ({ ret = r; kfree(iocpar); return ret; })
 
 	if (minor == ISDN_MINOR_STATUS) {
 		switch (cmd) {
@@ -1232,127 +1165,131 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
+		if ((iocpar = (iocpar_t *)kmalloc(sizeof(iocpar_t), GFP_KERNEL)) == NULL) {
+			printk(KERN_WARNING "isdn: Out of memory in isdn_ioctl\n");
+			return -ENOMEM;
+		}
 		switch (cmd) {
 #ifdef CONFIG_NETDEVICES
 			case IIOCNETAIF:
 				/* Add a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-						return ret;
+						RETURN(ret);
 					s = name;
 				} else
 					s = NULL;
 				if ((s = isdn_net_new(s, NULL))) {
 					if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1)))
-						return ret;
-					return 0;
+						RETURN(ret);
+					RETURN(0);
 				} else
-					return -ENODEV;
+					RETURN(-ENODEV);
 			case IIOCNETASL:
 				/* Add a slave to a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user(bname, (char *) arg, sizeof(bname) - 1)))
-						return ret;
+						RETURN(ret);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if ((s = isdn_net_newslave(bname))) {
 					if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1)))
-						return ret;
-					return 0;
+						RETURN(ret);
+					RETURN(0);
 				} else
-					return -ENODEV;
+					RETURN(-ENODEV);
 			case IIOCNETDIF:
 				/* Delete a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-						return ret;
-					return isdn_net_rm(name);
+						RETURN(ret);
+					RETURN(isdn_net_rm(name));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCNETSCF:
 				/* Set configurable parameters of a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))))
-						return ret;
-					return isdn_net_setcfg(&cfg);
+						RETURN(ret);
+					RETURN(isdn_net_setcfg(&cfg));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCNETGCF:
 				/* Get configurable parameters of a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))))
-						return ret;
+						RETURN(ret);
 					if (!(ret = isdn_net_getcfg(&cfg))) {
 						if ((ret = copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg))))
-							return ret;
+							RETURN(ret);
 					}
-					return ret;
+					RETURN(ret);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCNETANM:
 				/* Add a phone-number to a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone))))
-						return ret;
-					return isdn_net_addphone(&phone);
+						RETURN(ret);
+					RETURN(isdn_net_addphone(&phone));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCNETGNM:
 				/* Get list of phone-numbers of a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone))))
-						return ret;
-					return isdn_net_getphones(&phone, (char *) arg);
+						RETURN(ret);
+					RETURN(isdn_net_getphones(&phone, (char *) arg));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCNETDNM:
 				/* Delete a phone-number of a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone))))
-						return ret;
-					return isdn_net_delphone(&phone);
+						RETURN(ret);
+					RETURN(isdn_net_delphone(&phone));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCNETDIL:
 				/* Force dialing of a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-						return ret;
-					return isdn_net_force_dial(name);
+						RETURN(ret);
+					RETURN(isdn_net_force_dial(name));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 #ifdef CONFIG_ISDN_PPP
 			case IIOCNETALN:
 				if (!arg)
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-					return ret;
-				return isdn_ppp_dial_slave(name);
+					RETURN(ret);
+				RETURN(isdn_ppp_dial_slave(name));
 			case IIOCNETDLN:
 				if (!arg)
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-					return ret;
-				return isdn_ppp_hangup_slave(name);
+					RETURN(ret);
+				RETURN(isdn_ppp_hangup_slave(name));
 #endif
 			case IIOCNETHUP:
 				/* Force hangup of a network-interface */
 				if (!arg)
-					return -EINVAL;
+					RETURN(-EINVAL);
 			        if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-			        	return ret;
-			        return isdn_net_force_hangup(name);
+			        	RETURN(ret);
+			        RETURN(isdn_net_force_hangup(name));
 				break;
 #endif                          /* CONFIG_NETDEVICES */
 			case IIOCSETVER:
 				dev->net_verbose = arg;
 				printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
-				return 0;
+				RETURN(0);
 			case IIOCSETGST:
 				if (arg)
 					dev->global_flags |= ISDN_GLOBAL_STOPPED;
@@ -1360,7 +1297,7 @@
 					dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
 				printk(KERN_INFO "isdn: Global Mode %s\n",
 				       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
-				return 0;
+				RETURN(0);
 			case IIOCSETBRJ:
 				drvidx = -1;
 				if (arg) {
@@ -1368,7 +1305,7 @@
 					char *p;
 					if ((ret = copy_from_user((char *) &iocts, (char *) arg,
 					      sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					if (strlen(iocts.drvid)) {
 						if ((p = strchr(iocts.drvid, ',')))
 							*p = 0;
@@ -1381,28 +1318,15 @@
 					}
 				}
 				if (drvidx == -1)
-					return -ENODEV;
-				dev->drv[drvidx]->reject_bus = iocts.arg;
-				return 0;
-			case IIOCGETSET:
-				/* Get complete setup (all network-interfaces and profile-
-				   settings of all tty-devices */
-				if (arg)
-					return (isdn_get_allcfg((char *) arg));
-				else
-					return -EINVAL;
-				break;
-			case IIOCSETSET:
-				/* Set complete setup (all network-interfaces and profile-
-				   settings of all tty-devices */
-				if (arg)
-					return (isdn_set_allcfg((char *) arg));
+					RETURN(-ENODEV);
+				if (iocts.arg)
+					dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
 				else
-					return -EINVAL;
-				break;
+					dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+				RETURN(0);
 			case IIOCSIGPRF:
 				dev->profd = current;
-				return 0;
+				RETURN(0);
 				break;
 			case IIOCGETPRF:
 				/* Get all Modem-Profiles */
@@ -1413,20 +1337,20 @@
 					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
 					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
 						   * ISDN_MAX_CHANNELS)))
-						return ret;
+						RETURN(ret);
 
 					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 						if (copy_to_user(p, dev->mdm.info[i].emu.profile,
 						      ISDN_MODEM_ANZREG))
-							return -EFAULT;
+							RETURN(-EFAULT);
 						p += ISDN_MODEM_ANZREG;
 						if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
-							return -EFAULT;
+							RETURN(-EFAULT);
 						p += ISDN_MSNLEN;
 					}
-					return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+					RETURN((ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				break;
 			case IIOCSETPRF:
 				/* Set all Modem-Profiles */
@@ -1437,20 +1361,20 @@
 					if ((ret = verify_area(VERIFY_READ, (void *) arg,
 					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
 						   * ISDN_MAX_CHANNELS)))
-						return ret;
+						RETURN(ret);
 
 					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p,
 						      ISDN_MODEM_ANZREG)))
-							return ret;
+							RETURN(ret);
 						p += ISDN_MODEM_ANZREG;
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)))
-							return ret;
+							RETURN(ret);
 						p += ISDN_MSNLEN;
 					}
-					return 0;
+					RETURN(0);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				break;
 			case IIOCSETMAP:
 			case IIOCGETMAP:
@@ -1460,7 +1384,7 @@
 					if ((ret = copy_from_user((char *) &iocts,
 							    (char *) arg,
 					     sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					if (strlen(iocts.drvid)) {
 						drvidx = -1;
 						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
@@ -1471,7 +1395,7 @@
 					} else
 						drvidx = 0;
 					if (drvidx == -1)
-						return -ENODEV;
+						RETURN(-ENODEV);
 					if (cmd == IIOCSETMAP) {
 						int loop = 1;
 
@@ -1482,7 +1406,7 @@
 
 							while (1) {
 								if ((ret = verify_area(VERIFY_READ, p, 1)))
-									return ret;
+									RETURN(ret);
 								GET_USER(bname[j], p++);
 								switch (bname[j]) {
 									case '\0':
@@ -1510,31 +1434,31 @@
 								dev->drv[drvidx]->msn2eaz[i] : "-",
 								(i < 9) ? "," : "\0");
 							if ((ret = copy_to_user(p, bname, strlen(bname) + 1)))
-								return ret;
+								RETURN(ret);
 							p += strlen(bname);
 						}
 					}
-					return 0;
+					RETURN(0);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCDBGVAR:
 				if (arg) {
 					if ((ret = copy_to_user((char *) arg, (char *) &dev, sizeof(ulong))))
-						return ret;
-					return 0;
+						RETURN(ret);
+					RETURN(0);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				break;
 			default:
 				if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
 					cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
 				else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if (arg) {
 					int i;
 					char *p;
 					if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					if (strlen(iocts.drvid)) {
 						if ((p = strchr(iocts.drvid, ',')))
 							*p = 0;
@@ -1547,21 +1471,21 @@
 					} else
 						drvidx = 0;
 					if (drvidx == -1)
-						return -ENODEV;
+						RETURN(-ENODEV);
 					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
 					     sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					c.driver = drvidx;
 					c.command = ISDN_CMD_IOCTL;
 					c.arg = cmd;
 					memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
-					ret = dev->drv[drvidx]->interface->command(&c);
+					ret = isdn_command(&c);
 					memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
 					if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct))))
-						return -EFAULT;
-					return ret;
+						RETURN(-EFAULT);
+					RETURN(ret);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 		}
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1575,6 +1499,8 @@
 #undef iocts
 #undef phone
 #undef cfg
+
+#undef RETURN
 }
 
 /*
@@ -1588,7 +1514,6 @@
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
 	int chidx;
-	isdn_ctrl c;
 
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p;
@@ -1611,31 +1536,25 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
-		if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+		if (!(dev->drv[drvidx]->online & (1 << chidx)))
 			return -ENODEV;
-		c.command = ISDN_CMD_LOCK;
-		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
-		MOD_INC_USE_COUNT;
+		isdn_MOD_INC_USE_COUNT();
 		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 		if (drvidx < 0)
 			return -ENODEV;
-		c.command = ISDN_CMD_LOCK;
-		c.driver = drvidx;
-		MOD_INC_USE_COUNT;
-		(void) dev->drv[drvidx]->interface->command(&c);
+		isdn_MOD_INC_USE_COUNT();
 		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX) {
 		int ret;
 		if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
-			MOD_INC_USE_COUNT;
+			isdn_MOD_INC_USE_COUNT();
 		return ret;
 	}
 #endif
@@ -1646,13 +1565,13 @@
 isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
-	int drvidx;
-	isdn_ctrl c;
 
-	MOD_DEC_USE_COUNT;
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p = dev->infochain;
 		infostruct *q = NULL;
+
+
+		MOD_DEC_USE_COUNT;
 		while (p) {
 			if (p->private == (char *) &(filep->private_data)) {
 				if (q)
@@ -1668,24 +1587,12 @@
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
 		return CLOSEVAL;
 	}
-	if (minor < ISDN_MINOR_CTRL) {
-		drvidx = isdn_minor2drv(minor);
-		if (drvidx < 0)
-			return CLOSEVAL;
-		c.command = ISDN_CMD_UNLOCK;
-		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
+	isdn_MOD_DEC_USE_COUNT();
+	if (minor < ISDN_MINOR_CTRL)
 		return CLOSEVAL;
-	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
-		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-		if (drvidx < 0)
-			return CLOSEVAL;
 		if (dev->profd == current)
 			dev->profd = NULL;
-		c.command = ISDN_CMD_UNLOCK;
-		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
 		return CLOSEVAL;
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1739,7 +1646,6 @@
 	int i;
 	ulong flags;
 	ulong features;
-	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
@@ -1751,16 +1657,12 @@
 			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
 			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
 				continue;
-			if ((dev->drv[d]->running)) {
+			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
 				if ((dev->drv[d]->interface->features & features) == features) {
 					if ((pre_dev < 0) || (pre_chan < 0)) {
 						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 						dev->usage[i] |= usage;
 						isdn_info_update();
-						cmd.driver = d;
-						cmd.arg = 0;
-						cmd.command = ISDN_CMD_LOCK;
-						(void) dev->drv[d]->interface->command(&cmd);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1768,10 +1670,6 @@
 							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 							dev->usage[i] |= usage;
 							isdn_info_update();
-							cmd.driver = d;
-							cmd.arg = 0;
-							cmd.command = ISDN_CMD_LOCK;
-							(void) dev->drv[d]->interface->command(&cmd);
 							restore_flags(flags);
 							return i;
 						}
@@ -1807,11 +1705,12 @@
 			isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
 			cmd.driver = di;
 			cmd.arg = ch;
-			cmd.command = ISDN_CMD_UNLOCK;
+			cmd.command = ISDN_CMD_HANGUP;
 			restore_flags(flags);
-			(void) dev->drv[di]->interface->command(&cmd);
+			isdn_command(&cmd);
 			return;
 		}
+	printk(KERN_DEBUG "Unmatched free_channel d=%d c=%d u=%d\n", di, ch, usage);
 	restore_flags(flags);
 }
 
@@ -2007,12 +1906,11 @@
 	}
 	memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * n);
 	d->channels = n;
-	d->loaded = 1;
 	d->maxbufsize = i->maxbufsize;
 	d->pktcount = 0;
 	d->stavail = 0;
-	d->running = 0;
-	d->flags = 0;
+	d->flags = DRV_FLAG_LOADED;
+	d->online = 0;
 	d->interface = i;
 	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
 		if (!dev->drv[drvidx])

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