From 5f43addb8f9ba9b12f02ce4709ee300d492ea945 Mon Sep 17 00:00:00 2001 From: Olivier Martin Date: Thu, 28 Mar 2024 11:52:57 +0100 Subject: [PATCH] gattlib_disconnection: Added support to wait for the disconnection to be effective --- bluez/gattlib_connect.c | 2 +- common/gattlib_callback_disconnected_device.c | 6 +++++ common/gattlib_internal_defs.h | 9 ++++++++ dbus/gattlib.c | 20 ++++++++++++++-- examples/ble_scan/ble_scan.c | 2 +- examples/discover/discover.c | 2 +- examples/gatttool/interactive.c | 2 +- examples/nordic_uart/nordic_uart.c | 2 +- examples/notification/notification.c | 2 +- examples/read_write/read_write.c | 2 +- .../read_write_memory/read_write_memory.c | 2 +- gattlib-py/gattlib/__init__.py | 4 ++-- gattlib-py/gattlib/device.py | 4 ++-- gattlib-py/gattlib/exception.py | 4 ++-- include/gattlib.h | 23 +++++++++++++++---- 15 files changed, 66 insertions(+), 20 deletions(-) diff --git a/bluez/gattlib_connect.c b/bluez/gattlib_connect.c index 73cb5df..3bbfab9 100644 --- a/bluez/gattlib_connect.c +++ b/bluez/gattlib_connect.c @@ -483,7 +483,7 @@ gatt_connection_t *gattlib_connect(void* adapter, const char *dst, unsigned long return conn; } -int gattlib_disconnect(gatt_connection_t* connection) { +int gattlib_disconnect(gatt_connection_t* connection, bool wait_disconnection) { gattlib_context_t* conn_context = connection->context; #if BLUEZ_VERSION_MAJOR == 4 diff --git a/common/gattlib_callback_disconnected_device.c b/common/gattlib_callback_disconnected_device.c index e263937..65a918e 100644 --- a/common/gattlib_callback_disconnected_device.c +++ b/common/gattlib_callback_disconnected_device.c @@ -48,6 +48,12 @@ void gattlib_on_disconnected_device(gatt_connection_t* connection) { g_rec_mutex_unlock(&connection->on_disconnection.mutex); } + // Signal the device is now disconnected + g_mutex_lock(&connection->disconnection_wait.lock); + connection->disconnection_wait.value = true; + g_cond_broadcast(&connection->disconnection_wait.condition); + g_mutex_unlock(&connection->disconnection_wait.lock); + // Clean GATTLIB connection on disconnection gattlib_connection_free(connection); } diff --git a/common/gattlib_internal_defs.h b/common/gattlib_internal_defs.h index 51458d1..9fe4246 100644 --- a/common/gattlib_internal_defs.h +++ b/common/gattlib_internal_defs.h @@ -53,6 +53,15 @@ struct _gatt_connection_t { GMutex connection_mutex; + struct { + // Used by gattlib_disconnection when we want to wait for the disconnection to be effective + GCond condition; + // Mutex used for disconnection_condition synchronization + GMutex lock; + // Used to avoid spurious or stolen wakeup + bool value; + } disconnection_wait; + struct gattlib_handler on_connection; struct gattlib_handler notification; struct gattlib_handler indication; diff --git a/dbus/gattlib.c b/dbus/gattlib.c index b515637..9ccde30 100644 --- a/dbus/gattlib.c +++ b/dbus/gattlib.c @@ -241,7 +241,7 @@ int gattlib_connect(void *adapter, const char *dst, ret = GATTLIB_NOT_FOUND; } else if ((error->domain == 238) && (error->code == 60952)) { GATTLIB_LOG(GATTLIB_ERROR, "Device '%s': %s", dst, error->message); - ret = GATTLIB_ERROR_TIMEOUT; + ret = GATTLIB_TIMEOUT; } else { GATTLIB_LOG(GATTLIB_ERROR, "Device connected error (device:%s): %s", conn_context->device_object_path, @@ -325,7 +325,7 @@ void gattlib_connection_free(gatt_connection_t* connection) { free(connection); } -int gattlib_disconnect(gatt_connection_t* connection) { +int gattlib_disconnect(gatt_connection_t* connection, bool wait_disconnection) { gattlib_context_t* conn_context; int ret = GATTLIB_SUCCESS; GError *error = NULL; @@ -355,6 +355,22 @@ int gattlib_disconnect(gatt_connection_t* connection) { //Note: Signals and memory will be removed/clean on disconnction callback // See _gattlib_clean_on_disconnection() + if (wait_disconnection) { + gint64 end_time; + + g_mutex_lock(&connection->disconnection_wait.lock); + + end_time = g_get_monotonic_time() + GATTLIB_DISCONNECTION_WAIT_TIMEOUT_SEC * G_TIME_SPAN_SECOND; + + while (!connection->disconnection_wait.value) { + if (!g_cond_wait_until(&connection->disconnection_wait.condition, &connection->disconnection_wait.lock, end_time)) { + ret = GATTLIB_TIMEOUT; + break; + } + } + g_mutex_unlock(&connection->disconnection_wait.lock); + } + EXIT: g_mutex_unlock(&connection->connection_mutex); return ret; diff --git a/examples/ble_scan/ble_scan.c b/examples/ble_scan/ble_scan.c index fb753a3..0748da4 100644 --- a/examples/ble_scan/ble_scan.c +++ b/examples/ble_scan/ble_scan.c @@ -86,7 +86,7 @@ static void on_device_connect(void *adapter, const char *dst, gatt_connection_t* free(characteristics); disconnect_exit: - gattlib_disconnect(connection); + gattlib_disconnect(connection, false /* wait_disconnection */); } static void *ble_connect_device(void *arg) { diff --git a/examples/discover/discover.c b/examples/discover/discover.c index 8efba4f..cb92acc 100644 --- a/examples/discover/discover.c +++ b/examples/discover/discover.c @@ -81,7 +81,7 @@ static void on_device_connect(void *adapter, const char *dst, gatt_connection_t* free(characteristics); EXIT: - gattlib_disconnect(connection); + gattlib_disconnect(connection, false /* wait_disconnection */); pthread_mutex_lock(&m_connection_terminated_lock); pthread_cond_signal(&m_connection_terminated); diff --git a/examples/gatttool/interactive.c b/examples/gatttool/interactive.c index 95bd192..ad903fd 100644 --- a/examples/gatttool/interactive.c +++ b/examples/gatttool/interactive.c @@ -129,7 +129,7 @@ static void disconnect_io() if (conn_state == STATE_DISCONNECTED) return; - gattlib_disconnect(g_connection); + gattlib_disconnect(g_connection, false /* wait_disconnection */); opt_mtu = 0; set_state(STATE_DISCONNECTED); diff --git a/examples/nordic_uart/nordic_uart.c b/examples/nordic_uart/nordic_uart.c index 670a217..d17478c 100644 --- a/examples/nordic_uart/nordic_uart.c +++ b/examples/nordic_uart/nordic_uart.c @@ -52,7 +52,7 @@ static void usage(char *argv[]) { } void int_handler(int dummy) { - gattlib_disconnect(m_connection); + gattlib_disconnect(m_connection, false /* wait_disconnection */); exit(0); } diff --git a/examples/notification/notification.c b/examples/notification/notification.c index de6a171..b1f618a 100644 --- a/examples/notification/notification.c +++ b/examples/notification/notification.c @@ -136,7 +136,7 @@ int main(int argc, char *argv[]) { g_main_loop_unref(m_main_loop); DISCONNECT: - gattlib_disconnect(connection); + gattlib_disconnect(connection, false /* wait_disconnection */); puts("Done"); return ret; } diff --git a/examples/read_write/read_write.c b/examples/read_write/read_write.c index 454fad7..0d48f21 100644 --- a/examples/read_write/read_write.c +++ b/examples/read_write/read_write.c @@ -101,7 +101,7 @@ static void on_device_connect(void *adapter, const char *dst, gatt_connection_t* } EXIT: - gattlib_disconnect(connection); + gattlib_disconnect(connection, false /* wait_disconnection */); pthread_mutex_lock(&m_connection_terminated_lock); pthread_cond_signal(&m_connection_terminated); diff --git a/examples/read_write_memory/read_write_memory.c b/examples/read_write_memory/read_write_memory.c index aa36584..eec623c 100644 --- a/examples/read_write_memory/read_write_memory.c +++ b/examples/read_write_memory/read_write_memory.c @@ -104,7 +104,7 @@ void *connect_ble(void *arg) { } EXIT: - gattlib_disconnect(connection); + gattlib_disconnect(connection, false /* wait_disconnection */); g_main_loop_quit(m_main_loop); return NULL; diff --git a/gattlib-py/gattlib/__init__.py b/gattlib-py/gattlib/__init__.py index 48b7784..8cfaa7b 100644 --- a/gattlib-py/gattlib/__init__.py +++ b/gattlib-py/gattlib/__init__.py @@ -152,9 +152,9 @@ gattlib_adapter_scan_eddystone.argtypes = [c_void_p, c_int16, c_uint32, py_objec gattlib_connect = gattlib.gattlib_connect gattlib_connect.argtypes = [c_void_p, c_char_p, c_ulong, c_void_p, c_void_p] -# int gattlib_disconnect(gatt_connection_t* connection); +# int gattlib_disconnect(gatt_connection_t* connection, bool wait_disconnection); gattlib_disconnect = gattlib.gattlib_disconnect -gattlib_disconnect.argtypes = [c_void_p] +gattlib_disconnect.argtypes = [c_void_p, c_bool] # int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count); gattlib_discover_primary = gattlib.gattlib_discover_primary diff --git a/gattlib-py/gattlib/device.py b/gattlib-py/gattlib/device.py index a1a98d9..f061671 100644 --- a/gattlib-py/gattlib/device.py +++ b/gattlib-py/gattlib/device.py @@ -138,11 +138,11 @@ class Device: gattlib_disconnected_device_python_callback, gattlib_python_callback_args(on_disconnection, user_data)) - def disconnect(self): + def disconnect(self, wait_disconnection: bool = False): self._connection_lock.acquire() try: if self._connection: - ret = gattlib_disconnect(self.connection) + ret = gattlib_disconnect(self.connection, wait_disconnection) handle_return(ret) self._connection = None finally: diff --git a/gattlib-py/gattlib/exception.py b/gattlib-py/gattlib/exception.py index c3c29c6..d09c60b 100644 --- a/gattlib-py/gattlib/exception.py +++ b/gattlib-py/gattlib/exception.py @@ -7,7 +7,7 @@ GATTLIB_SUCCESS = 0 GATTLIB_INVALID_PARAMETER = 1 GATTLIB_NOT_FOUND = 2 -GATTLIB_ERROR_TIMEOUT = 3 +GATTLIB_TIMEOUT = 3 GATTLIB_OUT_OF_MEMORY = 4 GATTLIB_NOT_SUPPORTED = 5 GATTLIB_DEVICE_ERROR = 6 @@ -74,7 +74,7 @@ def handle_return(ret): raise NotFound() elif ret == GATTLIB_OUT_OF_MEMORY: raise OutOfMemory() - elif ret == GATTLIB_ERROR_TIMEOUT: + elif ret == GATTLIB_TIMEOUT: raise TimeoutError() elif ret == GATTLIB_NOT_SUPPORTED: raise NotSupported() diff --git a/include/gattlib.h b/include/gattlib.h index 87f33c0..0af8529 100644 --- a/include/gattlib.h +++ b/include/gattlib.h @@ -11,6 +11,7 @@ extern "C" { #endif +#include #include #include @@ -31,6 +32,11 @@ extern "C" { #define ATT_MAX_MTU ATT_MAX_VALUE_LEN #endif +/** + * Gattlib constants + */ +#define GATTLIB_DISCONNECTION_WAIT_TIMEOUT_SEC 5 + /** * @name Gattlib errors */ @@ -38,7 +44,7 @@ extern "C" { #define GATTLIB_SUCCESS 0 #define GATTLIB_INVALID_PARAMETER 1 #define GATTLIB_NOT_FOUND 2 -#define GATTLIB_ERROR_TIMEOUT 3 +#define GATTLIB_TIMEOUT 3 #define GATTLIB_OUT_OF_MEMORY 4 #define GATTLIB_NOT_SUPPORTED 5 #define GATTLIB_DEVICE_ERROR 6 @@ -132,7 +138,9 @@ extern "C" { #define EDDYSTONE_TYPE_EID 0x30 //@} - +/** + * Log level + */ #define GATTLIB_ERROR 0 #define GATTLIB_WARNING 1 #define GATTLIB_INFO 2 @@ -351,11 +359,18 @@ int gattlib_connect(void *adapter, const char *dst, /** * @brief Function to disconnect the GATT connection * - * @param connection Active GATT connection + * @note: If a callback has been registered by gattlib_register_on_disconnect() then it will be called + * when the device will have signaled is disconnected. + * + * @param connection Active GATT connection + * @param wait_disconnection If false gattlib_disconnect does not wait for the device to confirm it has been + * disconnected and return immediately. * * @return GATTLIB_SUCCESS on success or GATTLIB_* error code + * @return GATTLIB_TIMEOUT when wait_disconnection is true and the device has not been disconnected for + * GATTLIB_DISCONNECTION_WAIT_TIMEOUT_SEC seconds */ -int gattlib_disconnect(gatt_connection_t* connection); +int gattlib_disconnect(gatt_connection_t* connection, bool wait_disconnection); /** * @brief Function to register a callback on GATT disconnection