mirror of git://git.sysmocom.de/ofono
Add AT chat library implementation
This commit is contained in:
parent
4ea27466bf
commit
64c4276473
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libgatchat.la
|
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@
|
AM_CFLAGS = @GLIB_CFLAGS@
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 */
|
Loading…
Reference in New Issue