diff --git a/include/gattlib.h b/include/gattlib.h index 9dd57f8..cdaa398 100644 --- a/include/gattlib.h +++ b/include/gattlib.h @@ -2,7 +2,7 @@ * * GattLib - GATT Library * - * Copyright (C) 2016 Olivier Martin + * Copyright (C) 2016-2017 Olivier Martin * * * This program is free software; you can redistribute it and/or modify @@ -30,6 +30,7 @@ #include #include #include "uuid.h" +#include "btio.h" #ifndef BDADDR_BREDR /* GattLib note: BD Address have only been introduced into Bluez v4.100. */ @@ -41,12 +42,18 @@ #define BDADDR_LE_RANDOM 0x02 #endif +#if BLUEZ_VERSION_MAJOR == 5 + #define ATT_MAX_MTU ATT_MAX_VALUE_LEN +#endif + +#if BLUEZ_VERSION_MAJOR == 4 typedef enum { BT_IO_SEC_SDP = 0, BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH, } BtIOSecLevel; +#endif typedef struct _GAttrib GAttrib; @@ -65,7 +72,6 @@ typedef struct _gatt_connection_t { } gatt_connection_t; typedef void (*gatt_connect_cb_t)(gatt_connection_t* connection); -typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data); typedef void* (*gatt_read_cb_t)(void* buffer, size_t buffer_len); /** @@ -92,13 +98,23 @@ typedef struct { } gattlib_primary_service_t; typedef struct { + uint16_t handle; uint8_t properties; uint16_t value_handle; bt_uuid_t uuid; } gattlib_characteristic_t; +typedef struct { + char uuid[MAX_LEN_UUID_STR + 1]; + uint16_t handle; + uint16_t uuid16; +} gattlib_descriptor_t; + int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count); +int gattlib_discover_char_range(gatt_connection_t* connection, int start, int end, gattlib_characteristic_t** characteristics, int* characteristics_count); int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_t** characteristics, int* characteristic_count); +int gattlib_discover_desc_range(gatt_connection_t* connection, int start, int end, gattlib_descriptor_t** descriptors, int* descriptor_count); +int gattlib_discover_desc(gatt_connection_t* connection, gattlib_descriptor_t** descriptors, int* descriptor_count); int gattlib_read_char_by_uuid(gatt_connection_t* connection, bt_uuid_t* uuid, void* buffer, size_t buffer_len); int gattlib_read_char_by_uuid_async(gatt_connection_t* connection, bt_uuid_t* uuid, gatt_read_cb_t gatt_read_cb); diff --git a/include/gattlib_internal.h b/include/gattlib_internal.h index 687cd44..3fdc8b3 100644 --- a/include/gattlib_internal.h +++ b/include/gattlib_internal.h @@ -29,6 +29,11 @@ #include "gattlib.h" +#if BLUEZ_VERSION_MAJOR == 5 + #include "src/shared/att-types.h" + #include "src/shared/util.h" +#endif + struct gattlib_thread_t { int ref; pthread_t thread; diff --git a/src/gattlib_connect.c b/src/gattlib_connect.c index 20e7119..7efe4af 100644 --- a/src/gattlib_connect.c +++ b/src/gattlib_connect.c @@ -54,7 +54,11 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) uint8_t opdu[ATT_MAX_MTU]; uint16_t handle, i, olen = 0; +#if BLUEZ_VERSION_MAJOR == 4 handle = att_get_u16(&pdu[1]); +#else + handle = get_le16(&pdu[1]); +#endif switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: @@ -78,14 +82,26 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) olen = enc_confirmation(opdu, sizeof(opdu)); if (olen > 0) - g_attrib_send(conn->attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); + g_attrib_send(conn->attrib, 0, +#if BLUEZ_VERSION_MAJOR == 4 + opdu[0], +#endif + opdu, olen, NULL, NULL, NULL); } static gboolean io_listen_cb(gpointer user_data) { gatt_connection_t *conn = user_data; - g_attrib_register(conn->attrib, ATT_OP_HANDLE_NOTIFY, events_handler, conn, NULL); - g_attrib_register(conn->attrib, ATT_OP_HANDLE_IND, events_handler, conn, NULL); + g_attrib_register(conn->attrib, ATT_OP_HANDLE_NOTIFY, +#if BLUEZ_VERSION_MAJOR == 5 + GATTRIB_ALL_HANDLES, +#endif + events_handler, conn, NULL); + g_attrib_register(conn->attrib, ATT_OP_HANDLE_IND, +#if BLUEZ_VERSION_MAJOR == 5 + GATTRIB_ALL_HANDLES, +#endif + events_handler, conn, NULL); return FALSE; } @@ -101,7 +117,11 @@ static void io_connect_cb(GIOChannel *io, GError *err, gpointer user_data) { io_connect_arg->connect_cb(NULL); } } else { +#if BLUEZ_VERSION_MAJOR == 4 io_connect_arg->conn->attrib = g_attrib_new(io); +#else + io_connect_arg->conn->attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false); +#endif // // Register the listener callback @@ -207,22 +227,40 @@ static gatt_connection_t *initialize_gattlib_connection(const gchar *src, const io_connect_arg->error = NULL; if (psm == 0) - conn->io = bt_io_connect(BT_IO_L2CAP, io_connect_cb, io_connect_arg, NULL, &err, + conn->io = bt_io_connect( +#if BLUEZ_VERSION_MAJOR == 4 + BT_IO_L2CAP, +#endif + io_connect_cb, io_connect_arg, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &sba, +#if BLUEZ_VERSION_MAJOR == 5 + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, +#endif BT_IO_OPT_DEST_BDADDR, &dba, BT_IO_OPT_DEST_TYPE, dest_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, sec_level, +#if BLUEZ_VERSION_MAJOR == 4 BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT, +#endif BT_IO_OPT_INVALID); else - conn->io = bt_io_connect(BT_IO_L2CAP, io_connect_cb, io_connect_arg, NULL, &err, + conn->io = bt_io_connect( +#if BLUEZ_VERSION_MAJOR == 4 + BT_IO_L2CAP, +#endif + io_connect_cb, io_connect_arg, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &sba, +#if BLUEZ_VERSION_MAJOR == 5 + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, +#endif BT_IO_OPT_DEST_BDADDR, &dba, BT_IO_OPT_PSM, psm, BT_IO_OPT_IMTU, mtu, BT_IO_OPT_SEC_LEVEL, sec_level, +#if BLUEZ_VERSION_MAJOR == 4 BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT, +#endif BT_IO_OPT_INVALID); if (err) { diff --git a/src/gattlib_discover.c b/src/gattlib_discover.c index f972326..92a2e93 100644 --- a/src/gattlib_discover.c +++ b/src/gattlib_discover.c @@ -2,7 +2,7 @@ * * GattLib - GATT Library * - * Copyright (C) 2016 Olivier Martin + * Copyright (C) 2016-2017 Olivier Martin * * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,11 @@ struct primary_all_cb_t { int discovered; }; +#if BLUEZ_VERSION_MAJOR == 4 static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) { +#else +static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { +#endif struct primary_all_cb_t* data = user_data; GSList *l; int i; @@ -94,7 +98,11 @@ struct characteristic_cb_t { int discovered; }; +#if BLUEZ_VERSION_MAJOR == 4 static void characteristic_cb(GSList *characteristics, guint8 status, gpointer user_data) { +#else +static void characteristic_cb(uint8_t status, GSList *characteristics, void *user_data) { +#endif struct characteristic_cb_t* data = user_data; GSList *l; int i; @@ -111,8 +119,9 @@ static void characteristic_cb(GSList *characteristics, guint8 status, gpointer u for (i = 0, l = characteristics; l; l = l->next, i++) { struct gatt_char *chars = l->data; - data->characteristics[i].properties = chars->properties; - data->characteristics[i].value_handle = chars->value_handle; + data->characteristics[i].handle = chars->handle; + data->characteristics[i].properties = chars->properties; + data->characteristics[i].value_handle = chars->value_handle; bt_string_to_uuid(&data->characteristics[i].uuid, chars->uuid); assert(i < data->characteristics_count); @@ -122,10 +131,8 @@ done: data->discovered = TRUE; } -int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_t** characteristics, int* characteristics_count) { +int gattlib_discover_char_range(gatt_connection_t* connection, int start, int end, gattlib_characteristic_t** characteristics, int* characteristics_count) { struct characteristic_cb_t user_data; - const int start = 0x0001; - const int end = 0xffff; guint ret; bzero(&user_data, sizeof(user_data)); @@ -147,3 +154,121 @@ int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_ return 0; } + +int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_t** characteristics, int* characteristics_count) { + return gattlib_discover_char_range(connection, 0x0001, 0xffff, characteristics, characteristics_count); +} + +struct descriptor_cb_t { + gattlib_descriptor_t* descriptors; + int descriptors_count; + int discovered; +}; + +#if BLUEZ_VERSION_MAJOR == 4 +static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) +{ + struct descriptor_cb_t* data = user_data; + struct att_data_list *list; + guint8 format; + int i; + + if (status) { + fprintf(stderr, "Discover all descriptors failed: %s\n", att_ecode2str(status)); + goto done; + } + + list = dec_find_info_resp(pdu, plen, &format); + if (list == NULL) + goto done; + + // Allocate array + data->descriptors_count = list->num; + data->descriptors = malloc(data->descriptors_count * sizeof(gattlib_descriptor_t)); + + for (i = 0; i < list->num; i++) { + uint16_t handle; + uint8_t *value; + bt_uuid_t uuid; + + value = list->data[i]; + data->descriptors[i].handle = att_get_u16(value); + + if (format == 0x01) { + data->descriptors[i].uuid16 = *(uint16_t*)&value[2]; + uuid = att_get_uuid16(&value[2]); + } else { + uuid = att_get_uuid128(&value[2]); + } + + bt_uuid_to_string(&uuid, data->descriptors[i].uuid, MAX_LEN_UUID_STR); + + assert(i < data->descriptors_count); + } + + att_data_list_free(list); + +done: + data->discovered = TRUE; +} +#else +static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) +{ + struct descriptor_cb_t* data = user_data; + GSList *l; + int i; + + if (status) { + fprintf(stderr, "Discover all descriptors failed: %s\n", att_ecode2str(status)); + goto done; + } + + // Allocate array + data->descriptors_count = g_slist_length(descriptors); + data->descriptors = malloc(data->descriptors_count * sizeof(gattlib_descriptor_t)); + + for (i = 0, l = descriptors; l; l = l->next, i++) { + struct gatt_desc *desc = l->data; + + data->descriptors[i].handle = desc->handle; + data->descriptors[i].uuid16 = desc->uuid16; + strncpy(data->descriptors[i].uuid, desc->uuid, MAX_LEN_UUID_STR); + + assert(i < data->descriptors_count); + } + +done: + data->discovered = TRUE; +} +#endif + +int gattlib_discover_desc_range(gatt_connection_t* connection, int start, int end, gattlib_descriptor_t** descriptors, int* descriptor_count) { + struct descriptor_cb_t descriptor_data; + guint ret; + + bzero(&descriptor_data, sizeof(descriptor_data)); + +#if BLUEZ_VERSION_MAJOR == 4 + ret = gatt_find_info(connection->attrib, start, end, char_desc_cb, &descriptor_data); +#else + ret = gatt_discover_desc(connection->attrib, start, end, NULL, char_desc_cb, &descriptor_data); +#endif + if (ret == 0) { + fprintf(stderr, "Fail to discover descriptors.\n"); + return 1; + } + + // Wait for completion + while(descriptor_data.discovered == FALSE) { + g_main_context_iteration(g_gattlib_thread.loop_context, FALSE); + } + + *descriptors = descriptor_data.descriptors; + *descriptor_count = descriptor_data.descriptors_count; + + return 0; +} + +int gattlib_discover_desc(gatt_connection_t* connection, gattlib_descriptor_t** descriptors, int* descriptor_count) { + return gattlib_discover_desc_range(connection, 0x0001, 0xffff, descriptors, descriptor_count); +}