From 46563ba950bce914454aea923d0bb3761043fd2c Mon Sep 17 00:00:00 2001 From: Ossama Othman Date: Mon, 27 Apr 2015 14:36:19 -0700 Subject: [PATCH] Implemented Linux BLE transport. This is an implementation of the proposed OIC Bluetooth Low Energy transport for Linux. Change-Id: Id105a1d7a86c59d41900c6e4b0fc1b0a60b97c75 Signed-off-by: Ossama Othman Reviewed-on: https://gerrit.iotivity.org/gerrit/1125 Tested-by: jenkins-iotivity Reviewed-by: Jon A. Cruz Reviewed-by: Erich Keane --- .gitignore | 1 + ...e-advertising-Fix-using-wrong-instance-id.patch | 73 ++ extlibs/bluez/SConscript | 77 ++ extlibs/bluez/bluetooth.service.in.patch | 13 + resource/csdk/SConscript | 5 +- resource/csdk/connectivity/SConscript | 10 +- resource/csdk/connectivity/inc/cagattservice.h | 76 ++ resource/csdk/connectivity/inc/caleadapter.h | 392 +----- resource/csdk/connectivity/inc/caleinterface.h | 332 +++-- .../csdk/connectivity/src/bt_le_adapter/SConscript | 8 +- .../src/bt_le_adapter/android/caleclient.c | 10 +- .../src/bt_le_adapter/android/calenwmonitor.c | 7 + .../src/bt_le_adapter/android/caleserver.c | 11 +- .../src/bt_le_adapter/android/caleutils.h | 7 +- .../src/bt_le_adapter/arduino/cableclient.cpp | 2 +- .../src/bt_le_adapter/arduino/cablenwmonitor.cpp | 7 + .../src/bt_le_adapter/arduino/cableserver.cpp | 6 +- .../connectivity/src/bt_le_adapter/caleadapter.c | 671 +++++++--- .../connectivity/src/bt_le_adapter/linux/README | 57 + .../src/bt_le_adapter/linux/SConscript | 141 ++- .../src/bt_le_adapter/linux/advertisement.c | 132 ++ .../src/bt_le_adapter/linux/advertisement.h | 79 ++ .../connectivity/src/bt_le_adapter/linux/bluez.h | 53 + .../connectivity/src/bt_le_adapter/linux/bluez.xml | 170 +++ .../src/bt_le_adapter/linux/caleadapter.c | 121 -- .../src/bt_le_adapter/linux/caleinterface.c | 1330 ++++++++++++++++++++ .../connectivity/src/bt_le_adapter/linux/central.c | 444 +++++++ .../connectivity/src/bt_le_adapter/linux/central.h | 88 ++ .../src/bt_le_adapter/linux/characteristic.c | 622 +++++++++ .../src/bt_le_adapter/linux/characteristic.h | 146 +++ .../connectivity/src/bt_le_adapter/linux/client.c | 274 ++++ .../connectivity/src/bt_le_adapter/linux/client.h | 91 ++ .../connectivity/src/bt_le_adapter/linux/context.h | 196 +++ .../src/bt_le_adapter/linux/descriptor.c | 225 ++++ .../src/bt_le_adapter/linux/descriptor.h | 100 ++ .../src/bt_le_adapter/linux/gatt_dbus.h | 89 ++ .../src/bt_le_adapter/linux/object_manager.xml | 45 + .../linux/org.iotivity.gatt.service.conf.in | 19 + .../src/bt_le_adapter/linux/peripheral.c | 702 +++++++++++ .../src/bt_le_adapter/linux/peripheral.h | 128 ++ .../connectivity/src/bt_le_adapter/linux/recv.c | 64 + .../connectivity/src/bt_le_adapter/linux/recv.h | 98 ++ .../connectivity/src/bt_le_adapter/linux/server.c | 110 ++ .../connectivity/src/bt_le_adapter/linux/server.h | 56 + .../connectivity/src/bt_le_adapter/linux/service.c | 264 ++++ .../connectivity/src/bt_le_adapter/linux/service.h | 93 ++ .../connectivity/src/bt_le_adapter/linux/utils.c | 245 ++++ .../connectivity/src/bt_le_adapter/linux/utils.h | 147 +++ .../src/bt_le_adapter/tizen/cableclient.c | 29 +- .../src/bt_le_adapter/tizen/cablenwmonitor.c | 7 + .../src/bt_le_adapter/tizen/cableserver.c | 20 +- .../src/bt_le_adapter/tizen/cableutil.c | 5 +- .../src/bt_le_adapter/tizen/cableutil.h | 15 - tools/scons/RunTest.py | 5 + tools/valgrind/iotivity.supp | 115 ++ 55 files changed, 7357 insertions(+), 876 deletions(-) create mode 100644 extlibs/bluez/0001-core-advertising-Fix-using-wrong-instance-id.patch create mode 100644 extlibs/bluez/SConscript create mode 100644 extlibs/bluez/bluetooth.service.in.patch create mode 100644 resource/csdk/connectivity/inc/cagattservice.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/README create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.xml delete mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/caleadapter.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/caleinterface.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/central.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/central.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/client.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/client.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/context.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/gatt_dbus.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/object_manager.xml create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/org.iotivity.gatt.service.conf.in create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/recv.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/recv.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/server.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/server.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/service.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/service.h create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/utils.c create mode 100644 resource/csdk/connectivity/src/bt_le_adapter/linux/utils.h diff --git a/.gitignore b/.gitignore index c891e5c..3cb9926 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ extlibs/arduino/arduino-1.5.8 build_common/arduino/extlibs/arduino/arduino-1.5.8 extlibs/tinydtls/dtls-client extlibs/tinydtls/dtls-server +extlibs/bluez/bluez # Ignore editor (e.g. Emacs) backup and autosave files *~ diff --git a/extlibs/bluez/0001-core-advertising-Fix-using-wrong-instance-id.patch b/extlibs/bluez/0001-core-advertising-Fix-using-wrong-instance-id.patch new file mode 100644 index 0000000..dc290b3 --- /dev/null +++ b/extlibs/bluez/0001-core-advertising-Fix-using-wrong-instance-id.patch @@ -0,0 +1,73 @@ +From 31fafe0dd2265cd181734ee909554f24fd3c3c20 Mon Sep 17 00:00:00 2001 +From: Luiz Augusto von Dentz +Date: Fri, 26 Jun 2015 14:52:22 +0300 +Subject: [PATCH] core/advertising: Fix using wrong instance id + +The instance id shall be limited to the number of instance the kernel +support, and since this will probably be quite small the ids should be +reused once the client unregistered. +--- + src/advertising.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/src/advertising.c b/src/advertising.c +index d6ab084..3763d85 100644 +--- a/src/advertising.c ++++ b/src/advertising.c +@@ -48,7 +48,7 @@ struct btd_advertising { + uint16_t mgmt_index; + uint8_t max_adv_len; + uint8_t max_ads; +- unsigned int next_instance_id; ++ unsigned int instance_bitmap; + }; + + #define AD_TYPE_BROADCAST 0 +@@ -155,6 +155,8 @@ static void advertisement_remove(void *data) + + queue_remove(ad->manager->ads, ad); + ++ util_clear_uid(&ad->manager->instance_bitmap, ad->instance); ++ + g_idle_add(advertisement_free_idle_cb, ad); + } + +@@ -633,6 +635,7 @@ static DBusMessage *register_advertisement(DBusConnection *conn, + DBusMessageIter args; + struct advertisement *ad; + struct dbus_obj_match match; ++ uint8_t instance; + + DBG("RegisterAdvertisement"); + +@@ -649,7 +652,8 @@ static DBusMessage *register_advertisement(DBusConnection *conn, + if (queue_find(manager->ads, match_advertisement, &match)) + return btd_error_already_exists(msg); + +- if (queue_length(manager->ads) >= manager->max_ads) ++ instance = util_get_uid(&manager->instance_bitmap, manager->max_ads); ++ if (!instance) + return btd_error_failed(msg, "Maximum advertisements reached"); + + dbus_message_iter_next(&args); +@@ -664,7 +668,7 @@ static DBusMessage *register_advertisement(DBusConnection *conn, + + DBG("Registered advertisement at path %s", match.path); + +- ad->instance = manager->next_instance_id++; ++ ad->instance = instance; + ad->manager = manager; + + queue_push_tail(manager->ads, ad); +@@ -787,8 +791,6 @@ advertising_manager_create(struct btd_adapter *adapter) + + manager->ads = queue_new(); + +- manager->next_instance_id = 1; +- + return manager; + } + +-- +2.1.4 + diff --git a/extlibs/bluez/SConscript b/extlibs/bluez/SConscript new file mode 100644 index 0000000..05b2b1b --- /dev/null +++ b/extlibs/bluez/SConscript @@ -0,0 +1,77 @@ +# ------------------------------------------------------------------------ +# Copyright 2015 Intel Corporation +# +# 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. +# ------------------------------------------------------------------------ + +###################################################################### +# BlueZ download script +# +# BlueZ is currently only used by the IoTivity Linux BLE transport. +# The IoTivity Linux BLE transport depends on two headers found in the +# BlueZ bluetooth library: and +# . Those headers are used solely when pulling in +# the types and constants necessary to a create kernel Bluetooth +# management socket. There is no need to link BlueZ's libbluetooth +# library. +###################################################################### +import os +import subprocess + +Import('env') + +# First check if the BlueZ bluetooth library development files are +# installed. Add the location of the bluetooth library headers to the +# include path if they are installed. Otherwise, download the BlueZ +# source code and adjust the include path, accordingly. + +# The subprocess.run() function became available in Python 3.5. Use +# the older high-level API instead, since some installations may still +# be using older versions of Python. +bluez_installed = (subprocess.call([ 'pkg-config', + 'bluez', + '--exists' ]) == 0) + +if bluez_installed: + env.ParseConfig("pkg-config bluez --cflags-only-I") + Return() + +if not os.path.exists('bluez'): + bluez = env.Action([ + 'git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git', + 'cd bluez && ln -s lib bluetooth && cd -']) + + print 'Downloading BlueZ ...' + if env.Execute(bluez): + print ''' +*********************************** Error ********************************* +* Please download Bluez 5.32 or better, build it and install it. * +* Alternatively, but less preferrably, create a symbolic as follows * +* $ cd extlibs/bluez * +* $ git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git * +* $ cd bluez * +* $ ln -s lib bluetooth * +* $ cd ../../.. * +*************************************************************************** +''' + Exit(1) + else: + print 'BlueZ download complete.' + +env.AppendUnique(CPPPATH = ['#extlibs/bluez/bluez']) + + +# Local Variables: +# mode:python +# indent-tabs-mode: nil +# End: diff --git a/extlibs/bluez/bluetooth.service.in.patch b/extlibs/bluez/bluetooth.service.in.patch new file mode 100644 index 0000000..40bb01d --- /dev/null +++ b/extlibs/bluez/bluetooth.service.in.patch @@ -0,0 +1,13 @@ +diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in +index 35e9457..8204a79 100644 +--- a/src/bluetooth.service.in ++++ b/src/bluetooth.service.in +@@ -5,7 +5,7 @@ Documentation=man:bluetoothd(8) + [Service] + Type=dbus + BusName=org.bluez +-ExecStart=@libexecdir@/bluetoothd ++ExecStart=@libexecdir@/bluetoothd --experimental + NotifyAccess=main + #WatchdogSec=10 + #Restart=on-failure diff --git a/resource/csdk/SConscript b/resource/csdk/SConscript index cee8dc9..a9e30c8 100644 --- a/resource/csdk/SConscript +++ b/resource/csdk/SConscript @@ -66,10 +66,11 @@ if target_os not in ['windows', 'winrt']: liboctbstack_env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')]) -if target_os in ['android', 'linux','tizen']: - liboctbstack_env.AppendUnique(LIBS = ['connectivity_abstraction']) +if target_os in ['android', 'linux', 'tizen']: + liboctbstack_env.PrependUnique(LIBS = ['connectivity_abstraction']) if with_ra: liboctbstack_env.AppendUnique(LIBS = ['ra_xmpp']) + liboctbstack_env.AppendUnique(LIBS = ['coap', 'm']) if target_os == 'tizen': diff --git a/resource/csdk/connectivity/SConscript b/resource/csdk/connectivity/SConscript index f7560e6..e68924f 100644 --- a/resource/csdk/connectivity/SConscript +++ b/resource/csdk/connectivity/SConscript @@ -25,7 +25,7 @@ if 'ALL' in transport: if with_ra == True: env.AppendUnique(CPPDEFINES = ['RA_ADAPTER']) if target_os == 'linux': - env.AppendUnique(CPPDEFINES = ['IP_ADAPTER','NO_EDR_ADAPTER','NO_LE_ADAPTER']) + env.AppendUnique(CPPDEFINES = ['IP_ADAPTER','NO_EDR_ADAPTER','LE_ADAPTER']) elif target_os == 'tizen': env.AppendUnique(CPPDEFINES = ['IP_ADAPTER','EDR_ADAPTER','LE_ADAPTER']) elif target_os in['darwin','ios']: @@ -45,12 +45,8 @@ else: env.AppendUnique(CPPDEFINES = ['NO_EDR_ADAPTER']) if 'BLE' in transport: - if target_os == 'linux': - print "CA Transport BLE is not supported in Linux" - Exit(1) - else: - env.AppendUnique(CPPDEFINES = ['LE_ADAPTER']) - print "CA Transport is BLE" + env.AppendUnique(CPPDEFINES = ['LE_ADAPTER']) + print "CA Transport is BLE" else: env.AppendUnique(CPPDEFINES = ['NO_LE_ADAPTER']) diff --git a/resource/csdk/connectivity/inc/cagattservice.h b/resource/csdk/connectivity/inc/cagattservice.h new file mode 100644 index 0000000..a6ad4d5 --- /dev/null +++ b/resource/csdk/connectivity/inc/cagattservice.h @@ -0,0 +1,76 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_GATT_SERVICE_H +#define CA_GATT_SERVICE_H + + +/** + * @name OIC GATT Transport Constants + * + * Group of constants, such as UUIDs, specific to the OIC GATT + * Transport Profile. + */ +//@{ +/// OIC Transport Profile GATT service UUID. +#define CA_GATT_SERVICE_UUID "ADE3D529-C784-4F63-A987-EB69F70EE816" + +/// OIC Transport Profile GATT request characteristic UUID. +#define CA_GATT_REQUEST_CHRC_UUID "AD7B334F-4637-4B86-90B6-9D787F03D218" + +/** + * Standard Bluetooth GATT characteristic user description descriptor + * UUID. + * + * @note Used by both the OIC GATT request and response + * characteristics. + */ +#define CA_GATT_CHRC_USER_DESCRIPTION_DESC_UUID "2901" + +/** + * OIC Transport Profile GATT request characteristic user description + * descriptor value. + */ +#define CA_GATT_REQUEST_USER_DESCRIPTION "OIC Node Request" + +/// OIC Transport Profile GATT response characteristic UUID. +#define CA_GATT_RESPONSE_CHRC_UUID "E9241982-4580-42C4-8831-95048216B256" + +/** + * OIC Transport Profile GATT response characteristic user description + * descriptor value. + */ +#define CA_GATT_RESPONSE_USER_DESCRIPTION "OIC Node Response" + +/** + * Standard Bluetooth GATT client characteristic configuration + * descriptor UUID. + * + * @note Only used by the OIC GATT response characteristic. + */ +#define CA_GATT_CONFIGURATION_DESC_UUID "2902" + +/** + * OIC Transport Profile GATT response client characteristic + * configuration descriptor value. + */ +#define CA_GATT_RESPONSE_CONFIG_DESC "0001" +//@} + + +#endif // CA_GATT_SERVICE_H diff --git a/resource/csdk/connectivity/inc/caleadapter.h b/resource/csdk/connectivity/inc/caleadapter.h index 88b94af..bf85d27 100644 --- a/resource/csdk/connectivity/inc/caleadapter.h +++ b/resource/csdk/connectivity/inc/caleadapter.h @@ -18,414 +18,42 @@ * ******************************************************************/ -/** - * @file - * - * This file contains the APIs for LE adapters to be implemented. - */ #ifndef CA_LEADAPTER_H_ #define CA_LEADAPTER_H_ #include "cacommon.h" #include "caadapterinterface.h" -#include "cathreadpool.h" /* for thread pool */ +#include "cathreadpool.h" -// BLE Interface APIs. #ifdef __cplusplus extern "C" { #endif -/** - * Stores the information of the Data to be sent from the queues. - * This structure will be pushed to the sender/receiver queue for processing. - */ -typedef struct -{ - CAEndpoint_t *remoteEndpoint; /**< Remote endpoint contains the - information of remote device. */ - void *data; /**< Data to be transmitted over LE transport. */ - uint32_t dataLen; /**< Length of the data being transmitted. */ -} CALEData_t; - -/** - * Stores information of all the senders. - * This structure will be used to track and de-fragmentation all incoming data packets. - */ -typedef struct -{ - uint32_t recvDataLen; - uint32_t totalDataLen; - char *defragData; - CAEndpoint_t *remoteEndpoint; -}CABLESenderInfo_t; /** * Initialize LE connectivity interface. + * * @param[in] registerCallback Callback to register LE interfaces to - * Connectivity Abstraction Layer. + * Connectivity Abstraction Layer. * @param[in] reqRespCallback Callback to notify request and response - * messages from server(s) started at - * Connectivity Abstraction Layer. + * messages from server(s) started at + * Connectivity Abstraction Layer. * @param[in] netCallback Callback to notify the network additions - * to Connectivity Abstraction Layer. + * to Connectivity Abstraction Layer. * @param[in] errorCallback errorCallback to notify error to - * connectivity common logic layer from adapter. + * connectivity common logic layer from adapter. * @param[in] handle Threadpool Handle. + * * @return ::CA_STATUS_OK or Appropriate error code. */ CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback, CANetworkPacketReceivedCallback reqRespCallback, CANetworkChangeCallback netCallback, - CAErrorHandleCallback errorCallback, ca_thread_pool_t handle); - -/** - * Starting LE connectivity adapters. - * As its peer to peer it doesnot require to start any servers. - * @return ::CA_STATUS_OK or Appropriate error code. - */ -CAResult_t CAStartLE(); - -/** - * Starting listening server for receiving multicast search requests. - * Transport Specific Behavior: - * LE Starts GATT Server with prefixed UUID and Characteristics as per - * OIC Specification. - * @return ::CA_STATUS_OK or Appropriate error code. - */ -CAResult_t CAStartLEListeningServer(); - -/** - * for starting discovery servers for receiving multicast advertisements. - * Transport Specific Behavior: - * LE Starts GATT Server with prefixed UUID and Characteristics as per - * OIC Specification. - * @return ::CA_STATUS_OK or Appropriate error code - */ -CAResult_t CAStartLEDiscoveryServer(); - -/** - * Sends data to the endpoint using the adapter connectivity. - * @param[in] endpoint Remote Endpoint information (like ipaddress , - * port, reference uri and connectivity type) to - * which the unicast data has to be sent. - * @param[in] data Data which required to be sent. - * @param[in] dataLen Size of data to be sent. - * @note dataLen must be > 0. - * @return The number of bytes sent on the network, or -1 on error. - */ -int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data, - uint32_t dataLen); - -/** - * Sends Multicast data to the endpoint using the LE connectivity. - * @param[in] endpoint Remote Endpoint information to which the - * unicast data has to be sent. - * @param[in] data Data which required to be sent. - * @param[in] dataLen Size of data to be sent. - * @note dataLen must be > 0. - * @return The number of bytes sent on the network, or -1 on error. - */ -int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen); - -/** - * Starts notification server on EDR adapters. - * @return ::CA_STATUS_OK or Appropriate error code. - */ -CAResult_t CAStartLENotifyServer(); - -/** - * Send notification information. - * @param[in] endpoint Remote Endpoint information (like ipaddress , - * port, reference uri and connectivity type) - * to which the unicast data has to be sent. - * @param[in] data Data which required to be sent. - * @param[in] dataLen Size of data to be sent. - * @note dataLen must be > 0. - * @return The number of bytes sent on the network, or 0 on error. - */ -uint32_t CASendLENotification(const CAEndpoint_t *endpoint, const void *data, - uint32_t dataLen); - -/** - * Get LE Connectivity network information. - * @param[out] info Local connectivity information structures. - * @param[out] size Number of local connectivity structures. - * @return ::CA_STATUS_OK or Appropriate error code. - */ -CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size); - -/** - * Read Synchronous API callback. - * @return ::CA_STATUS_OK or Appropriate error code. - */ -CAResult_t CAReadLEData(); - -/** - * Stopping the adapters and close socket connections. - * LE Stops all GATT servers and GATT Clients. - * @return ::CA_STATUS_OK or Appropriate error code. - */ -CAResult_t CAStopLE(); - -/** - * Terminate the LE connectivity adapter. - * Configuration information will be deleted from further use. - */ -void CATerminateLE(); - -/** - * This function will receive the data from the GattServer and add the data to - * the Server receiver queue. - * @param[in] remoteAddress Remote address of the device from where data - * is received. - * @param[in] serviceUUID Uuid of the OIC service running on the remote - * device. - * @param[in] data Actual data Received from the remote device. - * @param[in] dataLength Length of the data received from the remote device. - * @param[in] sentLength Length of the data sent from the remote device. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - * - */ -CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, const char *serviceUUID, - const void *data, uint32_t dataLength, - uint32_t *sentLength); + CAErrorHandleCallback errorCallback, + ca_thread_pool_t handle); -/** - * This function will receive the data from the GattClient and add the - * data into the Client receiver queue. - * @param[in] remoteAddress Remote address of the device from where data - * is received. - * @param[in] serviceUUID Uuid of the OIC service running on the remote - * device. - * @param[in] data Actual data received from the remote device. - * @param[in] dataLength Length of the data received from the remote device. - * @param[in] sentLength Length of the data sent from the remote device. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, const char *serviceUUID, - const void *data, uint32_t dataLength, - uint32_t *sentLength); - -/** - * This function is used to set the NetworkPacket received callback to CA - * layer from adapter layer. - * @param[in] callback callback handle sent from the upper layer. - */ -void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback); - -/** - * This function will push the data from CA layer to the Sender processor queue. - * - * @param[in] remoteEndpoint Remote endpoint information of the server. - * @param[in] data Data to be transmitted from LE. - * @param[in] dataLen length of the Data being transmitted. - * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint, - const void *data, uint32_t dataLen); - -/** - * This function will push the data from CA layer to the Sender processor queue. - * - * @param[in] remoteEndpoint Remote endpoint information of the server. - * @param[in] data Data to be transmitted from LE. - * @param[in] dataLen length of the Data being transmitted. - * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint, - const void *data, uint32_t dataLen); - -/** - * This function will be associated with the sender queue for GattServer. - * This function will fragment the data to the MTU of the transport and - * send the data in fragments to the adapters. The function will be - * blocked untill all data is sent out from the adapter. - * - * @param[in] threadData Data pushed to the queue which contains the info - * about RemoteEndpoint and Data. - */ -void CALEServerSendDataThread(void *threadData); - -/** - * This function will be associated with the sender queue for GattClient. - * This function will fragment the data to the MTU of the transport and - * send the data in fragments to the adapters. The function will be - * blocked until all data is sent out from the adapter. - * - * @param[in] threadData Data pushed to the queue which contains the info - * about RemoteEndpoint and Data. - */ -void CALEClientSendDataThread(void *threadData); - -/** - * This function will be associated with the receiver queue. This function will reassemble - * the received data from each sender respectively and will send it up to CA layer. - * Respective sender's header will provide the length of the data sent. - * - * @param [IN] threadData Data pushed to the queue which contains the info about RemoteEndpoint - * and Data. - */ -void CALEDataReceiverHandler(void *threadData); - -/** - * This function is used to initialize both GattServer and GattClient - * queues. All four queues will be initialized with this function invocations. - */ -void CAInitLEQueues(); - -/** - * This function will stop all queues created for GattServer and GattClient. All - * four queues will be be stopped with this function invocations. - */ -void CAStopLEQueues(); - -/** - * This function will terminate all queues created for GattServer and - * GattClient. All four queues will be be terminated with this function - * invocations. - */ -void CATerminateLEQueues(); - -/** - * This function will initialize the Receiver and Sender queues for - * GattServer. This function will inturn call the functions - * CAInitBleServerReceiverQueue() and CAInitBleServerSenderQueue() to - * initialize the queues. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CAResult_t CAInitLEServerQueues(); - -/** - * This function will initialize the Receiver and Sender queues for - * GattClient. This function will inturn call the functions - * CAInitBleClientReceiverQueue() and CAInitBleClientSenderQueue() to - * initialize the queues. - * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - * - */ -CAResult_t CAInitLEClientQueues(); - -/** - * This function will initialize the Receiver queue for GattServer. This - * will initialize the queue to process the function - * CABLEServerSendDataThread() when ever the task is added to this queue. - * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CAResult_t CAInitLEServerSenderQueue(); - -/** - * This function will initialize the Receiver queue for GattClient. This - * will initialize the queue to process the function - * CABLEClientSendDataThread() when ever the task is added to this queue. - * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CAResult_t CAInitLEClientSenderQueue(); - -/** - * This function will initialize the Receiver queue for LEAdapter. This will initialize - * the queue to process the function CABLEDataReceiverHandler() when ever the task - * is added to this queue. - * - * @return ::CA_STATUS_OK or Appropriate error code - * @retval ::CA_STATUS_OK Successful - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments - * @retval ::CA_STATUS_FAILED Operation failed - * - */ -CAResult_t CAInitLEReceiverQueue(); - -/** - * This function will create the Data required to send it in the queue. - * - * @param[in] remoteEndpoint Remote endpoint information of the server. - * @param[in] data Data to be transmitted from LE. - * @param[in] dataLength length of the Data being transmitted. - * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, const void *data, - uint32_t dataLength); - -/** - * Used to free the BLE information stored in the sender/receiver queues. - * @param[in] bleData Structure contains the information of a particular - * data segment. - */ -void CAFreeLEData(CALEData_t *bleData); - -/** - * This will be used to notify device status changes to the LE adapter layer. - * @param[in] adapter_state State of the adapter. - */ -typedef void (*CALEDeviceStateChangedCallback)(CAAdapterState_t adapter_state); - -/** - * This will be used to notify that network packet received from - * GATTClient to adapter layer. - * @param[in] remoteAddress Remote endpoint Address. - * @param[in] serviceUUID Service UUID. - * @param[in] data Data received. - * @param[in] dataLength Length of the data received. - * @param[in] sentLength Length of the data sent. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -typedef CAResult_t (*CABLEClientDataReceivedCallback)(const char *remoteAddress, - const char *serviceUUID, const void *data, - uint32_t dataLength, uint32_t *sentLength); - -/** - * This will be used to notify that network packet received from - * GATTServer to adapter layer. - * @param[in] remoteAddress Remote endpoint Address. - * @param[in] serviceUUID Service UUID. - * @param[in] data Data received. - * @param[in] dataLength Length of the data received. - * @param[in] sentLength Length of the data sent. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. - */ -typedef CAResult_t (*CABLEServerDataReceivedCallback)(const char *remoteAddress, - const char *serviceUUID, const void *data, - uint32_t dataLength, uint32_t *sentLength); #ifdef __cplusplus } /* extern "C" */ diff --git a/resource/csdk/connectivity/inc/caleinterface.h b/resource/csdk/connectivity/inc/caleinterface.h index b5fd208..589d146 100644 --- a/resource/csdk/connectivity/inc/caleinterface.h +++ b/resource/csdk/connectivity/inc/caleinterface.h @@ -19,8 +19,6 @@ ******************************************************************/ /** - * @file - * * This file provides APIs for BLE modules. */ @@ -30,7 +28,7 @@ #include #include "cacommon.h" -#include "caleadapter.h" +#include "cathreadpool.h" #ifdef __cplusplus extern "C" @@ -39,7 +37,9 @@ extern "C" /** * Provide info about different mode of data transfer. - * This enum is used to differentiate between unicast and multicast data transfer. + * + * This enum is used to differentiate between unicast and multicast + * data transfer. */ typedef enum { @@ -47,234 +47,295 @@ typedef enum LE_UNICAST /**< When this enum is selected, data will be updated to desired OIC Server. */ } CALETransferType_t; +/** + * This will be used to notify device status changes to the LE adapter layer. + * @param[in] adapter_state State of the adapter. + */ +typedef void (*CALEDeviceStateChangedCallback)(CAAdapterState_t adapter_state); /** - * Initialize the LE adapter layer. This will be invoked from the CA layer. + * Notify the adapter layer that a packet was received from the GATT + * peer. + * + * @param[in] remoteAddress Remote endpoint Address. + * @param[in] data Data received. + * @param[in] dataLength Length of the data received. + * @param[in] sentLength Length of the data sent. * * @return ::CA_STATUS_OK or Appropriate error code. * @retval ::CA_STATUS_OK Successful. * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. * @retval ::CA_STATUS_FAILED Operation failed. */ +typedef CAResult_t (*CABLEDataReceivedCallback)(const char *remoteAddress, + const void *data, + uint32_t dataLength, + uint32_t *sentLength); + +/** + * Initialize the LE adapter layer. This will be invoked from the CA + * layer. + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed + */ CAResult_t CAInitializeLEAdapter(); /** + * Start the LE adapter layer. + * + * This function will be invoked from the CA layer when the LE + * "network" is selected via @c CASelectNetwork(). It gives an + * opportunity for LE adapter implementations to perform operations + * before starting a GATT client or server. Most LE adapter + * implementations will simply implement this function as no-op. + * + * @return ::CA_STATUS_OK or Appropriate error code + */ +CAResult_t CAStartLEAdapter(); + +/** * Used to get the current state of the LE adapter. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_ADAPTER_NOT_ENABLED adapter not enabled. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_ADAPTER_NOT_ENABLED adapter not enabled + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAGetLEAdapterState(); /** - * Used to initialize the network monitor layer of the LE adapter. Mutex variables required - * to operate in this layer and other paramters can be initialized in this function. + * Initialize the network monitor layer of the LE adapter. Mutex + * variables required to operate in this layer and other parameters + * can be initialized in this function. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAInitializeLENetworkMonitor(); /** - * Used to terminate the network monitor layer of the LE adapter. The variables intialized - * in CAInitializeLEAdapterController() must be cleared in this function. + * Terminate the network monitor layer of the LE adapter. The + * variables initialized in @c CAInitializeLENetworkMonitor() must be + * cleared in this function. */ void CATerminateLENetworkMonitor(); /** - * This function is used to set the callback for the Device state changes in the adapter. + * Set the callback for the device state changes in the adapter. * - * @param[in] callback Callback to notify the Device state change to the CA Layer. + * @param[in] callback Callback to notify the Device state change to + * the CA Layer * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CASetLEAdapterStateChangedCb(CALEDeviceStateChangedCallback callback); /** - * Used to initilaze all the mutex variables required. - * to operate the LE network monitor layer. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * Initialize all the mutex variables required to operate the LE + * network monitor layer. + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAInitLENetworkMonitorMutexVariables(); /** - * Used to terminate all the mutex variables required - * to operate the LE network monitor layer. + * Used to terminate all the mutex variables required to operate the LE + * network monitor layer. */ void CATerminateLENetworkMonitorMutexVariables(); /** - * Provides the BD address of the local adapter. - * @param[out] local_address pointer to the location where bd address needs to be stored. + * Provides the MAC address of the local Bluetooth adapter. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @param[out] local_address Pointer to the location where bd address + * needs to be stored. + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAGetLEAddress(char **local_address); /** - * Used to start Gatt Server thread for service creation and advertise ble service. + * Start Gatt Server thread for service creation and advertise BLE + * service. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAStartLEGattServer(); /** - * Used to stop BLE Gatt Service. + * Stop BLE Gatt Service. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAStopLEGattServer(); /** - * Used to stop Gatt Server thread and remove service registration, stop advertising. + * Stop Gatt Server thread and remove service registration, stop + * advertising. */ void CATerminateLEGattServer(); /** - * Used to store upper layer callback locally - * which will be used to send the data to application. - * @param[in] callback Callback function to pass the data to CA layer. + * Used to store upper layer callback locally which will be used to + * send the data to application. + * + * @param[in] callback Callback function to pass the data to CA layer. */ -void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback); +void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback); /** - * Used to update characteristics(Read/Write) value - * that we want to send to particular client. + * Update characteristics(Read/Write) value that we want to send to + * particular client. * - * @param[in] address BD address of Gatt client. - * @param[in] charValue Data that we want to send to client(unicast). - * @param[in] charValueLen Length of the data. + * @param[in] address BD address of Gatt client + * @param[in] charValue Data that we want to send to client(unicast) + * @param[in] charValueLen Length of the data. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ -CAResult_t CAUpdateCharacteristicsToGattClient(const char* address, const char *charValue, - const uint32_t charValueLen); +CAResult_t CAUpdateCharacteristicsToGattClient(const char *address, + const char *charValue, + uint32_t charValueLen); /** - * Used to update characteristics(Read/Write) value that we want to multicast to all clients. + * Update characteristics(Read/Write) value that we want to multicast + * to all clients. * - * @param[in] charValue Data that we want to send to clients(multicast). - * @param[in] charValueLen Length of the data. + * @param[in] charValue Data that we want to send to clients(multicast) + * @param[in] charValueLen Length of the data. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAUpdateCharacteristicsToAllGattClients(const char *charValue, uint32_t charValueLen); /** - * Used to start CAStartBleGattClientThread for initializing Gatt Client. + * Start @c CAStartBleGattClientThread for initializing Gatt Client. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ CAResult_t CAStartLEGattClient(); /** - * Used to stop Gatt Client gracefully in turn it will - * call CATerminateBLEGattClient function. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * Stop Gatt client gracefully. In turn it will call the + * @c CATerminateBLEGattClient function. + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ void CAStopLEGattClient(); /** - * Used to unset all the callbacks and stop service discovery. + * Unset all the callbacks and stop service discovery */ void CATerminateLEGattClient(); /** - * API to read the data from characteristics and invoke notifyCallback. + * Read the data from characteristics and invoke notify callback. */ void CACheckLEData(); /** - * Sets the value of characteristic and update the value to GATTServer(unicast). + * Set the value of characteristic and update the value to + * GATTServer (unicast). * - * @param[in] remoteAddress The address of the remote device. - * @param[in] data The value of characteristic (byte array). - * @param[in] dataLen The length of value. - * @param[in] type Type of the transfer(::CALETransferType_t). - * @param[in] position The unique index of each ble server. Used for multicast feature. + * @param[in] remoteAddress The address of the remote device + * @param[in] data The value of characteristic (byte array) + * @param[in] dataLen The length of value + * @param[in] type Type of the transfer(::CALETransferType_t) + * @param[in] position The unique index of each ble server. Used + * for multicast feature. * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ -CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress, const char *data, - const uint32_t dataLen, CALETransferType_t type, - const int32_t position); +CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress, + const char *data, + uint32_t dataLen, + CALETransferType_t type, + int32_t position); /** - * Sets the value of characteristic and update the value to all registered. - * GATTServer -> Multicast. - * @param[in] data The value of characteristic (byte array). - * @param[in] dataLen The length of value. + * Set the value of characteristic and update the value to all + * registered GATTServer (multicast). * - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @param[in] data The value of characteristic (byte array) + * @param[in] dataLen The length of value + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ -CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data, uint32_t dataLen); +CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data, + uint32_t dataLen); /** - * Used to store upper layer callback locally which will be used - * to send the data to application. - * @param[in] callback Callback function to pass the data to CA layer. + * Store upper layer callback locally which will be used to send the + * data to application. + * + * @param[in] callback Callback function to pass the data to CA layer. */ -void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback); +void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback); /** - * Used to Set the gThreadPool handle which is required for spawning new thread. + * Set the server thread pool handle which is required for spawning + * new thread. * - * @param[in] handle Thread pool handle which is given by above layer - * for using thread creation task. - * @return ::CA_STATUS_OK or Appropriate error code. - * @retval ::CA_STATUS_OK Successful. - * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. - * @retval ::CA_STATUS_FAILED Operation failed. + * @param[in] handle Thread pool handle which is given by above layer + * for using thread creation task. + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed */ void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle); /** -* Used to Set the gThreadPool handle which is required for spawning new thread. -* @param[in] handle Thread pool handle which is given by above layer -* for using thread creation task. +* Set the client thread pool handle which is required for spawning new +* thread. +* +* @param[in] handle Thread pool handle which is given by above layer +* for using thread creation task. */ void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle); /** - * Used to unset the callback of adapter connection state change. + * Unset the callback of adapter connection state change. * * @return ::CA_STATUS_OK or Appropriate error code. * @retval ::CA_STATUS_OK Successful. @@ -284,25 +345,31 @@ void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle); CAResult_t CAUnSetLEAdapterStateChangedCb(); /** - * This will be used to notify errors in BLE adapter - * @param[in] remoteAddress Remote endpoint Address. - * @param[in] serviceUUID Service UUID. - * @param[in] data Data received. - * @param[in] dataLength Length of the data received. - * @param[in] result error code as per CAResult_t. + * This will be used to notify errors in BLE adapter. + * + * @param[in] remoteAddress Remote endpoint Address + * @param[in] data Data received + * @param[in] dataLength Length of the data received + * @param[in] result error code as per CAResult_t */ -typedef void (*CABLEErrorHandleCallback)(const char *remoteAddress, const void *data, - uint32_t dataLength, CAResult_t result); +typedef void (*CABLEErrorHandleCallback)(const char *remoteAddress, + const void *data, + uint32_t dataLength, + CAResult_t result); /** - * sets the error handle callback. - * @param[in] callback Callback function to update error to the adapter. + * Set the client error handler callback. + * + * @param[in] callback Callback function to update error to the + * adapter. */ void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback); /** - * sets the error handle callback. - * @param[in] callback Callback function to update error to the adapter. + * Set the server error handler callback. + * + * @param[in] callback Callback function to update error to the + * adapter. */ void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback); @@ -311,4 +378,3 @@ void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback); #endif #endif /* CA_LE_INTERFACE_H_ */ - diff --git a/resource/csdk/connectivity/src/bt_le_adapter/SConscript b/resource/csdk/connectivity/src/bt_le_adapter/SConscript index c5468a1..ddbfa21 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/SConscript +++ b/resource/csdk/connectivity/src/bt_le_adapter/SConscript @@ -11,11 +11,8 @@ print "Reading BLE adapter script for", target_os src_dir = os.path.join(os.curdir, 'bt_le_adapter') -# Source files to build common for all platforms. -common_files = None -common_files = [ os.path.join(src_dir, - 'caleadapter.c') ] - +# Source files common to all platforms. +common_files = [ os.path.join(src_dir, 'caleadapter.c') ] # Get list of target-specific source file base names, i.e. no parent # directories prepended to the path. @@ -40,4 +37,3 @@ target_files = [ os.path.join(src_dir, target_os, f) for f in target_files ] # The list of BLE adapter source files is a combination of both the # common and target-specific source file lists. env.AppendUnique(CA_SRC = common_files + target_files) - diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c b/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c index adaf0d5..f97d59b 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleclient.c @@ -78,7 +78,7 @@ static ca_mutex g_deviceListMutex = NULL; static ca_mutex g_gattObjectMutex = NULL; static ca_mutex g_deviceStateListMutex = NULL; -static CABLEClientDataReceivedCallback g_CABLEClientDataReceivedCallback = NULL; +static CABLEDataReceivedCallback g_CABLEClientDataReceivedCallback = NULL; //getting jvm void CALEClientJniInit() @@ -3127,8 +3127,8 @@ void CATerminateLEGattClient() } CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress, const char *data, - const uint32_t dataLen, CALETransferType_t type, - const int32_t position) + uint32_t dataLen, CALETransferType_t type, + int32_t position) { OIC_LOG(DEBUG, TAG, "call CALEClientSendUnicastMessage"); VERIFY_NON_NULL(data, TAG, "data is null"); @@ -3145,7 +3145,7 @@ CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data, uint32_t da return CALEClientSendMulticastMessage(data, dataLen); } -void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback) +void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback) { OIC_LOG(DEBUG, TAG, "IN"); @@ -3574,7 +3574,7 @@ Java_org_iotivity_ca_CaLeClientInterface_caLeGattCharacteristicChangedCallback( ca_mutex_lock(g_bleServerBDAddressMutex); uint32_t sentLength = 0; - g_CABLEClientDataReceivedCallback(address, OIC_GATT_SERVICE_UUID, receivedData, length, + g_CABLEClientDataReceivedCallback(address, receivedData, length, &sentLength); ca_mutex_unlock(g_bleServerBDAddressMutex); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/calenwmonitor.c b/resource/csdk/connectivity/src/bt_le_adapter/android/calenwmonitor.c index 28da4db..328cd3e 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/calenwmonitor.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/calenwmonitor.c @@ -83,6 +83,13 @@ CAResult_t CAInitializeLEAdapter() return CA_STATUS_OK; } +CAResult_t CAStartLEAdapter() +{ + // Nothing to do. + + return CA_STATUS_OK; +} + CAResult_t CAInitLENwkMonitorMutexVaraibles() { OIC_LOG(DEBUG, TAG, "IN"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleserver.c b/resource/csdk/connectivity/src/bt_le_adapter/android/caleserver.c index e9680c5..994f9b4 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleserver.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleserver.c @@ -52,7 +52,7 @@ static ca_thread_pool_t g_threadPoolHandle = NULL; static bool g_isStartServer = false; static bool g_isInitializedServer = false; -static CABLEServerDataReceivedCallback g_CABLEServerDataReceivedCallback = NULL; +static CABLEDataReceivedCallback g_CABLEServerDataReceivedCallback = NULL; static ca_mutex g_bleReqRespCbMutex = NULL; static ca_mutex g_bleClientBDAddressMutex = NULL; static ca_mutex g_connectedDeviceListMutex = NULL; @@ -2264,7 +2264,7 @@ Java_org_iotivity_ca_CaLeServerInterface_caLeGattServerCharacteristicWriteReques ca_mutex_lock(g_bleClientBDAddressMutex); uint32_t sentLength = 0; - g_CABLEServerDataReceivedCallback(address, OIC_GATT_SERVICE_UUID, requestData, length, + g_CABLEServerDataReceivedCallback(address, requestData, length, &sentLength); ca_mutex_unlock(g_bleClientBDAddressMutex); @@ -2374,7 +2374,7 @@ void CATerminateLEGattServer() OIC_LOG(DEBUG, TAG, "OUT"); } -void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback) +void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback) { OIC_LOG(DEBUG, TAG, "IN"); @@ -2390,8 +2390,9 @@ void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback) g_serverErrorCallback = callback; } -CAResult_t CAUpdateCharacteristicsToGattClient(const char* address, const char *charValue, - const uint32_t charValueLen) +CAResult_t CAUpdateCharacteristicsToGattClient(const char *address, + const char *charValue, + uint32_t charValueLen) { CAResult_t result = CA_SEND_FAILED; OIC_LOG(DEBUG, TAG, "IN"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h b/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h index b56bcd7..8969b3e 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h +++ b/resource/csdk/connectivity/src/bt_le_adapter/android/caleutils.h @@ -27,6 +27,7 @@ #include "cacommon.h" #include "cathreadpool.h" +#include "cagattservice.h" #include "uarraylist.h" #include "jni.h" @@ -36,9 +37,9 @@ extern "C" #endif /* Service UUID */ -static const char OIC_GATT_SERVICE_UUID[] = "ADE3D529-C784-4F63-A987-EB69F70EE816"; -static const char OIC_GATT_CHARACTERISTIC_REQUEST_UUID[] = "AD7B334F-4637-4B86-90B6-9D787F03D218"; -static const char OIC_GATT_CHARACTERISTIC_RESPONSE_UUID[] = "E9241982-4580-42C4-8831-95048216B256"; +static const char OIC_GATT_SERVICE_UUID[] = CA_GATT_SERVICE_UUID; +static const char OIC_GATT_CHARACTERISTIC_REQUEST_UUID[] = CA_GATT_REQUEST_CHRC_UUID; +static const char OIC_GATT_CHARACTERISTIC_RESPONSE_UUID[] = CA_GATT_RESPONSE_CHRC_UUID; static const char OIC_GATT_CHARACTERISTIC_CONFIG_UUID[] = "00002902-0000-1000-8000-00805f9b34fb"; static const uint32_t GATT_SUCCESS = 0; diff --git a/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableclient.cpp b/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableclient.cpp index 6ea6967..ad99a59 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableclient.cpp +++ b/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableclient.cpp @@ -30,7 +30,7 @@ void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle) OIC_LOG(DEBUG, TAG, "IN"); OIC_LOG(DEBUG, TAG, "OUT"); } -void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback) +void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback) { OIC_LOG(DEBUG, TAG, "IN"); OIC_LOG(DEBUG, TAG, "OUT"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/arduino/cablenwmonitor.cpp b/resource/csdk/connectivity/src/bt_le_adapter/arduino/cablenwmonitor.cpp index 2740a70..0466261 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/arduino/cablenwmonitor.cpp +++ b/resource/csdk/connectivity/src/bt_le_adapter/arduino/cablenwmonitor.cpp @@ -70,6 +70,13 @@ CAResult_t CAInitializeLEAdapter() return CA_STATUS_OK; } +CAResult_t CAStartLEAdapter() +{ + // Nothing to do. + + return CA_STATUS_OK; +} + CAResult_t CAGetLEAdapterState() { OIC_LOG(DEBUG, TAG, "IN"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableserver.cpp b/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableserver.cpp index 74f9b14..09b4e20 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableserver.cpp +++ b/resource/csdk/connectivity/src/bt_le_adapter/arduino/cableserver.cpp @@ -41,7 +41,7 @@ * @brief Maintains the callback to be notified on receival of network packets from other * BLE devices */ -static CABLEServerDataReceivedCallback g_bleServerDataReceivedCallback = NULL; +static CABLEDataReceivedCallback g_bleServerDataReceivedCallback = NULL; /** * @def MAX_EVENT_COUNT @@ -113,7 +113,7 @@ void CACheckLEDataInternal() OIC_LOG_V(DEBUG, TAG, "recv dataLen=%d", g_receivedDataLen); uint32_t sentLength = 0; // g_coapBuffer getting freed by CAMesssageHandler - g_bleServerDataReceivedCallback("", "", g_coapBuffer, + g_bleServerDataReceivedCallback("", g_coapBuffer, g_receivedDataLen, &sentLength); } @@ -188,7 +188,7 @@ CAResult_t CAUpdateCharacteristicsToAllGattClients(const char *char_value, return CA_STATUS_OK; } -void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback) +void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback) { OIC_LOG(DEBUG, TAG, "IN"); g_bleServerDataReceivedCallback = callback; diff --git a/resource/csdk/connectivity/src/bt_le_adapter/caleadapter.c b/resource/csdk/connectivity/src/bt_le_adapter/caleadapter.c index 20cd845..6586b92 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/caleadapter.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/caleadapter.c @@ -39,16 +39,45 @@ */ #define CALEADAPTER_TAG "LAD" + +/** + * Stores the information of the Data to be sent from the queues. + * + * This structure will be pushed to the sender/receiver queue for + * processing. + */ +typedef struct +{ + CAEndpoint_t *remoteEndpoint; /**< Remote endpoint contains the + information of remote device. */ + void *data; /**< Data to be transmitted over LE transport. */ + uint32_t dataLen; /**< Length of the data being transmitted. */ +} CALEData_t; + +/** + * Stores information of all the senders. + * + * This structure will be used to track and defragment all incoming + * data packet. + */ +typedef struct +{ + uint32_t recvDataLen; + uint32_t totalDataLen; + char *defragData; + CAEndpoint_t *remoteEndpoint; + } CABLESenderInfo_t; + /** * Callback to provide the status of the network change to CA layer. */ static CANetworkChangeCallback g_networkCallback = NULL; /** - * bleAddress of the local adapter. Value will be initialized to zero, and will - * be updated later. + * bleAddress of the local adapter. Value will be initialized to zero, + * and will be updated later. */ -static char g_localBLEAddress[18] = {0}; +static char g_localBLEAddress[18] = { 0 }; /** * Variable to differentiate btw GattServer and GattClient. @@ -56,23 +85,25 @@ static char g_localBLEAddress[18] = {0}; static bool g_isServer = false; /** - * Mutex to synchronize the task to be executed on the GattServer function - * calls. + * Mutex to synchronize the task to be executed on the GattServer + * function calls. */ static ca_mutex g_bleIsServerMutex = NULL; /** - * Mutex to synchronize the callback to be called for the network changes. + * Mutex to synchronize the callback to be called for the network + * changes. */ static ca_mutex g_bleNetworkCbMutex = NULL; /** - * Mutex to synchronize the updation of the local LE address of the adapter. + * Mutex to synchronize the updates of the local LE address of the + * adapter. */ static ca_mutex g_bleLocalAddressMutex = NULL; /** - * reference to threadpool. + * Reference to thread pool. */ static ca_thread_pool_t g_bleAdapterThreadPool = NULL; @@ -98,12 +129,13 @@ static ca_mutex g_bleReceiveDataMutex = NULL; static ca_mutex g_bleServerSendDataMutex = NULL; /** - * Mutex to synchronize the callback to be called for the adapterReqResponse. + * Mutex to synchronize the callback to be called for the + * adapterReqResponse. */ static ca_mutex g_bleAdapterReqRespCbMutex = NULL; /** - * Callback to be called when network packet recieved from either + * Callback to be called when network packet received from either * GattServer or GattClient. */ static CANetworkPacketReceivedCallback g_networkPacketReceivedCallback = NULL; @@ -119,8 +151,9 @@ static CAErrorHandleCallback g_errorHandler = NULL; static CAAdapterState_t g_bleAdapterState = CA_ADAPTER_DISABLED; /** - * status of BLE Server Status. - * This ENUM provides information of LE Adapter Server status. + * BLE Server Status. + * + * This enumeration provides information of LE Adapter Server status. */ typedef enum { @@ -130,65 +163,68 @@ typedef enum } CALeServerStatus; /** - * structure to maintain the status of the server. + * Structure to maintain the status of the server. */ static CALeServerStatus gLeServerStatus = CA_SERVER_NOTSTARTED; /** -* This function is used to register network change notification callback. -* -* @param[in] netCallback CANetworkChangeCallback callback which will be -* set for the change in nwk. -* -* @return 0 on success otherwise a positive error value. -* @retval CA_STATUS_OK Successful. -* @retval CA_STATUS_INVALID_PARAM Invalid input argumets. -* @retval CA_STATUS_FAILED Operation failed. -* -*/ -CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback); + * Register network change notification callback. + * + * @param[in] netCallback CANetworkChangeCallback callback which will + * be set for the change in network. + * + * @return 0 on success otherwise a positive error value. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + * + */ +static CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback); /** -* Used to Set the gThreadPool handle which is required for spawning new thread. -* -* @param[in] handle - Thread pool handle which is given by above layer for -* using thread creation task. -* -*/ -void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle); + * Set the thread pool handle which is required for spawning new + * thread. + * + * @param[in] handle Thread pool handle which is given by above layer + * for using thread creation task. + * + */ +static void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle); /** -* This function is used to call the callback to the upper layer when the -* device state gets changed. -* -* @param[in] adapter_state New state of the adapter to be notified to the -* upper layer. -* -*/ -void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state); + * Call the callback to the upper layer when the device state gets + * changed. + * + * @param[in] adapter_state New state of the adapter to be notified to + * the upper layer. + */ +static void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state); /** -* Used to initialize all required mutex variable for LE Adapter implementation. -* -* @return 0 on success otherwise a positive error value. -* @retval CA_STATUS_OK Successful. -* @retval CA_STATUS_INVALID_PARAM Invalid input argumets. -* @retval CA_STATUS_FAILED Operation failed. -* -*/ -CAResult_t CAInitLEAdapterMutex(); + * Used to initialize all required mutex variable for LE Adapter + * implementation. + * + * @return 0 on success otherwise a positive error value. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + * + */ +static CAResult_t CAInitLEAdapterMutex(); /** -* Used to terminate all required mutex variable for LE adapter implementation. -* -*/ -void CATerminateLEAdapterMutex(); + * Terminate all required mutex variables for LE adapter + * implementation. + */ +static void CATerminateLEAdapterMutex(); /** -* prepares and notify error through error callback. -* -*/ -static void CALEErrorHandler(const char *remoteAddress, const void *data, uint32_t dataLen, + * Prepares and notify error through error callback. + * + */ +static void CALEErrorHandler(const char *remoteAddress, + const void *data, + uint32_t dataLen, CAResult_t result); #ifndef SINGLE_THREAD @@ -198,7 +234,7 @@ static void CALEErrorHandler(const char *remoteAddress, const void *data, uint32 static bool g_dataReceiverHandlerState = false; /** - * Sender informations to be stored here + * Sender information. */ static u_arraylist_t *g_senderInfo = NULL; @@ -218,33 +254,343 @@ static CAQueueingThread_t *g_bleReceiverQueue = NULL; static CAQueueingThread_t *g_bleServerSendQueueHandle = NULL; /** -* Used to free data. -* -*/ -static void CALEDataDestroyer(void *data, uint32_t size); + * Starting LE connectivity adapters. + * + * As its peer to peer it does not require to start any servers. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +static CAResult_t CAStartLE(); -void CAInitLEQueues() -{ - OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); +/** + * Start listening server for receiving multicast search requests. + * + * Transport Specific Behavior: + * LE Starts GATT Server with prefixed UUID and Characteristics + * per OIC Specification. + * @return ::CA_STATUS_OK or Appropriate error code. + */ +static CAResult_t CAStartLEListeningServer(); - CAResult_t result = CAInitLEServerQueues(); - if (CA_STATUS_OK != result) - { - OIC_LOG(ERROR, CALEADAPTER_TAG, "CAInitBleServerQueues failed"); - return; - } +/** + * Sarting discovery of servers for receiving multicast + * advertisements. + * + * Transport Specific Behavior: + * LE Starts GATT Server with prefixed UUID and Characteristics + * per OIC Specification. + * + * @return ::CA_STATUS_OK or Appropriate error code + */ +static CAResult_t CAStartLEDiscoveryServer(); - result = CAInitLEClientQueues(); - if (CA_STATUS_OK != result) - { - OIC_LOG(ERROR, CALEADAPTER_TAG, "CAInitBleClientQueues failed"); - return; - } +/** + * Send data to the endpoint using the adapter connectivity. + * + * @param[in] endpoint Remote Endpoint information (like MAC address, + * reference URI and connectivity type) to which + * the unicast data has to be sent. + * @param[in] data Data which required to be sent. + * @param[in] dataLen Size of data to be sent. + * + * @note dataLen must be > 0. + * + * @return The number of bytes sent on the network, or -1 on error. + */ +static int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, + const void *data, + uint32_t dataLen); - OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); -} +/** + * Send multicast data to the endpoint using the LE connectivity. + * + * @param[in] endpoint Remote Endpoint information to which the + * multicast data has to be sent. + * @param[in] data Data which required to be sent. + * @param[in] dataLen Size of data to be sent. + * + * @note dataLen must be > 0. + * + * @return The number of bytes sent on the network, or -1 on error. + */ +static int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, + const void *data, + uint32_t dataLen); + +/** + * Get LE Connectivity network information. + * + * @param[out] info Local connectivity information structures. + * @param[out] size Number of local connectivity structures. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +static CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, + uint32_t *size); + +/** + * Read Synchronous API callback. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +static CAResult_t CAReadLEData(); + +/** + * Stopping the adapters and close socket connections. + * + * LE Stops all GATT servers and GATT Clients. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +static CAResult_t CAStopLE(); + +/** + * Terminate the LE connectivity adapter. + * + * Configuration information will be deleted from further use. + */ +static void CATerminateLE(); + +/** + * This function will receive the data from the GattServer and add the + * data to the Server receiver queue. + * + * @param[in] remoteAddress Remote address of the device from where + * data is received. + * @param[in] data Actual data recevied from the remote + * device. + * @param[in] dataLength Length of the data received from the + * remote device. + * @param[in] sentLength Length of the data sent from the remote + * device. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + * + */ +static CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, + const void *data, + uint32_t dataLength, + uint32_t *sentLength); + +/** + * This function will receive the data from the GattClient and add the + * data into the Client receiver queue. + * + * @param[in] remoteAddress Remote address of the device from where + * data is received. + * @param[in] data Actual data recevied from the remote + * device. + * @param[in] dataLength Length of the data received from the + * remote device. + * @param[in] sentLength Length of the data sent from the remote + * device. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, + const void *data, + uint32_t dataLength, + uint32_t *sentLength); + +/** + * Set the NetworkPacket received callback to CA layer from adapter + * layer. + * + * @param[in] callback Callback handle sent from the upper layer. + */ +static void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback); -CAResult_t CAInitLEServerQueues() +/** + * Push the data from CA layer to the Sender processor queue. + * + * @param[in] remoteEndpoint Remote endpoint information of the + * server. + * @param[in] data Data to be transmitted from LE. + * @param[in] dataLen Length of the Data being transmitted. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint, + const void *data, + uint32_t dataLen); + +/** + * Push the data from CA layer to the Sender processor queue. + * + * @param[in] remoteEndpoint Remote endpoint information of the + * server. + * @param[in] data Data to be transmitted from LE. + * @param[in] dataLen Length of the Data being transmitted. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint, + const void *data, + uint32_t dataLen); + +/** + * This function will be associated with the sender queue for + * GattServer. + * + * This function will fragment the data to the MTU of the transport + * and send the data in fragments to the adapters. The function will + * be blocked until all data is sent out from the adapter. + * + * @param[in] threadData Data pushed to the queue which contains the + * info about RemoteEndpoint and Data. + */ +static void CALEServerSendDataThread(void *threadData); + +/** + * This function will be associated with the sender queue for + * GattClient. + * + * This function will fragment the data to the MTU of the transport + * and send the data in fragments to the adapters. The function will + * be blocked until all data is sent out from the adapter. + * + * @param[in] threadData Data pushed to the queue which contains the + * info about RemoteEndpoint and Data. + */ +static void CALEClientSendDataThread(void *threadData); + +/** + * This function will be associated with the receiver queue. + * + * This function will defragment the received data from each sender + * respectively and will send it up to CA layer. Respective sender's + * header will provide the length of the data sent. + * + * @param[in] threadData Data pushed to the queue which contains the + * info about RemoteEndpoint and Data. + */ +static void CALEDataReceiverHandler(void *threadData); + +/** + * This function will stop all queues created for GattServer and + * GattClient. All four queues will be be stopped with this function + * invocations. + */ +static void CAStopLEQueues(); + +/** + * This function will terminate all queues created for GattServer and + * GattClient. All four queues will be be terminated with this + * function invocations. + */ +static void CATerminateLEQueues(); + +/** + * This function will initalize the Receiver and Sender queues for + * GattServer. This function will in turn call the functions + * CAInitBleServerReceiverQueue() and CAInitBleServerSenderQueue() to + * initialize the queues. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CAResult_t CAInitLEServerQueues(); + +/** + * This function will initalize the Receiver and Sender queues for + * GattClient. This function will inturn call the functions + * CAInitBleClientReceiverQueue() and CAInitBleClientSenderQueue() to + * initialize the queues. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + * + */ +static CAResult_t CAInitLEClientQueues(); + +/** + * This function will initalize the Receiver queue for + * GattServer. This will initialize the queue to process the function + * CABLEServerSendDataThread() when ever the task is added to this + * queue. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CAResult_t CAInitLEServerSenderQueue(); + +/** + * This function will initalize the Receiver queue for + * GattClient. This will initialize the queue to process the function + * CABLEClientSendDataThread() when ever the task is added to this + * queue. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CAResult_t CAInitLEClientSenderQueue(); + +/** + * This function will initialize the Receiver queue for + * LEAdapter. This will initialize the queue to process the function + * CABLEDataReceiverHandler() when ever the task is added to this + * queue. + * + * @return ::CA_STATUS_OK or Appropriate error code + * @retval ::CA_STATUS_OK Successful + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments + * @retval ::CA_STATUS_FAILED Operation failed + * + */ +static CAResult_t CAInitLEReceiverQueue(); + +/** + * This function will create the Data required to send it in the + * queue. + * + * @param[in] remoteEndpoint Remote endpoint information of the + * server. + * @param[in] data Data to be transmitted from LE. + * @param[in] dataLength Length of the Data being transmitted. + * + * @return ::CA_STATUS_OK or Appropriate error code. + * @retval ::CA_STATUS_OK Successful. + * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments. + * @retval ::CA_STATUS_FAILED Operation failed. + */ +static CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, + const void *data, + uint32_t dataLength); + +/** + * Used to free the BLE information stored in the sender/receiver + * queues. + * + * @param[in] bleData Information for a particular data segment. + */ +static void CAFreeLEData(CALEData_t *bleData); + +/** + * Free data. + */ +static void CALEDataDestroyer(void *data, uint32_t size); + +static CAResult_t CAInitLEServerQueues() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -274,7 +620,7 @@ CAResult_t CAInitLEServerQueues() return CA_STATUS_OK; } -CAResult_t CAInitLEClientQueues() +static CAResult_t CAInitLEClientQueues() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -304,7 +650,7 @@ CAResult_t CAInitLEClientQueues() return CA_STATUS_OK; } -CAResult_t CAInitLEReceiverQueue() +static CAResult_t CAInitLEReceiverQueue() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); // Check if the message queue is already initialized @@ -354,7 +700,7 @@ CAResult_t CAInitLEReceiverQueue() return CA_STATUS_OK; } -CAResult_t CAInitLEServerSenderQueue() +static CAResult_t CAInitLEServerSenderQueue() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); // Check if the message queue is already initialized @@ -394,7 +740,7 @@ CAResult_t CAInitLEServerSenderQueue() return CA_STATUS_OK; } -void CALEClearSenderInfo() +static void CALEClearSenderInfo() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -416,7 +762,7 @@ void CALEClearSenderInfo() OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -CAResult_t CAInitLEClientSenderQueue() +static CAResult_t CAInitLEClientSenderQueue() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -456,7 +802,7 @@ CAResult_t CAInitLEClientSenderQueue() return CA_STATUS_OK; } -void CAStopLEQueues() +static void CAStopLEQueues() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -484,7 +830,7 @@ void CAStopLEQueues() OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -void CATerminateLEQueues() +static void CATerminateLEQueues() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -505,8 +851,9 @@ void CATerminateLEQueues() OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -CAResult_t CALEGetSenderInfo(char *leAddress, CABLESenderInfo_t **senderInfo, - uint32_t *senderIndex) +static CAResult_t CALEGetSenderInfo(char *leAddress, + CABLESenderInfo_t **senderInfo, + uint32_t *senderIndex) { VERIFY_NON_NULL_RET(leAddress, CALEADAPTER_TAG, "Ble-Address in-param NULL", CA_STATUS_FAILED); VERIFY_NON_NULL_RET(senderIndex, CALEADAPTER_TAG, "Index in-param NULL", CA_STATUS_FAILED); @@ -535,7 +882,7 @@ CAResult_t CALEGetSenderInfo(char *leAddress, CABLESenderInfo_t **senderInfo, return CA_STATUS_FAILED; } -void CALEDataReceiverHandler(void *threadData) +static void CALEDataReceiverHandler(void *threadData) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -686,7 +1033,7 @@ void CALEDataReceiverHandler(void *threadData) OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -void CALEServerSendDataThread(void *threadData) +static void CALEServerSendDataThread(void *threadData) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -848,7 +1195,7 @@ void CALEServerSendDataThread(void *threadData) OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -void CALEClientSendDataThread(void *threadData) +static void CALEClientSendDataThread(void *threadData) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1018,8 +1365,9 @@ void CALEClientSendDataThread(void *threadData) OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT - CABLEClientSendDataThread"); } -CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, const void *data, - uint32_t dataLength) +static CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, + const void *data, + uint32_t dataLength) { CALEData_t *bleData = (CALEData_t *) OICMalloc(sizeof(CALEData_t)); if (!bleData) @@ -1042,7 +1390,7 @@ CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, const void *data, return bleData; } -void CAFreeLEData(CALEData_t *bleData) +static void CAFreeLEData(CALEData_t *bleData) { VERIFY_NON_NULL_VOID(bleData, CALEADAPTER_TAG, "Param bleData is NULL"); @@ -1051,7 +1399,7 @@ void CAFreeLEData(CALEData_t *bleData) OICFree(bleData); } -void CALEDataDestroyer(void *data, uint32_t size) +static void CALEDataDestroyer(void *data, uint32_t size) { CALEData_t *ledata = (CALEData_t *) data; @@ -1059,7 +1407,7 @@ void CALEDataDestroyer(void *data, uint32_t size) } #endif -CAResult_t CAInitLEAdapterMutex() +static CAResult_t CAInitLEAdapterMutex() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1153,7 +1501,7 @@ CAResult_t CAInitLEAdapterMutex() return CA_STATUS_OK; } -void CATerminateLEAdapterMutex() +static void CATerminateLEAdapterMutex() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1187,7 +1535,8 @@ void CATerminateLEAdapterMutex() CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback, CANetworkPacketReceivedCallback reqRespCallback, CANetworkChangeCallback netCallback, - CAErrorHandleCallback errorCallback, ca_thread_pool_t handle) + CAErrorHandleCallback errorCallback, + ca_thread_pool_t handle) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1225,16 +1574,19 @@ CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback, g_errorHandler = errorCallback; - CAConnectivityHandler_t connHandler; - connHandler.startAdapter = CAStartLE; - connHandler.stopAdapter = CAStopLE; - connHandler.startListenServer = CAStartLEListeningServer; - connHandler.startDiscoveryServer = CAStartLEDiscoveryServer; - connHandler.sendData = CASendLEUnicastData; - connHandler.sendDataToAll = CASendLEMulticastData; - connHandler.GetnetInfo = CAGetLEInterfaceInformation; - connHandler.readData = CAReadLEData; - connHandler.terminate = CATerminateLE; + static const CAConnectivityHandler_t connHandler = + { + .startAdapter = CAStartLE, + .stopAdapter = CAStopLE, + .startListenServer = CAStartLEListeningServer, + .startDiscoveryServer = CAStartLEDiscoveryServer, + .sendData = CASendLEUnicastData, + .sendDataToAll = CASendLEMulticastData, + .GetnetInfo = CAGetLEInterfaceInformation, + .readData = CAReadLEData, + .terminate = CATerminateLE + }; + registerCallback(connHandler, CA_ADAPTER_GATT_BTLE); OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); @@ -1242,15 +1594,14 @@ CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback, return CA_STATUS_OK; } -CAResult_t CAStartLE() +static CAResult_t CAStartLE() { - OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); - OIC_LOG(DEBUG, CALEADAPTER_TAG, "CAStartLE, not implemented"); - OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); - return CA_STATUS_OK; + OIC_LOG(DEBUG, CALEADAPTER_TAG, __func__); + + return CAStartLEAdapter(); } -CAResult_t CAStopLE() +static CAResult_t CAStopLE() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); #ifndef SINGLE_THREAD @@ -1273,7 +1624,7 @@ CAResult_t CAStopLE() return CA_STATUS_OK; } -void CATerminateLE() +static void CATerminateLE() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1302,7 +1653,7 @@ void CATerminateLE() OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -CAResult_t CAStartLEListeningServer() +static CAResult_t CAStartLEListeningServer() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); CAResult_t result = CA_STATUS_OK; @@ -1339,7 +1690,7 @@ CAResult_t CAStartLEListeningServer() return CA_STATUS_OK; } -CAResult_t CAStartLEDiscoveryServer() +static CAResult_t CAStartLEDiscoveryServer() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); CAResult_t result = CA_STATUS_OK; @@ -1375,24 +1726,7 @@ CAResult_t CAStartLEDiscoveryServer() return CA_STATUS_OK; } -CAResult_t CAStartLENotifyServer() -{ - OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); - - OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); - return CA_STATUS_OK; -} - -uint32_t CASendLENotification(const CAEndpoint_t *endpoint, const void *data, - uint32_t dataLen) -{ - OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); - - OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); - return 0; -} - -CAResult_t CAReadLEData() +static CAResult_t CAReadLEData() { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); #ifdef SINGLE_THREAD @@ -1402,7 +1736,9 @@ CAResult_t CAReadLEData() return CA_STATUS_OK; } -int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen) +static int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, + const void *data, + uint32_t dataLen) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1447,7 +1783,9 @@ int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data, uint return dataLen; } -int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen) +static int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, + const void *data, + uint32_t dataLen) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1498,7 +1836,7 @@ int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, ui return dataLen; } -CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size) +static CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1552,7 +1890,7 @@ CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size) return CA_STATUS_OK; } -CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback) +static CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1581,7 +1919,7 @@ CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback) return res; } -void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state) +static void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1620,9 +1958,9 @@ void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state) OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint, - const void *data, - uint32_t dataLen) +static CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint, + const void *data, + uint32_t dataLen) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1655,10 +1993,9 @@ CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint, return CA_STATUS_OK; } - -CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint, - const void *data, - uint32_t dataLen) +static CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint, + const void *data, + uint32_t dataLen) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1743,14 +2080,14 @@ CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint, return CA_STATUS_OK; } -CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, const char *serviceUUID, - const void *data, uint32_t dataLength, - uint32_t *sentLength) +static CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, + const void *data, + uint32_t dataLength, + uint32_t *sentLength) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); //Input validation - VERIFY_NON_NULL(serviceUUID, CALEADAPTER_TAG, "service UUID is null"); VERIFY_NON_NULL(data, CALEADAPTER_TAG, "Data is null"); VERIFY_NON_NULL(sentLength, CALEADAPTER_TAG, "Sent data length holder is null"); @@ -1787,7 +2124,7 @@ CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, const char * } CAFreeEndpoint(remoteEndpoint); - // Add message to send queue + // Add message to receiver queue CAQueueingThreadAddData(g_bleReceiverQueue, bleData, sizeof(CALEData_t)); *sentLength = dataLength; @@ -1796,14 +2133,14 @@ CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, const char * return CA_STATUS_OK; } -CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, const char *serviceUUID, - const void *data, uint32_t dataLength, - uint32_t *sentLength) +static CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, + const void *data, + uint32_t dataLength, + uint32_t *sentLength) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); //Input validation - VERIFY_NON_NULL(serviceUUID, CALEADAPTER_TAG, "service UUID is null"); VERIFY_NON_NULL(data, CALEADAPTER_TAG, "Data is null"); VERIFY_NON_NULL(sentLength, CALEADAPTER_TAG, "Sent data length holder is null"); #ifndef SINGLE_THREAD @@ -1832,7 +2169,7 @@ CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, const char * } CAFreeEndpoint(remoteEndpoint); - // Add message to send queue + // Add message to receiver queue CAQueueingThreadAddData(g_bleReceiverQueue, bleData, sizeof(CALEData_t)); *sentLength = dataLength; @@ -1841,18 +2178,18 @@ CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, const char * return CA_STATUS_OK; } -void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle) +static void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); - ca_mutex_unlock(g_bleAdapterThreadPoolMutex); + ca_mutex_lock(g_bleAdapterThreadPoolMutex); g_bleAdapterThreadPool = handle; ca_mutex_unlock(g_bleAdapterThreadPoolMutex); OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback) +static void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN"); @@ -1865,14 +2202,18 @@ void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback) OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT"); } -void CALEErrorHandler(const char *remoteAddress, const void *data, uint32_t dataLen, - CAResult_t result) +static void CALEErrorHandler(const char *remoteAddress, + const void *data, + uint32_t dataLen, + CAResult_t result) { OIC_LOG(DEBUG, CALEADAPTER_TAG, "CALEErrorHandler IN"); VERIFY_NON_NULL_VOID(data, CALEADAPTER_TAG, "Data is null"); - CAEndpoint_t *rep = CACreateEndpointObject(CA_DEFAULT_FLAGS, CA_ADAPTER_GATT_BTLE, - remoteAddress, 0); + CAEndpoint_t *rep = CACreateEndpointObject(CA_DEFAULT_FLAGS, + CA_ADAPTER_GATT_BTLE, + remoteAddress, + 0); //if required, will be used to build remote end point g_errorHandler(rep, data, dataLen, result); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/README b/resource/csdk/connectivity/src/bt_le_adapter/linux/README new file mode 100644 index 0000000..0678fa2 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/README @@ -0,0 +1,57 @@ + OIC GATT Transport Service for Linux + ==================================== + +This is an IoTivity BLE adapter implementation of the OIC GATT +Transport specification for Linux. It relies on the GATT and LE +advertisement functionality found in BlueZ 5.31 or better. As of +BlueZ 5.31, the LE advertisement APIs are still considered +experimental, meaning specific steps must be taken to enable them for +that version of BlueZ, as described below. + +LE advertisement support requires Linux kernel 4.1 better. If a LE +peripheral won't be started, Linux kernel 3.19 is sufficient for LE +central-only use cases. + +Enable LE Advertisement Support in BlueZ 5.31 +--------------------------------------------- +1. Backport the advertising patch that was created after BlueZ 5.31 +was released. If you've cloned the BlueZ git repository this can be +achieved by cherry picking the patch as follows: + + git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git + cd bluez + git checkout 5.31 + git cherry-pick c73c1002331b2c0de0eaf555b5f1d05149f9a3b0 + +Otherwise apply to BlueZ 5.31 the patch file found in the IoTivity +`extlibs/bluez' directory: + + 0001-core-advertising-Fix-using-wrong-instance-id.patch + +2. Enable experimental APIs when building BlueZ 5.31, e.g.: + + ./configure --enable-experimental ... + +3. Enable experimental APIs at run-time by starting bluetoothd with + the "--experimental" command line flag. If you've installed BlueZ + on a platform with Systemd support, this can be done by appending + "--experimental" to the [Service] ExecStart option in the + bluetooth.service unit file (e.g. + /lib/systemd/system/bluetooth.service). If you're going to build + and install BlueZ from source often, you may want to patch the + `bluetooth.service.in' in your bluez source directory instead so + that you won't have to modify the unit file after installation: + +diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in +index 35e9457..368df4c 100644 +--- a/src/bluetooth.service.in ++++ b/src/bluetooth.service.in +@@ -5,7 +5,7 @@ Documentation=man:bluetoothd(8) + [Service] + Type=dbus + BusName=org.bluez +-ExecStart=@libexecdir@/bluetoothd ++ExecStart=@libexecdir@/bluetoothd --experimental + NotifyAccess=main + #WatchdogSec=10 + #Restart=on-failure diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/SConscript b/resource/csdk/connectivity/src/bt_le_adapter/linux/SConscript index 825d145..64f3a16 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/linux/SConscript +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/SConscript @@ -1,9 +1,148 @@ +# ------------------------------------------------------------------------ +# Copyright 2015 Intel Corporation +# +# 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. +# ------------------------------------------------------------------------ + ########################################## # Build BLE adapter for Linux ########################################## Import('env') -src_files = [ 'caleadapter.c'] +import os.path + +# Make sure the and headers +# necessary for use of the kernel Bluetooth management API are +# available. Only those headers are required. There is no need to +# build BlueZ in order to build IoTivity's BLE transport for Linux. +env.SConscript('#extlibs/bluez/SConscript') + +# Top-level build (variant) directory. +root_build_dir = env['BUILD_DIR'] + +# Build (variant) directory corresponding to this source directory. +this_build_dir = os.path.join(root_build_dir, + os.path.dirname(File(SConscript).srcnode().path)) + +# The Linux BLE adapter implementation uses GDBus to make D-Bus based +# method calls to BlueZ. Pull in the necessary dependencies. +env.ParseConfig("pkg-config gio-unix-2.0 --cflags --libs") + +# Set up commands to generate GDBus code from the D-Bus introspection +# XML. +freedesktop_prefix = 'org.freedesktop.DBus.' +bluez_prefix = 'org.bluez.' + +dbus_introspection_xml = { + 'object_manager' : freedesktop_prefix, + 'bluez' : bluez_prefix, +} + +# The source files to be compiled as part of the connectivity +# abstraction library. +glue_files = [] + +for file, prefix in dbus_introspection_xml.items(): + source_xml = file + '.xml' + glue = file + '-glue' + glue_source = glue + '.c' + glue_header = glue + '.h' + targets = [ glue_source, glue_header ] + + # Include the glue header in the list as well to make sure it is + # generated before other source files that depend on it are + # compiled. + glue_files += targets + + # Generate GDBus skeletons in the variant (build) directory. + env.Command(targets, + source_xml, + 'cd %s ' + '&& gdbus-codegen --generate-c-code %s --interface-prefix %s %s ' + '&& cd -' + % (this_build_dir, + glue, + prefix, + os.path.join(env['SRC_DIR'], '$SOURCE'))) + + # Mark generated file for cleaning when running "scons -c". + # + # @todo Verify that the generated *-glue.[ch] files are removed on + # running "scons -c" once that is working in the master + # branch again. + for target in targets: + generated_target = os.path.join(this_build_dir, target) + env.Clean(target, generated_target) + +# The generated "glue" headers are found in the build directory +# corresponding to this source directory. +env.AppendUnique(CPPPATH = this_build_dir) + +# The Linux BLE transport exports its GATT and LE advertisement +# related D-Bus interfaces to the D-Bus system bus so that they may be +# accessed by BlueZ. Set the bus names here, i.e. in one place, to +# avoid potential mismatches, and generate the D-Bus policy +# configuration file and related C preprocessor symbol definitions. +service_name = '\"org.iotivity.gatt.service\"' + +dbus_policy_in = 'org.iotivity.gatt.service.conf.in' + +conf_dict = {} +subst_env = env.Clone(tools = [ 'default', 'textfile' ], + SUBST_DICT = conf_dict) + +conf_dict = { '@service_name@' : service_name } + +subst_env.Substfile(dbus_policy_in, SUBST_DICT = conf_dict) + +# The resulting D-Bus policy file should go in to the appropriate +# D-Bus configuration directory, such as /etc/dbus-1/system.d/. + +dbus_policy = os.path.splitext(dbus_policy_in)[0] # Drop '.in' extension. +generated_dbus_policy = os.path.join(this_build_dir, dbus_policy) +env.Clean(dbus_policy, generated_dbus_policy) + +# Define the D-Bus bus name as a preprocessor symbol. Note the +# multiple quote levels to ensure that the double quotes surrounding +# the string are included as part of the preprocess symbol. +# +# Also add a minimum required version of GLib 2.32, which is what the +# older GNU/Linux distributions supported by IoTivity shipped with. +env.AppendUnique( + CPPDEFINES = [ + ('CA_DBUS_GATT_SERVICE_NAME', "'%s'" % service_name), + ('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_32') + ]) + +src_files = [ 'characteristic.c', + 'descriptor.c', + 'service.c', + 'advertisement.c', + 'utils.c', + 'central.c', + 'peripheral.c', + 'client.c', + 'server.c', + 'recv.c', + 'caleinterface.c' + ] +src_files = glue_files + src_files Return('src_files') + + +# Local Variables: +# mode:python +# indent-tabs-mode: nil +# End: diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.c new file mode 100644 index 0000000..980d9de --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.c @@ -0,0 +1,132 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "advertisement.h" +#include "gatt_dbus.h" + +#include "cagattservice.h" +#include "logger.h" + +#include + + +// Logging tag. +static char const TAG[] = "BLE_ADVERTISEMENT"; + +bool CALEAdvertisementInitialize(CALEAdvertisement * a, + GDBusConnection * connection, + GList * managers) +{ + assert(a != NULL); + assert(connection != NULL); + + /* + D-Bus object path for the LEAdvertisement1 object of the form + /org/iotivity/gatt/advertisement0. + + The same object path is registered with all + org.bluez.LEAdvertisingManager1 objects since the advertisement + data is same for all Bluetooth hardware adapters. + */ + static char const object_path[] = + CA_GATT_SERVICE_ROOT_PATH "/" CA_LE_ADVERTISEMENT_PATH; + + assert(g_variant_is_object_path(object_path)); + + a->advertisement = leadvertisement1_skeleton_new(); + + /* + Setting the BlueZ advertisement type to "peripheral" causes the + Bluetooth adapter to go into LE connectable and general + discoverable modes upon successful registration of the + advertisement with BlueZ. + */ + leadvertisement1_set_type_(a->advertisement, "peripheral"); + + static char const * service_uuids[] = { + CA_GATT_SERVICE_UUID, // Advertise OIC Transport Profile + NULL + }; + + leadvertisement1_set_service_uuids(a->advertisement, service_uuids); + leadvertisement1_set_manufacturer_data(a->advertisement, NULL); + leadvertisement1_set_solicit_uuids(a->advertisement, NULL); + leadvertisement1_set_service_data(a->advertisement, NULL); + leadvertisement1_set_include_tx_power(a->advertisement, FALSE); + + a->managers = managers; + + // Export the LEAdvertisement1 interface skeleton. + GError * error = NULL; + if (!g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(a->advertisement), + connection, + object_path, + &error)) + { + OIC_LOG_V(ERROR, + TAG, + "Unable to export LE advertisement interface: %s\n", + error->message); + + return false; + } + + return true; +} + +void CALEAdvertisementDestroy(CALEAdvertisement * a) +{ + if (a->advertisement != NULL) + { + char const * const advertisement_path = + g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON( + a->advertisement)); + + if (advertisement_path != NULL) + { + for (GList * l = a->managers; l != NULL; l = l->next) + { + GDBusProxy * const manager = G_DBUS_PROXY(l->data); + + GVariant * const parameters = + g_variant_new("(o)", advertisement_path, NULL); + + /* + Unregister our LE advertisement from the BlueZ LE + advertising manager. + */ + g_dbus_proxy_call( + manager, + "UnregisterAdvertisement", + parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + NULL, // callback + NULL); + } + } + + g_clear_object(&a->advertisement); + } + + g_list_free_full(a->managers, g_object_unref); + a->managers = NULL; +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.h new file mode 100644 index 0000000..87af42c --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/advertisement.h @@ -0,0 +1,79 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_ADVERTISEMENT_H +#define CA_BLE_LINUX_ADVERTISEMENT_H + +#include "bluez-glue.h" + +#include + + +/** + * Information needed for registering an LE advertisement with BlueZ. + */ +typedef struct CALEAdvertisement +{ + /// OIC LE advertisement D-Bus interface skeleton object. + LEAdvertisement1 * advertisement; + + /** + * Proxies to the BlueZ D-Bus objects that implement the + * "org.bluez.LEAdvertisingManager1" interface with which the @c + * advertisement is registered. + */ + GList * managers; + +} CALEAdvertisement; + +/** + * Initialize LE advertisement fields. + * + * This function initializes the @c CALEAdvertisement object fields. + * + * @param[out] a LE advertisement information to be + * initialized. + * @param[in] connection D-Bus connection to the bus on which the + * advertisement will be exported. + * @param[in] managers List of @c org.bluez.LEAdvertisingManager1 + * proxies. + * + * @return @c true on success, @c false otherwise. + * + * @note This function does not allocate the @a adv object itself. + * The caller is responsible for allocating that memory. + */ +bool CALEAdvertisementInitialize(CALEAdvertisement * a, + GDBusConnection * connection, + GList * managers); + +/** + * Destroy LE advertisement fields. + * + * This function finalizes the @c CALEAdvertisement object fields. + * + * @param[in] adv LE advertisement information to be finalized. + * + * @note This function does not deallocate the @a adv object itself. + * The caller is responsible for deallocating that memory. + */ +void CALEAdvertisementDestroy(CALEAdvertisement * adv); + + +#endif // CA_BLE_LINUX_ADVERTISEMENT_H + diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.h new file mode 100644 index 0000000..c4b445c --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.h @@ -0,0 +1,53 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_BLUEZ_H +#define CA_BLE_LINUX_BLUEZ_H + + +/// BlueZ D-Bus service name. +#define BLUEZ_NAME "org.bluez" + +/// BlueZ D-Bus adapter interface name. +static char const BLUEZ_ADAPTER_INTERFACE[] = BLUEZ_NAME ".Adapter1"; + +/// BlueZ D-Bus device interface name. +static char const BLUEZ_DEVICE_INTERFACE[] = BLUEZ_NAME ".Device1"; + +/// BlueZ D-Bus LE advertising manager interface. +static char const BLUEZ_ADVERTISING_MANAGER_INTERFACE[] = + BLUEZ_NAME ".LEAdvertisingManager1"; + +/// BlueZ D-Bus GATT manager interface. +static char const BLUEZ_GATT_MANAGER_INTERFACE[] = + BLUEZ_NAME ".GattManager1"; + +/// BlueZ D-Bus adapter GATT service interface name. +static char const BLUEZ_GATT_SERVICE_INTERFACE[] = + BLUEZ_NAME ".GattService1"; + +/// BlueZ D-Bus adapter GATT characteristic interface name. +static char const BLUEZ_GATT_CHARACTERISTIC_INTERFACE[] = + BLUEZ_NAME ".GattCharacteristic1"; + +/// BlueZ D-Bus adapter GATT service interface name. +static char const BLUEZ_GATT_DESCRIPTOR_INTERFACE[] = + BLUEZ_NAME ".GattDescriptor1"; + + +#endif // CA_BLE_LINUX_BLUEZ_H diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.xml b/resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.xml new file mode 100644 index 0000000..356cd56 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/bluez.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/caleadapter.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/caleadapter.c deleted file mode 100644 index 3e8d709..0000000 --- a/resource/csdk/connectivity/src/bt_le_adapter/linux/caleadapter.c +++ /dev/null @@ -1,121 +0,0 @@ -/****************************************************************** - * - * Copyright 2014 Samsung Electronics All Rights Reserved. - * - * - * - * 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 -#include -#include - -#include "caleadapter.h" -#include "logger.h" - -#define TAG PCF("CA") - -static CANetworkPacketReceivedCallback g_leReceivedCallback = NULL; -static ca_thread_pool_t g_threadPoolHandle = NULL; - -CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback, - CANetworkPacketReceivedCallback reqRespCallback, - CANetworkChangeCallback netCallback, - CAErrorHandleCallback errorCallback, ca_thread_pool_t handle) -{ - OIC_LOG(DEBUG, TAG, "CAInitializeLE"); - - g_leReceivedCallback = reqRespCallback; - g_threadPoolHandle = handle; - - // register handlers - CAConnectivityHandler_t handler = {}; - - handler.startAdapter = CAStartLE; - handler.startListenServer = CAStartLEListeningServer; - handler.startDiscoveryServer = CAStartLEDiscoveryServer; - handler.sendData = CASendLEUnicastData; - handler.sendDataToAll = CASendLEMulticastData; - handler.GetnetInfo = CAGetLEInterfaceInformation; - handler.readData = CAReadLEData; - handler.stopAdapter = CAStopLE; - handler.terminate = CATerminateLE; - - registerCallback(handler, CA_ADAPTER_GATT_BTLE); - - return CA_STATUS_OK; -} - -CAResult_t CAStartLE() -{ - OIC_LOG(DEBUG, TAG, "CAStartLE"); - - return CA_STATUS_OK; -} - -CAResult_t CAStartLEListeningServer() -{ - OIC_LOG(DEBUG, TAG, "CAStartLEListeningServer"); - - return CA_STATUS_OK; -} - -CAResult_t CAStartLEDiscoveryServer() -{ - OIC_LOG(DEBUG, TAG, "CAStartLEDiscoveryServer"); - - return CA_STATUS_OK; -} - -int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen) -{ - OIC_LOG(DEBUG, TAG, "CASendLEUnicastData"); - - return -1; -} - -int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen) -{ - OIC_LOG(DEBUG, TAG, "CASendLEMulticastData"); - - return -1; -} - -CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size) -{ - OIC_LOG(DEBUG, TAG, "CAGetLEInterfaceInformation"); - - return CA_STATUS_OK; -} - -CAResult_t CAReadLEData() -{ - OIC_LOG(DEBUG, TAG, "Read LE Data"); - - return CA_STATUS_OK; -} - -CAResult_t CAStopLE() -{ - OIC_LOG(DEBUG, TAG, "CAStopLE"); - - return CA_STATUS_OK; -} - -void CATerminateLE() -{ - OIC_LOG(DEBUG, TAG, "TerminatLE"); -} - diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/caleinterface.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/caleinterface.c new file mode 100644 index 0000000..5c54171 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/caleinterface.c @@ -0,0 +1,1330 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "caleinterface.h" +#include "bluez.h" +#include "central.h" +#include "peripheral.h" +#include "client.h" +#include "server.h" +#include "utils.h" + +#include "cagattservice.h" +#include "oic_malloc.h" +#include "oic_string.h" +#include "logger.h" + +#include +#include // For strcasecmp(). +#include + + +#define MICROSECS_PER_SEC 1000000 + +// Logging tag. +static char const TAG[] = "BLE_INTERFACE"; + +/* + The IoTivity adapter interface currently doesn't provide a means to + pass context down to the transport layer so rely on a file scope + context instead. +*/ +static CALEContext g_context = { + .lock = NULL +}; + +// ----------------------------------------------------------------------- +// Functions internal to this BLE adapter implementation. +// ----------------------------------------------------------------------- +static bool CALESetUpBlueZObjects(CALEContext * context); + +static bool CALECheckStarted() +{ + ca_mutex_lock(g_context.lock); + + bool const started = (g_context.event_loop != NULL); + + ca_mutex_unlock(g_context.lock); + + /** + * @todo Fix potential TOCTOU race condition. A LE transport + * adapter could have been started or stopped between the + * mutex unlock and boolean check. + */ + return started; +} + +static void CALEDumpDBusSignalParameters(char const * sender_name, + char const * object_path, + char const * interface_name, + char const * signal_name, + GVariant * parameters) +{ +#ifdef TB_LOG + gchar * const param_dump = + g_variant_print(parameters, TRUE); + + OIC_LOG_V(DEBUG, + TAG, + "%s()\n" + "\tsender_name: %s\n" + "\tobject_path: %s\n" + "\tinterface_name: %s\n" + "\tsignal_name: %s\n" + "\tparameters: %s\n", + __func__, + sender_name, + object_path, + interface_name, + signal_name, + param_dump); + + g_free(param_dump); +#endif // TB_LOG +} + +static void CALEOnInterfaceProxyPropertiesChanged( + GDBusObjectManagerClient * manager, + GDBusObjectProxy * object_proxy, + GDBusProxy * interface_proxy, + GVariant * changed_properties, + gchar const * const * invalidated_properties, + gpointer user_data) +{ + OIC_LOG_V(DEBUG, + TAG, + "Properties Changed on %s:\n", + g_dbus_object_get_object_path( + G_DBUS_OBJECT(object_proxy))); + + char const * const interface_name = + g_dbus_proxy_get_interface_name(interface_proxy); + + bool const is_adapter_interface = + (strcmp(BLUEZ_ADAPTER_INTERFACE, interface_name) == 0); + + if (!is_adapter_interface) + { + /* + Only specific org.bluez.Adapter1 property changes are + currently supported. + */ + return; + } + + CALEContext * const context = user_data; + + GVariantIter iter; + gchar const * key = NULL; + GVariant * value = NULL; + + g_variant_iter_init(&iter, changed_properties); + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) + { + if (strcmp(key, "Powered") == 0) + { + /* + Report a change in the availability of the bluetooth + adapter. + */ + + gboolean const powered = g_variant_get_boolean(value); + CAAdapterState_t const status = + (powered ? CA_ADAPTER_ENABLED : CA_ADAPTER_DISABLED); + + CAEndpoint_t info = + { + .adapter = CA_ADAPTER_GATT_BTLE, + }; + + GVariant * const prop = + g_dbus_proxy_get_cached_property(interface_proxy, + "Address"); + + gchar const * const address = g_variant_get_string(prop, NULL); + + OICStrcpy(info.addr, sizeof(info.addr), address); + + g_variant_unref(prop); + + /** + * @todo Should we acquire the context lock here to + * prevent the @c CALEDeviceStateChangedCallback + * from being potentially yanked out from under us + * if the CA adapters are stopped/terminated as + * we're about to invoke this callback? + * + * @todo Unfortunately the CA LE interface defined in + * caleinterface.h assumes that only one BLE adapter + * will exist on a given host. However, this + * implementation can handle multiple BLE adapters. + * The CA LE interface should be updated so that it + * can handle multiple BLE adapters. + */ + context->on_device_state_changed(status); + } + +#ifdef TB_LOG + gchar * const s = g_variant_print(value, TRUE); + OIC_LOG_V(DEBUG, TAG, " %s -> %s", key, s); + g_free(s); +#endif // TB_LOG + + g_variant_unref(value); + } +} + +static void CALEHandleInterfaceAdded(GList ** proxy_list, + char const * interface, + GVariant * parameters) +{ + /** + * @note The @a parameters are of the form "(oa{sv})". + */ + GDBusProxy * const proxy = + CAGetBlueZInterfaceProxy(parameters, + interface, + g_context.object_manager); + + if (proxy == NULL) + { + return; + } + + ca_mutex_lock(g_context.lock); + + /* + Add the object information to the list. + + Note that we prepend instead of append in this case since it + is more efficient to do so for linked lists like the one used + here. + */ + *proxy_list = g_list_prepend(*proxy_list, proxy); + + ca_mutex_unlock(g_context.lock); + + /** + * Let the thread that may be blocked waiting for Devices to be + * discovered know that at least one was found. + * + * @todo It doesn't feel good putting this @c org.bluez.Device1 + * specific code here since this function is meant to be + * BlueZ interface neutral. Look into ways of moving this + * out of here. + */ + if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0) + { + ca_cond_signal(g_context.condition); + } +} + +static void CALEOnInterfacesAdded(GDBusConnection * connection, + char const * sender_name, + char const * object_path, + char const * interface_name, + char const * signal_name, + GVariant * parameters, + gpointer user_data) +{ + CALEDumpDBusSignalParameters(sender_name, + object_path, + interface_name, + signal_name, + parameters); + + // The signal should always be InterfacesAdded. + assert(strcmp(signal_name, "InterfacesAdded") == 0); + + // Handle addition of a new org.bluez.Adapter1 interface. + CALEHandleInterfaceAdded(&g_context.adapters, + BLUEZ_ADAPTER_INTERFACE, + parameters); + + // Handle addition of a new org.bluez.Device1 interface. + CALEHandleInterfaceAdded(&g_context.devices, + BLUEZ_DEVICE_INTERFACE, + parameters); +} + +static void CALEOnInterfacesRemoved(GDBusConnection * connection, + char const * sender_name, + char const * object_path, + char const * interface_name, + char const * signal_name, + GVariant * parameters, + gpointer user_data) +{ + CALEDumpDBusSignalParameters(sender_name, + object_path, + interface_name, + signal_name, + parameters); + + // The signal should always be InterfacesRemoved. + assert(strcmp(signal_name, "InterfacesRemoved") == 0); + + /* + The object path is first tuple element, and the interface names + the second. Check if "org.bluez.Adapter1" exists in the + interface array. If it does, remove the corresponding + information from the adapter_infos list. + */ + GVariant * const interfaces = + g_variant_get_child_value(parameters, 1); + + GVariantIter * iter = NULL; + g_variant_get(interfaces, "as", &iter); + + /** + * Iterate over the array and remove all BlueZ interface proxies + * with a matching D-Bus object path from the corresponding list. + * + * @todo Determine whether we should optimize this nested loop. + * It may not be worthwhile to do so since the lists being + * iterated over should be very short. + */ + for (GVariant * child = g_variant_iter_next_value(iter); + child != NULL; + child = g_variant_iter_next_value(iter)) + { + char const * interface = NULL; + g_variant_get(child, "&s", &interface); + + GList ** list = NULL; + + if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) + { + list = &g_context.adapters; + } + else if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0) + { + list = &g_context.devices; + } + else + { + continue; + } + + // The object path is the first tuple element. + gchar const * path = NULL; + g_variant_get_child(parameters, 0, "&o", &path); + + ca_mutex_lock(g_context.lock); + + for (GList * l = *list; l != NULL; l = g_list_next(l)) + { + GDBusProxy * const proxy = G_DBUS_PROXY(l->data); + + if (strcmp(path, + g_dbus_proxy_get_object_path(proxy)) == 0) + { + // Found a match! + g_object_unref(proxy); + + *list = g_list_delete_link(*list, l); + + break; + } + } + + ca_mutex_unlock(g_context.lock); + + g_variant_unref(child); + } + + if (iter != NULL) + g_variant_iter_free(iter); +} + +static void CALEOnPropertiesChanged(GDBusConnection * connection, + char const * sender_name, + char const * object_path, + char const * interface_name, + char const * signal_name, + GVariant * parameters, + gpointer user_data) +{ + CALEDumpDBusSignalParameters(sender_name, + object_path, + interface_name, + signal_name, + parameters); +} + +static void CALEOnPropertyChanged(GDBusConnection * connection, + char const * sender_name, + char const * object_path, + char const * interface_name, + char const * signal_name, + GVariant * parameters, + gpointer user_data) +{ + CALEDumpDBusSignalParameters(sender_name, + object_path, + interface_name, + signal_name, + parameters); +} + +static void CALESubscribeToSignals(CALEContext * context, + GDBusConnection * connection, + GDBusObjectManager * object_manager) +{ + static char const om_interface[] = + "org.freedesktop.DBus.ObjectManager"; + + /* + Subscribe to D-Bus signals that will allow us to detect changes + in BlueZ adapter and device properties. + */ + guint const interfaces_added_sub_id = + g_dbus_connection_signal_subscribe( + connection, + NULL, // sender + om_interface, + "InterfacesAdded", + NULL, // object path + NULL, // arg0 + G_DBUS_SIGNAL_FLAGS_NONE, + CALEOnInterfacesAdded, + NULL, // user_data + NULL); + + guint const interfaces_removed_sub_id = + g_dbus_connection_signal_subscribe( + connection, + NULL, // sender + om_interface, + "InterfacesRemoved", + NULL, // object path + NULL, // arg0 + G_DBUS_SIGNAL_FLAGS_NONE, + CALEOnInterfacesRemoved, + NULL, // user_data + NULL); + +#if GLIB_CHECK_VERSION(2,38,0) + /* + The G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH flag was introduced in + GLib 2.38. + */ + static GDBusSignalFlags const device_signal_flags = + G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH; +#else + static GDBusSignalFlags const device_signal_flags = + G_DBUS_SIGNAL_FLAGS_NONE; +#endif + + /** + * @todo Verify that this signal subscription is needed. + * + * @bug The arg0 argument below should be a D-Bus object path, not + * interface name. + */ + guint const properties_changed_sub_id = + g_dbus_connection_signal_subscribe( + connection, + NULL, // sender + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + NULL, // object path + "org.bluez.Device1", // arg0 + device_signal_flags, + CALEOnPropertiesChanged, + NULL, // user_data + NULL); + + /** + * @todo Verify that this signal subscription is needed. + */ + guint const property_changed_sub_id = + g_dbus_connection_signal_subscribe(connection, + NULL, // sender + BLUEZ_ADAPTER_INTERFACE, + "PropertyChanged", + NULL, // object path + NULL, // arg0 + G_DBUS_SIGNAL_FLAGS_NONE, + CALEOnPropertyChanged, + NULL, // user_data + NULL); + + g_signal_connect(object_manager, + "interface-proxy-properties-changed", + G_CALLBACK(CALEOnInterfaceProxyPropertiesChanged), + context); + + ca_mutex_lock(context->lock); + + context->interfaces_added_sub_id = interfaces_added_sub_id; + context->interfaces_removed_sub_id = interfaces_removed_sub_id; + context->properties_changed_sub_id = properties_changed_sub_id; + context->property_changed_sub_id = property_changed_sub_id; + + ca_mutex_unlock(context->lock); +} + +static bool CALESetUpDBus(CALEContext * context) +{ + assert(context != NULL); + + bool success = false; + + GError * error = NULL; + + /* + Set up connection to the D-Bus system bus, where the BlueZ + daemon is found. + */ + GDBusConnection * const connection = + g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + + if (connection == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "Connection to D-Bus system bus failed: %s.", + error->message); + + g_error_free(error); + + return success; + } + + // Create a proxy to the BlueZ D-Bus ObjectManager. + static char const object_manager_path[] = "/"; + + GDBusObjectManager * const object_manager = + g_dbus_object_manager_client_new_sync( + connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + BLUEZ_NAME, + object_manager_path, + NULL, // get_proxy_type_func + NULL, // get_proxy_type_user_data + NULL, // get_proxy_type_destroy_notify + NULL, // cancellable + &error); + + if (object_manager == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "Unable to create D-Bus ObjectManager client: %s", + error->message); + + g_error_free(error); + + g_object_unref(connection); + + return success; + } + + CALESubscribeToSignals(context, connection, object_manager); + + ca_mutex_lock(context->lock); + context->connection = connection; + context->object_manager = object_manager; + ca_mutex_unlock(context->lock); + + success = CALESetUpBlueZObjects(context); + + return success; +} + +static void CALETearDownDBus(CALEContext * context) +{ + assert(context != NULL); + + /* + Minimize the time we hold the global lock by only clearing the + global state, and pushing resource finalization outside the global + lock. + */ + ca_mutex_lock(context->lock); + + GDBusConnection * const connection = context->connection; + context->connection = NULL; + + GDBusObjectManager * const object_manager = context->object_manager; + context->object_manager = NULL; + + GList * const objects = context->objects; + context->objects = NULL; + + GList * const adapters = context->adapters; + context->adapters = NULL; + + GList * const devices = context->devices; + context->devices = NULL; + + guint const interfaces_added = context->interfaces_added_sub_id; + guint const interfaces_removed = context->interfaces_removed_sub_id; + guint const properties_changed = context->properties_changed_sub_id; + guint const property_changed = context->property_changed_sub_id; + + context->interfaces_added_sub_id = 0; + context->interfaces_removed_sub_id = 0; + context->properties_changed_sub_id = 0; + context->property_changed_sub_id = 0; + + ca_mutex_unlock(context->lock); + + // Destroy the device proxies list. + g_list_free_full(devices, g_object_unref); + + // Destroy the adapter proxies list. + g_list_free_full(adapters, g_object_unref); + + // Destroy the list of objects obtained from the ObjectManager. + g_list_free_full(objects, g_object_unref); + + // Destroy the ObjectManager proxy. + if (object_manager != NULL) + { + g_object_unref(object_manager); + } + + // Tear down the D-Bus connection to the system bus. + if (connection != NULL) + { + g_dbus_connection_signal_unsubscribe(connection, + interfaces_added); + g_dbus_connection_signal_unsubscribe(connection, + interfaces_removed); + g_dbus_connection_signal_unsubscribe(connection, + properties_changed); + g_dbus_connection_signal_unsubscribe(connection, + property_changed); + g_object_unref(connection); + } +} + +static bool CALEDeviceFilter(GDBusProxy * device) +{ + bool accepted = false; + + /* + Filter out any devices that don't support the OIC Transport + Profile service. + */ + GVariant * const prop = + g_dbus_proxy_get_cached_property(device, "UUIDs"); + + if (prop == NULL) + { + // No remote services available on the device. + return accepted; + } + + char const ** const UUIDs = g_variant_get_strv(prop, NULL); + + /** + * @note It would have been nice to use @c g_strv_contains() here, + * but we would need to run it twice: once for the uppercase + * form of the UUID and once for for the lowercase form. + * Just run the loop manually, and use @c strcasecmp() + * instead. + */ + for (char const * const * u = UUIDs; u != NULL; ++u) + { + if (strcasecmp(*u, CA_GATT_SERVICE_UUID) == 0) + { + accepted = true; + break; + } + } + + g_free(UUIDs); + g_variant_unref(prop); + + return accepted; +} + + +static bool CALESetUpBlueZObjects(CALEContext * context) +{ + bool success = false; + + // Get the list of BlueZ D-Bus objects. + GList * const objects = + g_dbus_object_manager_get_objects(context->object_manager); + + if (objects == NULL) { + OIC_LOG(ERROR, + TAG, + "Unable to get objects from ObjectManager."); + + return success; + } + + ca_mutex_lock(context->lock); + context->objects = objects; + ca_mutex_unlock(context->lock); + + /* + Create a proxies to the org.bluez.Adapter1 D-Bus objects that + will later be used to obtain local bluetooth adapter properties, + as well as by the BLE central code to discover peripherals. + */ + GList * adapters = NULL; + success = CAGetBlueZManagedObjectProxies(&adapters, + BLUEZ_ADAPTER_INTERFACE, + context, + NULL); + + // An empty adapters list is NULL. + if (success && adapters != NULL) + { + ca_mutex_lock(context->lock); + context->adapters = adapters; + ca_mutex_unlock(context->lock); + } + + /* + Create a proxies to the org.bluez.Device1 D-Bus objects that + will later be used to establish connections. + */ + GList * devices = NULL; + success = CAGetBlueZManagedObjectProxies(&devices, + BLUEZ_DEVICE_INTERFACE, + context, + CALEDeviceFilter); + + // An empty device list is NULL. + if (success && devices != NULL) + { + ca_mutex_lock(context->lock); + context->devices = devices; + ca_mutex_unlock(context->lock); + } + + /* success = CAGattClientInitialize(context); */ + + return success; +} + +static void CALEStartEventLoop(void * data) +{ + CALEContext * const context = data; + + assert(context != NULL); + + // Create the event loop. + GMainContext * const loop_context = g_main_context_new(); + GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE); + + ca_mutex_lock(context->lock); + + assert(context->event_loop == NULL); + context->event_loop = event_loop; + + ca_mutex_unlock(context->lock); + + g_main_context_push_thread_default(loop_context); + + /* + We have to do the BlueZ object manager client initialization and + signal subscription here so that the corresponding asynchronous + signal handling occurs in the same thread as the one running the + GLib event loop. + */ + if (!CALESetUpDBus(&g_context)) + return; + + ca_cond_signal(g_context.condition); + + g_main_loop_run(event_loop); +} + +static void CALEStopEventLoop(CALEContext * context) +{ + ca_mutex_lock(context->lock); + + GMainLoop * const event_loop = context->event_loop; + context->event_loop = NULL; + + ca_mutex_unlock(context->lock); + + if (event_loop != NULL) + { + g_main_loop_quit(event_loop); + + GMainContext * const loop_context = + g_main_loop_get_context(event_loop); + + if (loop_context != NULL) + { + g_main_context_wakeup(loop_context); + g_main_context_unref(loop_context); + } + + g_main_loop_unref(event_loop); + } +} + +/** + * Wait for @a list to be non-empty. + * + * @param[in] list List that should not be empty. + * @param[in] retries Number of times to retry if the @a timeout is + * reached. + * @param[in] timeout Timeout in microseconds to wait between retries. + */ +static bool CALEWaitForNonEmptyList(GList * const * list, + int retries, + uint64_t timeout) +{ + bool success = false; + + ca_mutex_lock(g_context.lock); + + for (int i = 0; *list == NULL && i < retries; ++i) + { + if (ca_cond_wait_for(g_context.condition, + g_context.lock, + timeout) == 0) + { + /* + Condition variable was signaled before the timeout was + reached. + */ + success = true; + } + } + + ca_mutex_unlock(g_context.lock); + + return success; +} + +static CAResult_t CALEStop() +{ + CAResult_t result = CA_STATUS_FAILED; + + OIC_LOG(DEBUG, TAG, "Stop Linux BLE adapter."); + + // Only stop if we were previously started. + if (!CALECheckStarted()) + return result; + + // Stop the event loop thread regardless of previous errors. + CALEStopEventLoop(&g_context); + + CALETearDownDBus(&g_context); + + return result; +} + +static void CALETerminate() +{ + OIC_LOG(DEBUG, TAG, "Terminate BLE adapter."); + + CAPeripheralFinalize(); + + ca_mutex_lock(g_context.lock); + + g_context.on_device_state_changed = NULL; + g_context.on_server_received_data = NULL; + g_context.on_client_received_data = NULL; + g_context.client_thread_pool = NULL; + g_context.server_thread_pool = NULL; + g_context.on_client_error = NULL; + g_context.on_server_error = NULL; + + ca_mutex_unlock(g_context.lock); + + ca_cond_free(g_context.condition); + ca_mutex_free(g_context.lock); +} + +// ----------------------------------------------------------------------- + +CAResult_t CAInitializeLEAdapter() +{ +#if !GLIB_CHECK_VERSION(2,36,0) + /* + Initialize the GLib type system. + + As of GLib 2.36, it is no longer necessary to explicitly call + g_type_init(). + */ + g_type_init(); +#endif + + g_context.lock = ca_mutex_new(); + g_context.condition = ca_cond_new(); + + CAPeripheralInitialize(); + + return CA_STATUS_OK; +} + +CAResult_t CAStartLEAdapter() +{ + /* + This function is called by the connectivity abstraction when + CASelectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user. + */ + + OIC_LOG(DEBUG, TAG, __func__); + + CAResult_t result = CA_STATUS_FAILED; + + // Only start if we were previously stopped. + if (CALECheckStarted()) + return result; + + /** + * Spawn a thread to run the GLib event loop that will drive D-Bus + * signal handling. + * + * @note Ideally this should be done in the @c CAInitializeLE() + * function so that we can detect local bluetooth adapter + * changes right away, without having to first start this LE + * adapter/transport via @cCASelectNetwork(). However, a + * limitation in the CA termination code that destroys the + * thread pool before the transport adapters prevents us + * from doing that without potentially triggering a + * @c pthread_join() call that blocks indefinitely due to + * this event loop not be stopped. See the comments in the + * @c CAGetLEInterfaceInformation() function below for + * further details. + */ + result = ca_thread_pool_add_task(g_context.client_thread_pool, + CALEStartEventLoop, + &g_context); + + if (result != CA_STATUS_OK) + return result; + + /* + Wait until initialization completes before continuing, basically + until some Bluetooth adapters were found. + */ + + // Number of times to wait for initialization to complete. + static int const retries = 2; + + static uint64_t const timeout = + 2 * MICROSECS_PER_SEC; // Microseconds + + if (CALEWaitForNonEmptyList(&g_context.adapters, retries, timeout)) + { + result = CA_STATUS_OK; + } + + return result; +} + +CAResult_t CAGetLEAdapterState() +{ + /** + * @todo To be implemented shortly as part of the effort to + * address a critical code review that stated this BLE + * transport should implement the interface defined in + * caleinterface.h. + */ + return CA_NOT_SUPPORTED; +} + +CAResult_t CAInitializeLENetworkMonitor() +{ + /** + * @todo To be implemented shortly as part of the effort to + * address a critical code review that stated this BLE + * transport should implement the interface defined in + * caleinterface.h. + */ + return CA_STATUS_OK; +} + +void CATerminateLENetworkMonitor() +{ + /** + * @todo To be implemented shortly as part of the effort to + * address a critical code review that stated this BLE + * transport should implement the interface defined in + * caleinterface.h. + */ +} + +CAResult_t CASetLEAdapterStateChangedCb(CALEDeviceStateChangedCallback callback) +{ + ca_mutex_lock(g_context.lock); + g_context.on_device_state_changed = callback; + ca_mutex_unlock(g_context.lock); + + return CA_STATUS_OK; +} + +CAResult_t CAInitLENetworkMonitorMutexVariables() +{ + /* + This CA LE interface implementation doesn't use a network + monitor as the other platform implementationd do. + */ + return CA_STATUS_OK; +} + +void CATerminateLENetworkMonitorMutexVariables() +{ + /* + This CA LE interface implementation doesn't use a network + monitor as the other platform implementationd do. + */ +} + +CAResult_t CAGetLEAddress(char **local_address) +{ + OIC_LOG(DEBUG, TAG, "Get Linux BLE local device information."); + + if (local_address == NULL) + { + return CA_STATUS_INVALID_PARAM; + } + + /** + * @bug Attempting to get LE interface information before this + * connectivity abstraction adapter has started (e.g. via + * @c CASelectNetwork()) could result in an inaccurate list + * of LE interfaces. For example, detection of hot-plugged + * bluetooth adapters will only work after the LE IoTivity + * network has been selected. If the LE IoTivity network + * hasn't been selected, the hot-plugged bluetooth adapter + * will not be reflected in the @c CALocalConnectivity_t list + * returned by this function. + * @par + * This issue caused by the fact that the event loop that + * handles such events can only be run after this LE + * adapter/transport is started. The event loop cannot be + * started earlier, e.g. during @c CAInitialize(), due to a + * limitation in the CA transport termination code that + * requires thread pools to be destroyed before transport + * termination. If the event loop was added as a task to the + * thread pool during @c CAInitialize(), it's possible that + * the thread running that event loop could be blocked + * waiting for events during a later call to + * @c CATerminate() if @c CASelectNetwork() was not called + * beforehand. The thread pool destruction that occurs + * during @c CATerminate() waits for threads in the pool to + * exit. However, in this case the event loop thread is + * still blocked waiting for events since it may not have + * been stopped since the transport itself was not stopped, + * meaning termination will also be blocked. + * @par + * Other than refactoring to the termination code to allow + * thread pools to be started during @c CAInitialize(), the + * only other choice we have to prevent the hang at + * termination is to move the event loop creation and + * termination to the @c CAAdapterStart() and + * @c CAAdapterStop() implementations, respectively, which is + * why we have this bug. + */ + if (!CALECheckStarted()) + return CA_ADAPTER_NOT_ENABLED; + + *local_address = NULL; + + ca_mutex_lock(g_context.lock); + + for (GList * l = g_context.adapters; l != NULL; l = l->next) + { + GDBusProxy * const adapter = G_DBUS_PROXY(l->data); + + /* + The local bluetooth adapter MAC address is stored in the + org.bluez.Adapter1.Address property. + */ + GVariant * const prop = + g_dbus_proxy_get_cached_property(adapter, "Address"); + + /* + Unless the org.bluez.Adapter1.Address property no longer + exists, prop should not be NULL! We have bigger problems if + this assert() is ever tripped since that would mean the + org.bluez.Adapter1 D-Bus interface changed. + */ + assert(prop != NULL); + + gchar const * const address = g_variant_get_string(prop, NULL); + + *local_address = OICStrdup(address); + + /* + No longer need the property variant. The address has been + copied. + */ + g_variant_unref(prop); + + /** + * @todo Unfortunately the CA LE interface defined in + * caleinterface.h assumes that only one BLE adapter + * will exist on a given host. However, this + * implementation can handle multiple BLE adapters. The + * CA LE interface should be updated so that it can + * handle multiple BLE adapters. For now we'll just + * return the address for the first BLE adapter in the + * list. + */ + break; + } + + ca_mutex_unlock(g_context.lock); + + return *local_address != NULL ? CA_STATUS_OK : CA_STATUS_FAILED; +} + +CAResult_t CAStartLEGattServer() +{ + return CAPeripheralStart(&g_context); +} + +CAResult_t CAStopLEGattServer() +{ + CAResult_t result = CAPeripheralStop(); + CAResult_t const tmp = CALEStop(); + + if (result == CA_STATUS_OK && tmp != CA_STATUS_OK) + { + result = tmp; + } + + return result; +} + +void CATerminateLEGattServer() +{ + CALETerminate(); +} + +void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback) +{ + ca_mutex_lock(g_context.lock); + g_context.on_server_received_data = callback; + ca_mutex_unlock(g_context.lock); +} + +CAResult_t CAUpdateCharacteristicsToGattClient(char const * address, + char const * charValue, + uint32_t charValueLen) +{ + /** + * @todo To be implemented shortly as part of the effort to + * address a critical code review that stated this BLE + * transport should implement the interface defined in + * caleinterface.h. + */ + return CA_NOT_SUPPORTED; +} + +CAResult_t CAUpdateCharacteristicsToAllGattClients(char const * charValue, + uint32_t charValueLen) +{ + /** + * @todo To be implemented shortly as part of the effort to + * address a critical code review that stated this BLE + * transport should implement the interface defined in + * caleinterface.h. + */ + return CA_NOT_SUPPORTED; +} + +CAResult_t CAStartLEGattClient() +{ + return CACentralStart(&g_context); +} + +void CAStopLEGattClient() +{ + (void) CACentralStop(&g_context); + (void) CALEStop(); +} + +void CATerminateLEGattClient() +{ + CALETerminate(); +} + +void CACheckLEData() +{ + /* + This function is only used in single-threaded builds, but this + CA LE adapter implementation is multi-threaded. Consequently, + this function is a no-op. + */ +} + +CAResult_t CAUpdateCharacteristicsToGattServer( + char const * remoteAddress, + char const * data, + uint32_t dataLen, + CALETransferType_t type, + int32_t position) +{ + /** + * @todo To be implemented shortly as part of the effort to + * address a critical code review that stated this BLE + * transport should implement the interface defined in + * caleinterface.h. + */ + return CA_NOT_SUPPORTED; +} + +CAResult_t CAUpdateCharacteristicsToAllGattServers(char const * data, + uint32_t length) +{ + OIC_LOG(DEBUG, TAG, "Send data to all"); + + /* + Multicast data is only sent when a request is sent from a client + across all endpoints. We need not worry about sending a + response from a server here. + */ + + CAResult_t result = CA_STATUS_FAILED; + + ca_mutex_lock(g_context.lock); + bool found_peripherals = (g_context.devices != NULL); + ca_mutex_unlock(g_context.lock); + + if (!found_peripherals) + { + /* + Start discovery of LE peripherals that advertise the OIC + Transport Profile. + */ + result = CACentralStartDiscovery(&g_context); + + if (result != CA_STATUS_OK) + { + return -1; + } + + // Wait for LE peripherals to be discovered. + + // Number of times to wait for discovery to complete. + static int const retries = 5; + + static uint64_t const timeout = + 2 * MICROSECS_PER_SEC; // Microseconds + + if (!CALEWaitForNonEmptyList(&g_context.devices, + retries, + timeout)) + { + return -1; + } + + ca_mutex_lock(g_context.lock); + found_peripherals = (g_context.devices == NULL); + ca_mutex_unlock(g_context.lock); + + if (!found_peripherals) + { + // No peripherals discovered! + return -1; + } + } + + /* + Stop discovery so that we can connect to LE peripherals. + Otherwise, the bluetooth subsystem will claim the adapter is + busy. + */ + + result = CACentralStopDiscovery(&g_context); + + if (result != CA_STATUS_OK) + { + return -1; + } + + bool const connected = CACentralConnectToAll(&g_context); + + if (!connected) + { + return -1; + } + + /** + * @todo Start notifications on all response characteristics. + */ + + /* + Now send the request through all BLE connections through the + corresponding OIC GATT request characterstics. + */ + + CAGattRequestInfo const info = + { + .characteristic_info = NULL, // g_context.characteristics + .context = &g_context + }; + + return CAGattClientSendDataToAll(&info, data, length); + + /** + * @todo Should we restart discovery after the send? + */ +} + +void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback) +{ + ca_mutex_lock(g_context.lock); + g_context.on_client_received_data = callback; + ca_mutex_unlock(g_context.lock); +} + +void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle) +{ + ca_mutex_lock(g_context.lock); + g_context.server_thread_pool = handle; + ca_mutex_unlock(g_context.lock); +} + +void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle) +{ + ca_mutex_lock(g_context.lock); + g_context.client_thread_pool = handle; + ca_mutex_unlock(g_context.lock); +} + +CAResult_t CAUnSetLEAdapterStateChangedCb() +{ + ca_mutex_lock(g_context.lock); + g_context.on_device_state_changed = NULL; + ca_mutex_unlock(g_context.lock); + + return CA_STATUS_OK; +} + +void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback) +{ + ca_mutex_lock(g_context.lock); + g_context.on_client_error = callback; + ca_mutex_unlock(g_context.lock); +} + +void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback) +{ + ca_mutex_lock(g_context.lock); + g_context.on_server_error = callback; + ca_mutex_unlock(g_context.lock); +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/central.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/central.c new file mode 100644 index 0000000..874e3a2 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/central.c @@ -0,0 +1,444 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "central.h" +#include "utils.h" +#include "bluez.h" + +#include "cagattservice.h" // For CA_GATT_SERVICE_UUID. +#include "logger.h" + +#include +#include + + +// Logging tag. +static char const TAG[] = "BLE_CENTRAL"; + +static bool CACentralGetBooleanProperty(GDBusProxy * device, + char const * property) +{ + GVariant * const cached_property = + g_dbus_proxy_get_cached_property(device, property); + + if (cached_property == NULL) + { + return false; + } + + bool const value = g_variant_get_boolean(cached_property); + + g_variant_unref(cached_property); + + return value; +} + +static void CACentralStartDiscoveryImpl(gpointer proxy, gpointer user_data) +{ + assert(proxy != NULL); + assert(user_data != NULL); + + GDBusProxy * const adapter = G_DBUS_PROXY(proxy); + CAResult_t * const result = user_data; + + *result = CA_STATUS_FAILED; + + bool const is_discovering = + CACentralGetBooleanProperty(adapter, "Discovering"); + + if (is_discovering) + { + // Nothing to do. Avoid invoking a method over D-Bus. + *result = CA_STATUS_OK; + return; + } + + + // Make sure the adapter is powered on before starting discovery. + if (!CASetBlueZObjectProperty(adapter, + BLUEZ_ADAPTER_INTERFACE, + "Powered", + g_variant_new_boolean(TRUE))) + { + OIC_LOG(ERROR, + TAG, + "Unable to power on LE central adapter."); + + return; + } + + /* + Only scan for LE peripherals that advertise the OIC Transport + Profile GATT service UUID by setting a discovery filter on the BlueZ + org.bluez.Adapter1 object with two parameters: + + (1) "UUIDs": set to an array containing that OIC Transport + Profile GATT service UUID + (2) "Transport": set to "le" + + See the documentation for the SetDiscoveryFilter() method in the + BlueZ `adapter-api.txt' document for more details. + */ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + + static char const * const UUIDs[] = + { + CA_GATT_SERVICE_UUID + }; + + g_variant_builder_add(&builder, + "{sv}", + "UUIDs", + g_variant_new_strv( + UUIDs, + sizeof(UUIDs) / sizeof(UUIDs[0]))); + + g_variant_builder_add(&builder, + "{sv}", + "Transport", + g_variant_new_string("le")); + + GVariant * const filter = g_variant_builder_end(&builder); + + /* + SetDiscoveryFilter() expects a dictionary but it must be packed + into a tuple for the actual call through the proxy. + */ + GVariant * const filter_parameters = + g_variant_new("(@a{sv})", filter); + + GError * error = NULL; + + /* + This is a synchronous call, but the actually discovery is + performed asynchronously. org.bluez.Device1 objects will + reported through the + org.freedesktop.DBus.ObjectManager.InterfacesAdded signal as + peripherals that match our discovery filter criteria are found. + */ + GVariant * ret = + g_dbus_proxy_call_sync(adapter, + "SetDiscoveryFilter", + filter_parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "SetDiscoveryFilter() call failed: %s", + error->message); + + g_error_free(error); + + return; + } + + g_variant_unref(ret); + + // Start device discovery. + ret = g_dbus_proxy_call_sync(adapter, + "StartDiscovery", + NULL, // parameters + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "StartDiscovery() call failed: %s", + error->message); + + g_error_free(error); + + return; + } + + g_variant_unref(ret); + + *result = CA_STATUS_OK; +} + +static void CACentralStopDiscoveryImpl(gpointer proxy, gpointer user_data) +{ + assert(proxy != NULL); + assert(user_data != NULL); + + GDBusProxy * const adapter = G_DBUS_PROXY(proxy); + CAResult_t * const result = user_data; + + bool const is_discovering = + CACentralGetBooleanProperty(adapter, "Discovering"); + + if (!is_discovering) + { + // Nothing to do. Avoid invoking a method over D-Bus. + *result = CA_STATUS_OK; + return; + } + + *result = CA_STATUS_FAILED; + + + GError * error = NULL; + + // Stop discovery sessions. + GVariant * const ret = + g_dbus_proxy_call_sync(adapter, + "StopDiscovery", + NULL, // parameters + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1) + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "StopDiscovery() call failed: %s", + error->message); + + g_error_free(error); + + return; + } + + g_variant_unref(ret); + + *result = CA_STATUS_OK; +} + +static void CACentralConnectToDevice(gpointer data, gpointer user_data) +{ + assert(data != NULL); + assert(user_data != NULL); + + GDBusProxy * const device = G_DBUS_PROXY(data); + bool * const connected = user_data; + + if (!CACentralConnect(device)) + { + *connected = false; + } +} + +/** + * Disconnect from all LE peripherals. + * + * @param[in] context Context containing BlueZ device information. + */ +static void CACentralDisconnect(CALEContext * context) +{ + assert(context != NULL); + + ca_mutex_lock(context->lock); + + for (GList * l = context->devices; l != NULL; l = l->next) + { + GDBusProxy * const device = G_DBUS_PROXY(l->data); + + bool const is_connected = + CACentralGetBooleanProperty(device, "Connected"); + + if (is_connected) + { + /* + Asynchronously disconnect. We don't care about the + result so don't bother passing in a callback. + */ + g_dbus_proxy_call(device, + "Disconnect", + NULL, // parameters + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + NULL, // callback + NULL); // user data + } + } + + ca_mutex_unlock(context->lock); +} + +// ----------------------------------------------------------------------- + +CAResult_t CACentralStart(CALEContext * context) +{ + assert(context != NULL); + + CAResult_t result = CA_STATUS_FAILED; + + /* + Synchronize access to the adapter information using the base + context lock since we don't own the adapters. + */ + ca_mutex_lock(context->lock); + + /** + * Start discovery on all detected adapters. + * + * @todo The current start start_discovery() implementation makes + * two synchronous D-Bus calls to each BlueZ @c Adapter1 + * object in the @a adapters list. We may want to make them + * asynchronous to minimize blocking. + */ + g_list_foreach(context->adapters, + CACentralStartDiscoveryImpl, + &result); + + ca_mutex_unlock(context->lock); + + return result; +} + +CAResult_t CACentralStop(CALEContext * context) +{ + assert(context != NULL); + + CAResult_t result = CA_STATUS_FAILED; + + // Stop discovery on all detected adapters. + result = CACentralStopDiscovery(context); + + // Disconnect from all adapters. + CACentralDisconnect(context); + + /** + * @todo Stop notifications on all response characteristics. + */ + + return result; +} + +CAResult_t CACentralStartDiscovery(CALEContext * context) +{ + assert(context != NULL); + + CAResult_t result = CA_STATUS_FAILED; + + /* + Synchronize access to the adapter information using the base + context lock since we don't own the adapters. + */ + ca_mutex_lock(context->lock); + + // Start discovery on all detected adapters. + g_list_foreach(context->adapters, + CACentralStartDiscoveryImpl, + &result); + + ca_mutex_unlock(context->lock); + + return result; +} + +CAResult_t CACentralStopDiscovery(CALEContext * context) +{ + assert(context != NULL); + + CAResult_t result = CA_STATUS_FAILED; + + /* + Synchronize access to the adapter information using the base + context lock since we don't own the adapters. + */ + ca_mutex_lock(context->lock); + + // Stop discovery on all detected adapters. + g_list_foreach(context->adapters, + CACentralStopDiscoveryImpl, + &result); + + /** + * @todo Stop notifications on all response characteristics. + */ + + ca_mutex_unlock(context->lock); + + return result; +} + +bool CACentralConnect(GDBusProxy * device) +{ + assert(device != NULL); + + /* + Check if a connection to the LE peripheral was already + established. If not, establish a connection. + */ + bool const is_connected = + CACentralGetBooleanProperty(device, "Connected"); + + if (is_connected) + { + return true; + } + + GError * error = NULL; + + // Connect to the discovered LE peripheral asynchronously. + GVariant * const ret = + g_dbus_proxy_call_sync(device, + "Connect", + NULL, // parameters + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "%s.Connect() call failed: %s", + BLUEZ_DEVICE_INTERFACE, + error->message); + + g_error_free(error); + + return false; + } + + g_variant_unref(ret); + + return true; +} + +bool CACentralConnectToAll(CALEContext * context) +{ + bool connected = true; + + ca_mutex_lock(context->lock); + + // Connect to the LE peripherals, if we're not already connected. + g_list_foreach(context->devices, + CACentralConnectToDevice, + &connected); + + ca_mutex_unlock(context->lock); + + return connected; +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/central.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/central.h new file mode 100644 index 0000000..72a8db6 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/central.h @@ -0,0 +1,88 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_CENTRAL_H +#define CA_BLE_LINUX_CENTRAL_H + +#include "context.h" + +#include "cacommon.h" + + +/** + * Initialize and start a Linux BLE "central". + * + * Initialize all Linux BLE "central" state (i.e. a global + * @c CACentralContext instance), as well as start + * discovery of OIC GATT transport service capable peripherals. + * + * @param[in] context Context containing BlueZ adapter information. + * + * @return @c CA_STATUS_OK on success. + */ +CAResult_t CACentralStart(CALEContext * context); + +/** + * Stop the Linux BLE "central". + * + * @param[in] context Context containing BlueZ adapter information. + * + * @return @c CA_STATUS_OK on success. + */ +CAResult_t CACentralStop(CALEContext * context); + +/** + * Start discovery of OIC Transport Profile capable LE peripherals. + * + * @param[in] context Context containing BlueZ adapter information. + * + * @return @c CA_STATUS_OK on success. + */ +CAResult_t CACentralStartDiscovery(CALEContext * context); + +/** + * Stop discovery of OIC Transport Profile capable LE peripherals. + * + * @param[in] context Context containing BlueZ adapter information. + * + * @return @c CA_STATUS_OK on success. + */ +CAResult_t CACentralStopDiscovery(CALEContext * context); + +/** + * Connect to the LE peripheral pointed by @a device. + * + * @param[in] device Proxy to the BlueZ @c org.bluez.Device1 object + * through which the connection to the LE peripheral + * will be established. + * + * @return @c true on success, @c false otherwise. + */ +bool CACentralConnect(GDBusProxy * device); + +/** + * Connect to all discovered LE peripherals. + * + * @param[in] context Context containing BlueZ adapter information. + * + * @return @c true on success, @c false otherwise. + */ +bool CACentralConnectToAll(CALEContext * context); + + +#endif /* CA_BLE_LINUX_CENTRAL_H */ diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.c new file mode 100644 index 0000000..1b05ed9 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.c @@ -0,0 +1,622 @@ +/***************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "characteristic.h" +#include "service.h" +#include "gatt_dbus.h" +#include "utils.h" +#include "bluez.h" +#include "server.h" + +#include "oic_malloc.h" +#include "oic_string.h" +#include "logger.h" +#include "cagattservice.h" +#include "caremotehandler.h" + +#include +#include +#include + + +// Logging tag. +static char const TAG[] = "BLE_CHARACTERISTIC"; + +// --------------------------------------------------------------------- +// GATT Request Handling +// --------------------------------------------------------------------- +/** + * Handle @c org.bluez.GattCharacterstic1.WriteValue() method call. + * + * This handler is triggered when the + * @c org.bluez.GattCharacterstic1.WriteValue() method is called by a + * client on the BlueZ-based OIC GATT request characteristic. In + * particular, IoTivity request data is sent by the GATT client to the + * GATT server through this call. The server will retrieve the data + * that was sent from the @a value argument. Reassembly of any + * request data fragments will begin here. + * + * @param[in] object @c org.bluez.GattCharacteristic1 skeleton + * object associated with this method call. + * @param[in] invocation D-Bus method invocation related object used + * when sending results/errors asynchronously. + * @param[in] value The @c GVariant containing the byte array + * (ay) with th request data inside. + * @param[in] user_data Pointer to request + * @c CAGattCharacteristic object. + * + * @return @c TRUE to indicate that + * @c org.bluez.Characteristic.WriteValue() method is + * implemented. + */ +static gboolean CAGattCharacteristicHandleWriteValue( + GattCharacteristic1 * object, + GDBusMethodInvocation * invocation, + GVariant * value, + gpointer user_data) +{ + /* + This method is only trigged in a GATT server when receiving + data sent by a GATT client, i.e. the client wrote a value to + the server, and the server is handling that write request. + */ + + gsize len = 0; + gconstpointer const data = + g_variant_get_fixed_array(value, + &len, + 1); // sizeof(guchar) == 1 + + CAGattCharacteristic * const c = user_data; + + if (CAGattRecv(&c->recv_info, data, (uint32_t) len)) + { + gatt_characteristic1_complete_write_value(object, invocation); + } + else + { + g_dbus_method_invocation_return_dbus_error( + invocation, + "org.bluez.Error.Failed", + "Error when handling GATT request data fragment"); + } + + return TRUE; +} + +// --------------------------------------------------------------------- +// GATT Response Handling +// --------------------------------------------------------------------- +/** + * Make the peer address corresponding to the given GATT + * characteristic. + * + * @param[in] c Information about GATT characteristic for which the + * peer (client) @c CAEndpoint_t object is being + * created. + * + * @return @c String containing an encoded address associated with the + * peer connected to the peripheral on which the characteristic + * implementation resides, or @c NULL on error. + */ +static char * CAGattCharacteristicMakePeerAddress( + CAGattCharacteristic * c) +{ + assert(c != NULL); + + /* + Length of stringified pointer in hexadecimal format, plus one + for null terminator. + */ + static size_t const PSEUDO_ADDR_LEN = sizeof(intptr_t) / 4 + 1; + + assert(MAX_ADDR_STR_SIZE_CA > PSEUDO_ADDR_LEN); + + /* + Since there is no direct way to obtain the client endpoint + associated with the GATT characterstics on the server side, + embed a stringified pointer to the response charactertistic of + the form "&ABCDEF01" is the CAEndpoint_t instead. This works + since: + 1) only one LE central is ever connected to an LE peripheral + 2) the CA layer doesn't directly interpret the address + */ + char * const addr = OICMalloc(PSEUDO_ADDR_LEN); + int const count = snprintf(addr, + PSEUDO_ADDR_LEN, + "&%" PRIxPTR, + (uintptr_t) c); + + if (count >= (int) PSEUDO_ADDR_LEN) + { + OIC_LOG(ERROR, + TAG, + "Error creating peer address on server side."); + + return NULL; + } + + return addr; +} + +/** + * Handle @c org.bluez.GattCharacterstic1.StartNotify() method call. + * + * This handler is triggered when the + * @c org.bluez.GattCharacterstic1.StartNotify() method is called by a + * client on the BlueZ-based OIC GATT response characteristic. It + * sets the @c org.bluez.GattCharacterstic1.Notifying property to + * @c TRUE, and enables responses from the server. + * + * @note This handler is not available in the OIC GATT request + * characteristic implementation. + * + * @param[in] object @c org.bluez.GattCharacteristic1 skeleton + * object associated with this method call. + * @param[in] invocation D-Bus method invocation related object used + * when sending results/errors asynchronously. + * @param[in] user_data Pointer to the response + * @c CAGattCharacteristic object. + * + * @return @c TRUE to indicate that + * @c org.bluez.Characteristic.StartNotify() method is + * implemented. + */ +static gboolean CAGattCharacteristicHandleStartNotify( + GattCharacteristic1 * object, + GDBusMethodInvocation * invocation, + gpointer user_data) +{ + /** + * Only allow the client to start notifications once. + * + * @todo Does BlueZ already prevent redundant calls to + * @c org.bluez.GattCharacteristic1.StartNotify()? + */ + if (gatt_characteristic1_get_notifying(object)) + { + g_dbus_method_invocation_return_dbus_error( + invocation, + "org.bluez.Error.Failed", + "Notifications are already enabled."); + + return TRUE; + } + + // Retrieve the response characteristic information. + CAGattCharacteristic * const characteristic = user_data; + + char * const peer = + CAGattCharacteristicMakePeerAddress(characteristic); + + if (peer == NULL) + { + g_dbus_method_invocation_return_dbus_error( + invocation, + "org.bluez.Error.Failed", + "Error creating peer endpoint information"); + + return TRUE; + } + + /* + Create an entry in the endpoint-to-characteristic map so that + responses may be sent to the GATT client through the OIC GATT + response characteristic through this BLE adapter's + CAAdapterSendUnicastData() implementation. + */ + CALEContext * const context = characteristic->context; + + ca_mutex_lock(context->lock); + +#if GLIB_CHECK_VERSION(2,40,0) + /* + GLib hash table functions started returning a boolean result in + version 2.40.x. + */ + bool const inserted = +#endif + g_hash_table_insert(context->characteristic_map, + peer, + characteristic); + + ca_mutex_unlock(context->lock); + +#if GLIB_CHECK_VERSION(2,40,0) + if (!inserted) + { + g_dbus_method_invocation_return_dbus_error( + invocation, + "org.bluez.Error.Failed", + "Unable to set response endpoint."); + + OICFree(peer); + + return TRUE; + } +#endif + + /** + * @todo Do we need to explicitly emit the @c GObject @c notify or + * @c org.freedesktop.Dbus.Properties.PropertiesChanged + * signal here? + */ + gatt_characteristic1_set_notifying(object, TRUE); + + /* + Set the client endpoint field in the request characteristic so + that it may pass the appropriate endpoint object up the stack + through the CA request/response callback once a request has been + completely received and reassembled. + */ + CAGattRecvInfo * const recv_info = + &characteristic->service->request_characteristic.recv_info; + + recv_info->peer = peer; + + ca_mutex_lock(context->lock); + recv_info->on_packet_received = context->on_server_received_data; + recv_info->context = context; + ca_mutex_unlock(context->lock); + + gatt_characteristic1_complete_start_notify(object, invocation); + + return TRUE; +} + +/** + * Handle @c org.bluez.GattCharacterstic1.StopNotify() method call. + * + * This handler is triggered when the + * @c org.bluez.GattCharacterstic1.StopNotify() method is called by a + * client on the BlueZ-based OIC GATT response characteristic. It + * sets the @c org.bluez.GattCharacterstic1.Notifying property to + * @c FALSE, and disables responses from the server. + * + * @param[in] object @c org.bluez.GattCharacteristic1 skeleton + * object associated with this method call. + * @param[in] invocation D-Bus method invocation related object used + * when sending results/errors asynchronously. + * @param[in] user_data Pointer to the response + * @c CAGattCharacteristic object. + * + * @return @c TRUE to indicate that + * @c org.bluez.Characteristic.StopNotify() method is + * implemented. + */ +static gboolean CAGattCharacteristicHandleStopNotify( + GattCharacteristic1 * object, + GDBusMethodInvocation * invocation, + gpointer user_data) +{ + assert(user_data != NULL); + + /** + * @todo Does BlueZ already prevent redundant calls to + * @c org.bluez.GattCharacteristic1.StopNotify()? + */ + if (!gatt_characteristic1_get_notifying(object)) + { + g_dbus_method_invocation_return_dbus_error( + invocation, + "org.bluez.Error.Failed", + "Notifications were not previously enabled."); + + return TRUE; + } + + CAGattCharacteristic * const characteristic = user_data; + + // Clear the client endpoint from the request characteristic. + CAGattRecvInfo * const recv_info = + &characteristic->service->request_characteristic.recv_info; + + /* + Remove the appropriate entry from the endpoint-to-characteristic + map so that attempts to send a response through it will fail. + */ + CALEContext * const context = characteristic->context; + ca_mutex_lock(context->lock); + + bool const removed = + g_hash_table_remove(context->characteristic_map, recv_info->peer); + + ca_mutex_unlock(context->lock); + + CAGattRecvInfoDestroy(recv_info); + + /** + * @todo Do we need to explicitly emit the @c GObject @c notify or + * @c org.freedesktop.Dbus.Properties.PropertiesChanged + * signal here? + */ + gatt_characteristic1_set_notifying(object, FALSE); + + if (removed) + { + gatt_characteristic1_complete_stop_notify(object, invocation); + } + else + { + g_dbus_method_invocation_return_dbus_error( + invocation, + "org.bluez.Error.Failed", + "Error removing peer address information"); + } + + return TRUE; +} + +// --------------------------------------------------------------------- +// GATT Characteristic Lifecyle +// --------------------------------------------------------------------- + +/** + * Initialize GATT characteristic fields. + * + * This function initializes the @c CAGattCharacteristic object fields. + * + * @param[out] c GATT characteristic information to + * be initialized. + * @param[in] context Object containing the D-Bus + * connection to the bus on which the + * characteristic will be exported, as + * well as the list of connected + * devices. + * @param[in] s Information about GATT service + * to which the characteristic + * belongs. + * @param[in] characteristic_path @c GattCharacteristic1 object + * path. + * @param[in] uuid GATT characteristic UUID. + * @param[in] flag GATT characteristic property flag, + * i.e. @c "write-without-response" + * for the request characteristic, and + * @c "notify" for the response + * characteristic. + * + * @note This function does not allocate the @a characteristic object + * itself. The caller is responsible for allocating that + * memory. + * + * @todo Too many parameters. Perhaps pass in a pointer to a struct + * instead. + */ +static bool CAGattCharacteristicInitialize( + CAGattCharacteristic * c, + CALEContext * context, + CAGattService * s, + char const * characteristic_path, + char const * uuid, + char const * flag) +{ + // Path of the form /org/iotivity/gatt/hci0/service0/char0. + c->object_path = + g_strdup_printf("%s/%s", s->object_path, characteristic_path); + + assert(g_variant_is_object_path(c->object_path)); + + c->context = context; + c->service = s; + + c->characteristic = gatt_characteristic1_skeleton_new(); + + gatt_characteristic1_set_uuid(c->characteristic, uuid); + gatt_characteristic1_set_service(c->characteristic, s->object_path); + gatt_characteristic1_set_notifying(c->characteristic, FALSE); + + char const * flags[] = { flag, NULL }; + gatt_characteristic1_set_flags(c->characteristic, flags); + + CAGattRecvInfoInitialize(&c->recv_info); + + // Export the characteristic interface on the bus. + GError * error = NULL; + if (!g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(c->characteristic), + context->connection, + c->object_path, + &error)) + { + CAGattCharacteristicDestroy(c); + + OIC_LOG_V(ERROR, + TAG, + "Unable to export D-Bus GATT characteristic " + "interface: %s", + error->message); + + g_error_free(error); + + return false; + } + + return true; +} + +// ------------------------------------------------------------ + +bool CAGattRequestCharacteristicInitialize(struct CAGattService * s, + CALEContext * context) +{ + CAGattCharacteristic * const c = &s->request_characteristic; + + if (!CAGattCharacteristicInitialize(c, + context, + s, + CA_GATT_REQUEST_CHRC_PATH, + CA_GATT_REQUEST_CHRC_UUID, + "write-without-response")) + { + return false; + } + + if (!CAGattRequestDescriptorInitialize(s, context->connection)) + { + CAGattCharacteristicDestroy(c); + return false; + } + + /* + The descriptor object path is not fixed at compile-time. + Retrieve the object path that was set at run-time. + */ + char const * descriptor_paths[] = { + c->descriptor.object_path, + NULL + }; + + gatt_characteristic1_set_descriptors(c->characteristic, + descriptor_paths); + + // The request characteristic only handles writes. + g_signal_connect(c->characteristic, + "handle-write-value", + G_CALLBACK(CAGattCharacteristicHandleWriteValue), + c); + + return true; +} + +// ------------------------------------------------------------ + +bool CAGattResponseCharacteristicInitialize(struct CAGattService * s, + CALEContext * context) +{ + CAGattCharacteristic * const c = &s->response_characteristic; + + if (!CAGattCharacteristicInitialize(c, + context, + s, + CA_GATT_RESPONSE_CHRC_PATH, + CA_GATT_RESPONSE_CHRC_UUID, + "notify")) + { + return false; + } + + c->service = s; + + if (!CAGattResponseDescriptorInitialize(s, context->connection)) + { + CAGattCharacteristicDestroy(c); + return false; + } + + /* + The descriptor object path is not fixed at compile-time. + Retrieve the object path that was set at run-time. + + Note that we don't explicitly add the Client Characteristic + Configuration Descriptor for the response characteristic since + that is done by BlueZ when the "notify" property is set. + Furthermore, a client requests notifications by calling the + org.bluez.GattCharacteristic1.StartNotify() method. + Consequently, there is no need for the client to explicitly + enable notifications by writing to the client characteristic + configuration descriptor. + */ + char const * descriptor_paths[] = { + c->descriptor.object_path, + NULL + }; + + gatt_characteristic1_set_descriptors(c->characteristic, + descriptor_paths); + + // The response characteristic only handles notifications. + g_signal_connect( + c->characteristic, + "handle-start-notify", + G_CALLBACK(CAGattCharacteristicHandleStartNotify), + c); + + g_signal_connect( + c->characteristic, + "handle-stop-notify", + G_CALLBACK(CAGattCharacteristicHandleStopNotify), + c); + + return true; +} + +void CAGattCharacteristicDestroy(CAGattCharacteristic * c) +{ + assert(c != NULL); // As designed, c is always non-NULL. + + CAGattRecvInfoDestroy(&c->recv_info); + + CAGattDescriptorDestroy(&c->descriptor); + + g_clear_object(&c->characteristic); + + g_free(c->object_path); + c->object_path = NULL; + + c->service = NULL; + c->context = NULL; +} + +// --------------------------------------------------------------------- +// GATT Characteristic Properties +// --------------------------------------------------------------------- + + +GVariant * CAGattCharacteristicGetProperties( + GattCharacteristic1 * characteristic) +{ + /** + * Create a variant containing the @c GattCharacteristic1 + * properties, of the form @c a{sa{sv}}. + * + * @note We don't care about the "Value" property here since it is + * automatically made available by BlueZ on the client + * side. + * + * @todo Should we care about "Notifying" property here? + */ + + /* + Populate the property table, and create the variant to be + embedded in the results of the + org.freedesktop.Dbus.ObjectManager.GetManagedObjects() method + call. + */ + CADBusSkeletonProperty const properties[] = { + { "UUID", + g_variant_new_string( + gatt_characteristic1_get_uuid(characteristic)) }, + { "Service", + g_variant_new_object_path( + gatt_characteristic1_get_service(characteristic)) }, + { "Flags", + g_variant_new_strv( + gatt_characteristic1_get_flags(characteristic), + -1) }, + { "Descriptors", + g_variant_new_objv( + gatt_characteristic1_get_descriptors(characteristic), + -1) } + }; + + return + CAMakePropertyDictionary( + BLUEZ_GATT_CHARACTERISTIC_INTERFACE, + properties, + sizeof(properties) / sizeof(properties[0])); +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.h new file mode 100644 index 0000000..f7a4a37 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/characteristic.h @@ -0,0 +1,146 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_CHARACTERISTIC_H +#define CA_BLE_LINUX_CHARACTERISTIC_H + +#include "bluez-glue.h" +#include "descriptor.h" +#include "recv.h" +#include "context.h" + + +/** + * OIC GATT Characteristic Information + * + * OIC GATT characteristics contain one user description descriptor. + * + * @note The response characteristic should also have a client + * characteristic configuration descriptor. However, BlueZ + * implicitly adds that descriptor to the characteristic when + * the characteristic "notify" property is set. There is no + * need to explicitly add that descriptor. + */ +typedef struct CAGattCharacteristic +{ + + /** + * Object containing the D-Bus connection list of connected + * devices. + */ + CALEContext * context; + + /** + * IoTivity OIC GATT service information. + * + * @note This is currently only used by the response + * characteristic to gain access to the request + * characteristic @c client endpoint field. + * + * @todo It seems somewhat wasteful have a field available to both + * response and request characteristics, but used by only + * one of them. + */ + struct CAGattService * service; + + /// D-Bus object path for the GattCharacteristic1 object. + char * object_path; + + /// OIC GATT service D-Bus interface skeleton object. + GattCharacteristic1 * characteristic; + + /// OIC GATT user description descriptor information. + CAGattDescriptor descriptor; + + /** + * Information used to keep track of received data fragments. + * + * @note This is only used by the OIC GATT request characteristic + * skeleton implementation. It is not needed by the + * response characteristic skeleton. + * + * @todo It seems somewhat wasteful have a field available to both + * response and request characteristics, but used by only + * one of them. + */ + CAGattRecvInfo recv_info; + +} CAGattCharacteristic; + +/** + * Initialize GATT request characteristic fields. + * + * This function initializes the request @c CAGattCharacteristic + * object fields. + * + * @param[in,out] s Information about GATT service to which the + * characteristic belongs. + * @param[in] context Object containing the D-Bus connection to + * the bus on which the characteristic will be + * exported, as well as the list of connected + * devices. + * + * @return @c true on success, @c false otherwise. + */ +bool CAGattRequestCharacteristicInitialize(struct CAGattService * s, + CALEContext * context); + +/** + * Initialize GATT response characteristic fields. + * + * This function initializes the response @c CAGattCharacteristic + * object fields. + * + * @param[in,out] s Information about GATT service to which the + * characteristic belongs. + * @param[in] context Object containing the D-Bus connection to + * the bus on which the characteristic will be + * exported, as well as the list of connected + * devices. + * + * @return @c true on success, @c false otherwise. + */ +bool CAGattResponseCharacteristicInitialize(struct CAGattService * s, + CALEContext * context); + +/** + * Destroy GATT characteristic fields. + * + * This function finalizes the @c CAGattCharacteristic object fields. + * + * @param[in] characteristic GATT characteristic information to be + * finalized. + */ +void CAGattCharacteristicDestroy(CAGattCharacteristic * characteristic); + +/** + * Get all characteristic properties. + * + * @param[in] characteristic The D-Bus skeleton object from which the + * characteristic properties will be extracted. + * @return A variant of the form a{sa{sv}}, suitable for use in the + * result of the + * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() + * method provided by the IoTivity the + * @c org.bluez.GattService1 implementation. + */ +GVariant * CAGattCharacteristicGetProperties( + GattCharacteristic1 * characteristic); + + +#endif // CA_BLE_LINUX_CHARACTERISTIC_H diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/client.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/client.c new file mode 100644 index 0000000..6d5adba --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/client.c @@ -0,0 +1,274 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "client.h" +#include "recv.h" +#include "context.h" +#include "bluez.h" +#include "utils.h" + +#include "cafragmentation.h" +#include "logger.h" +#include "oic_malloc.h" +#include "oic_string.h" + +#include + +#include +#include + + +// Logging tag. +static char const TAG[] = "BLE_CLIENT"; + +// --------------------------------------------------------------------- +// GATT Client Set-up +// --------------------------------------------------------------------- +static bool CAGattClientServiceFilter(GDBusProxy * service) +{ + /* + On the client side, we only care about the GATT services on + remote devices. Ignore the locally created ones by checking for + the existence of the org.bluez.GattService1.Device property in + the service proxy. GATT services on remote devices will have + that property. + */ + GVariant * const remote_device = + g_dbus_proxy_get_cached_property(service, "Device"); + + if (remote_device == NULL) + { + return false; + } + + /* + org.bluez.GattService1.Device property exists, meaning the + GATT service was advertised from a remote object. + */ + g_object_unref(remote_device); + return true; +} + +bool CAGattClientsInitialize(CALEContext * context) +{ + /* + Create a proxies to the org.bluez.GattService1 D-Bus objects that + will later be used to send requests and receive responses on the + client side. + */ + GList * services = NULL; + bool success = + CAGetBlueZManagedObjectProxies(&services, + BLUEZ_GATT_SERVICE_INTERFACE, + context, + CAGattClientServiceFilter); + + /** + * @todo Is this really an error? + */ + if (!success) + { + return success; + } + + /* + Map Bluetooth MAC address to OIC Transport Profile + characteristics. + */ + GHashTable * const characteristic_map = + g_hash_table_new_full(g_str_hash, + g_str_equal, + OICFree, + g_object_unref); + + char const * const address = NULL; // OICMalloc(...); + GDBusProxy * const client = NULL; + +#if GLIB_CHECK_VERSION(2,40,0) + /* + GLib hash table functions started returning a boolean result in + version 2.40.x. + */ + success = +#endif + g_hash_table_insert(characteristic_map, + OICStrdup(address), + client); + + // An empty services list is NULL. + if (success && services != NULL) + { + ca_mutex_lock(context->lock); + context->characteristic_map = characteristic_map; + ca_mutex_unlock(context->lock); + } + + return success; +} + +bool CAGattClientsDestroy(CALEContext * context) +{ + /* g_hash_table_destroy(...); */ // FIXME + return false; +} + +// --------------------------------------------------------------------- +// GATT Request Send +// --------------------------------------------------------------------- +/** + * Send data to the GATT server through the given request + * @a characteristic proxy. + * + * @param[in] characteristic The D-Bus proxy of the request + * characteristic through which the + * @c WriteValue() method will be invoked. + * @param[in] data The byte array to be sent. + * @param[in] length The number of elements in the byte + * array. + */ +static bool CAGattClientSendRequestData(GDBusProxy * characteristic, + CALEContext * context, + char const * data, + size_t length) +{ + assert(context != NULL); + + GVariant * const value = + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + data, + length, + 1); // sizeof(data[0]) == 1 + + GError * error = NULL; + + GVariant * const ret = + g_dbus_proxy_call_sync(characteristic, + "WriteValue", + value, // parameters + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "[%p] WriteValue() call failed: %s", + characteristic, + error->message); + + g_error_free(error); + + ca_mutex_lock(context->lock); + + if (context->on_client_error != NULL) + { + /* + At this point endpoint and send data information is + available. + */ + context->on_client_error(NULL, // endpoint + data, + length, + CA_STATUS_FAILED); + } + + ca_mutex_unlock(context->lock); + + return false; + } + + g_variant_unref(ret); + + return true; +} + +CAResult_t CAGattClientSendData(void const * method_info, + void const * data, + size_t length) +{ + assert(method_info != NULL); + + CAGattRequestInfo const * const info = method_info; + + GDBusProxy * const characteristic = + G_DBUS_PROXY(info->characteristic_info); + + return CAGattClientSendRequestData(characteristic, + info->context, + (char const *) data, + length); +} + +CAResult_t CAGattClientSendDataToAll(void const * method_info, + void const * data, + size_t length) +{ + assert(method_info != NULL); + + CAResult_t result = CA_STATUS_OK; + + CAGattRequestInfo const * const info = method_info; + + for (GList const * l = info->characteristic_info; + l != NULL && result == CA_STATUS_OK; + l = l->next) + { + GDBusProxy * const characteristic = G_DBUS_PROXY(l->data); + + result = CAGattClientSendRequestData(characteristic, + info->context, + (char const *) data, + length); + } + + return result; +} + +// --------------------------------------------------------------------- +// GATT Response Receive +// --------------------------------------------------------------------- +void CAGattReceiveResponse(GDBusConnection * connection, + char const * sender_name, + char const * object_path, + char const * interface_name, + char const * signal_name, + GVariant * parameters, + gpointer user_data) +{ + /* + This handler is only trigged in a GATT client when receiving + data sent by a GATT server through a notification, e.g. such as + when a GATT server sent a response. + */ + gsize fragment_len = 0; + gconstpointer const fragment = + g_variant_get_fixed_array(parameters, + &fragment_len, + 1); // sizeof(guchar) == 1 + + CAGattRecvInfo * const info = user_data; + + if (CAGattRecv(info, fragment, fragment_len)) + { + } + else + { + } +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/client.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/client.h new file mode 100644 index 0000000..46d260c --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/client.h @@ -0,0 +1,91 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_CLIENT_H +#define CA_BLE_LINUX_CLIENT_H + +#include "context.h" + + +/** + * Information needed to when sending a request through a GATT + * client. + */ +typedef struct _CAGattRequestInfo +{ + /** + * Proxy or list of proxies to @c org.bluez.GattCharacteristic1 + * object(s) through which request data will be sent to the GATT + * server. + * + * In the case of a unicast-style send, @c info will be a + * @c GDBusProxy* to an @c org.bluez.GattCharacteristic1 object. + * For a multicast-style send, @c info will be a * @c GList* of + * @c GDBusProxy* to @c GattCharacterstic1 objects on all GATT + * servers to which a connection exists. + */ + void * const characteristic_info; + + /** + * Context containing additional information that may be needed + * when sending a request. + */ + CALEContext * const context; + +} CAGattRequestInfo; + +/** + * Send request data through a single user-specified BLE connection. + * + * @param[in] method_info Information necessary to send request. + * @param[in] data Octet array of request data to be sent. + * @param[in] length Length of the @a data octet array. + * + * @see @c CAGattSendMethod() for further details. + */ +bool CAGattSendRequest(void const * method_info, + void const * data, + size_t length); + +// --------------------------------------------------------------- +// Multicast-style Request Send +// --------------------------------------------------------------- +/** + * Send request data through all BLE connections. + * + * Send the @a data to the GATT server found in all discovered LE + * peripherals. + * + * @param[in] method_info Information necessary to send request. + * @param[in] data Octet array of request data to be sent. + * @param[in] length Length of the @a data octet array. + + * @note Since a multicast-like operation is being performed, an + * assumption is made that a GATT client is sending data to a + * server. It makes no sense to multicast a response from a + * single GATT server to multiple GATT clients in IoTivity's + * case. + * + * @return @c CA_STATUS_OK on success, @c CA_STATUS_FAILED otherwise. + */ +CAResult_t CAGattClientSendDataToAll(void const * method_info, + void const * data, + size_t length); + + +#endif /* CA_BLE_LINUX_CLIENT_H */ diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/context.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/context.h new file mode 100644 index 0000000..ce1ec01 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/context.h @@ -0,0 +1,196 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_CONTEXT_H +#define CA_BLE_LINUX_CONTEXT_H + +#include "caadapterinterface.h" +#include "camutex.h" +#include "cathreadpool.h" +#include "caleinterface.h" + +#include + + +/** + * @internal + * + * BLE Linux adapter base context. + */ +typedef struct _CALEContext +{ + /// Connection to the D-Bus system bus. + GDBusConnection * connection; + + /** + * Proxy to the BlueZ D-Bus object that implements the + * "org.freedesktop.DBus.ObjectManager" interface. + * + * @todo There's probably no need to keep this around after we've + * retrieved the managed objects the first time around since + * we can rely on signals to alert us to any subsequent + * changes. + */ + GDBusObjectManager * object_manager; + + /** + * List of @c GDBusObject objects obtained from the BlueZ + * @c ObjectManager. + * + * @note This list will be updated later on as needed if changes + * in the BlueZ ObjectManager are detected. + */ + GList * objects; + + /** + * BlueZ adapter list. + * + * List of @c GDBusProxy objects for all BlueZ adapters (i.e. + * @c org.bluez.Adapter1). More than one adapter can exist if + * multiple Bluetooth hardware interfaces are detected by BlueZ. + */ + GList * adapters; + + /** + * BlueZ device list. + * + * List of @c GDBusProxy objects for all BlueZ devices (i.e. + * @c org.bluez.Device1), such as those that matched the discovery + * criteria. + */ + GList * devices; + + /** + * Bluetooth MAC address to GATT characteristic map. + * + * Hash table that maps Bluetooth MAC address to a OIC Transport + * Profile GATT characteristic. The key is a string containing + * the peer Bluetooth adapter MAC address. The value is an + * interface proxy (@c GDBusProxy) to an + * @c org.bluez.GattCharacteristic1 object. + * + * On the client side, this maps a Bluetooth peripheral MAC + * address to the corresponding request characteristic proxy. On + * the server side, this maps Bluetooth central MAC address to the + * corresponding response characteristic proxy. + * + * @note On the server side a map is overkill since only one + * client is ever connected to the server. No? + * + * @todo We may want to have a seperate server-side map to reduce + * contention on this map. + */ + GHashTable * characteristic_map; + + /** + * GATT characteristics to Bluetooth MAC address map. + * + * Hash table that maps OIC Transport Profile GATT characteristic + * to a Bluetooth MAC address. The key is an interface proxy + * (@c GDBusProxy) to an @c org.bluez.GattCharacteristic1 object. + * The value is a pointer to the peer @c CAEndpoint_t object. + * + * On the client side, this maps a response characteristic to the + * corresponding MAC address. On the server side, this maps + * request characteristic to the corresponding MAC address. + * + * @note On the server side a map is overkill since only one + * client is ever connected to the server. No? + * + * @todo We may want to have a seperate server-side map to reduce + * contention on this map. + */ + GHashTable * address_map; + + /** + * D-Bus signal subscription identifiers. + * + * The Linux BLE transport implementation subscribes to three + * D-Bus signals: + * + * @li @c org.freedesktop.DBus.ObjectManager.InterfacesAdded + * @li @c org.freedesktop.DBus.ObjectManager.InterfacesRemoved + * @li @c org.freedesktop.DBus.Properties.PropertiesChanged + * @li @c org.bluez.Adapter1.PropertyChanged + * + * These subscription identifiers are only used when unsubscribing + * from the signals when stopping the LE transport. + * + * @todo Verify if we need the two property related signals at + * this level. + */ + //@{ + guint interfaces_added_sub_id; + guint interfaces_removed_sub_id; + guint properties_changed_sub_id; + guint property_changed_sub_id; + //@} + + /// Glib event loop that drives D-Bus signal handling. + GMainLoop * event_loop; + + /** + * Callback invoked upon change in local Bluetooth adapter state. + */ + CALEDeviceStateChangedCallback on_device_state_changed; + + /// Callback invoked upon server receiving request data. + CABLEDataReceivedCallback on_server_received_data; + + /// Callback invoked upon client receiving response data. + CABLEDataReceivedCallback on_client_received_data; + + /** + * Handle to thread pool to which client side tasks will be + * added. + */ + ca_thread_pool_t client_thread_pool; + + /** + * Handle to thread pool to which server side tasks will be + * added. + */ + ca_thread_pool_t server_thread_pool; + + /// Callback invoked when reporting a client side error. + CABLEErrorHandleCallback on_client_error; + + /// Callback invoked when reporting a server side error. + CABLEErrorHandleCallback on_server_error; + + /// Mutex used to synchronize access to context fields. + ca_mutex lock; + + /** + * BlueZ adapter list initialization condition variable. + * + * This condition variable is used to prevent the BLE adapter + * "start" from completing until the thread performing BlueZ + * adapter query completes. Initialization is performed in the + * same thread that will run the event loop. The condition + * variable is also used to wait for peripheral devices to be + * discovered. + * + * @see @c GMainLoop documentation for further details. + */ + ca_cond condition; + +} CALEContext; + + +#endif /* CA_BLE_LINUX_CONTEXT_H */ diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.c new file mode 100644 index 0000000..190d192 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.c @@ -0,0 +1,225 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "descriptor.h" +#include "service.h" +#include "gatt_dbus.h" +#include "utils.h" +#include "bluez.h" + +#include "logger.h" +#include "cagattservice.h" + +#include + + +// Logging tag. +static char const TAG[] = "BLE_DESCRIPTOR"; + +/** + * Implementation of the @c org.bluez.GattDescriptor1.ReadValue() + * method. + * + * This function is implemented as a GDBus signal handler that returns + * a byte array containing the @c Value property of the GATT + * descriptor. + * + * @param[in] object @c org.bluez.GattDescriptoror1 skeleton + * object associated with this method call. + * @param[in] invocation D-Bus method invocation related object used + * when sending results/errors asynchronously. + * @param[in] user_data Unused. + * + * @return @c TRUE to indicate that + * @c org.bluez.Descriptor.ReadValue() + * method is implemented. + */ +static gboolean CAGattDescriptorReadValue( + GattDescriptor1 * object, + GDBusMethodInvocation * invocation, + gpointer user_data) +{ + /** + * @todo The @c GattDescriptor1 object still owns the returned + * variant when using the below call. Should be we use + * @c gatt_descriptor1_dup_value() instead? + */ + GVariant * const value = gatt_descriptor1_get_value(object); + + gatt_descriptor1_complete_read_value(object, invocation, value); + + return TRUE; +} + +/** + * Initialize GATT descriptor fields. + * + * This function initializes the @c CAGattDescriptor object fields. + * + * @param[out] c Information about GATT characteristic + * to which the descriptor belongs. + * @param[in] connection D-Bus connection to the bus on which + * the descriptor will be exported. + * @param[in] s Information about GATT service + * information to which the descriptor + * belongs. + * @param[in] descriptor_path @c GattDescriptor1 object path for the + * user description descriptor. + * @param[in] value User description descriptor value, + * e.g. @c "OIC Node Request" or + * @c "OIC Node Response". + * + * @note This function does not allocate the @a descriptor object + * itself. The caller is responsible for allocating that + * memory. + * + * @todo Too many parameters. Perhaps pass in a pointer to a struct + * instead. + */ +static bool CAGattDescriptorInitialize(CAGattCharacteristic * c, + GDBusConnection * connection, + CAGattService * s, + char const * descriptor_path, + char const * value) +{ + CAGattDescriptor * const d = &c->descriptor; + + // Path of the form /org/iotivity/gatt/hci0/service0/char0/desc0. + d->object_path = + g_strdup_printf("%s/%s", c->object_path, descriptor_path); + assert(g_variant_is_object_path(d->object_path)); + + d->descriptor = gatt_descriptor1_skeleton_new(); + + gatt_descriptor1_set_uuid( + d->descriptor, + CA_GATT_CHRC_USER_DESCRIPTION_DESC_UUID); + + gatt_descriptor1_set_characteristic(d->descriptor, + c->object_path); + + gatt_descriptor1_set_value (d->descriptor, + g_variant_new_bytestring(value)); + + // Readable, no encryption, no authorization. + static char const * flags[] = { "read", NULL }; + gatt_descriptor1_set_flags(d->descriptor, flags); + + /* + Connect the signal handler that implements the + orb.bluez.GattDescriptor1.ReadValue() method. + */ + g_signal_connect(d->descriptor, + "handle-read-value", + G_CALLBACK(CAGattDescriptorReadValue), + NULL); + + // Export the descriptor interface on the bus. + GError * error = NULL; + if (!g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(d->descriptor), + connection, + d->object_path, + &error)) + { + CAGattDescriptorDestroy(d); + + OIC_LOG_V(ERROR, + TAG, + "Unable to export D-Bus GATT descriptor " + "interface: %s", + error->message); + + g_error_free(error); + + return false; + } + + return true; +} + +bool CAGattRequestDescriptorInitialize(struct CAGattService * s, + GDBusConnection * connection) +{ + CAGattCharacteristic * const c = &s->request_characteristic; + + return CAGattDescriptorInitialize(c, + connection, + s, + CA_GATT_REQUEST_USER_DESC_PATH, + CA_GATT_REQUEST_USER_DESCRIPTION); +} + +bool CAGattResponseDescriptorInitialize(struct CAGattService * s, + GDBusConnection * connection) +{ + CAGattCharacteristic * const c = &s->response_characteristic; + + return CAGattDescriptorInitialize(c, + connection, + s, + CA_GATT_RESPONSE_USER_DESC_PATH, + CA_GATT_RESPONSE_USER_DESCRIPTION); +} + +void CAGattDescriptorDestroy(CAGattDescriptor * d) +{ + assert(d != NULL); // As designed, d is always non-NULL. + + g_clear_object(&d->descriptor); + + g_free(d->object_path); + d->object_path = NULL; +} + +GVariant * CAGattDescriptorGetProperties(GattDescriptor1 * descriptor) +{ + /** + * Create a variant containing the @c GattDescriptor1 properties, + * of the form @c a{sa{sv}}. + * + * @note We don't care about the "Value" property here since it is + * automatically made available by BlueZ on the client + * side. + */ + + /* + Populate the property table, and create the variant to be + embedded in the results of the + org.freedesktop.Dbus.ObjectManager.GetManagedObjects() method + call. + */ + CADBusSkeletonProperty const properties[] = { + { "UUID", + g_variant_new_string( + gatt_descriptor1_get_uuid(descriptor)) }, + { "Characteristic", + g_variant_new_object_path( + gatt_descriptor1_get_characteristic(descriptor)) }, + { "Flags", + g_variant_new_strv( + gatt_descriptor1_get_flags(descriptor), + -1) } + }; + + return + CAMakePropertyDictionary( + BLUEZ_GATT_DESCRIPTOR_INTERFACE, + properties, + sizeof(properties) / sizeof(properties[0])); +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.h new file mode 100644 index 0000000..8856eb4 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/descriptor.h @@ -0,0 +1,100 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_DESCRIPTOR_H +#define CA_BLE_LINUX_DESCRIPTOR_H + +#include "bluez-glue.h" + +#include + + +/** + * OIC GATT Descriptor Information + */ +typedef struct CAGattDescriptor +{ + /// D-Bus object path for the GattCharacteristic1 object. + char * object_path; + + /// OIC GATT service D-Bus interface skeleton object. + GattDescriptor1 * descriptor; + +} CAGattDescriptor; + +// Forward declarations. +struct CAGattService; + +/** + * Initialize GATT request descriptor fields. + * + * This function initializes the request @c CAGattDescriptor object + * fields. + * + * @param[in,out] s Information about GATT service to which the + * characteristic and descriptor belong. + * @param[in] connection D-Bus connection to the bus on which the + * descriptor will be exported. + * + * @return @c true on success, @c false otherwise. + */ +bool CAGattRequestDescriptorInitialize(struct CAGattService * s, + GDBusConnection * connection); + +/** + * Initialize GATT response descriptor fields. + * + * This function initializes the response @c CAGattDescriptor object + * fields. + * + * @param[in,out] s Information about GATT service to which the + * characteristic and descriptor belong. + * @param[in] connection D-Bus connection to the bus on which the + * descriptor will be exported. + * + * @return @c true on success, @c false otherwise. + */ +bool CAGattResponseDescriptorInitialize(struct CAGattService *s, + GDBusConnection * connection); + +/** + * Destroy GATT descriptor fields. + * + * This function finalizes the @c CAGattDescriptor object fields. + * + * @param[in] descriptor GATT characteristic information to be + * finalized. + */ +void CAGattDescriptorDestroy(CAGattDescriptor * descriptor); + +/** + * Get all descriptor properties. + * + * @param[in] descriptor The D-Bus skeleton object from which the + * descriptor properties will be extracted. + * + * @return A variant of the form a{sa{sv}}, suitable for use in the + * result of the + * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() + * method provided by the IoTivity the + * @c org.bluez.GattService1 implementation. + */ +GVariant * CAGattDescriptorGetProperties(GattDescriptor1 * descriptor); + + +#endif // CA_BLE_LINUX_DESCRIPTOR_H diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/gatt_dbus.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/gatt_dbus.h new file mode 100644 index 0000000..5d55a8a --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/gatt_dbus.h @@ -0,0 +1,89 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_GATT_DBUS_H +#define CA_BLE_LINUX_GATT_DBUS_H + + +/** + * @name BlueZ GATT Service D-Bus Object Paths + * + * The IoTivity BlueZ GATT Service hierarchy is the following: + * + * -> /org/iotivity/gatt/advertisement0 + * + * -> /org/iotivity/gatt//service0 + * | - OIC GATT Service + * | + * -> /org/iotivity/gatt//service0/char0 + * | | - OIC GATT Request Characteristic Value + * | | + * | -> /org/iotivity/gatt//service0/char0/desc0 + * | - OIC GATT Request User Description Descriptor + * | + * -> /org/iotivity/gatt//service0/char1 + * | - OIC GATT Response Characteristic Value + * | + * -> /org/iotivity/gatt//service0/char1/desc0 + * - OIC GATT Response User Description Descriptor + * + * where corresponds to the bluetooth hardware adapter with + * which the GATT service is being registered, e.g. "hci0". + * + * @note The OIC GATT Client Characterstic Configuration Descriptor is + * implicitly added to the response characteristic hierarchy by + * BlueZ since its "notify" property is set. + */ +//@{ + +/** + * Root object path of the GATT service hierarchy. + * + * The GATT service object manager (i.e. implementation of + * @c org.freedesktop.DBus.ObjectManager) is found at this object + * path. + */ +#define CA_GATT_SERVICE_ROOT_PATH "/org/iotivity/gatt" + +// ------------------------ + +#define CA_LE_ADVERTISEMENT_PATH "advertisement0" + +// ------------------------ + +/// GATT service object path basename. +#define CA_GATT_SERVICE_PATH "service0" + +// ------------------------ + +/// Request GATT characteristic object path basename. +#define CA_GATT_REQUEST_CHRC_PATH "char0" + +/// Request GATT user description descriptor object path basename. +#define CA_GATT_REQUEST_USER_DESC_PATH "desc0" + +// ------------------------ + +/// Response GATT characteristic object path basename. +#define CA_GATT_RESPONSE_CHRC_PATH "char1" + +/// Response GATT user description descriptor object path basename. +#define CA_GATT_RESPONSE_USER_DESC_PATH "desc0" +//@} + +#endif // CA_BLE_LINUX_GATT_DBUS_H diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/object_manager.xml b/resource/csdk/connectivity/src/bt_le_adapter/linux/object_manager.xml new file mode 100644 index 0000000..58666c9 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/object_manager.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/org.iotivity.gatt.service.conf.in b/resource/csdk/connectivity/src/bt_le_adapter/linux/org.iotivity.gatt.service.conf.in new file mode 100644 index 0000000..a3f0a23 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/org.iotivity.gatt.service.conf.in @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.c new file mode 100644 index 0000000..0054767 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.c @@ -0,0 +1,702 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "peripheral.h" +#include "utils.h" +#include "bluez.h" +#include "service.h" +#include "characteristic.h" +#include "descriptor.h" + +#include "oic_malloc.h" +#include "logger.h" + +#include +#include + + +#define MICROSECS_PER_SEC 1000000 + +// Logging tag. +static char const TAG[] = "BLE_PERIPHERAL"; + +static CAPeripheralContext g_context = { + .lock = NULL +}; + +static bool CAPeripheralCheckStarted() +{ + ca_mutex_lock(g_context.lock); + + bool const started = (g_context.base != NULL); + + ca_mutex_unlock(g_context.lock); + + /** + * @todo Fix potential TOCTOU race condition. A peripheral could + * have been started or stopped between the mutex unlock and + * boolean check. + */ + return started; +} + + +static bool CAPeripheralAdaptersFound(CALEContext * context) +{ + // Check if BlueZ detected bluetooth hardware adapters. + ca_mutex_lock(context->lock); + + bool const found = (context->adapters != NULL); + + ca_mutex_unlock(context->lock); + + if (!found) + { + OIC_LOG(WARNING, TAG, "No bluetooth hardware found."); + } + + return found; +} + +static void CAPeripheralDestroyGattServices(gpointer data) +{ + CAGattService * const service = data; + + CAGattServiceDestroy(service); + + OICFree(service); +} + +static GList * CAPeripheralInitializeGattServices(CALEContext * context) +{ + GList * result = NULL; + + /* + Create a proxies to the org.bluez.GattManager1 D-Bus objects that + will later be used to register the OIC GATT service. + */ + GList * gatt_managers = NULL; + if (!CAGetBlueZManagedObjectProxies(&gatt_managers, + BLUEZ_GATT_MANAGER_INTERFACE, + context, + NULL)) + return result; + + GList * gatt_services = NULL; + + for (GList * l = gatt_managers; l != NULL; ) + { + CAGattService * const service = OICCalloc(1, sizeof(*service)); + + if (service == NULL) + { + g_list_free_full(gatt_services, + CAPeripheralDestroyGattServices); + g_list_free_full(gatt_managers, g_object_unref); + return result; + } + + /* + Use the HCI device name (e.g. hci0) in GattManager1 object + path (e.g. /org/bluez/hci0) as a means to differentiate the + GATT service hierarchy for each GattManager1 object. + */ + GDBusProxy * const manager = G_DBUS_PROXY(l->data); + char const * const path = g_dbus_proxy_get_object_path(manager); + + /* + The return value will actually be a pointer to a substring + starting with the '/' before the HCI name. We add 1 later + on to skip that character. + */ + char const * const hci_name = strrchr(path, '/'); + + if (hci_name == NULL + || !CAGattServiceInitialize(service, context, hci_name + 1)) + { + g_list_free_full(gatt_services, + CAPeripheralDestroyGattServices); + g_list_free_full(gatt_managers, g_object_unref); + return result; + } + + service->gatt_manager = manager; + + /* + The GattManager1 proxies are now owned by the CAGattService + objects. + */ + GList * const tmp = l; + l = l->next; + gatt_managers = g_list_delete_link(gatt_managers, tmp); + + // Prepend for efficiency. + gatt_services = g_list_prepend(gatt_services, service); + } + + result = gatt_services; + + return result; +} + +static bool CAPeripheralRegisterGattServices( + CAPeripheralContext * context) +{ + assert(context != NULL); + + bool success = false; + + ca_mutex_lock(context->lock); + + for (GList * l = context->gatt_services; l != NULL; l = l->next) + { + CAGattService * const service = l->data; + + // Register the OIC service with the corresponding BlueZ Gatt + // Manager. + + /* + org.bluez.GattManager1.RegisterService() accepts two + parameters: the service object path, and an options + dictionary. No options are used so pass a NULL pointer to + reflect an empty dictionary. + */ + GVariant * const parameters = + g_variant_new("(oa{sv})", service->object_path, NULL); + + GError * error = NULL; + + GVariant * const ret = + g_dbus_proxy_call_sync( + service->gatt_manager, + "RegisterService", + parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "GATT service registration failed: %s", + error->message); + + g_error_free(error); + + success = false; + + break; + } + + g_variant_unref(ret); + } + + ca_mutex_unlock(context->lock); + + success = true; + + return success; +} + +static bool CAPeripheralRegisterAdvertisements( + CAPeripheralContext * context) +{ + bool success = false; + + /* + Register the OIC LE advertisement service with each BlueZ + LE Advertisement Manager. + */ + + ca_mutex_lock(context->lock); + + char const * const advertisement_path = + g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON( + context->advertisement.advertisement)); + + GList * managers = context->advertisement.managers; + + for (GList * l = managers; l != NULL; ) + { + GDBusProxy * const manager = G_DBUS_PROXY(l->data); + + /** + * @c org.bluez.LEAdvertisingManager1.RegisterService() + * accepts two parameters: the advertisement object path, and + * an options dictionary. No options are used so pass a NULL + * pointer to reflect an empty dictionary. + * + * @todo The parameters don't change between loop iterations. + * Ideally we should initialize this variable before + * the loop is executed, but g_dbus_proxy_call_sync() + * takes ownership of the variant. Is there anyway to + * create a non-floating GVariant and pass it to that + * function? + */ + GVariant * const parameters = + g_variant_new("(oa{sv})", advertisement_path, NULL); + + GError * error = NULL; + + GVariant * const ret = + g_dbus_proxy_call_sync( + manager, + "RegisterAdvertisement", + parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(WARNING, + TAG, + "LE advertisement registration on %s failed: %s", + g_dbus_proxy_get_object_path(manager), + error->message); + + g_error_free(error); + + // We can't use the LE advertising manager. Drop it. + g_object_unref(manager); + + GList * const tmp = l; + l = l->next; + managers = g_list_delete_link(managers, tmp); + + continue; + } + + g_variant_unref(ret); + + /* + Note that we can do this in the for-statement because of + the similar code in the error case above. + */ + l = l->next; + } + + // Use the updated list of managers. + context->advertisement.managers = managers; + + if (managers == NULL) // Empty + { + OIC_LOG(ERROR, + TAG, + "LE advertisment registration failed for all " + "Bluetooth adapters."); + } + else + { + + success = true; + } + + ca_mutex_unlock(context->lock); + + return success; +} + +static void CAPeripheralSetDiscoverable(gpointer data, + gpointer user_data, + gboolean discoverable) +{ + assert(data != NULL); + assert(user_data != NULL); + + GDBusProxy * const adapter = G_DBUS_PROXY(data); + CAResult_t * const result = (CAResult_t *) user_data; + *result = CA_STATUS_FAILED; + + /* + Make sure the adapter is powered on before making it + discoverable. + */ + if (!CASetBlueZObjectProperty(adapter, + BLUEZ_ADAPTER_INTERFACE, + "Powered", + g_variant_new_boolean(discoverable))) + { + OIC_LOG_V(ERROR, + TAG, + "Unable to power %s LE peripheral adapter.", + discoverable ? "on" : "off"); + + return; + } + + /** + * @note Enabling LE advertising used to be done here using the + * kernel bluetooth management API. However, we now + * leverage the BlueZ LE Advertisment D-Bus API instead + * since it handles all of the desired advertising + * operations without need of the calling process to have + * @c CAP_NET_ADMIN capabilities. Advertisment registration + * is performed in this source file. + */ + + *result = CA_STATUS_OK; +} + +static void CAPeripheralMakeDiscoverable(gpointer adapter, + gpointer result) +{ + CAPeripheralSetDiscoverable(adapter, result, TRUE); +} + +static void CAPeripheralMakeUndiscoverable(gpointer adapter, + gpointer result) +{ + CAPeripheralSetDiscoverable(adapter, result, FALSE); +} + +static CAResult_t CAPeripheralSetDiscoverability( + CALEContext * context, + GFunc discoverability_func) +{ + CAResult_t result = CA_STATUS_FAILED; + + /* + Synchronize access to the adapter information using the base + context lock since we don't own the adapter_infos. + */ + ca_mutex_lock(context->lock); + + // Make all detected adapters discoverable. + g_list_foreach(context->adapters, + discoverability_func, + &result); + + ca_mutex_unlock(context->lock); + + return result; +} + +/** + * Callback function invoked when a D-Bus bus name is acquired. + * + * @param[in] connection The D-Bus connection on which the name was + * acquired. + * @param[in] name The bus name that was acquired. + * @param[in] user_data User-provided data. + */ +static void CAPeripheralOnNameAcquired(GDBusConnection * connection, + gchar const * name, + gpointer user_data) +{ + OIC_LOG_V(DEBUG, + TAG, + "Name \"%s\" acquired on D-Bus.", name); +} + +/** + * Callback function invoked when a D-Bus bus name is no longer owned + * or the D-Bus connection has been closed. + * + * @param[in] connection The D-Bus connection on which the bus name + * should have been acquired. + * @param[in] name The bus name that was not acquired. + * @param[in] user_data User-provided data. + */ +static void CAPeripheralOnNameLost(GDBusConnection * connection, + gchar const * name, + gpointer user_data) +{ + /* + This can happen if the appropriate D-Bus policy is not + installed, for example. + */ + OIC_LOG_V(WARNING, + TAG, + "Lost name \"%s\" on D-Bus!", name); +} + +static void CAPeripheralStartEventLoop(void * data) +{ + CALEContext * const context = data; + + assert(context != NULL); + + // Create the event loop. + GMainContext * const loop_context = g_main_context_new(); + GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE); + + g_main_context_push_thread_default(loop_context); + + // Acquire the bus name after exporting our D-Bus objects. + guint const owner_id = + g_bus_own_name_on_connection(context->connection, + CA_DBUS_GATT_SERVICE_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + CAPeripheralOnNameAcquired, + CAPeripheralOnNameLost, + NULL, // user_data, + NULL); + + /** + * Create proxies to the @c org.bluez.LEAdvertisingManager1 D-Bus + * objects that will later be used to register the OIC LE + * advertisement data. + * + * @todo Failure to retrieve the LE advertising managers is + * currently ignored. We should propagate the failure to + * the thread that spawned this one. + * + * @note Retrieval of the @c org.bluez.LEAdvertisingManager1 + * proxies must be done in a thread separate from the one + * that makes calls through those proxies since the + * underlying GDBusObjectManagerClient sets up signal + * subscriptions that are used when dispatching D-Bus method + * handling calls (e.g. property retrieval, etc). + * Otherwise, a distributed deadlock situation could occur + * if a synchronous D-Bus proxy call is made that causes the + * recipient (like BlueZ) to call back in to the thread that + * handles signals. For example, registration of our LE + * advertisment with BlueZ causes BlueZ itself to make a + * call to our own @c org.bluez.LEAdvertisement1 object. + * However, the thread that initiated the advertisement + * registration is blocked waiting for BlueZ to respond, but + * BlueZ is blocked waiting for that same thread to respond + * to its own advertisement property retrieval call. + */ + GList * advertising_managers = NULL; + if (!CAGetBlueZManagedObjectProxies( + &advertising_managers, + BLUEZ_ADVERTISING_MANAGER_INTERFACE, + context, + NULL)) + { + OIC_LOG(ERROR, + TAG, + "Failed to retrieve BlueZ LE advertising " + "manager interface."); + } + + /** + * Initialize all GATT services. + * + * @todo Failure to initialize the OIC GATT services is currently + * ignored. We should propagate the failure to the thread + * that spawned this one. + * + * @note See the @c org.bluez.LEAdvertisingManager1 note above to + * understand why the GATT services must be initialized in + * a thread seperate from the one that initiates GATT + * service registration. + */ + GList * const gatt_services = + CAPeripheralInitializeGattServices(context); + + ca_mutex_lock(g_context.lock); + + assert(g_context.event_loop == NULL); + g_context.event_loop = event_loop; + + g_context.base = context; + + g_context.owner_id = owner_id; + + CALEAdvertisementInitialize(&g_context.advertisement, + context->connection, + advertising_managers); + + g_context.gatt_services = gatt_services; + + ca_mutex_unlock(g_context.lock); + + ca_cond_signal(g_context.condition); + + g_main_loop_run(event_loop); +} + +static void CAPeripheralStopEventLoop(CAPeripheralContext * context) +{ + ca_mutex_lock(context->lock); + + GMainLoop * const event_loop = context->event_loop; + context->event_loop = NULL; + + ca_mutex_unlock(context->lock); + + if (event_loop != NULL) + { + g_main_loop_quit(event_loop); + + GMainContext * const loop_context = + g_main_loop_get_context(event_loop); + + if (loop_context != NULL) + { + g_main_context_wakeup(loop_context); + g_main_context_unref(loop_context); + } + + g_main_loop_unref(event_loop); + } +} + +// ------------------------------------------------------ + +void CAPeripheralInitialize() +{ + g_context.lock = ca_mutex_new(); + g_context.condition = ca_cond_new(); +} + +void CAPeripheralFinalize() +{ + ca_cond_free(g_context.condition); + ca_mutex_free(g_context.lock); +} + +CAResult_t CAPeripheralStart(CALEContext * context) +{ + /** + * @todo Bluetooth adapters that are hot-plugged after the + * peripheral has started will not be started! + */ + + CAResult_t result = CA_STATUS_FAILED; + + // Only start if we were previously stopped. + if (CAPeripheralCheckStarted()) + { + result = CA_SERVER_STARTED_ALREADY; + return result; + } + + if (!CAPeripheralAdaptersFound(context)) + { + // No Bluetooth adapters. Don't bother continuing. + return result; + } + + /* + Spawn a thread to run the Glib event loop that will drive D-Bus + signal handling. + */ + result = ca_thread_pool_add_task(context->server_thread_pool, + CAPeripheralStartEventLoop, + context); + + if (result != CA_STATUS_OK) + { + return result; + } + + /* + Wait until initialization completes before proceeding to + service and advertisement registration. + */ + + // Number of times to wait for initialization to complete. + static int const max_retries = 2; + + static uint64_t const timeout = + 2 * MICROSECS_PER_SEC; // Microseconds + + ca_mutex_lock(g_context.lock); + + for (int i = 0; + g_context.gatt_services == NULL && i < max_retries; + ++i) + { + if (ca_cond_wait_for(g_context.condition, + g_context.lock, + timeout) == 0) + { + result = CA_STATUS_OK; + } + } + + ca_mutex_unlock(g_context.lock); + + if (result == CA_STATUS_FAILED) + { + return result; + } + + /** + * First register the GATT services, then register the LE + * advertisments with BlueZ to make sure the service we're + * advertising actually exists. + */ + if (result == CA_STATUS_OK + && !(CAPeripheralRegisterGattServices(context) + && CAPeripheralRegisterAdvertisements(&g_context))) + { + result = CA_STATUS_FAILED; + } + + /* + Make the local bluetooth adapters discoverable over LE by + enabling LE, enabling advertising, and making the LE device + connectable. + */ + result = CAPeripheralSetDiscoverability(context, + CAPeripheralMakeDiscoverable); + + return result; +} + +CAResult_t CAPeripheralStop() +{ + CAResult_t result = CA_STATUS_FAILED; + + // Only stop if we were previously started. + if (!CAPeripheralCheckStarted()) + { + result = CA_STATUS_OK; + return result; + } + + /* + Make the local bluetooth adapters undiscoverable. + + This function also sets the base context to NULL. + */ + result = + CAPeripheralSetDiscoverability(g_context.base, + CAPeripheralMakeUndiscoverable); + + CAPeripheralStopEventLoop(&g_context); + + ca_mutex_lock(g_context.lock); + + guint const owner_id = g_context.owner_id; + g_context.owner_id = 0; + + GList * const gatt_services = g_context.gatt_services; + g_context.gatt_services = NULL; + + g_context.base = NULL; + + ca_mutex_unlock(g_context.lock); + + CALEAdvertisementDestroy(&g_context.advertisement); + + g_list_free_full(gatt_services, CAPeripheralDestroyGattServices); + + g_bus_unown_name(owner_id); + + return result; +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.h new file mode 100644 index 0000000..89d9318 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/peripheral.h @@ -0,0 +1,128 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_PERIPHERAL_H +#define CA_BLE_LINUX_PERIPHERAL_H + +#include "context.h" +#include "advertisement.h" + +#include "cacommon.h" + + +/** + * @internal + * + * Linux BLE "peripheral" context. + */ +typedef struct _CAPeripheralContext +{ + /** + * Base context. + * + * The base context contains core state used throughout the Linux + * BLE adapter implementation. + */ + CALEContext * base; + + /// D-Bus bus name owner ID. + guint owner_id; + + /** + * LE advertising information. + * + * The LE advertising information will be registered with BlueZ, + * which BlueZ will then use to add advertising data to each of + * the detected Bluetooth hardware adapters. Only one set of + * advertising data is needed since the data is the same for all + * adapters. + */ + CALEAdvertisement advertisement; + + /** + * List of @c CAGattService objects containing information for all + * exported and registered IoTivity BlueZ GATT related D-Bus + * objects. + */ + GList * gatt_services; + + /** + * Glib event loop that drives peripheral D-Bus signal + * handling. + * + * @note A seperate thread drives this event loop. This is + * necessitated by the need for signal subscriptions to be + * done in what the GLib documentation refers to as the + * "thread-default main context". By the time the + * peripheral is started it's too late the use the main loop + * that was run when this Linux BLE transport adapter itself + * was started through @c CASelectNetwork() and the @c + * CAAdapterStart() callback the peripheral must have its + * own main loop. + */ + GMainLoop * event_loop; + + /// Mutex used to synchronize access to context fields. + ca_mutex lock; + + /** + * Service registration condition variable. + * + * This condition variable is used to delay service registration + * until the thread performing service initialization completes. + * Initialization is performed in the same thread that will run + * the peripheral's event loop. + * + * @see @c GMainLoop documentation for further details. + */ + ca_cond condition; + +} CAPeripheralContext; + +/** + * Initialize global state. + */ +void CAPeripheralInitialize(); + +/** + * Finalize global state. + */ +void CAPeripheralFinalize(); + +/** + * Initialize and start a Linux BLE "peripheral". + * + * Initialize all Linux BLE "peripheral" state (i.e. a global + * @c CAPeripheralContext instance), as well as register the OIC + * GATT transport service with BlueZ to begin advertising. + * + * @param[in] context Base context. + * + * @return @c CA_STATUS_OK on success. + */ +CAResult_t CAPeripheralStart(CALEContext * context); + +/** + * Stop the Linux BLE "peripheral". + * + * @return @c CA_STATUS_OK on success. + */ +CAResult_t CAPeripheralStop(); + + +#endif /* CA_BLE_LINUX_PERIPHERAL_H */ diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/recv.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/recv.c new file mode 100644 index 0000000..9cadb89 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/recv.c @@ -0,0 +1,64 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "recv.h" + +#include "caremotehandler.h" +#include "cafragmentation.h" +#include "oic_malloc.h" +#include "oic_string.h" + +#include +#include + + +// Logging tag. +static char const TAG[] = "BLE_RECV"; + +static CAGattRecvInfo const g_null_info = + { + .peer = NULL + }; + +void CAGattRecvInfoInitialize(CAGattRecvInfo * info) +{ + *info = g_null_info; +} + +void CAGattRecvInfoDestroy(CAGattRecvInfo * info) +{ + OICFree(info->peer); + *info = g_null_info; +} + +bool CAGattRecv(CAGattRecvInfo * info, char const * data, uint32_t length) +{ + uint32_t sent_length = 0; + + ca_mutex_lock(info->context->lock); + + bool const success = + info->on_packet_received(info->peer, + data, + length, + &sent_length) == CA_STATUS_OK; + + ca_mutex_unlock(info->context->lock); + + return success && length == sent_length; +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/recv.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/recv.h new file mode 100644 index 0000000..a017c5a --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/recv.h @@ -0,0 +1,98 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_RECV_H +#define CA_BLE_LINUX_RECV_H + +#include "context.h" + +#include "caleinterface.h" + +#include +#include + + +/** + * Information used to keep track of received data fragments. + */ +typedef struct _CAGattRecvInfo +{ + + /** + * @name Peer Connection-specific Fields + * + * These fields are valid only as long as the peer is connected. + */ + //@{ + /// Peer address. + char * peer; + + /// Callback invoked upon receiving all data from GATT peer. + CABLEDataReceivedCallback on_packet_received; + + /** + * Context object containing lock used for synchronized access to + * the @c on_packet_received callback since that callback is actually + * owned by it. + */ + CALEContext * context; + //@} + +} CAGattRecvInfo; + +/** + * Initialize a @c CAGattRecvInfo object. + * + * @param[in] info Pointer to @c CAGattRecvInfo object being + * initialized. No memory is allocated by this + * function. The caller is responsible for + * instantiating the object prior to calling this + * function. + */ +void CAGattRecvInfoInitialize(CAGattRecvInfo * info); + +/** + * Destroy a @c CAGattRecvInfo object. + * + * Destruction of @a info involves deallocating and clearing out all + * fields, as necessary. + * + * @param[in] info Pointer to @c CAGattRecvInfo object being + * destroyed. Only the @a info fields are finalized. + * Memory of @a info itself is retained by the + * caller. + */ +void CAGattRecvInfoDestroy(CAGattRecvInfo * info); + +/** + * Handle data received from GATT a peer. + * + * @param[in] info Information required to complete the receive + * operation. + * @param[in] data Octet array containing received data + * fragment. + * @param[in] length Length of the @a data array. + * + * @return @c true on success, @c false otherwise. + */ +bool CAGattRecv(CAGattRecvInfo * info, + char const * data, + uint32_t length); + + +#endif /* CA_BLE_LINUX_RECV_H */ diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/server.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/server.c new file mode 100644 index 0000000..828183c --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/server.c @@ -0,0 +1,110 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "server.h" + +#include "cacommon.h" +#include "logger.h" + +#include + + +// Logging tag. +static char const TAG[] = "BLE_SERVER"; + +// --------------------------------------------------------------------- +// GATT Request Handling +// --------------------------------------------------------------------- +void CAGattServerHandleRequestData() +{ +} + +// --------------------------------------------------------------------- +// GATT Response Handling +// --------------------------------------------------------------------- +/** + * Send response data to the GATT client. + * + * Respone data will be sent to the client through the given response + * @a characteristic proxy as a GATT characteristic notification. + * + * @param[in] characteristic The D-Bus proxy for the response + * characteristic through which the + * notification will be sent. + * @param[in] data The byte array to be sent. + * @param[in] length The number of elements in the byte + * array. + */ +static bool CAGattServerSendResponseNotification( + GattCharacteristic1 * characteristic, + char const * data, + size_t length) +{ + if (!gatt_characteristic1_get_notifying(characteristic)) + { + OIC_LOG(WARNING, + TAG, + "Attempt to send response with notifications " + "disabled.\n" + "Client must enable notifications. " + "No response was sent."); + + return false; + } + + GVariant * const value = + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + data, + length, + sizeof(data[0])); + + /** + * Send the response fragment by setting the "Value" property on + * the response characteristic, and emitting the + * @c org.freedesktop.Dbus.Properties.PropertiesChanged signal, + * accordingly. + * + * @todo Do we need to explicitly emit the @c GObject @c notify or + * @c org.freedesktop.Dbus.Properties.PropertiesChanged + * signal here? + */ + gatt_characteristic1_set_value(characteristic, value); + + return true; +} + +bool CAGattServerSendResponse(void const * method_info, + void const * data, + size_t length) +{ + assert(method_info != NULL); + + CAGattResponseInfo const * const info = method_info; + + GattCharacteristic1 * const characteristic = + info->characteristic; + + if (!CAGattServerSendResponseNotification(characteristic, + (char const *) data, + length)) + { + return false; + } + + return true; +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/server.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/server.h new file mode 100644 index 0000000..91dcc26 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/server.h @@ -0,0 +1,56 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_SERVER_H +#define CA_BLE_LINUX_SERVER_H + +#include "bluez-glue.h" + +#include +#include + +/** + * Information needed to complete a GATT server response send. + */ +typedef struct _CAGattResponseInfo +{ + /** + * The BlueZ @c org.bluez.GattCharacteristic1 skeleton object + * through which data will be sent. + */ + GattCharacteristic1 * const characteristic; + +} CAGattResponseInfo; + +/** + * Send response notification to the GATT client. + * + * @param[in] method_info Pointer to @c GattResponseInfo object that + * contains information necessary to complete + * send of response. + * @param[in] data Octet array of response data to be sent. + * @param[in] length Length of the @a data octet array. + * + * @see @c CAGattSendMethod() for further details. + */ +bool CAGattServerSendResponse(void const * method_info, + void const * data, + size_t length); + + +#endif /* CA_BLE_LINUX_SERVER_H */ diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/service.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/service.c new file mode 100644 index 0000000..ef204e5 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/service.c @@ -0,0 +1,264 @@ +/* **************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "service.h" +#include "gatt_dbus.h" +#include "utils.h" +#include "bluez.h" + +#include "cagattservice.h" +#include "logger.h" + +#include + + +// Logging tag. +static char const TAG[] = "BLE_SERVICE"; + +static GVariant * CAGattServiceGetProperties(GattService1 * service) +{ + /* + Create a variant containing the @c GattService1 properties, of + the form @c a{sa{sv}}. + */ + + /** + * Populate the property table, and create the variant to be + * embedded in the results of the + * @c org.freedesktop.Dbus.ObjectManager.GetManagedObjects() + * method call. + * + * The @c "Device" property is only available on the client side + * so we don't bother returning it here. + * + * @todo Do we care about the @c "Includes" property? + * @c "Includes" isn't implemented by BlueZ as of version + * 5.30, so we can leave it out. + */ + CADBusSkeletonProperty const properties[] = { + { "UUID", + g_variant_new_string(gatt_service1_get_uuid(service)) }, + { "Primary", + g_variant_new_boolean(gatt_service1_get_primary(service)) }, + { "Characteristics", + g_variant_new_objv( + gatt_service1_get_characteristics(service), + -1) } + }; + + return + CAMakePropertyDictionary( + BLUEZ_GATT_SERVICE_INTERFACE, + properties, + sizeof(properties) / sizeof(properties[0])); +} + +/** + * Implementation of the + * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() method + * for the @c org.bluez.GattService1 interface. + */ +static gboolean CAGattServiceHandleGetManagedObjects( + ObjectManager * object, + GDBusMethodInvocation * invocation, + gpointer user_data) +{ + /** + * @note Ideally we shouldn't need this implementation, and should + * be able to simply rely GDBusObjectManagerServer instead. + * Unfortunately, BlueZ expects the @c + * org.bluez.GattService1 object to implement the @c + * ObjectManager interface, and both interfaces must rooted + * at the same object path, as well. That requirement + * prevents us from using @c GDBusObjectManagerServer since + * it won't allow us to export more than interface on a + * given object path. + */ + + /* + Build the object array containing the IoTivity + org.bluez.GattService1 hierarchy. + + a{oa{sa{sv}}} + */ + CAGattService * const service = user_data; + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{oa{sa{sv}}}")); + + // Start out with the service itself. + g_variant_builder_add(&builder, + "{o@a{sa{sv}}}", + service->object_path, + CAGattServiceGetProperties(service->service)); + + /* + Add the request characteristic and user description + descriptor. + */ + CAGattCharacteristic * const request_chrc = + &service->request_characteristic; + + g_variant_builder_add(&builder, + "{o@a{sa{sv}}}", + request_chrc->object_path, + CAGattCharacteristicGetProperties( + request_chrc->characteristic)); + + CAGattDescriptor * const request_desc = &request_chrc->descriptor; + + g_variant_builder_add(&builder, + "{o@a{sa{sv}}}", + request_desc->object_path, + CAGattDescriptorGetProperties( + request_desc->descriptor)); + + /* + Add the response characteristic and user description + descriptor. + */ + CAGattCharacteristic * const response_chrc = + &service->response_characteristic; + + g_variant_builder_add(&builder, + "{o@a{sa{sv}}}", + response_chrc->object_path, + CAGattCharacteristicGetProperties( + response_chrc->characteristic)); + + CAGattDescriptor * const response_desc = &response_chrc->descriptor; + + g_variant_builder_add(&builder, + "{o@a{sa{sv}}}", + response_desc->object_path, + CAGattDescriptorGetProperties( + response_desc->descriptor)); + + GVariant * const objects = g_variant_builder_end(&builder); + + object_manager_complete_get_managed_objects(object, + invocation, + objects); + + return TRUE; +} + +bool CAGattServiceInitialize(CAGattService * s, + CALEContext * context, + char const * hci_name) +{ + assert(s != NULL); + assert(context != NULL); + assert(hci_name != NULL); + + // Path of the form /org/iotivity/gatt/hci0/service0. + s->object_path = + g_strdup_printf("%s/%s/%s", + CA_GATT_SERVICE_ROOT_PATH, + hci_name, + CA_GATT_SERVICE_PATH); + + assert(g_variant_is_object_path(s->object_path)); + + s->object_manager = object_manager_skeleton_new(); + s->service = gatt_service1_skeleton_new(); + + gatt_service1_set_uuid(s->service, CA_GATT_SERVICE_UUID); + gatt_service1_set_primary(s->service, TRUE); + + if (!CAGattRequestCharacteristicInitialize(s, context) + || !CAGattResponseCharacteristicInitialize(s, context)) + { + CAGattServiceDestroy(s); + return false; + } + + /* + The characteristic object paths are not fixed at compile-time. + Retrieve the object paths that were set at run-time. + */ + char const * characteristic_paths[] = { + s->request_characteristic.object_path, + s->response_characteristic.object_path, + NULL + }; + + gatt_service1_set_characteristics(s->service, characteristic_paths); + + /* + Set the org.freedesktop.DBus.ObjectManager.GetManagedObjects() + handler for our BlueZ GATT service. + */ + g_signal_connect( + s->object_manager, + "handle-get-managed-objects", + G_CALLBACK(CAGattServiceHandleGetManagedObjects), + s); + + /* + BlueZ expects both the org.freedesktop.DBus.ObjectManager and + org.bluez.GattService1 interfaces to be rooted at the same + object path. Export the service and object manager interface + skeletons with the same object path. + */ + GError * error = NULL; + if (!g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(s->object_manager), + context->connection, + s->object_path, + &error) + || !g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(s->service), + context->connection, + s->object_path, + &error)) + { + OIC_LOG_V(ERROR, + TAG, + "Unable to export GATT service interfaces: %s", + error->message); + + return false; + } + + return true; +} + +void CAGattServiceDestroy(CAGattService * s) +{ + /** + * @todo If necessary, emit the + * @c org.freedesktop.DBus.ObjectManager.InterfacesRemoved + * signal via @c object_manager_emit_interfaces_removed() if + * the CA GATT service objects were removed from the + * @c ObjectManager. + */ + + assert(s != NULL); // As designed, s is always non-NULL. + + g_clear_object(&s->gatt_manager); + + CAGattCharacteristicDestroy(&s->response_characteristic); + CAGattCharacteristicDestroy(&s->request_characteristic); + + g_clear_object(&s->service); + g_clear_object(&s->object_manager); + + g_free(s->object_path); + s->object_path = NULL; +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/service.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/service.h new file mode 100644 index 0000000..2cfbc3e --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/service.h @@ -0,0 +1,93 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_SERVICE_H +#define CA_BLE_LINUX_SERVICE_H + +#include "bluez-glue.h" +#include "object_manager-glue.h" +#include "characteristic.h" + + +/** + * GATT Service Information + */ +typedef struct CAGattService +{ + /// D-Bus object path for the GattService1 object. + char * object_path; + + /** + * OIC GATT service object_manager D-Bus interface skeleton + * object. + */ + ObjectManager * object_manager; + + /// OIC GATT service D-Bus interface skeleton object. + GattService1 * service; + + /// OIC GATT request characteristic information. + CAGattCharacteristic request_characteristic; + + /// OIC GATT response characteristic information. + CAGattCharacteristic response_characteristic; + + /** + * org.bluez.GattManager1 object with which the service is + * registered. + */ + GDBusProxy * gatt_manager; + +} CAGattService; + +/** + * Initialize GATT service fields. + * + * This function initializes the @c CAGattService object fields. + * + * @param[out] service GATT service information to be initialized. + * @param[in] context Object containing the D-Bus connection to the + * bus on which the service will be exported. + * @param[in] hci_name Name of the bluetooth adapter installed on the + * system, e.g. @c "hci0". + * + * @return @c true on success, @c false otherwise. + * + * @note This function does not allocate the @a service object + * itself. The caller is responsible for allocating that + * memory. + */ +bool CAGattServiceInitialize(CAGattService * service, + CALEContext * context, + char const * hci_name); + +/** + * Destroy GATT service fields. + * + * This function finalizes the @c CAGattService object fields. + * + * @param[in] service GATT service information to be finalized. + * + * @note This function does not deallocate the @a service object + * itself. The caller is responsible for deallocating that + * memory. + */ +void CAGattServiceDestroy(CAGattService * service); + + +#endif // CA_BLE_LINUX_SERVICE_H diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/utils.c b/resource/csdk/connectivity/src/bt_le_adapter/linux/utils.c new file mode 100644 index 0000000..40708f1 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/utils.c @@ -0,0 +1,245 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 "utils.h" +#include "bluez.h" + +#include "logger.h" + +#include + + +// Logging tag. +static char const TAG[] = "BLE_UTILS"; + +bool CAGetBlueZManagedObjectProxies(GList ** proxies, + char const * interface, + CALEContext * context, + CALEProxyFilter filter) +{ + assert(interface != NULL); + assert(proxies != NULL); + assert(context != NULL); + + /* + Failure is only reported if an error occurred. An empty + returned list, for example, is not an error since it could + be updated at a later time when handling D-Bus signals related + to the given interface. + */ + bool success = true; + + ca_mutex_lock(context->lock); + + if (context->objects == NULL) + { + ca_mutex_unlock(context->lock); + return success; + } + + /* + Iterate over the objects to find those that implement the given + BlueZ interface and them to the given list. + */ + for (GList * l = context->objects; l != NULL; l = l->next) + { + GDBusProxy * const proxy = + G_DBUS_PROXY(g_dbus_object_get_interface( + G_DBUS_OBJECT(l->data), + interface)); + + if (proxy != NULL) + { + if (filter == NULL || filter(proxy)) + { + /* + Add the object information to the list. + + Note that we prepend instead of append in this case + since it is more efficient to do so for linked lists + like the one used here. + */ + *proxies = g_list_prepend(*proxies, proxy); + } + else + { + // Rejected by filter. + g_object_unref(proxy); + } + } + } + + ca_mutex_unlock(context->lock); + + return success; +} + +GDBusProxy * CAGetBlueZInterfaceProxy(GVariant * tuple, + char const * interface, + GDBusObjectManager * object_manager) +{ + /* + The tuple is of the form "(oa{sv})". + + The object path is first tuple element, and the properties the + second. Check if the given interface exists in the properties. + Return the object path (the dictionary item key) of the found + property (the dictionary item value). + */ + GVariant * const props = g_variant_get_child_value(tuple, 1); + + GVariant * const value = + g_variant_lookup_value(props, interface, NULL); + + GDBusProxy * proxy = NULL; + + if (value != NULL) + { + /* + A set of properties corresponding to the given D-Bus + interface was found. Create a proxy to the object that + implements that interface. Store that proxy, the D-Bus + object path and properties in the given list. + */ + + gchar const * path = NULL; + + // The object path is the first tuple element. + g_variant_get_child(tuple, 0, "&o", &path); + + /* + Create a proxy to the object that implements the given + interface. + */ + proxy = + G_DBUS_PROXY( + g_dbus_object_manager_get_interface(object_manager, + path, + interface)); + + g_variant_unref(value); + } + + g_variant_unref(props); + + return proxy; +} + +bool CASetBlueZObjectProperty(GDBusProxy * proxy, + char const * interface, + char const * property, + GVariant * value) +{ + /* + Only make the D-Bus call to set the property if we need to + change it. + */ + GVariant * const cached_value = + g_dbus_proxy_get_cached_property(proxy, property); + + if (cached_value != NULL) + { + bool const already_set = g_variant_equal(cached_value, value); + + g_variant_unref(cached_value); + + if (already_set) + { + /* + Ownership of the value GVariant was transferred when + this function was called. It won't be used since the + property with the same value is already set, so release + our reference to it. + */ + g_variant_unref(value); + + return true; + } + } + + /* + Either the property wasn't previously set or it is being + changed. Set property on the given interface. + */ + GError * error = NULL; + + GVariant * const ret = + g_dbus_proxy_call_sync(proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new("(ssv)", + interface, + property, + value), + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout (default == -1), + NULL, // cancellable + &error); + + if (ret == NULL) + { + OIC_LOG_V(ERROR, + TAG, + "Attempt to set \"%s\" property for " + "\"%s\" interface failed.: %s", + error->message); + + g_error_free(error); + + return false; + } + + g_variant_unref(ret); + + return true; +} + +GVariant * CAMakePropertyDictionary( + char const * interface_name, + CADBusSkeletonProperty const * properties, + size_t count) +{ + /* + Create a variant containing the proxy properties, of the form + a{sa{sv}}. + */ + + GVariantBuilder builder; + + // Create the inner (property) dictionary. + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + + CADBusSkeletonProperty const * const end = + properties + count; + + for (CADBusSkeletonProperty const * prop = properties; + prop != end; + ++prop) + { + g_variant_builder_add(&builder, "{sv}", prop->name, prop->value); + } + + GVariant * const props = g_variant_builder_end(&builder); + + // Now create the dictionary with the above property dictionary + // embedded. + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sa{sv}}")); + + g_variant_builder_add(&builder, "{s@a{sv}}", interface_name, props); + + return g_variant_builder_end(&builder); +} diff --git a/resource/csdk/connectivity/src/bt_le_adapter/linux/utils.h b/resource/csdk/connectivity/src/bt_le_adapter/linux/utils.h new file mode 100644 index 0000000..04292b6 --- /dev/null +++ b/resource/csdk/connectivity/src/bt_le_adapter/linux/utils.h @@ -0,0 +1,147 @@ +/****************************************************************** + * + * Copyright 2015 Intel Corporation All Rights Reserved. + * + * 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 CA_BLE_LINUX_UTILS_H +#define CA_BLE_LINUX_UTILS_H + +#include "context.h" + + +/** + * Proxy retrieval filter function type. + * + * A function that implements this interface may be passed to + * @c CAGetBlueZManagedObjectProxies() to filter proxies to be added + * to the returned list. + * + * @return @c true if the proxy should be added to the proxy list, + * @c false otherwise. + */ +typedef bool(*CALEProxyFilter)(GDBusProxy * proxy); + +/** + * Get information for all BlueZ managed objects with a given + * interface. + * + * @param[out] proxies List containing @a proxies (@c GDBusProxy) + * for all found BlueZ objects that match the + * given D-Bus @a interface. + * @param[in] interface D-Bus interface of BlueZ object for which a + * proxy will be created. + * @param[in] context BLE Linux adapter context. + * @param[in] filter Filter function used to determine whether or + * not a proxy retrieved from the BlueZ + * @c ObjectManager should be added to the + * returned @a proxies list. + * + * @return @c true if objects were found. + */ +bool CAGetBlueZManagedObjectProxies(GList ** proxies, + char const * interface, + CALEContext * context, + CALEProxyFilter filter); + + +/** + * Get the proxy to a object that implements the given D-Bus + * @a interface. + * + * @param[in] tuple A D-Bus tuple that contains the D-Bus object + * path and corresponding dictionary of + * properties, i.e. with the format specifier + * @c "(oa{sv})". + * @param[in] interface The D-Bus interface in which we're + * interested. A @c GDBusProxy object will be + * create using the properties found in the + * dictionary item whose key matches this + * interface name. + * @param[in] manager The D-Bus object manager that contains the + * information about the object in question. + */ +GDBusProxy * CAGetBlueZInterfaceProxy(GVariant * tuple, + char const * interface, + GDBusObjectManager * object_manager); + +/** + * Set @a property on BlueZ object to given @a value. + * + * Using the @c org.freedesktop.DBus.Properties.Set() method + * implemented by BlueZ objects, set the desired @a property on the + * object pointed to by @a proxy to the given @a value. + * + * @param[in] proxy D-Bus proxy to the BlueZ object. + * @param[in] interface Interface name of the object on which the + * property is being set. + * @param[in] property Property name. + * @param[in] value Property value. Ownership is transferred from + * the caller. + */ +bool CASetBlueZObjectProperty(GDBusProxy * proxy, + char const * interface, + char const * property, + GVariant * value); + +/** + * D-Bus skeleton propery name/value pair. + * + * This name value pair will be used when constructing the property + * dictionary embedded in + * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() results. + */ +typedef struct _CADBusSkeletonProperty +{ + /// Property name. + char const * const name; + + /** + * Property value. + * + * @c Ownership is transferred to the function this variant passed + * to. + */ + GVariant * const value; +} CADBusSkeletonProperty; + +/** + * Construct property dictionary suitable for place in ObjectManager + * results. + * + * @param[in] interface_name The name of the interface to which the + * properties correspond. + * @param[in] properties Array of property name/value pairs. + * Ownership of the value variant will be + * transferred to this function. + * @param[in] count Number of elements in the @a properties + * array. + * + * @return A variant of the form a{sa{sv}}, suitable for use in the + * results of + * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() + * or @c org.freedesktop.DBus.Properties.GetAll() method + * implementations. + * + * @note Ownership of the returned @c GVariant is transferred to the + * caller. + */ +GVariant * CAMakePropertyDictionary( + char const * interface_name, + CADBusSkeletonProperty const * properties, + size_t count); + + +#endif // CA_BLE_LINUX_UTILS_H diff --git a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableclient.c b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableclient.c index 1f02fd3..ffebf4d 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableclient.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableclient.c @@ -35,6 +35,7 @@ #include "caqueueingthread.h" #include "caadapterutils.h" #include "cafragmentation.h" +#include "cagattservice.h" #include "oic_string.h" #include "oic_malloc.h" @@ -44,12 +45,8 @@ #define TZ_BLE_CLIENT_TAG "TZ_BLE_GATT_CLIENT" /** - * Its the constant value for characteristic descriptor from spec. - */ -#define BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG "2902" - -/** - * This contains the list of OIC services a client connect tot. + * @var g_bLEServiceList + * @brief This contains the list of OIC services a client connect to. */ static BLEServiceList *g_bLEServiceList = NULL; @@ -101,7 +98,7 @@ static ca_mutex g_bleClientThreadPoolMutex = NULL; * Maintains the callback to be notified on receival of network packets * from other BLE devices */ -static CABLEClientDataReceivedCallback g_bleClientDataReceivedCallback = NULL; +static CABLEDataReceivedCallback g_bleClientDataReceivedCallback = NULL; /** * callback to update the error to le adapter @@ -152,8 +149,8 @@ void CABleGattCharacteristicChangedCb(bt_gatt_attribute_h characteristic, ca_mutex_lock(g_bleServerBDAddressMutex); uint32_t sentLength = 0; - g_bleClientDataReceivedCallback(g_remoteAddress, OIC_BLE_SERVICE_ID, - value, valueLen, &sentLength); + g_bleClientDataReceivedCallback(g_remoteAddress, value, valueLen, + &sentLength); OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "Sent data Length is %d", sentLength); ca_mutex_unlock(g_bleServerBDAddressMutex); @@ -262,7 +259,7 @@ bool CABleGattCharacteristicsDiscoveredCb(int result, OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "New Characteristics[%s] of uuid[%s] is obtained", (char *)characteristic, uuid); - if(0 == strcasecmp(uuid, CA_BLE_READ_CHAR_UUID)) // Server will read on this characterisctics + if(0 == strcasecmp(uuid, CA_GATT_RESPONSE_CHRC_UUID)) // Server will read on this characterisctics { OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG , "Read characteristics is obtained"); OICFree(uuid); @@ -314,7 +311,7 @@ bool CABleGattCharacteristicsDiscoveredCb(int result, } ca_mutex_unlock(g_bleClientThreadPoolMutex); } - else if (0 == strcasecmp(uuid, CA_BLE_WRITE_CHAR_UUID)) // Server will write on this characteristics. + else if (0 == strcasecmp(uuid, CA_GATT_REQUEST_CHRC_UUID)) // Server will write on this characteristics. { OICFree(uuid); OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG , "Write characteristics is obtained"); @@ -634,7 +631,7 @@ void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle) OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "OUT"); } -void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback) +void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback) { OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "IN"); @@ -1352,9 +1349,9 @@ CAResult_t CASetCharacteristicDescriptorValue(stGattCharDescriptor_t *stGattChar OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "desc x3 [%x]", stGattCharDescInfo->desc[3]); - OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG strUUID is [%s]", + OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "CA_GATT_CONFIGURATION_DESC_UUID strUUID is [%s]", strUUID); - //if (!strncmp(strUUID, BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG, 2)) + //if (!strncmp(strUUID, CA_GATT_CONFIGURATION_DESC_UUID, 2)) { OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "setting notification/indication for descriptor"); @@ -1377,8 +1374,8 @@ CAResult_t CASetCharacteristicDescriptorValue(stGattCharDescriptor_t *stGattChar } CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress, - const char *data, const uint32_t dataLen, - CALETransferType_t type, const int32_t position) + const char *data, uint32_t dataLen, + CALETransferType_t type, int32_t position) { OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "IN"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cablenwmonitor.c b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cablenwmonitor.c index 790a079..6d54736 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cablenwmonitor.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cablenwmonitor.c @@ -125,6 +125,13 @@ CAResult_t CAInitializeLEAdapter() return CA_STATUS_OK; } +CAResult_t CAStartLEAdapter() +{ + // Nothing to do. + + return CA_STATUS_OK; +} + CAResult_t CAGetLEAdapterState() { OIC_LOG(DEBUG, TZ_LE_NWK_MONITOR_TAG, "IN"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableserver.c b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableserver.c index 98e709c..88e365a 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableserver.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableserver.c @@ -31,6 +31,7 @@ #include "caqueueingthread.h" #include "caadapterutils.h" #include "cafragmentation.h" +#include "cagattservice.h" #include "cableutil.h" #include "oic_string.h" #include "oic_malloc.h" @@ -76,7 +77,7 @@ static bt_advertiser_h g_hAdvertiser = NULL; * @brief Maintains the callback to be notified on receival of network packets from other * BLE devices */ -static CABLEServerDataReceivedCallback g_bleServerDataReceivedCallback = NULL; +static CABLEDataReceivedCallback g_bleServerDataReceivedCallback = NULL; /** * @var g_serverErrorCallback @@ -207,7 +208,7 @@ void CAStartBleGattServerThread(void *data) sleep(5); // Sleep is must because of the platform issue. - char *serviceUUID = OIC_BLE_SERVICE_ID; + char *serviceUUID = CA_GATT_SERVICE_UUID; ret = CAAddNewBleServiceInGattServer(serviceUUID); if (CA_STATUS_OK != ret ) @@ -218,7 +219,7 @@ void CAStartBleGattServerThread(void *data) return; } - char *charReadUUID = CA_BLE_READ_CHAR_UUID; + char *charReadUUID = CA_GATT_RESPONSE_CHRC_UUID; char charReadValue[] = {33, 44, 55, 66}; // These are initial random values ret = CAAddNewCharacteristicsToGattServer(g_gattSvcPath, charReadUUID, charReadValue, @@ -231,7 +232,7 @@ void CAStartBleGattServerThread(void *data) return; } - char *charWriteUUID = CA_BLE_WRITE_CHAR_UUID; + char *charWriteUUID = CA_GATT_REQUEST_CHRC_UUID; char charWriteValue[] = {33, 44, 55, 66}; // These are initial random values @@ -610,8 +611,8 @@ void CABleGattRemoteCharacteristicWriteCb(char *charPath, OIC_LOG(DEBUG, TZ_BLE_SERVER_TAG, "Sending data up !"); uint32_t sentLength = 0; - g_bleServerDataReceivedCallback(remoteAddress, OIC_BLE_SERVICE_ID, - data, charValueLen, &sentLength); + g_bleServerDataReceivedCallback(remoteAddress, data, charValueLen, + &sentLength); ca_mutex_unlock(g_bleReqRespCbMutex); @@ -705,8 +706,9 @@ CAResult_t CARemoveCharacteristicsFromGattServer(const char *charPath) return CA_STATUS_OK; } -CAResult_t CAUpdateCharacteristicsToGattClient(const char* address, const char *charValue, - const uint32_t charValueLen) +CAResult_t CAUpdateCharacteristicsToGattClient(const char *address, + const char *charValue, + uint32_t charValueLen) { OIC_LOG(DEBUG, TZ_BLE_SERVER_TAG, "IN"); @@ -800,7 +802,7 @@ CAResult_t CAUpdateCharacteristicsToAllGattClients(const char *charValue, uint32 return CA_STATUS_OK; } -void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback) +void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback) { OIC_LOG(DEBUG, TZ_BLE_SERVER_TAG, "IN"); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.c b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.c index 1f8da50..a2e4ff6 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.c +++ b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.c @@ -30,6 +30,7 @@ #include "caadapterutils.h" +#include "cagattservice.h" #include "oic_string.h" #include "oic_malloc.h" @@ -329,7 +330,7 @@ CAResult_t CAVerifyOICServiceByUUID(const char* serviceUUID) VERIFY_NON_NULL(serviceUUID, TZ_BLE_CLIENT_UTIL_TAG, "Param serviceHandle is NULL"); - if (strcasecmp(serviceUUID, OIC_BLE_SERVICE_ID) != 0) + if (strcasecmp(serviceUUID, CA_GATT_SERVICE_UUID) != 0) { OIC_LOG(ERROR, TZ_BLE_CLIENT_UTIL_TAG, "It is not OIC service!"); return CA_STATUS_FAILED; @@ -353,7 +354,7 @@ CAResult_t CAVerifyOICServiceByServiceHandle(bt_gatt_attribute_h serviceHandle) return CA_STATUS_FAILED; } - if (strcasecmp(uuid, OIC_BLE_SERVICE_ID) != 0) + if (strcasecmp(uuid, CA_GATT_SERVICE_UUID) != 0) { OIC_LOG(ERROR, TZ_BLE_CLIENT_UTIL_TAG, "It is not OIC service!"); OICFree(uuid); diff --git a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.h b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.h index cfa63b1..a4847c7 100644 --- a/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.h +++ b/resource/csdk/connectivity/src/bt_le_adapter/tizen/cableutil.h @@ -91,21 +91,6 @@ typedef struct gattCharDescriptor int total; /**< The total number of descriptor in a characteristic */ } stGattCharDescriptor_t; -#define OIC_BLE_SERVICE_ID "ADE3D529-C784-4F63-A987-EB69F70EE816" -///TODO: OIC_BLE_SERVICE_ID will be generated by invoking API in future. - -/** - * @def CA_BLE_READ_CHAR_UUID - * @brief UUID of read characteristic. This UUID is common across all platform for LE transport. - */ -#define CA_BLE_READ_CHAR_UUID "E9241982-4580-42C4-8831-95048216B256" - -/** - * @def CA_BLE_WRITE_CHAR_UUID - * @brief UUID of write characteristic. This UUID is common across all platform for LE transport. - */ -#define CA_BLE_WRITE_CHAR_UUID "AD7B334F-4637-4B86-90B6-9D787F03D218" - /** * @brief Used to increment the registered service count. * @return NONE diff --git a/tools/scons/RunTest.py b/tools/scons/RunTest.py index 0d9f023..5ec0bad 100644 --- a/tools/scons/RunTest.py +++ b/tools/scons/RunTest.py @@ -48,6 +48,11 @@ def run_test(env, xml_file, test): # Valgrind run. valgrind_environment = '' + # GLib uses a custom memory allocation scheme that can + # sometimes confuse Valgrind. Configure GLib to be Valgrind + # friendly. + valgrind_environment += 'G_DEBUG=gc-friendly G_SLICE=always-malloc' + # Valgrind suppressions file. suppression_file = env.File('#tools/valgrind/iotivity.supp').srcnode().path diff --git a/tools/valgrind/iotivity.supp b/tools/valgrind/iotivity.supp index bc2289e..474bb6b 100644 --- a/tools/valgrind/iotivity.supp +++ b/tools/valgrind/iotivity.supp @@ -14,7 +14,122 @@ # limitations under the License. # ------------------------------------------------------------------------ +# ************************************************************************ # This file contains Valgrind suppressions. It is meant to make # Valgrind ignore memory violations that are out of the control of # IoTivity developers. Do NOT abuse this file by adding suppressions # for memory violations that can be addressed in IoTivity itself. +# ************************************************************************ + +# ************************************************************************ +# GLib related suppressions +# +# These suppressions hide "possibly lost" leak diagnostics from +# Valgrind that occur from running simple code like the following: +# GError * error = NULL; +# GDBusConnection * const connection = +# g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); +# g_object_unref(connection); +# ************************************************************************ + +# gdbus-codegen generated code +{ + gdbus-codegen/*-skeleton-new + Memcheck:Leak + ... + fun:*_skeleton_new +} +# GLib >= 2.44.1 +{ + glib/gobject-init-ctor + Memcheck:Leak + ... + fun:gobject_init_ctor +} +{ + glib/g-dbus-auth-run-client + Memcheck:Leak + ... + fun:_g_dbus_auth_run_client +} +{ + glib/g-main-context-iterate- + Memcheck:Leak + ... + fun:g_main_context_iterate.* +} +{ + glib/g-main-context-dispatch + Memcheck:Leak + ... + fun:g_main_context_dispatch +} +{ + glib/g-bus-get-sync + Memcheck:Leak + ... + fun:g_bus_get_sync +} +{ + glib/get-uninitialized-connection + Memcheck:Leak + ... + fun:get_uninitialized_connection +} +{ + glib/g-dbus-address-get-stream-sync + Memcheck:Leak + ... + fun:g_dbus_address_get_stream_sync +} +{ + glib/g-type-register-static-simple + Memcheck:Leak + ... + fun:g_type_register_static_simple +} +{ + glib/g-type-add-interface-static + Memcheck:Leak + ... + fun:g_type_add_interface_static +} +{ + glib/complete-in-idle-cb + Memcheck:Leak + ... + fun:complete_in_idle_cb +} +{ + glib/g-dbus-address-try-connect-one + Memcheck:Leak + ... + fun:g_dbus_address_try_connect_one +} +{ + glib/g-initable-new + Memcheck:Leak + ... + fun:g_initable_new +} +# GLib < 2.36 +{ + glib/g-type-register-fundamental + Memcheck:Leak + ... + fun:g_type_register_fundamental +} +{ + glib/g-type-register-static + Memcheck:Leak + ... + fun:g_type_register_static +} +{ + glib/g-malloc0 + Memcheck:Leak + ... + fun:g_malloc0 + obj:/usr/lib/*/libgobject-2.0*.so.* +} +# ************************************************************************ -- 2.7.4