diff --git a/src/shared/att.c b/src/shared/att.c index 0ea6d55..b0fdb8e 100644 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -62,6 +62,7 @@ struct bt_att { struct queue *ind_queue; /* Queued ATT protocol indications */ struct att_send_op *pending_ind; struct queue *write_queue; /* Queue of PDUs ready to send */ + bool in_disc; /* Cleanup queues on disconnect_cb */ bool writer_active; struct queue *notify_list; /* List of registered callbacks */ @@ -211,8 +212,10 @@ static void destroy_att_send_op(void *data) free(op); } -static void cancel_att_send_op(struct att_send_op *op) +static void cancel_att_send_op(void *data) { + struct att_send_op *op = data; + if (op->destroy) op->destroy(op->user_data); @@ -572,11 +575,6 @@ static bool disconnect_cb(struct io *io, void *user_data) att->io = NULL; att->fd = -1; - /* Notify request callbacks */ - queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op); - queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op); - queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op); - if (att->pending_req) { disc_att_send_op(att->pending_req); att->pending_req = NULL; @@ -589,6 +587,15 @@ static bool disconnect_cb(struct io *io, void *user_data) bt_att_ref(att); + att->in_disc = true; + + /* Notify request callbacks */ + queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op); + queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op); + queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op); + + att->in_disc = false; + queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err)); bt_att_unregister_all(att); @@ -1306,6 +1313,30 @@ static bool match_op_id(const void *a, const void *b) return op->id == id; } +static bool bt_att_disc_cancel(struct bt_att *att, unsigned int id) +{ + struct att_send_op *op; + + op = queue_find(att->req_queue, match_op_id, UINT_TO_PTR(id)); + if (op) + goto done; + + op = queue_find(att->ind_queue, match_op_id, UINT_TO_PTR(id)); + if (op) + goto done; + + op = queue_find(att->write_queue, match_op_id, UINT_TO_PTR(id)); + +done: + if (!op) + return false; + + /* Just cancel since disconnect_cb will be cleaning up */ + cancel_att_send_op(op); + + return true; +} + bool bt_att_cancel(struct bt_att *att, unsigned int id) { struct att_send_op *op; @@ -1325,6 +1356,9 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id) return true; } + if (att->in_disc) + return bt_att_disc_cancel(att, id); + op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id)); if (op) goto done;