aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Aillet <jonathan.aillet@iot.bzh>2018-08-24 20:17:12 +0200
committerJonathan Aillet <jonathan.aillet@iot.bzh>2018-10-08 15:56:09 +0200
commit92eb34c496de489f288c0eb59c9d84d6cf31decd (patch)
treec2b4a69c98ad8cb3017ecb824e7e2955586a572d
parentdeb99447f014426cf2aed8263932b38d412bd376 (diff)
Add bluetooth hal plugin
Add first version of a hal plugin that will handle hal that wants bluetooth as an audio device. Change-Id: I4f5628ef9688c417b1b443fc3c4948cb23c17214 Signed-off-by: Jonathan Aillet <jonathan.aillet@iot.bzh>
-rw-r--r--4a-hal-cfg-reference/hal-4a-rcar-m3-bt.json198
-rw-r--r--4a-hal/4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h2
-rw-r--r--conf.d/cmake/config.cmake6
-rw-r--r--plugins/lib/CMakeLists.txt31
-rw-r--r--plugins/lib/bluetooth/CMakeLists.txt45
-rw-r--r--plugins/lib/bluetooth/hal-bt-cb.c171
-rw-r--r--plugins/lib/bluetooth/hal-bt-cb.h40
-rw-r--r--plugins/lib/bluetooth/hal-bt-data.c220
-rw-r--r--plugins/lib/bluetooth/hal-bt-data.h52
-rw-r--r--plugins/lib/bluetooth/hal-bt.c195
-rw-r--r--plugins/lib/bluetooth/hal-bt.h33
11 files changed, 965 insertions, 28 deletions
diff --git a/4a-hal-cfg-reference/hal-4a-rcar-m3-bt.json b/4a-hal-cfg-reference/hal-4a-rcar-m3-bt.json
new file mode 100644
index 0000000..c2483ce
--- /dev/null
+++ b/4a-hal-cfg-reference/hal-4a-rcar-m3-bt.json
@@ -0,0 +1,198 @@
+{
+ "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json",
+ "metadata": {
+ "uid": "/dev/snd/by-path/platform-sound",
+ "version": "0.9",
+ "api": "4a-hal-rcar-m3-bt",
+ "require": [ "alsacore", "smixer" ],
+ "info": "4a hal for Rcar M3 device",
+ "author": "Jonathan Aillet",
+ "date": "2018-06-13"
+ },
+ "resources": [
+ {
+ "uid": "hal-bt",
+ "info": "Bluetooth hal plugin to get selected bluetooth device as an input by tweaking softmixer configuration",
+ "spath": "./package/lib/plugins:./package/var:./lib/plugins:./var:/usr/libexec/agl/4a-hal/lib/plugins",
+ "libs": ["hal-bt.ctlso"]
+ }
+ ],
+ "onload": [
+ {
+ "uid": "init-bt-plugin",
+ "info": "Init Bluetooth hal plugin",
+ "action": "plugin://hal-bt#init"
+ }
+ ],
+ "controls": [
+ {
+ "uid": "ping",
+ "info": "Ping hal",
+ "action": "api://4a-hal-manager#ping"
+ }
+ ],
+ "events": [
+ {
+ "uid": "Bluetooth-Manager/device_updated",
+ "action": "plugin://hal-bt#events"
+ }
+ ],
+ "halmap": [
+ {
+ "uid": "agl-master-playback-volume",
+ "alsa": {
+ "name": "DVC Out Playback Volume",
+ "value": 80
+ }
+ },
+ {
+ "uid": "agl-pcm-playback-volume",
+ "alsa": {
+ "name": "DVC Out Playback Volume",
+ "value": 80
+ }
+ },
+ {
+ "uid": "agl-pcm-playback-switch",
+ "alsa": {
+ "name": "SRC Out Rate Switch",
+ "value": 1
+ }
+ },
+ {
+ "uid": "agl-capture-volume",
+ "alsa": {
+ "name": "DVC In Capture Volume"
+ }
+ }
+ ],
+ "halmixer": {
+ "uid": "rcar-m3",
+ "mixerapi": "smixer",
+ "prefix": "m3",
+ "ramps": [
+ {
+ "uid": "ramp-very-fast",
+ "delay": 50,
+ "up": 6,
+ "down": 10
+ },
+ {
+ "uid": "ramp-fast",
+ "delay": 50,
+ "up": 2,
+ "down": 10
+ },
+ {
+ "uid": "ramp-normal",
+ "delay": 50,
+ "up": 2,
+ "down": 4
+ },
+ {
+ "uid": "ramp-slow",
+ "delay": 50,
+ "up": 2,
+ "down": 2
+ },
+ {
+ "uid": "ramp-very-slow",
+ "delay": 100,
+ "up": 1,
+ "down": 1
+ }
+ ],
+ "playbacks" : {
+ "uid": "RCAR-M3",
+ "path": "/dev/snd/by-path/platform-sound",
+ "params": {
+ "rate": 48000,
+ "format": "S24_LE"
+ },
+ "sink": {
+ "controls": {
+ "volume": {
+ "name": "DVC Out Playback Volume",
+ "value": 80
+ },
+ "mute": {
+ "name": "SRC Out Rate Switch"
+ }
+ },
+ "channels": [
+ {
+ "uid": "front-right",
+ "port": 0
+ },
+ {
+ "uid": "front-left",
+ "port": 1
+ }
+ ]
+ }
+ },
+ "zones": [
+ {
+ "uid": "full-stereo",
+ "sink": [
+ {
+ "target": "front-right",
+ "channel": 0
+ },
+ {
+ "target": "front-left",
+ "channel": 1
+ }
+ ]
+ },
+ {
+ "uid": "front-seats",
+ "sink": [
+ {
+ "target": "front-right",
+ "channel": 0
+ },
+ {
+ "target": "front-left",
+ "channel": 1
+ }
+ ]
+ }
+ ],
+ "streams": [
+ {
+ "uid": "multimedia",
+ "verb": "multimedia",
+ "zone": "full-stereo",
+ "volume": 60,
+ "mute": false,
+ "params": {
+ "rate": 48000,
+ "format": "S16_LE"
+ }
+ },
+ {
+ "uid": "navigation",
+ "verb": "navigation",
+ "zone": "front-seats",
+ "volume": 70,
+ "mute": false,
+ "params": {
+ "rate": 48000,
+ "format": "S16_LE"
+ }
+ },
+ {
+ "uid": "emergency",
+ "verb": "emergency",
+ "zone": "front-seats",
+ "volume": 60,
+ "mute": false,
+ "params": {
+ "rate": 48000,
+ "format": "S16_LE"
+ }
+ }
+ ]
+ }
+}
diff --git a/4a-hal/4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h b/4a-hal/4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h
index 3279e40..923ca6b 100644
--- a/4a-hal/4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h
+++ b/4a-hal/4a-hal-utilities/4a-hal-utilities-appfw-responses-handler.h
@@ -39,7 +39,7 @@ enum CallError {
};
// Handle application framework response function
-enum CallError HalUtlHandleAppFwCallError(AFB_ApiT apiHandle, char *apiCalled, char *verbCalled, json_object *callReturnJ, char **returnedStatus, char **returnedInfo);
+extern enum CallError HalUtlHandleAppFwCallError(AFB_ApiT apiHandle, char *apiCalled, char *verbCalled, json_object *callReturnJ, char **returnedStatus, char **returnedInfo);
void HalUtlHandleAppFwCallErrorInRequest(AFB_ReqT request, char *apiCalled, char *verbCalled, json_object *callReturnJ, char *errorStatusToSend);
#endif /* _HAL_UTILITIES_APPFW_RESP_HANDLER_INCLUDE_ */ \ No newline at end of file
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index 7f4245d..10d3975 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -130,9 +130,9 @@ set(CONTROL_SUPPORT_LUA 1 CACHE BOOL "Active or not LUA Support")
# CACHE STRING "Compilation flags for RELEASE build type.")
add_definitions(-DCTL_PLUGIN_MAGIC=7053042648)
-add_definitions(-DCONTROL_CONFIG_PATH="${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/etc:${CMAKE_BINARY_DIR}/package/etc")
-add_definitions(-DCONTROL_PLUGIN_PATH="${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/plugins:${CMAKE_BINARY_DIR}/package/lib/plugins")
-add_definitions(-DCONTROL_LUA_PATH="${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/data:${CMAKE_BINARY_DIR}/package/data")
+add_definitions(-DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/etc:${CMAKE_BINARY_DIR}/package/etc:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/etc")
+add_definitions(-DCONTROL_PLUGIN_PATH="${CMAKE_BINARY_DIR}/package/lib/plugins:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib/plugins")
+add_definitions(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/var")
add_definitions(-DAFB_BINDING_VERSION=3 -DAFB_BINDING_WANT_DYNAPI=1)
# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable]
diff --git a/plugins/lib/CMakeLists.txt b/plugins/lib/CMakeLists.txt
index cea78a8..3fd93ec 100644
--- a/plugins/lib/CMakeLists.txt
+++ b/plugins/lib/CMakeLists.txt
@@ -1,13 +1,13 @@
###########################################################################
-# Copyright 2015, 2016, 2017, 2018 IoT.bzh
+# Copyright 2015, 2016, 2017 IoT.bzh
#
-# author: Jonathan Aillet <jonathan.aillet@iot.bzh>
+# author: Fulup Ar Foll <fulup@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
+# 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,
@@ -16,24 +16,7 @@
# limitations under the License.
###########################################################################
-#PROJECT_TARGET_ADD(example)
-#
-# # Define targets
-# ADD_LIBRARY(${TARGET_NAME} MODULE ${TARGET_NAME}.c)
-#
-# # Alsa Plugin properties
-# SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
-# LABELS "PLUGIN"
-# PREFIX ""
-# SUFFIX ".ctlso"
-# OUTPUT_NAME ${TARGET_NAME}
-# )
-#
-# # Library dependencies (include updates automatically)
-# TARGET_LINK_LIBRARIES(${TARGET_NAME}
-# ${link_libraries}
-# )
-#
-# target_include_directories(${TARGET_NAME}
-# PRIVATE "../app-controller/ctl-lib"
-# PRIVATE "../4a-hal-manager")
+
+# Include any directory not starting with _
+# -----------------------------------------------------
+PROJECT_SUBDIRS_ADD()
diff --git a/plugins/lib/bluetooth/CMakeLists.txt b/plugins/lib/bluetooth/CMakeLists.txt
new file mode 100644
index 0000000..fa28ae1
--- /dev/null
+++ b/plugins/lib/bluetooth/CMakeLists.txt
@@ -0,0 +1,45 @@
+###########################################################################
+# Copyright 2015, 2016, 2017, 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.
+###########################################################################
+
+PROJECT_TARGET_ADD(hal-bt)
+
+ # Define targets
+ ADD_LIBRARY(${TARGET_NAME} MODULE
+ hal-bt.c
+ hal-bt-cb.c
+ hal-bt-data.c
+ )
+
+ # Alsa Plugin properties
+ SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ LABELS "PLUGIN"
+ PREFIX ""
+ SUFFIX ".ctlso"
+ OUTPUT_NAME ${TARGET_NAME}
+ )
+
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ afb-helpers
+ ctl-utilities
+ ${link_libraries}
+ )
+
+ target_include_directories(${TARGET_NAME}
+ PRIVATE "${CMAKE_SOURCE_DIR}/app-controller/ctl-lib"
+ )
diff --git a/plugins/lib/bluetooth/hal-bt-cb.c b/plugins/lib/bluetooth/hal-bt-cb.c
new file mode 100644
index 0000000..c4f0231
--- /dev/null
+++ b/plugins/lib/bluetooth/hal-bt-cb.c
@@ -0,0 +1,171 @@
+/*
+ * 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-plugin.h>
+
+#include "hal-bt-data.h"
+
+/*******************************************************************************
+ * HAL Bluetooth plugin verbs functions *
+ ******************************************************************************/
+
+void HalBtGetStreamingStatus(AFB_ReqT request)
+{
+ struct HalBtPluginData *localHalBtPluginData;
+
+ if(! (localHalBtPluginData = (struct HalBtPluginData *) afb_request_get_vcbdata(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;
+
+ if(! (localHalBtPluginData = (struct HalBtPluginData *) afb_request_get_vcbdata(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(wrap_json_unpack(requestJson, "{s:b}", "status", &requestedBtStreamingStatus)) {
+ AFB_ReqFail(request, "requested_status", "Can't get requested bluetooth streaming status");
+ return;
+ }
+
+ localHalBtPluginData->btStreamEnabled = requestedBtStreamingStatus;
+
+ // TODO JAI : Enable capture stream to playback using 'uid' used at 'set-capture' call
+
+ AFB_ReqSuccess(request, NULL, "Bluetooth streaming status successfully set");
+}
+
+void HalBtGetConnectedBluetoothDevices(AFB_ReqT request)
+{
+ struct HalBtPluginData *localHalBtPluginData;
+ struct HalBtDeviceData *currentBtDeviceData;
+
+ json_object *requestAnswer, *currentBtDeviceObjectJ;
+
+ if(! (localHalBtPluginData = (struct HalBtPluginData *) afb_request_get_vcbdata(request))) {
+ AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
+ return;
+ }
+
+ if(! (currentBtDeviceData = localHalBtPluginData->first)) {
+ AFB_ReqSuccess(request, NULL, "No 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}",
+ "Name", currentBtDeviceData->name,
+ "Address", currentBtDeviceData->address);
+ json_object_array_add(requestAnswer, currentBtDeviceObjectJ);
+
+ currentBtDeviceData = currentBtDeviceData->next;
+ }
+
+ AFB_ReqSuccess(request, requestAnswer, "Connected bluetooth devices list");
+}
+
+void HalBtGetSelectedBluetoothDevice(AFB_ReqT request)
+{
+ struct HalBtPluginData *localHalBtPluginData;
+
+ json_object *selectedBtDeviceObject;
+
+ if(! (localHalBtPluginData = (struct HalBtPluginData *) afb_request_get_vcbdata(request))) {
+ AFB_ReqFail(request, "bt_plugin_data", "Can't get bluetooth plugin data");
+ return;
+ }
+
+ if(! localHalBtPluginData->selectedBtDevice) {
+ AFB_ReqSuccess(request, NULL, "No bluetooth device connected, cannot provide selected device");
+ return;
+ }
+
+ wrap_json_pack(&selectedBtDeviceObject,
+ "{s:s s:s}",
+ "Name", localHalBtPluginData->selectedBtDevice->name,
+ "Address", localHalBtPluginData->selectedBtDevice->address);
+
+ AFB_ReqSuccess(request, selectedBtDeviceObject, "Selected Bluetooth device");
+}
+
+void HalBtSetSelectedBluetoothDevice(AFB_ReqT request)
+{
+ char *requestedBtDeviceToSelect;
+
+ struct HalBtPluginData *localHalBtPluginData;
+ struct HalBtDeviceData *selectedBtDeviceData;
+
+ json_object *requestJson;
+
+ if(! (localHalBtPluginData = (struct HalBtPluginData *) afb_request_get_vcbdata(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(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 bluetooth device to select is not currently connected");
+ return;
+ }
+
+ localHalBtPluginData->selectedBtDevice = selectedBtDeviceData;
+
+ // TODO JAI : Tell the softmixer that we want it as an input using 'bluealsa:HCI=hci0,DEV=F6:32:15:2A:80:70,PROFILE=a2dp'
+ // string as capture associated with an 'uid' using smixer verb 'set-capture'
+
+ 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
new file mode 100644
index 0000000..269da4f
--- /dev/null
+++ b/plugins/lib/bluetooth/hal-bt-cb.h
@@ -0,0 +1,40 @@
+/*
+ * 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-plugin.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_DEVICES_VERB "get_connected_bt_devices"
+#define HAL_BT_GET_SELECTED_DEVICE_VERB "get_selected_bt_device"
+#define HAL_BT_SET_SELECTED_DEVICE_VERB "set_selected_bt_device"
+
+// HAL Bluetooth plugin verbs functions
+void HalBtGetStreamingStatus(AFB_ReqT request);
+void HalBtSetStreamingStatus(AFB_ReqT request);
+void HalBtGetConnectedBluetoothDevices(AFB_ReqT request);
+void HalBtGetSelectedBluetoothDevice(AFB_ReqT request);
+void HalBtSetSelectedBluetoothDevice(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
new file mode 100644
index 0000000..d56d0db
--- /dev/null
+++ b/plugins/lib/bluetooth/hal-bt-data.c
@@ -0,0 +1,220 @@
+/*
+ * 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->uid);
+ free(matchingBtDevice->name);
+ free(matchingBtDevice->address);
+
+ free(matchingBtDevice);
+
+ return 0;
+}
+
+struct HalBtDeviceData *HalBtDataAddBtDeviceToBtDeviceList(struct HalBtDeviceData **firstBtDeviceData, json_object *currentSingleBtDeviceDataJ)
+{
+ char *currentBtDeviceName, *currentBtDeviceAddress;
+
+ 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}",
+ "Name", &currentBtDeviceName,
+ "Address", &currentBtDeviceAddress)) {
+ HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData);
+ return NULL;
+ }
+
+ if(asprintf(&currentBtDeviceData->uid, "BT#%s", currentBtDeviceAddress) == -1) {
+ 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)
+{
+ unsigned int currentBtDeviceIsConnected;
+
+ char *currentBtDeviceAddress, *currentBtDeviceIsConnectedString;
+
+ struct HalBtDeviceData *currentBtDevice;
+ if(! halBtPluginData || ! currentSingleBtDeviceDataJ)
+ return -1;
+
+ if(wrap_json_unpack(currentSingleBtDeviceDataJ,
+ "{s:s s:s}",
+ "Address", &currentBtDeviceAddress,
+ "Connected", &currentBtDeviceIsConnectedString)) {
+ return -2;
+ }
+
+ currentBtDeviceIsConnected = ! strncmp(currentBtDeviceIsConnectedString, "True", strlen(currentBtDeviceIsConnectedString));
+ currentBtDevice = HalBtDataSearchBtDeviceByAddress(&halBtPluginData->first, currentBtDeviceAddress);
+
+ if(currentBtDevice && ! currentBtDeviceIsConnected) {
+ if(HalBtDataRemoveSelectedBtDeviceFromList(&halBtPluginData->first, currentBtDevice))
+ return -3;
+
+ if(halBtPluginData->selectedBtDevice == currentBtDevice)
+ halBtPluginData->selectedBtDevice = halBtPluginData->first;
+ }
+ else if(! currentBtDevice && currentBtDeviceIsConnected) {
+
+ if(! HalBtDataAddBtDeviceToBtDeviceList(&halBtPluginData->first, currentSingleBtDeviceDataJ))
+ return -4;
+
+ if(! halBtPluginData->selectedBtDevice)
+ halBtPluginData->selectedBtDevice = halBtPluginData->first;
+ }
+
+ 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;
+} \ No newline at end of file
diff --git a/plugins/lib/bluetooth/hal-bt-data.h b/plugins/lib/bluetooth/hal-bt-data.h
new file mode 100644
index 0000000..030c8d1
--- /dev/null
+++ b/plugins/lib/bluetooth/hal-bt-data.h
@@ -0,0 +1,52 @@
+/*
+ * 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>
+
+// Structure to store bluetooth device data
+struct HalBtDeviceData {
+ char *uid;
+ char *name;
+ char *address;
+ // TODO JAI : Get bluetooth device's profile and store it here
+
+ struct HalBtDeviceData *next;
+};
+
+// Structure to store hal bluetooth plugin data
+struct HalBtPluginData {
+ // TODO JAI : Get hci device and store it here
+ 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.c b/plugins/lib/bluetooth/hal-bt.c
new file mode 100644
index 0000000..32fbbf7
--- /dev/null
+++ b/plugins/lib/bluetooth/hal-bt.c
@@ -0,0 +1,195 @@
+/*
+ * 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-plugin.h>
+
+#include "hal-bt.h"
+#include "hal-bt-cb.h"
+#include "hal-bt-data.h"
+
+// Local (static) Hal manager data structure
+static struct HalBtPluginData localHalBtPluginData;
+
+CTLP_CAPI_REGISTER(HAL_BT_PLUGIN_NAME)
+
+// Call at initialisation time
+CTLP_ONLOAD(plugin, callbacks)
+{
+ AFB_ApiNotice(plugin->api, "Hal-Bt Plugin Register: uid='%s' 'info='%s'", plugin->uid, plugin->info);
+
+ memset(&localHalBtPluginData, '\0', sizeof(localHalBtPluginData));
+
+ AFB_RequireApi(plugin->api, BT_MANAGER_API, 1);
+
+ /* TDB JAI :
+ - Register 'init' plugin function (HAL_BT_PLUGIN_NAME#init) as onload action here (to avoid adding it in json)
+ - Register 'event' plugin function (HAL_BT_PLUGIN_NAME#event) as action for BT_MANAGER_API#BT_MANAGER_DEVICE_UPDATE_EVENT event here (to avoid adding it in json)
+ */
+
+ return 0;
+}
+
+// Call at onload time
+CTLP_CAPI(init, source, argsJ, queryJ)
+{
+ unsigned int err;
+
+ struct json_object *toSendJ, *returnedJ, *returnedBtList = NULL;
+
+ AFB_ApiNotice(source->api, "Initializing HAL-BT plugin");
+
+ // Loading hal BT plugin specific verbs
+ if(afb_dynapi_add_verb(source->api,
+ HAL_BT_GET_STREAMING_STATUS_VERB,
+ "Get Bluetooth streaming status",
+ HalBtGetStreamingStatus,
+ (void *) &localHalBtPluginData,
+ NULL,
+ 0)) {
+ AFB_ApiError(source->api, "%s: error while creating verb for bluetooth plugin : '%s'", __func__, HAL_BT_GET_STREAMING_STATUS_VERB);
+ return -1;
+ }
+
+ if(afb_dynapi_add_verb(source->api,
+ HAL_BT_SET_STREAMING_STATUS_VERB,
+ "Set Bluetooth streaming status",
+ HalBtSetStreamingStatus,
+ (void *) &localHalBtPluginData,
+ NULL,
+ 0)) {
+ AFB_ApiError(source->api, "%s: error while creating verb for bluetooth plugin : '%s'", __func__, HAL_BT_SET_STREAMING_STATUS_VERB);
+ return -2;
+ }
+
+ if(afb_dynapi_add_verb(source->api,
+ HAL_BT_GET_CONNECTED_DEVICES_VERB,
+ "Get connected Bluetooth devices list",
+ HalBtGetConnectedBluetoothDevices,
+ (void *) &localHalBtPluginData,
+ NULL,
+ 0)) {
+ AFB_ApiError(source->api, "%s: error while creating verb for bluetooth plugin : '%s'", __func__, HAL_BT_GET_CONNECTED_DEVICES_VERB);
+ return -3;
+ }
+
+ if(afb_dynapi_add_verb(source->api,
+ HAL_BT_GET_SELECTED_DEVICE_VERB,
+ "Get selected Bluetooth device",
+ HalBtGetSelectedBluetoothDevice,
+ (void *) &localHalBtPluginData,
+ NULL,
+ 0)) {
+ AFB_ApiError(source->api, "%s: error while creating verb for bluetooth plugin : '%s'", __func__, HAL_BT_GET_SELECTED_DEVICE_VERB);
+ return -4;
+ }
+
+ if(afb_dynapi_add_verb(source->api,
+ HAL_BT_SET_SELECTED_DEVICE_VERB,
+ "Set selected Bluetooth device",
+ HalBtSetSelectedBluetoothDevice,
+ (void *) &localHalBtPluginData,
+ NULL,
+ 0)) {
+ AFB_ApiError(source->api, "%s: error while creating verb for bluetooth plugin : '%s'", __func__, HAL_BT_SET_SELECTED_DEVICE_VERB);
+ return -5;
+ }
+
+ // 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",
+ BT_MANAGER_API,
+ BT_MANAGER_SUBSCRIBE_VERB);
+
+ return -6;
+ }
+ else if(! wrap_json_unpack(returnedJ, "{s:{s:s}}", "request", "info", NULL)) {
+ AFB_ApiError(source->api,
+ "Couldn't subscribe to event '%s' during call to verb '%s' of api '%s'",
+ BT_MANAGER_DEVICE_UPDATE_EVENT,
+ BT_MANAGER_SUBSCRIBE_VERB,
+ BT_MANAGER_API);
+ return -7;
+ }
+
+ if(AFB_ServiceSync(source->api, BT_MANAGER_API, BT_MANAGER_GET_DEVICES_VERB, NULL, &returnedJ)) {
+ AFB_ApiError(source->api,
+ "Error during call to verb %s of %s api",
+ BT_MANAGER_API,
+ BT_MANAGER_GET_DEVICES_VERB);
+
+ 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);
+ return -9;
+ }
+
+ if((err = HalBtDataHandleReceivedMutlipleBtDeviceData(&localHalBtPluginData, returnedBtList)))
+ return (10 * err);
+
+ if(localHalBtPluginData.selectedBtDevice) {
+ localHalBtPluginData.btStreamEnabled = 1;
+ /* TODO JAI : send selected device to softmixer (and enable stream here for now)
+ * Tell the softmixer that we want it as an input using 'bluealsa:HCI=hci0,DEV=F6:32:15:2A:80:70,PROFILE=a2dp'
+ string as capture associated with an 'uid' using smixer verb 'set-capture'
+ * Enable capture stream to playback using 'uid' used at 'set-capture' call
+ */
+ }
+
+ return 0;
+}
+
+// This receive Hal bluetooth plugin events
+CTLP_CAPI(events, source, argsJ, queryJ)
+{
+ struct HalBtDeviceData *previouslySelectedBtDevice = localHalBtPluginData.selectedBtDevice;
+
+ AFB_ApiNotice(source->api, "JAI: bt event received: %s", json_object_get_string(queryJ));
+
+ if(HalBtDataHandleReceivedSingleBtDeviceData(&localHalBtPluginData, queryJ)) {
+ AFB_ApiError(source->api, "Error while decoding bluetooth event received json (%s)", json_object_get_string(queryJ));
+ return -1;
+ }
+
+ if(localHalBtPluginData.selectedBtDevice && localHalBtPluginData.selectedBtDevice != previouslySelectedBtDevice) {
+ localHalBtPluginData.btStreamEnabled = 1;
+ /* TODO JAI : send selected device to softmixer (and enable stream here for now)
+ * Tell the softmixer that we want it as an input using 'bluealsa:HCI=hci0,DEV=F6:32:15:2A:80:70,PROFILE=a2dp'
+ string as capture associated with an 'uid' using smixer verb 'set-capture'
+ * Enable capture stream to playback using 'uid' used at 'set-capture' call
+ */
+ }
+ else if (! localHalBtPluginData.selectedBtDevice) {
+ localHalBtPluginData.btStreamEnabled = 0;
+ // TODO JAI: Disable capture stream to playback using 'uid' used at 'set-capture' call
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/lib/bluetooth/hal-bt.h b/plugins/lib/bluetooth/hal-bt.h
new file mode 100644
index 0000000..ad94a7e
--- /dev/null
+++ b/plugins/lib/bluetooth/hal-bt.h
@@ -0,0 +1,33 @@
+/*
+ * 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_INCLUDE_
+#define _HAL_BT_INCLUDE_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define HAL_BT_PLUGIN_NAME "hal-bt"
+
+#define BT_MANAGER_API "Bluetooth-Manager"
+#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