Commit 2283ec95 authored by Rob Swindell's avatar Rob Swindell 💬
Browse files

Add "Caller ID" support, enabled with AT#CID=1 or AT+VCID=1

Also controlled via [modem] CallerID key in svdm.ini fiile.
Reports the connected IP address between the first and second RING result.
Required a fix to reset the ringcount to 0 upon new connection.

Simplified the AT command parsing logic a bit.
parent ccb4d7dd
...@@ -122,7 +122,6 @@ void usage(const char* progname) ...@@ -122,7 +122,6 @@ void usage(const char* progname)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
const char* supported_cmds = "ADEHIMOQSVXZ&";
const char* string_cmds = "D"; const char* string_cmds = "D";
#define MAX_SAVES 20 #define MAX_SAVES 20
struct modem { struct modem {
...@@ -141,6 +140,7 @@ struct modem { ...@@ -141,6 +140,7 @@ struct modem {
bool numeric_mode; bool numeric_mode;
bool offhook; bool offhook;
bool online; // false means "command mode" bool online; // false means "command mode"
bool caller_id;
bool ringing; bool ringing;
ulong ringcount; ulong ringcount;
ulong auto_answer; ulong auto_answer;
...@@ -214,17 +214,27 @@ const char* response_str[] = { ...@@ -214,17 +214,27 @@ const char* response_str[] = {
"CONNECT 9600" "CONNECT 9600"
}; };
char* response(struct modem* modem, enum modem_response code) char* verbal_response(struct modem* modem, const char* response)
{
static char str[128];
safe_snprintf(str, sizeof(str), "%c%c%s%c%c", modem->cr, modem->lf, response, modem->cr, modem->lf);
return str;
}
char* numeric_response(struct modem* modem, int code)
{ {
static char str[128]; static char str[128];
safe_snprintf(str, sizeof(str), "%u%c", code, modem->cr);
return str;
}
char* response(struct modem* modem, enum modem_response code)
{
if(modem->quiet) if(modem->quiet)
return ""; return "";
if(modem->numeric_mode) if(modem->numeric_mode)
safe_snprintf(str, sizeof(str), "%u%c", code, modem->cr); return numeric_response(modem, code);
else return verbal_response(modem, response_str[code]);
safe_snprintf(str, sizeof(str), "%c%c%s%c%c", modem->cr, modem->lf, response_str[code], modem->cr, modem->lf);
return str;
} }
char* ok(struct modem* modem) char* ok(struct modem* modem)
...@@ -288,6 +298,7 @@ const char* iniKeyESC = "ESC"; ...@@ -288,6 +298,7 @@ const char* iniKeyESC = "ESC";
const char* iniKeyExtResults = "ExtResults"; const char* iniKeyExtResults = "ExtResults";
const char* iniKeyDialWait = "DialWait"; const char* iniKeyDialWait = "DialWait";
const char* iniKeyGuardTime = "GuardTime"; const char* iniKeyGuardTime = "GuardTime";
const char* iniKeyCallerID = "CallerID";
void init(struct modem* modem) void init(struct modem* modem)
{ {
...@@ -297,6 +308,7 @@ void init(struct modem* modem) ...@@ -297,6 +308,7 @@ void init(struct modem* modem)
modem->echo_off = !iniGetBool(ini, section, iniKeyEcho, TRUE); modem->echo_off = !iniGetBool(ini, section, iniKeyEcho, TRUE);
modem->quiet = iniGetBool(ini, section, iniKeyQuiet, FALSE); modem->quiet = iniGetBool(ini, section, iniKeyQuiet, FALSE);
modem->numeric_mode = iniGetBool(ini, section, iniKeyNumeric, FALSE); modem->numeric_mode = iniGetBool(ini, section, iniKeyNumeric, FALSE);
modem->caller_id = iniGetBool(ini, section, iniKeyCallerID, FALSE);
modem->cr = (char)iniGetInteger(ini, section, iniKeyCR, '\r'); modem->cr = (char)iniGetInteger(ini, section, iniKeyCR, '\r');
modem->lf = (char)iniGetInteger(ini, section, iniKeyLF, '\n'); modem->lf = (char)iniGetInteger(ini, section, iniKeyLF, '\n');
modem->bs = (char)iniGetInteger(ini, section, iniKeyBS, '\b'); modem->bs = (char)iniGetInteger(ini, section, iniKeyBS, '\b');
...@@ -315,6 +327,7 @@ bool write_cfg(struct modem* modem) ...@@ -315,6 +327,7 @@ bool write_cfg(struct modem* modem)
iniSetBool(&ini, section, iniKeyEcho, !modem->echo_off, style); iniSetBool(&ini, section, iniKeyEcho, !modem->echo_off, style);
iniSetBool(&ini, section, iniKeyQuiet, modem->quiet, style); iniSetBool(&ini, section, iniKeyQuiet, modem->quiet, style);
iniSetBool(&ini, section, iniKeyNumeric, modem->numeric_mode, style); iniSetBool(&ini, section, iniKeyNumeric, modem->numeric_mode, style);
iniSetBool(&ini, section, iniKeyCallerID, modem->caller_id, style);
iniSetInteger(&ini, section, iniKeyCR, modem->cr, style); iniSetInteger(&ini, section, iniKeyCR, modem->cr, style);
iniSetInteger(&ini, section, iniKeyLF, modem->lf, style); iniSetInteger(&ini, section, iniKeyLF, modem->lf, style);
iniSetInteger(&ini, section, iniKeyBS, modem->bs, style); iniSetInteger(&ini, section, iniKeyBS, modem->bs, style);
...@@ -718,175 +731,192 @@ char* atmodem_exec(struct modem* modem) ...@@ -718,175 +731,192 @@ char* atmodem_exec(struct modem* modem)
for(char* p = modem->buf; *p != '\0';) { for(char* p = modem->buf; *p != '\0';) {
char ch = toupper(*p); char ch = toupper(*p);
p++; p++;
if(strchr(supported_cmds, ch) == NULL) if(ch == '&') {
ch = toupper(*p);
ulong val = strtoul(p + 1, &p, 10);
switch(ch) {
case 'W':
resp = write_cfg(modem) ? ok(modem) : error(modem);
break;
case 'Z':
if(val >= MAX_SAVES)
return error(modem);
if(*p == '=') {
p++;
if(stricmp(p, "L") == 0)
p = modem->last;
SAFECOPY(modem->save[val], p);
return write_save(modem, val) ? ok(modem) : error(modem);
}
if(*p == '?' || stricmp(p, "L?") == 0) {
if(stricmp(p, "L?") == 0)
p = modem->last;
else
p = modem->save[val];
safe_snprintf(respbuf, sizeof(respbuf), "%c%s%c%c%s"
,modem->lf, p, modem->cr, modem->lf, ok(modem));
return respbuf;
}
}
continue;
}
// Caller ID control
if(ch == '#' || ch == '+') {
if(ch == '+' && toupper(*p) == 'V')
p++;
if(stricmp(p, "CID?") == 0) {
safe_snprintf(respbuf, sizeof(respbuf), "%c%u%c%c%s"
,modem->lf, modem->caller_id, modem->cr, modem->lf, ok(modem));
return respbuf;
}
if(stricmp(p, "CID=?") == 0) {
safe_snprintf(respbuf, sizeof(respbuf), "%c0,1%c%c%s"
,modem->lf, modem->cr, modem->lf, ok(modem));
return respbuf;
}
if(strnicmp(p, "CID=", 4) == 0) {
modem->caller_id = strtoul(p + 4, &p, 10);
continue;
}
return error(modem); return error(modem);
if(strchr(string_cmds, ch) == NULL) { }
if(ch == '&') { // Numeric argument commands
ch = toupper(*p); ulong val = 0;
ulong val = strtoul(p + 1, &p, 10); // unused if(strchr(string_cmds, ch) == NULL)
switch(ch) { val = strtoul(p, &p, 10);
case 'W': switch(ch) {
resp = write_cfg(modem) ? ok(modem) : error(modem); case 'A':
return answer(modem);
case 'D':
if(sock != INVALID_SOCKET) {
dprintf("Can't dial: Already connected");
return error(modem);
}
if(*p == 'T' /* tone */|| *p == 'P' /* pulse */)
p++;
return dial(modem, p);
case 'E':
modem->echo_off = !val;
break;
case 'H':
modem->offhook = val;
modem->ringing = false;
if(!modem->offhook) {
if(sock != INVALID_SOCKET) {
disconnect(modem);
}
}
break;
case 'I':
switch(val) {
case 0:
safe_snprintf(respbuf, sizeof(respbuf)
,"\r\n" TITLE " v" VERSION " Copyright %s Rob Swindell\r\n%s/%s\r\n"
,&__DATE__[7]
,GIT_BRANCH
,GIT_HASH
);
break; break;
case 'Z': case 1:
if(val >= MAX_SAVES) safe_snprintf(respbuf, sizeof(respbuf), "\r\n%s\r\n", ini_fname);
return error(modem); break;
if(*p == '=') { default:
p++; return error(modem);
if(stricmp(p, "L") == 0)
p = modem->last;
SAFECOPY(modem->save[val], p);
return write_save(modem, val) ? ok(modem) : error(modem);
}
if(*p == '?' || stricmp(p, "L?") == 0) {
if(stricmp(p, "L?") == 0)
p = modem->last;
else
p = modem->save[val];
safe_snprintf(respbuf, sizeof(respbuf), "%c%s%c%c%s"
,modem->lf, p, modem->cr, modem->lf, ok(modem));
return respbuf;
}
} }
continue; return respbuf;
} case 'O':
// Numeric argument commands if(sock == INVALID_SOCKET)
ulong val = strtoul(p, &p, 10); return error(modem);
switch(ch) { modem->online = true;
case 'A': return connect_result(modem);
return answer(modem); break;
case 'E': case 'V':
modem->echo_off = !val; modem->numeric_mode = !val;
break; resp = ok(modem); // Use the new verbal/numeric mode in response (ala USRobotics)
case 'H': break;
modem->offhook = val; case 'Q':
modem->ringing = false; modem->quiet = val;
if(!modem->offhook) { resp = ok(modem); // Use the new quiet/verbose mode in response (ala USRobotics)
if(sock != INVALID_SOCKET) { break;
disconnect(modem); case 'S':
} if(*p == '=') {
ulong sreg = val;
ulong val = strtoul(p + 1, &p, 10);
dprintf("S%lu = %lu", sreg, val);
switch(sreg) {
case 0:
if(val && listening_sock == INVALID_SOCKET) {
dprintf("Can't enable auto-answer when not in listening mode");
return error(modem);
}
modem->auto_answer = val;
break;
case 1:
modem->ringcount = val;
break;
case 2:
modem->esc = (char)val;
break;
case 3:
modem->cr = (char)val;
break;
case 4:
modem->lf = (char)val;
break;
case 5:
modem->bs = (char)val;
break;
case 7:
modem->dial_wait = val;
break;
case 12:
modem->guard_time = val;
break;
} }
break; } else if(*p == '?') {
case 'I':
switch(val) { switch(val) {
case 0: case 0:
safe_snprintf(respbuf, sizeof(respbuf) val = modem->auto_answer;
,"\r\n" TITLE " v" VERSION " Copyright %s Rob Swindell\r\n%s/%s\r\n"
,&__DATE__[7]
,GIT_BRANCH
,GIT_HASH
);
break; break;
case 1: case 1:
safe_snprintf(respbuf, sizeof(respbuf), "\r\n%s\r\n", ini_fname); val = modem->ringcount;
break;
case 2:
val = modem->esc;
break;
case 3:
val = modem->cr;
break;
case 4:
val = modem->lf;
break;
case 5:
val = modem->bs;
break;
case 7:
val = modem->dial_wait;
break;
case 12:
val = modem->guard_time;
break; break;
default: default:
return error(modem); val = 0;
break;
} }
safe_snprintf(respbuf, sizeof(respbuf), "%c%03lu%c%c%s"
,modem->lf, val, modem->cr, modem->lf, ok(modem));
return respbuf; return respbuf;
case 'O': } else
if(sock == INVALID_SOCKET) return error(modem);
return error(modem); break;
modem->online = true; case 'X':
return connect_result(modem); modem->ext_results = val;
break; break;
case 'V': case 'Z':
modem->numeric_mode = !val; init(modem);
resp = ok(modem); // Use the new verbal/numeric mode in response (ala USRobotics) break;
break; default:
case 'Q': return error(modem);
modem->quiet = val;
resp = ok(modem); // Use the new quiet/verbose mode in response (ala USRobotics)
break;
case 'S':
if(*p == '=') {
ulong sreg = val;
ulong val = strtoul(p + 1, &p, 10);
dprintf("S%lu = %lu", sreg, val);
switch(sreg) {
case 0:
if(val && listening_sock == INVALID_SOCKET) {
dprintf("Can't enable auto-answer when not in listening mode");
return error(modem);
}
modem->auto_answer = val;
break;
case 1:
modem->ringcount = val;
break;
case 2:
modem->esc = (char)val;
break;
case 3:
modem->cr = (char)val;
break;
case 4:
modem->lf = (char)val;
break;
case 5:
modem->bs = (char)val;
break;
case 7:
modem->dial_wait = val;
break;
case 12:
modem->guard_time = val;
break;
}
} else if(*p == '?') {
switch(val) {
case 0:
val = modem->auto_answer;
break;
case 1:
val = modem->ringcount;
break;
case 2:
val = modem->esc;
break;
case 3:
val = modem->cr;
break;
case 4:
val = modem->lf;
break;
case 5:
val = modem->bs;
break;
case 7:
val = modem->dial_wait;
break;
case 12:
val = modem->guard_time;
break;
default:
val = 0;
break;
}
safe_snprintf(respbuf, sizeof(respbuf), "%c%03lu%c%c%s"
,modem->lf, val, modem->cr, modem->lf, ok(modem));
return respbuf;
} else
return error(modem);
break;
case 'X':
modem->ext_results = val;
break;
case 'Z':
init(modem);
break;
}
} else { // string argument commands
switch(ch) {
case 'D':
if(sock != INVALID_SOCKET) {
dprintf("Can't dial: Already connected");
return error(modem);
}
if(*p == 'T' /* tone */|| *p == 'P' /* pulse */)
p++;
return dial(modem, p);
}
} }
} }
return resp; return resp;
...@@ -988,8 +1018,10 @@ void listen_thread(void* arg) ...@@ -988,8 +1018,10 @@ void listen_thread(void* arg)
} }
sock = newsock; sock = newsock;
addr = newaddr; addr = newaddr;
if(!modem->offhook && !modem->online) if(!modem->offhook && !modem->online) {
modem->ringing = true; modem->ringing = true;
modem->ringcount = 0;
}
} }
} }
} }
...@@ -1324,6 +1356,11 @@ int main(int argc, char** argv) ...@@ -1324,6 +1356,11 @@ int main(int argc, char** argv)
vdd_writestr(&wrslot, response(&modem, RING)); vdd_writestr(&wrslot, response(&modem, RING));
lastring = now; lastring = now;
modem.ringcount++; modem.ringcount++;
if(modem.ringcount == 1 && modem.caller_id) {
char str[256];
SAFEPRINTF(str, "NMBR = %s", inet_addrtop(&addr, tmp, sizeof(tmp)));
vdd_writestr(&wrslot, verbal_response(&modem, str));
}
if(modem.auto_answer > 0 && modem.ringcount >= modem.auto_answer) { if(modem.auto_answer > 0 && modem.ringcount >= modem.auto_answer) {
vdd_writestr(&wrslot, answer(&modem)); vdd_writestr(&wrslot, answer(&modem));
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment