bumble 0.0.180__py3-none-any.whl → 0.0.182__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 +397 -133
- bumble/apps/ble_rpa_tool.py +63 -0
- bumble/apps/console.py +4 -4
- bumble/apps/controller_info.py +64 -6
- bumble/apps/controller_loopback.py +200 -0
- bumble/apps/l2cap_bridge.py +32 -24
- bumble/apps/pair.py +6 -8
- bumble/att.py +53 -11
- bumble/controller.py +159 -24
- bumble/crypto.py +10 -0
- bumble/device.py +580 -113
- bumble/drivers/__init__.py +27 -31
- bumble/drivers/common.py +45 -0
- bumble/drivers/rtk.py +11 -4
- bumble/gatt.py +66 -51
- bumble/gatt_server.py +30 -22
- bumble/hci.py +258 -91
- bumble/helpers.py +14 -0
- bumble/hfp.py +37 -27
- bumble/hid.py +282 -61
- bumble/host.py +158 -93
- bumble/l2cap.py +11 -6
- bumble/link.py +55 -1
- bumble/profiles/asha_service.py +2 -2
- bumble/profiles/bap.py +1247 -0
- bumble/profiles/cap.py +52 -0
- bumble/profiles/csip.py +119 -9
- bumble/rfcomm.py +31 -20
- bumble/smp.py +1 -1
- bumble/transport/__init__.py +51 -22
- bumble/transport/android_emulator.py +1 -1
- bumble/transport/common.py +2 -1
- bumble/transport/hci_socket.py +1 -4
- bumble/transport/usb.py +1 -1
- bumble/utils.py +3 -6
- {bumble-0.0.180.dist-info → bumble-0.0.182.dist-info}/METADATA +1 -1
- {bumble-0.0.180.dist-info → bumble-0.0.182.dist-info}/RECORD +42 -37
- {bumble-0.0.180.dist-info → bumble-0.0.182.dist-info}/entry_points.txt +1 -0
- {bumble-0.0.180.dist-info → bumble-0.0.182.dist-info}/LICENSE +0 -0
- {bumble-0.0.180.dist-info → bumble-0.0.182.dist-info}/WHEEL +0 -0
- {bumble-0.0.180.dist-info → bumble-0.0.182.dist-info}/top_level.txt +0 -0
bumble/hci.py
CHANGED
|
@@ -17,12 +17,15 @@
|
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
import collections
|
|
20
|
+
import dataclasses
|
|
20
21
|
import enum
|
|
21
22
|
import functools
|
|
22
23
|
import logging
|
|
24
|
+
import secrets
|
|
23
25
|
import struct
|
|
24
26
|
from typing import Any, Dict, Callable, Optional, Type, Union, List
|
|
25
27
|
|
|
28
|
+
from bumble import crypto
|
|
26
29
|
from .colors import color
|
|
27
30
|
from .core import (
|
|
28
31
|
BT_BR_EDR_TRANSPORT,
|
|
@@ -560,6 +563,12 @@ HCI_LE_TRANSMITTER_TEST_V4_COMMAND = hci_c
|
|
|
560
563
|
HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND = hci_command_op_code(0x08, 0x007C)
|
|
561
564
|
HCI_LE_SET_DEFAULT_SUBRATE_COMMAND = hci_command_op_code(0x08, 0x007D)
|
|
562
565
|
HCI_LE_SUBRATE_REQUEST_COMMAND = hci_command_op_code(0x08, 0x007E)
|
|
566
|
+
HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x007F)
|
|
567
|
+
HCI_LE_SET_PERIODIC_ADVERTISING_SUBEVENT_DATA_COMMAND = hci_command_op_code(0x08, 0x0082)
|
|
568
|
+
HCI_LE_SET_PERIODIC_ADVERTISING_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0083)
|
|
569
|
+
HCI_LE_SET_PERIODIC_SYNC_SUBEVENT_COMMAND = hci_command_op_code(0x08, 0x0084)
|
|
570
|
+
HCI_LE_EXTENDED_CREATE_CONNECTION_V2_COMMAND = hci_command_op_code(0x08, 0x0085)
|
|
571
|
+
HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x0086)
|
|
563
572
|
|
|
564
573
|
|
|
565
574
|
# HCI Error Codes
|
|
@@ -721,6 +730,19 @@ HCI_LE_PHY_TYPE_TO_BIT = {
|
|
|
721
730
|
HCI_LE_CODED_PHY: HCI_LE_CODED_PHY_BIT
|
|
722
731
|
}
|
|
723
732
|
|
|
733
|
+
|
|
734
|
+
class Phy(enum.IntEnum):
|
|
735
|
+
LE_1M = 0x01
|
|
736
|
+
LE_2M = 0x02
|
|
737
|
+
LE_CODED = 0x03
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
class PhyBit(enum.IntFlag):
|
|
741
|
+
LE_1M = 0b00000001
|
|
742
|
+
LE_2M = 0b00000010
|
|
743
|
+
LE_CODED = 0b00000100
|
|
744
|
+
|
|
745
|
+
|
|
724
746
|
# Connection Parameters
|
|
725
747
|
HCI_CONNECTION_INTERVAL_MS_PER_UNIT = 1.25
|
|
726
748
|
HCI_CONNECTION_LATENCY_MS_PER_UNIT = 1.25
|
|
@@ -1316,56 +1338,72 @@ HCI_SUPPORTED_COMMANDS_FLAGS = (
|
|
|
1316
1338
|
(
|
|
1317
1339
|
HCI_LE_SET_DEFAULT_SUBRATE_COMMAND,
|
|
1318
1340
|
HCI_LE_SUBRATE_REQUEST_COMMAND,
|
|
1341
|
+
HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2_COMMAND,
|
|
1342
|
+
None,
|
|
1343
|
+
None,
|
|
1344
|
+
HCI_LE_SET_PERIODIC_ADVERTISING_SUBEVENT_DATA_COMMAND,
|
|
1345
|
+
HCI_LE_SET_PERIODIC_ADVERTISING_RESPONSE_DATA_COMMAND,
|
|
1346
|
+
HCI_LE_SET_PERIODIC_SYNC_SUBEVENT_COMMAND
|
|
1347
|
+
),
|
|
1348
|
+
# Octet 47
|
|
1349
|
+
(
|
|
1350
|
+
HCI_LE_EXTENDED_CREATE_CONNECTION_V2_COMMAND,
|
|
1351
|
+
HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_V2_COMMAND,
|
|
1352
|
+
None,
|
|
1319
1353
|
None,
|
|
1320
1354
|
None,
|
|
1321
1355
|
None,
|
|
1322
1356
|
None,
|
|
1323
1357
|
None,
|
|
1324
|
-
None
|
|
1325
1358
|
)
|
|
1326
1359
|
)
|
|
1327
1360
|
|
|
1328
1361
|
# LE Supported Features
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1362
|
+
# See Bluetooth spec @ Vol 6, Part B, 4.6 FEATURE SUPPORT
|
|
1363
|
+
HCI_LE_ENCRYPTION_LE_SUPPORTED_FEATURE = 0
|
|
1364
|
+
HCI_CONNECTION_PARAMETERS_REQUEST_PROCEDURE_LE_SUPPORTED_FEATURE = 1
|
|
1365
|
+
HCI_EXTENDED_REJECT_INDICATION_LE_SUPPORTED_FEATURE = 2
|
|
1366
|
+
HCI_PERIPHERAL_INITIATED_FEATURE_EXCHANGE_LE_SUPPORTED_FEATURE = 3
|
|
1367
|
+
HCI_LE_PING_LE_SUPPORTED_FEATURE = 4
|
|
1368
|
+
HCI_LE_DATA_PACKET_LENGTH_EXTENSION_LE_SUPPORTED_FEATURE = 5
|
|
1369
|
+
HCI_LL_PRIVACY_LE_SUPPORTED_FEATURE = 6
|
|
1370
|
+
HCI_EXTENDED_SCANNER_FILTER_POLICIES_LE_SUPPORTED_FEATURE = 7
|
|
1371
|
+
HCI_LE_2M_PHY_LE_SUPPORTED_FEATURE = 8
|
|
1372
|
+
HCI_STABLE_MODULATION_INDEX_TRANSMITTER_LE_SUPPORTED_FEATURE = 9
|
|
1373
|
+
HCI_STABLE_MODULATION_INDEX_RECEIVER_LE_SUPPORTED_FEATURE = 10
|
|
1374
|
+
HCI_LE_CODED_PHY_LE_SUPPORTED_FEATURE = 11
|
|
1375
|
+
HCI_LE_EXTENDED_ADVERTISING_LE_SUPPORTED_FEATURE = 12
|
|
1376
|
+
HCI_LE_PERIODIC_ADVERTISING_LE_SUPPORTED_FEATURE = 13
|
|
1377
|
+
HCI_CHANNEL_SELECTION_ALGORITHM_2_LE_SUPPORTED_FEATURE = 14
|
|
1378
|
+
HCI_LE_POWER_CLASS_1_LE_SUPPORTED_FEATURE = 15
|
|
1379
|
+
HCI_MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE_LE_SUPPORTED_FEATURE = 16
|
|
1380
|
+
HCI_CONNECTION_CTE_REQUEST_LE_SUPPORTED_FEATURE = 17
|
|
1381
|
+
HCI_CONNECTION_CTE_RESPONSE_LE_SUPPORTED_FEATURE = 18
|
|
1382
|
+
HCI_CONNECTIONLESS_CTE_TRANSMITTER_LE_SUPPORTED_FEATURE = 19
|
|
1383
|
+
HCI_CONNECTIONLESS_CTR_RECEIVER_LE_SUPPORTED_FEATURE = 20
|
|
1384
|
+
HCI_ANTENNA_SWITCHING_DURING_CTE_TRANSMISSION_LE_SUPPORTED_FEATURE = 21
|
|
1385
|
+
HCI_ANTENNA_SWITCHING_DURING_CTE_RECEPTION_LE_SUPPORTED_FEATURE = 22
|
|
1386
|
+
HCI_RECEIVING_CONSTANT_TONE_EXTENSIONS_LE_SUPPORTED_FEATURE = 23
|
|
1387
|
+
HCI_PERIODIC_ADVERTISING_SYNC_TRANSFER_SENDER_LE_SUPPORTED_FEATURE = 24
|
|
1388
|
+
HCI_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECIPIENT_LE_SUPPORTED_FEATURE = 25
|
|
1389
|
+
HCI_SLEEP_CLOCK_ACCURACY_UPDATES_LE_SUPPORTED_FEATURE = 26
|
|
1390
|
+
HCI_REMOTE_PUBLIC_KEY_VALIDATION_LE_SUPPORTED_FEATURE = 27
|
|
1391
|
+
HCI_CONNECTED_ISOCHRONOUS_STREAM_CENTRAL_LE_SUPPORTED_FEATURE = 28
|
|
1392
|
+
HCI_CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL_LE_SUPPORTED_FEATURE = 29
|
|
1393
|
+
HCI_ISOCHRONOUS_BROADCASTER_LE_SUPPORTED_FEATURE = 30
|
|
1394
|
+
HCI_SYNCHRONIZED_RECEIVER_LE_SUPPORTED_FEATURE = 31
|
|
1395
|
+
HCI_CONNECTED_ISOCHRONOUS_STREAM_LE_SUPPORTED_FEATURE = 32
|
|
1396
|
+
HCI_LE_POWER_CONTROL_REQUEST_LE_SUPPORTED_FEATURE = 33
|
|
1397
|
+
HCI_LE_POWER_CONTROL_REQUEST_DUP_LE_SUPPORTED_FEATURE = 34
|
|
1398
|
+
HCI_LE_PATH_LOSS_MONITORING_LE_SUPPORTED_FEATURE = 35
|
|
1399
|
+
HCI_PERIODIC_ADVERTISING_ADI_SUPPORT_LE_SUPPORTED_FEATURE = 36
|
|
1400
|
+
HCI_CONNECTION_SUBRATING_LE_SUPPORTED_FEATURE = 37
|
|
1401
|
+
HCI_CONNECTION_SUBRATING_HOST_SUPPORT_LE_SUPPORTED_FEATURE = 38
|
|
1402
|
+
HCI_CHANNEL_CLASSIFICATION_LE_SUPPORTED_FEATURE = 39
|
|
1403
|
+
HCI_ADVERTISING_CODING_SELECTION_LE_SUPPORTED_FEATURE = 40
|
|
1404
|
+
HCI_ADVERTISING_CODING_SELECTION_HOST_SUPPORT_LE_SUPPORTED_FEATURE = 41
|
|
1405
|
+
HCI_PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER_LE_SUPPORTED_FEATURE = 43
|
|
1406
|
+
HCI_PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER_LE_SUPPORTED_FEATURE = 44
|
|
1369
1407
|
|
|
1370
1408
|
HCI_LE_SUPPORTED_FEATURES_NAMES = {
|
|
1371
1409
|
flag: feature_name for (feature_name, flag) in globals().items()
|
|
@@ -1382,6 +1420,45 @@ HCI_LE_SUPPORTED_FEATURES_NAMES = {
|
|
|
1382
1420
|
STATUS_SPEC = {'size': 1, 'mapper': lambda x: HCI_Constant.status_name(x)}
|
|
1383
1421
|
|
|
1384
1422
|
|
|
1423
|
+
class CodecID(enum.IntEnum):
|
|
1424
|
+
# fmt: off
|
|
1425
|
+
U_LOG = 0x00
|
|
1426
|
+
A_LOG = 0x01
|
|
1427
|
+
CVSD = 0x02
|
|
1428
|
+
TRANSPARENT = 0x03
|
|
1429
|
+
LINEAR_PCM = 0x04
|
|
1430
|
+
MSBC = 0x05
|
|
1431
|
+
LC3 = 0x06
|
|
1432
|
+
G729A = 0x07
|
|
1433
|
+
VENDOR_SPECIFIC = 0xFF
|
|
1434
|
+
|
|
1435
|
+
|
|
1436
|
+
@dataclasses.dataclass(frozen=True)
|
|
1437
|
+
class CodingFormat:
|
|
1438
|
+
codec_id: CodecID
|
|
1439
|
+
company_id: int = 0
|
|
1440
|
+
vendor_specific_codec_id: int = 0
|
|
1441
|
+
|
|
1442
|
+
@classmethod
|
|
1443
|
+
def parse_from_bytes(cls, data: bytes, offset: int):
|
|
1444
|
+
(codec_id, company_id, vendor_specific_codec_id) = struct.unpack_from(
|
|
1445
|
+
'<BHH', data, offset
|
|
1446
|
+
)
|
|
1447
|
+
return offset + 5, cls(
|
|
1448
|
+
codec_id=CodecID(codec_id),
|
|
1449
|
+
company_id=company_id,
|
|
1450
|
+
vendor_specific_codec_id=vendor_specific_codec_id,
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
def to_bytes(self) -> bytes:
|
|
1454
|
+
return struct.pack(
|
|
1455
|
+
'<BHH', self.codec_id, self.company_id, self.vendor_specific_codec_id
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1458
|
+
def __bytes__(self) -> bytes:
|
|
1459
|
+
return self.to_bytes()
|
|
1460
|
+
|
|
1461
|
+
|
|
1385
1462
|
# -----------------------------------------------------------------------------
|
|
1386
1463
|
class HCI_Constant:
|
|
1387
1464
|
@staticmethod
|
|
@@ -1477,6 +1554,12 @@ class HCI_Object:
|
|
|
1477
1554
|
# The rest of the bytes
|
|
1478
1555
|
field_value = data[offset:]
|
|
1479
1556
|
return (field_value, len(field_value))
|
|
1557
|
+
if field_type == 'v':
|
|
1558
|
+
# Variable-length bytes field, with 1-byte length at the beginning
|
|
1559
|
+
field_length = data[offset]
|
|
1560
|
+
offset += 1
|
|
1561
|
+
field_value = data[offset : offset + field_length]
|
|
1562
|
+
return (field_value, field_length + 1)
|
|
1480
1563
|
if field_type == 1:
|
|
1481
1564
|
# 8-bit unsigned
|
|
1482
1565
|
return (data[offset], 1)
|
|
@@ -1581,6 +1664,11 @@ class HCI_Object:
|
|
|
1581
1664
|
raise ValueError('value too large for *-typed field')
|
|
1582
1665
|
else:
|
|
1583
1666
|
field_bytes = bytes(field_value)
|
|
1667
|
+
elif field_type == 'v':
|
|
1668
|
+
# Variable-length bytes field, with 1-byte length at the beginning
|
|
1669
|
+
field_bytes = bytes(field_value)
|
|
1670
|
+
field_length = len(field_bytes)
|
|
1671
|
+
field_bytes = bytes([field_length]) + field_bytes
|
|
1584
1672
|
elif isinstance(field_value, (bytes, bytearray)) or hasattr(
|
|
1585
1673
|
field_value, 'to_bytes'
|
|
1586
1674
|
):
|
|
@@ -1795,6 +1883,43 @@ class Address:
|
|
|
1795
1883
|
address_type = data[offset - 1]
|
|
1796
1884
|
return Address.parse_address_with_type(data, offset, address_type)
|
|
1797
1885
|
|
|
1886
|
+
@classmethod
|
|
1887
|
+
def generate_static_address(cls) -> Address:
|
|
1888
|
+
'''Generates Random Static Address, with the 2 most significant bits of 0b11.
|
|
1889
|
+
|
|
1890
|
+
See Bluetooth spec, Vol 6, Part B - Table 1.2.
|
|
1891
|
+
'''
|
|
1892
|
+
address_bytes = secrets.token_bytes(6)
|
|
1893
|
+
address_bytes = address_bytes[:5] + bytes([address_bytes[5] | 0b11000000])
|
|
1894
|
+
return Address(
|
|
1895
|
+
address=address_bytes, address_type=Address.RANDOM_DEVICE_ADDRESS
|
|
1896
|
+
)
|
|
1897
|
+
|
|
1898
|
+
@classmethod
|
|
1899
|
+
def generate_private_address(cls, irk: bytes = b'') -> Address:
|
|
1900
|
+
'''Generates Random Private MAC Address.
|
|
1901
|
+
|
|
1902
|
+
If IRK is present, a Resolvable Private Address, with the 2 most significant
|
|
1903
|
+
bits of 0b01 will be generated. Otherwise, a Non-resolvable Private Address,
|
|
1904
|
+
with the 2 most significant bits of 0b00 will be generated.
|
|
1905
|
+
|
|
1906
|
+
See Bluetooth spec, Vol 6, Part B - Table 1.2.
|
|
1907
|
+
|
|
1908
|
+
Args:
|
|
1909
|
+
irk: Local Identity Resolving Key(IRK), in little-endian. If not set, a
|
|
1910
|
+
non-resolvable address will be generated.
|
|
1911
|
+
'''
|
|
1912
|
+
if irk:
|
|
1913
|
+
prand = crypto.generate_prand()
|
|
1914
|
+
address_bytes = crypto.ah(irk, prand) + prand
|
|
1915
|
+
else:
|
|
1916
|
+
address_bytes = secrets.token_bytes(6)
|
|
1917
|
+
address_bytes = address_bytes[:5] + bytes([address_bytes[5] & 0b00111111])
|
|
1918
|
+
|
|
1919
|
+
return Address(
|
|
1920
|
+
address=address_bytes, address_type=Address.RANDOM_DEVICE_ADDRESS
|
|
1921
|
+
)
|
|
1922
|
+
|
|
1798
1923
|
def __init__(
|
|
1799
1924
|
self, address: Union[bytes, str], address_type: int = RANDOM_DEVICE_ADDRESS
|
|
1800
1925
|
):
|
|
@@ -1888,26 +2013,28 @@ Address.NIL = Address(b"\xff\xff\xff\xff\xff\xff", Address.PUBLIC_DEVICE_ADDRESS
|
|
|
1888
2013
|
Address.ANY = Address(b"\x00\x00\x00\x00\x00\x00", Address.PUBLIC_DEVICE_ADDRESS)
|
|
1889
2014
|
Address.ANY_RANDOM = Address(b"\x00\x00\x00\x00\x00\x00", Address.RANDOM_DEVICE_ADDRESS)
|
|
1890
2015
|
|
|
2016
|
+
|
|
1891
2017
|
# -----------------------------------------------------------------------------
|
|
1892
|
-
class OwnAddressType:
|
|
2018
|
+
class OwnAddressType(enum.IntEnum):
|
|
1893
2019
|
PUBLIC = 0
|
|
1894
2020
|
RANDOM = 1
|
|
1895
2021
|
RESOLVABLE_OR_PUBLIC = 2
|
|
1896
2022
|
RESOLVABLE_OR_RANDOM = 3
|
|
1897
2023
|
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
RESOLVABLE_OR_PUBLIC: 'RESOLVABLE_OR_PUBLIC',
|
|
1902
|
-
RESOLVABLE_OR_RANDOM: 'RESOLVABLE_OR_RANDOM',
|
|
1903
|
-
}
|
|
2024
|
+
@classmethod
|
|
2025
|
+
def type_spec(cls):
|
|
2026
|
+
return {'size': 1, 'mapper': lambda x: OwnAddressType(x).name}
|
|
1904
2027
|
|
|
1905
|
-
@staticmethod
|
|
1906
|
-
def type_name(type_id):
|
|
1907
|
-
return name_or_number(OwnAddressType.TYPE_NAMES, type_id)
|
|
1908
2028
|
|
|
1909
|
-
|
|
1910
|
-
|
|
2029
|
+
# -----------------------------------------------------------------------------
|
|
2030
|
+
class LoopbackMode(enum.IntEnum):
|
|
2031
|
+
DISABLED = 0
|
|
2032
|
+
LOCAL = 1
|
|
2033
|
+
REMOTE = 2
|
|
2034
|
+
|
|
2035
|
+
@classmethod
|
|
2036
|
+
def type_spec(cls):
|
|
2037
|
+
return {'size': 1, 'mapper': lambda x: LoopbackMode(x).name}
|
|
1911
2038
|
|
|
1912
2039
|
|
|
1913
2040
|
# -----------------------------------------------------------------------------
|
|
@@ -1934,6 +2061,9 @@ class HCI_Packet:
|
|
|
1934
2061
|
if packet_type == HCI_EVENT_PACKET:
|
|
1935
2062
|
return HCI_Event.from_bytes(packet)
|
|
1936
2063
|
|
|
2064
|
+
if packet_type == HCI_ISO_DATA_PACKET:
|
|
2065
|
+
return HCI_IsoDataPacket.from_bytes(packet)
|
|
2066
|
+
|
|
1937
2067
|
return HCI_CustomPacket(packet)
|
|
1938
2068
|
|
|
1939
2069
|
def __init__(self, name):
|
|
@@ -1966,6 +2096,7 @@ class HCI_Command(HCI_Packet):
|
|
|
1966
2096
|
hci_packet_type = HCI_COMMAND_PACKET
|
|
1967
2097
|
command_names: Dict[int, str] = {}
|
|
1968
2098
|
command_classes: Dict[int, Type[HCI_Command]] = {}
|
|
2099
|
+
op_code: int
|
|
1969
2100
|
|
|
1970
2101
|
@staticmethod
|
|
1971
2102
|
def command(fields=(), return_parameters_fields=()):
|
|
@@ -2051,7 +2182,11 @@ class HCI_Command(HCI_Packet):
|
|
|
2051
2182
|
return_parameters.fields = cls.return_parameters_fields
|
|
2052
2183
|
return return_parameters
|
|
2053
2184
|
|
|
2054
|
-
def __init__(self, op_code, parameters=None, **kwargs):
|
|
2185
|
+
def __init__(self, op_code=-1, parameters=None, **kwargs):
|
|
2186
|
+
# Since the legacy implementation relies on an __init__ injector, typing always
|
|
2187
|
+
# complains that positional argument op_code is not passed, so here sets a
|
|
2188
|
+
# default value to allow building derived HCI_Command without op_code.
|
|
2189
|
+
assert op_code != -1
|
|
2055
2190
|
super().__init__(HCI_Command.command_name(op_code))
|
|
2056
2191
|
if (fields := getattr(self, 'fields', None)) and kwargs:
|
|
2057
2192
|
HCI_Object.init_from_fields(self, fields, kwargs)
|
|
@@ -2445,14 +2580,14 @@ class HCI_IO_Capability_Request_Negative_Reply_Command(HCI_Command):
|
|
|
2445
2580
|
('connection_handle', 2),
|
|
2446
2581
|
('transmit_bandwidth', 4),
|
|
2447
2582
|
('receive_bandwidth', 4),
|
|
2448
|
-
('transmit_coding_format',
|
|
2449
|
-
('receive_coding_format',
|
|
2583
|
+
('transmit_coding_format', CodingFormat.parse_from_bytes),
|
|
2584
|
+
('receive_coding_format', CodingFormat.parse_from_bytes),
|
|
2450
2585
|
('transmit_codec_frame_size', 2),
|
|
2451
2586
|
('receive_codec_frame_size', 2),
|
|
2452
2587
|
('input_bandwidth', 4),
|
|
2453
2588
|
('output_bandwidth', 4),
|
|
2454
|
-
('input_coding_format',
|
|
2455
|
-
('output_coding_format',
|
|
2589
|
+
('input_coding_format', CodingFormat.parse_from_bytes),
|
|
2590
|
+
('output_coding_format', CodingFormat.parse_from_bytes),
|
|
2456
2591
|
('input_coded_data_size', 2),
|
|
2457
2592
|
('output_coded_data_size', 2),
|
|
2458
2593
|
('input_pcm_data_format', 1),
|
|
@@ -2473,22 +2608,6 @@ class HCI_Enhanced_Setup_Synchronous_Connection_Command(HCI_Command):
|
|
|
2473
2608
|
See Bluetooth spec @ 7.1.45 Enhanced Setup Synchronous Connection Command
|
|
2474
2609
|
'''
|
|
2475
2610
|
|
|
2476
|
-
class CodingFormat(enum.IntEnum):
|
|
2477
|
-
U_LOG = 0x00
|
|
2478
|
-
A_LOG = 0x01
|
|
2479
|
-
CVSD = 0x02
|
|
2480
|
-
TRANSPARENT = 0x03
|
|
2481
|
-
PCM = 0x04
|
|
2482
|
-
MSBC = 0x05
|
|
2483
|
-
LC3 = 0x06
|
|
2484
|
-
G729A = 0x07
|
|
2485
|
-
|
|
2486
|
-
def to_bytes(self):
|
|
2487
|
-
return self.value.to_bytes(5, 'little')
|
|
2488
|
-
|
|
2489
|
-
def __bytes__(self):
|
|
2490
|
-
return self.to_bytes()
|
|
2491
|
-
|
|
2492
2611
|
class PcmDataFormat(enum.IntEnum):
|
|
2493
2612
|
NA = 0x00
|
|
2494
2613
|
ONES_COMPLEMENT = 0x01
|
|
@@ -2525,14 +2644,14 @@ class HCI_Enhanced_Setup_Synchronous_Connection_Command(HCI_Command):
|
|
|
2525
2644
|
('bd_addr', Address.parse_address),
|
|
2526
2645
|
('transmit_bandwidth', 4),
|
|
2527
2646
|
('receive_bandwidth', 4),
|
|
2528
|
-
('transmit_coding_format',
|
|
2529
|
-
('receive_coding_format',
|
|
2647
|
+
('transmit_coding_format', CodingFormat.parse_from_bytes),
|
|
2648
|
+
('receive_coding_format', CodingFormat.parse_from_bytes),
|
|
2530
2649
|
('transmit_codec_frame_size', 2),
|
|
2531
2650
|
('receive_codec_frame_size', 2),
|
|
2532
2651
|
('input_bandwidth', 4),
|
|
2533
2652
|
('output_bandwidth', 4),
|
|
2534
|
-
('input_coding_format',
|
|
2535
|
-
('output_coding_format',
|
|
2653
|
+
('input_coding_format', CodingFormat.parse_from_bytes),
|
|
2654
|
+
('output_coding_format', CodingFormat.parse_from_bytes),
|
|
2536
2655
|
('input_coded_data_size', 2),
|
|
2537
2656
|
('output_coded_data_size', 2),
|
|
2538
2657
|
('input_pcm_data_format', 1),
|
|
@@ -3244,6 +3363,27 @@ class HCI_Read_Encryption_Key_Size_Command(HCI_Command):
|
|
|
3244
3363
|
'''
|
|
3245
3364
|
|
|
3246
3365
|
|
|
3366
|
+
# -----------------------------------------------------------------------------
|
|
3367
|
+
@HCI_Command.command(
|
|
3368
|
+
return_parameters_fields=[
|
|
3369
|
+
('status', STATUS_SPEC),
|
|
3370
|
+
('loopback_mode', LoopbackMode.type_spec()),
|
|
3371
|
+
],
|
|
3372
|
+
)
|
|
3373
|
+
class HCI_Read_Loopback_Mode_Command(HCI_Command):
|
|
3374
|
+
'''
|
|
3375
|
+
See Bluetooth spec @ 7.6.1 Read Loopback Mode Command
|
|
3376
|
+
'''
|
|
3377
|
+
|
|
3378
|
+
|
|
3379
|
+
# -----------------------------------------------------------------------------
|
|
3380
|
+
@HCI_Command.command([('loopback_mode', 1)])
|
|
3381
|
+
class HCI_Write_Loopback_Mode_Command(HCI_Command):
|
|
3382
|
+
'''
|
|
3383
|
+
See Bluetooth spec @ 7.6.2 Write Loopback Mode Command
|
|
3384
|
+
'''
|
|
3385
|
+
|
|
3386
|
+
|
|
3247
3387
|
# -----------------------------------------------------------------------------
|
|
3248
3388
|
@HCI_Command.command([('le_event_mask', 8)])
|
|
3249
3389
|
class HCI_LE_Set_Event_Mask_Command(HCI_Command):
|
|
@@ -3308,7 +3448,7 @@ class HCI_LE_Set_Random_Address_Command(HCI_Command):
|
|
|
3308
3448
|
),
|
|
3309
3449
|
},
|
|
3310
3450
|
),
|
|
3311
|
-
('own_address_type', OwnAddressType.
|
|
3451
|
+
('own_address_type', OwnAddressType.type_spec()),
|
|
3312
3452
|
('peer_address_type', Address.ADDRESS_TYPE_SPEC),
|
|
3313
3453
|
('peer_address', Address.parse_address_preceded_by_type),
|
|
3314
3454
|
('advertising_channel_map', 1),
|
|
@@ -3401,7 +3541,7 @@ class HCI_LE_Set_Advertising_Enable_Command(HCI_Command):
|
|
|
3401
3541
|
('le_scan_type', 1),
|
|
3402
3542
|
('le_scan_interval', 2),
|
|
3403
3543
|
('le_scan_window', 2),
|
|
3404
|
-
('own_address_type', OwnAddressType.
|
|
3544
|
+
('own_address_type', OwnAddressType.type_spec()),
|
|
3405
3545
|
('scanning_filter_policy', 1),
|
|
3406
3546
|
]
|
|
3407
3547
|
)
|
|
@@ -3440,7 +3580,7 @@ class HCI_LE_Set_Scan_Enable_Command(HCI_Command):
|
|
|
3440
3580
|
('initiator_filter_policy', 1),
|
|
3441
3581
|
('peer_address_type', Address.ADDRESS_TYPE_SPEC),
|
|
3442
3582
|
('peer_address', Address.parse_address_preceded_by_type),
|
|
3443
|
-
('own_address_type', OwnAddressType.
|
|
3583
|
+
('own_address_type', OwnAddressType.type_spec()),
|
|
3444
3584
|
('connection_interval_min', 2),
|
|
3445
3585
|
('connection_interval_max', 2),
|
|
3446
3586
|
('max_latency', 2),
|
|
@@ -3847,7 +3987,7 @@ class HCI_LE_Set_Advertising_Set_Random_Address_Command(HCI_Command):
|
|
|
3847
3987
|
),
|
|
3848
3988
|
},
|
|
3849
3989
|
),
|
|
3850
|
-
('own_address_type', OwnAddressType.
|
|
3990
|
+
('own_address_type', OwnAddressType.type_spec()),
|
|
3851
3991
|
('peer_address_type', Address.ADDRESS_TYPE_SPEC),
|
|
3852
3992
|
('peer_address', Address.parse_address_preceded_by_type),
|
|
3853
3993
|
('advertising_filter_policy', 1),
|
|
@@ -4243,7 +4383,7 @@ class HCI_LE_Extended_Create_Connection_Command(HCI_Command):
|
|
|
4243
4383
|
('initiator_filter_policy:', self.initiator_filter_policy),
|
|
4244
4384
|
(
|
|
4245
4385
|
'own_address_type: ',
|
|
4246
|
-
OwnAddressType
|
|
4386
|
+
OwnAddressType(self.own_address_type).name,
|
|
4247
4387
|
),
|
|
4248
4388
|
(
|
|
4249
4389
|
'peer_address_type: ',
|
|
@@ -4451,7 +4591,10 @@ class HCI_LE_Accept_CIS_Request_Command(HCI_Command):
|
|
|
4451
4591
|
|
|
4452
4592
|
# -----------------------------------------------------------------------------
|
|
4453
4593
|
@HCI_Command.command(
|
|
4454
|
-
fields=[
|
|
4594
|
+
fields=[
|
|
4595
|
+
('connection_handle', 2),
|
|
4596
|
+
('reason', {'size': 1, 'mapper': HCI_Constant.error_name}),
|
|
4597
|
+
],
|
|
4455
4598
|
)
|
|
4456
4599
|
class HCI_LE_Reject_CIS_Request_Command(HCI_Command):
|
|
4457
4600
|
'''
|
|
@@ -4459,6 +4602,7 @@ class HCI_LE_Reject_CIS_Request_Command(HCI_Command):
|
|
|
4459
4602
|
'''
|
|
4460
4603
|
|
|
4461
4604
|
connection_handle: int
|
|
4605
|
+
reason: int
|
|
4462
4606
|
|
|
4463
4607
|
|
|
4464
4608
|
# -----------------------------------------------------------------------------
|
|
@@ -4467,9 +4611,9 @@ class HCI_LE_Reject_CIS_Request_Command(HCI_Command):
|
|
|
4467
4611
|
('connection_handle', 2),
|
|
4468
4612
|
('data_path_direction', 1),
|
|
4469
4613
|
('data_path_id', 1),
|
|
4470
|
-
('codec_id',
|
|
4614
|
+
('codec_id', CodingFormat.parse_from_bytes),
|
|
4471
4615
|
('controller_delay', 3),
|
|
4472
|
-
('codec_configuration', '
|
|
4616
|
+
('codec_configuration', 'v'),
|
|
4473
4617
|
],
|
|
4474
4618
|
return_parameters_fields=[
|
|
4475
4619
|
('status', STATUS_SPEC),
|
|
@@ -4481,12 +4625,16 @@ class HCI_LE_Setup_ISO_Data_Path_Command(HCI_Command):
|
|
|
4481
4625
|
See Bluetooth spec @ 7.8.109 LE Setup ISO Data Path command
|
|
4482
4626
|
'''
|
|
4483
4627
|
|
|
4628
|
+
class Direction(enum.IntEnum):
|
|
4629
|
+
HOST_TO_CONTROLLER = 0x00
|
|
4630
|
+
CONTROLLER_TO_HOST = 0x01
|
|
4631
|
+
|
|
4484
4632
|
connection_handle: int
|
|
4485
4633
|
data_path_direction: int
|
|
4486
4634
|
data_path_id: int
|
|
4487
|
-
codec_id:
|
|
4635
|
+
codec_id: CodingFormat
|
|
4488
4636
|
controller_delay: int
|
|
4489
|
-
codec_configuration:
|
|
4637
|
+
codec_configuration: bytes
|
|
4490
4638
|
|
|
4491
4639
|
|
|
4492
4640
|
# -----------------------------------------------------------------------------
|
|
@@ -4617,7 +4765,11 @@ class HCI_Event(HCI_Packet):
|
|
|
4617
4765
|
HCI_Object.init_from_bytes(self, parameters, 0, fields)
|
|
4618
4766
|
return self
|
|
4619
4767
|
|
|
4620
|
-
def __init__(self, event_code, parameters=None, **kwargs):
|
|
4768
|
+
def __init__(self, event_code=-1, parameters=None, **kwargs):
|
|
4769
|
+
# Since the legacy implementation relies on an __init__ injector, typing always
|
|
4770
|
+
# complains that positional argument event_code is not passed, so here sets a
|
|
4771
|
+
# default value to allow building derived HCI_Event without event_code.
|
|
4772
|
+
assert event_code != -1
|
|
4621
4773
|
super().__init__(HCI_Event.event_name(event_code))
|
|
4622
4774
|
if (fields := getattr(self, 'fields', None)) and kwargs:
|
|
4623
4775
|
HCI_Object.init_from_fields(self, fields, kwargs)
|
|
@@ -5120,6 +5272,21 @@ HCI_LE_Meta_Event.subevent_classes[
|
|
|
5120
5272
|
] = HCI_LE_Extended_Advertising_Report_Event
|
|
5121
5273
|
|
|
5122
5274
|
|
|
5275
|
+
# -----------------------------------------------------------------------------
|
|
5276
|
+
@HCI_LE_Meta_Event.event(
|
|
5277
|
+
[
|
|
5278
|
+
('status', 1),
|
|
5279
|
+
('advertising_handle', 1),
|
|
5280
|
+
('connection_handle', 2),
|
|
5281
|
+
('number_completed_extended_advertising_events', 1),
|
|
5282
|
+
]
|
|
5283
|
+
)
|
|
5284
|
+
class HCI_LE_Advertising_Set_Terminated_Event(HCI_LE_Meta_Event):
|
|
5285
|
+
'''
|
|
5286
|
+
See Bluetooth spec @ 7.7.65.18 LE Advertising Set Terminated Event
|
|
5287
|
+
'''
|
|
5288
|
+
|
|
5289
|
+
|
|
5123
5290
|
# -----------------------------------------------------------------------------
|
|
5124
5291
|
@HCI_LE_Meta_Event.event([('connection_handle', 2), ('channel_selection_algorithm', 1)])
|
|
5125
5292
|
class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event):
|
|
@@ -6053,7 +6220,7 @@ class HCI_IsoDataPacket(HCI_Packet):
|
|
|
6053
6220
|
if ts_flag:
|
|
6054
6221
|
if not should_include_sdu_info:
|
|
6055
6222
|
logger.warn(f'Timestamp included when pb_flag={bin(pb_flag)}')
|
|
6056
|
-
time_stamp, _ = struct.unpack_from('<I', packet, pos)
|
|
6223
|
+
time_stamp, *_ = struct.unpack_from('<I', packet, pos)
|
|
6057
6224
|
pos += 4
|
|
6058
6225
|
|
|
6059
6226
|
if should_include_sdu_info:
|
|
@@ -6120,7 +6287,7 @@ class HCI_IsoDataPacket(HCI_Packet):
|
|
|
6120
6287
|
self.packet_sequence_number,
|
|
6121
6288
|
self.iso_sdu_length | self.packet_status_flag << 14,
|
|
6122
6289
|
]
|
|
6123
|
-
return struct.pack(fmt, args) + self.iso_sdu_fragment
|
|
6290
|
+
return struct.pack(fmt, *args) + self.iso_sdu_fragment
|
|
6124
6291
|
|
|
6125
6292
|
def __str__(self) -> str:
|
|
6126
6293
|
return (
|
bumble/helpers.py
CHANGED
|
@@ -37,6 +37,7 @@ from bumble.l2cap import (
|
|
|
37
37
|
L2CAP_Connection_Response,
|
|
38
38
|
)
|
|
39
39
|
from bumble.hci import (
|
|
40
|
+
Address,
|
|
40
41
|
HCI_EVENT_PACKET,
|
|
41
42
|
HCI_ACL_DATA_PACKET,
|
|
42
43
|
HCI_DISCONNECTION_COMPLETE_EVENT,
|
|
@@ -48,6 +49,7 @@ from bumble.hci import (
|
|
|
48
49
|
)
|
|
49
50
|
from bumble.rfcomm import RFCOMM_Frame, RFCOMM_PSM
|
|
50
51
|
from bumble.sdp import SDP_PDU, SDP_PSM
|
|
52
|
+
from bumble import crypto
|
|
51
53
|
|
|
52
54
|
# -----------------------------------------------------------------------------
|
|
53
55
|
# Logging
|
|
@@ -232,3 +234,15 @@ class PacketTracer:
|
|
|
232
234
|
)
|
|
233
235
|
self.host_to_controller_analyzer.peer = self.controller_to_host_analyzer
|
|
234
236
|
self.controller_to_host_analyzer.peer = self.host_to_controller_analyzer
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def generate_irk() -> bytes:
|
|
240
|
+
return crypto.r()
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def verify_rpa_with_irk(rpa: Address, irk: bytes) -> bool:
|
|
244
|
+
rpa_bytes = bytes(rpa)
|
|
245
|
+
prand_given = rpa_bytes[3:]
|
|
246
|
+
hash_given = rpa_bytes[:3]
|
|
247
|
+
hash_local = crypto.ah(irk, prand_given)
|
|
248
|
+
return hash_local[:3] == hash_given
|