summaryrefslogtreecommitdiff
blob: a8475b0c427382420c4cd5f202d3c01c72af7a82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
From 697866e210e4e2dce14c95f7a7243e9b9128d01f Mon Sep 17 00:00:00 2001
From: Adam Williamson <awilliam@redhat.com>
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