From 8ddda0351f4869038bd943ece25895863c6c916f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Jan 2014 12:14:38 -0800 Subject: [PATCH] shared: Add simple framework for HFP command processing --- src/shared/hfp.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++ src/shared/hfp.h | 88 +++++++++++ 2 files changed, 481 insertions(+) create mode 100644 src/shared/hfp.c create mode 100644 src/shared/hfp.h diff --git a/src/shared/hfp.c b/src/shared/hfp.c new file mode 100644 index 000000000..c1bfc30bb --- /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 +#endif + +#include +#include +#include +#include + +#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 000000000..2eca6d845 --- /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 + +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); -- 2.47.3