bumble 0.0.211__py3-none-any.whl → 0.0.213__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/a2dp.py +6 -0
- bumble/apps/README.md +0 -3
- bumble/apps/auracast.py +11 -9
- bumble/apps/bench.py +482 -31
- bumble/apps/console.py +5 -5
- bumble/apps/controller_info.py +47 -10
- bumble/apps/controller_loopback.py +7 -3
- bumble/apps/controllers.py +2 -2
- bumble/apps/device_info.py +2 -2
- bumble/apps/gatt_dump.py +2 -2
- bumble/apps/gg_bridge.py +2 -2
- bumble/apps/hci_bridge.py +2 -2
- bumble/apps/l2cap_bridge.py +2 -2
- bumble/apps/lea_unicast/app.py +6 -1
- bumble/apps/pair.py +204 -43
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/scan.py +2 -2
- bumble/apps/show.py +4 -2
- bumble/apps/speaker/speaker.html +1 -0
- bumble/apps/speaker/speaker.js +113 -62
- bumble/apps/speaker/speaker.py +126 -18
- bumble/at.py +4 -4
- bumble/att.py +15 -18
- bumble/avc.py +7 -7
- bumble/avctp.py +5 -5
- bumble/avdtp.py +138 -88
- bumble/avrcp.py +52 -58
- bumble/colors.py +2 -2
- bumble/controller.py +84 -23
- bumble/core.py +13 -7
- bumble/{crypto.py → crypto/__init__.py} +11 -95
- bumble/crypto/builtin.py +652 -0
- bumble/crypto/cryptography.py +84 -0
- bumble/device.py +688 -345
- bumble/drivers/__init__.py +2 -2
- bumble/drivers/common.py +0 -2
- bumble/drivers/intel.py +40 -40
- bumble/drivers/rtk.py +28 -35
- bumble/gatt.py +7 -9
- bumble/gatt_adapters.py +4 -5
- bumble/gatt_client.py +31 -34
- bumble/gatt_server.py +15 -17
- bumble/hci.py +2635 -2878
- bumble/helpers.py +4 -5
- bumble/hfp.py +76 -57
- bumble/hid.py +24 -12
- bumble/host.py +117 -34
- bumble/keys.py +68 -52
- bumble/l2cap.py +329 -403
- bumble/link.py +6 -270
- bumble/pairing.py +23 -20
- bumble/pandora/__init__.py +1 -1
- bumble/pandora/config.py +2 -2
- bumble/pandora/device.py +6 -6
- bumble/pandora/host.py +38 -39
- bumble/pandora/l2cap.py +4 -4
- bumble/pandora/security.py +73 -57
- bumble/pandora/utils.py +3 -3
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +143 -136
- bumble/profiles/asha.py +13 -8
- bumble/profiles/bap.py +3 -4
- bumble/profiles/csip.py +3 -5
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +2 -2
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +42 -58
- bumble/profiles/le_audio.py +4 -4
- bumble/profiles/mcp.py +16 -13
- bumble/profiles/vcs.py +8 -10
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +27 -18
- bumble/rtp.py +1 -2
- bumble/sdp.py +2 -2
- bumble/smp.py +71 -69
- bumble/tools/rtk_util.py +2 -2
- bumble/transport/__init__.py +2 -16
- bumble/transport/android_netsim.py +5 -5
- bumble/transport/common.py +4 -4
- bumble/transport/pyusb.py +2 -2
- bumble/utils.py +2 -5
- bumble/vendor/android/hci.py +118 -200
- bumble/vendor/zephyr/hci.py +32 -27
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/METADATA +5 -5
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/RECORD +92 -93
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/entry_points.txt +0 -1
- bumble/apps/link_relay/__init__.py +0 -0
- bumble/apps/link_relay/link_relay.py +0 -289
- bumble/apps/link_relay/logging.yml +0 -21
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/top_level.txt +0 -0
bumble/l2cap.py
CHANGED
|
@@ -24,21 +24,19 @@ import struct
|
|
|
24
24
|
|
|
25
25
|
from collections import deque
|
|
26
26
|
from typing import (
|
|
27
|
-
Dict,
|
|
28
|
-
Type,
|
|
29
|
-
List,
|
|
30
27
|
Optional,
|
|
31
|
-
Tuple,
|
|
32
28
|
Callable,
|
|
33
29
|
Any,
|
|
34
30
|
Union,
|
|
35
|
-
Deque,
|
|
36
31
|
Iterable,
|
|
37
32
|
SupportsBytes,
|
|
33
|
+
TypeVar,
|
|
34
|
+
ClassVar,
|
|
38
35
|
TYPE_CHECKING,
|
|
39
36
|
)
|
|
40
37
|
|
|
41
38
|
from bumble import utils
|
|
39
|
+
from bumble import hci
|
|
42
40
|
from bumble.colors import color
|
|
43
41
|
from bumble.core import (
|
|
44
42
|
InvalidStateError,
|
|
@@ -47,13 +45,6 @@ from bumble.core import (
|
|
|
47
45
|
OutOfResourcesError,
|
|
48
46
|
ProtocolError,
|
|
49
47
|
)
|
|
50
|
-
from bumble.hci import (
|
|
51
|
-
HCI_LE_Connection_Update_Command,
|
|
52
|
-
HCI_Object,
|
|
53
|
-
Role,
|
|
54
|
-
key_with_value,
|
|
55
|
-
name_or_number,
|
|
56
|
-
)
|
|
57
48
|
|
|
58
49
|
if TYPE_CHECKING:
|
|
59
50
|
from bumble.device import Connection
|
|
@@ -98,54 +89,29 @@ L2CAP_PSM_DYNAMIC_RANGE_END = 0xFFFF
|
|
|
98
89
|
L2CAP_LE_PSM_DYNAMIC_RANGE_START = 0x0080
|
|
99
90
|
L2CAP_LE_PSM_DYNAMIC_RANGE_END = 0x00FF
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
L2CAP_COMMAND_REJECT = 0x01
|
|
103
|
-
L2CAP_CONNECTION_REQUEST = 0x02
|
|
104
|
-
L2CAP_CONNECTION_RESPONSE = 0x03
|
|
105
|
-
L2CAP_CONFIGURE_REQUEST = 0x04
|
|
106
|
-
L2CAP_CONFIGURE_RESPONSE = 0x05
|
|
107
|
-
L2CAP_DISCONNECTION_REQUEST = 0x06
|
|
108
|
-
L2CAP_DISCONNECTION_RESPONSE = 0x07
|
|
109
|
-
L2CAP_ECHO_REQUEST = 0x08
|
|
110
|
-
L2CAP_ECHO_RESPONSE = 0x09
|
|
111
|
-
L2CAP_INFORMATION_REQUEST = 0x0A
|
|
112
|
-
L2CAP_INFORMATION_RESPONSE = 0x0B
|
|
113
|
-
L2CAP_CREATE_CHANNEL_REQUEST = 0x0C
|
|
114
|
-
L2CAP_CREATE_CHANNEL_RESPONSE = 0x0D
|
|
115
|
-
L2CAP_MOVE_CHANNEL_REQUEST = 0x0E
|
|
116
|
-
L2CAP_MOVE_CHANNEL_RESPONSE = 0x0F
|
|
117
|
-
L2CAP_MOVE_CHANNEL_CONFIRMATION = 0x10
|
|
118
|
-
L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE = 0x11
|
|
119
|
-
L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST = 0x12
|
|
120
|
-
L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE = 0x13
|
|
121
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST = 0x14
|
|
122
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE = 0x15
|
|
123
|
-
L2CAP_LE_FLOW_CONTROL_CREDIT = 0x16
|
|
124
|
-
|
|
125
|
-
L2CAP_CONTROL_FRAME_NAMES = {
|
|
126
|
-
L2CAP_COMMAND_REJECT: 'L2CAP_COMMAND_REJECT',
|
|
127
|
-
L2CAP_CONNECTION_REQUEST: 'L2CAP_CONNECTION_REQUEST',
|
|
128
|
-
L2CAP_CONNECTION_RESPONSE: 'L2CAP_CONNECTION_RESPONSE',
|
|
129
|
-
L2CAP_CONFIGURE_REQUEST: 'L2CAP_CONFIGURE_REQUEST',
|
|
130
|
-
L2CAP_CONFIGURE_RESPONSE: 'L2CAP_CONFIGURE_RESPONSE',
|
|
131
|
-
L2CAP_DISCONNECTION_REQUEST: 'L2CAP_DISCONNECTION_REQUEST',
|
|
132
|
-
L2CAP_DISCONNECTION_RESPONSE: 'L2CAP_DISCONNECTION_RESPONSE',
|
|
133
|
-
L2CAP_ECHO_REQUEST: 'L2CAP_ECHO_REQUEST',
|
|
134
|
-
L2CAP_ECHO_RESPONSE: 'L2CAP_ECHO_RESPONSE',
|
|
135
|
-
L2CAP_INFORMATION_REQUEST: 'L2CAP_INFORMATION_REQUEST',
|
|
136
|
-
L2CAP_INFORMATION_RESPONSE: 'L2CAP_INFORMATION_RESPONSE',
|
|
137
|
-
L2CAP_CREATE_CHANNEL_REQUEST: 'L2CAP_CREATE_CHANNEL_REQUEST',
|
|
138
|
-
L2CAP_CREATE_CHANNEL_RESPONSE: 'L2CAP_CREATE_CHANNEL_RESPONSE',
|
|
139
|
-
L2CAP_MOVE_CHANNEL_REQUEST: 'L2CAP_MOVE_CHANNEL_REQUEST',
|
|
140
|
-
L2CAP_MOVE_CHANNEL_RESPONSE: 'L2CAP_MOVE_CHANNEL_RESPONSE',
|
|
141
|
-
L2CAP_MOVE_CHANNEL_CONFIRMATION: 'L2CAP_MOVE_CHANNEL_CONFIRMATION',
|
|
142
|
-
L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE: 'L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE',
|
|
143
|
-
L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST: 'L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST',
|
|
144
|
-
L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE: 'L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE',
|
|
145
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST: 'L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST',
|
|
146
|
-
L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE: 'L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE',
|
|
147
|
-
L2CAP_LE_FLOW_CONTROL_CREDIT: 'L2CAP_LE_FLOW_CONTROL_CREDIT'
|
|
148
|
-
}
|
|
92
|
+
class CommandCode(hci.SpecableEnum):
|
|
93
|
+
L2CAP_COMMAND_REJECT = 0x01
|
|
94
|
+
L2CAP_CONNECTION_REQUEST = 0x02
|
|
95
|
+
L2CAP_CONNECTION_RESPONSE = 0x03
|
|
96
|
+
L2CAP_CONFIGURE_REQUEST = 0x04
|
|
97
|
+
L2CAP_CONFIGURE_RESPONSE = 0x05
|
|
98
|
+
L2CAP_DISCONNECTION_REQUEST = 0x06
|
|
99
|
+
L2CAP_DISCONNECTION_RESPONSE = 0x07
|
|
100
|
+
L2CAP_ECHO_REQUEST = 0x08
|
|
101
|
+
L2CAP_ECHO_RESPONSE = 0x09
|
|
102
|
+
L2CAP_INFORMATION_REQUEST = 0x0A
|
|
103
|
+
L2CAP_INFORMATION_RESPONSE = 0x0B
|
|
104
|
+
L2CAP_CREATE_CHANNEL_REQUEST = 0x0C
|
|
105
|
+
L2CAP_CREATE_CHANNEL_RESPONSE = 0x0D
|
|
106
|
+
L2CAP_MOVE_CHANNEL_REQUEST = 0x0E
|
|
107
|
+
L2CAP_MOVE_CHANNEL_RESPONSE = 0x0F
|
|
108
|
+
L2CAP_MOVE_CHANNEL_CONFIRMATION = 0x10
|
|
109
|
+
L2CAP_MOVE_CHANNEL_CONFIRMATION_RESPONSE = 0x11
|
|
110
|
+
L2CAP_CONNECTION_PARAMETER_UPDATE_REQUEST = 0x12
|
|
111
|
+
L2CAP_CONNECTION_PARAMETER_UPDATE_RESPONSE = 0x13
|
|
112
|
+
L2CAP_LE_CREDIT_BASED_CONNECTION_REQUEST = 0x14
|
|
113
|
+
L2CAP_LE_CREDIT_BASED_CONNECTION_RESPONSE = 0x15
|
|
114
|
+
L2CAP_LE_FLOW_CONTROL_CREDIT = 0x16
|
|
149
115
|
|
|
150
116
|
L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT = 0x0000
|
|
151
117
|
L2CAP_CONNECTION_PARAMETERS_REJECTED_RESULT = 0x0001
|
|
@@ -237,46 +203,47 @@ class L2CAP_PDU:
|
|
|
237
203
|
|
|
238
204
|
|
|
239
205
|
# -----------------------------------------------------------------------------
|
|
206
|
+
@dataclasses.dataclass
|
|
240
207
|
class L2CAP_Control_Frame:
|
|
241
208
|
'''
|
|
242
209
|
See Bluetooth spec @ Vol 3, Part A - 4 SIGNALING PACKET FORMATS
|
|
243
210
|
'''
|
|
244
211
|
|
|
245
|
-
classes:
|
|
246
|
-
|
|
247
|
-
|
|
212
|
+
classes: ClassVar[dict[int, type[L2CAP_Control_Frame]]] = {}
|
|
213
|
+
fields: ClassVar[hci.Fields] = ()
|
|
214
|
+
code: int = dataclasses.field(default=0, init=False)
|
|
215
|
+
name: str = dataclasses.field(default='', init=False)
|
|
216
|
+
_data: Optional[bytes] = dataclasses.field(default=None, init=False)
|
|
248
217
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
218
|
+
identifier: int
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def from_bytes(cls, pdu: bytes) -> L2CAP_Control_Frame:
|
|
222
|
+
code, identifier, length = struct.unpack_from("<BBH", pdu)
|
|
252
223
|
|
|
253
|
-
|
|
254
|
-
if
|
|
224
|
+
subclass = L2CAP_Control_Frame.classes.get(code)
|
|
225
|
+
if subclass is None:
|
|
255
226
|
instance = L2CAP_Control_Frame(pdu)
|
|
256
|
-
instance.
|
|
257
|
-
instance.
|
|
227
|
+
instance.code = CommandCode(code)
|
|
228
|
+
instance.name = instance.code.name
|
|
258
229
|
return instance
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
230
|
+
frame = subclass(
|
|
231
|
+
**hci.HCI_Object.dict_from_bytes(pdu, 4, subclass.fields),
|
|
232
|
+
identifier=identifier,
|
|
233
|
+
)
|
|
234
|
+
frame.identifier = identifier
|
|
235
|
+
frame.data = pdu[4:]
|
|
236
|
+
if length != len(pdu):
|
|
264
237
|
logger.warning(
|
|
265
238
|
color(
|
|
266
239
|
f'!!! length mismatch: expected {len(pdu) - 4} but got {length}',
|
|
267
240
|
'red',
|
|
268
241
|
)
|
|
269
242
|
)
|
|
270
|
-
|
|
271
|
-
self.init_from_bytes(pdu, 4)
|
|
272
|
-
return self
|
|
273
|
-
|
|
274
|
-
@staticmethod
|
|
275
|
-
def code_name(code: int) -> str:
|
|
276
|
-
return name_or_number(L2CAP_CONTROL_FRAME_NAMES, code)
|
|
243
|
+
return frame
|
|
277
244
|
|
|
278
245
|
@staticmethod
|
|
279
|
-
def decode_configuration_options(data: bytes) ->
|
|
246
|
+
def decode_configuration_options(data: bytes) -> list[tuple[int, bytes]]:
|
|
280
247
|
options = []
|
|
281
248
|
while len(data) >= 2:
|
|
282
249
|
value_type = data[0]
|
|
@@ -288,119 +255,91 @@ class L2CAP_Control_Frame:
|
|
|
288
255
|
return options
|
|
289
256
|
|
|
290
257
|
@staticmethod
|
|
291
|
-
def encode_configuration_options(options:
|
|
258
|
+
def encode_configuration_options(options: list[tuple[int, bytes]]) -> bytes:
|
|
292
259
|
return b''.join(
|
|
293
260
|
[bytes([option[0], len(option[1])]) + option[1] for option in options]
|
|
294
261
|
)
|
|
295
262
|
|
|
296
|
-
|
|
297
|
-
def subclass(fields):
|
|
298
|
-
def inner(cls):
|
|
299
|
-
cls.name = cls.__name__.upper()
|
|
300
|
-
cls.code = key_with_value(L2CAP_CONTROL_FRAME_NAMES, cls.name)
|
|
301
|
-
if cls.code is None:
|
|
302
|
-
raise KeyError(
|
|
303
|
-
f'Control Frame name {cls.name} '
|
|
304
|
-
'not found in L2CAP_CONTROL_FRAME_NAMES'
|
|
305
|
-
)
|
|
306
|
-
cls.fields = fields
|
|
263
|
+
_ControlFrame = TypeVar('_ControlFrame', bound='L2CAP_Control_Frame')
|
|
307
264
|
|
|
308
|
-
|
|
309
|
-
|
|
265
|
+
@classmethod
|
|
266
|
+
def subclass(cls, subclass: type[_ControlFrame]) -> type[_ControlFrame]:
|
|
267
|
+
subclass.name = subclass.__name__.upper()
|
|
268
|
+
subclass.code = CommandCode[subclass.name]
|
|
269
|
+
subclass.fields = hci.HCI_Object.fields_from_dataclass(subclass)
|
|
310
270
|
|
|
311
|
-
|
|
271
|
+
# Register a factory for this class
|
|
272
|
+
L2CAP_Control_Frame.classes[subclass.code] = subclass
|
|
312
273
|
|
|
313
|
-
return
|
|
274
|
+
return subclass
|
|
314
275
|
|
|
315
|
-
def __init__(self, pdu=None, **kwargs) -> None:
|
|
276
|
+
def __init__(self, pdu: Optional[bytes] = None, **kwargs) -> None:
|
|
316
277
|
self.identifier = kwargs.get('identifier', 0)
|
|
317
|
-
if
|
|
278
|
+
if self.fields:
|
|
318
279
|
if kwargs:
|
|
319
|
-
HCI_Object.init_from_fields(self, self.fields, kwargs)
|
|
280
|
+
hci.HCI_Object.init_from_fields(self, self.fields, kwargs)
|
|
320
281
|
if pdu is None:
|
|
321
|
-
data = HCI_Object.dict_to_bytes(kwargs, self.fields)
|
|
282
|
+
data = hci.HCI_Object.dict_to_bytes(kwargs, self.fields)
|
|
322
283
|
pdu = (
|
|
323
284
|
bytes([self.code, self.identifier])
|
|
324
285
|
+ struct.pack('<H', len(data))
|
|
325
286
|
+ data
|
|
326
287
|
)
|
|
327
|
-
self.
|
|
288
|
+
self.data = pdu[4:] if pdu else b''
|
|
328
289
|
|
|
329
|
-
|
|
330
|
-
|
|
290
|
+
@property
|
|
291
|
+
def data(self) -> bytes:
|
|
292
|
+
if self._data is None:
|
|
293
|
+
self._data = hci.HCI_Object.dict_to_bytes(self.__dict__, self.fields)
|
|
294
|
+
return self._data
|
|
295
|
+
|
|
296
|
+
@data.setter
|
|
297
|
+
def data(self, parameters: bytes) -> None:
|
|
298
|
+
self._data = parameters
|
|
331
299
|
|
|
332
300
|
def __bytes__(self) -> bytes:
|
|
333
|
-
return
|
|
301
|
+
return (
|
|
302
|
+
struct.pack('<BBH', self.code, self.identifier, len(self.data) + 4)
|
|
303
|
+
+ self.data
|
|
304
|
+
)
|
|
334
305
|
|
|
335
306
|
def __str__(self) -> str:
|
|
336
307
|
result = f'{color(self.name, "yellow")} [ID={self.identifier}]'
|
|
337
308
|
if fields := getattr(self, 'fields', None):
|
|
338
|
-
result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, ' ')
|
|
309
|
+
result += ':\n' + hci.HCI_Object.format_fields(self.__dict__, fields, ' ')
|
|
339
310
|
else:
|
|
340
|
-
if len(self.
|
|
341
|
-
result += f': {self.
|
|
311
|
+
if len(self.data) > 1:
|
|
312
|
+
result += f': {self.data.hex()}'
|
|
342
313
|
return result
|
|
343
314
|
|
|
344
315
|
|
|
345
316
|
# -----------------------------------------------------------------------------
|
|
346
|
-
@L2CAP_Control_Frame.subclass
|
|
347
|
-
|
|
348
|
-
[
|
|
349
|
-
(
|
|
350
|
-
'reason',
|
|
351
|
-
{'size': 2, 'mapper': lambda x: L2CAP_Command_Reject.reason_name(x)},
|
|
352
|
-
),
|
|
353
|
-
('data', '*'),
|
|
354
|
-
]
|
|
355
|
-
)
|
|
317
|
+
@L2CAP_Control_Frame.subclass
|
|
318
|
+
@dataclasses.dataclass
|
|
356
319
|
class L2CAP_Command_Reject(L2CAP_Control_Frame):
|
|
357
320
|
'''
|
|
358
321
|
See Bluetooth spec @ Vol 3, Part A - 4.1 COMMAND REJECT
|
|
359
322
|
'''
|
|
360
323
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
324
|
+
class Reason(hci.SpecableEnum):
|
|
325
|
+
COMMAND_NOT_UNDERSTOOD = 0x0000
|
|
326
|
+
SIGNALING_MTU_EXCEEDED = 0x0001
|
|
327
|
+
INVALID_CID_IN_REQUEST = 0x0002
|
|
364
328
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
SIGNALING_MTU_EXCEEDED: 'SIGNALING_MTU_EXCEEDED',
|
|
368
|
-
INVALID_CID_IN_REQUEST: 'INVALID_CID_IN_REQUEST',
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
@staticmethod
|
|
372
|
-
def reason_name(reason: int) -> str:
|
|
373
|
-
return name_or_number(L2CAP_Command_Reject.REASON_NAMES, reason)
|
|
329
|
+
reason: int = dataclasses.field(metadata=Reason.type_metadata(2))
|
|
330
|
+
data: bytes = dataclasses.field(metadata=hci.metadata('*'))
|
|
374
331
|
|
|
375
332
|
|
|
376
333
|
# -----------------------------------------------------------------------------
|
|
377
|
-
@L2CAP_Control_Frame.subclass
|
|
378
|
-
|
|
379
|
-
[
|
|
380
|
-
(
|
|
381
|
-
'psm',
|
|
382
|
-
{
|
|
383
|
-
'parser': lambda data, offset: L2CAP_Connection_Request.parse_psm(
|
|
384
|
-
data, offset
|
|
385
|
-
),
|
|
386
|
-
'serializer': lambda value: L2CAP_Connection_Request.serialize_psm(
|
|
387
|
-
value
|
|
388
|
-
),
|
|
389
|
-
},
|
|
390
|
-
),
|
|
391
|
-
('source_cid', 2),
|
|
392
|
-
]
|
|
393
|
-
)
|
|
334
|
+
@L2CAP_Control_Frame.subclass
|
|
335
|
+
@dataclasses.dataclass
|
|
394
336
|
class L2CAP_Connection_Request(L2CAP_Control_Frame):
|
|
395
337
|
'''
|
|
396
338
|
See Bluetooth spec @ Vol 3, Part A - 4.2 CONNECTION REQUEST
|
|
397
339
|
'''
|
|
398
340
|
|
|
399
|
-
psm: int
|
|
400
|
-
source_cid: int
|
|
401
|
-
|
|
402
341
|
@staticmethod
|
|
403
|
-
def parse_psm(data: bytes, offset: int = 0) ->
|
|
342
|
+
def parse_psm(data: bytes, offset: int = 0) -> tuple[int, int]:
|
|
404
343
|
psm_length = 2
|
|
405
344
|
psm = data[offset] | data[offset + 1] << 8
|
|
406
345
|
|
|
@@ -421,156 +360,138 @@ class L2CAP_Connection_Request(L2CAP_Control_Frame):
|
|
|
421
360
|
|
|
422
361
|
return serialized
|
|
423
362
|
|
|
363
|
+
psm: int = dataclasses.field(
|
|
364
|
+
metadata=hci.metadata(
|
|
365
|
+
{
|
|
366
|
+
'parser': lambda data, offset: L2CAP_Connection_Request.parse_psm(
|
|
367
|
+
data, offset
|
|
368
|
+
),
|
|
369
|
+
'serializer': lambda value: L2CAP_Connection_Request.serialize_psm(
|
|
370
|
+
value
|
|
371
|
+
),
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
)
|
|
375
|
+
source_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
376
|
+
|
|
424
377
|
|
|
425
378
|
# -----------------------------------------------------------------------------
|
|
426
|
-
@L2CAP_Control_Frame.subclass
|
|
427
|
-
|
|
428
|
-
[
|
|
429
|
-
('destination_cid', 2),
|
|
430
|
-
('source_cid', 2),
|
|
431
|
-
(
|
|
432
|
-
'result',
|
|
433
|
-
{'size': 2, 'mapper': lambda x: L2CAP_Connection_Response.result_name(x)},
|
|
434
|
-
),
|
|
435
|
-
('status', 2),
|
|
436
|
-
]
|
|
437
|
-
)
|
|
379
|
+
@L2CAP_Control_Frame.subclass
|
|
380
|
+
@dataclasses.dataclass
|
|
438
381
|
class L2CAP_Connection_Response(L2CAP_Control_Frame):
|
|
439
382
|
'''
|
|
440
383
|
See Bluetooth spec @ Vol 3, Part A - 4.3 CONNECTION RESPONSE
|
|
441
384
|
'''
|
|
442
385
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004
|
|
453
|
-
CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0006
|
|
454
|
-
CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x0007
|
|
455
|
-
CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B
|
|
456
|
-
|
|
457
|
-
# pylint: disable=line-too-long
|
|
458
|
-
RESULT_NAMES = {
|
|
459
|
-
CONNECTION_SUCCESSFUL: 'CONNECTION_SUCCESSFUL',
|
|
460
|
-
CONNECTION_PENDING: 'CONNECTION_PENDING',
|
|
461
|
-
CONNECTION_REFUSED_PSM_NOT_SUPPORTED: 'CONNECTION_REFUSED_PSM_NOT_SUPPORTED',
|
|
462
|
-
CONNECTION_REFUSED_SECURITY_BLOCK: 'CONNECTION_REFUSED_SECURITY_BLOCK',
|
|
463
|
-
CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE: 'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE',
|
|
464
|
-
CONNECTION_REFUSED_INVALID_SOURCE_CID: 'CONNECTION_REFUSED_INVALID_SOURCE_CID',
|
|
465
|
-
CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED: 'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED',
|
|
466
|
-
CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS: 'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS',
|
|
467
|
-
}
|
|
386
|
+
class Result(hci.SpecableEnum):
|
|
387
|
+
CONNECTION_SUCCESSFUL = 0x0000
|
|
388
|
+
CONNECTION_PENDING = 0x0001
|
|
389
|
+
CONNECTION_REFUSED_PSM_NOT_SUPPORTED = 0x0002
|
|
390
|
+
CONNECTION_REFUSED_SECURITY_BLOCK = 0x0003
|
|
391
|
+
CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004
|
|
392
|
+
CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0006
|
|
393
|
+
CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x0007
|
|
394
|
+
CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B
|
|
468
395
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
396
|
+
destination_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
397
|
+
source_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
398
|
+
result: int = dataclasses.field(metadata=Result.type_metadata(2))
|
|
399
|
+
status: int = dataclasses.field(metadata=hci.metadata(2))
|
|
472
400
|
|
|
473
401
|
|
|
474
402
|
# -----------------------------------------------------------------------------
|
|
475
|
-
@L2CAP_Control_Frame.subclass
|
|
403
|
+
@L2CAP_Control_Frame.subclass
|
|
404
|
+
@dataclasses.dataclass
|
|
476
405
|
class L2CAP_Configure_Request(L2CAP_Control_Frame):
|
|
477
406
|
'''
|
|
478
407
|
See Bluetooth spec @ Vol 3, Part A - 4.4 CONFIGURATION REQUEST
|
|
479
408
|
'''
|
|
480
409
|
|
|
410
|
+
destination_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
411
|
+
flags: int = dataclasses.field(metadata=hci.metadata(2))
|
|
412
|
+
options: bytes = dataclasses.field(metadata=hci.metadata('*'))
|
|
413
|
+
|
|
481
414
|
|
|
482
415
|
# -----------------------------------------------------------------------------
|
|
483
|
-
@L2CAP_Control_Frame.subclass
|
|
484
|
-
|
|
485
|
-
[
|
|
486
|
-
('source_cid', 2),
|
|
487
|
-
('flags', 2),
|
|
488
|
-
(
|
|
489
|
-
'result',
|
|
490
|
-
{'size': 2, 'mapper': lambda x: L2CAP_Configure_Response.result_name(x)},
|
|
491
|
-
),
|
|
492
|
-
('options', '*'),
|
|
493
|
-
]
|
|
494
|
-
)
|
|
416
|
+
@L2CAP_Control_Frame.subclass
|
|
417
|
+
@dataclasses.dataclass
|
|
495
418
|
class L2CAP_Configure_Response(L2CAP_Control_Frame):
|
|
496
419
|
'''
|
|
497
420
|
See Bluetooth spec @ Vol 3, Part A - 4.5 CONFIGURATION RESPONSE
|
|
498
421
|
'''
|
|
499
422
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
RESULT_NAMES = {
|
|
508
|
-
SUCCESS: 'SUCCESS',
|
|
509
|
-
FAILURE_UNACCEPTABLE_PARAMETERS: 'FAILURE_UNACCEPTABLE_PARAMETERS',
|
|
510
|
-
FAILURE_REJECTED: 'FAILURE_REJECTED',
|
|
511
|
-
FAILURE_UNKNOWN_OPTIONS: 'FAILURE_UNKNOWN_OPTIONS',
|
|
512
|
-
PENDING: 'PENDING',
|
|
513
|
-
FAILURE_FLOW_SPEC_REJECTED: 'FAILURE_FLOW_SPEC_REJECTED',
|
|
514
|
-
}
|
|
423
|
+
class Result(hci.SpecableEnum):
|
|
424
|
+
SUCCESS = 0x0000
|
|
425
|
+
FAILURE_UNACCEPTABLE_PARAMETERS = 0x0001
|
|
426
|
+
FAILURE_REJECTED = 0x0002
|
|
427
|
+
FAILURE_UNKNOWN_OPTIONS = 0x0003
|
|
428
|
+
PENDING = 0x0004
|
|
429
|
+
FAILURE_FLOW_SPEC_REJECTED = 0x0005
|
|
515
430
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
431
|
+
source_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
432
|
+
flags: int = dataclasses.field(metadata=hci.metadata(2))
|
|
433
|
+
result: int = dataclasses.field(metadata=Result.type_metadata(2))
|
|
434
|
+
options: bytes = dataclasses.field(metadata=hci.metadata('*'))
|
|
519
435
|
|
|
520
436
|
|
|
521
437
|
# -----------------------------------------------------------------------------
|
|
522
|
-
@L2CAP_Control_Frame.subclass
|
|
438
|
+
@L2CAP_Control_Frame.subclass
|
|
439
|
+
@dataclasses.dataclass
|
|
523
440
|
class L2CAP_Disconnection_Request(L2CAP_Control_Frame):
|
|
524
441
|
'''
|
|
525
442
|
See Bluetooth spec @ Vol 3, Part A - 4.6 DISCONNECTION REQUEST
|
|
526
443
|
'''
|
|
527
444
|
|
|
445
|
+
destination_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
446
|
+
source_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
447
|
+
|
|
528
448
|
|
|
529
449
|
# -----------------------------------------------------------------------------
|
|
530
|
-
@L2CAP_Control_Frame.subclass
|
|
450
|
+
@L2CAP_Control_Frame.subclass
|
|
451
|
+
@dataclasses.dataclass
|
|
531
452
|
class L2CAP_Disconnection_Response(L2CAP_Control_Frame):
|
|
532
453
|
'''
|
|
533
454
|
See Bluetooth spec @ Vol 3, Part A - 4.7 DISCONNECTION RESPONSE
|
|
534
455
|
'''
|
|
535
456
|
|
|
457
|
+
destination_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
458
|
+
source_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
459
|
+
|
|
536
460
|
|
|
537
461
|
# -----------------------------------------------------------------------------
|
|
538
|
-
@L2CAP_Control_Frame.subclass
|
|
462
|
+
@L2CAP_Control_Frame.subclass
|
|
463
|
+
@dataclasses.dataclass
|
|
539
464
|
class L2CAP_Echo_Request(L2CAP_Control_Frame):
|
|
540
465
|
'''
|
|
541
466
|
See Bluetooth spec @ Vol 3, Part A - 4.8 ECHO REQUEST
|
|
542
467
|
'''
|
|
543
468
|
|
|
469
|
+
data: bytes = dataclasses.field(metadata=hci.metadata('*'))
|
|
470
|
+
|
|
544
471
|
|
|
545
472
|
# -----------------------------------------------------------------------------
|
|
546
|
-
@L2CAP_Control_Frame.subclass
|
|
473
|
+
@L2CAP_Control_Frame.subclass
|
|
474
|
+
@dataclasses.dataclass
|
|
547
475
|
class L2CAP_Echo_Response(L2CAP_Control_Frame):
|
|
548
476
|
'''
|
|
549
477
|
See Bluetooth spec @ Vol 3, Part A - 4.9 ECHO RESPONSE
|
|
550
478
|
'''
|
|
551
479
|
|
|
480
|
+
data: bytes = dataclasses.field(metadata=hci.metadata('*'))
|
|
481
|
+
|
|
552
482
|
|
|
553
483
|
# -----------------------------------------------------------------------------
|
|
554
|
-
@L2CAP_Control_Frame.subclass
|
|
555
|
-
|
|
556
|
-
(
|
|
557
|
-
'info_type',
|
|
558
|
-
{
|
|
559
|
-
'size': 2,
|
|
560
|
-
# pylint: disable-next=unnecessary-lambda
|
|
561
|
-
'mapper': lambda x: L2CAP_Information_Request.info_type_name(x),
|
|
562
|
-
},
|
|
563
|
-
)
|
|
564
|
-
]
|
|
565
|
-
)
|
|
484
|
+
@L2CAP_Control_Frame.subclass
|
|
485
|
+
@dataclasses.dataclass
|
|
566
486
|
class L2CAP_Information_Request(L2CAP_Control_Frame):
|
|
567
487
|
'''
|
|
568
488
|
See Bluetooth spec @ Vol 3, Part A - 4.10 INFORMATION REQUEST
|
|
569
489
|
'''
|
|
570
490
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
491
|
+
class InfoType(hci.SpecableEnum):
|
|
492
|
+
CONNECTIONLESS_MTU = 0x0001
|
|
493
|
+
EXTENDED_FEATURES_SUPPORTED = 0x0002
|
|
494
|
+
FIXED_CHANNELS_SUPPORTED = 0x0003
|
|
574
495
|
|
|
575
496
|
EXTENDED_FEATURE_FLOW_MODE_CONTROL = 0x0001
|
|
576
497
|
EXTENDED_FEATURE_RETRANSMISSION_MODE = 0x0002
|
|
@@ -584,139 +505,108 @@ class L2CAP_Information_Request(L2CAP_Control_Frame):
|
|
|
584
505
|
EXTENDED_FEATURE_UNICAST_CONNECTIONLESS_DATA = 0x0200
|
|
585
506
|
EXTENDED_FEATURE_ENHANCED_CREDIT_BASE_FLOW_CONTROL = 0x0400
|
|
586
507
|
|
|
587
|
-
|
|
588
|
-
CONNECTIONLESS_MTU: 'CONNECTIONLESS_MTU',
|
|
589
|
-
EXTENDED_FEATURES_SUPPORTED: 'EXTENDED_FEATURES_SUPPORTED',
|
|
590
|
-
FIXED_CHANNELS_SUPPORTED: 'FIXED_CHANNELS_SUPPORTED',
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
@staticmethod
|
|
594
|
-
def info_type_name(info_type: int) -> str:
|
|
595
|
-
return name_or_number(L2CAP_Information_Request.INFO_TYPE_NAMES, info_type)
|
|
508
|
+
info_type: int = dataclasses.field(metadata=InfoType.type_metadata(2))
|
|
596
509
|
|
|
597
510
|
|
|
598
511
|
# -----------------------------------------------------------------------------
|
|
599
|
-
@L2CAP_Control_Frame.subclass
|
|
600
|
-
|
|
601
|
-
('info_type', {'size': 2, 'mapper': L2CAP_Information_Request.info_type_name}),
|
|
602
|
-
(
|
|
603
|
-
'result',
|
|
604
|
-
# pylint: disable-next=unnecessary-lambda
|
|
605
|
-
{'size': 2, 'mapper': lambda x: L2CAP_Information_Response.result_name(x)},
|
|
606
|
-
),
|
|
607
|
-
('data', '*'),
|
|
608
|
-
]
|
|
609
|
-
)
|
|
512
|
+
@L2CAP_Control_Frame.subclass
|
|
513
|
+
@dataclasses.dataclass
|
|
610
514
|
class L2CAP_Information_Response(L2CAP_Control_Frame):
|
|
611
515
|
'''
|
|
612
516
|
See Bluetooth spec @ Vol 3, Part A - 4.11 INFORMATION RESPONSE
|
|
613
517
|
'''
|
|
614
518
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
RESULT_NAMES = {SUCCESS: 'SUCCESS', NOT_SUPPORTED: 'NOT_SUPPORTED'}
|
|
519
|
+
class Result(hci.SpecableEnum):
|
|
520
|
+
SUCCESS = 0x00
|
|
521
|
+
NOT_SUPPORTED = 0x01
|
|
619
522
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
523
|
+
info_type: int = dataclasses.field(
|
|
524
|
+
metadata=L2CAP_Information_Request.InfoType.type_metadata(2)
|
|
525
|
+
)
|
|
526
|
+
result: int = dataclasses.field(metadata=Result.type_metadata(2))
|
|
527
|
+
data: bytes = dataclasses.field(metadata=hci.metadata('*'))
|
|
623
528
|
|
|
624
529
|
|
|
625
530
|
# -----------------------------------------------------------------------------
|
|
626
|
-
@L2CAP_Control_Frame.subclass
|
|
627
|
-
|
|
628
|
-
)
|
|
531
|
+
@L2CAP_Control_Frame.subclass
|
|
532
|
+
@dataclasses.dataclass
|
|
629
533
|
class L2CAP_Connection_Parameter_Update_Request(L2CAP_Control_Frame):
|
|
630
534
|
'''
|
|
631
535
|
See Bluetooth spec @ Vol 3, Part A - 4.20 CONNECTION PARAMETER UPDATE REQUEST
|
|
632
536
|
'''
|
|
633
537
|
|
|
538
|
+
interval_min: int = dataclasses.field(metadata=hci.metadata(2))
|
|
539
|
+
interval_max: int = dataclasses.field(metadata=hci.metadata(2))
|
|
540
|
+
latency: int = dataclasses.field(metadata=hci.metadata(2))
|
|
541
|
+
timeout: int = dataclasses.field(metadata=hci.metadata(2))
|
|
542
|
+
|
|
634
543
|
|
|
635
544
|
# -----------------------------------------------------------------------------
|
|
636
|
-
@L2CAP_Control_Frame.subclass
|
|
545
|
+
@L2CAP_Control_Frame.subclass
|
|
546
|
+
@dataclasses.dataclass
|
|
637
547
|
class L2CAP_Connection_Parameter_Update_Response(L2CAP_Control_Frame):
|
|
638
548
|
'''
|
|
639
549
|
See Bluetooth spec @ Vol 3, Part A - 4.21 CONNECTION PARAMETER UPDATE RESPONSE
|
|
640
550
|
'''
|
|
641
551
|
|
|
552
|
+
result: int = dataclasses.field(metadata=hci.metadata(2))
|
|
553
|
+
|
|
642
554
|
|
|
643
555
|
# -----------------------------------------------------------------------------
|
|
644
|
-
@L2CAP_Control_Frame.subclass
|
|
645
|
-
|
|
646
|
-
)
|
|
556
|
+
@L2CAP_Control_Frame.subclass
|
|
557
|
+
@dataclasses.dataclass
|
|
647
558
|
class L2CAP_LE_Credit_Based_Connection_Request(L2CAP_Control_Frame):
|
|
648
559
|
'''
|
|
649
560
|
See Bluetooth spec @ Vol 3, Part A - 4.22 LE CREDIT BASED CONNECTION REQUEST
|
|
650
561
|
(CODE 0x14)
|
|
651
562
|
'''
|
|
652
563
|
|
|
653
|
-
|
|
564
|
+
le_psm: int = dataclasses.field(metadata=hci.metadata(2))
|
|
565
|
+
source_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
566
|
+
mtu: int = dataclasses.field(metadata=hci.metadata(2))
|
|
567
|
+
mps: int = dataclasses.field(metadata=hci.metadata(2))
|
|
568
|
+
initial_credits: int = dataclasses.field(metadata=hci.metadata(2))
|
|
654
569
|
|
|
655
570
|
|
|
656
571
|
# -----------------------------------------------------------------------------
|
|
657
|
-
@L2CAP_Control_Frame.subclass
|
|
658
|
-
|
|
659
|
-
[
|
|
660
|
-
('destination_cid', 2),
|
|
661
|
-
('mtu', 2),
|
|
662
|
-
('mps', 2),
|
|
663
|
-
('initial_credits', 2),
|
|
664
|
-
(
|
|
665
|
-
'result',
|
|
666
|
-
{
|
|
667
|
-
'size': 2,
|
|
668
|
-
'mapper': lambda x: L2CAP_LE_Credit_Based_Connection_Response.result_name(
|
|
669
|
-
x
|
|
670
|
-
),
|
|
671
|
-
},
|
|
672
|
-
),
|
|
673
|
-
]
|
|
674
|
-
)
|
|
572
|
+
@L2CAP_Control_Frame.subclass
|
|
573
|
+
@dataclasses.dataclass
|
|
675
574
|
class L2CAP_LE_Credit_Based_Connection_Response(L2CAP_Control_Frame):
|
|
676
575
|
'''
|
|
677
576
|
See Bluetooth spec @ Vol 3, Part A - 4.23 LE CREDIT BASED CONNECTION RESPONSE
|
|
678
577
|
(CODE 0x15)
|
|
679
578
|
'''
|
|
680
579
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION: 'CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION',
|
|
699
|
-
CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE: 'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE',
|
|
700
|
-
CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION: 'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION',
|
|
701
|
-
CONNECTION_REFUSED_INVALID_SOURCE_CID: 'CONNECTION_REFUSED_INVALID_SOURCE_CID',
|
|
702
|
-
CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED: 'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED',
|
|
703
|
-
CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS: 'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS',
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
@staticmethod
|
|
707
|
-
def result_name(result: int) -> str:
|
|
708
|
-
return name_or_number(
|
|
709
|
-
L2CAP_LE_Credit_Based_Connection_Response.RESULT_NAMES, result
|
|
710
|
-
)
|
|
580
|
+
class Result(hci.SpecableEnum):
|
|
581
|
+
CONNECTION_SUCCESSFUL = 0x0000
|
|
582
|
+
CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED = 0x0002
|
|
583
|
+
CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004
|
|
584
|
+
CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION = 0x0005
|
|
585
|
+
CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION = 0x0006
|
|
586
|
+
CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0007
|
|
587
|
+
CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION = 0x0008
|
|
588
|
+
CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0009
|
|
589
|
+
CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x000A
|
|
590
|
+
CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B
|
|
591
|
+
|
|
592
|
+
destination_cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
593
|
+
mtu: int = dataclasses.field(metadata=hci.metadata(2))
|
|
594
|
+
mps: int = dataclasses.field(metadata=hci.metadata(2))
|
|
595
|
+
initial_credits: int = dataclasses.field(metadata=hci.metadata(2))
|
|
596
|
+
result: int = dataclasses.field(metadata=Result.type_metadata(2))
|
|
711
597
|
|
|
712
598
|
|
|
713
599
|
# -----------------------------------------------------------------------------
|
|
714
|
-
@L2CAP_Control_Frame.subclass
|
|
600
|
+
@L2CAP_Control_Frame.subclass
|
|
601
|
+
@dataclasses.dataclass
|
|
715
602
|
class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
|
|
716
603
|
'''
|
|
717
604
|
See Bluetooth spec @ Vol 3, Part A - 4.24 LE FLOW CONTROL CREDIT (CODE 0x16)
|
|
718
605
|
'''
|
|
719
606
|
|
|
607
|
+
cid: int = dataclasses.field(metadata=hci.metadata(2))
|
|
608
|
+
credits: int = dataclasses.field(metadata=hci.metadata(2))
|
|
609
|
+
|
|
720
610
|
|
|
721
611
|
# -----------------------------------------------------------------------------
|
|
722
612
|
class ClassicChannel(utils.EventEmitter):
|
|
@@ -744,6 +634,9 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
744
634
|
WAIT_FINAL_RSP = 0x16
|
|
745
635
|
WAIT_CONTROL_IND = 0x17
|
|
746
636
|
|
|
637
|
+
EVENT_OPEN = "open"
|
|
638
|
+
EVENT_CLOSE = "close"
|
|
639
|
+
|
|
747
640
|
connection_result: Optional[asyncio.Future[None]]
|
|
748
641
|
disconnection_result: Optional[asyncio.Future[None]]
|
|
749
642
|
response: Optional[asyncio.Future[bytes]]
|
|
@@ -820,9 +713,7 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
820
713
|
|
|
821
714
|
# Wait for the connection to succeed or fail
|
|
822
715
|
try:
|
|
823
|
-
return await
|
|
824
|
-
self.connection, 'disconnection', self.connection_result
|
|
825
|
-
)
|
|
716
|
+
return await self.connection.cancel_on_disconnection(self.connection_result)
|
|
826
717
|
finally:
|
|
827
718
|
self.connection_result = None
|
|
828
719
|
|
|
@@ -847,7 +738,7 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
847
738
|
def abort(self) -> None:
|
|
848
739
|
if self.state == self.State.OPEN:
|
|
849
740
|
self._change_state(self.State.CLOSED)
|
|
850
|
-
self.emit(
|
|
741
|
+
self.emit(self.EVENT_CLOSE)
|
|
851
742
|
|
|
852
743
|
def send_configure_request(self) -> None:
|
|
853
744
|
options = L2CAP_Control_Frame.encode_configuration_options(
|
|
@@ -867,7 +758,7 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
867
758
|
)
|
|
868
759
|
)
|
|
869
760
|
|
|
870
|
-
def on_connection_request(self, request) -> None:
|
|
761
|
+
def on_connection_request(self, request: L2CAP_Connection_Request) -> None:
|
|
871
762
|
self.destination_cid = request.source_cid
|
|
872
763
|
self._change_state(self.State.WAIT_CONNECT)
|
|
873
764
|
self.send_control_frame(
|
|
@@ -875,7 +766,7 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
875
766
|
identifier=request.identifier,
|
|
876
767
|
destination_cid=self.source_cid,
|
|
877
768
|
source_cid=self.destination_cid,
|
|
878
|
-
result=L2CAP_Connection_Response.CONNECTION_SUCCESSFUL,
|
|
769
|
+
result=L2CAP_Connection_Response.Result.CONNECTION_SUCCESSFUL,
|
|
879
770
|
status=0x0000,
|
|
880
771
|
)
|
|
881
772
|
)
|
|
@@ -883,30 +774,31 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
883
774
|
self.send_configure_request()
|
|
884
775
|
self._change_state(self.State.WAIT_CONFIG_REQ_RSP)
|
|
885
776
|
|
|
886
|
-
def on_connection_response(self, response):
|
|
777
|
+
def on_connection_response(self, response: L2CAP_Connection_Response):
|
|
887
778
|
if self.state != self.State.WAIT_CONNECT_RSP:
|
|
888
779
|
logger.warning(color('invalid state', 'red'))
|
|
889
780
|
return
|
|
890
781
|
|
|
891
|
-
if response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL:
|
|
782
|
+
if response.result == L2CAP_Connection_Response.Result.CONNECTION_SUCCESSFUL:
|
|
892
783
|
self.destination_cid = response.destination_cid
|
|
893
784
|
self._change_state(self.State.WAIT_CONFIG)
|
|
894
785
|
self.send_configure_request()
|
|
895
786
|
self._change_state(self.State.WAIT_CONFIG_REQ_RSP)
|
|
896
|
-
elif response.result == L2CAP_Connection_Response.CONNECTION_PENDING:
|
|
787
|
+
elif response.result == L2CAP_Connection_Response.Result.CONNECTION_PENDING:
|
|
897
788
|
pass
|
|
898
789
|
else:
|
|
899
790
|
self._change_state(self.State.CLOSED)
|
|
900
|
-
self.connection_result
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
791
|
+
if self.connection_result:
|
|
792
|
+
self.connection_result.set_exception(
|
|
793
|
+
ProtocolError(
|
|
794
|
+
response.result,
|
|
795
|
+
'l2cap',
|
|
796
|
+
L2CAP_Connection_Response.Result(response.result).name,
|
|
797
|
+
)
|
|
905
798
|
)
|
|
906
|
-
|
|
907
|
-
self.connection_result = None
|
|
799
|
+
self.connection_result = None
|
|
908
800
|
|
|
909
|
-
def on_configure_request(self, request) -> None:
|
|
801
|
+
def on_configure_request(self, request: L2CAP_Configure_Request) -> None:
|
|
910
802
|
if self.state not in (
|
|
911
803
|
self.State.WAIT_CONFIG,
|
|
912
804
|
self.State.WAIT_CONFIG_REQ,
|
|
@@ -927,7 +819,7 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
927
819
|
identifier=request.identifier,
|
|
928
820
|
source_cid=self.destination_cid,
|
|
929
821
|
flags=0x0000,
|
|
930
|
-
result=L2CAP_Configure_Response.SUCCESS,
|
|
822
|
+
result=L2CAP_Configure_Response.Result.SUCCESS,
|
|
931
823
|
options=request.options, # TODO: don't accept everything blindly
|
|
932
824
|
)
|
|
933
825
|
)
|
|
@@ -940,12 +832,12 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
940
832
|
if self.connection_result:
|
|
941
833
|
self.connection_result.set_result(None)
|
|
942
834
|
self.connection_result = None
|
|
943
|
-
self.emit(
|
|
835
|
+
self.emit(self.EVENT_OPEN)
|
|
944
836
|
elif self.state == self.State.WAIT_CONFIG_REQ_RSP:
|
|
945
837
|
self._change_state(self.State.WAIT_CONFIG_RSP)
|
|
946
838
|
|
|
947
|
-
def on_configure_response(self, response) -> None:
|
|
948
|
-
if response.result == L2CAP_Configure_Response.SUCCESS:
|
|
839
|
+
def on_configure_response(self, response: L2CAP_Configure_Response) -> None:
|
|
840
|
+
if response.result == L2CAP_Configure_Response.Result.SUCCESS:
|
|
949
841
|
if self.state == self.State.WAIT_CONFIG_REQ_RSP:
|
|
950
842
|
self._change_state(self.State.WAIT_CONFIG_REQ)
|
|
951
843
|
elif self.state in (
|
|
@@ -956,11 +848,12 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
956
848
|
if self.connection_result:
|
|
957
849
|
self.connection_result.set_result(None)
|
|
958
850
|
self.connection_result = None
|
|
959
|
-
self.emit(
|
|
851
|
+
self.emit(self.EVENT_OPEN)
|
|
960
852
|
else:
|
|
961
853
|
logger.warning(color('invalid state', 'red'))
|
|
962
854
|
elif (
|
|
963
|
-
response.result
|
|
855
|
+
response.result
|
|
856
|
+
== L2CAP_Configure_Response.Result.FAILURE_UNACCEPTABLE_PARAMETERS
|
|
964
857
|
):
|
|
965
858
|
# Re-configure with what's suggested in the response
|
|
966
859
|
self.send_control_frame(
|
|
@@ -975,13 +868,13 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
975
868
|
logger.warning(
|
|
976
869
|
color(
|
|
977
870
|
'!!! configuration rejected: '
|
|
978
|
-
f'{L2CAP_Configure_Response.
|
|
871
|
+
f'{L2CAP_Configure_Response.Result(response.result).name}',
|
|
979
872
|
'red',
|
|
980
873
|
)
|
|
981
874
|
)
|
|
982
875
|
# TODO: decide how to fail gracefully
|
|
983
876
|
|
|
984
|
-
def on_disconnection_request(self, request) -> None:
|
|
877
|
+
def on_disconnection_request(self, request: L2CAP_Disconnection_Request) -> None:
|
|
985
878
|
if self.state in (self.State.OPEN, self.State.WAIT_DISCONNECT):
|
|
986
879
|
self.send_control_frame(
|
|
987
880
|
L2CAP_Disconnection_Response(
|
|
@@ -991,12 +884,12 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
991
884
|
)
|
|
992
885
|
)
|
|
993
886
|
self._change_state(self.State.CLOSED)
|
|
994
|
-
self.emit(
|
|
887
|
+
self.emit(self.EVENT_CLOSE)
|
|
995
888
|
self.manager.on_channel_closed(self)
|
|
996
889
|
else:
|
|
997
890
|
logger.warning(color('invalid state', 'red'))
|
|
998
891
|
|
|
999
|
-
def on_disconnection_response(self, response) -> None:
|
|
892
|
+
def on_disconnection_response(self, response: L2CAP_Disconnection_Response) -> None:
|
|
1000
893
|
if self.state != self.State.WAIT_DISCONNECT:
|
|
1001
894
|
logger.warning(color('invalid state', 'red'))
|
|
1002
895
|
return
|
|
@@ -1012,7 +905,7 @@ class ClassicChannel(utils.EventEmitter):
|
|
|
1012
905
|
if self.disconnection_result:
|
|
1013
906
|
self.disconnection_result.set_result(None)
|
|
1014
907
|
self.disconnection_result = None
|
|
1015
|
-
self.emit(
|
|
908
|
+
self.emit(self.EVENT_CLOSE)
|
|
1016
909
|
self.manager.on_channel_closed(self)
|
|
1017
910
|
|
|
1018
911
|
def __str__(self) -> str:
|
|
@@ -1038,7 +931,7 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1038
931
|
DISCONNECTED = 4
|
|
1039
932
|
CONNECTION_ERROR = 5
|
|
1040
933
|
|
|
1041
|
-
out_queue:
|
|
934
|
+
out_queue: deque[bytes]
|
|
1042
935
|
connection_result: Optional[asyncio.Future[LeCreditBasedChannel]]
|
|
1043
936
|
disconnection_result: Optional[asyncio.Future[None]]
|
|
1044
937
|
in_sdu: Optional[bytes]
|
|
@@ -1047,6 +940,9 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1047
940
|
connection: Connection
|
|
1048
941
|
sink: Optional[Callable[[bytes], Any]]
|
|
1049
942
|
|
|
943
|
+
EVENT_OPEN = "open"
|
|
944
|
+
EVENT_CLOSE = "close"
|
|
945
|
+
|
|
1050
946
|
def __init__(
|
|
1051
947
|
self,
|
|
1052
948
|
manager: ChannelManager,
|
|
@@ -1098,9 +994,9 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1098
994
|
self.state = new_state
|
|
1099
995
|
|
|
1100
996
|
if new_state == self.State.CONNECTED:
|
|
1101
|
-
self.emit(
|
|
997
|
+
self.emit(self.EVENT_OPEN)
|
|
1102
998
|
elif new_state == self.State.DISCONNECTED:
|
|
1103
|
-
self.emit(
|
|
999
|
+
self.emit(self.EVENT_CLOSE)
|
|
1104
1000
|
|
|
1105
1001
|
def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
|
|
1106
1002
|
self.manager.send_pdu(self.connection, self.destination_cid, pdu)
|
|
@@ -1226,7 +1122,9 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1226
1122
|
self.in_sdu = None
|
|
1227
1123
|
self.in_sdu_length = 0
|
|
1228
1124
|
|
|
1229
|
-
def on_connection_response(
|
|
1125
|
+
def on_connection_response(
|
|
1126
|
+
self, response: L2CAP_LE_Credit_Based_Connection_Response
|
|
1127
|
+
) -> None:
|
|
1230
1128
|
# Look for a matching pending response result
|
|
1231
1129
|
if self.connection_result is None:
|
|
1232
1130
|
logger.warning(
|
|
@@ -1236,7 +1134,7 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1236
1134
|
|
|
1237
1135
|
if (
|
|
1238
1136
|
response.result
|
|
1239
|
-
== L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL
|
|
1137
|
+
== L2CAP_LE_Credit_Based_Connection_Response.Result.CONNECTION_SUCCESSFUL
|
|
1240
1138
|
):
|
|
1241
1139
|
self.destination_cid = response.destination_cid
|
|
1242
1140
|
self.peer_mtu = response.mtu
|
|
@@ -1250,9 +1148,9 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1250
1148
|
ProtocolError(
|
|
1251
1149
|
response.result,
|
|
1252
1150
|
'l2cap',
|
|
1253
|
-
L2CAP_LE_Credit_Based_Connection_Response.
|
|
1151
|
+
L2CAP_LE_Credit_Based_Connection_Response.Result(
|
|
1254
1152
|
response.result
|
|
1255
|
-
),
|
|
1153
|
+
).name,
|
|
1256
1154
|
)
|
|
1257
1155
|
)
|
|
1258
1156
|
self._change_state(self.State.CONNECTION_ERROR)
|
|
@@ -1267,7 +1165,7 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1267
1165
|
# Try to send more data if we have any queued up
|
|
1268
1166
|
self.process_output()
|
|
1269
1167
|
|
|
1270
|
-
def on_disconnection_request(self, request) -> None:
|
|
1168
|
+
def on_disconnection_request(self, request: L2CAP_Disconnection_Request) -> None:
|
|
1271
1169
|
self.send_control_frame(
|
|
1272
1170
|
L2CAP_Disconnection_Response(
|
|
1273
1171
|
identifier=request.identifier,
|
|
@@ -1278,7 +1176,7 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1278
1176
|
self._change_state(self.State.DISCONNECTED)
|
|
1279
1177
|
self.flush_output()
|
|
1280
1178
|
|
|
1281
|
-
def on_disconnection_response(self, response) -> None:
|
|
1179
|
+
def on_disconnection_response(self, response: L2CAP_Disconnection_Response) -> None:
|
|
1282
1180
|
if self.state != self.State.DISCONNECTING:
|
|
1283
1181
|
logger.warning(color('invalid state', 'red'))
|
|
1284
1182
|
return
|
|
@@ -1381,6 +1279,8 @@ class LeCreditBasedChannel(utils.EventEmitter):
|
|
|
1381
1279
|
|
|
1382
1280
|
# -----------------------------------------------------------------------------
|
|
1383
1281
|
class ClassicChannelServer(utils.EventEmitter):
|
|
1282
|
+
EVENT_CONNECTION = "connection"
|
|
1283
|
+
|
|
1384
1284
|
def __init__(
|
|
1385
1285
|
self,
|
|
1386
1286
|
manager: ChannelManager,
|
|
@@ -1395,7 +1295,7 @@ class ClassicChannelServer(utils.EventEmitter):
|
|
|
1395
1295
|
self.mtu = mtu
|
|
1396
1296
|
|
|
1397
1297
|
def on_connection(self, channel: ClassicChannel) -> None:
|
|
1398
|
-
self.emit(
|
|
1298
|
+
self.emit(self.EVENT_CONNECTION, channel)
|
|
1399
1299
|
if self.handler:
|
|
1400
1300
|
self.handler(channel)
|
|
1401
1301
|
|
|
@@ -1406,6 +1306,8 @@ class ClassicChannelServer(utils.EventEmitter):
|
|
|
1406
1306
|
|
|
1407
1307
|
# -----------------------------------------------------------------------------
|
|
1408
1308
|
class LeCreditBasedChannelServer(utils.EventEmitter):
|
|
1309
|
+
EVENT_CONNECTION = "connection"
|
|
1310
|
+
|
|
1409
1311
|
def __init__(
|
|
1410
1312
|
self,
|
|
1411
1313
|
manager: ChannelManager,
|
|
@@ -1424,7 +1326,7 @@ class LeCreditBasedChannelServer(utils.EventEmitter):
|
|
|
1424
1326
|
self.mps = mps
|
|
1425
1327
|
|
|
1426
1328
|
def on_connection(self, channel: LeCreditBasedChannel) -> None:
|
|
1427
|
-
self.emit(
|
|
1329
|
+
self.emit(self.EVENT_CONNECTION, channel)
|
|
1428
1330
|
if self.handler:
|
|
1429
1331
|
self.handler(channel)
|
|
1430
1332
|
|
|
@@ -1435,13 +1337,13 @@ class LeCreditBasedChannelServer(utils.EventEmitter):
|
|
|
1435
1337
|
|
|
1436
1338
|
# -----------------------------------------------------------------------------
|
|
1437
1339
|
class ChannelManager:
|
|
1438
|
-
identifiers:
|
|
1439
|
-
channels:
|
|
1440
|
-
servers:
|
|
1441
|
-
le_coc_channels:
|
|
1442
|
-
le_coc_servers:
|
|
1443
|
-
le_coc_requests:
|
|
1444
|
-
fixed_channels:
|
|
1340
|
+
identifiers: dict[int, int]
|
|
1341
|
+
channels: dict[int, dict[int, Union[ClassicChannel, LeCreditBasedChannel]]]
|
|
1342
|
+
servers: dict[int, ClassicChannelServer]
|
|
1343
|
+
le_coc_channels: dict[int, dict[int, LeCreditBasedChannel]]
|
|
1344
|
+
le_coc_servers: dict[int, LeCreditBasedChannelServer]
|
|
1345
|
+
le_coc_requests: dict[int, L2CAP_LE_Credit_Based_Connection_Request]
|
|
1346
|
+
fixed_channels: dict[int, Optional[Callable[[int, bytes], Any]]]
|
|
1445
1347
|
_host: Optional[Host]
|
|
1446
1348
|
connection_parameters_update_response: Optional[asyncio.Future[int]]
|
|
1447
1349
|
|
|
@@ -1723,12 +1625,12 @@ class ChannelManager:
|
|
|
1723
1625
|
)
|
|
1724
1626
|
|
|
1725
1627
|
def on_l2cap_command_reject(
|
|
1726
|
-
self, _connection: Connection, _cid: int, packet
|
|
1628
|
+
self, _connection: Connection, _cid: int, packet: L2CAP_Command_Reject
|
|
1727
1629
|
) -> None:
|
|
1728
1630
|
logger.warning(f'{color("!!! Command rejected:", "red")} {packet.reason}')
|
|
1729
1631
|
|
|
1730
1632
|
def on_l2cap_connection_request(
|
|
1731
|
-
self, connection: Connection, cid: int, request
|
|
1633
|
+
self, connection: Connection, cid: int, request: L2CAP_Connection_Request
|
|
1732
1634
|
) -> None:
|
|
1733
1635
|
# Check if there's a server for this PSM
|
|
1734
1636
|
server = self.servers.get(request.psm)
|
|
@@ -1745,7 +1647,7 @@ class ChannelManager:
|
|
|
1745
1647
|
destination_cid=request.source_cid,
|
|
1746
1648
|
source_cid=0,
|
|
1747
1649
|
# pylint: disable=line-too-long
|
|
1748
|
-
result=L2CAP_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
|
|
1650
|
+
result=L2CAP_Connection_Response.Result.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
|
|
1749
1651
|
status=0x0000,
|
|
1750
1652
|
),
|
|
1751
1653
|
)
|
|
@@ -1776,13 +1678,16 @@ class ChannelManager:
|
|
|
1776
1678
|
destination_cid=request.source_cid,
|
|
1777
1679
|
source_cid=0,
|
|
1778
1680
|
# pylint: disable=line-too-long
|
|
1779
|
-
result=L2CAP_Connection_Response.CONNECTION_REFUSED_PSM_NOT_SUPPORTED,
|
|
1681
|
+
result=L2CAP_Connection_Response.Result.CONNECTION_REFUSED_PSM_NOT_SUPPORTED,
|
|
1780
1682
|
status=0x0000,
|
|
1781
1683
|
),
|
|
1782
1684
|
)
|
|
1783
1685
|
|
|
1784
1686
|
def on_l2cap_connection_response(
|
|
1785
|
-
self,
|
|
1687
|
+
self,
|
|
1688
|
+
connection: Connection,
|
|
1689
|
+
cid: int,
|
|
1690
|
+
response: L2CAP_Connection_Response,
|
|
1786
1691
|
) -> None:
|
|
1787
1692
|
if (
|
|
1788
1693
|
channel := self.find_channel(connection.handle, response.source_cid)
|
|
@@ -1799,7 +1704,7 @@ class ChannelManager:
|
|
|
1799
1704
|
channel.on_connection_response(response)
|
|
1800
1705
|
|
|
1801
1706
|
def on_l2cap_configure_request(
|
|
1802
|
-
self, connection: Connection, cid: int, request
|
|
1707
|
+
self, connection: Connection, cid: int, request: L2CAP_Configure_Request
|
|
1803
1708
|
) -> None:
|
|
1804
1709
|
if (
|
|
1805
1710
|
channel := self.find_channel(connection.handle, request.destination_cid)
|
|
@@ -1816,7 +1721,7 @@ class ChannelManager:
|
|
|
1816
1721
|
channel.on_configure_request(request)
|
|
1817
1722
|
|
|
1818
1723
|
def on_l2cap_configure_response(
|
|
1819
|
-
self, connection: Connection, cid: int, response
|
|
1724
|
+
self, connection: Connection, cid: int, response: L2CAP_Configure_Response
|
|
1820
1725
|
) -> None:
|
|
1821
1726
|
if (
|
|
1822
1727
|
channel := self.find_channel(connection.handle, response.source_cid)
|
|
@@ -1833,7 +1738,7 @@ class ChannelManager:
|
|
|
1833
1738
|
channel.on_configure_response(response)
|
|
1834
1739
|
|
|
1835
1740
|
def on_l2cap_disconnection_request(
|
|
1836
|
-
self, connection: Connection, cid: int, request
|
|
1741
|
+
self, connection: Connection, cid: int, request: L2CAP_Disconnection_Request
|
|
1837
1742
|
) -> None:
|
|
1838
1743
|
if (
|
|
1839
1744
|
channel := self.find_channel(connection.handle, request.destination_cid)
|
|
@@ -1850,7 +1755,7 @@ class ChannelManager:
|
|
|
1850
1755
|
channel.on_disconnection_request(request)
|
|
1851
1756
|
|
|
1852
1757
|
def on_l2cap_disconnection_response(
|
|
1853
|
-
self, connection: Connection, cid: int, response
|
|
1758
|
+
self, connection: Connection, cid: int, response: L2CAP_Disconnection_Response
|
|
1854
1759
|
) -> None:
|
|
1855
1760
|
if (
|
|
1856
1761
|
channel := self.find_channel(connection.handle, response.source_cid)
|
|
@@ -1866,7 +1771,9 @@ class ChannelManager:
|
|
|
1866
1771
|
|
|
1867
1772
|
channel.on_disconnection_response(response)
|
|
1868
1773
|
|
|
1869
|
-
def on_l2cap_echo_request(
|
|
1774
|
+
def on_l2cap_echo_request(
|
|
1775
|
+
self, connection: Connection, cid: int, request: L2CAP_Echo_Request
|
|
1776
|
+
) -> None:
|
|
1870
1777
|
logger.debug(f'<<< Echo request: data={request.data.hex()}')
|
|
1871
1778
|
self.send_control_frame(
|
|
1872
1779
|
connection,
|
|
@@ -1875,25 +1782,31 @@ class ChannelManager:
|
|
|
1875
1782
|
)
|
|
1876
1783
|
|
|
1877
1784
|
def on_l2cap_echo_response(
|
|
1878
|
-
self, _connection: Connection, _cid: int, response
|
|
1785
|
+
self, _connection: Connection, _cid: int, response: L2CAP_Echo_Response
|
|
1879
1786
|
) -> None:
|
|
1880
1787
|
logger.debug(f'<<< Echo response: data={response.data.hex()}')
|
|
1881
1788
|
# TODO notify listeners
|
|
1882
1789
|
|
|
1883
1790
|
def on_l2cap_information_request(
|
|
1884
|
-
self, connection: Connection, cid: int, request
|
|
1791
|
+
self, connection: Connection, cid: int, request: L2CAP_Information_Request
|
|
1885
1792
|
) -> None:
|
|
1886
|
-
if request.info_type == L2CAP_Information_Request.CONNECTIONLESS_MTU:
|
|
1887
|
-
result = L2CAP_Information_Response.SUCCESS
|
|
1793
|
+
if request.info_type == L2CAP_Information_Request.InfoType.CONNECTIONLESS_MTU:
|
|
1794
|
+
result = L2CAP_Information_Response.Result.SUCCESS
|
|
1888
1795
|
data = self.connectionless_mtu.to_bytes(2, 'little')
|
|
1889
|
-
elif
|
|
1890
|
-
|
|
1796
|
+
elif (
|
|
1797
|
+
request.info_type
|
|
1798
|
+
== L2CAP_Information_Request.InfoType.EXTENDED_FEATURES_SUPPORTED
|
|
1799
|
+
):
|
|
1800
|
+
result = L2CAP_Information_Response.Result.SUCCESS
|
|
1891
1801
|
data = sum(self.extended_features).to_bytes(4, 'little')
|
|
1892
|
-
elif
|
|
1893
|
-
|
|
1802
|
+
elif (
|
|
1803
|
+
request.info_type
|
|
1804
|
+
== L2CAP_Information_Request.InfoType.FIXED_CHANNELS_SUPPORTED
|
|
1805
|
+
):
|
|
1806
|
+
result = L2CAP_Information_Response.Result.SUCCESS
|
|
1894
1807
|
data = sum(1 << cid for cid in self.fixed_channels).to_bytes(8, 'little')
|
|
1895
1808
|
else:
|
|
1896
|
-
result = L2CAP_Information_Response.NOT_SUPPORTED
|
|
1809
|
+
result = L2CAP_Information_Response.Result.NOT_SUPPORTED
|
|
1897
1810
|
data = b''
|
|
1898
1811
|
|
|
1899
1812
|
self.send_control_frame(
|
|
@@ -1908,9 +1821,12 @@ class ChannelManager:
|
|
|
1908
1821
|
)
|
|
1909
1822
|
|
|
1910
1823
|
def on_l2cap_connection_parameter_update_request(
|
|
1911
|
-
self,
|
|
1824
|
+
self,
|
|
1825
|
+
connection: Connection,
|
|
1826
|
+
cid: int,
|
|
1827
|
+
request: L2CAP_Connection_Parameter_Update_Request,
|
|
1912
1828
|
):
|
|
1913
|
-
if connection.role == Role.CENTRAL:
|
|
1829
|
+
if connection.role == hci.Role.CENTRAL:
|
|
1914
1830
|
self.send_control_frame(
|
|
1915
1831
|
connection,
|
|
1916
1832
|
cid,
|
|
@@ -1920,7 +1836,7 @@ class ChannelManager:
|
|
|
1920
1836
|
),
|
|
1921
1837
|
)
|
|
1922
1838
|
self.host.send_command_sync(
|
|
1923
|
-
HCI_LE_Connection_Update_Command(
|
|
1839
|
+
hci.HCI_LE_Connection_Update_Command(
|
|
1924
1840
|
connection_handle=connection.handle,
|
|
1925
1841
|
connection_interval_min=request.interval_min,
|
|
1926
1842
|
connection_interval_max=request.interval_max,
|
|
@@ -1958,6 +1874,7 @@ class ChannelManager:
|
|
|
1958
1874
|
connection,
|
|
1959
1875
|
L2CAP_LE_SIGNALING_CID,
|
|
1960
1876
|
L2CAP_Connection_Parameter_Update_Request(
|
|
1877
|
+
identifier=self.next_identifier(connection),
|
|
1961
1878
|
interval_min=interval_min,
|
|
1962
1879
|
interval_max=interval_max,
|
|
1963
1880
|
latency=latency,
|
|
@@ -1967,7 +1884,10 @@ class ChannelManager:
|
|
|
1967
1884
|
return await self.connection_parameters_update_response
|
|
1968
1885
|
|
|
1969
1886
|
def on_l2cap_connection_parameter_update_response(
|
|
1970
|
-
self,
|
|
1887
|
+
self,
|
|
1888
|
+
connection: Connection,
|
|
1889
|
+
cid: int,
|
|
1890
|
+
response: L2CAP_Connection_Parameter_Update_Response,
|
|
1971
1891
|
) -> None:
|
|
1972
1892
|
if self.connection_parameters_update_response:
|
|
1973
1893
|
self.connection_parameters_update_response.set_result(response.result)
|
|
@@ -1981,7 +1901,10 @@ class ChannelManager:
|
|
|
1981
1901
|
)
|
|
1982
1902
|
|
|
1983
1903
|
def on_l2cap_le_credit_based_connection_request(
|
|
1984
|
-
self,
|
|
1904
|
+
self,
|
|
1905
|
+
connection: Connection,
|
|
1906
|
+
cid: int,
|
|
1907
|
+
request: L2CAP_LE_Credit_Based_Connection_Request,
|
|
1985
1908
|
) -> None:
|
|
1986
1909
|
if request.le_psm in self.le_coc_servers:
|
|
1987
1910
|
server = self.le_coc_servers[request.le_psm]
|
|
@@ -2002,7 +1925,7 @@ class ChannelManager:
|
|
|
2002
1925
|
mps=server.mps,
|
|
2003
1926
|
initial_credits=0,
|
|
2004
1927
|
# pylint: disable=line-too-long
|
|
2005
|
-
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED,
|
|
1928
|
+
result=L2CAP_LE_Credit_Based_Connection_Response.Result.CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED,
|
|
2006
1929
|
),
|
|
2007
1930
|
)
|
|
2008
1931
|
return
|
|
@@ -2021,7 +1944,7 @@ class ChannelManager:
|
|
|
2021
1944
|
mps=server.mps,
|
|
2022
1945
|
initial_credits=0,
|
|
2023
1946
|
# pylint: disable=line-too-long
|
|
2024
|
-
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
|
|
1947
|
+
result=L2CAP_LE_Credit_Based_Connection_Response.Result.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
|
|
2025
1948
|
),
|
|
2026
1949
|
)
|
|
2027
1950
|
return
|
|
@@ -2059,7 +1982,7 @@ class ChannelManager:
|
|
|
2059
1982
|
mps=server.mps,
|
|
2060
1983
|
initial_credits=server.max_credits,
|
|
2061
1984
|
# pylint: disable=line-too-long
|
|
2062
|
-
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL,
|
|
1985
|
+
result=L2CAP_LE_Credit_Based_Connection_Response.Result.CONNECTION_SUCCESSFUL,
|
|
2063
1986
|
),
|
|
2064
1987
|
)
|
|
2065
1988
|
|
|
@@ -2080,12 +2003,15 @@ class ChannelManager:
|
|
|
2080
2003
|
mps=L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS,
|
|
2081
2004
|
initial_credits=0,
|
|
2082
2005
|
# pylint: disable=line-too-long
|
|
2083
|
-
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED,
|
|
2006
|
+
result=L2CAP_LE_Credit_Based_Connection_Response.Result.CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED,
|
|
2084
2007
|
),
|
|
2085
2008
|
)
|
|
2086
2009
|
|
|
2087
2010
|
def on_l2cap_le_credit_based_connection_response(
|
|
2088
|
-
self,
|
|
2011
|
+
self,
|
|
2012
|
+
connection: Connection,
|
|
2013
|
+
_cid: int,
|
|
2014
|
+
response: L2CAP_LE_Credit_Based_Connection_Response,
|
|
2089
2015
|
) -> None:
|
|
2090
2016
|
# Find the pending request by identifier
|
|
2091
2017
|
request = self.le_coc_requests.get(response.identifier)
|
|
@@ -2110,7 +2036,7 @@ class ChannelManager:
|
|
|
2110
2036
|
channel.on_connection_response(response)
|
|
2111
2037
|
|
|
2112
2038
|
def on_l2cap_le_flow_control_credit(
|
|
2113
|
-
self, connection: Connection, _cid: int, credit
|
|
2039
|
+
self, connection: Connection, _cid: int, credit: L2CAP_LE_Flow_Control_Credit
|
|
2114
2040
|
) -> None:
|
|
2115
2041
|
channel = self.find_le_coc_channel(connection.handle, credit.cid)
|
|
2116
2042
|
if channel is None:
|