D-Modem/slmodemd/modem_at.c
2022-12-01 21:40:04 +08:00

1205 lines
26 KiB
C

/*
*
* Copyright (c) 2001, Smart Link Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. Neither the name of the Smart Link Ltd. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
*
* modem_at.c -- modem at command processor.
*
* Author: Sasha K (sashak@smlink.com)
* Seva (seva@smlink.com)
*
*/
/********************************************************************
*
* AT commands status. (p) - partiatly
*
* 1. Implemented:
*
* AT : A B C D E H I L M N O P Q S T V X Y Z
* AT&: C D E F H P R S F W
* AT\: A N
* AT%: C E
* AT+: MS FCLASS GCI GMI GMM GMR
* AT#: CID
*
********************************************************************
*
* 2. Not implemented yet:
*
* AT+: DSC DS DS44 DR
* FAE FTS FRS FTM FRM FRH FTH
* AT&: A K V Z
* AT\: B K
* AT*: B
* AT#: BDR RG TL CLS SPK UD
* VBS VBT VIP VIT VLS VRA VRN
* VRX VSD VSP VSR VSS VTD VTM
* VTS VGT VTX
*
*****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <modem.h>
#include <modem_debug.h>
#define AT_DBG(fmt...) // dprintf("at: " fmt)
/* ------------------------------------------------------------ */
/* prototypes from modem.c */
extern const char modem_author[];
extern const char modem_name[];
extern const char modem_version[];
extern const char modem_date[];
extern struct dp_driver modem_dp_drivers[];
extern int modem_send_to_tty(struct modem *m, const char *buf, int n);
extern int modem_answer(struct modem *m);
extern int modem_dial(struct modem *m);
extern int modem_hook(struct modem *m,unsigned hook_state);
extern int modem_online(struct modem *m);
extern int modem_reset(struct modem *m);
extern void modem_update_speaker(struct modem *m);
extern int modem_homolog_init(struct modem *m, int id, const char *name);
extern int modem_set_mode(struct modem *m, enum MODEM_MODE mode);
/* ------------------------------------------------------------ */
/*
* local util functions
*/
/* simple toupper + strncmp */
static int toupper_strncmp(char *s1, char *s2, int n)
{
int i;
for(i = 0 ; i < n ; i++)
if(toupper(s1[i]) != s2[i])
return s1[i] - s2[i];
return 0;
}
static int set_sreg(struct modem *m, int num, char c, int low, int hi, int *len)
{
if (!isdigit(c)) {
*len = 0;
c = '0';
}
else
*len = 1;
if ( c >= low + '0' && c <= hi + '0' )
return modem_set_sreg(m,num,c - '0');
return -1;
}
/* ------------------------------------------------------------ */
static int process_A(struct modem *m, char *p, int *len)
{
int ret;
AT_DBG("AT A command...\n");
ret = modem_answer(m);
if (ret)
return -1;
return 1;
}
static int process_B(struct modem *m, char *p, int *len)
{
AT_DBG("AT B command...\n");
MODEM_DP(m) = DP_V22;
MODEM_AUTOMODE(m) = 0;
m->min_rate = 1200;
m->max_rate = 1200;
return 0;
}
static int process_D(struct modem *m, char *p, int *len)
{
int ret;
AT_DBG("AT D command...\n");
switch (toupper(*p)) {
case 'T':
modem_set_sreg(m,SREG_TONE_OR_PULSE,1);
break;
case 'P':
#ifdef NO_PULSE_DIAL
return -1;
#endif
modem_set_sreg(m,SREG_TONE_OR_PULSE,0);
break;
case 'L':
if(m->dial_string) {
modem_send_to_tty(m,"Dialing ",8);
modem_send_to_tty(m,m->dial_string,
strlen(m->dial_string));
modem_send_to_tty(m,"...",3);
modem_send_to_tty(m,CRLF_CHARS(m),2);
}
p = 0;
break;
default:
break;
}
if (p)
strncpy(m->dial_string,p,sizeof(m->dial_string));
ret = modem_dial(m);
if (ret)
return -1;
return 1;
}
static int process_H(struct modem *m, char *p, int *len)
{
int ret;
AT_DBG("AT H command...\n");
switch(*p) {
case '1':
*len = 1;
ret = modem_hook(m, MODEM_HOOK_OFF);
break;
case '0':
*len = 1;
p++;
default:
if (isdigit(*p))
return -1;
ret = modem_hook(m, MODEM_HOOK_ON);
break;
}
return ret ? -1 : 0;
}
static int process_I(struct modem *m, char *p, int *len)
{
const char *s;
int c,i;
char *end;
AT_DBG("AT I command...\n");
c = strtoul (p,&end,0);
*len = end - p;
switch(c) {
case 1:
case 2:
modem_send_to_tty(m,modem_name,strlen(modem_name));
modem_send_to_tty(m,", ",2);
modem_send_to_tty(m,modem_version,strlen(modem_version));
modem_send_to_tty(m,CRLF_CHARS(m),2);
modem_send_to_tty(m,modem_author,strlen(modem_author));
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
#if 0
case 2:
s = "Modem Manufacturer: unknown (TBD)";
modem_send_to_tty(m,s,strlen(s));
modem_send_to_tty(m,CRLF_CHARS(m),2);
s = "Modem Provider: unknown (TBD)";
modem_send_to_tty(m,s,strlen(s));
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
#endif
case 3:
s = m->dev_name ? m->dev_name : "unknown";
modem_send_to_tty(m, s, strlen(s));
modem_send_to_tty(m, CRLF_CHARS(m),2);
s = m->driver.name ? m->driver.name : "unknown";
modem_send_to_tty(m, s, strlen(s));
modem_send_to_tty(m, CRLF_CHARS(m),2);
break;
case 4:
for (i=0;i<24;i++) {
char str[24];
snprintf(str,sizeof(str),"s%.2d=%.3d ",i,m->sregs[i]);
modem_send_to_tty(m,str,strlen(str));
if (!((i+1)%8))
modem_send_to_tty(m, CRLF_CHARS(m),2);
}
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
case 5:
modem_send_to_tty(m,"Stored Profile 0:",17);
modem_send_to_tty(m,CRLF_CHARS(m),2);
#if 0
s = load_profile(m,0);
if (s!=NULL)
ret = print_sregs(m,s);
#endif
break;
case 6:
modem_send_to_tty(m, "Stored Profile 1:",17);
modem_send_to_tty(m, CRLF_CHARS(m),2);
#if 0
s = load_profile(m,1);
if (s!=NULL)
ret = print_sregs (m,s);
#endif
break;
case 7:
s = m->homolog->name;
modem_send_to_tty(m,"Country: ",9);
modem_send_to_tty(m,s,strlen(s));
modem_send_to_tty(m, CRLF_CHARS(m),2);
break;
case 0:
modem_send_to_tty(m,modem_name,strlen(modem_name));
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
default:
return -1;
}
return 0;
}
static int process_O(struct modem *m, char *p, int *len)
{
int ret;
AT_DBG("AT O command...\n");
ret = modem_online(m);
if (ret)
return -1;
return 1;
}
static int process_S(struct modem *m, char *p, int *len)
{
unsigned n = 0;
int val;
char *end;
AT_DBG("AT S command...\n");
n = strtoul(p,&end,0);
*len = end - p + 1;
p = end;
if ( n >= sizeof(m->sregs)/sizeof(m->sregs[0]) )
return -1;
switch(*p++) {
case '?':
val = modem_get_sreg(m,n);
if (val < 0)
return -1;
else {
char strval[16];
int l = snprintf(strval,sizeof(strval),"%d",val);
modem_send_to_tty(m, strval,l);
modem_send_to_tty(m, CRLF_CHARS(m),2);
}
return 0;
case '=':
val = strtoul(p, &end, 0);
*len += end - p;
return modem_set_sreg(m,n,val);
break;
default:
return -1;
}
return 0;
}
static int process_Z(struct modem *m, char *p, int *len)
{
AT_DBG("AT Z command...\n");
#if 1
switch(*p) {
case '1':
case '2':
case '3':
case '0':
*len = 1;
break;
default:
*len = 0;
break;
}
modem_reset(m);
return 0;
#else
char *s;
int set_num;
set_num = strtoul (p,&s,0);
*len = s-p;
if (set_num < 0 || set_num > 4 )
return -1;
if (set_num == 0) { /* ATZ0 - load value defined by ATYn */
set_num = m->sregs[SREG_DEFAULT_SETTING];
}
else {
set_num--;
}
AT_DBG("AT Z command - going to load set %d\n",set_num);
s = load_profile (m,set_num);
AT_DBG("AT Z command - done\n");
if (s != NULL) {
/* FIXME: modem reset must be done instead */
/* ... */
memcpy (m->sregs,s,sizeof (m->sregs));
return 0;
}
return -1;
#endif
}
/*
* AT+ commands
*/
static int process_plus_GCI(struct modem *m, char *p, int *len)
{
static const char gci_string[] = "+GCI:";
char strval[8];
const struct homolog_set *h;
char *end;
unsigned long n;
AT_DBG ("AT+GCI...\n");
switch (*p) {
case '=':
p++;
*len = 1;
if (*p == '?') { /* show all country codes */
*len += 1;
modem_send_to_tty(m,gci_string,strlen(gci_string));
modem_send_to_tty(m,"(",1);
for(h=homolog_set;h->name;h++) {
n = sprintf(strval,"%x,",h->id);
modem_send_to_tty(m,strval,n);
}
modem_send_to_tty(m,")",1);
modem_send_to_tty(m, CRLF_CHARS(m),2);
}
else {
n = strtoul (p,&end,16);
if(end == p) {
return -1;
}
*len += end - p; p = end;
/* set country n */
if(modem_homolog_init(m,n,NULL))
return -1;
}
break;
case '?':
n = sprintf(strval,"%x",m->homolog->id);
modem_send_to_tty(m, "+GCI:",5);
modem_send_to_tty(m, strval, n);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
*len = 1;
break;
default:
*len = 0;
return -1;
}
return 0;
}
static int process_plus_MS(struct modem *m, char *p, int *len)
{
char strval[32];
struct dp_driver *drv;
char *end;
unsigned long n;
unsigned int dp_id;
int auto_mode,min_rate,max_rate;
int is_found=0;
AT_DBG ("AT+MS...\n");
switch (*p) {
case '=':
p++;
*len = 1;
if (*p == '?') {
*len += 1;
modem_send_to_tty(m,"(",1);
for(drv = modem_dp_drivers ; drv->id ; drv++ ) {
if(!drv->code || !drv->op) continue;
if(is_found++>0) modem_send_to_tty(m,",",1);
sprintf(strval,"%d",drv->id);
modem_send_to_tty(m,strval,strlen(strval));
}
modem_send_to_tty(m,"),(0,1),",1+7);
sprintf(strval,"(%u-%u),",MODEM_MIN_RATE,MODEM_MAX_RATE);
modem_send_to_tty(m,strval,strlen(strval));
modem_send_to_tty(m,strval,strlen(strval)-1);
modem_send_to_tty(m, CRLF_CHARS(m),2);
break;
}
#define PARSE_MS_ITEM(p,len,item) { \
n = strtoul (p,&end,0); \
if (end != p) { item = n; } \
*len += end - p; p = end; \
if (*p != ',') goto setup_ms; \
*len += 1; p++; \
}
dp_id = MODEM_DP(m);
auto_mode= MODEM_AUTOMODE(m);
min_rate = m->min_rate;
max_rate = m->max_rate;
PARSE_MS_ITEM(p,len,dp_id);
PARSE_MS_ITEM(p,len,auto_mode);
PARSE_MS_ITEM(p,len,min_rate);
PARSE_MS_ITEM(p,len,max_rate);
setup_ms:
if(dp_id != MODEM_DP(m)) {
for(drv = modem_dp_drivers ; drv->id ; drv++ ) {
if(drv->op && drv->code && drv->id == dp_id) {
is_found++;
break;
}
}
if(!is_found) return -1;
}
if((auto_mode != 0 && auto_mode != 1) || min_rate > max_rate ||
min_rate < MODEM_MIN_RATE || max_rate > MODEM_MAX_RATE )
return -1;
MODEM_DP(m) = dp_id;
MODEM_AUTOMODE(m) = auto_mode;
m->min_rate = min_rate;
m->max_rate = max_rate;
break;
case '?':
n = sprintf(strval,"%d,%d,%u,%u",MODEM_DP(m),MODEM_AUTOMODE(m),
m->min_rate,m->max_rate);
modem_send_to_tty(m, strval, n);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
*len = 1;
break;
default:
*len = 0;
return -1;
}
return 0;
}
static int process_plus_FCLASS(struct modem *m, char *p, int *len)
{
char ch;
if(*p == '=' ) {
p++;
*len += 2;
if(*p == '?') {
modem_send_to_tty(m, "0" , 1);
#ifdef MODEM_CONFIG_FAX_CLASS1
modem_send_to_tty(m, ",1" , 2);
#endif
#ifdef MODEM_CONFIG_VOICE
modem_send_to_tty(m, ",8" , 2);
#endif
modem_send_to_tty(m, CRLF_CHARS(m), 2);
}
else if (isdigit(*p)) {
int val = *p - '0';
if ( val != 0
#ifdef MODEM_CONFIG_FAX_CLASS1
&& val != 1
#endif
#ifdef MODEM_CONFIG_VOICE
&& val != 8
#endif
)
return -1;
return modem_set_mode(m, val);
}
else
return -1;
}
else if (*p == '?') {
*len += 1;
ch = '0' + m->mode;
modem_send_to_tty(m, &ch , 1);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
}
else
return -1;
return 0;
}
/*
* Caller ID
*/
#ifdef MODEM_CONFIG_CID
static int process_CID(struct modem *m, char *p, int *len)
{
char ch;
if(*p == '=' ) {
p++;
*len += 2;
if(*p == '?') {
modem_send_to_tty(m, "0,1,2" , 5);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
}
else if (isdigit(*p) && *p - '0' < 3) {
m->cid_requested = *p - '0';
}
else
return -1;
}
else if (*p == '?') {
*len += 1;
ch = '0' + m->cid_requested;
modem_send_to_tty(m, &ch , 1);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
}
else
return -1;
return 0;
}
#endif
/*
* Voice commands
*/
#ifdef MODEM_CONFIG_VOICE
extern int modem_voice_set_device(struct modem *m, unsigned device);
extern int modem_voice_command(struct modem *m, enum VOICE_CMD cmd);
extern int modem_voice_init(struct modem *m);
struct voice_param {
unsigned *val;
unsigned min, max;
};
static int set_voice_params(struct modem *m, char *p, const char *cmd,
struct voice_param *param, int *len,
const char *prefix, const char *suffix)
{
char strval[32];
unsigned val[3];
struct voice_param *prm;
int i;
char *end;
*len += 1;
if( *p == '=' ) {
p++;
if(*p == '?') {
*len += 1;
sprintf(strval, "+%s=", cmd);
modem_send_to_tty(m, strval, strlen(strval));
if(prefix)
modem_send_to_tty(m, prefix, strlen(prefix));
prm = param;
while(prm->val) {
sprintf(strval, "%d-%d", prm->min, prm->max);
modem_send_to_tty(m, strval, strlen(strval));
prm++;
if(prm->val)
modem_send_to_tty(m, ",", 1);
}
if(suffix)
modem_send_to_tty(m, suffix, strlen(suffix));
modem_send_to_tty(m, CRLF_CHARS(m), 2);
return 0;
}
if(prefix) {
if(strncmp(p,prefix,strlen(prefix)))
return -1;
p += strlen(prefix);
*len += strlen(prefix);
}
prm = param;
for (i = 0; prm->val && i < sizeof(val)/sizeof(val[0]) ; i++ ) {
val[i] = strtoul(p,&end,0);
if( (prm->min && val[i] < prm->min) ||
(prm->max && val[i] > prm->max) )
return -1;
*len += end - p;
p = end;
prm++;
if(prm->val) {
if(*p != ',')
return -1;
*len += 1;
p++;
}
}
if(suffix) {
if(strncmp(p,suffix,strlen(suffix)))
return -1;
*len += strlen(suffix);
}
prm = param;
for (i = 0; prm->val && i < sizeof(val)/sizeof(val[0]) ; i++ ) {
*prm->val = val[i];
prm++;
}
return 0;
}
else if ( *p == '?' ) {
sprintf(strval, "+%s=", cmd);
modem_send_to_tty(m, strval, strlen(strval));
if(prefix)
modem_send_to_tty(m, prefix, strlen(prefix));
prm = param;
while(prm->val) {
sprintf(strval, "%d", *prm->val);
modem_send_to_tty(m, strval, strlen(strval));
prm++;
if(prm->val)
modem_send_to_tty(m, ",", 1);
}
if(suffix)
modem_send_to_tty(m, suffix, strlen(suffix));
modem_send_to_tty(m, CRLF_CHARS(m), 2);
return 0;
}
else
return -1;
}
static int process_voice_command(struct modem *m, char *p, int *len)
{
AT_DBG("voice cmd: %s\n", p);
*len += 2;
/* AT+VIP */
if(!toupper_strncmp(p,"IP",2)) {
return modem_voice_init(m);
}
/* AT+VNH=0,1 */
else if(!toupper_strncmp(p,"NH",2)) {
unsigned val = 0;
struct voice_param params[] = {
{&val,0,1}, {} };
return set_voice_params(m,p+2,"VNH",params,len,NULL,NULL);
}
/* AT+VGT=127 */
else if(!toupper_strncmp(p,"GT",2)) {
struct voice_param params[] = {
{&m->voice_info.tx_gain,0,128}, {} };
return set_voice_params(m,p+2,"VGT",params,len,NULL,NULL);
}
/* AT+VGR=127 */
else if(!toupper_strncmp(p,"GR",2)) {
struct voice_param params[] = {
{&m->voice_info.rx_gain,0,128}, {} };
return set_voice_params(m,p+2,"VGR",params,len,NULL,NULL);
}
/* AT+VIT= */
else if(!toupper_strncmp(p,"IT",2)) {
struct voice_param params[] = {
{&m->voice_info.inactivity_timer,0,255}, {} };
return set_voice_params(m,p+2,"VIT",params,len,NULL,NULL);
}
/* AT+VSD=128,50 */
else if(!toupper_strncmp(p,"SD",2)) {
struct voice_param params[] = {
{&m->voice_info.silence_detect_sensitivity,0,255},
{&m->voice_info.silence_detect_period,0,255},
{}
};
return set_voice_params(m,p+2,"VSD",params,len,NULL,NULL);
}
/* AT+VSM=1,8000 */
else if(!toupper_strncmp(p,"SM",2)) {
struct voice_param params[] = {
{&m->voice_info.comp_method,0,1},
{&m->voice_info.sample_rate,8000,8000},
{}
};
return set_voice_params(m,p+2,"VSM",params,len,NULL,NULL);
}
/* AT+VLS=1 */
else if(!toupper_strncmp(p,"LS",2)) {
unsigned dev = (m->voice_obj != NULL);
struct voice_param params[] = {
{&dev,0,1}, {} };
int ret = set_voice_params(m,p+2,"VLS",params,len,NULL,NULL);
if(ret)
return ret;
return modem_voice_set_device(m, dev);
}
/* AT +VRX, +VTX, +VTR */
else if(!toupper_strncmp(p,"TX",2))
return modem_voice_command(m,VOICE_CMD_STATE_TX);
else if(!toupper_strncmp(p,"RX",2))
return modem_voice_command(m,VOICE_CMD_STATE_RX);
else if(!toupper_strncmp(p,"TR",2))
return modem_voice_command(m,VOICE_CMD_STATE_DUPLEX);
/* AT+VTS=[933,,150] */
else if(!toupper_strncmp(p,"TS",2)) {
unsigned orig_len;
int ret;
p += 2;
orig_len = *len;
{
struct voice_param params[] = {
{&m->voice_info.tone1_freq,0,0},
{&m->voice_info.tone2_freq,0,0},
{&m->voice_info.tone_duration,0,6000},
{} };
if(*p == '?' || (*p == '=' && *(p+1) == '?' ))
return set_voice_params(m,p,"VTS",params,len,NULL,NULL);
ret = set_voice_params(m,p,"VTS",params,len,"[","]");
if(!ret)
return modem_voice_command(m, VOICE_CMD_BEEP);
}
{
struct voice_param params[] = {
{&m->voice_info.dtmf_symbol,0,0},
{&m->voice_info.tone_duration,0,100},
{} };
*len = orig_len;
ret = set_voice_params(m,p,"VTS",params,len,"{","}");
if(!ret)
return modem_voice_command(m, VOICE_CMD_DTMF);
}
{
struct voice_param params[] = {
{&m->voice_info.dtmf_symbol,0,0}, {} };
*len = orig_len;
ret = set_voice_params(m,p,"VTS",params,len,NULL,NULL);
if(!ret)
return modem_voice_command(m, VOICE_CMD_DTMF);
}
return -1;
}
else
return -1;
}
#else
#define process_voice_command(m,p,plen) (-1)
#endif /* MODEM_CONFIG_VOICE */
/*
* Fax commands
*/
#ifdef MODEM_CONFIG_FAX
extern int FAX_class1_command(void *obj, unsigned cmd, unsigned param);
static int process_fax_command(struct modem *m, char *p, int *len)
{
unsigned val; char *end;
AT_DBG("fax cmd: %s\n", p);
*len += 2;
/* +FTS= */
if(!toupper_strncmp(p,"TS=",3)) {
*len += 1;
p += 3;
val = strtoul(p,&end,0);
if(p == end)
return -1;
*len += end - p;
p = end;
return FAX_class1_command(m->fax_obj, FAX_CMD_FTS, val);
}
/* +FRS= */
else if(!toupper_strncmp(p,"RS=",3)) { // FIXME: abort on tty input
*len += 1;
p += 3;
val = strtoul(p,&end,0);
if(p == end)
return -1;
*len += end - p;
p = end;
return FAX_class1_command(m->fax_obj, FAX_CMD_FRS, val);
}
/* +FTM= */
else if(!toupper_strncmp(p,"TM=",3)) {
*len += 2;
p+= 3;
if(*p == '?') {
modem_send_to_tty(m,"24,48,72,73,74,96,97,98,121,122,145,146",39);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
return 0;
}
else {
val = strtoul(p,&end,0);
if(p == end)
return -1;
*len += end - p - 1;
p = end;
return FAX_class1_command(m->fax_obj, FAX_CMD_FTM, val);
}
}
/* +FRM= */
else if(!toupper_strncmp(p,"RM=",3)) {
*len += 2;
p+= 3;
if(*p == '?') {
modem_send_to_tty(m,"24,48,72,73,74,96,97,98,121,122,145,146",39);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
}
else {
val = strtoul(p,&end,0);
if(p == end)
return -1;
*len += end - p - 1;
p = end;
return FAX_class1_command(m->fax_obj, FAX_CMD_FRM, val);
}
return 0;
}
/* +FRH= */
else if(!toupper_strncmp(p,"RH=",3)) {
*len += 2;
p+= 3;
if(*p == '?') {
modem_send_to_tty(m,"3",1);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
return 0;
}
else if (isdigit(*p))
return FAX_class1_command(m->fax_obj, FAX_CMD_FRH, *p - '0');
return -1;
}
/* +FTH= */
else if(!toupper_strncmp(p,"TH=",3)) {
*len += 2;
p+= 3;
if(*p == '?') {
modem_send_to_tty(m,"3",1);
modem_send_to_tty(m, CRLF_CHARS(m), 2);
return 0;
}
else if (isdigit(*p))
return FAX_class1_command(m->fax_obj, FAX_CMD_FTH, *p - '0');
return -1;
}
else
return -1;
}
#else
#define process_fax_command(m,p,plen) (-1)
#endif /* MODEM_CONFIG_FAX */
/* ------------------------------------------------------------ */
/*
* Main AT processor
*
*/
int process_at_command(struct modem *m, char *cmd)
{
char c, *p = cmd;
int len = 0, ret = -1;
/* verify 'AT' */
if (toupper(*p++) != 'A' || toupper(*p++) != 'T')
return -1;
while(*p) {
c = toupper(*p++);
len = 0;
switch(c) {
case 'A': /* ATA */
ret = process_A(m,p,&len);
return ret;
case 'B': /* ATB */
ret = process_B(m,p,&len);
break;
case 'D': /* ATD */
ret = process_D(m,p,&len);
return ret;
case 'E': /* ATE */
ret = set_sreg(m,SREG_ECHO,*p,0,1,&len);
break;
case 'H': /* ATH */
ret = process_H(m,p,&len);
break;
case 'I': /* ATI */
ret = process_I(m,p,&len);
break;
case 'L': /* ATL */
ret = set_sreg(m,SREG_SPEAKER_VOLUME,*p,0,3,&len);
if(!ret) modem_update_speaker(m);
break;
case 'M': /* ATM */
ret = set_sreg(m,SREG_SPEAKER_CONTROL,*p,0,2,&len);
if(!ret) modem_update_speaker(m);
break;
case 'N': /* ATN */
ret = set_sreg(m,SREG_AUTOMODE,*p,0,1,&len);
break;
case 'O': /* ATO */
ret = process_O(m,p,&len);
break;
case 'P': /* ATP */
#ifdef NO_PULSE_DIAL
return -1;
#endif
ret = modem_set_sreg(m,SREG_TONE_OR_PULSE,0);
break;
case 'Q': /* ATQ */
ret = set_sreg(m,SREG_QUIET,*p,0,1,&len);
break;
case 'S': /* ATS */
ret = process_S(m,p,&len);
break;
case 'T': /* ATT */
ret = modem_set_sreg(m,SREG_TONE_OR_PULSE,1);
break;
case 'V': /* ATV */
ret = set_sreg(m,SREG_VERBOSE,*p,0,1,&len);
break;
case 'X': /* ATX */
ret = set_sreg(m,SREG_X_CODE,*p,0,4,&len);
break;
case 'Y': /* ATY */
ret = set_sreg(m,SREG_DEFAULT_SETTING,*p,0,3,&len);
break;
case 'Z': /* ATZ */
ret = process_Z(m,p,&len);
return ret;
case '#': /* AT# */
#ifdef MODEM_CONFIG_CID
if(!toupper_strncmp(p,"CID",3)) {
p+= 3;
ret = process_CID(m, p, &len);
}
else
#endif
ret = -1;
break;
case '%': /* AT% */
c = toupper(*p++);
switch (c) {
case 'C': /* AT%C */
ret = set_sreg(m,SREG_COMP,*p,0,3,&len);
break;
case 'E': /* AT%E */
ret = set_sreg(m,SREG_LINE_QUALITY_CONTROL,*p,0,2,&len);
break;
default:
return -1;
} /* end AT% */
break;
case '&':
c = toupper(*p++);
switch (c) {
case 'A': /* AT&A */
ret = set_sreg(m,SREG_CONNNECT_MSG_FORMAT,*p,0,7,&len);
break;
case 'C': /* AT&C */
ret = set_sreg(m,SREG_CD,*p,0,1,&len);
break;
case 'D': /* AT&D */
case 'R': /* AT&R */
case 'S': /* AT&S */
len = isdigit(*p)?1:0; ret = 0;
break;
case 'E': /* AT&E */
ret = set_sreg(m,SREG_CONNNECT_MSG_SPEED_SRC,*p,0,1,&len);
break;
case 'F': /* AT&F */
len = isdigit(*p)?1:0; ret = 0;
break;
case 'H': /* AT&H */
ret = set_sreg(m,SREG_FLOW_CONTROL,*p,0,1,&len);
break;
case 'K': /* AT&K */
ret = set_sreg(m,SREG_COMP,*p,0,3,&len);
break;
case 'P': /* AT&P */
ret = set_sreg(m,SREG_PULSE_RATIO,*p,0,3,&len);
break;
case 'V': /* AT&V */
case 'W': /* AT&W */
ret = -1;
break;
#if 0
case 'Z': /* AT&Z */
c = toupper(*p++);
if (c == 'L'){
ret = print_last_dial_string (m);
break; /* ZL */
}
break; /* Z */
#endif
default:
return -1;
}
break;
case '*': /* AT* */
c = toupper(*p++);
switch(c) {
case 'B': /* AT*B */
break;
default:
return -1;
}
break;
case '+':
c = toupper(*p++);
switch (c) {
#if 0
case 'D': /* AT+D */
c = toupper(*p++);
switch (c) {
case 'C': /* AT+DC */
break;
case 'S': /* AT+DS */
break;
case 'R': /* AT+DR */
break;
default:
return -1;
}
break;
#endif
case 'G': /* AT+G - generic DCE cntrl */
c = toupper(*p++);
switch (c) {
case 'C': /* AT+GCI[=|?|=?] */
if (toupper(*p++) == 'I')
ret = process_plus_GCI(m,p,&len);
else
return -1;
break;
case 'M': /* AT+GM[I|M|R] */
c = toupper(*p++);
switch (c) {
case 'I': /* AT+GMI */
modem_send_to_tty(m,modem_author,strlen(modem_author));
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
case 'M': /* AT+GMM */
modem_send_to_tty(m,modem_name,strlen(modem_name));
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
case 'R': /* AT+GMR */
modem_send_to_tty(m,modem_version,strlen(modem_version));
modem_send_to_tty(m,CRLF_CHARS(m),2);
break;
default:
return -1;
}
ret = 0;
break;
default:
return -1;
}
break;
case 'F': /* AT+F */
if(!toupper_strncmp(p,"CLASS",5)) {
p+= 5;
ret = process_plus_FCLASS(m, p, &len);
}
else
ret = process_fax_command(m, p, &len);
break;
#ifdef MODEM_CONFIG_VOICE
/* AT+IFC=2,2 */ /* TBD: flow control - not voice command */
case 'I':
// FIXME
if(!toupper_strncmp(p,"FC=2,2",6)) {
p+= 6;
ret = 0;
}
else
ret = -1;
break;
#endif
case 'M': /* AT+M */
c = toupper (*p++);
if (c == 'S')
ret = process_plus_MS(m,p,&len);
break;
case 'V':
if(m->mode != MODEM_MODE_VOICE)
return -1;
#ifdef MODEM_CONFIG_CID
if(!toupper_strncmp(p,"CID",3)) {
p+= 3;
ret = process_CID(m, p, &len);
}
else
#endif
ret = process_voice_command(m, p, &len);
break;
default:
return -1;
}
break;
case '\\':
c = toupper(*p++);
switch (c) {
case 'A': /* AT\A */
if(isdigit(*p)) len++;
// ret = set_sreg(m,SREG_V42_BASEREG,*p,0,5,&len);
break;
#if 0
case 'B': /* AT\B */
break;
case 'K': /* AT\K */
break;
#endif
case 'N': /* AT\N */
ret = set_sreg(m,SREG_EC,*p,0,5,&len);
break;
default:
return -1;
}
break;
default:
AT_DBG("unknown command `%c` (%x).\n",c,c);
return -1;
}
if (ret) return ret;
p += len;
}
return 0;
}