From 92eb34c496de489f288c0eb59c9d84d6cf31decd Mon Sep 17 00:00:00 2001 From: Jonathan Aillet Date: Fri, 24 Aug 2018 20:17:12 +0200 Subject: 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 --- 4a-hal-cfg-reference/hal-4a-rcar-m3-bt.json | 198 +++++++++++++++++++ .../4a-hal-utilities-appfw-responses-handler.h | 2 +- conf.d/cmake/config.cmake | 6 +- plugins/lib/CMakeLists.txt | 31 +-- plugins/lib/bluetooth/CMakeLists.txt | 45 +++++ plugins/lib/bluetooth/hal-bt-cb.c | 171 ++++++++++++++++ plugins/lib/bluetooth/hal-bt-cb.h | 40 ++++ plugins/lib/bluetooth/hal-bt-data.c | 220 +++++++++++++++++++++ plugins/lib/bluetooth/hal-bt-data.h | 52 +++++ plugins/lib/bluetooth/hal-bt.c | 195 ++++++++++++++++++ plugins/lib/bluetooth/hal-bt.h | 33 ++++ 11 files changed, 965 insertions(+), 28 deletions(-) create mode 100644 4a-hal-cfg-reference/hal-4a-rcar-m3-bt.json create mode 100644 plugins/lib/bluetooth/CMakeLists.txt create mode 100644 plugins/lib/bluetooth/hal-bt-cb.c create mode 100644 plugins/lib/bluetooth/hal-bt-cb.h create mode 100644 plugins/lib/bluetooth/hal-bt-data.c create mode 100644 plugins/lib/bluetooth/hal-bt-data.h create mode 100644 plugins/lib/bluetooth/hal-bt.c create mode 100644 plugins/lib/bluetooth/hal-bt.h 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 +# author: Fulup Ar Foll # # 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 +# +# 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 + * + * 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 +#include +#include + +#include + +#include + +#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(¤tBtDeviceObjectJ, + "{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 + * + * 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 +#include +#include + +#include + +#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 + * + * 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 +#include +#include + +#include + +#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", ¤tBtDeviceName, + "Address", ¤tBtDeviceAddress)) { + HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData); + return NULL; + } + + if(asprintf(¤tBtDeviceData->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", ¤tBtDeviceAddress, + "Connected", ¤tBtDeviceIsConnectedString)) { + 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 + * + * 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 +#include +#include + +#include + +// 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 + * + * 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 +#include +#include + +#include + +#include + +#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 + * + * 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 +#include +#include + +#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 -- cgit 1.2.3-korg