examples: Added examples to demonstrate gattlib

pull/5/head
Olivier Martin 2016-04-28 00:22:00 +01:00
parent fc58fc0307
commit 815bc72492
10 changed files with 342 additions and 191 deletions

View File

@ -56,6 +56,8 @@ add_library(gattlib SHARED ${gattlib_SRCS})
target_link_libraries(gattlib ${gattlib_LIBS})
# Examples
add_subdirectory(examples/discover)
add_subdirectory(examples/read_write)
add_subdirectory(examples/gatttool)
#

View File

@ -0,0 +1,30 @@
#
#
# GattLib - GATT Library
#
# Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
#
#
# 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
#
cmake_minimum_required(VERSION 2.6)
set(discover_LIBS gattlib)
set(discover_SRCS discover.c)
add_executable(discover ${discover_SRCS})
target_link_libraries(discover ${discover_LIBS})

View File

@ -0,0 +1,80 @@
/*
*
* GattLib - GATT Library
*
* Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
*
*
* 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 <stdio.h>
#include <stdlib.h>
#include "gattlib.h"
int main(int argc, char *argv[])
{
gatt_connection_t* connection;
gattlib_primary_service_t* services;
gattlib_characteristic_t* characteristics;
int services_count, characteristics_count;
char uuid_str[MAX_LEN_UUID_STR + 1];
int ret, i;
if (argc != 2) {
printf("%s <device_address>\n", argv[0]);
return 1;
}
connection = gattlib_connect(NULL, argv[1], BDADDR_LE_PUBLIC, BT_IO_SEC_LOW, 0, 0);
if (connection == NULL) {
fprintf(stderr, "Fail to connect to the bluetooth device.\n");
return 1;
}
ret = gattlib_discover_primary(connection, &services, &services_count);
if (ret != 0) {
fprintf(stderr, "Fail to discover primary services.\n");
return 1;
}
for (i = 0; i < services_count; i++) {
gattlib_uuid_to_string(&services[i].uuid, uuid_str, sizeof(uuid_str));
printf("service[%d] start_handle:%02x end_handle:%02x uuid:%s\n", i,
services[i].attr_handle_start, services[i].attr_handle_end,
uuid_str);
}
free(services);
ret = gattlib_discover_char(connection, &characteristics, &characteristics_count);
if (ret != 0) {
fprintf(stderr, "Fail to discover characteristics.\n");
return 1;
}
for (i = 0; i < characteristics_count; i++) {
gattlib_uuid_to_string(&characteristics[i].uuid, uuid_str, sizeof(uuid_str));
printf("characteristic[%d] properties:%02x value_handle:%04x uuid:%s\n", i,
characteristics[i].properties, characteristics[i].value_handle,
uuid_str);
}
free(characteristics);
gattlib_disconnect(connection);
return 0;
}

View File

@ -1,3 +1,25 @@
#
#
# GattLib - GATT Library
#
# Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
#
#
# 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
#
cmake_minimum_required(VERSION 2.6)
set(gatttool_LIBS gattlib readline)

View File

@ -40,6 +40,7 @@
#include "btio.h"
#include "gattrib.h"
#include "gatt.h"
#include "gattlib.h"
#include "gatttool.h"
static gchar *opt_src = NULL;
@ -72,68 +73,43 @@ struct characteristic_data {
uint16_t end;
};
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
{
GAttrib *attrib = user_data;
uint8_t opdu[ATT_MAX_MTU];
uint16_t handle, i, olen = 0;
void notification_handler(uint16_t handle, const uint8_t* data, size_t data_length) {
int i;
handle = att_get_u16(&pdu[1]);
g_print("Notification handle = 0x%04x value: ", handle);
switch (pdu[0]) {
case ATT_OP_HANDLE_NOTIFY:
g_print("Notification handle = 0x%04x value: ", handle);
break;
case ATT_OP_HANDLE_IND:
g_print("Indication handle = 0x%04x value: ", handle);
break;
default:
g_print("Invalid opcode\n");
return;
}
for (i = 3; i < len; i++)
g_print("%02x ", pdu[i]);
for (i = 0; i < data_length; i++)
g_print("%02x ", data[i]);
g_print("\n");
if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
return;
olen = enc_confirmation(opdu, sizeof(opdu));
if (olen > 0)
g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
rl_forced_update_display();
}
static gboolean listen_start(gpointer user_data)
{
GAttrib *attrib = user_data;
void indication_handler(uint16_t handle, const uint8_t* data, size_t data_length) {
int i;
g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
attrib, NULL);
g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
attrib, NULL);
g_print("Indication handle = 0x%04x value: ", handle);
return FALSE;
for (i = 0; i < data_length; i++)
g_print("%02x ", data[i]);
g_print("\n");
rl_forced_update_display();
}
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
static void connect_cb(gatt_connection_t* connection)
{
GAttrib *attrib;
if (err) {
g_printerr("%s\n", err->message);
if (connection == NULL) {
got_error = TRUE;
g_main_loop_quit(event_loop);
} else {
if (opt_listen) {
gattlib_register_notification(notification_handler);
gattlib_register_indication(indication_handler);
}
operation(connection->attrib);
}
attrib = g_attrib_new(io);
if (opt_listen)
g_idle_add(listen_start, attrib);
operation(attrib);
}
static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
@ -540,7 +516,9 @@ int main(int argc, char *argv[])
GOptionContext *context;
GOptionGroup *gatt_group, *params_group, *char_rw_group;
GError *gerr = NULL;
GIOChannel *chan;
gatt_connection_t *connection;
BtIOSecLevel sec_level;
uint8_t dest_type;
opt_dst_type = g_strdup("public");
opt_sec_level = g_strdup("low");
@ -601,9 +579,11 @@ int main(int argc, char *argv[])
goto done;
}
chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
dest_type = get_dest_type_from_str(opt_dst_type);
sec_level = get_sec_level_from_str(opt_sec_level);
connection = gattlib_connect_async(opt_src, opt_dst, dest_type, sec_level,
opt_psm, opt_mtu, connect_cb);
if (chan == NULL) {
if (connection == NULL) {
got_error = TRUE;
goto done;
}

View File

@ -21,9 +21,11 @@
*
*/
void notification_handler(uint16_t handle, const uint8_t* data, size_t data_length);
void indication_handler(uint16_t handle, const uint8_t* data, size_t data_length);
int interactive(const gchar *src, const gchar *dst, const gchar *dst_type,
gboolean le);
GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
const gchar *dst_type, const gchar *sec_level,
int psm, int mtu, BtIOConnect connect_cb);
size_t gatt_attr_data_from_string(const char *str, uint8_t **data);
uint8_t get_dest_type_from_str(const char* dst_type);
BtIOSecLevel get_sec_level_from_str(const char* sec_level);

View File

@ -35,10 +35,10 @@
#include "btio.h"
#include "gattrib.h"
#include "gatt.h"
#include "gattlib.h"
#include "gatttool.h"
static GIOChannel *iochannel = NULL;
static GAttrib *attrib = NULL;
static gatt_connection_t* g_connection = NULL;
static GMainLoop *event_loop;
static GString *prompt;
@ -99,55 +99,16 @@ static void set_state(enum state st)
rl_redisplay();
}
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
static void connect_cb(gatt_connection_t* connection)
{
uint8_t opdu[ATT_MAX_MTU];
uint16_t handle, i, olen;
handle = att_get_u16(&pdu[1]);
printf("\n");
switch (pdu[0]) {
case ATT_OP_HANDLE_NOTIFY:
printf("Notification handle = 0x%04x value: ", handle);
break;
case ATT_OP_HANDLE_IND:
printf("Indication handle = 0x%04x value: ", handle);
break;
default:
printf("Invalid opcode\n");
return;
}
for (i = 3; i < len; i++)
printf("%02x ", pdu[i]);
printf("\n");
rl_forced_update_display();
if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
return;
olen = enc_confirmation(opdu, sizeof(opdu));
if (olen > 0)
g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
}
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
if (err) {
printf("connect error: %s\n", err->message);
if (connection == NULL) {
set_state(STATE_DISCONNECTED);
return;
} else {
g_connection = connection;
gattlib_register_notification(notification_handler);
gattlib_register_indication(indication_handler);
set_state(STATE_CONNECTED);
}
attrib = g_attrib_new(iochannel);
g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
attrib, NULL);
g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
attrib, NULL);
set_state(STATE_CONNECTED);
}
static void disconnect_io()
@ -155,14 +116,9 @@ static void disconnect_io()
if (conn_state == STATE_DISCONNECTED)
return;
g_attrib_unref(attrib);
attrib = NULL;
gattlib_disconnect(g_connection);
opt_mtu = 0;
g_io_channel_shutdown(iochannel, FALSE, NULL);
g_io_channel_unref(iochannel);
iochannel = NULL;
set_state(STATE_DISCONNECTED);
}
@ -354,6 +310,10 @@ static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
static void cmd_connect(int argcp, char **argvp)
{
gatt_connection_t *connection;
BtIOSecLevel sec_level;
uint8_t dst_type;
if (conn_state != STATE_DISCONNECTED)
return;
@ -374,12 +334,15 @@ static void cmd_connect(int argcp, char **argvp)
}
set_state(STATE_CONNECTING);
iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
opt_psm, opt_mtu, connect_cb);
if (iochannel == NULL)
dst_type = get_dest_type_from_str(opt_dst_type);
sec_level = get_sec_level_from_str(opt_sec_level);
connection = gattlib_connect_async(opt_src, opt_dst, dst_type, sec_level,
opt_psm, opt_mtu, connect_cb);
if (connection == NULL)
set_state(STATE_DISCONNECTED);
else
g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL);
g_io_add_watch(g_connection->io, G_IO_HUP, channel_watcher, NULL);
}
static void cmd_disconnect(int argcp, char **argvp)
@ -397,7 +360,7 @@ static void cmd_primary(int argcp, char **argvp)
}
if (argcp == 1) {
gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
gatt_discover_primary(g_connection->attrib, NULL, primary_all_cb, NULL);
return;
}
@ -406,7 +369,7 @@ static void cmd_primary(int argcp, char **argvp)
return;
}
gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL);
gatt_discover_primary(g_connection->attrib, &uuid, primary_by_uuid_cb, NULL);
}
static int strtohandle(const char *src)
@ -456,11 +419,11 @@ static void cmd_char(int argcp, char **argvp)
return;
}
gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
gatt_discover_char(g_connection->attrib, start, end, &uuid, char_cb, NULL);
return;
}
gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
gatt_discover_char(g_connection->attrib, start, end, NULL, char_cb, NULL);
}
static void cmd_char_desc(int argcp, char **argvp)
@ -489,7 +452,7 @@ static void cmd_char_desc(int argcp, char **argvp)
}
}
gatt_find_info(attrib, start, end, char_desc_cb, NULL);
gatt_find_info(g_connection->attrib, start, end, char_desc_cb, NULL);
}
static void cmd_read_hnd(int argcp, char **argvp)
@ -524,7 +487,7 @@ static void cmd_read_hnd(int argcp, char **argvp)
}
}
gatt_read_char(attrib, handle, offset, char_read_cb, attrib);
gatt_read_char(g_connection->attrib, handle, offset, char_read_cb, g_connection->attrib);
}
static void cmd_read_uuid(int argcp, char **argvp)
@ -571,7 +534,7 @@ static void cmd_read_uuid(int argcp, char **argvp)
char_data->end = end;
char_data->uuid = uuid;
gatt_read_char_by_uuid(attrib, start, end, &char_data->uuid,
gatt_read_char_by_uuid(g_connection->attrib, start, end, &char_data->uuid,
char_read_by_uuid_cb, char_data);
}
@ -621,10 +584,10 @@ static void cmd_char_write(int argcp, char **argvp)
}
if (g_strcmp0("char-write-req", argvp[0]) == 0)
gatt_write_char(attrib, handle, value, plen,
gatt_write_char(g_connection->attrib, handle, value, plen,
char_write_req_cb, NULL);
else
gatt_write_char(attrib, handle, value, plen, NULL, NULL);
gatt_write_char(g_connection->attrib, handle, value, plen, NULL, NULL);
g_free(value);
}
@ -661,7 +624,7 @@ static void cmd_sec_level(int argcp, char **argvp)
return;
}
bt_io_set(iochannel, BT_IO_L2CAP, &gerr,
bt_io_set(g_connection->io, BT_IO_L2CAP, &gerr,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_INVALID);
@ -689,7 +652,7 @@ static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
mtu = MIN(mtu, opt_mtu);
/* Set new value for MTU in client */
if (g_attrib_set_mtu(attrib, mtu))
if (g_attrib_set_mtu(g_connection->attrib, mtu))
printf("MTU was exchanged successfully: %d\n", mtu);
else
printf("Error exchanging MTU\n");
@ -727,7 +690,7 @@ static void cmd_mtu(int argcp, char **argvp)
return;
}
gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
gatt_exchange_mtu(g_connection->attrib, opt_mtu, exchange_mtu_cb, NULL);
}
static struct {

View File

@ -30,77 +30,12 @@
#include <bluetooth/uuid.h>
#include <bluetooth/sdp.h>
#include "att.h"
#include "gattlib.h"
#include "gattrib.h"
#include "gatt.h"
#include "btio.h"
#include "gatttool.h"
GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
const gchar *dst_type, const gchar *sec_level,
int psm, int mtu, BtIOConnect connect_cb)
{
GIOChannel *chan;
bdaddr_t sba, dba;
uint8_t dest_type;
GError *err = NULL;
BtIOSecLevel sec;
/* Remote device */
if (dst == NULL) {
g_printerr("Remote Bluetooth address required\n");
return NULL;
}
str2ba(dst, &dba);
/* Local adapter */
if (src != NULL) {
if (!strncmp(src, "hci", 3))
hci_devba(atoi(src + 3), &sba);
else
str2ba(src, &sba);
} else
bacpy(&sba, BDADDR_ANY);
/* Not used for BR/EDR */
if (strcmp(dst_type, "random") == 0)
dest_type = BDADDR_LE_RANDOM;
else
dest_type = BDADDR_LE_PUBLIC;
if (strcmp(sec_level, "medium") == 0)
sec = BT_IO_SEC_MEDIUM;
else if (strcmp(sec_level, "high") == 0)
sec = BT_IO_SEC_HIGH;
else
sec = BT_IO_SEC_LOW;
if (psm == 0)
chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &sba,
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,
BT_IO_OPT_INVALID);
else
chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
BT_IO_OPT_PSM, psm,
BT_IO_OPT_IMTU, mtu,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_INVALID);
if (err) {
g_printerr("%s\n", err->message);
g_error_free(err);
return NULL;
}
return chan;
}
size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
{
char tmp[3];
@ -119,3 +54,19 @@ size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
return size;
}
uint8_t get_dest_type_from_str(const char* dst_type) {
if (strcmp(dst_type, "random") == 0)
return BDADDR_LE_RANDOM;
else
return BDADDR_LE_PUBLIC;
}
BtIOSecLevel get_sec_level_from_str(const char* sec_level) {
if (strcasecmp(sec_level, "medium") == 0)
return BT_IO_SEC_MEDIUM;
else if (strcasecmp(sec_level, "high") == 0)
return BT_IO_SEC_HIGH;
else
return BT_IO_SEC_LOW;
}

View File

@ -0,0 +1,30 @@
#
#
# GattLib - GATT Library
#
# Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
#
#
# 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
#
cmake_minimum_required(VERSION 2.6)
set(read_write_LIBS gattlib)
set(read_write_SRCS read_write.c)
add_executable(read_write ${read_write_SRCS})
target_link_libraries(read_write ${read_write_LIBS})

View File

@ -0,0 +1,91 @@
/*
*
* GattLib - GATT Library
*
* Copyright (C) 2016 Olivier Martin <olivier@labapart.org>
*
*
* 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 <assert.h>
#include <stdio.h>
#include "gattlib.h"
typedef enum { READ, WRITE} operation_t;
operation_t g_operation;
static bt_uuid_t g_uuid;
long int value_data;
static void usage(char *argv[]) {
printf("%s <device_address> <read|write> <uuid> [<hex-value-to-write>]\n", argv[0]);
}
int main(int argc, char *argv[]) {
uint8_t buffer[100];
int i, len, ret;
gatt_connection_t* connection;
if ((argc != 4) && (argc != 5)) {
usage(argv);
return 1;
}
if (strcmp(argv[2], "read") == 0) {
g_operation = READ;
} else if ((strcmp(argv[2], "write") == 0) && (argc == 5)) {
g_operation = WRITE;
if ((strlen(argv[4]) >= 2) && (argv[4][0] == '0') && (argv[4][0] == 'x')) {
value_data = strtol(argv[4], NULL, 0);
} else {
value_data = strtol(argv[4], NULL, 16);
}
printf("Value to write: 0x%lx\n", value_data);
} else {
usage(argv);
return 1;
}
if (bt_string_to_uuid(&g_uuid, argv[3]) < 0) {
usage(argv);
return 1;
}
connection = gattlib_connect(NULL, argv[1], BDADDR_LE_PUBLIC, BT_IO_SEC_LOW, 0, 0);
if (connection == NULL) {
fprintf(stderr, "Fail to connect to the bluetooth device.\n");
return 1;
}
if (g_operation == READ) {
len = gattlib_read_char_by_uuid(connection, &g_uuid, buffer, sizeof(buffer));
printf("Read UUID completed: ");
for (i = 0; i < len; i++)
printf("%02x ", buffer[i]);
printf("\n");
} else {
uint16_t handle = 0; //TODO: FIXME
ret = gattlib_write_char_by_handle(connection, handle, buffer, sizeof(buffer));
assert(ret == 0);
}
gattlib_disconnect(connection);
return 0;
}