bumble 0.0.179__py3-none-any.whl → 0.0.181__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/apps/bench.py +110 -81
- bumble/apps/ble_rpa_tool.py +63 -0
- bumble/apps/console.py +4 -4
- bumble/apps/controller_info.py +34 -2
- bumble/apps/pair.py +6 -8
- bumble/att.py +53 -11
- bumble/controller.py +28 -1
- bumble/crypto.py +10 -0
- bumble/device.py +630 -134
- bumble/drivers/__init__.py +27 -31
- bumble/drivers/common.py +45 -0
- bumble/drivers/rtk.py +11 -4
- bumble/gatt.py +183 -58
- bumble/gatt_client.py +56 -20
- bumble/gatt_server.py +30 -22
- bumble/hci.py +227 -92
- bumble/helpers.py +81 -42
- bumble/hfp.py +37 -27
- bumble/hid.py +282 -61
- bumble/host.py +158 -93
- bumble/l2cap.py +11 -3
- bumble/profiles/asha_service.py +2 -2
- bumble/profiles/bap.py +1247 -0
- bumble/profiles/cap.py +52 -0
- bumble/profiles/csip.py +205 -0
- bumble/rfcomm.py +24 -17
- bumble/smp.py +1 -1
- bumble/transport/__init__.py +49 -21
- bumble/transport/android_emulator.py +1 -1
- bumble/transport/common.py +3 -2
- bumble/transport/hci_socket.py +1 -4
- bumble/transport/usb.py +1 -1
- bumble/utils.py +3 -6
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/METADATA +1 -1
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/RECORD +40 -35
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/entry_points.txt +1 -0
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/LICENSE +0 -0
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/WHEEL +0 -0
- {bumble-0.0.179.dist-info → bumble-0.0.181.dist-info}/top_level.txt +0 -0
bumble/host.py
CHANGED
|
@@ -21,7 +21,7 @@ import collections
|
|
|
21
21
|
import logging
|
|
22
22
|
import struct
|
|
23
23
|
|
|
24
|
-
from typing import
|
|
24
|
+
from typing import Any, Awaitable, Callable, Deque, Dict, Optional, cast, TYPE_CHECKING
|
|
25
25
|
|
|
26
26
|
from bumble.colors import color
|
|
27
27
|
from bumble.l2cap import L2CAP_PDU
|
|
@@ -32,8 +32,8 @@ from .hci import (
|
|
|
32
32
|
Address,
|
|
33
33
|
HCI_ACL_DATA_PACKET,
|
|
34
34
|
HCI_COMMAND_PACKET,
|
|
35
|
-
HCI_COMMAND_COMPLETE_EVENT,
|
|
36
35
|
HCI_EVENT_PACKET,
|
|
36
|
+
HCI_ISO_DATA_PACKET,
|
|
37
37
|
HCI_LE_READ_BUFFER_SIZE_COMMAND,
|
|
38
38
|
HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND,
|
|
39
39
|
HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND,
|
|
@@ -52,6 +52,7 @@ from .hci import (
|
|
|
52
52
|
HCI_Constant,
|
|
53
53
|
HCI_Error,
|
|
54
54
|
HCI_Event,
|
|
55
|
+
HCI_IsoDataPacket,
|
|
55
56
|
HCI_LE_Long_Term_Key_Request_Negative_Reply_Command,
|
|
56
57
|
HCI_LE_Long_Term_Key_Request_Reply_Command,
|
|
57
58
|
HCI_LE_Read_Buffer_Size_Command,
|
|
@@ -75,7 +76,6 @@ from .core import (
|
|
|
75
76
|
BT_LE_TRANSPORT,
|
|
76
77
|
ConnectionPHY,
|
|
77
78
|
ConnectionParameters,
|
|
78
|
-
InvalidStateError,
|
|
79
79
|
)
|
|
80
80
|
from .utils import AbortableEventEmitter
|
|
81
81
|
from .transport.common import TransportLostError
|
|
@@ -91,16 +91,49 @@ logger = logging.getLogger(__name__)
|
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
# -----------------------------------------------------------------------------
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
class AclPacketQueue:
|
|
95
|
+
max_packet_size: int
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
max_packet_size: int,
|
|
100
|
+
max_in_flight: int,
|
|
101
|
+
send: Callable[[HCI_Packet], None],
|
|
102
|
+
) -> None:
|
|
103
|
+
self.max_packet_size = max_packet_size
|
|
104
|
+
self.max_in_flight = max_in_flight
|
|
105
|
+
self.in_flight = 0
|
|
106
|
+
self.send = send
|
|
107
|
+
self.packets: Deque[HCI_AclDataPacket] = collections.deque()
|
|
108
|
+
|
|
109
|
+
def enqueue(self, packet: HCI_AclDataPacket) -> None:
|
|
110
|
+
self.packets.appendleft(packet)
|
|
111
|
+
self.check_queue()
|
|
112
|
+
|
|
113
|
+
if self.packets:
|
|
114
|
+
logger.debug(
|
|
115
|
+
f'{self.in_flight} ACL packets in flight, '
|
|
116
|
+
f'{len(self.packets)} in queue'
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def check_queue(self) -> None:
|
|
120
|
+
while self.packets and self.in_flight < self.max_in_flight:
|
|
121
|
+
packet = self.packets.pop()
|
|
122
|
+
self.send(packet)
|
|
123
|
+
self.in_flight += 1
|
|
97
124
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
def on_packets_completed(self, packet_count: int) -> None:
|
|
126
|
+
if packet_count > self.in_flight:
|
|
127
|
+
logger.warning(
|
|
128
|
+
color(
|
|
129
|
+
'!!! {packet_count} completed but only '
|
|
130
|
+
f'{self.in_flight} in flight'
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
packet_count = self.in_flight
|
|
102
134
|
|
|
103
|
-
|
|
135
|
+
self.in_flight -= packet_count
|
|
136
|
+
self.check_queue()
|
|
104
137
|
|
|
105
138
|
|
|
106
139
|
# -----------------------------------------------------------------------------
|
|
@@ -111,6 +144,13 @@ class Connection:
|
|
|
111
144
|
self.peer_address = peer_address
|
|
112
145
|
self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu)
|
|
113
146
|
self.transport = transport
|
|
147
|
+
acl_packet_queue: Optional[AclPacketQueue] = (
|
|
148
|
+
host.le_acl_packet_queue
|
|
149
|
+
if transport == BT_LE_TRANSPORT
|
|
150
|
+
else host.acl_packet_queue
|
|
151
|
+
)
|
|
152
|
+
assert acl_packet_queue
|
|
153
|
+
self.acl_packet_queue = acl_packet_queue
|
|
114
154
|
|
|
115
155
|
def on_hci_acl_data_packet(self, packet: HCI_AclDataPacket) -> None:
|
|
116
156
|
self.assembler.feed_packet(packet)
|
|
@@ -123,8 +163,10 @@ class Connection:
|
|
|
123
163
|
# -----------------------------------------------------------------------------
|
|
124
164
|
class Host(AbortableEventEmitter):
|
|
125
165
|
connections: Dict[int, Connection]
|
|
126
|
-
acl_packet_queue:
|
|
127
|
-
|
|
166
|
+
acl_packet_queue: Optional[AclPacketQueue] = None
|
|
167
|
+
le_acl_packet_queue: Optional[AclPacketQueue] = None
|
|
168
|
+
hci_sink: Optional[TransportSink] = None
|
|
169
|
+
hci_metadata: Dict[str, Any]
|
|
128
170
|
long_term_key_provider: Optional[
|
|
129
171
|
Callable[[int, bytes, int], Awaitable[Optional[bytes]]]
|
|
130
172
|
]
|
|
@@ -137,18 +179,11 @@ class Host(AbortableEventEmitter):
|
|
|
137
179
|
) -> None:
|
|
138
180
|
super().__init__()
|
|
139
181
|
|
|
140
|
-
self.hci_metadata =
|
|
182
|
+
self.hci_metadata = {}
|
|
141
183
|
self.ready = False # True when we can accept incoming packets
|
|
142
|
-
self.reset_done = False
|
|
143
184
|
self.connections = {} # Connections, by connection handle
|
|
144
185
|
self.pending_command = None
|
|
145
186
|
self.pending_response = None
|
|
146
|
-
self.hc_le_acl_data_packet_length = HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH
|
|
147
|
-
self.hc_total_num_le_acl_data_packets = HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS
|
|
148
|
-
self.hc_acl_data_packet_length = HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH
|
|
149
|
-
self.hc_total_num_acl_data_packets = HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS
|
|
150
|
-
self.acl_packet_queue = collections.deque()
|
|
151
|
-
self.acl_packets_in_flight = 0
|
|
152
187
|
self.local_version = None
|
|
153
188
|
self.local_supported_commands = bytes(64)
|
|
154
189
|
self.local_le_features = 0
|
|
@@ -162,10 +197,7 @@ class Host(AbortableEventEmitter):
|
|
|
162
197
|
|
|
163
198
|
# Connect to the source and sink if specified
|
|
164
199
|
if controller_source:
|
|
165
|
-
|
|
166
|
-
self.hci_metadata = getattr(
|
|
167
|
-
controller_source, 'metadata', self.hci_metadata
|
|
168
|
-
)
|
|
200
|
+
self.set_packet_source(controller_source)
|
|
169
201
|
if controller_sink:
|
|
170
202
|
self.set_packet_sink(controller_sink)
|
|
171
203
|
|
|
@@ -200,17 +232,21 @@ class Host(AbortableEventEmitter):
|
|
|
200
232
|
self.ready = False
|
|
201
233
|
await self.flush()
|
|
202
234
|
|
|
203
|
-
await self.send_command(HCI_Reset_Command(), check_result=True)
|
|
204
|
-
self.ready = True
|
|
205
|
-
|
|
206
235
|
# Instantiate and init a driver for the host if needed.
|
|
207
236
|
# NOTE: we don't keep a reference to the driver here, because we don't
|
|
208
237
|
# currently have a need for the driver later on. But if the driver interface
|
|
209
238
|
# evolves, it may be required, then, to store a reference to the driver in
|
|
210
239
|
# an object property.
|
|
240
|
+
reset_needed = True
|
|
211
241
|
if driver_factory is not None:
|
|
212
242
|
if driver := await driver_factory(self):
|
|
213
243
|
await driver.init_controller()
|
|
244
|
+
reset_needed = False
|
|
245
|
+
|
|
246
|
+
# Send a reset command unless a driver has already done so.
|
|
247
|
+
if reset_needed:
|
|
248
|
+
await self.send_command(HCI_Reset_Command(), check_result=True)
|
|
249
|
+
self.ready = True
|
|
214
250
|
|
|
215
251
|
response = await self.send_command(
|
|
216
252
|
HCI_Read_Local_Supported_Commands_Command(), check_result=True
|
|
@@ -243,7 +279,7 @@ class Host(AbortableEventEmitter):
|
|
|
243
279
|
# understand
|
|
244
280
|
le_event_mask = bytes.fromhex('1F00000000000000')
|
|
245
281
|
else:
|
|
246
|
-
le_event_mask = bytes.fromhex('
|
|
282
|
+
le_event_mask = bytes.fromhex('FFFFFFFF00000000')
|
|
247
283
|
|
|
248
284
|
await self.send_command(
|
|
249
285
|
HCI_LE_Set_Event_Mask_Command(le_event_mask=le_event_mask)
|
|
@@ -253,46 +289,54 @@ class Host(AbortableEventEmitter):
|
|
|
253
289
|
response = await self.send_command(
|
|
254
290
|
HCI_Read_Buffer_Size_Command(), check_result=True
|
|
255
291
|
)
|
|
256
|
-
|
|
292
|
+
hc_acl_data_packet_length = (
|
|
257
293
|
response.return_parameters.hc_acl_data_packet_length
|
|
258
294
|
)
|
|
259
|
-
|
|
295
|
+
hc_total_num_acl_data_packets = (
|
|
260
296
|
response.return_parameters.hc_total_num_acl_data_packets
|
|
261
297
|
)
|
|
262
298
|
|
|
263
299
|
logger.debug(
|
|
264
300
|
'HCI ACL flow control: '
|
|
265
|
-
f'hc_acl_data_packet_length={
|
|
266
|
-
f'hc_total_num_acl_data_packets={
|
|
301
|
+
f'hc_acl_data_packet_length={hc_acl_data_packet_length},'
|
|
302
|
+
f'hc_total_num_acl_data_packets={hc_total_num_acl_data_packets}'
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
self.acl_packet_queue = AclPacketQueue(
|
|
306
|
+
max_packet_size=hc_acl_data_packet_length,
|
|
307
|
+
max_in_flight=hc_total_num_acl_data_packets,
|
|
308
|
+
send=self.send_hci_packet,
|
|
267
309
|
)
|
|
268
310
|
|
|
311
|
+
hc_le_acl_data_packet_length = 0
|
|
312
|
+
hc_total_num_le_acl_data_packets = 0
|
|
269
313
|
if self.supports_command(HCI_LE_READ_BUFFER_SIZE_COMMAND):
|
|
270
314
|
response = await self.send_command(
|
|
271
315
|
HCI_LE_Read_Buffer_Size_Command(), check_result=True
|
|
272
316
|
)
|
|
273
|
-
|
|
317
|
+
hc_le_acl_data_packet_length = (
|
|
274
318
|
response.return_parameters.hc_le_acl_data_packet_length
|
|
275
319
|
)
|
|
276
|
-
|
|
320
|
+
hc_total_num_le_acl_data_packets = (
|
|
277
321
|
response.return_parameters.hc_total_num_le_acl_data_packets
|
|
278
322
|
)
|
|
279
323
|
|
|
280
324
|
logger.debug(
|
|
281
325
|
'HCI LE ACL flow control: '
|
|
282
|
-
f'hc_le_acl_data_packet_length={
|
|
283
|
-
'hc_total_num_le_acl_data_packets='
|
|
284
|
-
f'{self.hc_total_num_le_acl_data_packets}'
|
|
326
|
+
f'hc_le_acl_data_packet_length={hc_le_acl_data_packet_length},'
|
|
327
|
+
f'hc_total_num_le_acl_data_packets={hc_total_num_le_acl_data_packets}'
|
|
285
328
|
)
|
|
286
329
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
330
|
+
if hc_le_acl_data_packet_length == 0 or hc_total_num_le_acl_data_packets == 0:
|
|
331
|
+
# LE and Classic share the same queue
|
|
332
|
+
self.le_acl_packet_queue = self.acl_packet_queue
|
|
333
|
+
else:
|
|
334
|
+
# Create a separate queue for LE
|
|
335
|
+
self.le_acl_packet_queue = AclPacketQueue(
|
|
336
|
+
max_packet_size=hc_le_acl_data_packet_length,
|
|
337
|
+
max_in_flight=hc_total_num_le_acl_data_packets,
|
|
338
|
+
send=self.send_hci_packet,
|
|
339
|
+
)
|
|
296
340
|
|
|
297
341
|
if self.supports_command(
|
|
298
342
|
HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
|
|
@@ -313,29 +357,31 @@ class Host(AbortableEventEmitter):
|
|
|
313
357
|
)
|
|
314
358
|
)
|
|
315
359
|
|
|
316
|
-
self.reset_done = True
|
|
317
|
-
|
|
318
360
|
@property
|
|
319
|
-
def controller(self) -> TransportSink:
|
|
361
|
+
def controller(self) -> Optional[TransportSink]:
|
|
320
362
|
return self.hci_sink
|
|
321
363
|
|
|
322
364
|
@controller.setter
|
|
323
|
-
def controller(self, controller):
|
|
365
|
+
def controller(self, controller) -> None:
|
|
324
366
|
self.set_packet_sink(controller)
|
|
325
367
|
if controller:
|
|
326
368
|
controller.set_packet_sink(self)
|
|
327
369
|
|
|
328
|
-
def set_packet_sink(self, sink: TransportSink) -> None:
|
|
370
|
+
def set_packet_sink(self, sink: Optional[TransportSink]) -> None:
|
|
329
371
|
self.hci_sink = sink
|
|
330
372
|
|
|
373
|
+
def set_packet_source(self, source: TransportSource) -> None:
|
|
374
|
+
source.set_packet_sink(self)
|
|
375
|
+
self.hci_metadata = getattr(source, 'metadata', self.hci_metadata)
|
|
376
|
+
|
|
331
377
|
def send_hci_packet(self, packet: HCI_Packet) -> None:
|
|
378
|
+
logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: {packet}')
|
|
332
379
|
if self.snooper:
|
|
333
380
|
self.snooper.snoop(bytes(packet), Snooper.Direction.HOST_TO_CONTROLLER)
|
|
334
|
-
self.hci_sink
|
|
381
|
+
if self.hci_sink:
|
|
382
|
+
self.hci_sink.on_packet(bytes(packet))
|
|
335
383
|
|
|
336
384
|
async def send_command(self, command, check_result=False):
|
|
337
|
-
logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: {command}')
|
|
338
|
-
|
|
339
385
|
# Wait until we can send (only one pending command at a time)
|
|
340
386
|
async with self.command_semaphore:
|
|
341
387
|
assert self.pending_command is None
|
|
@@ -383,6 +429,17 @@ class Host(AbortableEventEmitter):
|
|
|
383
429
|
asyncio.create_task(send_command(command))
|
|
384
430
|
|
|
385
431
|
def send_l2cap_pdu(self, connection_handle: int, cid: int, pdu: bytes) -> None:
|
|
432
|
+
if not (connection := self.connections.get(connection_handle)):
|
|
433
|
+
logger.warning(f'connection 0x{connection_handle:04X} not found')
|
|
434
|
+
return
|
|
435
|
+
packet_queue = connection.acl_packet_queue
|
|
436
|
+
if packet_queue is None:
|
|
437
|
+
logger.warning(
|
|
438
|
+
f'no ACL packet queue for connection 0x{connection_handle:04X}'
|
|
439
|
+
)
|
|
440
|
+
return
|
|
441
|
+
|
|
442
|
+
# Create a PDU
|
|
386
443
|
l2cap_pdu = bytes(L2CAP_PDU(cid, pdu))
|
|
387
444
|
|
|
388
445
|
# Send the data to the controller via ACL packets
|
|
@@ -390,8 +447,7 @@ class Host(AbortableEventEmitter):
|
|
|
390
447
|
offset = 0
|
|
391
448
|
pb_flag = 0
|
|
392
449
|
while bytes_remaining:
|
|
393
|
-
|
|
394
|
-
data_total_length = min(bytes_remaining, self.hc_le_acl_data_packet_length)
|
|
450
|
+
data_total_length = min(bytes_remaining, packet_queue.max_packet_size)
|
|
395
451
|
acl_packet = HCI_AclDataPacket(
|
|
396
452
|
connection_handle=connection_handle,
|
|
397
453
|
pb_flag=pb_flag,
|
|
@@ -399,34 +455,12 @@ class Host(AbortableEventEmitter):
|
|
|
399
455
|
data_total_length=data_total_length,
|
|
400
456
|
data=l2cap_pdu[offset : offset + data_total_length],
|
|
401
457
|
)
|
|
402
|
-
logger.debug(
|
|
403
|
-
|
|
404
|
-
)
|
|
405
|
-
self.queue_acl_packet(acl_packet)
|
|
458
|
+
logger.debug(f'>>> ACL packet enqueue: (CID={cid}) {acl_packet}')
|
|
459
|
+
packet_queue.enqueue(acl_packet)
|
|
406
460
|
pb_flag = 1
|
|
407
461
|
offset += data_total_length
|
|
408
462
|
bytes_remaining -= data_total_length
|
|
409
463
|
|
|
410
|
-
def queue_acl_packet(self, acl_packet: HCI_AclDataPacket) -> None:
|
|
411
|
-
self.acl_packet_queue.appendleft(acl_packet)
|
|
412
|
-
self.check_acl_packet_queue()
|
|
413
|
-
|
|
414
|
-
if len(self.acl_packet_queue):
|
|
415
|
-
logger.debug(
|
|
416
|
-
f'{self.acl_packets_in_flight} ACL packets in flight, '
|
|
417
|
-
f'{len(self.acl_packet_queue)} in queue'
|
|
418
|
-
)
|
|
419
|
-
|
|
420
|
-
def check_acl_packet_queue(self) -> None:
|
|
421
|
-
# Send all we can (TODO: support different LE/Classic limits)
|
|
422
|
-
while (
|
|
423
|
-
len(self.acl_packet_queue) > 0
|
|
424
|
-
and self.acl_packets_in_flight < self.hc_total_num_le_acl_data_packets
|
|
425
|
-
):
|
|
426
|
-
packet = self.acl_packet_queue.pop()
|
|
427
|
-
self.send_hci_packet(packet)
|
|
428
|
-
self.acl_packets_in_flight += 1
|
|
429
|
-
|
|
430
464
|
def supports_command(self, command):
|
|
431
465
|
# Find the support flag position for this command
|
|
432
466
|
for octet, flags in enumerate(HCI_SUPPORTED_COMMANDS_FLAGS):
|
|
@@ -495,6 +529,8 @@ class Host(AbortableEventEmitter):
|
|
|
495
529
|
self.on_hci_acl_data_packet(cast(HCI_AclDataPacket, packet))
|
|
496
530
|
elif packet.hci_packet_type == HCI_SYNCHRONOUS_DATA_PACKET:
|
|
497
531
|
self.on_hci_sco_data_packet(cast(HCI_SynchronousDataPacket, packet))
|
|
532
|
+
elif packet.hci_packet_type == HCI_ISO_DATA_PACKET:
|
|
533
|
+
self.on_hci_iso_data_packet(cast(HCI_IsoDataPacket, packet))
|
|
498
534
|
else:
|
|
499
535
|
logger.warning(f'!!! unknown packet type {packet.hci_packet_type}')
|
|
500
536
|
|
|
@@ -515,6 +551,10 @@ class Host(AbortableEventEmitter):
|
|
|
515
551
|
# Experimental
|
|
516
552
|
self.emit('sco_packet', packet.connection_handle, packet)
|
|
517
553
|
|
|
554
|
+
def on_hci_iso_data_packet(self, packet: HCI_IsoDataPacket) -> None:
|
|
555
|
+
# Experimental
|
|
556
|
+
self.emit('iso_packet', packet.connection_handle, packet)
|
|
557
|
+
|
|
518
558
|
def on_l2cap_pdu(self, connection: Connection, cid: int, pdu: bytes) -> None:
|
|
519
559
|
self.emit('l2cap_pdu', connection.handle, cid, pdu)
|
|
520
560
|
|
|
@@ -543,7 +583,7 @@ class Host(AbortableEventEmitter):
|
|
|
543
583
|
# This is used just for the Num_HCI_Command_Packets field, not related to
|
|
544
584
|
# an actual command
|
|
545
585
|
logger.debug('no-command event')
|
|
546
|
-
return
|
|
586
|
+
return
|
|
547
587
|
|
|
548
588
|
return self.on_command_processed(event)
|
|
549
589
|
|
|
@@ -551,18 +591,17 @@ class Host(AbortableEventEmitter):
|
|
|
551
591
|
return self.on_command_processed(event)
|
|
552
592
|
|
|
553
593
|
def on_hci_number_of_completed_packets_event(self, event):
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
self.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
'!!! {total_packets} completed but only '
|
|
562
|
-
f'{self.acl_packets_in_flight} in flight'
|
|
594
|
+
for connection_handle, num_completed_packets in zip(
|
|
595
|
+
event.connection_handles, event.num_completed_packets
|
|
596
|
+
):
|
|
597
|
+
if not (connection := self.connections.get(connection_handle)):
|
|
598
|
+
logger.warning(
|
|
599
|
+
'received packet completion event for unknown handle '
|
|
600
|
+
f'0x{connection_handle:04X}'
|
|
563
601
|
)
|
|
564
|
-
|
|
565
|
-
|
|
602
|
+
continue
|
|
603
|
+
|
|
604
|
+
connection.acl_packet_queue.on_packets_completed(num_completed_packets)
|
|
566
605
|
|
|
567
606
|
# Classic only
|
|
568
607
|
def on_hci_connection_request_event(self, event):
|
|
@@ -715,6 +754,32 @@ class Host(AbortableEventEmitter):
|
|
|
715
754
|
def on_hci_le_extended_advertising_report_event(self, event):
|
|
716
755
|
self.on_hci_le_advertising_report_event(event)
|
|
717
756
|
|
|
757
|
+
def on_hci_le_advertising_set_terminated_event(self, event):
|
|
758
|
+
self.emit(
|
|
759
|
+
'advertising_set_termination',
|
|
760
|
+
event.status,
|
|
761
|
+
event.advertising_handle,
|
|
762
|
+
event.connection_handle,
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
def on_hci_le_cis_request_event(self, event):
|
|
766
|
+
self.emit(
|
|
767
|
+
'cis_request',
|
|
768
|
+
event.acl_connection_handle,
|
|
769
|
+
event.cis_connection_handle,
|
|
770
|
+
event.cig_id,
|
|
771
|
+
event.cis_id,
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
def on_hci_le_cis_established_event(self, event):
|
|
775
|
+
# The remaining parameters are unused for now.
|
|
776
|
+
if event.status == HCI_SUCCESS:
|
|
777
|
+
self.emit('cis_establishment', event.connection_handle)
|
|
778
|
+
else:
|
|
779
|
+
self.emit(
|
|
780
|
+
'cis_establishment_failure', event.connection_handle, event.status
|
|
781
|
+
)
|
|
782
|
+
|
|
718
783
|
def on_hci_le_remote_connection_parameter_request_event(self, event):
|
|
719
784
|
if event.connection_handle not in self.connections:
|
|
720
785
|
logger.warning('!!! REMOTE CONNECTION PARAMETER REQUEST: unknown handle')
|
bumble/l2cap.py
CHANGED
|
@@ -151,8 +151,8 @@ L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS = 65535
|
|
|
151
151
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU = 23
|
|
152
152
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS = 23
|
|
153
153
|
L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS = 65533
|
|
154
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU =
|
|
155
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS =
|
|
154
|
+
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU = 2048
|
|
155
|
+
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS = 2046
|
|
156
156
|
L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS = 256
|
|
157
157
|
|
|
158
158
|
L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE = 0x01
|
|
@@ -391,6 +391,9 @@ class L2CAP_Connection_Request(L2CAP_Control_Frame):
|
|
|
391
391
|
See Bluetooth spec @ Vol 3, Part A - 4.2 CONNECTION REQUEST
|
|
392
392
|
'''
|
|
393
393
|
|
|
394
|
+
psm: int
|
|
395
|
+
source_cid: int
|
|
396
|
+
|
|
394
397
|
@staticmethod
|
|
395
398
|
def parse_psm(data: bytes, offset: int = 0) -> Tuple[int, int]:
|
|
396
399
|
psm_length = 2
|
|
@@ -432,6 +435,11 @@ class L2CAP_Connection_Response(L2CAP_Control_Frame):
|
|
|
432
435
|
See Bluetooth spec @ Vol 3, Part A - 4.3 CONNECTION RESPONSE
|
|
433
436
|
'''
|
|
434
437
|
|
|
438
|
+
source_cid: int
|
|
439
|
+
destination_cid: int
|
|
440
|
+
status: int
|
|
441
|
+
result: int
|
|
442
|
+
|
|
435
443
|
CONNECTION_SUCCESSFUL = 0x0000
|
|
436
444
|
CONNECTION_PENDING = 0x0001
|
|
437
445
|
CONNECTION_REFUSED_PSM_NOT_SUPPORTED = 0x0002
|
|
@@ -1918,7 +1926,7 @@ class ChannelManager:
|
|
|
1918
1926
|
supervision_timeout=request.timeout,
|
|
1919
1927
|
min_ce_length=0,
|
|
1920
1928
|
max_ce_length=0,
|
|
1921
|
-
)
|
|
1929
|
+
)
|
|
1922
1930
|
)
|
|
1923
1931
|
else:
|
|
1924
1932
|
self.send_control_frame(
|
bumble/profiles/asha_service.py
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
# -----------------------------------------------------------------------------
|
|
19
19
|
import struct
|
|
20
20
|
import logging
|
|
21
|
-
from typing import List
|
|
21
|
+
from typing import List, Optional
|
|
22
22
|
|
|
23
23
|
from bumble import l2cap
|
|
24
24
|
from ..core import AdvertisingData
|
|
@@ -67,7 +67,7 @@ class AshaService(TemplateService):
|
|
|
67
67
|
self.emit('volume', connection, value[0])
|
|
68
68
|
|
|
69
69
|
# Handler for audio control commands
|
|
70
|
-
def on_audio_control_point_write(connection: Connection, value):
|
|
70
|
+
def on_audio_control_point_write(connection: Optional[Connection], value):
|
|
71
71
|
logger.info(f'--- AUDIO CONTROL POINT Write:{value.hex()}')
|
|
72
72
|
opcode = value[0]
|
|
73
73
|
if opcode == AshaService.OPCODE_START:
|