Hi, On 3 November 2014 16:05, Lukasz Rymanowski <lukasz.rymanowski@xxxxxxxxx> wrote: > This patch adds sco lib which gives means to create SCO object with > API to create listening socket, connect and disconnect. > > This is going to be common code for HFP HF and HFP GW. > For now we support only one SCO at the time. > > Before creating listening SCO socket, remember to register confirm > and connect callbacks. Those are called on incoming connection. > > Sco lib is not aware about voice settings. Those must be provided > on bt_sco_connect or in confirm callback. > > This patch also adds this lib to Android build. > --- ping > android/Android.mk | 1 + > android/Makefile.am | 1 + > android/sco.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > android/sco.h | 52 +++++++++ > 4 files changed, 361 insertions(+) > create mode 100644 android/sco.c > create mode 100644 android/sco.h > > diff --git a/android/Android.mk b/android/Android.mk > index aefe41c..dbbcdb2 100644 > --- a/android/Android.mk > +++ b/android/Android.mk > @@ -56,6 +56,7 @@ LOCAL_SRC_FILES := \ > bluez/android/handsfree-client.c \ > bluez/android/gatt.c \ > bluez/android/health.c \ > + bluez/android/sco.c \ > bluez/profiles/health/mcap.c \ > bluez/android/map-client.c \ > bluez/src/log.c \ > diff --git a/android/Makefile.am b/android/Makefile.am > index 496fbc4..4edb007 100644 > --- a/android/Makefile.am > +++ b/android/Makefile.am > @@ -40,6 +40,7 @@ android_bluetoothd_SOURCES = android/main.c \ > android/avrcp.h android/avrcp.c \ > android/avrcp-lib.h android/avrcp-lib.c \ > android/socket.h android/socket.c \ > + android/sco.h android/sco.c \ > android/pan.h android/pan.c \ > android/handsfree.h android/handsfree.c \ > android/handsfree-client.c android/handsfree-client.h \ > diff --git a/android/sco.c b/android/sco.c > new file mode 100644 > index 0000000..ebb81ad > --- /dev/null > +++ b/android/sco.c > @@ -0,0 +1,307 @@ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2014 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 > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#include <stdlib.h> > +#include <stdbool.h> > +#include <errno.h> > + > +#include <glib.h> > + > +#include "lib/bluetooth.h" > +#include "btio/btio.h" > +#include "src/log.h" > + > +#include "sco.h" > + > +struct bt_sco { > + int ref_count; > + > + GIOChannel *server_io; > + > + GIOChannel *io; > + guint watch; > + > + bdaddr_t local_addr; > + bdaddr_t remote_addr; > + > + bt_sco_conn_confirm_cb_func_t confirm_cb; > + bt_sco_conn_cb_func_t connect_cb; > + bt_sco_disconn_cb_func_t disconnect_cb; > +}; > + > +/* We support only one sco for the moment */ > +static bool sco_in_use = false; > + > +struct bt_sco *bt_sco_new(const bdaddr_t *addr) > +{ > + struct bt_sco *sco = NULL; > + > + if (sco_in_use) > + return NULL; > + > + sco = g_try_new0(struct bt_sco, 1); > + if (!sco) > + return NULL; > + > + bacpy(&sco->local_addr, addr); > + > + sco_in_use = true; > + > + return bt_sco_ref(sco); > +} > + > +struct bt_sco *bt_sco_ref(struct bt_sco *sco) > +{ > + if (!sco) > + return NULL; > + > + __sync_fetch_and_add(&sco->ref_count, 1); > + > + return sco; > +} > + > +static void sco_free(struct bt_sco *sco) > +{ > + if (sco->server_io) { > + g_io_channel_shutdown(sco->server_io, TRUE, NULL); > + g_io_channel_unref(sco->server_io); > + } > + > + if (sco->io) { > + g_io_channel_shutdown(sco->io, TRUE, NULL); > + g_io_channel_unref(sco->io); > + } > + > + g_free(sco); > + sco_in_use = false; > +} > + > +void bt_sco_unref(struct bt_sco *sco) > +{ > + DBG(""); > + > + if (!sco) > + return; > + > + if (__sync_sub_and_fetch(&sco->ref_count, 1)) > + return; > + > + sco_free(sco); > +} > + > +static void clear_remote_address(struct bt_sco *sco) > +{ > + memset(&sco->remote_addr, 0, sizeof(bdaddr_t)); > +} > + > +static gboolean disconnect_watch(GIOChannel *chan, GIOCondition cond, > + gpointer user_data) > +{ > + struct bt_sco *sco = user_data; > + > + g_io_channel_shutdown(sco->io, TRUE, NULL); > + g_io_channel_unref(sco->io); > + sco->io = NULL; > + > + DBG(""); > + > + sco->watch = 0; > + > + sco->disconnect_cb(&sco->remote_addr); > + > + clear_remote_address(sco); > + > + return FALSE; > +} > + > +static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data) > +{ > + struct bt_sco *sco = user_data; > + > + DBG(""); > + > + if (err) { > + error("sco: audio connect failed (%s)", err->message); > + > + sco->connect_cb(SCO_STATUS_ERROR, &sco->remote_addr); > + > + clear_remote_address(sco); > + > + return; > + } > + > + g_io_channel_set_close_on_unref(chan, TRUE); > + > + sco->io = g_io_channel_ref(chan); > + sco->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, > + disconnect_watch, sco); > + > + sco->connect_cb(SCO_STATUS_OK, &sco->remote_addr); > +} > + > +static void confirm_sco_cb(GIOChannel *chan, gpointer user_data) > +{ > + char address[18]; > + bdaddr_t bdaddr; > + GError *err = NULL; > + struct bt_sco *sco = user_data; > + uint32_t voice_settings; > + > + DBG(""); > + > + bt_io_get(chan, &err, > + BT_IO_OPT_DEST, address, > + BT_IO_OPT_DEST_BDADDR, &bdaddr, > + BT_IO_OPT_INVALID); > + if (err) { > + error("sco: audio confirm failed (%s)", err->message); > + g_error_free(err); > + goto drop; > + } > + > + if (!sco->confirm_cb(&bdaddr, &voice_settings)) { > + error("sco: audio connection from %s rejected", address); > + goto drop; > + } > + > + bacpy(&sco->remote_addr, &bdaddr); > + > + DBG("incoming SCO connection from %s, voice settings %d", address, > + voice_settings); > + > + err = NULL; > + bt_io_set(chan, &err, BT_IO_OPT_VOICE, voice_settings, > + BT_IO_OPT_INVALID); > + if (err) { > + error("sco: Could not set voice settings (%s)", err->message); > + g_error_free(err); > + goto drop; > + } > + > + if (!bt_io_accept(chan, connect_sco_cb, sco, NULL, NULL)) { > + error("sco: failed to accept audio connection"); > + goto drop; > + } > + > + return; > + > +drop: > + g_io_channel_shutdown(chan, TRUE, NULL); > +} > + > +bool bt_sco_listen(struct bt_sco *sco) > +{ > + GError *err = NULL; > + > + sco->server_io = bt_io_listen(NULL, confirm_sco_cb, sco, NULL, &err, > + BT_IO_OPT_SOURCE_BDADDR, > + &sco->local_addr, > + BT_IO_OPT_INVALID); > + if (!sco->server_io) { > + error("sco: Failed to listen on SCO: %s", err->message); > + g_error_free(err); > + return false; > + } > + > + return true; > +} > + > +bool bt_sco_connect(struct bt_sco *sco, const bdaddr_t *addr, > + uint16_t voice_settings) > +{ > + GIOChannel *io; > + GError *gerr = NULL; > + > + DBG(""); > + > + io = bt_io_connect(connect_sco_cb, sco, NULL, &gerr, > + BT_IO_OPT_SOURCE_BDADDR, &sco->local_addr, > + BT_IO_OPT_DEST_BDADDR, addr, > + BT_IO_OPT_VOICE, voice_settings, > + BT_IO_OPT_INVALID); > + > + if (!io) { > + error("sco: unable to connect audio: %s", gerr->message); > + g_error_free(gerr); > + return false; > + } > + > + g_io_channel_unref(io); > + > + bacpy(&sco->remote_addr, addr); > + > + return true; > +} > + > +void bt_sco_disconnect(struct bt_sco *sco) > +{ > + if (sco->watch) { > + g_source_remove(sco->watch); > + sco->watch = 0; > + } > + > + g_io_channel_shutdown(sco->io, TRUE, NULL); > + g_io_channel_unref(sco->io); > + sco->io = NULL; > + > + clear_remote_address(sco); > +} > + > +bool bt_sco_get_fd_and_mtu(struct bt_sco *sco, int *fd, uint16_t *mtu) > +{ > + GError *err; > + > + if (!sco->io || !fd || !mtu) > + return false; > + > + err = NULL; > + if (!bt_io_get(sco->io, &err, BT_IO_OPT_MTU, mtu, BT_IO_OPT_INVALID)) { > + error("Unable to get MTU: %s\n", err->message); > + g_clear_error(&err); > + return false; > + } > + > + *fd = g_io_channel_unix_get_fd(sco->io); > + > + return true; > +} > + > +void bt_sco_set_confirm_cb(struct bt_sco *sco, > + bt_sco_conn_confirm_cb_func_t func) > +{ > + sco->confirm_cb = func; > +} > + > +void bt_sco_set_connect_cb(struct bt_sco *sco, bt_sco_conn_cb_func_t func) > +{ > + sco->connect_cb = func; > +} > + > +void bt_sco_set_disconnect_cb(struct bt_sco *sco, > + bt_sco_disconn_cb_func_t func) > +{ > + sco->disconnect_cb = func; > +} > diff --git a/android/sco.h b/android/sco.h > new file mode 100644 > index 0000000..491e633 > --- /dev/null > +++ b/android/sco.h > @@ -0,0 +1,52 @@ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2014 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 > + * > + */ > + > +enum sco_status { > + SCO_STATUS_OK, > + SCO_STATUS_ERROR, > +}; > + > +struct bt_sco; > + > +struct bt_sco *bt_sco_new(const bdaddr_t *adapter_addr); > + > +struct bt_sco *bt_sco_ref(struct bt_sco *sco); > +void bt_sco_unref(struct bt_sco *sco); > + > +bool bt_sco_listen(struct bt_sco *sco); > +bool bt_sco_connect(struct bt_sco *sco, const bdaddr_t *remote_addr, > + uint16_t voice_settings); > +void bt_sco_disconnect(struct bt_sco *sco); > +bool bt_sco_get_fd_and_mtu(struct bt_sco *sco, int *fd, uint16_t *mtu); > + > +typedef bool (*bt_sco_conn_confirm_cb_func_t) (const bdaddr_t *remote_addr, > + uint32_t *voice_settings); > +typedef void (*bt_sco_conn_cb_func_t) (enum sco_status status, > + const bdaddr_t *addr); > +typedef void (*bt_sco_disconn_cb_func_t) (const bdaddr_t *addr); > + > +void bt_sco_set_confirm_cb(struct bt_sco *sco, > + bt_sco_conn_confirm_cb_func_t func); > +void bt_sco_set_connect_cb(struct bt_sco *sco, bt_sco_conn_cb_func_t func); > +void bt_sco_set_disconnect_cb(struct bt_sco *sco, > + bt_sco_disconn_cb_func_t func); > -- > 1.8.4 > -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html