This patch set moves GATT read/write functions from shared/gatt-helpers to
shared/gatt-client. The following new functions are now available:
bt_gatt_client_read_value
bt_gatt_client_read_long_value
bt_gatt_client_write_without_response
bt_gatt_client_write_value
bt_gatt_client_write_long_value
This patch set also adds new commands to tools/btgatt-client which map to the
functions above.
Arman Uguray (10):
shared/gatt-client: Added bt_gatt_client_read_value.
shared/gatt-client: Added bt_gatt_client_read_long_value.
shared/gatt-client: Added simple write operations.
shared/gatt-client: Added bt_gatt_client_write_long_value.
TODO: GATT read/write functions moved to shared/gatt-client.
tools/btgatt-client: Added the "read-value" command.
tools/btgatt-client: Added the "read-long-value" command.
tools/btgatt-client: Added the "write-value" command.
tools/btgatt-client: Added the "write-long-value" command.
TODO: tools/btgatt-client introduced
TODO | 13 -
src/shared/gatt-client.c | 774 ++++++++++++++++++++++++++++++++++++++++++++--
src/shared/gatt-client.h | 36 +++
src/shared/gatt-helpers.c | 625 -------------------------------------
src/shared/gatt-helpers.h | 30 --
tools/btgatt-client.c | 396 +++++++++++++++++++++++-
6 files changed, 1160 insertions(+), 714 deletions(-)
--
2.1.0.rc2.206.gedb03e5
Hi Arman,
> This patch set moves GATT read/write functions from shared/gatt-helpers to
> shared/gatt-client. The following new functions are now available:
>
> bt_gatt_client_read_value
> bt_gatt_client_read_long_value
> bt_gatt_client_write_without_response
> bt_gatt_client_write_value
> bt_gatt_client_write_long_value
>
> This patch set also adds new commands to tools/btgatt-client which map to the
> functions above.
>
> Arman Uguray (10):
> shared/gatt-client: Added bt_gatt_client_read_value.
> shared/gatt-client: Added bt_gatt_client_read_long_value.
> shared/gatt-client: Added simple write operations.
> shared/gatt-client: Added bt_gatt_client_write_long_value.
> TODO: GATT read/write functions moved to shared/gatt-client.
> tools/btgatt-client: Added the "read-value" command.
> tools/btgatt-client: Added the "read-long-value" command.
> tools/btgatt-client: Added the "write-value" command.
> tools/btgatt-client: Added the "write-long-value" command.
> TODO: tools/btgatt-client introduced
>
> TODO | 13 -
> src/shared/gatt-client.c | 774 ++++++++++++++++++++++++++++++++++++++++++++--
> src/shared/gatt-client.h | 36 +++
> src/shared/gatt-helpers.c | 625 -------------------------------------
> src/shared/gatt-helpers.h | 30 --
> tools/btgatt-client.c | 396 +++++++++++++++++++++++-
> 6 files changed, 1160 insertions(+), 714 deletions(-)
all 10 patches have been applied.
Regards
Marcel
Added the "write-long-value" command which can be used to perform the "long
write" procedure.
---
tools/btgatt-client.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 130 insertions(+)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 7b3c8ed..3fd492b 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -569,6 +569,134 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
free(value);
}
+static void write_long_value_usage(void)
+{
+ printf("Usage: write-long-value [options] <value_handle> <offset> "
+ "<value>\n"
+ "Options:\n"
+ "\t-r, --reliable-write\tReliable write\n"
+ "e.g.:\n"
+ "\twrite-long-value 0x0001 0 00 01 00\n");
+}
+
+static struct option write_long_value_options[] = {
+ { "reliable-write", 0, 0, 'r' },
+ { }
+};
+
+static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode,
+ void *user_data)
+{
+ if (success) {
+ PRLOG("Write successful\n");
+ } else if (reliable_error) {
+ PRLOG("Reliable write not verified\n");
+ } else {
+ PRLOG("Write failed: 0x%02x\n", att_ecode);
+ }
+}
+
+static void cmd_write_long_value(struct client *cli, char *cmd_str)
+{
+ int opt, i;
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 1;
+ uint16_t handle;
+ uint16_t offset;
+ char *endptr = NULL;
+ int length;
+ uint8_t *value = NULL;
+ bool reliable_writes = false;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ optind = 0;
+ argv[0] = "write-long-value";
+ while ((opt = getopt_long(argc, argv, "+r", write_long_value_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'r':
+ reliable_writes = true;
+ break;
+ default:
+ write_long_value_usage();
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ write_long_value_usage();
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 16);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid handle: %s\n", argv[0]);
+ return;
+ }
+
+ endptr = NULL;
+ offset = strtol(argv[1], &endptr, 10);
+ if (!endptr || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid offset: %s\n", argv[1]);
+ return;
+ }
+
+ length = argc - 1;
+
+ if (length > 0) {
+ if (length > UINT16_MAX) {
+ printf("Write value too long\n");
+ return;
+ }
+
+ value = malloc(length);
+ if (!value) {
+ printf("Failed to construct write value\n");
+ return;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (strlen(argv[i]) != 2) {
+ printf("Invalid value byte: %s\n",
+ argv[i]);
+ free(value);
+ return;
+ }
+
+ value[i-2] = strtol(argv[i], &endptr, 16);
+ if (endptr == argv[i] || *endptr != '\0'
+ || errno == ERANGE) {
+ printf("Invalid value byte: %s\n",
+ argv[i]);
+ free(value);
+ return;
+ }
+ }
+ }
+
+ if (!bt_gatt_client_write_long_value(cli->gatt, reliable_writes, handle,
+ offset, value, length,
+ write_long_cb,
+ NULL, NULL))
+ printf("Failed to initiate long write procedure\n");
+
+ free(value);
+}
+
static void cmd_help(struct client *cli, char *cmd_str);
typedef void (*command_func_t)(struct client *cli, char *cmd_str);
@@ -586,6 +714,8 @@ static struct {
"\tRead a long characteristic or desctriptor value" },
{ "write-value", cmd_write_value,
"\tWrite a characteristic or descriptor value" },
+ { "write-long-value", cmd_write_long_value,
+ "Write long characteristic or descriptor value" },
{ }
};
--
2.1.0.rc2.206.gedb03e5
---
TODO | 6 ------
1 file changed, 6 deletions(-)
diff --git a/TODO b/TODO
index 16de35b..5277e39 100644
--- a/TODO
+++ b/TODO
@@ -177,12 +177,6 @@ ATT/GATT (new shared stack)
Priority: Low
Complexity: C4
-- Introduce a new GATT client command-line tool to test and use a single
- instance of bt_gatt_client.
-
- Priority: Low
- Complexity: C1
-
- Move all daemon plugins and profiles that are GATT based to use
shared/gatt-client instead of attrib/*. This is a complicated task that
potentially needs a new plugin/profile probing interface and a lot of
--
2.1.0.rc2.206.gedb03e5
Added the "read-long-value" command which can be used to read long
characteristic and descriptor values.
---
tools/btgatt-client.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index e0c48be..dec1a29 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -401,6 +401,47 @@ static void cmd_read_value(struct client *cli, char *cmd_str)
printf("Failed to initiate read value procedure\n");
}
+static void read_long_value_usage(void)
+{
+ printf("Usage: read-long-value <value_handle> <offset>\n");
+}
+
+static void cmd_read_long_value(struct client *cli, char *cmd_str)
+{
+ char *argv[3];
+ int argc = 0;
+ uint16_t handle;
+ uint16_t offset;
+ char *endptr = NULL;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) {
+ read_long_value_usage();
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 16);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid value handle: %s\n", argv[0]);
+ return;
+ }
+
+ endptr = NULL;
+ offset = strtol(argv[1], &endptr, 16);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid offset: %s\n", argv[1]);
+ return;
+ }
+
+ if (!bt_gatt_client_read_long_value(cli->gatt, handle, offset, read_cb,
+ NULL, NULL))
+ printf("Failed to initiate read long value procedure\n");
+}
+
static void cmd_help(struct client *cli, char *cmd_str);
typedef void (*command_func_t)(struct client *cli, char *cmd_str);
@@ -414,6 +455,8 @@ static struct {
{ "services", cmd_services, "\tShow discovered services" },
{ "read-value", cmd_read_value,
"\tRead a characteristic or descriptor Value" },
+ { "read-long-value", cmd_read_long_value,
+ "\tRead a long characteristic or desctriptor value" },
{ }
};
--
2.1.0.rc2.206.gedb03e5
Added the "write-value" command which can be used to perform "write" and "write
without response" procedures.
---
tools/btgatt-client.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 130 insertions(+), 1 deletion(-)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index dec1a29..7b3c8ed 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -442,6 +442,133 @@ static void cmd_read_long_value(struct client *cli, char *cmd_str)
printf("Failed to initiate read long value procedure\n");
}
+static void write_value_usage(void)
+{
+ printf("Usage: write-value [options] <value_handle> <value>\n"
+ "Options:\n"
+ "\t-w, --without-response\tWrite without response\n"
+ "e.g.:\n"
+ "\twrite-value 0x0001 00 01 00\n");
+}
+
+static struct option write_value_options[] = {
+ { "without-response", 0, 0, 'w' },
+ { }
+};
+
+static void write_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+ if (success) {
+ PRLOG("\nWrite successful\n");
+ } else {
+ PRLOG("\nWrite failed: 0x%02x\n", att_ecode);
+ }
+}
+
+static void cmd_write_value(struct client *cli, char *cmd_str)
+{
+ int opt, i;
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 1;
+ uint16_t handle;
+ char *endptr = NULL;
+ int length;
+ uint8_t *value = NULL;
+ bool without_response = false;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ optind = 0;
+ argv[0] = "write-value";
+ while ((opt = getopt_long(argc, argv, "+w", write_value_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'w':
+ without_response = true;
+ break;
+ default:
+ write_value_usage();
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ write_value_usage();
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 16);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid handle: %s\n", argv[0]);
+ return;
+ }
+
+ length = argc - 1;
+
+ if (length > 0) {
+ if (length > UINT16_MAX) {
+ printf("Write value too long\n");
+ return;
+ }
+
+ value = malloc(length);
+ if (!value) {
+ printf("Failed to construct write value\n");
+ return;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (strlen(argv[i]) != 2) {
+ printf("Invalid value byte: %s\n",
+ argv[i]);
+ free(value);
+ return;
+ }
+
+ value[i-1] = strtol(argv[i], &endptr, 16);
+ if (endptr == argv[i] || *endptr != '\0'
+ || errno == ERANGE) {
+ printf("Invalid value byte: %s\n",
+ argv[i]);
+ free(value);
+ return;
+ }
+ }
+ }
+
+ if (without_response) {
+ if (!bt_gatt_client_write_without_response(cli->gatt, handle,
+ false, value, length)) {
+ printf("Failed to initiate write without response "
+ "procedure\n");
+ return;
+ }
+
+ printf("Write command sent\n");
+ return;
+ }
+
+ if (!bt_gatt_client_write_value(cli->gatt, handle, value, length,
+ write_cb,
+ NULL, NULL))
+ printf("Failed to initiate write procedure\n");
+
+ free(value);
+}
+
static void cmd_help(struct client *cli, char *cmd_str);
typedef void (*command_func_t)(struct client *cli, char *cmd_str);
@@ -454,9 +581,11 @@ static struct {
{ "help", cmd_help, "\tDisplay help message" },
{ "services", cmd_services, "\tShow discovered services" },
{ "read-value", cmd_read_value,
- "\tRead a characteristic or descriptor Value" },
+ "\tRead a characteristic or descriptor value" },
{ "read-long-value", cmd_read_long_value,
"\tRead a long characteristic or desctriptor value" },
+ { "write-value", cmd_write_value,
+ "\tWrite a characteristic or descriptor value" },
{ }
};
--
2.1.0.rc2.206.gedb03e5
---
TODO | 7 -------
1 file changed, 7 deletions(-)
diff --git a/TODO b/TODO
index be56f28..16de35b 100644
--- a/TODO
+++ b/TODO
@@ -145,13 +145,6 @@ ATT/GATT (new shared stack)
Priority: Medium
Complexity: C1
-- Merge functions from shared/gatt-helpers involving value reads and writes into
- shared/gatt-client. These functions should accept an instance of
- bt_gatt_client instead of taking a bt_att directly.
-
- Priority: Medium
- Complexity: C1
-
- Properly handle indications from the "Service Changed" characteristic.
shared/gatt-client should automatically rediscover all changed GATT services
and notify the upper layer using a specially assigned handler.
--
2.1.0.rc2.206.gedb03e5
Moved the bt_gatt_read_long_value function from shared/gatt-helpers to
shared/gatt-client as bt_gatt_client_read_long_value.
---
src/shared/gatt-client.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-client.h | 5 ++
src/shared/gatt-helpers.c | 210 ---------------------------------------------
src/shared/gatt-helpers.h | 9 --
4 files changed, 216 insertions(+), 219 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 45ea2c3..b839db1 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -26,6 +26,7 @@
#include "lib/uuid.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -736,3 +737,213 @@ bool bt_gatt_client_read_value(struct bt_gatt_client *client,
return true;
}
+
+struct read_long_op {
+ struct bt_gatt_client *client;
+ int ref_count;
+ uint16_t value_handle;
+ size_t orig_offset;
+ size_t offset;
+ struct queue *blobs;
+ bt_gatt_client_read_callback_t callback;
+ void *user_data;
+ bt_gatt_client_destroy_func_t destroy;
+};
+
+struct blob {
+ uint8_t *data;
+ uint16_t offset;
+ uint16_t length;
+};
+
+static struct blob *create_blob(const uint8_t *data, uint16_t len,
+ uint16_t offset)
+{
+ struct blob *blob;
+
+ blob = new0(struct blob, 1);
+ if (!blob)
+ return NULL;
+
+ blob->data = malloc(len);
+ if (!blob->data) {
+ free(blob);
+ return NULL;
+ }
+
+ memcpy(blob->data, data, len);
+ blob->length = len;
+ blob->offset = offset;
+
+ return blob;
+}
+
+static void destroy_blob(void *data)
+{
+ struct blob *blob = data;
+
+ free(blob->data);
+ free(blob);
+}
+
+static struct read_long_op *read_long_op_ref(struct read_long_op *op)
+{
+ __sync_fetch_and_add(&op->ref_count, 1);
+
+ return op;
+}
+
+static void read_long_op_unref(void *data)
+{
+ struct read_long_op *op = data;
+
+ if (__sync_sub_and_fetch(&op->ref_count, 1))
+ return;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ queue_destroy(op->blobs, destroy_blob);
+
+ free(op);
+}
+
+static void append_blob(void *data, void *user_data)
+{
+ struct blob *blob = data;
+ uint8_t *value = user_data;
+
+ memcpy(value + blob->offset, blob->data, blob->length);
+}
+
+static void complete_read_long_op(struct read_long_op *op, bool success,
+ uint8_t att_ecode)
+{
+ uint8_t *value = NULL;
+ uint16_t length = 0;
+
+ if (!success)
+ goto done;
+
+ length = op->offset - op->orig_offset;
+
+ if (!length)
+ goto done;
+
+ value = malloc(length);
+ if (!value) {
+ success = false;
+ goto done;
+ }
+
+ queue_foreach(op->blobs, append_blob, value - op->orig_offset);
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, value, length, op->user_data);
+
+ free(value);
+}
+
+static void read_long_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct read_long_op *op = user_data;
+ struct blob *blob;
+ bool success;
+ uint8_t att_ecode = 0;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_READ_BLOB_RSP || (!pdu && length)) {
+ success = false;
+ goto done;
+ }
+
+ if (!length)
+ goto success;
+
+ blob = create_blob(pdu, length, op->offset);
+ if (!blob) {
+ success = false;
+ goto done;
+ }
+
+ queue_push_tail(op->blobs, blob);
+ op->offset += length;
+ if (op->offset > UINT16_MAX)
+ goto success;
+
+ if (length >= bt_att_get_mtu(op->client->att) - 1) {
+ uint8_t pdu[4];
+
+ put_le16(op->value_handle, pdu);
+ put_le16(op->offset, pdu + 2);
+
+ if (bt_att_send(op->client->att, BT_ATT_OP_READ_BLOB_REQ,
+ pdu, sizeof(pdu),
+ read_long_cb,
+ read_long_op_ref(op),
+ read_long_op_unref))
+ return;
+
+ read_long_op_unref(op);
+ success = false;
+ goto done;
+ }
+
+success:
+ success = true;
+
+done:
+ complete_read_long_op(op, success, att_ecode);
+}
+
+bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
+ uint16_t value_handle, uint16_t offset,
+ bt_gatt_client_read_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct read_long_op *op;
+ uint8_t pdu[4];
+
+ if (!client)
+ return false;
+
+ op = new0(struct read_long_op, 1);
+ if (!op)
+ return false;
+
+ op->blobs = queue_new();
+ if (!op->blobs) {
+ free(op);
+ return false;
+ }
+
+ op->client = client;
+ op->value_handle = value_handle;
+ op->orig_offset = offset;
+ op->offset = offset;
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ put_le16(value_handle, pdu);
+ put_le16(offset, pdu + 2);
+
+ if (!bt_att_send(client->att, BT_ATT_OP_READ_BLOB_REQ, pdu, sizeof(pdu),
+ read_long_cb,
+ read_long_op_ref(op),
+ read_long_op_unref)) {
+ queue_destroy(op->blobs, free);
+ free(op);
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index b7ec84a..bcb8ab1 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -97,3 +97,8 @@ bool bt_gatt_client_read_value(struct bt_gatt_client *client,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
+ uint16_t value_handle, uint16_t offset,
+ bt_gatt_client_read_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 763292f..81fb039 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -912,216 +912,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
return true;
}
-struct read_long_op {
- struct bt_att *att;
- int ref_count;
- uint16_t value_handle;
- size_t orig_offset;
- size_t offset;
- struct queue *blobs;
- bt_gatt_read_callback_t callback;
- void *user_data;
- bt_gatt_destroy_func_t destroy;
-};
-
-struct blob {
- uint8_t *data;
- uint16_t offset;
- uint16_t length;
-};
-
-static struct blob *create_blob(const uint8_t *data, uint16_t len,
- uint16_t offset)
-{
- struct blob *blob;
-
- blob = new0(struct blob, 1);
- if (!blob)
- return NULL;
-
- blob->data = malloc(len);
- if (!blob->data) {
- free(blob);
- return NULL;
- }
-
- memcpy(blob->data, data, len);
- blob->length = len;
- blob->offset = offset;
-
- return blob;
-}
-
-static void destroy_blob(void *data)
-{
- struct blob *blob = data;
-
- free(blob->data);
- free(blob);
-}
-
-static struct read_long_op *read_long_op_ref(struct read_long_op *op)
-{
- __sync_fetch_and_add(&op->ref_count, 1);
-
- return op;
-}
-
-static void read_long_op_unref(void *data)
-{
- struct read_long_op *op = data;
-
- if (__sync_sub_and_fetch(&op->ref_count, 1))
- return;
-
- if (op->destroy)
- op->destroy(op->user_data);
-
- queue_destroy(op->blobs, destroy_blob);
-
- free(op);
-}
-
-static void append_blob(void *data, void *user_data)
-{
- struct blob *blob = data;
- uint8_t *value = user_data;
-
- memcpy(value + blob->offset, blob->data, blob->length);
-}
-
-static void complete_read_long_op(struct read_long_op *op, bool success,
- uint8_t att_ecode)
-{
- uint8_t *value = NULL;
- uint16_t length = 0;
-
- if (!success)
- goto done;
-
- length = op->offset - op->orig_offset;
-
- if (!length)
- goto done;
-
- value = malloc(length);
- if (!value) {
- success = false;
- goto done;
- }
-
- queue_foreach(op->blobs, append_blob, value - op->orig_offset);
-
-done:
- if (op->callback)
- op->callback(success, att_ecode, value, length, op->user_data);
-
- free(value);
-}
-
-static void read_long_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
-{
- struct read_long_op *op = user_data;
- struct blob *blob;
- bool success;
- uint8_t att_ecode = 0;
-
- if (opcode == BT_ATT_OP_ERROR_RSP) {
- success = false;
- att_ecode = process_error(pdu, length);
- goto done;
- }
-
- if (opcode != BT_ATT_OP_READ_BLOB_RSP || (!pdu && length)) {
- success = false;
- goto done;
- }
-
- if (!length)
- goto success;
-
- blob = create_blob(pdu, length, op->offset);
- if (!blob) {
- success = false;
- goto done;
- }
-
- queue_push_tail(op->blobs, blob);
- op->offset += length;
- if (op->offset > UINT16_MAX)
- goto success;
-
- if (length >= bt_att_get_mtu(op->att) - 1) {
- uint8_t pdu[4];
-
- put_le16(op->value_handle, pdu);
- put_le16(op->offset, pdu + 2);
-
- if (bt_att_send(op->att, BT_ATT_OP_READ_BLOB_REQ,
- pdu, sizeof(pdu),
- read_long_cb,
- read_long_op_ref(op),
- read_long_op_unref))
- return;
-
- read_long_op_unref(op);
- success = false;
- goto done;
- }
-
-success:
- success = true;
-
-done:
- complete_read_long_op(op, success, att_ecode);
-}
-
-bool bt_gatt_read_long_value(struct bt_att *att,
- uint16_t value_handle, uint16_t offset,
- bt_gatt_read_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy)
-{
- struct read_long_op *op;
- uint8_t pdu[4];
-
- if (!att)
- return false;
-
- op = new0(struct read_long_op, 1);
- if (!op)
- return false;
-
- op->blobs = queue_new();
- if (!op->blobs) {
- free(op);
- return false;
- }
-
- op->att = att;
- op->value_handle = value_handle;
- op->orig_offset = offset;
- op->offset = offset;
- op->callback = callback;
- op->user_data = user_data;
- op->destroy = destroy;
-
- put_le16(value_handle, pdu);
- put_le16(offset, pdu + 2);
-
- if (!bt_att_send(att, BT_ATT_OP_READ_BLOB_REQ, pdu, sizeof(pdu),
- read_long_cb,
- read_long_op_ref(op),
- read_long_op_unref)) {
- queue_destroy(op->blobs, free);
- free(op);
- return false;
- }
-
- return true;
-}
-
bool bt_gatt_write_without_response(struct bt_att *att,
uint16_t value_handle,
bool signed_write,
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index 1433a68..60feeaa 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -57,9 +57,6 @@ typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode,
typedef void (*bt_gatt_discovery_callback_t)(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
-typedef void (*bt_gatt_read_callback_t)(bool success, uint8_t att_ecode,
- const uint8_t *value, uint16_t length,
- void *user_data);
typedef void (*bt_gatt_write_long_callback_t)(bool success, bool reliable_error,
uint8_t att_ecode, void *user_data);
@@ -93,12 +90,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_read_long_value(struct bt_att *att,
- uint16_t value_handle, uint16_t offset,
- bt_gatt_read_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy);
-
bool bt_gatt_write_without_response(struct bt_att *att, uint16_t value_handle,
bool signed_write,
uint8_t *value, uint16_t length);
--
2.1.0.rc2.206.gedb03e5
Moved the bt_gatt_write_long_value function from shared/gatt-helpers to
shared/gatt-client as bt_gatt_client_write_long_value.
Multiple requests to start a long write operation are queued by bt_gatt_client.
This is to avoid canceling a prepared write to an unintended handle as well as
to prevent multiple long write requests to the same handle from resulting in an
unintended attribute value due to interleaving.
---
src/shared/gatt-client.c | 318 ++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-client.h | 10 ++
src/shared/gatt-helpers.c | 247 -----------------------------------
src/shared/gatt-helpers.h | 10 --
4 files changed, 328 insertions(+), 257 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 764cc75..101e47e 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -32,6 +32,10 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
#define UUID_BYTES (BT_GATT_UUID_SIZE * sizeof(uint8_t))
struct service_list {
@@ -54,6 +58,15 @@ struct bt_gatt_client {
struct service_list *svc_head, *svc_tail;
bool in_init;
bool ready;
+
+ /* Queue of long write requests. An error during "prepare write"
+ * requests can result in a cancel through "execute write". To prevent
+ * cancelation of prepared writes to the wrong attribute and multiple
+ * requests to the same attribute that may result in a corrupted final
+ * value, we avoid interleaving prepared writes.
+ */
+ struct queue *long_write_queue;
+ bool in_long_write;
};
static bool gatt_client_add_service(struct bt_gatt_client *client,
@@ -513,6 +526,12 @@ struct bt_gatt_client *bt_gatt_client_new(struct bt_att *att, uint16_t mtu)
if (!client)
return NULL;
+ client->long_write_queue = queue_new();
+ if (!client->long_write_queue) {
+ free(client);
+ return NULL;
+ }
+
client->att = bt_att_ref(att);
gatt_client_init(client, mtu);
@@ -530,6 +549,8 @@ struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client)
return client;
}
+static void long_write_op_unref(void *data);
+
void bt_gatt_client_unref(struct bt_gatt_client *client)
{
if (!client)
@@ -544,6 +565,7 @@ void bt_gatt_client_unref(struct bt_gatt_client *client)
if (client->debug_destroy)
client->debug_destroy(client->debug_data);
+ queue_destroy(client->long_write_queue, long_write_op_unref);
bt_att_unref(client->att);
free(client);
}
@@ -1038,3 +1060,299 @@ bool bt_gatt_client_write_value(struct bt_gatt_client *client,
return true;
}
+
+struct long_write_op {
+ struct bt_gatt_client *client;
+ int ref_count;
+ bool reliable;
+ bool success;
+ uint8_t att_ecode;
+ bool reliable_error;
+ uint16_t value_handle;
+ uint8_t *value;
+ uint16_t length;
+ uint16_t offset;
+ uint16_t index;
+ uint16_t cur_length;
+ bt_gatt_client_write_long_callback_t callback;
+ void *user_data;
+ bt_gatt_client_destroy_func_t destroy;
+};
+
+static struct long_write_op *long_write_op_ref(struct long_write_op *op)
+{
+ __sync_fetch_and_add(&op->ref_count, 1);
+
+ return op;
+}
+
+static void long_write_op_unref(void *data)
+{
+ struct long_write_op *op = data;
+
+ if (__sync_sub_and_fetch(&op->ref_count, 1))
+ return;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ free(op->value);
+ free(op);
+}
+
+static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data);
+static void complete_write_long_op(struct long_write_op *op, bool success,
+ uint8_t att_ecode, bool reliable_error);
+
+static void handle_next_prep_write(struct long_write_op *op)
+{
+ bool success = true;
+ uint8_t *pdu;
+
+ pdu = malloc(op->cur_length + 4);
+ if (!pdu) {
+ success = false;
+ goto done;
+ }
+
+ put_le16(op->value_handle, pdu);
+ put_le16(op->offset + op->index, pdu + 2);
+ memcpy(pdu + 4, op->value + op->index, op->cur_length);
+
+ if (!bt_att_send(op->client->att, BT_ATT_OP_PREP_WRITE_REQ,
+ pdu, op->cur_length + 4,
+ prepare_write_cb,
+ long_write_op_ref(op),
+ long_write_op_unref)) {
+ long_write_op_unref(op);
+ success = false;
+ }
+
+ free(pdu);
+
+ /* If so far successful, then the operation should continue.
+ * Otherwise, there was an error and the procedure should be
+ * completed.
+ */
+ if (success)
+ return;
+
+done:
+ complete_write_long_op(op, success, 0, false);
+}
+
+static void start_next_long_write(struct bt_gatt_client *client)
+{
+ struct long_write_op *op;
+
+ if (queue_isempty(client->long_write_queue)) {
+ client->in_long_write = false;
+ return;
+ }
+
+ op = queue_pop_head(client->long_write_queue);
+ if (!op)
+ return;
+
+ handle_next_prep_write(op);
+
+ /* send_next_prep_write adds an extra ref. Unref here to clean up if
+ * necessary, since we also added a ref before pushing to the queue.
+ */
+ long_write_op_unref(op);
+}
+
+static void execute_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct long_write_op *op = user_data;
+ bool success = op->success;
+ uint8_t att_ecode = op->att_ecode;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ } else if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length)
+ success = false;
+
+ if (op->callback)
+ op->callback(success, op->reliable_error, att_ecode,
+ op->user_data);
+
+ start_next_long_write(op->client);
+}
+
+static void complete_write_long_op(struct long_write_op *op, bool success,
+ uint8_t att_ecode, bool reliable_error)
+{
+ uint8_t pdu;
+
+ op->success = success;
+ op->att_ecode = att_ecode;
+ op->reliable_error = reliable_error;
+
+ if (success)
+ pdu = 0x01; /* Write */
+ else
+ pdu = 0x00; /* Cancel */
+
+ if (bt_att_send(op->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ &pdu, sizeof(pdu),
+ execute_write_cb,
+ long_write_op_ref(op),
+ long_write_op_unref))
+ return;
+
+ long_write_op_unref(op);
+ success = false;
+
+ if (op->callback)
+ op->callback(success, reliable_error, att_ecode, op->user_data);
+
+ start_next_long_write(op->client);
+}
+
+static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct long_write_op *op = user_data;
+ bool success = true;
+ bool reliable_error = false;
+ uint8_t att_ecode = 0;
+ uint16_t next_index;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_PREP_WRITE_RSP) {
+ success = false;
+ goto done;
+ }
+
+ if (op->reliable) {
+ if (!pdu || length != (op->cur_length + 4)) {
+ success = false;
+ reliable_error = true;
+ goto done;
+ }
+
+ if (get_le16(pdu) != op->value_handle ||
+ get_le16(pdu + 2) != (op->offset + op->index)) {
+ success = false;
+ reliable_error = true;
+ goto done;
+ }
+
+ if (memcmp(pdu + 4, op->value + op->index, op->cur_length)) {
+ success = false;
+ reliable_error = true;
+ goto done;
+ }
+ }
+
+ next_index = op->index + op->cur_length;
+ if (next_index == op->length) {
+ /* All bytes written */
+ goto done;
+ }
+
+ /* If the last written length was greater than or equal to what can fit
+ * inside a PDU, then there is more data to send.
+ */
+ if (op->cur_length >= bt_att_get_mtu(op->client->att) - 5) {
+ op->index = next_index;
+ op->cur_length = MIN(op->length - op->index,
+ bt_att_get_mtu(op->client->att) - 5);
+ handle_next_prep_write(op);
+ return;
+ }
+
+done:
+ complete_write_long_op(op, success, att_ecode, reliable_error);
+}
+
+bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+ bool reliable,
+ uint16_t value_handle, uint16_t offset,
+ uint8_t *value, uint16_t length,
+ bt_gatt_client_write_long_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct long_write_op *op;
+ uint8_t *pdu;
+ bool status;
+
+ if (!client)
+ return false;
+
+ if ((size_t)(length + offset) > UINT16_MAX)
+ return false;
+
+ /* Don't allow writing a 0-length value using this procedure. The
+ * upper-layer should use bt_gatt_write_value for that instead.
+ */
+ if (!length || !value)
+ return false;
+
+ op = new0(struct long_write_op, 1);
+ if (!op)
+ return false;
+
+ op->value = malloc(length);
+ if (!op->value) {
+ free(op);
+ return false;
+ }
+
+ memcpy(op->value, value, length);
+
+ op->client = client;
+ op->reliable = reliable;
+ op->value_handle = value_handle;
+ op->length = length;
+ op->offset = offset;
+ op->cur_length = MIN(length, bt_att_get_mtu(client->att) - 5);
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ if (client->in_long_write) {
+ queue_push_tail(client->long_write_queue,
+ long_write_op_ref(op));
+ return true;
+ }
+
+ pdu = malloc(op->cur_length + 4);
+ if (!pdu) {
+ free(op->value);
+ free(op);
+ return false;
+ }
+
+ put_le16(value_handle, pdu);
+ put_le16(offset, pdu + 2);
+ memcpy(pdu + 4, op->value, op->cur_length);
+
+ status = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ,
+ pdu, op->cur_length + 4,
+ prepare_write_cb,
+ long_write_op_ref(op),
+ long_write_op_unref);
+
+ free(pdu);
+
+ if (!status) {
+ free(op->value);
+ free(op);
+ return false;
+ }
+
+ client->in_long_write = true;
+
+ return true;
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index c3b3e20..8b0d334 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -38,6 +38,9 @@ typedef void (*bt_gatt_client_destroy_func_t)(void *user_data);
typedef void (*bt_gatt_client_callback_t)(bool success, uint8_t att_ecode,
void *user_data);
typedef void (*bt_gatt_client_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_gatt_client_write_long_callback_t)(bool success,
+ bool reliable_error, uint8_t att_ecode,
+ void *user_data);
bool bt_gatt_client_is_ready(struct bt_gatt_client *client);
bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
@@ -113,3 +116,10 @@ bool bt_gatt_client_write_value(struct bt_gatt_client *client,
bt_gatt_client_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+ bool reliable,
+ uint16_t value_handle, uint16_t offset,
+ uint8_t *value, uint16_t length,
+ bt_gatt_client_write_long_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 0331851..ede6a67 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -912,253 +912,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
return true;
}
-struct write_long_op {
- struct bt_att *att;
- int ref_count;
- bool reliable;
- bool success;
- uint8_t att_ecode;
- bool reliable_error;
- uint16_t value_handle;
- uint8_t *value;
- uint16_t length;
- uint16_t offset;
- uint16_t index;
- uint16_t cur_length;
- bt_gatt_write_long_callback_t callback;
- void *user_data;
- bt_gatt_destroy_func_t destroy;
-};
-
-static struct write_long_op *write_long_op_ref(struct write_long_op *op)
-{
- __sync_fetch_and_add(&op->ref_count, 1);
-
- return op;
-}
-
-static void write_long_op_unref(void *data)
-{
- struct write_long_op *op = data;
-
- if (__sync_sub_and_fetch(&op->ref_count, 1))
- return;
-
- if (op->destroy)
- op->destroy(op->user_data);
-
- free(op->value);
- free(op);
-}
-
-static void execute_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
- void *user_data)
-{
- struct write_long_op *op = user_data;
- bool success = op->success;
- uint8_t att_ecode = op->att_ecode;
-
- if (opcode == BT_ATT_OP_ERROR_RSP) {
- success = false;
- att_ecode = process_error(pdu, length);
- } else if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length)
- success = false;
-
- if (op->callback)
- op->callback(success, op->reliable_error, att_ecode,
- op->user_data);
-}
-
-static void complete_write_long_op(struct write_long_op *op, bool success,
- uint8_t att_ecode, bool reliable_error)
-{
- uint8_t pdu;
-
- op->success = success;
- op->att_ecode = att_ecode;
- op->reliable_error = reliable_error;
-
- if (success)
- pdu = 0x01; /* Write */
- else
- pdu = 0x00; /* Cancel */
-
- if (bt_att_send(op->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu, sizeof(pdu),
- execute_write_cb,
- write_long_op_ref(op),
- write_long_op_unref))
- return;
-
- write_long_op_unref(op);
- success = false;
-
- if (op->callback)
- op->callback(success, reliable_error, att_ecode, op->user_data);
-}
-
-static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
- void *user_data)
-{
- struct write_long_op *op = user_data;
- bool success = true;
- bool reliable_error = false;
- uint8_t att_ecode = 0;
- uint16_t next_index;
-
- if (opcode == BT_ATT_OP_ERROR_RSP) {
- success = false;
- att_ecode = process_error(pdu, length);
- goto done;
- }
-
- if (opcode != BT_ATT_OP_PREP_WRITE_RSP) {
- success = false;
- goto done;
- }
-
- if (op->reliable) {
- if (!pdu || length != (op->cur_length + 4)) {
- success = false;
- reliable_error = true;
- goto done;
- }
-
- if (get_le16(pdu) != op->value_handle ||
- get_le16(pdu + 2) != (op->offset + op->index)) {
- success = false;
- reliable_error = true;
- goto done;
- }
-
- if (memcmp(pdu + 4, op->value + op->index, op->cur_length)) {
- success = false;
- reliable_error = true;
- goto done;
- }
- }
-
- next_index = op->index + op->cur_length;
- if (next_index == op->length) {
- /* All bytes written */
- goto done;
- }
-
- /* If the last written length greater than or equal to what can fit
- * inside a PDU, then there is more data to send.
- */
- if (op->cur_length >= bt_att_get_mtu(op->att) - 5) {
- uint8_t *pdu;
-
- op->index = next_index;
- op->cur_length = MIN(op->length - op->index,
- bt_att_get_mtu(op->att) - 5);
-
- pdu = malloc(op->cur_length + 4);
- if (!pdu) {
- success = false;
- goto done;
- }
-
- put_le16(op->value_handle, pdu);
- put_le16(op->offset + op->index, pdu + 2);
- memcpy(pdu + 4, op->value + op->index, op->cur_length);
-
- if (!bt_att_send(op->att, BT_ATT_OP_PREP_WRITE_REQ,
- pdu, op->cur_length + 4,
- prepare_write_cb,
- write_long_op_ref(op),
- write_long_op_unref)) {
- write_long_op_unref(op);
- success = false;
- }
-
- free(pdu);
-
- /* If so far successful, then the operation should continue.
- * Otherwise, there was an error and the procedure should be
- * completed.
- */
- if (success)
- return;
- }
-
-done:
- complete_write_long_op(op, success, att_ecode, reliable_error);
-}
-
-bool bt_gatt_write_long_value(struct bt_att *att, bool reliable,
- uint16_t value_handle, uint16_t offset,
- uint8_t *value, uint16_t length,
- bt_gatt_write_long_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy)
-{
- struct write_long_op *op;
- uint8_t *pdu;
- bool status;
-
- if (!att)
- return false;
-
- if ((size_t)(length + offset) > UINT16_MAX)
- return false;
-
- /* Don't allow riting a 0-length value using this procedure. The
- * upper-layer should use bt_gatt_write_value for that instead.
- */
- if (!length || !value)
- return false;
-
- op = new0(struct write_long_op, 1);
- if (!op)
- return false;
-
- op->value = malloc(length);
- if (!op->value) {
- free(op);
- return false;
- }
-
- memcpy(op->value, value, length);
-
- op->att = att;
- op->reliable = reliable;
- op->value_handle = value_handle;
- op->length = length;
- op->offset = offset;
- op->cur_length = MIN(length, bt_att_get_mtu(att) - 5);
- op->callback = callback;
- op->user_data = user_data;
- op->destroy = destroy;
-
- pdu = malloc(op->cur_length + 4);
- if (!pdu) {
- free(op->value);
- free(op);
- return false;
- }
-
- put_le16(value_handle, pdu);
- put_le16(offset, pdu + 2);
- memcpy(pdu + 4, op->value, op->cur_length);
-
- status = bt_att_send(att, BT_ATT_OP_PREP_WRITE_REQ,
- pdu, op->cur_length + 4,
- prepare_write_cb,
- write_long_op_ref(op),
- write_long_op_unref);
-
- free(pdu);
-
- if (!status) {
- free(op->value);
- free(op);
- return false;
- }
-
- return true;
-}
-
struct notify_data {
struct bt_att *att;
bt_gatt_notify_callback_t callback;
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index a8e5f98..75ad4b0 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -57,8 +57,6 @@ typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode,
typedef void (*bt_gatt_discovery_callback_t)(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
-typedef void (*bt_gatt_write_long_callback_t)(bool success, bool reliable_error,
- uint8_t att_ecode, void *user_data);
typedef void (*bt_gatt_notify_callback_t)(uint16_t value_handle,
const uint8_t *value, uint16_t length,
@@ -90,14 +88,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
void *user_data,
bt_gatt_destroy_func_t destroy);
-
-bool bt_gatt_write_long_value(struct bt_att *att, bool reliable,
- uint16_t value_handle, uint16_t offset,
- uint8_t *value, uint16_t length,
- bt_gatt_write_long_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy);
-
unsigned int bt_gatt_register(struct bt_att *att, bool indications,
bt_gatt_notify_callback_t callback,
void *user_data,
--
2.1.0.rc2.206.gedb03e5
Added the "read-value" command which sends a simple read request to the
specified handle.
---
tools/btgatt-client.c | 94 ++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 82 insertions(+), 12 deletions(-)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 0f13d35..e0c48be 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -274,9 +274,27 @@ static void services_usage(void)
"\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n");
}
+static bool parse_args(char *str, int expected_argc, char **argv, int *argc)
+{
+ char **ap;
+
+ for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
+ if (**ap == '\0')
+ continue;
+
+ (*argc)++;
+ ap++;
+
+ if (*argc > expected_argc)
+ return false;
+ }
+
+ return true;
+}
+
static void cmd_services(struct client *cli, char *cmd_str)
{
- char **ap, *argv[3];
+ char *argv[3];
int argc = 0;
if (!bt_gatt_client_is_ready(cli->gatt)) {
@@ -284,17 +302,9 @@ static void cmd_services(struct client *cli, char *cmd_str)
return;
}
- for (ap = argv; (*ap = strsep(&cmd_str, " \t")) != NULL;) {
- if (**ap == '\0')
- continue;
-
- argc++;
- ap++;
-
- if (argc > 2) {
- services_usage();
- return;
- }
+ if (!parse_args(cmd_str, 2, argv, &argc)) {
+ services_usage();
+ return;
}
if (!argc) {
@@ -333,6 +343,64 @@ static void cmd_services(struct client *cli, char *cmd_str)
services_usage();
}
+static void read_value_usage(void)
+{
+ printf("Usage: read-value <value_handle>\n");
+}
+
+static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ int i;
+
+ if (!success) {
+ PRLOG("\nRead request failed: 0x%02x\n", att_ecode);
+ return;
+ }
+
+ printf("\nRead value");
+
+ if (length == 0) {
+ PRLOG(": 0 bytes\n");
+ return;
+ }
+
+ printf(" (%u bytes): ", length);
+
+ for (i = 0; i < length; i++)
+ printf("%02x ", value[i]);
+
+ PRLOG("\n");
+}
+
+static void cmd_read_value(struct client *cli, char *cmd_str)
+{
+ char *argv[2];
+ int argc = 0;
+ uint16_t handle;
+ char *endptr = NULL;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
+ read_value_usage();
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 16);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid value handle: %s\n", argv[0]);
+ return;
+ }
+
+ if (!bt_gatt_client_read_value(cli->gatt, handle, read_cb,
+ NULL, NULL))
+ printf("Failed to initiate read value procedure\n");
+}
+
static void cmd_help(struct client *cli, char *cmd_str);
typedef void (*command_func_t)(struct client *cli, char *cmd_str);
@@ -344,6 +412,8 @@ static struct {
} command[] = {
{ "help", cmd_help, "\tDisplay help message" },
{ "services", cmd_services, "\tShow discovered services" },
+ { "read-value", cmd_read_value,
+ "\tRead a characteristic or descriptor Value" },
{ }
};
--
2.1.0.rc2.206.gedb03e5
Moved the bt_gatt_write_without_response and bt_gatt_write_value functions from
shared/gatt-helpers to shared/gatt-client.
---
src/shared/gatt-client.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-client.h | 11 ++++++
src/shared/gatt-helpers.c | 91 -----------------------------------------------
src/shared/gatt-helpers.h | 9 +----
4 files changed, 103 insertions(+), 99 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index b839db1..764cc75 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -947,3 +947,94 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
return true;
}
+
+bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
+ uint16_t value_handle,
+ bool signed_write,
+ uint8_t *value, uint16_t length) {
+ uint8_t pdu[2 + length];
+
+ if (!client)
+ return 0;
+
+ /* TODO: Support this once bt_att_send supports signed writes. */
+ if (signed_write)
+ return 0;
+
+ put_le16(value_handle, pdu);
+ memcpy(pdu + 2, value, length);
+
+ return bt_att_send(client->att, BT_ATT_OP_WRITE_CMD, pdu, sizeof(pdu),
+ NULL, NULL, NULL);
+}
+
+struct write_op {
+ bt_gatt_result_callback_t callback;
+ void *user_data;
+ bt_gatt_destroy_func_t destroy;
+};
+
+static void destroy_write_op(void *data)
+{
+ struct write_op *op = data;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ free(op);
+}
+
+static void write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct write_op *op = user_data;
+ bool success = true;
+ uint8_t att_ecode = 0;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_WRITE_RSP || pdu || length)
+ success = false;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, op->user_data);
+}
+
+bool bt_gatt_client_write_value(struct bt_gatt_client *client,
+ uint16_t value_handle,
+ uint8_t *value, uint16_t length,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct write_op *op;
+ uint8_t pdu[2 + length];
+
+ if (!client)
+ return false;
+
+ op = new0(struct write_op, 1);
+ if (!op)
+ return false;
+
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ put_le16(value_handle, pdu);
+ memcpy(pdu + 2, value, length);
+
+ if (!bt_att_send(client->att, BT_ATT_OP_WRITE_REQ, pdu, sizeof(pdu),
+ write_cb, op,
+ destroy_write_op)) {
+ free(op);
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index bcb8ab1..c3b3e20 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -102,3 +102,14 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+
+bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
+ uint16_t value_handle,
+ bool signed_write,
+ uint8_t *value, uint16_t length);
+bool bt_gatt_client_write_value(struct bt_gatt_client *client,
+ uint16_t value_handle,
+ uint8_t *value, uint16_t length,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 81fb039..0331851 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -912,97 +912,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
return true;
}
-bool bt_gatt_write_without_response(struct bt_att *att,
- uint16_t value_handle,
- bool signed_write,
- uint8_t *value, uint16_t length)
-{
- uint8_t pdu[2 + length];
-
- if (!att)
- return 0;
-
- /* TODO: Support this once bt_att_send supports signed writes. */
- if (signed_write)
- return 0;
-
- put_le16(value_handle, pdu);
- memcpy(pdu + 2, value, length);
-
- return bt_att_send(att, BT_ATT_OP_WRITE_CMD, pdu, sizeof(pdu),
- NULL, NULL, NULL);
-}
-
-struct write_op {
- bt_gatt_result_callback_t callback;
- void *user_data;
- bt_gatt_destroy_func_t destroy;
-};
-
-static void destroy_write_op(void *data)
-{
- struct write_op *op = data;
-
- if (op->destroy)
- op->destroy(op->user_data);
-
- free(op);
-}
-
-static void write_cb(uint8_t opcode, const void *pdu, uint16_t length,
- void *user_data)
-{
- struct write_op *op = user_data;
- bool success = true;
- uint8_t att_ecode = 0;
-
- if (opcode == BT_ATT_OP_ERROR_RSP) {
- success = false;
- att_ecode = process_error(pdu, length);
- goto done;
- }
-
- if (opcode != BT_ATT_OP_WRITE_RSP || pdu || length)
- success = false;
-
-done:
- if (op->callback)
- op->callback(success, att_ecode, op->user_data);
-}
-
-bool bt_gatt_write_value(struct bt_att *att, uint16_t value_handle,
- uint8_t *value, uint16_t length,
- bt_gatt_result_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy)
-{
- struct write_op *op;
- uint8_t pdu[2 + length];
-
- if (!att)
- return false;
-
- op = new0(struct write_op, 1);
- if (!op)
- return false;
-
- op->callback = callback;
- op->user_data = user_data;
- op->destroy = destroy;
-
- put_le16(value_handle, pdu);
- memcpy(pdu + 2, value, length);
-
- if (!bt_att_send(att, BT_ATT_OP_WRITE_REQ, pdu, sizeof(pdu),
- write_cb, op,
- destroy_write_op)) {
- free(op);
- return false;
- }
-
- return true;
-}
-
struct write_long_op {
struct bt_att *att;
int ref_count;
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index 60feeaa..a8e5f98 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -90,14 +90,7 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_write_without_response(struct bt_att *att, uint16_t value_handle,
- bool signed_write,
- uint8_t *value, uint16_t length);
-bool bt_gatt_write_value(struct bt_att *att, uint16_t value_handle,
- uint8_t *value, uint16_t length,
- bt_gatt_result_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy);
+
bool bt_gatt_write_long_value(struct bt_att *att, bool reliable,
uint16_t value_handle, uint16_t offset,
uint8_t *value, uint16_t length,
--
2.1.0.rc2.206.gedb03e5
Moved the bt_gatt_read_value function from shared/gatt-helpers to
shared/gatt-client as bt_gatt_client_read_value.
---
src/shared/gatt-client.c | 154 ++++++++++++++++++++++++++++++++++++----------
src/shared/gatt-client.h | 10 +++
src/shared/gatt-helpers.c | 77 -----------------------
src/shared/gatt-helpers.h | 4 --
4 files changed, 130 insertions(+), 115 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index afd84eb..45ea2c3 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -105,7 +105,7 @@ static void gatt_client_clear_services(struct bt_gatt_client *client)
client->svc_head = client->svc_tail = NULL;
}
-struct async_op {
+struct discovery_op {
struct bt_gatt_client *client;
struct service_list *cur_service;
bt_gatt_characteristic_t *cur_chrc;
@@ -113,16 +113,16 @@ struct async_op {
int ref_count;
};
-static struct async_op *async_op_ref(struct async_op *op)
+static struct discovery_op *discovery_op_ref(struct discovery_op *op)
{
__sync_fetch_and_add(&op->ref_count, 1);
return op;
}
-static void async_op_unref(void *data)
+static void discovery_op_unref(void *data)
{
- struct async_op *op = data;
+ struct discovery_op *op = data;
if (__sync_sub_and_fetch(&op->ref_count, 1))
return;
@@ -130,7 +130,7 @@ static void async_op_unref(void *data)
free(data);
}
-static void async_op_complete(struct async_op *op, bool success,
+static void discovery_op_complete(struct discovery_op *op, bool success,
uint8_t att_ecode)
{
struct bt_gatt_client *client = op->client;
@@ -164,7 +164,7 @@ static void discover_descs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
{
- struct async_op *op = user_data;
+ struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
char uuid_str[MAX_LEN_UUID_STR];
@@ -227,13 +227,13 @@ static void discover_descs_cb(bool success, uint8_t att_ecode,
desc_start,
op->cur_chrc->end_handle,
discover_descs_cb,
- async_op_ref(op),
- async_op_unref))
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start descriptor discovery");
- async_op_unref(op);
+ discovery_op_unref(op);
success = false;
goto done;
@@ -249,24 +249,24 @@ next:
op->cur_service->service.start_handle,
op->cur_service->service.end_handle,
discover_chrcs_cb,
- async_op_ref(op),
- async_op_unref))
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
- async_op_unref(op);
+ discovery_op_unref(op);
success = false;
done:
- async_op_complete(op, success, att_ecode);
+ discovery_op_complete(op, success, att_ecode);
}
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
{
- struct async_op *op = user_data;
+ struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
char uuid_str[MAX_LEN_UUID_STR];
@@ -331,13 +331,13 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
if (bt_gatt_discover_descriptors(client->att,
desc_start, chrcs[i].end_handle,
discover_descs_cb,
- async_op_ref(op),
- async_op_unref))
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start descriptor discovery");
- async_op_unref(op);
+ discovery_op_unref(op);
success = false;
goto done;
@@ -353,24 +353,24 @@ next:
op->cur_service->service.start_handle,
op->cur_service->service.end_handle,
discover_chrcs_cb,
- async_op_ref(op),
- async_op_unref))
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
- async_op_unref(op);
+ discovery_op_unref(op);
success = false;
done:
- async_op_complete(op, success, att_ecode);
+ discovery_op_complete(op, success, att_ecode);
}
static void discover_primary_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
{
- struct async_op *op = user_data;
+ struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
struct bt_gatt_iter iter;
uint16_t start, end;
@@ -419,22 +419,22 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
op->cur_service->service.start_handle,
op->cur_service->service.end_handle,
discover_chrcs_cb,
- async_op_ref(op),
- async_op_unref))
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to start characteristic discovery");
- async_op_unref(op);
+ discovery_op_unref(op);
success = false;
done:
- async_op_complete(op, success, att_ecode);
+ discovery_op_complete(op, success, att_ecode);
}
static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
{
- struct async_op *op = user_data;
+ struct discovery_op *op = user_data;
struct bt_gatt_client *client = op->client;
if (!success) {
@@ -457,8 +457,8 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
if (bt_gatt_discover_primary_services(client->att, NULL,
discover_primary_cb,
- async_op_ref(op),
- async_op_unref))
+ discovery_op_ref(op),
+ discovery_op_unref))
return;
util_debug(client->debug_callback, client->debug_data,
@@ -469,17 +469,17 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
if (client->ready_callback)
client->ready_callback(success, att_ecode, client->ready_data);
- async_op_unref(op);
+ discovery_op_unref(op);
}
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
{
- struct async_op *op;
+ struct discovery_op *op;
if (client->in_init || client->ready)
return false;
- op = new0(struct async_op, 1);
+ op = new0(struct discovery_op, 1);
if (!op)
return false;
@@ -488,8 +488,8 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
/* Configure the MTU */
if (!bt_gatt_exchange_mtu(client->att, MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
exchange_mtu_cb,
- async_op_ref(op),
- async_op_unref)) {
+ discovery_op_ref(op),
+ discovery_op_unref)) {
if (client->ready_callback)
client->ready_callback(false, 0, client->ready_data);
@@ -650,3 +650,89 @@ bool bt_gatt_service_iter_next_by_uuid(struct bt_gatt_service_iter *iter,
return false;
}
+
+struct read_op {
+ bt_gatt_client_read_callback_t callback;
+ void *user_data;
+ bt_gatt_client_destroy_func_t destroy;
+};
+
+static void destroy_read_op(void *data)
+{
+ struct read_op *op = data;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ free(op);
+}
+
+static uint8_t process_error(const void *pdu, uint16_t length)
+{
+ if (!pdu || length != 4)
+ return 0;
+
+ return ((uint8_t *) pdu)[3];
+}
+
+static void read_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct read_op *op = user_data;
+ bool success;
+ uint8_t att_ecode = 0;
+ const uint8_t *value = NULL;
+ uint16_t value_len = 0;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) {
+ success = false;
+ goto done;
+ }
+
+ success = true;
+ value_len = length;
+ if (value_len)
+ value = pdu;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, value, length, op->user_data);
+}
+
+bool bt_gatt_client_read_value(struct bt_gatt_client *client,
+ uint16_t value_handle,
+ bt_gatt_client_read_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct read_op *op;
+ uint8_t pdu[2];
+
+ if (!client)
+ return false;
+
+ op = new0(struct read_op, 1);
+ if (!op)
+ return false;
+
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ put_le16(value_handle, pdu);
+
+ if (!bt_att_send(client->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
+ read_cb, op,
+ destroy_read_op)) {
+ free(op);
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index e4a4312..b7ec84a 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -87,3 +87,13 @@ bool bt_gatt_service_iter_next_by_handle(struct bt_gatt_service_iter *iter,
bool bt_gatt_service_iter_next_by_uuid(struct bt_gatt_service_iter *iter,
const uint8_t uuid[BT_GATT_UUID_SIZE],
bt_gatt_service_t *service);
+
+typedef void (*bt_gatt_client_read_callback_t)(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data);
+
+bool bt_gatt_client_read_value(struct bt_gatt_client *client,
+ uint16_t value_handle,
+ bt_gatt_client_read_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 00813f5..763292f 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -912,83 +912,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
return true;
}
-struct read_op {
- bt_gatt_read_callback_t callback;
- void *user_data;
- bt_gatt_destroy_func_t destroy;
-};
-
-static void destroy_read_op(void *data)
-{
- struct read_op *op = data;
-
- if (op->destroy)
- op->destroy(op->user_data);
-
- free(op);
-}
-
-static void read_cb(uint8_t opcode, const void *pdu, uint16_t length,
- void *user_data)
-{
- struct read_op *op = user_data;
- bool success;
- uint8_t att_ecode = 0;
- const uint8_t *value = NULL;
- uint16_t value_len = 0;
-
- if (opcode == BT_ATT_OP_ERROR_RSP) {
- success = false;
- att_ecode = process_error(pdu, length);
- goto done;
- }
-
- if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) {
- success = false;
- goto done;
- }
-
- success = true;
- value_len = length;
- if (value_len)
- value = pdu;
-
-done:
- if (op->callback)
- op->callback(success, att_ecode, value, length, op->user_data);
-}
-
-bool bt_gatt_read_value(struct bt_att *att, uint16_t value_handle,
- bt_gatt_read_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy)
-{
- struct read_op *op;
- uint8_t pdu[2];
-
- if (!att)
- return false;
-
- op = new0(struct read_op, 1);
- if (!op)
- return false;
-
- op->callback = callback;
- op->user_data = user_data;
- op->destroy = destroy;
-
- put_le16(value_handle, pdu);
-
- if (!bt_att_send(att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
- read_cb, op,
- destroy_read_op)) {
- free(op);
- return false;
- }
-
- return true;
-}
-
struct read_long_op {
struct bt_att *att;
int ref_count;
diff --git a/src/shared/gatt-helpers.h b/src/shared/gatt-helpers.h
index 443e6c5..1433a68 100644
--- a/src/shared/gatt-helpers.h
+++ b/src/shared/gatt-helpers.h
@@ -93,10 +93,6 @@ bool bt_gatt_discover_descriptors(struct bt_att *att,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_read_value(struct bt_att *att, uint16_t value_handle,
- bt_gatt_read_callback_t callback,
- void *user_data,
- bt_gatt_destroy_func_t destroy);
bool bt_gatt_read_long_value(struct bt_att *att,
uint16_t value_handle, uint16_t offset,
bt_gatt_read_callback_t callback,
--
2.1.0.rc2.206.gedb03e5