bumble 0.0.219__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 (104) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +5 -5
  3. bumble/apps/auracast.py +746 -479
  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 +8 -6
  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 +1201 -643
  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 +278 -325
  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 +54 -284
  48. bumble/ll.py +200 -0
  49. bumble/lmp.py +324 -0
  50. bumble/pairing.py +14 -15
  51. bumble/pandora/__init__.py +2 -2
  52. bumble/pandora/device.py +6 -4
  53. bumble/pandora/host.py +19 -10
  54. bumble/pandora/l2cap.py +8 -9
  55. bumble/pandora/security.py +18 -16
  56. bumble/pandora/utils.py +4 -4
  57. bumble/profiles/aics.py +6 -8
  58. bumble/profiles/ams.py +3 -5
  59. bumble/profiles/ancs.py +11 -11
  60. bumble/profiles/ascs.py +5 -5
  61. bumble/profiles/asha.py +10 -9
  62. bumble/profiles/bass.py +9 -3
  63. bumble/profiles/battery_service.py +1 -2
  64. bumble/profiles/csip.py +9 -10
  65. bumble/profiles/device_information_service.py +16 -17
  66. bumble/profiles/gap.py +3 -4
  67. bumble/profiles/gatt_service.py +0 -1
  68. bumble/profiles/gmap.py +12 -13
  69. bumble/profiles/hap.py +3 -3
  70. bumble/profiles/heart_rate_service.py +7 -8
  71. bumble/profiles/le_audio.py +1 -1
  72. bumble/profiles/mcp.py +28 -28
  73. bumble/profiles/pacs.py +13 -17
  74. bumble/profiles/pbp.py +16 -0
  75. bumble/profiles/vcs.py +2 -2
  76. bumble/profiles/vocs.py +6 -9
  77. bumble/rfcomm.py +19 -18
  78. bumble/sdp.py +12 -11
  79. bumble/smp.py +20 -30
  80. bumble/snoop.py +12 -5
  81. bumble/tools/generate_company_id_list.py +1 -1
  82. bumble/tools/intel_util.py +2 -2
  83. bumble/tools/rtk_fw_download.py +1 -1
  84. bumble/tools/rtk_util.py +1 -1
  85. bumble/transport/__init__.py +1 -2
  86. bumble/transport/android_emulator.py +2 -3
  87. bumble/transport/android_netsim.py +49 -40
  88. bumble/transport/common.py +9 -9
  89. bumble/transport/file.py +1 -2
  90. bumble/transport/hci_socket.py +2 -3
  91. bumble/transport/pty.py +3 -5
  92. bumble/transport/pyusb.py +8 -5
  93. bumble/transport/serial.py +1 -2
  94. bumble/transport/vhci.py +1 -2
  95. bumble/transport/ws_server.py +2 -3
  96. bumble/utils.py +23 -14
  97. bumble/vendor/android/hci.py +4 -2
  98. {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/METADATA +4 -3
  99. bumble-0.0.221.dist-info/RECORD +185 -0
  100. bumble-0.0.219.dist-info/RECORD +0 -183
  101. {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
  102. {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
  103. {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
  104. {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/controller.py CHANGED
@@ -23,48 +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
26
+ from typing import TYPE_CHECKING, Any, cast
27
27
 
28
- from bumble import hci
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
- from bumble.hci import (
32
- HCI_ACL_DATA_PACKET,
33
- HCI_COMMAND_DISALLOWED_ERROR,
34
- HCI_COMMAND_PACKET,
35
- HCI_COMMAND_STATUS_PENDING,
36
- HCI_CONTROLLER_BUSY_ERROR,
37
- HCI_EVENT_PACKET,
38
- HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR,
39
- HCI_LE_1M_PHY,
40
- HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
41
- HCI_SUCCESS,
42
- HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
43
- HCI_UNKNOWN_HCI_COMMAND_ERROR,
44
- HCI_VERSION_BLUETOOTH_CORE_5_0,
45
- Address,
46
- HCI_AclDataPacket,
47
- HCI_AclDataPacketAssembler,
48
- HCI_Command_Complete_Event,
49
- HCI_Command_Status_Event,
50
- HCI_Connection_Complete_Event,
51
- HCI_Connection_Request_Event,
52
- HCI_Disconnection_Complete_Event,
53
- HCI_Encryption_Change_Event,
54
- HCI_LE_Advertising_Report_Event,
55
- HCI_LE_CIS_Established_Event,
56
- HCI_LE_CIS_Request_Event,
57
- HCI_LE_Connection_Complete_Event,
58
- HCI_LE_Read_Remote_Features_Complete_Event,
59
- HCI_Number_Of_Completed_Packets_Event,
60
- HCI_Packet,
61
- HCI_Role_Change_Event,
62
- HCI_Synchronous_Connection_Complete_Event,
63
- Role,
64
- )
65
32
 
66
33
  if TYPE_CHECKING:
67
- from bumble.link import LocalLink
68
34
  from bumble.transport.common import TransportSink
69
35
 
70
36
  # -----------------------------------------------------------------------------
@@ -86,28 +52,156 @@ class CisLink:
86
52
  handle: int
87
53
  cis_id: int
88
54
  cig_id: int
89
- acl_connection: Optional[Connection] = None
55
+ acl_connection: Connection | None = None
90
56
  data_paths: set[int] = dataclasses.field(default_factory=set)
91
57
 
92
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
+
177
+ # -----------------------------------------------------------------------------
178
+ @dataclasses.dataclass
179
+ class ScoLink:
180
+ handle: int
181
+ link_type: int
182
+ peer_address: hci.Address
183
+
184
+
93
185
  # -----------------------------------------------------------------------------
94
186
  @dataclasses.dataclass
95
187
  class Connection:
96
188
  controller: Controller
97
189
  handle: int
98
- role: Role
99
- peer_address: Address
100
- link: Any
101
- transport: int
190
+ role: hci.Role
191
+ self_address: hci.Address
192
+ peer_address: hci.Address
193
+ link: link.LocalLink
194
+ transport: PhysicalTransport
102
195
  link_type: int
196
+ classic_allow_role_switch: bool = False
103
197
 
104
198
  def __post_init__(self) -> None:
105
- self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu)
199
+ self.assembler = hci.HCI_AclDataPacketAssembler(self.on_acl_pdu)
106
200
 
107
201
  def on_hci_acl_data_packet(self, packet: hci.HCI_AclDataPacket) -> None:
108
202
  self.assembler.feed_packet(packet)
109
203
  self.controller.send_hci_packet(
110
- HCI_Number_Of_Completed_Packets_Event(
204
+ hci.HCI_Number_Of_Completed_Packets_Event(
111
205
  connection_handles=[self.handle], num_completed_packets=[1]
112
206
  )
113
207
  )
@@ -118,24 +212,31 @@ class Connection:
118
212
  self.controller, self.peer_address, self.transport, pdu
119
213
  )
120
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
+
121
223
 
122
224
  # -----------------------------------------------------------------------------
123
225
  class Controller:
124
- hci_sink: Optional[TransportSink] = None
125
-
126
- central_connections: dict[
127
- Address, Connection
128
- ] # Connections where this controller is the central
129
- peripheral_connections: dict[
130
- Address, Connection
131
- ] # Connections where this controller is the peripheral
132
- classic_connections: dict[Address, Connection] # Connections in BR/EDR
226
+ hci_sink: TransportSink | None = None
227
+
228
+ le_connections: dict[hci.Address, Connection] # LE Connections
229
+ classic_connections: dict[hci.Address, Connection] # Connections in BR/EDR
230
+ classic_pending_commands: dict[hci.Address, dict[lmp.Opcode, asyncio.Future[int]]]
231
+ sco_links: dict[hci.Address, ScoLink] # SCO links by address
133
232
  central_cis_links: dict[int, CisLink] # CIS links by handle
134
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
135
236
 
136
- hci_version: int = HCI_VERSION_BLUETOOTH_CORE_5_0
237
+ hci_version: int = hci.HCI_VERSION_BLUETOOTH_CORE_5_0
137
238
  hci_revision: int = 0
138
- lmp_version: int = HCI_VERSION_BLUETOOTH_CORE_5_0
239
+ lmp_version: int = hci.HCI_VERSION_BLUETOOTH_CORE_5_0
139
240
  lmp_subversion: int = 0
140
241
  lmp_features: bytes = bytes.fromhex(
141
242
  '0000000060000000'
@@ -154,10 +255,20 @@ class Controller:
154
255
  '30f0f9ff01008004002000000000000000000000000000000000000000000000'
155
256
  )
156
257
  le_event_mask: int = 0
157
- advertising_parameters: Optional[hci.HCI_LE_Set_Advertising_Parameters_Command] = (
158
- 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
159
271
  )
160
- le_features: bytes = bytes.fromhex('ff49010000000000')
161
272
  le_states: bytes = bytes.fromhex('ffff3fffff030000')
162
273
  advertising_channel_tx_power: int = 0
163
274
  filter_accept_list_size: int = 8
@@ -173,49 +284,58 @@ class Controller:
173
284
  le_scan_type: int = 0
174
285
  le_scan_interval: int = 0x10
175
286
  le_scan_window: int = 0x10
176
- le_scan_enable: int = 0
177
- le_scan_own_address_type: int = Address.RANDOM_DEVICE_ADDRESS
287
+ le_scan_enable: bool = False
288
+ le_scan_own_address_type: int = hci.Address.RANDOM_DEVICE_ADDRESS
178
289
  le_scanning_filter_policy: int = 0
179
- le_scan_response_data: Optional[bytes] = None
180
290
  le_address_resolution: bool = False
181
291
  le_rpa_timeout: int = 0
182
292
  sync_flow_control: bool = False
183
293
  local_name: str = 'Bumble'
184
294
  advertising_interval: int = 2000
185
- advertising_data: Optional[bytes] = None
186
- advertising_timer_handle: Optional[asyncio.Handle] = None
187
-
188
- _random_address: 'Address' = Address('00:00:00:00:00:00')
295
+ advertising_data: bytes | None = None
296
+ advertising_timer_handle: asyncio.Handle | None = None
297
+ classic_scan_enable: int = 0
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
304
+
305
+ _random_address: hci.Address = hci.Address('00:00:00:00:00:00')
189
306
 
190
307
  def __init__(
191
308
  self,
192
309
  name: str,
193
310
  host_source=None,
194
- host_sink: Optional[TransportSink] = None,
195
- link: Optional[LocalLink] = None,
196
- public_address: Optional[Union[bytes, str, Address]] = None,
311
+ host_sink: TransportSink | None = None,
312
+ link: link.LocalLink | None = None,
313
+ public_address: bytes | str | hci.Address | None = None,
197
314
  ) -> None:
198
315
  self.name = name
199
- self.link = link
200
- self.central_connections = {}
201
- self.peripheral_connections = {}
316
+ self.link = link or bumble_link.LocalLink()
317
+ self.le_connections = {}
202
318
  self.classic_connections = {}
319
+ self.sco_links = {}
320
+ self.classic_pending_commands = {}
203
321
  self.central_cis_links = {}
204
322
  self.peripheral_cis_links = {}
323
+ self.advertising_sets = {}
205
324
  self.default_phy = {
206
325
  'all_phys': 0,
207
326
  'tx_phys': 0,
208
327
  'rx_phys': 0,
209
328
  }
329
+ self.le_legacy_advertiser = LegacyAdvertiser(self)
210
330
 
211
- if isinstance(public_address, Address):
331
+ if isinstance(public_address, hci.Address):
212
332
  self._public_address = public_address
213
333
  elif public_address is not None:
214
- self._public_address = Address(
215
- public_address, Address.PUBLIC_DEVICE_ADDRESS
334
+ self._public_address = hci.Address(
335
+ public_address, hci.Address.PUBLIC_DEVICE_ADDRESS
216
336
  )
217
337
  else:
218
- self._public_address = Address('00:00:00:00:00:00')
338
+ self._public_address = hci.Address('00:00:00:00:00:00')
219
339
 
220
340
  # Set the source and sink interfaces
221
341
  if host_source:
@@ -231,41 +351,41 @@ class Controller:
231
351
  )
232
352
 
233
353
  @property
234
- def host(self) -> Optional[TransportSink]:
354
+ def host(self) -> TransportSink | None:
235
355
  return self.hci_sink
236
356
 
237
357
  @host.setter
238
- def host(self, host: Optional[TransportSink]) -> None:
358
+ def host(self, host: TransportSink | None) -> None:
239
359
  '''
240
360
  Sets the host (sink) for this controller, and set this controller as the
241
361
  controller (sink) for the host
242
362
  '''
243
363
  self.set_packet_sink(host)
244
364
 
245
- def set_packet_sink(self, sink: Optional[TransportSink]) -> None:
365
+ def set_packet_sink(self, sink: TransportSink | None) -> None:
246
366
  '''
247
367
  Method from the Packet Source interface
248
368
  '''
249
369
  self.hci_sink = sink
250
370
 
251
371
  @property
252
- def public_address(self) -> Address:
372
+ def public_address(self) -> hci.Address:
253
373
  return self._public_address
254
374
 
255
375
  @public_address.setter
256
- def public_address(self, address: Union[Address, str]) -> None:
376
+ def public_address(self, address: hci.Address | str) -> None:
257
377
  if isinstance(address, str):
258
- address = Address(address)
378
+ address = hci.Address(address)
259
379
  self._public_address = address
260
380
 
261
381
  @property
262
- def random_address(self) -> Address:
382
+ def random_address(self) -> hci.Address:
263
383
  return self._random_address
264
384
 
265
385
  @random_address.setter
266
- def random_address(self, address: Union[Address, str]) -> None:
386
+ def random_address(self, address: hci.Address | str) -> None:
267
387
  if isinstance(address, str):
268
- address = Address(address)
388
+ address = hci.Address(address)
269
389
  self._random_address = address
270
390
  logger.debug(f'new random address: {address}')
271
391
 
@@ -274,9 +394,9 @@ class Controller:
274
394
 
275
395
  # Packet Sink protocol (packets coming from the host via HCI)
276
396
  def on_packet(self, packet: bytes) -> None:
277
- self.on_hci_packet(HCI_Packet.from_bytes(packet))
397
+ self.on_hci_packet(hci.HCI_Packet.from_bytes(packet))
278
398
 
279
- def on_hci_packet(self, packet: HCI_Packet) -> None:
399
+ def on_hci_packet(self, packet: hci.HCI_Packet) -> None:
280
400
  logger.debug(
281
401
  f'{color("<<<", "blue")} [{self.name}] '
282
402
  f'{color("HOST -> CONTROLLER", "blue")}: {packet}'
@@ -295,17 +415,17 @@ class Controller:
295
415
  def on_hci_command_packet(self, command: hci.HCI_Command) -> None:
296
416
  handler_name = f'on_{command.name.lower()}'
297
417
  handler = getattr(self, handler_name, self.on_hci_command)
298
- result: Optional[bytes] = handler(command)
418
+ result: bytes | None = handler(command)
299
419
  if isinstance(result, bytes):
300
420
  self.send_hci_packet(
301
- HCI_Command_Complete_Event(
421
+ hci.HCI_Command_Complete_Event(
302
422
  num_hci_command_packets=1,
303
423
  command_opcode=command.op_code,
304
424
  return_parameters=result,
305
425
  )
306
426
  )
307
427
 
308
- def on_hci_event_packet(self, _event: HCI_Packet) -> None:
428
+ def on_hci_event_packet(self, _event: hci.HCI_Packet) -> None:
309
429
  logger.warning('!!! unexpected event packet')
310
430
 
311
431
  def on_hci_acl_data_packet(self, packet: hci.HCI_AclDataPacket) -> None:
@@ -320,13 +440,13 @@ class Controller:
320
440
  # Pass the packet to the connection
321
441
  connection.on_hci_acl_data_packet(packet)
322
442
 
323
- def send_hci_packet(self, packet: HCI_Packet) -> None:
443
+ def send_hci_packet(self, packet: hci.HCI_Packet) -> None:
324
444
  logger.debug(
325
445
  f'{color(">>>", "green")} [{self.name}] '
326
446
  f'{color("CONTROLLER -> HOST", "green")}: {packet}'
327
447
  )
328
448
  if self.host:
329
- self.host.on_packet(bytes(packet))
449
+ asyncio.get_running_loop().call_soon(self.host.on_packet, bytes(packet))
330
450
 
331
451
  # This method allows the controller to emulate the same API as a transport source
332
452
  async def wait_for_termination(self) -> None:
@@ -336,99 +456,125 @@ class Controller:
336
456
  # Link connections
337
457
  ############################################################
338
458
  def allocate_connection_handle(self) -> int:
339
- handle = 0
340
- max_handle = 0
341
- for connection in itertools.chain(
342
- self.central_connections.values(),
343
- self.peripheral_connections.values(),
344
- self.classic_connections.values(),
345
- ):
346
- max_handle = max(max_handle, connection.handle)
347
- if connection.handle == handle:
348
- # Already used, continue searching after the current max
349
- handle = max_handle + 1
350
- for cis_handle in itertools.chain(
351
- self.central_cis_links.keys(), self.peripheral_cis_links.keys()
352
- ):
353
- max_handle = max(max_handle, cis_handle)
354
- if cis_handle == handle:
355
- # Already used, continue searching after the current max
356
- handle = max_handle + 1
357
- return handle
358
-
359
- def find_le_connection_by_address(self, address: Address) -> Optional[Connection]:
360
- return self.central_connections.get(address) or self.peripheral_connections.get(
361
- address
459
+ current_handles = set(
460
+ cast(Connection | CisLink | ScoLink, link).handle
461
+ for link in itertools.chain(
462
+ self.le_connections.values(),
463
+ self.classic_connections.values(),
464
+ self.sco_links.values(),
465
+ self.central_cis_links.values(),
466
+ self.peripheral_cis_links.values(),
467
+ )
468
+ )
469
+ return next(
470
+ handle
471
+ for handle in range(0x0001, 0xEFF + 1)
472
+ if handle not in current_handles
362
473
  )
363
474
 
364
- def find_classic_connection_by_address(
365
- self, address: Address
366
- ) -> Optional[Connection]:
367
- return self.classic_connections.get(address)
368
-
369
- def find_connection_by_handle(self, handle: int) -> Optional[Connection]:
475
+ def find_connection_by_handle(self, handle: int) -> Connection | None:
370
476
  for connection in itertools.chain(
371
- self.central_connections.values(),
372
- self.peripheral_connections.values(),
477
+ self.le_connections.values(),
373
478
  self.classic_connections.values(),
374
479
  ):
375
480
  if connection.handle == handle:
376
481
  return connection
377
482
  return None
378
483
 
379
- def find_central_connection_by_handle(self, handle: int) -> Optional[Connection]:
380
- for connection in self.central_connections.values():
381
- if connection.handle == handle:
382
- return connection
383
- return None
384
-
385
- def find_peripheral_connection_by_handle(self, handle: int) -> Optional[Connection]:
386
- for connection in self.peripheral_connections.values():
387
- if connection.handle == handle:
388
- return connection
389
- return None
390
-
391
- def find_classic_connection_by_handle(self, handle: int) -> Optional[Connection]:
392
- for connection in self.classic_connections.values():
484
+ def find_classic_sco_link_by_handle(self, handle: int) -> ScoLink | None:
485
+ for connection in self.sco_links.values():
393
486
  if connection.handle == handle:
394
487
  return connection
395
488
  return None
396
489
 
397
- def find_iso_link_by_handle(self, handle: int) -> Optional[CisLink]:
490
+ def find_iso_link_by_handle(self, handle: int) -> CisLink | None:
398
491
  return self.central_cis_links.get(handle) or self.peripheral_cis_links.get(
399
492
  handle
400
493
  )
401
494
 
402
- def on_link_central_connected(self, central_address: 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:
403
530
  '''
404
531
  Called when an incoming connection occurs from a central on the link
405
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
406
553
 
407
554
  # Allocate (or reuse) a connection handle
408
- peer_address = central_address
409
- peer_address_type = central_address.address_type
410
- connection = self.peripheral_connections.get(peer_address)
411
- if connection is None:
412
- connection_handle = self.allocate_connection_handle()
413
- connection = Connection(
414
- controller=self,
415
- handle=connection_handle,
416
- role=Role.PERIPHERAL,
417
- peer_address=peer_address,
418
- link=self.link,
419
- transport=PhysicalTransport.LE,
420
- link_type=HCI_Connection_Complete_Event.LinkType.ACL,
421
- )
422
- self.peripheral_connections[peer_address] = connection
423
- 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}')
424
570
 
425
571
  # Then say that the connection has completed
426
572
  self.send_hci_packet(
427
- HCI_LE_Connection_Complete_Event(
428
- status=HCI_SUCCESS,
573
+ hci.HCI_LE_Connection_Complete_Event(
574
+ status=hci.HCI_SUCCESS,
429
575
  connection_handle=connection.handle,
430
576
  role=connection.role,
431
- peer_address_type=peer_address_type,
577
+ peer_address_type=peer_address.address_type,
432
578
  peer_address=peer_address,
433
579
  connection_interval=10, # FIXME
434
580
  peripheral_latency=0, # FIXME
@@ -437,175 +583,199 @@ class Controller:
437
583
  )
438
584
  )
439
585
 
440
- def on_link_disconnected(self, peer_address: Address, reason: int) -> None:
441
- '''
442
- Called when an active disconnection occurs from a peer
443
- '''
444
-
445
- # Send a disconnection complete event
446
- if connection := self.peripheral_connections.get(peer_address):
586
+ if isinstance(advertiser, AdvertisingSet):
447
587
  self.send_hci_packet(
448
- HCI_Disconnection_Complete_Event(
449
- status=HCI_SUCCESS,
588
+ hci.HCI_LE_Advertising_Set_Terminated_Event(
589
+ status=hci.HCI_SUCCESS,
590
+ advertising_handle=advertiser.handle,
450
591
  connection_handle=connection.handle,
451
- reason=reason,
592
+ num_completed_extended_advertising_events=0,
452
593
  )
453
594
  )
595
+ advertiser.stop()
454
596
 
455
- # Remove the connection
456
- del self.peripheral_connections[peer_address]
457
- elif connection := self.central_connections.get(peer_address):
458
- self.send_hci_packet(
459
- HCI_Disconnection_Complete_Event(
460
- status=HCI_SUCCESS,
461
- connection_handle=connection.handle,
462
- reason=reason,
463
- )
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,
464
604
  )
605
+ )
465
606
 
466
- # Remove the connection
467
- del self.central_connections[peer_address]
468
- else:
469
- logger.warning(f'!!! No peripheral connection found for {peer_address}')
470
-
471
- def on_link_peripheral_connection_complete(
472
- self,
473
- le_create_connection_command: hci.HCI_LE_Create_Connection_Command,
474
- status: int,
475
- ) -> None:
607
+ def create_le_connection(self, peer_address: hci.Address) -> None:
476
608
  '''
477
- 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.
478
610
  '''
611
+ pending_le_connection = self.pending_le_connection
612
+ assert pending_le_connection
479
613
 
480
- if status == HCI_SUCCESS:
481
- # Allocate (or reuse) a connection handle
482
- peer_address = le_create_connection_command.peer_address
483
- connection = self.central_connections.get(peer_address)
484
- if connection is None:
485
- connection_handle = self.allocate_connection_handle()
486
- connection = Connection(
487
- controller=self,
488
- handle=connection_handle,
489
- role=Role.CENTRAL,
490
- peer_address=peer_address,
491
- link=self.link,
492
- transport=PhysicalTransport.LE,
493
- link_type=HCI_Connection_Complete_Event.LinkType.ACL,
494
- )
495
- self.central_connections[peer_address] = connection
496
- logger.debug(
497
- f'New CENTRAL connection handle: 0x{connection_handle:04X}'
498
- )
499
- else:
500
- connection = None
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}')
501
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]
646
+ else:
647
+ interval = pending_le_connection.connection_interval_min
648
+ latency = pending_le_connection.max_latency
649
+ timeout = pending_le_connection.supervision_timeout
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
+ )
502
660
  # Say that the connection has completed
503
661
  self.send_hci_packet(
504
662
  # pylint: disable=line-too-long
505
- HCI_LE_Connection_Complete_Event(
506
- status=status,
663
+ hci.HCI_LE_Connection_Complete_Event(
664
+ status=hci.HCI_SUCCESS,
507
665
  connection_handle=connection.handle if connection else 0,
508
- role=Role.CENTRAL,
509
- peer_address_type=le_create_connection_command.peer_address_type,
510
- peer_address=le_create_connection_command.peer_address,
511
- connection_interval=le_create_connection_command.connection_interval_min,
512
- peripheral_latency=le_create_connection_command.max_latency,
513
- supervision_timeout=le_create_connection_command.supervision_timeout,
666
+ role=hci.Role.CENTRAL,
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,
514
672
  central_clock_accuracy=0,
515
673
  )
516
674
  )
675
+ self.pending_le_connection = None
517
676
 
518
- def on_link_disconnection_complete(
519
- self, disconnection_command: hci.HCI_Disconnect_Command, status: int
520
- ) -> None:
521
- '''
522
- Called when a disconnection has been completed
523
- '''
524
-
525
- # 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
526
679
  self.send_hci_packet(
527
- HCI_Disconnection_Complete_Event(
528
- status=status,
529
- connection_handle=disconnection_command.connection_handle,
530
- reason=disconnection_command.reason,
680
+ hci.HCI_Encryption_Change_Event(
681
+ status=0, connection_handle=connection.handle, encryption_enabled=1
531
682
  )
532
683
  )
533
684
 
534
- # Remove the connection
535
- if connection := self.find_central_connection_by_handle(
536
- disconnection_command.connection_handle
537
- ):
538
- logger.debug(f'CENTRAL Connection removed: {connection}')
539
- del self.central_connections[connection.peer_address]
540
- elif connection := self.find_peripheral_connection_by_handle(
541
- disconnection_command.connection_handle
542
- ):
543
- logger.debug(f'PERIPHERAL Connection removed: {connection}')
544
- del self.peripheral_connections[connection.peer_address]
545
-
546
- def on_link_encrypted(
547
- self, peer_address: Address, _rand: int, _ediv: int, _ltk: bytes
548
- ) -> None:
549
- # For now, just setup the encryption without asking the host
550
- if connection := self.find_le_connection_by_address(peer_address):
551
- self.send_hci_packet(
552
- HCI_Encryption_Change_Event(
553
- status=0, connection_handle=connection.handle, encryption_enabled=1
554
- )
555
- )
556
-
557
685
  def on_link_acl_data(
558
- self, sender_address: Address, transport: PhysicalTransport, data: bytes
686
+ self, sender_address: hci.Address, transport: PhysicalTransport, data: bytes
559
687
  ) -> None:
560
688
  # Look for the connection to which this data belongs
561
689
  if transport == PhysicalTransport.LE:
562
- connection = self.find_le_connection_by_address(sender_address)
690
+ connection = self.le_connections.get(sender_address)
563
691
  else:
564
- connection = self.find_classic_connection_by_address(sender_address)
692
+ connection = self.classic_connections.get(sender_address)
565
693
  if connection is None:
566
694
  logger.warning(f'!!! no connection for {sender_address}')
567
695
  return
568
696
 
569
697
  # Send the data to the host
570
698
  # TODO: should fragment
571
- acl_packet = HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
699
+ acl_packet = hci.HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
572
700
  self.send_hci_packet(acl_packet)
573
701
 
574
- def on_link_advertising_data(self, sender_address: Address, data: bytes) -> None:
575
- # Ignore if we're not scanning
576
- if self.le_scan_enable == 0:
577
- 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
578
707
 
579
- # Send a scan report
580
- report = HCI_LE_Advertising_Report_Event.Report(
581
- event_type=HCI_LE_Advertising_Report_Event.EventType.ADV_IND,
582
- address_type=sender_address.address_type,
583
- address=sender_address,
584
- data=data,
585
- rssi=-50,
586
- )
587
- self.send_hci_packet(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]))
588
766
 
589
- # Simulate a scan response
590
- report = HCI_LE_Advertising_Report_Event.Report(
591
- event_type=HCI_LE_Advertising_Report_Event.EventType.SCAN_RSP,
592
- address_type=sender_address.address_type,
593
- address=sender_address,
594
- data=data,
595
- rssi=-50,
596
- )
597
- self.send_hci_packet(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)
598
772
 
599
- def on_link_cis_request(
600
- self, central_address: Address, cig_id: int, cis_id: int
773
+ def on_le_cis_request(
774
+ self, connection: Connection, cig_id: int, cis_id: int
601
775
  ) -> None:
602
776
  '''
603
777
  Called when an incoming CIS request occurs from a central on the link
604
778
  '''
605
-
606
- connection = self.peripheral_connections.get(central_address)
607
- assert connection
608
-
609
779
  pending_cis_link = CisLink(
610
780
  handle=self.allocate_connection_handle(),
611
781
  cis_id=cis_id,
@@ -615,7 +785,7 @@ class Controller:
615
785
  self.peripheral_cis_links[pending_cis_link.handle] = pending_cis_link
616
786
 
617
787
  self.send_hci_packet(
618
- HCI_LE_CIS_Request_Event(
788
+ hci.HCI_LE_CIS_Request_Event(
619
789
  acl_connection_handle=connection.handle,
620
790
  cis_connection_handle=pending_cis_link.handle,
621
791
  cig_id=cig_id,
@@ -623,7 +793,7 @@ class Controller:
623
793
  )
624
794
  )
625
795
 
626
- 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:
627
797
  '''
628
798
  Called when an incoming CIS established.
629
799
  '''
@@ -637,8 +807,8 @@ class Controller:
637
807
  )
638
808
 
639
809
  self.send_hci_packet(
640
- HCI_LE_CIS_Established_Event(
641
- status=HCI_SUCCESS,
810
+ hci.HCI_LE_CIS_Established_Event(
811
+ status=hci.HCI_SUCCESS,
642
812
  connection_handle=cis_link.handle,
643
813
  # CIS parameters are ignored.
644
814
  cig_sync_delay=0,
@@ -658,7 +828,7 @@ class Controller:
658
828
  )
659
829
  )
660
830
 
661
- 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:
662
832
  '''
663
833
  Called when a CIS disconnected.
664
834
  '''
@@ -681,16 +851,16 @@ class Controller:
681
851
  ),
682
852
  None,
683
853
  ):
684
- # Keep central CIS on disconnection. They should be removed by HCI_LE_Remove_CIG_Command.
854
+ # Keep central CIS on disconnection. They should be removed by hci.HCI_LE_Remove_CIG_Command.
685
855
  cis_link.acl_connection = None
686
856
  else:
687
857
  return
688
858
 
689
859
  self.send_hci_packet(
690
- HCI_Disconnection_Complete_Event(
691
- status=HCI_SUCCESS,
860
+ hci.HCI_Disconnection_Complete_Event(
861
+ status=hci.HCI_SUCCESS,
692
862
  connection_handle=cis_link.handle,
693
- reason=HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
863
+ reason=hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
694
864
  )
695
865
  )
696
866
 
@@ -698,11 +868,87 @@ class Controller:
698
868
  # Classic link connections
699
869
  ############################################################
700
870
 
871
+ def send_lmp_packet(
872
+ self, receiver_address: hci.Address, packet: lmp.Packet
873
+ ) -> asyncio.Future[int]:
874
+ loop = asyncio.get_running_loop()
875
+ assert self.link
876
+ self.link.send_lmp_packet(self, receiver_address, packet)
877
+ future = self.classic_pending_commands.setdefault(receiver_address, {})[
878
+ packet.opcode
879
+ ] = loop.create_future()
880
+ return future
881
+
882
+ def on_lmp_packet(self, sender_address: hci.Address, packet: lmp.Packet):
883
+ if isinstance(packet, (lmp.LmpAccepted, lmp.LmpAcceptedExt)):
884
+ if future := self.classic_pending_commands.setdefault(
885
+ sender_address, {}
886
+ ).get(packet.response_opcode):
887
+ future.set_result(hci.HCI_SUCCESS)
888
+ else:
889
+ logger.error("!!! Unhandled packet: %s", packet)
890
+ elif isinstance(packet, (lmp.LmpNotAccepted, lmp.LmpNotAcceptedExt)):
891
+ if future := self.classic_pending_commands.setdefault(
892
+ sender_address, {}
893
+ ).get(packet.response_opcode):
894
+ future.set_result(packet.error_code)
895
+ else:
896
+ logger.error("!!! Unhandled packet: %s", packet)
897
+ elif isinstance(packet, (lmp.LmpHostConnectionReq)):
898
+ self.on_classic_connection_request(
899
+ sender_address, hci.HCI_Connection_Complete_Event.LinkType.ACL
900
+ )
901
+ elif isinstance(packet, (lmp.LmpScoLinkReq)):
902
+ self.on_classic_connection_request(
903
+ sender_address, hci.HCI_Connection_Complete_Event.LinkType.SCO
904
+ )
905
+ elif isinstance(packet, (lmp.LmpEscoLinkReq)):
906
+ self.on_classic_connection_request(
907
+ sender_address, hci.HCI_Connection_Complete_Event.LinkType.ESCO
908
+ )
909
+ elif isinstance(packet, (lmp.LmpDetach)):
910
+ self.on_classic_disconnected(
911
+ sender_address, hci.HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
912
+ )
913
+ elif isinstance(packet, (lmp.LmpSwitchReq)):
914
+ self.on_classic_role_change_request(sender_address)
915
+ elif isinstance(packet, (lmp.LmpRemoveScoLinkReq, lmp.LmpRemoveEscoLinkReq)):
916
+ self.on_classic_sco_disconnected(sender_address, packet.error_code)
917
+ elif isinstance(packet, lmp.LmpNameReq):
918
+ self.on_classic_remote_name_request(sender_address, packet.name_offset)
919
+ elif isinstance(packet, lmp.LmpNameRes):
920
+ self.on_classic_remote_name_response(
921
+ sender_address,
922
+ packet.name_offset,
923
+ packet.name_length,
924
+ packet.name_fregment,
925
+ )
926
+ else:
927
+ logger.error("!!! Unhandled packet: %s", packet)
928
+
701
929
  def on_classic_connection_request(
702
- self, peer_address: Address, link_type: int
930
+ self, peer_address: hci.Address, link_type: int
703
931
  ) -> None:
932
+ if link_type == hci.HCI_Connection_Complete_Event.LinkType.ACL:
933
+ self.classic_connections[peer_address] = Connection(
934
+ controller=self,
935
+ handle=0,
936
+ role=hci.Role.PERIPHERAL,
937
+ self_address=self.public_address,
938
+ peer_address=peer_address,
939
+ link=self.link,
940
+ transport=PhysicalTransport.BR_EDR,
941
+ link_type=link_type,
942
+ classic_allow_role_switch=self.classic_allow_role_switch,
943
+ )
944
+ else:
945
+ self.sco_links[peer_address] = ScoLink(
946
+ handle=0,
947
+ link_type=link_type,
948
+ peer_address=peer_address,
949
+ )
704
950
  self.send_hci_packet(
705
- HCI_Connection_Request_Event(
951
+ hci.HCI_Connection_Request_Event(
706
952
  bd_addr=peer_address,
707
953
  class_of_device=0,
708
954
  link_type=link_type,
@@ -710,99 +956,127 @@ class Controller:
710
956
  )
711
957
 
712
958
  def on_classic_connection_complete(
713
- self, peer_address: Address, status: int
959
+ self, peer_address: hci.Address, status: int
714
960
  ) -> None:
715
- if status == HCI_SUCCESS:
961
+ if status == hci.HCI_SUCCESS:
716
962
  # Allocate (or reuse) a connection handle
717
963
  peer_address = peer_address
718
- connection = self.classic_connections.get(peer_address)
719
- if connection is None:
720
- connection_handle = self.allocate_connection_handle()
964
+ connection_handle = self.allocate_connection_handle()
965
+ if connection := self.classic_connections.get(peer_address):
966
+ connection.handle = connection_handle
967
+ else:
721
968
  connection = Connection(
722
969
  controller=self,
723
970
  handle=connection_handle,
724
- # Role doesn't matter in Classic because they are managed by HCI_Role_Change and HCI_Role_Discovery
725
- role=Role.CENTRAL,
971
+ role=hci.Role.CENTRAL,
972
+ self_address=self.public_address,
726
973
  peer_address=peer_address,
727
974
  link=self.link,
728
975
  transport=PhysicalTransport.BR_EDR,
729
- link_type=HCI_Connection_Complete_Event.LinkType.ACL,
976
+ link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
730
977
  )
731
978
  self.classic_connections[peer_address] = connection
732
979
  logger.debug(
733
980
  f'New CLASSIC connection handle: 0x{connection_handle:04X}'
734
981
  )
735
- else:
736
- connection_handle = connection.handle
737
982
  self.send_hci_packet(
738
- HCI_Connection_Complete_Event(
983
+ hci.HCI_Connection_Complete_Event(
739
984
  status=status,
740
985
  connection_handle=connection_handle,
741
986
  bd_addr=peer_address,
742
987
  encryption_enabled=False,
743
- link_type=HCI_Connection_Complete_Event.LinkType.ACL,
988
+ link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
744
989
  )
745
990
  )
746
991
  else:
747
992
  connection = None
748
993
  self.send_hci_packet(
749
- HCI_Connection_Complete_Event(
994
+ hci.HCI_Connection_Complete_Event(
750
995
  status=status,
751
996
  connection_handle=0,
752
997
  bd_addr=peer_address,
753
998
  encryption_enabled=False,
754
- link_type=HCI_Connection_Complete_Event.LinkType.ACL,
999
+ link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
755
1000
  )
756
1001
  )
757
1002
 
758
- def on_classic_disconnected(self, peer_address: Address, reason: int) -> None:
1003
+ def on_classic_disconnected(self, peer_address: hci.Address, reason: int) -> None:
759
1004
  # Send a disconnection complete event
760
- if connection := self.classic_connections.get(peer_address):
1005
+ if connection := self.classic_connections.pop(peer_address, None):
761
1006
  self.send_hci_packet(
762
- HCI_Disconnection_Complete_Event(
763
- status=HCI_SUCCESS,
1007
+ hci.HCI_Disconnection_Complete_Event(
1008
+ status=hci.HCI_SUCCESS,
764
1009
  connection_handle=connection.handle,
765
1010
  reason=reason,
766
1011
  )
767
1012
  )
1013
+ else:
1014
+ logger.warning(f'!!! No classic connection found for {peer_address}')
768
1015
 
769
- # Remove the connection
770
- del self.classic_connections[peer_address]
1016
+ def on_classic_sco_disconnected(
1017
+ self, peer_address: hci.Address, reason: int
1018
+ ) -> None:
1019
+ # Send a disconnection complete event
1020
+ if sco_link := self.sco_links.pop(peer_address, None):
1021
+ self.send_hci_packet(
1022
+ hci.HCI_Disconnection_Complete_Event(
1023
+ status=hci.HCI_SUCCESS,
1024
+ connection_handle=sco_link.handle,
1025
+ reason=reason,
1026
+ )
1027
+ )
771
1028
  else:
772
1029
  logger.warning(f'!!! No classic connection found for {peer_address}')
773
1030
 
774
- def on_classic_role_change(self, peer_address: Address, new_role: int) -> None:
1031
+ def on_classic_role_change_request(self, peer_address: hci.Address) -> None:
1032
+ assert (connection := self.classic_connections.get(peer_address))
1033
+ if not connection.classic_allow_role_switch:
1034
+ self.send_lmp_packet(
1035
+ peer_address,
1036
+ lmp.LmpNotAccepted(
1037
+ lmp.Opcode.LMP_SWITCH_REQ, hci.HCI_ROLE_CHANGE_NOT_ALLOWED_ERROR
1038
+ ),
1039
+ )
1040
+ else:
1041
+ self.send_lmp_packet(
1042
+ peer_address,
1043
+ lmp.LmpAccepted(lmp.Opcode.LMP_SWITCH_REQ),
1044
+ )
1045
+ self.classic_role_change(connection)
1046
+
1047
+ def classic_role_change(self, connection: Connection) -> None:
1048
+ new_role = (
1049
+ hci.Role.CENTRAL
1050
+ if connection.role == hci.Role.PERIPHERAL
1051
+ else hci.Role.PERIPHERAL
1052
+ )
1053
+ connection.role = new_role
775
1054
  self.send_hci_packet(
776
- HCI_Role_Change_Event(
777
- status=HCI_SUCCESS,
778
- bd_addr=peer_address,
1055
+ hci.HCI_Role_Change_Event(
1056
+ status=hci.HCI_SUCCESS,
1057
+ bd_addr=connection.peer_address,
779
1058
  new_role=new_role,
780
1059
  )
781
1060
  )
782
1061
 
783
1062
  def on_classic_sco_connection_complete(
784
- self, peer_address: Address, status: int, link_type: int
1063
+ self, peer_address: hci.Address, status: int, link_type: int
785
1064
  ) -> None:
786
- if status == HCI_SUCCESS:
1065
+ if status == hci.HCI_SUCCESS:
787
1066
  # Allocate (or reuse) a connection handle
788
1067
  connection_handle = self.allocate_connection_handle()
789
- connection = Connection(
790
- controller=self,
1068
+ sco_link = ScoLink(
791
1069
  handle=connection_handle,
792
- # Role doesn't matter in SCO.
793
- role=Role.CENTRAL,
794
- peer_address=peer_address,
795
- link=self.link,
796
- transport=PhysicalTransport.BR_EDR,
797
1070
  link_type=link_type,
1071
+ peer_address=peer_address,
798
1072
  )
799
- self.classic_connections[peer_address] = connection
1073
+ self.sco_links[peer_address] = sco_link
800
1074
  logger.debug(f'New SCO connection handle: 0x{connection_handle:04X}')
801
1075
  else:
802
1076
  connection_handle = 0
803
1077
 
804
1078
  self.send_hci_packet(
805
- HCI_Synchronous_Connection_Complete_Event(
1079
+ hci.HCI_Synchronous_Connection_Complete_Event(
806
1080
  status=status,
807
1081
  connection_handle=connection_handle,
808
1082
  bd_addr=peer_address,
@@ -816,47 +1090,53 @@ class Controller:
816
1090
  )
817
1091
  )
818
1092
 
819
- ############################################################
820
- # Advertising support
821
- ############################################################
822
- def on_advertising_timer_fired(self) -> None:
823
- self.send_advertising_data()
824
- self.advertising_timer_handle = asyncio.get_running_loop().call_later(
825
- self.advertising_interval / 1000.0, self.on_advertising_timer_fired
1093
+ def on_classic_remote_name_request(
1094
+ self, peer_address: hci.Address, name_offset: int
1095
+ ):
1096
+ self.send_lmp_packet(
1097
+ peer_address,
1098
+ lmp.LmpNameRes(
1099
+ name_offset=name_offset,
1100
+ name_length=len(self.local_name),
1101
+ name_fregment=self.local_name.encode('utf-8'),
1102
+ ),
826
1103
  )
827
1104
 
828
- def start_advertising(self) -> None:
829
- # Stop any ongoing advertising before we start again
830
- self.stop_advertising()
831
-
832
- # Advertise now
833
- self.advertising_timer_handle = asyncio.get_running_loop().call_soon(
834
- self.on_advertising_timer_fired
1105
+ def on_classic_remote_name_response(
1106
+ self,
1107
+ peer_address: hci.Address,
1108
+ name_offset: int,
1109
+ name_length: int,
1110
+ name_fregment: bytes,
1111
+ ):
1112
+ self.send_hci_packet(
1113
+ hci.HCI_Remote_Name_Request_Complete_Event(
1114
+ status=hci.HCI_SUCCESS,
1115
+ bd_addr=peer_address,
1116
+ remote_name=name_fregment,
1117
+ )
835
1118
  )
836
1119
 
837
- def stop_advertising(self) -> None:
838
- if self.advertising_timer_handle is not None:
839
- self.advertising_timer_handle.cancel()
840
- self.advertising_timer_handle = None
841
-
842
- def send_advertising_data(self) -> None:
843
- if self.link and self.advertising_data:
844
- self.link.send_advertising_data(self.random_address, self.advertising_data)
1120
+ ############################################################
1121
+ # Advertising support
1122
+ ############################################################
845
1123
 
846
1124
  @property
847
1125
  def is_advertising(self) -> bool:
848
- 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
+ )
849
1129
 
850
1130
  ############################################################
851
1131
  # HCI handlers
852
1132
  ############################################################
853
- def on_hci_command(self, command: hci.HCI_Command) -> Optional[bytes]:
1133
+ def on_hci_command(self, command: hci.HCI_Command) -> bytes | None:
854
1134
  logger.warning(color(f'--- Unsupported command {command}', 'red'))
855
- return bytes([HCI_UNKNOWN_HCI_COMMAND_ERROR])
1135
+ return bytes([hci.HCI_UNKNOWN_HCI_COMMAND_ERROR])
856
1136
 
857
1137
  def on_hci_create_connection_command(
858
1138
  self, command: hci.HCI_Create_Connection_Command
859
- ) -> Optional[bytes]:
1139
+ ) -> bytes | None:
860
1140
  '''
861
1141
  See Bluetooth spec Vol 4, Part E - 7.1.5 Create Connection command
862
1142
  '''
@@ -866,38 +1146,54 @@ class Controller:
866
1146
  logger.debug(f'Connection request to {command.bd_addr}')
867
1147
 
868
1148
  # Check that we don't already have a pending connection
869
- if self.link.get_pending_connection():
1149
+ if self.pending_le_connection:
870
1150
  self.send_hci_packet(
871
- HCI_Command_Status_Event(
872
- status=HCI_CONTROLLER_BUSY_ERROR,
1151
+ hci.HCI_Command_Status_Event(
1152
+ status=hci.HCI_CONTROLLER_BUSY_ERROR,
873
1153
  num_hci_command_packets=1,
874
1154
  command_opcode=command.op_code,
875
1155
  )
876
1156
  )
877
1157
  return None
878
1158
 
879
- self.link.classic_connect(self, command.bd_addr)
1159
+ self.classic_connections[command.bd_addr] = Connection(
1160
+ controller=self,
1161
+ handle=0,
1162
+ role=hci.Role.CENTRAL,
1163
+ self_address=self.public_address,
1164
+ peer_address=command.bd_addr,
1165
+ link=self.link,
1166
+ transport=PhysicalTransport.BR_EDR,
1167
+ link_type=hci.HCI_Connection_Complete_Event.LinkType.ACL,
1168
+ classic_allow_role_switch=bool(command.allow_role_switch),
1169
+ )
880
1170
 
881
1171
  # Say that the connection is pending
882
1172
  self.send_hci_packet(
883
- HCI_Command_Status_Event(
884
- status=HCI_COMMAND_STATUS_PENDING,
1173
+ hci.HCI_Command_Status_Event(
1174
+ status=hci.HCI_COMMAND_STATUS_PENDING,
885
1175
  num_hci_command_packets=1,
886
1176
  command_opcode=command.op_code,
887
1177
  )
888
1178
  )
1179
+ future = self.send_lmp_packet(command.bd_addr, lmp.LmpHostConnectionReq())
1180
+
1181
+ def on_response(future: asyncio.Future[int]):
1182
+ self.on_classic_connection_complete(command.bd_addr, future.result())
1183
+
1184
+ future.add_done_callback(on_response)
889
1185
  return None
890
1186
 
891
1187
  def on_hci_disconnect_command(
892
1188
  self, command: hci.HCI_Disconnect_Command
893
- ) -> Optional[bytes]:
1189
+ ) -> bytes | None:
894
1190
  '''
895
1191
  See Bluetooth spec Vol 4, Part E - 7.1.6 Disconnect Command
896
1192
  '''
897
1193
  # First, say that the disconnection is pending
898
1194
  self.send_hci_packet(
899
- HCI_Command_Status_Event(
900
- status=HCI_COMMAND_STATUS_PENDING,
1195
+ hci.HCI_Command_Status_Event(
1196
+ status=hci.HCI_COMMAND_STATUS_PENDING,
901
1197
  num_hci_command_packets=1,
902
1198
  command_opcode=command.op_code,
903
1199
  )
@@ -905,68 +1201,138 @@ class Controller:
905
1201
 
906
1202
  # Notify the link of the disconnection
907
1203
  handle = command.connection_handle
908
- if connection := self.find_central_connection_by_handle(handle):
909
- if self.link:
910
- self.link.disconnect(
911
- self.random_address, connection.peer_address, command
912
- )
913
- else:
914
- # Remove the connection
915
- del self.central_connections[connection.peer_address]
916
- elif connection := self.find_peripheral_connection_by_handle(handle):
1204
+ if connection := self.find_connection_by_handle(handle):
917
1205
  if self.link:
918
- self.link.disconnect(
919
- self.random_address, connection.peer_address, command
920
- )
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)
921
1217
  else:
922
1218
  # Remove the connection
923
- del self.peripheral_connections[connection.peer_address]
924
- elif connection := self.find_classic_connection_by_handle(handle):
1219
+ del self.classic_connections[connection.peer_address]
1220
+ elif sco_link := self.find_classic_sco_link_by_handle(handle):
925
1221
  if self.link:
926
- self.link.classic_disconnect(
927
- self,
928
- connection.peer_address,
929
- HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
930
- )
1222
+ if (
1223
+ sco_link.link_type
1224
+ == hci.HCI_Connection_Complete_Event.LinkType.ESCO
1225
+ ):
1226
+ self.send_lmp_packet(
1227
+ sco_link.peer_address,
1228
+ lmp.LmpRemoveScoLinkReq(
1229
+ sco_handle=0, error_code=command.reason
1230
+ ),
1231
+ )
1232
+ else:
1233
+ self.send_lmp_packet(
1234
+ sco_link.peer_address,
1235
+ lmp.LmpRemoveEscoLinkReq(
1236
+ esco_handle=0, error_code=command.reason
1237
+ ),
1238
+ )
1239
+ self.on_classic_sco_disconnected(sco_link.peer_address, command.reason)
931
1240
  else:
932
1241
  # Remove the connection
933
- del self.classic_connections[connection.peer_address]
1242
+ del self.sco_links[sco_link.peer_address]
934
1243
  elif cis_link := (
935
1244
  self.central_cis_links.get(handle) or self.peripheral_cis_links.get(handle)
936
1245
  ):
937
1246
  if self.link and cis_link.acl_connection:
938
- self.link.disconnect_cis(
939
- initiator_controller=self,
940
- peer_address=cis_link.acl_connection.peer_address,
941
- cig_id=cis_link.cig_id,
942
- 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
+ ),
943
1251
  )
1252
+ self.on_le_cis_disconnected(cis_link.cig_id, cis_link.cis_id)
944
1253
  # Spec requires handle to be kept after disconnection.
945
1254
 
946
1255
  return None
947
1256
 
948
1257
  def on_hci_accept_connection_request_command(
949
1258
  self, command: hci.HCI_Accept_Connection_Request_Command
950
- ) -> Optional[bytes]:
1259
+ ) -> bytes | None:
951
1260
  '''
952
1261
  See Bluetooth spec Vol 4, Part E - 7.1.8 Accept Connection Request command
953
1262
  '''
954
1263
 
955
1264
  if self.link is None:
956
1265
  return None
1266
+
1267
+ if not (connection := self.classic_connections.get(command.bd_addr)):
1268
+ self.send_hci_packet(
1269
+ hci.HCI_Command_Status_Event(
1270
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1271
+ num_hci_command_packets=1,
1272
+ command_opcode=command.op_code,
1273
+ )
1274
+ )
1275
+ return None
1276
+ self.send_hci_packet(
1277
+ hci.HCI_Command_Status_Event(
1278
+ status=hci.HCI_SUCCESS,
1279
+ num_hci_command_packets=1,
1280
+ command_opcode=command.op_code,
1281
+ )
1282
+ )
1283
+
1284
+ if command.role == hci.Role.CENTRAL:
1285
+ # Perform role switching before accept.
1286
+ future = self.send_lmp_packet(command.bd_addr, lmp.LmpSwitchReq())
1287
+
1288
+ def on_response(future: asyncio.Future[int]):
1289
+ if (status := future.result()) == hci.HCI_SUCCESS:
1290
+ self.classic_role_change(connection)
1291
+ # Continue connection setup.
1292
+ self.send_lmp_packet(
1293
+ command.bd_addr,
1294
+ lmp.LmpAccepted(lmp.Opcode.LMP_HOST_CONNECTION_REQ),
1295
+ )
1296
+ else:
1297
+ # Abort connection setup.
1298
+ self.send_lmp_packet(
1299
+ command.bd_addr,
1300
+ lmp.LmpNotAccepted(lmp.Opcode.LMP_HOST_CONNECTION_REQ, status),
1301
+ )
1302
+ self.on_classic_connection_complete(command.bd_addr, status)
1303
+
1304
+ future.add_done_callback(on_response)
1305
+
1306
+ else:
1307
+ # Simply accept connection.
1308
+ self.send_lmp_packet(
1309
+ command.bd_addr,
1310
+ lmp.LmpAccepted(lmp.Opcode.LMP_HOST_CONNECTION_REQ),
1311
+ )
1312
+ self.on_classic_connection_complete(command.bd_addr, hci.HCI_SUCCESS)
1313
+ return None
1314
+
1315
+ def on_hci_remote_name_request_command(
1316
+ self, command: hci.HCI_Remote_Name_Request_Command
1317
+ ) -> bytes | None:
1318
+ '''
1319
+ See Bluetooth spec Vol 4, Part E - 7.1.19 Remote Name Request command
1320
+ '''
957
1321
  self.send_hci_packet(
958
- HCI_Command_Status_Event(
959
- status=HCI_SUCCESS,
1322
+ hci.HCI_Command_Status_Event(
1323
+ status=hci.HCI_SUCCESS,
960
1324
  num_hci_command_packets=1,
961
1325
  command_opcode=command.op_code,
962
1326
  )
963
1327
  )
964
- self.link.classic_accept_connection(self, command.bd_addr, command.role)
1328
+
1329
+ self.send_lmp_packet(command.bd_addr, lmp.LmpNameReq(0))
1330
+
965
1331
  return None
966
1332
 
967
1333
  def on_hci_enhanced_setup_synchronous_connection_command(
968
1334
  self, command: hci.HCI_Enhanced_Setup_Synchronous_Connection_Command
969
- ) -> Optional[bytes]:
1335
+ ) -> bytes | None:
970
1336
  '''
971
1337
  See Bluetooth spec Vol 4, Part E - 7.1.45 Enhanced Setup Synchronous Connection command
972
1338
  '''
@@ -975,13 +1341,11 @@ class Controller:
975
1341
  return None
976
1342
 
977
1343
  if not (
978
- connection := self.find_classic_connection_by_handle(
979
- command.connection_handle
980
- )
1344
+ connection := self.find_connection_by_handle(command.connection_handle)
981
1345
  ):
982
1346
  self.send_hci_packet(
983
- HCI_Command_Status_Event(
984
- status=HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1347
+ hci.HCI_Command_Status_Event(
1348
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
985
1349
  num_hci_command_packets=1,
986
1350
  command_opcode=command.op_code,
987
1351
  )
@@ -989,20 +1353,43 @@ class Controller:
989
1353
  return None
990
1354
 
991
1355
  self.send_hci_packet(
992
- HCI_Command_Status_Event(
993
- status=HCI_SUCCESS,
1356
+ hci.HCI_Command_Status_Event(
1357
+ status=hci.HCI_SUCCESS,
994
1358
  num_hci_command_packets=1,
995
1359
  command_opcode=command.op_code,
996
1360
  )
997
1361
  )
998
- self.link.classic_sco_connect(
999
- self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
1362
+ future = self.send_lmp_packet(
1363
+ connection.peer_address,
1364
+ lmp.LmpEscoLinkReq(
1365
+ esco_handle=0,
1366
+ esco_lt_addr=0,
1367
+ timing_control_flags=0,
1368
+ d_esco=0,
1369
+ t_esco=0,
1370
+ w_esco=0,
1371
+ esco_packet_type_c_to_p=0,
1372
+ esco_packet_type_p_to_c=0,
1373
+ packet_length_c_to_p=0,
1374
+ packet_length_p_to_c=0,
1375
+ air_mode=0,
1376
+ negotiation_state=0,
1377
+ ),
1000
1378
  )
1379
+
1380
+ def on_response(future: asyncio.Future[int]):
1381
+ self.on_classic_sco_connection_complete(
1382
+ connection.peer_address,
1383
+ future.result(),
1384
+ hci.HCI_Connection_Complete_Event.LinkType.ESCO,
1385
+ )
1386
+
1387
+ future.add_done_callback(on_response)
1001
1388
  return None
1002
1389
 
1003
1390
  def on_hci_enhanced_accept_synchronous_connection_request_command(
1004
1391
  self, command: hci.HCI_Enhanced_Accept_Synchronous_Connection_Request_Command
1005
- ) -> Optional[bytes]:
1392
+ ) -> bytes | None:
1006
1393
  '''
1007
1394
  See Bluetooth spec Vol 4, Part E - 7.1.46 Enhanced Accept Synchronous Connection Request command
1008
1395
  '''
@@ -1010,10 +1397,10 @@ class Controller:
1010
1397
  if self.link is None:
1011
1398
  return None
1012
1399
 
1013
- if not (connection := self.find_classic_connection_by_address(command.bd_addr)):
1400
+ if not (connection := self.classic_connections.get(command.bd_addr)):
1014
1401
  self.send_hci_packet(
1015
- HCI_Command_Status_Event(
1016
- status=HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1402
+ hci.HCI_Command_Status_Event(
1403
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1017
1404
  num_hci_command_packets=1,
1018
1405
  command_opcode=command.op_code,
1019
1406
  )
@@ -1021,20 +1408,26 @@ class Controller:
1021
1408
  return None
1022
1409
 
1023
1410
  self.send_hci_packet(
1024
- HCI_Command_Status_Event(
1025
- status=HCI_SUCCESS,
1411
+ hci.HCI_Command_Status_Event(
1412
+ status=hci.HCI_SUCCESS,
1026
1413
  num_hci_command_packets=1,
1027
1414
  command_opcode=command.op_code,
1028
1415
  )
1029
1416
  )
1030
- self.link.classic_accept_sco_connection(
1031
- self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
1417
+ self.send_lmp_packet(
1418
+ connection.peer_address,
1419
+ lmp.LmpAcceptedExt(lmp.Opcode.LMP_ESCO_LINK_REQ),
1420
+ )
1421
+ self.on_classic_sco_connection_complete(
1422
+ connection.peer_address,
1423
+ hci.HCI_SUCCESS,
1424
+ hci.HCI_Connection_Complete_Event.LinkType.ESCO,
1032
1425
  )
1033
1426
  return None
1034
1427
 
1035
1428
  def on_hci_sniff_mode_command(
1036
1429
  self, command: hci.HCI_Sniff_Mode_Command
1037
- ) -> Optional[bytes]:
1430
+ ) -> bytes | None:
1038
1431
  '''
1039
1432
  See Bluetooth spec Vol 4, Part E - 7.2.2 Sniff Mode command
1040
1433
  '''
@@ -1050,14 +1443,14 @@ class Controller:
1050
1443
 
1051
1444
  self.send_hci_packet(
1052
1445
  hci.HCI_Command_Status_Event(
1053
- status=HCI_SUCCESS,
1446
+ status=hci.HCI_SUCCESS,
1054
1447
  num_hci_command_packets=1,
1055
1448
  command_opcode=command.op_code,
1056
1449
  )
1057
1450
  )
1058
1451
  self.send_hci_packet(
1059
1452
  hci.HCI_Mode_Change_Event(
1060
- status=HCI_SUCCESS,
1453
+ status=hci.HCI_SUCCESS,
1061
1454
  connection_handle=command.connection_handle,
1062
1455
  current_mode=hci.HCI_Mode_Change_Event.Mode.SNIFF,
1063
1456
  interval=2,
@@ -1067,7 +1460,7 @@ class Controller:
1067
1460
 
1068
1461
  def on_hci_exit_sniff_mode_command(
1069
1462
  self, command: hci.HCI_Exit_Sniff_Mode_Command
1070
- ) -> Optional[bytes]:
1463
+ ) -> bytes | None:
1071
1464
  '''
1072
1465
  See Bluetooth spec Vol 4, Part E - 7.2.3 Exit Sniff Mode command
1073
1466
  '''
@@ -1084,14 +1477,14 @@ class Controller:
1084
1477
 
1085
1478
  self.send_hci_packet(
1086
1479
  hci.HCI_Command_Status_Event(
1087
- status=HCI_SUCCESS,
1480
+ status=hci.HCI_SUCCESS,
1088
1481
  num_hci_command_packets=1,
1089
1482
  command_opcode=command.op_code,
1090
1483
  )
1091
1484
  )
1092
1485
  self.send_hci_packet(
1093
1486
  hci.HCI_Mode_Change_Event(
1094
- status=HCI_SUCCESS,
1487
+ status=hci.HCI_SUCCESS,
1095
1488
  connection_handle=command.connection_handle,
1096
1489
  current_mode=hci.HCI_Mode_Change_Event.Mode.ACTIVE,
1097
1490
  interval=2,
@@ -1101,44 +1494,82 @@ class Controller:
1101
1494
 
1102
1495
  def on_hci_switch_role_command(
1103
1496
  self, command: hci.HCI_Switch_Role_Command
1104
- ) -> Optional[bytes]:
1497
+ ) -> bytes | None:
1105
1498
  '''
1106
- See Bluetooth spec Vol 4, Part E - 7.2.8 Switch Role command
1499
+ See Bluetooth spec Vol 4, Part E - 7.2.8 Switch hci.Role command
1107
1500
  '''
1108
1501
 
1109
1502
  if self.link is None:
1110
1503
  return None
1111
- self.send_hci_packet(
1112
- HCI_Command_Status_Event(
1113
- status=HCI_SUCCESS,
1114
- num_hci_command_packets=1,
1115
- command_opcode=command.op_code,
1504
+
1505
+ if connection := self.classic_connections.get(command.bd_addr):
1506
+ current_role = connection.role
1507
+ self.send_hci_packet(
1508
+ hci.HCI_Command_Status_Event(
1509
+ status=hci.HCI_SUCCESS,
1510
+ num_hci_command_packets=1,
1511
+ command_opcode=command.op_code,
1512
+ )
1116
1513
  )
1117
- )
1118
- self.link.classic_switch_role(self, command.bd_addr, command.role)
1514
+ else:
1515
+ # Connection doesn't exist, reject.
1516
+ self.send_hci_packet(
1517
+ hci.HCI_Command_Status_Event(
1518
+ status=hci.HCI_COMMAND_DISALLOWED_ERROR,
1519
+ num_hci_command_packets=1,
1520
+ command_opcode=command.op_code,
1521
+ )
1522
+ )
1523
+ return None
1524
+
1525
+ # If role doesn't change, only send event to local host.
1526
+ if current_role == command.role:
1527
+ self.send_hci_packet(
1528
+ hci.HCI_Role_Change_Event(
1529
+ status=hci.HCI_SUCCESS,
1530
+ bd_addr=command.bd_addr,
1531
+ new_role=current_role,
1532
+ )
1533
+ )
1534
+ else:
1535
+ future = self.send_lmp_packet(command.bd_addr, lmp.LmpSwitchReq())
1536
+
1537
+ def on_response(future: asyncio.Future[int]):
1538
+ if (status := future.result()) == hci.HCI_SUCCESS:
1539
+ connection.role = hci.Role(command.role)
1540
+ self.send_hci_packet(
1541
+ hci.HCI_Role_Change_Event(
1542
+ status=status,
1543
+ bd_addr=command.bd_addr,
1544
+ new_role=connection.role,
1545
+ )
1546
+ )
1547
+
1548
+ future.add_done_callback(on_response)
1549
+
1119
1550
  return None
1120
1551
 
1121
1552
  def on_hci_set_event_mask_command(
1122
1553
  self, command: hci.HCI_Set_Event_Mask_Command
1123
- ) -> Optional[bytes]:
1554
+ ) -> bytes | None:
1124
1555
  '''
1125
1556
  See Bluetooth spec Vol 4, Part E - 7.3.1 Set Event Mask Command
1126
1557
  '''
1127
1558
  self.event_mask = int.from_bytes(
1128
1559
  command.event_mask, byteorder='little', signed=False
1129
1560
  )
1130
- return bytes([HCI_SUCCESS])
1561
+ return bytes([hci.HCI_SUCCESS])
1131
1562
 
1132
- 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:
1133
1564
  '''
1134
1565
  See Bluetooth spec Vol 4, Part E - 7.3.2 Reset Command
1135
1566
  '''
1136
1567
  # TODO: cleanup what needs to be reset
1137
- return bytes([HCI_SUCCESS])
1568
+ return bytes([hci.HCI_SUCCESS])
1138
1569
 
1139
1570
  def on_hci_write_local_name_command(
1140
1571
  self, command: hci.HCI_Write_Local_Name_Command
1141
- ) -> Optional[bytes]:
1572
+ ) -> bytes | None:
1142
1573
  '''
1143
1574
  See Bluetooth spec Vol 4, Part E - 7.3.11 Write Local Name Command
1144
1575
  '''
@@ -1151,11 +1582,11 @@ class Controller:
1151
1582
  self.local_name = str(local_name, 'utf-8')
1152
1583
  except UnicodeDecodeError:
1153
1584
  pass
1154
- return bytes([HCI_SUCCESS])
1585
+ return bytes([hci.HCI_SUCCESS])
1155
1586
 
1156
1587
  def on_hci_read_local_name_command(
1157
1588
  self, _command: hci.HCI_Read_Local_Name_Command
1158
- ) -> Optional[bytes]:
1589
+ ) -> bytes | None:
1159
1590
  '''
1160
1591
  See Bluetooth spec Vol 4, Part E - 7.3.12 Read Local Name Command
1161
1592
  '''
@@ -1163,27 +1594,27 @@ class Controller:
1163
1594
  if len(local_name) < 248:
1164
1595
  local_name = local_name + bytes(248 - len(local_name))
1165
1596
 
1166
- return bytes([HCI_SUCCESS]) + local_name
1597
+ return bytes([hci.HCI_SUCCESS]) + local_name
1167
1598
 
1168
1599
  def on_hci_read_class_of_device_command(
1169
1600
  self, _command: hci.HCI_Read_Class_Of_Device_Command
1170
- ) -> Optional[bytes]:
1601
+ ) -> bytes | None:
1171
1602
  '''
1172
1603
  See Bluetooth spec Vol 4, Part E - 7.3.25 Read Class of Device Command
1173
1604
  '''
1174
- return bytes([HCI_SUCCESS, 0, 0, 0])
1605
+ return bytes([hci.HCI_SUCCESS, 0, 0, 0])
1175
1606
 
1176
1607
  def on_hci_write_class_of_device_command(
1177
1608
  self, _command: hci.HCI_Write_Class_Of_Device_Command
1178
- ) -> Optional[bytes]:
1609
+ ) -> bytes | None:
1179
1610
  '''
1180
1611
  See Bluetooth spec Vol 4, Part E - 7.3.26 Write Class of Device Command
1181
1612
  '''
1182
- return bytes([HCI_SUCCESS])
1613
+ return bytes([hci.HCI_SUCCESS])
1183
1614
 
1184
1615
  def on_hci_read_synchronous_flow_control_enable_command(
1185
1616
  self, _command: hci.HCI_Read_Synchronous_Flow_Control_Enable_Command
1186
- ) -> Optional[bytes]:
1617
+ ) -> bytes | None:
1187
1618
  '''
1188
1619
  See Bluetooth spec Vol 4, Part E - 7.3.36 Read Synchronous Flow Control Enable
1189
1620
  Command
@@ -1192,109 +1623,109 @@ class Controller:
1192
1623
  ret = 1
1193
1624
  else:
1194
1625
  ret = 0
1195
- return bytes([HCI_SUCCESS, ret])
1626
+ return bytes([hci.HCI_SUCCESS, ret])
1196
1627
 
1197
1628
  def on_hci_write_synchronous_flow_control_enable_command(
1198
1629
  self, command: hci.HCI_Write_Synchronous_Flow_Control_Enable_Command
1199
- ) -> Optional[bytes]:
1630
+ ) -> bytes | None:
1200
1631
  '''
1201
1632
  See Bluetooth spec Vol 4, Part E - 7.3.37 Write Synchronous Flow Control Enable
1202
1633
  Command
1203
1634
  '''
1204
- ret = HCI_SUCCESS
1635
+ ret = hci.HCI_SUCCESS
1205
1636
  if command.synchronous_flow_control_enable == 1:
1206
1637
  self.sync_flow_control = True
1207
1638
  elif command.synchronous_flow_control_enable == 0:
1208
1639
  self.sync_flow_control = False
1209
1640
  else:
1210
- ret = HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR
1641
+ ret = hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR
1211
1642
  return bytes([ret])
1212
1643
 
1213
1644
  def on_hci_set_controller_to_host_flow_control_command(
1214
1645
  self, _command: hci.HCI_Set_Controller_To_Host_Flow_Control_Command
1215
- ) -> Optional[bytes]:
1646
+ ) -> bytes | None:
1216
1647
  '''
1217
1648
  See Bluetooth spec Vol 4, Part E - 7.3.38 Set Controller To Host Flow Control
1218
1649
  Command
1219
1650
  '''
1220
1651
  # For now we just accept the command but ignore the values.
1221
1652
  # TODO: respect the passed in values.
1222
- return bytes([HCI_SUCCESS])
1653
+ return bytes([hci.HCI_SUCCESS])
1223
1654
 
1224
1655
  def on_hci_host_buffer_size_command(
1225
1656
  self, _command: hci.HCI_Host_Buffer_Size_Command
1226
- ) -> Optional[bytes]:
1657
+ ) -> bytes | None:
1227
1658
  '''
1228
1659
  See Bluetooth spec Vol 4, Part E - 7.3.39 Host Buffer Size Command
1229
1660
  '''
1230
1661
  # For now we just accept the command but ignore the values.
1231
1662
  # TODO: respect the passed in values.
1232
- return bytes([HCI_SUCCESS])
1663
+ return bytes([hci.HCI_SUCCESS])
1233
1664
 
1234
1665
  def on_hci_write_extended_inquiry_response_command(
1235
1666
  self, _command: hci.HCI_Write_Extended_Inquiry_Response_Command
1236
- ) -> Optional[bytes]:
1667
+ ) -> bytes | None:
1237
1668
  '''
1238
1669
  See Bluetooth spec Vol 4, Part E - 7.3.56 Write Extended Inquiry Response
1239
1670
  Command
1240
1671
  '''
1241
- return bytes([HCI_SUCCESS])
1672
+ return bytes([hci.HCI_SUCCESS])
1242
1673
 
1243
1674
  def on_hci_write_simple_pairing_mode_command(
1244
1675
  self, _command: hci.HCI_Write_Simple_Pairing_Mode_Command
1245
- ) -> Optional[bytes]:
1676
+ ) -> bytes | None:
1246
1677
  '''
1247
1678
  See Bluetooth spec Vol 4, Part E - 7.3.59 Write Simple Pairing Mode Command
1248
1679
  '''
1249
- return bytes([HCI_SUCCESS])
1680
+ return bytes([hci.HCI_SUCCESS])
1250
1681
 
1251
1682
  def on_hci_set_event_mask_page_2_command(
1252
1683
  self, command: hci.HCI_Set_Event_Mask_Page_2_Command
1253
- ) -> Optional[bytes]:
1684
+ ) -> bytes | None:
1254
1685
  '''
1255
1686
  See Bluetooth spec Vol 4, Part E - 7.3.69 Set Event Mask Page 2 Command
1256
1687
  '''
1257
1688
  self.event_mask_page_2 = int.from_bytes(
1258
1689
  command.event_mask_page_2, byteorder='little', signed=False
1259
1690
  )
1260
- return bytes([HCI_SUCCESS])
1691
+ return bytes([hci.HCI_SUCCESS])
1261
1692
 
1262
1693
  def on_hci_read_le_host_support_command(
1263
1694
  self, _command: hci.HCI_Read_LE_Host_Support_Command
1264
- ) -> Optional[bytes]:
1695
+ ) -> bytes | None:
1265
1696
  '''
1266
1697
  See Bluetooth spec Vol 4, Part E - 7.3.78 Write LE Host Support Command
1267
1698
  '''
1268
- return bytes([HCI_SUCCESS, 1, 0])
1699
+ return bytes([hci.HCI_SUCCESS, 1, 0])
1269
1700
 
1270
1701
  def on_hci_write_le_host_support_command(
1271
1702
  self, _command: hci.HCI_Write_LE_Host_Support_Command
1272
- ) -> Optional[bytes]:
1703
+ ) -> bytes | None:
1273
1704
  '''
1274
1705
  See Bluetooth spec Vol 4, Part E - 7.3.79 Write LE Host Support Command
1275
1706
  '''
1276
1707
  # TODO / Just ignore for now
1277
- return bytes([HCI_SUCCESS])
1708
+ return bytes([hci.HCI_SUCCESS])
1278
1709
 
1279
1710
  def on_hci_write_authenticated_payload_timeout_command(
1280
1711
  self, command: hci.HCI_Write_Authenticated_Payload_Timeout_Command
1281
- ) -> Optional[bytes]:
1712
+ ) -> bytes | None:
1282
1713
  '''
1283
1714
  See Bluetooth spec Vol 4, Part E - 7.3.94 Write Authenticated Payload Timeout
1284
1715
  Command
1285
1716
  '''
1286
1717
  # TODO
1287
- return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
1718
+ return struct.pack('<BH', hci.HCI_SUCCESS, command.connection_handle)
1288
1719
 
1289
1720
  def on_hci_read_local_version_information_command(
1290
1721
  self, _command: hci.HCI_Read_Local_Version_Information_Command
1291
- ) -> Optional[bytes]:
1722
+ ) -> bytes | None:
1292
1723
  '''
1293
1724
  See Bluetooth spec Vol 4, Part E - 7.4.1 Read Local Version Information Command
1294
1725
  '''
1295
1726
  return struct.pack(
1296
1727
  '<BBHBHH',
1297
- HCI_SUCCESS,
1728
+ hci.HCI_SUCCESS,
1298
1729
  self.hci_version,
1299
1730
  self.hci_revision,
1300
1731
  self.lmp_version,
@@ -1304,33 +1735,33 @@ class Controller:
1304
1735
 
1305
1736
  def on_hci_read_local_supported_commands_command(
1306
1737
  self, _command: hci.HCI_Read_Local_Supported_Commands_Command
1307
- ) -> Optional[bytes]:
1738
+ ) -> bytes | None:
1308
1739
  '''
1309
1740
  See Bluetooth spec Vol 4, Part E - 7.4.2 Read Local Supported Commands Command
1310
1741
  '''
1311
- return bytes([HCI_SUCCESS]) + self.supported_commands
1742
+ return bytes([hci.HCI_SUCCESS]) + self.supported_commands
1312
1743
 
1313
1744
  def on_hci_read_local_supported_features_command(
1314
1745
  self, _command: hci.HCI_Read_Local_Supported_Features_Command
1315
- ) -> Optional[bytes]:
1746
+ ) -> bytes | None:
1316
1747
  '''
1317
1748
  See Bluetooth spec Vol 4, Part E - 7.4.3 Read Local Supported Features Command
1318
1749
  '''
1319
- return bytes([HCI_SUCCESS]) + self.lmp_features[:8]
1750
+ return bytes([hci.HCI_SUCCESS]) + self.lmp_features[:8]
1320
1751
 
1321
1752
  def on_hci_read_local_extended_features_command(
1322
1753
  self, command: hci.HCI_Read_Local_Extended_Features_Command
1323
- ) -> Optional[bytes]:
1754
+ ) -> bytes | None:
1324
1755
  '''
1325
1756
  See Bluetooth spec Vol 4, Part E - 7.4.4 Read Local Extended Features Command
1326
1757
  '''
1327
1758
  if command.page_number * 8 > len(self.lmp_features):
1328
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1759
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1329
1760
  return (
1330
1761
  bytes(
1331
1762
  [
1332
1763
  # Status
1333
- HCI_SUCCESS,
1764
+ hci.HCI_SUCCESS,
1334
1765
  # Page number
1335
1766
  command.page_number,
1336
1767
  # Max page number
@@ -1343,13 +1774,13 @@ class Controller:
1343
1774
 
1344
1775
  def on_hci_read_buffer_size_command(
1345
1776
  self, _command: hci.HCI_Read_Buffer_Size_Command
1346
- ) -> Optional[bytes]:
1777
+ ) -> bytes | None:
1347
1778
  '''
1348
1779
  See Bluetooth spec Vol 4, Part E - 7.4.5 Read Buffer Size Command
1349
1780
  '''
1350
1781
  return struct.pack(
1351
1782
  '<BHBHH',
1352
- HCI_SUCCESS,
1783
+ hci.HCI_SUCCESS,
1353
1784
  self.acl_data_packet_length,
1354
1785
  0,
1355
1786
  self.total_num_acl_data_packets,
@@ -1358,7 +1789,7 @@ class Controller:
1358
1789
 
1359
1790
  def on_hci_read_bd_addr_command(
1360
1791
  self, _command: hci.HCI_Read_BD_ADDR_Command
1361
- ) -> Optional[bytes]:
1792
+ ) -> bytes | None:
1362
1793
  '''
1363
1794
  See Bluetooth spec Vol 4, Part E - 7.4.6 Read BD_ADDR Command
1364
1795
  '''
@@ -1367,11 +1798,11 @@ class Controller:
1367
1798
  if self._public_address is not None
1368
1799
  else bytes(6)
1369
1800
  )
1370
- return bytes([HCI_SUCCESS]) + bd_addr
1801
+ return bytes([hci.HCI_SUCCESS]) + bd_addr
1371
1802
 
1372
1803
  def on_hci_le_set_default_subrate_command(
1373
1804
  self, command: hci.HCI_LE_Set_Default_Subrate_Command
1374
- ) -> Optional[bytes]:
1805
+ ) -> bytes | None:
1375
1806
  '''
1376
1807
  See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
1377
1808
  '''
@@ -1381,13 +1812,13 @@ class Controller:
1381
1812
  or command.subrate_max < command.subrate_min
1382
1813
  or command.continuation_number >= command.subrate_max
1383
1814
  ):
1384
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1815
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1385
1816
 
1386
- return bytes([HCI_SUCCESS])
1817
+ return bytes([hci.HCI_SUCCESS])
1387
1818
 
1388
1819
  def on_hci_le_subrate_request_command(
1389
1820
  self, command: hci.HCI_LE_Subrate_Request_Command
1390
- ) -> Optional[bytes]:
1821
+ ) -> bytes | None:
1391
1822
  '''
1392
1823
  See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
1393
1824
  '''
@@ -1397,7 +1828,7 @@ class Controller:
1397
1828
  or command.subrate_max < command.subrate_min
1398
1829
  or command.continuation_number >= command.subrate_max
1399
1830
  ):
1400
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1831
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1401
1832
 
1402
1833
  self.send_hci_packet(
1403
1834
  hci.HCI_Command_Status_Event(
@@ -1421,37 +1852,37 @@ class Controller:
1421
1852
 
1422
1853
  def on_hci_le_set_event_mask_command(
1423
1854
  self, command: hci.HCI_LE_Set_Event_Mask_Command
1424
- ) -> Optional[bytes]:
1855
+ ) -> bytes | None:
1425
1856
  '''
1426
1857
  See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
1427
1858
  '''
1428
1859
  self.le_event_mask = int.from_bytes(
1429
1860
  command.le_event_mask, byteorder='little', signed=False
1430
1861
  )
1431
- return bytes([HCI_SUCCESS])
1862
+ return bytes([hci.HCI_SUCCESS])
1432
1863
 
1433
1864
  def on_hci_le_read_buffer_size_command(
1434
1865
  self, _command: hci.HCI_LE_Read_Buffer_Size_Command
1435
- ) -> Optional[bytes]:
1866
+ ) -> bytes | None:
1436
1867
  '''
1437
1868
  See Bluetooth spec Vol 4, Part E - 7.8.2 LE Read Buffer Size Command
1438
1869
  '''
1439
1870
  return struct.pack(
1440
1871
  '<BHB',
1441
- HCI_SUCCESS,
1872
+ hci.HCI_SUCCESS,
1442
1873
  self.le_acl_data_packet_length,
1443
1874
  self.total_num_le_acl_data_packets,
1444
1875
  )
1445
1876
 
1446
1877
  def on_hci_le_read_buffer_size_v2_command(
1447
1878
  self, _command: hci.HCI_LE_Read_Buffer_Size_V2_Command
1448
- ) -> Optional[bytes]:
1879
+ ) -> bytes | None:
1449
1880
  '''
1450
1881
  See Bluetooth spec Vol 4, Part E - 7.8.2 LE Read Buffer Size Command
1451
1882
  '''
1452
1883
  return struct.pack(
1453
1884
  '<BHBHB',
1454
- HCI_SUCCESS,
1885
+ hci.HCI_SUCCESS,
1455
1886
  self.le_acl_data_packet_length,
1456
1887
  self.total_num_le_acl_data_packets,
1457
1888
  self.iso_data_packet_length,
@@ -1460,100 +1891,116 @@ class Controller:
1460
1891
 
1461
1892
  def on_hci_le_read_local_supported_features_command(
1462
1893
  self, _command: hci.HCI_LE_Read_Local_Supported_Features_Command
1463
- ) -> Optional[bytes]:
1894
+ ) -> bytes | None:
1464
1895
  '''
1465
1896
  See Bluetooth spec Vol 4, Part E - 7.8.3 LE Read Local Supported Features
1466
1897
  Command
1467
1898
  '''
1468
- return bytes([HCI_SUCCESS]) + self.le_features
1899
+ return bytes([hci.HCI_SUCCESS]) + self.le_features.value.to_bytes(8, 'little')
1469
1900
 
1470
1901
  def on_hci_le_set_random_address_command(
1471
1902
  self, command: hci.HCI_LE_Set_Random_Address_Command
1472
- ) -> Optional[bytes]:
1903
+ ) -> bytes | None:
1473
1904
  '''
1474
- See Bluetooth spec Vol 4, Part E - 7.8.4 LE Set Random Address Command
1905
+ See Bluetooth spec Vol 4, Part E - 7.8.4 LE Set Random hci.Address Command
1475
1906
  '''
1476
1907
  self.random_address = command.random_address
1477
- return bytes([HCI_SUCCESS])
1908
+ return bytes([hci.HCI_SUCCESS])
1478
1909
 
1479
1910
  def on_hci_le_set_advertising_parameters_command(
1480
1911
  self, command: hci.HCI_LE_Set_Advertising_Parameters_Command
1481
- ) -> Optional[bytes]:
1912
+ ) -> bytes | None:
1482
1913
  '''
1483
1914
  See Bluetooth spec Vol 4, Part E - 7.8.5 LE Set Advertising Parameters Command
1484
1915
  '''
1485
- self.advertising_parameters = command
1486
- return bytes([HCI_SUCCESS])
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
+ )
1932
+ return bytes([hci.HCI_SUCCESS])
1487
1933
 
1488
1934
  def on_hci_le_read_advertising_physical_channel_tx_power_command(
1489
1935
  self, _command: hci.HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command
1490
- ) -> Optional[bytes]:
1936
+ ) -> bytes | None:
1491
1937
  '''
1492
1938
  See Bluetooth spec Vol 4, Part E - 7.8.6 LE Read Advertising Physical Channel
1493
1939
  Tx Power Command
1494
1940
  '''
1495
- return bytes([HCI_SUCCESS, self.advertising_channel_tx_power])
1941
+ return bytes([hci.HCI_SUCCESS, self.advertising_channel_tx_power])
1496
1942
 
1497
1943
  def on_hci_le_set_advertising_data_command(
1498
1944
  self, command: hci.HCI_LE_Set_Advertising_Data_Command
1499
- ) -> Optional[bytes]:
1945
+ ) -> bytes | None:
1500
1946
  '''
1501
1947
  See Bluetooth spec Vol 4, Part E - 7.8.7 LE Set Advertising Data Command
1502
1948
  '''
1503
- self.advertising_data = command.advertising_data
1504
- return bytes([HCI_SUCCESS])
1949
+ self.le_legacy_advertiser.advertising_data = command.advertising_data
1950
+
1951
+ return bytes([hci.HCI_SUCCESS])
1505
1952
 
1506
1953
  def on_hci_le_set_scan_response_data_command(
1507
1954
  self, command: hci.HCI_LE_Set_Scan_Response_Data_Command
1508
- ) -> Optional[bytes]:
1955
+ ) -> bytes | None:
1509
1956
  '''
1510
1957
  See Bluetooth spec Vol 4, Part E - 7.8.8 LE Set Scan Response Data Command
1511
1958
  '''
1512
- self.le_scan_response_data = command.scan_response_data
1513
- return bytes([HCI_SUCCESS])
1959
+ self.le_legacy_advertiser.scan_response_data = command.scan_response_data
1960
+ return bytes([hci.HCI_SUCCESS])
1514
1961
 
1515
1962
  def on_hci_le_set_advertising_enable_command(
1516
1963
  self, command: hci.HCI_LE_Set_Advertising_Enable_Command
1517
- ) -> Optional[bytes]:
1964
+ ) -> bytes | None:
1518
1965
  '''
1519
1966
  See Bluetooth spec Vol 4, Part E - 7.8.9 LE Set Advertising Enable Command
1520
1967
  '''
1521
1968
  if command.advertising_enable:
1522
- self.start_advertising()
1969
+ self.le_legacy_advertiser.start()
1523
1970
  else:
1524
- self.stop_advertising()
1971
+ self.le_legacy_advertiser.stop()
1525
1972
 
1526
- return bytes([HCI_SUCCESS])
1973
+ return bytes([hci.HCI_SUCCESS])
1527
1974
 
1528
1975
  def on_hci_le_set_scan_parameters_command(
1529
1976
  self, command: hci.HCI_LE_Set_Scan_Parameters_Command
1530
- ) -> Optional[bytes]:
1977
+ ) -> bytes | None:
1531
1978
  '''
1532
1979
  See Bluetooth spec Vol 4, Part E - 7.8.10 LE Set Scan Parameters Command
1533
1980
  '''
1534
1981
  if self.le_scan_enable:
1535
- return bytes([HCI_COMMAND_DISALLOWED_ERROR])
1982
+ return bytes([hci.HCI_COMMAND_DISALLOWED_ERROR])
1536
1983
 
1537
1984
  self.le_scan_type = command.le_scan_type
1538
1985
  self.le_scan_interval = command.le_scan_interval
1539
1986
  self.le_scan_window = command.le_scan_window
1540
1987
  self.le_scan_own_address_type = hci.AddressType(command.own_address_type)
1541
1988
  self.le_scanning_filter_policy = command.scanning_filter_policy
1542
- return bytes([HCI_SUCCESS])
1989
+ return bytes([hci.HCI_SUCCESS])
1543
1990
 
1544
1991
  def on_hci_le_set_scan_enable_command(
1545
1992
  self, command: hci.HCI_LE_Set_Scan_Enable_Command
1546
- ) -> Optional[bytes]:
1993
+ ) -> bytes | None:
1547
1994
  '''
1548
1995
  See Bluetooth spec Vol 4, Part E - 7.8.11 LE Set Scan Enable Command
1549
1996
  '''
1550
1997
  self.le_scan_enable = bool(command.le_scan_enable)
1551
1998
  self.filter_duplicates = bool(command.filter_duplicates)
1552
- return bytes([HCI_SUCCESS])
1999
+ return bytes([hci.HCI_SUCCESS])
1553
2000
 
1554
2001
  def on_hci_le_create_connection_command(
1555
2002
  self, command: hci.HCI_LE_Create_Connection_Command
1556
- ) -> Optional[bytes]:
2003
+ ) -> bytes | None:
1557
2004
  '''
1558
2005
  See Bluetooth spec Vol 4, Part E - 7.8.12 LE Create Connection Command
1559
2006
  '''
@@ -1564,23 +2011,22 @@ class Controller:
1564
2011
  logger.debug(f'Connection request to {command.peer_address}')
1565
2012
 
1566
2013
  # Check that we don't already have a pending connection
1567
- if self.link.get_pending_connection():
2014
+ if self.pending_le_connection:
1568
2015
  self.send_hci_packet(
1569
- HCI_Command_Status_Event(
1570
- status=HCI_COMMAND_DISALLOWED_ERROR,
2016
+ hci.HCI_Command_Status_Event(
2017
+ status=hci.HCI_COMMAND_DISALLOWED_ERROR,
1571
2018
  num_hci_command_packets=1,
1572
2019
  command_opcode=command.op_code,
1573
2020
  )
1574
2021
  )
1575
2022
  return None
1576
2023
 
1577
- # Initiate the connection
1578
- self.link.connect(self.random_address, command)
2024
+ self.pending_le_connection = command
1579
2025
 
1580
2026
  # Say that the connection is pending
1581
2027
  self.send_hci_packet(
1582
- HCI_Command_Status_Event(
1583
- status=HCI_COMMAND_STATUS_PENDING,
2028
+ hci.HCI_Command_Status_Event(
2029
+ status=hci.HCI_COMMAND_STATUS_PENDING,
1584
2030
  num_hci_command_packets=1,
1585
2031
  command_opcode=command.op_code,
1586
2032
  )
@@ -1589,50 +2035,90 @@ class Controller:
1589
2035
 
1590
2036
  def on_hci_le_create_connection_cancel_command(
1591
2037
  self, _command: hci.HCI_LE_Create_Connection_Cancel_Command
1592
- ) -> Optional[bytes]:
2038
+ ) -> bytes | None:
1593
2039
  '''
1594
2040
  See Bluetooth spec Vol 4, Part E - 7.8.13 LE Create Connection Cancel Command
1595
2041
  '''
1596
- return bytes([HCI_SUCCESS])
2042
+ return bytes([hci.HCI_SUCCESS])
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
1597
2074
 
1598
2075
  def on_hci_le_read_filter_accept_list_size_command(
1599
2076
  self, _command: hci.HCI_LE_Read_Filter_Accept_List_Size_Command
1600
- ) -> Optional[bytes]:
2077
+ ) -> bytes | None:
1601
2078
  '''
1602
2079
  See Bluetooth spec Vol 4, Part E - 7.8.14 LE Read Filter Accept List Size
1603
2080
  Command
1604
2081
  '''
1605
- return bytes([HCI_SUCCESS, self.filter_accept_list_size])
2082
+ return bytes([hci.HCI_SUCCESS, self.filter_accept_list_size])
1606
2083
 
1607
2084
  def on_hci_le_clear_filter_accept_list_command(
1608
2085
  self, _command: hci.HCI_LE_Clear_Filter_Accept_List_Command
1609
- ) -> Optional[bytes]:
2086
+ ) -> bytes | None:
1610
2087
  '''
1611
2088
  See Bluetooth spec Vol 4, Part E - 7.8.15 LE Clear Filter Accept List Command
1612
2089
  '''
1613
- return bytes([HCI_SUCCESS])
2090
+ return bytes([hci.HCI_SUCCESS])
1614
2091
 
1615
2092
  def on_hci_le_add_device_to_filter_accept_list_command(
1616
2093
  self, _command: hci.HCI_LE_Add_Device_To_Filter_Accept_List_Command
1617
- ) -> Optional[bytes]:
2094
+ ) -> bytes | None:
1618
2095
  '''
1619
2096
  See Bluetooth spec Vol 4, Part E - 7.8.16 LE Add Device To Filter Accept List
1620
2097
  Command
1621
2098
  '''
1622
- return bytes([HCI_SUCCESS])
2099
+ return bytes([hci.HCI_SUCCESS])
1623
2100
 
1624
2101
  def on_hci_le_remove_device_from_filter_accept_list_command(
1625
2102
  self, _command: hci.HCI_LE_Remove_Device_From_Filter_Accept_List_Command
1626
- ) -> Optional[bytes]:
2103
+ ) -> bytes | None:
1627
2104
  '''
1628
2105
  See Bluetooth spec Vol 4, Part E - 7.8.17 LE Remove Device From Filter Accept
1629
2106
  List Command
1630
2107
  '''
1631
- return bytes([HCI_SUCCESS])
2108
+ return bytes([hci.HCI_SUCCESS])
2109
+
2110
+ def on_hci_write_scan_enable_command(
2111
+ self, command: hci.HCI_Write_Scan_Enable_Command
2112
+ ) -> bytes | None:
2113
+ '''
2114
+ See Bluetooth spec Vol 4, Part E - 7.3.18 Write Scan Enable Command
2115
+ '''
2116
+ self.classic_scan_enable = command.scan_enable
2117
+ return bytes([hci.HCI_SUCCESS])
1632
2118
 
1633
2119
  def on_hci_le_read_remote_features_command(
1634
2120
  self, command: hci.HCI_LE_Read_Remote_Features_Command
1635
- ) -> Optional[bytes]:
2121
+ ) -> bytes | None:
1636
2122
  '''
1637
2123
  See Bluetooth spec Vol 4, Part E - 7.8.21 LE Read Remote Features Command
1638
2124
  '''
@@ -1641,8 +2127,8 @@ class Controller:
1641
2127
 
1642
2128
  if not self.find_connection_by_handle(handle):
1643
2129
  self.send_hci_packet(
1644
- HCI_Command_Status_Event(
1645
- status=HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR,
2130
+ hci.HCI_Command_Status_Event(
2131
+ status=hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR,
1646
2132
  num_hci_command_packets=1,
1647
2133
  command_opcode=command.op_code,
1648
2134
  )
@@ -1651,8 +2137,8 @@ class Controller:
1651
2137
 
1652
2138
  # First, say that the command is pending
1653
2139
  self.send_hci_packet(
1654
- HCI_Command_Status_Event(
1655
- status=HCI_COMMAND_STATUS_PENDING,
2140
+ hci.HCI_Command_Status_Event(
2141
+ status=hci.HCI_COMMAND_STATUS_PENDING,
1656
2142
  num_hci_command_packets=1,
1657
2143
  command_opcode=command.op_code,
1658
2144
  )
@@ -1660,25 +2146,23 @@ class Controller:
1660
2146
 
1661
2147
  # Then send the remote features
1662
2148
  self.send_hci_packet(
1663
- HCI_LE_Read_Remote_Features_Complete_Event(
1664
- status=HCI_SUCCESS,
2149
+ hci.HCI_LE_Read_Remote_Features_Complete_Event(
2150
+ status=hci.HCI_SUCCESS,
1665
2151
  connection_handle=handle,
1666
2152
  le_features=bytes.fromhex('dd40000000000000'),
1667
2153
  )
1668
2154
  )
1669
2155
  return None
1670
2156
 
1671
- def on_hci_le_rand_command(
1672
- self, _command: hci.HCI_LE_Rand_Command
1673
- ) -> Optional[bytes]:
2157
+ def on_hci_le_rand_command(self, _command: hci.HCI_LE_Rand_Command) -> bytes | None:
1674
2158
  '''
1675
2159
  See Bluetooth spec Vol 4, Part E - 7.8.23 LE Rand Command
1676
2160
  '''
1677
- return bytes([HCI_SUCCESS]) + struct.pack('Q', random.randint(0, 1 << 64))
2161
+ return bytes([hci.HCI_SUCCESS]) + struct.pack('Q', random.randint(0, 1 << 64))
1678
2162
 
1679
2163
  def on_hci_le_enable_encryption_command(
1680
2164
  self, command: hci.HCI_LE_Enable_Encryption_Command
1681
- ) -> Optional[bytes]:
2165
+ ) -> bytes | None:
1682
2166
  '''
1683
2167
  See Bluetooth spec Vol 4, Part E - 7.8.24 LE Enable Encryption Command
1684
2168
  '''
@@ -1686,58 +2170,61 @@ class Controller:
1686
2170
  return None
1687
2171
 
1688
2172
  # Check the parameters
1689
- if not (
1690
- connection := self.find_central_connection_by_handle(
1691
- command.connection_handle
2173
+ if (
2174
+ not (
2175
+ connection := self.find_connection_by_handle(command.connection_handle)
1692
2176
  )
2177
+ or connection.transport != PhysicalTransport.LE
1693
2178
  ):
1694
2179
  logger.warning('connection not found')
1695
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
2180
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1696
2181
 
1697
- # Notify that the connection is now encrypted
1698
- self.link.on_connection_encrypted(
1699
- self.random_address,
1700
- connection.peer_address,
1701
- command.random_number,
1702
- command.encrypted_diversifier,
1703
- 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
+ ),
1704
2188
  )
1705
2189
 
1706
2190
  self.send_hci_packet(
1707
- HCI_Command_Status_Event(
1708
- status=HCI_COMMAND_STATUS_PENDING,
2191
+ hci.HCI_Command_Status_Event(
2192
+ status=hci.HCI_COMMAND_STATUS_PENDING,
1709
2193
  num_hci_command_packets=1,
1710
2194
  command_opcode=command.op_code,
1711
2195
  )
1712
2196
  )
1713
2197
 
2198
+ # TODO: Handle authentication
2199
+ self.on_le_encrypted(connection)
2200
+
1714
2201
  return None
1715
2202
 
1716
2203
  def on_hci_le_read_supported_states_command(
1717
2204
  self, _command: hci.HCI_LE_Read_Supported_States_Command
1718
- ) -> Optional[bytes]:
2205
+ ) -> bytes | None:
1719
2206
  '''
1720
2207
  See Bluetooth spec Vol 4, Part E - 7.8.27 LE Read Supported States Command
1721
2208
  '''
1722
- return bytes([HCI_SUCCESS]) + self.le_states
2209
+ return bytes([hci.HCI_SUCCESS]) + self.le_states
1723
2210
 
1724
2211
  def on_hci_le_read_suggested_default_data_length_command(
1725
2212
  self, _command: hci.HCI_LE_Read_Suggested_Default_Data_Length_Command
1726
- ) -> Optional[bytes]:
2213
+ ) -> bytes | None:
1727
2214
  '''
1728
2215
  See Bluetooth spec Vol 4, Part E - 7.8.34 LE Read Suggested Default Data Length
1729
2216
  Command
1730
2217
  '''
1731
2218
  return struct.pack(
1732
2219
  '<BHH',
1733
- HCI_SUCCESS,
2220
+ hci.HCI_SUCCESS,
1734
2221
  self.suggested_max_tx_octets,
1735
2222
  self.suggested_max_tx_time,
1736
2223
  )
1737
2224
 
1738
2225
  def on_hci_le_write_suggested_default_data_length_command(
1739
2226
  self, command: hci.HCI_LE_Write_Suggested_Default_Data_Length_Command
1740
- ) -> Optional[bytes]:
2227
+ ) -> bytes | None:
1741
2228
  '''
1742
2229
  See Bluetooth spec Vol 4, Part E - 7.8.35 LE Write Suggested Default Data Length
1743
2230
  Command
@@ -1745,77 +2232,77 @@ class Controller:
1745
2232
  self.suggested_max_tx_octets, self.suggested_max_tx_time = struct.unpack(
1746
2233
  '<HH', command.parameters[:4]
1747
2234
  )
1748
- return bytes([HCI_SUCCESS])
2235
+ return bytes([hci.HCI_SUCCESS])
1749
2236
 
1750
2237
  def on_hci_le_read_local_p_256_public_key_command(
1751
2238
  self, _command: hci.HCI_LE_Read_Local_P_256_Public_Key_Command
1752
- ) -> Optional[bytes]:
2239
+ ) -> bytes | None:
1753
2240
  '''
1754
2241
  See Bluetooth spec Vol 4, Part E - 7.8.36 LE Read P-256 Public Key Command
1755
2242
  '''
1756
- # TODO create key and send HCI_LE_Read_Local_P-256_Public_Key_Complete event
1757
- return bytes([HCI_SUCCESS])
2243
+ # TODO create key and send hci.HCI_LE_Read_Local_P-256_Public_Key_Complete event
2244
+ return bytes([hci.HCI_SUCCESS])
1758
2245
 
1759
2246
  def on_hci_le_add_device_to_resolving_list_command(
1760
2247
  self, _command: hci.HCI_LE_Add_Device_To_Resolving_List_Command
1761
- ) -> Optional[bytes]:
2248
+ ) -> bytes | None:
1762
2249
  '''
1763
2250
  See Bluetooth spec Vol 4, Part E - 7.8.38 LE Add Device To Resolving List
1764
2251
  Command
1765
2252
  '''
1766
- return bytes([HCI_SUCCESS])
2253
+ return bytes([hci.HCI_SUCCESS])
1767
2254
 
1768
2255
  def on_hci_le_clear_resolving_list_command(
1769
2256
  self, _command: hci.HCI_LE_Clear_Resolving_List_Command
1770
- ) -> Optional[bytes]:
2257
+ ) -> bytes | None:
1771
2258
  '''
1772
2259
  See Bluetooth spec Vol 4, Part E - 7.8.40 LE Clear Resolving List Command
1773
2260
  '''
1774
- return bytes([HCI_SUCCESS])
2261
+ return bytes([hci.HCI_SUCCESS])
1775
2262
 
1776
2263
  def on_hci_le_read_resolving_list_size_command(
1777
2264
  self, _command: hci.HCI_LE_Read_Resolving_List_Size_Command
1778
- ) -> Optional[bytes]:
2265
+ ) -> bytes | None:
1779
2266
  '''
1780
2267
  See Bluetooth spec Vol 4, Part E - 7.8.41 LE Read Resolving List Size Command
1781
2268
  '''
1782
- return bytes([HCI_SUCCESS, self.resolving_list_size])
2269
+ return bytes([hci.HCI_SUCCESS, self.resolving_list_size])
1783
2270
 
1784
2271
  def on_hci_le_set_address_resolution_enable_command(
1785
2272
  self, command: hci.HCI_LE_Set_Address_Resolution_Enable_Command
1786
- ) -> Optional[bytes]:
2273
+ ) -> bytes | None:
1787
2274
  '''
1788
- See Bluetooth spec Vol 4, Part E - 7.8.44 LE Set Address Resolution Enable
2275
+ See Bluetooth spec Vol 4, Part E - 7.8.44 LE Set hci.Address Resolution Enable
1789
2276
  Command
1790
2277
  '''
1791
- ret = HCI_SUCCESS
2278
+ ret = hci.HCI_SUCCESS
1792
2279
  if command.address_resolution_enable == 1:
1793
2280
  self.le_address_resolution = True
1794
2281
  elif command.address_resolution_enable == 0:
1795
2282
  self.le_address_resolution = False
1796
2283
  else:
1797
- ret = HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR
2284
+ ret = hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR
1798
2285
  return bytes([ret])
1799
2286
 
1800
2287
  def on_hci_le_set_resolvable_private_address_timeout_command(
1801
2288
  self, command: hci.HCI_LE_Set_Resolvable_Private_Address_Timeout_Command
1802
- ) -> Optional[bytes]:
2289
+ ) -> bytes | None:
1803
2290
  '''
1804
- See Bluetooth spec Vol 4, Part E - 7.8.45 LE Set Resolvable Private Address
2291
+ See Bluetooth spec Vol 4, Part E - 7.8.45 LE Set Resolvable Private hci.Address
1805
2292
  Timeout Command
1806
2293
  '''
1807
2294
  self.le_rpa_timeout = command.rpa_timeout
1808
- return bytes([HCI_SUCCESS])
2295
+ return bytes([hci.HCI_SUCCESS])
1809
2296
 
1810
2297
  def on_hci_le_read_maximum_data_length_command(
1811
2298
  self, _command: hci.HCI_LE_Read_Maximum_Data_Length_Command
1812
- ) -> Optional[bytes]:
2299
+ ) -> bytes | None:
1813
2300
  '''
1814
2301
  See Bluetooth spec Vol 4, Part E - 7.8.46 LE Read Maximum Data Length Command
1815
2302
  '''
1816
2303
  return struct.pack(
1817
2304
  '<BHHHH',
1818
- HCI_SUCCESS,
2305
+ hci.HCI_SUCCESS,
1819
2306
  self.supported_max_tx_octets,
1820
2307
  self.supported_max_tx_time,
1821
2308
  self.supported_max_rx_octets,
@@ -1824,130 +2311,207 @@ class Controller:
1824
2311
 
1825
2312
  def on_hci_le_read_phy_command(
1826
2313
  self, command: hci.HCI_LE_Read_PHY_Command
1827
- ) -> Optional[bytes]:
2314
+ ) -> bytes | None:
1828
2315
  '''
1829
2316
  See Bluetooth spec Vol 4, Part E - 7.8.47 LE Read PHY Command
1830
2317
  '''
1831
2318
  return struct.pack(
1832
2319
  '<BHBB',
1833
- HCI_SUCCESS,
2320
+ hci.HCI_SUCCESS,
1834
2321
  command.connection_handle,
1835
- HCI_LE_1M_PHY,
1836
- HCI_LE_1M_PHY,
2322
+ hci.HCI_LE_1M_PHY,
2323
+ hci.HCI_LE_1M_PHY,
1837
2324
  )
1838
2325
 
1839
2326
  def on_hci_le_set_default_phy_command(
1840
2327
  self, command: hci.HCI_LE_Set_Default_PHY_Command
1841
- ) -> Optional[bytes]:
2328
+ ) -> bytes | None:
1842
2329
  '''
1843
2330
  See Bluetooth spec Vol 4, Part E - 7.8.48 LE Set Default PHY Command
1844
2331
  '''
1845
2332
  self.default_phy['all_phys'] = command.all_phys
1846
2333
  self.default_phy['tx_phys'] = command.tx_phys
1847
2334
  self.default_phy['rx_phys'] = command.rx_phys
1848
- return bytes([HCI_SUCCESS])
2335
+ return bytes([hci.HCI_SUCCESS])
1849
2336
 
1850
2337
  def on_hci_le_set_advertising_set_random_address_command(
1851
- self, _command: hci.HCI_LE_Set_Advertising_Set_Random_Address_Command
1852
- ) -> Optional[bytes]:
2338
+ self, command: hci.HCI_LE_Set_Advertising_Set_Random_Address_Command
2339
+ ) -> bytes | None:
1853
2340
  '''
1854
- See Bluetooth spec Vol 4, Part E - 7.8.52 LE Set Advertising Set Random Address
2341
+ See Bluetooth spec Vol 4, Part E - 7.8.52 LE Set Advertising Set Random hci.Address
1855
2342
  Command
1856
2343
  '''
1857
- return bytes([HCI_SUCCESS])
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
2350
+ return bytes([hci.HCI_SUCCESS])
1858
2351
 
1859
2352
  def on_hci_le_set_extended_advertising_parameters_command(
1860
- self, _command: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command
1861
- ) -> Optional[bytes]:
2353
+ self, command: hci.HCI_LE_Set_Extended_Advertising_Parameters_Command
2354
+ ) -> bytes | None:
1862
2355
  '''
1863
2356
  See Bluetooth spec Vol 4, Part E - 7.8.53 LE Set Extended Advertising Parameters
1864
2357
  Command
1865
2358
  '''
1866
- return bytes([HCI_SUCCESS, 0])
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
2366
+ return bytes([hci.HCI_SUCCESS, 0])
1867
2367
 
1868
2368
  def on_hci_le_set_extended_advertising_data_command(
1869
- self, _command: hci.HCI_LE_Set_Extended_Advertising_Data_Command
1870
- ) -> Optional[bytes]:
2369
+ self, command: hci.HCI_LE_Set_Extended_Advertising_Data_Command
2370
+ ) -> bytes | None:
1871
2371
  '''
1872
2372
  See Bluetooth spec Vol 4, Part E - 7.8.54 LE Set Extended Advertising Data
1873
2373
  Command
1874
2374
  '''
1875
- return bytes([HCI_SUCCESS])
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
+
2390
+ return bytes([hci.HCI_SUCCESS])
1876
2391
 
1877
2392
  def on_hci_le_set_extended_scan_response_data_command(
1878
- self, _command: hci.HCI_LE_Set_Extended_Scan_Response_Data_Command
1879
- ) -> Optional[bytes]:
2393
+ self, command: hci.HCI_LE_Set_Extended_Scan_Response_Data_Command
2394
+ ) -> bytes | None:
1880
2395
  '''
1881
2396
  See Bluetooth spec Vol 4, Part E - 7.8.55 LE Set Extended Scan Response Data
1882
2397
  Command
1883
2398
  '''
1884
- return bytes([HCI_SUCCESS])
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
+
2414
+ return bytes([hci.HCI_SUCCESS])
1885
2415
 
1886
2416
  def on_hci_le_set_extended_advertising_enable_command(
1887
- self, _command: hci.HCI_LE_Set_Extended_Advertising_Enable_Command
1888
- ) -> Optional[bytes]:
2417
+ self, command: hci.HCI_LE_Set_Extended_Advertising_Enable_Command
2418
+ ) -> bytes | None:
1889
2419
  '''
1890
2420
  See Bluetooth spec Vol 4, Part E - 7.8.56 LE Set Extended Advertising Enable
1891
2421
  Command
1892
2422
  '''
1893
- return bytes([HCI_SUCCESS])
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()
2457
+ return bytes([hci.HCI_SUCCESS])
1894
2458
 
1895
2459
  def on_hci_le_read_maximum_advertising_data_length_command(
1896
2460
  self, _command: hci.HCI_LE_Read_Maximum_Advertising_Data_Length_Command
1897
- ) -> Optional[bytes]:
2461
+ ) -> bytes | None:
1898
2462
  '''
1899
2463
  See Bluetooth spec Vol 4, Part E - 7.8.57 LE Read Maximum Advertising Data
1900
2464
  Length Command
1901
2465
  '''
1902
- return struct.pack('<BH', HCI_SUCCESS, 0x0672)
2466
+ return struct.pack('<BH', hci.HCI_SUCCESS, 0x0672)
1903
2467
 
1904
2468
  def on_hci_le_read_number_of_supported_advertising_sets_command(
1905
2469
  self, _command: hci.HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command
1906
- ) -> Optional[bytes]:
2470
+ ) -> bytes | None:
1907
2471
  '''
1908
2472
  See Bluetooth spec Vol 4, Part E - 7.8.58 LE Read Number of Supported
1909
2473
  Advertising Set Command
1910
2474
  '''
1911
- return struct.pack('<BB', HCI_SUCCESS, 0xF0)
2475
+ return struct.pack('<BB', hci.HCI_SUCCESS, 0xF0)
1912
2476
 
1913
2477
  def on_hci_le_set_periodic_advertising_parameters_command(
1914
2478
  self, _command: hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command
1915
- ) -> Optional[bytes]:
2479
+ ) -> bytes | None:
1916
2480
  '''
1917
2481
  See Bluetooth spec Vol 4, Part E - 7.8.61 LE Set Periodic Advertising Parameters
1918
2482
  Command
1919
2483
  '''
1920
- return bytes([HCI_SUCCESS])
2484
+ return bytes([hci.HCI_SUCCESS])
1921
2485
 
1922
2486
  def on_hci_le_set_periodic_advertising_data_command(
1923
2487
  self, _command: hci.HCI_LE_Set_Periodic_Advertising_Data_Command
1924
- ) -> Optional[bytes]:
2488
+ ) -> bytes | None:
1925
2489
  '''
1926
2490
  See Bluetooth spec Vol 4, Part E - 7.8.62 LE Set Periodic Advertising Data
1927
2491
  Command
1928
2492
  '''
1929
- return bytes([HCI_SUCCESS])
2493
+ return bytes([hci.HCI_SUCCESS])
1930
2494
 
1931
2495
  def on_hci_le_set_periodic_advertising_enable_command(
1932
2496
  self, _command: hci.HCI_LE_Set_Periodic_Advertising_Enable_Command
1933
- ) -> Optional[bytes]:
2497
+ ) -> bytes | None:
1934
2498
  '''
1935
2499
  See Bluetooth spec Vol 4, Part E - 7.8.63 LE Set Periodic Advertising Enable
1936
2500
  Command
1937
2501
  '''
1938
- return bytes([HCI_SUCCESS])
2502
+ return bytes([hci.HCI_SUCCESS])
1939
2503
 
1940
2504
  def on_hci_le_read_transmit_power_command(
1941
2505
  self, _command: hci.HCI_LE_Read_Transmit_Power_Command
1942
- ) -> Optional[bytes]:
2506
+ ) -> bytes | None:
1943
2507
  '''
1944
2508
  See Bluetooth spec Vol 4, Part E - 7.8.74 LE Read Transmit Power Command
1945
2509
  '''
1946
- return struct.pack('<BBB', HCI_SUCCESS, 0, 0)
2510
+ return struct.pack('<BBB', hci.HCI_SUCCESS, 0, 0)
1947
2511
 
1948
2512
  def on_hci_le_set_cig_parameters_command(
1949
2513
  self, command: hci.HCI_LE_Set_CIG_Parameters_Command
1950
- ) -> Optional[bytes]:
2514
+ ) -> bytes | None:
1951
2515
  '''
1952
2516
  See Bluetooth spec Vol 4, Part E - 7.8.97 LE Set CIG Parameter Command
1953
2517
  '''
@@ -1968,12 +2532,12 @@ class Controller:
1968
2532
  handle=handle,
1969
2533
  )
1970
2534
  return struct.pack(
1971
- '<BBB', HCI_SUCCESS, command.cig_id, len(handles)
2535
+ '<BBB', hci.HCI_SUCCESS, command.cig_id, len(handles)
1972
2536
  ) + b''.join([struct.pack('<H', handle) for handle in handles])
1973
2537
 
1974
2538
  def on_hci_le_create_cis_command(
1975
2539
  self, command: hci.HCI_LE_Create_CIS_Command
1976
- ) -> Optional[bytes]:
2540
+ ) -> bytes | None:
1977
2541
  '''
1978
2542
  See Bluetooth spec Vol 4, Part E - 7.8.99 LE Create CIS Command
1979
2543
  '''
@@ -1985,24 +2549,21 @@ class Controller:
1985
2549
  ):
1986
2550
  if not (connection := self.find_connection_by_handle(acl_handle)):
1987
2551
  logger.error(f'Cannot find connection with handle={acl_handle}')
1988
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
2552
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1989
2553
 
1990
2554
  if not (cis_link := self.central_cis_links.get(cis_handle)):
1991
2555
  logger.error(f'Cannot find CIS with handle={cis_handle}')
1992
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
2556
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1993
2557
 
1994
2558
  cis_link.acl_connection = connection
1995
2559
 
1996
- self.link.create_cis(
1997
- self,
1998
- peripheral_address=connection.peer_address,
1999
- cig_id=cis_link.cig_id,
2000
- 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)
2001
2562
  )
2002
2563
 
2003
2564
  self.send_hci_packet(
2004
- HCI_Command_Status_Event(
2005
- status=HCI_COMMAND_STATUS_PENDING,
2565
+ hci.HCI_Command_Status_Event(
2566
+ status=hci.HCI_COMMAND_STATUS_PENDING,
2006
2567
  num_hci_command_packets=1,
2007
2568
  command_opcode=command.op_code,
2008
2569
  )
@@ -2011,24 +2572,24 @@ class Controller:
2011
2572
 
2012
2573
  def on_hci_le_remove_cig_command(
2013
2574
  self, command: hci.HCI_LE_Remove_CIG_Command
2014
- ) -> Optional[bytes]:
2575
+ ) -> bytes | None:
2015
2576
  '''
2016
2577
  See Bluetooth spec Vol 4, Part E - 7.8.100 LE Remove CIG Command
2017
2578
  '''
2018
2579
 
2019
- status = HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR
2580
+ status = hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR
2020
2581
 
2021
2582
  cis_links = list(self.central_cis_links.items())
2022
2583
  for cis_handle, cis_link in cis_links:
2023
2584
  if cis_link.cig_id == command.cig_id:
2024
2585
  self.central_cis_links.pop(cis_handle)
2025
- status = HCI_SUCCESS
2586
+ status = hci.HCI_SUCCESS
2026
2587
 
2027
2588
  return struct.pack('<BH', status, command.cig_id)
2028
2589
 
2029
2590
  def on_hci_le_accept_cis_request_command(
2030
2591
  self, command: hci.HCI_LE_Accept_CIS_Request_Command
2031
- ) -> Optional[bytes]:
2592
+ ) -> bytes | None:
2032
2593
  '''
2033
2594
  See Bluetooth spec Vol 4, Part E - 7.8.101 LE Accept CIS Request Command
2034
2595
  '''
@@ -2039,19 +2600,16 @@ class Controller:
2039
2600
  pending_cis_link := self.peripheral_cis_links.get(command.connection_handle)
2040
2601
  ):
2041
2602
  logger.error(f'Cannot find CIS with handle={command.connection_handle}')
2042
- return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
2603
+ return bytes([hci.HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
2043
2604
 
2044
2605
  assert pending_cis_link.acl_connection
2045
- self.link.accept_cis(
2046
- peripheral_controller=self,
2047
- central_address=pending_cis_link.acl_connection.peer_address,
2048
- cig_id=pending_cis_link.cig_id,
2049
- 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),
2050
2608
  )
2051
2609
 
2052
2610
  self.send_hci_packet(
2053
- HCI_Command_Status_Event(
2054
- status=HCI_COMMAND_STATUS_PENDING,
2611
+ hci.HCI_Command_Status_Event(
2612
+ status=hci.HCI_COMMAND_STATUS_PENDING,
2055
2613
  num_hci_command_packets=1,
2056
2614
  command_opcode=command.op_code,
2057
2615
  )
@@ -2060,35 +2618,35 @@ class Controller:
2060
2618
 
2061
2619
  def on_hci_le_setup_iso_data_path_command(
2062
2620
  self, command: hci.HCI_LE_Setup_ISO_Data_Path_Command
2063
- ) -> Optional[bytes]:
2621
+ ) -> bytes | None:
2064
2622
  '''
2065
2623
  See Bluetooth spec Vol 4, Part E - 7.8.109 LE Setup ISO Data Path Command
2066
2624
  '''
2067
2625
  if not (iso_link := self.find_iso_link_by_handle(command.connection_handle)):
2068
2626
  return struct.pack(
2069
2627
  '<BH',
2070
- HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
2628
+ hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
2071
2629
  command.connection_handle,
2072
2630
  )
2073
2631
  if command.data_path_direction in iso_link.data_paths:
2074
2632
  return struct.pack(
2075
2633
  '<BH',
2076
- HCI_COMMAND_DISALLOWED_ERROR,
2634
+ hci.HCI_COMMAND_DISALLOWED_ERROR,
2077
2635
  command.connection_handle,
2078
2636
  )
2079
2637
  iso_link.data_paths.add(command.data_path_direction)
2080
- return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
2638
+ return struct.pack('<BH', hci.HCI_SUCCESS, command.connection_handle)
2081
2639
 
2082
2640
  def on_hci_le_remove_iso_data_path_command(
2083
2641
  self, command: hci.HCI_LE_Remove_ISO_Data_Path_Command
2084
- ) -> Optional[bytes]:
2642
+ ) -> bytes | None:
2085
2643
  '''
2086
2644
  See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
2087
2645
  '''
2088
2646
  if not (iso_link := self.find_iso_link_by_handle(command.connection_handle)):
2089
2647
  return struct.pack(
2090
2648
  '<BH',
2091
- HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
2649
+ hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
2092
2650
  command.connection_handle,
2093
2651
  )
2094
2652
  data_paths: set[int] = set(
@@ -2099,16 +2657,16 @@ class Controller:
2099
2657
  if not data_paths.issubset(iso_link.data_paths):
2100
2658
  return struct.pack(
2101
2659
  '<BH',
2102
- HCI_COMMAND_DISALLOWED_ERROR,
2660
+ hci.HCI_COMMAND_DISALLOWED_ERROR,
2103
2661
  command.connection_handle,
2104
2662
  )
2105
2663
  iso_link.data_paths.difference_update(data_paths)
2106
- return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
2664
+ return struct.pack('<BH', hci.HCI_SUCCESS, command.connection_handle)
2107
2665
 
2108
2666
  def on_hci_le_set_host_feature_command(
2109
2667
  self, _command: hci.HCI_LE_Set_Host_Feature_Command
2110
- ) -> Optional[bytes]:
2668
+ ) -> bytes | None:
2111
2669
  '''
2112
2670
  See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
2113
2671
  '''
2114
- return bytes([HCI_SUCCESS])
2672
+ return bytes([hci.HCI_SUCCESS])