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

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

diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $
+/* $Id: isdn_common.c,v 1.55 1998/02/23 23:35:32 fritz Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -21,6 +21,55 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.55  1998/02/23 23:35:32  fritz
+ * Eliminated some compiler warnings.
+ *
+ * Revision 1.54  1998/02/22 19:44:19  fritz
+ * Bugfixes and improvements regarding V.110, V.110 now running.
+ *
+ * Revision 1.53  1998/02/20 17:18:05  fritz
+ * Changes for recent kernels.
+ * Added common stub for sending commands to lowlevel.
+ * Added V.110.
+ *
+ * Revision 1.52  1998/01/31 22:05:57  keil
+ * Lots of changes for X.25 support:
+ * Added generic support for connection-controlling encapsulation protocols
+ * Added support of BHUP status message
+ * Added support for additional p_encap X25IFACE
+ * Added support for kernels >= 2.1.72
+ *
+ * Revision 1.51  1998/01/31 19:17:29  calle
+ * merged changes from and for 2.1.82
+ *
+ * Revision 1.50  1997/12/12 06:12:11  calle
+ * moved EXPORT_SYMBOL(register_isdn) from isdn_syms.c to isdn_common.c
+ *
+ * Revision 1.49  1997/11/06 17:16:52  keil
+ * Sync to 2.1.62 changes
+ *
+ * Revision 1.48  1997/11/02 23:55:50  keil
+ * Andi Kleen's changes for 2.1.60
+ * without it the isdninfo and isdnctrl devices won't work
+ *
+ * Revision 1.47  1997/10/09 21:28:46  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.46  1997/10/01 09:20:27  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.45  1997/08/21 23:11:41  fritz
+ * Added changes for kernels >= 2.1.45
+ *
  * Revision 1.44  1997/05/27 15:17:23  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -197,12 +246,9 @@
  */
 
 #include <linux/config.h>
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#if (LINUX_VERSION_CODE >= 0x020117)
 #include <linux/poll.h>
-#endif
 #include <linux/isdn.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
@@ -211,6 +257,7 @@
 #ifdef CONFIG_ISDN_AUDIO
 #include "isdn_audio.h"
 #endif
+#include "isdn_v110.h"
 #include "isdn_cards.h"
 
 /* Debugflags */
@@ -218,7 +265,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.44 $";
+static char *isdn_revision = "$Revision: 1.55 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -232,6 +279,7 @@
 #else
 static char *isdn_audio_revision = ": none $";
 #endif
+extern char *isdn_v110_revision;
 
 static int isdn_writebuf_stub(int, int, const u_char *, int, int);
 
@@ -260,13 +308,6 @@
 }
 #endif
 
-static __inline void
-isdn_trash_skb(struct sk_buff *skb)
-{
-	SET_SKB_FREE(skb);
-	kfree_skb(skb);
-}
-
 static void
 isdn_free_queue(struct sk_buff_head *queue)
 {
@@ -277,7 +318,7 @@
 	cli();
 	if (skb_queue_len(queue))
 		while ((skb = skb_dequeue(queue)))
-			isdn_trash_skb(skb);
+			dev_kfree_skb(skb);
 	restore_flags(flags);
 }
 
@@ -294,6 +335,7 @@
 static int isdn_timer_cnt1 = 0;
 static int isdn_timer_cnt2 = 0;
 static int isdn_timer_cnt3 = 0;
+static int isdn_timer_cnt4 = 0;
 
 static void
 isdn_timer_funct(ulong dummy)
@@ -323,6 +365,11 @@
 				if (tf & ISDN_TIMER_MODEMRING)
 					isdn_tty_modem_ring();
 			}
+			if (++isdn_timer_cnt4 > ISDN_TIMER_KEEPINT) {
+				isdn_timer_cnt4 = 0;
+				if (tf & ISDN_TIMER_KEEPALIVE)
+					isdn_net_slarp_out();
+			}
 #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP)
 			if (tf & ISDN_TIMER_IPPP)
 				isdn_ppp_timer_timeout();
@@ -374,22 +421,71 @@
 	int i;
 
 	if ((i = isdn_dc2minor(di, channel)) == -1) {
-		isdn_trash_skb(skb);
+		dev_kfree_skb(skb);
 		return;
 	}
 	/* Update statistics */
 	dev->ibytes[i] += skb->len;
+	
 	/* First, try to deliver data to network-device */
 	if (isdn_net_rcv_skb(i, skb))
 		return;
+
+	/* V.110 handling
+	 * makes sense for async streams only, so it is
+	 * called after possible net-device delivery.
+	 */
+	if (dev->v110[i]) {
+		atomic_inc(&dev->v110use[i]);
+		skb = isdn_v110_decode(dev->v110[i], skb);
+		atomic_dec(&dev->v110use[i]);
+		if (!skb)
+			return;
+	}
+
 	/* No network-device found, deliver to tty or raw-channel */
-	SET_SKB_FREE(skb);
 	if (skb->len) {
 		if (isdn_tty_rcv_skb(i, di, channel, skb))
 			return;
 		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
 	} else
-		isdn_trash_skb(skb);
+		dev_kfree_skb(skb);
+}
+
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ * If layer 2 protocol is V.110 and this is not supported by current
+ * lowlevel-driver, use driver's transparent mode and handle V.110 in
+ * linklevel instead.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+	if (cmd->command == ISDN_CMD_SETL2) {
+			int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
+			unsigned long l2prot = (cmd->arg >> 8) & 255;
+			unsigned long features = (dev->drv[cmd->driver]->interface->features
+						 >> ISDN_FEATURE_L2_SHIFT) &
+				ISDN_FEATURE_L2_MASK;
+			unsigned long l2_feature = (1 << l2prot);
+
+			switch (l2prot) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+						/* If V.110 requested, but not supported by
+						 * HL-driver, set emulator-flag and change
+						 * Layer-2 to transparent
+						 */
+					if (!(features & l2_feature)) {
+						dev->v110emu[idx] = l2prot;
+						cmd->arg = (cmd->arg & 255) |
+								   (ISDN_PROTO_L2_TRANS << 8);
+					} else
+						dev->v110emu[idx] = 0;
+			}
+	}
+	return dev->drv[cmd->driver]->interface->command(cmd);
 }
 
 void
@@ -403,7 +499,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 +520,9 @@
 				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_v110_stat_callback(i, c))
 				return 0;
 			if (isdn_tty_stat_callback(i, c))
 				return 0;
@@ -456,14 +554,14 @@
 				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);
+			isdn_command(&cmd);
 			r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
 			switch (r) {
 				case 0:
@@ -476,7 +574,7 @@
 						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 +584,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 +594,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 */
@@ -509,7 +607,7 @@
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_UNLOCK;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_command(&cmd);
 			}
 			return retval;
 			break;
@@ -522,7 +620,8 @@
 			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);
+			isdn_tty_stat_callback(i, c);
 			break;
 		case ISDN_STAT_CAUSE:
 #ifdef ISDN_DEBUG_STATCALLB
@@ -541,14 +640,15 @@
 			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;
+			isdn_v110_stat_callback(i, c);
 			/* 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;
@@ -563,8 +663,9 @@
 			dev->drv[di]->flags &= ~(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;
+			isdn_v110_stat_callback(i, c);
 			if (isdn_tty_stat_callback(i, c))
 				break;
 			break;
@@ -579,8 +680,9 @@
 				return 0;
 			dev->drv[di]->flags |= (1 << (c->arg));
 			isdn_info_update();
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
+			isdn_v110_stat_callback(i, c);
 			if (isdn_tty_stat_callback(i, c))
 				break;
 			break;
@@ -594,6 +696,12 @@
 				return 0;
 			dev->drv[di]->flags &= ~(1 << (c->arg));
 			isdn_info_update();
+#ifdef CONFIG_ISDN_X25
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c))
+				break;
+#endif
+			isdn_v110_stat_callback(i, c);
 			if (isdn_tty_stat_callback(i, c))
 				break;
 			break;
@@ -605,7 +713,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;
@@ -636,6 +744,8 @@
 			isdn_info_update();
 			restore_flags(flags);
 			return 0;
+		case ISDN_STAT_L1ERR:
+			break;
 		default:
 			return -1;
 	}
@@ -752,7 +862,7 @@
 			ISDN_AUDIO_SKB_LOCK(skb) = 0;
 #endif
 			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
-			isdn_trash_skb(skb);
+			dev_kfree_skb(skb);
 		} else {
 			/* Not yet emptied this buff, so it
 			 * must stay in the queue, for further calls
@@ -848,8 +958,8 @@
 	wake_up_interruptible(&(dev->info_waitq));
 }
 
-static RWTYPE
-isdn_read(struct file *file, char *buf, RWARG count, loff_t *off)
+static ssize_t
+isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
 {
 	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	int len = 0;
@@ -857,6 +967,9 @@
 	int drvidx;
 	int chidx;
 
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
 	if (minor == ISDN_MINOR_STATUS) {
 		char *p;
 		if (!file->private_data) {
@@ -922,18 +1035,22 @@
 	return -ENODEV;
 }
 
-static LSTYPE isdn_lseek(struct file *file, LSARG offset, int orig)
+static loff_t
+isdn_lseek(struct file *file, loff_t offset, int orig)
 {
 	return -ESPIPE;
 }
 
-static RWTYPE
-isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off)
+static ssize_t
+isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
 {
 	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	int drvidx;
 	int chidx;
 
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
 	if (minor == ISDN_MINOR_STATUS)
 		return -EPERM;
 	if (!dev->drivers)
@@ -972,41 +1089,6 @@
 	return -ENODEV;
 }
 
-#if (LINUX_VERSION_CODE < 0x020117)
-static int
-isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
-{
-	uint minor = MINOR(inode->i_rdev);
-	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-
-	if (minor == ISDN_MINOR_STATUS) {
-		if (file->private_data)
-			return 1;
-		else {
-			if (st)
-				select_wait(&(dev->info_waitq), st);
-			return 0;
-		}
-	}
-	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
-		if (drvidx < 0)
-			return -ENODEV;
-		if (dev->drv[drvidx]->stavail)
-			return 1;
-		else {
-			if (st)
-				select_wait(&(dev->drv[drvidx]->st_waitq), st);
-			return 0;
-		}
-		return 1;
-	}
-#ifdef CONFIG_ISDN_PPP
-	if (minor <= ISDN_MINOR_PPPMAX)
-		return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st));
-#endif
-	return -ENODEV;
-}
-#else
 static unsigned int
 isdn_poll(struct file *file, poll_table * wait)
 {
@@ -1041,7 +1123,6 @@
 	printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n");
 	return POLLERR;
 }
-#endif
 
 static int
 isdn_set_allcfg(char *src)
@@ -1055,7 +1136,7 @@
 	if ((ret = isdn_net_rmall()))
 		return ret;
 	if ((ret = copy_from_user((char *) &i, src, sizeof(int))))
-		return ret;
+		 return ret;
 	save_flags(flags);
 	cli();
 	src += sizeof(int);
@@ -1082,7 +1163,7 @@
 				restore_flags(flags);
 				return ret;
 			}
-			GET_USER(phone.phone[phone_len], src++);
+			get_user(phone.phone[phone_len], src++);
 			if ((phone.phone[phone_len] == ' ') ||
 			    (phone.phone[phone_len] == '\0')) {
 				if (phone_len) {
@@ -1122,28 +1203,30 @@
 	cli();
 	p = dev->netdev;
 	while (p) {
+		isdn_net_local *lp = p->local;
+
 		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);
+		strcpy(cfg.eaz, lp->msn);
+		cfg.exclusive = lp->exclusive;
+		if (lp->pre_device >= 0) {
+			sprintf(cfg.drvid, "%s,%d", dev->drvid[lp->pre_device],
+				lp->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)) {
+		cfg.onhtime = lp->onhtime;
+		cfg.charge = lp->charge;
+		cfg.l2_proto = lp->l2_proto;
+		cfg.l3_proto = lp->l3_proto;
+		cfg.p_encap = lp->p_encap;
+		cfg.secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
+		cfg.callback = (lp->flags & ISDN_NET_CALLBACK) ? 1 : 0;
+		cfg.chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
+		cfg.ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0;
+		cfg.chargeint = lp->chargeint;
+		if (copy_to_user(dest, lp->name, 10)) {
 			restore_flags(flags);
 			return -EFAULT;
 		}
@@ -1153,14 +1236,14 @@
 			return -EFAULT;
 		}
 		dest += sizeof(cfg);
-		strcpy(phone.name, p->local.name);
+		strcpy(phone.name, lp->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);
+		strcpy(phone.name, lp->name);
 		phone.outgoing = 1;
 		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
 			restore_flags(flags);
@@ -1343,9 +1426,9 @@
 				/* Force hangup of a network-interface */
 				if (!arg)
 					return -EINVAL;
-			        if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-			        	return ret;
-			        return isdn_net_force_hangup(name);
+				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+					return ret;
+				return isdn_net_force_hangup(name);
 				break;
 #endif                          /* CONFIG_NETDEVICES */
 			case IIOCSETVER:
@@ -1366,7 +1449,7 @@
 					int i;
 					char *p;
 					if ((ret = copy_from_user((char *) &iocts, (char *) arg,
-					      sizeof(isdn_ioctl_struct))))
+					     sizeof(isdn_ioctl_struct))))
 						return ret;
 					if (strlen(iocts.drvid)) {
 						if ((p = strchr(iocts.drvid, ',')))
@@ -1440,7 +1523,7 @@
 
 					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p,
-						      ISDN_MODEM_ANZREG)))
+						     ISDN_MODEM_ANZREG)))
 							return ret;
 						p += ISDN_MODEM_ANZREG;
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)))
@@ -1482,7 +1565,7 @@
 							while (1) {
 								if ((ret = verify_area(VERIFY_READ, p, 1)))
 									return ret;
-								GET_USER(bname[j], p++);
+								get_user(bname[j], p++);
 								switch (bname[j]) {
 									case '\0':
 										loop = 0;
@@ -1554,7 +1637,7 @@
 					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;
@@ -1616,7 +1699,7 @@
 			return -ENODEV;
 		c.command = ISDN_CMD_LOCK;
 		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
+		isdn_command(&c);
 		MOD_INC_USE_COUNT;
 		return 0;
 	}
@@ -1627,7 +1710,7 @@
 		c.command = ISDN_CMD_LOCK;
 		c.driver = drvidx;
 		MOD_INC_USE_COUNT;
-		(void) dev->drv[drvidx]->interface->command(&c);
+		isdn_command(&c);
 		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1641,7 +1724,7 @@
 	return -ENODEV;
 }
 
-static CLOSETYPE
+static int
 isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
@@ -1659,39 +1742,39 @@
 				else
 					dev->infochain = (infostruct *) (p->next);
 				kfree(p);
-				return CLOSEVAL;
+				return 0;
 			}
 			q = p;
 			p = (infostruct *) (p->next);
 		}
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
-		return CLOSEVAL;
+		return 0;
 	}
 	if (minor < ISDN_MINOR_CTRL) {
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
-			return CLOSEVAL;
+			return 0;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
-		return CLOSEVAL;
+		isdn_command(&c);
+		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 		if (drvidx < 0)
-			return CLOSEVAL;
+			return 0;
 		if (dev->profd == current)
 			dev->profd = NULL;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
-		return CLOSEVAL;
+		isdn_command(&c);
+		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX)
 		isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
 #endif
-	return CLOSEVAL;
+	return 0;
 }
 
 static struct file_operations isdn_fops =
@@ -1700,11 +1783,7 @@
 	isdn_read,
 	isdn_write,
 	NULL,                   /* isdn_readdir */
-#if (LINUX_VERSION_CODE < 0x020117)
-	isdn_select,            /* isdn_select */
-#else
 	isdn_poll,              /* isdn_poll */
-#endif
 	isdn_ioctl,             /* isdn_ioctl */
 	NULL,                   /* isdn_mmap */
 	isdn_open,
@@ -1731,6 +1810,8 @@
  * Find an unused ISDN-channel, whose feature-flags match the
  * given L2- and L3-protocols.
  */
+#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
+
 int
 isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
 		      ,int pre_chan)
@@ -1738,11 +1819,18 @@
 	int i;
 	ulong flags;
 	ulong features;
+	ulong vfeatures;
 	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
-	features = (1 << l2_proto) | (0x100 << l3_proto);
+	features = ((1 << l2_proto) | (0x10000 << l3_proto));
+	vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
+		     ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
+	/* If Layer-2 protocol is V.110, accept drivers with
+	 * transparent feature even if these don't support V.110
+	 * because we can emulate this in linklevel.
+	 */
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 		if (USG_NONE(dev->usage[i]) &&
 		    (dev->drvmap[i] != -1)) {
@@ -1751,7 +1839,9 @@
 			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
 				continue;
 			if ((dev->drv[d]->running)) {
-				if ((dev->drv[d]->interface->features & features) == features) {
+				if (((dev->drv[d]->interface->features & features) == features) ||
+				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
+				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
 					if ((pre_dev < 0) || (pre_chan < 0)) {
 						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 						dev->usage[i] |= usage;
@@ -1759,7 +1849,7 @@
 						cmd.driver = d;
 						cmd.arg = 0;
 						cmd.command = ISDN_CMD_LOCK;
-						(void) dev->drv[d]->interface->command(&cmd);
+						isdn_command(&cmd);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1770,7 +1860,7 @@
 							cmd.driver = d;
 							cmd.arg = 0;
 							cmd.command = ISDN_CMD_LOCK;
-							(void) dev->drv[d]->interface->command(&cmd);
+							isdn_command(&cmd);
 							restore_flags(flags);
 							return i;
 						}
@@ -1808,7 +1898,7 @@
 			cmd.arg = ch;
 			cmd.command = ISDN_CMD_UNLOCK;
 			restore_flags(flags);
-			(void) dev->drv[di]->interface->command(&cmd);
+			isdn_command(&cmd);
 			return;
 		}
 	restore_flags(flags);
@@ -1837,31 +1927,6 @@
 }
 
 /*
- * receive callback handler for drivers not supporting sk_buff's.
- * Parameters:
- *
- * di      = Driver-Index.
- * channel = Number of B-Channel (0...)
- * buf     = pointer to packet-data
- * len     = Length of packet-data
- *
- */
-static void
-isdn_receive_callback(int drvidx, int chan, u_char * buf, int len)
-{
-	struct sk_buff *skb;
-
-	if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-		return;
-	skb = dev_alloc_skb(len);
-	if (skb) {
-		memcpy(skb_put(skb, len), buf, len);
-		isdn_receive_skb_callback(drvidx, chan, skb);
-	} else
-		printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n");
-}
-
-/*
  *  writebuf replacement for SKB_ABLE drivers
  */
 static int
@@ -1869,60 +1934,69 @@
 		   int user)
 {
 	int ret;
+	int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+	struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC);
 
-	if (dev->drv[drvidx]->interface->writebuf)
-		ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf,
-							    len, user);
-	else {
-		struct sk_buff *skb;
-
-		skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len,
-				GFP_ATOMIC);
-		if (skb == NULL)
-			return 0;
-
-		skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
-		SET_SKB_FREE(skb);
-
-		if (user)
-			copy_from_user(skb_put(skb, len), buf, len);
-		else
-			memcpy(skb_put(skb, len), buf, len);
+	if (!skb)
+		return 0;
+	skb_reserve(skb, hl);
+	if (user)
+		copy_from_user(skb_put(skb, len), buf, len);
+	else
+		memcpy(skb_put(skb, len), buf, len);
 
-		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx,
-							      chan, skb);
-		if (ret <= 0)
-			kfree_skb(skb);
-	}
+	ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
+	if (ret <= 0)
+		dev_kfree_skb(skb);
 	if (ret > 0)
 		dev->obytes[isdn_dc2minor(drvidx, chan)] += ret;
 	return ret;
 }
 
 /*
- * writebuf_skb replacement for NON SKB_ABLE drivers
- * If lowlevel-device does not support supports skbufs, use
- * standard send-routine, else sind directly.
- *
  * Return: length of data on success, -ERRcode on failure.
  */
-
 int
-isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb)
+isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
 {
 	int ret;
-	int len = skb->len;     /* skb pointer no longer valid after free */
-
-	if (dev->drv[drvidx]->interface->writebuf_skb)
-		ret = dev->drv[drvidx]->interface->
-		    writebuf_skb(drvidx, chan, skb);
-	else {
-		if ((ret = dev->drv[drvidx]->interface->
-		  writebuf(drvidx, chan, skb->data, skb->len, 0)) == len)
+	struct sk_buff *nskb = NULL;
+	int v110_ret = skb->len;
+	int idx = isdn_dc2minor(drvidx, chan);
+
+	if (dev->v110[idx]) {
+		atomic_inc(&dev->v110use[idx]);
+		nskb = isdn_v110_encode(dev->v110[idx], skb);
+		atomic_dec(&dev->v110use[idx]);
+		if (!nskb)
+			return 0;
+		v110_ret = *((int *)nskb->data);
+		skb_pull(nskb, sizeof(int));
+		if (!nskb->len) {
+			dev_kfree_skb(nskb);
 			dev_kfree_skb(skb);
-	}
-	if (ret > 0)
-		dev->obytes[isdn_dc2minor(drvidx, chan)] += len;
+			return v110_ret;
+		}
+		/* V.110 must always be acknowledged */
+		ack = 1;
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
+	} else
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+	if (ret > 0) {
+		dev->obytes[idx] += ret;
+		if (dev->v110[idx]) {
+			atomic_inc(&dev->v110use[idx]);
+			dev->v110[idx]->skbuser++;
+			atomic_dec(&dev->v110use[idx]);
+			dev_kfree_skb(skb);
+			/* For V.110 return unencoded data length */
+			ret = v110_ret;
+			if (ret == skb->len)
+				dev_kfree_skb(skb);
+		}
+	} else
+		if (dev->v110[idx])
+			dev_kfree_skb(nskb);
 	return ret;
 }
 
@@ -1930,6 +2004,8 @@
  * Low-level-driver registration
  */
 
+EXPORT_SYMBOL(register_isdn);
+
 int
 register_isdn(isdn_if * i)
 {
@@ -1951,7 +2027,7 @@
 		       ISDN_MAX_CHANNELS);
 		return 0;
 	}
-	if ((!i->writebuf_skb) && (!i->writebuf)) {
+	if (!i->writebuf_skb) {
 		printk(KERN_WARNING "register_isdn: No write routine given.\n");
 		return 0;
 	}
@@ -2019,7 +2095,6 @@
 	i->channels = drvidx;
 
 	i->rcvcallb_skb = isdn_receive_skb_callback;
-	i->rcvcallb = isdn_receive_callback;
 	i->statcallb = isdn_status_callback;
 	if (!strlen(i->id))
 		sprintf(i->id, "line%d", drvidx);
@@ -2078,11 +2153,7 @@
 isdn_init(void)
 {
 	int i;
-	char irev[50];
-	char trev[50];
-	char nrev[50];
-	char prev[50];
-	char arev[50];
+	char tmprev[50];
 
 	sti();
 	if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
@@ -2126,18 +2197,18 @@
 	}
 #endif                          /* CONFIG_ISDN_PPP */
 
-	isdn_export_syms();
-
-	strcpy(irev, isdn_revision);
-	strcpy(trev, isdn_tty_revision);
-	strcpy(nrev, isdn_net_revision);
-	strcpy(prev, isdn_ppp_revision);
-	strcpy(arev, isdn_audio_revision);
-	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(irev));
-	printk("%s/", isdn_getrev(trev));
-	printk("%s/", isdn_getrev(nrev));
-	printk("%s/", isdn_getrev(prev));
-	printk("%s", isdn_getrev(arev));
+	strcpy(tmprev, isdn_revision);
+	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_tty_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_net_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_ppp_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_audio_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_v110_revision);
+	printk("%s", isdn_getrev(tmprev));
 
 #ifdef MODULE
 	printk(" loaded\n");

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