All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client.
@ 2014-08-30 16:02 Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 1/7] tools/btgatt-client: " Arman Uguray
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

* v2: Rebased on latest master & fixed conflicts in Makefile.tools
* v1: Removed L2CAP_LM socket options; using only BT_SECURITY now.

Arman Uguray (7):
  tools/btgatt-client: Introduce tools/btgatt-client
  tools/btgatt-client: Added L2CAP LE ATT connection.
  tools/btgatt-client: Integrate bt_att and bt_gatt_client.
  tools/btgatt-client: Add command parsing.
  tools/btgatt-client: Add "ready" handler.
  tools/btgatt-client: Add "services" command.
  tools/btgatt: Add command argument support.

 Makefile.tools        |  13 +-
 tools/btgatt-client.c | 657 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 669 insertions(+), 1 deletion(-)
 create mode 100644 tools/btgatt-client.c

-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 1/7] tools/btgatt-client: Introduce tools/btgatt-client
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 2/7] tools/btgatt-client: Added L2CAP LE ATT connection Arman Uguray
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This is the initial commit for tools/btgatt-client.c, a command line tool to
interactively test and debug shared/gatt-client.
---
 Makefile.tools        |  13 ++-
 tools/btgatt-client.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 230 insertions(+), 1 deletion(-)
 create mode 100644 tools/btgatt-client.c

diff --git a/Makefile.tools b/Makefile.tools
index 3616a46..bf637e2 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -179,7 +179,7 @@ if TOOLS
 bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
 			tools/rfcomm tools/rctest tools/l2test tools/l2ping \
 			tools/sdptool tools/ciptool tools/bccmd \
-			tools/bluemoon tools/mpris-proxy
+			tools/bluemoon tools/mpris-proxy tools/btgatt-client
 
 tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
 						tools/hciattach_st.c \
@@ -254,6 +254,17 @@ tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \
 tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
 tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
 
+tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c \
+				monitor/mainloop.h monitor/mainloop.c \
+				src/shared/io.h src/shared/io-mainloop.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/timeout.h src/shared/timeout-mainloop.c \
+				src/shared/att-types.h src/shared/att.h src/shared/att.c \
+				src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
+				src/shared/gatt-client.h src/shared/gatt-client.c
+tools_btgatt_client_LDADD = lib/libbluetooth-internal.la
+
 dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
 			tools/hcitool.1 tools/hcidump.1 \
 			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
new file mode 100644
index 0000000..a5ce70b
--- /dev/null
+++ b/tools/btgatt-client.c
@@ -0,0 +1,218 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <limits.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "monitor/mainloop.h"
+
+static bool verbose = false;
+
+static void print_prompt(void)
+{
+	printf("[GATT client]# ");
+	fflush(stdout);
+}
+
+static void prompt_read_cb(int fd, uint32_t events, void *user_data)
+{
+	size_t len = 0;
+	char *line = NULL;
+
+	if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+		mainloop_quit();
+		return;
+	}
+
+	if (getline(&line, &len, stdin) == -1)
+		return;
+
+	/* TODO: Process commands here */
+	printf("  Typed line: %s\n", line);
+	print_prompt();
+
+	free(line);
+}
+
+static void usage(void)
+{
+	printf("btgatt-client\n");
+	printf("Usage:\n\tbtgatt-client [options]\n");
+
+	printf("Options:\n"
+		"\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
+		"\t-d, --dest <addr>\t\tSpecify the destination address\n"
+		"\t-t, --type [random|public] \tSpecify the LE address type\n"
+		"\t-m, --mtu <mtu> \t\tThe ATT MTU to use\n"
+		"\t-s, --security-level <sec> \tSet security level (low|"
+								"medium|high)\n"
+		"\t-v, --verbose\t\t\tEnable extra logging\n"
+		"\t-h, --help\t\t\tDisplay help\n");
+}
+
+static struct option main_options[] = {
+	{ "index",		1, 0, 'i' },
+	{ "dest",		1, 0, 'd' },
+	{ "type",		1, 0, 't' },
+	{ "mtu",		1, 0, 'm' },
+	{ "security-level",	1, 0, 's' },
+	{ "verbose",		0, 0, 'v' },
+	{ "help",		0, 0, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	int sec;
+	uint16_t mtu = 0;
+	uint8_t dst_type = BDADDR_LE_PUBLIC;
+	bool dst_addr_given = false;
+	bdaddr_t src_addr, dst_addr;
+	int dev_id = -1;
+
+	while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
+						main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		case 'v':
+			verbose = true;
+			break;
+		case 's':
+			if (strcmp(optarg, "low") == 0)
+				sec = BT_SECURITY_LOW;
+			else if (strcmp(optarg, "medium") == 0)
+				sec = BT_SECURITY_MEDIUM;
+			else if (strcmp(optarg, "high") == 0)
+				sec = BT_SECURITY_HIGH;
+			else {
+				fprintf(stderr, "Invalid security level\n");
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'm': {
+			int arg;
+
+			arg = atoi(optarg);
+			if (arg <= 0) {
+				fprintf(stderr, "Invalid MTU: %d\n", arg);
+				return EXIT_FAILURE;
+			}
+
+			if (arg > UINT16_MAX) {
+				fprintf(stderr, "MTU too large: %d\n", arg);
+				return EXIT_FAILURE;
+			}
+
+			mtu = (uint16_t)arg;
+			break;
+		}
+		case 't':
+			if (strcmp(optarg, "random") == 0)
+				dst_type = BDADDR_LE_RANDOM;
+			else if (strcmp(optarg, "public") == 0)
+				dst_type = BDADDR_LE_PUBLIC;
+			else {
+				fprintf(stderr,
+					"Allowed types: random, public\n");
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'd':
+			if (str2ba(optarg, &dst_addr) < 0) {
+				fprintf(stderr, "Invalid remote address: %s\n",
+									optarg);
+				return EXIT_FAILURE;
+			}
+
+			dst_addr_given = true;
+			break;
+
+		case 'i':
+			dev_id = hci_devid(optarg);
+			if (dev_id < 0) {
+				perror("Invalid adapter");
+				return EXIT_FAILURE;
+			}
+
+			break;
+		default:
+			fprintf(stderr, "Invalid option: %c\n", opt);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!argc) {
+		usage();
+		return EXIT_SUCCESS;
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc) {
+		usage();
+		return EXIT_SUCCESS;
+	}
+
+	if (dev_id == -1)
+		bacpy(&src_addr, BDADDR_ANY);
+	else if (hci_devba(dev_id, &src_addr) < 0) {
+		perror("Adapter not available");
+		return EXIT_FAILURE;
+	}
+
+	if (!dst_addr_given) {
+		fprintf(stderr, "Destination address required!\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	if (mainloop_add_fd(fileno(stdin),
+				EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
+				prompt_read_cb, NULL, NULL) < 0) {
+		fprintf(stderr, "Failed to initialize console\n");
+		return EXIT_FAILURE;
+	}
+
+	print_prompt();
+
+	mainloop_run();
+
+	return EXIT_SUCCESS;
+}
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 2/7] tools/btgatt-client: Added L2CAP LE ATT connection.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 1/7] tools/btgatt-client: " Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 3/7] tools/btgatt-client: Integrate bt_att and bt_gatt_client Arman Uguray
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch adds support for connection establishment with different security
levels. Currently, only LE connections are supported.
---
 tools/btgatt-client.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index a5ce70b..30e8e50 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -28,15 +28,20 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <getopt.h>
 #include <limits.h>
+#include <errno.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
 
 #include "monitor/mainloop.h"
 
+#define ATT_CID 4
+
 static bool verbose = false;
 
 static void print_prompt(void)
@@ -65,6 +70,74 @@ static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 	free(line);
 }
 
+static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
+									int sec)
+{
+	int sock;
+	struct sockaddr_l2 srcaddr, dstaddr;
+	struct bt_security btsec;
+
+	if (verbose) {
+		char srcaddr_str[18], dstaddr_str[18];
+
+		ba2str(src, srcaddr_str);
+		ba2str(dst, dstaddr_str);
+
+		printf("Opening L2CAP LE connection on ATT channel:\n"
+						"\t src: %s\n\tdest: %s\n",
+						srcaddr_str, dstaddr_str);
+	}
+
+	sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sock < 0) {
+		perror("Failed to create L2CAP socket");
+		return -1;
+	}
+
+	/* Set up source address */
+	memset(&srcaddr, 0, sizeof(srcaddr));
+	srcaddr.l2_family = AF_BLUETOOTH;
+	srcaddr.l2_cid = htobs(ATT_CID);
+	srcaddr.l2_bdaddr_type = 0;
+	bacpy(&srcaddr.l2_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
+		perror("Failed to bind L2CAP socket");
+		close(sock);
+		return -1;
+	}
+
+	/* Set the security level */
+	memset(&btsec, 0, sizeof(btsec));
+	btsec.level = sec;
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
+							sizeof(btsec)) != 0) {
+		fprintf(stderr, "Failed to set L2CAP security level\n");
+		close(sock);
+		return -1;
+	}
+
+	/* Set up destination address */
+	memset(&dstaddr, 0, sizeof(dstaddr));
+	dstaddr.l2_family = AF_BLUETOOTH;
+	dstaddr.l2_cid = htobs(ATT_CID);
+	dstaddr.l2_bdaddr_type = dst_type;
+	bacpy(&dstaddr.l2_bdaddr, dst);
+
+	printf("Connecting to device...");
+	fflush(stdout);
+
+	if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
+		perror(" Failed to connect");
+		close(sock);
+		return -1;
+	}
+
+	printf(" Done\n");
+
+	return sock;
+}
+
 static void usage(void)
 {
 	printf("btgatt-client\n");
@@ -95,12 +168,13 @@ static struct option main_options[] = {
 int main(int argc, char *argv[])
 {
 	int opt;
-	int sec;
+	int sec = BT_SECURITY_LOW;
 	uint16_t mtu = 0;
 	uint8_t dst_type = BDADDR_LE_PUBLIC;
 	bool dst_addr_given = false;
 	bdaddr_t src_addr, dst_addr;
 	int dev_id = -1;
+	int fd;
 
 	while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
 						main_options, NULL)) != -1) {
@@ -203,6 +277,10 @@ int main(int argc, char *argv[])
 
 	mainloop_init();
 
+	fd = l2cap_le_att_connect(&src_addr, &dst_addr, dst_type, sec);
+	if (fd < 0)
+		return EXIT_FAILURE;
+
 	if (mainloop_add_fd(fileno(stdin),
 				EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
 				prompt_read_cb, NULL, NULL) < 0) {
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 3/7] tools/btgatt-client: Integrate bt_att and bt_gatt_client.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 1/7] tools/btgatt-client: " Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 2/7] tools/btgatt-client: Added L2CAP LE ATT connection Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 4/7] tools/btgatt-client: Add command parsing Arman Uguray
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

This patch adds the initial set up of a bt_att and bt_gatt_client around the
connection socket.
---
 tools/btgatt-client.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 114 insertions(+), 3 deletions(-)

diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 30e8e50..008cc9f 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -39,17 +39,98 @@
 #include <bluetooth/l2cap.h>
 
 #include "monitor/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-client.h"
 
 #define ATT_CID 4
 
+#define PRLOG(format, ...) \
+	printf(format, __VA_ARGS__); print_prompt();
+
 static bool verbose = false;
 
+struct client {
+	int fd;
+	struct bt_gatt_client *gatt;
+};
+
 static void print_prompt(void)
 {
 	printf("[GATT client]# ");
 	fflush(stdout);
 }
 
+static void att_disconnect_cb(void *user_data)
+{
+	printf("Device disconnected\n");
+
+	mainloop_quit();
+}
+
+static void att_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	PRLOG("%s%s\n", prefix, str);
+}
+
+static struct client *client_create(int fd, uint16_t mtu)
+{
+	struct client *cli;
+	struct bt_att *att;
+
+	cli = new0(struct client, 1);
+	if (!cli) {
+		fprintf(stderr, "Failed to allocate memory for client\n");
+		return NULL;
+	}
+
+	att = bt_att_new(fd);
+	if (!att) {
+		fprintf(stderr, "Failed to initialze ATT transport layer\n");
+		bt_att_unref(att);
+		free(cli);
+		return NULL;
+	}
+
+	if (!bt_att_set_close_on_unref(att, true)) {
+		fprintf(stderr, "Failed to set up ATT transport layer\n");
+		bt_att_unref(att);
+		free(cli);
+		return NULL;
+	}
+
+	if (!bt_att_register_disconnect(att, att_disconnect_cb, NULL, NULL)) {
+		fprintf(stderr, "Failed to set ATT disconnect handler\n");
+		bt_att_unref(att);
+		free(cli);
+		return NULL;
+	}
+
+	if (verbose)
+		bt_att_set_debug(att, att_debug, "att: ", NULL);
+
+	cli->fd = fd;
+	cli->gatt = bt_gatt_client_new(att, mtu);
+	if (!cli->gatt) {
+		fprintf(stderr, "Failed to create GATT client\n");
+		bt_att_unref(att);
+		free(cli);
+		return NULL;
+	}
+
+	/* bt_gatt_client already holds a reference */
+	bt_att_unref(att);
+
+	return cli;
+}
+
+static void client_destroy(struct client *cli)
+{
+	bt_gatt_client_unref(cli->gatt);
+}
+
 static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 {
 	size_t len = 0;
@@ -70,6 +151,18 @@ static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 	free(line);
 }
 
+static void signal_cb(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	default:
+		break;
+	}
+}
+
 static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
 									int sec)
 {
@@ -83,9 +176,9 @@ static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
 		ba2str(src, srcaddr_str);
 		ba2str(dst, dstaddr_str);
 
-		printf("Opening L2CAP LE connection on ATT channel:\n"
-						"\t src: %s\n\tdest: %s\n",
-						srcaddr_str, dstaddr_str);
+		printf("btgatt-client: Opening L2CAP LE connection on ATT "
+					"channel:\n\t src: %s\n\tdest: %s\n",
+					srcaddr_str, dstaddr_str);
 	}
 
 	sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
@@ -175,6 +268,8 @@ int main(int argc, char *argv[])
 	bdaddr_t src_addr, dst_addr;
 	int dev_id = -1;
 	int fd;
+	sigset_t mask;
+	struct client *cli;
 
 	while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
 						main_options, NULL)) != -1) {
@@ -281,6 +376,12 @@ int main(int argc, char *argv[])
 	if (fd < 0)
 		return EXIT_FAILURE;
 
+	cli = client_create(fd, mtu);
+	if (!cli) {
+		close(fd);
+		return EXIT_FAILURE;
+	}
+
 	if (mainloop_add_fd(fileno(stdin),
 				EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
 				prompt_read_cb, NULL, NULL) < 0) {
@@ -288,9 +389,19 @@ int main(int argc, char *argv[])
 		return EXIT_FAILURE;
 	}
 
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_cb, NULL, NULL);
+
 	print_prompt();
 
 	mainloop_run();
 
+	printf("\n\nShutting down...\n");
+
+	client_destroy(cli);
+
 	return EXIT_SUCCESS;
 }
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 4/7] tools/btgatt-client: Add command parsing.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
                   ` (2 preceding siblings ...)
  2014-08-30 16:02 ` [PATCH BlueZ v2 3/7] tools/btgatt-client: Integrate bt_att and bt_gatt_client Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 5/7] tools/btgatt-client: Add "ready" handler Arman Uguray
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

Added basic support for command parsing, including the "help" command. Also
added a call to bt_gatt_client_set_debug.
---
 tools/btgatt-client.c | 53 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 45 insertions(+), 8 deletions(-)

diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 008cc9f..26f2d14 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -68,7 +68,7 @@ static void att_disconnect_cb(void *user_data)
 	mainloop_quit();
 }
 
-static void att_debug(const char *str, void *user_data)
+static void debug_cb(const char *str, void *user_data)
 {
 	const char *prefix = user_data;
 
@@ -108,9 +108,6 @@ static struct client *client_create(int fd, uint16_t mtu)
 		return NULL;
 	}
 
-	if (verbose)
-		bt_att_set_debug(att, att_debug, "att: ", NULL);
-
 	cli->fd = fd;
 	cli->gatt = bt_gatt_client_new(att, mtu);
 	if (!cli->gatt) {
@@ -120,6 +117,12 @@ static struct client *client_create(int fd, uint16_t mtu)
 		return NULL;
 	}
 
+	if (verbose) {
+		bt_att_set_debug(att, debug_cb, "att: ", NULL);
+		bt_gatt_client_set_debug(cli->gatt, debug_cb, "gatt-client: ",
+									NULL);
+	}
+
 	/* bt_gatt_client already holds a reference */
 	bt_att_unref(att);
 
@@ -131,21 +134,55 @@ static void client_destroy(struct client *cli)
 	bt_gatt_client_unref(cli->gatt);
 }
 
+
+typedef void (*command_func_t)(struct client *cli);
+
+static void cmd_help(struct client *cli);
+
+static struct {
+	char *cmd;
+	command_func_t func;
+	char *doc;
+} command[] = {
+	{ "help", cmd_help, "\tDisplay help message" },
+	{ }
+};
+
+static void cmd_help(struct client *cli)
+{
+	int i;
+
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+}
+
 static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 {
+	ssize_t read;
 	size_t len = 0;
 	char *line = NULL;
+	struct client *cli = user_data;
+	int i;
 
 	if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
 		mainloop_quit();
 		return;
 	}
 
-	if (getline(&line, &len, stdin) == -1)
+	if ((read = getline(&line, &len, stdin)) == -1)
 		return;
 
-	/* TODO: Process commands here */
-	printf("  Typed line: %s\n", line);
+	for (i = 0; command[i].cmd; i++) {
+		if (strncmp(command[i].cmd, line, read - 1) == 0)
+			break;
+	}
+
+	if (command[i].cmd)
+		command[i].func(cli);
+	else
+		fprintf(stderr, "Unknown command: %s\n", line);
+
 	print_prompt();
 
 	free(line);
@@ -384,7 +421,7 @@ int main(int argc, char *argv[])
 
 	if (mainloop_add_fd(fileno(stdin),
 				EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
-				prompt_read_cb, NULL, NULL) < 0) {
+				prompt_read_cb, cli, NULL) < 0) {
 		fprintf(stderr, "Failed to initialize console\n");
 		return EXIT_FAILURE;
 	}
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 5/7] tools/btgatt-client: Add "ready" handler.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
                   ` (3 preceding siblings ...)
  2014-08-30 16:02 ` [PATCH BlueZ v2 4/7] tools/btgatt-client: Add command parsing Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 6/7] tools/btgatt-client: Add "services" command Arman Uguray
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

Added a ready handler which gets called by bt_gatt_client when the MTU exchange
and service discovery procedures complete. Also added some (bad) coloring for
verbose output.
---
 tools/btgatt-client.c | 48 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 40 insertions(+), 8 deletions(-)

diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 26f2d14..6299586 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -45,8 +45,17 @@
 
 #define ATT_CID 4
 
-#define PRLOG(format, ...) \
-	printf(format, __VA_ARGS__); print_prompt();
+#define PRLOG(...) \
+	printf(__VA_ARGS__); print_prompt();
+
+#define COLOR_OFF	"\x1B[0m"
+#define COLOR_RED	"\x1B[0;91m"
+#define COLOR_GREEN	"\x1B[0;92m"
+#define COLOR_YELLOW	"\x1B[0;93m"
+#define COLOR_BLUE	"\x1B[0;94m"
+#define COLOR_MAGENTA	"\x1B[0;95m"
+#define COLOR_BOLDGRAY	"\x1B[1;30m"
+#define COLOR_BOLDWHITE	"\x1B[1;37m"
 
 static bool verbose = false;
 
@@ -57,7 +66,7 @@ struct client {
 
 static void print_prompt(void)
 {
-	printf("[GATT client]# ");
+	printf(COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
 	fflush(stdout);
 }
 
@@ -68,13 +77,22 @@ static void att_disconnect_cb(void *user_data)
 	mainloop_quit();
 }
 
-static void debug_cb(const char *str, void *user_data)
+static void att_debug_cb(const char *str, void *user_data)
 {
 	const char *prefix = user_data;
 
-	PRLOG("%s%s\n", prefix, str);
+	PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix, str);
 }
 
+static void gatt_debug_cb(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
+}
+
+static void ready_cb(bool success, uint8_t att_ecode, void *user_data);
+
 static struct client *client_create(int fd, uint16_t mtu)
 {
 	struct client *cli;
@@ -118,11 +136,13 @@ static struct client *client_create(int fd, uint16_t mtu)
 	}
 
 	if (verbose) {
-		bt_att_set_debug(att, debug_cb, "att: ", NULL);
-		bt_gatt_client_set_debug(cli->gatt, debug_cb, "gatt-client: ",
+		bt_att_set_debug(att, att_debug_cb, "att: ", NULL);
+		bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",
 									NULL);
 	}
 
+	bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
+
 	/* bt_gatt_client already holds a reference */
 	bt_att_unref(att);
 
@@ -134,11 +154,21 @@ static void client_destroy(struct client *cli)
 	bt_gatt_client_unref(cli->gatt);
 }
 
+static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	if (!success) {
+		PRLOG("GATT discovery procedures failed - error code: 0x02%x\n",
+								att_ecode);
+		return;
+	}
 
-typedef void (*command_func_t)(struct client *cli);
+	PRLOG("GATT discovery procedures complete\n");
+}
 
 static void cmd_help(struct client *cli);
 
+typedef void (*command_func_t)(struct client *cli);
+
 static struct {
 	char *cmd;
 	command_func_t func;
@@ -178,6 +208,8 @@ static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 			break;
 	}
 
+	print_prompt();
+
 	if (command[i].cmd)
 		command[i].func(cli);
 	else
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 6/7] tools/btgatt-client: Add "services" command.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
                   ` (4 preceding siblings ...)
  2014-08-30 16:02 ` [PATCH BlueZ v2 5/7] tools/btgatt-client: Add "ready" handler Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:02 ` [PATCH BlueZ v2 7/7] tools/btgatt: Add command argument support Arman Uguray
  2014-08-30 16:48 ` [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Marcel Holtmann
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

Added the "services" command, which enumerates all discovered services,
characteristics, and descriptors.
---
 tools/btgatt-client.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 71 insertions(+), 3 deletions(-)

diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 6299586..445cfc7 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -37,6 +37,7 @@
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/l2cap.h>
+#include "lib/uuid.h"
 
 #include "monitor/mainloop.h"
 #include "src/shared/util.h"
@@ -154,15 +155,83 @@ static void client_destroy(struct client *cli)
 	bt_gatt_client_unref(cli->gatt);
 }
 
+static void print_uuid(const uint8_t uuid[16])
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t tmp;
+
+	tmp.type = BT_UUID128;
+	memcpy(tmp.value.u128.data, uuid, 16 * sizeof(uint8_t));
+	bt_uuid_to_string(&tmp, uuid_str, sizeof(uuid_str));
+
+	printf("%s\n", uuid_str);
+}
+
+static void print_services(struct client *cli)
+{
+	struct bt_gatt_service_iter iter;
+	bt_gatt_service_t service;
+	const bt_gatt_characteristic_t *chrc;
+
+	if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
+		PRLOG("Failed to initialize service iterator\n");
+		return;
+	}
+
+	printf("\n");
+
+	while (bt_gatt_service_iter_next(&iter, &service)) {
+		size_t i, j;
+
+		printf("service - start: 0x%04x, end: 0x%04x, uuid: ",
+				service.start_handle, service.end_handle);
+		print_uuid(service.uuid);
+
+		for (i = 0; i < service.num_chrcs; i++) {
+			chrc = service.chrcs + i;
+			printf("\t  charac - start: 0x%04x, end: 0x%04x, "
+					"value: 0x%04x, props: 0x%02x, uuid: ",
+					chrc->start_handle,
+					chrc->end_handle,
+					chrc->value_handle,
+					chrc->properties);
+			print_uuid(service.chrcs[i].uuid);
+
+			for (j = 0; j < chrc->num_descs; j++) {
+				printf("\t\t  descr - handle: 0x%04x, uuid: ",
+							chrc->descs[j].handle);
+				print_uuid(chrc->descs[j].uuid);
+			}
+		}
+
+		printf("\n");
+	}
+}
+
 static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
 {
+	struct client *cli = user_data;
+
 	if (!success) {
-		PRLOG("GATT discovery procedures failed - error code: 0x02%x\n",
+		PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
 								att_ecode);
 		return;
 	}
 
 	PRLOG("GATT discovery procedures complete\n");
+
+	print_services(cli);
+	print_prompt();
+}
+
+static void cmd_services(struct client *cli)
+{
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	print_services(cli);
 }
 
 static void cmd_help(struct client *cli);
@@ -175,6 +244,7 @@ static struct {
 	char *doc;
 } command[] = {
 	{ "help", cmd_help, "\tDisplay help message" },
+	{ "services", cmd_services, "\tShow discovered services" },
 	{ }
 };
 
@@ -208,8 +278,6 @@ static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 			break;
 	}
 
-	print_prompt();
-
 	if (command[i].cmd)
 		command[i].func(cli);
 	else
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ v2 7/7] tools/btgatt: Add command argument support.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
                   ` (5 preceding siblings ...)
  2014-08-30 16:02 ` [PATCH BlueZ v2 6/7] tools/btgatt-client: Add "services" command Arman Uguray
@ 2014-08-30 16:02 ` Arman Uguray
  2014-08-30 16:48 ` [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Marcel Holtmann
  7 siblings, 0 replies; 9+ messages in thread
From: Arman Uguray @ 2014-08-30 16:02 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

Added support for command arguments and added the -u and -a flags to the
"services" command to filter services by UUID or by start handle.
---
 tools/btgatt-client.c | 177 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 145 insertions(+), 32 deletions(-)

diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 445cfc7..0f13d35 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -167,11 +167,42 @@ static void print_uuid(const uint8_t uuid[16])
 	printf("%s\n", uuid_str);
 }
 
+static void print_service(const bt_gatt_service_t *service)
+{
+	const bt_gatt_characteristic_t *chrc;
+	size_t i, j;
+
+	printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+				"end: 0x%04x, uuid: ",
+				service->start_handle, service->end_handle);
+	print_uuid(service->uuid);
+
+	for (i = 0; i < service->num_chrcs; i++) {
+		chrc = service->chrcs + i;
+		printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
+				" - start: 0x%04x, end: 0x%04x, "
+				"value: 0x%04x, props: 0x%02x, uuid: ",
+				chrc->start_handle,
+				chrc->end_handle,
+				chrc->value_handle,
+				chrc->properties);
+		print_uuid(service->chrcs[i].uuid);
+
+		for (j = 0; j < chrc->num_descs; j++) {
+			printf("\t\t  " COLOR_MAGENTA "descr" COLOR_OFF
+						" - handle: 0x%04x, uuid: ",
+						chrc->descs[j].handle);
+			print_uuid(chrc->descs[j].uuid);
+		}
+	}
+
+	printf("\n");
+}
+
 static void print_services(struct client *cli)
 {
 	struct bt_gatt_service_iter iter;
 	bt_gatt_service_t service;
-	const bt_gatt_characteristic_t *chrc;
 
 	if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
 		PRLOG("Failed to initialize service iterator\n");
@@ -180,32 +211,41 @@ static void print_services(struct client *cli)
 
 	printf("\n");
 
-	while (bt_gatt_service_iter_next(&iter, &service)) {
-		size_t i, j;
-
-		printf("service - start: 0x%04x, end: 0x%04x, uuid: ",
-				service.start_handle, service.end_handle);
-		print_uuid(service.uuid);
-
-		for (i = 0; i < service.num_chrcs; i++) {
-			chrc = service.chrcs + i;
-			printf("\t  charac - start: 0x%04x, end: 0x%04x, "
-					"value: 0x%04x, props: 0x%02x, uuid: ",
-					chrc->start_handle,
-					chrc->end_handle,
-					chrc->value_handle,
-					chrc->properties);
-			print_uuid(service.chrcs[i].uuid);
-
-			for (j = 0; j < chrc->num_descs; j++) {
-				printf("\t\t  descr - handle: 0x%04x, uuid: ",
-							chrc->descs[j].handle);
-				print_uuid(chrc->descs[j].uuid);
-			}
-		}
+	while (bt_gatt_service_iter_next(&iter, &service))
+		print_service(&service);
+}
+
+static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
+{
+	struct bt_gatt_service_iter iter;
+	bt_gatt_service_t service;
 
-		printf("\n");
+	if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
+		PRLOG("Failed to initialize service iterator\n");
+		return;
 	}
+
+	printf("\n");
+
+	while (bt_gatt_service_iter_next_by_uuid(&iter, uuid->value.u128.data,
+								&service))
+		print_service(&service);
+}
+
+static void print_services_by_handle(struct client *cli, uint16_t handle)
+{
+	struct bt_gatt_service_iter iter;
+	bt_gatt_service_t service;
+
+	if (!bt_gatt_service_iter_init(&iter, cli->gatt)) {
+		PRLOG("Failed to initialize service iterator\n");
+		return;
+	}
+
+	printf("\n");
+
+	while (bt_gatt_service_iter_next_by_handle(&iter, handle, &service))
+		print_service(&service);
 }
 
 static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -224,19 +264,78 @@ static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
 	print_prompt();
 }
 
-static void cmd_services(struct client *cli)
+static void services_usage(void)
+{
+	printf("Usage: services [options]\nOptions:\n"
+		"\t -u, --uuid <uuid>\tService UUID\n"
+		"\t -a, --handle <handle>\tService start handle\n"
+		"\t -h, --help\t\tShow help message\n"
+		"e.g.:\n"
+		"\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n");
+}
+
+static void cmd_services(struct client *cli, char *cmd_str)
 {
+	char **ap, *argv[3];
+	int argc = 0;
+
 	if (!bt_gatt_client_is_ready(cli->gatt)) {
 		printf("GATT client not initialized\n");
 		return;
 	}
 
-	print_services(cli);
+	for (ap = argv; (*ap = strsep(&cmd_str, " \t")) != NULL;) {
+		if (**ap == '\0')
+			continue;
+
+		argc++;
+		ap++;
+
+		if (argc > 2) {
+			services_usage();
+			return;
+		}
+	}
+
+	if (!argc) {
+		print_services(cli);
+		return;
+	}
+
+	if (argc != 2) {
+		services_usage();
+		return;
+	}
+
+	if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--uuid")) {
+		bt_uuid_t tmp, uuid;
+
+		if (bt_string_to_uuid(&tmp, argv[1]) < 0) {
+			printf("Invalid UUID: %s\n", argv[1]);
+			return;
+		}
+
+		bt_uuid_to_uuid128(&tmp, &uuid);
+
+		print_services_by_uuid(cli, &uuid);
+	} else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--handle")) {
+		uint16_t handle;
+		char *endptr = NULL;
+
+		handle = strtol(argv[1], &endptr, 16);
+		if (!endptr || *endptr != '\0') {
+			printf("Invalid start handle: %s\n", argv[1]);
+			return;
+		}
+
+		print_services_by_handle(cli, handle);
+	} else
+		services_usage();
 }
 
-static void cmd_help(struct client *cli);
+static void cmd_help(struct client *cli, char *cmd_str);
 
-typedef void (*command_func_t)(struct client *cli);
+typedef void (*command_func_t)(struct client *cli, char *cmd_str);
 
 static struct {
 	char *cmd;
@@ -248,7 +347,7 @@ static struct {
 	{ }
 };
 
-static void cmd_help(struct client *cli)
+static void cmd_help(struct client *cli, char *cmd_str)
 {
 	int i;
 
@@ -262,6 +361,7 @@ static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 	ssize_t read;
 	size_t len = 0;
 	char *line = NULL;
+	char *cmd = NULL, *args;
 	struct client *cli = user_data;
 	int i;
 
@@ -273,13 +373,26 @@ static void prompt_read_cb(int fd, uint32_t events, void *user_data)
 	if ((read = getline(&line, &len, stdin)) == -1)
 		return;
 
+	if (read <= 1) {
+		cmd_help(cli, NULL);
+		print_prompt();
+		return;
+	}
+
+	line[read-1] = '\0';
+	args = line;
+
+	while ((cmd = strsep(&args, " \t")))
+		if (*cmd != '\0')
+			break;
+
 	for (i = 0; command[i].cmd; i++) {
-		if (strncmp(command[i].cmd, line, read - 1) == 0)
+		if (strcmp(command[i].cmd, cmd) == 0)
 			break;
 	}
 
 	if (command[i].cmd)
-		command[i].func(cli);
+		command[i].func(cli, args);
 	else
 		fprintf(stderr, "Unknown command: %s\n", line);
 
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client.
  2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
                   ` (6 preceding siblings ...)
  2014-08-30 16:02 ` [PATCH BlueZ v2 7/7] tools/btgatt: Add command argument support Arman Uguray
@ 2014-08-30 16:48 ` Marcel Holtmann
  7 siblings, 0 replies; 9+ messages in thread
From: Marcel Holtmann @ 2014-08-30 16:48 UTC (permalink / raw)
  To: Arman Uguray; +Cc: linux-bluetooth

Hi Arman,

> * v2: Rebased on latest master & fixed conflicts in Makefile.tools
> * v1: Removed L2CAP_LM socket options; using only BT_SECURITY now.
> 
> Arman Uguray (7):
>  tools/btgatt-client: Introduce tools/btgatt-client
>  tools/btgatt-client: Added L2CAP LE ATT connection.
>  tools/btgatt-client: Integrate bt_att and bt_gatt_client.
>  tools/btgatt-client: Add command parsing.
>  tools/btgatt-client: Add "ready" handler.
>  tools/btgatt-client: Add "services" command.
>  tools/btgatt: Add command argument support.
> 
> Makefile.tools        |  13 +-
> tools/btgatt-client.c | 657 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 669 insertions(+), 1 deletion(-)
> create mode 100644 tools/btgatt-client.c

all 7 patches have been applied.

When adding new tools you need to remember to add the binary to .gitignore.

Regards

Marcel


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2014-08-30 16:48 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-30 16:02 [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 1/7] tools/btgatt-client: " Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 2/7] tools/btgatt-client: Added L2CAP LE ATT connection Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 3/7] tools/btgatt-client: Integrate bt_att and bt_gatt_client Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 4/7] tools/btgatt-client: Add command parsing Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 5/7] tools/btgatt-client: Add "ready" handler Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 6/7] tools/btgatt-client: Add "services" command Arman Uguray
2014-08-30 16:02 ` [PATCH BlueZ v2 7/7] tools/btgatt: Add command argument support Arman Uguray
2014-08-30 16:48 ` [PATCH BlueZ v2 0/7] Introduce tools/btgatt-client Marcel Holtmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.