bumble 0.0.220__py3-none-any.whl → 0.0.221__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.
Files changed (102) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +5 -5
  3. bumble/apps/auracast.py +746 -473
  4. bumble/apps/bench.py +4 -5
  5. bumble/apps/console.py +5 -10
  6. bumble/apps/controller_info.py +12 -7
  7. bumble/apps/controller_loopback.py +1 -2
  8. bumble/apps/device_info.py +2 -3
  9. bumble/apps/gatt_dump.py +0 -1
  10. bumble/apps/lea_unicast/app.py +1 -1
  11. bumble/apps/pair.py +49 -46
  12. bumble/apps/pandora_server.py +2 -2
  13. bumble/apps/player/player.py +10 -12
  14. bumble/apps/rfcomm_bridge.py +10 -11
  15. bumble/apps/scan.py +1 -3
  16. bumble/apps/speaker/speaker.py +3 -4
  17. bumble/at.py +4 -5
  18. bumble/att.py +91 -25
  19. bumble/audio/io.py +5 -3
  20. bumble/avc.py +1 -2
  21. bumble/avctp.py +2 -3
  22. bumble/avdtp.py +53 -57
  23. bumble/avrcp.py +25 -27
  24. bumble/codecs.py +15 -15
  25. bumble/colors.py +7 -8
  26. bumble/controller.py +663 -391
  27. bumble/core.py +41 -49
  28. bumble/crypto/__init__.py +2 -1
  29. bumble/crypto/builtin.py +2 -8
  30. bumble/data_types.py +2 -1
  31. bumble/decoder.py +2 -3
  32. bumble/device.py +171 -142
  33. bumble/drivers/__init__.py +3 -2
  34. bumble/drivers/intel.py +6 -8
  35. bumble/drivers/rtk.py +1 -1
  36. bumble/gatt.py +9 -9
  37. bumble/gatt_adapters.py +6 -6
  38. bumble/gatt_client.py +110 -60
  39. bumble/gatt_server.py +209 -139
  40. bumble/hci.py +87 -74
  41. bumble/helpers.py +5 -5
  42. bumble/hfp.py +27 -26
  43. bumble/hid.py +9 -9
  44. bumble/host.py +44 -50
  45. bumble/keys.py +17 -17
  46. bumble/l2cap.py +1015 -218
  47. bumble/link.py +26 -159
  48. bumble/ll.py +200 -0
  49. bumble/pairing.py +14 -15
  50. bumble/pandora/__init__.py +2 -2
  51. bumble/pandora/device.py +6 -4
  52. bumble/pandora/host.py +19 -10
  53. bumble/pandora/l2cap.py +8 -9
  54. bumble/pandora/security.py +18 -16
  55. bumble/pandora/utils.py +4 -4
  56. bumble/profiles/aics.py +6 -8
  57. bumble/profiles/ams.py +3 -5
  58. bumble/profiles/ancs.py +11 -11
  59. bumble/profiles/ascs.py +5 -5
  60. bumble/profiles/asha.py +10 -9
  61. bumble/profiles/bass.py +9 -3
  62. bumble/profiles/battery_service.py +1 -2
  63. bumble/profiles/csip.py +9 -10
  64. bumble/profiles/device_information_service.py +16 -17
  65. bumble/profiles/gap.py +3 -4
  66. bumble/profiles/gatt_service.py +0 -1
  67. bumble/profiles/gmap.py +12 -13
  68. bumble/profiles/hap.py +3 -3
  69. bumble/profiles/heart_rate_service.py +7 -8
  70. bumble/profiles/le_audio.py +1 -1
  71. bumble/profiles/mcp.py +28 -28
  72. bumble/profiles/pacs.py +13 -17
  73. bumble/profiles/pbp.py +16 -0
  74. bumble/profiles/vcs.py +2 -2
  75. bumble/profiles/vocs.py +6 -9
  76. bumble/rfcomm.py +19 -18
  77. bumble/sdp.py +12 -11
  78. bumble/smp.py +20 -30
  79. bumble/snoop.py +2 -1
  80. bumble/tools/generate_company_id_list.py +1 -1
  81. bumble/tools/intel_util.py +2 -2
  82. bumble/tools/rtk_fw_download.py +1 -1
  83. bumble/tools/rtk_util.py +1 -1
  84. bumble/transport/__init__.py +1 -2
  85. bumble/transport/android_emulator.py +2 -3
  86. bumble/transport/android_netsim.py +49 -40
  87. bumble/transport/common.py +9 -9
  88. bumble/transport/file.py +1 -2
  89. bumble/transport/hci_socket.py +2 -3
  90. bumble/transport/pty.py +3 -5
  91. bumble/transport/pyusb.py +8 -5
  92. bumble/transport/serial.py +1 -2
  93. bumble/transport/vhci.py +1 -2
  94. bumble/transport/ws_server.py +2 -3
  95. bumble/utils.py +22 -9
  96. bumble/vendor/android/hci.py +4 -2
  97. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/METADATA +3 -2
  98. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/RECORD +102 -101
  99. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
  100. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
  101. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
  102. {bumble-0.0.220.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/controller.py CHANGED
@@ -23,14 +23,14 @@ import itertools
23
23
  import logging
24
24
  import random
25
25
  import struct
26
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
26
+ from typing import TYPE_CHECKING, Any, cast
27
27
 
28
- from bumble import hci, lmp
28
+ from bumble import hci, link, ll, lmp
29
+ from bumble import link as bumble_link
29
30
  from bumble.colors import color
30
31
  from bumble.core import PhysicalTransport
31
32
 
32
33
  if TYPE_CHECKING:
33
- from bumble.link import LocalLink
34
34
  from bumble.transport.common import TransportSink
35
35
 
36
36
  # -----------------------------------------------------------------------------
@@ -52,10 +52,128 @@ class CisLink:
52
52
  handle: int
53
53
  cis_id: int
54
54
  cig_id: int
55
- acl_connection: Optional[Connection] = None
55
+ acl_connection: Connection | None = None
56
56
  data_paths: set[int] = dataclasses.field(default_factory=set)
57
57
 
58
58
 
59
+ # -----------------------------------------------------------------------------
60
+ @dataclasses.dataclass
61
+ class LegacyAdvertiser:
62
+ controller: Controller
63
+ advertising_interval_min: int = 0
64
+ advertising_interval_max: int = 0
65
+ advertising_type: int = 0
66
+ own_address_type: int = 0
67
+ peer_address_type: int = 0
68
+ peer_address: hci.Address = hci.Address.ANY
69
+ advertising_channel_map: int = 0
70
+ advertising_filter_policy: int = 0
71
+
72
+ advertising_data: bytes = b''
73
+ scan_response_data: bytes = b''
74
+
75
+ enabled: bool = False
76
+ timer_handle: asyncio.Handle | None = None
77
+
78
+ @property
79
+ def address(self) -> hci.Address:
80
+ '''Address used in advertising PDU.'''
81
+ if self.own_address_type == hci.Address.PUBLIC_DEVICE_ADDRESS:
82
+ return self.controller.public_address
83
+ else:
84
+ return self.controller.random_address
85
+
86
+ def _on_timer_fired(self) -> None:
87
+ self.send_advertising_data()
88
+ self.timer_handle = asyncio.get_running_loop().call_later(
89
+ self.advertising_interval_min / 1000.0, self._on_timer_fired
90
+ )
91
+
92
+ def start(self) -> None:
93
+ # Stop any ongoing advertising before we start again
94
+ self.stop()
95
+ self.enabled = True
96
+
97
+ # Advertise now
98
+ self.timer_handle = asyncio.get_running_loop().call_soon(self._on_timer_fired)
99
+
100
+ def stop(self) -> None:
101
+ if self.timer_handle is not None:
102
+ self.timer_handle.cancel()
103
+ self.timer_handle = None
104
+ self.enabled = False
105
+
106
+ def send_advertising_data(self) -> None:
107
+ if not self.enabled:
108
+ return
109
+
110
+ if (
111
+ self.advertising_type
112
+ == hci.HCI_LE_Set_Advertising_Parameters_Command.AdvertisingType.ADV_IND
113
+ ):
114
+ self.controller.send_advertising_pdu(
115
+ ll.AdvInd(
116
+ advertiser_address=self.address,
117
+ data=self.advertising_data,
118
+ )
119
+ )
120
+
121
+
122
+ # -----------------------------------------------------------------------------
123
+ @dataclasses.dataclass
124
+ class AdvertisingSet:
125
+ controller: Controller
126
+ handle: int
127
+ parameters: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command | None = None
128
+ data: bytearray = dataclasses.field(default_factory=bytearray)
129
+ scan_response_data: bytearray = dataclasses.field(default_factory=bytearray)
130
+ enabled: bool = False
131
+ timer_handle: asyncio.Handle | None = None
132
+ random_address: hci.Address | None = None
133
+
134
+ @property
135
+ def address(self) -> hci.Address | None:
136
+ '''Address used in advertising PDU.'''
137
+ if not self.parameters:
138
+ return None
139
+ if self.parameters.own_address_type == hci.Address.PUBLIC_DEVICE_ADDRESS:
140
+ return self.controller.public_address
141
+ else:
142
+ return self.random_address
143
+
144
+ def _on_extended_advertising_timer_fired(self) -> None:
145
+ if not self.enabled:
146
+ return
147
+
148
+ self.send_extended_advertising_data()
149
+
150
+ interval = (
151
+ self.parameters.primary_advertising_interval_min * 0.625 / 1000.0
152
+ if self.parameters
153
+ else 1.0
154
+ )
155
+ self.timer_handle = asyncio.get_running_loop().call_later(
156
+ interval, self._on_extended_advertising_timer_fired
157
+ )
158
+
159
+ def start(self) -> None:
160
+ self.enabled = True
161
+ asyncio.get_running_loop().call_soon(self._on_extended_advertising_timer_fired)
162
+
163
+ def stop(self) -> None:
164
+ self.enabled = False
165
+ if timer_handle := self.timer_handle:
166
+ timer_handle.cancel()
167
+ self.timer_handle = None
168
+
169
+ def send_extended_advertising_data(self) -> None:
170
+ if self.controller.link:
171
+ address = self.address
172
+ assert address
173
+
174
+ self.controller.send_advertising_pdu(ll.AdvInd(address, bytes(self.data)))
175
+
176
+
59
177
  # -----------------------------------------------------------------------------
60
178
  @dataclasses.dataclass
61
179
  class ScoLink:
@@ -70,9 +188,10 @@ class Connection:
70
188
  controller: Controller
71
189
  handle: int
72
190
  role: hci.Role
191
+ self_address: hci.Address
73
192
  peer_address: hci.Address
74
- link: Any
75
- transport: int
193
+ link: link.LocalLink
194
+ transport: PhysicalTransport
76
195
  link_type: int
77
196
  classic_allow_role_switch: bool = False
78
197
 
@@ -93,22 +212,27 @@ class Connection:
93
212
  self.controller, self.peer_address, self.transport, pdu
94
213
  )
95
214
 
215
+ def send_ll_control_pdu(self, packet: ll.ControlPdu) -> None:
216
+ if self.link:
217
+ self.link.send_ll_control_pdu(
218
+ sender_address=self.self_address,
219
+ receiver_address=self.peer_address,
220
+ packet=packet,
221
+ )
222
+
96
223
 
97
224
  # -----------------------------------------------------------------------------
98
225
  class Controller:
99
- hci_sink: Optional[TransportSink] = None
100
-
101
- central_connections: dict[
102
- hci.Address, Connection
103
- ] # Connections where this controller is the central
104
- peripheral_connections: dict[
105
- hci.Address, Connection
106
- ] # Connections where this controller is the peripheral
226
+ hci_sink: TransportSink | None = None
227
+
228
+ le_connections: dict[hci.Address, Connection] # LE Connections
107
229
  classic_connections: dict[hci.Address, Connection] # Connections in BR/EDR
108
230
  classic_pending_commands: dict[hci.Address, dict[lmp.Opcode, asyncio.Future[int]]]
109
231
  sco_links: dict[hci.Address, ScoLink] # SCO links by address
110
232
  central_cis_links: dict[int, CisLink] # CIS links by handle
111
233
  peripheral_cis_links: dict[int, CisLink] # CIS links by handle
234
+ advertising_sets: dict[int, AdvertisingSet] # Advertising sets by handle
235
+ le_legacy_advertiser: LegacyAdvertiser
112
236
 
113
237
  hci_version: int = hci.HCI_VERSION_BLUETOOTH_CORE_5_0
114
238
  hci_revision: int = 0
@@ -131,10 +255,20 @@ class Controller:
131
255
  '30f0f9ff01008004002000000000000000000000000000000000000000000000'
132
256
  )
133
257
  le_event_mask: int = 0
134
- advertising_parameters: Optional[hci.HCI_LE_Set_Advertising_Parameters_Command] = (
135
- None
258
+ le_features: hci.LeFeatureMask = (
259
+ hci.LeFeatureMask.LE_ENCRYPTION
260
+ | hci.LeFeatureMask.CONNECTION_PARAMETERS_REQUEST_PROCEDURE
261
+ | hci.LeFeatureMask.EXTENDED_REJECT_INDICATION
262
+ | hci.LeFeatureMask.PERIPHERAL_INITIATED_FEATURE_EXCHANGE
263
+ | hci.LeFeatureMask.LE_PING
264
+ | hci.LeFeatureMask.LE_DATA_PACKET_LENGTH_EXTENSION
265
+ | hci.LeFeatureMask.LL_PRIVACY
266
+ | hci.LeFeatureMask.EXTENDED_SCANNER_FILTER_POLICIES
267
+ | hci.LeFeatureMask.LE_2M_PHY
268
+ | hci.LeFeatureMask.LE_CODED_PHY
269
+ | hci.LeFeatureMask.CHANNEL_SELECTION_ALGORITHM_2
270
+ | hci.LeFeatureMask.MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE
136
271
  )
137
- le_features: bytes = bytes.fromhex('ff49010000000000')
138
272
  le_states: bytes = bytes.fromhex('ffff3fffff030000')
139
273
  advertising_channel_tx_power: int = 0
140
274
  filter_accept_list_size: int = 8
@@ -150,19 +284,23 @@ class Controller:
150
284
  le_scan_type: int = 0
151
285
  le_scan_interval: int = 0x10
152
286
  le_scan_window: int = 0x10
153
- le_scan_enable: int = 0
287
+ le_scan_enable: bool = False
154
288
  le_scan_own_address_type: int = hci.Address.RANDOM_DEVICE_ADDRESS
155
289
  le_scanning_filter_policy: int = 0
156
- le_scan_response_data: Optional[bytes] = None
157
290
  le_address_resolution: bool = False
158
291
  le_rpa_timeout: int = 0
159
292
  sync_flow_control: bool = False
160
293
  local_name: str = 'Bumble'
161
294
  advertising_interval: int = 2000
162
- advertising_data: Optional[bytes] = None
163
- advertising_timer_handle: Optional[asyncio.Handle] = None
295
+ advertising_data: bytes | None = None
296
+ advertising_timer_handle: asyncio.Handle | None = None
164
297
  classic_scan_enable: int = 0
165
298
  classic_allow_role_switch: bool = True
299
+ pending_le_connection: (
300
+ hci.HCI_LE_Create_Connection_Command
301
+ | hci.HCI_LE_Extended_Create_Connection_Command
302
+ | None
303
+ ) = None
166
304
 
167
305
  _random_address: hci.Address = hci.Address('00:00:00:00:00:00')
168
306
 
@@ -170,24 +308,25 @@ class Controller:
170
308
  self,
171
309
  name: str,
172
310
  host_source=None,
173
- host_sink: Optional[TransportSink] = None,
174
- link: Optional[LocalLink] = None,
175
- public_address: Optional[Union[bytes, str, hci.Address]] = None,
311
+ host_sink: TransportSink | None = None,
312
+ link: link.LocalLink | None = None,
313
+ public_address: bytes | str | hci.Address | None = None,
176
314
  ) -> None:
177
315
  self.name = name
178
- self.link = link
179
- self.central_connections = {}
180
- self.peripheral_connections = {}
316
+ self.link = link or bumble_link.LocalLink()
317
+ self.le_connections = {}
181
318
  self.classic_connections = {}
182
319
  self.sco_links = {}
183
320
  self.classic_pending_commands = {}
184
321
  self.central_cis_links = {}
185
322
  self.peripheral_cis_links = {}
323
+ self.advertising_sets = {}
186
324
  self.default_phy = {
187
325
  'all_phys': 0,
188
326
  'tx_phys': 0,
189
327
  'rx_phys': 0,
190
328
  }
329
+ self.le_legacy_advertiser = LegacyAdvertiser(self)
191
330
 
192
331
  if isinstance(public_address, hci.Address):
193
332
  self._public_address = public_address
@@ -212,18 +351,18 @@ class Controller:
212
351
  )
213
352
 
214
353
  @property
215
- def host(self) -> Optional[TransportSink]:
354
+ def host(self) -> TransportSink | None:
216
355
  return self.hci_sink
217
356
 
218
357
  @host.setter
219
- def host(self, host: Optional[TransportSink]) -> None:
358
+ def host(self, host: TransportSink | None) -> None:
220
359
  '''
221
360
  Sets the host (sink) for this controller, and set this controller as the
222
361
  controller (sink) for the host
223
362
  '''
224
363
  self.set_packet_sink(host)
225
364
 
226
- def set_packet_sink(self, sink: Optional[TransportSink]) -> None:
365
+ def set_packet_sink(self, sink: TransportSink | None) -> None:
227
366
  '''
228
367
  Method from the Packet Source interface
229
368
  '''
@@ -234,7 +373,7 @@ class Controller:
234
373
  return self._public_address
235
374
 
236
375
  @public_address.setter
237
- def public_address(self, address: Union[hci.Address, str]) -> None:
376
+ def public_address(self, address: hci.Address | str) -> None:
238
377
  if isinstance(address, str):
239
378
  address = hci.Address(address)
240
379
  self._public_address = address
@@ -244,7 +383,7 @@ class Controller:
244
383
  return self._random_address
245
384
 
246
385
  @random_address.setter
247
- def random_address(self, address: Union[hci.Address, str]) -> None:
386
+ def random_address(self, address: hci.Address | str) -> None:
248
387
  if isinstance(address, str):
249
388
  address = hci.Address(address)
250
389
  self._random_address = address
@@ -276,7 +415,7 @@ class Controller:
276
415
  def on_hci_command_packet(self, command: hci.HCI_Command) -> None:
277
416
  handler_name = f'on_{command.name.lower()}'
278
417
  handler = getattr(self, handler_name, self.on_hci_command)
279
- result: Optional[bytes] = handler(command)
418
+ result: bytes | None = handler(command)
280
419
  if isinstance(result, bytes):
281
420
  self.send_hci_packet(
282
421
  hci.HCI_Command_Complete_Event(
@@ -320,8 +459,7 @@ class Controller:
320
459
  current_handles = set(
321
460
  cast(Connection | CisLink | ScoLink, link).handle
322
461
  for link in itertools.chain(
323
- self.central_connections.values(),
324
- self.peripheral_connections.values(),
462
+ self.le_connections.values(),
325
463
  self.classic_connections.values(),
326
464
  self.sco_links.values(),
327
465
  self.central_cis_links.values(),
@@ -329,82 +467,106 @@ class Controller:
329
467
  )
330
468
  )
331
469
  return next(
332
- handle for handle in range(0xEFF + 1) if handle not in current_handles
333
- )
334
-
335
- def find_le_connection_by_address(
336
- self, address: hci.Address
337
- ) -> Optional[Connection]:
338
- return self.central_connections.get(address) or self.peripheral_connections.get(
339
- address
470
+ handle
471
+ for handle in range(0x0001, 0xEFF + 1)
472
+ if handle not in current_handles
340
473
  )
341
474
 
342
- def find_classic_connection_by_address(
343
- self, address: hci.Address
344
- ) -> Optional[Connection]:
345
- return self.classic_connections.get(address)
346
-
347
- def find_connection_by_handle(self, handle: int) -> Optional[Connection]:
475
+ def find_connection_by_handle(self, handle: int) -> Connection | None:
348
476
  for connection in itertools.chain(
349
- self.central_connections.values(),
350
- self.peripheral_connections.values(),
477
+ self.le_connections.values(),
351
478
  self.classic_connections.values(),
352
479
  ):
353
480
  if connection.handle == handle:
354
481
  return connection
355
482
  return None
356
483
 
357
- def find_central_connection_by_handle(self, handle: int) -> Optional[Connection]:
358
- for connection in self.central_connections.values():
359
- if connection.handle == handle:
360
- return connection
361
- return None
362
-
363
- def find_peripheral_connection_by_handle(self, handle: int) -> Optional[Connection]:
364
- for connection in self.peripheral_connections.values():
365
- if connection.handle == handle:
366
- return connection
367
- return None
368
-
369
- def find_classic_connection_by_handle(self, handle: int) -> Optional[Connection]:
370
- for connection in self.classic_connections.values():
371
- if connection.handle == handle:
372
- return connection
373
- return None
374
-
375
- def find_classic_sco_link_by_handle(self, handle: int) -> Optional[ScoLink]:
484
+ def find_classic_sco_link_by_handle(self, handle: int) -> ScoLink | None:
376
485
  for connection in self.sco_links.values():
377
486
  if connection.handle == handle:
378
487
  return connection
379
488
  return None
380
489
 
381
- def find_iso_link_by_handle(self, handle: int) -> Optional[CisLink]:
490
+ def find_iso_link_by_handle(self, handle: int) -> CisLink | None:
382
491
  return self.central_cis_links.get(handle) or self.peripheral_cis_links.get(
383
492
  handle
384
493
  )
385
494
 
386
- def on_link_central_connected(self, central_address: hci.Address) -> None:
495
+ def send_advertising_pdu(self, packet: ll.AdvertisingPdu) -> None:
496
+ logger.debug("[%s] >>> Advertising PDU: %s", self.name, packet)
497
+ if self.link:
498
+ self.link.send_advertising_pdu(self, packet)
499
+
500
+ def on_ll_control_pdu(
501
+ self, sender_address: hci.Address, packet: ll.ControlPdu
502
+ ) -> None:
503
+ logger.debug("[%s] >>> LL Control PDU: %s", self.name, packet)
504
+ if not (connection := self.le_connections.get(sender_address)):
505
+ logger.error("Cannot find a connection for %s", sender_address)
506
+ return
507
+
508
+ if isinstance(packet, ll.TerminateInd):
509
+ self.on_le_disconnected(connection, packet.error_code)
510
+ elif isinstance(packet, ll.CisReq):
511
+ self.on_le_cis_request(connection, packet.cig_id, packet.cis_id)
512
+ elif isinstance(packet, ll.CisRsp):
513
+ self.on_le_cis_established(packet.cig_id, packet.cis_id)
514
+ connection.send_ll_control_pdu(ll.CisInd(packet.cig_id, packet.cis_id))
515
+ elif isinstance(packet, ll.CisInd):
516
+ self.on_le_cis_established(packet.cig_id, packet.cis_id)
517
+ elif isinstance(packet, ll.CisTerminateInd):
518
+ self.on_le_cis_disconnected(packet.cig_id, packet.cis_id)
519
+ elif isinstance(packet, ll.EncReq):
520
+ self.on_le_encrypted(connection)
521
+
522
+ def on_ll_advertising_pdu(self, packet: ll.AdvertisingPdu) -> None:
523
+ logger.debug("[%s] <<< Advertising PDU: %s", self.name, packet)
524
+ if isinstance(packet, ll.ConnectInd):
525
+ self.on_le_connect_ind(packet)
526
+ elif isinstance(packet, (ll.AdvInd, ll.AdvExtInd)):
527
+ self.on_advertising_pdu(packet)
528
+
529
+ def on_le_connect_ind(self, packet: ll.ConnectInd) -> None:
387
530
  '''
388
531
  Called when an incoming connection occurs from a central on the link
389
532
  '''
533
+ advertiser: LegacyAdvertiser | AdvertisingSet | None
534
+ if (
535
+ self.le_legacy_advertiser.address == packet.advertiser_address
536
+ and self.le_legacy_advertiser.enabled
537
+ ):
538
+ advertiser = self.le_legacy_advertiser
539
+ else:
540
+ advertiser = next(
541
+ (
542
+ advertising_set
543
+ for advertising_set in self.advertising_sets.values()
544
+ if advertising_set.address == packet.advertiser_address
545
+ and advertising_set.enabled
546
+ ),
547
+ None,
548
+ )
549
+
550
+ if not advertiser:
551
+ # This is not send to us.
552
+ return
390
553
 
391
554
  # Allocate (or reuse) a connection handle
392
- peer_address = central_address
393
- peer_address_type = central_address.address_type
394
- connection = self.peripheral_connections.get(peer_address)
395
- if connection is None:
396
- connection_handle = self.allocate_connection_handle()
397
- connection = Connection(
398
- controller=self,
399
- handle=connection_handle,
400
- role=hci.Role.PERIPHERAL,
401
- peer_address=peer_address,
402
- link=self.link,
403
- transport=PhysicalTransport.LE,
404
- link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
405
- )
406
- self.peripheral_connections[peer_address] = connection
407
- logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}')
555
+ peer_address = packet.initiator_address
556
+
557
+ connection_handle = self.allocate_connection_handle()
558
+ connection = Connection(
559
+ controller=self,
560
+ handle=connection_handle,
561
+ role=hci.Role.PERIPHERAL,
562
+ self_address=packet.advertiser_address,
563
+ peer_address=peer_address,
564
+ link=self.link,
565
+ transport=PhysicalTransport.LE,
566
+ link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
567
+ )
568
+ self.le_connections[peer_address] = connection
569
+ logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}')
408
570
 
409
571
  # Then say that the connection has completed
410
572
  self.send_hci_packet(
@@ -412,7 +574,7 @@ class Controller:
412
574
  status=hci.HCI_SUCCESS,
413
575
  connection_handle=connection.handle,
414
576
  role=connection.role,
415
- peer_address_type=peer_address_type,
577
+ peer_address_type=peer_address.address_type,
416
578
  peer_address=peer_address,
417
579
  connection_interval=10, # FIXME
418
580
  peripheral_latency=0, # FIXME
@@ -421,131 +583,113 @@ class Controller:
421
583
  )
422
584
  )
423
585
 
424
- def on_link_disconnected(self, peer_address: hci.Address, reason: int) -> None:
425
- '''
426
- Called when an active disconnection occurs from a peer
427
- '''
428
-
429
- # Send a disconnection complete event
430
- if connection := self.peripheral_connections.get(peer_address):
586
+ if isinstance(advertiser, AdvertisingSet):
431
587
  self.send_hci_packet(
432
- hci.HCI_Disconnection_Complete_Event(
588
+ hci.HCI_LE_Advertising_Set_Terminated_Event(
433
589
  status=hci.HCI_SUCCESS,
590
+ advertising_handle=advertiser.handle,
434
591
  connection_handle=connection.handle,
435
- reason=reason,
592
+ num_completed_extended_advertising_events=0,
436
593
  )
437
594
  )
595
+ advertiser.stop()
438
596
 
439
- # Remove the connection
440
- del self.peripheral_connections[peer_address]
441
- elif connection := self.central_connections.get(peer_address):
442
- self.send_hci_packet(
443
- hci.HCI_Disconnection_Complete_Event(
444
- status=hci.HCI_SUCCESS,
445
- connection_handle=connection.handle,
446
- reason=reason,
447
- )
597
+ def on_le_disconnected(self, connection: Connection, reason: int) -> None:
598
+ # Send a disconnection complete event
599
+ self.send_hci_packet(
600
+ hci.HCI_Disconnection_Complete_Event(
601
+ status=hci.HCI_SUCCESS,
602
+ connection_handle=connection.handle,
603
+ reason=reason,
448
604
  )
605
+ )
449
606
 
450
- # Remove the connection
451
- del self.central_connections[peer_address]
452
- else:
453
- logger.warning(f'!!! No peripheral connection found for {peer_address}')
454
-
455
- def on_link_peripheral_connection_complete(
456
- self,
457
- le_create_connection_command: hci.HCI_LE_Create_Connection_Command,
458
- status: int,
459
- ) -> None:
607
+ def create_le_connection(self, peer_address: hci.Address) -> None:
460
608
  '''
461
- Called by the link when a connection has been made or has failed to be made
609
+ Called when we receive advertisement matching connection filter.
462
610
  '''
611
+ pending_le_connection = self.pending_le_connection
612
+ assert pending_le_connection
463
613
 
464
- if status == hci.HCI_SUCCESS:
465
- # Allocate (or reuse) a connection handle
466
- peer_address = le_create_connection_command.peer_address
467
- connection = self.central_connections.get(peer_address)
468
- if connection is None:
469
- connection_handle = self.allocate_connection_handle()
470
- connection = Connection(
471
- controller=self,
472
- handle=connection_handle,
473
- role=hci.Role.CENTRAL,
474
- peer_address=peer_address,
475
- link=self.link,
476
- transport=PhysicalTransport.LE,
477
- link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
478
- )
479
- self.central_connections[peer_address] = connection
480
- logger.debug(
481
- f'New CENTRAL connection handle: 0x{connection_handle:04X}'
482
- )
614
+ if self.le_connections.get(peer_address):
615
+ logger.error("Connection for %s already exists?", peer_address)
616
+ return
617
+
618
+ self_address = (
619
+ self.public_address
620
+ if pending_le_connection.own_address_type == hci.OwnAddressType.PUBLIC
621
+ else self.random_address
622
+ )
623
+
624
+ # Allocate (or reuse) a connection handle
625
+ peer_address = pending_le_connection.peer_address
626
+ connection_handle = self.allocate_connection_handle()
627
+ connection = Connection(
628
+ controller=self,
629
+ handle=connection_handle,
630
+ role=hci.Role.CENTRAL,
631
+ self_address=self_address,
632
+ peer_address=peer_address,
633
+ link=self.link,
634
+ transport=PhysicalTransport.LE,
635
+ link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
636
+ )
637
+ self.le_connections[peer_address] = connection
638
+ logger.debug(f'New CENTRAL connection handle: 0x{connection_handle:04X}')
639
+
640
+ if isinstance(
641
+ pending_le_connection, hci.HCI_LE_Extended_Create_Connection_Command
642
+ ):
643
+ interval = pending_le_connection.connection_interval_mins[0]
644
+ latency = pending_le_connection.max_latencies[0]
645
+ timeout = pending_le_connection.supervision_timeouts[0]
483
646
  else:
484
- connection = None
647
+ interval = pending_le_connection.connection_interval_min
648
+ latency = pending_le_connection.max_latency
649
+ timeout = pending_le_connection.supervision_timeout
485
650
 
651
+ self.send_advertising_pdu(
652
+ ll.ConnectInd(
653
+ initiator_address=self_address,
654
+ advertiser_address=peer_address,
655
+ interval=interval,
656
+ latency=latency,
657
+ timeout=timeout,
658
+ )
659
+ )
486
660
  # Say that the connection has completed
487
661
  self.send_hci_packet(
488
662
  # pylint: disable=line-too-long
489
663
  hci.HCI_LE_Connection_Complete_Event(
490
- status=status,
664
+ status=hci.HCI_SUCCESS,
491
665
  connection_handle=connection.handle if connection else 0,
492
666
  role=hci.Role.CENTRAL,
493
- peer_address_type=le_create_connection_command.peer_address_type,
494
- peer_address=le_create_connection_command.peer_address,
495
- connection_interval=le_create_connection_command.connection_interval_min,
496
- peripheral_latency=le_create_connection_command.max_latency,
497
- supervision_timeout=le_create_connection_command.supervision_timeout,
667
+ peer_address_type=peer_address.address_type,
668
+ peer_address=peer_address,
669
+ connection_interval=interval,
670
+ peripheral_latency=latency,
671
+ supervision_timeout=timeout,
498
672
  central_clock_accuracy=0,
499
673
  )
500
674
  )
675
+ self.pending_le_connection = None
501
676
 
502
- def on_link_disconnection_complete(
503
- self, disconnection_command: hci.HCI_Disconnect_Command, status: int
504
- ) -> None:
505
- '''
506
- Called when a disconnection has been completed
507
- '''
508
-
509
- # Send a disconnection complete event
677
+ def on_le_encrypted(self, connection: Connection) -> None:
678
+ # For now, just setup the encryption without asking the host
510
679
  self.send_hci_packet(
511
- hci.HCI_Disconnection_Complete_Event(
512
- status=status,
513
- connection_handle=disconnection_command.connection_handle,
514
- reason=disconnection_command.reason,
680
+ hci.HCI_Encryption_Change_Event(
681
+ status=0, connection_handle=connection.handle, encryption_enabled=1
515
682
  )
516
683
  )
517
684
 
518
- # Remove the connection
519
- if connection := self.find_central_connection_by_handle(
520
- disconnection_command.connection_handle
521
- ):
522
- logger.debug(f'CENTRAL Connection removed: {connection}')
523
- del self.central_connections[connection.peer_address]
524
- elif connection := self.find_peripheral_connection_by_handle(
525
- disconnection_command.connection_handle
526
- ):
527
- logger.debug(f'PERIPHERAL Connection removed: {connection}')
528
- del self.peripheral_connections[connection.peer_address]
529
-
530
- def on_link_encrypted(
531
- self, peer_address: hci.Address, _rand: bytes, _ediv: int, _ltk: bytes
532
- ) -> None:
533
- # For now, just setup the encryption without asking the host
534
- if connection := self.find_le_connection_by_address(peer_address):
535
- self.send_hci_packet(
536
- hci.HCI_Encryption_Change_Event(
537
- status=0, connection_handle=connection.handle, encryption_enabled=1
538
- )
539
- )
540
-
541
685
  def on_link_acl_data(
542
686
  self, sender_address: hci.Address, transport: PhysicalTransport, data: bytes
543
687
  ) -> None:
544
688
  # Look for the connection to which this data belongs
545
689
  if transport == PhysicalTransport.LE:
546
- connection = self.find_le_connection_by_address(sender_address)
690
+ connection = self.le_connections.get(sender_address)
547
691
  else:
548
- connection = self.find_classic_connection_by_address(sender_address)
692
+ connection = self.classic_connections.get(sender_address)
549
693
  if connection is None:
550
694
  logger.warning(f'!!! no connection for {sender_address}')
551
695
  return
@@ -555,43 +699,83 @@ class Controller:
555
699
  acl_packet = hci.HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
556
700
  self.send_hci_packet(acl_packet)
557
701
 
558
- def on_link_advertising_data(
559
- self, sender_address: hci.Address, data: bytes
560
- ) -> None:
561
- # Ignore if we're not scanning
562
- if self.le_scan_enable == 0:
563
- return
702
+ def on_advertising_pdu(self, pdu: ll.AdvInd | ll.AdvExtInd) -> None:
703
+ if isinstance(pdu, ll.AdvExtInd):
704
+ direct_address = pdu.target_address
705
+ else:
706
+ direct_address = None
564
707
 
565
- # Send a scan report
566
- report = hci.HCI_LE_Advertising_Report_Event.Report(
567
- event_type=hci.HCI_LE_Advertising_Report_Event.EventType.ADV_IND,
568
- address_type=sender_address.address_type,
569
- address=sender_address,
570
- data=data,
571
- rssi=-50,
572
- )
573
- self.send_hci_packet(hci.HCI_LE_Advertising_Report_Event([report]))
708
+ if self.le_scan_enable:
709
+ # Send a scan report
710
+ if self.le_features & hci.LeFeatureMask.LE_EXTENDED_ADVERTISING:
711
+ ext_report = hci.HCI_LE_Extended_Advertising_Report_Event.Report(
712
+ event_type=hci.HCI_LE_Extended_Advertising_Report_Event.EventType.CONNECTABLE_ADVERTISING,
713
+ address_type=pdu.advertiser_address.address_type,
714
+ address=pdu.advertiser_address,
715
+ primary_phy=hci.Phy.LE_1M,
716
+ secondary_phy=hci.Phy.LE_1M,
717
+ advertising_sid=0,
718
+ tx_power=0,
719
+ rssi=-50,
720
+ periodic_advertising_interval=0,
721
+ direct_address_type=(
722
+ direct_address.address_type if direct_address else 0
723
+ ),
724
+ direct_address=direct_address or hci.Address.ANY,
725
+ data=pdu.data,
726
+ )
727
+ self.send_hci_packet(
728
+ hci.HCI_LE_Extended_Advertising_Report_Event([ext_report])
729
+ )
730
+ ext_report = hci.HCI_LE_Extended_Advertising_Report_Event.Report(
731
+ event_type=hci.HCI_LE_Extended_Advertising_Report_Event.EventType.SCAN_RESPONSE,
732
+ address_type=pdu.advertiser_address.address_type,
733
+ address=pdu.advertiser_address,
734
+ primary_phy=hci.Phy.LE_1M,
735
+ secondary_phy=hci.Phy.LE_1M,
736
+ advertising_sid=0,
737
+ tx_power=0,
738
+ rssi=-50,
739
+ periodic_advertising_interval=0,
740
+ direct_address_type=(
741
+ direct_address.address_type if direct_address else 0
742
+ ),
743
+ direct_address=direct_address or hci.Address.ANY,
744
+ data=pdu.data,
745
+ )
746
+ self.send_hci_packet(
747
+ hci.HCI_LE_Extended_Advertising_Report_Event([ext_report])
748
+ )
749
+ else:
750
+ report = hci.HCI_LE_Advertising_Report_Event.Report(
751
+ event_type=hci.HCI_LE_Advertising_Report_Event.EventType.ADV_IND,
752
+ address_type=pdu.advertiser_address.address_type,
753
+ address=pdu.advertiser_address,
754
+ data=pdu.data,
755
+ rssi=-50,
756
+ )
757
+ self.send_hci_packet(hci.HCI_LE_Advertising_Report_Event([report]))
758
+ report = hci.HCI_LE_Advertising_Report_Event.Report(
759
+ event_type=hci.HCI_LE_Advertising_Report_Event.EventType.SCAN_RSP,
760
+ address_type=pdu.advertiser_address.address_type,
761
+ address=pdu.advertiser_address,
762
+ data=pdu.data,
763
+ rssi=-50,
764
+ )
765
+ self.send_hci_packet(hci.HCI_LE_Advertising_Report_Event([report]))
574
766
 
575
- # Simulate a scan response
576
- report = hci.HCI_LE_Advertising_Report_Event.Report(
577
- event_type=hci.HCI_LE_Advertising_Report_Event.EventType.SCAN_RSP,
578
- address_type=sender_address.address_type,
579
- address=sender_address,
580
- data=data,
581
- rssi=-50,
582
- )
583
- self.send_hci_packet(hci.HCI_LE_Advertising_Report_Event([report]))
767
+ # Create connection.
768
+ if (
769
+ pending_le_connection := self.pending_le_connection
770
+ ) and pending_le_connection.peer_address == pdu.advertiser_address:
771
+ self.create_le_connection(pdu.advertiser_address)
584
772
 
585
- def on_link_cis_request(
586
- self, central_address: hci.Address, cig_id: int, cis_id: int
773
+ def on_le_cis_request(
774
+ self, connection: Connection, cig_id: int, cis_id: int
587
775
  ) -> None:
588
776
  '''
589
777
  Called when an incoming CIS request occurs from a central on the link
590
778
  '''
591
-
592
- connection = self.peripheral_connections.get(central_address)
593
- assert connection
594
-
595
779
  pending_cis_link = CisLink(
596
780
  handle=self.allocate_connection_handle(),
597
781
  cis_id=cis_id,
@@ -609,7 +793,7 @@ class Controller:
609
793
  )
610
794
  )
611
795
 
612
- def on_link_cis_established(self, cig_id: int, cis_id: int) -> None:
796
+ def on_le_cis_established(self, cig_id: int, cis_id: int) -> None:
613
797
  '''
614
798
  Called when an incoming CIS established.
615
799
  '''
@@ -644,7 +828,7 @@ class Controller:
644
828
  )
645
829
  )
646
830
 
647
- def on_link_cis_disconnected(self, cig_id: int, cis_id: int) -> None:
831
+ def on_le_cis_disconnected(self, cig_id: int, cis_id: int) -> None:
648
832
  '''
649
833
  Called when a CIS disconnected.
650
834
  '''
@@ -750,6 +934,7 @@ class Controller:
750
934
  controller=self,
751
935
  handle=0,
752
936
  role=hci.Role.PERIPHERAL,
937
+ self_address=self.public_address,
753
938
  peer_address=peer_address,
754
939
  link=self.link,
755
940
  transport=PhysicalTransport.BR_EDR,
@@ -784,6 +969,7 @@ class Controller:
784
969
  controller=self,
785
970
  handle=connection_handle,
786
971
  role=hci.Role.CENTRAL,
972
+ self_address=self.public_address,
787
973
  peer_address=peer_address,
788
974
  link=self.link,
789
975
  transport=PhysicalTransport.BR_EDR,
@@ -934,44 +1120,23 @@ class Controller:
934
1120
  ############################################################
935
1121
  # Advertising support
936
1122
  ############################################################
937
- def on_advertising_timer_fired(self) -> None:
938
- self.send_advertising_data()
939
- self.advertising_timer_handle = asyncio.get_running_loop().call_later(
940
- self.advertising_interval / 1000.0, self.on_advertising_timer_fired
941
- )
942
-
943
- def start_advertising(self) -> None:
944
- # Stop any ongoing advertising before we start again
945
- self.stop_advertising()
946
-
947
- # Advertise now
948
- self.advertising_timer_handle = asyncio.get_running_loop().call_soon(
949
- self.on_advertising_timer_fired
950
- )
951
-
952
- def stop_advertising(self) -> None:
953
- if self.advertising_timer_handle is not None:
954
- self.advertising_timer_handle.cancel()
955
- self.advertising_timer_handle = None
956
-
957
- def send_advertising_data(self) -> None:
958
- if self.link and self.advertising_data:
959
- self.link.send_advertising_data(self.random_address, self.advertising_data)
960
1123
 
961
1124
  @property
962
1125
  def is_advertising(self) -> bool:
963
- return self.advertising_timer_handle is not None
1126
+ return self.le_legacy_advertiser.enabled or any(
1127
+ s.enabled for s in self.advertising_sets.values()
1128
+ )
964
1129
 
965
1130
  ############################################################
966
1131
  # HCI handlers
967
1132
  ############################################################
968
- def on_hci_command(self, command: hci.HCI_Command) -> Optional[bytes]:
1133
+ def on_hci_command(self, command: hci.HCI_Command) -> bytes | None:
969
1134
  logger.warning(color(f'--- Unsupported command {command}', 'red'))
970
1135
  return bytes([hci.HCI_UNKNOWN_HCI_COMMAND_ERROR])
971
1136
 
972
1137
  def on_hci_create_connection_command(
973
1138
  self, command: hci.HCI_Create_Connection_Command
974
- ) -> Optional[bytes]:
1139
+ ) -> bytes | None:
975
1140
  '''
976
1141
  See Bluetooth spec Vol 4, Part E - 7.1.5 Create Connection command
977
1142
  '''
@@ -981,7 +1146,7 @@ class Controller:
981
1146
  logger.debug(f'Connection request to {command.bd_addr}')
982
1147
 
983
1148
  # Check that we don't already have a pending connection
984
- if self.link.get_pending_connection():
1149
+ if self.pending_le_connection:
985
1150
  self.send_hci_packet(
986
1151
  hci.HCI_Command_Status_Event(
987
1152
  status=hci.HCI_CONTROLLER_BUSY_ERROR,
@@ -995,6 +1160,7 @@ class Controller:
995
1160
  controller=self,
996
1161
  handle=0,
997
1162
  role=hci.Role.CENTRAL,
1163
+ self_address=self.public_address,
998
1164
  peer_address=command.bd_addr,
999
1165
  link=self.link,
1000
1166
  transport=PhysicalTransport.BR_EDR,
@@ -1020,7 +1186,7 @@ class Controller:
1020
1186
 
1021
1187
  def on_hci_disconnect_command(
1022
1188
  self, command: hci.HCI_Disconnect_Command
1023
- ) -> Optional[bytes]:
1189
+ ) -> bytes | None:
1024
1190
  '''
1025
1191
  See Bluetooth spec Vol 4, Part E - 7.1.6 Disconnect Command
1026
1192
  '''
@@ -1035,29 +1201,19 @@ class Controller:
1035
1201
 
1036
1202
  # Notify the link of the disconnection
1037
1203
  handle = command.connection_handle
1038
- if connection := self.find_central_connection_by_handle(handle):
1039
- if self.link:
1040
- self.link.disconnect(
1041
- self.random_address, connection.peer_address, command
1042
- )
1043
- else:
1044
- # Remove the connection
1045
- del self.central_connections[connection.peer_address]
1046
- elif connection := self.find_peripheral_connection_by_handle(handle):
1204
+ if connection := self.find_connection_by_handle(handle):
1047
1205
  if self.link:
1048
- self.link.disconnect(
1049
- self.random_address, connection.peer_address, command
1050
- )
1051
- else:
1052
- # Remove the connection
1053
- del self.peripheral_connections[connection.peer_address]
1054
- elif connection := self.find_classic_connection_by_handle(handle):
1055
- if self.link:
1056
- self.send_lmp_packet(
1057
- connection.peer_address,
1058
- lmp.LmpDetach(command.reason),
1059
- )
1060
- self.on_classic_disconnected(connection.peer_address, command.reason)
1206
+ if connection.transport == PhysicalTransport.BR_EDR:
1207
+ self.send_lmp_packet(
1208
+ connection.peer_address,
1209
+ lmp.LmpDetach(command.reason),
1210
+ )
1211
+ self.on_classic_disconnected(
1212
+ connection.peer_address, command.reason
1213
+ )
1214
+ else:
1215
+ connection.send_ll_control_pdu(ll.TerminateInd(command.reason))
1216
+ self.on_le_disconnected(connection, command.reason)
1061
1217
  else:
1062
1218
  # Remove the connection
1063
1219
  del self.classic_connections[connection.peer_address]
@@ -1088,19 +1244,19 @@ class Controller:
1088
1244
  self.central_cis_links.get(handle) or self.peripheral_cis_links.get(handle)
1089
1245
  ):
1090
1246
  if self.link and cis_link.acl_connection:
1091
- self.link.disconnect_cis(
1092
- initiator_controller=self,
1093
- peer_address=cis_link.acl_connection.peer_address,
1094
- cig_id=cis_link.cig_id,
1095
- cis_id=cis_link.cis_id,
1247
+ cis_link.acl_connection.send_ll_control_pdu(
1248
+ ll.CisTerminateInd(
1249
+ cis_link.cig_id, cis_link.cis_id, command.reason
1250
+ ),
1096
1251
  )
1252
+ self.on_le_cis_disconnected(cis_link.cig_id, cis_link.cis_id)
1097
1253
  # Spec requires handle to be kept after disconnection.
1098
1254
 
1099
1255
  return None
1100
1256
 
1101
1257
  def on_hci_accept_connection_request_command(
1102
1258
  self, command: hci.HCI_Accept_Connection_Request_Command
1103
- ) -> Optional[bytes]:
1259
+ ) -> bytes | None:
1104
1260
  '''
1105
1261
  See Bluetooth spec Vol 4, Part E - 7.1.8 Accept Connection Request command
1106
1262
  '''
@@ -1158,7 +1314,7 @@ class Controller:
1158
1314
 
1159
1315
  def on_hci_remote_name_request_command(
1160
1316
  self, command: hci.HCI_Remote_Name_Request_Command
1161
- ) -> Optional[bytes]:
1317
+ ) -> bytes | None:
1162
1318
  '''
1163
1319
  See Bluetooth spec Vol 4, Part E - 7.1.19 Remote Name Request command
1164
1320
  '''
@@ -1176,7 +1332,7 @@ class Controller:
1176
1332
 
1177
1333
  def on_hci_enhanced_setup_synchronous_connection_command(
1178
1334
  self, command: hci.HCI_Enhanced_Setup_Synchronous_Connection_Command
1179
- ) -> Optional[bytes]:
1335
+ ) -> bytes | None:
1180
1336
  '''
1181
1337
  See Bluetooth spec Vol 4, Part E - 7.1.45 Enhanced Setup Synchronous Connection command
1182
1338
  '''
@@ -1185,9 +1341,7 @@ class Controller:
1185
1341
  return None
1186
1342
 
1187
1343
  if not (
1188
- connection := self.find_classic_connection_by_handle(
1189
- command.connection_handle
1190
- )
1344
+ connection := self.find_connection_by_handle(command.connection_handle)
1191
1345
  ):
1192
1346
  self.send_hci_packet(
1193
1347
  hci.HCI_Command_Status_Event(
@@ -1235,7 +1389,7 @@ class Controller:
1235
1389
 
1236
1390
  def on_hci_enhanced_accept_synchronous_connection_request_command(
1237
1391
  self, command: hci.HCI_Enhanced_Accept_Synchronous_Connection_Request_Command
1238
- ) -> Optional[bytes]:
1392
+ ) -> bytes | None:
1239
1393
  '''
1240
1394
  See Bluetooth spec Vol 4, Part E - 7.1.46 Enhanced Accept Synchronous Connection Request command
1241
1395
  '''
@@ -1243,7 +1397,7 @@ class Controller:
1243
1397
  if self.link is None:
1244
1398
  return None
1245
1399
 
1246
- if not (connection := self.find_classic_connection_by_address(command.bd_addr)):
1400
+ if not (connection := self.classic_connections.get(command.bd_addr)):
1247
1401
  self.send_hci_packet(
1248
1402
  hci.HCI_Command_Status_Event(
1249
1403
  status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
@@ -1273,7 +1427,7 @@ class Controller:
1273
1427
 
1274
1428
  def on_hci_sniff_mode_command(
1275
1429
  self, command: hci.HCI_Sniff_Mode_Command
1276
- ) -> Optional[bytes]:
1430
+ ) -> bytes | None:
1277
1431
  '''
1278
1432
  See Bluetooth spec Vol 4, Part E - 7.2.2 Sniff Mode command
1279
1433
  '''
@@ -1306,7 +1460,7 @@ class Controller:
1306
1460
 
1307
1461
  def on_hci_exit_sniff_mode_command(
1308
1462
  self, command: hci.HCI_Exit_Sniff_Mode_Command
1309
- ) -> Optional[bytes]:
1463
+ ) -> bytes | None:
1310
1464
  '''
1311
1465
  See Bluetooth spec Vol 4, Part E - 7.2.3 Exit Sniff Mode command
1312
1466
  '''
@@ -1340,7 +1494,7 @@ class Controller:
1340
1494
 
1341
1495
  def on_hci_switch_role_command(
1342
1496
  self, command: hci.HCI_Switch_Role_Command
1343
- ) -> Optional[bytes]:
1497
+ ) -> bytes | None:
1344
1498
  '''
1345
1499
  See Bluetooth spec Vol 4, Part E - 7.2.8 Switch hci.Role command
1346
1500
  '''
@@ -1397,7 +1551,7 @@ class Controller:
1397
1551
 
1398
1552
  def on_hci_set_event_mask_command(
1399
1553
  self, command: hci.HCI_Set_Event_Mask_Command
1400
- ) -> Optional[bytes]:
1554
+ ) -> bytes | None:
1401
1555
  '''
1402
1556
  See Bluetooth spec Vol 4, Part E - 7.3.1 Set Event Mask Command
1403
1557
  '''
@@ -1406,7 +1560,7 @@ class Controller:
1406
1560
  )
1407
1561
  return bytes([hci.HCI_SUCCESS])
1408
1562
 
1409
- def on_hci_reset_command(self, _command: hci.HCI_Reset_Command) -> Optional[bytes]:
1563
+ def on_hci_reset_command(self, _command: hci.HCI_Reset_Command) -> bytes | None:
1410
1564
  '''
1411
1565
  See Bluetooth spec Vol 4, Part E - 7.3.2 Reset Command
1412
1566
  '''
@@ -1415,7 +1569,7 @@ class Controller:
1415
1569
 
1416
1570
  def on_hci_write_local_name_command(
1417
1571
  self, command: hci.HCI_Write_Local_Name_Command
1418
- ) -> Optional[bytes]:
1572
+ ) -> bytes | None:
1419
1573
  '''
1420
1574
  See Bluetooth spec Vol 4, Part E - 7.3.11 Write Local Name Command
1421
1575
  '''
@@ -1432,7 +1586,7 @@ class Controller:
1432
1586
 
1433
1587
  def on_hci_read_local_name_command(
1434
1588
  self, _command: hci.HCI_Read_Local_Name_Command
1435
- ) -> Optional[bytes]:
1589
+ ) -> bytes | None:
1436
1590
  '''
1437
1591
  See Bluetooth spec Vol 4, Part E - 7.3.12 Read Local Name Command
1438
1592
  '''
@@ -1444,7 +1598,7 @@ class Controller:
1444
1598
 
1445
1599
  def on_hci_read_class_of_device_command(
1446
1600
  self, _command: hci.HCI_Read_Class_Of_Device_Command
1447
- ) -> Optional[bytes]:
1601
+ ) -> bytes | None:
1448
1602
  '''
1449
1603
  See Bluetooth spec Vol 4, Part E - 7.3.25 Read Class of Device Command
1450
1604
  '''
@@ -1452,7 +1606,7 @@ class Controller:
1452
1606
 
1453
1607
  def on_hci_write_class_of_device_command(
1454
1608
  self, _command: hci.HCI_Write_Class_Of_Device_Command
1455
- ) -> Optional[bytes]:
1609
+ ) -> bytes | None:
1456
1610
  '''
1457
1611
  See Bluetooth spec Vol 4, Part E - 7.3.26 Write Class of Device Command
1458
1612
  '''
@@ -1460,7 +1614,7 @@ class Controller:
1460
1614
 
1461
1615
  def on_hci_read_synchronous_flow_control_enable_command(
1462
1616
  self, _command: hci.HCI_Read_Synchronous_Flow_Control_Enable_Command
1463
- ) -> Optional[bytes]:
1617
+ ) -> bytes | None:
1464
1618
  '''
1465
1619
  See Bluetooth spec Vol 4, Part E - 7.3.36 Read Synchronous Flow Control Enable
1466
1620
  Command
@@ -1473,7 +1627,7 @@ class Controller:
1473
1627
 
1474
1628
  def on_hci_write_synchronous_flow_control_enable_command(
1475
1629
  self, command: hci.HCI_Write_Synchronous_Flow_Control_Enable_Command
1476
- ) -> Optional[bytes]:
1630
+ ) -> bytes | None:
1477
1631
  '''
1478
1632
  See Bluetooth spec Vol 4, Part E - 7.3.37 Write Synchronous Flow Control Enable
1479
1633
  Command
@@ -1489,7 +1643,7 @@ class Controller:
1489
1643
 
1490
1644
  def on_hci_set_controller_to_host_flow_control_command(
1491
1645
  self, _command: hci.HCI_Set_Controller_To_Host_Flow_Control_Command
1492
- ) -> Optional[bytes]:
1646
+ ) -> bytes | None:
1493
1647
  '''
1494
1648
  See Bluetooth spec Vol 4, Part E - 7.3.38 Set Controller To Host Flow Control
1495
1649
  Command
@@ -1500,7 +1654,7 @@ class Controller:
1500
1654
 
1501
1655
  def on_hci_host_buffer_size_command(
1502
1656
  self, _command: hci.HCI_Host_Buffer_Size_Command
1503
- ) -> Optional[bytes]:
1657
+ ) -> bytes | None:
1504
1658
  '''
1505
1659
  See Bluetooth spec Vol 4, Part E - 7.3.39 Host Buffer Size Command
1506
1660
  '''
@@ -1510,7 +1664,7 @@ class Controller:
1510
1664
 
1511
1665
  def on_hci_write_extended_inquiry_response_command(
1512
1666
  self, _command: hci.HCI_Write_Extended_Inquiry_Response_Command
1513
- ) -> Optional[bytes]:
1667
+ ) -> bytes | None:
1514
1668
  '''
1515
1669
  See Bluetooth spec Vol 4, Part E - 7.3.56 Write Extended Inquiry Response
1516
1670
  Command
@@ -1519,7 +1673,7 @@ class Controller:
1519
1673
 
1520
1674
  def on_hci_write_simple_pairing_mode_command(
1521
1675
  self, _command: hci.HCI_Write_Simple_Pairing_Mode_Command
1522
- ) -> Optional[bytes]:
1676
+ ) -> bytes | None:
1523
1677
  '''
1524
1678
  See Bluetooth spec Vol 4, Part E - 7.3.59 Write Simple Pairing Mode Command
1525
1679
  '''
@@ -1527,7 +1681,7 @@ class Controller:
1527
1681
 
1528
1682
  def on_hci_set_event_mask_page_2_command(
1529
1683
  self, command: hci.HCI_Set_Event_Mask_Page_2_Command
1530
- ) -> Optional[bytes]:
1684
+ ) -> bytes | None:
1531
1685
  '''
1532
1686
  See Bluetooth spec Vol 4, Part E - 7.3.69 Set Event Mask Page 2 Command
1533
1687
  '''
@@ -1538,7 +1692,7 @@ class Controller:
1538
1692
 
1539
1693
  def on_hci_read_le_host_support_command(
1540
1694
  self, _command: hci.HCI_Read_LE_Host_Support_Command
1541
- ) -> Optional[bytes]:
1695
+ ) -> bytes | None:
1542
1696
  '''
1543
1697
  See Bluetooth spec Vol 4, Part E - 7.3.78 Write LE Host Support Command
1544
1698
  '''
@@ -1546,7 +1700,7 @@ class Controller:
1546
1700
 
1547
1701
  def on_hci_write_le_host_support_command(
1548
1702
  self, _command: hci.HCI_Write_LE_Host_Support_Command
1549
- ) -> Optional[bytes]:
1703
+ ) -> bytes | None:
1550
1704
  '''
1551
1705
  See Bluetooth spec Vol 4, Part E - 7.3.79 Write LE Host Support Command
1552
1706
  '''
@@ -1555,7 +1709,7 @@ class Controller:
1555
1709
 
1556
1710
  def on_hci_write_authenticated_payload_timeout_command(
1557
1711
  self, command: hci.HCI_Write_Authenticated_Payload_Timeout_Command
1558
- ) -> Optional[bytes]:
1712
+ ) -> bytes | None:
1559
1713
  '''
1560
1714
  See Bluetooth spec Vol 4, Part E - 7.3.94 Write Authenticated Payload Timeout
1561
1715
  Command
@@ -1565,7 +1719,7 @@ class Controller:
1565
1719
 
1566
1720
  def on_hci_read_local_version_information_command(
1567
1721
  self, _command: hci.HCI_Read_Local_Version_Information_Command
1568
- ) -> Optional[bytes]:
1722
+ ) -> bytes | None:
1569
1723
  '''
1570
1724
  See Bluetooth spec Vol 4, Part E - 7.4.1 Read Local Version Information Command
1571
1725
  '''
@@ -1581,7 +1735,7 @@ class Controller:
1581
1735
 
1582
1736
  def on_hci_read_local_supported_commands_command(
1583
1737
  self, _command: hci.HCI_Read_Local_Supported_Commands_Command
1584
- ) -> Optional[bytes]:
1738
+ ) -> bytes | None:
1585
1739
  '''
1586
1740
  See Bluetooth spec Vol 4, Part E - 7.4.2 Read Local Supported Commands Command
1587
1741
  '''
@@ -1589,7 +1743,7 @@ class Controller:
1589
1743
 
1590
1744
  def on_hci_read_local_supported_features_command(
1591
1745
  self, _command: hci.HCI_Read_Local_Supported_Features_Command
1592
- ) -> Optional[bytes]:
1746
+ ) -> bytes | None:
1593
1747
  '''
1594
1748
  See Bluetooth spec Vol 4, Part E - 7.4.3 Read Local Supported Features Command
1595
1749
  '''
@@ -1597,7 +1751,7 @@ class Controller:
1597
1751
 
1598
1752
  def on_hci_read_local_extended_features_command(
1599
1753
  self, command: hci.HCI_Read_Local_Extended_Features_Command
1600
- ) -> Optional[bytes]:
1754
+ ) -> bytes | None:
1601
1755
  '''
1602
1756
  See Bluetooth spec Vol 4, Part E - 7.4.4 Read Local Extended Features Command
1603
1757
  '''
@@ -1620,7 +1774,7 @@ class Controller:
1620
1774
 
1621
1775
  def on_hci_read_buffer_size_command(
1622
1776
  self, _command: hci.HCI_Read_Buffer_Size_Command
1623
- ) -> Optional[bytes]:
1777
+ ) -> bytes | None:
1624
1778
  '''
1625
1779
  See Bluetooth spec Vol 4, Part E - 7.4.5 Read Buffer Size Command
1626
1780
  '''
@@ -1635,7 +1789,7 @@ class Controller:
1635
1789
 
1636
1790
  def on_hci_read_bd_addr_command(
1637
1791
  self, _command: hci.HCI_Read_BD_ADDR_Command
1638
- ) -> Optional[bytes]:
1792
+ ) -> bytes | None:
1639
1793
  '''
1640
1794
  See Bluetooth spec Vol 4, Part E - 7.4.6 Read BD_ADDR Command
1641
1795
  '''
@@ -1648,7 +1802,7 @@ class Controller:
1648
1802
 
1649
1803
  def on_hci_le_set_default_subrate_command(
1650
1804
  self, command: hci.HCI_LE_Set_Default_Subrate_Command
1651
- ) -> Optional[bytes]:
1805
+ ) -> bytes | None:
1652
1806
  '''
1653
1807
  See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
1654
1808
  '''
@@ -1664,7 +1818,7 @@ class Controller:
1664
1818
 
1665
1819
  def on_hci_le_subrate_request_command(
1666
1820
  self, command: hci.HCI_LE_Subrate_Request_Command
1667
- ) -> Optional[bytes]:
1821
+ ) -> bytes | None:
1668
1822
  '''
1669
1823
  See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
1670
1824
  '''
@@ -1698,7 +1852,7 @@ class Controller:
1698
1852
 
1699
1853
  def on_hci_le_set_event_mask_command(
1700
1854
  self, command: hci.HCI_LE_Set_Event_Mask_Command
1701
- ) -> Optional[bytes]:
1855
+ ) -> bytes | None:
1702
1856
  '''
1703
1857
  See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
1704
1858
  '''
@@ -1709,7 +1863,7 @@ class Controller:
1709
1863
 
1710
1864
  def on_hci_le_read_buffer_size_command(
1711
1865
  self, _command: hci.HCI_LE_Read_Buffer_Size_Command
1712
- ) -> Optional[bytes]:
1866
+ ) -> bytes | None:
1713
1867
  '''
1714
1868
  See Bluetooth spec Vol 4, Part E - 7.8.2 LE Read Buffer Size Command
1715
1869
  '''
@@ -1722,7 +1876,7 @@ class Controller:
1722
1876
 
1723
1877
  def on_hci_le_read_buffer_size_v2_command(
1724
1878
  self, _command: hci.HCI_LE_Read_Buffer_Size_V2_Command
1725
- ) -> Optional[bytes]:
1879
+ ) -> bytes | None:
1726
1880
  '''
1727
1881
  See Bluetooth spec Vol 4, Part E - 7.8.2 LE Read Buffer Size Command
1728
1882
  '''
@@ -1737,16 +1891,16 @@ class Controller:
1737
1891
 
1738
1892
  def on_hci_le_read_local_supported_features_command(
1739
1893
  self, _command: hci.HCI_LE_Read_Local_Supported_Features_Command
1740
- ) -> Optional[bytes]:
1894
+ ) -> bytes | None:
1741
1895
  '''
1742
1896
  See Bluetooth spec Vol 4, Part E - 7.8.3 LE Read Local Supported Features
1743
1897
  Command
1744
1898
  '''
1745
- return bytes([hci.HCI_SUCCESS]) + self.le_features
1899
+ return bytes([hci.HCI_SUCCESS]) + self.le_features.value.to_bytes(8, 'little')
1746
1900
 
1747
1901
  def on_hci_le_set_random_address_command(
1748
1902
  self, command: hci.HCI_LE_Set_Random_Address_Command
1749
- ) -> Optional[bytes]:
1903
+ ) -> bytes | None:
1750
1904
  '''
1751
1905
  See Bluetooth spec Vol 4, Part E - 7.8.4 LE Set Random hci.Address Command
1752
1906
  '''
@@ -1755,16 +1909,31 @@ class Controller:
1755
1909
 
1756
1910
  def on_hci_le_set_advertising_parameters_command(
1757
1911
  self, command: hci.HCI_LE_Set_Advertising_Parameters_Command
1758
- ) -> Optional[bytes]:
1912
+ ) -> bytes | None:
1759
1913
  '''
1760
1914
  See Bluetooth spec Vol 4, Part E - 7.8.5 LE Set Advertising Parameters Command
1761
1915
  '''
1762
- self.advertising_parameters = command
1916
+ self.le_legacy_advertiser.advertising_interval_min = (
1917
+ command.advertising_interval_min
1918
+ )
1919
+ self.le_legacy_advertiser.advertising_interval_max = (
1920
+ command.advertising_interval_max
1921
+ )
1922
+ self.le_legacy_advertiser.advertising_type = command.advertising_type
1923
+ self.le_legacy_advertiser.own_address_type = command.own_address_type
1924
+ self.le_legacy_advertiser.peer_address_type = command.peer_address_type
1925
+ self.le_legacy_advertiser.peer_address = command.peer_address
1926
+ self.le_legacy_advertiser.advertising_channel_map = (
1927
+ command.advertising_channel_map
1928
+ )
1929
+ self.le_legacy_advertiser.advertising_filter_policy = (
1930
+ command.advertising_filter_policy
1931
+ )
1763
1932
  return bytes([hci.HCI_SUCCESS])
1764
1933
 
1765
1934
  def on_hci_le_read_advertising_physical_channel_tx_power_command(
1766
1935
  self, _command: hci.HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command
1767
- ) -> Optional[bytes]:
1936
+ ) -> bytes | None:
1768
1937
  '''
1769
1938
  See Bluetooth spec Vol 4, Part E - 7.8.6 LE Read Advertising Physical Channel
1770
1939
  Tx Power Command
@@ -1773,38 +1942,39 @@ class Controller:
1773
1942
 
1774
1943
  def on_hci_le_set_advertising_data_command(
1775
1944
  self, command: hci.HCI_LE_Set_Advertising_Data_Command
1776
- ) -> Optional[bytes]:
1945
+ ) -> bytes | None:
1777
1946
  '''
1778
1947
  See Bluetooth spec Vol 4, Part E - 7.8.7 LE Set Advertising Data Command
1779
1948
  '''
1780
- self.advertising_data = command.advertising_data
1949
+ self.le_legacy_advertiser.advertising_data = command.advertising_data
1950
+
1781
1951
  return bytes([hci.HCI_SUCCESS])
1782
1952
 
1783
1953
  def on_hci_le_set_scan_response_data_command(
1784
1954
  self, command: hci.HCI_LE_Set_Scan_Response_Data_Command
1785
- ) -> Optional[bytes]:
1955
+ ) -> bytes | None:
1786
1956
  '''
1787
1957
  See Bluetooth spec Vol 4, Part E - 7.8.8 LE Set Scan Response Data Command
1788
1958
  '''
1789
- self.le_scan_response_data = command.scan_response_data
1959
+ self.le_legacy_advertiser.scan_response_data = command.scan_response_data
1790
1960
  return bytes([hci.HCI_SUCCESS])
1791
1961
 
1792
1962
  def on_hci_le_set_advertising_enable_command(
1793
1963
  self, command: hci.HCI_LE_Set_Advertising_Enable_Command
1794
- ) -> Optional[bytes]:
1964
+ ) -> bytes | None:
1795
1965
  '''
1796
1966
  See Bluetooth spec Vol 4, Part E - 7.8.9 LE Set Advertising Enable Command
1797
1967
  '''
1798
1968
  if command.advertising_enable:
1799
- self.start_advertising()
1969
+ self.le_legacy_advertiser.start()
1800
1970
  else:
1801
- self.stop_advertising()
1971
+ self.le_legacy_advertiser.stop()
1802
1972
 
1803
1973
  return bytes([hci.HCI_SUCCESS])
1804
1974
 
1805
1975
  def on_hci_le_set_scan_parameters_command(
1806
1976
  self, command: hci.HCI_LE_Set_Scan_Parameters_Command
1807
- ) -> Optional[bytes]:
1977
+ ) -> bytes | None:
1808
1978
  '''
1809
1979
  See Bluetooth spec Vol 4, Part E - 7.8.10 LE Set Scan Parameters Command
1810
1980
  '''
@@ -1820,7 +1990,7 @@ class Controller:
1820
1990
 
1821
1991
  def on_hci_le_set_scan_enable_command(
1822
1992
  self, command: hci.HCI_LE_Set_Scan_Enable_Command
1823
- ) -> Optional[bytes]:
1993
+ ) -> bytes | None:
1824
1994
  '''
1825
1995
  See Bluetooth spec Vol 4, Part E - 7.8.11 LE Set Scan Enable Command
1826
1996
  '''
@@ -1830,7 +2000,7 @@ class Controller:
1830
2000
 
1831
2001
  def on_hci_le_create_connection_command(
1832
2002
  self, command: hci.HCI_LE_Create_Connection_Command
1833
- ) -> Optional[bytes]:
2003
+ ) -> bytes | None:
1834
2004
  '''
1835
2005
  See Bluetooth spec Vol 4, Part E - 7.8.12 LE Create Connection Command
1836
2006
  '''
@@ -1841,7 +2011,7 @@ class Controller:
1841
2011
  logger.debug(f'Connection request to {command.peer_address}')
1842
2012
 
1843
2013
  # Check that we don't already have a pending connection
1844
- if self.link.get_pending_connection():
2014
+ if self.pending_le_connection:
1845
2015
  self.send_hci_packet(
1846
2016
  hci.HCI_Command_Status_Event(
1847
2017
  status=hci.HCI_COMMAND_DISALLOWED_ERROR,
@@ -1851,8 +2021,7 @@ class Controller:
1851
2021
  )
1852
2022
  return None
1853
2023
 
1854
- # Initiate the connection
1855
- self.link.connect(self.random_address, command)
2024
+ self.pending_le_connection = command
1856
2025
 
1857
2026
  # Say that the connection is pending
1858
2027
  self.send_hci_packet(
@@ -1866,15 +2035,46 @@ class Controller:
1866
2035
 
1867
2036
  def on_hci_le_create_connection_cancel_command(
1868
2037
  self, _command: hci.HCI_LE_Create_Connection_Cancel_Command
1869
- ) -> Optional[bytes]:
2038
+ ) -> bytes | None:
1870
2039
  '''
1871
2040
  See Bluetooth spec Vol 4, Part E - 7.8.13 LE Create Connection Cancel Command
1872
2041
  '''
1873
2042
  return bytes([hci.HCI_SUCCESS])
1874
2043
 
2044
+ def on_hci_le_extended_create_connection_command(
2045
+ self, command: hci.HCI_LE_Extended_Create_Connection_Command
2046
+ ) -> bytes | None:
2047
+ '''
2048
+ See Bluetooth spec Vol 4, Part E - 7.8.66 LE Extended Create Connection Command
2049
+ '''
2050
+ if not self.link:
2051
+ return None
2052
+
2053
+ # Check pending
2054
+ if self.pending_le_connection:
2055
+ self.send_hci_packet(
2056
+ hci.HCI_Command_Status_Event(
2057
+ status=hci.HCI_COMMAND_DISALLOWED_ERROR,
2058
+ num_hci_command_packets=1,
2059
+ command_opcode=command.op_code,
2060
+ )
2061
+ )
2062
+ return None
2063
+
2064
+ self.pending_le_connection = command
2065
+
2066
+ self.send_hci_packet(
2067
+ hci.HCI_Command_Status_Event(
2068
+ status=hci.HCI_COMMAND_STATUS_PENDING,
2069
+ num_hci_command_packets=1,
2070
+ command_opcode=command.op_code,
2071
+ )
2072
+ )
2073
+ return None
2074
+
1875
2075
  def on_hci_le_read_filter_accept_list_size_command(
1876
2076
  self, _command: hci.HCI_LE_Read_Filter_Accept_List_Size_Command
1877
- ) -> Optional[bytes]:
2077
+ ) -> bytes | None:
1878
2078
  '''
1879
2079
  See Bluetooth spec Vol 4, Part E - 7.8.14 LE Read Filter Accept List Size
1880
2080
  Command
@@ -1883,7 +2083,7 @@ class Controller:
1883
2083
 
1884
2084
  def on_hci_le_clear_filter_accept_list_command(
1885
2085
  self, _command: hci.HCI_LE_Clear_Filter_Accept_List_Command
1886
- ) -> Optional[bytes]:
2086
+ ) -> bytes | None:
1887
2087
  '''
1888
2088
  See Bluetooth spec Vol 4, Part E - 7.8.15 LE Clear Filter Accept List Command
1889
2089
  '''
@@ -1891,7 +2091,7 @@ class Controller:
1891
2091
 
1892
2092
  def on_hci_le_add_device_to_filter_accept_list_command(
1893
2093
  self, _command: hci.HCI_LE_Add_Device_To_Filter_Accept_List_Command
1894
- ) -> Optional[bytes]:
2094
+ ) -> bytes | None:
1895
2095
  '''
1896
2096
  See Bluetooth spec Vol 4, Part E - 7.8.16 LE Add Device To Filter Accept List
1897
2097
  Command
@@ -1900,7 +2100,7 @@ class Controller:
1900
2100
 
1901
2101
  def on_hci_le_remove_device_from_filter_accept_list_command(
1902
2102
  self, _command: hci.HCI_LE_Remove_Device_From_Filter_Accept_List_Command
1903
- ) -> Optional[bytes]:
2103
+ ) -> bytes | None:
1904
2104
  '''
1905
2105
  See Bluetooth spec Vol 4, Part E - 7.8.17 LE Remove Device From Filter Accept
1906
2106
  List Command
@@ -1909,7 +2109,7 @@ class Controller:
1909
2109
 
1910
2110
  def on_hci_write_scan_enable_command(
1911
2111
  self, command: hci.HCI_Write_Scan_Enable_Command
1912
- ) -> Optional[bytes]:
2112
+ ) -> bytes | None:
1913
2113
  '''
1914
2114
  See Bluetooth spec Vol 4, Part E - 7.3.18 Write Scan Enable Command
1915
2115
  '''
@@ -1918,7 +2118,7 @@ class Controller:
1918
2118
 
1919
2119
  def on_hci_le_read_remote_features_command(
1920
2120
  self, command: hci.HCI_LE_Read_Remote_Features_Command
1921
- ) -> Optional[bytes]:
2121
+ ) -> bytes | None:
1922
2122
  '''
1923
2123
  See Bluetooth spec Vol 4, Part E - 7.8.21 LE Read Remote Features Command
1924
2124
  '''
@@ -1954,9 +2154,7 @@ class Controller:
1954
2154
  )
1955
2155
  return None
1956
2156
 
1957
- def on_hci_le_rand_command(
1958
- self, _command: hci.HCI_LE_Rand_Command
1959
- ) -> Optional[bytes]:
2157
+ def on_hci_le_rand_command(self, _command: hci.HCI_LE_Rand_Command) -> bytes | None:
1960
2158
  '''
1961
2159
  See Bluetooth spec Vol 4, Part E - 7.8.23 LE Rand Command
1962
2160
  '''
@@ -1964,7 +2162,7 @@ class Controller:
1964
2162
 
1965
2163
  def on_hci_le_enable_encryption_command(
1966
2164
  self, command: hci.HCI_LE_Enable_Encryption_Command
1967
- ) -> Optional[bytes]:
2165
+ ) -> bytes | None:
1968
2166
  '''
1969
2167
  See Bluetooth spec Vol 4, Part E - 7.8.24 LE Enable Encryption Command
1970
2168
  '''
@@ -1972,21 +2170,21 @@ class Controller:
1972
2170
  return None
1973
2171
 
1974
2172
  # Check the parameters
1975
- if not (
1976
- connection := self.find_central_connection_by_handle(
1977
- command.connection_handle
2173
+ if (
2174
+ not (
2175
+ connection := self.find_connection_by_handle(command.connection_handle)
1978
2176
  )
2177
+ or connection.transport != PhysicalTransport.LE
1979
2178
  ):
1980
2179
  logger.warning('connection not found')
1981
2180
  return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1982
2181
 
1983
- # Notify that the connection is now encrypted
1984
- self.link.on_connection_encrypted(
1985
- self.random_address,
1986
- connection.peer_address,
1987
- command.random_number,
1988
- command.encrypted_diversifier,
1989
- command.long_term_key,
2182
+ connection.send_ll_control_pdu(
2183
+ ll.EncReq(
2184
+ rand=command.random_number,
2185
+ ediv=command.encrypted_diversifier,
2186
+ ltk=command.long_term_key,
2187
+ ),
1990
2188
  )
1991
2189
 
1992
2190
  self.send_hci_packet(
@@ -1997,11 +2195,14 @@ class Controller:
1997
2195
  )
1998
2196
  )
1999
2197
 
2198
+ # TODO: Handle authentication
2199
+ self.on_le_encrypted(connection)
2200
+
2000
2201
  return None
2001
2202
 
2002
2203
  def on_hci_le_read_supported_states_command(
2003
2204
  self, _command: hci.HCI_LE_Read_Supported_States_Command
2004
- ) -> Optional[bytes]:
2205
+ ) -> bytes | None:
2005
2206
  '''
2006
2207
  See Bluetooth spec Vol 4, Part E - 7.8.27 LE Read Supported States Command
2007
2208
  '''
@@ -2009,7 +2210,7 @@ class Controller:
2009
2210
 
2010
2211
  def on_hci_le_read_suggested_default_data_length_command(
2011
2212
  self, _command: hci.HCI_LE_Read_Suggested_Default_Data_Length_Command
2012
- ) -> Optional[bytes]:
2213
+ ) -> bytes | None:
2013
2214
  '''
2014
2215
  See Bluetooth spec Vol 4, Part E - 7.8.34 LE Read Suggested Default Data Length
2015
2216
  Command
@@ -2023,7 +2224,7 @@ class Controller:
2023
2224
 
2024
2225
  def on_hci_le_write_suggested_default_data_length_command(
2025
2226
  self, command: hci.HCI_LE_Write_Suggested_Default_Data_Length_Command
2026
- ) -> Optional[bytes]:
2227
+ ) -> bytes | None:
2027
2228
  '''
2028
2229
  See Bluetooth spec Vol 4, Part E - 7.8.35 LE Write Suggested Default Data Length
2029
2230
  Command
@@ -2035,7 +2236,7 @@ class Controller:
2035
2236
 
2036
2237
  def on_hci_le_read_local_p_256_public_key_command(
2037
2238
  self, _command: hci.HCI_LE_Read_Local_P_256_Public_Key_Command
2038
- ) -> Optional[bytes]:
2239
+ ) -> bytes | None:
2039
2240
  '''
2040
2241
  See Bluetooth spec Vol 4, Part E - 7.8.36 LE Read P-256 Public Key Command
2041
2242
  '''
@@ -2044,7 +2245,7 @@ class Controller:
2044
2245
 
2045
2246
  def on_hci_le_add_device_to_resolving_list_command(
2046
2247
  self, _command: hci.HCI_LE_Add_Device_To_Resolving_List_Command
2047
- ) -> Optional[bytes]:
2248
+ ) -> bytes | None:
2048
2249
  '''
2049
2250
  See Bluetooth spec Vol 4, Part E - 7.8.38 LE Add Device To Resolving List
2050
2251
  Command
@@ -2053,7 +2254,7 @@ class Controller:
2053
2254
 
2054
2255
  def on_hci_le_clear_resolving_list_command(
2055
2256
  self, _command: hci.HCI_LE_Clear_Resolving_List_Command
2056
- ) -> Optional[bytes]:
2257
+ ) -> bytes | None:
2057
2258
  '''
2058
2259
  See Bluetooth spec Vol 4, Part E - 7.8.40 LE Clear Resolving List Command
2059
2260
  '''
@@ -2061,7 +2262,7 @@ class Controller:
2061
2262
 
2062
2263
  def on_hci_le_read_resolving_list_size_command(
2063
2264
  self, _command: hci.HCI_LE_Read_Resolving_List_Size_Command
2064
- ) -> Optional[bytes]:
2265
+ ) -> bytes | None:
2065
2266
  '''
2066
2267
  See Bluetooth spec Vol 4, Part E - 7.8.41 LE Read Resolving List Size Command
2067
2268
  '''
@@ -2069,7 +2270,7 @@ class Controller:
2069
2270
 
2070
2271
  def on_hci_le_set_address_resolution_enable_command(
2071
2272
  self, command: hci.HCI_LE_Set_Address_Resolution_Enable_Command
2072
- ) -> Optional[bytes]:
2273
+ ) -> bytes | None:
2073
2274
  '''
2074
2275
  See Bluetooth spec Vol 4, Part E - 7.8.44 LE Set hci.Address Resolution Enable
2075
2276
  Command
@@ -2085,7 +2286,7 @@ class Controller:
2085
2286
 
2086
2287
  def on_hci_le_set_resolvable_private_address_timeout_command(
2087
2288
  self, command: hci.HCI_LE_Set_Resolvable_Private_Address_Timeout_Command
2088
- ) -> Optional[bytes]:
2289
+ ) -> bytes | None:
2089
2290
  '''
2090
2291
  See Bluetooth spec Vol 4, Part E - 7.8.45 LE Set Resolvable Private hci.Address
2091
2292
  Timeout Command
@@ -2095,7 +2296,7 @@ class Controller:
2095
2296
 
2096
2297
  def on_hci_le_read_maximum_data_length_command(
2097
2298
  self, _command: hci.HCI_LE_Read_Maximum_Data_Length_Command
2098
- ) -> Optional[bytes]:
2299
+ ) -> bytes | None:
2099
2300
  '''
2100
2301
  See Bluetooth spec Vol 4, Part E - 7.8.46 LE Read Maximum Data Length Command
2101
2302
  '''
@@ -2110,7 +2311,7 @@ class Controller:
2110
2311
 
2111
2312
  def on_hci_le_read_phy_command(
2112
2313
  self, command: hci.HCI_LE_Read_PHY_Command
2113
- ) -> Optional[bytes]:
2314
+ ) -> bytes | None:
2114
2315
  '''
2115
2316
  See Bluetooth spec Vol 4, Part E - 7.8.47 LE Read PHY Command
2116
2317
  '''
@@ -2124,7 +2325,7 @@ class Controller:
2124
2325
 
2125
2326
  def on_hci_le_set_default_phy_command(
2126
2327
  self, command: hci.HCI_LE_Set_Default_PHY_Command
2127
- ) -> Optional[bytes]:
2328
+ ) -> bytes | None:
2128
2329
  '''
2129
2330
  See Bluetooth spec Vol 4, Part E - 7.8.48 LE Set Default PHY Command
2130
2331
  '''
@@ -2134,53 +2335,130 @@ class Controller:
2134
2335
  return bytes([hci.HCI_SUCCESS])
2135
2336
 
2136
2337
  def on_hci_le_set_advertising_set_random_address_command(
2137
- self, _command: hci.HCI_LE_Set_Advertising_Set_Random_Address_Command
2138
- ) -> Optional[bytes]:
2338
+ self, command: hci.HCI_LE_Set_Advertising_Set_Random_Address_Command
2339
+ ) -> bytes | None:
2139
2340
  '''
2140
2341
  See Bluetooth spec Vol 4, Part E - 7.8.52 LE Set Advertising Set Random hci.Address
2141
2342
  Command
2142
2343
  '''
2344
+ handle = command.advertising_handle
2345
+ if handle not in self.advertising_sets:
2346
+ self.advertising_sets[handle] = AdvertisingSet(
2347
+ controller=self, handle=handle
2348
+ )
2349
+ self.advertising_sets[handle].random_address = command.random_address
2143
2350
  return bytes([hci.HCI_SUCCESS])
2144
2351
 
2145
2352
  def on_hci_le_set_extended_advertising_parameters_command(
2146
- self, _command: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command
2147
- ) -> Optional[bytes]:
2353
+ self, command: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command
2354
+ ) -> bytes | None:
2148
2355
  '''
2149
2356
  See Bluetooth spec Vol 4, Part E - 7.8.53 LE Set Extended Advertising Parameters
2150
2357
  Command
2151
2358
  '''
2359
+ handle = command.advertising_handle
2360
+ if handle not in self.advertising_sets:
2361
+ self.advertising_sets[handle] = AdvertisingSet(
2362
+ controller=self, handle=handle
2363
+ )
2364
+
2365
+ self.advertising_sets[handle].parameters = command
2152
2366
  return bytes([hci.HCI_SUCCESS, 0])
2153
2367
 
2154
2368
  def on_hci_le_set_extended_advertising_data_command(
2155
- self, _command: hci.HCI_LE_Set_Extended_Advertising_Data_Command
2156
- ) -> Optional[bytes]:
2369
+ self, command: hci.HCI_LE_Set_Extended_Advertising_Data_Command
2370
+ ) -> bytes | None:
2157
2371
  '''
2158
2372
  See Bluetooth spec Vol 4, Part E - 7.8.54 LE Set Extended Advertising Data
2159
2373
  Command
2160
2374
  '''
2375
+ handle = command.advertising_handle
2376
+ if not (adv_set := self.advertising_sets.get(handle)):
2377
+ return bytes([hci.HCI_UNKNOWN_ADVERTISING_IDENTIFIER_ERROR])
2378
+
2379
+ if command.operation in (
2380
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.FIRST_FRAGMENT,
2381
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
2382
+ ):
2383
+ adv_set.data = bytearray(command.advertising_data)
2384
+ elif command.operation in (
2385
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.INTERMEDIATE_FRAGMENT,
2386
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.LAST_FRAGMENT,
2387
+ ):
2388
+ adv_set.data.extend(command.advertising_data)
2389
+
2161
2390
  return bytes([hci.HCI_SUCCESS])
2162
2391
 
2163
2392
  def on_hci_le_set_extended_scan_response_data_command(
2164
- self, _command: hci.HCI_LE_Set_Extended_Scan_Response_Data_Command
2165
- ) -> Optional[bytes]:
2393
+ self, command: hci.HCI_LE_Set_Extended_Scan_Response_Data_Command
2394
+ ) -> bytes | None:
2166
2395
  '''
2167
2396
  See Bluetooth spec Vol 4, Part E - 7.8.55 LE Set Extended Scan Response Data
2168
2397
  Command
2169
2398
  '''
2399
+ handle = command.advertising_handle
2400
+ if not (adv_set := self.advertising_sets.get(handle)):
2401
+ return bytes([hci.HCI_UNKNOWN_ADVERTISING_IDENTIFIER_ERROR])
2402
+
2403
+ if command.operation in (
2404
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.FIRST_FRAGMENT,
2405
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
2406
+ ):
2407
+ adv_set.scan_response_data = bytearray(command.scan_response_data)
2408
+ elif command.operation in (
2409
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.INTERMEDIATE_FRAGMENT,
2410
+ hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.LAST_FRAGMENT,
2411
+ ):
2412
+ adv_set.scan_response_data.extend(command.scan_response_data)
2413
+
2170
2414
  return bytes([hci.HCI_SUCCESS])
2171
2415
 
2172
2416
  def on_hci_le_set_extended_advertising_enable_command(
2173
- self, _command: hci.HCI_LE_Set_Extended_Advertising_Enable_Command
2174
- ) -> Optional[bytes]:
2417
+ self, command: hci.HCI_LE_Set_Extended_Advertising_Enable_Command
2418
+ ) -> bytes | None:
2175
2419
  '''
2176
2420
  See Bluetooth spec Vol 4, Part E - 7.8.56 LE Set Extended Advertising Enable
2177
2421
  Command
2178
2422
  '''
2423
+ if command.enable:
2424
+ for handle in command.advertising_handles:
2425
+ if advertising_set := self.advertising_sets.get(handle):
2426
+ advertising_set.start()
2427
+ else:
2428
+ if not command.advertising_handles:
2429
+ for advertising_set in self.advertising_sets.values():
2430
+ advertising_set.stop()
2431
+ else:
2432
+ for handle in command.advertising_handles:
2433
+ if advertising_set := self.advertising_sets.get(handle):
2434
+ advertising_set.stop()
2435
+ return bytes([hci.HCI_SUCCESS])
2436
+
2437
+ def on_hci_le_remove_advertising_set_command(
2438
+ self, command: hci.HCI_LE_Remove_Advertising_Set_Command
2439
+ ) -> bytes | None:
2440
+ '''
2441
+ See Bluetooth spec Vol 4, Part E - 7.8.59 LE Remove Advertising Set Command
2442
+ '''
2443
+ handle = command.advertising_handle
2444
+ if advertising_set := self.advertising_sets.pop(handle, None):
2445
+ advertising_set.stop()
2446
+ return bytes([hci.HCI_SUCCESS])
2447
+
2448
+ def on_hci_le_clear_advertising_sets_command(
2449
+ self, _command: hci.HCI_LE_Clear_Advertising_Sets_Command
2450
+ ) -> bytes | None:
2451
+ '''
2452
+ See Bluetooth spec Vol 4, Part E - 7.8.60 LE Clear Advertising Sets Command
2453
+ '''
2454
+ for advertising_set in self.advertising_sets.values():
2455
+ advertising_set.stop()
2456
+ self.advertising_sets.clear()
2179
2457
  return bytes([hci.HCI_SUCCESS])
2180
2458
 
2181
2459
  def on_hci_le_read_maximum_advertising_data_length_command(
2182
2460
  self, _command: hci.HCI_LE_Read_Maximum_Advertising_Data_Length_Command
2183
- ) -> Optional[bytes]:
2461
+ ) -> bytes | None:
2184
2462
  '''
2185
2463
  See Bluetooth spec Vol 4, Part E - 7.8.57 LE Read Maximum Advertising Data
2186
2464
  Length Command
@@ -2189,7 +2467,7 @@ class Controller:
2189
2467
 
2190
2468
  def on_hci_le_read_number_of_supported_advertising_sets_command(
2191
2469
  self, _command: hci.HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command
2192
- ) -> Optional[bytes]:
2470
+ ) -> bytes | None:
2193
2471
  '''
2194
2472
  See Bluetooth spec Vol 4, Part E - 7.8.58 LE Read Number of Supported
2195
2473
  Advertising Set Command
@@ -2198,7 +2476,7 @@ class Controller:
2198
2476
 
2199
2477
  def on_hci_le_set_periodic_advertising_parameters_command(
2200
2478
  self, _command: hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command
2201
- ) -> Optional[bytes]:
2479
+ ) -> bytes | None:
2202
2480
  '''
2203
2481
  See Bluetooth spec Vol 4, Part E - 7.8.61 LE Set Periodic Advertising Parameters
2204
2482
  Command
@@ -2207,7 +2485,7 @@ class Controller:
2207
2485
 
2208
2486
  def on_hci_le_set_periodic_advertising_data_command(
2209
2487
  self, _command: hci.HCI_LE_Set_Periodic_Advertising_Data_Command
2210
- ) -> Optional[bytes]:
2488
+ ) -> bytes | None:
2211
2489
  '''
2212
2490
  See Bluetooth spec Vol 4, Part E - 7.8.62 LE Set Periodic Advertising Data
2213
2491
  Command
@@ -2216,7 +2494,7 @@ class Controller:
2216
2494
 
2217
2495
  def on_hci_le_set_periodic_advertising_enable_command(
2218
2496
  self, _command: hci.HCI_LE_Set_Periodic_Advertising_Enable_Command
2219
- ) -> Optional[bytes]:
2497
+ ) -> bytes | None:
2220
2498
  '''
2221
2499
  See Bluetooth spec Vol 4, Part E - 7.8.63 LE Set Periodic Advertising Enable
2222
2500
  Command
@@ -2225,7 +2503,7 @@ class Controller:
2225
2503
 
2226
2504
  def on_hci_le_read_transmit_power_command(
2227
2505
  self, _command: hci.HCI_LE_Read_Transmit_Power_Command
2228
- ) -> Optional[bytes]:
2506
+ ) -> bytes | None:
2229
2507
  '''
2230
2508
  See Bluetooth spec Vol 4, Part E - 7.8.74 LE Read Transmit Power Command
2231
2509
  '''
@@ -2233,7 +2511,7 @@ class Controller:
2233
2511
 
2234
2512
  def on_hci_le_set_cig_parameters_command(
2235
2513
  self, command: hci.HCI_LE_Set_CIG_Parameters_Command
2236
- ) -> Optional[bytes]:
2514
+ ) -> bytes | None:
2237
2515
  '''
2238
2516
  See Bluetooth spec Vol 4, Part E - 7.8.97 LE Set CIG Parameter Command
2239
2517
  '''
@@ -2259,7 +2537,7 @@ class Controller:
2259
2537
 
2260
2538
  def on_hci_le_create_cis_command(
2261
2539
  self, command: hci.HCI_LE_Create_CIS_Command
2262
- ) -> Optional[bytes]:
2540
+ ) -> bytes | None:
2263
2541
  '''
2264
2542
  See Bluetooth spec Vol 4, Part E - 7.8.99 LE Create CIS Command
2265
2543
  '''
@@ -2279,11 +2557,8 @@ class Controller:
2279
2557
 
2280
2558
  cis_link.acl_connection = connection
2281
2559
 
2282
- self.link.create_cis(
2283
- self,
2284
- peripheral_address=connection.peer_address,
2285
- cig_id=cis_link.cig_id,
2286
- cis_id=cis_link.cis_id,
2560
+ connection.send_ll_control_pdu(
2561
+ ll.CisReq(cig_id=cis_link.cig_id, cis_id=cis_link.cis_id)
2287
2562
  )
2288
2563
 
2289
2564
  self.send_hci_packet(
@@ -2297,7 +2572,7 @@ class Controller:
2297
2572
 
2298
2573
  def on_hci_le_remove_cig_command(
2299
2574
  self, command: hci.HCI_LE_Remove_CIG_Command
2300
- ) -> Optional[bytes]:
2575
+ ) -> bytes | None:
2301
2576
  '''
2302
2577
  See Bluetooth spec Vol 4, Part E - 7.8.100 LE Remove CIG Command
2303
2578
  '''
@@ -2314,7 +2589,7 @@ class Controller:
2314
2589
 
2315
2590
  def on_hci_le_accept_cis_request_command(
2316
2591
  self, command: hci.HCI_LE_Accept_CIS_Request_Command
2317
- ) -> Optional[bytes]:
2592
+ ) -> bytes | None:
2318
2593
  '''
2319
2594
  See Bluetooth spec Vol 4, Part E - 7.8.101 LE Accept CIS Request Command
2320
2595
  '''
@@ -2328,11 +2603,8 @@ class Controller:
2328
2603
  return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
2329
2604
 
2330
2605
  assert pending_cis_link.acl_connection
2331
- self.link.accept_cis(
2332
- peripheral_controller=self,
2333
- central_address=pending_cis_link.acl_connection.peer_address,
2334
- cig_id=pending_cis_link.cig_id,
2335
- cis_id=pending_cis_link.cis_id,
2606
+ pending_cis_link.acl_connection.send_ll_control_pdu(
2607
+ ll.CisRsp(cig_id=pending_cis_link.cig_id, cis_id=pending_cis_link.cis_id),
2336
2608
  )
2337
2609
 
2338
2610
  self.send_hci_packet(
@@ -2346,7 +2618,7 @@ class Controller:
2346
2618
 
2347
2619
  def on_hci_le_setup_iso_data_path_command(
2348
2620
  self, command: hci.HCI_LE_Setup_ISO_Data_Path_Command
2349
- ) -> Optional[bytes]:
2621
+ ) -> bytes | None:
2350
2622
  '''
2351
2623
  See Bluetooth spec Vol 4, Part E - 7.8.109 LE Setup ISO Data Path Command
2352
2624
  '''
@@ -2367,7 +2639,7 @@ class Controller:
2367
2639
 
2368
2640
  def on_hci_le_remove_iso_data_path_command(
2369
2641
  self, command: hci.HCI_LE_Remove_ISO_Data_Path_Command
2370
- ) -> Optional[bytes]:
2642
+ ) -> bytes | None:
2371
2643
  '''
2372
2644
  See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
2373
2645
  '''
@@ -2393,7 +2665,7 @@ class Controller:
2393
2665
 
2394
2666
  def on_hci_le_set_host_feature_command(
2395
2667
  self, _command: hci.HCI_LE_Set_Host_Feature_Command
2396
- ) -> Optional[bytes]:
2668
+ ) -> bytes | None:
2397
2669
  '''
2398
2670
  See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
2399
2671
  '''