diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 0070e4a..2c14c3a 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
struct request {
struct bt_gatt_client *client;
+ bool long_write;
bool removed;
int ref_count;
unsigned int id;
bt_gatt_client_unref(client);
}
-static void long_write_op_unref(void *data);
-
static void bt_gatt_client_free(struct bt_gatt_client *client)
{
bt_gatt_client_cancel_all(client);
gatt_db_unref(client->db);
queue_destroy(client->svc_chngd_queue, free);
- queue_destroy(client->long_write_queue, long_write_op_unref);
+ queue_destroy(client->long_write_queue, request_unref);
queue_destroy(client->notify_list, notify_data_unref);
queue_destroy(client->notify_chrcs, notify_chrc_free);
queue_destroy(client->pending_requests, request_unref);
return req->id == id;
}
+static void cancel_long_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
+ void *user_data)
+{
+ /* Do nothing */
+}
+
bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
{
struct request *req;
+ uint8_t pdu = 0x00;
if (!client || !id || !client->att)
return false;
req->removed = true;
- return bt_att_cancel(client->att, req->att_id);
+ if (!bt_att_cancel(client->att, req->att_id) && !req->long_write)
+ return false;
+
+ /* If this was a long-write, we need to abort all prepared writes */
+ if (!req->long_write)
+ return true;
+
+ if (!req->att_id)
+ queue_remove(client->long_write_queue, req);
+ else
+ bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ &pdu, sizeof(pdu),
+ cancel_long_write_cb,
+ NULL, NULL);
+
+ if (queue_isempty(client->long_write_queue))
+ client->in_long_write = false;
+
+ return true;
}
static void cancel_request(void *data)
{
struct request *req = data;
+ uint8_t pdu = 0x00;
req->removed = true;
bt_att_cancel(req->client->att, req->att_id);
+
+ if (!req->long_write)
+ return;
+
+ if (!req->att_id)
+ queue_remove(req->client->long_write_queue, req);
+
+ if (queue_isempty(req->client->long_write_queue))
+ req->client->in_long_write = false;
+
+ bt_att_send(req->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ &pdu, sizeof(pdu),
+ cancel_long_write_cb,
+ NULL, NULL);
}
bool bt_gatt_client_cancel_all(struct bt_gatt_client *client)
struct long_write_op {
struct bt_gatt_client *client;
- int ref_count;
bool reliable;
bool success;
uint8_t att_ecode;
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)
+static void long_write_op_free(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);
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,
+static void complete_write_long_op(struct request *req, bool success,
uint8_t att_ecode, bool reliable_error);
-static void handle_next_prep_write(struct long_write_op *op)
+static void handle_next_prep_write(struct request *req)
{
+ struct long_write_op *op = req->data;
bool success = true;
uint8_t *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,
+ req->att_id = 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);
+ request_ref(req),
+ request_unref);
+ if (!req->att_id) {
+ request_unref(req);
success = false;
}
return;
done:
- complete_write_long_op(op, success, 0, false);
+ complete_write_long_op(req, success, 0, false);
}
static void start_next_long_write(struct bt_gatt_client *client)
{
- struct long_write_op *op;
+ struct request *req;
if (queue_isempty(client->long_write_queue)) {
client->in_long_write = false;
return;
}
- op = queue_pop_head(client->long_write_queue);
- if (!op)
+ req = queue_pop_head(client->long_write_queue);
+ if (!req)
return;
- handle_next_prep_write(op);
+ handle_next_prep_write(req);
- /* send_next_prep_write adds an extra ref. Unref here to clean up if
+ /*
+ * 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);
+ request_unref(req);
}
static void execute_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct long_write_op *op = user_data;
+ struct request *req = user_data;
+ struct long_write_op *op = req->data;
bool success = op->success;
uint8_t att_ecode = op->att_ecode;
start_next_long_write(op->client);
}
-static void complete_write_long_op(struct long_write_op *op, bool success,
+static void complete_write_long_op(struct request *req, bool success,
uint8_t att_ecode, bool reliable_error)
{
+ struct long_write_op *op = req->data;
uint8_t pdu;
op->success = success;
else
pdu = 0x00; /* Cancel */
- if (bt_att_send(op->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ req->att_id = 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))
+ request_ref(req),
+ request_unref);
+ if (req->att_id)
return;
- long_write_op_unref(op);
+
+ request_unref(req);
success = false;
if (op->callback)
static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct long_write_op *op = user_data;
+ struct request *req = user_data;
+ struct long_write_op *op = req->data;
bool success = true;
bool reliable_error = false;
uint8_t att_ecode = 0;
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);
+ handle_next_prep_write(req);
return;
}
done:
- complete_write_long_op(op, success, att_ecode, reliable_error);
+ complete_write_long_op(req, success, att_ecode, reliable_error);
}
-bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
bool reliable,
uint16_t value_handle, uint16_t offset,
const uint8_t *value, uint16_t length,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
+ struct request *req;
struct long_write_op *op;
uint8_t *pdu;
- bool status;
if (!client)
- return false;
+ return 0;
if ((size_t)(length + offset) > UINT16_MAX)
- return false;
+ return 0;
/* 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;
+ return 0;
op = new0(struct long_write_op, 1);
if (!op)
- return false;
+ return 0;
op->value = malloc(length);
if (!op->value) {
free(op);
- return false;
+ return 0;
+ }
+
+ req = request_create(client);
+ if (!req) {
+ free(op->value);
+ free(op);
+ return 0;
}
memcpy(op->value, value, length);
op->user_data = user_data;
op->destroy = destroy;
+ req->data = op;
+ req->destroy = long_write_op_free;
+ req->long_write = true;
+
if (client->in_long_write) {
- queue_push_tail(client->long_write_queue,
- long_write_op_ref(op));
- return true;
+ queue_push_tail(client->long_write_queue, req);
+ return req->id;
}
pdu = malloc(op->cur_length + 4);
if (!pdu) {
free(op->value);
free(op);
- return false;
+ return 0;
}
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,
+ req->att_id = 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);
-
+ prepare_write_cb, req,
+ request_unref);
free(pdu);
- if (!status) {
- free(op->value);
- free(op);
- return false;
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
}
client->in_long_write = true;
- return true;
+ return req->id;
}
static bool match_notify_chrc_value_handle(const void *a, const void *b)
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 74d781c..9a00feb 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
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,
+unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
bool reliable,
uint16_t value_handle, uint16_t offset,
const uint8_t *value, uint16_t length,