2010-01-18 14:31:18 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* AT server library with GLib integration
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008-2010 Intel Corporation. All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#include "ringbuffer.h"
|
|
|
|
#include "gatserver.h"
|
|
|
|
|
2010-02-26 09:56:30 +00:00
|
|
|
#define BUF_SIZE 4096
|
2010-03-30 17:34:47 +00:00
|
|
|
/* <cr><lf> + the max length of information text + <cr><lf> */
|
|
|
|
#define MAX_TEXT_SIZE 2052
|
2010-02-26 09:56:32 +00:00
|
|
|
/* #define WRITE_SCHEDULER_DEBUG 1 */
|
2010-02-26 09:56:30 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
enum ParserState {
|
|
|
|
PARSER_STATE_IDLE,
|
|
|
|
PARSER_STATE_A,
|
|
|
|
PARSER_STATE_COMMAND,
|
|
|
|
PARSER_STATE_GARBAGE,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum ParserResult {
|
|
|
|
PARSER_RESULT_COMMAND,
|
|
|
|
PARSER_RESULT_EMPTY_COMMAND,
|
|
|
|
PARSER_RESULT_REPEAT_LAST,
|
|
|
|
PARSER_RESULT_GARBAGE,
|
|
|
|
PARSER_RESULT_UNSURE,
|
|
|
|
};
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
/* V.250 Table 1/V.250 Result codes */
|
2010-01-29 20:15:37 +00:00
|
|
|
static const char *server_result_to_string(GAtServerResult result)
|
|
|
|
{
|
|
|
|
switch (result) {
|
|
|
|
case G_AT_SERVER_RESULT_OK:
|
|
|
|
return "OK";
|
|
|
|
case G_AT_SERVER_RESULT_CONNECT:
|
|
|
|
return "CONNECT";
|
|
|
|
case G_AT_SERVER_RESULT_RING:
|
|
|
|
return "RING";
|
|
|
|
case G_AT_SERVER_RESULT_NO_CARRIER:
|
|
|
|
return "NO CARRIER";
|
|
|
|
case G_AT_SERVER_RESULT_ERROR:
|
|
|
|
return "ERROR";
|
|
|
|
case G_AT_SERVER_RESULT_NO_DIALTONE:
|
|
|
|
return "NO DIALTONE";
|
|
|
|
case G_AT_SERVER_RESULT_BUSY:
|
|
|
|
return "BUSY";
|
|
|
|
case G_AT_SERVER_RESULT_NO_ANSWER:
|
|
|
|
return "NO ANSWER";
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
/* Basic command setting for V.250 */
|
|
|
|
struct v250_settings {
|
|
|
|
char s3; /* set by S3=<val> */
|
|
|
|
char s4; /* set by S4=<val> */
|
|
|
|
char s5; /* set by S5=<val> */
|
|
|
|
gboolean echo; /* set by E<val> */
|
|
|
|
gboolean quiet; /* set by Q<val> */
|
|
|
|
gboolean is_v1; /* set by V<val>, v0 or v1 */
|
2010-03-31 13:38:01 +00:00
|
|
|
int res_format; /* set by X<val> */
|
|
|
|
int c109; /* set by &C<val> */
|
|
|
|
int c108; /* set by &D<val> */
|
2010-01-18 14:31:18 +00:00
|
|
|
};
|
|
|
|
|
2010-03-19 09:44:42 +00:00
|
|
|
/* AT command set that server supported */
|
|
|
|
struct at_command {
|
|
|
|
GAtServerNotifyFunc notify;
|
|
|
|
gpointer user_data;
|
|
|
|
GDestroyNotify destroy_notify;
|
|
|
|
};
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
struct _GAtServer {
|
|
|
|
gint ref_count; /* Ref count */
|
|
|
|
struct v250_settings v250; /* V.250 command setting */
|
2010-02-09 14:59:29 +00:00
|
|
|
GIOChannel *channel; /* Server IO */
|
2010-02-26 09:56:29 +00:00
|
|
|
guint read_watch; /* GSource read id, 0 if none */
|
2010-02-26 09:56:31 +00:00
|
|
|
guint write_watch; /* GSource write id, 0 if none */
|
2010-01-18 14:31:18 +00:00
|
|
|
guint read_so_far; /* Number of bytes processed */
|
|
|
|
GAtDisconnectFunc user_disconnect; /* User disconnect func */
|
|
|
|
gpointer user_disconnect_data; /* User disconnect data */
|
|
|
|
GAtDebugFunc debugf; /* Debugging output function */
|
|
|
|
gpointer debug_data; /* Data to pass to debug func */
|
2010-03-19 09:44:42 +00:00
|
|
|
GHashTable *command_list; /* List of AT commands */
|
2010-02-09 14:59:27 +00:00
|
|
|
struct ring_buffer *read_buf; /* Current read buffer */
|
2010-02-26 09:56:31 +00:00
|
|
|
GQueue *write_queue; /* Write buffer queue */
|
2010-01-29 21:18:16 +00:00
|
|
|
guint max_read_attempts; /* Max reads per select */
|
2010-02-01 21:00:45 +00:00
|
|
|
enum ParserState parser_state;
|
2010-02-26 09:56:29 +00:00
|
|
|
gboolean destroyed; /* Re-entrancy guard */
|
2010-03-30 20:24:48 +00:00
|
|
|
char *last_line; /* Last read line */
|
2010-03-31 00:15:55 +00:00
|
|
|
unsigned int cur_pos; /* Where we are on the line */
|
|
|
|
GAtServerResult last_result;
|
|
|
|
gboolean processing_cmdline;
|
|
|
|
gboolean final_sent;
|
|
|
|
gboolean final_async;
|
2010-01-18 14:31:18 +00:00
|
|
|
};
|
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
static void g_at_server_wakeup_writer(GAtServer *server);
|
2010-03-31 00:15:55 +00:00
|
|
|
static void server_parse_line(GAtServer *server);
|
2010-02-26 09:56:31 +00:00
|
|
|
|
|
|
|
static struct ring_buffer *allocate_next(GAtServer *server)
|
|
|
|
{
|
|
|
|
struct ring_buffer *buf = ring_buffer_new(BUF_SIZE);
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
g_queue_push_tail(server->write_queue, buf);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_common(GAtServer *server, const char *buf, unsigned int len)
|
|
|
|
{
|
|
|
|
gsize towrite = len;
|
|
|
|
gsize bytes_written = 0;
|
|
|
|
struct ring_buffer *write_buf;
|
|
|
|
|
|
|
|
write_buf = g_queue_peek_tail(server->write_queue);
|
|
|
|
|
|
|
|
while (bytes_written < towrite) {
|
|
|
|
gsize wbytes = MIN((gsize)ring_buffer_avail(write_buf),
|
|
|
|
towrite - bytes_written);
|
|
|
|
|
|
|
|
bytes_written += ring_buffer_write(write_buf,
|
|
|
|
buf + bytes_written,
|
|
|
|
wbytes);
|
|
|
|
|
2010-02-26 22:16:46 +00:00
|
|
|
/*
|
|
|
|
* Make sure we don't allocate a buffer if we've written
|
|
|
|
* everything out already
|
|
|
|
*/
|
|
|
|
if (ring_buffer_avail(write_buf) == 0 &&
|
|
|
|
bytes_written < towrite)
|
2010-02-26 09:56:31 +00:00
|
|
|
write_buf = allocate_next(server);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_at_server_wakeup_writer(server);
|
|
|
|
}
|
|
|
|
|
2010-03-30 17:34:47 +00:00
|
|
|
static void send_result_common(GAtServer *server, const char *result)
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
|
|
|
struct v250_settings v250 = server->v250;
|
2010-03-31 01:29:10 +00:00
|
|
|
char buf[MAX_TEXT_SIZE + 1];
|
2010-01-18 14:31:18 +00:00
|
|
|
char t = v250.s3;
|
|
|
|
char r = v250.s4;
|
2010-02-26 09:56:31 +00:00
|
|
|
unsigned int len;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
if (v250.quiet)
|
|
|
|
return;
|
|
|
|
|
2010-03-30 17:34:47 +00:00
|
|
|
if (result == NULL)
|
2010-01-29 20:15:37 +00:00
|
|
|
return;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-03-31 01:29:10 +00:00
|
|
|
if (strlen(result) > 2048)
|
|
|
|
return;
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
if (v250.is_v1)
|
2010-03-31 01:29:10 +00:00
|
|
|
len = sprintf(buf, "%c%c%s%c%c", t, r, result, t, r);
|
2010-01-18 14:31:18 +00:00
|
|
|
else
|
2010-03-31 01:29:10 +00:00
|
|
|
len = sprintf(buf, "%s%c", result, t);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-03-31 01:29:10 +00:00
|
|
|
send_common(server, buf, len);
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-03-30 17:34:47 +00:00
|
|
|
void g_at_server_send_final(GAtServer *server, GAtServerResult result)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
|
2010-03-31 00:15:55 +00:00
|
|
|
server->final_sent = TRUE;
|
|
|
|
server->last_result = result;
|
|
|
|
|
|
|
|
if (result == G_AT_SERVER_RESULT_OK && server->processing_cmdline) {
|
|
|
|
if (server->final_async)
|
|
|
|
server_parse_line(server);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
server->processing_cmdline = FALSE;
|
|
|
|
|
2010-03-30 17:34:47 +00:00
|
|
|
if (server->v250.is_v1)
|
|
|
|
sprintf(buf, "%s", server_result_to_string(result));
|
|
|
|
else
|
|
|
|
sprintf(buf, "%u", (unsigned int)result);
|
|
|
|
|
|
|
|
send_result_common(server, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void g_at_server_send_ext_final(GAtServer *server, const char *result)
|
|
|
|
{
|
2010-03-31 00:15:55 +00:00
|
|
|
server->final_sent = TRUE;
|
|
|
|
server->last_result = G_AT_SERVER_RESULT_EXT_ERROR;
|
|
|
|
server->processing_cmdline = FALSE;
|
|
|
|
|
2010-03-30 17:34:47 +00:00
|
|
|
send_result_common(server, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
void g_at_server_send_intermediate(GAtServer *server, const char *result)
|
|
|
|
{
|
|
|
|
send_result_common(server, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
void g_at_server_send_unsolicited(GAtServer *server, const char *result)
|
|
|
|
{
|
|
|
|
send_result_common(server, result);
|
|
|
|
}
|
|
|
|
|
2010-03-31 01:29:10 +00:00
|
|
|
void g_at_server_send_info(GAtServer *server, const char *line, gboolean last)
|
2010-03-30 19:35:29 +00:00
|
|
|
{
|
2010-03-31 01:29:10 +00:00
|
|
|
char buf[MAX_TEXT_SIZE + 1];
|
2010-03-30 17:34:47 +00:00
|
|
|
char t = server->v250.s3;
|
|
|
|
char r = server->v250.s4;
|
|
|
|
unsigned int len;
|
|
|
|
|
2010-03-31 01:29:10 +00:00
|
|
|
if (strlen(line) > 2048)
|
2010-03-30 17:34:47 +00:00
|
|
|
return;
|
|
|
|
|
2010-03-31 01:29:10 +00:00
|
|
|
if (last)
|
|
|
|
len = sprintf(buf, "%c%c%s%c%c", t, r, line, t, r);
|
|
|
|
else
|
|
|
|
len = sprintf(buf, "%c%c%s", t, r, line);
|
2010-03-30 17:34:47 +00:00
|
|
|
|
|
|
|
send_common(server, buf, len);
|
|
|
|
}
|
|
|
|
|
2010-03-31 09:50:35 +00:00
|
|
|
static gboolean get_result_value(GAtServer *server, GAtResult *result,
|
|
|
|
const char *command,
|
|
|
|
int min, int max, int *value)
|
|
|
|
{
|
|
|
|
GAtResultIter iter;
|
|
|
|
int val;
|
|
|
|
char prefix[10];
|
|
|
|
|
2010-03-31 09:50:36 +00:00
|
|
|
if (command[0] == 'S')
|
|
|
|
sprintf(prefix, "%s=", command);
|
|
|
|
else
|
|
|
|
strcpy(prefix, command);
|
2010-03-31 09:50:35 +00:00
|
|
|
|
|
|
|
g_at_result_iter_init(&iter, result);
|
|
|
|
|
|
|
|
if (!g_at_result_iter_next(&iter, prefix))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!g_at_result_iter_next_number(&iter, &val))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (val < min || val > max)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
*value = val;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s_template_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:22:40 +00:00
|
|
|
GAtServer *server, char *sreg,
|
|
|
|
const char *prefix, int min, int max)
|
2010-03-31 09:50:35 +00:00
|
|
|
{
|
|
|
|
char buf[20];
|
2010-03-31 13:22:40 +00:00
|
|
|
int tmp;
|
2010-03-31 09:50:35 +00:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_SET:
|
2010-03-31 13:22:40 +00:00
|
|
|
if (!get_result_value(server, result, prefix, min, max, &tmp)) {
|
2010-03-31 09:50:35 +00:00
|
|
|
g_at_server_send_final(server,
|
|
|
|
G_AT_SERVER_RESULT_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-31 13:22:40 +00:00
|
|
|
*sreg = tmp;
|
2010-03-31 09:50:35 +00:00
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_QUERY:
|
2010-03-31 13:22:40 +00:00
|
|
|
tmp = *sreg;
|
|
|
|
sprintf(buf, "%03d", tmp);
|
2010-03-31 09:50:35 +00:00
|
|
|
g_at_server_send_info(server, buf, TRUE);
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
|
|
|
|
sprintf(buf, "%s: (%d-%d)", prefix, min, max);
|
|
|
|
g_at_server_send_info(server, buf, TRUE);
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void at_s3_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:22:40 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:35 +00:00
|
|
|
{
|
2010-03-31 13:22:40 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
s_template_cb(type, result, server, &server->v250.s3, "S3", 0, 127);
|
2010-03-31 09:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_s4_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:22:40 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:35 +00:00
|
|
|
{
|
2010-03-31 13:22:40 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
s_template_cb(type, result, server, &server->v250.s4, "S4", 0, 127);
|
2010-03-31 09:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_s5_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:22:40 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:35 +00:00
|
|
|
{
|
2010-03-31 13:22:40 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
s_template_cb(type, result, server, &server->v250.s5, "S5", 0, 127);
|
2010-03-31 09:50:35 +00:00
|
|
|
}
|
|
|
|
|
2010-03-31 09:50:36 +00:00
|
|
|
static void at_template_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server, int *value,
|
|
|
|
const char *prefix,
|
2010-03-31 09:50:36 +00:00
|
|
|
int min, int max, int deftval)
|
|
|
|
{
|
|
|
|
char buf[20];
|
2010-03-31 13:38:01 +00:00
|
|
|
int tmp;
|
2010-03-31 09:50:36 +00:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_SET:
|
2010-03-31 13:38:01 +00:00
|
|
|
if (!get_result_value(server, result, prefix, min, max, &tmp)) {
|
2010-03-31 09:50:36 +00:00
|
|
|
g_at_server_send_final(server,
|
|
|
|
G_AT_SERVER_RESULT_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-31 13:38:01 +00:00
|
|
|
*value = tmp;
|
2010-03-31 09:50:36 +00:00
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_QUERY:
|
2010-03-31 13:38:01 +00:00
|
|
|
tmp = *value;
|
|
|
|
sprintf(buf, "%s: %d", prefix, tmp);
|
2010-03-31 09:50:36 +00:00
|
|
|
g_at_server_send_info(server, buf, TRUE);
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
|
|
|
|
sprintf(buf, "%s: (%d-%d)", prefix, min, max);
|
|
|
|
g_at_server_send_info(server, buf, TRUE);
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
|
2010-03-31 13:38:01 +00:00
|
|
|
*value = deftval;
|
2010-03-31 09:50:36 +00:00
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void at_e_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:36 +00:00
|
|
|
{
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
at_template_cb(type, result, server, &server->v250.echo, "E", 0, 1, 1);
|
2010-03-31 09:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_q_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:36 +00:00
|
|
|
{
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
at_template_cb(type, result, server, &server->v250.quiet, "Q", 0, 1, 0);
|
2010-03-31 09:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_v_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:36 +00:00
|
|
|
{
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
at_template_cb(type, result, server, &server->v250.is_v1, "V", 0, 1, 1);
|
2010-03-31 09:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_x_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:36 +00:00
|
|
|
{
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
at_template_cb(type, result, server, &server->v250.res_format,
|
|
|
|
"X", 0, 4, 4);
|
2010-03-31 09:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_c109_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:36 +00:00
|
|
|
{
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
at_template_cb(type, result, server, &server->v250.c109, "&C", 0, 1, 1);
|
2010-03-31 09:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void at_c108_cb(GAtServerRequestType type, GAtResult *result,
|
2010-03-31 13:38:01 +00:00
|
|
|
gpointer user_data)
|
2010-03-31 09:50:36 +00:00
|
|
|
{
|
2010-03-31 13:38:01 +00:00
|
|
|
GAtServer *server = user_data;
|
|
|
|
at_template_cb(type, result, server, &server->v250.c108, "&D", 0, 2, 2);
|
2010-03-31 09:50:36 +00:00
|
|
|
}
|
|
|
|
|
2010-03-18 15:14:25 +00:00
|
|
|
static inline gboolean is_extended_command_prefix(const char c)
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
2010-02-01 21:00:45 +00:00
|
|
|
switch (c) {
|
|
|
|
case '+':
|
|
|
|
case '*':
|
|
|
|
case '!':
|
|
|
|
case '%':
|
|
|
|
return TRUE;
|
|
|
|
default:
|
2010-01-18 14:31:18 +00:00
|
|
|
return FALSE;
|
2010-02-01 21:00:45 +00:00
|
|
|
}
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
static void at_command_notify(GAtServer *server, char *command,
|
|
|
|
char *prefix, GAtServerRequestType type)
|
2010-03-19 09:44:41 +00:00
|
|
|
{
|
2010-03-19 09:44:43 +00:00
|
|
|
struct at_command *node;
|
2010-03-22 17:56:57 +00:00
|
|
|
GAtResult result;
|
2010-03-19 09:44:43 +00:00
|
|
|
|
|
|
|
node = g_hash_table_lookup(server->command_list, prefix);
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
if (node == NULL) {
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
|
|
|
|
return;
|
2010-03-19 09:44:43 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
result.lines = g_slist_prepend(NULL, command);
|
|
|
|
result.final_or_pdu = 0;
|
2010-03-19 09:44:43 +00:00
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
node->notify(type, &result, node->user_data);
|
2010-03-19 09:44:43 +00:00
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
g_slist_free(result.lines);
|
2010-03-19 09:44:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
static unsigned int parse_extended_command(GAtServer *server, char *buf)
|
2010-03-19 09:44:41 +00:00
|
|
|
{
|
2010-03-22 17:56:57 +00:00
|
|
|
const char *valid_extended_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
"0123456789!%-./:_";
|
|
|
|
const char *separators = ";?=";
|
|
|
|
unsigned int prefix_len, i;
|
|
|
|
gboolean in_string = FALSE;
|
|
|
|
gboolean seen_equals = FALSE;
|
|
|
|
char prefix[18]; /* According to V250, 5.4.1 */
|
|
|
|
GAtServerRequestType type;
|
2010-03-30 22:12:43 +00:00
|
|
|
char tmp;
|
2010-03-22 17:56:57 +00:00
|
|
|
|
|
|
|
prefix_len = strcspn(buf, separators);
|
|
|
|
|
|
|
|
if (prefix_len > 17 || prefix_len < 2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Convert to upper case, we will always use upper case naming */
|
|
|
|
for (i = 0; i < prefix_len; i++)
|
|
|
|
prefix[i] = g_ascii_toupper(buf[i]);
|
|
|
|
|
|
|
|
prefix[prefix_len] = '\0';
|
|
|
|
|
|
|
|
if (strspn(prefix + 1, valid_extended_chars) != (prefix_len - 1))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* V.250 Section 5.4.1: "The first character following "+" shall be
|
|
|
|
* an alphabetic character in the range "A" through "Z".
|
|
|
|
*/
|
|
|
|
if (prefix[1] <= 'A' || prefix[1] >= 'Z')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (buf[i] != '\0' && buf[i] != ';' && buf[i] != '?' && buf[i] != '=')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY;
|
|
|
|
|
|
|
|
/* Continue until we hit eol or ';' */
|
|
|
|
while (buf[i] && !(buf[i] == ';' && in_string == FALSE)) {
|
|
|
|
if (buf[i] == '"') {
|
|
|
|
in_string = !in_string;
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_string == TRUE)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
if (buf[i] == '?') {
|
2010-03-30 19:36:02 +00:00
|
|
|
if (seen_equals && buf[i-1] != '=')
|
2010-03-22 17:56:57 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (buf[i + 1] != '\0' && buf[i + 1] != ';')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_QUERY;
|
2010-03-30 19:36:02 +00:00
|
|
|
|
|
|
|
if (seen_equals)
|
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_SUPPORT;
|
2010-03-22 17:56:57 +00:00
|
|
|
} else if (buf[i] == '=') {
|
2010-03-30 19:36:02 +00:00
|
|
|
if (seen_equals)
|
2010-03-22 17:56:57 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
seen_equals = TRUE;
|
2010-03-30 19:36:02 +00:00
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_SET;
|
2010-03-22 17:56:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
next:
|
|
|
|
i++;
|
2010-03-19 09:44:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
/* We can scratch in this buffer, so mark ';' as null */
|
2010-03-30 22:12:43 +00:00
|
|
|
tmp = buf[i];
|
2010-03-22 17:56:57 +00:00
|
|
|
buf[i] = '\0';
|
|
|
|
at_command_notify(server, buf, prefix, type);
|
2010-03-30 22:12:43 +00:00
|
|
|
buf[i] = tmp;
|
2010-03-19 09:44:41 +00:00
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
/* Also consume the terminating null */
|
|
|
|
return i + 1;
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
static int get_basic_prefix_size(const char *buf)
|
2010-03-24 01:47:30 +00:00
|
|
|
{
|
2010-03-25 00:37:18 +00:00
|
|
|
if (g_ascii_isalpha(buf[0])) {
|
|
|
|
if (g_ascii_toupper(buf[0]) == 'S') {
|
|
|
|
int size;
|
2010-03-24 01:47:30 +00:00
|
|
|
|
|
|
|
/* V.250 5.3.2 'S' command follows with a parameter
|
2010-03-25 00:37:18 +00:00
|
|
|
* number.
|
2010-03-24 01:47:30 +00:00
|
|
|
*/
|
2010-03-25 00:37:18 +00:00
|
|
|
for (size = 1; g_ascii_isdigit(buf[size]); size++)
|
|
|
|
;
|
2010-03-24 21:28:19 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do some basic sanity checking, don't accept 00, 01,
|
|
|
|
* etc or empty S values
|
|
|
|
*/
|
2010-03-25 00:37:18 +00:00
|
|
|
if (size == 1)
|
|
|
|
return 0;
|
2010-03-24 21:28:19 +00:00
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
if (size > 2 && buf[1] == '0')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
2010-03-24 21:28:19 +00:00
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
/* All other cases it is a simple 1 character prefix */
|
|
|
|
return 1;
|
2010-03-24 01:47:30 +00:00
|
|
|
}
|
2010-03-25 00:37:18 +00:00
|
|
|
|
|
|
|
if (buf[0] == '&') {
|
|
|
|
if (g_ascii_isalpha(buf[0] == FALSE))
|
|
|
|
return 0;
|
2010-03-24 01:47:30 +00:00
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
return 2;
|
|
|
|
}
|
2010-03-24 21:28:19 +00:00
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
return 0;
|
2010-03-24 01:47:30 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
static unsigned int parse_basic_command(GAtServer *server, char *buf)
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
2010-03-24 01:47:30 +00:00
|
|
|
gboolean seen_equals = FALSE;
|
2010-03-25 00:37:18 +00:00
|
|
|
char prefix[4], tmp;
|
|
|
|
unsigned int i, prefix_size;
|
|
|
|
GAtServerRequestType type;
|
2010-03-24 01:47:30 +00:00
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
prefix_size = get_basic_prefix_size(buf);
|
|
|
|
if (prefix_size == 0)
|
2010-03-24 01:47:30 +00:00
|
|
|
return 0;
|
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
i = prefix_size;
|
|
|
|
prefix[0] = g_ascii_toupper(buf[0]);
|
2010-03-24 01:47:30 +00:00
|
|
|
|
2010-03-24 21:28:42 +00:00
|
|
|
if (prefix[0] == 'D') {
|
2010-03-24 01:47:30 +00:00
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_SET;
|
|
|
|
|
|
|
|
/* All characters appearing on the same line, up to a
|
|
|
|
* semicolon character (IA5 3/11) or the end of the
|
|
|
|
* command line is the part of the call.
|
|
|
|
*/
|
|
|
|
while (buf[i] != '\0' || buf[i] != ';')
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY;
|
2010-03-24 01:47:30 +00:00
|
|
|
|
|
|
|
/* Match '?', '=', '=?' and '=xxx' */
|
|
|
|
if (buf[i] == '=') {
|
|
|
|
seen_equals = TRUE;
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf[i] == '?') {
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
if (seen_equals)
|
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_SUPPORT;
|
|
|
|
else
|
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_QUERY;
|
|
|
|
} else {
|
2010-03-25 00:37:18 +00:00
|
|
|
int before = i;
|
|
|
|
|
2010-03-24 01:47:30 +00:00
|
|
|
/* V.250 5.3.1 The subparameter (if any) are all digits */
|
|
|
|
while (g_ascii_isdigit(buf[i]))
|
|
|
|
i++;
|
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
if (i - before > 0)
|
|
|
|
type = G_AT_SERVER_REQUEST_TYPE_SET;
|
2010-03-24 01:47:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2010-03-25 00:37:18 +00:00
|
|
|
if (prefix_size <= 3) {
|
|
|
|
memcpy(prefix + 1, buf + 1, prefix_size - 1);
|
|
|
|
prefix[prefix_size] = '\0';
|
|
|
|
|
|
|
|
tmp = buf[i];
|
|
|
|
buf[i] = '\0';
|
|
|
|
at_command_notify(server, buf, prefix, type);
|
|
|
|
buf[i] = tmp;
|
|
|
|
} else /* Handle S-parameter with 100+ */
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
|
2010-03-24 01:47:30 +00:00
|
|
|
|
|
|
|
/* Commands like ATA, ATZ cause the remainder line
|
|
|
|
* to be ignored.
|
|
|
|
*/
|
2010-03-24 21:28:42 +00:00
|
|
|
if (prefix[0] == 'A' || prefix[0] == 'Z')
|
2010-03-24 01:47:30 +00:00
|
|
|
return strlen(buf);
|
|
|
|
|
2010-03-25 00:37:18 +00:00
|
|
|
/* Consume the seperator ';' */
|
2010-03-24 01:47:30 +00:00
|
|
|
if (buf[i] == ';')
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
return i;
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-03-30 20:24:48 +00:00
|
|
|
static void server_parse_line(GAtServer *server)
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
2010-03-30 20:24:48 +00:00
|
|
|
char *line = server->last_line;
|
2010-03-31 00:15:55 +00:00
|
|
|
unsigned int pos = server->cur_pos;
|
2010-03-22 17:56:57 +00:00
|
|
|
unsigned int len = strlen(line);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-03-31 00:15:55 +00:00
|
|
|
server->final_async = FALSE;
|
|
|
|
|
|
|
|
if (pos == 0)
|
|
|
|
server->processing_cmdline = TRUE;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
while (pos < len) {
|
|
|
|
unsigned int consumed;
|
2010-03-19 09:44:39 +00:00
|
|
|
|
2010-03-31 00:15:55 +00:00
|
|
|
server->final_sent = FALSE;
|
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
if (is_extended_command_prefix(line[pos]))
|
|
|
|
consumed = parse_extended_command(server, line + pos);
|
|
|
|
else
|
2010-03-25 00:40:59 +00:00
|
|
|
consumed = parse_basic_command(server, line + pos);
|
2010-03-19 09:44:39 +00:00
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
if (consumed == 0) {
|
|
|
|
g_at_server_send_final(server,
|
|
|
|
G_AT_SERVER_RESULT_ERROR);
|
2010-03-31 00:15:55 +00:00
|
|
|
return;
|
2010-03-22 17:56:57 +00:00
|
|
|
}
|
2010-03-19 09:44:39 +00:00
|
|
|
|
2010-03-22 17:56:57 +00:00
|
|
|
pos += consumed;
|
2010-03-31 00:15:55 +00:00
|
|
|
server->cur_pos = pos;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We wait the callback until it finished processing
|
|
|
|
* the command and called the send_final.
|
|
|
|
*/
|
|
|
|
if (server->final_sent == FALSE) {
|
|
|
|
server->final_async = TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (server->last_result != G_AT_SERVER_RESULT_OK)
|
|
|
|
return;
|
2010-03-19 09:44:39 +00:00
|
|
|
}
|
2010-03-31 00:15:55 +00:00
|
|
|
|
|
|
|
server->processing_cmdline = FALSE;
|
|
|
|
g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
static enum ParserResult server_feed(GAtServer *server,
|
|
|
|
const char *bytes, gsize *len)
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
|
|
|
gsize i = 0;
|
2010-02-01 21:00:45 +00:00
|
|
|
enum ParserResult res = PARSER_RESULT_UNSURE;
|
|
|
|
char s3 = server->v250.s3;
|
|
|
|
|
|
|
|
while (i < *len) {
|
|
|
|
char byte = bytes[i];
|
|
|
|
|
|
|
|
switch (server->parser_state) {
|
|
|
|
case PARSER_STATE_IDLE:
|
|
|
|
if (byte == s3) {
|
|
|
|
i += 1;
|
|
|
|
res = PARSER_RESULT_EMPTY_COMMAND;
|
|
|
|
goto out;
|
|
|
|
} else if (byte == '\n') {
|
|
|
|
i += 1;
|
|
|
|
res = PARSER_RESULT_GARBAGE;
|
|
|
|
goto out;
|
|
|
|
} else if (byte == 'A' || byte == 'a')
|
|
|
|
server->parser_state = PARSER_STATE_A;
|
|
|
|
else if (byte != ' ' && byte != '\t')
|
|
|
|
server->parser_state = PARSER_STATE_GARBAGE;
|
|
|
|
break;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
case PARSER_STATE_A:
|
|
|
|
if (byte == s3) {
|
|
|
|
server->parser_state = PARSER_STATE_IDLE;
|
|
|
|
i += 1;
|
|
|
|
res = PARSER_RESULT_GARBAGE;
|
|
|
|
goto out;
|
2010-02-02 13:23:22 +00:00
|
|
|
} else if (byte == '/') {
|
2010-02-01 21:00:45 +00:00
|
|
|
server->parser_state = PARSER_STATE_IDLE;
|
2010-02-02 13:23:22 +00:00
|
|
|
i += 1;
|
2010-02-01 21:00:45 +00:00
|
|
|
res = PARSER_RESULT_REPEAT_LAST;
|
|
|
|
goto out;
|
2010-02-02 13:23:22 +00:00
|
|
|
} else if (byte == 'T' || byte == 't')
|
|
|
|
server->parser_state = PARSER_STATE_COMMAND;
|
|
|
|
else
|
2010-02-01 21:00:45 +00:00
|
|
|
server->parser_state = PARSER_STATE_GARBAGE;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
break;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
case PARSER_STATE_COMMAND:
|
|
|
|
if (byte == s3) {
|
|
|
|
server->parser_state = PARSER_STATE_IDLE;
|
|
|
|
i += 1;
|
|
|
|
res = PARSER_RESULT_COMMAND;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
case PARSER_STATE_GARBAGE:
|
|
|
|
if (byte == s3) {
|
|
|
|
server->parser_state = PARSER_STATE_IDLE;
|
|
|
|
i += 1;
|
|
|
|
res = PARSER_RESULT_GARBAGE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
};
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
*len = i;
|
|
|
|
return res;
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
static char *extract_line(GAtServer *p)
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
2010-02-09 14:59:27 +00:00
|
|
|
unsigned int wrap = ring_buffer_len_no_wrap(p->read_buf);
|
2010-01-18 14:31:18 +00:00
|
|
|
unsigned int pos = 0;
|
2010-02-09 14:59:27 +00:00
|
|
|
unsigned char *buf = ring_buffer_read_ptr(p->read_buf, pos);
|
2010-01-18 14:31:18 +00:00
|
|
|
int strip_front = 0;
|
|
|
|
int line_length = 0;
|
2010-02-01 23:58:48 +00:00
|
|
|
gboolean in_string = FALSE;
|
2010-03-22 17:58:04 +00:00
|
|
|
char s3 = p->v250.s3;
|
2010-02-01 21:00:45 +00:00
|
|
|
char *line;
|
2010-02-01 23:58:48 +00:00
|
|
|
int i;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
while (pos < p->read_so_far) {
|
2010-02-01 23:58:48 +00:00
|
|
|
if (*buf == '"')
|
|
|
|
in_string = !in_string;
|
|
|
|
|
|
|
|
if (in_string == FALSE && (*buf == ' ' || *buf == '\t')) {
|
|
|
|
if (line_length == 0)
|
2010-01-18 14:31:18 +00:00
|
|
|
strip_front += 1;
|
2010-02-01 23:58:48 +00:00
|
|
|
} else
|
2010-01-18 14:31:18 +00:00
|
|
|
line_length += 1;
|
|
|
|
|
|
|
|
buf += 1;
|
|
|
|
pos += 1;
|
|
|
|
|
|
|
|
if (pos == wrap)
|
2010-02-09 14:59:27 +00:00
|
|
|
buf = ring_buffer_read_ptr(p->read_buf, pos);
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:58:04 +00:00
|
|
|
/* We will strip AT and S3 */
|
2010-02-01 21:00:45 +00:00
|
|
|
line_length -= 3;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
line = g_try_new(char, line_length + 1);
|
|
|
|
|
|
|
|
if (!line) {
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_drain(p->read_buf, p->read_so_far);
|
2010-01-18 14:31:18 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
/* Strip leading whitespace + AT */
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_drain(p->read_buf, strip_front + 2);
|
2010-02-01 23:58:48 +00:00
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
i = 0;
|
2010-02-09 14:59:27 +00:00
|
|
|
wrap = ring_buffer_len_no_wrap(p->read_buf);
|
|
|
|
buf = ring_buffer_read_ptr(p->read_buf, pos);
|
2010-02-01 23:58:48 +00:00
|
|
|
|
|
|
|
while (pos < (p->read_so_far - strip_front - 2)) {
|
|
|
|
if (*buf == '"')
|
|
|
|
in_string = !in_string;
|
|
|
|
|
|
|
|
if ((*buf == ' ' || *buf == '\t') && in_string == FALSE)
|
|
|
|
; /* Skip */
|
2010-03-22 17:58:04 +00:00
|
|
|
else if (*buf != s3)
|
2010-02-01 23:58:48 +00:00
|
|
|
line[i++] = *buf;
|
|
|
|
|
|
|
|
buf += 1;
|
|
|
|
pos += 1;
|
|
|
|
|
|
|
|
if (pos == wrap)
|
2010-02-09 14:59:27 +00:00
|
|
|
buf = ring_buffer_read_ptr(p->read_buf, pos);
|
2010-02-01 23:58:48 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 17:58:04 +00:00
|
|
|
/* Strip S3 */
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_drain(p->read_buf, p->read_so_far - strip_front - 2);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 23:58:48 +00:00
|
|
|
line[i] = '\0';
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void new_bytes(GAtServer *p)
|
|
|
|
{
|
2010-02-09 14:59:27 +00:00
|
|
|
unsigned int len = ring_buffer_len(p->read_buf);
|
|
|
|
unsigned int wrap = ring_buffer_len_no_wrap(p->read_buf);
|
|
|
|
unsigned char *buf = ring_buffer_read_ptr(p->read_buf, p->read_so_far);
|
2010-03-03 19:07:24 +00:00
|
|
|
enum ParserResult result;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-09 14:59:29 +00:00
|
|
|
while (p->channel && (p->read_so_far < len)) {
|
2010-01-18 14:31:18 +00:00
|
|
|
gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
|
2010-02-01 21:00:45 +00:00
|
|
|
result = server_feed(p, (char *)buf, &rbytes);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
buf += rbytes;
|
2010-01-18 14:31:18 +00:00
|
|
|
p->read_so_far += rbytes;
|
|
|
|
|
|
|
|
if (p->read_so_far == wrap) {
|
2010-02-09 14:59:27 +00:00
|
|
|
buf = ring_buffer_read_ptr(p->read_buf, p->read_so_far);
|
2010-01-18 14:31:18 +00:00
|
|
|
wrap = len;
|
|
|
|
}
|
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
if (result == PARSER_RESULT_UNSURE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case PARSER_RESULT_EMPTY_COMMAND:
|
|
|
|
/*
|
|
|
|
* According to section 5.2.4 and 5.6 of V250,
|
|
|
|
* Empty commands must be OK by the DCE
|
|
|
|
*/
|
2010-03-18 15:14:24 +00:00
|
|
|
g_at_server_send_final(p, G_AT_SERVER_RESULT_OK);
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_drain(p->read_buf, p->read_so_far);
|
2010-02-01 21:00:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PARSER_RESULT_COMMAND:
|
2010-03-22 17:57:40 +00:00
|
|
|
{
|
2010-03-30 20:24:48 +00:00
|
|
|
g_free(p->last_line);
|
2010-03-22 17:57:40 +00:00
|
|
|
|
2010-03-30 20:24:48 +00:00
|
|
|
p->last_line = extract_line(p);
|
2010-03-31 00:15:55 +00:00
|
|
|
p->cur_pos = 0;
|
2010-03-30 20:24:48 +00:00
|
|
|
|
|
|
|
if (p->last_line)
|
|
|
|
server_parse_line(p);
|
|
|
|
else
|
2010-03-22 17:57:40 +00:00
|
|
|
g_at_server_send_final(p,
|
|
|
|
G_AT_SERVER_RESULT_ERROR);
|
2010-02-01 21:00:45 +00:00
|
|
|
break;
|
2010-03-22 17:57:40 +00:00
|
|
|
}
|
2010-02-01 21:00:45 +00:00
|
|
|
|
|
|
|
case PARSER_RESULT_REPEAT_LAST:
|
2010-03-31 09:50:37 +00:00
|
|
|
p->cur_pos = 0;
|
|
|
|
|
|
|
|
if (p->last_line)
|
|
|
|
server_parse_line(p);
|
|
|
|
else
|
|
|
|
g_at_server_send_final(p,
|
|
|
|
G_AT_SERVER_RESULT_OK);
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_drain(p->read_buf, p->read_so_far);
|
2010-02-01 21:00:45 +00:00
|
|
|
break;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-01 21:00:45 +00:00
|
|
|
default:
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_drain(p->read_buf, p->read_so_far);
|
2010-02-01 21:00:45 +00:00
|
|
|
break;
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
2010-02-01 21:00:45 +00:00
|
|
|
|
|
|
|
len -= p->read_so_far;
|
|
|
|
wrap -= p->read_so_far;
|
|
|
|
p->read_so_far = 0;
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
2010-02-01 21:00:45 +00:00
|
|
|
|
|
|
|
/* We're overflowing the buffer, shutdown the socket */
|
2010-02-09 14:59:27 +00:00
|
|
|
if (p->read_buf && ring_buffer_avail(p->read_buf) == 0)
|
2010-02-26 09:56:29 +00:00
|
|
|
g_source_remove(p->read_watch);
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
unsigned char *buf;
|
|
|
|
GAtServer *server = data;
|
|
|
|
GIOError err;
|
|
|
|
gsize rbytes;
|
|
|
|
gsize toread;
|
2010-01-29 21:18:16 +00:00
|
|
|
guint total_read = 0;
|
|
|
|
guint read_count = 0;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
if (cond & G_IO_NVAL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
do {
|
2010-02-09 14:59:27 +00:00
|
|
|
toread = ring_buffer_avail_no_wrap(server->read_buf);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
if (toread == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
rbytes = 0;
|
2010-02-09 14:59:27 +00:00
|
|
|
buf = ring_buffer_write_ptr(server->read_buf);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
err = g_io_channel_read(channel, (char *) buf, toread, &rbytes);
|
2010-01-29 19:28:03 +00:00
|
|
|
g_at_util_debug_chat(TRUE, (char *)buf, rbytes,
|
|
|
|
server->debugf, server->debug_data);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-03-31 03:06:12 +00:00
|
|
|
read_count++;
|
|
|
|
|
2010-03-31 09:50:38 +00:00
|
|
|
if (rbytes == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (server->v250.echo)
|
|
|
|
send_common(server, (char *)buf, rbytes);
|
|
|
|
|
2010-03-31 02:40:08 +00:00
|
|
|
/* Ignore incoming bytes when processing a command line */
|
|
|
|
if (server->processing_cmdline)
|
|
|
|
continue;
|
|
|
|
|
2010-01-29 21:18:16 +00:00
|
|
|
total_read += rbytes;
|
2010-03-31 09:50:38 +00:00
|
|
|
ring_buffer_write_advance(server->read_buf, rbytes);
|
|
|
|
} while (err == G_IO_ERROR_NONE &&
|
2010-01-29 21:18:16 +00:00
|
|
|
read_count < server->max_read_attempts);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-01-29 21:18:16 +00:00
|
|
|
if (total_read > 0)
|
|
|
|
new_bytes(server);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
if (cond & (G_IO_HUP | G_IO_ERR))
|
|
|
|
return FALSE;
|
|
|
|
|
2010-01-29 21:18:16 +00:00
|
|
|
if (read_count > 0 && rbytes == 0 && err != G_IO_ERROR_AGAIN)
|
2010-01-18 14:31:18 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2010-02-26 09:56:32 +00:00
|
|
|
GAtServer *server = data;
|
|
|
|
GIOError err;
|
|
|
|
gsize bytes_written;
|
|
|
|
gsize towrite;
|
|
|
|
struct ring_buffer *write_buf;
|
|
|
|
unsigned char *buf;
|
|
|
|
#ifdef WRITE_SCHEDULER_DEBUG
|
|
|
|
int limiter;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!server->write_queue)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* Write data out from the head of the queue */
|
|
|
|
write_buf = g_queue_peek_head(server->write_queue);
|
|
|
|
|
|
|
|
buf = ring_buffer_read_ptr(write_buf, 0);
|
|
|
|
|
|
|
|
towrite = ring_buffer_len_no_wrap(write_buf);
|
|
|
|
|
|
|
|
#ifdef WRITE_SCHEDULER_DEBUG
|
|
|
|
limiter = towrite;
|
|
|
|
|
|
|
|
if (limiter > 5)
|
|
|
|
limiter = 5;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
err = g_io_channel_write(server->channel,
|
|
|
|
(char *)buf,
|
|
|
|
#ifdef WRITE_SCHEDULER_DEBUG
|
|
|
|
limiter,
|
|
|
|
#else
|
|
|
|
towrite,
|
|
|
|
#endif
|
|
|
|
&bytes_written);
|
|
|
|
|
|
|
|
if (err != G_IO_ERROR_NONE) {
|
|
|
|
g_source_remove(server->read_watch);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_at_util_debug_chat(FALSE, (char *)buf, bytes_written, server->debugf,
|
|
|
|
server->debug_data);
|
|
|
|
|
|
|
|
ring_buffer_drain(write_buf, bytes_written);
|
|
|
|
|
|
|
|
/* All data in current buffer is written, free it
|
|
|
|
* unless it's the last buffer in the queue.
|
|
|
|
*/
|
|
|
|
if ((ring_buffer_len(write_buf) == 0) &&
|
2010-02-26 22:17:10 +00:00
|
|
|
(g_queue_get_length(server->write_queue) > 1)) {
|
2010-02-26 09:56:32 +00:00
|
|
|
write_buf = g_queue_pop_head(server->write_queue);
|
|
|
|
ring_buffer_free(write_buf);
|
2010-02-26 22:17:10 +00:00
|
|
|
write_buf = g_queue_peek_head(server->write_queue);
|
2010-02-26 09:56:32 +00:00
|
|
|
}
|
|
|
|
|
2010-02-26 22:17:10 +00:00
|
|
|
if (ring_buffer_len(write_buf) > 0)
|
2010-02-26 09:56:32 +00:00
|
|
|
return TRUE;
|
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_queue_free(GQueue *write_queue)
|
|
|
|
{
|
|
|
|
struct ring_buffer *write_buf;
|
|
|
|
|
|
|
|
while ((write_buf = g_queue_pop_head(write_queue)))
|
|
|
|
ring_buffer_free(write_buf);
|
|
|
|
|
|
|
|
g_queue_free(write_queue);
|
|
|
|
}
|
|
|
|
|
2010-02-26 09:56:29 +00:00
|
|
|
static void g_at_server_cleanup(GAtServer *server)
|
2010-01-18 14:31:18 +00:00
|
|
|
{
|
2010-02-26 09:56:29 +00:00
|
|
|
/* Cleanup all received data */
|
2010-02-09 14:59:27 +00:00
|
|
|
ring_buffer_free(server->read_buf);
|
|
|
|
server->read_buf = NULL;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
/* Cleanup pending data to write */
|
|
|
|
write_queue_free(server->write_queue);
|
|
|
|
|
2010-03-19 09:44:42 +00:00
|
|
|
g_hash_table_destroy(server->command_list);
|
|
|
|
server->command_list = NULL;
|
|
|
|
|
2010-03-30 20:24:48 +00:00
|
|
|
g_free(server->last_line);
|
|
|
|
|
2010-02-09 14:59:29 +00:00
|
|
|
server->channel = NULL;
|
2010-02-26 09:56:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void read_watcher_destroy_notify(GAtServer *server)
|
|
|
|
{
|
|
|
|
g_at_server_cleanup(server);
|
|
|
|
server->read_watch = 0;
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
if (server->user_disconnect)
|
|
|
|
server->user_disconnect(server->user_disconnect_data);
|
2010-02-26 09:56:29 +00:00
|
|
|
|
|
|
|
if (server->destroyed)
|
|
|
|
g_free(server);
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
static void write_watcher_destroy_notify(GAtServer *server)
|
|
|
|
{
|
|
|
|
server->write_watch = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void g_at_server_wakeup_writer(GAtServer *server)
|
|
|
|
{
|
|
|
|
if (server->write_watch != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
server->write_watch = g_io_add_watch_full(server->channel,
|
|
|
|
G_PRIORITY_DEFAULT,
|
|
|
|
G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
|
|
|
|
can_write_data, server,
|
|
|
|
(GDestroyNotify)write_watcher_destroy_notify);
|
|
|
|
}
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
static void v250_settings_create(struct v250_settings *v250)
|
|
|
|
{
|
|
|
|
v250->s3 = '\r';
|
|
|
|
v250->s4 = '\n';
|
|
|
|
v250->s5 = '\b';
|
|
|
|
v250->echo = TRUE;
|
|
|
|
v250->quiet = FALSE;
|
|
|
|
v250->is_v1 = TRUE;
|
|
|
|
v250->res_format = 0;
|
|
|
|
v250->c109 = 1;
|
|
|
|
v250->c108 = 0;
|
|
|
|
}
|
|
|
|
|
2010-03-19 09:44:42 +00:00
|
|
|
static void at_notify_node_destroy(gpointer data)
|
|
|
|
{
|
|
|
|
struct at_command *node = data;
|
|
|
|
|
|
|
|
if (node->destroy_notify)
|
|
|
|
node->destroy_notify(node->user_data);
|
|
|
|
|
|
|
|
g_free(node);
|
|
|
|
}
|
|
|
|
|
2010-03-31 09:50:35 +00:00
|
|
|
static void basic_command_register(GAtServer *server)
|
|
|
|
{
|
|
|
|
g_at_server_register(server, "S3", at_s3_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "S4", at_s4_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "S5", at_s5_cb, server, NULL);
|
2010-03-31 09:50:36 +00:00
|
|
|
g_at_server_register(server, "E", at_e_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "Q", at_q_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "V", at_v_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "X", at_x_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "&C", at_c109_cb, server, NULL);
|
|
|
|
g_at_server_register(server, "&D", at_c108_cb, server, NULL);
|
2010-03-31 09:50:35 +00:00
|
|
|
}
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
GAtServer *g_at_server_new(GIOChannel *io)
|
|
|
|
{
|
|
|
|
GAtServer *server;
|
|
|
|
|
|
|
|
if (!io)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
server = g_try_new0(GAtServer, 1);
|
|
|
|
if (!server)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
server->ref_count = 1;
|
|
|
|
v250_settings_create(&server->v250);
|
2010-02-09 14:59:29 +00:00
|
|
|
server->channel = io;
|
2010-03-19 09:44:42 +00:00
|
|
|
server->command_list = g_hash_table_new_full(g_str_hash, g_str_equal,
|
|
|
|
g_free,
|
|
|
|
at_notify_node_destroy);
|
2010-02-26 09:56:30 +00:00
|
|
|
server->read_buf = ring_buffer_new(BUF_SIZE);
|
2010-02-09 14:59:27 +00:00
|
|
|
if (!server->read_buf)
|
2010-01-18 14:31:18 +00:00
|
|
|
goto error;
|
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
server->write_queue = g_queue_new();
|
|
|
|
if (!server->write_queue)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!allocate_next(server))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
server->max_read_attempts = 3;
|
|
|
|
|
2010-02-09 14:59:29 +00:00
|
|
|
if (!g_at_util_setup_io(server->channel, G_IO_FLAG_NONBLOCK))
|
2010-01-18 14:31:18 +00:00
|
|
|
goto error;
|
|
|
|
|
2010-02-26 09:56:29 +00:00
|
|
|
server->read_watch = g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
|
2010-01-18 14:31:18 +00:00
|
|
|
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
|
|
|
|
received_data, server,
|
2010-02-26 09:56:29 +00:00
|
|
|
(GDestroyNotify)read_watcher_destroy_notify);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-03-31 09:50:35 +00:00
|
|
|
basic_command_register(server);
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
return server;
|
|
|
|
|
|
|
|
error:
|
2010-03-19 09:44:42 +00:00
|
|
|
if (server->command_list)
|
|
|
|
g_hash_table_destroy(server->command_list);
|
|
|
|
|
2010-02-09 14:59:27 +00:00
|
|
|
if (server->read_buf)
|
|
|
|
ring_buffer_free(server->read_buf);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
if (server->write_queue)
|
|
|
|
write_queue_free(server->write_queue);
|
|
|
|
|
2010-01-18 14:31:18 +00:00
|
|
|
if (server)
|
|
|
|
g_free(server);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GAtServer *g_at_server_ref(GAtServer *server)
|
|
|
|
{
|
|
|
|
if (server == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
g_atomic_int_inc(&server->ref_count);
|
|
|
|
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
void g_at_server_unref(GAtServer *server)
|
|
|
|
{
|
|
|
|
gboolean is_zero;
|
|
|
|
|
|
|
|
if (server == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
is_zero = g_atomic_int_dec_and_test(&server->ref_count);
|
|
|
|
|
|
|
|
if (is_zero == FALSE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_at_server_shutdown(server);
|
2010-02-26 09:56:29 +00:00
|
|
|
|
|
|
|
/* glib delays the destruction of the watcher until it exits, this
|
|
|
|
* means we can't free the data just yet, even though we've been
|
|
|
|
* destroyed already. We have to wait until the read_watcher
|
|
|
|
* destroy function gets called
|
|
|
|
*/
|
|
|
|
if (server->read_watch != 0)
|
|
|
|
server->destroyed = TRUE;
|
|
|
|
else
|
|
|
|
g_free(server);
|
2010-01-18 14:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gboolean g_at_server_shutdown(GAtServer *server)
|
|
|
|
{
|
|
|
|
if (!server)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* Don't trigger user disconnect on shutdown */
|
|
|
|
server->user_disconnect = NULL;
|
|
|
|
server->user_disconnect_data = NULL;
|
|
|
|
|
2010-02-26 09:56:31 +00:00
|
|
|
if (server->write_watch)
|
|
|
|
g_source_remove(server->write_watch);
|
|
|
|
|
2010-02-26 09:56:29 +00:00
|
|
|
if (server->read_watch)
|
|
|
|
g_source_remove(server->read_watch);
|
2010-01-18 14:31:18 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean g_at_server_set_disconnect_function(GAtServer *server,
|
|
|
|
GAtDisconnectFunc disconnect,
|
|
|
|
gpointer user)
|
|
|
|
{
|
|
|
|
if (server == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
server->user_disconnect = disconnect;
|
|
|
|
server->user_disconnect_data = user;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean g_at_server_set_debug(GAtServer *server, GAtDebugFunc func,
|
|
|
|
gpointer user)
|
|
|
|
{
|
|
|
|
if (server == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
server->debugf = func;
|
|
|
|
server->debug_data = user;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2010-03-19 09:44:44 +00:00
|
|
|
|
|
|
|
gboolean g_at_server_register(GAtServer *server, char *prefix,
|
|
|
|
GAtServerNotifyFunc notify,
|
|
|
|
gpointer user_data,
|
|
|
|
GDestroyNotify destroy_notify)
|
|
|
|
{
|
|
|
|
struct at_command *node;
|
|
|
|
|
|
|
|
if (server == NULL || server->command_list == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (notify == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (prefix == NULL || strlen(prefix) == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
node = g_try_new0(struct at_command, 1);
|
|
|
|
if (!node)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
node->notify = notify;
|
|
|
|
node->user_data = user_data;
|
|
|
|
node->destroy_notify = destroy_notify;
|
|
|
|
|
|
|
|
g_hash_table_replace(server->command_list, g_strdup(prefix), node);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean g_at_server_unregister(GAtServer *server, const char *prefix)
|
|
|
|
{
|
|
|
|
struct at_command *node;
|
|
|
|
|
|
|
|
if (server == NULL || server->command_list == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (prefix == NULL || strlen(prefix) == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
node = g_hash_table_lookup(server->command_list, prefix);
|
|
|
|
if (!node)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
g_hash_table_remove(server->command_list, prefix);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|