All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] shared/hfp: Add support for HFP HF
@ 2014-10-02 19:29 Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 01/11] " Lukasz Rymanowski
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

Following patches extends hfp API with HFP HF functionality.
HFP HF parser has been added and unit test for it.

TODO: add functions to parse AT response.

To consider: how strict we should be when it comes to parsing
AT responses. For example, at the moment, command +CCLC:<cr><lf>
will be recognized as +CCLC: eventhough correct response format
should be <cr><lf>+CCLC:<cr><lf>

Note: As discussed on IRC I did not try to generalize code.

Lukasz Rymanowski (11):
  shared/hfp: Add support for HFP HF
  shared/hfp: Add set_debug and close_on_unref API for HFP HF
  shared/hfp: Add set disconnect handler and disconnect API to HFP HF
  shared/hfp: Add register/unregister event for HFP HF
  shared/hfp: Add HFP HF parser
  shared/hfp: Add send AT command API for HFP HF
  unit/test-hfp: Provide test_handler function via struct data
  unit/test-hfp: Add init test for HFP HF
  unit/test-hfp: Add send command tests for HFP HF
  unit/test-hfp: Add tests for unsolicited results for HFP HF
  unit/test-hfp: Add some robustness tests for HFP HF

 src/shared/hfp.c | 618 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/hfp.h |  28 +++
 unit/test-hfp.c  | 272 ++++++++++++++++++++++--
 3 files changed, 899 insertions(+), 19 deletions(-)

-- 
1.8.4


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 01/11] shared/hfp: Add support for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 02/11] shared/hfp: Add set_debug and close_on_unref API " Lukasz Rymanowski
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch add struct hfp_hf plus fuctions to create an instance ref and
unref. This code based on existing hfp_gw
---
 src/shared/hfp.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/hfp.h |  6 ++++
 2 files changed, 98 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index efc981f..dbd049a 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -62,6 +62,18 @@ struct hfp_gw {
 	bool destroyed;
 };
 
+struct hfp_hf {
+	int ref_count;
+	int fd;
+	bool close_on_unref;
+	struct io *io;
+	struct ringbuf *read_buf;
+	struct ringbuf *write_buf;
+
+	bool in_disconnect;
+	bool destroyed;
+};
+
 struct cmd_handler {
 	char *prefix;
 	void *user_data;
@@ -807,3 +819,83 @@ bool hfp_gw_disconnect(struct hfp_gw *hfp)
 
 	return io_shutdown(hfp->io);
 }
+
+struct hfp_hf *hfp_hf_new(int fd)
+{
+	struct hfp_hf *hfp;
+
+	if (fd < 0)
+		return NULL;
+
+	hfp = new0(struct hfp_hf, 1);
+	if (!hfp)
+		return NULL;
+
+	hfp->fd = fd;
+	hfp->close_on_unref = false;
+
+	hfp->read_buf = ringbuf_new(4096);
+	if (!hfp->read_buf) {
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->write_buf = ringbuf_new(4096);
+	if (!hfp->write_buf) {
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->io = io_new(fd);
+	if (!hfp->io) {
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	return hfp_hf_ref(hfp);
+}
+
+struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp)
+{
+	if (!hfp)
+		return NULL;
+
+	__sync_fetch_and_add(&hfp->ref_count, 1);
+
+	return hfp;
+}
+
+void hfp_hf_unref(struct hfp_hf *hfp)
+{
+	if (!hfp)
+		return;
+
+	if (__sync_sub_and_fetch(&hfp->ref_count, 1))
+		return;
+
+	io_set_write_handler(hfp->io, NULL, NULL, NULL);
+	io_set_read_handler(hfp->io, NULL, NULL, NULL);
+	io_set_disconnect_handler(hfp->io, NULL, NULL, NULL);
+
+	io_destroy(hfp->io);
+	hfp->io = NULL;
+
+	if (hfp->close_on_unref)
+		close(hfp->fd);
+
+	ringbuf_free(hfp->read_buf);
+	hfp->read_buf = NULL;
+
+	ringbuf_free(hfp->write_buf);
+	hfp->write_buf = NULL;
+
+	if (!hfp->in_disconnect) {
+		free(hfp);
+		return;
+	}
+
+	hfp->destroyed = true;
+}
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
index 743db65..50d9c4b 100644
--- a/src/shared/hfp.h
+++ b/src/shared/hfp.h
@@ -76,9 +76,11 @@ typedef void (*hfp_destroy_func_t)(void *user_data);
 typedef void (*hfp_debug_func_t)(const char *str, void *user_data);
 
 typedef void (*hfp_command_func_t)(const char *command, void *user_data);
+
 typedef void (*hfp_disconnect_func_t)(void *user_data);
 
 struct hfp_gw;
+struct hfp_hf;
 
 struct hfp_gw *hfp_gw_new(int fd);
 
@@ -124,3 +126,7 @@ bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf,
 bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf,
 								uint8_t len);
 bool hfp_gw_result_has_next(struct hfp_gw_result *result);
+
+struct hfp_hf *hfp_hf_new(int fd);
+struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp);
+void hfp_hf_unref(struct hfp_hf *hfp);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 02/11] shared/hfp: Add set_debug and close_on_unref API for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 01/11] " Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 03/11] shared/hfp: Add set disconnect handler and disconnect API to " Lukasz Rymanowski
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

---
 src/shared/hfp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/hfp.h |  3 +++
 2 files changed, 60 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index dbd049a..ad2daa2 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -70,6 +70,10 @@ struct hfp_hf {
 	struct ringbuf *read_buf;
 	struct ringbuf *write_buf;
 
+	hfp_debug_func_t debug_callback;
+	hfp_destroy_func_t debug_destroy;
+	void *debug_data;
+
 	bool in_disconnect;
 	bool destroyed;
 };
@@ -886,6 +890,8 @@ void hfp_hf_unref(struct hfp_hf *hfp)
 	if (hfp->close_on_unref)
 		close(hfp->fd);
 
+	hfp_hf_set_debug(hfp, NULL, NULL, NULL);
+
 	ringbuf_free(hfp->read_buf);
 	hfp->read_buf = NULL;
 
@@ -899,3 +905,54 @@ void hfp_hf_unref(struct hfp_hf *hfp)
 
 	hfp->destroyed = true;
 }
+
+static void hf_read_tracing(const void *buf, size_t count,
+							void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+static void hf_write_tracing(const void *buf, size_t count,
+							void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->debug_destroy)
+		hfp->debug_destroy(hfp->debug_data);
+
+	hfp->debug_callback = callback;
+	hfp->debug_destroy = destroy;
+	hfp->debug_data = user_data;
+
+	if (hfp->debug_callback) {
+		ringbuf_set_input_tracing(hfp->read_buf, hf_read_tracing, hfp);
+		ringbuf_set_input_tracing(hfp->write_buf, hf_write_tracing,
+									hfp);
+	} else {
+		ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL);
+		ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL);
+	}
+
+	return true;
+}
+
+bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close)
+{
+	if (!hfp)
+		return false;
+
+	hfp->close_on_unref = do_close;
+
+	return true;
+}
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
index 50d9c4b..ae67ac2 100644
--- a/src/shared/hfp.h
+++ b/src/shared/hfp.h
@@ -130,3 +130,6 @@ bool hfp_gw_result_has_next(struct hfp_gw_result *result);
 struct hfp_hf *hfp_hf_new(int fd);
 struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp);
 void hfp_hf_unref(struct hfp_hf *hfp);
+bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy);
+bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 03/11] shared/hfp: Add set disconnect handler and disconnect API to HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 01/11] " Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 02/11] shared/hfp: Add set_debug and close_on_unref API " Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 04/11] shared/hfp: Add register/unregister event for " Lukasz Rymanowski
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

---
 src/shared/hfp.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/hfp.h |  5 +++++
 2 files changed, 68 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index ad2daa2..b7855ed 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -74,6 +74,10 @@ struct hfp_hf {
 	hfp_destroy_func_t debug_destroy;
 	void *debug_data;
 
+	hfp_disconnect_func_t disconnect_callback;
+	hfp_destroy_func_t disconnect_destroy;
+	void *disconnect_data;
+
 	bool in_disconnect;
 	bool destroyed;
 };
@@ -956,3 +960,62 @@ bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close)
 
 	return true;
 }
+
+static void hf_disconnect_watch_destroy(void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	if (hfp->disconnect_destroy)
+		hfp->disconnect_destroy(hfp->disconnect_data);
+
+	if (hfp->destroyed)
+		free(hfp);
+}
+
+static bool hf_io_disconnected(struct io *io, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	hfp->in_disconnect = true;
+
+	if (hfp->disconnect_callback)
+		hfp->disconnect_callback(hfp->disconnect_data);
+
+	hfp->in_disconnect = false;
+
+	return false;
+}
+
+bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp,
+						hfp_disconnect_func_t callback,
+						void *user_data,
+						hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->disconnect_destroy)
+		hfp->disconnect_destroy(hfp->disconnect_data);
+
+	if (!io_set_disconnect_handler(hfp->io, hf_io_disconnected, hfp,
+						hf_disconnect_watch_destroy)) {
+		hfp->disconnect_callback = NULL;
+		hfp->disconnect_destroy = NULL;
+		hfp->disconnect_data = NULL;
+		return false;
+	}
+
+	hfp->disconnect_callback = callback;
+	hfp->disconnect_destroy = destroy;
+	hfp->disconnect_data = user_data;
+
+	return true;
+}
+
+bool hfp_hf_disconnect(struct hfp_hf *hfp)
+{
+	if (!hfp)
+		return false;
+
+	return io_shutdown(hfp->io);
+}
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
index ae67ac2..a9a169b 100644
--- a/src/shared/hfp.h
+++ b/src/shared/hfp.h
@@ -133,3 +133,8 @@ void hfp_hf_unref(struct hfp_hf *hfp);
 bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback,
 				void *user_data, hfp_destroy_func_t destroy);
 bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close);
+bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp,
+					hfp_disconnect_func_t callback,
+					void *user_data,
+					hfp_destroy_func_t destroy);
+bool hfp_hf_disconnect(struct hfp_hf *hfp);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 04/11] shared/hfp: Add register/unregister event for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (2 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 03/11] shared/hfp: Add set disconnect handler and disconnect API to " Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 05/11] shared/hfp: Add HFP HF parser Lukasz Rymanowski
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds API which allows to register/unregister for unsolicited
responses.
---
 src/shared/hfp.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/hfp.h |   8 +++++
 2 files changed, 116 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index b7855ed..b1cf08e 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -70,6 +70,8 @@ struct hfp_hf {
 	struct ringbuf *read_buf;
 	struct ringbuf *write_buf;
 
+	struct queue *event_handlers;
+
 	hfp_debug_func_t debug_callback;
 	hfp_destroy_func_t debug_destroy;
 	void *debug_data;
@@ -94,6 +96,18 @@ struct hfp_gw_result {
 	unsigned int offset;
 };
 
+struct hfp_hf_result {
+	const char *data;
+	unsigned int offset;
+};
+
+struct event_handler {
+	char *prefix;
+	void *user_data;
+	hfp_destroy_func_t destroy;
+	hfp_hf_result_func_t callback;
+};
+
 static void destroy_cmd_handler(void *data)
 {
 	struct cmd_handler *handler = data;
@@ -828,6 +842,32 @@ bool hfp_gw_disconnect(struct hfp_gw *hfp)
 	return io_shutdown(hfp->io);
 }
 
+static bool match_handler_event_prefix(const void *a, const void *b)
+{
+	const struct event_handler *handler = a;
+	const char *prefix = b;
+
+	if (strlen(handler->prefix) != strlen(prefix))
+		return false;
+
+	if (memcmp(handler->prefix, prefix, strlen(prefix)))
+		return false;
+
+	return true;
+}
+
+static void destroy_event_handler(void *data)
+{
+	struct event_handler *handler = data;
+
+	if (handler->destroy)
+		handler->destroy(handler->user_data);
+
+	free(handler->prefix);
+
+	free(handler);
+}
+
 struct hfp_hf *hfp_hf_new(int fd)
 {
 	struct hfp_hf *hfp;
@@ -863,6 +903,15 @@ struct hfp_hf *hfp_hf_new(int fd)
 		return NULL;
 	}
 
+	hfp->event_handlers = queue_new();
+	if (!hfp->event_handlers) {
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
 	return hfp_hf_ref(hfp);
 }
 
@@ -902,6 +951,9 @@ void hfp_hf_unref(struct hfp_hf *hfp)
 	ringbuf_free(hfp->write_buf);
 	hfp->write_buf = NULL;
 
+	queue_destroy(hfp->event_handlers, destroy_event_handler);
+	hfp->event_handlers = NULL;
+
 	if (!hfp->in_disconnect) {
 		free(hfp);
 		return;
@@ -961,6 +1013,62 @@ bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close)
 	return true;
 }
 
+bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
+						const char *prefix,
+						void *user_data,
+						hfp_destroy_func_t destroy)
+{
+	struct event_handler *handler;
+
+	if (!callback)
+		return false;
+
+	handler = new0(struct event_handler, 1);
+	if (!handler)
+		return false;
+
+	handler->callback = callback;
+	handler->user_data = user_data;
+
+	handler->prefix = strdup(prefix);
+	if (!handler->prefix) {
+		free(handler);
+		return false;
+	}
+
+	if (queue_find(hfp->event_handlers, match_handler_event_prefix,
+							handler->prefix)) {
+		destroy_event_handler(handler);
+		return false;
+	}
+
+	handler->destroy = destroy;
+
+	return queue_push_tail(hfp->event_handlers, handler);
+}
+
+bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix)
+{
+	struct cmd_handler *handler;
+	char *lookup_prefix;
+
+	lookup_prefix = strdup(prefix);
+	if (!lookup_prefix)
+		return false;
+
+	handler = queue_remove_if(hfp->event_handlers,
+						match_handler_event_prefix,
+						lookup_prefix);
+	free(lookup_prefix);
+
+	if (!handler)
+		return false;
+
+	destroy_event_handler(handler);
+
+	return true;
+}
+
 static void hf_disconnect_watch_destroy(void *user_data)
 {
 	struct hfp_hf *hfp = user_data;
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
index a9a169b..85037b1 100644
--- a/src/shared/hfp.h
+++ b/src/shared/hfp.h
@@ -68,10 +68,14 @@ enum hfp_gw_cmd_type {
 };
 
 struct hfp_gw_result;
+struct hfp_hf_result;
 
 typedef void (*hfp_result_func_t)(struct hfp_gw_result *result,
 				enum hfp_gw_cmd_type type, void *user_data);
 
+typedef void (*hfp_hf_result_func_t)(struct hfp_hf_result *result,
+							void *user_data);
+
 typedef void (*hfp_destroy_func_t)(void *user_data);
 typedef void (*hfp_debug_func_t)(const char *str, void *user_data);
 
@@ -138,3 +142,7 @@ bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp,
 					void *user_data,
 					hfp_destroy_func_t destroy);
 bool hfp_hf_disconnect(struct hfp_hf *hfp);
+bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
+					const char *prefix, void *user_data,
+					hfp_destroy_func_t destroy);
+bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 05/11] shared/hfp: Add HFP HF parser
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (3 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 04/11] shared/hfp: Add register/unregister event for " Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 06/11] shared/hfp: Add send AT command API for HFP HF Lukasz Rymanowski
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds parser for AT responses and unsolicited results.
---
 src/shared/hfp.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index b1cf08e..d267698 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -868,6 +868,128 @@ static void destroy_event_handler(void *data)
 	free(handler);
 }
 
+static void hf_skip_whitespace(struct hfp_hf_result *result)
+{
+	while (result->data[result->offset] == ' ')
+		result->offset++;
+}
+
+static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data)
+{
+	struct event_handler *handler;
+	const char *separators = ";:\0";
+	struct hfp_hf_result result_data;
+	char lookup_prefix[18];
+	uint8_t pref_len = 0;
+	const char *prefix;
+	int i;
+
+	result_data.offset = 0;
+	result_data.data = data;
+
+	hf_skip_whitespace(&result_data);
+
+	if (strlen(data + result_data.offset) < 2)
+		return;
+
+	prefix = data + result_data.offset;
+
+	pref_len = strcspn(prefix, separators);
+	if (pref_len > 17 || pref_len < 2)
+		return;
+
+	for (i = 0; i < pref_len; i++)
+		lookup_prefix[i] = toupper(prefix[i]);
+
+	lookup_prefix[pref_len] = '\0';
+	result_data.offset += pref_len;
+
+	handler = queue_find(hfp->event_handlers, match_handler_event_prefix,
+								lookup_prefix);
+
+	if (!handler)
+		return;
+
+	handler->callback(&result_data, handler->user_data);
+
+}
+
+static char *find_cr_lf(char *str, size_t len)
+{
+	char *ptr;
+	int count;
+	int offset;
+
+	offset = 0;
+
+	ptr = memchr(str, '\r', len);
+	while (ptr) {
+		/*
+		 * Check if there is more data after '\r'. If so check for
+		 * '\n'
+		 */
+		count = ptr-str;
+		if ((count < (int) (len - 1)) && *(ptr + 1) == '\n')
+			return ptr;
+
+		/* There is only '\r'? Let's try to find next one */
+		offset += count + 1;
+
+		if (offset >= (int)len)
+			return NULL;
+
+		ptr = memchr(str + offset, '\r', len - offset);
+	}
+
+	return NULL;
+}
+
+static void hf_process_input(struct hfp_hf *hfp)
+{
+	char *str, *ptr;
+	size_t len, count, offset;
+
+	str = ringbuf_peek(hfp->read_buf, 0, &len);
+	if (!str)
+		return;
+
+	offset = 0;
+
+	ptr = find_cr_lf(str, len);
+	while (ptr) {
+		count = ptr - (str + offset);
+		if (count == 0) {
+			/* 2 is for <cr><lf> */
+			offset += 2;
+		} else {
+			*ptr = '\0';
+			hf_call_prefix_handler(hfp, str + offset);
+			offset += count + 2;
+		}
+
+		if (offset >= len)
+			break;
+
+		ptr = find_cr_lf(str + offset, len - offset);
+	}
+
+	ringbuf_drain(hfp->read_buf, offset < len ? offset : len);
+}
+
+static bool hf_can_read_data(struct io *io, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+	ssize_t bytes_read;
+
+	bytes_read = ringbuf_read(hfp->read_buf, hfp->fd);
+	if (bytes_read < 0)
+		return false;
+
+	hf_process_input(hfp);
+
+	return true;
+}
+
 struct hfp_hf *hfp_hf_new(int fd)
 {
 	struct hfp_hf *hfp;
@@ -912,6 +1034,15 @@ struct hfp_hf *hfp_hf_new(int fd)
 		return NULL;
 	}
 
+	if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp,
+							read_watch_destroy)) {
+		queue_destroy(hfp->event_handlers,
+						destroy_event_handler);
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+	}
+
 	return hfp_hf_ref(hfp);
 }
 
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 06/11] shared/hfp: Add send AT command API for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (4 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 05/11] shared/hfp: Add HFP HF parser Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 07/11] unit/test-hfp: Provide test_handler function via struct data Lukasz Rymanowski
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds handling send and response of AT command.
Note that we always wait for AT command response before sending next
command, however user can fill hfp_hf with more than one command.
All the commands are queued and send one by one.
---
 src/shared/hfp.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/hfp.h |   6 ++
 2 files changed, 173 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index d267698..9ac508d 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -70,6 +70,10 @@ struct hfp_hf {
 	struct ringbuf *read_buf;
 	struct ringbuf *write_buf;
 
+	bool writer_active;
+	struct queue *cmd_queue;
+	bool command_in_progress;
+
 	struct queue *event_handlers;
 
 	hfp_debug_func_t debug_callback;
@@ -96,6 +100,13 @@ struct hfp_gw_result {
 	unsigned int offset;
 };
 
+struct cmd_response {
+	char *prefix;
+	hfp_response_func_t resp_cb;
+	hfp_hf_result_func_t unsolicited_cb;
+	void *user_data;
+};
+
 struct hfp_hf_result {
 	const char *data;
 	unsigned int offset;
@@ -868,12 +879,64 @@ static void destroy_event_handler(void *data)
 	free(handler);
 }
 
+static bool hf_can_write_data(struct io *io, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+	ssize_t bytes_written;
+
+	bytes_written = ringbuf_write(hfp->write_buf, hfp->fd);
+	if (bytes_written < 0)
+		return false;
+
+	if (ringbuf_len(hfp->write_buf) > 0)
+		return true;
+
+	return false;
+}
+
+static void hf_write_watch_destroy(void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	hfp->writer_active = false;
+}
+
 static void hf_skip_whitespace(struct hfp_hf_result *result)
 {
 	while (result->data[result->offset] == ' ')
 		result->offset++;
 }
 
+static bool is_response(const char *msg, enum hfp_result *result)
+{
+	if (strcmp(msg, "OK") == 0) {
+		*result = HFP_RESULT_OK;
+		return true;
+	}
+
+	if (strcmp(msg, "ERROR") == 0) {
+		*result = HFP_RESULT_ERROR;
+		return true;
+	}
+
+	return false;
+}
+
+static void hf_wakeup_writer(struct hfp_hf *hfp)
+{
+	if (hfp->writer_active)
+		return;
+
+	if (!ringbuf_len(hfp->write_buf))
+		return;
+
+	if (!io_set_write_handler(hfp->io, hf_can_write_data,
+					hfp, hf_write_watch_destroy))
+		return;
+
+	hfp->writer_active = true;
+}
+
 static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data)
 {
 	struct event_handler *handler;
@@ -904,6 +967,42 @@ static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data)
 	lookup_prefix[pref_len] = '\0';
 	result_data.offset += pref_len;
 
+	if (hfp->command_in_progress) {
+		struct cmd_response *cmd;
+		enum hfp_result result;
+
+		cmd = queue_peek_head(hfp->cmd_queue);
+		if (!cmd)
+			return;
+
+		if (is_response(lookup_prefix, &result)) {
+			cmd->resp_cb(cmd->prefix, result, cmd->user_data);
+
+			queue_remove(hfp->cmd_queue, cmd);
+			free(cmd);
+
+			if (!queue_isempty(hfp->cmd_queue)) {
+				hf_wakeup_writer(hfp);
+				return;
+			}
+
+			hfp->command_in_progress = false;
+
+			return;
+		}
+		/*
+		 * Check if unsolicited result is the response for ongoing
+		 * command. If not we try to find registered handler for it
+		 * later.
+		 */
+		if (strcmp(lookup_prefix, &cmd->prefix[2]) == 0 &&
+							cmd->unsolicited_cb) {
+			cmd->unsolicited_cb(&result_data, cmd->user_data);
+
+			return;
+		}
+	}
+
 	handler = queue_find(hfp->event_handlers, match_handler_event_prefix,
 								lookup_prefix);
 
@@ -1034,6 +1133,19 @@ struct hfp_hf *hfp_hf_new(int fd)
 		return NULL;
 	}
 
+	hfp->cmd_queue = queue_new();
+	if (!hfp->cmd_queue) {
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		queue_destroy(hfp->event_handlers, NULL);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->writer_active = false;
+	hfp->command_in_progress = false;
+
 	if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp,
 							read_watch_destroy)) {
 		queue_destroy(hfp->event_handlers,
@@ -1144,6 +1256,61 @@ bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close)
 	return true;
 }
 
+bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb,
+					hfp_hf_result_func_t unsolicited_cb,
+					void *user_data,
+					const char *format, ...)
+{
+	va_list ap;
+	char *fmt;
+	int len;
+	const char *separators = ";?=\0";
+	uint8_t prefix_len;
+	struct cmd_response *cmd;
+
+	if (!hfp || !format || !resp_cb)
+		return false;
+
+	if (asprintf(&fmt, "%s\r", format) < 0)
+		return false;
+
+	cmd = new0(struct cmd_response, 1);
+	if (!cmd)
+		return false;
+
+	va_start(ap, format);
+	len = ringbuf_vprintf(hfp->write_buf, fmt, ap);
+	va_end(ap);
+
+	free(fmt);
+
+	if (len < 0) {
+		free(cmd);
+		return false;
+	}
+
+	prefix_len = strcspn(format, separators);
+	cmd->prefix = strndup(format, prefix_len);
+	cmd->resp_cb = resp_cb;
+	cmd->unsolicited_cb = unsolicited_cb;
+	cmd->user_data = user_data;
+
+	if (!queue_push_tail(hfp->cmd_queue, cmd)) {
+		ringbuf_drain(hfp->write_buf, len);
+		free(cmd);
+		return false;
+	}
+
+	if (hfp->command_in_progress)
+		return true;
+
+	hfp->command_in_progress = true;
+
+	hf_wakeup_writer(hfp);
+
+	return true;
+}
+
 bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
 						const char *prefix,
 						void *user_data,
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
index 85037b1..6f2e17c 100644
--- a/src/shared/hfp.h
+++ b/src/shared/hfp.h
@@ -83,6 +83,9 @@ typedef void (*hfp_command_func_t)(const char *command, void *user_data);
 
 typedef void (*hfp_disconnect_func_t)(void *user_data);
 
+typedef void (*hfp_response_func_t)(const char *prefix,
+							enum hfp_result result,
+							void *user_data);
 struct hfp_gw;
 struct hfp_hf;
 
@@ -146,3 +149,6 @@ bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
 					const char *prefix, void *user_data,
 					hfp_destroy_func_t destroy);
 bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix);
+bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb,
+				hfp_hf_result_func_t unsolicited_cb,
+				void *user_data, const char *format, ...);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 07/11] unit/test-hfp: Provide test_handler function via struct data
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (5 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 06/11] shared/hfp: Add send AT command API for HFP HF Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 08/11] unit/test-hfp: Add init test for HFP HF Lukasz Rymanowski
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch allows us to use user defined test handler depends on needs.
Will use it in following patches which implements tests for HFP HF.
---
 unit/test-hfp.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index a8801b0..4b3473b 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -52,6 +52,7 @@ struct test_data {
 	char *test_name;
 	struct test_pdu *pdu_list;
 	hfp_result_func_t result_func;
+	GIOFunc test_handler;
 };
 
 #define data(args...) ((const unsigned char[]) { args })
@@ -95,6 +96,7 @@ struct test_data {
 		data.result_func = result_function;			\
 		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
 		g_test_add_data_func(name, &data, function);		\
+		data.test_handler = test_handler;			\
 	} while (0)
 
 static void context_quit(struct context *context)
@@ -158,6 +160,7 @@ static struct context *create_context(gconstpointer data)
 	struct context *context = g_new0(struct context, 1);
 	GIOChannel *channel;
 	int err, sv[2];
+	const struct test_data *d = data;
 
 	context->main_loop = g_main_loop_new(NULL, FALSE);
 	g_assert(context->main_loop);
@@ -173,7 +176,8 @@ static struct context *create_context(gconstpointer data)
 
 	context->watch_id = g_io_add_watch(channel,
 				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-				test_handler, context);
+				d->test_handler, context);
+
 	g_assert(context->watch_id > 0);
 
 	g_io_channel_unref(channel);
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 08/11] unit/test-hfp: Add init test for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (6 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 07/11] unit/test-hfp: Provide test_handler function via struct data Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 09/11] unit/test-hfp: Add send command tests " Lukasz Rymanowski
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds basic infrastruction for HFP HF test plus
init test.

It also moves send_pdu function in the file so it can be used by
test_hf_handler
---
 unit/test-hfp.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 78 insertions(+), 18 deletions(-)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 4b3473b..ad26058 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -36,6 +36,7 @@ struct context {
 	int fd_server;
 	int fd_client;
 	struct hfp_gw *hfp;
+	struct hfp_hf *hfp_hf;
 	const struct test_data *data;
 	unsigned int pdu_offset;
 };
@@ -52,6 +53,8 @@ struct test_data {
 	char *test_name;
 	struct test_pdu *pdu_list;
 	hfp_result_func_t result_func;
+	hfp_response_func_t response_func;
+	hfp_hf_result_func_t hf_result_func;
 	GIOFunc test_handler;
 };
 
@@ -99,6 +102,22 @@ struct test_data {
 		data.test_handler = test_handler;			\
 	} while (0)
 
+#define define_hf_test(name, function, result_func, response_function,	\
+								args...)\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_malloc(sizeof(pdus));			\
+		data.hf_result_func = result_func;			\
+		data.response_func = response_function;			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+		data.test_handler = test_hf_handler;			\
+	} while (0)
+
 static void context_quit(struct context *context)
 {
 	g_main_loop_quit(context->main_loop);
@@ -128,6 +147,46 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 	return FALSE;
 }
 
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (pdu && !pdu->valid)
+		return FALSE;
+
+	len = write(context->fd_server, pdu->data, pdu->size);
+	g_assert_cmpint(len, ==, pdu->size);
+
+	pdu = &context->data->pdu_list[context->pdu_offset];
+	if (pdu->fragmented)
+		g_idle_add(send_pdu, context);
+
+	return FALSE;
+}
+
+static gboolean test_hf_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto done;
+
+	send_pdu(context);
+
+	return TRUE;
+
+done:
+	context_quit(context);
+	context->watch_id = 0;
+
+	return FALSE;
+}
+
 static void cmd_handler(const char *command, void *user_data)
 {
 	struct context *context = user_data;
@@ -203,6 +262,9 @@ static void execute_context(struct context *context)
 	if (context->hfp)
 		hfp_gw_unref(context->hfp);
 
+	if (context->hfp_hf)
+		hfp_hf_unref(context->hfp_hf);
+
 	g_free(context);
 }
 
@@ -275,24 +337,6 @@ static void test_register(gconstpointer data)
 	execute_context(context);
 }
 
-static gboolean send_pdu(gpointer user_data)
-{
-	struct context *context = user_data;
-	const struct test_pdu *pdu;
-	ssize_t len;
-
-	pdu = &context->data->pdu_list[context->pdu_offset++];
-
-	len = write(context->fd_server, pdu->data, pdu->size);
-	g_assert_cmpint(len, ==, pdu->size);
-
-	pdu = &context->data->pdu_list[context->pdu_offset];
-	if (pdu->fragmented)
-		g_idle_add(send_pdu, context);
-
-	return FALSE;
-}
-
 static void test_fragmented(gconstpointer data)
 {
 	struct context *context = create_context(data);
@@ -404,6 +448,20 @@ static void check_string_2(struct hfp_gw_result *result,
 	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
 }
 
+static void test_hf_init(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+	g_assert(hfp_hf_set_close_on_unref(context->hfp_hf, true));
+
+	hfp_hf_unref(context->hfp_hf);
+	context->hfp_hf = NULL;
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -473,5 +531,7 @@ int main(int argc, char *argv[])
 			raw_pdu('\r'),
 			data_end());
 
+	define_hf_test("/hfp/test_init", test_hf_init, NULL, NULL, data_end());
+
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 09/11] unit/test-hfp: Add send command tests for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (7 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 08/11] unit/test-hfp: Add init test for HFP HF Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 10/11] unit/test-hfp: Add tests for unsolicited results " Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 11/11] unit/test-hfp: Add some robustness tests " Lukasz Rymanowski
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds following tests:
/hfp/test_send_command_1
/hfp/test_send_command_2
---
 unit/test-hfp.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index ad26058..1926615 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -118,6 +118,9 @@ struct test_data {
 		data.test_handler = test_hf_handler;			\
 	} while (0)
 
+
+static bool received_unsolicited = false;
+
 static void context_quit(struct context *context)
 {
 	g_main_loop_quit(context->main_loop);
@@ -462,6 +465,60 @@ static void test_hf_init(gconstpointer data)
 	execute_context(context);
 }
 
+static void hf_unsolicited_resp_cb(struct hfp_hf_result *result,
+							void *user_data)
+{
+	received_unsolicited = true;
+}
+
+static void hf_response_with_unsolicited_cb(const char *prefix,
+							enum hfp_result res,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert_cmpstr(prefix, ==,  "AT+BRSF");
+	g_assert(received_unsolicited);
+	received_unsolicited = false;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_response_cb(const char *prefix, enum hfp_result res,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert_cmpstr(prefix, ==, "AT+BRSF");
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void test_hf_send_command(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	const struct test_pdu *pdu;
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	if (context->data->response_func) {
+		ret = hfp_hf_send_command(context->hfp_hf,
+						context->data->response_func,
+						context->data->hf_result_func,
+						context, (char *)pdu->data);
+		g_assert(ret);
+	}
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -532,6 +589,20 @@ int main(int argc, char *argv[])
 			data_end());
 
 	define_hf_test("/hfp/test_init", test_hf_init, NULL, NULL, data_end());
+	define_hf_test("/hfp/test_send_command_1", test_hf_send_command, NULL,
+			hf_response_cb,
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp/test_send_command_2", test_hf_send_command,
+			hf_unsolicited_resp_cb,
+			hf_response_with_unsolicited_cb,
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('\r', '\n', '+', 'B', 'R', 'S', 'F', '\r',
+									'\n'),
+			raw_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
+			data_end());
 
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 10/11] unit/test-hfp: Add tests for unsolicited results for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (8 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 09/11] unit/test-hfp: Add send command tests " Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  2014-10-02 19:29 ` [PATCH 11/11] unit/test-hfp: Add some robustness tests " Lukasz Rymanowski
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds three test case:
/hfp/test_unsolicited_1
/hfp/test_unsolicited_2
/hfp/test_unsolicited_3
---
 unit/test-hfp.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 1926615..4d552fd 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -519,6 +519,42 @@ static void test_hf_send_command(gconstpointer data)
 	execute_context(context);
 }
 
+static void hf_result_handler(struct hfp_hf_result *result,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void test_hf_unsolicited(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	if (context->data->hf_result_func) {
+		const struct test_pdu *pdu;
+
+		pdu = &context->data->pdu_list[context->pdu_offset++];
+
+		ret = hfp_hf_register(context->hfp_hf,
+						context->data->hf_result_func,
+						(char *)pdu->data, context,
+						NULL);
+		g_assert(ret);
+	}
+
+	send_pdu(context);
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -604,5 +640,28 @@ int main(int argc, char *argv[])
 			raw_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
 			data_end());
 
+	define_hf_test("/hfp/test_unsolicited_1", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			raw_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', '\r',
+									'\n'),
+			data_end());
+
+	define_hf_test("/hfp/test_unsolicited_2", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			raw_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+							'3', '0', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp/test_unsolicited_3", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r'), frg_pdu('\n'), frg_pdu('+'),
+			frg_pdu('C'), frg_pdu('L'), frg_pdu('C'), frg_pdu('C'),
+			frg_pdu(':'), frg_pdu('1'), frg_pdu('3'), frg_pdu('0'),
+			frg_pdu('\r'), frg_pdu('\n'),
+			data_end());
+
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 11/11] unit/test-hfp: Add some robustness tests for HFP HF
  2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
                   ` (9 preceding siblings ...)
  2014-10-02 19:29 ` [PATCH 10/11] unit/test-hfp: Add tests for unsolicited results " Lukasz Rymanowski
@ 2014-10-02 19:29 ` Lukasz Rymanowski
  10 siblings, 0 replies; 12+ messages in thread
From: Lukasz Rymanowski @ 2014-10-02 19:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski

This patch adds folowing tests:
/hfp/test_hf_corrupted_1
/hfp/test_hf_corrupted_2
/hfp/test_hf_empty
/hfp/test_hf_unknown
---
 unit/test-hfp.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 4d552fd..1702cb6 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -555,6 +555,25 @@ static void test_hf_unsolicited(gconstpointer data)
 	execute_context(context);
 }
 
+static void test_hf_robustness(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	send_pdu(context);
+
+	hfp_hf_unref(context->hfp_hf);
+	context->hfp_hf = NULL;
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -663,5 +682,26 @@ int main(int argc, char *argv[])
 			frg_pdu('\r'), frg_pdu('\n'),
 			data_end());
 
+	define_hf_test("/hfp/test_hf_corrupted_1", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', 'X', '\r', '\n'),
+			frg_pdu('+', 'C', 'L', 'C', 'C', ':', '1', '3', '0',
+								'\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp/test_hf_corrupted_2", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp/test_hf_empty", test_hf_robustness, NULL, NULL,
+			raw_pdu('\r'), data_end());
+
+	define_hf_test("/hfp/test_hf_unknown", test_hf_robustness, NULL, NULL,
+			raw_pdu('\r', '\n', 'B', 'R', '\r', '\n'),
+			data_end());
+
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2014-10-02 19:29 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-02 19:29 [PATCH 00/11] shared/hfp: Add support for HFP HF Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 01/11] " Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 02/11] shared/hfp: Add set_debug and close_on_unref API " Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 03/11] shared/hfp: Add set disconnect handler and disconnect API to " Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 04/11] shared/hfp: Add register/unregister event for " Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 05/11] shared/hfp: Add HFP HF parser Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 06/11] shared/hfp: Add send AT command API for HFP HF Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 07/11] unit/test-hfp: Provide test_handler function via struct data Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 08/11] unit/test-hfp: Add init test for HFP HF Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 09/11] unit/test-hfp: Add send command tests " Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 10/11] unit/test-hfp: Add tests for unsolicited results " Lukasz Rymanowski
2014-10-02 19:29 ` [PATCH 11/11] unit/test-hfp: Add some robustness tests " Lukasz Rymanowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.