summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Bultel <thierry.bultel@iot.bzh>2018-12-05 14:05:10 +0100
committerThierry Bultel <thierry.bultel@iot.bzh>2018-12-20 16:55:46 +0000
commit01d55ed8cdd01ca4a7b391ca61c80084bd5a6f2f (patch)
treed73ce778a2177e82e07e9d31891a827467fbadd4
parentdbe555be80dd10bb315b14bbebcb42f8b883e2c9 (diff)
Adds support for bluetooth audio through bluez-alsaguppy_6.99.4guppy_6.99.3guppy/6.99.4guppy/6.99.36.99.46.99.3
Implements a new bluealsa plugin to the HAL manager, reacting to the changes of the available transports. This plugin is linked with the new bluealsa.so shared library. New transports (SCO & A2DP) result in softmixer invocations of the "attach" verb, that creates the new capture (eg, A2DP capture from bluealsa ioplug PCM, SCO microphone capture), playbacks (SCO playback to a softmixer zone, and SCO output to bluealsa iogplug PCM). When a transport disappears, the hal manager calls the transaction deletion verb that will tell the softmixer to remove the created streams and associated objects. Change-Id: I36037a4f14ef7fee38070fc0df66c40b4ce46e8b Signed-off-by: Thierry Bultel <thierry.bultel@iot.bzh>
-rw-r--r--4a-hal-cfg-example/hal-4a-2ch-generic-usb.json17
-rw-r--r--plugins/lib/bluealsa/CMakeLists.txt (renamed from plugins/lib/bluetooth/CMakeLists.txt)18
-rw-r--r--plugins/lib/bluealsa/hal-bluealsa-transports.c153
-rw-r--r--plugins/lib/bluealsa/hal-bluealsa-transports.h53
-rw-r--r--plugins/lib/bluealsa/hal-bluealsa-watch.h (renamed from plugins/lib/bluetooth/hal-bt.h)25
-rw-r--r--plugins/lib/bluealsa/hal-bluealsa.c723
-rw-r--r--plugins/lib/bluealsa/hal-bluealsa.h21
-rw-r--r--plugins/lib/bluetooth/hal-bt-cb.c209
-rw-r--r--plugins/lib/bluetooth/hal-bt-cb.h40
-rw-r--r--plugins/lib/bluetooth/hal-bt-data.c277
-rw-r--r--plugins/lib/bluetooth/hal-bt-data.h64
-rw-r--r--plugins/lib/bluetooth/hal-bt-mixer-link.c80
-rw-r--r--plugins/lib/bluetooth/hal-bt-mixer-link.h65
-rw-r--r--plugins/lib/bluetooth/hal-bt.c437
14 files changed, 983 insertions, 1199 deletions
diff --git a/4a-hal-cfg-example/hal-4a-2ch-generic-usb.json b/4a-hal-cfg-example/hal-4a-2ch-generic-usb.json
index efc59e3..be3bdb6 100644
--- a/4a-hal-cfg-example/hal-4a-2ch-generic-usb.json
+++ b/4a-hal-cfg-example/hal-4a-2ch-generic-usb.json
@@ -11,13 +11,20 @@
},
"resources": [
{
- "uid": "hal-bt",
- "info": "Bluetooth hal plugin to get A2DP bluetooth device as an input by tweaking softmixer configuration",
+ "uid": "hal-bluealsa",
+ "info": "Bluealsa hal plugin",
"params": {
- "channels": 2,
- "zone": "front-seats"
+ "sco": {
+ "mic": "2CH-GENERIC-USB",
+ "zone": "full-stereo",
+ "delayms": 800
+ },
+ "a2dp": {
+ "zone": "full-stereo",
+ "delayms": 500
+ }
},
- "libs": ["hal-bt.ctlso"]
+ "libs": ["hal-bluealsa.ctlso"]
}
],
"controls": [
diff --git a/plugins/lib/bluetooth/CMakeLists.txt b/plugins/lib/bluealsa/CMakeLists.txt
index ae9f5ab..699ccc4 100644
--- a/plugins/lib/bluetooth/CMakeLists.txt
+++ b/plugins/lib/bluealsa/CMakeLists.txt
@@ -1,7 +1,7 @@
###########################################################################
-# Copyright 2015, 2016, 2017, 2018 IoT.bzh
+# Copyright 2018 IoT.bzh
#
-# author: Jonathan Aillet <jonathan.aillet@iot.bzh>
+# author: Thierry Bultel <thierry.bultel@iot.bzh>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,14 +16,15 @@
# limitations under the License.
###########################################################################
-PROJECT_TARGET_ADD(hal-bt)
+
+PROJECT_TARGET_ADD(hal-bluealsa)
+
+ pkg_check_modules(BLUEALSA REQUIRED bluealsa>=1.3.1)
# Define targets
ADD_LIBRARY(${TARGET_NAME} MODULE
- hal-bt.c
- hal-bt-cb.c
- hal-bt-data.c
- hal-bt-mixer-link.c
+ hal-bluealsa.c
+ hal-bluealsa-transports.c
)
# Alsa Plugin properties
@@ -38,9 +39,12 @@ PROJECT_TARGET_ADD(hal-bt)
TARGET_LINK_LIBRARIES(${TARGET_NAME}
afb-helpers
ctl-utilities
+ ${BLUEALSA_LIBRARIES}
${link_libraries}
)
+ TARGET_COMPILE_OPTIONS(${TARGET_NAME} PUBLIC ${BLUEALSA_CFLAGS})
+
target_include_directories(${TARGET_NAME}
PRIVATE "${CMAKE_SOURCE_DIR}/app-controller/ctl-lib"
PRIVATE "${CMAKE_SOURCE_DIR}/4a-hal/4a-hal-utilities"
diff --git a/plugins/lib/bluealsa/hal-bluealsa-transports.c b/plugins/lib/bluealsa/hal-bluealsa-transports.c
new file mode 100644
index 0000000..2c58023
--- /dev/null
+++ b/plugins/lib/bluealsa/hal-bluealsa-transports.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author : Thierry Bultel <thierry.bultel@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <malloc.h>
+
+#include "hal-bluealsa-transports.h"
+
+
+static bool halBlueAlsaTransportMatch(
+ const struct ba_msg_transport * transport1,
+ const struct ba_msg_transport * transport2);
+
+int halBlueAlsaTransportsInit(bluealsa_transport_t * list) {
+ CDS_INIT_LIST_HEAD(&list->list);
+ return 0;
+}
+
+bluealsa_transport_t * halBlueAlsaTransportsAdd(
+ const bluealsa_watch * watch,
+ bluealsa_transport_t * list,
+ const struct ba_msg_transport * transport) {
+
+ bluealsa_transport_t * newTransport = (bluealsa_transport_t*) calloc(1, sizeof(bluealsa_transport_t));
+ if (newTransport == NULL)
+ goto fail;
+
+ memcpy(&newTransport->transport, transport, sizeof(struct ba_msg_transport));
+
+ newTransport->watch = watch;
+
+ CDS_INIT_LIST_HEAD(&newTransport->list);
+ cds_list_add(&newTransport->list, &list->list);
+
+ return newTransport;
+fail:
+ return NULL;
+
+}
+
+bool halBlueAlsaTransportFind(
+ const bluealsa_watch * watch,
+ bluealsa_transport_t * list,
+ const struct ba_msg_transport * transport) {
+
+ bluealsa_transport_t *tmp;
+ bool found = false;
+ cds_list_for_each_entry(tmp, &list->list, list) {
+ struct ba_msg_transport * _transport = &tmp->transport;
+
+ if (watch != tmp->watch)
+ continue;
+
+ if (!halBlueAlsaTransportMatch(_transport, transport))
+ continue;
+
+ found = true;
+ break;
+ }
+ return found;
+}
+
+
+int halBlueAlsaTransportUpdate(
+ const bluealsa_watch * watch,
+ bluealsa_transport_t * list,
+ const struct ba_msg_transport *transports,
+ size_t nb,
+ transport_destructor destructor) {
+
+ bluealsa_transport_t *transport, *sav;
+
+ cds_list_for_each_entry_safe(transport, sav, &list->list, list) {
+ struct ba_msg_transport * ba_transport1 = &transport->transport;
+
+ if (watch != transport->watch)
+ continue;
+
+ bool found = false;
+
+ for (int ix = 0; ix<nb; ix++) {
+ const struct ba_msg_transport * ba_transport2 = &transports[ix];
+
+ if (halBlueAlsaTransportMatch(ba_transport1, ba_transport2)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ char transportS[HAL_BLUEALSA_TRANSPORT_LEN_MAX];
+
+ AFB_ApiInfo(watch->plugin->api,
+ "Unregister transport %s",
+ halBlueAlsaTransportAsString(transportS, HAL_BLUEALSA_TRANSPORT_LEN_MAX, &transport->transport));
+
+ cds_list_del(&transport->list);
+ if (destructor) {
+ destructor(transport);
+ }
+
+ free(transport->transactionUidS);
+ free(transport);
+
+ }
+
+ return 0;
+}
+
+
+static bool halBlueAlsaTransportMatch(const struct ba_msg_transport * transport1, const struct ba_msg_transport * transport2) {
+ bool ret = false;
+
+ if (transport1->type != transport2->type)
+ goto done;
+
+ if (transport1->stream != transport2->stream)
+ goto done;
+
+ if (bacmp(&transport1->addr, &transport2->addr) != 0) {
+ goto done;
+ }
+ ret = true;
+done:
+ return ret;
+}
+
+
+char * halBlueAlsaTransportAsString(char * buff, size_t len, const struct ba_msg_transport * transport) {
+ char addr[18];
+
+ ba2str(&transport->addr, addr);
+ snprintf(buff, len, "%s,%s,%s", addr,
+ transport->type == BA_PCM_TYPE_A2DP?"a2dp":"sco",
+ transport->stream == BA_PCM_STREAM_PLAYBACK?"playback":"capture");
+
+ return buff;
+}
diff --git a/plugins/lib/bluealsa/hal-bluealsa-transports.h b/plugins/lib/bluealsa/hal-bluealsa-transports.h
new file mode 100644
index 0000000..0c5d6a4
--- /dev/null
+++ b/plugins/lib/bluealsa/hal-bluealsa-transports.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author : Thierry Bultel <thierry.bultel@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INC_HAL_BLUEALSA_TRANSPORT_H
+#define __INC_HAL_BLUEALSA_TRANSPORT_H
+
+#include <bluealsa/bluealsa.h>
+#include <stdbool.h>
+#include <urcu/list.h>
+
+#include "afb-definitions.h"
+#include "hal-bluealsa-watch.h"
+
+typedef struct {
+ const bluealsa_watch * watch; /* owner */
+ struct cds_list_head list;
+ struct ba_msg_transport transport;
+ char * transactionUidS;
+} bluealsa_transport_t;
+
+extern int halBlueAlsaTransportsInit(bluealsa_transport_t * list);
+
+extern bluealsa_transport_t * halBlueAlsaTransportsAdd(const bluealsa_watch * watch, bluealsa_transport_t * list, const struct ba_msg_transport * transport);
+extern bool halBlueAlsaTransportFind(const bluealsa_watch * watch, bluealsa_transport_t * list, const struct ba_msg_transport * transport);
+
+typedef int (*transport_destructor)(bluealsa_transport_t *);
+
+extern int halBlueAlsaTransportUpdate(
+ const bluealsa_watch * watch,
+ bluealsa_transport_t * list,
+ const struct ba_msg_transport *transports,
+ size_t nb,
+ transport_destructor);
+
+#define HAL_BLUEALSA_TRANSPORT_LEN_MAX 64
+
+extern char * halBlueAlsaTransportAsString(char * s, size_t len, const struct ba_msg_transport * );
+
+#endif /* __INC_HAL_BLUEALSA_TRANSPORT_H */
diff --git a/plugins/lib/bluetooth/hal-bt.h b/plugins/lib/bluealsa/hal-bluealsa-watch.h
index af5f6c3..114f515 100644
--- a/plugins/lib/bluetooth/hal-bt.h
+++ b/plugins/lib/bluealsa/hal-bluealsa-watch.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
+ * Author : Thierry Bultel <thierry.bultel@iot.bzh>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,20 +15,15 @@
* limitations under the License.
*/
-#ifndef _HAL_BT_INCLUDE_
-#define _HAL_BT_INCLUDE_
+#ifndef __INC_HAL_BLUEALSA_WATCH_H
+#define __INC_HAL_BLUEALSA_WATCH_H
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
+#include <ctl-config.h>
-#define HAL_BT_PLUGIN_NAME "hal-bt"
+typedef struct {
+ int fd;
+ const char * interface;
+ CtlPluginT * plugin;
+} bluealsa_watch;
-#define BT_MANAGER_API "Bluetooth-Manager"
-#define BT_MANAGER_GET_POWER_INFO "power"
-#define BT_MANAGER_SUBSCRIBE_VERB "subscribe"
-#define BT_MANAGER_GET_DEVICES_VERB "discovery_result"
-
-#define BT_MANAGER_DEVICE_UPDATE_EVENT "device_updated"
-
-#endif /* _HAL_BT_INCLUDE_ */ \ No newline at end of file
+#endif /* __INC_HAL_BLUEALSA_WATCH_H */
diff --git a/plugins/lib/bluealsa/hal-bluealsa.c b/plugins/lib/bluealsa/hal-bluealsa.c
new file mode 100644
index 0000000..c8cfe98
--- /dev/null
+++ b/plugins/lib/bluealsa/hal-bluealsa.c
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author : Thierry Bultel <thierry.bultel@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+
+#include <wrap-json.h>
+
+#include <errno.h>
+#include <ctl-config.h>
+#include <systemd/sd-event.h>
+#include <bluealsa/bluealsa.h>
+
+#include "hal-bluealsa.h"
+
+#define HAL_BLUEALSA_PLUGIN_NAME "hal-bluealsa"
+
+#define SCO_TALK_RATE 44100
+#define SCO_TALK_FORMAT "S16_LE"
+
+#define SCO_TALK_ZONE "sco_talk_zone"
+#define SCO_CHANNEL_MONO "sco-mono"
+
+/* forward static declarations */
+static int halBlueAlsaTransportEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData);
+static int halBlueAlsaFetchTransports(bluealsa_watch * plugin);
+static int halBlueAlsaRegisterAll(CtlPluginT* plugin);
+static int halBlueAlsaRegister(CtlPluginT* plugin, const char * interface);
+
+CTLP_CAPI_REGISTER(HAL_BLUEALSA_PLUGIN_NAME)
+
+
+// Call at initialization time ('requires' are forbidden at this stage)
+CTLP_ONLOAD(plugin, callbacks)
+{
+ AFB_ApiNotice(plugin->api, "%s Plugin Registered correctly: uid='%s' 'info='%s'", HAL_BLUEALSA_PLUGIN_NAME, plugin->uid, plugin->info);
+ return 0;
+}
+
+
+CTLP_INIT(plugin, callbacks)
+{
+ json_object *actionsToAdd = NULL;
+ CtlConfigT *ctrlConfig;
+
+ wrap_json_pack(&actionsToAdd, "{s:s s:s s:s}",
+ "uid", "init-bluealsa-plugin",
+ "info", "Init Bluez-Alsa hal plugin",
+ "action", "plugin://hal-bluealsa#init");
+
+ if (!(ctrlConfig = (CtlConfigT *) AFB_ApiGetUserData(plugin->api))) {
+ AFB_ApiError(plugin->api, "Can't get current hal controller config");
+ goto fail;
+ }
+
+ int idx = 0;
+ while (ctrlConfig->sections[idx].key && strcasecmp(ctrlConfig->sections[idx].key, "onload"))
+ idx++;
+
+ if (!ctrlConfig->sections[idx].key) {
+ AFB_ApiError(plugin->api, "Wasn't able to add '%s' as a new onload, 'onload' section not found", json_object_get_string(actionsToAdd));
+ goto fail;
+ }
+
+ if(AddActionsToSection(plugin->api, &ctrlConfig->sections[idx], actionsToAdd, 0)) {
+ AFB_ApiError(plugin->api, "Wasn't able to add '%s' as a new onload to %s", json_object_get_string(actionsToAdd), ctrlConfig->sections[idx].uid);
+ goto fail;
+ }
+
+ AFB_ApiNotice(plugin->api, "Plugin initialization of %s plugin correctly done", HAL_BLUEALSA_PLUGIN_NAME);
+
+ return 0;
+fail:
+ json_object_put(actionsToAdd);
+ return -1;
+}
+
+
+// Call at controller onload time
+CTLP_CAPI(init, source, argsJ, queryJ)
+{
+ AFB_ApiNotice(source->api, "Controller onload event");
+
+ CtlPluginT * plugin = source->plugin;
+
+ hal_bluealsa_plugin_data_t * pluginData = (hal_bluealsa_plugin_data_t*) calloc(1, sizeof(hal_bluealsa_plugin_data_t));
+ int error;
+
+ /*
+ * "params": {
+ "sco": {
+ "mic": "2CH-GENERIC-USB",
+ "zone": "full-stereo",
+ "delayms": 300
+ },
+ "a2dp": {
+ "zone": "full-stereo",
+ "delayms": 1000
+ }
+ }
+ */
+
+ json_object * scoParamsJ = NULL;
+ json_object * a2dpParamsJ = NULL;
+
+ error = wrap_json_unpack(plugin->paramsJ, "{s?o,s?o !}",
+ "sco", &scoParamsJ,
+ "a2dp", &a2dpParamsJ);
+
+ if (error) {
+ AFB_ApiError(plugin->api, "%s: wrong parameters", __func__);
+ goto fail;
+ }
+
+ if (scoParamsJ) {
+ AFB_ApiInfo(plugin->api, "%s: sco parameters: %s", __func__, json_object_get_string(scoParamsJ));
+ error = wrap_json_unpack(scoParamsJ, "{s:s,s:s,s?i !}",
+ "mic", &pluginData->sco.mic,
+ "zone", &pluginData->sco.speaker,
+ "delayms", &pluginData->sco.delayms);
+ if (error) {
+ AFB_ApiError(plugin->api, "%s: wrong sco parameters: err %s", __func__, wrap_json_get_error_string(error));
+ goto fail;
+ }
+ }
+
+ if (a2dpParamsJ) {
+ AFB_ApiInfo(plugin->api, "%s: a2dp parameters: %s", __func__, json_object_get_string(a2dpParamsJ));
+ error = wrap_json_unpack(a2dpParamsJ, "{s:s,s?i !}",
+ "zone", &pluginData->a2dp.zone,
+ "delayms", &pluginData->a2dp.delayms);
+ if (error) {
+ AFB_ApiError(plugin->api, "%s: wrong a2dp parameters: err=%s", __func__, wrap_json_get_error_string(error));
+ goto fail;
+ }
+ }
+
+ halBlueAlsaTransportsInit(&pluginData->transport_list);
+
+ setPluginContext(plugin, pluginData);
+
+ if (halBlueAlsaRegisterAll(plugin) != 0)
+ goto fail;
+
+ return 0;
+fail:
+ return -1;
+}
+
+
+static int halBlueAlsaTransportEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData) {
+
+ bluealsa_watch * watch = (bluealsa_watch *)userData;
+ CtlPluginT * plugin = watch->plugin;
+
+ struct ba_msg_event event;
+ ssize_t ret;
+
+ AFB_ApiDebug(plugin->api, "--- %s ----!", __func__);
+
+ if ((revents & EPOLLIN) == 0)
+ goto done;
+
+ if (revents & EPOLLHUP) {
+ AFB_ApiInfo(plugin->api, "Lost connection with bluealsa on interface %s", watch->interface);
+ sd_event_source_unref(src);
+ close(fd);
+ halBlueAlsaRegister(plugin, watch->interface);
+ goto done;
+ }
+
+ while ((ret = recv(watch->fd, &event, sizeof(event), MSG_DONTWAIT)) == -1 && errno == EINTR)
+ continue;
+
+ if (ret != sizeof(event)) {
+ AFB_ApiError(plugin->api, "Couldn't read event: %s", strerror(ret == -1 ? errno : EBADMSG));
+ goto done;
+ }
+
+ halBlueAlsaFetchTransports(watch);
+
+done:
+ return 0;
+}
+
+
+/*
+ *
+ * The following function builds that kind of json data and send it to smixer
+ *
+ * A2DP Case:
+ *
+ * {
+ "uid":"a2dp_F6:32:15:2A:80:70",
+ "captures":
+ {
+ "uid":"a2dp_listen_capture",
+ "pcmplug_params" : "bluealsa:HCI=hci0,DEV=F6:32:15:2A:80:70,PROFILE=a2dp",
+ "source" : {
+ "channels": [
+ {
+ "uid": "a2dp-right",
+ "port": 0
+ },
+ {
+ "uid": "a2dp-left",
+ "port": 1
+ }
+ ]
+ }
+ },
+ "streams":
+ {
+ "uid" : "a2dp_listen_stream",
+ "source":"a2dp_listen_capture",
+ "zone": "full-stereo"
+ }
+ }
+ *
+ *-----------------------------------------------------------------------------------
+ *
+ * SCO Case:
+ *
+ * {
+ "uid":"sco_F6:32:15:2A:80:70",",
+ "captures":
+ {
+ "uid":"sco_listen_capture",
+ "pcmplug_params" : "bluealsa:HCI=hci0,DEV=F6:32:15:2A:80:70,PROFILE=sco",
+ "source" : {
+ "channels": [
+ {
+ "uid": "sco-right",
+ "port": 0
+ },
+ {
+ "uid": "sco-left",
+ "port": 1
+ }
+ ]
+ }
+ },
+ "playbacks" :
+ {
+ "uid"="sco_talk_playback",
+ "pcmplug_params" : "bluealsa:HCI=hci0,DEV=F6:32:15:2A:80:70,PROFILE=sco",
+ "params": {
+ "rate": 44100,
+ "format": "S16_LE",
+ },
+ "sink": {
+ "channels": [
+ {
+ "uid": "sco-mono"
+ }
+ ]
+ }
+ },
+ "zones" : [
+ {
+ "uid": "sco_talk_zone",
+ "sink": [
+ {
+ "target": "sco-mono",
+ "channel": 0,
+ "volume": 0.5
+ },
+ {
+ "target": "sco-mono",
+ "channel": 1,
+ "volume": 0.5
+ }
+ ]
+ }
+ ] ,
+ "streams": [
+ {
+ "uid" : "sco_listen_stream",
+ "source":"sco_listen_capture",
+ "zone": "full-stereo",
+ "delayms": 300
+ },
+ {
+ "uid" : "sco_talk_stream",
+ "source": "2CH-GENERIC-USB",
+ "zone": "sco_talk_zone",
+ "delayms": 300
+ }
+ ]
+ }
+ *
+ * */
+
+static json_object* halBlueAlsaA2DPTransportChannels(const char * transportTypeS) {
+ json_object* channelRightJ;
+ json_object* channelLeftJ;
+ json_object* channelsJ = json_object_new_array();
+ char * channelRightS;
+ char * channelLeftS;
+
+ channelRightJ = json_object_new_object();
+ if (asprintf(&channelRightS, "%s-right", transportTypeS) == -1)
+ goto fail;
+
+ json_object_object_add(channelRightJ, "uid", json_object_new_string(channelRightS));
+ json_object_object_add(channelRightJ, "port", json_object_new_int(0));
+
+ channelLeftJ = json_object_new_object();
+ if (asprintf(&channelLeftS, "%s-left", transportTypeS) == -1)
+ goto fail;
+
+ json_object_object_add(channelLeftJ, "uid", json_object_new_string(channelLeftS));
+ json_object_object_add(channelLeftJ, "port", json_object_new_int(1));
+
+ json_object_array_add(channelsJ, channelRightJ);
+ json_object_array_add(channelsJ, channelLeftJ);
+fail:
+ return channelsJ;
+}
+
+
+static json_object* halBlueAlsaSCOTransportChannels(const char * transportTypeS) {
+ json_object* channelMonoJ;
+
+ json_object* channelsJ = json_object_new_array();
+ char * channelMonoS;
+
+ channelMonoJ = json_object_new_object();
+ if (asprintf(&channelMonoS, "%s-mono", transportTypeS) == -1)
+ goto fail;
+
+ json_object_object_add(channelMonoJ, "uid", json_object_new_string(channelMonoS));
+ json_object_object_add(channelMonoJ, "port", json_object_new_int(0));
+
+ json_object_array_add(channelsJ, channelMonoJ);
+
+fail:
+ return channelsJ;
+}
+
+
+static json_object* halBlueAlsaPcmPlugParams(const char * interface, const char * addr, const char * transportTypeS) {
+ char * pcmplug_paramsS = NULL;
+ if (asprintf(&pcmplug_paramsS, "bluealsa:HCI=%s,DEV=%s,PROFILE=%s", interface, addr, transportTypeS) == -1)
+ goto fail;
+ return json_object_new_string(pcmplug_paramsS);
+fail:
+ return NULL;
+}
+
+
+static json_object* halBlueAlsaListenCapture(
+ const char * listenCaptureS,
+ json_object * pcmplugParamsJ,
+ json_object * sourceJ) {
+
+ json_object * captureJ = json_object_new_object();
+
+ json_object_object_add(captureJ, "uid", json_object_new_string(listenCaptureS));
+ json_object_object_add(captureJ, "pcmplug_params", pcmplugParamsJ);
+ json_object_object_add(captureJ, "source", sourceJ);
+
+ return captureJ;
+}
+
+static json_object * halBlueAlsaTalkPlayback(
+ const char * talkPlaybackS,
+ json_object* pcmplugParamsJ,
+ json_object* paramsJ,
+ json_object* sinkJ ) {
+
+ json_object * playbackJ = json_object_new_object();
+
+ json_object_object_add(playbackJ, "uid", json_object_new_string(talkPlaybackS));
+ json_object_object_add(playbackJ, "pcmplug_params", pcmplugParamsJ);
+ json_object_object_add(playbackJ, "params", paramsJ);
+ json_object_object_add(playbackJ, "sink", sinkJ);
+
+ return playbackJ;
+}
+
+static json_object * halBlueAlsaScoTalkParamsJ() {
+ json_object * paramsJ = json_object_new_object();
+
+ json_object_object_add(paramsJ, "rate", json_object_new_int(SCO_TALK_RATE));
+ json_object_object_add(paramsJ, "format", json_object_new_string(SCO_TALK_FORMAT));
+ return paramsJ;
+}
+
+static json_object * halBlueAlsaScoZone() {
+ json_object * zoneJ = json_object_new_object();
+ json_object * sinkJ = json_object_new_array();
+ json_object_object_add(zoneJ, "uid", json_object_new_string(SCO_TALK_ZONE));
+
+ json_object * channel1J = json_object_new_object();
+ json_object_object_add(channel1J, "target" , json_object_new_string(SCO_CHANNEL_MONO));
+ json_object_object_add(channel1J, "channel", json_object_new_int(0));
+ json_object_object_add(channel1J, "volume", json_object_new_double(0.5));
+
+ json_object * channel2J = json_object_new_object();
+ json_object_object_add(channel2J, "target" , json_object_new_string(SCO_CHANNEL_MONO));
+ json_object_object_add(channel2J, "channel", json_object_new_int(1));
+ json_object_object_add(channel2J, "volume", json_object_new_double(0.5));
+
+ json_object_array_add(sinkJ, channel1J);
+ json_object_array_add(sinkJ, channel2J);
+
+ json_object_object_add(zoneJ, "sink", sinkJ);
+
+ return zoneJ;
+}
+
+static int halBlueAlsaAttachTransportStreams(bluealsa_transport_t * transport) {
+ int ret = -1;
+ const char* transportTypeS;
+
+ struct ba_msg_transport * ba_transport = &transport->transport;
+ const bluealsa_watch * watch = transport->watch;
+
+ CtlPluginT* plugin = watch->plugin;
+ hal_bluealsa_plugin_data_t * pluginData=getPluginContext(plugin);
+
+ json_object* requestJ = NULL;
+ json_object* returnJ = NULL;
+
+ json_object* sourceJ = NULL;
+ json_object* playbackJ = NULL;
+
+ json_object* pcmplugParamsJ, *pcmplugParamsJ2 = NULL;
+
+ json_object* streamsJ = NULL;
+ json_object* streamJ = NULL;
+
+ json_object* zonesJ = NULL;
+
+ char * playbackZoneS;
+ uint32_t delayms;
+
+ if (ba_transport->type == BA_PCM_TYPE_SCO) {
+ transportTypeS = "sco";
+ delayms = pluginData->sco.delayms;
+ playbackZoneS = pluginData->sco.speaker;
+ }
+ else if (ba_transport->type == BA_PCM_TYPE_A2DP) {
+ transportTypeS = "a2dp";
+ delayms = pluginData->a2dp.delayms;
+ playbackZoneS = pluginData->a2dp.zone;
+ } else {
+ AFB_ApiError(plugin->api, "%s: unsupported transport type", __func__ );
+ goto fail;
+ }
+
+ char * captureS = NULL;
+ if (asprintf(&captureS, "%s_listen_capture", transportTypeS) == -1)
+ goto fail;
+
+ // for SCO only
+ char * playbackS = NULL;
+ if (asprintf(&playbackS, "%s_talk_playback", transportTypeS) == -1)
+ goto fail;
+
+ char * transactionUidS = NULL;
+ char * streamS = NULL;
+
+ char addr[18];
+ ba2str(&ba_transport->addr, addr);
+
+ requestJ = json_object_new_object();
+ if (!requestJ)
+ goto fail;
+
+ if (asprintf(&transactionUidS, "%s_%s", transportTypeS, addr) == -1)
+ goto fail;
+
+ json_object_object_add(requestJ, "uid", json_object_new_string(transactionUidS));
+
+ pcmplugParamsJ = halBlueAlsaPcmPlugParams(watch->interface, addr, transportTypeS);
+
+ sourceJ = json_object_new_object();
+ if (ba_transport->type == BA_PCM_TYPE_A2DP)
+ json_object_object_add(sourceJ, "channels", halBlueAlsaA2DPTransportChannels(transportTypeS));
+ else
+ json_object_object_add(sourceJ, "channels", halBlueAlsaSCOTransportChannels(transportTypeS));
+
+ json_object_object_add(requestJ, "captures", halBlueAlsaListenCapture(captureS, pcmplugParamsJ, sourceJ));
+
+ if (ba_transport->type == BA_PCM_TYPE_SCO) {
+ playbackJ = json_object_new_object();
+ // that is a shame that deep copy in not available in the current json-c version
+ pcmplugParamsJ2 = halBlueAlsaPcmPlugParams(watch->interface, addr, transportTypeS);
+ json_object * paramsJ = halBlueAlsaScoTalkParamsJ();
+
+ json_object_object_add(playbackJ, "channels", halBlueAlsaSCOTransportChannels(transportTypeS));
+ json_object_object_add(requestJ, "playbacks", halBlueAlsaTalkPlayback(playbackS, pcmplugParamsJ2, paramsJ, playbackJ));
+
+ }
+
+ /* ZONES */
+
+ if (ba_transport->type == BA_PCM_TYPE_SCO) {
+ zonesJ = json_object_new_array();
+ json_object_array_add(zonesJ, halBlueAlsaScoZone());
+ json_object_object_add(requestJ, "zones", zonesJ);
+ }
+
+ /* STREAMS */
+
+ /* Build the array of streams */
+ streamsJ = json_object_new_array();
+
+ streamJ = json_object_new_object();
+ if (asprintf(&streamS, "%s_listen_stream", transportTypeS) == -1)
+ goto fail;
+
+ json_object_object_add(streamJ, "uid", json_object_new_string(streamS));
+ json_object_object_add(streamJ, "source", json_object_new_string(captureS));
+ json_object_object_add(streamJ, "zone", json_object_new_string(playbackZoneS));
+ if (delayms != 0) {
+ json_object_object_add(streamJ, "delayms", json_object_new_int(delayms));
+ }
+
+ json_object_array_add(streamsJ, streamJ);
+
+ /* In case of SCO, to have full-duplex, we instantiate a stream for talk */
+ if (ba_transport->type == BA_PCM_TYPE_SCO ) {
+
+ streamJ = json_object_new_object();
+ if (asprintf(&streamS, "%s_talk_stream", transportTypeS) == -1)
+ goto fail;
+
+ json_object_object_add(streamJ, "uid", json_object_new_string(streamS));
+ json_object_object_add(streamJ, "source", json_object_new_string(pluginData->sco.mic));
+ json_object_object_add(streamJ, "zone", json_object_new_string(SCO_TALK_ZONE));
+ if (delayms != 0)
+ json_object_object_add(streamJ, "delayms", json_object_new_int(delayms));
+ json_object_array_add(streamsJ, streamJ);
+ }
+
+ json_object_object_add(requestJ, "streams", streamsJ);
+
+ /* In softmixer, this will create a transaction verb (whose name is transactionUidS),
+ * will be used later to destroy all the created objects upon transport removal */
+
+ if (AFB_ServiceSync(plugin->api, SMIXER_API_NAME, "attach", requestJ, &returnJ)) {
+ AFB_ApiError(plugin->api, "Error calling attach verb of mixer" );
+ goto done;
+ }
+
+ transport->transactionUidS = transactionUidS;
+
+ if (returnJ)
+ json_object_put(returnJ);
+
+ ret = 0;
+ goto done;
+
+fail:
+ return -1;
+done:
+ AFB_ApiDebug(plugin->api, "DONE.");
+ return ret;
+}
+
+
+static int halBluezAlsaRemoveTransportStream(bluealsa_transport_t * transport) {
+
+ CtlPluginT * plugin = transport->watch->plugin;
+ json_object* requestJ = NULL;
+ json_object* returnJ = NULL;
+
+ AFB_ApiInfo(plugin->api, "Call transaction detach verb %s", transport->transactionUidS);
+ if (transport->transactionUidS == NULL)
+ goto fail;
+
+ requestJ = json_object_new_object();
+ if (!requestJ)
+ goto fail;
+
+ json_object_object_add(requestJ, "action", json_object_new_string("remove"));
+
+ if (AFB_ServiceSync(plugin->api, SMIXER_API_NAME, transport->transactionUidS, requestJ, &returnJ)) {
+ AFB_ApiError(plugin->api, "Error calling attach verb of mixer" );
+ goto fail;
+ }
+
+ if (returnJ)
+ json_object_put(returnJ);
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static int halBlueAlsaFetchTransports(bluealsa_watch * watch) {
+ ssize_t nbTransports;
+ struct ba_msg_transport *transports;
+ CtlPluginT * plugin = watch->plugin;
+
+ hal_bluealsa_plugin_data_t * pluginData = (hal_bluealsa_plugin_data_t*)getPluginContext(plugin);
+ bluealsa_transport_t * transport_list = &pluginData->transport_list;
+ bluealsa_transport_t * transport = NULL;
+
+ AFB_ApiDebug(plugin->api, "Fetching available transports of interface %s", watch->interface);
+
+ if ((nbTransports = bluealsa_get_transports(watch->fd, &transports)) == -1) {
+ AFB_ApiError(plugin->api, "Couldn't get transports: %s", strerror(errno));
+ goto done;
+ }
+
+ AFB_ApiDebug(plugin->api, "Got %zu transport(s)", nbTransports);
+
+ for (int ix=0; ix<nbTransports; ix++) {
+ char addr[18];
+ struct ba_msg_transport * ba_transport = &transports[ix];
+ ba2str(&ba_transport->addr, addr);
+ const char * typeS;
+ if (ba_transport->type == BA_PCM_TYPE_SCO)
+ typeS = "sco";
+ else if (ba_transport->type == BA_PCM_TYPE_A2DP)
+ typeS = "a2dp";
+ else
+ typeS = "unknown";
+
+ AFB_ApiDebug(plugin->api, "Transport %d: type %s, dev %s", ix, typeS, addr);
+
+ if (halBlueAlsaTransportFind(watch, transport_list, ba_transport)) {
+ AFB_ApiDebug(plugin->api, "This transport is already streamed");
+ continue;
+ }
+
+ AFB_ApiInfo(plugin->api, "Registering transport type %s, dev %s", typeS, addr);
+ transport = halBlueAlsaTransportsAdd(watch, transport_list, ba_transport);
+ if (transport == NULL) {
+ AFB_ApiError(plugin->api, "Failed to register this transport");
+ goto done;
+ }
+
+ // Do the softmixer stuff
+ if (halBlueAlsaAttachTransportStreams(transport) != 0) {
+ AFB_ApiError(plugin->api, "Failed create transport streams");
+ goto done;
+ }
+
+ AFB_ApiDebug(plugin->api, "%s: transaction id %s", __func__, transport->transactionUidS);
+
+ }
+
+ halBlueAlsaTransportUpdate(watch, transport_list, transports, nbTransports , halBluezAlsaRemoveTransportStream);
+
+done:
+ free(transports);
+ return 0;
+}
+
+static int halBlueAlsaRegister(CtlPluginT* plugin, const char * interface) {
+ int ret;
+ sd_event *sdLoop;
+ sd_event_source* evtsrc;
+
+ sdLoop = AFB_GetEventLoop(plugin->api);
+
+ enum ba_event transport_mask = BA_EVENT_TRANSPORT_ADDED |
+// BA_EVENT_TRANSPORT_CHANGED |
+ BA_EVENT_TRANSPORT_REMOVED;
+
+ bluealsa_watch * watch = (bluealsa_watch *)malloc(sizeof(bluealsa_watch));
+ if (watch == NULL)
+ goto fail;
+
+ watch->interface = interface;
+ watch->plugin = plugin;
+
+ if ((watch->fd = bluealsa_open(interface)) == -1) {
+ AFB_ApiError(plugin->api, "BlueALSA connection failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ halBlueAlsaFetchTransports(watch);
+
+ if (bluealsa_subscribe(watch->fd, transport_mask) == -1) {
+ AFB_ApiError(plugin->api, "BlueALSA subscription failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ // Register sound event to the main loop of the binder
+ if ((ret = sd_event_add_io(sdLoop, &evtsrc, watch->fd, EPOLLIN, halBlueAlsaTransportEventCB, watch)) < 0) {
+ AFB_ApiError(plugin->api,
+ "%s: Failed to register event fd to io loop",
+ __func__);
+ goto fail;
+ }
+ return 0;
+
+fail:
+ if (watch->fd)
+ close(watch->fd);
+ if (watch)
+ free(watch);
+ return -1;
+}
+
+
+static int halBlueAlsaRegisterAll(CtlPluginT* plugin) {
+ /* TODO add a watch to all the available interfaces */
+ halBlueAlsaRegister(plugin, "hci0");
+ return 0;
+}
diff --git a/plugins/lib/bluealsa/hal-bluealsa.h b/plugins/lib/bluealsa/hal-bluealsa.h
new file mode 100644
index 0000000..86fe45a
--- /dev/null
+++ b/plugins/lib/bluealsa/hal-bluealsa.h
@@ -0,0 +1,21 @@
+#ifndef __INC_HAL_BLUEALSA_H
+#define __INC_HAL_BLUEALSA_H
+
+#include "hal-bluealsa-transports.h"
+
+typedef struct {
+ bluealsa_transport_t transport_list;
+ struct {
+ char * mic; // capture for talking
+ char * speaker; // playback zone
+ uint32_t delayms;
+ } sco;
+ struct {
+ char * zone;
+ uint32_t delayms;
+ } a2dp;
+} hal_bluealsa_plugin_data_t;
+
+#define SMIXER_API_NAME "smixer"
+
+#endif /* __INC_HAL_BLUEALSA_H */
diff --git a/plugins/lib/bluetooth/hal-bt-cb.c b/plugins/lib/bluetooth/hal-bt-cb.c
deleted file mode 100644
index 0638d50..0000000
--- a/plugins/lib/bluetooth/hal-bt-cb.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <wrap-json.h>
-
-#include <ctl-config.h>
-
-#include "hal-bt-data.h"
-#include "hal-bt-mixer-link.h"
-
-/*******************************************************************************
- * HAL Bluetooth plugin verbs functions *
- ******************************************************************************/
-
-void HalBtGetStreamingStatus(AFB_ReqT request)
-{
- struct HalBtPluginData *localHalBtPluginData;
-
- if(! (localHalBtPluginData = (struct HalBtPluginData *) AFB_ReqVCBData(request))) {
- AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
- return;
- }
-
- AFB_ReqSuccess(request, json_object_new_string(localHalBtPluginData->btStreamEnabled ? "enabled" : "disabled"), "Bluetooth streaming status");
-}
-
-void HalBtSetStreamingStatus(AFB_ReqT request)
-{
- unsigned int requestedBtStreamingStatus;
-
- struct HalBtPluginData *localHalBtPluginData;
-
- json_object *requestJson;
-
- AFB_ApiT apiHandle;
-
- if(! (localHalBtPluginData = (struct HalBtPluginData *) AFB_ReqVCBData(request))) {
- AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
- return;
- }
-
- if(! (requestJson = AFB_ReqJson(request))) {
- AFB_ReqFail(request, "request_json", "Can't get request json");
- return;
- }
-
- if(! (apiHandle = (AFB_ApiT ) AFB_ReqGetApi(request))) {
- AFB_ReqFail(request, "api_handle", "Can't get current hal controller api handle");
- return;
- }
-
- if(wrap_json_unpack(requestJson, "{s:b}", "status", &requestedBtStreamingStatus)) {
- AFB_ReqFail(request, "requested_status", "Can't get requested bluetooth streaming status");
- return;
- }
-
- localHalBtPluginData->btStreamEnabled = requestedBtStreamingStatus;
-
- if(HalBtMixerLinkSetBtStreamingSettings(apiHandle,
- localHalBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName,
- localHalBtPluginData->btStreamEnabled,
- localHalBtPluginData->selectedBtDevice ? localHalBtPluginData->selectedBtDevice->hci : NULL,
- localHalBtPluginData->selectedBtDevice ? localHalBtPluginData->selectedBtDevice->address : NULL)) {
- AFB_ApiError(apiHandle,
- "Couldn't set bluetooth streaming settings during call to verb '%s' of api '%s'",
- MIXER_SET_STREAMED_BT_DEVICE_VERB,
- localHalBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName);
- AFB_ReqFail(request, "requested_device_to_select", "Error happened during set of streamed bt device");
- return;
- }
-
- AFB_ReqSuccess(request, NULL, "Bluetooth streaming status successfully set");
-}
-
-void HalBtGetA2DPBluetoothDevices(AFB_ReqT request)
-{
- struct HalBtPluginData *localHalBtPluginData;
- struct HalBtDeviceData *currentBtDeviceData;
-
- json_object *requestAnswer, *currentBtDeviceObjectJ;
-
- if(! (localHalBtPluginData = (struct HalBtPluginData *) AFB_ReqVCBData(request))) {
- AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
- return;
- }
-
- if(! (currentBtDeviceData = localHalBtPluginData->first)) {
- AFB_ReqSuccess(request, NULL, "No A2DP bluetooth device connected");
- return;
- }
-
- requestAnswer = json_object_new_array();
- if(! requestAnswer) {
- AFB_ReqFail(request, "json_answer", "Can't generate json answer");
- return;
- }
-
- while(currentBtDeviceData) {
- wrap_json_pack(&currentBtDeviceObjectJ,
- "{s:s s:s s:s}",
- "Interface", currentBtDeviceData->hci,
- "Name", currentBtDeviceData->name,
- "Address", currentBtDeviceData->address);
- json_object_array_add(requestAnswer, currentBtDeviceObjectJ);
-
- currentBtDeviceData = currentBtDeviceData->next;
- }
-
- AFB_ReqSuccess(request, requestAnswer, "Connected A2DP bluetooth devices list");
-}
-
-void HalBtGetSelectedA2DPBluetoothDevice(AFB_ReqT request)
-{
- struct HalBtPluginData *localHalBtPluginData;
-
- json_object *selectedBtDeviceObject;
-
- if(! (localHalBtPluginData = (struct HalBtPluginData *) AFB_ReqVCBData(request))) {
- AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
- return;
- }
-
- if(! localHalBtPluginData->selectedBtDevice) {
- AFB_ReqSuccess(request, NULL, "No bluetooth device selected");
- return;
- }
-
- wrap_json_pack(&selectedBtDeviceObject,
- "{s:s s:s s:s}",
- "Interface", localHalBtPluginData->selectedBtDevice->hci,
- "Name", localHalBtPluginData->selectedBtDevice->name,
- "Address", localHalBtPluginData->selectedBtDevice->address);
-
- AFB_ReqSuccess(request, selectedBtDeviceObject, "Selected Bluetooth device");
-}
-
-void HalBtSetSelectedA2DPBluetoothDevice(AFB_ReqT request)
-{
- char *requestedBtDeviceToSelect;
-
- struct HalBtPluginData *localHalBtPluginData;
- struct HalBtDeviceData *selectedBtDeviceData;
-
- json_object *requestJson;
-
- AFB_ApiT apiHandle;
-
- if(! (localHalBtPluginData = (struct HalBtPluginData *) AFB_ReqVCBData(request))) {
- AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
- return;
- }
-
- if(! (requestJson = AFB_ReqJson(request))) {
- AFB_ReqFail(request, "request_json", "Can't get request json");
- return;
- }
-
- if(! (apiHandle = (AFB_ApiT ) AFB_ReqGetApi(request))) {
- AFB_ReqFail(request, "api_handle", "Can't get current hal controller api handle");
- return;
- }
-
- if(wrap_json_unpack(requestJson, "{s:s}", "Address", &requestedBtDeviceToSelect)) {
- AFB_ReqFail(request, "requested_device_to_select", "Can't get requested bluetooth device to select");
- return;
- }
-
- if(! (selectedBtDeviceData = HalBtDataSearchBtDeviceByAddress(&localHalBtPluginData->first, requestedBtDeviceToSelect))) {
- AFB_ReqFail(request, "requested_device_to_select", "Requested A2DP bluetooth device to select is not currently connected");
- return;
- }
-
- localHalBtPluginData->selectedBtDevice = selectedBtDeviceData;
-
- if(HalBtMixerLinkSetBtStreamingSettings(apiHandle,
- localHalBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName,
- localHalBtPluginData->btStreamEnabled,
- localHalBtPluginData->selectedBtDevice->hci,
- localHalBtPluginData->selectedBtDevice->address)) {
- AFB_ApiError(apiHandle,
- "Couldn't set bluetooth streaming settings during call to verb '%s' of api '%s'",
- MIXER_SET_STREAMED_BT_DEVICE_VERB,
- localHalBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName);
- AFB_ReqFail(request, "requested_device_to_select", "Error happened during set of streamed bt device");
- return;
- }
-
- AFB_ReqSuccess(request, NULL, "Selected bluetooth device successfully set");
-} \ No newline at end of file
diff --git a/plugins/lib/bluetooth/hal-bt-cb.h b/plugins/lib/bluetooth/hal-bt-cb.h
deleted file mode 100644
index f0f1052..0000000
--- a/plugins/lib/bluetooth/hal-bt-cb.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _HAL_BT_CB_INCLUDE_
-#define _HAL_BT_CB_INCLUDE_
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <ctl-config.h>
-
-#define HAL_BT_GET_STREAMING_STATUS_VERB "get_bt_streaming_status"
-#define HAL_BT_SET_STREAMING_STATUS_VERB "set_bt_streaming_status"
-#define HAL_BT_GET_CONNECTED_A2DP_DEVICES_VERB "get_connected_bt_a2dp_devices"
-#define HAL_BT_GET_SELECTED_A2DP_DEVICE_VERB "get_selected_bt_a2dp_device"
-#define HAL_BT_SET_SELECTED_A2DP_DEVICE_VERB "set_selected_bt_a2dp_device"
-
-// HAL Bluetooth plugin verbs functions
-void HalBtGetStreamingStatus(AFB_ReqT request);
-void HalBtSetStreamingStatus(AFB_ReqT request);
-void HalBtGetA2DPBluetoothDevices(AFB_ReqT request);
-void HalBtGetSelectedA2DPBluetoothDevice(AFB_ReqT request);
-void HalBtSetSelectedA2DPBluetoothDevice(AFB_ReqT request);
-
-#endif /* _HAL_BT_CB_INCLUDE_ */ \ No newline at end of file
diff --git a/plugins/lib/bluetooth/hal-bt-data.c b/plugins/lib/bluetooth/hal-bt-data.c
deleted file mode 100644
index f3cb0e4..0000000
--- a/plugins/lib/bluetooth/hal-bt-data.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <wrap-json.h>
-
-#include "hal-bt-data.h"
-
-/*******************************************************************************
- * Bt device data handling functions *
- ******************************************************************************/
-
-int HalBtDataRemoveSelectedBtDeviceFromList(struct HalBtDeviceData **firstBtDeviceData, struct HalBtDeviceData *btDeviceDataToRemove)
-{
- struct HalBtDeviceData *currentBtDevice, *matchingBtDevice;
-
- if(! firstBtDeviceData || ! btDeviceDataToRemove)
- return -1;
-
- currentBtDevice = *firstBtDeviceData;
-
- if(currentBtDevice == btDeviceDataToRemove) {
- *firstBtDeviceData = currentBtDevice->next;
- matchingBtDevice = currentBtDevice;
- }
- else {
- while(currentBtDevice && currentBtDevice->next != btDeviceDataToRemove)
- currentBtDevice = currentBtDevice->next;
-
- if(currentBtDevice) {
- matchingBtDevice = currentBtDevice->next;
- currentBtDevice->next = currentBtDevice->next->next;
- }
- else {
- return -2;
- }
- }
-
- free(matchingBtDevice->hci);
- free(matchingBtDevice->uid);
- free(matchingBtDevice->name);
- free(matchingBtDevice->address);
-
- free(matchingBtDevice);
-
- return 0;
-}
-
-struct HalBtDeviceData *HalBtDataAddBtDeviceToBtDeviceList(struct HalBtDeviceData **firstBtDeviceData, json_object *currentSingleBtDeviceDataJ)
-{
- char *currentBtDeviceName, *currentBtDeviceAddress, *currentBtDevicePath, *currentBtDeviceHci;
-
- struct HalBtDeviceData *currentBtDeviceData;
-
- if(! firstBtDeviceData || ! currentSingleBtDeviceDataJ)
- return NULL;
-
- currentBtDeviceData = *firstBtDeviceData;
-
- if(! currentBtDeviceData) {
- currentBtDeviceData = (struct HalBtDeviceData *) calloc(1, sizeof(struct HalBtDeviceData));
- if(! currentBtDeviceData)
- return NULL;
-
- *firstBtDeviceData = currentBtDeviceData;
- }
- else {
- while(currentBtDeviceData->next)
- currentBtDeviceData = currentBtDeviceData->next;
-
- currentBtDeviceData->next = calloc(1, sizeof(struct HalBtDeviceData));
- if(! currentBtDeviceData)
- return NULL;
-
- currentBtDeviceData = currentBtDeviceData->next;
- }
-
- if(wrap_json_unpack(currentSingleBtDeviceDataJ,
- "{s:s s:s s:s}",
- "Name", &currentBtDeviceName,
- "Address", &currentBtDeviceAddress,
- "Path", &currentBtDevicePath)) {
- HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData);
- return NULL;
- }
-
- if(asprintf(&currentBtDeviceData->uid, "BT#%s", currentBtDeviceAddress) == -1) {
- HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData);
- return NULL;
- }
-
- currentBtDeviceHci = strtok(currentBtDevicePath, "/");
- while(currentBtDeviceHci && strncmp(currentBtDeviceHci, "hci", 3))
- currentBtDeviceHci = strtok(NULL, "/");
-
- if((! currentBtDeviceHci) || (! (currentBtDeviceData->hci = strdup(currentBtDeviceHci)))) {
- HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData);
- return NULL;
- }
-
- if(! (currentBtDeviceData->name = strdup(currentBtDeviceName))) {
- HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData);
- return NULL;
- }
-
- if(! (currentBtDeviceData->address = strdup(currentBtDeviceAddress))) {
- HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData);
- return NULL;
- }
-
- return currentBtDeviceData;
-}
-
-int HalBtDataGetNumberOfBtDeviceInList(struct HalBtDeviceData **firstBtDeviceData)
-{
- unsigned int btDeviceNb = 0;
-
- struct HalBtDeviceData *currentBtDeviceData;
-
- if(! firstBtDeviceData)
- return -1;
-
- currentBtDeviceData = *firstBtDeviceData;
-
- while(currentBtDeviceData) {
- currentBtDeviceData = currentBtDeviceData->next;
- btDeviceNb++;
- }
-
- return btDeviceNb;
-}
-
-struct HalBtDeviceData *HalBtDataSearchBtDeviceByAddress(struct HalBtDeviceData **firstBtDeviceData, char *btAddress)
-{
- struct HalBtDeviceData *currentBtDevice;
-
- if(! firstBtDeviceData || ! btAddress)
- return NULL;
-
- currentBtDevice = *firstBtDeviceData;
-
- while(currentBtDevice) {
- if(! strcmp(btAddress, currentBtDevice->address))
- return currentBtDevice;
-
- currentBtDevice = currentBtDevice->next;
- }
-
- return NULL;
-}
-
-int HalBtDataHandleReceivedSingleBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentSingleBtDeviceDataJ)
-{
- int btProfilesCount;
-
- unsigned int idx = 0, currentBtDeviceIsConnected, currentBtDeviceIsA2DP;
-
- char *currentBtDeviceAddress, *currentBtDeviceIsConnectedString, *currentBtDeviceIsAVPConnectedString;
-
- json_object *currentBtAllProfilesJ = NULL, *currentBtCurrentProfileJ;
-
- struct HalBtDeviceData *currentBtDevice;
- if(! halBtPluginData || ! currentSingleBtDeviceDataJ)
- return -1;
-
- if(wrap_json_unpack(currentSingleBtDeviceDataJ,
- "{s:s s:s s:s s?:o}",
- "Address", &currentBtDeviceAddress,
- "Connected", &currentBtDeviceIsConnectedString,
- "AVPConnected", &currentBtDeviceIsAVPConnectedString,
- "UUIDs", &currentBtAllProfilesJ)) {
- return -2;
- }
-
- if(! currentBtAllProfilesJ) {
- AFB_ApiInfo(halBtPluginData->currentHalApiHandle, "Bluetooth device (address = %s) has no specified profiles, ignore it", currentBtDeviceAddress);
- return 0;
- }
-
- if(json_object_is_type(currentBtAllProfilesJ, json_type_array)) {
- btProfilesCount = json_object_array_length(currentBtAllProfilesJ);
-
- while(idx < btProfilesCount) {
- currentBtCurrentProfileJ = json_object_array_get_idx(currentBtAllProfilesJ, idx);
-
- if(json_object_is_type(currentBtCurrentProfileJ, json_type_string) &&
- ! strncasecmp(json_object_get_string(currentBtCurrentProfileJ), A2DP_AUDIOSOURCE_UUID, sizeof(A2DP_AUDIOSOURCE_UUID))) {
- currentBtDeviceIsA2DP = 1;
- break;
- }
-
- idx++;
- }
- }
- else if(json_object_is_type(currentBtAllProfilesJ, json_type_string) &&
- ! strncasecmp(json_object_get_string(currentBtAllProfilesJ), A2DP_AUDIOSOURCE_UUID, sizeof(A2DP_AUDIOSOURCE_UUID))) {
- currentBtDeviceIsA2DP = 1;
- }
-
- if(! currentBtDeviceIsA2DP)
- return 0;
-
- currentBtDeviceIsConnected = ((! strncmp(currentBtDeviceIsConnectedString, "True", strlen(currentBtDeviceIsConnectedString))) &&
- (! strncmp(currentBtDeviceIsAVPConnectedString, "True", strlen(currentBtDeviceIsAVPConnectedString))));
-
- currentBtDevice = HalBtDataSearchBtDeviceByAddress(&halBtPluginData->first, currentBtDeviceAddress);
-
- if(currentBtDevice && ! currentBtDeviceIsConnected) {
- if(HalBtDataRemoveSelectedBtDeviceFromList(&halBtPluginData->first, currentBtDevice))
- return -3;
-
- AFB_ApiInfo(halBtPluginData->currentHalApiHandle, "Bluetooth device (address = %s) successfully removed from list", currentBtDeviceAddress);
-
- if(halBtPluginData->selectedBtDevice == currentBtDevice) {
- halBtPluginData->selectedBtDevice = halBtPluginData->first;
- AFB_ApiDebug(halBtPluginData->currentHalApiHandle,
- "Bluetooth selected device changed to '%s'",
- halBtPluginData->selectedBtDevice ? halBtPluginData->selectedBtDevice->address : "none");
- }
- }
- else if(! currentBtDevice && currentBtDeviceIsConnected) {
- if(! HalBtDataAddBtDeviceToBtDeviceList(&halBtPluginData->first, currentSingleBtDeviceDataJ))
- return -4;
-
- AFB_ApiInfo(halBtPluginData->currentHalApiHandle, "Bluetooth device (address = %s) successfully added to list", currentBtDeviceAddress);
-
- if(! halBtPluginData->selectedBtDevice) {
- halBtPluginData->selectedBtDevice = halBtPluginData->first;
- AFB_ApiDebug(halBtPluginData->currentHalApiHandle,
- "Bluetooth selected device changed to '%s'",
- halBtPluginData->selectedBtDevice ? halBtPluginData->selectedBtDevice->address : "none");
- }
- }
-
- return 0;
-}
-
-int HalBtDataHandleReceivedMutlipleBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentMultipleBtDeviceDataJ)
-{
- int err = 0;
- size_t idx, btDeviceNumber;
-
- if(! halBtPluginData || ! currentMultipleBtDeviceDataJ)
- return -1;
-
- if(! json_object_is_type(currentMultipleBtDeviceDataJ, json_type_array))
- return -2;
-
- btDeviceNumber = json_object_array_length(currentMultipleBtDeviceDataJ);
-
- for(idx = 0; idx < btDeviceNumber; idx++) {
- if((err = HalBtDataHandleReceivedSingleBtDeviceData(halBtPluginData, json_object_array_get_idx(currentMultipleBtDeviceDataJ, (unsigned int) idx)))) {
- return ((int) idx * err * 10);
- }
- }
-
- return 0;
-}
diff --git a/plugins/lib/bluetooth/hal-bt-data.h b/plugins/lib/bluetooth/hal-bt-data.h
deleted file mode 100644
index 36e0430..0000000
--- a/plugins/lib/bluetooth/hal-bt-data.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _HAL_BT_DATA_INCLUDE_
-#define _HAL_BT_DATA_INCLUDE_
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <json-c/json.h>
-
-#include <4a-hal-utilities-data.h>
-
-#define A2DP_AUDIOSOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb"
-#define HSP_GATEAWAY_UUID "00001112-0000-1000-8000-00805f9b34fb"
-#define HFP_GATEAWAY_UUID "0000111f-0000-1000-8000-00805f9b34fb"
-
-// Structure to store bluetooth device data
-struct HalBtDeviceData {
- char *uid;
-
- char *hci;
- char *name;
- char *address;
-
- struct HalBtDeviceData *next;
-};
-
-// Structure to store hal bluetooth plugin data
-struct HalBtPluginData {
- unsigned int halBtPluginEnabled;
-
- AFB_ApiT currentHalApiHandle;
-
- struct SpecificHalData *currentHalData;
-
- unsigned int btStreamEnabled;
-
- struct HalBtDeviceData *selectedBtDevice;
- struct HalBtDeviceData *first;
-};
-
-// Exported verbs for 'struct HalBtDeviceData' list (available in 'struct HalBtPluginData') handling
-int HalBtDataGetNumberOfBtDeviceInList(struct HalBtDeviceData **firstBtDeviceData);
-struct HalBtDeviceData *HalBtDataSearchBtDeviceByAddress(struct HalBtDeviceData **firstBtDeviceData, char *btAddress);
-int HalBtDataHandleReceivedSingleBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentSingleBtDeviceDataJ);
-int HalBtDataHandleReceivedMutlipleBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentMultipleBtDeviceDataJ);
-
-#endif /* _HAL_BT_DATA_INCLUDE_ */ \ No newline at end of file
diff --git a/plugins/lib/bluetooth/hal-bt-mixer-link.c b/plugins/lib/bluetooth/hal-bt-mixer-link.c
deleted file mode 100644
index abbd1b7..0000000
--- a/plugins/lib/bluetooth/hal-bt-mixer-link.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <wrap-json.h>
-
-#include <ctl-config.h>
-
-#include "hal-bt-mixer-link.h"
-
-/*******************************************************************************
- * HAL controllers handle mixer bt calls functions *
- ******************************************************************************/
-
-int HalBtMixerLinkSetBtStreamingSettings(AFB_ApiT apiHandle, char *mixerApiName, unsigned int btStreamStatus, char *hci, char *btAddress)
-{
- char *returnedError = NULL;
-
- json_object *toSendJ, *returnedJ;
-
- if(! apiHandle || ! mixerApiName)
- return -1;
-
- if(! btStreamStatus) {
- AFB_ApiDebug(apiHandle, "Will try to disable bluetooth streamed device");
- toSendJ = NULL;
- }
- else if(btStreamStatus == 1 && hci && btAddress) {
- AFB_ApiDebug(apiHandle, "Will try to change bluetooth streamed device to hci='%s' address='%s'", hci, btAddress);
- wrap_json_pack(&toSendJ, "{s:s s:s s:s}", "interface", hci, "device", btAddress, "profile", "a2dp");
- }
- else {
- return -2;
- }
-
- if(AFB_ServiceSync(apiHandle, mixerApiName, MIXER_SET_STREAMED_BT_DEVICE_VERB, toSendJ, &returnedJ)) {
- AFB_ApiError(apiHandle,
- "Error during call to verb %s of %s api",
- MIXER_SET_STREAMED_BT_DEVICE_VERB,
- mixerApiName);
- return -3;
- }
- else if(! wrap_json_unpack(returnedJ, "{s:{s:s}}", "request", "info", &returnedError)) {
- AFB_ApiError(apiHandle,
- "Error '%s' happened during set bluetooth streaming settings during call to verb '%s' of api '%s'",
- returnedError ? returnedError : "no_error_string",
- MIXER_SET_STREAMED_BT_DEVICE_VERB,
- mixerApiName);
- json_object_put(returnedJ);
- return -4;
- }
-
- json_object_put(returnedJ);
-
- if(btStreamStatus)
- AFB_ApiInfo(apiHandle, "Bluetooth streamed device changed to hci='%s' address='%s'", hci, btAddress);
- else
- AFB_ApiInfo(apiHandle, "Bluetooth streamed device disbaled");
-
- return 0;
-} \ No newline at end of file
diff --git a/plugins/lib/bluetooth/hal-bt-mixer-link.h b/plugins/lib/bluetooth/hal-bt-mixer-link.h
deleted file mode 100644
index 0550339..0000000
--- a/plugins/lib/bluetooth/hal-bt-mixer-link.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _HAL_BT_MIXER_LINK_INCLUDE_
-#define _HAL_BT_MIXER_LINK_INCLUDE_
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <ctl-config.h>
-
-#define MIXER_SET_STREAMED_BT_DEVICE_VERB "bluezalsa_dev"
-
-#define MIXER_BT_CAPTURE_JSON_SECTION "\
-{\
- \"uid\": \"bluetooth\",\
- \"pcmplug_params\": \"bluealsa_proxy\",\
- \"source\": {\
- \"channels\": [\
- {\
- \"uid\": \"bluetooth-right\",\
- \"port\": 0\
- },\
- {\
- \"uid\": \"bluetooth-left\",\
- \"port\": 1\
- }\
- ]\
- }\
-}\
-"
-#define MIXER_BT_STREAM_JSON_SECTION "\
-{\
- \"uid\": \"bluetooth_stream\",\
- \"verb\": \"bluetooth_stream\",\
- \"source\" : \"bluetooth\",\
- \"volume\": 80,\
- \"mute\": false,\
- \"params\": {\
- \"rate\" : 44100,\
- \"format\": \"S16_LE\",\
- \"channels\": 2\
- }\
-}\
-"
-
-// HAL controllers handle mixer bt calls functions
-int HalBtMixerLinkSetBtStreamingSettings(AFB_ApiT apiHandle, char *mixerApiName, unsigned int btStreamStatus, char *hci, char *btAddress);
-
-#endif /* _HAL_BT_MIXER_LINK_INCLUDE_ */
diff --git a/plugins/lib/bluetooth/hal-bt.c b/plugins/lib/bluetooth/hal-bt.c
deleted file mode 100644
index f270593..0000000
--- a/plugins/lib/bluetooth/hal-bt.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Copyright (C) 2018 "IoT.bzh"
- * Author Jonathan Aillet <jonathan.aillet@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <wrap-json.h>
-
-#include <ctl-config.h>
-
-#include "hal-bt.h"
-#include "hal-bt-cb.h"
-#include "hal-bt-data.h"
-#include "hal-bt-mixer-link.h"
-
-CTLP_CAPI_REGISTER(HAL_BT_PLUGIN_NAME)
-
-// Call at initialisation time
-CTLP_ONLOAD(plugin, callbacks)
-{
- struct HalBtPluginData *currentBtPluginData;
-
- AFB_ApiInfo(plugin->api, "%s Plugin Registering: uid='%s' 'info='%s'", HAL_BT_PLUGIN_NAME, plugin->uid, plugin->info);
-
- currentBtPluginData = calloc(1, sizeof(struct HalBtPluginData));
- if(! currentBtPluginData) {
- AFB_ApiError(plugin->api, "An error happenned during allocation of %s plugin data structure ", HAL_BT_PLUGIN_NAME);
- return -1;
- }
-
- setPluginContext(plugin, (void *) currentBtPluginData);
-
- currentBtPluginData->currentHalApiHandle = plugin->api;
-
- AFB_ApiNotice(plugin->api, "%s Plugin Registered correctly: uid='%s' 'info='%s'", HAL_BT_PLUGIN_NAME, plugin->uid, plugin->info);
-
- return 0;
-}
-
-CTLP_INIT(plugin, callbacks)
-{
- int btChannelsNumber;
-
- unsigned int idx;
-
- char *btStreamZone, *returnedInfo;
-
- CtlConfigT *ctrlConfig;
-
- struct HalBtPluginData *currentBtPluginData;
-
- json_object *actionsToAdd, *returnedJ, *halMixerJ, *halOrigCaptureJ, *halNewCaptureJ, *halOrigStreamJ, *halNewStreamJ, *btCaptureJ, *btCaptureParamsJ, *btStreamJ;
-
- AFB_ApiInfo(plugin->api, "Plugin initialization of %s plugin", HAL_BT_PLUGIN_NAME);
-
- if(AFB_RequireApi(plugin->api, BT_MANAGER_API, 1)) {
- AFB_ApiWarning(plugin->api, "Didn't succeed to require %s api, bluetooth is disable because not reachable", BT_MANAGER_API);
- return 0;
- }
-
- if(! (currentBtPluginData = (struct HalBtPluginData *) getPluginContext(plugin))) {
- AFB_ApiError(plugin->api, "Can't get current %s plugin data", HAL_BT_PLUGIN_NAME);
- return -1;
- }
-
- if(! (ctrlConfig = (CtlConfigT *) AFB_ApiGetUserData(plugin->api))) {
- AFB_ApiError(plugin->api, "Can't get current hal controller config");
- return -2;
- }
-
- if(! (currentBtPluginData->currentHalData = getExternalData(ctrlConfig))) {
- AFB_ApiError(plugin->api, "Can't get current hal controller data");
- return -3;
- }
-
- if(currentBtPluginData->currentHalData->status != HAL_STATUS_AVAILABLE) {
- AFB_ApiWarning(plugin->api, "Controller initialization of %s plugin cannot be done because hal is not ready to be used", HAL_BT_PLUGIN_NAME);
- return 0;
- }
-
- if((! currentBtPluginData->currentHalData->ctlHalSpecificData) ||
- (! (halMixerJ = currentBtPluginData->currentHalData->ctlHalSpecificData->halMixerJ))) {
- AFB_ApiError(plugin->api, "Can't get current hal mixer json section");
- return -4;
- }
-
- if(AFB_ServiceSync(plugin->api, BT_MANAGER_API, BT_MANAGER_GET_POWER_INFO, NULL, &returnedJ)) {
- if((! wrap_json_unpack(returnedJ, "{s:{s:s}}", "request", "info", &returnedInfo)) &&
- (! strncmp(returnedInfo, "Unable to get power status", strlen(returnedInfo)))) {
- AFB_ApiWarning(plugin->api,
- "No bluetooth receiver detected when calling verb '%s' of '%s' api, bluetooth is disable because not reachable",
- BT_MANAGER_GET_POWER_INFO,
- BT_MANAGER_API);
- return 0;
- }
- else {
- AFB_ApiError(plugin->api,
- "Error during call to verb '%s' of '%s' api (%s)",
- BT_MANAGER_GET_POWER_INFO,
- BT_MANAGER_API,
- json_object_get_string(returnedJ));
- return -5;
- }
- }
-
- if((! json_object_is_type(plugin->paramsJ, json_type_object)) ||
- (wrap_json_unpack(plugin->paramsJ, "{s:i, s:s}", "channels", &btChannelsNumber, "zone", &btStreamZone))) {
- AFB_ApiError(plugin->api, "Can't get %s plugin parameters from json ('%s')", HAL_BT_PLUGIN_NAME, json_object_get_string(plugin->paramsJ));
- return -6;
- }
-
- wrap_json_pack(&actionsToAdd, "{s:s s:s}",
- "uid", "Bluetooth-Manager/device_updated",
- "action", "plugin://hal-bt#events");
-
- idx = 0;
- while(ctrlConfig->sections[idx].key && strcasecmp(ctrlConfig->sections[idx].key, "events"))
- idx++;
-
- if(! ctrlConfig->sections[idx].key) {
- AFB_ApiError(plugin->api, "Wasn't able to add '%s' as a new event, 'events' section not found", json_object_get_string(actionsToAdd));
- json_object_put(actionsToAdd);
- return -7;
- }
-
- if(AddActionsToSection(plugin->api, &ctrlConfig->sections[idx], actionsToAdd, 0)) {
- AFB_ApiError(plugin->api, "Wasn't able to add '%s' as a new event to %s", json_object_get_string(actionsToAdd), ctrlConfig->sections[idx].key);
- json_object_put(actionsToAdd);
- return -8;
- }
-
- wrap_json_pack(&actionsToAdd, "{s:s s:s s:s}",
- "uid", "init-bt-plugin",
- "info", "Init Bluetooth hal plugin",
- "action", "plugin://hal-bt#init");
-
- idx = 0;
- while(ctrlConfig->sections[idx].key && strcasecmp(ctrlConfig->sections[idx].key, "onload"))
- idx++;
-
- if(! ctrlConfig->sections[idx].key) {
- AFB_ApiError(plugin->api, "Wasn't able to add '%s' as a new onload, 'onload' section not found", json_object_get_string(actionsToAdd));
- json_object_put(actionsToAdd);
- return -9;
- }
-
- if(AddActionsToSection(plugin->api, &ctrlConfig->sections[idx], actionsToAdd, 0)) {
- AFB_ApiError(plugin->api, "Wasn't able to add '%s' as a new onload to %s", json_object_get_string(actionsToAdd), ctrlConfig->sections[idx].uid);
- json_object_put(actionsToAdd);
- return -10;
- }
-
- btCaptureJ = json_tokener_parse(MIXER_BT_CAPTURE_JSON_SECTION);
- wrap_json_pack(&btCaptureParamsJ, "{s:i}", "channels", btChannelsNumber);
- json_object_object_add(btCaptureJ, "params", btCaptureParamsJ);
-
- if(! json_object_object_get_ex(halMixerJ, "captures", &halOrigCaptureJ)) {
- halNewCaptureJ = json_object_new_array();
- json_object_array_add(halNewCaptureJ, btCaptureJ);
- json_object_object_add(halMixerJ, "captures", halNewCaptureJ);
- }
- else if(json_object_is_type(halOrigCaptureJ, json_type_array)) {
- halNewCaptureJ = halOrigCaptureJ;
- json_object_array_add(halNewCaptureJ, btCaptureJ);
- }
- else if(json_object_is_type(halOrigCaptureJ, json_type_object)) {
- json_object_get(halOrigCaptureJ);
- json_object_object_del(halMixerJ, "captures");
- halNewCaptureJ = json_object_new_array();
- json_object_array_add(halNewCaptureJ, halOrigCaptureJ);
- json_object_array_add(halNewCaptureJ, btCaptureJ);
- json_object_object_add(halMixerJ, "captures", halNewCaptureJ);
- }
- else {
- AFB_ApiError(plugin->api, "Unrecognized 'halmixer' captures format");
- return -11;
- }
-
- btStreamJ = json_tokener_parse(MIXER_BT_STREAM_JSON_SECTION);
- json_object_object_add(btStreamJ, "zone", json_object_new_string(btStreamZone));
-
- if(! json_object_object_get_ex(halMixerJ, "streams", &halOrigStreamJ)) {
- halNewStreamJ = json_object_new_array();
- json_object_array_add(halNewStreamJ, btStreamJ);
- json_object_object_add(halMixerJ, "streams", halNewStreamJ);
- }
- else if(json_object_is_type(halOrigStreamJ, json_type_array)) {
- halNewStreamJ = halOrigStreamJ;
- json_object_array_add(halNewStreamJ, btStreamJ);
- }
- else if(json_object_is_type(halOrigStreamJ, json_type_object)) {
- json_object_get(halOrigStreamJ);
- json_object_object_del(halMixerJ, "streams");
- halNewStreamJ = json_object_new_array();
- json_object_array_add(halNewStreamJ, halOrigStreamJ);
- json_object_array_add(halNewStreamJ, btStreamJ);
- json_object_object_add(halMixerJ, "streams", halNewStreamJ);
- }
- else {
- AFB_ApiError(plugin->api, "Unrecognized 'halmixer' streams format");
- return -12;
- }
-
- currentBtPluginData->halBtPluginEnabled = 1;
-
- AFB_ApiNotice(plugin->api, "Plugin initialization of %s plugin correctly done", HAL_BT_PLUGIN_NAME);
-
- return 0;
-}
-
-// Call at contoller onload time
-CTLP_CAPI(init, source, argsJ, queryJ)
-{
- int err;
-
- char *returnedInfo;
-
- struct HalBtPluginData *currentBtPluginData;
-
- json_object *toSendJ, *returnedJ, *returnedBtList = NULL;
-
- if(! (currentBtPluginData = (struct HalBtPluginData *) getPluginContext(source->plugin))) {
- AFB_ApiError(source->api, "Can't get current %s plugin data", HAL_BT_PLUGIN_NAME);
- return -1;
- }
-
- if(! currentBtPluginData->halBtPluginEnabled) {
- AFB_ApiWarning(source->api, "Controller onload initialization of %s plugin cannot be done because bluetooth is not reachable", HAL_BT_PLUGIN_NAME);
- return 0;
- }
-
- AFB_ApiInfo(source->api, "Controller onload initialization of %s plugin", HAL_BT_PLUGIN_NAME);
-
- // Loading hal BT plugin specific verbs
- if(AFB_ApiAddVerb(source->api,
- HAL_BT_GET_STREAMING_STATUS_VERB,
- "Get Bluetooth streaming status",
- HalBtGetStreamingStatus,
- (void *) currentBtPluginData,
- NULL,
- 0,
- 0)) {
- AFB_ApiError(source->api, "Error while creating verb for bluetooth plugin : '%s'", HAL_BT_GET_STREAMING_STATUS_VERB);
- return -2;
- }
-
- if(AFB_ApiAddVerb(source->api,
- HAL_BT_SET_STREAMING_STATUS_VERB,
- "Set Bluetooth streaming status",
- HalBtSetStreamingStatus,
- (void *) currentBtPluginData,
- NULL,
- 0,
- 0)) {
- AFB_ApiError(source->api, "Error while creating verb for bluetooth plugin : '%s'", HAL_BT_SET_STREAMING_STATUS_VERB);
- return -3;
- }
-
- if(AFB_ApiAddVerb(source->api,
- HAL_BT_GET_CONNECTED_A2DP_DEVICES_VERB,
- "Get connected Bluetooth A2DP devices list",
- HalBtGetA2DPBluetoothDevices,
- (void *) currentBtPluginData,
- NULL,
- 0,
- 0)) {
- AFB_ApiError(source->api, "Error while creating verb for bluetooth plugin : '%s'", HAL_BT_GET_CONNECTED_A2DP_DEVICES_VERB);
- return -4;
- }
-
- if(AFB_ApiAddVerb(source->api,
- HAL_BT_GET_SELECTED_A2DP_DEVICE_VERB,
- "Get selected Bluetooth A2DP device",
- HalBtGetSelectedA2DPBluetoothDevice,
- (void *) currentBtPluginData,
- NULL,
- 0,
- 0)) {
- AFB_ApiError(source->api, "Error while creating verb for bluetooth plugin : '%s'", HAL_BT_GET_SELECTED_A2DP_DEVICE_VERB);
- return -5;
- }
-
- if(AFB_ApiAddVerb(source->api,
- HAL_BT_SET_SELECTED_A2DP_DEVICE_VERB,
- "Set selected Bluetooth A2DP device",
- HalBtSetSelectedA2DPBluetoothDevice,
- (void *) currentBtPluginData,
- NULL,
- 0,
- 0)) {
- AFB_ApiError(source->api, "Error while creating verb for bluetooth plugin : '%s'", HAL_BT_SET_SELECTED_A2DP_DEVICE_VERB);
- return -6;
- }
-
- // Register to Bluetooth manager
- wrap_json_pack(&toSendJ, "{s:s}", "value", BT_MANAGER_DEVICE_UPDATE_EVENT);
- if(AFB_ServiceSync(source->api, BT_MANAGER_API, BT_MANAGER_SUBSCRIBE_VERB, toSendJ, &returnedJ)) {
- AFB_ApiError(source->api,
- "Error during call to verb '%s' of '%s' api (%s)",
- BT_MANAGER_SUBSCRIBE_VERB,
- BT_MANAGER_API,
- json_object_get_string(returnedJ));
-
- return -7;
- }
- else if(! wrap_json_unpack(returnedJ, "{s:{s:s}}", "request", "info", &returnedInfo)) {
- AFB_ApiError(source->api,
- "Couldn't subscribe to event '%s' during call to verb '%s' of api '%s' (error '%s')",
- BT_MANAGER_DEVICE_UPDATE_EVENT,
- BT_MANAGER_SUBSCRIBE_VERB,
- BT_MANAGER_API,
- returnedInfo);
- json_object_put(returnedJ);
- return -7;
- }
- json_object_put(returnedJ);
-
- if(AFB_ServiceSync(source->api, BT_MANAGER_API, BT_MANAGER_GET_DEVICES_VERB, NULL, &returnedJ)) {
- if((! wrap_json_unpack(returnedJ, "{s:{s:s}}", "request", "info", &returnedInfo)) &&
- (! strncmp(returnedInfo, "No find devices", strlen(returnedInfo)))) {
- AFB_ApiInfo(source->api,
- "No bluetooth devices returned by call to verb '%s' of '%s' api",
- BT_MANAGER_GET_DEVICES_VERB,
- BT_MANAGER_API);
- }
- else {
- AFB_ApiError(source->api,
- "Error during call to verb '%s' of '%s' api (%s)",
- BT_MANAGER_GET_DEVICES_VERB,
- BT_MANAGER_API,
- json_object_get_string(returnedJ));
-
- return -8;
- }
- }
- else if(wrap_json_unpack(returnedJ, "{s:{s:o}}", "response", "list", &returnedBtList)) {
- AFB_ApiError(source->api,
- "Couldn't get bluetooth device list during call to verb '%s' of api '%s'",
- BT_MANAGER_GET_DEVICES_VERB,
- BT_MANAGER_API);
- json_object_put(returnedJ);
- return -8;
- }
-
- if((returnedBtList) && (err = HalBtDataHandleReceivedMutlipleBtDeviceData(currentBtPluginData, returnedBtList)))
- return (10 * err);
-
- json_object_put(returnedJ);
-
- if(currentBtPluginData->selectedBtDevice) {
- currentBtPluginData->btStreamEnabled = 1;
-
- AFB_ApiInfo(source->api, "Useable bluetooth device detected at initialization, will try to use it");
-
- if(HalBtMixerLinkSetBtStreamingSettings(source->api,
- currentBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName,
- currentBtPluginData->btStreamEnabled,
- currentBtPluginData->selectedBtDevice->hci,
- currentBtPluginData->selectedBtDevice->address)) {
- AFB_ApiError(source->api,
- "Couldn't set bluetooth streaming settings during call to verb '%s' of api '%s'",
- MIXER_SET_STREAMED_BT_DEVICE_VERB,
- currentBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName);
- return -9;
- }
- }
-
- AFB_ApiNotice(source->api, "Controller onload initialization of %s plugin correctly done", HAL_BT_PLUGIN_NAME);
-
- return 0;
-}
-
-// This receive Hal bluetooth plugin events
-CTLP_CAPI(events, source, argsJ, queryJ)
-{
- struct HalBtPluginData *currentBtPluginData;
-
- struct HalBtDeviceData *previouslySelectedBtDevice;
-
- if(! (currentBtPluginData = (struct HalBtPluginData *) getPluginContext(source->plugin))) {
- AFB_ApiError(source->api, "Can't get current %s plugin data", HAL_BT_PLUGIN_NAME);
- return -1;
- }
-
- if(! currentBtPluginData->halBtPluginEnabled) {
- AFB_ApiWarning(source->api, "Bluetooth event received but cannot be handled because bluetooth is not reachable");
- return 0;
- }
-
- previouslySelectedBtDevice = currentBtPluginData->selectedBtDevice;
-
- if(HalBtDataHandleReceivedSingleBtDeviceData(currentBtPluginData, queryJ)) {
- AFB_ApiError(source->api, "Error while decoding bluetooth event received json (%s)", json_object_get_string(queryJ));
- return -2;
- }
-
- if(currentBtPluginData->selectedBtDevice == previouslySelectedBtDevice) {
- AFB_ApiDebug(source->api, "Bluetooth event received but device didn't change");
- return 0;
- }
-
- if(currentBtPluginData->selectedBtDevice)
- currentBtPluginData->btStreamEnabled = 1;
- else
- currentBtPluginData->btStreamEnabled = 0;
-
- if(HalBtMixerLinkSetBtStreamingSettings(source->api,
- currentBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName,
- currentBtPluginData->btStreamEnabled,
- currentBtPluginData->selectedBtDevice ? currentBtPluginData->selectedBtDevice->hci : NULL,
- currentBtPluginData->selectedBtDevice ? currentBtPluginData->selectedBtDevice->address : NULL)) {
- AFB_ApiError(source->api,
- "Couldn't set bluetooth streaming settings during call to verb '%s' of api '%s'",
- MIXER_SET_STREAMED_BT_DEVICE_VERB,
- currentBtPluginData->currentHalData->ctlHalSpecificData->mixerApiName);
- return -3;
- }
-
- return 0;
-} \ No newline at end of file