Skip to content

Gattlib write char by uuid #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions bluez/bluez5/attrib/gattrib.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,10 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
cb->destroy_func = notify;
cb->parent = attrib;
queue_push_head(attrib->callbacks, cb);
response_cb = attrib_callback_result;
destroy_cb = attrib_callbacks_remove;
if ( func )
response_cb = attrib_callback_result;
if ( notify )
destroy_cb = attrib_callbacks_remove;

}

Expand Down
85 changes: 69 additions & 16 deletions bluez/gattlib_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
#include "hci.h"
#include "hci_lib.h"

#define CONNECTION_TIMEOUT 2
#define BT_IO_TIMEOUT 2
#define CONNECTION_TIMEOUT (BT_IO_OPT_TIMEOUT + 4)

struct gattlib_thread_t g_gattlib_thread = { 0 };

Expand Down Expand Up @@ -165,6 +166,7 @@ static void io_connect_cb(GIOChannel *io, GError *err, gpointer user_data) {
}
}

#ifdef USE_THREAD
static void *connection_thread(void* arg) {
struct gattlib_thread_t* loop_thread = arg;

Expand All @@ -175,17 +177,19 @@ static void *connection_thread(void* arg) {
g_main_loop_unref(loop_thread->loop);
assert(0);
}
#endif

static gatt_connection_t *initialize_gattlib_connection(const gchar *src, const gchar *dst,
uint8_t dest_type, BtIOSecLevel sec_level, int psm, int mtu,
gatt_connect_cb_t connect_cb,
io_connect_arg_t* io_connect_arg)
io_connect_arg_t* io_connect_arg, int timeout_sec)
{
bdaddr_t sba, dba;
GError *err = NULL;

/* Check if the GattLib thread has been started */
if (g_gattlib_thread.ref == 0) {
#ifdef USE_THREAD
/* Start it */

/* Create a thread that will handle Bluetooth events */
Expand All @@ -199,11 +203,15 @@ static gatt_connection_t *initialize_gattlib_connection(const gchar *src, const
while (!g_gattlib_thread.loop || !g_main_loop_is_running (g_gattlib_thread.loop)) {
usleep(1000);
}
} else {
/* Increase the reference to know how many GATT connection use the loop */
g_gattlib_thread.ref++;
#else
g_gattlib_thread.loop_context = g_main_context_new();
g_gattlib_thread.loop = g_main_loop_new(g_gattlib_thread.loop_context, TRUE);
#endif
}

/* Increase the reference to know how many GATT connection use the loop */
g_gattlib_thread.ref++;

/* Remote device */
if (dst == NULL) {
fprintf(stderr, "Remote Bluetooth address required\n");
Expand Down Expand Up @@ -262,7 +270,7 @@ static gatt_connection_t *initialize_gattlib_connection(const gchar *src, const
BT_IO_OPT_DEST_TYPE, dest_type,
BT_IO_OPT_CID, ATT_CID,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT,
BT_IO_OPT_TIMEOUT, BT_IO_OPT_TIMEOUT,
BT_IO_OPT_INVALID);
} else {
conn_context->io = bt_io_connect(
Expand All @@ -278,7 +286,7 @@ static gatt_connection_t *initialize_gattlib_connection(const gchar *src, const
BT_IO_OPT_PSM, psm,
BT_IO_OPT_IMTU, mtu,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_TIMEOUT, CONNECTION_TIMEOUT,
BT_IO_OPT_TIMEOUT, BT_IO_TIMEOUT,
BT_IO_OPT_INVALID);
}

Expand Down Expand Up @@ -308,15 +316,39 @@ static BtIOSecLevel get_bt_io_sec_level(gattlib_bt_sec_level_t sec_level) {
}
}



void gattlib_iteration(void){
#ifdef USE_THREAD
if( pthread_self() == g_gattlib_thread.thread) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
}
else {
pthread_yield();
}
#else
g_main_context_iteration(g_gattlib_thread.loop_context, TRUE);
#endif
}


gatt_connection_t *gattlib_connect_async(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu,
gatt_connect_cb_t connect_cb)
{
return gattlib_connect_async_timeout(src, dst, dest_type, sec_level,
psm, mtu, connect_cb, CONNECTION_TIMEOUT);
}

gatt_connection_t *gattlib_connect_async_timeout(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu,
gatt_connect_cb_t connect_cb, int timeout_sec)
{
io_connect_arg_t* io_connect_arg = malloc(sizeof(io_connect_arg_t));
BtIOSecLevel bt_io_sec_level = get_bt_io_sec_level(sec_level);

return initialize_gattlib_connection(src, dst, dest_type, bt_io_sec_level,
psm, mtu, connect_cb, io_connect_arg);
psm, mtu, connect_cb, io_connect_arg, timeout_sec);
}

static gboolean connection_timeout(gpointer user_data) {
Expand All @@ -335,15 +367,32 @@ static gboolean connection_timeout(gpointer user_data) {
* @param psm Specify the PSM for GATT/ATT over BR/EDR
* @param mtu Specify the MTU size
*/

gatt_connection_t *gattlib_connect(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu)
{
return gattlib_connect_timeout(src, dst, dest_type, sec_level, psm, mtu, CONNECTION_TIMEOUT);
}

/**
* @param src Local Adaptater interface
* @param dst Remote Bluetooth address
* @param dst_type Set LE address type (either BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM)
* @param sec_level Set security level (either BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH)
* @param psm Specify the PSM for GATT/ATT over BR/EDR
* @param mtu Specify the MTU size
* @param timeout_sec connection timeout in seconds
*/
gatt_connection_t *gattlib_connect_timeout(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level,
int psm, int mtu, int timeout_sec)
{
BtIOSecLevel bt_io_sec_level = get_bt_io_sec_level(sec_level);
io_connect_arg_t io_connect_arg;
io_connect_arg_t io_connect_arg = {};
GSource* timeout;

gatt_connection_t *conn = initialize_gattlib_connection(src, dst, dest_type, bt_io_sec_level,
psm, mtu, NULL, &io_connect_arg);
psm, mtu, NULL, &io_connect_arg, timeout_sec);
if (conn == NULL) {
if (io_connect_arg.error) {
fprintf(stderr, "Error: gattlib_connect - initialization error:%s\n", io_connect_arg.error->message);
Expand All @@ -353,18 +402,19 @@ gatt_connection_t *gattlib_connect(const char *src, const char *dst,
return NULL;
}

// Timeout of 'CONNECTION_TIMEOUT+4' seconds
timeout = gattlib_timeout_add_seconds(CONNECTION_TIMEOUT + 4, connection_timeout, &io_connect_arg);
// Timeout of 'timeout_sec+4' seconds
timeout = gattlib_timeout_add_seconds(timeout_sec, connection_timeout, &io_connect_arg);

// Wait for the connection to be done
while ((io_connect_arg.connected == FALSE) && (io_connect_arg.timeout == FALSE)) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
while ((io_connect_arg.connected == FALSE) && (io_connect_arg.timeout == FALSE) && (0 == io_connect_arg.error )) {
gattlib_iteration();
}

// Disconnect the timeout source
g_source_destroy(timeout);

if (io_connect_arg.timeout) {
fprintf(stderr, "gattlib_connect - connection timeout\n");
return NULL;
}

Expand All @@ -379,6 +429,7 @@ gatt_connection_t *gattlib_connect(const char *src, const char *dst,
int gattlib_disconnect(gatt_connection_t* connection) {
gattlib_context_t* conn_context = connection->context;


#if BLUEZ_VERSION_MAJOR == 4
// Stop the I/O Channel
GIOStatus status = g_io_channel_shutdown(conn_context->io, FALSE, NULL);
Expand All @@ -387,15 +438,16 @@ int gattlib_disconnect(gatt_connection_t* connection) {
#endif

g_attrib_unref(conn_context->attrib);

free(conn_context->characteristics);
free(connection->context);
free(connection);

#if 0 // todo fix, but for now just leave thread running
//TODO: Add a mutex around this code to avoid a race condition
/* Decrease the reference counter of the loop */
g_gattlib_thread.ref--;
/* Check if we are the last one */


if (g_gattlib_thread.ref == 0) {
g_main_loop_quit(g_gattlib_thread.loop);
g_main_loop_unref(g_gattlib_thread.loop);
Expand All @@ -404,6 +456,7 @@ int gattlib_disconnect(gatt_connection_t* connection) {
// Detach the thread
pthread_detach(g_gattlib_thread.thread);
}
#endif

return 0;
}
Expand Down
7 changes: 4 additions & 3 deletions bluez/gattlib_discover.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_serv

// Wait for completion
while(user_data.discovered == FALSE) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
gattlib_iteration();
}

*services = user_data.services;
Expand Down Expand Up @@ -150,9 +150,10 @@ int gattlib_discover_char_range(gatt_connection_t* connection, int start, int en

// Wait for completion
while(user_data.discovered == FALSE) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
gattlib_iteration();
}


*characteristics = user_data.characteristics;
*characteristics_count = user_data.characteristics_count;

Expand Down Expand Up @@ -264,7 +265,7 @@ int gattlib_discover_desc_range(gatt_connection_t* connection, int start, int en

// Wait for completion
while(descriptor_data.discovered == FALSE) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
gattlib_iteration();
}

*descriptors = descriptor_data.descriptors;
Expand Down
9 changes: 9 additions & 0 deletions bluez/gattlib_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
#ifndef __GATTLIB_INTERNAL_H__
#define __GATTLIB_INTERNAL_H__


// uncomment the the following line to have an glib main event (g_main_loop_run)
// loop created in its own thread for gattlib, otherwise operations will
// do gattlib_iterate when required.
//#define USE_THREAD

#include <glib.h>

#define BLUEZ_VERSIONS(major, minor) (((major) << 8) | (minor))
Expand All @@ -38,6 +44,8 @@
#include "src/shared/util.h"
#endif

#define DEFAULT_TIMEOUT_SEC 5 // default timeout for operations

struct gattlib_thread_t {
int ref;
pthread_t thread;
Expand All @@ -62,6 +70,7 @@ extern struct gattlib_thread_t g_gattlib_thread;
GSource* gattlib_watch_connection_full(GIOChannel* io, GIOCondition condition,
GIOFunc func, gpointer user_data, GDestroyNotify notify);
GSource* gattlib_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data);
void gattlib_iteration(void);

void uuid_to_bt_uuid(uuid_t* uuid, bt_uuid_t* bt_uuid);
void bt_uuid_to_uuid(bt_uuid_t* bt_uuid, uuid_t* uuid);
Expand Down
34 changes: 31 additions & 3 deletions bluez/gattlib_read_write.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
*/

#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <errno.h>

#include "gattlib_internal.h"

Expand Down Expand Up @@ -120,7 +123,7 @@ int gattlib_read_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid,

// Wait for completion of the event
while(gattlib_result->completed == FALSE) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
gattlib_iteration();
}

*buffer_len = gattlib_result->buffer_len;
Expand Down Expand Up @@ -178,12 +181,37 @@ int gattlib_write_char_by_handle(gatt_connection_t* connection, uint16_t handle,

// Wait for completion of the event
while(write_completed == FALSE) {
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
gattlib_iteration();
}

return 0;
}

void gattlib_write_cmd_cb (gpointer data)
{
int* write_completed = data;
*write_completed = TRUE;
}

int gattlib_write_cmd_by_handle(gatt_connection_t* connection, uint16_t handle, const void* buffer, size_t buffer_len) {
gattlib_context_t* conn_context = connection->context;
int write_completed = FALSE;

guint ret = gatt_write_cmd(conn_context->attrib, handle, (void*)buffer, buffer_len,
gattlib_write_cmd_cb, &write_completed);
if (ret == 0) {
return 1;
}

// Wait for completion of the event
while(write_completed == FALSE) {
gattlib_iteration();
}

return 0;
}


int gattlib_write_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, const void* buffer, size_t buffer_len) {
uint16_t handle = 0;
int ret;
Expand All @@ -194,7 +222,7 @@ int gattlib_write_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, cons
return ret;
}

return gattlib_write_char_by_handle(connection, handle, buffer, sizeof(buffer));
return gattlib_write_char_by_handle(connection, handle, buffer, buffer_len);
}

int gattlib_notification_start(gatt_connection_t* connection, const uuid_t* uuid) {
Expand Down
23 changes: 22 additions & 1 deletion dbus/gattlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,20 @@ gboolean on_handle_device_property_change(
*/
gatt_connection_t *gattlib_connect(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu)
{
return gattlib_connect_timeout(src, dst, dest_type, sec_level, psm, mtu, CONNECT_TIMEOUT);
}

/**
* @param src Local Adaptater interface
* @param dst Remote Bluetooth address
* @param dst_type Set LE address type (either BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM)
* @param sec_level Set security level (either BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH)
* @param psm Specify the PSM for GATT/ATT over BR/EDR
* @param mtu Specify the MTU size
*/
gatt_connection_t *gattlib_connect_timeout(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu, int timeout_sec)
{
GError *error = NULL;
const char* adapter_name;
Expand Down Expand Up @@ -273,7 +287,7 @@ gatt_connection_t *gattlib_connect(const char *src, const char *dst,
G_CALLBACK (on_handle_device_property_change),
loop);

g_timeout_add_seconds (CONNECT_TIMEOUT, stop_scan_func, loop);
g_timeout_add_seconds (timeout_sec, stop_scan_func, loop);
g_main_loop_run(loop);
g_main_loop_unref(loop);

Expand All @@ -295,6 +309,13 @@ gatt_connection_t *gattlib_connect_async(const char *src, const char *dst,
return NULL;
}

gatt_connection_t *gattlib_connect_async_timeout(const char *src, const char *dst,
uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu,
gatt_connect_cb_t connect_cb, int timeout_sec)
{
return NULL;
}

int gattlib_disconnect(gatt_connection_t* connection) {
gattlib_context_t* conn_context = connection->context;
GError *error = NULL;
Expand Down
Loading