patch-2.3.4 linux/drivers/isdn/isdn_tty.c

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

diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
@@ -1,8 +1,8 @@
-/* $Id: isdn_tty.c,v 1.47 1998/02/22 19:44:14 fritz Exp $
+/* $Id: isdn_tty.c,v 1.63 1999/04/12 12:33:39 fritz Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,73 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.63  1999/04/12 12:33:39  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.62  1999/03/02 12:04:48  armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ *  register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ *  establishment and register S7.
+ *
+ * Revision 1.61  1999/01/27 22:53:11  he
+ * minor updates (spellings, jiffies wrap around in isdn_tty)
+ *
+ * Revision 1.60  1998/11/15 23:57:32  keil
+ * changes for 2.1.127
+ *
+ * Revision 1.59  1998/08/20 13:50:15  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.58  1998/07/26 18:48:45  armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.57  1998/06/26 15:12:36  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.56  1998/06/18 23:31:51  fritz
+ * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
+ * Removed direct-senddown feature in isdn_tty_write because it will
+ * never succeed with locking and is useless anyway.
+ *
+ * Revision 1.55  1998/06/17 19:50:55  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.54  1998/06/07 00:20:13  fritz
+ * abc cleanup.
+ *
+ * Revision 1.53  1998/06/02 12:10:16  detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.52  1998/03/19 13:18:21  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 1.51  1998/03/08 14:26:11  detabc
+ * change kfree_skb to dev_kfree_skb
+ * remove SET_SKB_FREE
+ *
+ * Revision 1.50  1998/03/08 13:14:28  detabc
+ * abc-extension support for kernels > 2.1.x
+ * first try (sorry experimental)
+ *
+ * Revision 1.49  1998/03/08 00:01:59  fritz
+ * Bugfix: Lowlevel module usage and channel usage were not
+ *         reset on NO DCHANNEL.
+ *
+ * Revision 1.48  1998/03/07 12:28:15  tsbogend
+ * fixed kernel unaligned traps on Linux/Alpha
+ *
  * Revision 1.47  1998/02/22 19:44:14  fritz
  * Bugfixes and improvements regarding V.110, V.110 now running.
  *
@@ -254,7 +321,7 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.47 $";
+char *isdn_tty_revision = "$Revision: 1.63 $";
 
 #define DLE 0x10
 #define ETX 0x03
@@ -270,6 +337,8 @@
 #define REG_LF        4
 #define REG_BS        5
 
+#define REG_WAITC     7
+
 #define REG_RESP     12
 #define BIT_RESP      1
 #define REG_RESPNUM  12
@@ -287,8 +356,6 @@
 #define REG_CPPP     12
 #define BIT_CPPP    128
 
-#define REG_DELXMT   13
-#define BIT_DELXMT    1
 #define REG_T70      13
 #define BIT_T70       2
 #define BIT_T70_EXT  32
@@ -300,6 +367,8 @@
 #define BIT_CIDONCE  16
 #define REG_RUNG     13
 #define BIT_RUNG     64
+#define REG_RESRXT   13
+#define BIT_RESRXT  128
 
 #define REG_L2PROT   14
 #define REG_L3PROT   15
@@ -389,6 +458,8 @@
 				r = 0;
 #ifdef CONFIG_ISDN_AUDIO
 				isdn_audio_eval_dtmf(info);
+				if ((info->vonline & 1) && (info->emu.vpar[1]))
+					isdn_audio_eval_silence(info);
 #endif
 				if ((tty = info->tty)) {
 					if (info->mcr & UART_MCR_RTS) {
@@ -445,6 +516,8 @@
 	
 	if (info->vonline)
 		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+	if ((info->vonline & 1) && (info->emu.vpar[1]))
+		isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
 #endif
 	if ((info->online < 2)
 #ifdef CONFIG_ISDN_AUDIO
@@ -566,8 +639,8 @@
 					   info->isdn_channel, 1, skb)) == len) {
 		struct tty_struct *tty = info->tty;
 		info->send_outstanding++;
-		info->msr |= UART_MSR_CTS;
-		info->lsr |= UART_LSR_TEMT;
+		info->msr &= ~UART_MSR_CTS;
+		info->lsr &= ~UART_LSR_TEMT;
 		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 		    tty->ldisc.write_wakeup)
 			(tty->ldisc.write_wakeup) (tty);
@@ -579,8 +652,6 @@
 		dev_kfree_skb(skb);
 		return;
 	}
-	if (slen)
-		skb_pull(skb, slen);
 	skb_queue_head(&info->xmit_queue, skb);
 }
 
@@ -702,7 +773,6 @@
 	int audio_len;
 #endif
 	struct sk_buff *skb;
-	unsigned long flags;
 
 #ifdef CONFIG_ISDN_AUDIO
 	if (info->vonline & 4) {
@@ -717,18 +787,20 @@
 		}
 	}
 #endif
-	save_flags(flags);
-	cli();
-	if (!(buflen = info->xmit_count)) {
-		restore_flags(flags);
+	if (!(buflen = info->xmit_count))
 		return;
-	}
-	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
+ 	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
 		info->msr &= ~UART_MSR_CTS;
-	info->lsr &= ~UART_LSR_TEMT;
+	info->lsr &= ~UART_LSR_TEMT;	
+	/* info->xmit_count is modified here and in isdn_tty_write().
+	 * So we return here if isdn_tty_write() is in the
+	 * critical section.
+	 */
+	atomic_inc(&info->xmit_lock);
+	if (!(atomic_dec_and_test(&info->xmit_lock)))
+		return;
 	if (info->isdn_driver < 0) {
 		info->xmit_count = 0;
-		restore_flags(flags);
 		return;
 	}
 	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
@@ -742,7 +814,6 @@
 	skb = dev_alloc_skb(skb_res + buflen);
 #endif
 	if (!skb) {
-		restore_flags(flags);
 		printk(KERN_WARNING
 		       "isdn_tty: Out of memory in ttyI%d senddown\n",
 		       info->line);
@@ -751,7 +822,6 @@
 	skb_reserve(skb, skb_res);
 	memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
 	info->xmit_count = 0;
-	restore_flags(flags);
 #ifdef CONFIG_ISDN_AUDIO
 	if (info->vonline & 2) {
 		/* For now, ifmt is fixed to 1 (alaw), since this
@@ -859,7 +929,7 @@
 			break;
 		}
 #ifdef CONFIG_ISDN_AUDIO
-	if (si == 1) {
+	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -907,9 +977,11 @@
 		cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
 		cmd.command = ISDN_CMD_DIAL;
 		info->dialing = 1;
+		info->emu.carrierwait = 0;
 		strcpy(dev->num[i], n);
 		isdn_info_update();
 		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
 	}
 }
 
@@ -942,6 +1014,10 @@
 		kfree(info->dtmf_state);
 		info->dtmf_state = NULL;
 	}
+	if (info->silence_state) {
+		kfree(info->silence_state);
+		info->silence_state = NULL;
+	}
 	if (info->adpcms) {
 		kfree(info->adpcms);
 		info->adpcms = NULL;
@@ -965,8 +1041,9 @@
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
 		info->emu.mdmreg[REG_RINGCNT] = 0;
-		usage = (info->emu.mdmreg[REG_SI1I] == 1) ?
-		    ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
+		usage = ((info->emu.mdmreg[REG_SI1I] != 1) || 
+		    (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ?
+			ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
 				  usage);
 	}
@@ -978,6 +1055,226 @@
 	}
 }
 
+/*
+ * Begin of a CAPI like interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h"  /* this should be moved in a common place */
+
+int
+isdn_tty_capi_facility(capi_msg *cm) {
+	return(-1); /* dummy */
+}
+
+/* isdn_tty_suspend() tries to suspend the current tty connection
+ */
+static void
+isdn_tty_suspend(char *id, modem_info * info, atemu * m)
+{
+	isdn_ctrl cmd;
+	
+	int l;
+
+	if (!info)
+		return;
+
+#ifdef ISDN_DEBUG_MODEM_SERVICES
+	printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
+#endif
+	l = strlen(id);
+	if ((info->isdn_driver >= 0) && l) {
+		cmd.parm.cmsg.Length = l+17;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l + 3;
+		cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command = CAPI_PUT_MESSAGE;
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		isdn_command(&cmd);
+	}
+}
+
+/* isdn_tty_resume() tries to resume a suspended call
+ * setup of the lower levels before that. unfortunatly here is no
+ * checking for compatibility of used protocols implemented by Q931
+ * It does the same things like isdn_tty_dial, the last command
+ * is different, may be we can merge it.
+ */
+
+static void
+isdn_tty_resume(char *id, modem_info * info, atemu * m)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(id);
+	if (!l) {
+		isdn_tty_modem_result(4, info);
+		return;
+	}
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(6, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+//		strcpy(info->last_num, n);
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+17;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l+3;
+		cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command =CAPI_PUT_MESSAGE;
+/*		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+*/
+		isdn_command(&cmd);
+	}
+}
+
+/* isdn_tty_send_msg() sends a message to a HL driver
+ * This is used for hybrid modem cards to send AT commands to it
+ */
+
+static void
+isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(msg);
+	if (!l) {
+		isdn_tty_modem_result(4, info);
+		return;
+	}
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(6, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+14;
+		cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = l+1;
+		strncpy(&cmd.parm.cmsg.para[1], msg, l);
+		cmd.parm.cmsg.para[l+1] = 0xd;
+		cmd.command =CAPI_PUT_MESSAGE;
+/*		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+*/
+		isdn_command(&cmd);
+	}
+}
+
 static inline int
 isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
 {
@@ -1138,15 +1435,16 @@
 {
 	int c;
 	int total = 0;
-	ulong flags;
 	modem_info *info = (modem_info *) tty->driver_data;
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
 		return 0;
 	if (!tty)
 		return 0;
-	save_flags(flags);
-	cli();
+	if (from_user)
+		down(&info->write_sem);
+	/* See isdn_tty_senddown() */
+	atomic_inc(&info->xmit_lock);
 	while (1) {
 		c = MIN(count, info->xmit_size - info->xmit_count);
 		if (info->isdn_driver >= 0)
@@ -1205,10 +1503,6 @@
 			} else
 #endif
 				info->xmit_count += c;
-			if (m->mdmreg[REG_DELXMT] & BIT_DELXMT) {
-				isdn_tty_senddown(info);
-				isdn_tty_tint(info);
-			}
 		} else {
 			info->msr |= UART_MSR_CTS;
 			info->lsr |= UART_LSR_TEMT;
@@ -1228,7 +1522,9 @@
 	}
 	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
 		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
-	restore_flags(flags);
+	atomic_dec(&info->xmit_lock);
+	if (from_user)
+		up(&info->write_sem);
 	return total;
 }
 
@@ -1359,7 +1655,7 @@
 	status = info->lsr;
 	restore_flags(flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-	put_user(result, (ulong *) value);
+	put_user(result, (uint *) value);
 	return 0;
 }
 
@@ -1383,7 +1679,7 @@
 	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
 	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
 	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-	put_user(result, (ulong *) value);
+	put_user(result, (uint *) value);
 	return 0;
 }
 
@@ -1567,7 +1863,12 @@
 static int
 isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
 {
-	DECLARE_WAITQUEUE(wait, current); 
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue wait =
+	{current, NULL};
+#else
+	DECLARE_WAITQUEUE(wait, NULL);
+#endif
 	int do_clocal = 0;
 	unsigned long flags;
 	int retval;
@@ -1891,6 +2192,7 @@
 	m->profile[19] = 0;
 	m->profile[20] = 0;
 	m->pmsn[0] = '\0';
+	m->plmsn[0] = '\0';
 }
 
 #ifdef CONFIG_ISDN_AUDIO
@@ -1911,6 +2213,7 @@
 	if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
 		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
 		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+		memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
 		info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
 	}
 #ifdef CONFIG_ISDN_AUDIO
@@ -1924,6 +2227,7 @@
 {
 	memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
 	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+	memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
 	if (dev->profd)
 		send_sig(SIGIO, dev->profd, 1);
 }
@@ -1987,6 +2291,11 @@
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		info = &m->info[i];
+#if LINUX_VERSION_CODE < 131841
+		info->write_sem = MUTEX;
+#else
+		init_MUTEX(&info->write_sem);
+#endif
 		sprintf(info->last_cause, "0000");
 		sprintf(info->last_num, "none");
 		info->last_dir = 0;
@@ -2003,8 +2312,13 @@
 		info->blocked_open = 0;
 		info->callout_termios = m->cua_modem.init_termios;
 		info->normal_termios = m->tty_modem.init_termios;
+#if LINUX_VERSION_CODE < 131841
+		info->open_wait = 0;
+		info->close_wait = 0;
+#else
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
+#endif
 		info->isdn_driver = -1;
 		info->isdn_channel = -1;
 		info->drv_index = -1;
@@ -2023,25 +2337,67 @@
 	return 0;
 }
 
+static int
+isdn_tty_match_icall(char *cid, atemu *emu, int di)
+{
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
+	       emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
+	       emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
+#endif
+	if (strlen(emu->lmsn)) {
+		char *p = emu->lmsn;
+		char *q;
+		int  tmp;
+		int  ret = 0;
+
+		while (1) {
+			if ((q = strchr(p, ';')))
+				*q = '\0';
+			if ((tmp = isdn_wildmat(cid, isdn_map_eaz2msn(p, di))) > ret)
+				ret = tmp;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
+			       p, isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+			if (q) {
+				*q = ';';
+				p = q;
+				p++;
+			}
+			if (!tmp)
+				return 0;
+			if (!q)
+				break;
+		}
+		return ret;
+	} else
+		return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+}
+
 /*
  * An incoming call-request has arrived.
  * Search the tty-devices for an appropriate device and bind
  * it to the ISDN-Channel.
- * Return Index to dev->mdm or -1 if none found.
+ * Return:
+ *
+ *  0 = No matching device found.
+ *  1 = A matching device found.
+ *  3 = No match found, but eventually would match, if
+ *      CID is longer.
  */
 int
 isdn_tty_find_icall(int di, int ch, setup_parm setup)
 {
 	char *eaz;
 	int i;
+	int wret;
 	int idx;
 	int si1;
 	int si2;
 	char nr[32];
 	ulong flags;
 
-	save_flags(flags);
-	cli();
 	if (!setup.phone[0]) {
 		nr[0] = '0';
 		nr[1] = '\0';
@@ -2058,17 +2414,14 @@
 #ifdef ISDN_DEBUG_MODEM_ICALL
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
+	wret = 0;
+	save_flags(flags);
+	cli();
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		modem_info *info = &dev->mdm.info[i];
-#ifdef ISDN_DEBUG_MODEM_ICALL
-		printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
-		       info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
-		       info->emu.mdmreg[REG_SI1], info->emu.mdmreg[REG_SI2]);
-#endif
-		if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
-			     eaz)) &&	                          /* EAZ is matching */
-		    (info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
-		    (info->emu.mdmreg[REG_SI2] == si2)) {	  /* SI2 is matching */
+
+		if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
+		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: match1\n");
@@ -2080,34 +2433,42 @@
 #ifndef FIX_FILE_TRANSFER
 				(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
 #endif
-			    (info->isdn_driver == -1) &&
-			    (info->isdn_channel == -1) &&
-			    (USG_NONE(dev->usage[idx]))) {
-				info->isdn_driver = di;
-				info->isdn_channel = ch;
-				info->drv_index = idx;
-				dev->m_idx[idx] = info->line;
-				dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-				dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
-				strcpy(dev->num[idx], nr);
-				info->emu.mdmreg[REG_SI1I] = si2bit[si1];
-				info->emu.mdmreg[REG_PLAN] = setup.plan;
-				info->emu.mdmreg[REG_SCREEN] = setup.screen;
-				isdn_info_update();
-				restore_flags(flags);
-				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
-				       info->line);
-				info->msr |= UART_MSR_RI;
-				isdn_tty_modem_result(2, info);
-				isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
-				return info->line;
+				(info->isdn_driver == -1) &&
+				(info->isdn_channel == -1) &&
+				(USG_NONE(dev->usage[idx]))) {
+				int matchret;
+
+				if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
+					wret = matchret;
+				if (!matchret) {                  /* EAZ is matching */
+					info->isdn_driver = di;
+					info->isdn_channel = ch;
+					info->drv_index = idx;
+					dev->m_idx[idx] = info->line;
+					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+					dev->usage[idx] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? 
+						ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
+					strcpy(dev->num[idx], nr);
+					strcpy(info->emu.cpn, eaz);
+					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+					info->emu.mdmreg[REG_PLAN] = setup.plan;
+					info->emu.mdmreg[REG_SCREEN] = setup.screen;
+					isdn_info_update();
+					restore_flags(flags);
+					printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+					       info->line);
+					info->msr |= UART_MSR_RI;
+					isdn_tty_modem_result(2, info);
+					isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+					return 1;
+				}
 			}
 		}
 	}
-	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
-	       dev->drv[di]->reject_bus ? "rejected" : "ignored");
 	restore_flags(flags);
-	return -1;
+	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+	       ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
+	return (wret == 2)?3:0;
 }
 
 #define TTY_IS_ACTIVE(info) \
@@ -2128,7 +2489,7 @@
                         case ISDN_STAT_CINF:
                                 printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
                                 info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
-                                if (e == c->parm.num)
+                                if (e == (char *)c->parm.num)
 					info->emu.charge = 0;
 				
                                 break;			
@@ -2169,10 +2530,11 @@
 				printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
 #endif
 				if (TTY_IS_ACTIVE(info)) {
-					if (info->dialing == 1) {
-						info->dialing = 0;
+					if (info->dialing == 1) 
 						isdn_tty_modem_result(7, info);
-					}
+					if (info->dialing > 1) 
+						isdn_tty_modem_result(3, info);
+					info->dialing = 0;
 #ifdef ISDN_DEBUG_MODEM_HUP
 					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
 #endif
@@ -2191,14 +2553,20 @@
 				if (TTY_IS_ACTIVE(info)) {
 					info->msr |= UART_MSR_DCD;
 					info->emu.charge = 0;
-					if (info->dialing) {
-						info->dialing = 0;
+					if (info->dialing & 0xf)
 						info->last_dir = 1;
-					} else
+					else
 						info->last_dir = 0;
+					info->dialing = 0;
 					info->rcvsched = 1;
-					if (USG_MODEM(dev->usage[i]))
-						isdn_tty_modem_result(5, info);
+					if (USG_MODEM(dev->usage[i])) {
+						if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
+							strcpy(info->emu.connmsg, c->parm.num);
+							isdn_tty_modem_result(1, info);
+						}
+						else
+							isdn_tty_modem_result(5, info);
+					}
 					if (USG_VOICE(dev->usage[i]))
 						isdn_tty_modem_result(11, info);
 					return 1;
@@ -2228,11 +2596,7 @@
 						sprintf(info->last_cause, "0000");
 						isdn_tty_modem_result(6, info);
 					}
-					info->msr &= ~UART_MSR_DCD;
-					if (info->online) {
-						isdn_tty_modem_result(3, info);
-						info->online = 0;
-					}
+					isdn_tty_modem_hup(info, 0);
 					return 1;
 				}
 				break;
@@ -2393,7 +2757,7 @@
 	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
 	 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
 	ulong flags;
-	char s[10];
+	char s[ISDN_MSNLEN+10];
 
 	switch (code) {
 		case 2:
@@ -2473,7 +2837,20 @@
 			}
 			isdn_tty_at_cout(msg[code], info);
 			switch (code) {
+				case 1:
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_MODEM:
+							isdn_tty_at_cout(" ", info);
+							isdn_tty_at_cout(m->connmsg, info);
+							break;
+					}
+					break;
 				case 2:
+					/* Append CPN, if enabled */
+					if ((m->mdmreg[REG_RESRXT] & BIT_RESRXT)) {
+						sprintf(s, "/%s", m->cpn);
+						isdn_tty_at_cout(s, info);
+					}
 					/* Print CID only once, _after_ 1.st RING */
 					if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
 					    (m->mdmreg[REG_RINGCNT] == 1)) {
@@ -2560,7 +2937,9 @@
 static void
 isdn_tty_get_msnstr(char *n, char **p)
 {
-	while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
+	while ((*p[0] >= '0' && *p[0] <= '9') ||
+	       /* Why a comma ??? */
+	       (*p[0] == ','))
 		*n++ = *p[0]++;
 	*n = '\0';
 }
@@ -2626,6 +3005,9 @@
 		case ISDN_PROTO_L2_TRANS:
 			isdn_tty_at_cout("transparent", info);
 			break;
+		case ISDN_PROTO_L2_MODEM:
+			isdn_tty_at_cout("modem", info);
+			break;
 		default:
 			isdn_tty_at_cout("unknown", info);
 			break;
@@ -2668,6 +3050,8 @@
 	int i;
 	char rb[100];
 
+#define MAXRB (sizeof(rb) - 1)
+
 	switch (*p[0]) {
 		case 'B':
 			/* &B - Set Buffersize */
@@ -2719,6 +3103,15 @@
 			isdn_tty_reset_profile(m);
 			isdn_tty_modem_reset_regs(info, 1);
 			break;
+		case 'L':
+			/* &L -Set Numbers to listen on */
+			p[0]++;
+			i = 0;
+			while ((strchr("0123456789,*[]?;", *p[0])) &&
+			       (i < ISDN_LMSNLEN))
+				m->lmsn[i++] = *p[0]++;
+			m->lmsn[i] = '\0';
+			break;
 		case 'R':
 			/* &R - Set V.110 bitrate adaption */
 			p[0]++;
@@ -2774,6 +3167,11 @@
 			sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
 				strlen(m->msn) ? m->msn : "None");
 			isdn_tty_at_cout(rb, info);
+			if (strlen(m->lmsn)) {
+				isdn_tty_at_cout("\r\nListen: ", info);
+				isdn_tty_at_cout(m->lmsn, info);
+				isdn_tty_at_cout("\r\n", info);
+			}
 			break;
 		case 'W':
 			/* &W - Write Profile */
@@ -2939,9 +3337,10 @@
 #ifdef CONFIG_ISDN_AUDIO
 		/* If more than one bit set in reg18, autoselect Layer2 */
 		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
-			if (m->mdmreg[REG_SI1I] == 1)
-				l2 = ISDN_PROTO_L2_TRANS;
-			else
+			if (m->mdmreg[REG_SI1I] == 1) {
+				if (l2 != ISDN_PROTO_L2_MODEM)
+					l2 = ISDN_PROTO_L2_TRANS;
+			} else
 				l2 = ISDN_PROTO_L2_X75I;
 		}
 #endif
@@ -2957,7 +3356,10 @@
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
 		cmd.command = ISDN_CMD_ACCEPTD;
+		info->dialing = 16;
+		info->emu.carrierwait = 0;
 		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
 	} else
 		isdn_tty_modem_result(8, info);
 }
@@ -3221,6 +3623,11 @@
 				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
 				PARSE_ERROR1;
 			}
+			info->silence_state = isdn_audio_silence_init(info->silence_state);
+			if (!info->silence_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+				PARSE_ERROR1;
+			}
 			if (m->vpar[3] < 5) {
 				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
 				if (!info->adpcmr) {
@@ -3247,31 +3654,27 @@
 					break;
 				case '=':
 					p[0]++;
-					switch (*p[0]) {
-						case '0':
-						case '1':
-						case '2':
-						case '3':
-							par1 = isdn_getnum(p);
-							if ((par1 < 0) || (par1 > 31))
-								PARSE_ERROR1;
-							if (*p[0] != ',')
-								PARSE_ERROR1;
-							p[0]++;
-							par2 = isdn_getnum(p);
-							if ((par2 < 0) || (par2 > 255))
-								PARSE_ERROR1;
-							m->vpar[1] = par1;
-							m->vpar[2] = par2;
-							break;
-						case '?':
-							p[0]++;
-							isdn_tty_at_cout("\r\n<0-31>,<0-255>",
-								   info);
-							break;
-						default:
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 31))
 							PARSE_ERROR1;
-					}
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[1] = par1;
+						m->vpar[2] = par2;
+						break;
+					} else 
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+							   info);
+						break;
+					} else
+					PARSE_ERROR1;
 					break;
 				default:
 					PARSE_ERROR1;
@@ -3445,7 +3848,7 @@
 				p++;
 				if (info->msr & UART_MSR_DCD)
 					/* if B-Channel is up */
-					isdn_tty_modem_result(5, info);
+					isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? 1:5, info);
 				else
 					isdn_tty_modem_result(3, info);
 				return;
@@ -3486,29 +3889,46 @@
 			case 'Z':
 				/* Z - Load Registers from Profile */
 				p++;
+				if (info->msr & UART_MSR_DCD) {
+					info->online = 0;
+					isdn_tty_on_hook(info);
+				}
 				isdn_tty_modem_reset_regs(info, 1);
 				break;
-#ifdef CONFIG_ISDN_AUDIO
 			case '+':
 				p++;
 				switch (*p) {
+#ifdef CONFIG_ISDN_AUDIO
 					case 'F':
 						p++;
 						if (isdn_tty_cmd_PLUSF(&p, info))
 							return;
 						break;
 					case 'V':
-						if (!(m->mdmreg[REG_SI1] & 1))
+						if ((!(m->mdmreg[REG_SI1] & 1)) ||
+							(m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
 							PARSE_ERROR;
 						p++;
 						if (isdn_tty_cmd_PLUSV(&p, info))
 							return;
 						break;
+#endif                          /* CONFIG_ISDN_AUDIO */
+					case 'S':	/* SUSPEND */
+						p++;
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_suspend(ds, info, m);
+						break;
+					case 'R':	/* RESUME */
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_resume(ds, info, m);
+					case 'M':	/* MESSAGE */
+						p++;
+						isdn_tty_send_msg(info, m, p);
+						break;
 					default:
 						PARSE_ERROR;
 				}
 				break;
-#endif                          /* CONFIG_ISDN_AUDIO */
 			case '&':
 				p++;
 				if (isdn_tty_cmd_ATand(&p, info))
@@ -3672,4 +4092,29 @@
 		}
 	}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+/*
+ * Check all channels if we have a 'no carrier' timeout.
+ * Timeout value is set by Register S7.
+ */
+void
+isdn_tty_carrier_timeout(void)
+{
+	int ton = 0;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->dialing) {
+			if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
+				info->dialing = 0;
+				isdn_tty_modem_result(3, info);
+				isdn_tty_modem_hup(info, 1);
+			}
+			else
+				ton = 1;
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
 }

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