mirror of https://github.com/labapart/gattlib
examples: Added examples to demonstrate gattlib
parent
fc58fc0307
commit
815bc72492
|
@ -56,6 +56,8 @@ add_library(gattlib SHARED ${gattlib_SRCS})
|
||||||
target_link_libraries(gattlib ${gattlib_LIBS})
|
target_link_libraries(gattlib ${gattlib_LIBS})
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
add_subdirectory(examples/discover)
|
||||||
|
add_subdirectory(examples/read_write)
|
||||||
add_subdirectory(examples/gatttool)
|
add_subdirectory(examples/gatttool)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -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})
|
|
@ -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;
|
||||||
|
}
|
|
@ -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)
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
set(gatttool_LIBS gattlib readline)
|
set(gatttool_LIBS gattlib readline)
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "btio.h"
|
#include "btio.h"
|
||||||
#include "gattrib.h"
|
#include "gattrib.h"
|
||||||
#include "gatt.h"
|
#include "gatt.h"
|
||||||
|
#include "gattlib.h"
|
||||||
#include "gatttool.h"
|
#include "gatttool.h"
|
||||||
|
|
||||||
static gchar *opt_src = NULL;
|
static gchar *opt_src = NULL;
|
||||||
|
@ -72,68 +73,43 @@ struct characteristic_data {
|
||||||
uint16_t end;
|
uint16_t end;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
|
void notification_handler(uint16_t handle, const uint8_t* data, size_t data_length) {
|
||||||
{
|
int i;
|
||||||
GAttrib *attrib = user_data;
|
|
||||||
uint8_t opdu[ATT_MAX_MTU];
|
|
||||||
uint16_t handle, i, olen = 0;
|
|
||||||
|
|
||||||
handle = att_get_u16(&pdu[1]);
|
g_print("Notification handle = 0x%04x value: ", handle);
|
||||||
|
|
||||||
switch (pdu[0]) {
|
for (i = 0; i < data_length; i++)
|
||||||
case ATT_OP_HANDLE_NOTIFY:
|
g_print("%02x ", data[i]);
|
||||||
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]);
|
|
||||||
|
|
||||||
g_print("\n");
|
g_print("\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 gboolean listen_start(gpointer user_data)
|
void indication_handler(uint16_t handle, const uint8_t* data, size_t data_length) {
|
||||||
{
|
int i;
|
||||||
GAttrib *attrib = user_data;
|
|
||||||
|
|
||||||
g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
|
g_print("Indication handle = 0x%04x value: ", handle);
|
||||||
attrib, NULL);
|
|
||||||
g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
|
|
||||||
attrib, NULL);
|
|
||||||
|
|
||||||
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 (connection == NULL) {
|
||||||
|
|
||||||
if (err) {
|
|
||||||
g_printerr("%s\n", err->message);
|
|
||||||
got_error = TRUE;
|
got_error = TRUE;
|
||||||
g_main_loop_quit(event_loop);
|
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)
|
static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
|
||||||
|
@ -540,7 +516,9 @@ int main(int argc, char *argv[])
|
||||||
GOptionContext *context;
|
GOptionContext *context;
|
||||||
GOptionGroup *gatt_group, *params_group, *char_rw_group;
|
GOptionGroup *gatt_group, *params_group, *char_rw_group;
|
||||||
GError *gerr = NULL;
|
GError *gerr = NULL;
|
||||||
GIOChannel *chan;
|
gatt_connection_t *connection;
|
||||||
|
BtIOSecLevel sec_level;
|
||||||
|
uint8_t dest_type;
|
||||||
|
|
||||||
opt_dst_type = g_strdup("public");
|
opt_dst_type = g_strdup("public");
|
||||||
opt_sec_level = g_strdup("low");
|
opt_sec_level = g_strdup("low");
|
||||||
|
@ -601,9 +579,11 @@ int main(int argc, char *argv[])
|
||||||
goto done;
|
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);
|
opt_psm, opt_mtu, connect_cb);
|
||||||
if (chan == NULL) {
|
if (connection == NULL) {
|
||||||
got_error = TRUE;
|
got_error = TRUE;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
int interactive(const gchar *src, const gchar *dst, const gchar *dst_type,
|
||||||
gboolean le);
|
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);
|
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);
|
||||||
|
|
|
@ -35,10 +35,10 @@
|
||||||
#include "btio.h"
|
#include "btio.h"
|
||||||
#include "gattrib.h"
|
#include "gattrib.h"
|
||||||
#include "gatt.h"
|
#include "gatt.h"
|
||||||
|
#include "gattlib.h"
|
||||||
#include "gatttool.h"
|
#include "gatttool.h"
|
||||||
|
|
||||||
static GIOChannel *iochannel = NULL;
|
static gatt_connection_t* g_connection = NULL;
|
||||||
static GAttrib *attrib = NULL;
|
|
||||||
static GMainLoop *event_loop;
|
static GMainLoop *event_loop;
|
||||||
static GString *prompt;
|
static GString *prompt;
|
||||||
|
|
||||||
|
@ -99,55 +99,16 @@ static void set_state(enum state st)
|
||||||
rl_redisplay();
|
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];
|
if (connection == NULL) {
|
||||||
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);
|
|
||||||
set_state(STATE_DISCONNECTED);
|
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()
|
static void disconnect_io()
|
||||||
|
@ -155,14 +116,9 @@ static void disconnect_io()
|
||||||
if (conn_state == STATE_DISCONNECTED)
|
if (conn_state == STATE_DISCONNECTED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_attrib_unref(attrib);
|
gattlib_disconnect(g_connection);
|
||||||
attrib = NULL;
|
|
||||||
opt_mtu = 0;
|
opt_mtu = 0;
|
||||||
|
|
||||||
g_io_channel_shutdown(iochannel, FALSE, NULL);
|
|
||||||
g_io_channel_unref(iochannel);
|
|
||||||
iochannel = NULL;
|
|
||||||
|
|
||||||
set_state(STATE_DISCONNECTED);
|
set_state(STATE_DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,6 +310,10 @@ static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
|
||||||
|
|
||||||
static void cmd_connect(int argcp, char **argvp)
|
static void cmd_connect(int argcp, char **argvp)
|
||||||
{
|
{
|
||||||
|
gatt_connection_t *connection;
|
||||||
|
BtIOSecLevel sec_level;
|
||||||
|
uint8_t dst_type;
|
||||||
|
|
||||||
if (conn_state != STATE_DISCONNECTED)
|
if (conn_state != STATE_DISCONNECTED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -374,12 +334,15 @@ static void cmd_connect(int argcp, char **argvp)
|
||||||
}
|
}
|
||||||
|
|
||||||
set_state(STATE_CONNECTING);
|
set_state(STATE_CONNECTING);
|
||||||
iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
|
|
||||||
opt_psm, opt_mtu, connect_cb);
|
dst_type = get_dest_type_from_str(opt_dst_type);
|
||||||
if (iochannel == NULL)
|
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);
|
set_state(STATE_DISCONNECTED);
|
||||||
else
|
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)
|
static void cmd_disconnect(int argcp, char **argvp)
|
||||||
|
@ -397,7 +360,7 @@ static void cmd_primary(int argcp, char **argvp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argcp == 1) {
|
if (argcp == 1) {
|
||||||
gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
|
gatt_discover_primary(g_connection->attrib, NULL, primary_all_cb, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +369,7 @@ static void cmd_primary(int argcp, char **argvp)
|
||||||
return;
|
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)
|
static int strtohandle(const char *src)
|
||||||
|
@ -456,11 +419,11 @@ static void cmd_char(int argcp, char **argvp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
|
gatt_discover_char(g_connection->attrib, start, end, &uuid, char_cb, NULL);
|
||||||
return;
|
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)
|
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)
|
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)
|
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->end = end;
|
||||||
char_data->uuid = uuid;
|
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);
|
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)
|
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);
|
char_write_req_cb, NULL);
|
||||||
else
|
else
|
||||||
gatt_write_char(attrib, handle, value, plen, NULL, NULL);
|
gatt_write_char(g_connection->attrib, handle, value, plen, NULL, NULL);
|
||||||
|
|
||||||
g_free(value);
|
g_free(value);
|
||||||
}
|
}
|
||||||
|
@ -661,7 +624,7 @@ static void cmd_sec_level(int argcp, char **argvp)
|
||||||
return;
|
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_SEC_LEVEL, sec_level,
|
||||||
BT_IO_OPT_INVALID);
|
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);
|
mtu = MIN(mtu, opt_mtu);
|
||||||
/* Set new value for MTU in client */
|
/* 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);
|
printf("MTU was exchanged successfully: %d\n", mtu);
|
||||||
else
|
else
|
||||||
printf("Error exchanging MTU\n");
|
printf("Error exchanging MTU\n");
|
||||||
|
@ -727,7 +690,7 @@ static void cmd_mtu(int argcp, char **argvp)
|
||||||
return;
|
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 {
|
static struct {
|
||||||
|
|
|
@ -30,77 +30,12 @@
|
||||||
#include <bluetooth/uuid.h>
|
#include <bluetooth/uuid.h>
|
||||||
#include <bluetooth/sdp.h>
|
#include <bluetooth/sdp.h>
|
||||||
|
|
||||||
#include "att.h"
|
#include "gattlib.h"
|
||||||
|
|
||||||
#include "gattrib.h"
|
#include "gattrib.h"
|
||||||
#include "gatt.h"
|
|
||||||
#include "btio.h"
|
#include "btio.h"
|
||||||
#include "gatttool.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)
|
size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
|
||||||
{
|
{
|
||||||
char tmp[3];
|
char tmp[3];
|
||||||
|
@ -119,3 +54,19 @@ size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
|
||||||
|
|
||||||
return size;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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})
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue