All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 BlueZ 0/9] client: Add GATT application support
@ 2017-06-30 10:18 Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 1/8] client: Allow register-application without any UUID Luiz Augusto von Dentz
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds necessary commands to create GATT services which can then
be registered with bluetoothd:

[bluetooth]# register-service 00001802-0000-1000-8000-00805f9b34fb
[NEW] Primary Service
	/org/bluez/app/service0x902610
	00001802-0000-1000-8000-00805f9b34fb
	Immediate Alert
[bluetooth]# register-characteristic 00002a06-0000-1000-8000-00805f9b34fb write-without-response
[NEW] Characteristic
	/org/bluez/app/service0x902610/chrc0x91d690
	00002a06-0000-1000-8000-00805f9b34fb
	Alert Level
[/org/bluez/app/service0x902610/chrc0x91d690] Enter value:  
[bluetooth]# register-descriptor 8260c653-1a54-426b-9e36-e84c238bc669 read,write
[NEW] Descriptor
	/org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0
	8260c653-1a54-426b-9e36-e84c238bc669
	Vendor specific
[/org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0] Enter value:  
[bluetooth]# register-application
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001112-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000112d-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110a-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110b-0000-1000-8000-00805f9b34fb
[CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001802-0000-1000-8000-00805f9b34fb
Application registered


bluetoothd[7704]: src/gatt-database.c:manager_register_app() Registering application: :1.79:/
bluetoothd[7704]: src/gatt-database.c:proxy_added_cb() Object received: /org/bluez/app/service0x902610, iface: org.bluez.GattService1
bluetoothd[7704]: src/gatt-database.c:proxy_added_cb() Object received: /org/bluez/app/service0x902610/chrc0x91d690, iface: org.bluez.GattCharacteristic1
bluetoothd[7704]: src/gatt-database.c:proxy_added_cb() Object received: /org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0, iface: org.bluez.GattDescriptor1

v2: Fix memory leaks pointed out by Eramoto, make register-service ask for primary
property.

Luiz Augusto von Dentz (9):
  client: Allow register-application without any UUID
  client: Add register-service command
  client: Add unregister-service command
  client: Add register-characteristic command
  client: Add unregister-characteristic command
  client: Add generic way to request input from user
  client: Ask user the characteristic value
  client: Add register-descriptor command
  client: Add unregister-descriptor command

 client/display.c |  58 ++++
 client/display.h |   5 +
 client/gatt.c    | 833 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 client/gatt.h    |  13 +
 client/main.c    | 154 +++++++++-
 5 files changed, 1000 insertions(+), 63 deletions(-)

-- 
2.9.4


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

* [PATCH v2 1/8] client: Allow register-application without any UUID
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 2/8] client: Add generic way to request input from user Luiz Augusto von Dentz
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This means that GattProfile1 should not be registered but there could still
be services that needs registered with bluetoothd.
---
 client/gatt.c | 24 +++++++++++++++---------
 client/main.c |  7 +------
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 40d3f6a..8b7a9c6 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -726,16 +726,19 @@ void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w)
 		return;
 	}
 
-	if (g_dbus_register_interface(conn, APP_PATH,
-					PROFILE_INTERFACE, methods,
-					NULL, properties, NULL, NULL) == FALSE) {
-		rl_printf("Failed to register application object\n");
-		return;
-	}
-
 	for (i = 0; i < w->we_wordc; i++)
 		uuids = g_list_append(uuids, g_strdup(w->we_wordv[i]));
 
+	if (uuids) {
+		if (g_dbus_register_interface(conn, APP_PATH,
+						PROFILE_INTERFACE, methods,
+						NULL, properties, NULL,
+						NULL) == FALSE) {
+			rl_printf("Failed to register application object\n");
+			return;
+		}
+	}
+
 	if (g_dbus_proxy_method_call(l->data, "RegisterApplication",
 						register_app_setup,
 						register_app_reply, w,
@@ -759,11 +762,14 @@ static void unregister_app_reply(DBusMessage *message, void *user_data)
 		return;
 	}
 
+	rl_printf("Application unregistered\n");
+
+	if (!uuids)
+		return;
+
 	g_list_free_full(uuids, g_free);
 	uuids = NULL;
 
-	rl_printf("Application unregistered\n");
-
 	g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
 }
 
diff --git a/client/main.c b/client/main.c
index 578dde9..31d06b8 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1826,11 +1826,6 @@ static void cmd_register_app(const char *arg)
 		return;
 	}
 
-	if (w.we_wordc == 0) {
-		rl_printf("Missing argument\n");
-		return;
-	}
-
 	gatt_register_app(dbus_conn, default_ctrl->proxy, &w);
 
 	wordfree(&w);
@@ -2142,7 +2137,7 @@ static const struct {
 	{ "write",        "<data=[xx xx ...]>", cmd_write,
 						"Write attribute value" },
 	{ "notify",       "<on/off>", cmd_notify, "Notify attribute value" },
-	{ "register-application", "<UUID ...>", cmd_register_app,
+	{ "register-application", "[UUID ...]", cmd_register_app,
 						"Register profile to connect" },
 	{ "unregister-application", NULL, cmd_unregister_app,
 						"Unregister profile" },
-- 
2.9.4


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

* [PATCH v2 2/8] client: Add generic way to request input from user
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 1/8] client: Allow register-application without any UUID Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 3/8] client: Add register-service command Luiz Augusto von Dentz
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds rl_prompt_input which can be used by different parts to ask
user input.
---
 client/display.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 client/display.h |  5 +++++
 client/main.c    |  3 +++
 3 files changed, 66 insertions(+)

diff --git a/client/display.c b/client/display.c
index d85b9d0..1dc4294 100644
--- a/client/display.c
+++ b/client/display.c
@@ -34,6 +34,11 @@
 
 #include "display.h"
 
+static char *saved_prompt = NULL;
+static int saved_point = 0;
+static rl_prompt_input_func saved_func = NULL;
+static void *saved_user_data = NULL;
+
 void rl_printf(const char *fmt, ...)
 {
 	va_list args;
@@ -104,3 +109,56 @@ void rl_hexdump(const unsigned char *buf, size_t len)
 		rl_printf("%s\n", str);
 	}
 }
+
+void rl_prompt_input(const char *label, const char *msg,
+				rl_prompt_input_func func, void *user_data)
+{
+	char prompt[256];
+
+	/* Normal use should not prompt for user input to the value a second
+	 * time before it releases the prompt, but we take a safe action. */
+	if (saved_prompt)
+		return;
+
+	saved_point = rl_point;
+	saved_prompt = strdup(rl_prompt);
+	saved_func = func;
+	saved_user_data = user_data;
+
+	memset(prompt, 0, sizeof(prompt));
+	snprintf(prompt, sizeof(prompt), COLOR_RED "[%s]" COLOR_OFF " %s ",
+								label, msg);
+	rl_set_prompt(prompt);
+
+	rl_replace_line("", 0);
+	rl_redisplay();
+}
+
+int rl_release_prompt(const char *input)
+{
+	rl_prompt_input_func func;
+	void *user_data;
+
+	if (!saved_prompt)
+		return -1;
+
+	/* This will cause rl_expand_prompt to re-run over the last prompt, but
+	 * our prompt doesn't expand anyway. */
+	rl_set_prompt(saved_prompt);
+	rl_replace_line("", 0);
+	rl_point = saved_point;
+	rl_redisplay();
+
+	free(saved_prompt);
+	saved_prompt = NULL;
+
+	func = saved_func;
+	user_data = saved_user_data;
+
+	saved_func = NULL;
+	saved_user_data = NULL;
+
+	func(input, user_data);
+
+	return 0;
+}
diff --git a/client/display.h b/client/display.h
index 88dbbd0..e991d19 100644
--- a/client/display.h
+++ b/client/display.h
@@ -31,3 +31,8 @@
 
 void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 void rl_hexdump(const unsigned char *buf, size_t len);
+
+typedef void (*rl_prompt_input_func) (const char *input, void *user_data);
+void rl_prompt_input(const char *label, const char *msg,
+				rl_prompt_input_func func, void *user_data);
+int rl_release_prompt(const char *input);
diff --git a/client/main.c b/client/main.c
index 31d06b8..c42bf50 100644
--- a/client/main.c
+++ b/client/main.c
@@ -2223,6 +2223,9 @@ static void rl_handler(char *input)
 	if (agent_input(dbus_conn, input) == TRUE)
 		goto done;
 
+	if (!rl_release_prompt(input))
+		goto done;
+
 	add_history(input);
 
 	cmd = strtok_r(input, " ", &arg);
-- 
2.9.4


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

* [PATCH v2 3/8] client: Add register-service command
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 1/8] client: Allow register-application without any UUID Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 2/8] client: Add generic way to request input from user Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 4/8] client: Add unregister-service command Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds register-service command which can be used to add GATT services
to the application:

[bluetooth]# register-service 00001820-0000-1000-8000-00805f9b34fb
[NEW] Primary Service
	/org/bluez/app/service0x8c2610
	00001820-0000-1000-8000-00805f9b34fb
	Internet Protocol Support
[/org/bluez/app/service0x8c2610] Primary (yes/no): yes
[bluetooth]# register-application
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 00001112-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 0000112d-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 00001820-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 0000110a-0000-1000-8000-00805f9b34fb
[CHG] Controller 00:1B:DC:07:31:88 UUIDs: 0000110b-0000-1000-8000-00805f9b34fb

Note: register-application still has to be called at the end to register
with bluetoothd as everything is done with ObjectManager.
---
 client/gatt.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++---------
 client/gatt.h |   3 ++
 client/main.c |  25 ++++++++++
 3 files changed, 155 insertions(+), 22 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 8b7a9c6..82e7851 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -45,22 +45,55 @@
 
 #define APP_PATH "/org/bluez/app"
 #define PROFILE_INTERFACE "org.bluez.GattProfile1"
+#define SERVICE_INTERFACE "org.bluez.GattService1"
 
 /* String display constants */
 #define COLORED_NEW	COLOR_GREEN "NEW" COLOR_OFF
 #define COLORED_CHG	COLOR_YELLOW "CHG" COLOR_OFF
 #define COLORED_DEL	COLOR_RED "DEL" COLOR_OFF
 
+struct service {
+	DBusConnection *conn;
+	char *path;
+	char *uuid;
+	bool primary;
+};
+
+static GList *local_services;
 static GList *services;
 static GList *characteristics;
 static GList *descriptors;
 static GList *managers;
 static GList *uuids;
 
-static void print_service(GDBusProxy *proxy, const char *description)
+static void print_service(struct service *service, const char *description)
+{
+	const char *text;
+
+	text = uuidstr_to_str(service->uuid);
+	if (!text)
+		rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					service->primary ? "Primary" :
+					"Secondary",
+					service->path, service->uuid);
+	else
+		rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					service->primary ? "Primary" :
+					"Secondary",
+					service->path, service->uuid, text);
+}
+
+static void print_service_proxy(GDBusProxy *proxy, const char *description)
 {
+	struct service service;
 	DBusMessageIter iter;
-	const char *uuid, *text;
+	const char *uuid;
 	dbus_bool_t primary;
 
 	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
@@ -73,30 +106,18 @@ static void print_service(GDBusProxy *proxy, const char *description)
 
 	dbus_message_iter_get_basic(&iter, &primary);
 
-	text = uuidstr_to_str(uuid);
-	if (!text)
-		rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
-					description ? "[" : "",
-					description ? : "",
-					description ? "] " : "",
-					primary ? "Primary" : "Secondary",
-					g_dbus_proxy_get_path(proxy),
-					uuid);
-	else
-		rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
-					description ? "[" : "",
-					description ? : "",
-					description ? "] " : "",
-					primary ? "Primary" : "Secondary",
-					g_dbus_proxy_get_path(proxy),
-					uuid, text);
+	service.path = (char *) g_dbus_proxy_get_path(proxy);
+	service.uuid = (char *) uuid;
+	service.primary = primary;
+
+	print_service(&service, description);
 }
 
 void gatt_add_service(GDBusProxy *proxy)
 {
 	services = g_list_append(services, proxy);
 
-	print_service(proxy, COLORED_NEW);
+	print_service_proxy(proxy, COLORED_NEW);
 }
 
 void gatt_remove_service(GDBusProxy *proxy)
@@ -109,7 +130,7 @@ void gatt_remove_service(GDBusProxy *proxy)
 
 	services = g_list_delete_link(services, l);
 
-	print_service(proxy, COLORED_DEL);
+	print_service_proxy(proxy, COLORED_DEL);
 }
 
 static void print_characteristic(GDBusProxy *proxy, const char *description)
@@ -272,7 +293,7 @@ static void list_attributes(const char *path, GList *source)
 			continue;
 
 		if (source == services) {
-			print_service(proxy, NULL);
+			print_service_proxy(proxy, NULL);
 			list_attributes(proxy_path, characteristics);
 		} else if (source == characteristics) {
 			print_characteristic(proxy, NULL);
@@ -798,3 +819,87 @@ void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
 		return;
 	}
 }
+
+static void service_free(void *data)
+{
+	struct service *service = data;
+
+	g_free(service->path);
+	g_free(service->uuid);
+	g_free(service);
+}
+
+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct service *service = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &service->uuid);
+
+	return TRUE;
+}
+
+static gboolean service_get_primary(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct service *service = data;
+	dbus_bool_t primary;
+
+	primary = service->primary ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+	{ "UUID", "s", service_get_uuid },
+	{ "Primary", "b", service_get_primary },
+	{ }
+};
+
+static void service_set_primary(const char *input, void *user_data)
+{
+	struct service *service = user_data;
+
+	if (!strcmp(input, "yes"))
+		service->primary = true;
+	else if (!strcmp(input, "no")) {
+		service->primary = false;
+	} else {
+		rl_printf("Invalid option: %s\n", input);
+		local_services = g_list_remove(local_services, service);
+		print_service(service, COLORED_DEL);
+		g_dbus_unregister_interface(service->conn, service->path,
+						SERVICE_INTERFACE);
+	}
+}
+
+void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w)
+{
+	struct service *service;
+	bool primary = true;
+
+	service = g_new0(struct service, 1);
+	service->conn = conn;
+	service->uuid = g_strdup(w->we_wordv[0]);
+	service->path = g_strdup_printf("%s/service%p", APP_PATH, service);
+	service->primary = primary;
+
+	if (g_dbus_register_interface(conn, service->path,
+					SERVICE_INTERFACE, NULL, NULL,
+					service_properties, service,
+					service_free) == FALSE) {
+		rl_printf("Failed to register service object\n");
+		service_free(service);
+		return;
+	}
+
+	rl_printf("Service registered at %s\n", service->path);
+
+	local_services = g_list_append(local_services, service);
+
+	rl_prompt_input(service->path, "Primary (yes/no):", service_set_primary,
+			service);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 4c9fd5b..7f116df 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -43,3 +43,6 @@ void gatt_remove_manager(GDBusProxy *proxy);
 
 void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w);
 void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy);
+
+void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w);
diff --git a/client/main.c b/client/main.c
index c42bf50..726d749 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1839,6 +1839,29 @@ static void cmd_unregister_app(const char *arg)
 	gatt_unregister_app(dbus_conn, default_ctrl->proxy);
 }
 
+static void cmd_register_service(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc == 0) {
+		rl_printf("Missing argument\n");
+		goto done;
+	}
+
+	gatt_register_service(dbus_conn, default_ctrl->proxy, &w);
+
+done:
+	wordfree(&w);
+}
+
 static void cmd_version(const char *arg)
 {
 	rl_printf("Version %s\n", VERSION);
@@ -2141,6 +2164,8 @@ static const struct {
 						"Register profile to connect" },
 	{ "unregister-application", NULL, cmd_unregister_app,
 						"Unregister profile" },
+	{ "register-service", "<UUID>", cmd_register_service,
+					"Register application service."  },
 	{ "version",      NULL,       cmd_version, "Display version" },
 	{ "quit",         NULL,       cmd_quit, "Quit program" },
 	{ "exit",         NULL,       cmd_quit, "Quit program" },
-- 
2.9.4


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

* [PATCH v2 4/8] client: Add unregister-service command
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2017-06-30 10:18 ` [PATCH v2 3/8] client: Add register-service command Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 5/8] client: Add register-characteristic command Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds unregister-service which can be used to unregister an
application service registered with register-service:

register-service 00001820-0000-1000-8000-00805f9b34fb
[NEW] Primary Service
	/org/bluez/app/service0x92a150
	00001820-0000-1000-8000-00805f9b34fb
	Internet Protocol Support
[bluetooth]# unregister-service /org/bluez/app/service0x92a150
[DEL] Primary Service
	/org/bluez/app/service0x92a150
	00001820-0000-1000-8000-00805f9b34fb
	Internet Protocol Support
---
 client/gatt.c | 40 +++++++++++++++++++++++++++++++++++++++-
 client/gatt.h |  2 ++
 client/main.c | 25 +++++++++++++++++++++++++
 3 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/client/gatt.c b/client/gatt.c
index 82e7851..3ceea45 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -896,10 +896,48 @@ void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
 		return;
 	}
 
-	rl_printf("Service registered at %s\n", service->path);
+	print_service(service, COLORED_NEW);
 
 	local_services = g_list_append(local_services, service);
 
 	rl_prompt_input(service->path, "Primary (yes/no):", service_set_primary,
 			service);
 }
+
+static struct service *service_find(const char *pattern)
+{
+	GList *l;
+
+	for (l = local_services; l; l = g_list_next(l)) {
+		struct service *service = l->data;
+
+		/* match object path */
+		if (!strcmp(service->path, pattern))
+			return service;
+
+		/* match UUID */
+		if (!strcmp(service->uuid, pattern))
+			return service;
+	}
+
+	return NULL;
+}
+
+void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w)
+{
+	struct service *service;
+
+	service = service_find(w->we_wordv[0]);
+	if (!service) {
+		rl_printf("Failed to unregister service object\n");
+		return;
+	}
+
+	local_services = g_list_remove(local_services, service);
+
+	print_service(service, COLORED_DEL);
+
+	g_dbus_unregister_interface(service->conn, service->path,
+						SERVICE_INTERFACE);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 7f116df..4b9edd5 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -46,3 +46,5 @@ void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy);
 
 void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
 								wordexp_t *w);
+void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w);
diff --git a/client/main.c b/client/main.c
index 726d749..dd18eb8 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1862,6 +1862,29 @@ done:
 	wordfree(&w);
 }
 
+static void cmd_unregister_service(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc == 0) {
+		rl_printf("Missing argument\n");
+		goto done;
+	}
+
+	gatt_unregister_service(dbus_conn, default_ctrl->proxy, &w);
+
+done:
+	wordfree(&w);
+}
+
 static void cmd_version(const char *arg)
 {
 	rl_printf("Version %s\n", VERSION);
@@ -2166,6 +2189,8 @@ static const struct {
 						"Unregister profile" },
 	{ "register-service", "<UUID>", cmd_register_service,
 					"Register application service."  },
+	{ "unregister-service", "<UUID/object>", cmd_unregister_service,
+					"Unregister application service" },
 	{ "version",      NULL,       cmd_version, "Display version" },
 	{ "quit",         NULL,       cmd_quit, "Quit program" },
 	{ "exit",         NULL,       cmd_quit, "Quit program" },
-- 
2.9.4


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

* [PATCH v2 5/8] client: Add register-characteristic command
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2017-06-30 10:18 ` [PATCH v2 4/8] client: Add unregister-service command Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 6/8] client: Add unregister-characteristic command Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds register-characteristic which can be used to register
characteristic to a service registered with register-service:

register-characteristic 00002a06-0000-1000-8000-00805f9b34fb write-without-response
[NEW] Characteristic
	/org/bluez/app/service0x1122150/chrc0x113fa40
	00002a06-0000-1000-8000-00805f9b34fb
	Alert Level
---
 client/gatt.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 client/gatt.h |   2 +
 client/main.c |  26 +++++
 3 files changed, 335 insertions(+), 15 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 3ceea45..3185c31 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -46,17 +46,30 @@
 #define APP_PATH "/org/bluez/app"
 #define PROFILE_INTERFACE "org.bluez.GattProfile1"
 #define SERVICE_INTERFACE "org.bluez.GattService1"
+#define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
 
 /* String display constants */
 #define COLORED_NEW	COLOR_GREEN "NEW" COLOR_OFF
 #define COLORED_CHG	COLOR_YELLOW "CHG" COLOR_OFF
 #define COLORED_DEL	COLOR_RED "DEL" COLOR_OFF
 
+struct chrc {
+	struct service *service;
+	char *path;
+	char *uuid;
+	char **flags;
+	bool notifying;
+	GList *descs;
+	int value_len;
+	uint8_t *value;
+};
+
 struct service {
 	DBusConnection *conn;
 	char *path;
 	char *uuid;
 	bool primary;
+	GList *chrcs;
 };
 
 static GList *local_services;
@@ -133,34 +146,43 @@ void gatt_remove_service(GDBusProxy *proxy)
 	print_service_proxy(proxy, COLORED_DEL);
 }
 
-static void print_characteristic(GDBusProxy *proxy, const char *description)
+static void print_chrc(struct chrc *chrc, const char *description)
 {
-	DBusMessageIter iter;
-	const char *uuid, *text;
-
-	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
-		return;
-
-	dbus_message_iter_get_basic(&iter, &uuid);
+	const char *text;
 
-	text = uuidstr_to_str(uuid);
+	text = uuidstr_to_str(chrc->uuid);
 	if (!text)
 		rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
 					description ? "[" : "",
 					description ? : "",
 					description ? "] " : "",
-					g_dbus_proxy_get_path(proxy),
-					uuid);
+					chrc->path, chrc->uuid);
 	else
 		rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
 					description ? "[" : "",
 					description ? : "",
 					description ? "] " : "",
-					g_dbus_proxy_get_path(proxy),
-					uuid, text);
+					chrc->path, chrc->uuid, text);
+}
+
+static void print_characteristic(GDBusProxy *proxy, const char *description)
+{
+	struct chrc chrc;
+	DBusMessageIter iter;
+	const char *uuid;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	chrc.path = (char *) g_dbus_proxy_get_path(proxy);
+	chrc.uuid = (char *) uuid;
+
+	print_chrc(&chrc, description);
 }
 
-static gboolean characteristic_is_child(GDBusProxy *characteristic)
+static gboolean chrc_is_child(GDBusProxy *characteristic)
 {
 	GList *l;
 	DBusMessageIter iter;
@@ -185,7 +207,7 @@ static gboolean characteristic_is_child(GDBusProxy *characteristic)
 
 void gatt_add_characteristic(GDBusProxy *proxy)
 {
-	if (!characteristic_is_child(proxy))
+	if (!chrc_is_child(proxy))
 		return;
 
 	characteristics = g_list_append(characteristics, proxy);
@@ -820,10 +842,32 @@ void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
 	}
 }
 
+static void chrc_free(void *data)
+{
+	struct chrc *chrc = data;
+
+	g_free(chrc->path);
+	g_free(chrc->uuid);
+	g_strfreev(chrc->flags);
+	g_free(chrc->value);
+	g_free(chrc);
+}
+
+static void chrc_unregister(void *data)
+{
+	struct chrc *chrc = data;
+
+	print_chrc(chrc, COLORED_DEL);
+
+	g_dbus_unregister_interface(chrc->service->conn, chrc->path,
+						CHRC_INTERFACE);
+}
+
 static void service_free(void *data)
 {
 	struct service *service = data;
 
+	g_list_free_full(service->chrcs, chrc_unregister);
 	g_free(service->path);
 	g_free(service->uuid);
 	g_free(service);
@@ -941,3 +985,251 @@ void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
 	g_dbus_unregister_interface(service->conn, service->path,
 						SERVICE_INTERFACE);
 }
+
+static gboolean chrc_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct chrc *chrc = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chrc->uuid);
+
+	return TRUE;
+}
+
+static gboolean chrc_get_service(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct chrc *chrc = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+						&chrc->service->path);
+
+	return TRUE;
+}
+
+static gboolean chrc_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct chrc *chrc = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&chrc->value, chrc->value_len);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean chrc_get_notifying(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct chrc *chrc = data;
+	dbus_bool_t value;
+
+	value = chrc->notifying ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean chrc_get_flags(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct chrc *chrc = data;
+	int i;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+	for (i = 0; chrc->flags[i]; i++)
+		dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+							&chrc->flags[i]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable chrc_properties[] = {
+	{ "UUID", "s", chrc_get_uuid, NULL, NULL },
+	{ "Service", "o", chrc_get_service, NULL, NULL },
+	{ "Value", "ay", chrc_get_value, NULL, NULL },
+	{ "Notifying", "b", chrc_get_notifying, NULL, NULL },
+	{ "Flags", "as", chrc_get_flags, NULL, NULL },
+	{ }
+};
+
+static DBusMessage *read_value(DBusMessage *msg, uint8_t *value,
+						uint16_t value_len)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+
+	reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&value, value_len);
+	dbus_message_iter_close_container(&iter, &array);
+
+	return reply;
+}
+
+static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct chrc *chrc = user_data;
+
+	return read_value(msg, chrc->value, chrc->value_len);
+}
+
+static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
+{
+	DBusMessageIter array;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, value, len);
+
+	return 0;
+}
+
+static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct chrc *chrc = user_data;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_value_arg(&iter, &chrc->value, &chrc->value_len))
+		return g_dbus_create_error(msg,
+					"org.bluez.Error.InvalidArguments",
+					NULL);
+
+	rl_printf("[" COLORED_CHG "] Attribute %s written" , chrc->path);
+
+	g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Value");
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct chrc *chrc = user_data;
+
+	if (!chrc->notifying)
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	chrc->notifying = true;
+	rl_printf("[" COLORED_CHG "] Attribute %s notifications enabled",
+							chrc->path);
+	g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+							"Notifying");
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *chrc_stop_notify(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct chrc *chrc = user_data;
+
+	if (chrc->notifying)
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	chrc->notifying = false;
+	rl_printf("[" COLORED_CHG "] Attribute %s notifications disabled",
+							chrc->path);
+	g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+							"Notifying");
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable chrc_methods[] = {
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					chrc_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL, chrc_write_value) },
+	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
+	{ GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
+	{ }
+};
+
+static void chrc_set_value(const char *input, void *user_data)
+{
+	struct chrc *chrc = user_data;
+	uint8_t value[512];
+	char *entry;
+	unsigned int i;
+
+	g_free(chrc->value);
+
+	for (i = 0; (entry = strsep((char **)&input, " \t")) != NULL; i++) {
+		long int val;
+		char *endptr = NULL;
+
+		if (*entry == '\0')
+			continue;
+
+		if (i >= G_N_ELEMENTS(value)) {
+			rl_printf("Too much data\n");
+			return;
+		}
+
+		val = strtol(entry, &endptr, 0);
+		if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+			rl_printf("Invalid value at index %d\n", i);
+			return;
+		}
+
+		value[i] = val;
+	}
+
+	chrc->value_len = i;
+	chrc->value = g_memdup(value, i);
+}
+
+void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w)
+{
+	struct service *service;
+	struct chrc *chrc;
+
+	if (!local_services) {
+		rl_printf("No service registered\n");
+		return;
+	}
+
+	service = g_list_last(local_services)->data;
+
+	chrc = g_new0(struct chrc, 1);
+	chrc->service = service;
+	chrc->uuid = g_strdup(w->we_wordv[0]);
+	chrc->path = g_strdup_printf("%s/chrc%p", service->path, chrc);
+	chrc->flags = g_strsplit(w->we_wordv[1], ",", -1);
+
+	if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE,
+					chrc_methods, NULL, chrc_properties,
+					chrc, chrc_free) == FALSE) {
+		rl_printf("Failed to register characteristic object\n");
+		chrc_free(chrc);
+		return;
+	}
+
+	service->chrcs = g_list_append(service->chrcs, chrc);
+
+	print_chrc(chrc, COLORED_NEW);
+
+	rl_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 4b9edd5..4ecf642 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -48,3 +48,5 @@ void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
 								wordexp_t *w);
 void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
 								wordexp_t *w);
+
+void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w);
diff --git a/client/main.c b/client/main.c
index dd18eb8..34271aa 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1885,6 +1885,29 @@ done:
 	wordfree(&w);
 }
 
+static void cmd_register_characteristic(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc < 2) {
+		rl_printf("Missing arguments\n");
+		goto done;
+	}
+
+	gatt_register_chrc(dbus_conn, default_ctrl->proxy, &w);
+
+done:
+	wordfree(&w);
+}
+
 static void cmd_version(const char *arg)
 {
 	rl_printf("Version %s\n", VERSION);
@@ -2191,6 +2214,9 @@ static const struct {
 					"Register application service."  },
 	{ "unregister-service", "<UUID/object>", cmd_unregister_service,
 					"Unregister application service" },
+	{ "register-characteristic", "<UUID> <Flags=read,write,notify...>",
+					cmd_register_characteristic,
+					"Register application characteristic" },
 	{ "version",      NULL,       cmd_version, "Display version" },
 	{ "quit",         NULL,       cmd_quit, "Quit program" },
 	{ "exit",         NULL,       cmd_quit, "Quit program" },
-- 
2.9.4


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

* [PATCH v2 6/8] client: Add unregister-characteristic command
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2017-06-30 10:18 ` [PATCH v2 5/8] client: Add register-characteristic command Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 7/8] client: Add register-descriptor command Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds unregister-characteristic which can be used to unregister
characteristics registered with register-characteristic:

unregister-characteristic /org/bluez/app/service0xc80150/chrc0xc99960
[DEL] Characteristic
	/org/bluez/app/service0xc80150/chrc0xc99960
	00002a06-0000-1000-8000-00805f9b34fb
	Alert Level
---
 client/gatt.c | 41 +++++++++++++++++++++++++++++++++++++++++
 client/gatt.h |  2 ++
 client/main.c | 26 ++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)

diff --git a/client/gatt.c b/client/gatt.c
index 3185c31..4466490 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -1233,3 +1233,44 @@ void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w)
 
 	rl_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc);
 }
+
+static struct chrc *chrc_find(const char *pattern)
+{
+	GList *l, *lc;
+	struct service *service;
+	struct chrc *chrc;
+
+	for (l = local_services; l; l = g_list_next(l)) {
+		service = l->data;
+
+		for (lc = service->chrcs; lc; lc =  g_list_next(lc)) {
+			chrc = lc->data;
+
+			/* match object path */
+			if (!strcmp(chrc->path, pattern))
+				return chrc;
+
+			/* match UUID */
+			if (!strcmp(chrc->uuid, pattern))
+				return chrc;
+		}
+	}
+
+	return NULL;
+}
+
+void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w)
+{
+	struct chrc *chrc;
+
+	chrc = chrc_find(w->we_wordv[0]);
+	if (!chrc) {
+		rl_printf("Failed to unregister characteristic object\n");
+		return;
+	}
+
+	chrc->service->chrcs = g_list_remove(chrc->service->chrcs, chrc);
+
+	chrc_unregister(chrc);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 4ecf642..0acce4d 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -50,3 +50,5 @@ void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
 								wordexp_t *w);
 
 void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w);
+void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w);
diff --git a/client/main.c b/client/main.c
index 34271aa..f3dfa2c 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1908,6 +1908,29 @@ done:
 	wordfree(&w);
 }
 
+static void cmd_unregister_characteristic(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc < 1) {
+		rl_printf("Missing arguments\n");
+		goto done;
+	}
+
+	gatt_unregister_chrc(dbus_conn, default_ctrl->proxy, &w);
+
+done:
+	wordfree(&w);
+}
+
 static void cmd_version(const char *arg)
 {
 	rl_printf("Version %s\n", VERSION);
@@ -2217,6 +2240,9 @@ static const struct {
 	{ "register-characteristic", "<UUID> <Flags=read,write,notify...>",
 					cmd_register_characteristic,
 					"Register application characteristic" },
+	{ "unregister-characteristic", "<UUID/object>",
+				cmd_unregister_characteristic,
+				"Unregister application characteristic" },
 	{ "version",      NULL,       cmd_version, "Display version" },
 	{ "quit",         NULL,       cmd_quit, "Quit program" },
 	{ "exit",         NULL,       cmd_quit, "Quit program" },
-- 
2.9.4


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

* [PATCH v2 7/8] client: Add register-descriptor command
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2017-06-30 10:18 ` [PATCH v2 6/8] client: Add unregister-characteristic command Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-06-30 10:18 ` [PATCH v2 8/8] client: Add unregister-descriptor command Luiz Augusto von Dentz
  2017-07-03 12:12 ` [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds register-descriptor which can be used to register
descriptors to a characteristic registered with register-characteristic:

register-descriptor 8260c653-1a54-426b-9e36-e84c238bc669 read,write
[NEW] Descriptor
	/org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0
	8260c653-1a54-426b-9e36-e84c238bc669
	Vendor specific
[/org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0] Enter value: 00
---
 client/gatt.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 client/gatt.h |   2 +
 client/main.c |  26 +++++++
 3 files changed, 248 insertions(+), 22 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 4466490..e7d9a03 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -47,12 +47,22 @@
 #define PROFILE_INTERFACE "org.bluez.GattProfile1"
 #define SERVICE_INTERFACE "org.bluez.GattService1"
 #define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
+#define DESC_INTERFACE "org.bluez.GattDescriptor1"
 
 /* String display constants */
 #define COLORED_NEW	COLOR_GREEN "NEW" COLOR_OFF
 #define COLORED_CHG	COLOR_YELLOW "CHG" COLOR_OFF
 #define COLORED_DEL	COLOR_RED "DEL" COLOR_OFF
 
+struct desc {
+	struct chrc *chrc;
+	char *path;
+	char *uuid;
+	char **flags;
+	int value_len;
+	uint8_t *value;
+};
+
 struct chrc {
 	struct service *service;
 	char *path;
@@ -228,31 +238,40 @@ void gatt_remove_characteristic(GDBusProxy *proxy)
 	print_characteristic(proxy, COLORED_DEL);
 }
 
-static void print_descriptor(GDBusProxy *proxy, const char *description)
+static void print_desc(struct desc *desc, const char *description)
 {
-	DBusMessageIter iter;
-	const char *uuid, *text;
-
-	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
-		return;
-
-	dbus_message_iter_get_basic(&iter, &uuid);
+	const char *text;
 
-	text = uuidstr_to_str(uuid);
+	text = uuidstr_to_str(desc->uuid);
 	if (!text)
 		rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
 					description ? "[" : "",
 					description ? : "",
 					description ? "] " : "",
-					g_dbus_proxy_get_path(proxy),
-					uuid);
+					desc->path, desc->uuid);
 	else
 		rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
 					description ? "[" : "",
 					description ? : "",
 					description ? "] " : "",
-					g_dbus_proxy_get_path(proxy),
-					uuid, text);
+					desc->path, desc->uuid, text);
+}
+
+static void print_descriptor(GDBusProxy *proxy, const char *description)
+{
+	struct desc desc;
+	DBusMessageIter iter;
+	const char *uuid;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	desc.path = (char *) g_dbus_proxy_get_path(proxy);
+	desc.uuid = (char *) uuid;
+
+	print_desc(&desc, description);
 }
 
 static gboolean descriptor_is_child(GDBusProxy *characteristic)
@@ -842,10 +861,32 @@ void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
 	}
 }
 
+static void desc_free(void *data)
+{
+	struct desc *desc = data;
+
+	g_free(desc->path);
+	g_free(desc->uuid);
+	g_strfreev(desc->flags);
+	g_free(desc->value);
+	g_free(desc);
+}
+
+static void desc_unregister(void *data)
+{
+	struct desc *desc = data;
+
+	print_desc(desc, COLORED_DEL);
+
+	g_dbus_unregister_interface(desc->chrc->service->conn, desc->path,
+						DESC_INTERFACE);
+}
+
 static void chrc_free(void *data)
 {
 	struct chrc *chrc = data;
 
+	g_list_free_full(chrc->descs, desc_unregister);
 	g_free(chrc->path);
 	g_free(chrc->uuid);
 	g_strfreev(chrc->flags);
@@ -1167,16 +1208,13 @@ static const GDBusMethodTable chrc_methods[] = {
 	{ }
 };
 
-static void chrc_set_value(const char *input, void *user_data)
+static uint8_t *str2bytearray(char *arg, int *val_len)
 {
-	struct chrc *chrc = user_data;
 	uint8_t value[512];
 	char *entry;
 	unsigned int i;
 
-	g_free(chrc->value);
-
-	for (i = 0; (entry = strsep((char **)&input, " \t")) != NULL; i++) {
+	for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
 		long int val;
 		char *endptr = NULL;
 
@@ -1185,20 +1223,30 @@ static void chrc_set_value(const char *input, void *user_data)
 
 		if (i >= G_N_ELEMENTS(value)) {
 			rl_printf("Too much data\n");
-			return;
+			return NULL;
 		}
 
 		val = strtol(entry, &endptr, 0);
 		if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
 			rl_printf("Invalid value at index %d\n", i);
-			return;
+			return NULL;
 		}
 
 		value[i] = val;
 	}
 
-	chrc->value_len = i;
-	chrc->value = g_memdup(value, i);
+	*val_len = i;
+
+	return g_memdup(value, i);
+}
+
+static void chrc_set_value(const char *input, void *user_data)
+{
+	struct chrc *chrc = user_data;
+
+	g_free(chrc->value);
+
+	chrc->value = str2bytearray((char *) input, &chrc->value_len);
 }
 
 void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w)
@@ -1274,3 +1322,153 @@ void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
 
 	chrc_unregister(chrc);
 }
+
+static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct desc *desc = user_data;
+
+	return read_value(msg, desc->value, desc->value_len);
+}
+
+static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct desc *desc = user_data;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_value_arg(&iter, &desc->value, &desc->value_len))
+		return g_dbus_create_error(msg,
+					"org.bluez.Error.InvalidArguments",
+					NULL);
+
+	rl_printf("[" COLORED_CHG "] Attribute %s written" , desc->path);
+
+	g_dbus_emit_property_changed(conn, desc->path, CHRC_INTERFACE, "Value");
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable desc_methods[] = {
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					desc_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL, desc_write_value) },
+	{ }
+};
+
+static gboolean desc_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct desc *desc = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
+
+	return TRUE;
+}
+
+static gboolean desc_get_chrc(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct desc *desc = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+						&desc->chrc->path);
+
+	return TRUE;
+}
+
+static gboolean desc_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct desc *desc = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+	if (desc->value)
+		dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+							&desc->value,
+							desc->value_len);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean desc_get_flags(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct desc *desc = data;
+	int i;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+	for (i = 0; desc->flags[i]; i++)
+		dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+							&desc->flags[i]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable desc_properties[] = {
+	{ "UUID", "s", desc_get_uuid, NULL, NULL },
+	{ "Characteristic", "o", desc_get_chrc, NULL, NULL },
+	{ "Value", "ay", desc_get_value, NULL, NULL },
+	{ "Flags", "as", desc_get_flags, NULL, NULL },
+	{ }
+};
+
+static void desc_set_value(const char *input, void *user_data)
+{
+	struct desc *desc = user_data;
+
+	g_free(desc->value);
+
+	desc->value = str2bytearray((char *) input, &desc->value_len);
+}
+
+void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w)
+{
+	struct service *service;
+	struct desc *desc;
+
+	if (!local_services) {
+		rl_printf("No service registered\n");
+		return;
+	}
+
+	service = g_list_last(local_services)->data;
+
+	if (!service->chrcs) {
+		rl_printf("No characteristic registered\n");
+		return;
+	}
+
+	desc = g_new0(struct desc, 1);
+	desc->chrc = g_list_last(service->chrcs)->data;
+	desc->uuid = g_strdup(w->we_wordv[0]);
+	desc->path = g_strdup_printf("%s/desc%p", desc->chrc->path, desc);
+	desc->flags = g_strsplit(w->we_wordv[1], ",", -1);
+
+	if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE,
+					desc_methods, NULL, desc_properties,
+					desc, desc_free) == FALSE) {
+		rl_printf("Failed to register descriptor object\n");
+		desc_free(desc);
+		return;
+	}
+
+	desc->chrc->descs = g_list_append(desc->chrc->descs, desc);
+
+	print_desc(desc, COLORED_NEW);
+
+	rl_prompt_input(desc->path, "Enter value:", desc_set_value, desc);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 0acce4d..4d1e63f 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -52,3 +52,5 @@ void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
 void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w);
 void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
 								wordexp_t *w);
+
+void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w);
diff --git a/client/main.c b/client/main.c
index f3dfa2c..2c5fe26 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1931,6 +1931,29 @@ done:
 	wordfree(&w);
 }
 
+static void cmd_register_descriptor(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc < 2) {
+		rl_printf("Missing arguments\n");
+		goto done;
+	}
+
+	gatt_register_desc(dbus_conn, default_ctrl->proxy, &w);
+
+done:
+	wordfree(&w);
+}
+
 static void cmd_version(const char *arg)
 {
 	rl_printf("Version %s\n", VERSION);
@@ -2243,6 +2266,9 @@ static const struct {
 	{ "unregister-characteristic", "<UUID/object>",
 				cmd_unregister_characteristic,
 				"Unregister application characteristic" },
+	{ "register-descriptor", "<UUID> <Flags=read,write...>",
+					cmd_register_descriptor,
+					"Register application descriptor" },
 	{ "version",      NULL,       cmd_version, "Display version" },
 	{ "quit",         NULL,       cmd_quit, "Quit program" },
 	{ "exit",         NULL,       cmd_quit, "Quit program" },
-- 
2.9.4


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

* [PATCH v2 8/8] client: Add unregister-descriptor command
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
                   ` (6 preceding siblings ...)
  2017-06-30 10:18 ` [PATCH v2 7/8] client: Add register-descriptor command Luiz Augusto von Dentz
@ 2017-06-30 10:18 ` Luiz Augusto von Dentz
  2017-07-03 12:12 ` [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-06-30 10:18 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds unregister-descriptor which can be used to unregister
descriptors registered with register-descriptor:

unregister-descriptor /org/bluez/app/service0xf48150/chrc0xf49a40/desc0xf4d350
[DEL] Descriptor
	/org/bluez/app/service0xf48150/chrc0xf49a40/desc0xf4d350
	8260c653-1a54-426b-9e36-e84c238bc669
	Vendor specific
---
 client/gatt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 client/gatt.h |  2 ++
 client/main.c | 26 ++++++++++++++++++++++++++
 3 files changed, 74 insertions(+)

diff --git a/client/gatt.c b/client/gatt.c
index e7d9a03..8a4491a 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -1472,3 +1472,49 @@ void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w)
 
 	rl_prompt_input(desc->path, "Enter value:", desc_set_value, desc);
 }
+
+static struct desc *desc_find(const char *pattern)
+{
+	GList *l, *lc, *ld;
+	struct service *service;
+	struct chrc *chrc;
+	struct desc *desc;
+
+	for (l = local_services; l; l = g_list_next(l)) {
+		service = l->data;
+
+		for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
+			chrc = lc->data;
+
+			for (ld = chrc->descs; ld; ld = g_list_next(ld)) {
+				desc = ld->data;
+
+				/* match object path */
+				if (!strcmp(desc->path, pattern))
+					return desc;
+
+				/* match UUID */
+				if (!strcmp(desc->uuid, pattern))
+					return desc;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w)
+{
+	struct desc *desc;
+
+	desc = desc_find(w->we_wordv[0]);
+	if (!desc) {
+		rl_printf("Failed to unregister descriptor object\n");
+		return;
+	}
+
+	desc->chrc->descs = g_list_remove(desc->chrc->descs, desc);
+
+	desc_unregister(desc);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 4d1e63f..8031a46 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -54,3 +54,5 @@ void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
 								wordexp_t *w);
 
 void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, wordexp_t *w);
+void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w);
diff --git a/client/main.c b/client/main.c
index 2c5fe26..3af533e 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1954,6 +1954,29 @@ done:
 	wordfree(&w);
 }
 
+static void cmd_unregister_descriptor(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc < 1) {
+		rl_printf("Missing arguments\n");
+		goto done;
+	}
+
+	gatt_unregister_desc(dbus_conn, default_ctrl->proxy, &w);
+
+done:
+	wordfree(&w);
+}
+
 static void cmd_version(const char *arg)
 {
 	rl_printf("Version %s\n", VERSION);
@@ -2269,6 +2292,9 @@ static const struct {
 	{ "register-descriptor", "<UUID> <Flags=read,write...>",
 					cmd_register_descriptor,
 					"Register application descriptor" },
+	{ "unregister-descriptor", "<UUID/object>",
+					cmd_unregister_descriptor,
+					"Unregister application descriptor" },
 	{ "version",      NULL,       cmd_version, "Display version" },
 	{ "quit",         NULL,       cmd_quit, "Quit program" },
 	{ "exit",         NULL,       cmd_quit, "Quit program" },
-- 
2.9.4


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

* Re: [PATCH v2 BlueZ 0/9] client: Add GATT application support
  2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
                   ` (7 preceding siblings ...)
  2017-06-30 10:18 ` [PATCH v2 8/8] client: Add unregister-descriptor command Luiz Augusto von Dentz
@ 2017-07-03 12:12 ` Luiz Augusto von Dentz
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2017-07-03 12:12 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

On Fri, Jun 30, 2017 at 1:18 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> This adds necessary commands to create GATT services which can then
> be registered with bluetoothd:
>
> [bluetooth]# register-service 00001802-0000-1000-8000-00805f9b34fb
> [NEW] Primary Service
>         /org/bluez/app/service0x902610
>         00001802-0000-1000-8000-00805f9b34fb
>         Immediate Alert
> [bluetooth]# register-characteristic 00002a06-0000-1000-8000-00805f9b34fb write-without-response
> [NEW] Characteristic
>         /org/bluez/app/service0x902610/chrc0x91d690
>         00002a06-0000-1000-8000-00805f9b34fb
>         Alert Level
> [/org/bluez/app/service0x902610/chrc0x91d690] Enter value:
> [bluetooth]# register-descriptor 8260c653-1a54-426b-9e36-e84c238bc669 read,write
> [NEW] Descriptor
>         /org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0
>         8260c653-1a54-426b-9e36-e84c238bc669
>         Vendor specific
> [/org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0] Enter value:
> [bluetooth]# register-application
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001112-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001801-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000112d-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001800-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001200-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110a-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 0000110b-0000-1000-8000-00805f9b34fb
> [CHG] Controller B8:8A:60:D8:17:D7 UUIDs: 00001802-0000-1000-8000-00805f9b34fb
> Application registered
>
>
> bluetoothd[7704]: src/gatt-database.c:manager_register_app() Registering application: :1.79:/
> bluetoothd[7704]: src/gatt-database.c:proxy_added_cb() Object received: /org/bluez/app/service0x902610, iface: org.bluez.GattService1
> bluetoothd[7704]: src/gatt-database.c:proxy_added_cb() Object received: /org/bluez/app/service0x902610/chrc0x91d690, iface: org.bluez.GattCharacteristic1
> bluetoothd[7704]: src/gatt-database.c:proxy_added_cb() Object received: /org/bluez/app/service0x902610/chrc0x91d690/desc0x9095a0, iface: org.bluez.GattDescriptor1
>
> v2: Fix memory leaks pointed out by Eramoto, make register-service ask for primary
> property.
>
> Luiz Augusto von Dentz (9):
>   client: Allow register-application without any UUID
>   client: Add register-service command
>   client: Add unregister-service command
>   client: Add register-characteristic command
>   client: Add unregister-characteristic command
>   client: Add generic way to request input from user
>   client: Ask user the characteristic value
>   client: Add register-descriptor command
>   client: Add unregister-descriptor command
>
>  client/display.c |  58 ++++
>  client/display.h |   5 +
>  client/gatt.c    | 833 +++++++++++++++++++++++++++++++++++++++++++++++++++----
>  client/gatt.h    |  13 +
>  client/main.c    | 154 +++++++++-
>  5 files changed, 1000 insertions(+), 63 deletions(-)
>
> --
> 2.9.4

Applied.


-- 
Luiz Augusto von Dentz

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

end of thread, other threads:[~2017-07-03 12:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-30 10:18 [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 1/8] client: Allow register-application without any UUID Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 2/8] client: Add generic way to request input from user Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 3/8] client: Add register-service command Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 4/8] client: Add unregister-service command Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 5/8] client: Add register-characteristic command Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 6/8] client: Add unregister-characteristic command Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 7/8] client: Add register-descriptor command Luiz Augusto von Dentz
2017-06-30 10:18 ` [PATCH v2 8/8] client: Add unregister-descriptor command Luiz Augusto von Dentz
2017-07-03 12:12 ` [PATCH v2 BlueZ 0/9] client: Add GATT application support Luiz Augusto von Dentz

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.