2016-04-23 18:26:11 +02:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* GattLib - GATT Library
|
|
|
|
*
|
2021-09-01 00:00:36 +02:00
|
|
|
* Copyright (C) 2016-2021 Olivier Martin <olivier@labapart.org>
|
2016-04-23 18:26:11 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "gattlib_internal.h"
|
|
|
|
|
2017-02-06 16:13:17 +01:00
|
|
|
#include "uuid.h"
|
2016-04-23 18:26:11 +02:00
|
|
|
#include "att.h"
|
2017-01-30 11:52:54 +01:00
|
|
|
#include "gattrib.h"
|
|
|
|
#include "gatt.h"
|
2016-04-23 18:26:11 +02:00
|
|
|
|
|
|
|
struct gattlib_result_read_uuid_t {
|
2019-05-27 17:21:59 +02:00
|
|
|
void** buffer;
|
|
|
|
size_t* buffer_len;
|
2016-04-23 18:26:11 +02:00
|
|
|
gatt_read_cb_t callback;
|
|
|
|
int completed;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void gattlib_result_read_uuid_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) {
|
|
|
|
struct gattlib_result_read_uuid_t* gattlib_result = user_data;
|
|
|
|
struct att_data_list *list;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (status == ATT_ECODE_ATTR_NOT_FOUND) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != 0) {
|
|
|
|
fprintf(stderr, "Read characteristics by UUID failed: %s\n", att_ecode2str(status));
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
list = dec_read_by_type_resp(pdu, len);
|
|
|
|
if (list == NULL) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < list->num; i++) {
|
|
|
|
uint8_t *value = list->data[i];
|
2019-05-27 17:21:59 +02:00
|
|
|
size_t buffer_len = list->len - 2;
|
2016-04-23 18:26:11 +02:00
|
|
|
|
2019-05-27 17:21:59 +02:00
|
|
|
// Move the value to the beginning of the data
|
2016-04-23 18:26:11 +02:00
|
|
|
value += 2;
|
|
|
|
|
|
|
|
if (gattlib_result->callback) {
|
2019-05-27 17:21:59 +02:00
|
|
|
gattlib_result->callback(value, buffer_len);
|
2016-04-23 18:26:11 +02:00
|
|
|
} else {
|
2019-05-27 17:21:59 +02:00
|
|
|
void* buffer = malloc(buffer_len);
|
|
|
|
if (buffer == NULL) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy value into the buffer
|
|
|
|
memcpy(buffer, value, buffer_len);
|
|
|
|
|
|
|
|
*gattlib_result->buffer_len = buffer_len;
|
|
|
|
*gattlib_result->buffer = buffer;
|
2016-04-23 18:26:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
att_data_list_free(list);
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (gattlib_result->callback) {
|
|
|
|
free(gattlib_result);
|
|
|
|
} else {
|
|
|
|
gattlib_result->completed = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-06 16:13:17 +01:00
|
|
|
void uuid_to_bt_uuid(uuid_t* uuid, bt_uuid_t* bt_uuid) {
|
|
|
|
memcpy(&bt_uuid->value, &uuid->value, sizeof(bt_uuid->value));
|
|
|
|
if (uuid->type == SDP_UUID16) {
|
|
|
|
bt_uuid->type = BT_UUID16;
|
|
|
|
} else if (uuid->type == SDP_UUID32) {
|
|
|
|
bt_uuid->type = BT_UUID32;
|
|
|
|
} else if (uuid->type == SDP_UUID128) {
|
|
|
|
bt_uuid->type = BT_UUID128;
|
|
|
|
} else {
|
|
|
|
bt_uuid->type = BT_UUID_UNSPEC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int gattlib_read_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid,
|
2019-05-27 17:21:59 +02:00
|
|
|
void **buffer, size_t* buffer_len)
|
2016-04-23 18:26:11 +02:00
|
|
|
{
|
2017-03-15 01:37:33 +01:00
|
|
|
gattlib_context_t* conn_context = connection->context;
|
2016-04-23 18:26:11 +02:00
|
|
|
struct gattlib_result_read_uuid_t* gattlib_result;
|
2017-02-06 16:13:17 +01:00
|
|
|
bt_uuid_t bt_uuid;
|
2016-04-23 18:26:11 +02:00
|
|
|
const int start = 0x0001;
|
|
|
|
const int end = 0xffff;
|
|
|
|
|
|
|
|
gattlib_result = malloc(sizeof(struct gattlib_result_read_uuid_t));
|
|
|
|
if (gattlib_result == NULL) {
|
2019-05-21 21:25:04 +02:00
|
|
|
return GATTLIB_OUT_OF_MEMORY;
|
2016-04-23 18:26:11 +02:00
|
|
|
}
|
|
|
|
gattlib_result->buffer = buffer;
|
2019-05-27 17:21:59 +02:00
|
|
|
gattlib_result->buffer_len = buffer_len;
|
2016-04-23 18:26:11 +02:00
|
|
|
gattlib_result->callback = NULL;
|
|
|
|
gattlib_result->completed = FALSE;
|
|
|
|
|
2017-02-06 16:13:17 +01:00
|
|
|
uuid_to_bt_uuid(uuid, &bt_uuid);
|
|
|
|
|
2017-03-15 01:37:33 +01:00
|
|
|
gatt_read_char_by_uuid(conn_context->attrib, start, end, &bt_uuid,
|
2019-05-27 17:21:59 +02:00
|
|
|
gattlib_result_read_uuid_cb, gattlib_result);
|
2016-04-23 18:26:11 +02:00
|
|
|
|
|
|
|
// Wait for completion of the event
|
|
|
|
while(gattlib_result->completed == FALSE) {
|
|
|
|
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(gattlib_result);
|
2019-05-21 21:25:04 +02:00
|
|
|
return GATTLIB_SUCCESS;
|
2016-04-23 18:26:11 +02:00
|
|
|
}
|
|
|
|
|
2017-02-06 16:13:17 +01:00
|
|
|
int gattlib_read_char_by_uuid_async(gatt_connection_t* connection, uuid_t* uuid,
|
2019-05-27 17:21:59 +02:00
|
|
|
gatt_read_cb_t gatt_read_cb)
|
2016-04-23 18:26:11 +02:00
|
|
|
{
|
2017-03-15 01:37:33 +01:00
|
|
|
gattlib_context_t* conn_context = connection->context;
|
2016-04-23 18:26:11 +02:00
|
|
|
struct gattlib_result_read_uuid_t* gattlib_result;
|
|
|
|
const int start = 0x0001;
|
|
|
|
const int end = 0xffff;
|
2017-02-06 16:13:17 +01:00
|
|
|
bt_uuid_t bt_uuid;
|
2016-04-23 18:26:11 +02:00
|
|
|
|
|
|
|
gattlib_result = malloc(sizeof(struct gattlib_result_read_uuid_t));
|
|
|
|
if (gattlib_result == NULL) {
|
2019-05-21 21:25:04 +02:00
|
|
|
return GATTLIB_OUT_OF_MEMORY;
|
2016-04-23 18:26:11 +02:00
|
|
|
}
|
|
|
|
gattlib_result->buffer = NULL;
|
|
|
|
gattlib_result->buffer_len = 0;
|
|
|
|
gattlib_result->callback = gatt_read_cb;
|
|
|
|
gattlib_result->completed = FALSE;
|
|
|
|
|
2017-02-06 16:13:17 +01:00
|
|
|
uuid_to_bt_uuid(uuid, &bt_uuid);
|
|
|
|
|
2017-03-15 01:37:33 +01:00
|
|
|
guint id = gatt_read_char_by_uuid(conn_context->attrib, start, end, &bt_uuid,
|
2019-05-27 17:21:59 +02:00
|
|
|
gattlib_result_read_uuid_cb, gattlib_result);
|
2017-02-22 12:04:54 +01:00
|
|
|
|
|
|
|
if (id) {
|
2019-05-21 21:25:04 +02:00
|
|
|
return GATTLIB_SUCCESS;
|
2017-02-22 12:04:54 +01:00
|
|
|
} else {
|
2019-05-21 21:25:04 +02:00
|
|
|
return GATTLIB_NOT_FOUND;
|
2017-02-22 12:04:54 +01:00
|
|
|
}
|
2016-04-23 18:26:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void gattlib_write_result_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) {
|
|
|
|
int* write_completed = user_data;
|
|
|
|
|
|
|
|
*write_completed = TRUE;
|
|
|
|
}
|
|
|
|
|
2017-03-15 18:11:48 +01:00
|
|
|
int gattlib_write_char_by_handle(gatt_connection_t* connection, uint16_t handle, const void* buffer, size_t buffer_len) {
|
2017-03-15 01:37:33 +01:00
|
|
|
gattlib_context_t* conn_context = connection->context;
|
2016-04-23 18:26:11 +02:00
|
|
|
int write_completed = FALSE;
|
|
|
|
|
2017-03-17 11:35:13 +01:00
|
|
|
guint ret = gatt_write_char(conn_context->attrib, handle, (void*)buffer, buffer_len,
|
2019-06-26 17:24:32 +02:00
|
|
|
gattlib_write_result_cb, &write_completed);
|
2016-04-23 18:26:11 +02:00
|
|
|
if (ret == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for completion of the event
|
|
|
|
while(write_completed == FALSE) {
|
|
|
|
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
|
2019-06-26 17:24:32 +02:00
|
|
|
}
|
2016-04-23 18:26:11 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-03-14 22:51:06 +01:00
|
|
|
|
2017-03-15 18:11:48 +01:00
|
|
|
int gattlib_write_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, const void* buffer, size_t buffer_len) {
|
2017-03-14 22:51:06 +01:00
|
|
|
uint16_t handle = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = get_handle_from_uuid(connection, uuid, &handle);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "Fail to find handle for UUID.\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-05 13:19:56 +01:00
|
|
|
return gattlib_write_char_by_handle(connection, handle, buffer, buffer_len);
|
2017-03-14 22:51:06 +01:00
|
|
|
}
|
2017-03-15 01:43:04 +01:00
|
|
|
|
2019-07-05 22:43:38 +02:00
|
|
|
int gattlib_write_without_response_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, const void* buffer, size_t buffer_len)
|
|
|
|
{
|
|
|
|
// Only supported in the DBUS API (ie: Bluez > v5.40) at the moment
|
|
|
|
return GATTLIB_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gattlib_write_without_response_char_by_handle(gatt_connection_t* connection, uint16_t handle, const void* buffer, size_t buffer_len)
|
|
|
|
{
|
|
|
|
// Only supported in the DBUS API (ie: Bluez > v5.40) at the moment
|
|
|
|
return GATTLIB_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2017-03-15 01:43:04 +01:00
|
|
|
int gattlib_notification_start(gatt_connection_t* connection, const uuid_t* uuid) {
|
|
|
|
uint16_t handle;
|
|
|
|
uint16_t enable_notification = 0x0001;
|
|
|
|
|
|
|
|
int ret = get_handle_from_uuid(connection, uuid, &handle);
|
|
|
|
if (ret) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable Status Notification
|
|
|
|
return gattlib_write_char_by_handle(connection, handle + 1, &enable_notification, sizeof(enable_notification));
|
|
|
|
}
|
|
|
|
|
|
|
|
int gattlib_notification_stop(gatt_connection_t* connection, const uuid_t* uuid) {
|
|
|
|
uint16_t handle;
|
|
|
|
uint16_t enable_notification = 0x0000;
|
|
|
|
|
|
|
|
int ret = get_handle_from_uuid(connection, uuid, &handle);
|
|
|
|
if (ret) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable Status Notification
|
|
|
|
return gattlib_write_char_by_handle(connection, handle + 1, &enable_notification, sizeof(enable_notification));
|
|
|
|
}
|