This serie of patches introduces AT chat module which is meant to be
used by the Handsfree Profile for receiving and sending AT commands and
results. As of now it is only used by unit tests.
Patches contain:
*circular buffer implementation
*unit tests for the circular buffer
*AT chat module skeleton
*basic AT commands parser
*unit tests for the AT command parser
Jakub Tyszkowski (22):
cbuffer: Circular buffer initial implementation
unit: Add basic tests for the circular buffer implementation
cbuffer: Add direct access to buffer's memory
unit: Add test case for cbuffer's manual mode
cbuffer: Add methods for browsing through data
unit: Add test cases for cbuffer memory acess routines
cbuffer: Add seek and check routines
unit: Add test cases for cbuffer seek, check, get_length
blueatchat: Add bluetooth at chat module skeleton
blueatchat: Use circullar buffer as data storage
cbuffer: Add discard method
unit: Add test case for cbuffer's discard
blueatchat: Buffered data sanity check
blueatchat: Add basic parsing capability
blueatchat: Add functions for parsing data sent with the commands
unit: Add basic unit test for the blueatchat module
unit: Add more test cases for the at parser
blueatchat: Add more data parsing functionality
blueatchat: Extend API for sending AT commands
unit: Circular buffer overflow test
gitignore: Add curcular buffer unit tests
gitignore: Add blueatchat unit tests
.gitignore | 2 +
Makefile.am | 15 +
src/shared/blueatchat.c | 503 ++++++++++++++++++++++++++
src/shared/blueatchat.h | 82 +++++
src/shared/cbuffer.c | 239 +++++++++++++
src/shared/cbuffer.h | 49 +++
src/shared/dparser.c | 84 +++++
src/shared/dparser.h | 26 ++
unit/test-blueatchat.c | 901 +++++++++++++++++++++++++++++++++++++++++++++++
unit/test-cbuffer.c | 550 +++++++++++++++++++++++++++++
10 files changed, 2451 insertions(+)
create mode 100644 src/shared/blueatchat.c
create mode 100644 src/shared/blueatchat.h
create mode 100644 src/shared/cbuffer.c
create mode 100644 src/shared/cbuffer.h
create mode 100644 src/shared/dparser.c
create mode 100644 src/shared/dparser.h
create mode 100644 unit/test-blueatchat.c
create mode 100644 unit/test-cbuffer.c
--
1.7.9.5
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index f9bf975..c72c911 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@ unit/test-sdp
unit/test-lib
unit/test-mgmt
unit/test-cbuffer
+unit/test-blueatchat
tools/mgmt-tester
tools/gap-tester
tools/btattach
--
1.7.9.5
---
unit/test-blueatchat.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
index e0cd23b..e0c51cd 100644
--- a/unit/test-blueatchat.c
+++ b/unit/test-blueatchat.c
@@ -595,6 +595,50 @@ static void tc_blueat_atbac(struct blueat_fixture *fix,
g_assert_cmpint(called, ==, 1);
}
+static void tc_blueat_overflow(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 180);
+}
+
/******************************************************************************
* Parser configuration and command registration
******************************************************************************/
@@ -850,5 +894,8 @@ int main(int argc, char **argv)
g_test_add("/blueatchat/cind_2", struct blueat_fixture, 0,
blueat_fix_setup, tc_blueat_cind2,
blueat_fix_teardown);
+ g_test_add("/blueatchat/overflow", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_overflow,
+ blueat_fix_teardown);
return g_test_run();
}
--
1.7.9.5
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index e492c3f..f9bf975 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,7 @@ unit/test-gdbus-client
unit/test-sdp
unit/test-lib
unit/test-mgmt
+unit/test-cbuffer
tools/mgmt-tester
tools/gap-tester
tools/btattach
--
1.7.9.5
---
src/shared/blueatchat.c | 20 ++++++++++++++++++++
src/shared/blueatchat.h | 2 ++
2 files changed, 22 insertions(+)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index 06d8c65..430bb56 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -47,6 +47,7 @@ struct blueatchat_session {
struct circular_buffer *buffer;
GSList *data; /* stores parsed data */
blueatchat_debug_func_t debug_callback;
+ struct circular_buffer *send_buffer;
};
#ifdef BLUEATCHAT_VERBOSE
@@ -449,6 +450,18 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
return true;
}
+bool blueatchat_write(GIOChannel *gio, struct blueatchat_session *session)
+{
+ /* TODO: send as much buffered data as possible to io channel */
+ return true;
+}
+
+bool blueatchat_send(struct blueatchat_session *session, char *message)
+{
+ /* TODO: validate data and push it to the buffer */
+ return true;
+}
+
struct blueatchat_session *blueatchat_init_session(
struct blueatchat_config *config,
struct blueatchat_cmd_descriptor *cmd_list,
@@ -467,6 +480,12 @@ struct blueatchat_session *blueatchat_init_session(
g_free(session);
return NULL;
}
+ session->send_buffer = cbuffer_init(session->config->buff_size);
+ if (!session->send_buffer) {
+ cbuffer_free(session->buffer);
+ free(session);
+ return NULL;
+ }
return session;
}
@@ -478,6 +497,7 @@ void blueatchat_cleanup_session(struct blueatchat_session *session)
g_slist_free_full(session->data, g_free);
cbuffer_free(session->buffer);
+ cbuffer_free(session->send_buffer);
g_free(session);
session = NULL;
}
diff --git a/src/shared/blueatchat.h b/src/shared/blueatchat.h
index 75d055c..ccdcaed 100644
--- a/src/shared/blueatchat.h
+++ b/src/shared/blueatchat.h
@@ -78,3 +78,5 @@ struct blueatchat_session *blueatchat_init_session(
blueatchat_debug_func_t debug_fun);
void blueatchat_cleanup_session(struct blueatchat_session *session);
bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio);
+bool blueatchat_write(GIOChannel *gio, struct blueatchat_session *session);
+bool blueatchat_send(struct blueatchat_session *session, char *message);
--
1.7.9.5
This patch adds data recognition for the compound value, char, quoted
string and value sepparator.
---
src/shared/blueatchat.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/dparser.c | 39 +++++++++++++++++++++++++++++
src/shared/dparser.h | 2 ++
3 files changed, 103 insertions(+)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index 6a12b22..06d8c65 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -152,6 +152,7 @@ static int blueatchat_parse_data(char *descriptor, int *descr_off,
struct blueatchat_session *session,
int *data_off, int amount)
{
+ /* TODO: Add simple recursive call depth counter? */
unsigned int i;
int begin_offset = *data_off;
bool optional = false;
@@ -169,9 +170,29 @@ static int blueatchat_parse_data(char *descriptor, int *descr_off,
if (*data_off - begin_offset == amount)
return 0;
+ /* offset storage for recursive calls */
+ *descr_off = i;
+
switch (ch) {
case '(': /* compound value start */
BLUEATCHAT_DEBUG(session, "(");
+
+ if (*cbuffer_peek_tail(session->buffer, *data_off) ==
+ '(')
+ ++(*data_off);
+ else if (!optional)
+ return -ENODATA;
+
+ ++(*descr_off);
+ if (blueatchat_parse_data(descriptor, descr_off,
+ session, data_off, amount) < 0)
+ return -ENODATA;
+ /*
+ * After recursive call returns, descr_off holds offset
+ * indicating how much the descriptor was processed.
+ * Advance by this value to not process this data again.
+ */
+ i = *descr_off;
break;
case 'o': /* optional value */
BLUEATCHAT_DEBUG(session, "o");
@@ -188,17 +209,58 @@ static int blueatchat_parse_data(char *descriptor, int *descr_off,
session->data = g_slist_prepend(session->data, newdata);
break;
+ case 'c': /* single char */
+ BLUEATCHAT_DEBUG(session, "c");
+
+ newdata = dparse_char(session->buffer, data_off);
+ if (!newdata && !optional)
+ return -ENODATA;
+
+ session->data = g_slist_prepend(session->data, newdata);
+ break;
case 'q': /* quoted string */
BLUEATCHAT_DEBUG(session, "q");
+
+ newdata = dparse_qstr(session->buffer, data_off);
+ if (!newdata && !optional)
+ return -ENODATA;
+
+ session->data = g_slist_prepend(session->data, newdata);
break;
case 's': /* TODO: unquoted string */
BLUEATCHAT_DEBUG(session, "s");
break;
case ')': /* compound value end */
BLUEATCHAT_DEBUG(session, ")");
+
+ if (*cbuffer_peek_tail(session->buffer, *data_off)
+ == ')')
+ ++(*data_off);
+ else if (!optional)
+ return -ENODATA;
+
+ return 0;
+ case '-': /* TODO: value range */
+ BLUEATCHAT_DEBUG(session, "-");
+
+ ++(*data_off); /* For now it just skip it */
break;
case ',': /* value separator */
BLUEATCHAT_DEBUG(session, ",");
+
+ if (*cbuffer_peek_tail(session->buffer, *data_off)
+ == ',')
+ ++(*data_off);
+ else if (!optional)
+ return -ENODATA;
+
+ /*
+ * Optional classifier works on a single descriptor, but
+ * turn this flag off only when this is not the opening
+ * one, like in "uo,u", where ',' is also optional.
+ */
+ if (i > 0 && descriptor[i - 1] != 'o')
+ optional = 0;
break;
case '*':
/*
diff --git a/src/shared/dparser.c b/src/shared/dparser.c
index 7b7a2a2..0de7038 100644
--- a/src/shared/dparser.c
+++ b/src/shared/dparser.c
@@ -43,3 +43,42 @@ unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset)
}
return ret;
}
+
+char *dparse_qstr(struct circular_buffer *buffer, int *offset)
+{
+ char *str;
+ int len;
+ int i;
+
+ if (*cbuffer_peek_tail(buffer, *offset) != '"')
+ return NULL;
+
+ len = cbuffer_seek_tail_for_seq(buffer, "\"", *offset + 1) - *offset;
+ if (len <= 0)
+ return NULL;
+
+ str = g_malloc(len);
+
+ for (i = 0; i < len - 1; ++i)
+ str[i] = *cbuffer_peek_tail(buffer, *offset + i + 1);
+ str[i] = '\0';
+ /* Move offset on first char after closing quotation mark */
+ *offset += len + 1;
+ return str;
+}
+
+char *dparse_char(struct circular_buffer *buffer, int *offset)
+{
+ const char *c = NULL;
+ char *ret = NULL;
+
+ c = cbuffer_peek_tail(buffer, *offset);
+ if (!isascii(*c))
+ return NULL;
+
+ ret = g_new(char, 1);
+ *ret = *c;
+ ++(*offset);
+
+ return ret;
+}
diff --git a/src/shared/dparser.h b/src/shared/dparser.h
index 7c0bf58..66dd4a7 100644
--- a/src/shared/dparser.h
+++ b/src/shared/dparser.h
@@ -22,3 +22,5 @@
*/
unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset);
+char *dparse_qstr(struct circular_buffer *buffer, int *offset);
+char *dparse_char(struct circular_buffer *buffer, int *offset);
--
1.7.9.5
This patch adds first few basic unit test for blueatchat's AT parsing
functionality.
---
Makefile.am | 9 ++
unit/test-blueatchat.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 343 insertions(+)
create mode 100644 unit/test-blueatchat.c
diff --git a/Makefile.am b/Makefile.am
index 079405f..76bcc75 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -289,6 +289,15 @@ unit_test_cbuffer_SOURCES = unit/test-cbuffer.c \
src/shared/cbuffer.h src/shared/cbuffer.c
unit_test_cbuffer_LDADD = @GLIB_LIBS@
+unit_tests += unit/test-blueatchat
+
+unit_test_blueatchat_SOURCES = unit/test-blueatchat.c \
+ src/shared/util.h src/shared/util.c \
+ src/shared/cbuffer.h src/shared/cbuffer.c \
+ src/shared/blueatchat.h src/shared/blueatchat.c \
+ src/shared/dparser.h src/shared/dparser.c
+unit_test_blueatchat_LDADD = @GLIB_LIBS@
+
noinst_PROGRAMS += $(unit_tests)
TESTS = $(unit_tests)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
new file mode 100644
index 0000000..ae10ff7
--- /dev/null
+++ b/unit/test-blueatchat.c
@@ -0,0 +1,334 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include "src/shared/cbuffer.h"
+#include "src/shared/blueatchat.h"
+
+/* use power of 2 for the buffer size */
+#define BUFFER_SIZE (1<<8)
+
+/* #define BLUEATCHAT_DEBUG */
+
+/* storage used by unit testing framework */
+struct blueat_fixture {
+ GMainLoop *loop;
+ char *msg;
+ struct blueatchat_session *session;
+ GIOChannel *gio_read, *gio_write;
+ bool issent;
+};
+
+/* stores callback calls count for each test case if data was parsed */
+volatile int called;
+/* stores callback calls for unknown commands or with wring data */
+volatile int unknown;
+
+/******************************************************************************
+ * Add test cases below:
+ * - create test case for sending commands data
+ * - if sending new commands - remember to register this command
+ * and its callback which verifies parsed data
+ * - add prepared test case in the main function at the very bottom
+ ******************************************************************************/
+/* Unknown command test case and callback that verifies (non)parsed data */
+static bool tc_blueat_unknown_cb(GSList *data)
+{
+ unknown += 1;
+
+ /* In this test case there should be no parsed data */
+ g_assert(data);
+ g_assert(data->data);
+ g_message("Unknown command callback: %s",
+ g_strescape((char *)data->data, NULL));
+
+ return true;
+}
+static void tc_blueat_unknown(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Defined test case */
+ fix->msg = "\r\nunknown command and garbage\r\n";
+
+ /* start the loop for IO ops execution */
+ g_main_loop_run(fix->loop);
+
+ /* one unknown command should be found */
+ g_assert_cmpint(called, ==, 0);
+ g_assert_cmpint(unknown, ==, 1);
+}
+
+/* OK test case and callback that verifies parsed data */
+static bool tc_blueat_ok_cb(GSList *data)
+{
+ called += 1;
+ g_message("OK callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_ok(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ fix->msg = "\r\nOK\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* one ok command should be found */
+ g_assert_cmpint(called, ==, 1);
+}
+static void tc_blueat_garbagemix(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "garbage\n\r" /* garbage */
+ "\r\nunknown\r\n" /* unknown */
+ "\r\nOK\r\n" /* ok */
+ "\r\n\n\rgarbage\r\n" /* unknown */
+ "\r\rgarbageggegg\n\n" /* garbage */
+ "\r\nOK\r\n"; /* ok */
+
+ g_main_loop_run(fix->loop);
+
+ /* 2 OK and 2 unknown commands should be found */
+ g_assert_cmpint(called, ==, 2);
+ g_assert_cmpint(unknown, ==, 2);
+}
+
+/* AT+BRSF test case and callback that verifies parsed data */
+static bool tc_blueat_atbrsf_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+BRSF= callback");
+
+ g_assert(data);
+ g_assert(data->data);
+ g_assert_cmpint(*(int *)data->data, ==, 128);
+
+ return true;
+}
+static void tc_blueat_atbrsf(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "\r\nAT+BRSF=128\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* 1 callback should be called */
+ g_assert_cmpint(called, ==, 1);
+}
+
+static void tc_blueat_garbage(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "dfg5745j46\74845r4\68r\48un468n4wn\4r4hgnn\r\nOKjf\rkfhn"
+ "\r\n\nlsfg\aargarbage\r;\nsrdgfhdnOK\r\n";
+
+ /* start the loop for IO ops execution */
+ g_main_loop_run(fix->loop);
+
+ /* no commands should be found in this garbage */
+ g_assert_cmpint(called, ==, 0);
+}
+
+static bool tc_blueat_cnum_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("+CNUM: callback");
+
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ g_assert(item);
+ /* no optional parameter */
+ g_assert((char *)item->data == NULL);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert((char *)item->data != NULL);
+ g_assert_cmpint(strcmp((char *)item->data, "5551212"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert((int *)item->data != NULL);
+ g_assert_cmpint(*(int *)item->data, ==, 129);
+
+ /* no optional parameter */
+ g_assert((item = g_slist_next(item)));
+ g_assert((int *)item->data == NULL);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert((int *)item->data != NULL);
+ g_assert_cmpint(*(int *)item->data, ==, 4);
+ return true;
+}
+static void tc_blueat_cnum(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ /* Define test case */
+ /* two optional command parameters absent */
+ fix->msg = "\r\n+CNUM: ,\"5551212\",129,,4\r\n";
+
+ /* start the loop for IO ops execution */
+ g_main_loop_run(fix->loop);
+
+ /* 1 cnum command should be found */
+ g_assert_cmpint(called, ==, 1);
+}
+
+/******************************************************************************
+ * Parser configuration and command registration
+ ******************************************************************************/
+static struct blueatchat_config cfg = {
+ .buff_size = BUFFER_SIZE,
+ .cmd_prefix = "\r\n",
+ .cmd_postfix = "\r\n"
+};
+
+/* Commands that should be recognised by the parser.
+ * !IMPORTANT: unknown command callback should always be last.
+ */
+static struct blueatchat_cmd_descriptor commands[] = {
+ /* recognised command | syntax descriptor | callback */
+ {"OK", NULL, tc_blueat_ok_cb},
+ {"AT+BRSF=", "u", tc_blueat_atbrsf_cb},
+ {"+CNUM: ", "oq,q,u,ou,u*", tc_blueat_cnum_cb},
+ {NULL, "s", tc_blueat_unknown_cb},
+};
+
+/* Callback sending messages to the blueatchat module */
+static gboolean gio_out_cb(GIOChannel *gio, GIOCondition condition,
+ gpointer data)
+{
+ struct blueat_fixture *fix = data;
+
+ if (condition & G_IO_HUP)
+ g_error("Write end of pipe died!\n");
+
+ if (!fix->issent) {
+ gsize len;
+ GIOStatus ret;
+ GError *err = NULL;
+ const gchar *msg = fix->msg;
+
+ ret = g_io_channel_write_chars(gio, msg, -1, &len,
+ &err);
+ if (ret == G_IO_STATUS_ERROR)
+ g_error("Error writing: %s\n", err->message);
+ fix->issent = true;
+ return TRUE;
+ }
+
+ /* It will end up here at second io write,
+ * when message has already been written.
+ */
+ g_main_loop_quit(fix->loop);
+ return FALSE;
+}
+
+/* Make blueatchat silent */
+static void blueatchat_debug(const char *str, void *user_data)
+{
+#ifdef BLUEATCHAT_DEBUG
+ const char *prefix = user_data;
+ g_message("%s: %s", prefix, g_strescape(str, NULL));
+#endif
+}
+
+static gboolean blueat_read_cb(GIOChannel *gio, GIOCondition condition,
+ gpointer data)
+{
+ struct blueatchat_session *session = (struct blueatchat_session *)data;
+
+ if (!session)
+ g_error("Session died!\n");
+
+ g_message("Read started");
+
+ if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+ g_message("Read end of pipe died!");
+ return FALSE;
+ }
+
+ if (!blueatchat_read(session, gio)) {
+ g_message("AT chat died!");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void blueat_fix_setup(struct blueat_fixture *fix, gconstpointer tdata)
+{
+ int fd[2], ret;
+
+ called = 0;
+ unknown = 0;
+ fix->issent = false;
+ fix->loop = g_main_loop_new(NULL, FALSE);
+
+ ret = pipe(fd);
+ if (ret == -1)
+ g_error("Creating pipe failed: %s\n", strerror(errno));
+
+ fix->gio_read = g_io_channel_unix_new(fd[0]);
+ fix->gio_write = g_io_channel_unix_new(fd[1]);
+
+ g_io_channel_set_encoding(fix->gio_write, NULL, NULL);
+ g_io_channel_set_buffered(fix->gio_write, FALSE);
+
+ g_io_channel_set_encoding(fix->gio_read, NULL, NULL);
+ g_io_channel_set_buffered(fix->gio_read, FALSE);
+ g_io_channel_set_flags(fix->gio_read, G_IO_FLAG_NONBLOCK, NULL);
+
+ fix->session =
+ blueatchat_init_session(&cfg, commands, blueatchat_debug);
+
+ if (!fix->session)
+ g_error("Cannot initialize the gat_parser!\n");
+
+ if (!fix->gio_read || !fix->gio_write)
+ g_error("Cannot create new GIOChannel!\n");
+
+ g_io_add_watch_full(fix->gio_read, G_PRIORITY_DEFAULT,
+ (G_IO_IN | G_IO_ERR | G_IO_NVAL),
+ blueat_read_cb,
+ fix->session, NULL);
+ if (!g_io_add_watch(fix->gio_write, G_IO_OUT | G_IO_HUP,
+ gio_out_cb, fix))
+ g_error("Cannot add watch on GIOChannel!\n");
+}
+
+static void blueat_fix_teardown(struct blueat_fixture *fix, gconstpointer tdata)
+{
+ /* channel clean up */
+ blueatchat_cleanup_session(fix->session);
+ g_io_channel_unref(fix->gio_read);
+ g_io_channel_unref(fix->gio_write);
+ g_main_loop_unref(fix->loop);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add("/blueatchat/unknown", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_unknown,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/ok", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_ok,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/garbagemix", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_garbagemix,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atbrsf", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atbrsf,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/garbage", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_garbage,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/cnum", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_cnum,
+ blueat_fix_teardown);
+ return g_test_run();
+}
--
1.7.9.5
This patch adds unit tests for AT commands required by HFP to establish
SLC.
---
unit/test-blueatchat.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 521 insertions(+), 1 deletion(-)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
index ae10ff7..e0cd23b 100644
--- a/unit/test-blueatchat.c
+++ b/unit/test-blueatchat.c
@@ -78,6 +78,24 @@ static void tc_blueat_ok(struct blueat_fixture *fix, gconstpointer test_data)
/* one ok command should be found */
g_assert_cmpint(called, ==, 1);
}
+
+static bool tc_blueat_error_cb(GSList *data)
+{
+ called += 1;
+ g_message("ERROR callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_error(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ fix->msg = "\r\nERROR\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* one error command should be found */
+ g_assert_cmpint(called, ==, 1);
+}
+
static void tc_blueat_garbagemix(struct blueat_fixture *fix,
gconstpointer test_data)
{
@@ -120,6 +138,28 @@ static void tc_blueat_atbrsf(struct blueat_fixture *fix,
g_assert_cmpint(called, ==, 1);
}
+static bool tc_blueat_brsf_cb(GSList *data)
+{
+ called += 1;
+ g_message("+BRSF: callback");
+
+ g_assert(data);
+ g_assert(data->data);
+ g_assert_cmpint(*(int *)data->data, ==, 192);
+
+ return true;
+}
+static void tc_blueat_brsf(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "\r\n+BRSF: 192\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* 1 callback should be called */
+ g_assert_cmpint(called, ==, 1);
+}
+
static void tc_blueat_garbage(struct blueat_fixture *fix,
gconstpointer test_data)
{
@@ -178,6 +218,383 @@ static void tc_blueat_cnum(struct blueat_fixture *fix, gconstpointer test_data)
g_assert_cmpint(called, ==, 1);
}
+static bool tc_blueat_atchld_t_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CHLD=? callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_atchld_t(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CHLD=?\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atchld_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CHLD= callback");
+ g_assert(data);
+ g_assert(data->data);
+ g_assert_cmpint(*(int *)data->data, ==, 3);
+
+ return true;
+}
+static void tc_blueat_atchld(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CHLD=3\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_chld_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("+CHLD: callback");
+
+ g_assert(data);
+ item = g_slist_reverse(data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(char *)item->data, ==, 'x');
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(char *)item->data, ==, 'x');
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+ return true;
+}
+static void tc_blueat_chld(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CHLD: (0,1,1x,2,2x,3)\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atcind_r_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CIND? callback");
+
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_atcind_r(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CIND?\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atcind_t_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CIND=? callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_atcind_t(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CIND=?\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_cind1_cb(GSList *data)
+{
+ GSList *item = NULL;
+ int values[] = { 1, 1, 0, 0, 5, 0, 0 };
+ int unsigned i;
+
+ called += 1;
+ g_message("1+CIND: callback");
+
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ for (i = 0; i < sizeof(values) / sizeof(int); ++i) {
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, values[i]);
+ item = g_slist_next(item);
+ }
+ /* We declared 20 parameters in descriptor so there can be more data.
+ * but of course not in this case, because we know what we have sent.
+ */
+ if (item)
+ for (; i < 19; ++i) {
+ g_assert(item->data == NULL);
+ item = g_slist_next(item);
+ }
+ /* there should be no more data */
+ g_assert(!item);
+ return true;
+}
+static void tc_blueat_cind1(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CIND: 1,1,0,0,5,0,0\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_cind2_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("2+CIND: callback");
+ g_assert(data);
+
+ item = g_slist_reverse(data);
+
+ g_assert(item);
+ g_assert(item->data);
+ g_assert((char *)item->data);
+ g_assert_cmpint(strcmp(item->data, "Voice Mail"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "service"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "call"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "Roam"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "signal"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 5);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "callsetup"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "smsfull"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)) == NULL);
+ return true;
+}
+static void tc_blueat_cind2(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CIND: (\"Voice Mail\",(0,1)),(\"service\",(0,1)),"
+ "(\"call\",(0,1)),(\"Roam\",(0-2)),(\"signal\",(0-5)),"
+ "(\"callsetup\",(0-3)),(\"smsfull\",(0,1))\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atcmer_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("AT+CMER= callback");
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ /* Check for data "ou,ou,ou,ou*" */
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+ /* no second parameter because its optional */
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data == NULL);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+ return true;
+}
+static void tc_blueat_atcmer(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CMER=3,,0,1\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_ciev_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("+CIEV: callback");
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+ return true;
+}
+static void tc_blueat_ciev(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CIEV: 3,0\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atbac_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("AT+BAC= callback");
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ /* Check returned data "u,ou,ou,ou,ou,ou,ou" */
+
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 12);
+
+ /* According to descriptor there should be 4 nulls but not
+ * if there was no more data - of course there wont be any here
+ */
+ item = g_slist_next(item);
+ if (item) {
+ g_assert(item->data == NULL);
+ g_assert((item = g_slist_next(item))->data == NULL);
+ g_assert((item = g_slist_next(item))->data == NULL);
+ g_assert((item = g_slist_next(item))->data == NULL);
+ }
+
+ /* there should be no more data */
+ g_assert(!item);
+ return true;
+}
+static void tc_blueat_atbac(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+BAC=1,2,12\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
/******************************************************************************
* Parser configuration and command registration
******************************************************************************/
@@ -193,8 +610,72 @@ static struct blueatchat_config cfg = {
static struct blueatchat_cmd_descriptor commands[] = {
/* recognised command | syntax descriptor | callback */
{"OK", NULL, tc_blueat_ok_cb},
- {"AT+BRSF=", "u", tc_blueat_atbrsf_cb},
+ {"ERROR", NULL, tc_blueat_error_cb},
+ {"NO CARRIER", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"BUSY", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"NO ANSWER", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"DELAYED", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"BLACKLISTED", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"RING", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"ATA", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CCWA=", "ou", /*TODO*/ tc_blueat_unknown_cb},
+ {"+CCWA: ", "s,ou*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CHLD=?", NULL, tc_blueat_atchld_t_cb},
+ {"AT+CHLD=", "u", tc_blueat_atchld_cb},
+ {"+CHLD: ", "(ou,ou,ouc,ou,ouc,ou)", tc_blueat_chld_cb},
+ {"AT+CHUP", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"+CHUP", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CIND?", NULL, tc_blueat_atcind_r_cb},
+ {"AT+CIND=?", NULL, tc_blueat_atcind_t_cb},
+ {"+CIND: ", "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou,"
+ "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou", tc_blueat_cind1_cb},
+ {"+CIND: ", "(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))", tc_blueat_cind2_cb},
+ {"AT+CLCC", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"+CLCC: ", "u,u,u,u,u,os,ou*",/*TODO*/ tc_blueat_unknown_cb},
+ {"AT+COPS=", "ou,ou,os*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+COPS?", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"+COPS: ", "u,ou,os*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CMEE=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+CMEE ERROR: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CLIP=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+CLIP: ", "q,u*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CMER=", "ou,ou,ou,ou*", tc_blueat_atcmer_cb},
+ {"+CIEV: ", "u,u", tc_blueat_ciev_cb},
+ {"AT+VTS=", "c", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CNUM=?", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CNUM", NULL, /*TODO*/ tc_blueat_unknown_cb},
{"+CNUM: ", "oq,q,u,ou,u*", tc_blueat_cnum_cb},
+ {"ATD>", "q", /*TODO*/ tc_blueat_unknown_cb},
+ {"ATD", "s", /*TODO*/ tc_blueat_unknown_cb},
+ /* Bluetooth cpecific */
+ {"AT+BIA=", "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou,"
+ "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou", tc_blueat_unknown_cb},
+ {"AT+BINP=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BINP: ", "q*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BLDN=", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BVRA=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BVRA: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BRSF=", "u", tc_blueat_atbrsf_cb},
+ {"+BRSF: ", "u", tc_blueat_brsf_cb},
+ {"AT+NREC=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+VGM=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+VGM:", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+VGS=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+VGS: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BSIR: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BTRH=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BTRH: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BCC=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BCS=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BCS: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BAC=", "u,ou,ou,ou,ou,ou,ou", tc_blueat_atbac_cb},
+ /* used lastly if no other command matches */
{NULL, "s", tc_blueat_unknown_cb},
};
@@ -327,8 +808,47 @@ int main(int argc, char **argv)
g_test_add("/blueatchat/garbage", struct blueat_fixture, 0,
blueat_fix_setup, tc_blueat_garbage,
blueat_fix_teardown);
+ g_test_add("/blueatchat/error", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_error,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/brsf", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_brsf,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atchld_test", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atchld_t,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atchld", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atchld,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/chld", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_chld,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/brsf", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_brsf,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atcind_request", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atcind_r,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atcind_test", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atcind_t,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atcmer", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atcmer,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/ciev", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_ciev,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atbac", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atbac,
+ blueat_fix_teardown);
g_test_add("/blueatchat/cnum", struct blueat_fixture, 0,
blueat_fix_setup, tc_blueat_cnum,
blueat_fix_teardown);
+ g_test_add("/blueatchat/cind_1", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_cind1,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/cind_2", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_cind2,
+ blueat_fix_teardown);
return g_test_run();
}
--
1.7.9.5
This patch adds registered commands recognition and callback calls.
---
src/shared/blueatchat.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index c7eee4a..e9b3c9d 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -30,8 +30,16 @@
#include "blueatchat.h"
#include "util.h"
+/* uncomment for extended debug messages */
+/* #define BLUEATCHAT_VERBOSE */
+
+#ifdef BLUEATCHAT_VERBOSE
#define BLUEATCHAT_DEBUG(session, ...) (util_debug(session->debug_callback, \
(void *)__func__, __VA_ARGS__))
+#else
+#define BLUEATCHAT_DEBUG(...) {}
+#endif
+
struct blueatchat_session {
struct blueatchat_config *config;
struct blueatchat_cmd_descriptor *cmd_list;
@@ -40,6 +48,23 @@ struct blueatchat_session {
blueatchat_debug_func_t debug_callback;
};
+#ifdef BLUEATCHAT_VERBOSE
+static void blueatchat_buffer_dump(struct blueatchat_session *session,
+ unsigned int amount)
+{
+ unsigned int i;
+ struct circular_buffer *buffer = session->buffer;
+
+ BLUEATCHAT_DEBUG(session, "BEGIN");
+ for (i = 0; i < cbuffer_get_length(buffer); ++i) {
+ if (i == amount)
+ break;
+ BLUEATCHAT_DEBUG(session, "%c", *cbuffer_peek_tail(buffer, i));
+ }
+ BLUEATCHAT_DEBUG(session, "END");
+}
+#endif
+
/*
* returns offset:
* > 0 - if complete command found, which ends at offset
@@ -99,13 +124,45 @@ static int blueatchat_get_complete_cmd_end(struct blueatchat_session *session)
return -ENODATA;
}
+static int blueatchat_parse_command(struct blueatchat_session *session,
+ const int amount)
+{
+ struct blueatchat_cmd_descriptor *item;
+
+ BLUEATCHAT_DEBUG(session, "Starting");
+#ifdef BLUEATCHAT_VERBOSE
+ blueatchat_buffer_dump(session, amount);
+#endif
+
+ for (item = session->cmd_list; item->cmd != NULL; ++item) {
+ if (cbuffer_starts_with_seq(session->buffer, item->cmd,
+ strlen(session->config->cmd_prefix))) {
+ /* TODO: process buffered data
+ *-> peek a char to choose appropriate parser routine
+ * or use syntax descriptor string?
+ *-> fill in sessions GSList* data with parsed data
+ */
+ item->notify(session->data);
+ cbuffer_discard(session->buffer, amount);
+ return 0;
+ }
+ }
+ /* no matching command */
+ /* TODO: push unknown or vendor message string to GSList and notify */
+ item->notify(session->data);
+ cbuffer_discard(session->buffer, amount);
+ return 1;
+}
+
static void process_bufferred_data(struct blueatchat_session *session)
{
struct circular_buffer *buffer = session->buffer;
int offset;
+ /* Stop processing if buffer empty or offset < 0 (incomplete data) */
while ((offset = blueatchat_get_complete_cmd_end(session)) >= 0) {
- /* TODO: parse the data */
+ if (offset)
+ blueatchat_parse_command(session, offset);
if (cbuffer_is_empty(buffer))
return;
--
1.7.9.5
This is an initial attempt to parse data passed with the commands.
Parser uses syntax descriptor to find out what kind of data it should
expect and parse. Parsed data are being pushed to the list and sent
using proper callback.
---
src/shared/blueatchat.c | 182 ++++++++++++++++++++++++++++++++++++++++++++---
src/shared/blueatchat.h | 30 ++++++++
src/shared/dparser.c | 45 ++++++++++++
src/shared/dparser.h | 24 +++++++
4 files changed, 273 insertions(+), 8 deletions(-)
create mode 100644 src/shared/dparser.c
create mode 100644 src/shared/dparser.h
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index e9b3c9d..6a12b22 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -29,6 +29,7 @@
#include "cbuffer.h"
#include "blueatchat.h"
#include "util.h"
+#include "dparser.h"
/* uncomment for extended debug messages */
/* #define BLUEATCHAT_VERBOSE */
@@ -124,33 +125,196 @@ static int blueatchat_get_complete_cmd_end(struct blueatchat_session *session)
return -ENODATA;
}
+static char *blueatchat_buffdup(struct circular_buffer *buffer,
+ unsigned int amount)
+{
+ /*
+ * Returns a pointer to newly allocated string containing 'amount'
+ * of data taken from the buffer.
+ */
+ unsigned int i;
+ char *msg = g_try_new(char, (amount + 1));
+
+ for (i = 0; i < amount; ++i)
+ msg[i] = *cbuffer_peek_tail(buffer, i);
+ msg[amount] = '\0';
+
+ return msg;
+}
+
+/*
+ * This function returns:
+ * < 0 for error,
+ * = 0 for success,
+ * > 0 for redundant data left in the buffer
+ */
+static int blueatchat_parse_data(char *descriptor, int *descr_off,
+ struct blueatchat_session *session,
+ int *data_off, int amount)
+{
+ unsigned int i;
+ int begin_offset = *data_off;
+ bool optional = false;
+ void *newdata;
+ unsigned int descrlen = strlen(descriptor);
+
+ for (i = *descr_off; i < descrlen; ++i) {
+ char ch = descriptor[i];
+ newdata = NULL;
+
+ /*
+ * Safety check if no more data in this command and descriptor
+ * indicates that more optional data may follow.
+ */
+ if (*data_off - begin_offset == amount)
+ return 0;
+
+ switch (ch) {
+ case '(': /* compound value start */
+ BLUEATCHAT_DEBUG(session, "(");
+ break;
+ case 'o': /* optional value */
+ BLUEATCHAT_DEBUG(session, "o");
+
+ /* Parse following data with proper flag set */
+ optional = true;
+ continue;
+ case 'u': /* integer value */
+ BLUEATCHAT_DEBUG(session, "u");
+
+ newdata = dparse_uint(session->buffer, data_off);
+ if (!newdata && !optional)
+ return -ENODATA;
+
+ session->data = g_slist_prepend(session->data, newdata);
+ break;
+ case 'q': /* quoted string */
+ BLUEATCHAT_DEBUG(session, "q");
+ break;
+ case 's': /* TODO: unquoted string */
+ BLUEATCHAT_DEBUG(session, "s");
+ break;
+ case ')': /* compound value end */
+ BLUEATCHAT_DEBUG(session, ")");
+ break;
+ case ',': /* value separator */
+ BLUEATCHAT_DEBUG(session, ",");
+ break;
+ case '*':
+ /*
+ * Do nothing as `*` should only exist at the very end
+ * of command descriptor and in next loop iteration
+ * loop will stop anyway.
+ */
+ break;
+ default:
+ BLUEATCHAT_DEBUG(session, "unknown descriptor");
+ break;
+ }
+ }
+ BLUEATCHAT_DEBUG(session, "parsed %i/%i bytes",
+ *data_off - begin_offset, amount);
+
+ /*
+ * If no data was parsed try different match returning error
+ * return 1 if not all data has been parsed but data partially matched.
+ */
+ if (*data_off - begin_offset == 0)
+ return -ENODATA;
+ else if (*data_off - begin_offset < amount)
+ return 1;
+
+ return 0;
+}
+
static int blueatchat_parse_command(struct blueatchat_session *session,
const int amount)
{
struct blueatchat_cmd_descriptor *item;
+ char *prefix = session->config->cmd_prefix;
+ char *postfix = session->config->cmd_postfix;
+ int data_off = 0;
+ int descr_off = 0;
BLUEATCHAT_DEBUG(session, "Starting");
#ifdef BLUEATCHAT_VERBOSE
blueatchat_buffer_dump(session, amount);
#endif
+ /* memory leaks shouldn't happen */
+ g_assert(!session->data);
+
for (item = session->cmd_list; item->cmd != NULL; ++item) {
+ descr_off = 0;
+ data_off = 0;
if (cbuffer_starts_with_seq(session->buffer, item->cmd,
- strlen(session->config->cmd_prefix))) {
- /* TODO: process buffered data
- *-> peek a char to choose appropriate parser routine
- * or use syntax descriptor string?
- *-> fill in sessions GSList* data with parsed data
+ strlen(prefix))) {
+ /*
+ * Parse only if data is expected, and do not populate
+ * the list if something was parsed wrong. Instead
+ * calling callback with wrong or without data treat
+ * this command as vendor/unknown/not matching one.
*/
+ if (item->cmd_descr) {
+ int res;
+ /* start parsing right where data is */
+ data_off = strlen(prefix) + strlen(item->cmd);
+
+ BLUEATCHAT_DEBUG(session,
+ "Checking for data: %s",
+ item->cmd_descr);
+
+ res = blueatchat_parse_data(item->cmd_descr,
+ &descr_off, session,
+ &data_off,
+ amount - (data_off +
+ strlen(postfix)));
+ if (res < 0) {
+ /*
+ * Error may be triggered in the middle
+ * of parsing when some data has already
+ * been pushed to the data list.
+ * Clean that up.
+ */
+ g_slist_free_full(session->data,
+ g_free);
+ session->data = NULL;
+ continue;
+ } else if (res > 0) {
+ BLUEATCHAT_DEBUG(session,
+ "redundant data found");
+ }
+ } else {
+ /*
+ * Check if there is really no data, like
+ * descriptor indicates. If there is, this
+ * may be a different command.
+ */
+ if ((strlen(session->config->cmd_prefix) +
+ strlen(postfix) + strlen(item->cmd)) !=
+ (unsigned int)amount)
+ continue;
+ }
+
item->notify(session->data);
+
+ /* Free all data after notification returns */
+ g_slist_free_full(session->data, g_free);
+ session->data = NULL;
cbuffer_discard(session->buffer, amount);
return 0;
}
}
- /* no matching command */
- /* TODO: push unknown or vendor message string to GSList and notify */
- item->notify(session->data);
+ /* Unknown or vendor command - push it to upper layers */
+ session->data = g_slist_prepend(session->data,
+ blueatchat_buffdup(session->buffer,
+ amount));
cbuffer_discard(session->buffer, amount);
+ item->notify(session->data);
+
+ /* Free data after notification returns */
+ g_slist_free_full(session->data, g_free);
+ session->data = NULL;
return 1;
}
@@ -234,6 +398,7 @@ struct blueatchat_session *blueatchat_init_session(
session->cmd_list = cmd_list;
session->config = config;
session->debug_callback = debug_fun;
+ session->data = NULL;
session->buffer = cbuffer_init(session->config->buff_size);
if (!session->buffer) {
@@ -249,6 +414,7 @@ void blueatchat_cleanup_session(struct blueatchat_session *session)
session->config = NULL;
session->debug_callback = NULL;
+ g_slist_free_full(session->data, g_free);
cbuffer_free(session->buffer);
g_free(session);
session = NULL;
diff --git a/src/shared/blueatchat.h b/src/shared/blueatchat.h
index 9c1ca1b..75d055c 100644
--- a/src/shared/blueatchat.h
+++ b/src/shared/blueatchat.h
@@ -21,6 +21,36 @@
*
*/
+/*
+ * cmd_descr - string describing commands content
+ *
+ * It uses the following characters to describe expected data units:
+ * , - unit/value separator
+ * ( - compound value section begin
+ * ) - compound value section end
+ * o - optional value
+ * u - unsigned integer
+ * i - integer
+ * * - unsignificant - its should be used for ignoring the rest of
+ * data in this command - can only be used at the very end
+ * c - single unquoted char, like in "AT+VTS=A"
+ * s - string
+ * q - quoted string
+ * - - indicates value range like in "+CIND: ("callsetup",(0-3))"
+ *
+ * Example:
+ * (oi,q) - compund value with optional integer and a mandatory string.
+ *
+ * Combinations should be possible:
+ * (oq,oq),(oq,i),u,oq,oi
+ * which should match string like: `(,),("Test message",1023),256,,1`
+ *
+ * Nesting should be possible:
+ * (u,q,(oq,(q,i),i,u,q),i)
+ *
+ * TODO: add more descriptors if needed.
+ */
+
#include <stdbool.h>
typedef void (*blueatchat_debug_func_t)(const char *str, void *user_data);
diff --git a/src/shared/dparser.c b/src/shared/dparser.c
new file mode 100644
index 0000000..7b7a2a2
--- /dev/null
+++ b/src/shared/dparser.c
@@ -0,0 +1,45 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <ctype.h>
+#include <glib.h>
+
+#include "cbuffer.h"
+#include "dparser.h"
+
+unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset)
+{
+ unsigned int *ret;
+ const char *ch = cbuffer_peek_tail(buffer, *offset);
+
+ if (!isdigit(*ch))
+ return NULL;
+
+ ret = g_new0(unsigned int, 1);
+
+ while (isdigit(*ch)) {
+ *ret = (*ret * 10) + (*ch - '0');
+ ch = cbuffer_peek_tail(buffer, ++(*offset));
+ }
+ return ret;
+}
diff --git a/src/shared/dparser.h b/src/shared/dparser.h
new file mode 100644
index 0000000..7c0bf58
--- /dev/null
+++ b/src/shared/dparser.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset);
--
1.7.9.5
---
unit/test-cbuffer.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index d51ce64..cb99506 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -462,6 +462,59 @@ static void cbt_get_length(struct cbt_fixture *fix, gconstpointer test_data)
cbuffer_get_length(fix->cbuff));
}
+static void cbt_discard(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ /* head */
+ g_assert_cmpint(3, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+ g_assert_cmpint(7, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_discard(fix->cbuff, 2);
+ g_assert_cmpint(5, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('c', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* unsuccessful discard */
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 2), ==, 0);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 1), ==, 1);
+ g_assert_cmpint(6, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('x', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 2), ==, 2);
+ g_assert_cmpint(4, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('v', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 3), ==, 3);
+ g_assert_cmpint(1, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('m', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 4), ==, 1);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -491,5 +544,7 @@ int main(int argc, char **argv)
cbt_check_for, cbt_fix_teardown);
g_test_add("/cbuffer/get_length", struct cbt_fixture, 0, cbt_fix_setup,
cbt_get_length, cbt_fix_teardown);
+ g_test_add("/cbuffer/discard", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_discard, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
This patch adds checking for AT command prefix and postfix to determine
data completeness.
---
src/shared/blueatchat.c | 109 ++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 94 insertions(+), 15 deletions(-)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index a32a7bf..c7eee4a 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -22,6 +22,8 @@
*/
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
#include <glib.h>
#include "cbuffer.h"
@@ -38,6 +40,77 @@ struct blueatchat_session {
blueatchat_debug_func_t debug_callback;
};
+/*
+ * returns offset:
+ * > 0 - if complete command found, which ends at offset
+ * < 0 - if no complete command found wait for more
+ * = 0 - some garbage was consumed check again
+ */
+static int blueatchat_get_complete_cmd_end(struct blueatchat_session *session)
+{
+ struct blueatchat_config *config = session->config;
+ struct circular_buffer *buffer = session->buffer;
+ int offset;
+ unsigned int prelen = strlen(config->cmd_prefix);
+
+ /* Check for command prefix */
+ offset = cbuffer_seek_tail_for_seq(buffer, config->cmd_prefix, 0);
+
+ /* Not enough data if only prefix in the buffer */
+ if (cbuffer_get_length(session->buffer) == prelen)
+ return -ENODATA;
+
+ /* No prefix - drain the buffer */
+ if (offset < 0) {
+ cbuffer_drain(buffer);
+ return -ENODATA;
+ }
+
+ /* Discard data before the prefix */
+ if (offset) {
+ cbuffer_discard(session->buffer, offset);
+ return 0;
+ }
+
+ /* check for postfix */
+ offset = cbuffer_seek_tail_for_seq(buffer, config->cmd_postfix, prelen);
+
+ /* If prefix != postfix check for multiple prefixing before postfix */
+ if (strncmp(config->cmd_prefix, config->cmd_postfix, prelen)) {
+ int off = cbuffer_seek_tail_for_seq(buffer, config->cmd_prefix,
+ prelen);
+
+ /* If second prefix exist before postfix consume */
+ if (off > 0 && off < offset) {
+ cbuffer_discard(session->buffer, off);
+ return 0;
+ }
+ }
+
+ /* If <fix><fix> consume one fix and check again later */
+ if (offset == (int)prelen) {
+ cbuffer_discard(session->buffer, offset);
+ return 0;
+ } else if (offset > 0) {
+ return offset + strlen(config->cmd_postfix);
+ }
+
+ /* If offset == 0 no tail found wait for more */
+ return -ENODATA;
+}
+
+static void process_bufferred_data(struct blueatchat_session *session)
+{
+ struct circular_buffer *buffer = session->buffer;
+ int offset;
+
+ while ((offset = blueatchat_get_complete_cmd_end(session)) >= 0) {
+ /* TODO: parse the data */
+
+ if (cbuffer_is_empty(buffer))
+ return;
+ }
+}
bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
{
@@ -48,10 +121,24 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
gsize len;
gsize i;
- /* read all data into the buffer */
+ /* Fill the buffer in chunks if needed to drain the IO */
do {
freechunk = cbuffer_get_free_chunk_size(session->buffer);
+ /* Free some space by processing the buffer if its full */
+ if (!freechunk) {
+ process_bufferred_data(session);
+
+ /* If couldn't process anything discard all data */
+ if (!cbuffer_get_free_chunk_size(session->buffer))
+ cbuffer_drain(session->buffer);
+ continue;
+ }
+
+ /*
+ * If there is no space after processing or draining the buffer
+ * we have a serious buffer error.
+ */
msg = cbuffer_get_free_cell(session->buffer);
if (!msg) {
BLUEATCHAT_DEBUG(session, "Buffer malfunction!");
@@ -65,25 +152,17 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
return false;
}
- if (len != 0)
- BLUEATCHAT_DEBUG(session,
- "Blueatchat red %u bytes: %.*s",
- (unsigned int)len, (unsigned int)len,
- msg);
-
for (i = 0; i < len; ++i)
cbuffer_consume_next_cell(session->buffer);
+
+ freechunk = cbuffer_get_free_chunk_size(session->buffer);
} while ((len != 0) && (ret != G_IO_STATUS_EOF));
- /* TODO: process buffered data
- * -> check for complete command (check for head, tail)
- * -> start consuming buffered data and parsing it
- * -> iterate through session->cmd_list and match commands
- * -> peek a char to choose appropriate parser routine or use
- * syntax descriptor string?
- * -> fill in sessions GSList* data with parsed data
- * -> call '*(session->cmd_list[cmd_index].notify)(session->data)
+ /*
+ * Now check for data completeness (command prefix and postfix).
+ * Than parse it and consume parsed data from the buffer.
*/
+ process_bufferred_data(session);
return true;
}
--
1.7.9.5
This patch adds discarding desired amount of oldest data from the buffer.
---
src/shared/cbuffer.c | 9 +++++++++
src/shared/cbuffer.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index 8d85059..a5ed7c5 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -124,6 +124,15 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff)
return cbuff->size;
}
+int cbuffer_discard(struct circular_buffer *cbuff, unsigned int amount)
+{
+ unsigned int len = cbuffer_get_length(cbuff);
+
+ amount = amount > len ? len : amount;
+ cbuff->start = (cbuff->start + amount) & (2 * cbuff->size - 1);
+ return amount;
+}
+
/* Manual mode gives direct access to buffers memory space */
char *cbuffer_get_free_cell(struct circular_buffer *cbuff)
{
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index ff29d22..759d8a7 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -27,6 +27,7 @@ struct circular_buffer *cbuffer_init(unsigned int size);
void cbuffer_free(struct circular_buffer *cbuff);
int cbuffer_write(struct circular_buffer *cbuff, char source);
int cbuffer_read(struct circular_buffer *cbuff, char *dest);
+int cbuffer_discard(struct circular_buffer *cbuff, unsigned int amount);
void cbuffer_drain(struct circular_buffer *cbuff);
int cbuffer_is_full(struct circular_buffer *cbuff);
int cbuffer_is_empty(struct circular_buffer *cbuff);
--
1.7.9.5
This patch makes blueatchat use circular buffer for storing data read
from the glib's IO channel.
---
src/shared/blueatchat.c | 49 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 38 insertions(+), 11 deletions(-)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index be26bad..a32a7bf 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <glib.h>
+#include "cbuffer.h"
#include "blueatchat.h"
#include "util.h"
@@ -32,9 +33,9 @@
struct blueatchat_session {
struct blueatchat_config *config;
struct blueatchat_cmd_descriptor *cmd_list;
+ struct circular_buffer *buffer;
GSList *data; /* stores parsed data */
blueatchat_debug_func_t debug_callback;
- /* TODO: Add ring buffer. */
};
@@ -42,16 +43,37 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
{
GIOStatus ret = G_IO_STATUS_EOF;
GError *err = NULL;
- gchar *msg;
+ gchar *msg = NULL;
+ int freechunk;
gsize len;
+ gsize i;
- /* TODO: use ring buffer inside the session object */
- ret = g_io_channel_read_line(gio, &msg, &len, NULL, &err);
- if (ret == G_IO_STATUS_ERROR)
- BLUEATCHAT_DEBUG(session, "Error reading: %s\n", err->message);
- else
- BLUEATCHAT_DEBUG(session, "Blueatchat red %u bytes: %s",
- (unsigned int)len, msg);
+ /* read all data into the buffer */
+ do {
+ freechunk = cbuffer_get_free_chunk_size(session->buffer);
+
+ msg = cbuffer_get_free_cell(session->buffer);
+ if (!msg) {
+ BLUEATCHAT_DEBUG(session, "Buffer malfunction!");
+ return false;
+ }
+
+ ret = g_io_channel_read_chars(gio, msg, freechunk, &len, &err);
+ if (ret == G_IO_STATUS_ERROR) {
+ BLUEATCHAT_DEBUG(session, "Error reading: %s\n",
+ err->message);
+ return false;
+ }
+
+ if (len != 0)
+ BLUEATCHAT_DEBUG(session,
+ "Blueatchat red %u bytes: %.*s",
+ (unsigned int)len, (unsigned int)len,
+ msg);
+
+ for (i = 0; i < len; ++i)
+ cbuffer_consume_next_cell(session->buffer);
+ } while ((len != 0) && (ret != G_IO_STATUS_EOF));
/* TODO: process buffered data
* -> check for complete command (check for head, tail)
@@ -62,7 +84,6 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
* -> fill in sessions GSList* data with parsed data
* -> call '*(session->cmd_list[cmd_index].notify)(session->data)
*/
- g_free(msg);
return true;
}
@@ -77,7 +98,12 @@ struct blueatchat_session *blueatchat_init_session(
session->cmd_list = cmd_list;
session->config = config;
session->debug_callback = debug_fun;
- /* TODO: initialize ring buffer */
+
+ session->buffer = cbuffer_init(session->config->buff_size);
+ if (!session->buffer) {
+ g_free(session);
+ return NULL;
+ }
return session;
}
@@ -87,6 +113,7 @@ void blueatchat_cleanup_session(struct blueatchat_session *session)
session->config = NULL;
session->debug_callback = NULL;
+ cbuffer_free(session->buffer);
g_free(session);
session = NULL;
}
--
1.7.9.5
This patch adds AT chat module skeleton used for sending and receiving
AT commands. This patch contains only part required for reading.
---
src/shared/blueatchat.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/blueatchat.h | 50 ++++++++++++++++++++++++++
2 files changed, 142 insertions(+)
create mode 100644 src/shared/blueatchat.c
create mode 100644 src/shared/blueatchat.h
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
new file mode 100644
index 0000000..be26bad
--- /dev/null
+++ b/src/shared/blueatchat.c
@@ -0,0 +1,92 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+
+#include "blueatchat.h"
+#include "util.h"
+
+#define BLUEATCHAT_DEBUG(session, ...) (util_debug(session->debug_callback, \
+ (void *)__func__, __VA_ARGS__))
+struct blueatchat_session {
+ struct blueatchat_config *config;
+ struct blueatchat_cmd_descriptor *cmd_list;
+ GSList *data; /* stores parsed data */
+ blueatchat_debug_func_t debug_callback;
+ /* TODO: Add ring buffer. */
+};
+
+
+bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
+{
+ GIOStatus ret = G_IO_STATUS_EOF;
+ GError *err = NULL;
+ gchar *msg;
+ gsize len;
+
+ /* TODO: use ring buffer inside the session object */
+ ret = g_io_channel_read_line(gio, &msg, &len, NULL, &err);
+ if (ret == G_IO_STATUS_ERROR)
+ BLUEATCHAT_DEBUG(session, "Error reading: %s\n", err->message);
+ else
+ BLUEATCHAT_DEBUG(session, "Blueatchat red %u bytes: %s",
+ (unsigned int)len, msg);
+
+ /* TODO: process buffered data
+ * -> check for complete command (check for head, tail)
+ * -> start consuming buffered data and parsing it
+ * -> iterate through session->cmd_list and match commands
+ * -> peek a char to choose appropriate parser routine or use
+ * syntax descriptor string?
+ * -> fill in sessions GSList* data with parsed data
+ * -> call '*(session->cmd_list[cmd_index].notify)(session->data)
+ */
+ g_free(msg);
+ return true;
+}
+
+struct blueatchat_session *blueatchat_init_session(
+ struct blueatchat_config *config,
+ struct blueatchat_cmd_descriptor *cmd_list,
+ blueatchat_debug_func_t debug_fun)
+{
+ struct blueatchat_session *session =
+ g_new(struct blueatchat_session, 1);
+
+ session->cmd_list = cmd_list;
+ session->config = config;
+ session->debug_callback = debug_fun;
+ /* TODO: initialize ring buffer */
+ return session;
+}
+
+void blueatchat_cleanup_session(struct blueatchat_session *session)
+{
+ session->cmd_list = NULL;
+ session->config = NULL;
+ session->debug_callback = NULL;
+
+ g_free(session);
+ session = NULL;
+}
diff --git a/src/shared/blueatchat.h b/src/shared/blueatchat.h
new file mode 100644
index 0000000..9c1ca1b
--- /dev/null
+++ b/src/shared/blueatchat.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdbool.h>
+
+typedef void (*blueatchat_debug_func_t)(const char *str, void *user_data);
+typedef bool (*blueatchat_notify_cb)(GSList *);
+
+/* command/response descriptor */
+struct blueatchat_cmd_descriptor {
+ char *cmd; /* cmd/response string to match */
+ char *cmd_descr; /* data descriptor - expected data to be parsed */
+ blueatchat_notify_cb notify; /* callback for the given command */
+};
+
+/* simple config */
+struct blueatchat_config {
+ size_t buff_size;
+ char *cmd_prefix;
+ char *cmd_postfix;
+};
+
+struct blueatchat_session;
+
+struct blueatchat_session *blueatchat_init_session(
+ struct blueatchat_config *config,
+ struct blueatchat_cmd_descriptor *cmd_list,
+ blueatchat_debug_func_t debug_fun);
+void blueatchat_cleanup_session(struct blueatchat_session *session);
+bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio);
--
1.7.9.5
This patch adds unit tests for cbuffer's memory direct access
finctionality and browsing methods.
---
unit/test-cbuffer.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 121 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index 5a3482d..d5507a1 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -200,6 +200,123 @@ static void cbt_manual(struct cbt_fixture *fix, gconstpointer test_data)
g_assert(cbuffer_is_empty(fix->cbuff));
}
+static void cbt_peek(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 1;
+ /* tail */
+ cbuffer_write(fix->cbuff, 'a');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'd');
+ /* head */
+
+ /* peek from head of "abcd" string */
+ g_assert_cmpint('a', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+ g_assert_cmpint('b', ==, *cbuffer_peek_tail(fix->cbuff, 1));
+ g_assert_cmpint('c', ==, *cbuffer_peek_tail(fix->cbuff, 2));
+ g_assert_cmpint('d', ==, *cbuffer_peek_tail(fix->cbuff, 3));
+ /* no boundary check here - will wrap around if index > buffer size */
+ /* g_assert_cmpint('\0', ==, *cbuffer_peek_head(fix->cbuff, 4)); */
+
+ /* peek from tail of "abcd" string */
+ g_assert_cmpint('d', ==, *cbuffer_peek_head(fix->cbuff, 0));
+ g_assert_cmpint('c', ==, *cbuffer_peek_head(fix->cbuff, 1));
+ g_assert_cmpint('b', ==, *cbuffer_peek_head(fix->cbuff, 2));
+ g_assert_cmpint('a', ==, *cbuffer_peek_head(fix->cbuff, 3));
+ /* no boundary check here - will wrap around if index > buffer size */
+ /* g_assert_cmpint('\0', ==, *cbuffer_peek_tail(fix->cbuff, 4)); */
+
+ /* now read peeked chars in the right sequence */
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('b', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('c', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('d', ==, ch);
+ ch = 1;
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(1, ==, ch);
+ g_assert(cbuffer_is_empty(fix->cbuff));
+}
+
+static void cbt_get_freechunk(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 0;
+ unsigned int i;
+
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff));
+
+ /* fill the whole buffer */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff),
+ ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff), ==, 0);
+
+ /* read more than a half of the data */
+ for (i = 0; i <= cbuffer_get_size(fix->cbuff) / 2; ++i) {
+ g_assert(!cbuffer_is_empty(fix->cbuff));
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ }
+ g_assert_cmpint((cbuffer_get_size(fix->cbuff) / 2) - 1, ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* fill it again leaving 2 empty spaces */
+ for (i = 0; i < (cbuffer_get_size(fix->cbuff) / 2) - 1; ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'b');
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff) - 2, ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* s- buffers start offset
+ * e- buffers end offset
+ * n- place where new data will be appended
+ */
+ /* [XXXX|n...|XXXXX]
+ * e s
+ */
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff), ==, 2);
+
+ /* [|n.............]
+ * s/e
+ */
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff));
+
+ /* [|X|n...........]
+ * s e
+ */
+ cbuffer_drain(fix->cbuff);
+ cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff) - 1);
+
+ /* [n...........|X|]
+ * e s
+ */
+ cbuffer_drain(fix->cbuff);
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+ /* read all but one */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff) - 1; ++i) {
+ g_assert(!cbuffer_is_empty(fix->cbuff));
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ }
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff) - 1);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -217,5 +334,9 @@ int main(int argc, char **argv)
cbt_drain, cbt_fix_teardown);
g_test_add("/cbuffer/manual", struct cbt_fixture, 0, cbt_fix_setup,
cbt_manual, cbt_fix_teardown);
+ g_test_add("/cbuffer/peek", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_peek, cbt_fix_teardown);
+ g_test_add("/cbuffer/get_freechunk", struct cbt_fixture, 0,
+ cbt_fix_setup, cbt_get_freechunk, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
---
unit/test-cbuffer.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 153 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index d5507a1..d51ce64 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -317,6 +317,151 @@ static void cbt_get_freechunk(struct cbt_fixture *fix, gconstpointer test_data)
==, cbuffer_get_size(fix->cbuff) - 1);
}
+static void cbt_seek_head(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int found = -128;
+
+ /* tail */
+ cbuffer_write(fix->cbuff, '<');
+ cbuffer_write(fix->cbuff, '.');
+ cbuffer_write(fix->cbuff, '>');
+ cbuffer_write(fix->cbuff, 'a');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'c');
+ /* head */
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "<.>", 0);
+ g_assert_cmpint(found, ==, 0);
+
+ g_assert_cmpint('<', ==, *cbuffer_peek_tail(fix->cbuff, found));
+ g_assert_cmpint('.', ==, *cbuffer_peek_tail(fix->cbuff, found + 1));
+ g_assert_cmpint('>', ==, *cbuffer_peek_tail(fix->cbuff, found + 2));
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, ">.>", 0);
+ g_assert_cmpint(found, <, 0);
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "<>", 0);
+ g_assert_cmpint(found, <, 0);
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "a", 1);
+ g_assert_cmpint(found, ==, 3);
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "b", 2);
+ g_assert_cmpint(found, ==, 4);
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "c", 3);
+ g_assert_cmpint(found, ==, 5);
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "abc", 0);
+ g_assert(found == 3);
+ g_assert_cmpint('a', ==, *cbuffer_peek_tail(fix->cbuff, found));
+ g_assert_cmpint('b', ==, *cbuffer_peek_tail(fix->cbuff, found + 1));
+ g_assert_cmpint('c', ==, *cbuffer_peek_tail(fix->cbuff, found + 2));
+}
+
+static void cbt_seek_tail(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int found = -1;
+
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+
+ /* starts seeking for the exact sequence 3 bytes from the end */
+ found = cbuffer_seek_tail_for_seq(fix->cbuff, "bnm", 3);
+ /* should return its distance from the begining */
+ g_assert_cmpint(found, ==, 4);
+ found = cbuffer_seek_tail_for_seq(fix->cbuff, "zxc", 0);
+ g_assert_cmpint(found, ==, 0);
+}
+
+static void cbt_check_for(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+
+ g_assert_cmpint(0, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "zxcvbm", 0));
+ g_assert_cmpint(1, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "zxcvbn", 0));
+ g_assert_cmpint(0, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "cvb", 1));
+ g_assert_cmpint(1, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "cvb", 2));
+ g_assert_cmpint(0, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "cvb", 3));
+ g_assert_cmpint(1, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "nm", 5));
+}
+
+static void cbt_get_length(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 0;
+ int unsigned i;
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ /* head */
+ g_assert_cmpint(3, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+ g_assert_cmpint(7, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_read(fix->cbuff, &ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(5, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* test for buffer wrapping around */
+
+ /* fill the whole buffer */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff), ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* read half of the data */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff) / 2; ++i) {
+ g_assert(!cbuffer_is_empty(fix->cbuff));
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff) / 2, ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* fill it again */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff) / 2; ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+
+ /* after refilling should be full */
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff), ==,
+ cbuffer_get_length(fix->cbuff));
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -338,5 +483,13 @@ int main(int argc, char **argv)
cbt_peek, cbt_fix_teardown);
g_test_add("/cbuffer/get_freechunk", struct cbt_fixture, 0,
cbt_fix_setup, cbt_get_freechunk, cbt_fix_teardown);
+ g_test_add("/cbuffer/seek_head", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_seek_head, cbt_fix_teardown);
+ g_test_add("/cbuffer/seek_tail", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_seek_tail, cbt_fix_teardown);
+ g_test_add("/cbuffer/check_for", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_check_for, cbt_fix_teardown);
+ g_test_add("/cbuffer/get_length", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_get_length, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
This patch adds seek and check routines needed for some sanity checks
by the AT chat module. It will be needed for the boundary checks.
---
src/shared/cbuffer.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/cbuffer.h | 6 ++++++
2 files changed, 60 insertions(+)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index c1e5b08..8d85059 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -22,6 +22,7 @@
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include "cbuffer.h"
@@ -156,6 +157,59 @@ const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n)
return &(cbuff->data[idx & (cbuff->size - 1)]);
}
+/* Checks for sequence in the given position */
+int cbuffer_starts_with_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset)
+{
+ int i;
+ int seqlen = strlen(seq);
+ const char *ch;
+
+ ch = NULL;
+ for (i = 0; i < seqlen; ++i) {
+ ch = cbuffer_peek_tail(cbuff, offset + i);
+ if (seq[i] != *ch)
+ return 0;
+ }
+ return 1;
+}
+
+/* Seeks for sequence in the circular buffer */
+int cbuffer_seek_head_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset)
+{
+ int i;
+ const char *pos;
+ const char *end;
+
+ end = cbuffer_peek_tail(cbuff, 0);
+ i = offset;
+ for (pos = cbuffer_peek_head(cbuff, i); pos != end; ++i) {
+ pos = cbuffer_peek_head(cbuff, i);
+ if (cbuffer_starts_with_seq(cbuff, seq, i))
+ return i;
+ }
+ return -ENODATA;
+}
+
+/* Seeks for sequence in the circular buffer */
+int cbuffer_seek_tail_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset)
+{
+ int i;
+ const char *pos;
+ const char *end;
+
+ end = cbuffer_peek_head(cbuff, 0);
+ i = offset;
+ for (pos = cbuffer_peek_tail(cbuff, i); pos != end; ++i) {
+ pos = cbuffer_peek_tail(cbuff, i);
+ if (cbuffer_starts_with_seq(cbuff, seq, i))
+ return i;
+ }
+ return -ENODATA;
+}
+
unsigned int cbuffer_get_length(struct circular_buffer *cbuff)
{
return abs(cbuff->end - cbuff->start);
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index 3880acd..ff29d22 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -40,3 +40,9 @@ const char *cbuffer_peek_head(struct circular_buffer *cbuff, unsigned int n);
const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n);
unsigned int cbuffer_get_free_chunk_size(struct circular_buffer *cbuff);
unsigned int cbuffer_get_length(struct circular_buffer *cbuff);
+int cbuffer_starts_with_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset);
+int cbuffer_seek_head_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset);
+int cbuffer_seek_tail_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset);
--
1.7.9.5
This patch provides methods to read buffered data without taking it out
from the buffer. Its possible to take a peek at a byte stored at the
N-th positon from buffer's tail. It also adds method for checking the
size of free continous memory space of the buffer, which in pair with
manual memory access methods can reduce io read calls by allowing to
write more than one byte at a time.
---
src/shared/cbuffer.c | 37 ++++++++++++++++++++++++++++++++++++-
src/shared/cbuffer.h | 6 ++++++
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index 7fd95cc..c1e5b08 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -123,7 +123,7 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff)
return cbuff->size;
}
-/* manual mode gives direct access to buffers memory space */
+/* Manual mode gives direct access to buffers memory space */
char *cbuffer_get_free_cell(struct circular_buffer *cbuff)
{
if (cbuffer_is_full(cbuff))
@@ -139,3 +139,38 @@ int cbuffer_consume_next_cell(struct circular_buffer *cbuff)
}
return -ENOSPC;
}
+
+const char *cbuffer_peek_head(struct circular_buffer *cbuff, unsigned int n)
+{
+ int idx;
+ /* find decremented end offset */
+ idx = (cbuff->end - (n + 1)) & (2 * cbuff->size - 1);
+ return &cbuff->data[idx & (cbuff->size - 1)];
+}
+
+const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n)
+{
+ int idx;
+ /* find incremented start offset */
+ idx = (cbuff->start + n) & (2 * cbuff->size - 1);
+ return &(cbuff->data[idx & (cbuff->size - 1)]);
+}
+
+unsigned int cbuffer_get_length(struct circular_buffer *cbuff)
+{
+ return abs(cbuff->end - cbuff->start);
+}
+
+unsigned int cbuffer_get_free_chunk_size(struct circular_buffer *cbuff)
+{
+ int realstart = (cbuff->start & (cbuff->size - 1));
+ int realend = (cbuff->end & (cbuff->size - 1));
+
+ if (cbuffer_is_full(cbuff))
+ return 0;
+
+ if (realend >= realstart)
+ return cbuff->size - realend;
+ else
+ return realstart - realend;
+}
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index 4aaacfa..3880acd 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -34,3 +34,9 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff);
/* memory direct access functions */
char *cbuffer_get_free_cell(struct circular_buffer *cbuff);
int cbuffer_consume_next_cell(struct circular_buffer *cbuff);
+
+/* peek n-th char */
+const char *cbuffer_peek_head(struct circular_buffer *cbuff, unsigned int n);
+const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n);
+unsigned int cbuffer_get_free_chunk_size(struct circular_buffer *cbuff);
+unsigned int cbuffer_get_length(struct circular_buffer *cbuff);
--
1.7.9.5
Test cbuffers direct memory access functionality.
---
unit/test-cbuffer.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index 4af73b8..5a3482d 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -167,6 +167,39 @@ static void cbt_drain(struct cbt_fixture *fix, gconstpointer test_data)
g_assert_cmpint(cbuffer_is_empty(fix->cbuff), >, 0);
}
+/* test cbuffer's manual mode */
+static void cbt_manual(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 1;
+ char *inbuffer = NULL;
+
+ inbuffer = cbuffer_get_free_cell(fix->cbuff);
+ g_assert(cbuffer_is_empty(fix->cbuff));
+ g_assert(inbuffer != NULL);
+
+ /* write directly to buffers memory */
+ if (inbuffer)
+ *inbuffer = 'a';
+
+ /* mark cell as written */
+ g_assert_cmpint(cbuffer_consume_next_cell(fix->cbuff),
+ >=, 0);
+ inbuffer = NULL;
+ inbuffer = cbuffer_get_free_cell(fix->cbuff);
+ g_assert(inbuffer != NULL);
+
+ if (inbuffer)
+ *inbuffer = 'b';
+
+ g_assert_cmpint(cbuffer_consume_next_cell(fix->cbuff),
+ >=, 0);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('b', ==, ch);
+ g_assert(cbuffer_is_empty(fix->cbuff));
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -182,5 +215,7 @@ int main(int argc, char **argv)
cbt_overflow, cbt_fix_teardown);
g_test_add("/cbuffer/drain", struct cbt_fixture, 0, cbt_fix_setup,
cbt_drain, cbt_fix_teardown);
+ g_test_add("/cbuffer/manual", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_manual, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
---
Makefile.am | 6 ++
unit/test-cbuffer.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 192 insertions(+)
create mode 100644 unit/test-cbuffer.c
diff --git a/Makefile.am b/Makefile.am
index 51204f4..079405f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -283,6 +283,12 @@ unit_tests += unit/test-lib
unit_test_lib_SOURCES = unit/test-lib.c
unit_test_lib_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+unit_tests += unit/test-cbuffer
+
+unit_test_cbuffer_SOURCES = unit/test-cbuffer.c \
+ src/shared/cbuffer.h src/shared/cbuffer.c
+unit_test_cbuffer_LDADD = @GLIB_LIBS@
+
noinst_PROGRAMS += $(unit_tests)
TESTS = $(unit_tests)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
new file mode 100644
index 0000000..4af73b8
--- /dev/null
+++ b/unit/test-cbuffer.c
@@ -0,0 +1,186 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include "src/shared/cbuffer.h"
+
+#define BUFFER_SIZE 100
+
+struct cbt_fixture {
+ struct circular_buffer *cbuff;
+};
+
+static void cbt_fix_setup(struct cbt_fixture *fix, gconstpointer tdata)
+{
+ /* the best fitting size is being determined during buffer init */
+ fix->cbuff = cbuffer_init(BUFFER_SIZE);
+}
+
+static void cbt_fix_teardown(struct cbt_fixture *fix, gconstpointer tdata)
+{
+ cbuffer_free(fix->cbuff);
+}
+
+static void cbt_deinit(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ cbuffer_free(fix->cbuff);
+
+ fix->cbuff = cbuffer_init(BUFFER_SIZE);
+ g_assert(fix->cbuff != NULL);
+}
+
+static void cbt_write(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int ret;
+
+ ret = cbuffer_write(fix->cbuff, 'a');
+ g_assert_cmpint(ret, >=, 0);
+
+ ret = cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpint(ret, >=, 0);
+}
+
+static void cbt_read_empty(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int ret;
+ char ch = 111;
+
+ ret = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(ret, <, 0);
+ g_assert_cmpint(111, ==, ch);
+}
+
+static void cbt_read_write(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int res;
+ char ch = 111;
+
+ res = cbuffer_write(fix->cbuff, 'a');
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('a', ==, ch);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, <, 0);
+
+ res = cbuffer_write(fix->cbuff, 'a');
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('a', ==, ch);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('b', ==, ch);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, <, 0);
+
+ res = cbuffer_write(fix->cbuff, 'a');
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_write(fix->cbuff, 'c');
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('c', ==, ch);
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, <, 0);
+}
+
+static void cbt_overflow(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int res;
+ unsigned int i = 0;
+ char ch = 1;
+
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ res = cbuffer_write(fix->cbuff, 'a');
+ g_assert_cmpint(res, >=, 0);
+ }
+
+ res = cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpint(res, <, 0);
+
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('a', ==, ch);
+
+ res = cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpint(res, >=, 0);
+
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('a', ==, ch);
+
+ res = cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(res, >=, 0);
+ g_assert_cmpint('a', ==, ch);
+}
+
+static void cbt_drain(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 1;
+
+ g_assert_cmpint(cbuffer_write(fix->cbuff, 'a'), >=, 0);
+ g_assert_cmpint(cbuffer_read(fix->cbuff, &ch), >=, 0);
+ g_assert_cmpint('a', ==, ch);
+ g_assert_cmpint(cbuffer_is_empty(fix->cbuff), >, 0);
+
+ g_assert_cmpint(cbuffer_write(fix->cbuff, 'a'),
+ >=, 0);
+ g_assert_cmpint(cbuffer_write(fix->cbuff, 'a'),
+ >=, 0);
+ g_assert_cmpint(cbuffer_write(fix->cbuff, 'a'),
+ >=, 0);
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpint(cbuffer_is_empty(fix->cbuff), >, 0);
+ ch = 1;
+ g_assert_cmpint(cbuffer_read(fix->cbuff, &ch), <, 0);
+ g_assert_cmpint(1, ==, ch);
+ g_assert_cmpint(cbuffer_is_empty(fix->cbuff), >, 0);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add("/cbuffer/(de)initialization", struct cbt_fixture, 0,
+ cbt_fix_setup, cbt_deinit, cbt_fix_teardown);
+ g_test_add("/cbuffer/write", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_write, cbt_fix_teardown);
+ g_test_add("/cbuffer/read_empty", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_read_empty, cbt_fix_teardown);
+ g_test_add("/cbuffer/read_write", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_read_write, cbt_fix_teardown);
+ g_test_add("/cbuffer/overflow", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_overflow, cbt_fix_teardown);
+ g_test_add("/cbuffer/drain", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_drain, cbt_fix_teardown);
+ return g_test_run();
+}
--
1.7.9.5
This patch gives direct access to cbuffers memory. Its essential to
make glib's io read/write functions operate directly in the buffer
instead using intermediate buffering or reading by one char.
---
src/shared/cbuffer.c | 17 +++++++++++++++++
src/shared/cbuffer.h | 3 +++
2 files changed, 20 insertions(+)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index 7dde707..7fd95cc 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -122,3 +122,20 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff)
{
return cbuff->size;
}
+
+/* manual mode gives direct access to buffers memory space */
+char *cbuffer_get_free_cell(struct circular_buffer *cbuff)
+{
+ if (cbuffer_is_full(cbuff))
+ return NULL;
+ return &(cbuff->data[cbuff->end & (cbuff->size - 1)]);
+}
+
+int cbuffer_consume_next_cell(struct circular_buffer *cbuff)
+{
+ if (!cbuffer_is_full(cbuff)) {
+ cbuffer_incr(cbuff, &cbuff->end);
+ return 0;
+ }
+ return -ENOSPC;
+}
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index 8865f1f..4aaacfa 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -31,3 +31,6 @@ void cbuffer_drain(struct circular_buffer *cbuff);
int cbuffer_is_full(struct circular_buffer *cbuff);
int cbuffer_is_empty(struct circular_buffer *cbuff);
unsigned int cbuffer_get_size(struct circular_buffer *cbuff);
+/* memory direct access functions */
+char *cbuffer_get_free_cell(struct circular_buffer *cbuff);
+int cbuffer_consume_next_cell(struct circular_buffer *cbuff);
--
1.7.9.5
Adds circular buffer needed by the AT command parser.
---
src/shared/cbuffer.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/cbuffer.h | 33 ++++++++++++++
2 files changed, 157 insertions(+)
create mode 100644 src/shared/cbuffer.c
create mode 100644 src/shared/cbuffer.h
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
new file mode 100644
index 0000000..7dde707
--- /dev/null
+++ b/src/shared/cbuffer.c
@@ -0,0 +1,124 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "cbuffer.h"
+
+struct circular_buffer {
+ int size;
+ int start;
+ int end;
+ char *data;
+};
+
+/* find next closest power of 2 */
+static int cbuffer_find_next_po2(unsigned int size)
+{
+ size--;
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size |= size >> 16;
+ size++;
+ return size;
+}
+
+struct circular_buffer *cbuffer_init(unsigned int size)
+{
+ struct circular_buffer *cbuff = malloc(sizeof(struct circular_buffer));
+
+ if (!cbuff)
+ return NULL;
+
+ /* Need 2^x size for this implementation of circular buffer */
+ cbuff->size = cbuffer_find_next_po2(size);
+ cbuff->start = 0;
+ cbuff->end = 0;
+ cbuff->data = NULL;
+
+ cbuff->data = malloc(cbuff->size);
+ if (!cbuff->data) {
+ free(cbuff);
+ return NULL;
+ }
+
+ return cbuff;
+}
+
+void cbuffer_free(struct circular_buffer *cbuff)
+{
+ free(cbuff->data);
+ cbuff->data = NULL;
+ free(cbuff);
+}
+
+void cbuffer_drain(struct circular_buffer *cbuff)
+{
+ cbuff->start = 0;
+ cbuff->end = 0;
+}
+
+int cbuffer_is_full(struct circular_buffer *cbuff)
+{
+ /* Most significant bit inverted for comparison - index mirroring */
+ return cbuff->end == (cbuff->start ^ cbuff->size);
+}
+
+int cbuffer_is_empty(struct circular_buffer *cbuff)
+{
+ return cbuff->end == cbuff->start;
+}
+
+static void cbuffer_incr(struct circular_buffer *cbuff, int *p)
+{
+ /* Modulo 2*size offset incrementation - index mirroring */
+ *p = (*p + 1) & (2 * cbuff->size - 1);
+}
+
+int cbuffer_write(struct circular_buffer *cbuff, char source)
+{
+ if (!cbuffer_is_full(cbuff)) {
+ cbuff->data[cbuff->end & (cbuff->size - 1)] = source;
+ cbuffer_incr(cbuff, &cbuff->end);
+ return 0;
+ }
+ return -ENOSPC;
+}
+
+int cbuffer_read(struct circular_buffer *cbuff, char *dest)
+{
+ if (!cbuffer_is_empty(cbuff)) {
+ *dest = cbuff->data[cbuff->start & (cbuff->size - 1)];
+ cbuffer_incr(cbuff, &cbuff->start);
+ return 0;
+ }
+ return -ENODATA;
+}
+
+unsigned int cbuffer_get_size(struct circular_buffer *cbuff)
+{
+ return cbuff->size;
+}
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
new file mode 100644
index 0000000..8865f1f
--- /dev/null
+++ b/src/shared/cbuffer.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct circular_buffer;
+
+struct circular_buffer *cbuffer_init(unsigned int size);
+void cbuffer_free(struct circular_buffer *cbuff);
+int cbuffer_write(struct circular_buffer *cbuff, char source);
+int cbuffer_read(struct circular_buffer *cbuff, char *dest);
+void cbuffer_drain(struct circular_buffer *cbuff);
+int cbuffer_is_full(struct circular_buffer *cbuff);
+int cbuffer_is_empty(struct circular_buffer *cbuff);
+unsigned int cbuffer_get_size(struct circular_buffer *cbuff);
--
1.7.9.5