Skip to content

Commit e7ff724

Browse files
committed
extmod/btstack: Reset pending_value_handle before calling write-done cb.
The pending_value_handle needs to be freed and reset before calling mp_bluetooth_gattc_on_read_write_status(), which will call the Python IRQ handler, which may in turn call back into BTstack to perform an action like a write. In that case the pending_value_handle will need to be available for the write/read/etc to proceed. Fixes issue #13611. Signed-off-by: Damien George <damien@micropython.org>
1 parent cc7cfc7 commit e7ff724

File tree

3 files changed

+233
-1
lines changed

3 files changed

+233
-1
lines changed

extmod/btstack/modbluetooth_btstack.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,12 @@ STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint
490490
if (!conn) {
491491
return;
492492
}
493-
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, conn->pending_value_handle, status);
493+
uint16_t value_handle = conn->pending_value_handle;
494494
conn->pending_value_handle = 0xffff;
495495
m_del(uint8_t, conn->pending_write_value, conn->pending_write_value_len);
496496
conn->pending_write_value = NULL;
497497
conn->pending_write_value_len = 0;
498+
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, value_handle, status);
498499
}
499500
}
500501
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Test calling BLE methods from within the BLE.irq event handler.
2+
3+
from micropython import const
4+
import struct
5+
import time
6+
import bluetooth
7+
8+
_IRQ_CENTRAL_CONNECT = const(1)
9+
_IRQ_CENTRAL_DISCONNECT = const(2)
10+
_IRQ_PERIPHERAL_CONNECT = const(7)
11+
_IRQ_PERIPHERAL_DISCONNECT = const(8)
12+
_IRQ_GATTC_SERVICE_RESULT = const(9)
13+
_IRQ_GATTC_SERVICE_DONE = const(10)
14+
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
15+
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
16+
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
17+
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
18+
_IRQ_GATTC_READ_RESULT = const(15)
19+
_IRQ_GATTC_READ_DONE = const(16)
20+
_IRQ_GATTC_WRITE_DONE = const(17)
21+
_IRQ_MTU_EXCHANGED = const(21)
22+
_IRQ_GET_SECRET = const(29)
23+
_IRQ_SET_SECRET = const(30)
24+
25+
EVENT_NAMES = {
26+
1: "_IRQ_CENTRAL_CONNECT",
27+
2: "_IRQ_CENTRAL_DISCONNECT",
28+
3: "_IRQ_GATTS_WRITE",
29+
4: "_IRQ_GATTS_READ_REQUEST",
30+
7: "_IRQ_PERIPHERAL_CONNECT",
31+
8: "_IRQ_PERIPHERAL_DISCONNECT",
32+
9: "_IRQ_GATTC_SERVICE_RESULT",
33+
10: "_IRQ_GATTC_SERVICE_DONE",
34+
11: "_IRQ_GATTC_CHARACTERISTIC_RESULT",
35+
12: "_IRQ_GATTC_CHARACTERISTIC_DONE",
36+
13: "_IRQ_GATTC_DESCRIPTOR_RESULT",
37+
14: "_IRQ_GATTC_DESCRIPTOR_DONE",
38+
15: "_IRQ_GATTC_READ_RESULT",
39+
16: "_IRQ_GATTC_READ_DONE",
40+
17: "_IRQ_GATTC_WRITE_DONE",
41+
18: "_IRQ_GATTC_NOTIFY",
42+
21: "_IRQ_MTU_EXCHANGED",
43+
}
44+
45+
_ADV_TYPE_FLAGS = const(0x01)
46+
_ADV_TYPE_NAME = const(0x09)
47+
_ADV_TYPE_UUID128_COMPLETE = const(0x7)
48+
49+
_NOTIFY_ENABLE = const(1)
50+
51+
ACCESSORY_UUID = bluetooth.UUID("a5a5a5a5-ffff-9999-1111-5a5a5a5a5a5a")
52+
STATE_UUID = bluetooth.UUID("a5a5a5a5-eeee-9999-1111-5a5a5a5a5a5a")
53+
CCC_UUID = bluetooth.UUID(0x2902)
54+
55+
STATE_CHARACTERISTIC = (
56+
STATE_UUID,
57+
bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,
58+
)
59+
60+
ACCESSORY_SERVICE = (ACCESSORY_UUID, (STATE_CHARACTERISTIC,))
61+
62+
63+
class Central:
64+
def __init__(self):
65+
self.done = False
66+
self._conn_handle = None
67+
self._service = None
68+
self._characteristic = None
69+
self._cccd_handle = None
70+
ble.active(1)
71+
ble.irq(self._ble_event_handler)
72+
ble.gap_connect(*BDADDR)
73+
74+
def _ble_event_handler(self, event, data):
75+
print(EVENT_NAMES[event])
76+
77+
if event == _IRQ_PERIPHERAL_CONNECT:
78+
conn_handle, _, _ = data
79+
self._conn_handle = conn_handle
80+
ble.gattc_discover_services(self._conn_handle, ACCESSORY_UUID)
81+
82+
elif event == _IRQ_PERIPHERAL_DISCONNECT:
83+
conn_handle, _, addr = data
84+
assert self._conn_handle == conn_handle
85+
self._conn_handle = None
86+
print("connection closed")
87+
88+
elif event == _IRQ_GATTC_SERVICE_RESULT:
89+
_, first_handle, last_handle, uuid = data
90+
print("service found:", last_handle - first_handle, uuid)
91+
if uuid == ACCESSORY_UUID:
92+
assert self._service is None
93+
self._service = (first_handle, last_handle)
94+
95+
elif event == _IRQ_GATTC_SERVICE_DONE:
96+
print("service handle range:", self._service[1] - self._service[0])
97+
start_handle, end_handle = self._service
98+
ble.gattc_discover_characteristics(self._conn_handle, start_handle, end_handle)
99+
100+
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
101+
_, end_handle, value_handle, properties, uuid = data
102+
assert uuid == STATE_UUID
103+
print("characteristic found:", uuid)
104+
self._characteristic = (end_handle, value_handle, properties)
105+
106+
elif event == _IRQ_GATTC_CHARACTERISTIC_DONE:
107+
start_handle, end_handle = self._service
108+
ble.gattc_discover_descriptors(self._conn_handle, start_handle, end_handle)
109+
110+
elif event == _IRQ_GATTC_DESCRIPTOR_RESULT:
111+
_, dsc_handle, uuid = data
112+
if uuid == CCC_UUID:
113+
print("CCCD found:", uuid)
114+
assert self._cccd_handle is None
115+
self._cccd_handle = dsc_handle
116+
117+
elif event == _IRQ_GATTC_DESCRIPTOR_DONE:
118+
# Discovery complete, proceed to MTU exchange.
119+
ble.gattc_exchange_mtu(self._conn_handle)
120+
121+
elif event == _IRQ_MTU_EXCHANGED:
122+
# MTU exchanged, proceed to enable CCCD.
123+
print("CCCD write")
124+
ble.gattc_write(
125+
self._conn_handle, self._cccd_handle, struct.pack("<h", _NOTIFY_ENABLE), 1
126+
)
127+
128+
elif event == _IRQ_GATTC_WRITE_DONE:
129+
conn_handle, _, result = data
130+
print("CCCD write result:", result)
131+
_, state_handle, _ = self._characteristic
132+
print("issue gattc_read")
133+
ble.gattc_read(self._conn_handle, state_handle)
134+
135+
elif event == _IRQ_GATTC_READ_RESULT:
136+
_, _, char_data = data
137+
print("gattc_read result:", bytes(char_data))
138+
139+
elif event == _IRQ_GATTC_READ_DONE:
140+
self.done = True
141+
ble.gap_disconnect(self._conn_handle)
142+
143+
144+
class Peripheral:
145+
def __init__(self):
146+
self.done = False
147+
ble.active(1)
148+
ble.irq(self._ble_event_handler)
149+
ble.gatts_register_services((ACCESSORY_SERVICE,))
150+
add_payload = self.advertising_payload("acc", (ACCESSORY_UUID,))
151+
ble.gap_advertise(500000, add_payload)
152+
153+
def advertising_payload(self, name, services):
154+
payload = bytearray()
155+
156+
def _append(adv_type, value):
157+
nonlocal payload
158+
payload.extend(struct.pack("BB", len(value) + 1, adv_type) + value)
159+
160+
_append(_ADV_TYPE_FLAGS, struct.pack("B", 0x02 + 0x04))
161+
_append(_ADV_TYPE_NAME, name)
162+
163+
for uuid in services:
164+
b = bytes(uuid)
165+
assert len(b) == 16
166+
_append(_ADV_TYPE_UUID128_COMPLETE, b)
167+
168+
return payload
169+
170+
def _ble_event_handler(self, event, data):
171+
if event not in (_IRQ_GET_SECRET, _IRQ_SET_SECRET):
172+
print(EVENT_NAMES[event])
173+
if event == _IRQ_CENTRAL_DISCONNECT:
174+
self.done = True
175+
176+
177+
# Acting in peripheral role.
178+
def instance0():
179+
print("peripheral start")
180+
peripheral = Peripheral()
181+
multitest.globals(BDADDR=ble.config("mac"))
182+
multitest.next()
183+
while not peripheral.done:
184+
time.sleep_ms(100)
185+
multitest.broadcast("finished")
186+
ble.active(0)
187+
188+
189+
# Acting in central role.
190+
def instance1():
191+
print("central start")
192+
multitest.next()
193+
central = Central()
194+
while not central.done:
195+
time.sleep_ms(100)
196+
multitest.wait("finished")
197+
ble.active(0)
198+
199+
200+
ble = bluetooth.BLE()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--- instance0 ---
2+
peripheral start
3+
_IRQ_CENTRAL_CONNECT
4+
_IRQ_MTU_EXCHANGED
5+
_IRQ_GATTS_READ_REQUEST
6+
_IRQ_CENTRAL_DISCONNECT
7+
--- instance1 ---
8+
central start
9+
_IRQ_PERIPHERAL_CONNECT
10+
_IRQ_GATTC_SERVICE_RESULT
11+
service found: 3 UUID('a5a5a5a5-ffff-9999-1111-5a5a5a5a5a5a')
12+
_IRQ_GATTC_SERVICE_DONE
13+
service handle range: 3
14+
_IRQ_GATTC_CHARACTERISTIC_RESULT
15+
characteristic found: UUID('a5a5a5a5-eeee-9999-1111-5a5a5a5a5a5a')
16+
_IRQ_GATTC_CHARACTERISTIC_DONE
17+
_IRQ_GATTC_DESCRIPTOR_RESULT
18+
_IRQ_GATTC_DESCRIPTOR_RESULT
19+
_IRQ_GATTC_DESCRIPTOR_RESULT
20+
CCCD found: UUID(0x2902)
21+
_IRQ_GATTC_DESCRIPTOR_DONE
22+
_IRQ_MTU_EXCHANGED
23+
CCCD write
24+
_IRQ_GATTC_WRITE_DONE
25+
CCCD write result: 0
26+
issue gattc_read
27+
_IRQ_GATTC_READ_RESULT
28+
gattc_read result: b''
29+
_IRQ_GATTC_READ_DONE
30+
_IRQ_PERIPHERAL_DISCONNECT
31+
connection closed

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy