Diff between 611f14dec82b58b371d13dbc361eb911467cf597 and 8ddda0351f4869038bd943ece25895863c6c916f

Changed Files

File Additions Deletions Status
src/shared/hfp.c +393 -0 added
src/shared/hfp.h +88 -0 added

Full Patch

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
new file mode 100644
index 0000000..c1bfc30
--- /dev/null
+++ b/src/shared/hfp.c
@@ -0,0 +1,393 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "util.h"
+#include "ringbuf.h"
+#include "io.h"
+#include "hfp.h"
+
+struct hfp_gw {
+	int ref_count;
+	int fd;
+	bool close_on_unref;
+	struct io *io;
+	struct ringbuf *read_buf;
+	struct ringbuf *write_buf;
+	bool writer_active;
+	bool permissive_syntax;
+	bool result_pending;
+	hfp_command_func_t command_callback;
+	hfp_destroy_func_t command_destroy;
+	void *command_data;
+	hfp_debug_func_t debug_callback;
+	hfp_destroy_func_t debug_destroy;
+	void *debug_data;
+};
+
+static void write_watch_destroy(void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	hfp->writer_active = false;
+}
+
+static bool can_write_data(struct io *io, void *user_data)
+{
+	struct hfp_gw *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 wakeup_writer(struct hfp_gw *hfp)
+{
+	if (hfp->writer_active)
+		return;
+
+	if (!ringbuf_len(hfp->write_buf))
+		return;
+
+	if (!io_set_write_handler(hfp->io, can_write_data,
+					hfp, write_watch_destroy))
+		return;
+
+	hfp->writer_active = true;
+}
+
+static void process_input(struct hfp_gw *hfp)
+{
+	char *str, *ptr;
+	size_t len, count;
+
+	str = ringbuf_peek(hfp->read_buf, 0, &len);
+	if (!str)
+		return;
+
+	ptr = memchr(str, '\r', len);
+	if (!ptr) {
+		char *str2;
+		size_t len2;
+
+		str2 = ringbuf_peek(hfp->read_buf, len, &len2);
+		if (!str2)
+			return;
+
+		ptr = memchr(str2, '\r', len2);
+		if (!ptr)
+			return;
+
+		*ptr = '\0';
+		count = asprintf(&ptr, "%s%s", str, str2);
+		str = ptr;
+	} else {
+		count = ptr - str;
+		*ptr = '\0';
+	}
+
+	hfp->result_pending = true;
+
+	if (hfp->command_callback)
+		hfp->command_callback(str, hfp->command_data);
+	else
+		hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
+
+	len = ringbuf_drain(hfp->read_buf, count + 1);
+
+	if (str == ptr)
+		free(ptr);
+}
+
+static void read_watch_destroy(void *user_data)
+{
+}
+
+static bool can_read_data(struct io *io, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+	ssize_t bytes_read;
+
+	bytes_read = ringbuf_read(hfp->read_buf, hfp->fd);
+	if (bytes_read < 0)
+		return false;
+
+	if (hfp->result_pending)
+		return true;
+
+	process_input(hfp);
+
+	return true;
+}
+
+struct hfp_gw *hfp_gw_new(int fd)
+{
+	struct hfp_gw *hfp;
+
+	if (fd < 0)
+		return NULL;
+
+	hfp = new0(struct hfp_gw, 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;
+	}
+
+	if (!io_set_read_handler(hfp->io, can_read_data,
+					hfp, read_watch_destroy)) {
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->writer_active = false;
+	hfp->permissive_syntax = false;
+	hfp->result_pending = false;
+
+	return hfp_gw_ref(hfp);
+}
+
+struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp)
+{
+	if (!hfp)
+		return NULL;
+
+	__sync_fetch_and_add(&hfp->ref_count, 1);
+
+	return hfp;
+}
+
+void hfp_gw_unref(struct hfp_gw *hfp)
+{
+	if (!hfp)
+		return;
+
+	if (__sync_sub_and_fetch(&hfp->ref_count, 1))
+		return;
+
+	hfp_gw_set_command_handler(hfp, NULL, NULL, NULL);
+
+	io_set_write_handler(hfp->io, NULL, NULL, NULL);
+	io_set_read_handler(hfp->io, NULL, NULL, NULL);
+
+	io_destroy(hfp->io);
+	hfp->io = NULL;
+
+	if (hfp->close_on_unref)
+		close(hfp->fd);
+
+	hfp_gw_set_debug(hfp, NULL, NULL, NULL);
+
+	ringbuf_free(hfp->read_buf);
+	hfp->read_buf = NULL;
+
+	ringbuf_free(hfp->write_buf);
+	hfp->write_buf = NULL;
+
+	free(hfp);
+}
+
+static void read_tracing(const void *buf, size_t count, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+static void write_tracing(const void *buf, size_t count, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+bool hfp_gw_set_debug(struct hfp_gw *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, read_tracing, hfp);
+		ringbuf_set_input_tracing(hfp->write_buf, 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_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close)
+{
+	if (!hfp)
+		return false;
+
+	hfp->close_on_unref = do_close;
+
+	return true;
+}
+
+bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive)
+{
+	if (!hfp)
+		return false;
+
+	hfp->permissive_syntax = permissive;
+
+	return true;
+}
+
+bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result)
+{
+	const char *str;
+
+	if (!hfp)
+		return false;
+
+	switch (result) {
+	case HFP_RESULT_OK:
+		str = "OK";
+		break;
+	case HFP_RESULT_ERROR:
+		str = "ERROR";
+		break;
+	default:
+		return false;
+	}
+
+	if (ringbuf_printf(hfp->write_buf, "\r\n%s\r\n", str) < 0)
+		return false;
+
+	wakeup_writer(hfp);
+
+	hfp->result_pending = false;
+
+	return true;
+}
+
+bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error)
+{
+	if (!hfp)
+		return false;
+
+	if (ringbuf_printf(hfp->write_buf, "\r\n+CME ERROR: %u\r\n", error) < 0)
+		return false;
+
+	wakeup_writer(hfp);
+
+	hfp->result_pending = false;
+
+	return true;
+}
+
+bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...)
+{
+	va_list ap;
+	char *fmt;
+	int len;
+
+	if (!hfp || !format)
+		return false;
+
+	if (asprintf(&fmt, "\r\n%s\r\n", format) < 0)
+		return false;
+
+	va_start(ap, format);
+	len = ringbuf_vprintf(hfp->write_buf, fmt, ap);
+	va_end(ap);
+
+	free(fmt);
+
+	if (len < 0)
+		return false;
+
+	if (hfp->result_pending)
+		return true;
+
+	wakeup_writer(hfp);
+
+	return true;
+}
+
+bool hfp_gw_set_command_handler(struct hfp_gw *hfp,
+				hfp_command_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->command_destroy)
+		hfp->command_destroy(hfp->command_data);
+
+	hfp->command_callback = callback;
+	hfp->command_destroy = destroy;
+	hfp->command_data = user_data;
+
+	return true;
+}
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
new file mode 100644
index 0000000..2eca6d8
--- /dev/null
+++ b/src/shared/hfp.h
@@ -0,0 +1,88 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  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>
+
+enum hfp_result {
+	HFP_RESULT_OK		= 0,
+	HFP_RESULT_CONNECT	= 1,
+	HFP_RESULT_RING		= 2,
+	HFP_RESULT_NO_CARRIER	= 3,
+	HFP_RESULT_ERROR	= 4,
+	HFP_RESULT_NO_DIALTONE	= 6,
+	HFP_RESULT_BUSY		= 7,
+	HFP_RESULT_NO_ANSWER	= 8,
+};
+
+enum hfp_error {
+	HFP_ERROR_AG_FAILURE			= 0,
+	HFP_ERROR_NO_CONNECTION_TO_PHONE	= 1,
+	HFP_ERROR_OPERATION_NOT_ALLOWED		= 3,
+	HFP_ERROR_OPERATION_NOT_SUPPORTED	= 4,
+	HFP_ERROR_PH_SIM_PIN_REQUIRED		= 5,
+	HFP_ERROR_SIM_NOT_INSERTED		= 10,
+	HFP_ERROR_SIM_PIN_REQUIRED		= 11,
+	HFP_ERROR_SIM_PUK_REQUIRED		= 12,
+	HFP_ERROR_SIM_FAILURE			= 13,
+	HFP_ERROR_SIM_BUSY			= 14,
+	HFP_ERROR_INCORRECT_PASSWORD		= 16,
+	HFP_ERROR_SIM_PIN2_REQUIRED		= 17,
+	HFP_ERROR_SIM_PUK2_REQUIRED		= 18,
+	HFP_ERROR_MEMORY_FULL			= 20,
+	HFP_ERROR_INVALID_INDEX			= 21,
+	HFP_ERROR_MEMORY_FAILURE		= 23,
+	HFP_ERROR_TEXT_STRING_TOO_LONG		= 24,
+	HFP_ERROR_INVALID_CHARS_IN_TEXT_STRING	= 25,
+	HFP_ERROR_DIAL_STRING_TO_LONG		= 26,
+	HFP_ERROR_INVALID_CHARS_IN_DIAL_STRING	= 27,
+	HFP_ERROR_NO_NETWORK_SERVICE		= 30,
+	HFP_ERROR_NETWORK_TIMEOUT		= 31,
+	HFP_ERROR_NETWORK_NOT_ALLOWED		= 32,
+};
+
+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);
+
+struct hfp_gw;
+
+struct hfp_gw *hfp_gw_new(int fd);
+
+struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp);
+void hfp_gw_unref(struct hfp_gw *hfp);
+
+bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy);
+
+bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close);
+bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive);
+
+bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result);
+bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error);
+bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+
+bool hfp_gw_set_command_handler(struct hfp_gw *hfp,
+				hfp_command_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy);