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/link.py CHANGED
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ from __future__ import annotations
14
15
 
15
16
  import asyncio
16
17
 
@@ -18,18 +19,12 @@ import asyncio
18
19
  # Imports
19
20
  # -----------------------------------------------------------------------------
20
21
  import logging
21
- from typing import Optional
22
+ from typing import TYPE_CHECKING
22
23
 
23
- from bumble import controller, core
24
- from bumble.hci import (
25
- HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR,
26
- HCI_PAGE_TIMEOUT_ERROR,
27
- HCI_SUCCESS,
28
- HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
29
- Address,
30
- HCI_Connection_Complete_Event,
31
- Role,
32
- )
24
+ from bumble import core, hci, ll, lmp
25
+
26
+ if TYPE_CHECKING:
27
+ from bumble import controller
33
28
 
34
29
  # -----------------------------------------------------------------------------
35
30
  # Logging
@@ -37,18 +32,6 @@ from bumble.hci import (
37
32
  logger = logging.getLogger(__name__)
38
33
 
39
34
 
40
- # -----------------------------------------------------------------------------
41
- # Utils
42
- # -----------------------------------------------------------------------------
43
- def parse_parameters(params_str):
44
- result = {}
45
- for param_str in params_str.split(','):
46
- if '=' in param_str:
47
- key, value = param_str.split('=')
48
- result[key] = value
49
- return result
50
-
51
-
52
35
  # -----------------------------------------------------------------------------
53
36
  # TODO: add more support for various LL exchanges
54
37
  # (see Vol 6, Part B - 2.4 DATA CHANNEL PDU)
@@ -62,37 +45,34 @@ class LocalLink:
62
45
 
63
46
  def __init__(self):
64
47
  self.controllers = set()
65
- self.pending_connection = None
66
48
  self.pending_classic_connection = None
67
49
 
68
50
  ############################################################
69
51
  # Common utils
70
52
  ############################################################
71
53
 
72
- def add_controller(self, controller):
54
+ def add_controller(self, controller: controller.Controller):
73
55
  logger.debug(f'new controller: {controller}')
74
56
  self.controllers.add(controller)
75
57
 
76
- def remove_controller(self, controller):
58
+ def remove_controller(self, controller: controller.Controller):
77
59
  self.controllers.remove(controller)
78
60
 
79
- def find_controller(self, address):
61
+ def find_le_controller(self, address: hci.Address) -> controller.Controller | None:
80
62
  for controller in self.controllers:
81
- if controller.random_address == address:
82
- return controller
63
+ for connection in controller.le_connections.values():
64
+ if connection.self_address == address:
65
+ return controller
83
66
  return None
84
67
 
85
68
  def find_classic_controller(
86
- self, address: Address
87
- ) -> Optional[controller.Controller]:
69
+ self, address: hci.Address
70
+ ) -> controller.Controller | None:
88
71
  for controller in self.controllers:
89
72
  if controller.public_address == address:
90
73
  return controller
91
74
  return None
92
75
 
93
- def get_pending_connection(self):
94
- return self.pending_connection
95
-
96
76
  ############################################################
97
77
  # LE handlers
98
78
  ############################################################
@@ -100,16 +80,16 @@ class LocalLink:
100
80
  def on_address_changed(self, controller):
101
81
  pass
102
82
 
103
- def send_advertising_data(self, sender_address, data):
104
- # Send the advertising data to all controllers, except the sender
105
- for controller in self.controllers:
106
- if controller.random_address != sender_address:
107
- controller.on_link_advertising_data(sender_address, data)
108
-
109
- def send_acl_data(self, sender_controller, destination_address, transport, data):
83
+ def send_acl_data(
84
+ self,
85
+ sender_controller: controller.Controller,
86
+ destination_address: hci.Address,
87
+ transport: core.PhysicalTransport,
88
+ data: bytes,
89
+ ):
110
90
  # Send the data to the first controller with a matching address
111
91
  if transport == core.PhysicalTransport.LE:
112
- destination_controller = self.find_controller(destination_address)
92
+ destination_controller = self.find_le_controller(destination_address)
113
93
  source_address = sender_controller.random_address
114
94
  elif transport == core.PhysicalTransport.BR_EDR:
115
95
  destination_controller = self.find_classic_controller(destination_address)
@@ -118,262 +98,52 @@ class LocalLink:
118
98
  raise ValueError("unsupported transport type")
119
99
 
120
100
  if destination_controller is not None:
121
- destination_controller.on_link_acl_data(source_address, transport, data)
122
-
123
- def on_connection_complete(self):
124
- # Check that we expect this call
125
- if not self.pending_connection:
126
- logger.warning('on_connection_complete with no pending connection')
127
- return
128
-
129
- central_address, le_create_connection_command = self.pending_connection
130
- self.pending_connection = None
131
-
132
- # Find the controller that initiated the connection
133
- if not (central_controller := self.find_controller(central_address)):
134
- logger.warning('!!! Initiating controller not found')
135
- return
136
-
137
- # Connect to the first controller with a matching address
138
- if peripheral_controller := self.find_controller(
139
- le_create_connection_command.peer_address
140
- ):
141
- central_controller.on_link_peripheral_connection_complete(
142
- le_create_connection_command, HCI_SUCCESS
143
- )
144
- peripheral_controller.on_link_central_connected(central_address)
145
- return
146
-
147
- # No peripheral found
148
- central_controller.on_link_peripheral_connection_complete(
149
- le_create_connection_command, HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR
150
- )
151
-
152
- def connect(self, central_address, le_create_connection_command):
153
- logger.debug(
154
- f'$$$ CONNECTION {central_address} -> '
155
- f'{le_create_connection_command.peer_address}'
156
- )
157
- self.pending_connection = (central_address, le_create_connection_command)
158
- asyncio.get_running_loop().call_soon(self.on_connection_complete)
159
-
160
- def on_disconnection_complete(
161
- self, initiating_address, target_address, disconnect_command
162
- ):
163
- # Find the controller that initiated the disconnection
164
- if not (initiating_controller := self.find_controller(initiating_address)):
165
- logger.warning('!!! Initiating controller not found')
166
- return
167
-
168
- # Disconnect from the first controller with a matching address
169
- if target_controller := self.find_controller(target_address):
170
- target_controller.on_link_disconnected(
171
- initiating_address, disconnect_command.reason
172
- )
173
-
174
- initiating_controller.on_link_disconnection_complete(
175
- disconnect_command, HCI_SUCCESS
176
- )
177
-
178
- def disconnect(self, initiating_address, target_address, disconnect_command):
179
- logger.debug(
180
- f'$$$ DISCONNECTION {initiating_address} -> '
181
- f'{target_address}: reason = {disconnect_command.reason}'
182
- )
183
- args = [initiating_address, target_address, disconnect_command]
184
- asyncio.get_running_loop().call_soon(self.on_disconnection_complete, *args)
185
-
186
- # pylint: disable=too-many-arguments
187
- def on_connection_encrypted(
188
- self, central_address, peripheral_address, rand, ediv, ltk
189
- ):
190
- logger.debug(f'*** ENCRYPTION {central_address} -> {peripheral_address}')
191
-
192
- if central_controller := self.find_controller(central_address):
193
- central_controller.on_link_encrypted(peripheral_address, rand, ediv, ltk)
194
-
195
- if peripheral_controller := self.find_controller(peripheral_address):
196
- peripheral_controller.on_link_encrypted(central_address, rand, ediv, ltk)
197
-
198
- def create_cis(
199
- self,
200
- central_controller: controller.Controller,
201
- peripheral_address: Address,
202
- cig_id: int,
203
- cis_id: int,
204
- ) -> None:
205
- logger.debug(
206
- f'$$$ CIS Request {central_controller.random_address} -> {peripheral_address}'
207
- )
208
- if peripheral_controller := self.find_controller(peripheral_address):
209
101
  asyncio.get_running_loop().call_soon(
210
- peripheral_controller.on_link_cis_request,
211
- central_controller.random_address,
212
- cig_id,
213
- cis_id,
102
+ lambda: destination_controller.on_link_acl_data(
103
+ source_address, transport, data
104
+ )
214
105
  )
215
106
 
216
- def accept_cis(
107
+ def send_advertising_pdu(
217
108
  self,
218
- peripheral_controller: controller.Controller,
219
- central_address: Address,
220
- cig_id: int,
221
- cis_id: int,
222
- ) -> None:
223
- logger.debug(
224
- f'$$$ CIS Accept {peripheral_controller.random_address} -> {central_address}'
225
- )
226
- if central_controller := self.find_controller(central_address):
227
- asyncio.get_running_loop().call_soon(
228
- central_controller.on_link_cis_established, cig_id, cis_id
229
- )
230
- asyncio.get_running_loop().call_soon(
231
- peripheral_controller.on_link_cis_established, cig_id, cis_id
232
- )
109
+ sender_controller: controller.Controller,
110
+ packet: ll.AdvertisingPdu,
111
+ ):
112
+ loop = asyncio.get_running_loop()
113
+ for c in self.controllers:
114
+ if c != sender_controller:
115
+ loop.call_soon(c.on_ll_advertising_pdu, packet)
233
116
 
234
- def disconnect_cis(
117
+ def send_ll_control_pdu(
235
118
  self,
236
- initiator_controller: controller.Controller,
237
- peer_address: Address,
238
- cig_id: int,
239
- cis_id: int,
240
- ) -> None:
241
- logger.debug(
242
- f'$$$ CIS Disconnect {initiator_controller.random_address} -> {peer_address}'
243
- )
244
- if peer_controller := self.find_controller(peer_address):
245
- asyncio.get_running_loop().call_soon(
246
- initiator_controller.on_link_cis_disconnected, cig_id, cis_id
247
- )
248
- asyncio.get_running_loop().call_soon(
249
- peer_controller.on_link_cis_disconnected, cig_id, cis_id
119
+ sender_address: hci.Address,
120
+ receiver_address: hci.Address,
121
+ packet: ll.ControlPdu,
122
+ ):
123
+ if not (receiver_controller := self.find_le_controller(receiver_address)):
124
+ raise core.InvalidArgumentError(
125
+ f"Unable to find controller for address {receiver_address}"
250
126
  )
127
+ asyncio.get_running_loop().call_soon(
128
+ lambda: receiver_controller.on_ll_control_pdu(sender_address, packet)
129
+ )
251
130
 
252
131
  ############################################################
253
132
  # Classic handlers
254
133
  ############################################################
255
134
 
256
- def classic_connect(self, initiator_controller, responder_address):
257
- logger.debug(
258
- f'[Classic] {initiator_controller.public_address} connects to {responder_address}'
259
- )
260
- responder_controller = self.find_classic_controller(responder_address)
261
- if responder_controller is None:
262
- initiator_controller.on_classic_connection_complete(
263
- responder_address, HCI_PAGE_TIMEOUT_ERROR
264
- )
265
- return
266
- self.pending_classic_connection = (initiator_controller, responder_controller)
267
-
268
- responder_controller.on_classic_connection_request(
269
- initiator_controller.public_address,
270
- HCI_Connection_Complete_Event.LinkType.ACL,
271
- )
272
-
273
- def classic_accept_connection(
274
- self, responder_controller, initiator_address, responder_role
275
- ):
276
- logger.debug(
277
- f'[Classic] {responder_controller.public_address} accepts to connect {initiator_address}'
278
- )
279
- initiator_controller = self.find_classic_controller(initiator_address)
280
- if initiator_controller is None:
281
- responder_controller.on_classic_connection_complete(
282
- responder_controller.public_address, HCI_PAGE_TIMEOUT_ERROR
283
- )
284
- return
285
-
286
- async def task():
287
- if responder_role != Role.PERIPHERAL:
288
- initiator_controller.on_classic_role_change(
289
- responder_controller.public_address, int(not (responder_role))
290
- )
291
- initiator_controller.on_classic_connection_complete(
292
- responder_controller.public_address, HCI_SUCCESS
293
- )
294
-
295
- asyncio.create_task(task())
296
- responder_controller.on_classic_role_change(
297
- initiator_controller.public_address, responder_role
298
- )
299
- responder_controller.on_classic_connection_complete(
300
- initiator_controller.public_address, HCI_SUCCESS
301
- )
302
- self.pending_classic_connection = None
303
-
304
- def classic_disconnect(self, initiator_controller, responder_address, reason):
305
- logger.debug(
306
- f'[Classic] {initiator_controller.public_address} disconnects {responder_address}'
307
- )
308
- responder_controller = self.find_classic_controller(responder_address)
309
-
310
- async def task():
311
- initiator_controller.on_classic_disconnected(responder_address, reason)
312
-
313
- asyncio.create_task(task())
314
- responder_controller.on_classic_disconnected(
315
- initiator_controller.public_address, reason
316
- )
317
-
318
- def classic_switch_role(
319
- self, initiator_controller, responder_address, initiator_new_role
320
- ):
321
- responder_controller = self.find_classic_controller(responder_address)
322
- if responder_controller is None:
323
- return
324
-
325
- async def task():
326
- initiator_controller.on_classic_role_change(
327
- responder_address, initiator_new_role
328
- )
329
-
330
- asyncio.create_task(task())
331
- responder_controller.on_classic_role_change(
332
- initiator_controller.public_address, int(not (initiator_new_role))
333
- )
334
-
335
- def classic_sco_connect(
336
- self,
337
- initiator_controller: controller.Controller,
338
- responder_address: Address,
339
- link_type: int,
340
- ):
341
- logger.debug(
342
- f'[Classic] {initiator_controller.public_address} connects SCO to {responder_address}'
343
- )
344
- responder_controller = self.find_classic_controller(responder_address)
345
- # Initiator controller should handle it.
346
- assert responder_controller
347
-
348
- responder_controller.on_classic_connection_request(
349
- initiator_controller.public_address,
350
- link_type,
351
- )
352
-
353
- def classic_accept_sco_connection(
135
+ def send_lmp_packet(
354
136
  self,
355
- responder_controller: controller.Controller,
356
- initiator_address: Address,
357
- link_type: int,
137
+ sender_controller: controller.Controller,
138
+ receiver_address: hci.Address,
139
+ packet: lmp.Packet,
358
140
  ):
359
- logger.debug(
360
- f'[Classic] {responder_controller.public_address} accepts to connect SCO {initiator_address}'
361
- )
362
- initiator_controller = self.find_classic_controller(initiator_address)
363
- if initiator_controller is None:
364
- responder_controller.on_classic_sco_connection_complete(
365
- responder_controller.public_address,
366
- HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
367
- link_type,
141
+ if not (receiver_controller := self.find_classic_controller(receiver_address)):
142
+ raise core.InvalidArgumentError(
143
+ f"Unable to find controller for address {receiver_address}"
368
144
  )
369
- return
370
-
371
- async def task():
372
- initiator_controller.on_classic_sco_connection_complete(
373
- responder_controller.public_address, HCI_SUCCESS, link_type
145
+ asyncio.get_running_loop().call_soon(
146
+ lambda: receiver_controller.on_lmp_packet(
147
+ sender_controller.public_address, packet
374
148
  )
375
-
376
- asyncio.create_task(task())
377
- responder_controller.on_classic_sco_connection_complete(
378
- initiator_controller.public_address, HCI_SUCCESS, link_type
379
149
  )
bumble/ll.py ADDED
@@ -0,0 +1,200 @@
1
+ # Copyright 2021-2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # -----------------------------------------------------------------------------
16
+ # Imports
17
+ # -----------------------------------------------------------------------------
18
+ from __future__ import annotations
19
+
20
+ import dataclasses
21
+ from typing import ClassVar
22
+
23
+ from bumble import hci
24
+
25
+
26
+ # -----------------------------------------------------------------------------
27
+ # Advertising PDU
28
+ # -----------------------------------------------------------------------------
29
+ class AdvertisingPdu:
30
+ """Base Advertising Physical Channel PDU class.
31
+
32
+ See Core Spec 6.0, Volume 6, Part B, 2.3. Advertising physical channel PDU.
33
+
34
+ Currently these messages don't really follow the LL spec, because LL protocol is
35
+ context-aware and we don't have real physical transport.
36
+ """
37
+
38
+
39
+ @dataclasses.dataclass
40
+ class ConnectInd(AdvertisingPdu):
41
+ initiator_address: hci.Address
42
+ advertiser_address: hci.Address
43
+ interval: int
44
+ latency: int
45
+ timeout: int
46
+
47
+
48
+ @dataclasses.dataclass
49
+ class AdvInd(AdvertisingPdu):
50
+ advertiser_address: hci.Address
51
+ data: bytes
52
+
53
+
54
+ @dataclasses.dataclass
55
+ class AdvDirectInd(AdvertisingPdu):
56
+ advertiser_address: hci.Address
57
+ target_address: hci.Address
58
+
59
+
60
+ @dataclasses.dataclass
61
+ class AdvNonConnInd(AdvertisingPdu):
62
+ advertiser_address: hci.Address
63
+ data: bytes
64
+
65
+
66
+ @dataclasses.dataclass
67
+ class AdvExtInd(AdvertisingPdu):
68
+ advertiser_address: hci.Address
69
+ data: bytes
70
+
71
+ target_address: hci.Address | None = None
72
+ adi: int | None = None
73
+ tx_power: int | None = None
74
+
75
+
76
+ # -----------------------------------------------------------------------------
77
+ # LL Control PDU
78
+ # -----------------------------------------------------------------------------
79
+ class ControlPdu:
80
+ """Base LL Control PDU Class.
81
+
82
+ See Core Spec 6.0, Volume 6, Part B, 2.4.2. LL Control PDU.
83
+
84
+ Currently these messages don't really follow the LL spec, because LL protocol is
85
+ context-aware and we don't have real physical transport.
86
+ """
87
+
88
+ class Opcode(hci.SpecableEnum):
89
+ LL_CONNECTION_UPDATE_IND = 0x00
90
+ LL_CHANNEL_MAP_IND = 0x01
91
+ LL_TERMINATE_IND = 0x02
92
+ LL_ENC_REQ = 0x03
93
+ LL_ENC_RSP = 0x04
94
+ LL_START_ENC_REQ = 0x05
95
+ LL_START_ENC_RSP = 0x06
96
+ LL_UNKNOWN_RSP = 0x07
97
+ LL_FEATURE_REQ = 0x08
98
+ LL_FEATURE_RSP = 0x09
99
+ LL_PAUSE_ENC_REQ = 0x0A
100
+ LL_PAUSE_ENC_RSP = 0x0B
101
+ LL_VERSION_IND = 0x0C
102
+ LL_REJECT_IND = 0x0D
103
+ LL_PERIPHERAL_FEATURE_REQ = 0x0E
104
+ LL_CONNECTION_PARAM_REQ = 0x0F
105
+ LL_CONNECTION_PARAM_RSP = 0x10
106
+ LL_REJECT_EXT_IND = 0x11
107
+ LL_PING_REQ = 0x12
108
+ LL_PING_RSP = 0x13
109
+ LL_LENGTH_REQ = 0x14
110
+ LL_LENGTH_RSP = 0x15
111
+ LL_PHY_REQ = 0x16
112
+ LL_PHY_RSP = 0x17
113
+ LL_PHY_UPDATE_IND = 0x18
114
+ LL_MIN_USED_CHANNELS_IND = 0x19
115
+ LL_CTE_REQ = 0x1A
116
+ LL_CTE_RSP = 0x1B
117
+ LL_PERIODIC_SYNC_IND = 0x1C
118
+ LL_CLOCK_ACCURACY_REQ = 0x1D
119
+ LL_CLOCK_ACCURACY_RSP = 0x1E
120
+ LL_CIS_REQ = 0x1F
121
+ LL_CIS_RSP = 0x20
122
+ LL_CIS_IND = 0x21
123
+ LL_CIS_TERMINATE_IND = 0x22
124
+ LL_POWER_CONTROL_REQ = 0x23
125
+ LL_POWER_CONTROL_RSP = 0x24
126
+ LL_POWER_CHANGE_IND = 0x25
127
+ LL_SUBRATE_REQ = 0x26
128
+ LL_SUBRATE_IND = 0x27
129
+ LL_CHANNEL_REPORTING_IND = 0x28
130
+ LL_CHANNEL_STATUS_IND = 0x29
131
+ LL_PERIODIC_SYNC_WR_IND = 0x2A
132
+ LL_FEATURE_EXT_REQ = 0x2B
133
+ LL_FEATURE_EXT_RSP = 0x2C
134
+ LL_CS_SEC_RSP = 0x2D
135
+ LL_CS_CAPABILITIES_REQ = 0x2E
136
+ LL_CS_CAPABILITIES_RSP = 0x2F
137
+ LL_CS_CONFIG_REQ = 0x30
138
+ LL_CS_CONFIG_RSP = 0x31
139
+ LL_CS_REQ = 0x32
140
+ LL_CS_RSP = 0x33
141
+ LL_CS_IND = 0x34
142
+ LL_CS_TERMINATE_REQ = 0x35
143
+ LL_CS_FAE_REQ = 0x36
144
+ LL_CS_FAE_RSP = 0x37
145
+ LL_CS_CHANNEL_MAP_IND = 0x38
146
+ LL_CS_SEC_REQ = 0x39
147
+ LL_CS_TERMINATE_RSP = 0x3A
148
+ LL_FRAME_SPACE_REQ = 0x3B
149
+ LL_FRAME_SPACE_RSP = 0x3C
150
+
151
+ opcode: ClassVar[Opcode]
152
+
153
+
154
+ @dataclasses.dataclass
155
+ class TerminateInd(ControlPdu):
156
+ opcode = ControlPdu.Opcode.LL_TERMINATE_IND
157
+
158
+ error_code: int
159
+
160
+
161
+ @dataclasses.dataclass
162
+ class EncReq(ControlPdu):
163
+ opcode = ControlPdu.Opcode.LL_ENC_REQ
164
+
165
+ rand: bytes
166
+ ediv: int
167
+ ltk: bytes
168
+
169
+
170
+ @dataclasses.dataclass
171
+ class CisReq(ControlPdu):
172
+ opcode = ControlPdu.Opcode.LL_CIS_REQ
173
+
174
+ cig_id: int
175
+ cis_id: int
176
+
177
+
178
+ @dataclasses.dataclass
179
+ class CisRsp(ControlPdu):
180
+ opcode = ControlPdu.Opcode.LL_CIS_REQ
181
+
182
+ cig_id: int
183
+ cis_id: int
184
+
185
+
186
+ @dataclasses.dataclass
187
+ class CisInd(ControlPdu):
188
+ opcode = ControlPdu.Opcode.LL_CIS_REQ
189
+
190
+ cig_id: int
191
+ cis_id: int
192
+
193
+
194
+ @dataclasses.dataclass
195
+ class CisTerminateInd(ControlPdu):
196
+ opcode = ControlPdu.Opcode.LL_CIS_TERMINATE_IND
197
+
198
+ cig_id: int
199
+ cis_id: int
200
+ error_code: int