patch-2.3.14 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.13/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.63 1999/04/12 12:33:39 fritz Exp $
+/* $Id: isdn_tty.c,v 1.72 1999/07/31 12:59:45 armin Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -20,6 +20,43 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.72  1999/07/31 12:59:45  armin
+ * Added tty fax capabilities.
+ *
+ * Revision 1.71  1999/07/27 10:34:34  armin
+ * Fixed last change. Did not compile with AUDIO support off.
+ *
+ * Revision 1.70  1999/07/25 16:17:58  keil
+ * Fix Suspend/Resume
+ *
+ * Revision 1.69  1999/07/25 12:56:15  armin
+ * isdn_tty_at_cout() now queues the message if online and
+ * data is in queue or flip buffer is full.
+ * needed for fax connections.
+ *
+ * Revision 1.68  1999/07/11 17:51:51  armin
+ * Bugfix, "-" was missing for AT&L settings.
+ *
+ * Revision 1.67  1999/07/11 17:14:12  armin
+ * Added new layer 2 and 3 protocols for Fax and DSP functions.
+ * Moved "Add CPN to RING message" to new register S23,
+ * "Display message" is now correct on register S13 bit 7.
+ * New audio command AT+VDD implemented (deactivate DTMF decoder and
+ * activate possible existing hardware/DSP decoder).
+ * Moved some tty defines to .h file.
+ * Made whitespace possible in AT command line.
+ * Some AT-emulator output bugfixes.
+ * First Fax G3 implementations.
+ *
+ * Revision 1.66  1999/07/07 10:13:46  detabc
+ * remove unused messages
+ *
+ * Revision 1.65  1999/07/04 21:01:59  werner
+ * Added support for keypad and display (ported from 2.0)
+ *
+ * Revision 1.64  1999/07/01 08:30:00  keil
+ * compatibility to 2.3 kernel
+ *
  * Revision 1.63  1999/04/12 12:33:39  fritz
  * Changes from 2.0 tree.
  *
@@ -61,24 +98,12 @@
  * 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
@@ -303,7 +328,6 @@
 static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
 static void isdn_tty_modem_reset_regs(modem_info *, int);
 static void isdn_tty_cmd_ATA(modem_info *);
-static void isdn_tty_at_cout(char *, modem_info *);
 static void isdn_tty_flush_buffer(struct tty_struct *);
 static void isdn_tty_modem_result(int, modem_info *);
 #ifdef CONFIG_ISDN_AUDIO
@@ -321,64 +345,8 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.63 $";
+char *isdn_tty_revision = "$Revision: 1.72 $";
 
-#define DLE 0x10
-#define ETX 0x03
-#define DC4 0x14
-
-/*
- * Definition of some special Registers of AT-Emulator
- */
-#define REG_RINGATA   0
-#define REG_RINGCNT   1
-#define REG_ESC       2
-#define REG_CR        3
-#define REG_LF        4
-#define REG_BS        5
-
-#define REG_WAITC     7
-
-#define REG_RESP     12
-#define BIT_RESP      1
-#define REG_RESPNUM  12
-#define BIT_RESPNUM   2
-#define REG_ECHO     12
-#define BIT_ECHO      4
-#define REG_DCD      12
-#define BIT_DCD       8
-#define REG_CTS      12
-#define BIT_CTS      16
-#define REG_DTRR     12
-#define BIT_DTRR     32
-#define REG_DSR      12
-#define BIT_DSR      64
-#define REG_CPPP     12
-#define BIT_CPPP    128
-
-#define REG_T70      13
-#define BIT_T70       2
-#define BIT_T70_EXT  32
-#define REG_DTRHUP   13
-#define BIT_DTRHUP    4
-#define REG_RESPXT   13
-#define BIT_RESPXT    8
-#define REG_CIDONCE  13
-#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
-#define REG_PSIZE    16
-#define REG_WSIZE    17
-#define REG_SI1      18
-#define REG_SI2      19
-#define REG_SI1I     20
-#define REG_PLAN     21
-#define REG_SCREEN   22
 
 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
  * to stuff incoming data directly into a tty's flip-buffer. This
@@ -514,7 +482,7 @@
 #ifdef CONFIG_ISDN_AUDIO
 	ifmt = 1;
 	
-	if (info->vonline)
+	if ((info->vonline) && (!info->emu.vpar[4]))
 		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);
@@ -580,6 +548,15 @@
 		ISDN_AUDIO_SKB_DLECOUNT(skb) =
 			isdn_tty_countDLE(skb->data, skb->len);
 	}
+#ifdef CONFIG_ISDN_TTY_FAX
+	else {
+		if (info->faxonline & 2) {
+			isdn_tty_fax_bitorder(info, skb);
+			ISDN_AUDIO_SKB_DLECOUNT(skb) =
+				isdn_tty_countDLE(skb->data, skb->len);
+		}
+	}
+#endif
 #endif
 	/* Try to deliver directly via tty-flip-buf if queue is empty */
 	save_flags(flags);
@@ -909,6 +886,35 @@
 	}
 }
 
+/*
+ * return the usage calculated by si and layer 2 protocol
+ */
+int
+isdn_calc_usage(int si, int l2)
+{
+	int usg = ISDN_USAGE_MODEM;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (si == 1) {
+		switch(l2) {
+			case ISDN_PROTO_L2_MODEM: 
+				usg = ISDN_USAGE_MODEM;
+				break;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_PROTO_L2_FAX: 
+				usg = ISDN_USAGE_FAX;
+				break;
+#endif
+			case ISDN_PROTO_L2_TRANS: 
+			default:
+				usg = ISDN_USAGE_VOICE;
+				break;
+		}
+	}
+#endif
+	return(usg);
+}
+
 /* isdn_tty_dial() performs dialing of a tty an the necessary
  * setup of the lower levels before that.
  */
@@ -928,8 +934,14 @@
 			si = bit2si[j];
 			break;
 		}
+	usg = isdn_calc_usage(si, l2);
 #ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -967,6 +979,12 @@
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL3;
 		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
+		}
+#endif
 		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
@@ -989,7 +1007,7 @@
  * ISDN-line (hangup). The usage-status is cleared
  * and some cleanup is done also.
  */
-static void
+void
 isdn_tty_modem_hup(modem_info * info, int local)
 {
 	isdn_ctrl cmd;
@@ -1010,6 +1028,12 @@
 	}
 #ifdef CONFIG_ISDN_AUDIO
 	info->vonline = 0;
+#ifdef CONFIG_ISDN_TTY_FAX
+	info->faxonline = 0;
+	info->fax->phase = ISDN_FAX_PHASE_IDLE;
+#endif
+	info->emu.vpar[4] = 0;
+	info->emu.vpar[5] = 8;
 	if (info->dtmf_state) {
 		kfree(info->dtmf_state);
 		info->dtmf_state = NULL;
@@ -1041,9 +1065,8 @@
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
 		info->emu.mdmreg[REG_RINGCNT] = 0;
-		usage = ((info->emu.mdmreg[REG_SI1I] != 1) || 
-		    (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ?
-			ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
+		usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I],
+					info->emu.mdmreg[REG_L2PROT]);
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
 				  usage);
 	}
@@ -1130,8 +1153,14 @@
 			si = bit2si[j];
 			break;
 		}
+	usg = isdn_calc_usage(si, l2);
 #ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -1184,11 +1213,11 @@
 		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);
+		info->dialing = 1;
+//		strcpy(dev->num[i], n);
 		isdn_info_update();
-*/
 		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
 	}
 }
 
@@ -1218,8 +1247,14 @@
 			si = bit2si[j];
 			break;
 		}
+	usg = isdn_calc_usage(si, l2);
 #ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -1863,11 +1898,11 @@
 static int
 isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
 {
-#if LINUX_VERSION_CODE < 131841
+#ifdef COMPAT_HAS_NEW_WAITQ
+	DECLARE_WAITQUEUE(wait, NULL);
+#else
 	struct wait_queue wait =
 	{current, NULL};
-#else
-	DECLARE_WAITQUEUE(wait, NULL);
 #endif
 	int do_clocal = 0;
 	unsigned long flags;
@@ -2191,6 +2226,7 @@
 	m->profile[18] = 4;
 	m->profile[19] = 0;
 	m->profile[20] = 0;
+	m->profile[23] = 0;
 	m->pmsn[0] = '\0';
 	m->plmsn[0] = '\0';
 }
@@ -2203,6 +2239,42 @@
 	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
 	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
 	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
+	m->vpar[4] = 0;         /* DTMF detection level    (0 = softcode  ) */
+	m->vpar[5] = 8;         /* DTMF interval           (8 * 5 ms.     ) */
+}
+#endif
+
+#ifdef CONFIG_ISDN_TTY_FAX
+static void
+isdn_tty_modem_reset_faxpar(modem_info * info)
+{
+	T30_s *f = info->fax;
+
+	f->code = 0;
+	f->phase = ISDN_FAX_PHASE_IDLE;
+	f->direction = 0;
+	f->resolution = 1;	/* fine */
+	f->rate = 5;		/* 14400 bit/s */
+	f->width = 0;
+	f->length = 0;
+	f->compression = 0;
+	f->ecm = 0;
+	f->binary = 0;
+	f->scantime = 0;
+	memset(&f->id[0], 32, FAXIDLEN - 1);
+	f->id[FAXIDLEN - 1] = 0;
+	f->badlin = 0;
+	f->badmul = 0;
+	f->bor = 0;
+	f->nbc = 0;
+	f->cq = 0;
+	f->cr = 0;
+	f->ctcrty = 0;
+	f->minsp = 0;
+	f->phcto = 30;
+	f->rel = 0;
+	memset(&f->pollid[0], 32, FAXIDLEN - 1);
+	f->pollid[FAXIDLEN - 1] = 0;
 }
 #endif
 
@@ -2219,6 +2291,9 @@
 #ifdef CONFIG_ISDN_AUDIO
 	isdn_tty_modem_reset_vpar(m);
 #endif
+#ifdef CONFIG_ISDN_TTY_FAX
+	isdn_tty_modem_reset_faxpar(info);
+#endif
 	m->mdmcmdl = 0;
 }
 
@@ -2291,10 +2366,16 @@
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		info = &m->info[i];
-#if LINUX_VERSION_CODE < 131841
-		info->write_sem = MUTEX;
-#else
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate fax t30-buffer\n");
+			return -3;
+		}
+#endif
+#ifdef COMPAT_HAS_NEW_WAITQ
 		init_MUTEX(&info->write_sem);
+#else
+		info->write_sem = MUTEX;
 #endif
 		sprintf(info->last_cause, "0000");
 		sprintf(info->last_num, "none");
@@ -2312,14 +2393,14 @@
 		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
+#ifdef COMPAT_HAS_NEW_WAITQ
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
-#endif
+#else
+		info->open_wait = 0;
+		info->close_wait = 0;
 		info->isdn_driver = -1;
+#endif
 		info->isdn_channel = -1;
 		info->drv_index = -1;
 		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
@@ -2371,8 +2452,15 @@
 				break;
 		}
 		return ret;
-	} else
-		return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+	} else {
+		int tmp;
+		tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
+			       isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+		return tmp;
+	}
 }
 
 /*
@@ -2424,7 +2512,7 @@
 		    (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");
+			printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
 			printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
 			       info->flags, info->isdn_driver, info->isdn_channel,
 			       dev->usage[idx]);
@@ -2446,8 +2534,7 @@
 					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;
+					dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); 
 					strcpy(dev->num[idx], nr);
 					strcpy(info->emu.cpn, eaz);
 					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
@@ -2514,6 +2601,19 @@
 				/* Signal cause to tty-device */
 				strncpy(info->last_cause, c->parm.num, 5);
 				return 1;
+			case ISDN_STAT_DISPLAY:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line);
+#endif
+				/* Signal display to tty-device */
+				if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && 
+					!(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) {
+				  isdn_tty_at_cout("\r\n", info);
+				  isdn_tty_at_cout("DISPLAY: ", info);
+				  isdn_tty_at_cout(c->parm.display, info);
+				  isdn_tty_at_cout("\r\n", info);
+				}
+				return 1;
 			case ISDN_STAT_DCONN:
 #ifdef ISDN_TTY_STAT_DEBUG
 				printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
@@ -2612,6 +2712,27 @@
 					}
 				}
 				return 1;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_STAT_FAXIND:
+				if (TTY_IS_ACTIVE(info)) {
+					isdn_tty_fax_command(info); 
+				}
+				break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+			case ISDN_STAT_AUDIO:
+				if (TTY_IS_ACTIVE(info)) {
+					switch(c->parm.num[0]) {
+						case ISDN_AUDIO_DTMF:
+							if (info->vonline) {
+								isdn_audio_put_dle_code(info,
+									c->parm.num[1]);
+							}
+							break;
+					}
+				}
+				break;
+#endif
 		}
 	}
 	return 0;
@@ -2621,13 +2742,13 @@
  Modem-Emulator-Routines
  *********************************************************************/
 
-#define cmdchar(c) ((c>' ')&&(c<=0x7f))
+#define cmdchar(c) ((c>=' ')&&(c<=0x7f))
 
 /*
  * Put a message from the AT-emulator into receive-buffer of tty,
  * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
  */
-static void
+void
 isdn_tty_at_cout(char *msg, modem_info * info)
 {
 	struct tty_struct *tty;
@@ -2635,6 +2756,8 @@
 	char *p;
 	char c;
 	ulong flags;
+	struct sk_buff *skb = 0;
+	char *sp = 0;
 
 	if (!msg) {
 		printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
@@ -2643,6 +2766,34 @@
 	save_flags(flags);
 	cli();
 	tty = info->tty;
+	if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+		restore_flags(flags);
+		return;
+	}
+
+	/* use queue instead of direct flip, if online and */
+	/* data is in queue or flip buffer is full */
+	if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) ||
+	    (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) {
+		skb = alloc_skb(strlen(msg)
+#ifdef CONFIG_ISDN_AUDIO
+			+ sizeof(isdn_audio_skb)
+#endif
+			, GFP_ATOMIC);
+		if (!skb) {
+			restore_flags(flags);
+			return;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+		skb_reserve(skb, sizeof(isdn_audio_skb));
+#endif
+		sp = skb_put(skb, strlen(msg));
+#ifdef CONFIG_ISDN_AUDIO
+		ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+		ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+	}
+
 	for (p = msg; *p; p++) {
 		switch (*p) {
 			case '\r':
@@ -2657,16 +2808,26 @@
 			default:
 				c = *p;
 		}
-		if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
-			restore_flags(flags);
-			return;
+		if (skb) {
+			*sp++ = c;
+		} else {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				break;
+			tty_insert_flip_char(tty, c, 0);
 		}
-		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-			break;
-		tty_insert_flip_char(tty, c, 0);
 	}
-	restore_flags(flags);
-	queue_task(&tty->flip.tqueue, &tq_timer);
+	if (skb) {
+		__skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
+		dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
+		restore_flags(flags);
+		/* Schedule dequeuing */
+		if ((dev->modempoll) && (info->rcvsched))
+			isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+
+	} else {
+		restore_flags(flags);
+		queue_task(&tty->flip.tqueue, &tq_timer);
+	}
 }
 
 /*
@@ -2847,7 +3008,7 @@
 					break;
 				case 2:
 					/* Append CPN, if enabled */
-					if ((m->mdmreg[REG_RESRXT] & BIT_RESRXT)) {
+					if ((m->mdmreg[REG_CPN] & BIT_CPN)) {
 						sprintf(s, "/%s", m->cpn);
 						isdn_tty_at_cout(s, info);
 					}
@@ -2954,8 +3115,9 @@
 	int limit=39;	/* MUST match the size in isdn_tty_parse to avoid
 				buffer overflow */
 
-	while (strchr("0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
-		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
+	while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
+		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
+		    (*p == '*') || (*p == '#'))
 			*q++ = *p;
 		p++;
 		if(!--limit)
@@ -3008,6 +3170,9 @@
 		case ISDN_PROTO_L2_MODEM:
 			isdn_tty_at_cout("modem", info);
 			break;
+		case ISDN_PROTO_L2_FAX:
+			isdn_tty_at_cout("fax", info);
+			break;
 		default:
 			isdn_tty_at_cout("unknown", info);
 			break;
@@ -3107,8 +3272,8 @@
 			/* &L -Set Numbers to listen on */
 			p[0]++;
 			i = 0;
-			while ((strchr("0123456789,*[]?;", *p[0])) &&
-			       (i < ISDN_LMSNLEN))
+			while ((strchr("0123456789,-*[]?;", *p[0])) &&
+			       (i < ISDN_LMSNLEN) && *p[0])
 				m->lmsn[i++] = *p[0]++;
 			m->lmsn[i] = '\0';
 			break;
@@ -3264,7 +3429,7 @@
 	int bval;
 
 	mreg = isdn_getnum(p);
-	if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
+	if (mreg < 0 || mreg >= ISDN_MODEM_ANZREG)
 		PARSE_ERROR1;
 	switch (*p[0]) {
 		case '=':
@@ -3338,7 +3503,7 @@
 		/* 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) {
-				if (l2 != ISDN_PROTO_L2_MODEM)
+				if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
 					l2 = ISDN_PROTO_L2_TRANS;
 			} else
 				l2 = ISDN_PROTO_L2_X75I;
@@ -3352,6 +3517,12 @@
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL3;
 		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_IN;
+		}
+#endif
 		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
@@ -3372,7 +3543,6 @@
 isdn_tty_cmd_PLUSF(char **p, modem_info * info)
 {
 	atemu *m = &info->emu;
-	int par;
 	char rs[20];
 
 	if (!strncmp(p[0], "CLASS", 5)) {
@@ -3382,6 +3552,10 @@
 				p[0]++;
 				sprintf(rs, "\r\n%d",
 					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
+#ifdef CONFIG_ISDN_TTY_FAX
+				if (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX)
+				sprintf(rs, "\r\n2");
+#endif
 				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
@@ -3389,23 +3563,37 @@
 				switch (*p[0]) {
 					case '0':
 						p[0]++;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
 						m->mdmreg[REG_SI1] = 4;
 						info->xmit_size =
 						    m->mdmreg[REG_PSIZE] * 16;
 						break;
+#ifdef CONFIG_ISDN_TTY_FAX
 					case '2':
-						printk(KERN_DEBUG "isdn_tty: FCLASS=2\n");
 						p[0]++;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
 						break;
+#endif
 					case '8':
 						p[0]++;
+						/* L2 will change on dialout with si=1 */
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
 						m->mdmreg[REG_SI1] = 5;
 						info->xmit_size = VBUF;
 						break;
 					case '?':
 						p[0]++;
-						isdn_tty_at_cout("\r\n0,2,8",
-								 info);
+#ifdef CONFIG_ISDN_TTY_FAX
+						isdn_tty_at_cout("\r\n0,2,8", info);
+#else
+						isdn_tty_at_cout("\r\n0,8", info);
+#endif
 						break;
 					default:
 						PARSE_ERROR1;
@@ -3416,115 +3604,11 @@
 		}
 		return 0;
 	}
-	if (!strncmp(p[0], "AA", 2)) {
-		p[0] += 2;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d",
-					m->mdmreg[REG_RINGATA]);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				par = isdn_getnum(p);
-				if ((par < 0) || (par > 255))
-					PARSE_ERROR1;
-				m->mdmreg[REG_RINGATA] = par;
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-        if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
-                switch (*p[0]) {
-                        case '0':
-                                p[0]++;
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
-        }
-        if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]);
-                switch (*p[0]) {
-                        case '0':
-                                p[0]++;
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
-        }
-        if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */
-		int i, val[]={0,0,0,0,0,0,0,0};
-
-                p[0] += 4;
-                if (*p[0] == '?') {
-			isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info);
-			p[0]++;
-                } else {
-			for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) {
-				val[i] = *p[0] - 48;
-				p[0]++;
-				if (*p[0] == ',')
-					p[0]++;
-			}
-			printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n",
-			       val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
-                }
-                return 0;
-        }
-        if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */
-		char senderID[80];
-		int i;
-
-                p[0] += 4;
-                if (*p[0] =='"')
-			p[0]++;
-                for(i=0; (*p[0]) && (*p[0] != '"'); i++)
-			senderID[i] = *p[0]++;
-                senderID[i] = 0;
-                if (*p[0] =='"')
-			p[0]++;
-                printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID);
-                return 0;
-        }
-        if (!strncmp(p[0], "MFR?", 4)) {
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: FMFR?\n");
-                isdn_tty_at_cout("\r\nISDNfax", info);
-                return 0;
-        }
-        if (!strncmp(p[0], "MDL?", 4)) {
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: FMDL?\n");
-                isdn_tty_at_cout("\r\nAVM-B1", info);
-                return 0;
-        }
-        if (!strncmp(p[0], "AP=?", 4)) {
-                p[0] += 4;
-                printk(KERN_DEBUG "isdn_tty: FAP=?\n");
-                return 0;
-        }
-        if (!strncmp(p[0], "PHCTO=", 6)) {
-		/* beim trace mit dem zyxel folgt der wert 30 ;*/
-                p[0] += 6;
-                printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]);
-                return 0;
-        }
-        if (!strncmp(p[0], "CR=", 3)) {
-                p[0] += 3;
-                printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]);
-                return 0;
-        }
-        printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+#ifdef CONFIG_ISDN_TTY_FAX
+	return (isdn_tty_cmd_PLUSF_FAX(p, info));
+#else
 	PARSE_ERROR1;
+#endif
 }
 
 /*
@@ -3534,8 +3618,9 @@
 isdn_tty_cmd_PLUSV(char **p, modem_info * info)
 {
 	atemu *m = &info->emu;
+	isdn_ctrl cmd;
 	static char *vcmd[] =
-	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL};
+	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL};
 	int i;
 	int par1;
 	int par2;
@@ -3711,9 +3796,9 @@
 								   info);
 							isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
 								   info);
-							isdn_tty_at_cout("5;ALAW;8;0;(8000)",
+							isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n",
 								   info);
-							isdn_tty_at_cout("6;ULAW;8;0;(8000)",
+							isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n",
 								   info);
 							break;
 						default:
@@ -3752,6 +3837,52 @@
 			isdn_tty_modem_result(1, info);
 			return 0;
 			break;
+		case 7:
+			/* AT+VDD - DTMF detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[4],
+						m->vpar[5]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						if (info->online != 1)
+							PARSE_ERROR1;
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 15))
+							PARSE_ERROR1;
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[4] = par1;
+						m->vpar[5] = par2;
+						cmd.driver = info->isdn_driver;
+						cmd.command = ISDN_CMD_AUDIO;
+						cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8);
+						cmd.parm.num[0] = par1;
+						cmd.parm.num[1] = par2;
+						isdn_command(&cmd);
+						break;
+					} else
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-15>,<0-255>",
+							info);
+						break;
+					} else
+					PARSE_ERROR1;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
 		default:
 			PARSE_ERROR1;
 	}
@@ -3774,6 +3905,9 @@
 #endif
 	for (p = &m->mdmcmd[2]; *p;) {
 		switch (*p) {
+			case ' ':
+				p++;
+				break;
 			case 'A':
 				/* A - Accept incoming call */
 				p++;
@@ -3919,8 +4053,10 @@
 						isdn_tty_suspend(ds, info, m);
 						break;
 					case 'R':	/* RESUME */
+						p++;
 						isdn_tty_get_msnstr(ds, &p);
 						isdn_tty_resume(ds, info, m);
+						break;
 					case 'M':	/* MESSAGE */
 						p++;
 						isdn_tty_send_msg(info, m, p);
@@ -3981,7 +4117,7 @@
 				eb[1] = 0;
 				isdn_tty_at_cout(eb, info);
 			}
-			if (m->mdmcmdl >= 2)
+			if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2))))
 				isdn_tty_parse_at(info);
 			m->mdmcmdl = 0;
 			continue;
@@ -4007,15 +4143,16 @@
 				switch (m->mdmcmdl) {
 					case 0:
 						if (c == 'A')
-							m->mdmcmd[m->mdmcmdl++] = c;
+							m->mdmcmd[m->mdmcmdl] = c;
 						break;
 					case 1:
 						if (c == 'T')
-							m->mdmcmd[m->mdmcmdl++] = c;
+							m->mdmcmd[m->mdmcmdl] = c;
 						break;
 					default:
-						m->mdmcmd[m->mdmcmdl++] = c;
+						m->mdmcmd[m->mdmcmdl] = c;
 				}
+				m->mdmcmd[++m->mdmcmdl] = 0;
 			}
 		}
 	}

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