177 lines
4.3 KiB
C
177 lines
4.3 KiB
C
/*
|
|
* Simple AT command parser
|
|
*
|
|
* Copyright (c) 2000 Fabrice Bellard.
|
|
*
|
|
* This code is released under the GNU General Public License version
|
|
* 2. Please read the file COPYING to know the exact terms of the
|
|
* license.
|
|
*
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "lm.h"
|
|
|
|
#define DEBUG
|
|
|
|
static void parse_at_line(struct lm_at_state *s, const char *line);
|
|
static void at_putc(struct lm_at_state *s, int c);
|
|
static void at_printf(struct lm_at_state *s, char *fmt, ...);
|
|
|
|
/*
|
|
* This AT command parser is not intended to be complete nor accurate
|
|
* but to provide the minimum functionality so that the programs can
|
|
* make a data/voice/fax connection.
|
|
*/
|
|
|
|
void lm_at_parser_init(struct lm_at_state *s, struct sm_state *sm)
|
|
{
|
|
s->sm = sm;
|
|
s->at_line_ptr = 0;
|
|
s->at_state = AT_MODE_COMMAND;
|
|
}
|
|
|
|
void lm_at_parser(struct lm_at_state *s)
|
|
{
|
|
int state, c;
|
|
|
|
switch(s->at_state) {
|
|
case AT_MODE_COMMAND:
|
|
for(;;) {
|
|
/* handle incoming character */
|
|
c = sm_get_bit(&s->sm->tx_fifo);
|
|
if (c == -1)
|
|
break;
|
|
|
|
if (c == '\n' || c == '\r') {
|
|
/* line validation */
|
|
s->at_line[s->at_line_ptr] = '\0';
|
|
if (s->at_line_ptr != 0) {
|
|
at_putc(s, '\n');
|
|
parse_at_line(s, s->at_line);
|
|
}
|
|
s->at_line_ptr = 0;
|
|
} else if (c == '\b') {
|
|
/* backspace */
|
|
if (s->at_line_ptr > 0) {
|
|
s->at_line_ptr--;
|
|
at_printf(s, "\b \b");
|
|
}
|
|
} else {
|
|
/* add new char */
|
|
if (s->at_line_ptr < (sizeof(s->at_line) - 1)) {
|
|
s->at_line[s->at_line_ptr++] = c;
|
|
at_putc(s, c);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case AT_MODE_DIALING:
|
|
state = lm_get_state(s->sm);
|
|
if (state == LM_STATE_IDLE) {
|
|
at_printf(s, "ERROR\n");
|
|
s->at_state = AT_MODE_COMMAND;
|
|
} else if (state == LM_STATE_CONNECTED) {
|
|
at_printf(s, "CONNECT\n");
|
|
s->at_state = AT_MODE_CONNECTED;
|
|
}
|
|
break;
|
|
|
|
case AT_MODE_CONNECTED:
|
|
if (lm_get_state(s->sm) == LM_STATE_IDLE) {
|
|
at_printf(s, "OK\n");
|
|
s->at_state = AT_MODE_COMMAND;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* return TRUE if val is a prefix of str. If it returns TRUE, ptr is
|
|
set to the next character in 'str' after the prefix */
|
|
#if 0
|
|
static int strstart(const char *str, const char *val, const char **ptr)
|
|
{
|
|
const char *p, *q;
|
|
p = str;
|
|
q = val;
|
|
while (*q != '\0') {
|
|
if (*p != *q)
|
|
return 0;
|
|
p++;
|
|
q++;
|
|
}
|
|
*ptr = p;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static int strcasestart(const char *str, const char *val, const char **ptr)
|
|
{
|
|
const char *p, *q;
|
|
p = str;
|
|
q = val;
|
|
while (*q != '\0') {
|
|
if (toupper(*p) != toupper(*q))
|
|
return 0;
|
|
p++;
|
|
q++;
|
|
}
|
|
*ptr = p;
|
|
return 1;
|
|
}
|
|
|
|
static void at_putc(struct lm_at_state *s, int c)
|
|
{
|
|
if (c == '\n')
|
|
sm_put_bit(&s->sm->rx_fifo, '\r');
|
|
sm_put_bit(&s->sm->rx_fifo, c);
|
|
}
|
|
|
|
static void at_printf(struct lm_at_state *s, char *fmt, ...)
|
|
{
|
|
char buf[256], *p;
|
|
int c;
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
snprintf(buf, sizeof(buf), fmt, ap);
|
|
p = buf;
|
|
while (*p) {
|
|
c = *p++;
|
|
at_putc(s, c);
|
|
}
|
|
va_end(ap);
|
|
}
|
|
|
|
static void parse_at_line(struct lm_at_state *s, const char *line)
|
|
{
|
|
const char *p;
|
|
struct sm_state *sm = s->sm;
|
|
|
|
if (strcasestart(line, "ATD", &p)) {
|
|
int pulse;
|
|
|
|
pulse = sm->lm_config->pulse_dial;
|
|
if (strcasestart(p, "P", &p))
|
|
pulse = 1;
|
|
if (strcasestart(p, "T", &p))
|
|
pulse = 0;
|
|
lm_start_dial(sm, pulse, p);
|
|
s->at_state = AT_MODE_DIALING;
|
|
} else if (strcasestart(line, "ATI", &p)) {
|
|
at_printf(s, "Linmodem " LM_VERSION "\n");
|
|
} else if (strcasestart(line, "ATZ", &p)) {
|
|
at_printf(s, "OK\n");
|
|
} else if (strcasestart(line, "ATA", &p)) {
|
|
lm_start_receive(sm);
|
|
s->at_state = AT_MODE_DIALING;
|
|
} else if (strcasestart(line, "ATH", &p)) {
|
|
lm_hangup(sm);
|
|
}
|
|
}
|