bumble 0.0.212__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 +480 -31
- bumble/apps/console.py +3 -3
- 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 +19 -11
- 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 +2 -6
- bumble/avc.py +7 -7
- bumble/avctp.py +3 -3
- bumble/avdtp.py +16 -20
- bumble/avrcp.py +41 -53
- bumble/colors.py +2 -2
- bumble/controller.py +84 -23
- bumble/device.py +348 -182
- bumble/drivers/__init__.py +2 -2
- bumble/drivers/common.py +0 -2
- bumble/drivers/intel.py +37 -40
- bumble/drivers/rtk.py +28 -35
- bumble/gatt.py +4 -4
- bumble/gatt_adapters.py +4 -5
- bumble/gatt_client.py +26 -31
- bumble/gatt_server.py +7 -11
- bumble/hci.py +2601 -2909
- bumble/helpers.py +4 -5
- bumble/hfp.py +32 -37
- bumble/host.py +94 -35
- bumble/keys.py +5 -5
- bumble/l2cap.py +310 -394
- 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 +27 -28
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +6 -6
- bumble/pandora/utils.py +3 -3
- bumble/profiles/ascs.py +132 -131
- bumble/profiles/asha.py +2 -2
- bumble/profiles/bap.py +3 -4
- bumble/profiles/csip.py +2 -2
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +2 -2
- bumble/profiles/hap.py +34 -33
- bumble/profiles/le_audio.py +4 -4
- bumble/profiles/mcp.py +4 -4
- bumble/profiles/vcs.py +3 -5
- bumble/rfcomm.py +10 -10
- bumble/rtp.py +1 -2
- bumble/sdp.py +2 -2
- bumble/smp.py +57 -61
- 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.212.dist-info → bumble-0.0.213.dist-info}/METADATA +2 -2
- {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/RECORD +83 -86
- {bumble-0.0.212.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
- {bumble-0.0.212.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.212.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.212.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(
|
|
@@ -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,18 +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
|
-
self.device.config.keystore = "JsonKeyStore"
|
|
1260
1489
|
self.device.pairing_config_factory = lambda _: PairingConfig(
|
|
1261
1490
|
sc=False, mitm=False, bonding=False
|
|
1262
1491
|
)
|
|
1263
1492
|
|
|
1493
|
+
await pre_power_on(self.device, self.classic)
|
|
1264
1494
|
await self.device.power_on()
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
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
|
+
)
|
|
1269
1502
|
|
|
1270
1503
|
logging.info(
|
|
1271
1504
|
color(f'### Connecting to {self.peripheral_address}...', 'cyan')
|
|
@@ -1340,7 +1573,72 @@ class Central(Connection.Listener):
|
|
|
1340
1573
|
)
|
|
1341
1574
|
)
|
|
1342
1575
|
|
|
1343
|
-
|
|
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)
|
|
1344
1642
|
|
|
1345
1643
|
await scenario.run()
|
|
1346
1644
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
|
@@ -1376,24 +1674,38 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1376
1674
|
scenario_factory,
|
|
1377
1675
|
mode_factory,
|
|
1378
1676
|
classic,
|
|
1677
|
+
iso,
|
|
1379
1678
|
extended_data_length,
|
|
1380
1679
|
role_switch,
|
|
1680
|
+
le_scan,
|
|
1681
|
+
le_advertise,
|
|
1682
|
+
classic_page_scan,
|
|
1683
|
+
classic_inquiry_scan,
|
|
1381
1684
|
):
|
|
1382
1685
|
self.transport = transport
|
|
1383
1686
|
self.classic = classic
|
|
1687
|
+
self.iso = iso
|
|
1384
1688
|
self.scenario_factory = scenario_factory
|
|
1385
1689
|
self.mode_factory = mode_factory
|
|
1386
1690
|
self.extended_data_length = extended_data_length
|
|
1387
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
|
|
1388
1695
|
self.scenario = None
|
|
1389
1696
|
self.mode = None
|
|
1390
1697
|
self.device = None
|
|
1391
1698
|
self.connection = None
|
|
1392
1699
|
self.connected = asyncio.Event()
|
|
1393
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
|
+
|
|
1394
1706
|
async def run(self):
|
|
1395
1707
|
logging.info(color('>>> Connecting to HCI...', 'green'))
|
|
1396
|
-
async with await
|
|
1708
|
+
async with await open_transport(self.transport) as (
|
|
1397
1709
|
hci_source,
|
|
1398
1710
|
hci_sink,
|
|
1399
1711
|
):
|
|
@@ -1407,20 +1719,22 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1407
1719
|
self.mode = self.mode_factory(self.device)
|
|
1408
1720
|
self.scenario = self.scenario_factory(self.mode)
|
|
1409
1721
|
self.device.classic_enabled = self.classic
|
|
1722
|
+
self.device.cis_enabled = self.iso
|
|
1410
1723
|
|
|
1411
1724
|
# Set up a pairing config factory with minimal requirements.
|
|
1412
|
-
self.device.config.keystore = "JsonKeyStore"
|
|
1413
1725
|
self.device.pairing_config_factory = lambda _: PairingConfig(
|
|
1414
1726
|
sc=False, mitm=False, bonding=False
|
|
1415
1727
|
)
|
|
1416
1728
|
|
|
1729
|
+
await pre_power_on(self.device, self.classic)
|
|
1417
1730
|
await self.device.power_on()
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
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
|
+
)
|
|
1424
1738
|
|
|
1425
1739
|
if self.classic:
|
|
1426
1740
|
logging.info(
|
|
@@ -1442,7 +1756,21 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1442
1756
|
logging.info(color('### Connected', 'cyan'))
|
|
1443
1757
|
print_connection(self.connection)
|
|
1444
1758
|
|
|
1445
|
-
|
|
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
|
+
|
|
1446
1774
|
await self.scenario.run()
|
|
1447
1775
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
|
1448
1776
|
|
|
@@ -1451,10 +1779,14 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1451
1779
|
self.connection = connection
|
|
1452
1780
|
self.connected.set()
|
|
1453
1781
|
|
|
1454
|
-
# Stop being discoverable and connectable
|
|
1782
|
+
# Stop being discoverable and connectable if possible
|
|
1455
1783
|
if self.classic:
|
|
1456
|
-
|
|
1457
|
-
|
|
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))
|
|
1458
1790
|
|
|
1459
1791
|
# Request a new data length if needed
|
|
1460
1792
|
if not self.classic and self.extended_data_length:
|
|
@@ -1475,7 +1807,9 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|
|
1475
1807
|
self.scenario.reset()
|
|
1476
1808
|
|
|
1477
1809
|
if self.classic:
|
|
1810
|
+
logging.info(color("*** Enabling inquiry scan", "blue"))
|
|
1478
1811
|
AsyncRunner.spawn(self.device.set_discoverable(True))
|
|
1812
|
+
logging.info(color("*** Enabling page scan", "blue"))
|
|
1479
1813
|
AsyncRunner.spawn(self.device.set_connectable(True))
|
|
1480
1814
|
|
|
1481
1815
|
def on_connection_parameters_update(self):
|
|
@@ -1548,6 +1882,12 @@ def create_mode_factory(ctx, default_mode):
|
|
|
1548
1882
|
credits_threshold=ctx.obj['rfcomm_credits_threshold'],
|
|
1549
1883
|
)
|
|
1550
1884
|
|
|
1885
|
+
if mode == 'iso-server':
|
|
1886
|
+
return IsoServer(device)
|
|
1887
|
+
|
|
1888
|
+
if mode == 'iso-client':
|
|
1889
|
+
return IsoClient(device)
|
|
1890
|
+
|
|
1551
1891
|
raise ValueError('invalid mode')
|
|
1552
1892
|
|
|
1553
1893
|
return create_mode
|
|
@@ -1575,6 +1915,9 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1575
1915
|
return Receiver(packet_io, ctx.obj['linger'])
|
|
1576
1916
|
|
|
1577
1917
|
if scenario == 'ping':
|
|
1918
|
+
if isinstance(packet_io, (IsoClient, IsoServer)):
|
|
1919
|
+
raise ValueError('ping not supported with ISO')
|
|
1920
|
+
|
|
1578
1921
|
return Ping(
|
|
1579
1922
|
packet_io,
|
|
1580
1923
|
start_delay=ctx.obj['start_delay'],
|
|
@@ -1586,6 +1929,9 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1586
1929
|
)
|
|
1587
1930
|
|
|
1588
1931
|
if scenario == 'pong':
|
|
1932
|
+
if isinstance(packet_io, (IsoClient, IsoServer)):
|
|
1933
|
+
raise ValueError('pong not supported with ISO')
|
|
1934
|
+
|
|
1589
1935
|
return Pong(packet_io, ctx.obj['linger'])
|
|
1590
1936
|
|
|
1591
1937
|
raise ValueError('invalid scenario')
|
|
@@ -1609,6 +1955,8 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1609
1955
|
'l2cap-server',
|
|
1610
1956
|
'rfcomm-client',
|
|
1611
1957
|
'rfcomm-server',
|
|
1958
|
+
'iso-client',
|
|
1959
|
+
'iso-server',
|
|
1612
1960
|
]
|
|
1613
1961
|
),
|
|
1614
1962
|
)
|
|
@@ -1621,6 +1969,7 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1621
1969
|
)
|
|
1622
1970
|
@click.option(
|
|
1623
1971
|
'--extended-data-length',
|
|
1972
|
+
metavar='<TX-OCTETS>/<TX-TIME>',
|
|
1624
1973
|
help='Request a data length upon connection, specified as tx_octets/tx_time',
|
|
1625
1974
|
)
|
|
1626
1975
|
@click.option(
|
|
@@ -1628,6 +1977,26 @@ def create_scenario_factory(ctx, default_scenario):
|
|
|
1628
1977
|
type=click.Choice(['central', 'peripheral']),
|
|
1629
1978
|
help='Request role switch upon connection (central or peripheral)',
|
|
1630
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
|
+
)
|
|
1631
2000
|
@click.option(
|
|
1632
2001
|
'--rfcomm-channel',
|
|
1633
2002
|
type=int,
|
|
@@ -1753,6 +2122,10 @@ def bench(
|
|
|
1753
2122
|
att_mtu,
|
|
1754
2123
|
extended_data_length,
|
|
1755
2124
|
role_switch,
|
|
2125
|
+
le_scan,
|
|
2126
|
+
le_advertise,
|
|
2127
|
+
classic_page_scan,
|
|
2128
|
+
classic_inquiry_scan,
|
|
1756
2129
|
packet_size,
|
|
1757
2130
|
packet_count,
|
|
1758
2131
|
start_delay,
|
|
@@ -1801,7 +2174,12 @@ def bench(
|
|
|
1801
2174
|
else None
|
|
1802
2175
|
)
|
|
1803
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
|
|
1804
2181
|
ctx.obj['classic'] = mode in ('rfcomm-client', 'rfcomm-server')
|
|
2182
|
+
ctx.obj['iso'] = mode in ('iso-client', 'iso-server')
|
|
1805
2183
|
|
|
1806
2184
|
|
|
1807
2185
|
@bench.command()
|
|
@@ -1823,28 +2201,94 @@ def bench(
|
|
|
1823
2201
|
@click.option('--phy', type=click.Choice(['1m', '2m', 'coded']), help='PHY to use')
|
|
1824
2202
|
@click.option('--authenticate', is_flag=True, help='Authenticate (RFComm only)')
|
|
1825
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
|
+
)
|
|
1826
2244
|
@click.pass_context
|
|
1827
2245
|
def central(
|
|
1828
|
-
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,
|
|
1829
2261
|
):
|
|
1830
2262
|
"""Run as a central (initiates the connection)"""
|
|
1831
2263
|
scenario_factory = create_scenario_factory(ctx, 'send')
|
|
1832
2264
|
mode_factory = create_mode_factory(ctx, 'gatt-client')
|
|
1833
|
-
classic = ctx.obj['classic']
|
|
1834
2265
|
|
|
1835
2266
|
async def run_central():
|
|
1836
2267
|
await Central(
|
|
1837
2268
|
transport,
|
|
1838
2269
|
peripheral_address,
|
|
1839
|
-
classic,
|
|
1840
2270
|
scenario_factory,
|
|
1841
2271
|
mode_factory,
|
|
1842
2272
|
connection_interval,
|
|
1843
2273
|
phy,
|
|
1844
2274
|
authenticate,
|
|
1845
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'],
|
|
1846
2286
|
ctx.obj['extended_data_length'],
|
|
1847
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'],
|
|
1848
2292
|
).run()
|
|
1849
2293
|
|
|
1850
2294
|
asyncio.run(run_central())
|
|
@@ -1864,8 +2308,13 @@ def peripheral(ctx, transport):
|
|
|
1864
2308
|
scenario_factory,
|
|
1865
2309
|
mode_factory,
|
|
1866
2310
|
ctx.obj['classic'],
|
|
2311
|
+
ctx.obj['iso'],
|
|
1867
2312
|
ctx.obj['extended_data_length'],
|
|
1868
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'],
|
|
1869
2318
|
).run()
|
|
1870
2319
|
|
|
1871
2320
|
asyncio.run(run_peripheral())
|