mirror of https://github.com/labapart/gattlib
gattlib: Added support for Bluez 4 and 5
parent
48f3ab4e5f
commit
9c43746ec6
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* GattLib - GATT Library
|
* GattLib - GATT Library
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
|
* Copyright (C) 2016-2017 Olivier Martin <olivier@labapart.org>
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
#include <bluetooth/bluetooth.h>
|
#include <bluetooth/bluetooth.h>
|
||||||
#include <bluetooth/sdp.h>
|
#include <bluetooth/sdp.h>
|
||||||
#include "uuid.h"
|
#include "uuid.h"
|
||||||
|
#include "btio.h"
|
||||||
|
|
||||||
#ifndef BDADDR_BREDR
|
#ifndef BDADDR_BREDR
|
||||||
/* GattLib note: BD Address have only been introduced into Bluez v4.100. */
|
/* GattLib note: BD Address have only been introduced into Bluez v4.100. */
|
||||||
|
@ -41,12 +42,18 @@
|
||||||
#define BDADDR_LE_RANDOM 0x02
|
#define BDADDR_LE_RANDOM 0x02
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 5
|
||||||
|
#define ATT_MAX_MTU ATT_MAX_VALUE_LEN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BT_IO_SEC_SDP = 0,
|
BT_IO_SEC_SDP = 0,
|
||||||
BT_IO_SEC_LOW,
|
BT_IO_SEC_LOW,
|
||||||
BT_IO_SEC_MEDIUM,
|
BT_IO_SEC_MEDIUM,
|
||||||
BT_IO_SEC_HIGH,
|
BT_IO_SEC_HIGH,
|
||||||
} BtIOSecLevel;
|
} BtIOSecLevel;
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct _GAttrib GAttrib;
|
typedef struct _GAttrib GAttrib;
|
||||||
|
|
||||||
|
@ -65,7 +72,6 @@ typedef struct _gatt_connection_t {
|
||||||
} gatt_connection_t;
|
} gatt_connection_t;
|
||||||
|
|
||||||
typedef void (*gatt_connect_cb_t)(gatt_connection_t* connection);
|
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);
|
typedef void* (*gatt_read_cb_t)(void* buffer, size_t buffer_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,13 +98,23 @@ typedef struct {
|
||||||
} gattlib_primary_service_t;
|
} gattlib_primary_service_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
uint16_t handle;
|
||||||
uint8_t properties;
|
uint8_t properties;
|
||||||
uint16_t value_handle;
|
uint16_t value_handle;
|
||||||
bt_uuid_t uuid;
|
bt_uuid_t uuid;
|
||||||
} gattlib_characteristic_t;
|
} 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_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_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(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);
|
int gattlib_read_char_by_uuid_async(gatt_connection_t* connection, bt_uuid_t* uuid, gatt_read_cb_t gatt_read_cb);
|
||||||
|
|
|
@ -29,6 +29,11 @@
|
||||||
|
|
||||||
#include "gattlib.h"
|
#include "gattlib.h"
|
||||||
|
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 5
|
||||||
|
#include "src/shared/att-types.h"
|
||||||
|
#include "src/shared/util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
struct gattlib_thread_t {
|
struct gattlib_thread_t {
|
||||||
int ref;
|
int ref;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
|
@ -54,7 +54,11 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
|
||||||
uint8_t opdu[ATT_MAX_MTU];
|
uint8_t opdu[ATT_MAX_MTU];
|
||||||
uint16_t handle, i, olen = 0;
|
uint16_t handle, i, olen = 0;
|
||||||
|
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
handle = att_get_u16(&pdu[1]);
|
handle = att_get_u16(&pdu[1]);
|
||||||
|
#else
|
||||||
|
handle = get_le16(&pdu[1]);
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (pdu[0]) {
|
switch (pdu[0]) {
|
||||||
case ATT_OP_HANDLE_NOTIFY:
|
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));
|
olen = enc_confirmation(opdu, sizeof(opdu));
|
||||||
|
|
||||||
if (olen > 0)
|
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) {
|
static gboolean io_listen_cb(gpointer user_data) {
|
||||||
gatt_connection_t *conn = 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_NOTIFY,
|
||||||
g_attrib_register(conn->attrib, ATT_OP_HANDLE_IND, events_handler, conn, NULL);
|
#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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +117,11 @@ static void io_connect_cb(GIOChannel *io, GError *err, gpointer user_data) {
|
||||||
io_connect_arg->connect_cb(NULL);
|
io_connect_arg->connect_cb(NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
io_connect_arg->conn->attrib = g_attrib_new(io);
|
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
|
// Register the listener callback
|
||||||
|
@ -207,22 +227,40 @@ static gatt_connection_t *initialize_gattlib_connection(const gchar *src, const
|
||||||
io_connect_arg->error = NULL;
|
io_connect_arg->error = NULL;
|
||||||
|
|
||||||
if (psm == 0)
|
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,
|
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_BDADDR, &dba,
|
||||||
BT_IO_OPT_DEST_TYPE, dest_type,
|
BT_IO_OPT_DEST_TYPE, dest_type,
|
||||||
BT_IO_OPT_CID, ATT_CID,
|
BT_IO_OPT_CID, ATT_CID,
|
||||||
BT_IO_OPT_SEC_LEVEL, sec_level,
|
BT_IO_OPT_SEC_LEVEL, sec_level,
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT,
|
BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT,
|
||||||
|
#endif
|
||||||
BT_IO_OPT_INVALID);
|
BT_IO_OPT_INVALID);
|
||||||
else
|
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,
|
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_BDADDR, &dba,
|
||||||
BT_IO_OPT_PSM, psm,
|
BT_IO_OPT_PSM, psm,
|
||||||
BT_IO_OPT_IMTU, mtu,
|
BT_IO_OPT_IMTU, mtu,
|
||||||
BT_IO_OPT_SEC_LEVEL, sec_level,
|
BT_IO_OPT_SEC_LEVEL, sec_level,
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT,
|
BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT,
|
||||||
|
#endif
|
||||||
BT_IO_OPT_INVALID);
|
BT_IO_OPT_INVALID);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* GattLib - GATT Library
|
* GattLib - GATT Library
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
|
* Copyright (C) 2016-2017 Olivier Martin <olivier@labapart.org>
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -36,7 +36,11 @@ struct primary_all_cb_t {
|
||||||
int discovered;
|
int discovered;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) {
|
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;
|
struct primary_all_cb_t* data = user_data;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
int i;
|
int i;
|
||||||
|
@ -94,7 +98,11 @@ struct characteristic_cb_t {
|
||||||
int discovered;
|
int discovered;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if BLUEZ_VERSION_MAJOR == 4
|
||||||
static void characteristic_cb(GSList *characteristics, guint8 status, gpointer user_data) {
|
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;
|
struct characteristic_cb_t* data = user_data;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
int i;
|
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++) {
|
for (i = 0, l = characteristics; l; l = l->next, i++) {
|
||||||
struct gatt_char *chars = l->data;
|
struct gatt_char *chars = l->data;
|
||||||
|
|
||||||
data->characteristics[i].properties = chars->properties;
|
data->characteristics[i].handle = chars->handle;
|
||||||
data->characteristics[i].value_handle = chars->value_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);
|
bt_string_to_uuid(&data->characteristics[i].uuid, chars->uuid);
|
||||||
|
|
||||||
assert(i < data->characteristics_count);
|
assert(i < data->characteristics_count);
|
||||||
|
@ -122,10 +131,8 @@ done:
|
||||||
data->discovered = TRUE;
|
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;
|
struct characteristic_cb_t user_data;
|
||||||
const int start = 0x0001;
|
|
||||||
const int end = 0xffff;
|
|
||||||
guint ret;
|
guint ret;
|
||||||
|
|
||||||
bzero(&user_data, sizeof(user_data));
|
bzero(&user_data, sizeof(user_data));
|
||||||
|
@ -147,3 +154,121 @@ int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_
|
||||||
|
|
||||||
return 0;
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue