bumble 0.0.214__py3-none-any.whl → 0.0.215__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 +16 -3
- bumble/a2dp.py +15 -16
- bumble/apps/auracast.py +13 -38
- bumble/apps/bench.py +9 -10
- bumble/apps/ble_rpa_tool.py +1 -0
- bumble/apps/console.py +22 -25
- bumble/apps/controller_info.py +19 -19
- bumble/apps/controller_loopback.py +2 -2
- bumble/apps/controllers.py +1 -1
- bumble/apps/device_info.py +3 -3
- bumble/apps/gatt_dump.py +1 -1
- bumble/apps/gg_bridge.py +5 -6
- bumble/apps/hci_bridge.py +3 -3
- bumble/apps/l2cap_bridge.py +3 -3
- bumble/apps/lea_unicast/app.py +15 -25
- bumble/apps/pair.py +30 -43
- bumble/apps/pandora_server.py +5 -4
- bumble/apps/player/player.py +19 -22
- bumble/apps/rfcomm_bridge.py +3 -8
- bumble/apps/scan.py +16 -6
- bumble/apps/show.py +3 -4
- bumble/apps/speaker/speaker.py +22 -22
- bumble/apps/unbond.py +2 -1
- bumble/apps/usb_probe.py +1 -2
- bumble/att.py +241 -246
- bumble/audio/io.py +5 -9
- bumble/avc.py +2 -2
- bumble/avctp.py +6 -7
- bumble/avdtp.py +19 -22
- bumble/avrcp.py +1096 -588
- bumble/codecs.py +2 -0
- bumble/controller.py +52 -13
- bumble/core.py +567 -248
- bumble/crypto/__init__.py +2 -2
- bumble/crypto/builtin.py +1 -1
- bumble/crypto/cryptography.py +2 -4
- bumble/data_types.py +1025 -0
- bumble/device.py +280 -278
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +3 -4
- bumble/drivers/rtk.py +26 -9
- bumble/gap.py +4 -4
- bumble/gatt.py +3 -2
- bumble/gatt_adapters.py +3 -11
- bumble/gatt_client.py +69 -81
- bumble/gatt_server.py +124 -124
- bumble/hci.py +67 -18
- bumble/helpers.py +19 -26
- bumble/hfp.py +10 -21
- bumble/hid.py +22 -16
- bumble/host.py +181 -103
- bumble/keys.py +5 -3
- bumble/l2cap.py +121 -74
- bumble/link.py +8 -9
- bumble/pairing.py +7 -6
- bumble/pandora/__init__.py +8 -7
- bumble/pandora/config.py +3 -1
- bumble/pandora/device.py +3 -2
- bumble/pandora/host.py +38 -36
- bumble/pandora/l2cap.py +22 -21
- bumble/pandora/security.py +15 -15
- bumble/pandora/utils.py +5 -3
- bumble/profiles/aics.py +11 -11
- bumble/profiles/ams.py +7 -8
- bumble/profiles/ancs.py +6 -7
- bumble/profiles/ascs.py +4 -9
- bumble/profiles/asha.py +8 -12
- bumble/profiles/bap.py +11 -23
- bumble/profiles/bass.py +2 -7
- bumble/profiles/battery_service.py +3 -4
- bumble/profiles/cap.py +1 -2
- bumble/profiles/csip.py +2 -6
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +4 -4
- bumble/profiles/gatt_service.py +1 -4
- bumble/profiles/gmap.py +5 -5
- bumble/profiles/hap.py +62 -59
- bumble/profiles/heart_rate_service.py +5 -4
- bumble/profiles/le_audio.py +3 -1
- bumble/profiles/mcp.py +3 -7
- bumble/profiles/pacs.py +3 -6
- bumble/profiles/pbp.py +2 -0
- bumble/profiles/tmap.py +2 -3
- bumble/profiles/vcs.py +2 -8
- bumble/profiles/vocs.py +8 -8
- bumble/rfcomm.py +11 -14
- bumble/rtp.py +1 -0
- bumble/sdp.py +10 -8
- bumble/smp.py +142 -153
- bumble/snoop.py +5 -5
- bumble/tools/generate_company_id_list.py +1 -0
- bumble/tools/intel_fw_download.py +3 -3
- bumble/tools/intel_util.py +4 -4
- bumble/tools/rtk_fw_download.py +6 -3
- bumble/tools/rtk_util.py +24 -7
- bumble/transport/__init__.py +19 -15
- bumble/transport/android_emulator.py +8 -13
- bumble/transport/android_netsim.py +19 -18
- bumble/transport/common.py +12 -15
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +4 -6
- bumble/transport/pty.py +5 -6
- bumble/transport/pyusb.py +7 -10
- bumble/transport/serial.py +2 -1
- bumble/transport/tcp_client.py +2 -2
- bumble/transport/tcp_server.py +11 -14
- bumble/transport/udp.py +3 -3
- bumble/transport/unix.py +67 -1
- bumble/transport/usb.py +6 -6
- bumble/transport/vhci.py +0 -1
- bumble/transport/ws_client.py +2 -1
- bumble/transport/ws_server.py +3 -2
- bumble/utils.py +20 -5
- bumble/vendor/android/hci.py +1 -2
- bumble/vendor/zephyr/hci.py +0 -1
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/METADATA +2 -1
- bumble-0.0.215.dist-info/RECORD +183 -0
- bumble-0.0.214.dist-info/RECORD +0 -182
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.214.dist-info → bumble-0.0.215.dist-info}/top_level.txt +0 -0
bumble/tools/rtk_util.py
CHANGED
|
@@ -20,11 +20,10 @@ import logging
|
|
|
20
20
|
|
|
21
21
|
import click
|
|
22
22
|
|
|
23
|
-
from bumble import transport
|
|
24
|
-
from bumble.host import Host
|
|
25
|
-
from bumble.drivers import rtk
|
|
26
23
|
import bumble.logging
|
|
27
|
-
|
|
24
|
+
from bumble import company_ids, hci, transport
|
|
25
|
+
from bumble.drivers import rtk
|
|
26
|
+
from bumble.host import Host
|
|
28
27
|
|
|
29
28
|
# -----------------------------------------------------------------------------
|
|
30
29
|
# Logging
|
|
@@ -62,10 +61,22 @@ async def do_load(usb_transport, force):
|
|
|
62
61
|
# Get the driver.
|
|
63
62
|
driver = await rtk.Driver.for_host(host, force)
|
|
64
63
|
if driver is None:
|
|
65
|
-
|
|
64
|
+
# Try to see if there's already a FW image loaded
|
|
65
|
+
firmware_version = await rtk.Driver.get_loaded_firmware_version(host)
|
|
66
|
+
if firmware_version is None:
|
|
67
|
+
print("Device not supported")
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
print(f"Firmware already loaded: 0x{firmware_version:08X}")
|
|
66
71
|
return
|
|
67
72
|
|
|
68
|
-
await driver.download_firmware()
|
|
73
|
+
firmware_version = await driver.download_firmware()
|
|
74
|
+
|
|
75
|
+
if firmware_version is None:
|
|
76
|
+
print("Failed to load firmware")
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
print(f"Loaded firmware version 0x{firmware_version:08X}")
|
|
69
80
|
|
|
70
81
|
|
|
71
82
|
# -----------------------------------------------------------------------------
|
|
@@ -107,7 +118,13 @@ async def do_info(usb_transport, force):
|
|
|
107
118
|
f" Config: {driver_info.config_name}\n"
|
|
108
119
|
)
|
|
109
120
|
else:
|
|
110
|
-
|
|
121
|
+
# Try to see if there's already a FW image loaded
|
|
122
|
+
firmware_version = await rtk.Driver.get_loaded_firmware_version(host)
|
|
123
|
+
if firmware_version is None:
|
|
124
|
+
print("Device not supported")
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
print(f"Firmware loaded: 0x{firmware_version:08X}")
|
|
111
128
|
|
|
112
129
|
|
|
113
130
|
# -----------------------------------------------------------------------------
|
bumble/transport/__init__.py
CHANGED
|
@@ -15,18 +15,14 @@
|
|
|
15
15
|
# -----------------------------------------------------------------------------
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
|
-
from contextlib import asynccontextmanager
|
|
19
18
|
import logging
|
|
20
19
|
import os
|
|
20
|
+
import re
|
|
21
21
|
from typing import Optional
|
|
22
22
|
|
|
23
23
|
from bumble import utils
|
|
24
|
-
from bumble.transport.common import (
|
|
25
|
-
Transport,
|
|
26
|
-
SnoopingTransport,
|
|
27
|
-
TransportSpecError,
|
|
28
|
-
)
|
|
29
24
|
from bumble.snoop import create_snooper
|
|
25
|
+
from bumble.transport.common import SnoopingTransport, Transport, TransportSpecError
|
|
30
26
|
|
|
31
27
|
# -----------------------------------------------------------------------------
|
|
32
28
|
# Logging
|
|
@@ -48,8 +44,8 @@ def _wrap_transport(transport: Transport) -> Transport:
|
|
|
48
44
|
return SnoopingTransport.create_with(
|
|
49
45
|
transport, create_snooper(snooper_spec)
|
|
50
46
|
)
|
|
51
|
-
except Exception
|
|
52
|
-
logger.
|
|
47
|
+
except Exception:
|
|
48
|
+
logger.exception('Exception while creating snooper')
|
|
53
49
|
|
|
54
50
|
return transport
|
|
55
51
|
|
|
@@ -88,12 +84,14 @@ async def open_transport(name: str) -> Transport:
|
|
|
88
84
|
scheme, *tail = name.split(':', 1)
|
|
89
85
|
spec = tail[0] if tail else None
|
|
90
86
|
metadata = None
|
|
91
|
-
if spec:
|
|
92
|
-
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
spec =
|
|
96
|
-
|
|
87
|
+
if spec and (m := re.search(r'\[(\w+=\w+(?:,\w+=\w+)*,?)\]', spec)):
|
|
88
|
+
metadata_str = m.group(1)
|
|
89
|
+
if m.start() == 0:
|
|
90
|
+
# <metadata><spec>
|
|
91
|
+
spec = spec[m.end() :]
|
|
92
|
+
else:
|
|
93
|
+
spec = spec[: m.start()]
|
|
94
|
+
metadata = dict([entry.split('=') for entry in metadata_str.split(',')])
|
|
97
95
|
|
|
98
96
|
transport = await _open_transport(scheme, spec)
|
|
99
97
|
if metadata:
|
|
@@ -185,12 +183,18 @@ async def _open_transport(scheme: str, spec: Optional[str]) -> Transport:
|
|
|
185
183
|
|
|
186
184
|
return await open_android_netsim_transport(spec)
|
|
187
185
|
|
|
188
|
-
if scheme
|
|
186
|
+
if scheme in ('unix', 'unix-client'):
|
|
189
187
|
from bumble.transport.unix import open_unix_client_transport
|
|
190
188
|
|
|
191
189
|
assert spec
|
|
192
190
|
return await open_unix_client_transport(spec)
|
|
193
191
|
|
|
192
|
+
if scheme == 'unix-server':
|
|
193
|
+
from bumble.transport.unix import open_unix_server_transport
|
|
194
|
+
|
|
195
|
+
assert spec
|
|
196
|
+
return await open_unix_server_transport(spec)
|
|
197
|
+
|
|
194
198
|
raise TransportSpecError('unknown transport scheme')
|
|
195
199
|
|
|
196
200
|
|
|
@@ -16,28 +16,27 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import logging
|
|
19
|
-
import grpc.aio
|
|
20
|
-
|
|
21
19
|
from typing import Optional, Union
|
|
22
20
|
|
|
21
|
+
import grpc.aio
|
|
22
|
+
|
|
23
23
|
from bumble.transport.common import (
|
|
24
|
-
PumpedTransport,
|
|
25
|
-
PumpedPacketSource,
|
|
26
24
|
PumpedPacketSink,
|
|
25
|
+
PumpedPacketSource,
|
|
26
|
+
PumpedTransport,
|
|
27
27
|
Transport,
|
|
28
28
|
TransportSpecError,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
# pylint: disable=no-name-in-module
|
|
32
|
+
from bumble.transport.grpc_protobuf.emulated_bluetooth_packets_pb2 import HCIPacket
|
|
32
33
|
from bumble.transport.grpc_protobuf.emulated_bluetooth_pb2_grpc import (
|
|
33
34
|
EmulatedBluetoothServiceStub,
|
|
34
35
|
)
|
|
35
|
-
from bumble.transport.grpc_protobuf.emulated_bluetooth_packets_pb2 import HCIPacket
|
|
36
36
|
from bumble.transport.grpc_protobuf.emulated_bluetooth_vhci_pb2_grpc import (
|
|
37
37
|
VhciForwardingServiceStub,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
-
|
|
41
40
|
# -----------------------------------------------------------------------------
|
|
42
41
|
# Logging
|
|
43
42
|
# -----------------------------------------------------------------------------
|
|
@@ -77,21 +76,17 @@ async def open_android_emulator_transport(spec: Optional[str]) -> Transport:
|
|
|
77
76
|
|
|
78
77
|
# Parse the parameters
|
|
79
78
|
mode = 'host'
|
|
80
|
-
|
|
81
|
-
server_port = '8554'
|
|
79
|
+
server_address = 'localhost:8554'
|
|
82
80
|
if spec:
|
|
83
81
|
params = spec.split(',')
|
|
84
82
|
for param in params:
|
|
85
83
|
if param.startswith('mode='):
|
|
86
84
|
mode = param.split('=')[1]
|
|
87
|
-
elif ':' in param:
|
|
88
|
-
server_host, server_port = param.split(':')
|
|
89
85
|
else:
|
|
90
|
-
|
|
86
|
+
server_address = param
|
|
91
87
|
|
|
92
88
|
# Connect to the gRPC server
|
|
93
|
-
|
|
94
|
-
logger.debug(f'connecting to gRPC server at {server_address}')
|
|
89
|
+
logger.debug('connecting to gRPC server at %s', server_address)
|
|
95
90
|
channel = grpc.aio.insecure_channel(server_address)
|
|
96
91
|
|
|
97
92
|
service: Union[EmulatedBluetoothServiceStub, VhciForwardingServiceStub]
|
|
@@ -29,28 +29,27 @@ import grpc.aio
|
|
|
29
29
|
import bumble
|
|
30
30
|
from bumble.transport.common import (
|
|
31
31
|
ParserSource,
|
|
32
|
-
PumpedTransport,
|
|
33
|
-
PumpedPacketSource,
|
|
34
32
|
PumpedPacketSink,
|
|
33
|
+
PumpedPacketSource,
|
|
34
|
+
PumpedTransport,
|
|
35
35
|
Transport,
|
|
36
|
-
TransportSpecError,
|
|
37
36
|
TransportInitError,
|
|
37
|
+
TransportSpecError,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
# pylint: disable=no-name-in-module
|
|
41
|
-
from bumble.transport.grpc_protobuf.netsim.
|
|
42
|
-
|
|
43
|
-
PacketStreamerServicer,
|
|
44
|
-
add_PacketStreamerServicer_to_server,
|
|
45
|
-
)
|
|
41
|
+
from bumble.transport.grpc_protobuf.netsim.common_pb2 import ChipKind
|
|
42
|
+
from bumble.transport.grpc_protobuf.netsim.hci_packet_pb2 import HCIPacket
|
|
46
43
|
from bumble.transport.grpc_protobuf.netsim.packet_streamer_pb2 import (
|
|
47
44
|
PacketRequest,
|
|
48
45
|
PacketResponse,
|
|
49
46
|
)
|
|
50
|
-
from bumble.transport.grpc_protobuf.netsim.
|
|
47
|
+
from bumble.transport.grpc_protobuf.netsim.packet_streamer_pb2_grpc import (
|
|
48
|
+
PacketStreamerServicer,
|
|
49
|
+
PacketStreamerStub,
|
|
50
|
+
add_PacketStreamerServicer_to_server,
|
|
51
|
+
)
|
|
51
52
|
from bumble.transport.grpc_protobuf.netsim.startup_pb2 import Chip, ChipInfo, DeviceInfo
|
|
52
|
-
from bumble.transport.grpc_protobuf.netsim.common_pb2 import ChipKind
|
|
53
|
-
|
|
54
53
|
|
|
55
54
|
# -----------------------------------------------------------------------------
|
|
56
55
|
# Logging
|
|
@@ -145,8 +144,6 @@ def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:
|
|
|
145
144
|
async def open_android_netsim_controller_transport(
|
|
146
145
|
server_host: Optional[str], server_port: int, options: dict[str, str]
|
|
147
146
|
) -> Transport:
|
|
148
|
-
if not server_port:
|
|
149
|
-
raise TransportSpecError('invalid port')
|
|
150
147
|
if server_host == '_' or not server_host:
|
|
151
148
|
server_host = 'localhost'
|
|
152
149
|
|
|
@@ -168,14 +165,16 @@ async def open_android_netsim_controller_transport(
|
|
|
168
165
|
await self.pump_loop()
|
|
169
166
|
except asyncio.CancelledError:
|
|
170
167
|
logger.debug('Pump task canceled')
|
|
171
|
-
self.done.
|
|
168
|
+
if not self.done.done():
|
|
169
|
+
self.done.set_result(None)
|
|
172
170
|
|
|
173
171
|
async def pump_loop(self):
|
|
174
172
|
while True:
|
|
175
173
|
request = await self.context.read()
|
|
176
174
|
if request == grpc.aio.EOF:
|
|
177
175
|
logger.debug('End of request stream')
|
|
178
|
-
self.done.
|
|
176
|
+
if not self.done.done():
|
|
177
|
+
self.done.set_result(None)
|
|
179
178
|
return
|
|
180
179
|
|
|
181
180
|
# If we're not initialized yet, wait for a init packet.
|
|
@@ -220,6 +219,8 @@ async def open_android_netsim_controller_transport(
|
|
|
220
219
|
async def wait_for_termination(self):
|
|
221
220
|
await self.done
|
|
222
221
|
|
|
222
|
+
server_address = f'{server_host}:{server_port}'
|
|
223
|
+
|
|
223
224
|
class Server(PacketStreamerServicer, ParserSource):
|
|
224
225
|
def __init__(self):
|
|
225
226
|
PacketStreamerServicer.__init__(self)
|
|
@@ -230,8 +231,8 @@ async def open_android_netsim_controller_transport(
|
|
|
230
231
|
# a server listening on that port, we get an exception.
|
|
231
232
|
self.grpc_server = grpc.aio.server(options=(('grpc.so_reuseport', 0),))
|
|
232
233
|
add_PacketStreamerServicer_to_server(self, self.grpc_server)
|
|
233
|
-
self.grpc_server.add_insecure_port(
|
|
234
|
-
logger.debug(
|
|
234
|
+
self.port = self.grpc_server.add_insecure_port(server_address)
|
|
235
|
+
logger.debug('gRPC server listening on %s', server_address)
|
|
235
236
|
|
|
236
237
|
async def start(self):
|
|
237
238
|
logger.debug('Starting gRPC server')
|
|
@@ -443,7 +444,7 @@ async def open_android_netsim_transport(spec: Optional[str]) -> Transport:
|
|
|
443
444
|
params = spec.split(',') if spec else []
|
|
444
445
|
if params and ':' in params[0]:
|
|
445
446
|
# Explicit <host>:<port>
|
|
446
|
-
host, port_str = params[0].
|
|
447
|
+
host, port_str = params[0].rsplit(':', maxsplit=1)
|
|
447
448
|
port = int(port_str)
|
|
448
449
|
params_offset = 1
|
|
449
450
|
else:
|
bumble/transport/common.py
CHANGED
|
@@ -16,19 +16,18 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
from __future__ import annotations
|
|
19
|
-
|
|
20
|
-
import struct
|
|
19
|
+
|
|
21
20
|
import asyncio
|
|
22
|
-
import
|
|
21
|
+
import contextlib
|
|
23
22
|
import io
|
|
23
|
+
import logging
|
|
24
|
+
import struct
|
|
24
25
|
from typing import Any, ContextManager, Optional, Protocol
|
|
25
26
|
|
|
26
|
-
from bumble import core
|
|
27
|
-
from bumble import hci
|
|
27
|
+
from bumble import core, hci
|
|
28
28
|
from bumble.colors import color
|
|
29
29
|
from bumble.snoop import Snooper
|
|
30
30
|
|
|
31
|
-
|
|
32
31
|
# -----------------------------------------------------------------------------
|
|
33
32
|
# Logging
|
|
34
33
|
# -----------------------------------------------------------------------------
|
|
@@ -90,8 +89,8 @@ class PacketPump:
|
|
|
90
89
|
try:
|
|
91
90
|
# Deliver the packet to the sink
|
|
92
91
|
self.sink.on_packet(await self.reader.next_packet())
|
|
93
|
-
except Exception
|
|
94
|
-
logger.
|
|
92
|
+
except Exception:
|
|
93
|
+
logger.exception('!!!')
|
|
95
94
|
|
|
96
95
|
|
|
97
96
|
# -----------------------------------------------------------------------------
|
|
@@ -158,10 +157,8 @@ class PacketParser:
|
|
|
158
157
|
if self.sink:
|
|
159
158
|
try:
|
|
160
159
|
self.sink.on_packet(bytes(self.packet))
|
|
161
|
-
except Exception
|
|
162
|
-
logger.exception(
|
|
163
|
-
color(f'!!! Exception in on_packet: {error}', 'red')
|
|
164
|
-
)
|
|
160
|
+
except Exception:
|
|
161
|
+
logger.exception(color('!!! Exception in on_packet', 'red'))
|
|
165
162
|
self.reset()
|
|
166
163
|
|
|
167
164
|
def set_packet_sink(self, sink: TransportSink) -> None:
|
|
@@ -378,7 +375,7 @@ class PumpedPacketSource(ParserSource):
|
|
|
378
375
|
self.terminated.set_result(None)
|
|
379
376
|
break
|
|
380
377
|
except Exception as error:
|
|
381
|
-
logger.
|
|
378
|
+
logger.exception('exception while waiting for packet')
|
|
382
379
|
if not self.terminated.done():
|
|
383
380
|
self.terminated.set_exception(error)
|
|
384
381
|
break
|
|
@@ -409,8 +406,8 @@ class PumpedPacketSink:
|
|
|
409
406
|
except asyncio.CancelledError:
|
|
410
407
|
logger.debug('sink pump task done')
|
|
411
408
|
break
|
|
412
|
-
except Exception
|
|
413
|
-
logger.
|
|
409
|
+
except Exception:
|
|
410
|
+
logger.exception('exception while sending packet')
|
|
414
411
|
break
|
|
415
412
|
|
|
416
413
|
self.pump_task = asyncio.create_task(pump_packets())
|
bumble/transport/file.py
CHANGED
|
@@ -19,7 +19,7 @@ import asyncio
|
|
|
19
19
|
import io
|
|
20
20
|
import logging
|
|
21
21
|
|
|
22
|
-
from bumble.transport.common import
|
|
22
|
+
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
|
|
23
23
|
|
|
24
24
|
# -----------------------------------------------------------------------------
|
|
25
25
|
# Logging
|
bumble/transport/hci_socket.py
CHANGED
|
@@ -16,17 +16,15 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import asyncio
|
|
19
|
+
import collections
|
|
20
|
+
import ctypes
|
|
19
21
|
import logging
|
|
20
|
-
import struct
|
|
21
22
|
import os
|
|
22
23
|
import socket
|
|
23
|
-
import
|
|
24
|
-
import collections
|
|
25
|
-
|
|
24
|
+
import struct
|
|
26
25
|
from typing import Optional
|
|
27
26
|
|
|
28
|
-
from bumble.transport.common import
|
|
29
|
-
|
|
27
|
+
from bumble.transport.common import ParserSource, Transport
|
|
30
28
|
|
|
31
29
|
# -----------------------------------------------------------------------------
|
|
32
30
|
# Logging
|
bumble/transport/pty.py
CHANGED
|
@@ -16,16 +16,15 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import asyncio
|
|
19
|
-
import pty
|
|
20
|
-
import tty
|
|
21
|
-
import io
|
|
22
19
|
import atexit
|
|
23
|
-
import
|
|
20
|
+
import io
|
|
24
21
|
import logging
|
|
25
|
-
|
|
22
|
+
import os
|
|
23
|
+
import pty
|
|
24
|
+
import tty
|
|
26
25
|
from typing import Optional
|
|
27
26
|
|
|
28
|
-
from bumble.transport.common import
|
|
27
|
+
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
|
|
29
28
|
|
|
30
29
|
# -----------------------------------------------------------------------------
|
|
31
30
|
# Logging
|
bumble/transport/pyusb.py
CHANGED
|
@@ -19,20 +19,18 @@ import asyncio
|
|
|
19
19
|
import logging
|
|
20
20
|
import threading
|
|
21
21
|
import time
|
|
22
|
+
from typing import Optional
|
|
22
23
|
|
|
23
24
|
import usb.core
|
|
24
25
|
import usb.util
|
|
25
|
-
|
|
26
|
-
from typing import Optional
|
|
27
26
|
from usb.core import Device as UsbDevice
|
|
28
27
|
from usb.core import USBError
|
|
29
|
-
from usb.
|
|
30
|
-
from usb.
|
|
28
|
+
from usb.legacy import CLASS_HUB, REQ_CLEAR_FEATURE, REQ_SET_FEATURE
|
|
29
|
+
from usb.util import CTRL_RECIPIENT_OTHER, CTRL_TYPE_CLASS
|
|
31
30
|
|
|
32
|
-
from bumble.transport.common import Transport, ParserSource, TransportInitError
|
|
33
31
|
from bumble import hci
|
|
34
32
|
from bumble.colors import color
|
|
35
|
-
|
|
33
|
+
from bumble.transport.common import ParserSource, Transport, TransportInitError
|
|
36
34
|
|
|
37
35
|
# -----------------------------------------------------------------------------
|
|
38
36
|
# Constant
|
|
@@ -285,7 +283,7 @@ async def open_pyusb_transport(spec: str) -> Transport:
|
|
|
285
283
|
try:
|
|
286
284
|
device = await _power_cycle(device) # type: ignore
|
|
287
285
|
except Exception as e:
|
|
288
|
-
logging.debug(e)
|
|
286
|
+
logging.debug(e, stack_info=True)
|
|
289
287
|
logging.info(f"Unable to power cycle {hex(device.idVendor)} {hex(device.idProduct)}") # type: ignore
|
|
290
288
|
|
|
291
289
|
# Collect the metadata
|
|
@@ -371,9 +369,8 @@ async def _power_cycle(device: UsbDevice) -> UsbDevice:
|
|
|
371
369
|
|
|
372
370
|
# Device needs to be find again otherwise it will appear as disconnected
|
|
373
371
|
return usb.core.find(idVendor=device.idVendor, idProduct=device.idProduct) # type: ignore
|
|
374
|
-
except USBError
|
|
375
|
-
logger.
|
|
376
|
-
logger.error(e)
|
|
372
|
+
except USBError:
|
|
373
|
+
logger.exception(f"Adjustment needed: Please revise the udev rule for device {hex(device.idVendor)}:{hex(device.idProduct)} for proper recognition.") # type: ignore
|
|
377
374
|
|
|
378
375
|
return device
|
|
379
376
|
|
bumble/transport/serial.py
CHANGED
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import asyncio
|
|
19
19
|
import logging
|
|
20
|
+
|
|
20
21
|
import serial_asyncio
|
|
21
22
|
|
|
22
|
-
from bumble.transport.common import
|
|
23
|
+
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
|
|
23
24
|
|
|
24
25
|
# -----------------------------------------------------------------------------
|
|
25
26
|
# Logging
|
bumble/transport/tcp_client.py
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import asyncio
|
|
19
19
|
import logging
|
|
20
20
|
|
|
21
|
-
from bumble.transport.common import
|
|
21
|
+
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
|
|
22
22
|
|
|
23
23
|
# -----------------------------------------------------------------------------
|
|
24
24
|
# Logging
|
|
@@ -41,7 +41,7 @@ async def open_tcp_client_transport(spec: str) -> Transport:
|
|
|
41
41
|
logger.debug(f'connection lost: {exc}')
|
|
42
42
|
self.on_transport_lost()
|
|
43
43
|
|
|
44
|
-
remote_host, remote_port = spec.
|
|
44
|
+
remote_host, remote_port = spec.rsplit(':', maxsplit=1)
|
|
45
45
|
tcp_transport, packet_source = await asyncio.get_running_loop().create_connection(
|
|
46
46
|
TcpPacketSource,
|
|
47
47
|
host=remote_host,
|
bumble/transport/tcp_server.py
CHANGED
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
from __future__ import annotations
|
|
19
|
+
|
|
19
20
|
import asyncio
|
|
20
21
|
import logging
|
|
21
22
|
import socket
|
|
22
23
|
|
|
23
|
-
from bumble.transport.common import
|
|
24
|
+
from bumble.transport.common import StreamPacketSource, Transport
|
|
24
25
|
|
|
25
26
|
# -----------------------------------------------------------------------------
|
|
26
27
|
# Logging
|
|
@@ -29,13 +30,6 @@ logger = logging.getLogger(__name__)
|
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
# -----------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# A pass-through function to ease mock testing.
|
|
35
|
-
async def _create_server(*args, **kw_args):
|
|
36
|
-
await asyncio.get_running_loop().create_server(*args, **kw_args)
|
|
37
|
-
|
|
38
|
-
|
|
39
33
|
async def open_tcp_server_transport(spec: str) -> Transport:
|
|
40
34
|
'''
|
|
41
35
|
Open a TCP server transport.
|
|
@@ -46,13 +40,15 @@ async def open_tcp_server_transport(spec: str) -> Transport:
|
|
|
46
40
|
|
|
47
41
|
Example: _:9001
|
|
48
42
|
'''
|
|
49
|
-
local_host, local_port = spec.
|
|
43
|
+
local_host, local_port = spec.rsplit(':', maxsplit=1)
|
|
50
44
|
return await _open_tcp_server_transport_impl(
|
|
51
45
|
host=local_host if local_host != '_' else None, port=int(local_port)
|
|
52
46
|
)
|
|
53
47
|
|
|
54
48
|
|
|
55
|
-
async def open_tcp_server_transport_with_socket(
|
|
49
|
+
async def open_tcp_server_transport_with_socket(
|
|
50
|
+
sock: socket.socket,
|
|
51
|
+
) -> Transport:
|
|
56
52
|
'''
|
|
57
53
|
Open a TCP server transport with an existing socket.
|
|
58
54
|
|
|
@@ -63,8 +59,9 @@ async def open_tcp_server_transport_with_socket(sock: socket.socket) -> Transpor
|
|
|
63
59
|
|
|
64
60
|
async def _open_tcp_server_transport_impl(**kwargs) -> Transport:
|
|
65
61
|
class TcpServerTransport(Transport):
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
def __init__(self, source, sink, server):
|
|
63
|
+
self.server = server
|
|
64
|
+
super().__init__(source, sink)
|
|
68
65
|
|
|
69
66
|
class TcpServerProtocol(asyncio.BaseProtocol):
|
|
70
67
|
def __init__(self, packet_source, packet_sink):
|
|
@@ -102,8 +99,8 @@ async def _open_tcp_server_transport_impl(**kwargs) -> Transport:
|
|
|
102
99
|
|
|
103
100
|
packet_source = StreamPacketSource()
|
|
104
101
|
packet_sink = TcpServerPacketSink()
|
|
105
|
-
await
|
|
102
|
+
server = await asyncio.get_running_loop().create_server(
|
|
106
103
|
lambda: TcpServerProtocol(packet_source, packet_sink), **kwargs
|
|
107
104
|
)
|
|
108
105
|
|
|
109
|
-
return TcpServerTransport(packet_source, packet_sink)
|
|
106
|
+
return TcpServerTransport(packet_source, packet_sink, server)
|
bumble/transport/udp.py
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import asyncio
|
|
19
19
|
import logging
|
|
20
20
|
|
|
21
|
-
from bumble.transport.common import
|
|
21
|
+
from bumble.transport.common import ParserSource, Transport
|
|
22
22
|
|
|
23
23
|
# -----------------------------------------------------------------------------
|
|
24
24
|
# Logging
|
|
@@ -51,8 +51,8 @@ async def open_udp_transport(spec: str) -> Transport:
|
|
|
51
51
|
self.transport.close()
|
|
52
52
|
|
|
53
53
|
local, remote = spec.split(',')
|
|
54
|
-
local_host, local_port = local.
|
|
55
|
-
remote_host, remote_port = remote.
|
|
54
|
+
local_host, local_port = local.rsplit(':', maxsplit=1)
|
|
55
|
+
remote_host, remote_port = remote.rsplit(':', maxsplit=1)
|
|
56
56
|
(
|
|
57
57
|
udp_transport,
|
|
58
58
|
packet_source,
|
bumble/transport/unix.py
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import asyncio
|
|
19
19
|
import logging
|
|
20
20
|
|
|
21
|
-
from bumble.transport.common import
|
|
21
|
+
from bumble.transport.common import StreamPacketSink, StreamPacketSource, Transport
|
|
22
22
|
|
|
23
23
|
# -----------------------------------------------------------------------------
|
|
24
24
|
# Logging
|
|
@@ -54,3 +54,69 @@ async def open_unix_client_transport(spec: str) -> Transport:
|
|
|
54
54
|
packet_sink = StreamPacketSink(unix_transport)
|
|
55
55
|
|
|
56
56
|
return Transport(packet_source, packet_sink)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# -----------------------------------------------------------------------------
|
|
60
|
+
async def open_unix_server_transport(spec: str) -> Transport:
|
|
61
|
+
'''Open a UNIX socket server transport.
|
|
62
|
+
|
|
63
|
+
The parameter is the path of unix socket. For abstract socket, the first character
|
|
64
|
+
needs to be '@'.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
* /tmp/hci.socket
|
|
68
|
+
* @hci_socket
|
|
69
|
+
'''
|
|
70
|
+
# For abstract socket, the first character should be null character.
|
|
71
|
+
if spec.startswith('@'):
|
|
72
|
+
spec = '\0' + spec[1:]
|
|
73
|
+
|
|
74
|
+
class UnixServerTransport(Transport):
|
|
75
|
+
def __init__(self, source, sink, server):
|
|
76
|
+
self.server = server
|
|
77
|
+
super().__init__(source, sink)
|
|
78
|
+
|
|
79
|
+
async def close(self):
|
|
80
|
+
await super().close()
|
|
81
|
+
|
|
82
|
+
class UnixServerProtocol(asyncio.BaseProtocol):
|
|
83
|
+
def __init__(self, packet_source, packet_sink):
|
|
84
|
+
self.packet_source = packet_source
|
|
85
|
+
self.packet_sink = packet_sink
|
|
86
|
+
|
|
87
|
+
# Called when a new connection is established
|
|
88
|
+
def connection_made(self, transport):
|
|
89
|
+
peer_name = transport.get_extra_info('peer_name')
|
|
90
|
+
logger.debug('connection from %s', peer_name)
|
|
91
|
+
self.packet_sink.transport = transport
|
|
92
|
+
|
|
93
|
+
# Called when the client is disconnected
|
|
94
|
+
def connection_lost(self, error):
|
|
95
|
+
logger.debug('connection lost: %s', error)
|
|
96
|
+
self.packet_sink.transport = None
|
|
97
|
+
|
|
98
|
+
def eof_received(self):
|
|
99
|
+
logger.debug('connection end')
|
|
100
|
+
self.packet_sink.transport = None
|
|
101
|
+
|
|
102
|
+
# Called when data is received on the socket
|
|
103
|
+
def data_received(self, data):
|
|
104
|
+
self.packet_source.data_received(data)
|
|
105
|
+
|
|
106
|
+
class UnixServerPacketSink:
|
|
107
|
+
def __init__(self):
|
|
108
|
+
self.transport = None
|
|
109
|
+
|
|
110
|
+
def on_packet(self, packet):
|
|
111
|
+
if self.transport:
|
|
112
|
+
self.transport.write(packet)
|
|
113
|
+
else:
|
|
114
|
+
logger.debug('no client, dropping packet')
|
|
115
|
+
|
|
116
|
+
packet_source = StreamPacketSource()
|
|
117
|
+
packet_sink = UnixServerPacketSink()
|
|
118
|
+
server = await asyncio.get_running_loop().create_unix_server(
|
|
119
|
+
lambda: UnixServerProtocol(packet_source, packet_sink), spec
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return UnixServerTransport(packet_source, packet_sink, server)
|