From 697866e210e4e2dce14c95f7a7243e9b9128d01f Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Sun, 7 Nov 2021 15:10:53 -0800 Subject: [PATCH] Revert "attrib: Make use of bt_att_resend" This reverts commit ac2c2e10b3adb432a572b618c6f53cabb6b3c80b. It causes problems with Logitech MX wireless input devices (see https://github.com/bluez/bluez/issues/220 and https://bugzilla.redhat.com/show_bug.cgi?id=2019970 ). --- attrib/gattrib.c | 95 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/attrib/gattrib.c b/attrib/gattrib.c index 270a37ebe..bc7d4f22c 100644 --- a/attrib/gattrib.c +++ b/attrib/gattrib.c @@ -41,8 +41,13 @@ struct _GAttrib { struct queue *track_ids; }; +struct id_pair { + unsigned int org_id; + unsigned int pend_id; +}; + struct attrib_callbacks { - unsigned int id; + struct id_pair *id; GAttribResultFunc result_func; GAttribNotifyFunc notify_func; GDestroyNotify destroy_func; @@ -51,6 +56,32 @@ struct attrib_callbacks { uint16_t notify_handle; }; +static bool find_with_org_id(const void *data, const void *user_data) +{ + const struct id_pair *p = data; + unsigned int orig_id = PTR_TO_UINT(user_data); + + return (p->org_id == orig_id); +} + +static struct id_pair *store_id(GAttrib *attrib, unsigned int org_id, + unsigned int pend_id) +{ + struct id_pair *t; + + t = new0(struct id_pair, 1); + if (!t) + return NULL; + + t->org_id = org_id; + t->pend_id = pend_id; + + if (queue_push_tail(attrib->track_ids, t)) + return t; + + return NULL; +} + GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed) { gint fd; @@ -119,6 +150,9 @@ static void attrib_callbacks_destroy(void *data) if (cb->destroy_func) cb->destroy_func(cb->user_data); + if (queue_remove(cb->parent->track_ids, cb->id)) + free(cb->id); + free(data); } @@ -148,7 +182,7 @@ void g_attrib_unref(GAttrib *attrib) bt_att_unref(attrib->att); queue_destroy(attrib->callbacks, attrib_callbacks_destroy); - queue_destroy(attrib->track_ids, NULL); + queue_destroy(attrib->track_ids, free); free(attrib->buf); @@ -261,6 +295,7 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, struct attrib_callbacks *cb = NULL; bt_att_response_func_t response_cb = NULL; bt_att_destroy_func_t destroy_cb = NULL; + unsigned int pend_id; if (!attrib) return 0; @@ -282,47 +317,62 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, } - if (id == 0) - id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1, - len - 1, response_cb, cb, destroy_cb); - else { - int err; - - err = bt_att_resend(attrib->att, id, pdu[0], (void *) pdu + 1, - len - 1, response_cb, cb, destroy_cb); - if (err) - return 0; - } + pend_id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1, len - 1, + response_cb, cb, destroy_cb); - if (!id) - return id; + /* + * We store here pair as it is easier to handle it in response and in + * case where user request us to use specific id request - see below. + */ + if (id == 0) + id = pend_id; /* * If user what us to use given id, lets keep track on that so we give * user a possibility to cancel ongoing request. */ - if (cb) { - cb->id = id; - queue_push_tail(attrib->track_ids, UINT_TO_PTR(id)); - } + if (cb) + cb->id = store_id(attrib, id, pend_id); return id; } gboolean g_attrib_cancel(GAttrib *attrib, guint id) { + struct id_pair *p; + if (!attrib) return FALSE; + /* + * If request belongs to gattrib and is not yet done it has to be on + * the tracking id queue + * + * FIXME: It can happen that on the queue there is id_pair with + * given id which was provided by the user. In the same time it might + * happen that other attrib user got dynamic allocated req_id with same + * value as the one provided by the other user. + * In such case there are two clients having same request id and in + * this point of time we don't know which one calls cancel. For + * now we cancel request in which id was specified by the user. + */ + p = queue_remove_if(attrib->track_ids, find_with_org_id, + UINT_TO_PTR(id)); + if (!p) + return FALSE; + + id = p->pend_id; + free(p); + return bt_att_cancel(attrib->att, id); } static void cancel_request(void *data, void *user_data) { - unsigned int id = PTR_TO_UINT(data); + struct id_pair *p = data; GAttrib *attrib = user_data; - bt_att_cancel(attrib->att, id); + bt_att_cancel(attrib->att, p->pend_id); } gboolean g_attrib_cancel_all(GAttrib *attrib) @@ -330,8 +380,9 @@ gboolean g_attrib_cancel_all(GAttrib *attrib) if (!attrib) return FALSE; + /* Cancel only request which belongs to gattrib */ queue_foreach(attrib->track_ids, cancel_request, attrib); - queue_remove_all(attrib->track_ids, NULL, NULL, NULL); + queue_remove_all(attrib->track_ids, NULL, NULL, free); return TRUE; } -- 2.33.1