From 815bc72492ec66dfba2dcb8b35dc2fcb72d3fb4b Mon Sep 17 00:00:00 2001 From: Olivier Martin Date: Thu, 28 Apr 2016 00:22:00 +0100 Subject: [PATCH] examples: Added examples to demonstrate gattlib --- CMakeLists.txt | 2 + examples/discover/CMakeLists.txt | 30 +++++++++ examples/discover/discover.c | 80 ++++++++++++++++++++++ examples/gatttool/CMakeLists.txt | 22 ++++++ examples/gatttool/gatttool.c | 82 +++++++++-------------- examples/gatttool/gatttool.h | 8 ++- examples/gatttool/interactive.c | 103 +++++++++-------------------- examples/gatttool/utils.c | 85 +++++------------------- examples/read_write/CMakeLists.txt | 30 +++++++++ examples/read_write/read_write.c | 91 +++++++++++++++++++++++++ 10 files changed, 342 insertions(+), 191 deletions(-) create mode 100644 examples/discover/CMakeLists.txt create mode 100644 examples/discover/discover.c create mode 100644 examples/read_write/CMakeLists.txt create mode 100644 examples/read_write/read_write.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5749b71..dd72969 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) # diff --git a/examples/discover/CMakeLists.txt b/examples/discover/CMakeLists.txt new file mode 100644 index 0000000..4fdd99d --- /dev/null +++ b/examples/discover/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# +# GattLib - GATT Library +# +# Copyright (C) 2016 Olivier Martin +# +# +# 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}) diff --git a/examples/discover/discover.c b/examples/discover/discover.c new file mode 100644 index 0000000..6b8b109 --- /dev/null +++ b/examples/discover/discover.c @@ -0,0 +1,80 @@ +/* + * + * GattLib - GATT Library + * + * Copyright (C) 2016 Olivier Martin + * + * + * 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 +#include + +#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 \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; +} diff --git a/examples/gatttool/CMakeLists.txt b/examples/gatttool/CMakeLists.txt index d544850..5efa86e 100644 --- a/examples/gatttool/CMakeLists.txt +++ b/examples/gatttool/CMakeLists.txt @@ -1,3 +1,25 @@ +# +# +# GattLib - GATT Library +# +# Copyright (C) 2016 Olivier Martin +# +# +# 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) diff --git a/examples/gatttool/gatttool.c b/examples/gatttool/gatttool.c index 8a43ec1..a0e8a18 100644 --- a/examples/gatttool/gatttool.c +++ b/examples/gatttool/gatttool.c @@ -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; } diff --git a/examples/gatttool/gatttool.h b/examples/gatttool/gatttool.h index a38339b..d201bc1 100644 --- a/examples/gatttool/gatttool.h +++ b/examples/gatttool/gatttool.h @@ -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); diff --git a/examples/gatttool/interactive.c b/examples/gatttool/interactive.c index 0a01cdf..0df8872 100644 --- a/examples/gatttool/interactive.c +++ b/examples/gatttool/interactive.c @@ -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 { diff --git a/examples/gatttool/utils.c b/examples/gatttool/utils.c index d856fe2..0d9e4c2 100644 --- a/examples/gatttool/utils.c +++ b/examples/gatttool/utils.c @@ -30,77 +30,12 @@ #include #include -#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; +} diff --git a/examples/read_write/CMakeLists.txt b/examples/read_write/CMakeLists.txt new file mode 100644 index 0000000..e0bba4f --- /dev/null +++ b/examples/read_write/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# +# GattLib - GATT Library +# +# Copyright (C) 2016 Olivier Martin +# +# +# 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}) diff --git a/examples/read_write/read_write.c b/examples/read_write/read_write.c new file mode 100644 index 0000000..073547a --- /dev/null +++ b/examples/read_write/read_write.c @@ -0,0 +1,91 @@ +/* + * + * GattLib - GATT Library + * + * Copyright (C) 2016 Olivier Martin + * + * + * 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 +#include + +#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 []\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; +}