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

+2-1
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
+200
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()
+31
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)