From: Luiz Augusto von Dentz <[email protected]>
This adds basic support for GATT API, the main idea is that one can
navigate into the attributes and read and write values to them.
Some of the output of the changes can be found bellow:
[bluetooth]# connect XX:XX:XX:XX:XX:XX
Attempting to connect to XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
Connection successful
[CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
[NEW] Service /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011 Battery Service (Primary)
[NEW] Characteristic /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Battery Level
[NEW] Descriptor /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012/desc0014 Client Characteristic Configuration
[Arc Touch Mouse SE]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011 /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012/desc0014
[Arc Touch Mouse SE]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011
[Arc Touch Mouse SE:/service0011]# attribute-info
Service - Battery Service
UUID: 0000180f-0000-1000-8000-00805f9b34fb
Primary: yes
Characteristics: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
[Arc Touch Mouse SE:/service0011]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
[Arc Touch Mouse SE:/service0011/char0012]# attribute-info
Characteristic - Battery Level
UUID: 00002a19-0000-1000-8000-00805f9b34fb
Service: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011
Notifying: no
Flags: read
Flags: notify
Descriptors: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012/desc0014
[Arc Touch Mouse SE:/service0011/char0012]# read
Attempting to read /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x63
63 c
[Arc Touch Mouse SE:/service0011/char0012]# write 00
Attempting to write /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
Failed to write: org.bluez.Error.NotSupported
[Arc Touch Mouse SE:/service0011/char0012]# notify on
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Notifying: yes
Notify started
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x55
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x56
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x57
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x58
[Arc Touch Mouse SE:/service0011/char0012]# notify off
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Notifying: no
Notify stopped
[Arc Touch Mouse SE:/service0011/char0012]#
Luiz Augusto von Dentz (12):
client: Add support for GattService1
client: Add support for GattCharacteristic1
client: Add support for GattDescriptor1
client: Make commands relative to device
client: Add command list-attributes
client: Add command select-attribute
client: Add attribute-info command
client/display: Add rl_hexdump
client: Add read command
client: Add write command
client: Add notify command
client: Handle attribute notifications
Makefile.tools | 1 +
client/display.c | 41 +++++
client/display.h | 1 +
client/gatt.c | 514 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 39 +++++
client/main.c | 409 ++++++++++++++++++++++++++++++++++---------
6 files changed, 925 insertions(+), 80 deletions(-)
create mode 100644 client/gatt.c
create mode 100644 client/gatt.h
--
2.1.0
Hi,
On Fri, Feb 6, 2015 at 1:03 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This adds basic support for GATT API, the main idea is that one can
> navigate into the attributes and read and write values to them.
>
> Some of the output of the changes can be found bellow:
>
> [bluetooth]# connect XX:XX:XX:XX:XX:XX
> Attempting to connect to XX:XX:XX:XX:XX:XX
> [CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
> Connection successful
> [CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
> [NEW] Service /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011 Battery Service (Primary)
> [NEW] Characteristic /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Battery Level
> [NEW] Descriptor /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012/desc0014 Client Characteristic Configuration
> [Arc Touch Mouse SE]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011
> /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011 /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012/desc0014
> [Arc Touch Mouse SE]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011
> [Arc Touch Mouse SE:/service0011]# attribute-info
> Service - Battery Service
> UUID: 0000180f-0000-1000-8000-00805f9b34fb
> Primary: yes
> Characteristics: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
> [Arc Touch Mouse SE:/service0011]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
> [Arc Touch Mouse SE:/service0011/char0012]# attribute-info
> Characteristic - Battery Level
> UUID: 00002a19-0000-1000-8000-00805f9b34fb
> Service: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011
> Notifying: no
> Flags: read
> Flags: notify
> Descriptors: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012/desc0014
> [Arc Touch Mouse SE:/service0011/char0012]# read
> Attempting to read /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x63
> 63 c
> [Arc Touch Mouse SE:/service0011/char0012]# write 00
> Attempting to write /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012
> Failed to write: org.bluez.Error.NotSupported
> [Arc Touch Mouse SE:/service0011/char0012]# notify on
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Notifying: yes
> Notify started
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x55
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x56
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x57
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Value: 0x58
> [Arc Touch Mouse SE:/service0011/char0012]# notify off
> [CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0011/char0012 Notifying: no
> Notify stopped
> [Arc Touch Mouse SE:/service0011/char0012]#
>
> Luiz Augusto von Dentz (12):
> client: Add support for GattService1
> client: Add support for GattCharacteristic1
> client: Add support for GattDescriptor1
> client: Make commands relative to device
> client: Add command list-attributes
> client: Add command select-attribute
> client: Add attribute-info command
> client/display: Add rl_hexdump
> client: Add read command
> client: Add write command
> client: Add notify command
> client: Handle attribute notifications
>
> Makefile.tools | 1 +
> client/display.c | 41 +++++
> client/display.h | 1 +
> client/gatt.c | 514 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> client/gatt.h | 39 +++++
> client/main.c | 409 ++++++++++++++++++++++++++++++++++---------
> 6 files changed, 925 insertions(+), 80 deletions(-)
> create mode 100644 client/gatt.c
> create mode 100644 client/gatt.h
>
> --
> 2.1.0
Pushed.
--
Luiz Augusto von Dentz
Hi Gowtham,
On Mon, Feb 9, 2015 at 1:41 PM, Gowtham Anandha Babu
<[email protected]> wrote:
> Hi Luiz,
>
>> -----Original Message-----
>> From: [email protected] [mailto:linux-bluetooth-
>> [email protected]] On Behalf Of Luiz Augusto von Dentz
>> Sent: Friday, February 06, 2015 4:34 PM
>> To: [email protected]
>> Subject: [PATCH BlueZ 12/12] client: Handle attribute notifications
>>
>> From: Luiz Augusto von Dentz <[email protected]>
>>
>> This enable printing properties changes to the current selected attribute.
>> ---
>> client/main.c | 13 +++++++++++++
>> 1 file changed, 13 insertions(+)
>>
>> diff --git a/client/main.c b/client/main.c index 57b1201..fbfd8c8 100644
>> --- a/client/main.c
>> +++ b/client/main.c
>> @@ -158,6 +158,7 @@ static void print_iter(const char *label, const char
>> *name,
>> dbus_uint32_t valu32;
>> dbus_uint16_t valu16;
>> dbus_int16_t vals16;
>> + unsigned char byte;
>> const char *valstr;
>> DBusMessageIter subiter;
>>
>> @@ -192,6 +193,10 @@ static void print_iter(const char *label, const char
>> *name,
>> dbus_message_iter_get_basic(iter, &vals16);
>> rl_printf("%s%s: %d\n", label, name, vals16);
>> break;
>> + case DBUS_TYPE_BYTE:
>> + dbus_message_iter_get_basic(iter, &byte);
>> + rl_printf("%s%s: 0x%02x\n", label, name, byte);
>> + break;
>> case DBUS_TYPE_VARIANT:
>> dbus_message_iter_recurse(iter, &subiter);
>> print_iter(label, name, &subiter);
>> @@ -494,6 +499,14 @@ static void property_changed(GDBusProxy *proxy,
>> const char *name,
>>
>> print_iter(str, name, iter);
>> g_free(str);
>> + } else if (proxy == default_attr) {
>> + char *str;
>> +
>> + str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
>> +
>> g_dbus_proxy_get_path(proxy));
>> +
>> + print_iter(str, name, iter);
>
> Here I suppose, g_free(str) should come. Because the returned string (str)
> should be freed when no longer needed.
> And str = g_strdup(""); seems like Dead assignment. Other than that
> everything applied cleanly with no warnings/errors.
>
> Regards,
> Gowtham Anandha Babu
>
>> + str = g_strdup("");
Indeed this is a dead assignment, not sure why it did not catch during
the build, perhaps that should be g_free not g_strdup.
--
Luiz Augusto von Dentz
Hi Luiz,
> -----Original Message-----
> From: [email protected] [mailto:linux-bluetooth-
> [email protected]] On Behalf Of Luiz Augusto von Dentz
> Sent: Friday, February 06, 2015 4:34 PM
> To: [email protected]
> Subject: [PATCH BlueZ 12/12] client: Handle attribute notifications
>
> From: Luiz Augusto von Dentz <[email protected]>
>
> This enable printing properties changes to the current selected attribute.
> ---
> client/main.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/client/main.c b/client/main.c index 57b1201..fbfd8c8 100644
> --- a/client/main.c
> +++ b/client/main.c
> @@ -158,6 +158,7 @@ static void print_iter(const char *label, const char
> *name,
> dbus_uint32_t valu32;
> dbus_uint16_t valu16;
> dbus_int16_t vals16;
> + unsigned char byte;
> const char *valstr;
> DBusMessageIter subiter;
>
> @@ -192,6 +193,10 @@ static void print_iter(const char *label, const char
> *name,
> dbus_message_iter_get_basic(iter, &vals16);
> rl_printf("%s%s: %d\n", label, name, vals16);
> break;
> + case DBUS_TYPE_BYTE:
> + dbus_message_iter_get_basic(iter, &byte);
> + rl_printf("%s%s: 0x%02x\n", label, name, byte);
> + break;
> case DBUS_TYPE_VARIANT:
> dbus_message_iter_recurse(iter, &subiter);
> print_iter(label, name, &subiter);
> @@ -494,6 +499,14 @@ static void property_changed(GDBusProxy *proxy,
> const char *name,
>
> print_iter(str, name, iter);
> g_free(str);
> + } else if (proxy == default_attr) {
> + char *str;
> +
> + str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
> +
> g_dbus_proxy_get_path(proxy));
> +
> + print_iter(str, name, iter);
Here I suppose, g_free(str) should come. Because the returned string (str)
should be freed when no longer needed.
And str = g_strdup(""); seems like Dead assignment. Other than that
everything applied cleanly with no warnings/errors.
Regards,
Gowtham Anandha Babu
> + str = g_strdup("");
> }
> }
>
> --
> 2.1.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth"
in
> the body of a message to [email protected] More majordomo
> info at http://vger.kernel.org/majordomo-info.html
From: Luiz Augusto von Dentz <[email protected]>
This command can be used to read attributes, it only works if an
attribute has been selected with select-attribute.
---
client/gatt.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 2 ++
client/main.c | 11 +++++++++++
3 files changed, 72 insertions(+)
diff --git a/client/gatt.c b/client/gatt.c
index 8fb3f62..ce709c8 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -326,3 +326,62 @@ char *gatt_attribute_generator(const char *text, int state)
return attribute_generator(text, state, list);
}
+
+static void read_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+ DBusMessageIter iter, array;
+ uint8_t *value;
+ int len;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to read: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ dbus_message_iter_init(message, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ rl_printf("Invalid response to read\n");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ if (len < 0) {
+ rl_printf("Unable to parse value\n");
+ return;
+ }
+
+ rl_hexdump(value, len);
+}
+
+static void read_attribute(GDBusProxy *proxy)
+{
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+ NULL, NULL) == FALSE) {
+ rl_printf("Failed to read\n");
+ return;
+ }
+
+ rl_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
+}
+
+void gatt_read_attribute(GDBusProxy *proxy)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
+ !strcmp(iface, "org.bluez.GattDescriptor1")) {
+ read_attribute(proxy);
+ return;
+ }
+
+ rl_printf("Unable to read attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
diff --git a/client/gatt.h b/client/gatt.h
index bb7cb1c..ac18a2a 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -33,3 +33,5 @@ void gatt_remove_descriptor(GDBusProxy *proxy);
void gatt_list_attributes(const char *device);
GDBusProxy *gatt_select_attribute(const char *path);
char *gatt_attribute_generator(const char *text, int state);
+
+void gatt_read_attribute(GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index 614efca..04cb51b 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1276,6 +1276,16 @@ static void cmd_attribute_info(const char *arg)
}
}
+static void cmd_read(const char *arg)
+{
+ if (!default_attr) {
+ rl_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_read_attribute(default_attr);
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1405,6 +1415,7 @@ static const struct {
"Select attribute", attribute_generator },
{ "attribute-info", "[attribute]", cmd_attribute_info,
"Select attribute", attribute_generator },
+ { "read", NULL, cmd_read, "Read attribute value" },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This enable printing properties changes to the current selected
attribute.
---
client/main.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/client/main.c b/client/main.c
index 57b1201..fbfd8c8 100644
--- a/client/main.c
+++ b/client/main.c
@@ -158,6 +158,7 @@ static void print_iter(const char *label, const char *name,
dbus_uint32_t valu32;
dbus_uint16_t valu16;
dbus_int16_t vals16;
+ unsigned char byte;
const char *valstr;
DBusMessageIter subiter;
@@ -192,6 +193,10 @@ static void print_iter(const char *label, const char *name,
dbus_message_iter_get_basic(iter, &vals16);
rl_printf("%s%s: %d\n", label, name, vals16);
break;
+ case DBUS_TYPE_BYTE:
+ dbus_message_iter_get_basic(iter, &byte);
+ rl_printf("%s%s: 0x%02x\n", label, name, byte);
+ break;
case DBUS_TYPE_VARIANT:
dbus_message_iter_recurse(iter, &subiter);
print_iter(label, name, &subiter);
@@ -494,6 +499,14 @@ static void property_changed(GDBusProxy *proxy, const char *name,
print_iter(str, name, iter);
g_free(str);
+ } else if (proxy == default_attr) {
+ char *str;
+
+ str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
+ g_dbus_proxy_get_path(proxy));
+
+ print_iter(str, name, iter);
+ str = g_strdup("");
}
}
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This command can be used to start/stop changes on the attribute value,
it only works if an attribute has been selected with select-attribute.
---
client/gatt.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 1 +
client/main.c | 16 ++++++++++++++++
3 files changed, 64 insertions(+)
diff --git a/client/gatt.c b/client/gatt.c
index 207e049..3785452 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -465,3 +465,50 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
rl_printf("Unable to write attribute %s\n",
g_dbus_proxy_get_path(proxy));
}
+
+static void notify_reply(DBusMessage *message, void *user_data)
+{
+ bool enable = GPOINTER_TO_UINT(user_data);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to %s notify: %s\n",
+ enable ? "start" : "stop", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ rl_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
+}
+
+static void notify_attribute(GDBusProxy *proxy, bool enable)
+{
+ const char *method;
+
+ if (enable == TRUE)
+ method = "StartNotify";
+ else
+ method = "StopNotify";
+
+ if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply,
+ GUINT_TO_POINTER(enable), NULL) == FALSE) {
+ rl_printf("Failed to %s notify\n", enable ? "start" : "stop");
+ return;
+ }
+}
+
+void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
+ notify_attribute(proxy, enable);
+ return;
+ }
+
+ rl_printf("Unable to notify attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
diff --git a/client/gatt.h b/client/gatt.h
index b99f0f0..effee5e 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -36,3 +36,4 @@ char *gatt_attribute_generator(const char *text, int state);
void gatt_read_attribute(GDBusProxy *proxy);
void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
+void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
diff --git a/client/main.c b/client/main.c
index 56d4750..57b1201 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1301,6 +1301,21 @@ static void cmd_write(const char *arg)
gatt_write_attribute(default_attr, arg);
}
+static void cmd_notify(const char *arg)
+{
+ dbus_bool_t enable;
+
+ if (parse_argument_on_off(arg, &enable) == FALSE)
+ return;
+
+ if (!default_attr) {
+ rl_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_notify_attribute(default_attr, enable ? true : false);
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1433,6 +1448,7 @@ static const struct {
{ "read", NULL, cmd_read, "Read attribute value" },
{ "write", "<data=[xx xx ...]>", cmd_write,
"Write attribute value" },
+ { "notify", "<on/off>", cmd_notify, "Notify attribute value" },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This command can be used to write attributes, it only works if an
attribute has been selected with select-attribute.
The data argument should be passed hexdump format, %02x separated by
spaces, which is the same format used by read command.
---
client/gatt.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 1 +
client/main.c | 17 +++++++++++++
3 files changed, 98 insertions(+)
diff --git a/client/gatt.c b/client/gatt.c
index ce709c8..207e049 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -30,6 +30,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
+#include <sys/uio.h>
#include <readline/readline.h>
#include <readline/history.h>
@@ -385,3 +386,82 @@ void gatt_read_attribute(GDBusProxy *proxy)
rl_printf("Unable to read attribute %s\n",
g_dbus_proxy_get_path(proxy));
}
+
+static void write_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to write: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+}
+
+static void write_setup(DBusMessageIter *iter, void *user_data)
+{
+ struct iovec *iov = user_data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &iov->iov_base, iov->iov_len);
+ dbus_message_iter_close_container(iter, &array);
+}
+
+static void write_attribute(GDBusProxy *proxy, char *arg)
+{
+ struct iovec iov;
+ uint8_t value[512];
+ char *entry;
+ int i;
+
+ for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
+ long int val;
+ char *endptr = NULL;
+
+ if (*entry == '\0')
+ continue;
+
+ if (i > 512) {
+ 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;
+ }
+
+ iov.iov_base = value;
+ iov.iov_len = i;
+
+ if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
+ write_reply, &iov, NULL) == FALSE) {
+ rl_printf("Failed to write\n");
+ return;
+ }
+
+ rl_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
+}
+
+void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
+ !strcmp(iface, "org.bluez.GattDescriptor1")) {
+ write_attribute(proxy, (char *) arg);
+ return;
+ }
+
+ rl_printf("Unable to write attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
diff --git a/client/gatt.h b/client/gatt.h
index ac18a2a..b99f0f0 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -35,3 +35,4 @@ GDBusProxy *gatt_select_attribute(const char *path);
char *gatt_attribute_generator(const char *text, int state);
void gatt_read_attribute(GDBusProxy *proxy);
+void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
diff --git a/client/main.c b/client/main.c
index 04cb51b..56d4750 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1286,6 +1286,21 @@ static void cmd_read(const char *arg)
gatt_read_attribute(default_attr);
}
+static void cmd_write(const char *arg)
+{
+ if (!arg || !strlen(arg)) {
+ rl_printf("Missing data argument\n");
+ return;
+ }
+
+ if (!default_attr) {
+ rl_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_write_attribute(default_attr, arg);
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1416,6 +1431,8 @@ static const struct {
{ "attribute-info", "[attribute]", cmd_attribute_info,
"Select attribute", attribute_generator },
{ "read", NULL, cmd_read, "Read attribute value" },
+ { "write", "<data=[xx xx ...]>", cmd_write,
+ "Write attribute value" },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
---
client/display.c | 41 +++++++++++++++++++++++++++++++++++++++++
client/display.h | 1 +
2 files changed, 42 insertions(+)
diff --git a/client/display.c b/client/display.c
index 0f212b6..619973c 100644
--- a/client/display.c
+++ b/client/display.c
@@ -62,3 +62,44 @@ void rl_printf(const char *fmt, ...)
free(saved_line);
}
}
+
+void rl_hexdump(const unsigned char *buf, size_t len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char str[68];
+ size_t i;
+
+ if (!len)
+ return;
+
+ str[0] = ' ';
+
+ for (i = 0; i < len; i++) {
+ str[((i % 16) * 3) + 1] = ' ';
+ str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
+ str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
+ str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
+
+ if ((i + 1) % 16 == 0) {
+ str[49] = ' ';
+ str[50] = ' ';
+ str[67] = '\0';
+ rl_printf("%s\n", str);
+ str[0] = ' ';
+ }
+ }
+
+ if (i % 16 > 0) {
+ size_t j;
+ for (j = (i % 16); j < 16; j++) {
+ str[(j * 3) + 1] = ' ';
+ str[(j * 3) + 2] = ' ';
+ str[(j * 3) + 3] = ' ';
+ str[j + 51] = ' ';
+ }
+ str[49] = ' ';
+ str[50] = ' ';
+ str[67] = '\0';
+ rl_printf("%s\n", str);
+ }
+}
diff --git a/client/display.h b/client/display.h
index 91a0be9..88dbbd0 100644
--- a/client/display.h
+++ b/client/display.h
@@ -30,3 +30,4 @@
#define COLOR_BOLDWHITE "\x1B[1;37m"
void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void rl_hexdump(const unsigned char *buf, size_t len);
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
attribute-info can be used to print out attribute information.
---
client/main.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 81 insertions(+), 15 deletions(-)
diff --git a/client/main.c b/client/main.c
index 1481694..614efca 100644
--- a/client/main.c
+++ b/client/main.c
@@ -160,7 +160,6 @@ static void print_iter(const char *label, const char *name,
dbus_int16_t vals16;
const char *valstr;
DBusMessageIter subiter;
- int type;
if (iter == NULL) {
rl_printf("%s%s is nil\n", label, name);
@@ -193,23 +192,23 @@ static void print_iter(const char *label, const char *name,
dbus_message_iter_get_basic(iter, &vals16);
rl_printf("%s%s: %d\n", label, name, vals16);
break;
+ case DBUS_TYPE_VARIANT:
+ dbus_message_iter_recurse(iter, &subiter);
+ print_iter(label, name, &subiter);
+ break;
case DBUS_TYPE_ARRAY:
dbus_message_iter_recurse(iter, &subiter);
- rl_printf("%s%s:\n", label, name);
-
- do {
- type = dbus_message_iter_get_arg_type(&subiter);
- if (type == DBUS_TYPE_INVALID)
- break;
-
- if (type == DBUS_TYPE_STRING) {
- dbus_message_iter_get_basic(&subiter, &valstr);
- rl_printf("\t%s\n", valstr);
- }
-
+ while (dbus_message_iter_get_arg_type(&subiter) !=
+ DBUS_TYPE_INVALID) {
+ print_iter(label, name, &subiter);
dbus_message_iter_next(&subiter);
- } while(true);
-
+ }
+ break;
+ case DBUS_TYPE_DICT_ENTRY:
+ dbus_message_iter_recurse(iter, &subiter);
+ dbus_message_iter_get_basic(&subiter, &valstr);
+ dbus_message_iter_next(&subiter);
+ print_iter(label, valstr, &subiter);
break;
default:
rl_printf("%s%s has unsupported type\n", label, name);
@@ -1212,6 +1211,71 @@ static void cmd_select_attribute(const char *arg)
set_default_attribute(proxy);
}
+static struct GDBusProxy *find_attribute(const char *arg)
+{
+ GDBusProxy *proxy;
+
+ if (!arg || !strlen(arg)) {
+ if (default_attr)
+ return default_attr;
+ rl_printf("Missing attribute argument\n");
+ return NULL;
+ }
+
+ proxy = gatt_select_attribute(arg);
+ if (!proxy) {
+ rl_printf("Attribute %s not available\n", arg);
+ return NULL;
+ }
+
+ return proxy;
+}
+
+static void cmd_attribute_info(const char *arg)
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *iface, *uuid, *text;
+
+ proxy = find_attribute(arg);
+ if (!proxy)
+ return;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = g_dbus_proxy_get_path(proxy);
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattService1")) {
+ rl_printf("Service - %s\n", text);
+
+ print_property(proxy, "UUID");
+ print_property(proxy, "Primary");
+ print_property(proxy, "Characteristics");
+ print_property(proxy, "Includes");
+ } else if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
+ rl_printf("Characteristic - %s\n", text);
+
+ print_property(proxy, "UUID");
+ print_property(proxy, "Service");
+ print_property(proxy, "Value");
+ print_property(proxy, "Notifying");
+ print_property(proxy, "Flags");
+ print_property(proxy, "Descriptors");
+ } else if (!strcmp(iface, "org.bluez.GattDescriptor1")) {
+ rl_printf("Descriptor - %s\n", text);
+
+ print_property(proxy, "UUID");
+ print_property(proxy, "Characteristic");
+ print_property(proxy, "Value");
+ }
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1339,6 +1403,8 @@ static const struct {
dev_generator },
{ "select-attribute", "<attribute>", cmd_select_attribute,
"Select attribute", attribute_generator },
+ { "attribute-info", "[attribute]", cmd_attribute_info,
+ "Select attribute", attribute_generator },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This command can be used to list service attributes of a device.
---
client/gatt.c | 29 +++++++++++++++++++++++++++++
client/gatt.h | 2 ++
client/main.c | 13 +++++++++++++
3 files changed, 44 insertions(+)
diff --git a/client/gatt.c b/client/gatt.c
index 47785a8..7c44a9f 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -220,3 +220,32 @@ void gatt_remove_descriptor(GDBusProxy *proxy)
print_descriptor(proxy, COLORED_DEL);
}
+
+static void list_attributes(const char *path, GList *source)
+{
+ GList *l;
+
+ for (l = source; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+ const char *proxy_path;
+
+ proxy_path = g_dbus_proxy_get_path(proxy);
+
+ if (!g_str_has_prefix(proxy_path, path))
+ continue;
+
+ if (source == services) {
+ print_service(proxy, NULL);
+ list_attributes(proxy_path, characteristics);
+ } else if (source == characteristics) {
+ print_characteristic(proxy, NULL);
+ list_attributes(proxy_path, descriptors);
+ } else if (source == descriptors)
+ print_descriptor(proxy, NULL);
+ }
+}
+
+void gatt_list_attributes(const char *path)
+{
+ list_attributes(path, services);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 8b30668..5785073 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -29,3 +29,5 @@ void gatt_remove_characteristic(GDBusProxy *proxy);
void gatt_add_descriptor(GDBusProxy *proxy);
void gatt_remove_descriptor(GDBusProxy *proxy);
+
+void gatt_list_attributes(const char *device);
diff --git a/client/main.c b/client/main.c
index 72223d5..7cdb19a 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1150,6 +1150,17 @@ static void cmd_disconn(const char *arg)
rl_printf("Attempting to disconnect from %s\n", arg);
}
+static void cmd_list_attributes(const char *arg)
+{
+ GDBusProxy *proxy;
+
+ proxy = find_device(arg);
+ if (!proxy)
+ return;
+
+ gatt_list_attributes(g_dbus_proxy_get_path(proxy));
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1268,6 +1279,8 @@ static const struct {
dev_generator },
{ "disconnect", "[dev]", cmd_disconn, "Disconnect device",
dev_generator },
+ { "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
+ dev_generator },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
Command select-attribute can be used to select a service attribute.
---
client/gatt.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 2 ++
client/main.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
3 files changed, 146 insertions(+), 9 deletions(-)
diff --git a/client/gatt.c b/client/gatt.c
index 7c44a9f..8fb3f62 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -249,3 +249,80 @@ void gatt_list_attributes(const char *path)
{
list_attributes(path, services);
}
+
+static GDBusProxy *select_proxy(const char *path, GList *source)
+{
+ GList *l;
+
+ for (l = source; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+ return proxy;
+ }
+
+ return NULL;
+}
+
+GDBusProxy *gatt_select_attribute(const char *path)
+{
+ GDBusProxy *proxy;
+
+ proxy = select_proxy(path, services);
+ if (proxy)
+ return proxy;
+
+ proxy = select_proxy(path, characteristics);
+ if (proxy)
+ return proxy;
+
+ return select_proxy(path, descriptors);
+}
+
+static char *attribute_generator(const char *text, int state, GList *source)
+{
+ static int index, len;
+ GList *list;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ for (list = g_list_nth(source, index); list;
+ list = g_list_next(list)) {
+ GDBusProxy *proxy = list->data;
+ const char *path;
+
+ index++;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strncmp(path, text, len))
+ return strdup(path);
+ }
+
+ return NULL;
+}
+
+char *gatt_attribute_generator(const char *text, int state)
+{
+ static GList *list = NULL;
+
+ if (!state) {
+ GList *list1;
+
+ if (list) {
+ g_list_free(list);
+ list = NULL;
+ }
+
+ list1 = g_list_copy(characteristics);
+ list1 = g_list_concat(list1, g_list_copy(descriptors));
+
+ list = g_list_copy(services);
+ list = g_list_concat(list, list1);
+ }
+
+ return attribute_generator(text, state, list);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 5785073..bb7cb1c 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -31,3 +31,5 @@ void gatt_add_descriptor(GDBusProxy *proxy);
void gatt_remove_descriptor(GDBusProxy *proxy);
void gatt_list_attributes(const char *device);
+GDBusProxy *gatt_select_attribute(const char *path);
+char *gatt_attribute_generator(const char *text, int state);
diff --git a/client/main.c b/client/main.c
index 7cdb19a..1481694 100644
--- a/client/main.c
+++ b/client/main.c
@@ -59,6 +59,7 @@ static char *auto_register_agent = NULL;
static GDBusProxy *default_ctrl;
static GDBusProxy *default_dev;
+static GDBusProxy *default_attr;
static GList *ctrl_list;
static GList *dev_list;
@@ -346,20 +347,30 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
}
}
-static void set_default_device(GDBusProxy *proxy)
+static void set_default_device(GDBusProxy *proxy, const char *attribute)
{
char *desc = NULL;
DBusMessageIter iter;
+ const char *path;
default_dev = proxy;
+ if (proxy == NULL) {
+ default_attr = NULL;
+ goto done;
+ }
+
if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) {
if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
goto done;
}
+ path = g_dbus_proxy_get_path(proxy);
+
dbus_message_iter_get_basic(&iter, &desc);
- desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
+ desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc,
+ attribute ? ":" : "",
+ attribute ? attribute + strlen(path) : "");
done:
rl_set_prompt(desc ? desc : PROMPT_ON);
@@ -367,6 +378,17 @@ done:
g_free(desc);
}
+static void set_default_attribute(GDBusProxy *proxy)
+{
+ const char *path;
+
+ default_attr = proxy;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ set_default_device(default_dev, path);
+}
+
static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -380,7 +402,7 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
print_device(proxy, COLORED_DEL);
if (default_dev == proxy)
- set_default_device(NULL);
+ set_default_device(NULL, NULL);
}
} else if (!strcmp(interface, "org.bluez.Adapter1")) {
ctrl_list = g_list_remove(ctrl_list, proxy);
@@ -389,7 +411,7 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
if (default_ctrl == proxy) {
default_ctrl = NULL;
- set_default_device(NULL);
+ set_default_device(NULL, NULL);
g_list_free(dev_list);
dev_list = NULL;
@@ -401,12 +423,22 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
agent_unregister(dbus_conn, NULL);
}
} else if (!strcmp(interface, "org.bluez.GattService1")) {
- if (service_is_child(proxy))
+ if (service_is_child(proxy)) {
gatt_remove_service(proxy);
+
+ if (default_attr == proxy)
+ set_default_attribute(NULL);
+ }
} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
gatt_remove_characteristic(proxy);
+
+ if (default_attr == proxy)
+ set_default_attribute(NULL);
} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
gatt_remove_descriptor(proxy);
+
+ if (default_attr == proxy)
+ set_default_attribute(NULL);
}
}
@@ -439,9 +471,9 @@ static void property_changed(GDBusProxy *proxy, const char *name,
dbus_message_iter_get_basic(iter, &connected);
if (connected && default_dev == NULL)
- set_default_device(proxy);
+ set_default_device(proxy, NULL);
else if (!connected && default_dev == proxy)
- set_default_device(NULL);
+ set_default_device(NULL, NULL);
}
print_iter(str, name, iter);
@@ -1085,7 +1117,7 @@ static void connect_reply(DBusMessage *message, void *user_data)
rl_printf("Connection successful\n");
- set_default_device(proxy);
+ set_default_device(proxy, NULL);
}
static void cmd_connect(const char *arg)
@@ -1130,7 +1162,7 @@ static void disconn_reply(DBusMessage *message, void *user_data)
if (proxy != default_dev)
return;
- set_default_device(NULL);
+ set_default_device(NULL, NULL);
}
static void cmd_disconn(const char *arg)
@@ -1161,6 +1193,25 @@ static void cmd_list_attributes(const char *arg)
gatt_list_attributes(g_dbus_proxy_get_path(proxy));
}
+static void cmd_select_attribute(const char *arg)
+{
+ GDBusProxy *proxy;
+
+ if (!arg || !strlen(arg)) {
+ rl_printf("Missing attribute argument\n");
+ return;
+ }
+
+ if (!default_dev) {
+ rl_printf("No device connected\n");
+ return;
+ }
+
+ proxy = gatt_select_attribute(arg);
+ if (proxy)
+ set_default_attribute(proxy);
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1212,6 +1263,11 @@ static char *dev_generator(const char *text, int state)
return generic_generator(text, state, dev_list, "Address");
}
+static char *attribute_generator(const char *text, int state)
+{
+ return gatt_attribute_generator(text, state);
+}
+
static char *capability_generator(const char *text, int state)
{
static int index, len;
@@ -1281,6 +1337,8 @@ static const struct {
dev_generator },
{ "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
dev_generator },
+ { "select-attribute", "<attribute>", cmd_select_attribute,
+ "Select attribute", attribute_generator },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
In case a device is connected make it the default and print to the
prompt, so the user don't have to type in its address.
---
client/main.c | 153 ++++++++++++++++++++++++++++++++--------------------------
1 file changed, 85 insertions(+), 68 deletions(-)
diff --git a/client/main.c b/client/main.c
index 0590266..72223d5 100644
--- a/client/main.c
+++ b/client/main.c
@@ -58,6 +58,7 @@ static GDBusProxy *agent_manager;
static char *auto_register_agent = NULL;
static GDBusProxy *default_ctrl;
+static GDBusProxy *default_dev;
static GList *ctrl_list;
static GList *dev_list;
@@ -345,6 +346,27 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
}
}
+static void set_default_device(GDBusProxy *proxy)
+{
+ char *desc = NULL;
+ DBusMessageIter iter;
+
+ default_dev = proxy;
+
+ if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) {
+ if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&iter, &desc);
+ desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
+
+done:
+ rl_set_prompt(desc ? desc : PROMPT_ON);
+ rl_redisplay();
+ g_free(desc);
+}
+
static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -356,6 +378,9 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
dev_list = g_list_remove(dev_list, proxy);
print_device(proxy, COLORED_DEL);
+
+ if (default_dev == proxy)
+ set_default_device(NULL);
}
} else if (!strcmp(interface, "org.bluez.Adapter1")) {
ctrl_list = g_list_remove(ctrl_list, proxy);
@@ -364,6 +389,7 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
if (default_ctrl == proxy) {
default_ctrl = NULL;
+ set_default_device(NULL);
g_list_free(dev_list);
dev_list = NULL;
@@ -407,6 +433,17 @@ static void property_changed(GDBusProxy *proxy, const char *name,
} else
str = g_strdup("");
+ if (strcmp(name, "Connected") == 0) {
+ dbus_bool_t connected;
+
+ dbus_message_iter_get_basic(iter, &connected);
+
+ if (connected && default_dev == NULL)
+ set_default_device(proxy);
+ else if (!connected && default_dev == proxy)
+ set_default_device(NULL);
+ }
+
print_iter(str, name, iter);
g_free(str);
}
@@ -810,23 +847,36 @@ static void cmd_scan(const char *arg)
}
}
-static void cmd_info(const char *arg)
+static struct GDBusProxy *find_device(const char *arg)
{
GDBusProxy *proxy;
- DBusMessageIter iter;
- const char *address;
if (!arg || !strlen(arg)) {
+ if (default_dev)
+ return default_dev;
rl_printf("Missing device address argument\n");
- return;
+ return NULL;
}
proxy = find_proxy_by_address(dev_list, arg);
if (!proxy) {
rl_printf("Device %s not available\n", arg);
- return;
+ return NULL;
}
+ return proxy;
+}
+
+static void cmd_info(const char *arg)
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *address;
+
+ proxy = find_device(arg);
+ if (!proxy)
+ return;
+
if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
return;
@@ -866,16 +916,9 @@ static void cmd_pair(const char *arg)
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
- return;
- }
if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
NULL, NULL) == FALSE) {
@@ -892,16 +935,9 @@ static void cmd_trust(const char *arg)
dbus_bool_t trusted;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
trusted = TRUE;
@@ -921,16 +957,9 @@ static void cmd_untrust(const char *arg)
dbus_bool_t trusted;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
trusted = FALSE;
@@ -950,16 +979,9 @@ static void cmd_block(const char *arg)
dbus_bool_t blocked;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
blocked = TRUE;
@@ -979,16 +1001,9 @@ static void cmd_unblock(const char *arg)
dbus_bool_t blocked;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
blocked = FALSE;
@@ -1057,6 +1072,7 @@ static void cmd_remove(const char *arg)
static void connect_reply(DBusMessage *message, void *user_data)
{
+ GDBusProxy *proxy = user_data;
DBusError error;
dbus_error_init(&error);
@@ -1068,6 +1084,8 @@ static void connect_reply(DBusMessage *message, void *user_data)
}
rl_printf("Connection successful\n");
+
+ set_default_device(proxy);
}
static void cmd_connect(const char *arg)
@@ -1086,7 +1104,7 @@ static void cmd_connect(const char *arg)
}
if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
- NULL, NULL) == FALSE) {
+ proxy, NULL) == FALSE) {
rl_printf("Failed to connect\n");
return;
}
@@ -1096,6 +1114,7 @@ static void cmd_connect(const char *arg)
static void disconn_reply(DBusMessage *message, void *user_data)
{
+ GDBusProxy *proxy = user_data;
DBusError error;
dbus_error_init(&error);
@@ -1107,25 +1126,23 @@ static void disconn_reply(DBusMessage *message, void *user_data)
}
rl_printf("Successful disconnected\n");
+
+ if (proxy != default_dev)
+ return;
+
+ set_default_device(NULL);
}
static void cmd_disconn(const char *arg)
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
- return;
- }
if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
- NULL, NULL) == FALSE) {
+ proxy, NULL) == FALSE) {
rl_printf("Failed to disconnect\n");
return;
}
@@ -1233,23 +1250,23 @@ static const struct {
{ "default-agent",NULL, cmd_default_agent,
"Set agent as the default one" },
{ "scan", "<on/off>", cmd_scan, "Scan for devices" },
- { "info", "<dev>", cmd_info, "Device information",
+ { "info", "[dev]", cmd_info, "Device information",
dev_generator },
- { "pair", "<dev>", cmd_pair, "Pair with device",
+ { "pair", "[dev]", cmd_pair, "Pair with device",
dev_generator },
- { "trust", "<dev>", cmd_trust, "Trust device",
+ { "trust", "[dev]", cmd_trust, "Trust device",
dev_generator },
- { "untrust", "<dev>", cmd_untrust, "Untrust device",
+ { "untrust", "[dev]", cmd_untrust, "Untrust device",
dev_generator },
- { "block", "<dev>", cmd_block, "Block device",
+ { "block", "[dev]", cmd_block, "Block device",
dev_generator },
- { "unblock", "<dev>", cmd_unblock, "Unblock device",
+ { "unblock", "[dev]", cmd_unblock, "Unblock device",
dev_generator },
{ "remove", "<dev>", cmd_remove, "Remove device",
dev_generator },
{ "connect", "<dev>", cmd_connect, "Connect device",
dev_generator },
- { "disconnect", "<dev>", cmd_disconn, "Disconnect device",
+ { "disconnect", "[dev]", cmd_disconn, "Disconnect device",
dev_generator },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This add support for GattDescriptor1 interface detection and
prints when they are added or removed.
---
client/gatt.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 3 +++
client/main.c | 4 ++++
3 files changed, 73 insertions(+)
diff --git a/client/gatt.c b/client/gatt.c
index 8fd113f..47785a8 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -47,6 +47,7 @@
static GList *services;
static GList *characteristics;
+static GList *descriptors;
static void print_service(GDBusProxy *proxy, const char *description)
{
@@ -154,3 +155,68 @@ void gatt_remove_characteristic(GDBusProxy *proxy)
print_characteristic(proxy, COLORED_DEL);
}
+
+static void print_descriptor(GDBusProxy *proxy, 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);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = uuid;
+
+ rl_printf("%s%s%sDescriptor %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ text);
+}
+
+static gboolean descriptor_is_child(GDBusProxy *characteristic)
+{
+ GList *l;
+ DBusMessageIter iter;
+ const char *service, *path;
+
+ if (!g_dbus_proxy_get_property(characteristic, "Characteristic", &iter))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &service);
+
+ for (l = characteristics; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strcmp(path, service))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void gatt_add_descriptor(GDBusProxy *proxy)
+{
+ if (!descriptor_is_child(proxy))
+ return;
+
+ descriptors = g_list_append(descriptors, proxy);
+
+ print_descriptor(proxy, COLORED_NEW);
+}
+
+void gatt_remove_descriptor(GDBusProxy *proxy)
+{
+ if (!descriptor_is_child(proxy))
+ return;
+
+ descriptors = g_list_remove(descriptors, proxy);
+
+ print_descriptor(proxy, COLORED_DEL);
+}
diff --git a/client/gatt.h b/client/gatt.h
index 924c4d9..8b30668 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -26,3 +26,6 @@ void gatt_remove_service(GDBusProxy *proxy);
void gatt_add_characteristic(GDBusProxy *proxy);
void gatt_remove_characteristic(GDBusProxy *proxy);
+
+void gatt_add_descriptor(GDBusProxy *proxy);
+void gatt_remove_descriptor(GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index 3db18af..0590266 100644
--- a/client/main.c
+++ b/client/main.c
@@ -340,6 +340,8 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
gatt_add_service(proxy);
} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
gatt_add_characteristic(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+ gatt_add_descriptor(proxy);
}
}
@@ -377,6 +379,8 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
gatt_remove_service(proxy);
} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
gatt_remove_characteristic(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+ gatt_remove_descriptor(proxy);
}
}
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This add support for GattCharacteristici1 interface detection and
prints when they are added or removed
---
client/gatt.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 3 +++
client/main.c | 4 ++++
3 files changed, 73 insertions(+)
diff --git a/client/gatt.c b/client/gatt.c
index 7de5c7b..8fd113f 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -46,6 +46,7 @@
#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
static GList *services;
+static GList *characteristics;
static void print_service(GDBusProxy *proxy, const char *description)
{
@@ -88,3 +89,68 @@ void gatt_remove_service(GDBusProxy *proxy)
print_service(proxy, COLORED_DEL);
}
+
+static void print_characteristic(GDBusProxy *proxy, 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);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = uuid;
+
+ rl_printf("%s%s%sCharacteristic %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ text);
+}
+
+static gboolean characteristic_is_child(GDBusProxy *characteristic)
+{
+ GList *l;
+ DBusMessageIter iter;
+ const char *service, *path;
+
+ if (!g_dbus_proxy_get_property(characteristic, "Service", &iter))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &service);
+
+ for (l = services; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strcmp(path, service))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void gatt_add_characteristic(GDBusProxy *proxy)
+{
+ if (!characteristic_is_child(proxy))
+ return;
+
+ characteristics = g_list_append(characteristics, proxy);
+
+ print_characteristic(proxy, COLORED_NEW);
+}
+
+void gatt_remove_characteristic(GDBusProxy *proxy)
+{
+ if (!characteristic_is_child(proxy))
+ return;
+
+ characteristics = g_list_remove(characteristics, proxy);
+
+ print_characteristic(proxy, COLORED_DEL);
+}
diff --git a/client/gatt.h b/client/gatt.h
index f049039..924c4d9 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -23,3 +23,6 @@
void gatt_add_service(GDBusProxy *proxy);
void gatt_remove_service(GDBusProxy *proxy);
+
+void gatt_add_characteristic(GDBusProxy *proxy);
+void gatt_remove_characteristic(GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index ab644ba..3db18af 100644
--- a/client/main.c
+++ b/client/main.c
@@ -338,6 +338,8 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
} else if (!strcmp(interface, "org.bluez.GattService1")) {
if (service_is_child(proxy))
gatt_add_service(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+ gatt_add_characteristic(proxy);
}
}
@@ -373,6 +375,8 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
} else if (!strcmp(interface, "org.bluez.GattService1")) {
if (service_is_child(proxy))
gatt_remove_service(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+ gatt_remove_characteristic(proxy);
}
}
--
2.1.0
From: Luiz Augusto von Dentz <[email protected]>
This add support for GattService1 interface detection and prints when
they are added or removed
---
Makefile.tools | 1 +
client/gatt.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client/gatt.h | 25 ++++++++++++++++
client/main.c | 30 ++++++++++++++++++++
4 files changed, 146 insertions(+)
create mode 100644 client/gatt.c
create mode 100644 client/gatt.h
diff --git a/Makefile.tools b/Makefile.tools
index e42e42d..64da54f 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -5,6 +5,7 @@ bin_PROGRAMS += client/bluetoothctl
client_bluetoothctl_SOURCES = client/main.c \
client/display.h client/display.c \
client/agent.h client/agent.c \
+ client/gatt.h client/gatt.c \
monitor/uuid.h monitor/uuid.c
client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
-lreadline
diff --git a/client/gatt.c b/client/gatt.c
new file mode 100644
index 0000000..7de5c7b
--- /dev/null
+++ b/client/gatt.c
@@ -0,0 +1,90 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "monitor/uuid.h"
+#include "display.h"
+#include "gatt.h"
+
+/* 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
+
+static GList *services;
+
+static void print_service(GDBusProxy *proxy, const char *description)
+{
+ DBusMessageIter iter;
+ const char *uuid, *text;
+ dbus_bool_t primary;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &primary);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = uuid;
+
+ rl_printf("%s%s%sService %s %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ text, primary ? "(Primary)" : "(Secondary)");
+}
+
+void gatt_add_service(GDBusProxy *proxy)
+{
+ services = g_list_append(services, proxy);
+
+ print_service(proxy, COLORED_NEW);
+}
+
+void gatt_remove_service(GDBusProxy *proxy)
+{
+ services = g_list_remove(services, proxy);
+
+ print_service(proxy, COLORED_DEL);
+}
diff --git a/client/gatt.h b/client/gatt.h
new file mode 100644
index 0000000..f049039
--- /dev/null
+++ b/client/gatt.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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
+ *
+ */
+
+void gatt_add_service(GDBusProxy *proxy);
+void gatt_remove_service(GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index ea80ee7..ab644ba 100644
--- a/client/main.c
+++ b/client/main.c
@@ -41,6 +41,7 @@
#include "monitor/uuid.h"
#include "agent.h"
#include "display.h"
+#include "gatt.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
@@ -284,6 +285,29 @@ static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master)
return FALSE;
}
+static gboolean service_is_child(GDBusProxy *service)
+{
+ GList *l;
+ DBusMessageIter iter;
+ const char *device, *path;
+
+ if (g_dbus_proxy_get_property(service, "Device", &iter) == FALSE)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &device);
+
+ for (l = dev_list; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strcmp(path, device))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -311,6 +335,9 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
agent_register(dbus_conn, agent_manager,
auto_register_agent);
}
+ } else if (!strcmp(interface, "org.bluez.GattService1")) {
+ if (service_is_child(proxy))
+ gatt_add_service(proxy);
}
}
@@ -343,6 +370,9 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
if (auto_register_agent)
agent_unregister(dbus_conn, NULL);
}
+ } else if (!strcmp(interface, "org.bluez.GattService1")) {
+ if (service_is_child(proxy))
+ gatt_remove_service(proxy);
}
}
--
2.1.0