gattlib_disconnection: Added support to wait for the disconnection to be effective

pull/274/head
Olivier Martin 2024-03-28 11:52:57 +01:00
parent a85dd83015
commit 5f43addb8f
15 changed files with 66 additions and 20 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -11,6 +11,7 @@
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <bluetooth/bluetooth.h>
@ -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