Add AT chat library implementation

This commit is contained in:
Denis Kenzior 2009-05-06 14:33:17 -07:00 committed by Marcel Holtmann
parent 4ea27466bf
commit 64c4276473
7 changed files with 1980 additions and 1 deletions

View File

@ -1,7 +1,8 @@
noinst_LTLIBRARIES = libgatchat.la
libgatchat_la_SOURCES =
libgatchat_la_SOURCES = gatchat.h gatchat.c gatresult.h gatresult.c \
ringbuffer.h ringbuffer.c
AM_CFLAGS = @GLIB_CFLAGS@

1104
gatchat/gatchat.c Normal file

File diff suppressed because it is too large Load Diff

105
gatchat/gatchat.h Normal file
View File

@ -0,0 +1,105 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 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
*
*/
#ifndef __GATCHAT_H
#define __GATCHAT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "gatresult.h"
struct _GAtChat;
typedef struct _GAtChat GAtChat;
typedef void (*GAtResultFunc)(gboolean success, GAtResult *result,
gpointer user_data);
typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data);
typedef void (*GAtDisconnectFunc)(gpointer user_data);
enum _GAtChatFlags {
G_AT_CHAT_FLAG_NO_LEADING_CRLF = 1, /* Some emulators are broken */
};
typedef enum _GAtChatFlags GAtChatFlags;
GAtChat *g_at_chat_new(GIOChannel *channel, int flags);
GAtChat *g_at_chat_ref(GAtChat *chat);
void g_at_chat_unref(GAtChat *chat);
gboolean g_at_chat_shutdown(GAtChat *chat);
gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
GAtDisconnectFunc disconnect, gpointer user_data);
/*!
* Queue an AT command for execution. The command contents are given
* in cmd. Once the command executes, the callback function given by
* func is called with user provided data in user_data.
*
* Returns an id of the queued command which can be canceled using
* g_at_chat_cancel. If an error occurred, an id of 0 is returned.
*
* This function can be used in three ways:
* - Send a simple command such as g_at_chat_send(p, "AT+CGMI?", ...
*
* - Send a compound command: g_at_chat_send(p, "AT+CMD1;+CMD2", ...
*
* - Send a command requiring a prompt. The command up to '\r' is sent
* after which time a '> ' prompt is expected from the modem. Further
* contents of the command are sent until a '\r' or end of string is
* encountered. If end of string is encountered, the Ctrl-Z character
* is sent automatically. There is no need to include the Ctrl-Z
* by the caller.
*
* The valid_resp field can be used to send an array of strings which will
* be accepted as a valid response for this command. This is treated as a
* simple prefix match. If a response line comes in from the modem and it
* does not match any of the prefixes in valid_resp, it is treated as an
* unsolicited notification. If valid_resp is NULL, then all response
* lines after command submission and final response line are treated as
* part of the command response. This can be used to get around broken
* modems which send unsolicited notifications during command processing.
*/
guint g_at_chat_send(GAtChat *chat, const char *cmd,
const char **valid_resp, GAtResultFunc func,
gpointer user_data, GDestroyNotify notify);
gboolean g_at_chat_cancel(GAtChat *chat, guint id);
guint g_at_chat_register(GAtChat *chat, const char *prefix,
GAtNotifyFunc func, gboolean expect_pdu,
gpointer user_data, GDestroyNotify notify);
gboolean g_at_chat_unregister(GAtChat *chat, guint id);
gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
guint timeout, guint msec);
#ifdef __cplusplus
}
#endif
#endif /* __GATCHAT_H */

377
gatchat/gatresult.c Normal file
View File

@ -0,0 +1,377 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 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 <string.h>
#include <glib.h>
#include "gatresult.h"
void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result)
{
iter->result = result;
iter->pre.next = result->lines;
iter->pre.data = NULL;
iter->l = &iter->pre;
iter->line_pos = 0;
}
gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix)
{
char *line;
int prefix_len = prefix ? strlen(prefix) : 0;
while ((iter->l = iter->l->next)) {
line = iter->l->data;
if (prefix_len == 0) {
iter->line_pos = 0;
return TRUE;
}
if (g_str_has_prefix(line, prefix) == FALSE)
continue;
iter->line_pos = prefix_len;
while (iter->line_pos < strlen(line) &&
line[iter->line_pos] == ' ')
iter->line_pos += 1;
return TRUE;
}
return FALSE;
}
const char *g_at_result_iter_raw_line(GAtResultIter *iter)
{
const char *line;
if (!iter)
return NULL;
if (!iter->l)
return NULL;
line = iter->l->data;
line += iter->line_pos;
return line;
}
static inline int skip_to_next_field(const char *line, int pos, int len)
{
if (pos < len && line[pos] == ',')
pos += 1;
while (pos < len && line[pos] == ' ')
pos += 1;
return pos;
}
gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str)
{
unsigned int pos;
unsigned int end;
unsigned int len;
char *line;
if (!iter)
return FALSE;
if (!iter->l)
return FALSE;
line = iter->l->data;
len = strlen(line);
pos = iter->line_pos;
/* Omitted string */
if (line[pos] == ',') {
end = pos;
memset(iter->buf, 0, sizeof(iter->buf));
goto out;
}
if (line[pos++] != '"')
return FALSE;
end = pos;
while (end < len && line[end] != '"')
end += 1;
if (line[end] != '"')
return FALSE;
if (end - pos >= sizeof(iter->buf))
return FALSE;
strncpy(iter->buf, line+pos, end-pos);
memset(iter->buf + end - pos, 0, sizeof(iter->buf) - end + pos);
/* Skip " */
end += 1;
out:
iter->line_pos = skip_to_next_field(line, end, len);
if (str)
*str = iter->buf;
return TRUE;
}
gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number)
{
int pos;
int end;
int len;
int value = 0;
char *line;
if (!iter)
return FALSE;
if (!iter->l)
return FALSE;
line = iter->l->data;
len = strlen(line);
pos = iter->line_pos;
end = pos;
while (line[end] >= '0' && line[end] <= '9') {
value = value * 10 + (int)(line[end] - '0');
end += 1;
}
if (pos == end)
return FALSE;
iter->line_pos = skip_to_next_field(line, end, len);
if (number)
*number = value;
return TRUE;
}
gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max)
{
int pos;
int end;
int len;
int low = 0;
int high = 0;
char *line;
if (!iter)
return FALSE;
if (!iter->l)
return FALSE;
line = iter->l->data;
len = strlen(line);
pos = iter->line_pos;
while (pos < len && line[pos] == ' ')
pos += 1;
end = pos;
while (line[end] >= '0' && line[end] <= '9') {
low = low * 10 + (int)(line[end] - '0');
end += 1;
}
if (pos == end)
return FALSE;
if (line[end] == ',') {
high = low;
goto out;
}
if (line[end] == '-')
pos = end = end + 1;
else
return FALSE;
while (line[end] >= '0' && line[end] <= '9') {
high = high * 10 + (int)(line[end] - '0');
end += 1;
}
if (pos == end)
return FALSE;
out:
iter->line_pos = skip_to_next_field(line, end, len);
if (min)
*min = low;
if (max)
*max = high;
return TRUE;
}
static gint skip_until(const char *line, int start, const char delim)
{
int len = strlen(line);
int i = start;
while (i < len) {
if (line[i] == delim)
return i;
if (line[i] != '(') {
i += 1;
continue;
}
i = skip_until(line, i+1, ')');
if (i < len)
i += 1;
}
return i;
}
gboolean g_at_result_iter_skip_next(GAtResultIter *iter)
{
unsigned int skipped_to;
char *line;
if (!iter)
return FALSE;
if (!iter->l)
return FALSE;
line = iter->l->data;
skipped_to = skip_until(line, iter->line_pos, ',');
if (skipped_to == iter->line_pos && line[skipped_to] != ',')
return FALSE;
iter->line_pos = skip_to_next_field(line, skipped_to, strlen(line));
return TRUE;
}
gboolean g_at_result_iter_open_list(GAtResultIter *iter)
{
char *line;
unsigned int len;
if (!iter)
return FALSE;
if (!iter->l)
return FALSE;
line = iter->l->data;
len = strlen(line);
if (iter->line_pos >= len)
return FALSE;
if (line[iter->line_pos] != '(')
return FALSE;
iter->line_pos += 1;
while (iter->line_pos < strlen(line) &&
line[iter->line_pos] == ' ')
iter->line_pos += 1;
return TRUE;
}
gboolean g_at_result_iter_close_list(GAtResultIter *iter)
{
char *line;
unsigned int len;
if (!iter)
return FALSE;
if (!iter->l)
return FALSE;
line = iter->l->data;
len = strlen(line);
if (iter->line_pos >= len)
return FALSE;
if (line[iter->line_pos] != ')')
return FALSE;
iter->line_pos += 1;
iter->line_pos = skip_to_next_field(line, iter->line_pos, len);
return TRUE;
}
const char *g_at_result_final_response(GAtResult *result)
{
if (!result)
return NULL;
return result->final_or_pdu;
}
const char *g_at_result_pdu(GAtResult *result)
{
if (!result)
return NULL;
return result->final_or_pdu;
}
gint g_at_result_num_response_lines(GAtResult *result)
{
if (!result)
return 0;
if (!result->lines)
return 0;
return g_slist_length(result->lines);
}

69
gatchat/gatresult.h Normal file
View File

@ -0,0 +1,69 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 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
*
*/
#ifndef __GATCHAT_RESULT_H
#define __GATCHAT_RESULT_H
#ifdef __cplusplus
extern "C" {
#endif
struct _GAtResult {
GSList *lines;
char *final_or_pdu;
};
typedef struct _GAtResult GAtResult;
struct _GAtResultIter {
GAtResult *result;
GSList *l;
char buf[2048];
unsigned int line_pos;
GSList pre;
};
typedef struct _GAtResultIter GAtResultIter;
void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result);
gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix);
gboolean g_at_result_iter_open_list(GAtResultIter *iter);
gboolean g_at_result_iter_close_list(GAtResultIter *iter);
gboolean g_at_result_iter_skip_next(GAtResultIter *iter);
gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max);
gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str);
gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number);
const char *g_at_result_iter_raw_line(GAtResultIter *iter);
const char *g_at_result_final_response(GAtResult *result);
const char *g_at_result_pdu(GAtResult *result);
gint g_at_result_num_response_lines(GAtResult *result);
#ifdef __cplusplus
}
#endif
#endif /* __GATCHAT_RESULT_H */

199
gatchat/ringbuffer.c Normal file
View File

@ -0,0 +1,199 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 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 <string.h>
#include <glib.h>
#include "ringbuffer.h"
#define MAX_SIZE 262144
struct ring_buffer *ring_buffer_new(unsigned int size)
{
unsigned int real_size = 1;
struct ring_buffer *buffer;
/* Find the next power of two for size */
while (real_size < size && real_size < MAX_SIZE)
real_size = real_size << 1;
if (real_size > MAX_SIZE)
return NULL;
buffer = g_new(struct ring_buffer, 1);
if (!buffer)
return NULL;
buffer->buffer = g_new(unsigned char, real_size);
if (!buffer->buffer) {
g_free(buffer);
return NULL;
}
buffer->size = real_size;
buffer->in = 0;
buffer->out = 0;
return buffer;
}
int ring_buffer_write(struct ring_buffer *buf, const void *data,
unsigned int len)
{
unsigned int end;
unsigned int offset;
const unsigned char *d = data; /* Needed to satisfy non-gcc compilers */
/* Determine how much we can actually write */
len = MIN(len, buf->size - buf->in + buf->out);
/* Determine how much to write before wrapping */
offset = buf->in % buf->size;
end = MIN(len, buf->size - offset);
memcpy(buf->buffer+offset, d, end);
/* Now put the remainder on the beginning of the buffer */
memcpy(buf->buffer, d + end, len - end);
buf->in += len;
return len;
}
unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf)
{
return buf->buffer + buf->in % buf->size;
}
int ring_buffer_avail_no_wrap(struct ring_buffer *buf)
{
unsigned int offset = buf->in % buf->size;
unsigned int len = buf->size - buf->in + buf->out;
return MIN(len, buf->size - offset);
}
int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len)
{
len = MIN(len, buf->size - buf->in + buf->out);
buf->in += len;
return len;
}
int ring_buffer_read(struct ring_buffer *buf, void *data, unsigned int len)
{
unsigned int end;
unsigned int offset;
unsigned char *d = data;
len = MIN(len, buf->in - buf->out);
/* Grab data from buffer starting at offset until the end */
offset = buf->out % buf->size;
end = MIN(len, buf->size - offset);
memcpy(d, buf->buffer + offset, end);
/* Now grab remainder from the beginning */
memcpy(d + end, buf->buffer, len - end);
buf->out += len;
if (buf->out == buf->in)
buf->out = buf->in = 0;
return len;
}
int ring_buffer_drain(struct ring_buffer *buf, unsigned int len)
{
len = MIN(len, buf->in - buf->out);
buf->out += len;
if (buf->out == buf->in)
buf->out = buf->in = 0;
return len;
}
int ring_buffer_len_no_wrap(struct ring_buffer *buf)
{
unsigned int offset = buf->out % buf->size;
unsigned int len = buf->in - buf->out;
return MIN(len, buf->size - offset);
}
unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf,
unsigned int offset)
{
return buf->buffer + (buf->out + offset) % buf->size;
}
int ring_buffer_len(struct ring_buffer *buf)
{
if (!buf)
return -1;
return buf->in - buf->out;
}
void ring_buffer_reset(struct ring_buffer *buf)
{
if (!buf)
return;
buf->in = 0;
buf->out = 0;
}
int ring_buffer_avail(struct ring_buffer *buf)
{
if (!buf)
return -1;
return buf->size - buf->in + buf->out;
}
int ring_buffer_capacity(struct ring_buffer *buf)
{
if (!buf)
return -1;
return buf->size;
}
void ring_buffer_free(struct ring_buffer *buf)
{
if (!buf)
return;
g_free(buf->buffer);
g_free(buf);
}

124
gatchat/ringbuffer.h Normal file
View File

@ -0,0 +1,124 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 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
*
*/
#ifndef __GATCHAT_RINGBUFFER_H
#define __GATCHAT_RINGBUFFER_H
#ifdef __cplusplus
extern "C" {
#endif
struct ring_buffer {
unsigned char *buffer;
unsigned int size;
unsigned int in;
unsigned int out;
};
/*!
* Creates a new ring buffer with capacity size
*/
struct ring_buffer *ring_buffer_new(unsigned int size);
/*!
* Frees the resources allocated for the ring buffer
*/
void ring_buffer_free(struct ring_buffer *buf);
/*!
* Returns the capacity of the ring buffer
*/
int ring_buffer_capacity(struct ring_buffer *buf);
/*!
* Resets the ring buffer, all data inside the buffer is lost
*/
void ring_buffer_reset(struct ring_buffer *buf);
/*!
* Writes data of size len into the ring buffer buf. Returns -1 if the
* write failed or the number of bytes written
*/
int ring_buffer_write(struct ring_buffer *buf, const void *data,
unsigned int len);
/*!
* Advances the write counter by len, this is meant to be used with
* the ring_buffer_write_ptr function. Returns the number of bytes
* actually advanced (the capacity of the buffer)
*/
int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len);
/*!
* Returns the write pointer. Careful not to write past the end of the
* buffer. Use the ring_buffer_avail_no_wrap function,
* ring_buffer_write_advance.
*/
unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf);
/*!
* Returns the number of free bytes available in the buffer
*/
int ring_buffer_avail(struct ring_buffer *buf);
/*!
* Returns the number of free bytes available in the buffer without wrapping
*/
int ring_buffer_avail_no_wrap(struct ring_buffer *buf);
/*!
* Reads data from the ring buffer buf into memory region pointed to by data.
* A maximum of len bytes will be read. Returns -1 if the read failed or
* the number of bytes read
*/
int ring_buffer_read(struct ring_buffer *buf, void *data,
unsigned int len);
/*!
* Returns the read pointer with read offset specified by offset. No bounds
* checking is performed. Be careful not to read past the end of the buffer.
* Use the ring_buffer_len_no_wrap function, and ring_buffer_drain.
*/
unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf,
unsigned int offset);
/*!
* Returns the number of bytes currently available to be read in the buffer
*/
int ring_buffer_len(struct ring_buffer *buf);
/*!
* Returns the number of bytes currently available to be read in the buffer
* without wrapping.
*/
int ring_buffer_len_no_wrap(struct ring_buffer *buf);
/*!
* Drains the ring buffer of len bytes. Returns the number of bytes the
* read counter was actually advanced.
*/
int ring_buffer_drain(struct ring_buffer *buf, unsigned int len);
#ifdef __cplusplus
}
#endif
#endif /* __GATCHAT_RINGBUFFER_H */