bumble 0.0.211__py3-none-any.whl → 0.0.213__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/a2dp.py +6 -0
- bumble/apps/README.md +0 -3
- bumble/apps/auracast.py +11 -9
- bumble/apps/bench.py +482 -31
- bumble/apps/console.py +5 -5
- bumble/apps/controller_info.py +47 -10
- bumble/apps/controller_loopback.py +7 -3
- bumble/apps/controllers.py +2 -2
- bumble/apps/device_info.py +2 -2
- bumble/apps/gatt_dump.py +2 -2
- bumble/apps/gg_bridge.py +2 -2
- bumble/apps/hci_bridge.py +2 -2
- bumble/apps/l2cap_bridge.py +2 -2
- bumble/apps/lea_unicast/app.py +6 -1
- bumble/apps/pair.py +204 -43
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/scan.py +2 -2
- bumble/apps/show.py +4 -2
- bumble/apps/speaker/speaker.html +1 -0
- bumble/apps/speaker/speaker.js +113 -62
- bumble/apps/speaker/speaker.py +126 -18
- bumble/at.py +4 -4
- bumble/att.py +15 -18
- bumble/avc.py +7 -7
- bumble/avctp.py +5 -5
- bumble/avdtp.py +138 -88
- bumble/avrcp.py +52 -58
- bumble/colors.py +2 -2
- bumble/controller.py +84 -23
- bumble/core.py +13 -7
- bumble/{crypto.py → crypto/__init__.py} +11 -95
- bumble/crypto/builtin.py +652 -0
- bumble/crypto/cryptography.py +84 -0
- bumble/device.py +688 -345
- bumble/drivers/__init__.py +2 -2
- bumble/drivers/common.py +0 -2
- bumble/drivers/intel.py +40 -40
- bumble/drivers/rtk.py +28 -35
- bumble/gatt.py +7 -9
- bumble/gatt_adapters.py +4 -5
- bumble/gatt_client.py +31 -34
- bumble/gatt_server.py +15 -17
- bumble/hci.py +2635 -2878
- bumble/helpers.py +4 -5
- bumble/hfp.py +76 -57
- bumble/hid.py +24 -12
- bumble/host.py +117 -34
- bumble/keys.py +68 -52
- bumble/l2cap.py +329 -403
- bumble/link.py +6 -270
- bumble/pairing.py +23 -20
- bumble/pandora/__init__.py +1 -1
- bumble/pandora/config.py +2 -2
- bumble/pandora/device.py +6 -6
- bumble/pandora/host.py +38 -39
- bumble/pandora/l2cap.py +4 -4
- bumble/pandora/security.py +73 -57
- bumble/pandora/utils.py +3 -3
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +143 -136
- bumble/profiles/asha.py +13 -8
- bumble/profiles/bap.py +3 -4
- bumble/profiles/csip.py +3 -5
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +2 -2
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +42 -58
- bumble/profiles/le_audio.py +4 -4
- bumble/profiles/mcp.py +16 -13
- bumble/profiles/vcs.py +8 -10
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +27 -18
- bumble/rtp.py +1 -2
- bumble/sdp.py +2 -2
- bumble/smp.py +71 -69
- bumble/tools/rtk_util.py +2 -2
- bumble/transport/__init__.py +2 -16
- bumble/transport/android_netsim.py +5 -5
- bumble/transport/common.py +4 -4
- bumble/transport/pyusb.py +2 -2
- bumble/utils.py +2 -5
- bumble/vendor/android/hci.py +118 -200
- bumble/vendor/zephyr/hci.py +32 -27
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/METADATA +5 -5
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/RECORD +92 -93
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/entry_points.txt +0 -1
- bumble/apps/link_relay/__init__.py +0 -0
- bumble/apps/link_relay/link_relay.py +0 -289
- bumble/apps/link_relay/logging.yml +0 -21
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/top_level.txt +0 -0
bumble/apps/bench.py
CHANGED
|
@@ -23,6 +23,7 @@ import os
|
|
|
23
23
|
import statistics
|
|
24
24
|
import struct
|
|
25
25
|
import time
|
|
26
|
+
from typing import Optional
|
|
26
27
|
|
|
27
28
|
import click
|
|
28
29
|
|
|
@@ -35,7 +36,15 @@ from bumble.core import (
|
|
|
35
36
|
CommandTimeoutError,
|
|
36
37
|
)
|
|
37
38
|
from bumble.colors import color
|
|
38
|
-
from bumble.
|
|
39
|
+
from bumble.core import ConnectionPHY
|
|
40
|
+
from bumble.device import (
|
|
41
|
+
CigParameters,
|
|
42
|
+
CisLink,
|
|
43
|
+
Connection,
|
|
44
|
+
ConnectionParametersPreferences,
|
|
45
|
+
Device,
|
|
46
|
+
Peer,
|
|
47
|
+
)
|
|
39
48
|
from bumble.gatt import Characteristic, CharacteristicValue, Service
|
|
40
49
|
from bumble.hci import (
|
|
41
50
|
HCI_LE_1M_PHY,
|
|
@@ -45,6 +54,7 @@ from bumble.hci import (
|
|
|
45
54
|
HCI_Constant,
|
|
46
55
|
HCI_Error,
|
|
47
56
|
HCI_StatusError,
|
|
57
|
+
HCI_IsoDataPacket,
|
|
48
58
|
)
|
|
49
59
|
from bumble.sdp import (
|
|
50
60
|
SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
|
|
@@ -55,7 +65,7 @@ from bumble.sdp import (
|
|
|
55
65
|
DataElement,
|
|
56
66
|
ServiceAttribute,
|
|
57
67
|
)
|
|
58
|
-
from bumble.transport import
|
|
68
|
+
from bumble.transport import open_transport
|
|
59
69
|
import bumble.rfcomm
|
|
60
70
|
import bumble.core
|
|
61
71
|
from bumble.utils import AsyncRunner
|
|
@@ -75,17 +85,28 @@ DEFAULT_CENTRAL_ADDRESS = 'F0:F0:F0:F0:F0:F0'
|
|
|
75
85
|
DEFAULT_CENTRAL_NAME = 'Speed Central'
|
|
76
86
|
DEFAULT_PERIPHERAL_ADDRESS = 'F1:F1:F1:F1:F1:F1'
|
|
77
87
|
DEFAULT_PERIPHERAL_NAME = 'Speed Peripheral'
|
|
88
|
+
DEFAULT_ADVERTISING_INTERVAL = 100
|
|
78
89
|
|
|
79
90
|
SPEED_SERVICE_UUID = '50DB505C-8AC4-4738-8448-3B1D9CC09CC5'
|
|
80
91
|
SPEED_TX_UUID = 'E789C754-41A1-45F4-A948-A0A1A90DBA53'
|
|
81
92
|
SPEED_RX_UUID = '016A2CC7-E14B-4819-935F-1F56EAE4098D'
|
|
82
93
|
|
|
83
94
|
DEFAULT_RFCOMM_UUID = 'E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'
|
|
95
|
+
|
|
84
96
|
DEFAULT_L2CAP_PSM = 128
|
|
85
97
|
DEFAULT_L2CAP_MAX_CREDITS = 128
|
|
86
98
|
DEFAULT_L2CAP_MTU = 1024
|
|
87
99
|
DEFAULT_L2CAP_MPS = 1024
|
|
88
100
|
|
|
101
|
+
DEFAULT_ISO_MAX_SDU_C_TO_P = 251
|
|
102
|
+
DEFAULT_ISO_MAX_SDU_P_TO_C = 251
|
|
103
|
+
DEFAULT_ISO_SDU_INTERVAL_C_TO_P = 10000
|
|
104
|
+
DEFAULT_ISO_SDU_INTERVAL_P_TO_C = 10000
|
|
105
|
+
DEFAULT_ISO_MAX_TRANSPORT_LATENCY_C_TO_P = 35
|
|
106
|
+
DEFAULT_ISO_MAX_TRANSPORT_LATENCY_P_TO_C = 35
|
|
107
|
+
DEFAULT_ISO_RTN_C_TO_P = 3
|
|
108
|
+
DEFAULT_ISO_RTN_P_TO_C = 3
|
|
109
|
+
|
|
89
110
|
DEFAULT_LINGER_TIME = 1.0
|
|
90
111
|
DEFAULT_POST_CONNECTION_WAIT_TIME = 1.0
|
|
91
112
|
|
|
@@ -102,14 +123,14 @@ def le_phy_name(phy_id):
|
|
|
102
123
|
)
|
|
103
124
|
|
|
104
125
|
|
|
105
|
-
def print_connection_phy(phy):
|
|
126
|
+
def print_connection_phy(phy: ConnectionPHY) -> None:
|
|
106
127
|
logging.info(
|
|
107
128
|
color('@@@ PHY: ', 'yellow') + f'TX:{le_phy_name(phy.tx_phy)}/'
|
|
108
129
|
f'RX:{le_phy_name(phy.rx_phy)}'
|
|
109
130
|
)
|
|
110
131
|
|
|
111
132
|
|
|
112
|
-
def print_connection(connection):
|
|
133
|
+
def print_connection(connection: Connection) -> None:
|
|
113
134
|
params = []
|
|
114
135
|
if connection.transport == PhysicalTransport.LE:
|
|
115
136
|
params.append(
|
|
@@ -121,9 +142,9 @@ def print_connection(connection):
|
|
|
121
142
|
|
|
122
143
|
params.append(
|
|
123
144
|
'Parameters='
|
|
124
|
-
f'{connection.parameters.connection_interval
|
|
145
|
+
f'{connection.parameters.connection_interval:.2f}/'
|
|
125
146
|
f'{connection.parameters.peripheral_latency}/'
|
|
126
|
-
f'{connection.parameters.supervision_timeout
|
|
147
|
+
f'{connection.parameters.supervision_timeout:.2f} '
|
|
127
148
|
)
|
|
128
149
|
|
|
129
150
|
params.append(f'MTU={connection.att_mtu}')
|
|
@@ -134,6 +155,34 @@ def print_connection(connection):
|
|
|
134
155
|
logging.info(color('@@@ Connection: ', 'yellow') + ' '.join(params))
|
|
135
156
|
|
|
136
157
|
|
|
158
|
+
def print_cis_link(cis_link: CisLink) -> None:
|
|
159
|
+
logging.info(color("@@@ CIS established", "green"))
|
|
160
|
+
logging.info(color('@@@ ISO interval: ', 'green') + f"{cis_link.iso_interval}ms")
|
|
161
|
+
logging.info(color('@@@ NSE: ', 'green') + f"{cis_link.nse}")
|
|
162
|
+
logging.info(color('@@@ Central->Peripheral:', 'green'))
|
|
163
|
+
if cis_link.phy_c_to_p is not None:
|
|
164
|
+
logging.info(
|
|
165
|
+
color('@@@ PHY: ', 'green') + f"{cis_link.phy_c_to_p.name}"
|
|
166
|
+
)
|
|
167
|
+
logging.info(
|
|
168
|
+
color('@@@ Latency: ', 'green') + f"{cis_link.transport_latency_c_to_p}µs"
|
|
169
|
+
)
|
|
170
|
+
logging.info(color('@@@ BN: ', 'green') + f"{cis_link.bn_c_to_p}")
|
|
171
|
+
logging.info(color('@@@ FT: ', 'green') + f"{cis_link.ft_c_to_p}")
|
|
172
|
+
logging.info(color('@@@ Max PDU: ', 'green') + f"{cis_link.max_pdu_c_to_p}")
|
|
173
|
+
logging.info(color('@@@ Peripheral->Central:', 'green'))
|
|
174
|
+
if cis_link.phy_p_to_c is not None:
|
|
175
|
+
logging.info(
|
|
176
|
+
color('@@@ PHY: ', 'green') + f"{cis_link.phy_p_to_c.name}"
|
|
177
|
+
)
|
|
178
|
+
logging.info(
|
|
179
|
+
color('@@@ Latency: ', 'green') + f"{cis_link.transport_latency_p_to_c}µs"
|
|
180
|
+
)
|
|
181
|
+
logging.info(color('@@@ BN: ', 'green') + f"{cis_link.bn_p_to_c}")
|
|
182
|
+
logging.info(color('@@@ FT: ', 'green') + f"{cis_link.ft_p_to_c}")
|
|
183
|
+
logging.info(color('@@@ Max PDU: ', 'green') + f"{cis_link.max_pdu_p_to_c}")
|
|
184
|
+
|
|
185
|
+
|
|
137
186
|
def make_sdp_records(channel):
|
|
138
187
|
return {
|
|
139
188
|
0x00010001: [
|
|
@@ -197,6 +246,51 @@ async def switch_roles(connection, role):
|
|
|
197
246
|
logging.info(f'{color("### Role switch failed:", "red")} {error}')
|
|
198
247
|
|
|
199
248
|
|
|
249
|
+
async def pre_power_on(device: Device, classic: bool) -> None:
|
|
250
|
+
device.classic_enabled = classic
|
|
251
|
+
|
|
252
|
+
# Set up a pairing config factory with minimal requirements.
|
|
253
|
+
device.config.keystore = "JsonKeyStore"
|
|
254
|
+
device.pairing_config_factory = lambda _: PairingConfig(
|
|
255
|
+
sc=False, mitm=False, bonding=False
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
async def post_power_on(
|
|
260
|
+
device: Device,
|
|
261
|
+
le_scan: Optional[tuple[int, int]],
|
|
262
|
+
le_advertise: Optional[int],
|
|
263
|
+
classic_page_scan: bool,
|
|
264
|
+
classic_inquiry_scan: bool,
|
|
265
|
+
) -> None:
|
|
266
|
+
if classic_page_scan:
|
|
267
|
+
logging.info(color("*** Enabling page scan", "blue"))
|
|
268
|
+
await device.set_connectable(True)
|
|
269
|
+
if classic_inquiry_scan:
|
|
270
|
+
logging.info(color("*** Enabling inquiry scan", "blue"))
|
|
271
|
+
await device.set_discoverable(True)
|
|
272
|
+
|
|
273
|
+
if le_scan:
|
|
274
|
+
scan_window, scan_interval = le_scan
|
|
275
|
+
logging.info(
|
|
276
|
+
color(
|
|
277
|
+
f"*** Starting LE scanning [{scan_window}ms/{scan_interval}ms]",
|
|
278
|
+
"blue",
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
await device.start_scanning(
|
|
282
|
+
scan_interval=scan_interval, scan_window=scan_window
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
if le_advertise:
|
|
286
|
+
logging.info(color(f"*** Starting LE advertising [{le_advertise}ms]", "blue"))
|
|
287
|
+
await device.start_advertising(
|
|
288
|
+
advertising_interval_min=le_advertise,
|
|
289
|
+
advertising_interval_max=le_advertise,
|
|
290
|
+
auto_restart=True,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
200
294
|
# -----------------------------------------------------------------------------
|
|
201
295
|
# Packet
|
|
202
296
|
# -----------------------------------------------------------------------------
|
|
@@ -414,7 +508,8 @@ class Sender:
|
|
|
414
508
|
self.bytes_sent += len(packet)
|
|
415
509
|
await self.packet_io.send_packet(packet)
|
|
416
510
|
|
|
417
|
-
|
|
511
|
+
if self.packet_io.can_receive():
|
|
512
|
+
await self.done.wait()
|
|
418
513
|
|
|
419
514
|
run_counter = f'[{run + 1} of {self.repeat + 1}]' if self.repeat else ''
|
|
420
515
|
logging.info(color(f'=== {run_counter} Done!', 'magenta'))
|
|
@@ -444,6 +539,9 @@ class Sender:
|
|
|
444
539
|
)
|
|
445
540
|
self.done.set()
|
|
446
541
|
|
|
542
|
+
def is_sender(self):
|
|
543
|
+
return True
|
|
544
|
+
|
|
447
545
|
|
|
448
546
|
# -----------------------------------------------------------------------------
|
|
449
547
|
# Receiver
|
|
@@ -491,7 +589,8 @@ class Receiver:
|
|
|
491
589
|
logging.info(
|
|
492
590
|
color(
|
|
493
591
|
f'!!! Unexpected packet, expected {self.expected_packet_index} '
|
|
494
|
-
f'but received {packet.sequence}'
|
|
592
|
+
f'but received {packet.sequence}',
|
|
593
|
+
'red',
|
|
495
594
|
)
|
|
496
595
|
)
|
|
497
596
|
|
|
@@ -534,6 +633,9 @@ class Receiver:
|
|
|
534
633
|
await self.done.wait()
|
|
535
634
|
logging.info(color('=== Done!', 'magenta'))
|
|
536
635
|
|
|
636
|
+
def is_sender(self):
|
|
637
|
+
return False
|
|
638
|
+
|
|
537
639
|
|
|
538
640
|
# -----------------------------------------------------------------------------
|
|
539
641
|
# Ping
|
|
@@ -669,7 +771,8 @@ class Ping:
|
|
|
669
771
|
color(
|
|
670
772
|
f'!!! Unexpected packet, '
|
|
671
773
|
f'expected {self.next_expected_packet_index} '
|
|
672
|
-
f'but received {packet.sequence}'
|
|
774
|
+
f'but received {packet.sequence}',
|
|
775
|
+
'red',
|
|
673
776
|
)
|
|
674
777
|
)
|
|
675
778
|
|
|
@@ -677,6 +780,9 @@ class Ping:
|
|
|
677
780
|
self.done.set()
|
|
678
781
|
return
|
|
679
782
|
|
|
783
|
+
def is_sender(self):
|
|
784
|
+
return True
|
|
785
|
+
|
|
680
786
|
|
|
681
787
|
# -----------------------------------------------------------------------------
|
|
682
788
|
# Pong
|
|
@@ -721,7 +827,8 @@ class Pong:
|
|
|
721
827
|
logging.info(
|
|
722
828
|
color(
|
|
723
829
|
f'!!! Unexpected packet, expected {self.expected_packet_index} '
|
|
724
|
-
f'but received {packet.sequence}'
|
|
830
|
+
f'but received {packet.sequence}',
|
|
831
|
+
'red',
|
|
725
832
|
)
|
|
726
833
|
)
|
|
727
834
|
|
|
@@ -743,6 +850,9 @@ class Pong:
|
|
|
743
850
|
await self.done.wait()
|
|
744
851
|
logging.info(color('=== Done!', 'magenta'))
|
|
745
852
|
|
|
853
|
+
def is_sender(self):
|
|
854
|
+
return False
|
|
855
|
+
|
|
746
856
|
|
|
747
857
|
# -----------------------------------------------------------------------------
|
|
748
858
|
# GattClient
|
|
@@ -906,6 +1016,9 @@ class StreamedPacketIO:
|
|
|
906
1016
|
# pylint: disable-next=not-callable
|
|
907
1017
|
self.io_sink(struct.pack('>H', len(packet)) + packet)
|
|
908
1018
|
|
|
1019
|
+
def can_receive(self):
|
|
1020
|
+
return True
|
|
1021
|
+
|
|
909
1022
|
|
|
910
1023
|
# -----------------------------------------------------------------------------
|
|
911
1024
|
# L2capClient
|
|
@@ -1177,6 +1290,96 @@ class RfcommServer(StreamedPacketIO):
|
|
|
1177
1290
|
await self.dlc.drain()
|
|
1178
1291
|
|
|
1179
1292
|
|
|
1293
|
+
# -----------------------------------------------------------------------------
|
|
1294
|
+
# IsoClient
|
|
1295
|
+
# -----------------------------------------------------------------------------
|
|
1296
|
+
class IsoClient(StreamedPacketIO):
|
|
1297
|
+
def __init__(
|
|
1298
|
+
self,
|
|
1299
|
+
device: Device,
|
|
1300
|
+
) -> None:
|
|
1301
|
+
super().__init__()
|
|
1302
|
+
self.device = device
|
|
1303
|
+
self.ready = asyncio.Event()
|
|
1304
|
+
self.cis_link: Optional[CisLink] = None
|
|
1305
|
+
|
|
1306
|
+
async def on_connection(
|
|
1307
|
+
self, connection: Connection, cis_link: CisLink, sender: bool
|
|
1308
|
+
) -> None:
|
|
1309
|
+
connection.on(connection.EVENT_DISCONNECTION, self.on_disconnection)
|
|
1310
|
+
self.cis_link = cis_link
|
|
1311
|
+
self.io_sink = cis_link.write
|
|
1312
|
+
await cis_link.setup_data_path(
|
|
1313
|
+
cis_link.Direction.HOST_TO_CONTROLLER
|
|
1314
|
+
if sender
|
|
1315
|
+
else cis_link.Direction.CONTROLLER_TO_HOST
|
|
1316
|
+
)
|
|
1317
|
+
cis_link.sink = self.on_iso_packet
|
|
1318
|
+
self.ready.set()
|
|
1319
|
+
|
|
1320
|
+
def on_iso_packet(self, iso_packet: HCI_IsoDataPacket) -> None:
|
|
1321
|
+
self.on_packet(iso_packet.iso_sdu_fragment)
|
|
1322
|
+
|
|
1323
|
+
def on_disconnection(self, _):
|
|
1324
|
+
pass
|
|
1325
|
+
|
|
1326
|
+
async def drain(self):
|
|
1327
|
+
if self.cis_link is None:
|
|
1328
|
+
return
|
|
1329
|
+
await self.cis_link.drain()
|
|
1330
|
+
|
|
1331
|
+
def can_receive(self):
|
|
1332
|
+
return False
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
# -----------------------------------------------------------------------------
|
|
1336
|
+
# IsoServer
|
|
1337
|
+
# -----------------------------------------------------------------------------
|
|
1338
|
+
class IsoServer(StreamedPacketIO):
|
|
1339
|
+
def __init__(
|
|
1340
|
+
self,
|
|
1341
|
+
device: Device,
|
|
1342
|
+
):
|
|
1343
|
+
super().__init__()
|
|
1344
|
+
self.device = device
|
|
1345
|
+
self.cis_link: Optional[CisLink] = None
|
|
1346
|
+
self.ready = asyncio.Event()
|
|
1347
|
+
|
|
1348
|
+
logging.info(
|
|
1349
|
+
color(
|
|
1350
|
+
'### Listening for ISO connection',
|
|
1351
|
+
'yellow',
|
|
1352
|
+
)
|
|
1353
|
+
)
|
|
1354
|
+
|
|
1355
|
+
async def on_connection(
|
|
1356
|
+
self, connection: Connection, cis_link: CisLink, sender: bool
|
|
1357
|
+
) -> None:
|
|
1358
|
+
connection.on(connection.EVENT_DISCONNECTION, self.on_disconnection)
|
|
1359
|
+
self.io_sink = cis_link.write
|
|
1360
|
+
await cis_link.setup_data_path(
|
|
1361
|
+
cis_link.Direction.HOST_TO_CONTROLLER
|
|
1362
|
+
if sender
|
|
1363
|
+
else cis_link.Direction.CONTROLLER_TO_HOST
|
|
1364
|
+
)
|
|
1365
|
+
cis_link.sink = self.on_iso_packet
|
|
1366
|
+
self.ready.set()
|
|
1367
|
+
|
|
1368
|
+
def on_iso_packet(self, iso_packet: HCI_IsoDataPacket) -> None:
|
|
1369
|
+
self.on_packet(iso_packet.iso_sdu_fragment)
|
|
1370
|
+
|
|
1371
|
+
def on_disconnection(self, _):
|
|
1372
|
+
pass
|
|
1373
|
+
|
|
1374
|
+
async def drain(self):
|
|
1375
|
+
if self.cis_link is None:
|
|
1376
|
+
return
|
|
1377
|
+
await self.cis_link.drain()
|
|
1378
|
+
|
|
1379
|
+
def can_receive(self):
|
|
1380
|
+
return False
|
|
1381
|
+
|
|
1382
|
+
|
|
1180
1383
|
# -----------------------------------------------------------------------------
|
|
1181
1384
|
# Central
|
|
1182
1385
|
# -----------------------------------------------------------------------------
|
|
@@ -1185,26 +1388,52 @@ class Central(Connection.Listener):
|
|
|
1185
1388
|
self,
|
|
1186
1389
|
transport,
|
|
1187
1390
|
peripheral_address,
|
|
1188
|
-
classic,
|
|
1189
1391
|
scenario_factory,
|
|
1190
1392
|
mode_factory,
|
|
1191
1393
|
connection_interval,
|
|
1192
1394
|
phy,
|
|
1193
1395
|
authenticate,
|
|
1194
1396
|
encrypt,
|
|
1397
|
+
iso,
|
|
1398
|
+
iso_sdu_interval_c_to_p,
|
|
1399
|
+
iso_sdu_interval_p_to_c,
|
|
1400
|
+
iso_max_sdu_c_to_p,
|
|
1401
|
+
iso_max_sdu_p_to_c,
|
|
1402
|
+
iso_max_transport_latency_c_to_p,
|
|
1403
|
+
iso_max_transport_latency_p_to_c,
|
|
1404
|
+
iso_rtn_c_to_p,
|
|
1405
|
+
iso_rtn_p_to_c,
|
|
1406
|
+
classic,
|
|
1195
1407
|
extended_data_length,
|
|
1196
1408
|
role_switch,
|
|
1409
|
+
le_scan,
|
|
1410
|
+
le_advertise,
|
|
1411
|
+
classic_page_scan,
|
|
1412
|
+
classic_inquiry_scan,
|
|
1197
1413
|
):
|
|
1198
1414
|
super().__init__()
|
|
1199
1415
|
self.transport = transport
|
|
1200
1416
|
self.peripheral_address = peripheral_address
|
|
1201
1417
|
self.classic = classic
|
|
1418
|
+
self.iso = iso
|
|
1419
|
+
self.iso_sdu_interval_c_to_p = iso_sdu_interval_c_to_p
|
|
1420
|
+
self.iso_sdu_interval_p_to_c = iso_sdu_interval_p_to_c
|
|
1421
|
+
self.iso_max_sdu_c_to_p = iso_max_sdu_c_to_p
|
|
1422
|
+
self.iso_max_sdu_p_to_c = iso_max_sdu_p_to_c
|
|
1423
|
+
self.iso_max_transport_latency_c_to_p = iso_max_transport_latency_c_to_p
|
|
1424
|
+
self.iso_max_transport_latency_p_to_c = iso_max_transport_latency_p_to_c
|
|
1425
|
+
self.iso_rtn_c_to_p = iso_rtn_c_to_p
|
|
1426
|
+
self.iso_rtn_p_to_c = iso_rtn_p_to_c
|
|
1202
1427
|
self.scenario_factory = scenario_factory
|
|
1203
1428
|
self.mode_factory = mode_factory
|
|
1204
1429
|
self.authenticate = authenticate
|
|
1205
1430
|
self.encrypt = encrypt or authenticate
|
|
1206
1431
|
self.extended_data_length = extended_data_length
|
|
1207
1432
|
self.role_switch = role_switch
|
|
1433
|
+
self.le_scan = le_scan
|
|
1434
|
+
self.le_advertise = le_advertise
|
|
1435
|
+
self.classic_page_scan = classic_page_scan
|
|
1436
|
+
self.classic_inquiry_scan = classic_inquiry_scan
|
|
1208
1437
|
self.device = None
|
|
1209
1438
|
self.connection = None
|
|
1210
1439
|
|
|
@@ -1241,7 +1470,7 @@ class Central(Connection.Listener):
|
|
|
1241
1470
|
|
|
1242
1471
|
async def run(self):
|
|
1243
1472
|
logging.info(color('>>> Connecting to HCI...', 'green'))
|
|
1244
|
-
async with await
|
|
1473
|
+
async with await open_transport(self.transport) as (
|
|
1245
1474
|
hci_source,
|
|
1246
1475
|
hci_sink,
|
|
1247
1476
|
):
|
|
@@ -1254,17 +1483,22 @@ class Central(Connection.Listener):
|
|
|
1254
1483
|
mode = self.mode_factory(self.device)
|
|
1255
1484
|
scenario = self.scenario_factory(mode)
|
|
1256
1485
|
self.device.classic_enabled = self.classic
|
|
1486
|
+
self.device.cis_enabled = self.iso
|
|
1257
1487
|
|
|
1258
1488
|
# Set up a pairing config factory with minimal requirements.
|
|
1259
1489
|
self.device.pairing_config_factory = lambda _: PairingConfig(
|
|
1260
1490
|
sc=False, mitm=False, bonding=False
|
|
1261
1491
|
)
|
|
1262
1492
|
|
|
1493
|
+
await pre_power_on(self.device, self.classic)
|
|
1263
1494
|
await self.device.power_on()
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1495
|
+
await post_power_on(
|
|
1496
|
+
self.device,
|
|
1497
|
+
self.le_scan,
|
|
1498
|
+
self.le_advertise,
|
|
1499
|
+
self.classic_page_scan,
|
|
1500
|
+
self.classic_inquiry_scan,
|
|
1501
|
+
)
|
|
1268
1502
|
|
|
1269
1503
|
logging.info(
|
|
1270
1504
|
color(f'### Connecting to {self.peripheral_address}...', 'cyan')
|
|
@@ -1339,7 +1573,72 @@ class Central(Connection.Listener):
|
|
|
1339
1573
|
)
|
|
1340
1574
|
)
|
|
1341
1575
|
|
|
1342
|
-
|
|
1576
|
+
# Setup ISO streams.
|
|
1577
|
+
if self.iso:
|
|
1578
|
+
if scenario.is_sender():
|
|
1579
|
+
sdu_interval_c_to_p = (
|
|
1580
|
+
self.iso_sdu_interval_c_to_p or DEFAULT_ISO_SDU_INTERVAL_C_TO_P
|
|
1581
|
+
)
|
|
1582
|
+
sdu_interval_p_to_c = self.iso_sdu_interval_p_to_c or 0
|
|
1583
|
+
max_transport_latency_c_to_p = (
|
|
1584
|
+
self.iso_max_transport_latency_c_to_p
|
|
1585
|
+
or DEFAULT_ISO_MAX_TRANSPORT_LATENCY_C_TO_P
|
|
1586
|
+
)
|
|
1587
|
+
max_transport_latency_p_to_c = (
|
|
1588
|
+
self.iso_max_transport_latency_p_to_c or 0
|
|
1589
|
+
)
|
|
1590
|
+
max_sdu_c_to_p = (
|
|
1591
|
+
self.iso_max_sdu_c_to_p or DEFAULT_ISO_MAX_SDU_C_TO_P
|
|
1592
|
+
)
|
|
1593
|
+
max_sdu_p_to_c = self.iso_max_sdu_p_to_c or 0
|
|
1594
|
+
rtn_c_to_p = self.iso_rtn_c_to_p or DEFAULT_ISO_RTN_C_TO_P
|
|
1595
|
+
rtn_p_to_c = self.iso_rtn_p_to_c or 0
|
|
1596
|
+
else:
|
|
1597
|
+
sdu_interval_p_to_c = (
|
|
1598
|
+
self.iso_sdu_interval_p_to_c or DEFAULT_ISO_SDU_INTERVAL_P_TO_C
|
|
1599
|
+
)
|
|
1600
|
+
sdu_interval_c_to_p = self.iso_sdu_interval_c_to_p or 0
|
|
1601
|
+
max_transport_latency_p_to_c = (
|
|
1602
|
+
self.iso_max_transport_latency_p_to_c
|
|
1603
|
+
or DEFAULT_ISO_MAX_TRANSPORT_LATENCY_P_TO_C
|
|
1604
|
+
)
|
|
1605
|
+
max_transport_latency_c_to_p = (
|
|
1606
|
+
self.iso_max_transport_latency_c_to_p or 0
|
|
1607
|
+
)
|
|
1608
|
+
max_sdu_p_to_c = (
|
|
1609
|
+
self.iso_max_sdu_p_to_c or DEFAULT_ISO_MAX_SDU_P_TO_C
|
|
1610
|
+
)
|
|
1611
|
+
max_sdu_c_to_p = self.iso_max_sdu_c_to_p or 0
|
|
1612
|
+
rtn_p_to_c = self.iso_rtn_p_to_c or DEFAULT_ISO_RTN_P_TO_C
|
|
1613
|
+
rtn_c_to_p = self.iso_rtn_c_to_p or 0
|
|
1614
|
+
cis_handles = await self.device.setup_cig(
|
|
1615
|
+
CigParameters(
|
|
1616
|
+
cig_id=1,
|
|
1617
|
+
sdu_interval_c_to_p=sdu_interval_c_to_p,
|
|
1618
|
+
sdu_interval_p_to_c=sdu_interval_p_to_c,
|
|
1619
|
+
max_transport_latency_c_to_p=max_transport_latency_c_to_p,
|
|
1620
|
+
max_transport_latency_p_to_c=max_transport_latency_p_to_c,
|
|
1621
|
+
cis_parameters=[
|
|
1622
|
+
CigParameters.CisParameters(
|
|
1623
|
+
cis_id=2,
|
|
1624
|
+
max_sdu_c_to_p=max_sdu_c_to_p,
|
|
1625
|
+
max_sdu_p_to_c=max_sdu_p_to_c,
|
|
1626
|
+
rtn_c_to_p=rtn_c_to_p,
|
|
1627
|
+
rtn_p_to_c=rtn_p_to_c,
|
|
1628
|
+
)
|
|
1629
|
+
],
|
|
1630
|
+
)
|
|
1631
|
+
)
|
|
1632
|
+
cis_link = (
|
|
1633
|
+
await self.device.create_cis([(cis_handles[0], self.connection)])
|
|
1634
|
+
)[0]
|
|
1635
|
+
print_cis_link(cis_link)
|
|
1636
|
+
|
|
1637
|
+
await mode.on_connection(
|
|
1638
|
+
self.connection, cis_link, scenario.is_sender()
|
|
1639
|
+
)
|
|
1640
|
+
else:
|
|
1641
|
+
await mode.on_connection(self.connection)
|
|
1343
1642
|
|
|
1344
1643
|
await scenario.run()
|
|
1345
1644
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
|
@@ -1375,24 +1674,38 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1375
1674
|
scenario_factory,
|
|
1376
1675
|
mode_factory,
|
|
1377
1676
|
classic,
|
|
1677
|
+
iso,
|
|
1378
1678
|
extended_data_length,
|
|
1379
1679
|
role_switch,
|
|
1680
|
+
le_scan,
|
|
1681
|
+
le_advertise,
|
|
1682
|
+
classic_page_scan,
|
|
1683
|
+
classic_inquiry_scan,
|
|
1380
1684
|
):
|
|
1381
1685
|
self.transport = transport
|
|
1382
1686
|
self.classic = classic
|
|
1687
|
+
self.iso = iso
|
|
1383
1688
|
self.scenario_factory = scenario_factory
|
|
1384
1689
|
self.mode_factory = mode_factory
|
|
1385
1690
|
self.extended_data_length = extended_data_length
|
|
1386
1691
|
self.role_switch = role_switch
|
|
1692
|
+
self.le_scan = le_scan
|
|
1693
|
+
self.classic_page_scan = classic_page_scan
|
|
1694
|
+
self.classic_inquiry_scan = classic_inquiry_scan
|
|
1387
1695
|
self.scenario = None
|
|
1388
1696
|
self.mode = None
|
|
1389
1697
|
self.device = None
|
|
1390
1698
|
self.connection = None
|
|
1391
1699
|
self.connected = asyncio.Event()
|
|
1392
1700
|
|
|
1701
|
+
if le_advertise:
|
|
1702
|
+
self.le_advertise = le_advertise
|
|
1703
|
+
else:
|
|
1704
|
+
self.le_advertise = 0 if classic else DEFAULT_ADVERTISING_INTERVAL
|
|
1705
|
+
|
|
1393
1706
|
async def run(self):
|
|
1394
1707
|
logging.info(color('>>> Connecting to HCI...', 'green'))
|
|
1395
|
-
async with await
|
|
1708
|
+
async with await open_transport(self.transport) as (
|
|
1396
1709
|
hci_source,
|
|
1397
1710
|
hci_sink,
|
|
1398
1711
|
):
|
|
@@ -1406,19 +1719,22 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1406
1719
|
self.mode = self.mode_factory(self.device)
|
|
1407
1720
|
self.scenario = self.scenario_factory(self.mode)
|
|
1408
1721
|
self.device.classic_enabled = self.classic
|
|
1722
|
+
self.device.cis_enabled = self.iso
|
|
1409
1723
|
|
|
1410
1724
|
# Set up a pairing config factory with minimal requirements.
|
|
1411
1725
|
self.device.pairing_config_factory = lambda _: PairingConfig(
|
|
1412
1726
|
sc=False, mitm=False, bonding=False
|
|
1413
1727
|
)
|
|
1414
1728
|
|
|
1729
|
+
await pre_power_on(self.device, self.classic)
|
|
1415
1730
|
await self.device.power_on()
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1731
|
+
await post_power_on(
|
|
1732
|
+
self.device,
|
|
1733
|
+
self.le_scan,
|
|
1734
|
+
self.le_advertise,
|
|
1735
|
+
self.classic or self.classic_page_scan,
|
|
1736
|
+
self.classic or self.classic_inquiry_scan,
|
|
1737
|
+
)
|
|
1422
1738
|
|
|
1423
1739
|
if self.classic:
|
|
1424
1740
|
logging.info(
|
|
@@ -1440,7 +1756,21 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1440
1756
|
logging.info(color('### Connected', 'cyan'))
|
|
1441
1757
|
print_connection(self.connection)
|
|
1442
1758
|
|
|
1443
|
-
|
|
1759
|
+
if self.iso:
|
|
1760
|
+
|
|
1761
|
+
async def on_cis_request(cis_link: CisLink) -> None:
|
|
1762
|
+
logging.info(color("@@@ Accepting CIS", "green"))
|
|
1763
|
+
await self.device.accept_cis_request(cis_link)
|
|
1764
|
+
print_cis_link(cis_link)
|
|
1765
|
+
|
|
1766
|
+
await self.mode.on_connection(
|
|
1767
|
+
self.connection, cis_link, self.scenario.is_sender()
|
|
1768
|
+
)
|
|
1769
|
+
|
|
1770
|
+
self.connection.on(self.connection.EVENT_CIS_REQUEST, on_cis_request)
|
|
1771
|
+
else:
|
|
1772
|
+
await self.mode.on_connection(self.connection)
|
|
1773
|
+
|
|
1444
1774
|
await self.scenario.run()
|
|
1445
1775
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
|
1446
1776
|
|
|
@@ -1449,10 +1779,14 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1449
1779
|
self.connection = connection
|
|
1450
1780
|
self.connected.set()
|
|
1451
1781
|
|
|
1452
|
-
# Stop being discoverable and connectable
|
|
1782
|
+
# Stop being discoverable and connectable if possible
|
|
1453
1783
|
if self.classic:
|
|
1454
|
-
|
|
1455
|
-
|
|
1784
|
+
if not self.classic_inquiry_scan:
|
|
1785
|
+
logging.info(color("*** Stopping inquiry scan", "blue"))
|
|
1786
|
+
AsyncRunner.spawn(self.device.set_discoverable(False))
|
|
1787
|
+
if not self.classic_page_scan:
|
|
1788
|
+
logging.info(color("*** Stopping page scan", "blue"))
|
|
1789
|
+
AsyncRunner.spawn(self.device.set_connectable(False))
|
|
1456
1790
|
|
|
1457
1791
|
# Request a new data length if needed
|
|
1458
1792
|
if not self.classic and self.extended_data_length:
|
|
@@ -1473,7 +1807,9 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1473
1807
|
self.scenario.reset()
|
|
1474
1808
|
|
|
1475
1809
|
if self.classic:
|
|
1810
|
+
logging.info(color("*** Enabling inquiry scan", "blue"))
|
|
1476
1811
|
AsyncRunner.spawn(self.device.set_discoverable(True))
|
|
1812
|
+
logging.info(color("*** Enabling page scan", "blue"))
|
|
1477
1813
|
AsyncRunner.spawn(self.device.set_connectable(True))
|
|
1478
1814
|
|
|
1479
1815
|
def on_connection_parameters_update(self):
|
|
@@ -1546,6 +1882,12 @@ def create_mode_factory(ctx, default_mode):
|
|
|
1546
1882
|
credits_threshold=ctx.obj['rfcomm_credits_threshold'],
|
|
1547
1883
|
)
|
|
1548
1884
|
|
|
1885
|
+
if mode == 'iso-server':
|
|
1886
|
+
return IsoServer(device)
|
|
1887
|
+
|
|
1888
|
+
if mode == 'iso-client':
|
|
1889
|
+
return IsoClient(device)
|
|
1890
|
+
|
|
1549
1891
|
raise ValueError('invalid mode')
|
|
1550
1892
|
|
|
1551
1893
|
return create_mode
|
|
@@ -1573,6 +1915,9 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1573
1915
|
return Receiver(packet_io, ctx.obj['linger'])
|
|
1574
1916
|
|
|
1575
1917
|
if scenario == 'ping':
|
|
1918
|
+
if isinstance(packet_io, (IsoClient, IsoServer)):
|
|
1919
|
+
raise ValueError('ping not supported with ISO')
|
|
1920
|
+
|
|
1576
1921
|
return Ping(
|
|
1577
1922
|
packet_io,
|
|
1578
1923
|
start_delay=ctx.obj['start_delay'],
|
|
@@ -1584,6 +1929,9 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1584
1929
|
)
|
|
1585
1930
|
|
|
1586
1931
|
if scenario == 'pong':
|
|
1932
|
+
if isinstance(packet_io, (IsoClient, IsoServer)):
|
|
1933
|
+
raise ValueError('pong not supported with ISO')
|
|
1934
|
+
|
|
1587
1935
|
return Pong(packet_io, ctx.obj['linger'])
|
|
1588
1936
|
|
|
1589
1937
|
raise ValueError('invalid scenario')
|
|
@@ -1607,6 +1955,8 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1607
1955
|
'l2cap-server',
|
|
1608
1956
|
'rfcomm-client',
|
|
1609
1957
|
'rfcomm-server',
|
|
1958
|
+
'iso-client',
|
|
1959
|
+
'iso-server',
|
|
1610
1960
|
]
|
|
1611
1961
|
),
|
|
1612
1962
|
)
|
|
@@ -1619,6 +1969,7 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1619
1969
|
)
|
|
1620
1970
|
@click.option(
|
|
1621
1971
|
'--extended-data-length',
|
|
1972
|
+
metavar='<TX-OCTETS>/<TX-TIME>',
|
|
1622
1973
|
help='Request a data length upon connection, specified as tx_octets/tx_time',
|
|
1623
1974
|
)
|
|
1624
1975
|
@click.option(
|
|
@@ -1626,6 +1977,26 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1626
1977
|
type=click.Choice(['central', 'peripheral']),
|
|
1627
1978
|
help='Request role switch upon connection (central or peripheral)',
|
|
1628
1979
|
)
|
|
1980
|
+
@click.option(
|
|
1981
|
+
'--le-scan',
|
|
1982
|
+
metavar='<WINDOW>/<INTERVAL>',
|
|
1983
|
+
help='Perform an LE scan with a given window and interval (milliseconds)',
|
|
1984
|
+
)
|
|
1985
|
+
@click.option(
|
|
1986
|
+
'--le-advertise',
|
|
1987
|
+
metavar='<INTERVAL>',
|
|
1988
|
+
help='Advertise with a given interval (milliseconds)',
|
|
1989
|
+
)
|
|
1990
|
+
@click.option(
|
|
1991
|
+
'--classic-page-scan',
|
|
1992
|
+
is_flag=True,
|
|
1993
|
+
help='Enable Classic page scanning',
|
|
1994
|
+
)
|
|
1995
|
+
@click.option(
|
|
1996
|
+
'--classic-inquiry-scan',
|
|
1997
|
+
is_flag=True,
|
|
1998
|
+
help='Enable Classic enquiry scanning',
|
|
1999
|
+
)
|
|
1629
2000
|
@click.option(
|
|
1630
2001
|
'--rfcomm-channel',
|
|
1631
2002
|
type=int,
|
|
@@ -1751,6 +2122,10 @@ def bench(
|
|
|
1751
2122
|
att_mtu,
|
|
1752
2123
|
extended_data_length,
|
|
1753
2124
|
role_switch,
|
|
2125
|
+
le_scan,
|
|
2126
|
+
le_advertise,
|
|
2127
|
+
classic_page_scan,
|
|
2128
|
+
classic_inquiry_scan,
|
|
1754
2129
|
packet_size,
|
|
1755
2130
|
packet_count,
|
|
1756
2131
|
start_delay,
|
|
@@ -1799,7 +2174,12 @@ def bench(
|
|
|
1799
2174
|
else None
|
|
1800
2175
|
)
|
|
1801
2176
|
ctx.obj['role_switch'] = role_switch
|
|
2177
|
+
ctx.obj['le_scan'] = [float(x) for x in le_scan.split('/')] if le_scan else None
|
|
2178
|
+
ctx.obj['le_advertise'] = float(le_advertise) if le_advertise else None
|
|
2179
|
+
ctx.obj['classic_page_scan'] = classic_page_scan
|
|
2180
|
+
ctx.obj['classic_inquiry_scan'] = classic_inquiry_scan
|
|
1802
2181
|
ctx.obj['classic'] = mode in ('rfcomm-client', 'rfcomm-server')
|
|
2182
|
+
ctx.obj['iso'] = mode in ('iso-client', 'iso-server')
|
|
1803
2183
|
|
|
1804
2184
|
|
|
1805
2185
|
@bench.command()
|
|
@@ -1821,28 +2201,94 @@ def bench(
|
|
|
1821
2201
|
@click.option('--phy', type=click.Choice(['1m', '2m', 'coded']), help='PHY to use')
|
|
1822
2202
|
@click.option('--authenticate', is_flag=True, help='Authenticate (RFComm only)')
|
|
1823
2203
|
@click.option('--encrypt', is_flag=True, help='Encrypt the connection (RFComm only)')
|
|
2204
|
+
@click.option(
|
|
2205
|
+
'--iso-sdu-interval-c-to-p',
|
|
2206
|
+
type=int,
|
|
2207
|
+
help='ISO SDU central -> peripheral (microseconds)',
|
|
2208
|
+
)
|
|
2209
|
+
@click.option(
|
|
2210
|
+
'--iso-sdu-interval-p-to-c',
|
|
2211
|
+
type=int,
|
|
2212
|
+
help='ISO SDU interval peripheral -> central (microseconds)',
|
|
2213
|
+
)
|
|
2214
|
+
@click.option(
|
|
2215
|
+
'--iso-max-sdu-c-to-p',
|
|
2216
|
+
type=int,
|
|
2217
|
+
help='ISO max SDU central -> peripheral',
|
|
2218
|
+
)
|
|
2219
|
+
@click.option(
|
|
2220
|
+
'--iso-max-sdu-p-to-c',
|
|
2221
|
+
type=int,
|
|
2222
|
+
help='ISO max SDU peripheral -> central',
|
|
2223
|
+
)
|
|
2224
|
+
@click.option(
|
|
2225
|
+
'--iso-max-transport-latency-c-to-p',
|
|
2226
|
+
type=int,
|
|
2227
|
+
help='ISO max transport latency central -> peripheral (milliseconds)',
|
|
2228
|
+
)
|
|
2229
|
+
@click.option(
|
|
2230
|
+
'--iso-max-transport-latency-p-to-c',
|
|
2231
|
+
type=int,
|
|
2232
|
+
help='ISO max transport latency peripheral -> central (milliseconds)',
|
|
2233
|
+
)
|
|
2234
|
+
@click.option(
|
|
2235
|
+
'--iso-rtn-c-to-p',
|
|
2236
|
+
type=int,
|
|
2237
|
+
help='ISO RTN central -> peripheral (integer count)',
|
|
2238
|
+
)
|
|
2239
|
+
@click.option(
|
|
2240
|
+
'--iso-rtn-p-to-c',
|
|
2241
|
+
type=int,
|
|
2242
|
+
help='ISO RTN peripheral -> central (integer count)',
|
|
2243
|
+
)
|
|
1824
2244
|
@click.pass_context
|
|
1825
2245
|
def central(
|
|
1826
|
-
ctx,
|
|
2246
|
+
ctx,
|
|
2247
|
+
transport,
|
|
2248
|
+
peripheral_address,
|
|
2249
|
+
connection_interval,
|
|
2250
|
+
phy,
|
|
2251
|
+
authenticate,
|
|
2252
|
+
encrypt,
|
|
2253
|
+
iso_sdu_interval_c_to_p,
|
|
2254
|
+
iso_sdu_interval_p_to_c,
|
|
2255
|
+
iso_max_sdu_c_to_p,
|
|
2256
|
+
iso_max_sdu_p_to_c,
|
|
2257
|
+
iso_max_transport_latency_c_to_p,
|
|
2258
|
+
iso_max_transport_latency_p_to_c,
|
|
2259
|
+
iso_rtn_c_to_p,
|
|
2260
|
+
iso_rtn_p_to_c,
|
|
1827
2261
|
):
|
|
1828
2262
|
"""Run as a central (initiates the connection)"""
|
|
1829
2263
|
scenario_factory = create_scenario_factory(ctx, 'send')
|
|
1830
2264
|
mode_factory = create_mode_factory(ctx, 'gatt-client')
|
|
1831
|
-
classic = ctx.obj['classic']
|
|
1832
2265
|
|
|
1833
2266
|
async def run_central():
|
|
1834
2267
|
await Central(
|
|
1835
2268
|
transport,
|
|
1836
2269
|
peripheral_address,
|
|
1837
|
-
classic,
|
|
1838
2270
|
scenario_factory,
|
|
1839
2271
|
mode_factory,
|
|
1840
2272
|
connection_interval,
|
|
1841
2273
|
phy,
|
|
1842
2274
|
authenticate,
|
|
1843
2275
|
encrypt or authenticate,
|
|
2276
|
+
ctx.obj['iso'],
|
|
2277
|
+
iso_sdu_interval_c_to_p,
|
|
2278
|
+
iso_sdu_interval_p_to_c,
|
|
2279
|
+
iso_max_sdu_c_to_p,
|
|
2280
|
+
iso_max_sdu_p_to_c,
|
|
2281
|
+
iso_max_transport_latency_c_to_p,
|
|
2282
|
+
iso_max_transport_latency_p_to_c,
|
|
2283
|
+
iso_rtn_c_to_p,
|
|
2284
|
+
iso_rtn_p_to_c,
|
|
2285
|
+
ctx.obj['classic'],
|
|
1844
2286
|
ctx.obj['extended_data_length'],
|
|
1845
2287
|
ctx.obj['role_switch'],
|
|
2288
|
+
ctx.obj['le_scan'],
|
|
2289
|
+
ctx.obj['le_advertise'],
|
|
2290
|
+
ctx.obj['classic_page_scan'],
|
|
2291
|
+
ctx.obj['classic_inquiry_scan'],
|
|
1846
2292
|
).run()
|
|
1847
2293
|
|
|
1848
2294
|
asyncio.run(run_central())
|
|
@@ -1862,8 +2308,13 @@ def peripheral(ctx, transport):
|
|
|
1862
2308
|
scenario_factory,
|
|
1863
2309
|
mode_factory,
|
|
1864
2310
|
ctx.obj['classic'],
|
|
2311
|
+
ctx.obj['iso'],
|
|
1865
2312
|
ctx.obj['extended_data_length'],
|
|
1866
2313
|
ctx.obj['role_switch'],
|
|
2314
|
+
ctx.obj['le_scan'],
|
|
2315
|
+
ctx.obj['le_advertise'],
|
|
2316
|
+
ctx.obj['classic_page_scan'],
|
|
2317
|
+
ctx.obj['classic_inquiry_scan'],
|
|
1867
2318
|
).run()
|
|
1868
2319
|
|
|
1869
2320
|
asyncio.run(run_peripheral())
|