patch-pre2.0.6 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 pre2.0.5/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.4 1996/04/20 16:39:54 fritz Exp $
+/* $Id: isdn_tty.c,v 1.11 1996/05/18 01:37:03 fritz Exp $
  *
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -20,6 +20,35 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.11  1996/05/18 01:37:03  fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.10  1996/05/17 03:51:49  fritz
+ * Changed DLE handling for audio receive.
+ *
+ * Revision 1.9  1996/05/11 21:52:07  fritz
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.8  1996/05/10 08:49:43  fritz
+ * Checkin before major changes of tty-code.
+ *
+ * Revision 1.7  1996/05/07 09:15:09  fritz
+ * Reorganized and general cleanup.
+ * Bugfixes:
+ *  - Audio-transmit working now.
+ *  - "NO CARRIER" now reported, when hanging up with DTR low.
+ *  - Corrected CTS handling.
+ *
+ * Revision 1.6  1996/05/02 03:59:25  fritz
+ * Bugfixes:
+ *  - On dialout, layer-2 setup had been incomplete
+ *    when using new auto-layer2 feature.
+ *  - On hangup, "NO CARRIER" message sometimes missing.
+ *
+ * Revision 1.5  1996/04/30 21:05:25  fritz
+ * Test commit
+ *
  * Revision 1.4  1996/04/20 16:39:54  fritz
  * Changed all io to go through generic routines in isdn_common.c
  * Fixed a real ugly bug in modem-emulator: 'ATA' had been accepted
@@ -39,14 +68,22 @@
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/isdn.h>
+#include <linux/config.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#define VBUF 0x300
+#define VBUFX (VBUF/16)
+#endif
 
 /* Prototypes */
 
 static int  isdn_tty_edit_at(const char *, int, modem_info *, int);
 static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
 static void isdn_tty_modem_reset_regs(atemu *, int);
+static void isdn_tty_cmd_ATA(modem_info *);
+static void isdn_tty_at_cout(char *, modem_info *);
 
 /* Leave this unchanged unless you know what you do! */
 #define MODEM_PARANOIA_CHECK
@@ -54,31 +91,53 @@
 
 static char *isdn_ttyname_ttyI = "ttyI";
 static char *isdn_ttyname_cui  = "cui";
-char *isdn_tty_revision        = "$Revision: 1.4 $";
-
-int isdn_tty_try_read(int i, u_char * buf, int len)
+static int bit2si[8] = {1,5,7,7,7,7,7,7};
+static int si2bit[8] = {4,1,4,4,4,4,4,4};
+                                
+char *isdn_tty_revision        = "$Revision: 1.11 $";
+
+/* isdn_tty_try_read() is called from within isdn_receive_callback()
+ * to stuff incoming data directly into a tty's flip-buffer. This
+ * is done to speed up tty-receiving if the receive-queue is empty.
+ * This routine MUST be called with interrupts off.
+ * Return:
+ *  1 = Success
+ *  0 = Failure, data has to be bufferd and later processed by
+ *      isdn_tty_readmodem().
+ */
+#define DLE 0x10
+#define ETX 0x03
+int isdn_tty_try_read(modem_info *info, struct sk_buff *skb)
 {
         int c;
+        int len;
         struct tty_struct *tty;
 
-	if (i < 0)
-		return 0;
-	if (dev->mdm.online[i]) {
-		if ((tty = dev->mdm.info[i].tty)) {
-			if (dev->mdm.info[i].MCR & UART_MCR_RTS) {
-				c = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+	if (info->online) {
+		if ((tty = info->tty)) {
+			if (info->mcr & UART_MCR_RTS) {
+				c = TTY_FLIPBUF_SIZE - tty->flip.count;
+                                len = skb->len + skb->users;
 				if (c >= len) {
-					if (len > 1) {
-						memcpy(tty->flip.char_buf_ptr, buf, len);
-						tty->flip.count += len;
-						memset(tty->flip.flag_buf_ptr, 0, len);
-						if (dev->mdm.atmodem[i].mdmreg[12] & 128)
-							tty->flip.flag_buf_ptr[len - 1] = 0xff;
-						tty->flip.flag_buf_ptr += len;
-						tty->flip.char_buf_ptr += len;
-					} else
-						tty_insert_flip_char(tty, buf[0], 0);
+                                        if (skb->users)
+                                                while (skb->len--) {
+                                                        if (*skb->data == DLE)
+                                                                tty_insert_flip_char(tty, DLE, 0);
+                                                        tty_insert_flip_char(tty, *skb->data++, 0);
+                                                }
+                                        else {
+                                                memcpy(tty->flip.char_buf_ptr,
+                                                       skb->data, len);
+                                                tty->flip.count += len;
+                                                tty->flip.char_buf_ptr += len;
+                                                memset(tty->flip.flag_buf_ptr, 0, len);
+                                                tty->flip.flag_buf_ptr += len;
+                                        }
+                                        if (info->emu.mdmreg[12] & 128)
+                                                tty->flip.flag_buf_ptr[len - 1] = 0xff;
 					queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                                        skb->free = 1;
+                                        kfree_skb(skb, FREE_READ);
 					return 1;
 				}
 			}
@@ -87,6 +146,10 @@
 	return 0;
 }
 
+/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
+ * It tries getting received data from the receive queue an stuff it into
+ * the tty's flip-buffer.
+ */
 void isdn_tty_readmodem(void)
 {
 	int resched = 0;
@@ -99,43 +162,140 @@
 	modem_info *info;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		if ((midx = dev->m_idx[i]) >= 0)
-			if (dev->mdm.online[midx]) {
-				save_flags(flags);
-				cli();
+		if ((midx = dev->m_idx[i]) >= 0) {
+                        info = &dev->mdm.info[midx];
+			if (info->online) {
 				r = 0;
-				info = &dev->mdm.info[midx];
 				if ((tty = info->tty)) {
-					if (info->MCR & UART_MCR_RTS) {
-						c = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+					if (info->mcr & UART_MCR_RTS) {
+						c = TTY_FLIPBUF_SIZE - tty->flip.count;
 						if (c > 0) {
+                                                        save_flags(flags);
+                                                        cli();
 							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
 								      tty->flip.char_buf_ptr,
 								      tty->flip.flag_buf_ptr, c, 0);
-							if (!(dev->mdm.atmodem[midx].mdmreg[12] & 128))
+                                                        /* CISCO AsyncPPP Hack */
+							if (!(info->emu.mdmreg[12] & 128))
 								memset(tty->flip.flag_buf_ptr, 0, r);
 							tty->flip.count += r;
 							tty->flip.flag_buf_ptr += r;
 							tty->flip.char_buf_ptr += r;
 							if (r)
 								queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                                                        restore_flags(flags);
 						}
 					} else
 						r = 1;
 				} else
 					r = 1;
-				restore_flags(flags);
 				if (r) {
-					dev->mdm.rcvsched[midx] = 0;
+					info->rcvsched = 0;
 					resched = 1;
 				} else
-					dev->mdm.rcvsched[midx] = 1;
+					info->rcvsched = 1;
 			}
+                }
 	}
 	if (!resched)
 		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
 }
 
+void isdn_tty_cleanup_xmit(modem_info *info)
+{
+        struct sk_buff *skb;
+        unsigned long flags;
+
+        save_flags(flags);
+        cli();
+        if (info->xmit_buf->qlen)
+                while ((skb = skb_dequeue(info->xmit_buf))) {
+                        skb->free = 1;
+                        kfree_skb(skb,FREE_WRITE);
+                }
+        restore_flags(flags);
+}
+
+/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
+ * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
+ * outgoing data from the tty's xmit-buffer, handles voice-decompression or
+ * T.70 if necessary, and finally sends it out via isdn_writebuf_stub.
+ */
+static void isdn_tty_senddown(modem_info * info)
+{
+        struct tty_struct *tty = info->tty;
+        struct sk_buff *skb = skb_dequeue(info->xmit_buf);
+
+        if (!skb)
+                return;
+        if (skb->free > 1) {
+                /* Post processing only once, if skb->free > 1 */
+#ifdef CONFIG_ISDN_AUDIO
+                if (info->vonline==2) {
+                        char *ptr = skb->data;
+                        int len = skb->len;
+
+                        /* For now, ifmt is fixed to 1 (alaw), since this
+                         * is used with ISDN everywhere in the world, except
+                         * US, Canadia and Japan.
+                         * Later, when US-ISDN protocols are implemented,
+                         * this setting will depend on the D-channel protocol.
+                         */
+                        int ifmt = 1;
+                        /* voice conversion/decompression */
+                        switch (info->emu.vpar[3]) {
+                                case 4:
+                                        skb_put(skb, len);
+                                        /* fall through */
+                                case 3:
+                                        skb_put(skb, len);
+                                        /* fall through */
+                                case 2:
+                                        skb_put(skb, len * 2);
+                                        /* adpcm, compatible to ZyXel 1496 modem
+                                         * with ROM revision 6.01
+                                         */
+                                        skb_pull(skb, len);
+                                        skb_trim(skb, isdn_audio_adpcm2xlaw(info->adpcms,
+                                                                       ifmt,
+                                                                       ptr,
+                                                                       skb->data,
+                                                                       len));
+                                        break;
+                                case 5:
+                                        /* a-law */
+                                        if (!ifmt)
+                                                isdn_audio_alaw2ulaw(ptr,len);
+                                        break;
+                                case 6:
+                                        /* u-law */
+                                        if (ifmt)
+                                                isdn_audio_ulaw2alaw(ptr,len);
+                                        break;
+                        }
+                }
+#endif          /* CONFIG_ISDN_AUDIO */
+                if (info->emu.mdmreg[13] & 2)
+                        /* Add T.70 simplified header */
+                        memcpy(skb_push(skb,4), "\1\0\1\0", 4);
+                /* Mark skb post-processed */
+                skb->free = 1;
+        }
+        if (isdn_writebuf_skb_stub(info->isdn_driver, info->isdn_channel,
+                                   skb) > 0) {
+                if (skb_queue_empty(info->xmit_buf))
+                        info->xmit_count = 0;
+                if ((info->send_outstanding++) > 10)
+                        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);
+                wake_up_interruptible(&tty->write_wait);
+        } else
+                skb_queue_head(info->xmit_buf,skb);
+}
+
 /************************************************************
  *
  * Modem-functions
@@ -144,78 +304,149 @@
  *
  ************************************************************/
 
+/* The nex routine is called once from within timer-interrupt
+ * triggered within isdn_tty_modem_ncarrier(). It calls
+ * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
+ * into the tty's flip-buffer.
+ */
+static void isdn_tty_modem_do_ncarrier(unsigned long data)
+{
+        modem_info * info = (modem_info *)data;
+        isdn_tty_modem_result(3, info);
+}
+
+/* Next routine is called, whenever the DTR-signal is raised.
+ * It checks the ncarrier-flag, and triggers the above routine
+ * when necessary. The ncarrier-flag is set, whenever DTR goes
+ * low.
+ */      
+static void isdn_tty_modem_ncarrier(modem_info * info)
+{
+        if (info->ncarrier) {
+                info->ncarrier = 0;
+                info->nc_timer.expires = jiffies + HZ;
+                info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+                info->nc_timer.data = (unsigned long)info;
+                add_timer(&info->nc_timer);
+        }
+}
+
+/* isdn_tty_dial() performs dialing of a tty an the necessary
+ * setup of the lower levels befor that.
+ */
 static void isdn_tty_dial(char *n, modem_info * info, atemu * m)
 {
+        int usg = ISDN_USAGE_MODEM;
+        int si = 7;
+        int l2 = m->mdmreg[14];
 	isdn_ctrl cmd;
 	ulong flags;
 	int i;
+        int j;
 
+        for (j=7;j>=0;j--)
+                if (m->mdmreg[18] & (1<<j)) {
+                        si = bit2si[j];
+                        break;
+                }
+#ifdef CONFIG_ISDN_AUDIO
+                if (si == 1) {
+                        l2 = 4;
+                        usg = ISDN_USAGE_VOICE;
+                }
+#endif
+        m->mdmreg[20] = si2bit[si];
 	save_flags(flags);
 	cli();
-	i = isdn_get_free_channel(ISDN_USAGE_MODEM, m->mdmreg[14], m->mdmreg[15], -1, -1);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1);
 	if (i < 0) {
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
 	} else {
-		if (strlen(m->msn)) {
-			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;
-			isdn_info_update();
-			restore_flags(flags);
-			cmd.driver = info->isdn_driver;
-			cmd.arg = info->isdn_channel;
-			cmd.command = ISDN_CMD_CLREAZ;
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
-			strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
-			cmd.driver = info->isdn_driver;
-			cmd.command = ISDN_CMD_SETEAZ;
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
-			cmd.driver = info->isdn_driver;
-			cmd.command = ISDN_CMD_SETL2;
-			cmd.arg = info->isdn_channel + (m->mdmreg[14] << 8);
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
-			cmd.driver = info->isdn_driver;
-			cmd.command = ISDN_CMD_SETL3;
-			cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
-			cmd.driver = info->isdn_driver;
-			cmd.arg = info->isdn_channel;
-			sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
-				m->mdmreg[18], m->mdmreg[19]);
-			cmd.command = ISDN_CMD_DIAL;
-			dev->mdm.dialing[info->line] = 1;
-			strcpy(dev->num[i], n);
-			isdn_info_update();
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
-		} else
-			restore_flags(flags);
+                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;
+                isdn_info_update();
+                restore_flags(flags);
+                cmd.driver = info->isdn_driver;
+                cmd.arg = info->isdn_channel;
+                cmd.command = ISDN_CMD_CLREAZ;
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+                strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+                cmd.driver = info->isdn_driver;
+                cmd.command = ISDN_CMD_SETEAZ;
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+                cmd.driver = info->isdn_driver;
+                cmd.command = ISDN_CMD_SETL2;
+                cmd.arg = info->isdn_channel + (l2 << 8);
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+                cmd.driver = info->isdn_driver;
+                cmd.command = ISDN_CMD_SETL3;
+                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+                cmd.driver = info->isdn_driver;
+                cmd.arg = info->isdn_channel;
+                sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
+                        si, m->mdmreg[19]);
+                cmd.command = ISDN_CMD_DIAL;
+                info->dialing = 1;
+                strcpy(dev->num[i], n);
+                isdn_info_update();
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
 	}
 }
 
+/* isdn_tty_hangup() disassociates a tty from the real
+ * ISDN-line (hangup). The usage-status is cleared
+ * and some cleanup is done also.
+ */
 void isdn_tty_modem_hup(modem_info * info)
 {
 	isdn_ctrl cmd;
+        int usage;
 
         if (!info)
                 return;
-        dev->mdm.rcvsched[info->line] = 0;
-        dev->mdm.online[info->line] = 0;
+        info->rcvsched = 0;
+        info->online = 0;
+        isdn_tty_cleanup_xmit(info);
+        switch (info->vonline) {
+                case 1:
+                        /* voice-recording, add DLE-ETX */
+                        isdn_tty_at_cout("\020\003", info);
+                        break;
+                case 2:
+                        break;
+        }
+        info->vonline = 0;
+        if (info->adpcms) {
+                kfree(info->adpcms);
+                info->adpcms = NULL;
+        }
+        if (info->adpcmr) {
+                kfree(info->adpcmr);
+                info->adpcmr = NULL;
+        }
+        info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+        info->lsr &= ~UART_LSR_TEMT;
 	if (info->isdn_driver >= 0) {
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_HANGUP;
 		cmd.arg = info->isdn_channel;
 		dev->drv[info->isdn_driver]->interface->command(&cmd);
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
-		isdn_free_channel(info->isdn_driver, info->isdn_channel, ISDN_USAGE_MODEM);
+                usage = (info->emu.mdmreg[20] == 1)?
+                        ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+		isdn_free_channel(info->isdn_driver, info->isdn_channel,
+                                  usage);
 	}
 	info->isdn_driver = -1;
 	info->isdn_channel = -1;
         if (info->drv_index >= 0) {
-                info->drv_index = -1;
                 dev->m_idx[info->drv_index] = -1;
+                info->drv_index = -1;
         }
 }
 
@@ -258,17 +489,20 @@
 			i += 15;
 	}
 	if (quot) {
-		info->MCR |= UART_MCR_DTR;
+		info->mcr |= UART_MCR_DTR;
+                isdn_tty_modem_ncarrier(info);                
 	} else {
-		info->MCR &= ~UART_MCR_DTR;
-		isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+		info->mcr &= ~UART_MCR_DTR;
+                if (info->emu.mdmreg[13] & 4) {
 #ifdef ISDN_DEBUG_MODEM_HUP
-                printk(KERN_DEBUG "Mhup in changespeed\n");
+                        printk(KERN_DEBUG "Mhup in changespeed\n");
 #endif
-                isdn_tty_modem_hup(info);
-		if (dev->mdm.online[info->line])
-			isdn_tty_modem_result(3, info);
-		return;
+                        if (info->online)
+                                info->ncarrier = 1;
+                        isdn_tty_modem_reset_regs(&info->emu, 0);
+                        isdn_tty_modem_hup(info);
+                }
+                return;
 	}
 	/* byte size and parity */
 	cval = cflag & (CSIZE | CSTOPB);
@@ -297,11 +531,6 @@
 
 	if (info->flags & ISDN_ASYNC_INITIALIZED)
 		return 0;
-	if (!info->type) {
-		if (info->tty)
-			set_bit(TTY_IO_ERROR, &info->tty->flags);
-		return 0;
-	}
 	save_flags(flags);
 	cli();
 
@@ -311,7 +540,7 @@
 	/*
 	 * Now, initialize the UART 
 	 */
-	info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	/*
@@ -320,10 +549,8 @@
 	isdn_tty_change_speed(info);
 
 	info->flags |= ISDN_ASYNC_INITIALIZED;
-	dev->mdm.msr[info->line] |= UART_MSR_DSR;
-#if FUTURE
+	info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
 	info->send_outstanding = 0;
-#endif
 	restore_flags(flags);
 	return 0;
 }
@@ -344,12 +571,14 @@
 	save_flags(flags);
 	cli();			/* Disable interrupts */
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-		info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
-		isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
+                if (info->emu.mdmreg[13] & 4) {
+                        isdn_tty_modem_reset_regs(&info->emu, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+                        printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
 #endif
-                isdn_tty_modem_hup(info);
+                        isdn_tty_modem_hup(info);
+                }
 	}
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -358,12 +587,111 @@
 	restore_flags(flags);
 }
 
+#ifdef CONFIG_ISDN_AUDIO
+
+
+int isdn_tty_countDLE(unsigned char *buf, int len)
+{
+        int count = 0;
+
+        while (len--)
+                if (*buf++ == DLE)
+                        count++;
+        return count;
+}
+
+/* This routine is called wrom within isdn_tty_write() to perform
+ * DLE-decoding when sending audio-data.
+ */
+static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, struct sk_buff *skb)
+{
+        unsigned char *p = skb->data;
+        int len = skb->len;
+        int count = 0;
+
+        while (len>0) {
+                if (m->lastDLE) {
+                        m->lastDLE = 0;
+                        switch (*p) {
+                                case DLE:
+                                        /* Escape code */
+                                        if (len>1)
+                                                memmove(p,p+1,len-1);
+                                        p--;
+                                        count++;
+                                        break;
+                                case ETX:
+                                        /* End of data */
+                                        info->vonline = 0;
+                                        isdn_tty_at_cout("\r\nVCON\r\n",info);
+                                        return count;
+                                case 'q':
+                                case 's':
+                                        /* Silence */
+                                        if (len>1)
+                                                memmove(p,p+1,len-1);
+                                        p--;
+                                        break;
+                        }
+                } else {
+                        if (*p == DLE)
+                                m->lastDLE = 1;
+                        else
+                                count++;
+                }
+                p++;
+                len--;
+        }
+        if (len<0) {
+                printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+                return 0;
+        }
+        skb_trim(skb,count);
+        return count;
+}
+
+/* This routine is called from within isdn_tty_write() when receiving
+ * audio-data. It interrupts receiving, if an character other than
+ * ^S or ^Q is sent.
+ */
+static int isdn_tty_end_vrx(const char *buf, int c, int from_user)
+{
+	char tmpbuf[VBUF];
+        char *p;
+
+        if (c > VBUF) {
+                printk(KERN_ERR "isdn_tty: (end_vrx) BUFFER OVERFLOW!!!\n");
+                return 1;
+        }
+	if (from_user) {
+		memcpy_fromfs(tmpbuf, buf, c);
+                p = tmpbuf;
+        } else
+                p = (char *)buf;
+        while (c--) {
+                if ((*p != 0x11) && (*p != 0x13))
+                        return 1;
+                p++;
+        }
+        return 0;
+}
+#endif        /* CONFIG_ISDN_AUDIO */
+
+/* isdn_tty_write() is the main send-routine. It is called from the upper
+ * levels within the kernel to perform sending data. Depending on the
+ * online-flag it either directs output to the at-command-interpreter or
+ * to the lower level. Additional tasks done here:
+ *  - If online, check for escape-sequence (+++)
+ *  - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
+ *  - If receiving audio-data, call isdn_tty_end_vrx() to abor if needed.
+ *  - If dialing, abort dial.
+ */
 static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
 {
 	int c, total = 0;
-	modem_info *info = (modem_info *) tty->driver_data;
 	ulong flags;
-	int i;
+	modem_info *info = (modem_info *) tty->driver_data;
+        struct sk_buff *skb;
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
 		return 0;
@@ -372,84 +700,79 @@
 	save_flags(flags);
 	cli();
 	while (1) {
-		c = MIN(count, info->xmit_size - info->xmit_count - 1);
-		if (info->isdn_driver >= 0) {
-#if 0
-			if (info->isdn_driver != 0) {
-				printk(KERN_DEBUG "FIDO: Zwei HW-Treiber geladen? Ansonsten ist was faul.\n");
-				break;
-			}
-			int drvidx = info->isdn_driver;
-			driver *driv = dev->drv[drvidx];
-			i = driv->maxbufsize;
-#else
-			i = dev->drv[info->isdn_driver]->maxbufsize;
-#endif
-			c = MIN(c, i);
-		}
+		c = MIN(count, info->xmit_size - info->xmit_count);
+		if (info->isdn_driver >= 0)
+			c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize);
 		if (c <= 0)
 			break;
-		i = info->line;
-		if (dev->mdm.online[i]) {
-			isdn_tty_check_esc(buf, dev->mdm.atmodem[i].mdmreg[2], c,
-				       &(dev->mdm.atmodem[i].pluscount),
-			     &(dev->mdm.atmodem[i].lastplus), from_user);
-			if (from_user)
-				memcpy_fromfs(&(info->xmit_buf[info->xmit_count]), buf, c);
-			else
-				memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
-			info->xmit_count += c;
-			if (dev->mdm.atmodem[i].mdmreg[13] & 1) {
-				char *bufptr;
-				int buflen;
-#if 0
-				printk(KERN_DEBUG "WB1: %d\n", info->xmit_count);
-#endif
-				bufptr = info->xmit_buf;
-				buflen = info->xmit_count;
-				if (dev->mdm.atmodem[i].mdmreg[13] & 2) {
-					/* Add T.70 simplified header */
-
-#ifdef ISDN_DEBUG_MODEM_DUMP
-					isdn_dumppkt("T70pack1:", bufptr, buflen, 40);
-#endif
-					bufptr -= 4;
-					buflen += 4;
-					memcpy(bufptr, "\1\0\1\0", 4);
-#ifdef ISDN_DEBUG_MODEM_DUMP
-					isdn_dumppkt("T70pack2:", bufptr, buflen, 40);
-#endif
-				}
-				if (isdn_writebuf_stub(info->isdn_driver, info->isdn_channel, bufptr,
-                                                       buflen, 0) > 0) {
-					info->xmit_count = 0;
-					info->xmit_size = dev->mdm.atmodem[i].mdmreg[16] * 16;
-#if FUTURE
-					info->send_outstanding++;
-					dev->mdm.msr[i] &= ~UART_MSR_CTS;
-#endif
-					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-					    tty->ldisc.write_wakeup)
-						(tty->ldisc.write_wakeup) (tty);
-					wake_up_interruptible(&tty->write_wait);
-				}
-			}
+                if ((info->online > 1) ||
+                    (info->vonline == 2)) {
+                        atemu *m = &info->emu;
+
+                        if (info->vonline != 2)
+                                isdn_tty_check_esc(buf, m->mdmreg[2], c,
+                                                   &(m->pluscount),
+                                                   &(m->lastplus),
+                                                   from_user);
+                        if ((skb = alloc_skb(((info->vonline == 2)?
+                                                  (c+(c<<2)):c)+16,GFP_ATOMIC))==NULL) {
+                                printk(KERN_WARNING "isdn_tty: Cannot alloc skb in tty_write\n");
+                                restore_flags(flags);
+                                return total;
+                        }
+                        skb_reserve(skb, 4); /* For T.70 header */
+                        if (from_user)
+                                memcpy_fromfs(skb_put(skb, c), buf, c);
+                        else
+                                memcpy(skb_put(skb, c), buf, c);
+#ifdef CONFIG_ISDN_AUDIO
+                        if (info->vonline == 2) {
+                                int cc = isdn_tty_handleDLEdown(info,m,skb);
+                                info->xmit_count += cc;
+                                if ((cc==0) && (c > 0)) {
+                                        /* If DLE decoding results in zero-transmit, but
+                                         * c originally was non-zero, do a wakeup.
+                                         */
+                                        if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                                            tty->ldisc.write_wakeup)
+                                                (tty->ldisc.write_wakeup) (tty);
+                                        wake_up_interruptible(&tty->write_wait);
+                                }
+                        } else
+#endif
+                                info->xmit_count += c;
+                        skb->users = c;
+                        skb_queue_tail(info->xmit_buf,skb);
+			if (m->mdmreg[13] & 1) {
+                                sti();
+                                isdn_tty_senddown(info);
+                        }
 		} else {
-			if (dev->mdm.dialing[i]) {
-				dev->mdm.dialing[i] = 0;
+                        info->msr |= UART_MSR_CTS;
+                        info->lsr |= UART_LSR_TEMT;
+#ifdef CONFIG_ISDN_AUDIO
+                        if (info->vonline == 1) {
+                                if (isdn_tty_end_vrx(buf, c, from_user)) {
+                                        info->vonline = 0;
+                                        isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+                                }
+                        } else
+#endif
+                                if (info->dialing) {
+                                        info->dialing = 0;
 #ifdef ISDN_DEBUG_MODEM_HUP
-				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+                                        printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
 #endif
-				isdn_tty_modem_hup(info);
-				isdn_tty_modem_result(3, info);
-			} else
-				c = isdn_tty_edit_at(buf, c, info, from_user);
+                                        isdn_tty_modem_result(3, info);
+                                        isdn_tty_modem_hup(info);
+                                } else
+                                        c = isdn_tty_edit_at(buf, c, info, from_user);
 		}
 		buf += c;
 		count -= c;
 		total += c;
 	}
-	if (info->xmit_count)
+	if (total)
 		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
 	restore_flags(flags);
 	return total;
@@ -462,9 +785,9 @@
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
 		return 0;
-	if (!dev->mdm.online[info->line])
-		return info->xmit_size - 1;
-	ret = info->xmit_size - info->xmit_count - 1;
+	if (!info->online)
+		return info->xmit_size;
+	ret = info->xmit_size - info->xmit_count;
 	return (ret < 0) ? 0 : ret;
 }
 
@@ -474,7 +797,7 @@
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
 		return 0;
-	if (!dev->mdm.online[info->line])
+	if (!info->online)
 		return 0;
 	return (info->xmit_count);
 }
@@ -482,14 +805,11 @@
 static void isdn_tty_flush_buffer(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
-	uint flags;
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer"))
 		return;
-	save_flags(flags);
-	cli();
-	info->xmit_count = 0;
-	restore_flags(flags);
+        isdn_tty_cleanup_xmit(info);
+        info->xmit_count = 0;
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
@@ -502,7 +822,7 @@
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
 		return;
-	if (info->xmit_count > 0)
+	if (skb_queue_len(info->xmit_buf))
 		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
 }
 
@@ -522,7 +842,7 @@
 		return;
 	if (I_IXOFF(tty))
 		info->x_char = STOP_CHAR(tty);
-	info->MCR &= ~UART_MCR_RTS;
+	info->mcr &= ~UART_MCR_RTS;
 }
 
 static void isdn_tty_unthrottle(struct tty_struct *tty)
@@ -537,7 +857,7 @@
 		else
 			info->x_char = START_CHAR(tty);
 	}
-	info->MCR |= UART_MCR_RTS;
+	info->mcr |= UART_MCR_RTS;
 }
 
 /*
@@ -564,7 +884,7 @@
 
 	save_flags(flags);
 	cli();
-	status = dev->mdm.msr[info->line];
+	status = info->lsr;
 	restore_flags(flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
 	put_fs_long(result, (ulong *) value);
@@ -578,10 +898,10 @@
 	uint result;
 	ulong flags;
 
-	control = info->MCR;
+	control = info->mcr;
 	save_flags(flags);
 	cli();
-	status = dev->mdm.msr[info->line];
+	status = info->msr;
 	restore_flags(flags);
 	result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
 	    | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
@@ -596,47 +916,66 @@
 static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
 {
 	uint arg = get_fs_long((ulong *) value);
+        int pre_dtr;
 
 	switch (cmd) {
-	case TIOCMBIS:
-		if (arg & TIOCM_RTS) {
-			info->MCR |= UART_MCR_RTS;
-		}
-		if (arg & TIOCM_DTR) {
-			info->MCR |= UART_MCR_DTR;
-		}
-		break;
-	case TIOCMBIC:
-		if (arg & TIOCM_RTS) {
-			info->MCR &= ~UART_MCR_RTS;
-		}
-		if (arg & TIOCM_DTR) {
-			info->MCR &= ~UART_MCR_DTR;
-			isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+                case TIOCMBIS:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
+#endif
+                        if (arg & TIOCM_RTS) {
+                                info->mcr |= UART_MCR_RTS;
+                        }
+                        if (arg & TIOCM_DTR) {
+                                info->mcr |= UART_MCR_DTR;
+                                isdn_tty_modem_ncarrier(info);
+                        }
+                        break;
+                case TIOCMBIC:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
+#endif
+                        if (arg & TIOCM_RTS) {
+                                info->mcr &= ~UART_MCR_RTS;
+                        }
+                        if (arg & TIOCM_DTR) {
+                                info->mcr &= ~UART_MCR_DTR;
+                                if (info->emu.mdmreg[13] & 4) {
+                                        isdn_tty_modem_reset_regs(&info->emu, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+                                        printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
 #endif
-                        isdn_tty_modem_hup(info);
-			if (dev->mdm.online[info->line])
-				isdn_tty_modem_result(3, info);
-		}
-		break;
-	case TIOCMSET:
-		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
-			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
-		if (!(info->MCR & UART_MCR_DTR)) {
-			isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+                                        if (info->online)
+                                                info->ncarrier = 1;
+                                        isdn_tty_modem_hup(info);
+                                }
+                        }
+                        break;
+                case TIOCMSET:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
+#endif
+                        pre_dtr = (info->mcr & UART_MCR_DTR);
+                        info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
+                                     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+                                     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+                        if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
+                                if (!(info->mcr & UART_MCR_DTR)) {
+                                        if (info->emu.mdmreg[13] & 4) {
+                                                isdn_tty_modem_reset_regs(&info->emu, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+                                                printk(KERN_DEBUG "Mhup in TIOCMSET\n");
 #endif
-                        isdn_tty_modem_hup(info);
-			if (dev->mdm.online[info->line])
-				isdn_tty_modem_result(3, info);
-		}
-		break;
-	default:
-		return -EINVAL;
+                                                if (info->online)
+                                                        info->ncarrier = 1;
+                                                isdn_tty_modem_hup(info);
+                                        }
+                                } else
+                                        isdn_tty_modem_ncarrier(info);
+                        }
+                        break;
+                default:
+                        return -EINVAL;
 	}
 	return 0;
 }
@@ -651,104 +990,72 @@
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
 		return -ENODEV;
 	switch (cmd) {
-	case TCSBRK:		/* SVID version: non-zero arg --> no break */
-		retval = tty_check_change(tty);
-		if (retval)
-			return retval;
-		tty_wait_until_sent(tty, 0);
-		return 0;
-	case TCSBRKP:		/* support for POSIX tcsendbreak() */
-		retval = tty_check_change(tty);
-		if (retval)
-			return retval;
-		tty_wait_until_sent(tty, 0);
-		return 0;
-	case TIOCGSOFTCAR:
-		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
-		if (error)
-			return error;
-		put_fs_long(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
-		return 0;
-	case TIOCSSOFTCAR:
-		arg = get_fs_long((ulong *) arg);
-		tty->termios->c_cflag =
-		    ((tty->termios->c_cflag & ~CLOCAL) |
-		     (arg ? CLOCAL : 0));
-		return 0;
-	case TIOCMGET:
-		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
-		if (error)
-			return error;
-		return isdn_tty_get_modem_info(info, (uint *) arg);
-	case TIOCMBIS:
-	case TIOCMBIC:
-	case TIOCMSET:
-		return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
-	case TIOCSERGETLSR:	/* Get line status register */
-		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
-		if (error)
-			return error;
-		else
-			return isdn_tty_get_lsr_info(info, (uint *) arg);
-
-	case TIOCGSERIAL:
-		return -ENOIOCTLCMD;
-#if 0
-		error = verify_area(VERIFY_WRITE, (void *) arg,
-				    sizeof(struct serial_struct));
-		if (error)
-			return error;
-		return get_serial_info(info,
-				       (struct serial_struct *) arg);
-#endif
-	case TIOCSSERIAL:
-		return -ENOIOCTLCMD;
-#if 0
-		return set_serial_info(info,
-				       (struct serial_struct *) arg);
-#endif
-	case TIOCSERCONFIG:
-		return -ENOIOCTLCMD;
-#if 0
-		return do_autoconfig(info);
-#endif
-
-	case TIOCSERGWILD:
-		return -ENOIOCTLCMD;
-#if 0
-		error = verify_area(VERIFY_WRITE, (void *) arg,
-				    sizeof(int));
-		if (error)
-			return error;
-		put_fs_long(modem_wild_int_mask, (ulong *) arg);
-		return 0;
+                case TCSBRK:		/* SVID version: non-zero arg --> no break */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
 #endif
-	case TIOCSERSWILD:
-		return -ENOIOCTLCMD;
-#if 0
-		if (!suser())
-			return -EPERM;
-		modem_wild_int_mask = get_fs_long((ulong *) arg);
-		if (modem_wild_int_mask < 0)
-			modem_wild_int_mask = check_wild_interrupts(0);
-		return 0;
+                        retval = tty_check_change(tty);
+                        if (retval)
+                                return retval;
+                        tty_wait_until_sent(tty, 0);
+                        return 0;
+                case TCSBRKP:		/* support for POSIX tcsendbreak() */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
 #endif
-	case TIOCSERGSTRUCT:
-		return -ENOIOCTLCMD;
-#if 0
-		error = verify_area(VERIFY_WRITE, (void *) arg,
-				    sizeof(modem_info));
-		if (error)
-			return error;
-		memcpy_tofs((modem_info *) arg,
-			    info, sizeof(modem_info));
-		return 0;
+                        retval = tty_check_change(tty);
+                        if (retval)
+                                return retval;
+                        tty_wait_until_sent(tty, 0);
+                        return 0;
+                case TIOCGSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
+#endif
+                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+                        if (error)
+                                return error;
+                        put_fs_long(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+                        return 0;
+                case TIOCSSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
+#endif
+                        arg = get_fs_long((ulong *) arg);
+                        tty->termios->c_cflag =
+                                ((tty->termios->c_cflag & ~CLOCAL) |
+                                 (arg ? CLOCAL : 0));
+                        return 0;
+                case TIOCMGET:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+#endif
+                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+                        if (error)
+                                return error;
+                        return isdn_tty_get_modem_info(info, (uint *) arg);
+                case TIOCMBIS:
+                case TIOCMBIC:
+                case TIOCMSET:
+                        error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint));
+                        if (error)
+                                return error;
+                        return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+                case TIOCSERGETLSR:	/* Get line status register */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+                        printk(KERN_DEBUG "ttyI%d ioctl TIOSERGETLSR\n", info->line);
 #endif
-	default:
+                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+                        if (error)
+                                return error;
+                        else
+                                return isdn_tty_get_lsr_info(info, (uint *) arg);
+                        
+                default:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-		printk(KERN_DEBUG "unsupp. ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+                        printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
 #endif
-		return -ENOIOCTLCMD;
+                        return -ENOIOCTLCMD;
 	}
 	return 0;
 }
@@ -757,13 +1064,17 @@
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
-	if (tty->termios->c_cflag == old_termios->c_cflag)
-		return;
-	isdn_tty_change_speed(info);
-	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
-		tty->hw_stopped = 0;
-	}
+        if (!old_termios)
+                isdn_tty_change_speed(info);
+        else {
+                if (tty->termios->c_cflag == old_termios->c_cflag)
+                        return;
+                isdn_tty_change_speed(info);
+                if ((old_termios->c_cflag & CRTSCTS) &&
+                    !(tty->termios->c_cflag & CRTSCTS)) {
+                        tty->hw_stopped = 0;
+                }
+        }
 }
 
 /*
@@ -817,7 +1128,8 @@
 	 * If non-blocking mode is set, then make the check up front
 	 * and then exit.
 	 */
-	if (filp->f_flags & O_NONBLOCK) {
+	if ((filp->f_flags & O_NONBLOCK) ||
+            (tty->flags & (1 << TTY_IO_ERROR))) {
 		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
 			return -EBUSY;
 		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
@@ -865,9 +1177,7 @@
 		}
 		if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
 		    !(info->flags & ISDN_ASYNC_CLOSING) &&
-		    (do_clocal || (
-					  dev->mdm.msr[info->line] &
-					  UART_MSR_DCD))) {
+		    (do_clocal || (info->msr & UART_MSR_DCD))) {
 			break;
 		}
 		if (current->signal & ~current->blocked) {
@@ -972,7 +1282,6 @@
 #endif
 		return;
 	}
-	dev->modempoll--;
 	if ((tty->count == 1) && (info->count != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
@@ -1007,8 +1316,7 @@
 	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
 		info->callout_termios = *tty->termios;
 
-	tty->closing = 1;
-
+        tty->closing = 1;
 	/*
 	 * At this point we stop accepting input.  To do this, we
 	 * disable the receive line status interrupts, and tell the
@@ -1023,8 +1331,7 @@
 		 * important if there is a transmit FIFO!
 		 */
 		timeout = jiffies + HZ;
-		while (!(dev->mdm.mlr[info->line]
-			 & UART_LSR_TEMT)) {
+		while (!(info->lsr & UART_LSR_TEMT)) {
 			current->state = TASK_INTERRUPTIBLE;
 			current->timeout = jiffies + 20;
 			schedule();
@@ -1032,6 +1339,7 @@
 				break;
 		}
 	}
+	dev->modempoll--;
 	isdn_tty_shutdown(info);
 	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
@@ -1039,22 +1347,10 @@
 		tty->ldisc.flush_buffer(tty);
 	info->tty = 0;
 	tty->closing = 0;
-#if 00
-	if (tty->ldisc.num != ldiscs[N_TTY].num) {
-		if (tty->ldisc.close)
-			(tty->ldisc.close) (tty);
-		tty->ldisc = ldiscs[N_TTY];
-		tty->termios->c_line = N_TTY;
-		if (tty->ldisc.open)
-			(tty->ldisc.open) (tty);
-	}
-#endif
 	if (info->blocked_open) {
-		if (info->close_delay) {
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + info->close_delay;
-			schedule();
-		}
+                current->state = TASK_INTERRUPTIBLE;
+                current->timeout = jiffies + 50;
+                schedule();
 		wake_up_interruptible(&info->open_wait);
 	}
 	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
@@ -1082,6 +1378,8 @@
 	wake_up_interruptible(&info->open_wait);
 }
 
+/* This routine initializes all emulator-data.
+ */
 static void isdn_tty_reset_profile(atemu * m)
 {
 	m->profile[0] = 0;
@@ -1097,22 +1395,32 @@
 	m->profile[10] = 7;
 	m->profile[11] = 70;
 	m->profile[12] = 0x45;
-	m->profile[13] = 0;
+	m->profile[13] = 4;
 	m->profile[14] = ISDN_PROTO_L2_X75I;
 	m->profile[15] = ISDN_PROTO_L3_TRANS;
 	m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
 	m->profile[17] = ISDN_MODEM_WINSIZE;
-	m->profile[18] = 7;
+	m->profile[18] = 4;
 	m->profile[19] = 0;
+	m->profile[20] = 0;
 	m->pmsn[0] = '\0';
 }
 
+static void isdn_tty_modem_reset_vpar(atemu *m)
+{
+        m->vpar[0] = 2;  /* Voice-device            (2 = phone line) */
+        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   ) */
+}
+
 static void isdn_tty_modem_reset_regs(atemu * m, int force)
 {
 	if ((m->mdmreg[12] & 32) || force) {
 		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
 		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
 	}
+        isdn_tty_modem_reset_vpar(m);
 	m->mdmcmdl = 0;
 }
 
@@ -1131,10 +1439,6 @@
 	modem_info *info;
 
 	m = &dev->mdm;
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		isdn_tty_reset_profile(&(m->atmodem[i]));
-		isdn_tty_modem_reset_regs(&(m->atmodem[i]), 1);
-	}
 	memset(&m->tty_modem, 0, sizeof(struct tty_driver));
 	m->tty_modem.magic = TTY_DRIVER_MAGIC;
 	m->tty_modem.name = isdn_ttyname_ttyI;
@@ -1148,8 +1452,8 @@
 	m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
 	m->tty_modem.refcount = &m->refcount;
 	m->tty_modem.table = m->modem_table;
-	m->tty_modem.termios = m->modem_termios;
-	m->tty_modem.termios_locked = m->modem_termios_locked;
+	m->tty_modem.termios = (struct termios **)m->modem_termios;
+	m->tty_modem.termios_locked = (struct termios **)m->modem_termios_locked;
 	m->tty_modem.open = isdn_tty_open;
 	m->tty_modem.close = isdn_tty_close;
 	m->tty_modem.write = isdn_tty_write;
@@ -1184,11 +1488,12 @@
 		return -2;
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		info = &(m->info[i]);
+		info = &m->info[i];
+		isdn_tty_reset_profile(&info->emu);
+		isdn_tty_modem_reset_regs(&info->emu, 1);
 		info->magic = ISDN_ASYNC_MAGIC;
 		info->line = i;
 		info->tty = 0;
-		info->close_delay = 50;
 		info->x_char = 0;
 		info->count = 0;
 		info->blocked_open = 0;
@@ -1196,16 +1501,15 @@
 		info->normal_termios = m->tty_modem.init_termios;
 		info->open_wait = 0;
 		info->close_wait = 0;
-		info->type = ISDN_PORT_16550A;
 		info->isdn_driver = -1;
 		info->isdn_channel = -1;
 		info->drv_index = -1;
 		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
-		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+		if (!(info->xmit_buf = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL))) {
 			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
 			return -3;
 		}
-		info->xmit_buf += 4;	/* Make room for T.70 header */
+                skb_queue_head_init(info->xmit_buf);
 	}
 	return 0;
 }
@@ -1261,16 +1565,16 @@
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
 	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,
-		       dev->mdm.atmodem[i].msn, isdn_map_eaz2msn(dev->mdm.atmodem[i].msn, di),
-		       dev->mdm.atmodem[i].mdmreg[18], dev->mdm.atmodem[i].mdmreg[19]);
+		       info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
+		       info->emu.mdmreg[18], info->emu.mdmreg[19]);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(dev->mdm.atmodem[i].msn, di)
-			     ,eaz)) &&	/* EAZ is matching   */
-		    (dev->mdm.atmodem[i].mdmreg[18] == si1) &&	/* SI1 is matching   */
-		    (dev->mdm.atmodem[i].mdmreg[19] == si2)) {	/* SI2 is matching   */
-			modem_info *info = &dev->mdm.info[i];
+		if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
+                             eaz)) &&                             /* EAZ is matching      */
+		    (info->emu.mdmreg[18] & si2bit[si1]) &&       /* SI1 is matching      */
+		    ((info->emu.mdmreg[19] == si2) || !si2)) {    /* SI2 is matching or 0 */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: match1\n");
@@ -1287,8 +1591,9 @@
 				info->drv_index = idx;
 				dev->m_idx[idx] = info->line;
 				dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-				dev->usage[idx] |= ISDN_USAGE_MODEM;
+				dev->usage[idx] |= (si1==1)?ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
 				strcpy(dev->num[idx], nr);
+                                info->emu.mdmreg[20] = si2bit[si1];
 				isdn_info_update();
 				restore_flags(flags);
 				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
@@ -1316,7 +1621,7 @@
 static void isdn_tty_at_cout(char *msg, modem_info * info)
 {
 	struct tty_struct *tty;
-	atemu *m = &(dev->mdm.atmodem[info->line]);
+	atemu *m = &info->emu;
 	char *p;
 	char c;
 	ulong flags;
@@ -1330,17 +1635,17 @@
 	tty = info->tty;
 	for (p = msg; *p; p++) {
 		switch (*p) {
-		case '\r':
-			c = m->mdmreg[3];
-			break;
-		case '\n':
-			c = m->mdmreg[4];
-			break;
-		case '\b':
-			c = m->mdmreg[5];
-			break;
-		default:
-			c = *p;
+                        case '\r':
+                                c = m->mdmreg[3];
+                                break;
+                        case '\n':
+                                c = m->mdmreg[4];
+                                break;
+                        case '\b':
+                                c = m->mdmreg[5];
+                                break;
+                        default:
+                                c = *p;
 		}
 		if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
 			restore_flags(flags);
@@ -1363,8 +1668,8 @@
 #ifdef ISDN_DEBUG_MODEM_HUP
 		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
 #endif
-		isdn_tty_modem_hup(info);
 		isdn_tty_modem_result(3, info);
+		isdn_tty_modem_hup(info);
 	}
 }
 
@@ -1433,43 +1738,45 @@
  */
 void isdn_tty_modem_result(int code, modem_info * info)
 {
-	atemu *m = &dev->mdm.atmodem[info->line];
+	atemu *m = &info->emu;
 	static char *msg[] =
 	{"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
 	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
-	 "RINGING", "NO MSN/EAZ"};
+	 "RINGING", "NO MSN/EAZ", "VCON"};
 	ulong flags;
 	char s[4];
 
 	switch (code) {
-	case 2:
-		m->mdmreg[1]++;	/* RING */
-		if (m->mdmreg[1] == m->mdmreg[0]) {
-			/* Accept incoming call */
-			isdn_ctrl cmd;
-			m->mdmreg[1] = 0;
-			dev->mdm.msr[info->line] &= ~UART_MSR_RI;
-			cmd.driver = info->isdn_driver;
-			cmd.arg = info->isdn_channel;
-			cmd.command = ISDN_CMD_ACCEPTD;
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
-		}
-		break;
-	case 3:
-		/* NO CARRIER */
-		save_flags(flags);
-		cli();
-		dev->mdm.msr[info->line] &= ~(UART_MSR_DCD | UART_MSR_RI);
-		if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
-			restore_flags(flags);
-			return;
-		}
-		restore_flags(flags);
-		break;
-	case 1:
-	case 5:
-		dev->mdm.online[info->line] = 1;
-		break;
+                case 2:
+                        m->mdmreg[1]++;	/* RING */
+                        if (m->mdmreg[1] == m->mdmreg[0])
+                                /* Automatically accept incoming call */
+                                isdn_tty_cmd_ATA(info);
+                        break;
+                case 3:
+                        /* NO CARRIER */
+                        save_flags(flags);
+                        cli();
+#ifdef ISDN_DEBUG_MODEM_HUP
+                        printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+                               (info->flags & ISDN_ASYNC_CLOSING),
+                               (!info->tty));
+#endif
+                        if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+                                restore_flags(flags);
+                                return;
+                        }
+                        restore_flags(flags);
+                        break;
+                case 1:
+                case 5:
+                        if (!info->online)
+                                info->online = 2;
+                        break;
+                case 11:
+                        if (!info->online)
+                                info->online = 1;
+                        break;
 	}
 	if (m->mdmreg[12] & 1) {
 		/* Show results */
@@ -1503,8 +1810,9 @@
 		}
 		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
 		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
-		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP))))
+		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
 			tty_hangup(info->tty);
+                }
 		restore_flags(flags);
 	}
 }
@@ -1516,7 +1824,7 @@
 {
 	char v[6];
 
-	sprintf(v, "%d\r\n", dev->mdm.atmodem[info->line].mdmreg[ridx]);
+	sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
 	isdn_tty_at_cout(v, info);
 }
 
@@ -1546,319 +1854,594 @@
 	*q = 0;
 }
 
+#define PARSE_ERROR { isdn_tty_modem_result(4, info); return; }
+#define PARSE_ERROR1 { isdn_tty_modem_result(4, info); return 1; }
+
+/*
+ * Parse AT&.. commands.
+ */
+static int isdn_tty_cmd_ATand(char **p, modem_info * info)
+{
+        atemu *m = &info->emu;
+        int i;
+        char rb[100];
+
+        switch (*p[0]) {
+                case 'B':
+                        /* &B - Set Buffersize */
+                        p[0]++;
+                        i = isdn_getnum(p);
+                        if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
+                                PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+                        if ((m->mdmreg[18] & 1) && (i > VBUF))
+                                PARSE_ERROR1;
+#endif
+                        m->mdmreg[16] = i / 16;
+                        info->xmit_size = m->mdmreg[16] * 16;
+                        break;
+                case 'D':
+                        /* &D - Set DCD-Low-behavior */
+                        p[0]++;
+                        switch (isdn_getnum(p)) {
+                                case 0:
+                                        m->mdmreg[13] &= ~4;
+                                        break;
+                                case 2:
+                                        m->mdmreg[13] |= 4;
+                                        m->mdmreg[12] &= ~32;
+                                        break;
+                                case 3:
+                                        m->mdmreg[13] |= 4;
+                                        m->mdmreg[12] |= 32;
+                                        break;
+                                default:
+                                        PARSE_ERROR1
+                        }
+                        break;
+                case 'E':
+                        /* &E -Set EAZ/MSN */
+                        p[0]++;
+                        isdn_tty_get_msnstr(m->msn, p);
+                        break;
+                case 'F':
+                        /* &F -Set Factory-Defaults */
+                        p[0]++;
+                        isdn_tty_reset_profile(m);
+                        isdn_tty_modem_reset_regs(m, 1);
+                        break;
+                case 'S':
+                        /* &S - Set Windowsize */
+                        p[0]++;
+                        i = isdn_getnum(p);
+                        if ((i > 0) && (i < 9))
+                                m->mdmreg[17] = i;
+                        else
+                                PARSE_ERROR1;
+                        break;
+                case 'V':
+                        /* &V - Show registers */
+                        p[0]++;
+                        for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
+                                sprintf(rb, "S%d=%d%s", i, 
+                                        m->mdmreg[i], (i == 6) ? "\r\n" : " ");
+                                isdn_tty_at_cout(rb, info);
+                        }
+                        sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
+                                strlen(m->msn) ? m->msn : "None");
+                        isdn_tty_at_cout(rb, info);
+                        break;
+                case 'W':
+                        /* &W - Write Profile */
+                        p[0]++;
+                        switch (*p[0]) {
+                                case '0':
+                                        p[0]++;
+                                        modem_write_profile(m);
+                                        break;
+                                default:
+                                        PARSE_ERROR1;
+                        }
+                        break;
+                case 'X':
+                        /* &X - Switch to BTX-Mode */
+                        p[0]++;
+                        switch (isdn_getnum(p)) {
+                                case 0:
+                                        m->mdmreg[13] &= ~2;
+                                        break;
+                                case 1:
+                                        m->mdmreg[13] |= 2;
+                                        m->mdmreg[14] = 0;
+                                        m->mdmreg[16] = 7;
+                                        info->xmit_size = 112;
+                                        m->mdmreg[18] = 4;
+                                        m->mdmreg[19] = 0;
+                                        break;
+                                default:
+                                        PARSE_ERROR1;
+                        }
+                        break;
+                default:
+                        PARSE_ERROR1;
+        }
+        return 0;
+}
+
+/*
+ * Perform ATS command
+ */
+static int isdn_tty_cmd_ATS(char **p, modem_info * info)
+{
+        atemu *m = &info->emu;
+        int mreg;
+        int mval;
+
+        mreg = isdn_getnum(p);
+        if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
+                PARSE_ERROR1;
+        switch (*p[0]) {
+                case '=':
+                        p[0]++;
+                        mval = isdn_getnum(p);
+                        if (mval < 0 || mval > 255)
+                                PARSE_ERROR1;
+                        switch (mreg) {
+                                /* Some plausibility checks */
+                                case 14:
+                                        if (mval > ISDN_PROTO_L2_TRANS)
+                                                PARSE_ERROR1;
+                                        break;
+                                case 16:
+                                        if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
+                                                PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+                                        if ((m->mdmreg[18] & 1) && (mval > VBUFX))
+                                                PARSE_ERROR1;
+#endif
+                                        info->xmit_size = mval * 16;
+                                        break;
+                                case 20:
+                                        PARSE_ERROR1;
+                        }
+                        m->mdmreg[mreg] = mval;
+                        break;
+                case '?':
+                        p[0]++;
+                        isdn_tty_show_profile(mreg, info);
+                        break;
+                default:
+                        PARSE_ERROR1;
+                        break;
+        }
+        return 0;
+}
+
+/*
+ * Perform ATA command
+ */
+static void isdn_tty_cmd_ATA(modem_info * info)
+{
+        atemu *m = &info->emu;
+        isdn_ctrl cmd;
+        int l2;
+
+        if (info->msr & UART_MSR_RI) {
+                /* Accept incoming call */
+                m->mdmreg[1] = 0;
+                info->msr &= ~UART_MSR_RI;
+                l2 = m->mdmreg[14];
+#ifdef CONFIG_ISDN_AUDIO
+                /* If more than one bit set in reg18, autoselect Layer2 */
+                if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18])
+                        if (m->mdmreg[20] == 1) l2 = 4;
+#endif
+                cmd.driver = info->isdn_driver;
+                cmd.command = ISDN_CMD_SETL2;
+                cmd.arg = info->isdn_channel + (l2 << 8);
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+                cmd.driver = info->isdn_driver;
+                cmd.command = ISDN_CMD_SETL3;
+                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+                cmd.driver = info->isdn_driver;
+                cmd.arg = info->isdn_channel;
+                cmd.command = ISDN_CMD_ACCEPTD;
+                dev->drv[info->isdn_driver]->interface->command(&cmd);
+        } else
+                isdn_tty_modem_result(8, info);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+/*
+ * Parse AT+V.. commands
+ */
+static int isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+{
+        atemu *m = &info->emu;
+        static char *vcmd[] = {"NH","IP","LS","RX","SD","SM","TX",NULL};
+        int i;
+	int par1;
+	int par2;
+	char rs[20];
+
+        i = 0;
+        while (vcmd[i]) {
+                if (!strncmp(vcmd[i],p[0],2)) {
+                        p[0] += 2;
+                        break;
+                }
+                i++;
+        }
+        switch (i) {
+                case 0:
+                        /* AT+VNH - Auto hangup feature */
+                        switch (*p[0]) {
+                                case '?':
+                                        p[0]++;
+                                        isdn_tty_at_cout("\r\n1", info);
+                                        break;
+                                case '=':
+                                        p[0]++;
+                                        switch (*p[0]) {
+                                                case '1':
+                                                        p[0]++;
+                                                        break;
+                                                case '?':
+                                                        p[0]++;
+                                                        isdn_tty_at_cout("\r\n1", info);
+                                                        break;
+                                                default:
+                                                        PARSE_ERROR1;
+                                        }
+                                        break;
+                                default:
+                                        PARSE_ERROR1;
+                        }
+                        break;
+                case 1:
+                        /* AT+VIP - Reset all voice parameters */
+                        isdn_tty_modem_reset_vpar(m);
+                        break;
+                case 2:
+                        /* AT+VLS - Select device, accept incoming call */
+                        switch (*p[0]) {
+                                case '?':
+                                        p[0]++;
+                                        sprintf(rs,"\r\n%d",m->vpar[0]);
+                                        isdn_tty_at_cout(rs, info);
+                                        break;
+                                case '=':
+                                        p[0]++;
+                                        switch (*p[0]) {
+                                                case '0':
+                                                        p[0]++;
+                                                        m->vpar[0] = 0;
+                                                        break;
+                                                case '2':
+                                                        p[0]++;
+                                                        m->vpar[0] = 2;
+                                                        break;
+                                                case '?':
+                                                        p[0]++;
+                                                        isdn_tty_at_cout("\r\n0,2", info);
+                                                        break;
+                                                default:
+                                                        PARSE_ERROR1;
+                                        }
+                                        break;
+                                default:
+                                        PARSE_ERROR1;
+                        }
+                        break;
+                case 3:
+                        /* AT+VRX - Start recording */
+                        if (!m->vpar[0])
+                                PARSE_ERROR1;
+                        if (m->vpar[3] < 5) {
+                                info->adpcmr = isdn_audio_adpcm_init(m->vpar[3]);
+                                if (!info->adpcmr) {
+                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+                                        PARSE_ERROR1;
+                                }
+                        }
+                        info->vonline = 1;
+                        isdn_tty_modem_result(1, info);
+                        return 1;
+                        break;
+                case 4:
+                        /* AT+VSD - Silence detection */
+                        switch (*p[0]) {
+                                case '?':
+                                        p[0]++;
+                                        sprintf(rs,"\r\n<%d>,<%d>",
+                                                m->vpar[1],
+                                                m->vpar[2]);
+                                        isdn_tty_at_cout(rs, info);
+                                        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:
+                                                        PARSE_ERROR1;
+                                        }
+                                        break;
+                                default:
+                                        PARSE_ERROR1;
+                        }
+                        break;
+                case 5:
+                        /* AT+VSM - Select compression */
+                        switch (*p[0]) {
+                                case '?':
+                                        p[0]++;
+                                        sprintf(rs,"\r\n<%d>,<%d><8000>",
+                                                m->vpar[3],
+                                                m->vpar[1]);
+                                        isdn_tty_at_cout(rs, info);
+                                        break;
+                                case '=':
+                                        p[0]++;
+                                        switch (*p[0]) {
+                                                case '2':
+                                                case '3':
+                                                case '4':
+                                                case '5':
+                                                case '6':
+                                                        par1 = isdn_getnum(p);
+                                                        if ((par1 < 2) || (par1 > 6))
+                                                                PARSE_ERROR1;
+                                                        m->vpar[3] = par1;
+                                                        break;
+                                                case '?':
+                                                        p[0]++;
+                                                        isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+                                                                         info);
+                                                        isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+                                                                         info);
+                                                        isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+                                                                         info);
+                                                        isdn_tty_at_cout("5;ALAW;8;0;(8000)",
+                                                                         info);
+                                                        isdn_tty_at_cout("6;ULAW;8;0;(8000)",
+                                                                         info);
+                                                        break;
+                                                default:
+                                                        PARSE_ERROR1;
+                                        }
+                                        break;
+                                default:
+                                        PARSE_ERROR1;
+                        }
+                        break;
+                case 6:
+                        /* AT+VTX - Start sending */
+                        if (!m->vpar[0])
+                                PARSE_ERROR1;
+                        if (m->vpar[3] < 5) {
+                                info->adpcms = isdn_audio_adpcm_init(m->vpar[3]);
+                                if (!info->adpcms) {
+                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+                                        PARSE_ERROR1;
+                                }
+                        }
+                        info->vonline = 2;
+                        isdn_tty_modem_result(1, info);
+                        return 1;
+                        break;
+                default:
+                        PARSE_ERROR1;
+        }
+        return 0;
+}
+#endif        /* CONFIG_ISDN_AUDIO */
+
 /*
  * Parse and perform an AT-command-line.
- *
- * Parameter:
- *   channel   index to line (minor-device)
  */
 static void isdn_tty_parse_at(modem_info * info)
 {
-	atemu *m = &dev->mdm.atmodem[info->line];
-	char *p;
-	int mreg;
-	int mval;
-	int i;
-	char rb[100];
-	char ds[40];
-	isdn_ctrl cmd;
+        atemu *m = &info->emu;
+        char *p;
+        char ds[40];
 
 #ifdef ISDN_DEBUG_AT
-	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
-#endif
-	for (p = &m->mdmcmd[2]; *p;) {
-		switch (*p) {
-		case 'A':
-			/* A - Accept incoming call */
-			p++;
-                        if (dev->mdm.msr[info->line] & UART_MSR_RI) {
-#define FIDOBUG
-#ifdef FIDOBUG
-/* Variables fido... defined temporarily for finding a strange bug */
-				driver *fido_drv;
-				isdn_if *fido_if;
-				int fido_isdn_driver;
-				modem_info *fido_modem_info;
-				int (*fido_command) (isdn_ctrl *);
-#endif
-				/* Accept incoming call */
-				m->mdmreg[1] = 0;
-				dev->mdm.msr[info->line] &= ~UART_MSR_RI;
-				cmd.driver = info->isdn_driver;
-				cmd.command = ISDN_CMD_SETL2;
-				cmd.arg = info->isdn_channel + (m->mdmreg[14] << 8);
-				dev->drv[info->isdn_driver]->interface->command(&cmd);
-				cmd.driver = info->isdn_driver;
-				cmd.command = ISDN_CMD_SETL3;
-				cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-				dev->drv[info->isdn_driver]->interface->command(&cmd);
-				cmd.driver = info->isdn_driver;
-				cmd.arg = info->isdn_channel;
-				cmd.command = ISDN_CMD_ACCEPTD;
-#ifdef FIDOBUG
-				fido_modem_info = info;
-				fido_isdn_driver = fido_modem_info->isdn_driver;
-				fido_drv = dev->drv[fido_isdn_driver];
-				fido_if = fido_drv->interface;
-				fido_command = fido_if->command;
-				fido_command(&cmd);
-#else
-				dev->drv[info->isdn_driver]->interface->command(&cmd);
+        printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
 #endif
-			} else {
-				isdn_tty_modem_result(8, info);
-				return;
-			}
-			break;
-		case 'D':
-			/* D - Dial */
-			isdn_tty_getdial(++p, ds);
-			p += strlen(p);
-			if (!strlen(m->msn))
-				isdn_tty_modem_result(10, info);
-			else if (strlen(ds))
-				isdn_tty_dial(ds, info, m);
-			else
-				isdn_tty_modem_result(4, info);
-			return;
-		case 'E':
-			/* E - Turn Echo on/off */
-			p++;
-			switch (*p) {
-			case '0':
-				p++;
-				m->mdmreg[12] &= ~4;
-				break;
-			case '1':
-				p++;
-				m->mdmreg[12] |= 4;
-				break;
-			default:
-				isdn_tty_modem_result(4, info);
-				return;
-			}
-			break;
-		case 'H':
-			/* H - On/Off-hook */
-			p++;
-			switch (*p) {
-			case '0':
-				p++;
-				isdn_tty_on_hook(info);
-				break;
-			case '1':
-				p++;
-				isdn_tty_off_hook();
-				break;
-			default:
-				isdn_tty_on_hook(info);
-				break;
-			}
-			break;
-		case 'I':
-			/* I - Information */
-			p++;
-			isdn_tty_at_cout("ISDN for Linux  (c) by Fritz Elfert\r\n", info);
-			switch (*p) {
-			case '0':
-			case '1':
-				p++;
-				break;
-			default:
-			}
-			break;
-		case 'O':
-			/* O - Go online */
-			p++;
-			if (dev->mdm.msr[info->line] & UART_MSR_DCD)	/* if B-Channel is up */
-				isdn_tty_modem_result(5, info);
-			else
-				isdn_tty_modem_result(3, info);
-			return;
-		case 'Q':
-			/* Q - Turn Emulator messages on/off */
-			p++;
-			switch (*p) {
-			case '0':
-				p++;
-				m->mdmreg[12] |= 1;
-				break;
-			case '1':
-				p++;
-				m->mdmreg[12] &= ~1;
-				break;
-			default:
-				isdn_tty_modem_result(4, info);
-				return;
-			}
-			break;
-		case 'S':
-			/* S - Set/Get Register */
-			p++;
-			mreg = isdn_getnum(&p);
-			if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) {
-				isdn_tty_modem_result(4, info);
-				return;
-			}
-			switch (*p) {
-			case '=':
-				p++;
-				mval = isdn_getnum(&p);
-				if (mval >= 0 && mval <= 255) {
-					if ((mreg == 16) && ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)) {
-						isdn_tty_modem_result(4, info);
-						return;
-					}
-					m->mdmreg[mreg] = mval;
-				} else {
-					isdn_tty_modem_result(4, info);
-					return;
-				}
-				break;
-			case '?':
-				p++;
-				isdn_tty_show_profile(mreg, info);
-				return;
-				break;
-			default:
-				isdn_tty_modem_result(4, info);
-				return;
-			}
-			break;
-		case 'V':
-			/* V - Numeric or ASCII Emulator-messages */
-			p++;
-			switch (*p) {
-			case '0':
-				p++;
-				m->mdmreg[12] |= 2;
-				break;
-			case '1':
-				p++;
-				m->mdmreg[12] &= ~2;
-				break;
-			default:
-				isdn_tty_modem_result(4, info);
-				return;
-			}
-			break;
-		case 'Z':
-			/* Z - Load Registers from Profile */
-			p++;
-			isdn_tty_modem_reset_regs(m, 1);
-			break;
-		case '+':
-			p++;
-			switch (*p) {
-			case 'F':
-				break;
-			}
-			break;
-		case '&':
-			p++;
-			switch (*p) {
-			case 'B':
-				/* &B - Set Buffersize */
-				p++;
-				i = isdn_getnum(&p);
-				if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) {
-					isdn_tty_modem_result(4, info);
-					return;
-				}
-				m->mdmreg[16] = i / 16;
-				break;
-			case 'D':
-				/* &D - Set DCD-Low-behavior */
-				p++;
-				switch (isdn_getnum(&p)) {
-				case 2:
-					m->mdmreg[12] &= ~32;
-					break;
-				case 3:
-					m->mdmreg[12] |= 32;
-					break;
-				default:
-					isdn_tty_modem_result(4, info);
-					return;
-				}
-				break;
-			case 'E':
-				/* &E -Set EAZ/MSN */
-				p++;
-				isdn_tty_get_msnstr(m->msn, &p);
-				break;
-			case 'F':
-				/* &F -Set Factory-Defaults */
-				p++;
-				isdn_tty_reset_profile(m);
-				isdn_tty_modem_reset_regs(m, 1);
-				break;
-			case 'S':
-				/* &S - Set Windowsize */
-				p++;
-				i = isdn_getnum(&p);
-				if ((i > 0) && (i < 9))
-					m->mdmreg[17] = i;
-				else {
-					isdn_tty_modem_result(4, info);
-					return;
-				}
-				break;
-			case 'V':
-				/* &V - Show registers */
-				p++;
-				for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
-					sprintf(rb, "S%d=%d%s", i, m->mdmreg[i], (i == 6) ? "\r\n" : " ");
-					isdn_tty_at_cout(rb, info);
-				}
-				sprintf(rb, "\r\nEAZ/MSN: %s\r\n", strlen(m->msn) ? m->msn : "None");
-				isdn_tty_at_cout(rb, info);
-				break;
-			case 'W':
-				/* &W - Write Profile */
-				p++;
-				switch (*p) {
-				case '0':
-					p++;
-					modem_write_profile(m);
-					break;
-				default:
-					isdn_tty_modem_result(4, info);
-					return;
-				}
-				break;
-			case 'X':
-				/* &X - Switch to BTX-Mode */
-				p++;
-				switch (*p) {
-				case '0':
-					p++;
-					m->mdmreg[13] &= ~2;
-					break;
-				case '1':
-					p++;
-					m->mdmreg[13] |= 2;
-					m->mdmreg[14] = 0;
-					m->mdmreg[16] = 7;
-					m->mdmreg[18] = 7;
-					m->mdmreg[19] = 0;
-					break;
-				default:
-					isdn_tty_modem_result(4, info);
-					return;
-				}
-				break;
-			default:
-				isdn_tty_modem_result(4, info);
-				return;
-			}
-			break;
-		default:
-			isdn_tty_modem_result(4, info);
-			return;
-		}
-	}
-	isdn_tty_modem_result(0, info);
+        for (p = &m->mdmcmd[2]; *p;) {
+                switch (*p) {
+                        case 'A':
+                                /* A - Accept incoming call */
+                                p++;
+                                isdn_tty_cmd_ATA(info);
+                                return;
+                                break;
+                        case 'D':
+                                /* D - Dial */
+                                isdn_tty_getdial(++p, ds);
+                                p += strlen(p);
+                                if (!strlen(m->msn))
+                                        isdn_tty_modem_result(10, info);
+                                else if (strlen(ds))
+                                        isdn_tty_dial(ds, info, m);
+                                else
+                                        isdn_tty_modem_result(4, info);
+                                return;
+                        case 'E':
+                                /* E - Turn Echo on/off */
+                                p++;
+                                switch (isdn_getnum(&p)) {
+                                        case 0:
+                                                m->mdmreg[12] &= ~4;
+                                                break;
+                                        case 1:
+                                                m->mdmreg[12] |= 4;
+                                                break;
+                                        default:
+                                                PARSE_ERROR;
+                                }
+                                break;
+                        case 'H':
+                                /* H - On/Off-hook */
+                                p++;
+                                switch (*p) {
+                                        case '0':
+                                                p++;
+                                                isdn_tty_on_hook(info);
+                                                break;
+                                        case '1':
+                                                p++;
+                                                isdn_tty_off_hook();
+                                                break;
+                                        default:
+                                                isdn_tty_on_hook(info);
+                                                break;
+                                }
+                                break;
+                        case 'I':
+                                /* I - Information */
+                                p++;
+                                isdn_tty_at_cout("\r\nLinux ISDN", info);
+                                switch (*p) {
+                                        case '0':
+                                        case '1':
+                                                p++;
+                                                break;
+                                        default:
+                                }
+                                break;
+                        case 'O':
+                                /* O - Go online */
+                                p++;
+                                if (info->msr & UART_MSR_DCD)
+                                        /* if B-Channel is up */
+                                        isdn_tty_modem_result(5, info);
+                                else
+                                        isdn_tty_modem_result(3, info);
+                                return;
+                        case 'Q':
+                                /* Q - Turn Emulator messages on/off */
+                                p++;
+                                switch (isdn_getnum(&p)) {
+                                        case 0:
+                                                m->mdmreg[12] |= 1;
+                                                break;
+                                        case 1:
+                                                m->mdmreg[12] &= ~1;
+                                                break;
+                                        default:
+                                                PARSE_ERROR;
+                                }
+                                break;
+                        case 'S':
+                                /* S - Set/Get Register */
+                                p++;
+                                if (isdn_tty_cmd_ATS(&p, info))
+                                        return;
+                                break;
+                        case 'V':
+                                /* V - Numeric or ASCII Emulator-messages */
+                                p++;
+                                switch (isdn_getnum(&p)) {
+                                        case 0:
+                                                m->mdmreg[12] |= 2;
+                                                break;
+                                        case 1:
+                                                m->mdmreg[12] &= ~2;
+                                                break;
+                                        default:
+                                                PARSE_ERROR;
+                                }
+                                break;
+                        case 'Z':
+                                /* Z - Load Registers from Profile */
+                                p++;
+                                isdn_tty_modem_reset_regs(m, 1);
+                                break;
+#ifdef CONFIG_ISDN_AUDIO
+                        case '+':
+                                p++;
+                                switch (*p) {
+                                        case 'F':
+                                                if (strncmp(p,"FCLASS",6))
+                                                        PARSE_ERROR;
+                                                p += 6;
+                                                switch (*p) {
+                                                        case '?':
+                                                                p++;
+                                                                sprintf(ds,"\r\n%d",
+                                                                        (m->mdmreg[18]&1)?8:0);
+                                                                isdn_tty_at_cout(ds, info);
+                                                                break;
+                                                        case '=':
+                                                                p++;
+                                                                switch (*p) {
+                                                                        case '0':
+                                                                                p++;
+                                                                                m->mdmreg[18] = 4;
+                                                                                break;
+                                                                        case '8':
+                                                                                p++;
+                                                                                m->mdmreg[18] = 5;
+                                                                                m->mdmreg[16] = VBUFX;
+                                                                                info->xmit_size = VBUF;
+                                                                                break;
+                                                                        case '?':
+                                                                                p++;
+                                                                                isdn_tty_at_cout("\r\n0,8",
+                                                                                                 info);
+                                                                                break;
+                                                                        default:
+                                                                                PARSE_ERROR;
+                                                                }
+                                                                break;
+                                                        default:
+                                                                PARSE_ERROR;
+                                                                
+                                                }
+                                                break;
+                                        case 'V':
+                                                if (!(m->mdmreg[18] & 1))
+                                                        PARSE_ERROR;
+                                                p++;
+                                                if (isdn_tty_cmd_PLUSV(&p, info))
+                                                        return;
+                                                break;
+                                }
+                                break;
+#endif        /* CONFIG_ISDN_AUDIO */
+                        case '&':
+                                p++;
+                                if (isdn_tty_cmd_ATand(&p, info))
+                                        return;
+                                break;
+                        default:
+                                isdn_tty_modem_result(4, info);
+                                return;
+                }
+        }
+        isdn_tty_modem_result(0, info);
 }
 
 /* Need own toupper() because standard-toupper is not available
@@ -1877,7 +2460,7 @@
  */
 static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
 {
-	atemu *m = &dev->mdm.atmodem[info->line];
+	atemu *m = &info->emu;
 	int total = 0;
 	u_char c;
 	char eb[2];
@@ -1921,16 +2504,16 @@
 			if (m->mdmcmdl < 255) {
 				c = my_toupper(c);
 				switch (m->mdmcmdl) {
-				case 0:
-					if (c == 'A')
-						m->mdmcmd[m->mdmcmdl++] = c;
-					break;
-				case 1:
-					if (c == 'T')
-						m->mdmcmd[m->mdmcmdl++] = c;
-					break;
-				default:
-					m->mdmcmd[m->mdmcmdl++] = c;
+                                        case 0:
+                                                if (c == 'A')
+                                                        m->mdmcmd[m->mdmcmdl++] = c;
+                                                break;
+                                        case 1:
+                                                if (c == 'T')
+                                                        m->mdmcmd[m->mdmcmdl++] = c;
+                                                break;
+                                        default:
+                                                m->mdmcmd[m->mdmcmdl++] = c;
 				}
 			}
 		}
@@ -1952,16 +2535,18 @@
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 		if (USG_MODEM(dev->usage[i]))
-			if ((midx = dev->m_idx[i]) >= 0)
-				if (dev->mdm.online[midx]) {
+			if ((midx = dev->m_idx[i]) >= 0) {
+                                modem_info *info = &dev->mdm.info[midx];
+				if (info->online) {
 					ton = 1;
-					if ((dev->mdm.atmodem[midx].pluscount == 3) &&
-					    ((jiffies - dev->mdm.atmodem[midx].lastplus) > PLUSWAIT2)) {
-						dev->mdm.atmodem[midx].pluscount = 0;
-						dev->mdm.online[midx] = 0;
-						isdn_tty_modem_result(0, &dev->mdm.info[midx]);
+					if ((info->emu.pluscount == 3) &&
+					    ((jiffies - info->emu.lastplus) > PLUSWAIT2)) {
+						info->emu.pluscount = 0;
+						info->online = 0;
+						isdn_tty_modem_result(0, info);
 					}
 				}
+                        }
 	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
 }
 
@@ -1977,76 +2562,46 @@
 	int midx;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (USG_MODEM(dev->usage[i]))
-			if ((midx = dev->m_idx[i]) >= 0)
-				if (dev->mdm.msr[midx] & UART_MSR_RI) {
+		if (USG_MODEMORVOICE(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0) {
+                                modem_info *info = &dev->mdm.info[midx];
+				if (info->msr & UART_MSR_RI) {
 					ton = 1;
-					isdn_tty_modem_result(2, &dev->mdm.info[midx]);
+					isdn_tty_modem_result(2, info);
 				}
+                        }
 	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
 }
 
+/*
+ * For all online tty's, try sending data to
+ * the lower levels.
+ */
 void isdn_tty_modem_xmit(void)
 {
 	int ton = 0;
 	int i;
 	int midx;
-	char *bufptr;
-	int buflen;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (USG_MODEM(dev->usage[i]))
-			if ((midx = dev->m_idx[i]) >= 0)
-				if (dev->mdm.online[midx]) {
-					modem_info *info = &(dev->mdm.info[midx]);
-					ulong flags;
-
-					save_flags(flags);
-					cli();
-					if (info->xmit_count > 0) {
-						struct tty_struct *tty = info->tty;
+		if (USG_MODEMORVOICE(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0) {
+                                modem_info *info = &dev->mdm.info[midx];
+				if ((info->online > 1) ||
+                                    (info->vonline ==2 )) {
+					if (skb_queue_len(info->xmit_buf)) {
 						ton = 1;
-#if 0
-						printk(KERN_DEBUG "WB2: %d\n", info->xmit_count);
-#endif
-						bufptr = info->xmit_buf;
-						buflen = info->xmit_count;
-						if (dev->mdm.atmodem[midx].mdmreg[13] & 2) {
-							/* Add T.70 simplified header */
-#ifdef ISDN_DEBUG_MODEM_DUMP
-							isdn_dumppkt("T70pack3:", bufptr, buflen, 40);
-#endif
-							bufptr -= 4;
-							buflen += 4;
-							memcpy(bufptr, "\1\0\1\0", 4);
-#ifdef ISDN_DEBUG_MODEM_DUMP
-							isdn_dumppkt("T70pack4:", bufptr, buflen, 40);
-#endif
-						}
-						if (isdn_writebuf_stub(info->isdn_driver, info->isdn_channel,
-                                                                       bufptr, buflen, 0) > 0) {
-							info->xmit_count = 0;
-							info->xmit_size = dev->mdm.atmodem[midx].mdmreg[16] * 16;
-#if FUTURE
-							info->send_outstanding++;
-							dev->mdm.msr[midx] &= ~UART_MSR_CTS;
-#endif
-							if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-							    tty->ldisc.write_wakeup)
-								(tty->ldisc.write_wakeup) (tty);
-							wake_up_interruptible(&tty->write_wait);
-						}
+                                                isdn_tty_senddown(info);
 					}
-					restore_flags(flags);
 				}
+                        }
 	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
 }
 
-#if FUTURE
 /*
  * A packet has been output successfully.
  * Search the tty-devices for an appropriate device, decrement its
- * counter for outstanding packets, and set CTS if this counter reaches 0.
+ * counter for outstanding packets, and set CTS.
  */
 void isdn_tty_bsent(int drv, int chan)
 {
@@ -2058,15 +2613,13 @@
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		modem_info *info = &dev->mdm.info[i];
 		if ((info->isdn_driver == drv) &&
-		    (info->isdn_channel == chan) &&
-		    (info->send_outstanding)) {
-			if (!(--info->send_outstanding))
-				dev->mdm.msr[i] |= UART_MSR_CTS;
-			restore_flags(flags);
-			return;
-		}
+		    (info->isdn_channel == chan) ) {
+                        info->msr |= UART_MSR_CTS;
+                        if (info->send_outstanding)
+                                if (!(--info->send_outstanding))
+                                        info->lsr &= ~UART_LSR_TEMT;
+                }
 	}
 	restore_flags(flags);
 	return;
 }
-#endif				/* FUTURE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this