gattlib/common/gattlib_common.c

221 lines
6.2 KiB
C

/*
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
*
* Copyright (c) 2021-2024, Olivier Martin <olivier@labapart.org>
*/
#include <stdio.h>
#include "gattlib_internal.h"
int gattlib_register_notification(gattlib_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data) {
GError *error = NULL;
if (connection == NULL) {
return GATTLIB_INVALID_PARAMETER;
}
connection->notification.callback.notification_handler = notification_handler;
connection->notification.user_data = user_data;
connection->notification.thread_pool = g_thread_pool_new(
gattlib_notification_device_thread,
&connection->notification,
1 /* max_threads */, FALSE /* exclusive */, &error);
if (error != NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "gattlib_register_notification: Failed to create thread pool: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_INTERNAL;
} else {
assert(connection->notification.thread_pool != NULL);
return GATTLIB_SUCCESS;
}
}
int gattlib_register_indication(gattlib_connection_t* connection, gattlib_event_handler_t indication_handler, void* user_data) {
GError *error = NULL;
if (connection == NULL) {
return GATTLIB_INVALID_PARAMETER;
}
connection->indication.callback.notification_handler = indication_handler;
connection->indication.user_data = user_data;
connection->indication.thread_pool = g_thread_pool_new(
gattlib_notification_device_thread,
&connection->indication,
1 /* max_threads */, FALSE /* exclusive */, &error);
if (error != NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "gattlib_register_indication: Failed to create thread pool: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_INTERNAL;
} else {
return GATTLIB_SUCCESS;
}
}
int gattlib_register_on_disconnect(gattlib_connection_t *connection, gattlib_disconnection_handler_t handler, void* user_data) {
if (connection == NULL) {
return GATTLIB_INVALID_PARAMETER;
}
connection->on_disconnection.callback.disconnection_handler = handler;
connection->on_disconnection.user_data = user_data;
return GATTLIB_SUCCESS;
}
void bt_uuid_to_uuid(bt_uuid_t* bt_uuid, uuid_t* uuid) {
memcpy(&uuid->value, &bt_uuid->value, sizeof(uuid->value));
if (bt_uuid->type == BT_UUID16) {
uuid->type = SDP_UUID16;
} else if (bt_uuid->type == BT_UUID32) {
uuid->type = SDP_UUID32;
} else if (bt_uuid->type == BT_UUID128) {
uuid->type = SDP_UUID128;
} else {
uuid->type = SDP_UUID_UNSPEC;
}
}
int gattlib_uuid_to_string(const uuid_t *uuid, char *str, size_t n) {
if (uuid->type == SDP_UUID16) {
snprintf(str, n, "0x%.4x", uuid->value.uuid16);
} else if (uuid->type == SDP_UUID32) {
snprintf(str, n, "0x%.8x", uuid->value.uuid32);
} else if (uuid->type == SDP_UUID128) {
unsigned int data0;
unsigned short data1;
unsigned short data2;
unsigned short data3;
unsigned int data4;
unsigned short data5;
memcpy(&data0, &uuid->value.uuid128.data[0], 4);
memcpy(&data1, &uuid->value.uuid128.data[4], 2);
memcpy(&data2, &uuid->value.uuid128.data[6], 2);
memcpy(&data3, &uuid->value.uuid128.data[8], 2);
memcpy(&data4, &uuid->value.uuid128.data[10], 4);
memcpy(&data5, &uuid->value.uuid128.data[14], 2);
snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
ntohl(data0), ntohs(data1), ntohs(data2),
ntohs(data3), ntohl(data4), ntohs(data5));
} else {
snprintf(str, n, "Unsupported type:%d", uuid->type);
return -1;
}
return 0;
}
int gattlib_string_to_uuid(const char *str, size_t n, uuid_t *uuid) {
bt_uuid_t bt_uuid;
int ret = bt_string_to_uuid(&bt_uuid, str);
if (ret == 0) {
bt_uuid_to_uuid(&bt_uuid, uuid);
}
return ret;
}
int gattlib_uuid_cmp(const uuid_t *uuid1, const uuid_t *uuid2) {
if (uuid1->type != uuid2->type) {
return 1;
} else if (uuid1->type == SDP_UUID16) {
if (uuid1->value.uuid16 == uuid2->value.uuid16) {
return 0;
} else {
return 2;
}
} else if (uuid1->type == SDP_UUID32) {
if (uuid1->value.uuid32 == uuid2->value.uuid32) {
return 0;
} else {
return 2;
}
} else if (uuid1->type == SDP_UUID128) {
if (memcmp(&uuid1->value.uuid128, &uuid2->value.uuid128, sizeof(uuid1->value.uuid128)) == 0) {
return 0;
} else {
return 2;
}
} else {
return 3;
}
}
void gattlib_handler_free(struct gattlib_handler* handler) {
g_rec_mutex_lock(&handler->mutex);
if (!gattlib_has_valid_handler(handler)) {
goto EXIT;
}
// Reset callback to stop calling it after we stopped
handler->callback.callback = NULL;
#if defined(WITH_PYTHON)
if (handler->python_args != NULL) {
struct gattlib_python_args* args = handler->python_args;
if (args->callback != NULL) {
Py_DECREF(args->callback);
}
if (args->args != NULL) {
Py_DECREF(args->args);
}
handler->python_args = NULL;
free(handler->python_args);
handler->python_args = NULL;
}
#endif
if (handler->thread_pool != NULL) {
g_thread_pool_free(handler->thread_pool, FALSE /* immediate */, TRUE /* wait */);
handler->thread_pool = NULL;
}
EXIT:
g_rec_mutex_unlock(&handler->mutex);
}
bool gattlib_has_valid_handler(struct gattlib_handler* handler) {
return (handler != NULL) && (handler->callback.callback != NULL);
}
void gattlib_handler_dispatch_to_thread(struct gattlib_handler* handler, void (*python_callback)(),
GThreadFunc thread_func, const char* thread_name, void* (*thread_args_allocator)(va_list args), ...) {
GError *error = NULL;
if (!gattlib_has_valid_handler(handler)) {
// We do not have (anymore) a callback, nothing to do
return;
}
#if defined(WITH_PYTHON)
// Check if we are using the Python callback, in case of Python argument we keep track of the argument to free them
// once we are done with the handler.
if (handler->callback.callback == python_callback) {
handler->python_args = handler->user_data;
}
#endif
// We create a thread to ensure the callback is not blocking the mainloop
va_list args;
va_start(args, thread_args_allocator);
void* thread_args = thread_args_allocator(args);
va_end(args);
handler->thread = g_thread_try_new(thread_name, thread_func, thread_args, &error);
if (handler->thread == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create thread '%s': %s", thread_name, error->message);
g_error_free(error);
return;
}
}
// Helper function to free memory from Python frontend
void gattlib_free_mem(void *ptr) {
if (ptr != NULL) {
free(ptr);
}
}