/* $Id: emulator.c,v 1.1 2000/08/30 18:27:01 armin Exp $ * * ttyId - CAPI TTY AT-command emulator * * based on the AT-command emulator of the isdn4linux * kernel subsystem. * * Copyright 2000 by Armin Schindler (mac@melware.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: emulator.c,v $ * Revision 1.1 2000/08/30 18:27:01 armin * Okay, here is the first try for an user-land * ttyI daemon. Compilable but not useable. * * */ #include "ttyId.h" #define cmdchar(c) ((c>=' ')&&(c<=0x7f)) #define PARSE_ERROR { tty_modem_result(RESULT_ERROR); return; } #define PARSE_ERROR1 { tty_modem_result(RESULT_ERROR); return 1; } void tty_at_cout(char *msg) { atemu *m = &info.emu; char *p; char c; char *sp = 0; if (!msg) { logit(LOG_ERR, "Null-Message in tty_at_cout"); return; } for (p = msg; *p; p++) { switch (*p) { case '\r': c = m->mdmreg[REG_CR]; break; case '\n': c = m->mdmreg[REG_LF]; break; case '\b': c = m->mdmreg[REG_BS]; break; default: c = *p; } if (!writepty(&c, 1)) logit(LOG_ERR, "could not write in tty_at_cout"); } } /* * Return result of AT-emulator to tty-receive-buffer, depending on * modem-register 12, bit 0 and 1. * For CONNECT-messages also switch to online-mode. * For RING-message handle auto-ATA if register 0 != 0 */ static void tty_modem_result(int code) { atemu *m = &info.emu; static char *msg[] = {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR", "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER", "RINGING", "NO MSN/EAZ", "VCON", "RUNG"}; char s[ISDN_MSNLEN+10]; switch (code) { case RESULT_RING: m->mdmreg[REG_RINGCNT]++; if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA]) /* Automatically accept incoming call */ tty_cmd_ATA(); break; case RESULT_NO_CARRIER: logit(LOG_DEBUG, "Res: NO CARRIER"); m->mdmreg[REG_RINGCNT] = 0; info.ncarrier = 0; if (info.vonline & 1) { logit(LOG_DEBUG, "res: send DLE_ETX"); /* voice-recording, add DLE-ETX */ tty_at_cout("\020\003"); } if (info.vonline & 2) { logit(LOG_DEBUG, "res: send DLE_DC4"); /* voice-playing, add DLE-DC4 */ tty_at_cout("\020\024"); } break; case RESULT_CONNECT: case RESULT_CONNECT64000: logit(LOG_DEBUG, "Res: CONNECT"); sprintf(info.last_cause, "0000"); if (!info.online) info.online = 2; break; case RESULT_VCON: logit(LOG_DEBUG, "res: send VCON"); sprintf(info.last_cause, "0000"); if (!info.online) info.online = 1; break; } /* switch(code) */ if (m->mdmreg[REG_RESP] & BIT_RESP) { /* Show results */ if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) { /* Show numeric results only */ sprintf(s, "\r\n%d\r\n", code); tty_at_cout(s); } else { if (code == RESULT_RING) { /* return if "show RUNG" and ringcounter>1 */ if ((m->mdmreg[REG_RUNG] & BIT_RUNG) && (m->mdmreg[REG_RINGCNT] > 1)) return; /* print CID, _before_ _every_ ring */ if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) { tty_at_cout("\r\nCALLER NUMBER: "); tty_at_cout(info.OAD); } } tty_at_cout("\r\n"); tty_at_cout(msg[code]); switch (code) { case RESULT_CONNECT: switch (m->mdmreg[REG_L2PROT]) { case ISDN_PROTO_L2_MODEM: tty_at_cout(" "); tty_at_cout(m->connmsg); break; } break; case RESULT_RING: /* Append CPN, if enabled */ if ((m->mdmreg[REG_CPN] & BIT_CPN)) { sprintf(s, "/%s", m->cpn); tty_at_cout(s); } /* Print CID only once, _after_ 1st RING */ if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) && (m->mdmreg[REG_RINGCNT] == 1)) { tty_at_cout("\r\n"); tty_at_cout("CALLER NUMBER: "); tty_at_cout(info.OAD); } break; case RESULT_NO_CARRIER: case RESULT_NO_DIALTONE: case RESULT_BUSY: case RESULT_NO_ANSWER: m->mdmreg[REG_RINGCNT] = 0; /* Append Cause-Message if enabled */ if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) { sprintf(s, "/%s", info.last_cause); tty_at_cout(s); } break; case RESULT_CONNECT64000: /* Append Protocol to CONNECT message */ switch (m->mdmreg[REG_L2PROT]) { case ISDN_PROTO_L2_X75I: case ISDN_PROTO_L2_X75UI: case ISDN_PROTO_L2_X75BUI: tty_at_cout("/X.75"); break; case ISDN_PROTO_L2_HDLC: tty_at_cout("/HDLC"); break; case ISDN_PROTO_L2_V11096: tty_at_cout("/V110/9600"); break; case ISDN_PROTO_L2_V11019: tty_at_cout("/V110/19200"); break; case ISDN_PROTO_L2_V11038: tty_at_cout("/V110/38400"); break; } if (m->mdmreg[REG_T70] & BIT_T70) { tty_at_cout("/T.70"); if (m->mdmreg[REG_T70] & BIT_T70_EXT) tty_at_cout("+"); } break; } tty_at_cout("\r\n"); } } } static void tty_report(void) { atemu *m = &info.emu; char s[80]; tty_at_cout("\r\nStatistics of last connection:\r\n\r\n"); sprintf(s, " Remote Number: %s\r\n", info.last_num); tty_at_cout(s); sprintf(s, " Direction: %s\r\n", info.last_dir ? "outgoing" : "incoming"); tty_at_cout(s); tty_at_cout(" Layer-2 Protocol: "); switch (info.last_l2) { case ISDN_PROTO_L2_X75I: tty_at_cout("X.75i"); break; case ISDN_PROTO_L2_X75UI: tty_at_cout("X.75ui"); break; case ISDN_PROTO_L2_X75BUI: tty_at_cout("X.75bui"); break; case ISDN_PROTO_L2_HDLC: tty_at_cout("HDLC"); break; case ISDN_PROTO_L2_V11096: tty_at_cout("V.110 9600 Baud"); break; case ISDN_PROTO_L2_V11019: tty_at_cout("V.110 19200 Baud"); break; case ISDN_PROTO_L2_V11038: tty_at_cout("V.110 38400 Baud"); break; case ISDN_PROTO_L2_TRANS: tty_at_cout("transparent"); break; case ISDN_PROTO_L2_MODEM: tty_at_cout("modem"); break; case ISDN_PROTO_L2_FAX: tty_at_cout("fax"); break; default: tty_at_cout("unknown"); break; } if (m->mdmreg[REG_T70] & BIT_T70) { tty_at_cout("/T.70"); if (m->mdmreg[REG_T70] & BIT_T70_EXT) tty_at_cout("+"); } tty_at_cout("\r\n"); tty_at_cout(" Service: "); switch (info.last_si) { case 1: tty_at_cout("audio\r\n"); break; case 5: tty_at_cout("btx\r\n"); break; case 7: tty_at_cout("data\r\n"); break; default: sprintf(s, "%d\r\n", info.last_si); tty_at_cout(s); break; } sprintf(s, " Hangup location: %s\r\n", info.last_lhup ? "local" : "remote"); tty_at_cout(s); sprintf(s, " Last cause: %s\r\n", info.last_cause); tty_at_cout(s); } /* * Display a modem-register-value. */ static void tty_show_profile(int ridx) { char v[6]; sprintf(v, "\r\n%d", info.emu.mdmreg[ridx]); tty_at_cout(v); } static int tty_check_ats(int mreg, int mval, atemu * m) { /* Some plausibility checks */ switch (mreg) { case REG_L2PROT: if (mval > ISDN_PROTO_L2_MAX) return 1; break; #if 0 /* TODO */ case REG_PSIZE: if ((mval * 16) > 1024) /* TODO */ return 1; if ((m->mdmreg[REG_SI1] & 1) && (mval > 2048)) /* TODO */ return 1; info.xmit_size = mval * 16; switch (m->mdmreg[REG_L2PROT]) { case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: info.xmit_size /= 10; } break; #endif case REG_SI1I: case REG_PLAN: case REG_SCREEN: /* readonly registers */ return 1; } return 0; } /* * Perform ATS command */ static int tty_cmd_ATS(char **p) { atemu *m = &info.emu; int bitpos; int mreg; int mval; int bval; mreg = getnum(p); if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG) PARSE_ERROR1; switch (*p[0]) { case '=': p[0]++; mval = getnum(p); if (mval < 0 || mval > 255) PARSE_ERROR1; if (tty_check_ats(mreg, mval, m)) PARSE_ERROR1; m->mdmreg[mreg] = mval; break; case '.': /* Set/Clear a single bit */ p[0]++; bitpos = getnum(p); if ((bitpos < 0) || (bitpos > 7)) PARSE_ERROR1; switch (*p[0]) { case '=': p[0]++; bval = getnum(p); if (bval < 0 || bval > 1) PARSE_ERROR1; if (bval) mval = m->mdmreg[mreg] | (1 << bitpos); else mval = m->mdmreg[mreg] & ~(1 << bitpos); if (tty_check_ats(mreg, mval, m)) PARSE_ERROR1; m->mdmreg[mreg] = mval; break; case '?': p[0]++; tty_at_cout("\r\n"); tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0"); break; default: PARSE_ERROR1; } break; case '?': p[0]++; tty_show_profile(mreg); break; default: PARSE_ERROR1; break; } return 0; } /* * Perform ATA command */ void tty_cmd_ATA(void) { atemu *m = &info.emu; int l2; if (info.msr & UART_MSR_RI) { /* Accept incoming call */ info.last_dir = 0; strcpy(info.last_num, info.OAD); m->mdmreg[REG_RINGCNT] = 0; info.msr &= ~UART_MSR_RI; l2 = m->mdmreg[REG_L2PROT]; /* If more than one bit set in reg18, autoselect Layer2 */ if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { if (m->mdmreg[REG_SI1I] == 1) { if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX)) l2 = ISDN_PROTO_L2_TRANS; } else l2 = ISDN_PROTO_L2_X75I; } info.last_l2 = l2; #if 0 cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL2; cmd.arg = info->isdn_channel + (l2 << 8); isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); if (l2 == ISDN_PROTO_L2_FAX) { cmd.parm.fax = info->fax; info->fax->direction = ISDN_TTY_FAX_CONN_IN; } isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_ACCEPTD; isdn_command(&cmd); #endif info.dialing = 16; info.emu.carrierwait = 0; /* isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); */ } else tty_modem_result(RESULT_NO_ANSWER); } /* * tty_cmd_dial() performs dialing of a tty */ static void tty_cmd_dial(char *n, atemu * m) { int usg = 0; /* ISDN_USAGE_MODEM; */ int si = 7; int l2 = m->mdmreg[REG_L2PROT]; int i; int j; tty_modem_result(RESULT_NO_DIALTONE); return; #if 0 /* TODO */ for (j = 7; j >= 0; j--) if (m->mdmreg[REG_SI1] & (1 << j)) { si = bit2si[j]; break; } usg = isdn_calc_usage(si, l2); if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX) ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } m->mdmreg[REG_SI1I] = si2bit[si]; i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); if (i < 0) { isdn_tty_modem_result(RESULT_NO_DIALTONE, info); } else { info->isdn_driver = dev->drvmap[i]; info->isdn_channel = dev->chanmap[i]; info->drv_index = i; dev->m_idx[i] = info->line; dev->usage[i] |= ISDN_USAGE_OUTGOING; info->last_dir = 1; strcpy(info->last_num, n); isdn_info_update(); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_CLREAZ; isdn_command(&cmd); strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETEAZ; isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL2; info->last_l2 = l2; cmd.arg = info->isdn_channel + (l2 << 8); isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); #ifdef CONFIG_ISDN_TTY_FAX if (l2 == ISDN_PROTO_L2_FAX) { cmd.parm.fax = info->fax; info->fax->direction = ISDN_TTY_FAX_CONN_OUT; } #endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; sprintf(cmd.parm.setup.phone, "%s", n); sprintf(cmd.parm.setup.eazmsn, "%s", isdn_map_eaz2msn(m->msn, info->isdn_driver)); cmd.parm.setup.si1 = si; cmd.parm.setup.si2 = m->mdmreg[REG_SI2]; cmd.command = ISDN_CMD_DIAL; info->dialing = 1; info->emu.carrierwait = 0; strcpy(dev->num[i], n); isdn_info_update(); isdn_command(&cmd); isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); } #endif } static void tty_off_hook(void) { logit(LOG_DEBUG, "isdn_tty_off_hook"); } /* * Perform ATH Hangup */ static void tty_on_hook(void) { logit(LOG_DEBUG, "isdn_tty_on_hook"); /* TODO tty_modem_hup(1); */ } /* * Parse AT&.. commands. */ static int tty_cmd_ATand(char **p) { atemu *m = &info.emu; int i; char rb[100]; #define MAXRB (sizeof(rb) - 1) switch (*p[0]) { #if 0 /*TODO*/ case 'B': /* &B - Set Buffersize */ p[0]++; i = getnum(p); if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX)) PARSE_ERROR1; if ((m->mdmreg[REG_SI1] & 1) && (i > 2048)) PARSE_ERROR1; m->mdmreg[REG_PSIZE] = i / 16; info.xmit_size = m->mdmreg[REG_PSIZE] * 16; switch (m->mdmreg[REG_L2PROT]) { case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: info.xmit_size /= 10; } break; #endif case 'C': /* &C - DCD Status */ p[0]++; switch (getnum(p)) { case 0: m->mdmreg[REG_DCD] &= ~BIT_DCD; break; case 1: m->mdmreg[REG_DCD] |= BIT_DCD; break; default: PARSE_ERROR1 } break; case 'D': /* &D - Set DTR-Low-behavior */ p[0]++; switch (getnum(p)) { case 0: m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP; m->mdmreg[REG_DTRR] &= ~BIT_DTRR; break; case 2: m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; m->mdmreg[REG_DTRR] &= ~BIT_DTRR; break; case 3: m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; m->mdmreg[REG_DTRR] |= BIT_DTRR; break; default: PARSE_ERROR1 } break; case 'E': /* &E -Set EAZ/MSN */ p[0]++; get_msnstr(m->msn, p); break; case 'F': /* &F -Set Factory-Defaults */ p[0]++; if (info.msr & UART_MSR_DCD) PARSE_ERROR1; reset_profile(); modem_reset_regs(1); break; case 'K': /* only for be compilant with common scripts */ /* &K Flowcontrol - no function */ p[0]++; getnum(p); break; case 'L': /* &L -Set Numbers to listen on */ p[0]++; i = 0; while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) && (i < ISDN_LMSNLEN)) m->lmsn[i++] = *p[0]++; m->lmsn[i] = '\0'; break; #if 0 /*TODO*/ case 'R': /* &R - Set V.110 bitrate adaption */ p[0]++; i = getnum(p); switch (i) { case 0: /* Switch off V.110, back to X.75 */ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; m->mdmreg[REG_SI2] = 0; info.xmit_size = m->mdmreg[REG_PSIZE] * 16; break; case 9600: m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096; m->mdmreg[REG_SI2] = 197; info.xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; break; case 19200: m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019; m->mdmreg[REG_SI2] = 199; info.xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; break; case 38400: m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038; m->mdmreg[REG_SI2] = 198; /* no existing standard for this */ info.xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; break; default: PARSE_ERROR1; } /* Switch off T.70 */ m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); /* Set Service 7 */ m->mdmreg[REG_SI1] |= 4; break; #endif case 'S': /* &S - Set Windowsize */ p[0]++; i = getnum(p); if ((i > 0) && (i < 9)) m->mdmreg[REG_WSIZE] = i; else PARSE_ERROR1; break; case 'V': /* &V - Show registers */ p[0]++; tty_at_cout("\r\n"); for (i = 0; i < ISDN_MODEM_NUMREG; i++) { sprintf(rb, "S%02d=%03d%s", i, m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n"); tty_at_cout(rb); } sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n", strlen(m->msn) ? m->msn : "None"); tty_at_cout(rb); if (strlen(m->lmsn)) { tty_at_cout("\r\nListen: "); tty_at_cout(m->lmsn); tty_at_cout("\r\n"); } break; case 'W': /* &W - Write Profile */ p[0]++; switch (*p[0]) { case '0': p[0]++; /* TODO modem_write_profile(m); */ break; default: PARSE_ERROR1; } break; #if 0 /*TODO*/ case 'X': /* &X - Switch to BTX-Mode and T.70 */ p[0]++; switch (getnum(p)) { case 0: m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); info.xmit_size = m->mdmreg[REG_PSIZE] * 16; break; case 1: m->mdmreg[REG_T70] |= BIT_T70; m->mdmreg[REG_T70] &= ~BIT_T70_EXT; m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; info.xmit_size = 112; m->mdmreg[REG_SI1] = 4; m->mdmreg[REG_SI2] = 0; break; case 2: m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT); m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; info.xmit_size = 112; m->mdmreg[REG_SI1] = 4; m->mdmreg[REG_SI2] = 0; break; default: PARSE_ERROR1; } break; #endif default: PARSE_ERROR1; } return 0; } /* * Parse and perform an AT-command-line. */ static void tty_parse_at(void) { atemu *m = &info.emu; char *p; char ds[80]; logit(LOG_DEBUG, "AT: '%s'", m->mdmcmd); for (p = &m->mdmcmd[2]; *p;) { switch (*p) { case ' ': p++; break; case 'A': /* A - Accept incoming call */ p++; tty_cmd_ATA(); return; case 'D': /* D - Dial */ if (info.msr & UART_MSR_DCD) PARSE_ERROR; if (info.msr & UART_MSR_RI) { tty_modem_result(RESULT_NO_CARRIER); return; } getdial(++p, ds, sizeof(ds)); p += strlen(p); if (!strlen(m->msn)) tty_modem_result(RESULT_NO_MSN_EAZ); else if (strlen(ds)) tty_cmd_dial(ds, m); else PARSE_ERROR; return; case 'E': /* E - Turn Echo on/off */ p++; switch (getnum(&p)) { case 0: m->mdmreg[REG_ECHO] &= ~BIT_ECHO; break; case 1: m->mdmreg[REG_ECHO] |= BIT_ECHO; break; default: PARSE_ERROR; } break; case 'H': /* H - On/Off-hook */ p++; switch (*p) { case '0': p++; tty_on_hook(); break; case '1': p++; tty_off_hook(); break; default: tty_on_hook(); break; } break; case 'I': /* I - Information */ p++; tty_at_cout("\r\nisdn4linux AT command emulator"); switch (*p) { case '0': case '1': p++; break; case '2': p++; tty_report(); break; case '3': p++; sprintf(ds, "\r\n%d", info.emu.charge); tty_at_cout(ds); break; default: } break; case 'L': case 'M': /* only for be compilant with common scripts */ /* no function */ p++; getnum(&p); break; case 'O': /* O - Go online */ p++; if (info.msr & UART_MSR_DCD) { /* if B-Channel is up */ tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT:RESULT_CONNECT64000); } else tty_modem_result(RESULT_NO_CARRIER); return; case 'Q': /* Q - Turn Emulator messages on/off */ p++; switch (getnum(&p)) { case 0: m->mdmreg[REG_RESP] |= BIT_RESP; break; case 1: m->mdmreg[REG_RESP] &= ~BIT_RESP; break; default: PARSE_ERROR; } break; case 'S': /* S - Set/Get Register */ p++; if (tty_cmd_ATS(&p)) return; break; case 'V': /* V - Numeric or ASCII Emulator-messages */ p++; switch (getnum(&p)) { case 0: m->mdmreg[REG_RESP] |= BIT_RESPNUM; break; case 1: m->mdmreg[REG_RESP] &= ~BIT_RESPNUM; break; default: PARSE_ERROR; } break; case 'Z': /* Z - Load Registers from Profile */ p++; if (info.msr & UART_MSR_DCD) { info.online = 0; /* TODO isdn_tty_on_hook(info); */ } modem_reset_regs(1); break; #if 0 /* TODO */ case '+': p++; switch (*p) { case 'F': p++; if (isdn_tty_cmd_PLUSF(&p, info)) return; break; case 'V': if ((!(m->mdmreg[REG_SI1] & 1)) || (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) PARSE_ERROR; p++; if (isdn_tty_cmd_PLUSV(&p, info)) return; break; case 'S': /* SUSPEND */ p++; isdn_tty_get_msnstr(ds, &p); isdn_tty_suspend(ds, info, m); break; case 'R': /* RESUME */ p++; isdn_tty_get_msnstr(ds, &p); isdn_tty_resume(ds, info, m); break; case 'M': /* MESSAGE */ p++; isdn_tty_send_msg(info, m, p); break; default: PARSE_ERROR; } break; #endif case '&': p++; if (tty_cmd_ATand(&p)) return; break; default: PARSE_ERROR; } } if (!info.vonline) tty_modem_result(RESULT_OK); } /* * Perform line-editing of AT-commands * * Parameters: * p inputbuffer * count length of buffer */ int tty_edit_at(const char *p, int count) { atemu *m = &info.emu; int total = 0; u_char c; char eb[2]; int cnt; for (cnt = count; cnt > 0; p++, cnt--) { c = *p; total++; if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) { /* Separator (CR or LF) */ m->mdmcmd[m->mdmcmdl] = 0; if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; eb[1] = 0; tty_at_cout(eb); } if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2)))) tty_parse_at(); m->mdmcmdl = 0; continue; } if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) { /* Backspace-Function */ if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) { if (m->mdmcmdl) m->mdmcmdl--; if (m->mdmreg[REG_ECHO] & BIT_ECHO) tty_at_cout("\b"); } continue; } if (cmdchar(c)) { if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; eb[1] = 0; tty_at_cout(eb); } if (m->mdmcmdl < 255) { c = toupper(c); switch (m->mdmcmdl) { case 1: if (c == 'T') { m->mdmcmd[m->mdmcmdl] = c; m->mdmcmd[++m->mdmcmdl] = 0; break; } else m->mdmcmdl = 0; /* Fall through, check for 'A' */ case 0: if (c == 'A') { m->mdmcmd[m->mdmcmdl] = c; m->mdmcmd[++m->mdmcmdl] = 0; } break; default: m->mdmcmd[m->mdmcmdl] = c; m->mdmcmd[++m->mdmcmdl] = 0; } } } } return total; }