isdn4k-utils/ttyId/emulator.c

1031 lines
42 KiB
C

/* $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;
}