bumble 0.0.223__py3-none-any.whl → 0.0.224__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/apps/controller_info.py +90 -114
- bumble/apps/controller_loopback.py +11 -9
- bumble/apps/gg_bridge.py +1 -1
- bumble/apps/hci_bridge.py +3 -1
- bumble/apps/l2cap_bridge.py +1 -1
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/scan.py +10 -4
- bumble/apps/speaker/speaker.py +1 -1
- bumble/avrcp.py +366 -190
- bumble/bridge.py +10 -2
- bumble/controller.py +14 -1
- bumble/core.py +1 -1
- bumble/device.py +998 -573
- bumble/drivers/intel.py +45 -39
- bumble/drivers/rtk.py +76 -40
- bumble/hci.py +1318 -796
- bumble/host.py +329 -157
- bumble/l2cap.py +10 -5
- bumble/smp.py +8 -3
- bumble/snoop.py +111 -1
- bumble/transport/android_netsim.py +1 -1
- bumble/vendor/android/hci.py +108 -86
- bumble/vendor/zephyr/hci.py +24 -18
- {bumble-0.0.223.dist-info → bumble-0.0.224.dist-info}/METADATA +4 -3
- {bumble-0.0.223.dist-info → bumble-0.0.224.dist-info}/RECORD +30 -30
- {bumble-0.0.223.dist-info → bumble-0.0.224.dist-info}/WHEEL +1 -1
- {bumble-0.0.223.dist-info → bumble-0.0.224.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.223.dist-info → bumble-0.0.224.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.223.dist-info → bumble-0.0.224.dist-info}/top_level.txt +0 -0
bumble/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.224'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 224)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
bumble/apps/controller_info.py
CHANGED
|
@@ -27,23 +27,17 @@ from bumble.core import name_or_number
|
|
|
27
27
|
from bumble.hci import (
|
|
28
28
|
HCI_LE_READ_BUFFER_SIZE_COMMAND,
|
|
29
29
|
HCI_LE_READ_BUFFER_SIZE_V2_COMMAND,
|
|
30
|
-
HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND,
|
|
31
30
|
HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND,
|
|
32
|
-
|
|
31
|
+
HCI_LE_READ_MINIMUM_SUPPORTED_CONNECTION_INTERVAL_COMMAND,
|
|
33
32
|
HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND,
|
|
34
33
|
HCI_READ_BD_ADDR_COMMAND,
|
|
35
34
|
HCI_READ_BUFFER_SIZE_COMMAND,
|
|
36
35
|
HCI_READ_LOCAL_NAME_COMMAND,
|
|
37
|
-
HCI_SUCCESS,
|
|
38
|
-
CodecID,
|
|
39
36
|
HCI_Command,
|
|
40
|
-
HCI_Command_Complete_Event,
|
|
41
|
-
HCI_Command_Status_Event,
|
|
42
37
|
HCI_LE_Read_Buffer_Size_Command,
|
|
43
38
|
HCI_LE_Read_Buffer_Size_V2_Command,
|
|
44
|
-
HCI_LE_Read_Maximum_Advertising_Data_Length_Command,
|
|
45
39
|
HCI_LE_Read_Maximum_Data_Length_Command,
|
|
46
|
-
|
|
40
|
+
HCI_LE_Read_Minimum_Supported_Connection_Interval_Command,
|
|
47
41
|
HCI_LE_Read_Suggested_Default_Data_Length_Command,
|
|
48
42
|
HCI_Read_BD_ADDR_Command,
|
|
49
43
|
HCI_Read_Buffer_Size_Command,
|
|
@@ -59,85 +53,81 @@ from bumble.host import Host
|
|
|
59
53
|
from bumble.transport import open_transport
|
|
60
54
|
|
|
61
55
|
|
|
62
|
-
# -----------------------------------------------------------------------------
|
|
63
|
-
def command_succeeded(response):
|
|
64
|
-
if isinstance(response, HCI_Command_Status_Event):
|
|
65
|
-
return response.status == HCI_SUCCESS
|
|
66
|
-
if isinstance(response, HCI_Command_Complete_Event):
|
|
67
|
-
return response.return_parameters.status == HCI_SUCCESS
|
|
68
|
-
return False
|
|
69
|
-
|
|
70
|
-
|
|
71
56
|
# -----------------------------------------------------------------------------
|
|
72
57
|
async def get_classic_info(host: Host) -> None:
|
|
73
58
|
if host.supports_command(HCI_READ_BD_ADDR_COMMAND):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
59
|
+
response1 = await host.send_sync_command(HCI_Read_BD_ADDR_Command())
|
|
60
|
+
print()
|
|
61
|
+
print(
|
|
62
|
+
color('Public Address:', 'yellow'),
|
|
63
|
+
response1.bd_addr.to_string(False),
|
|
64
|
+
)
|
|
81
65
|
|
|
82
66
|
if host.supports_command(HCI_READ_LOCAL_NAME_COMMAND):
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
67
|
+
response2 = await host.send_sync_command(HCI_Read_Local_Name_Command())
|
|
68
|
+
print()
|
|
69
|
+
print(
|
|
70
|
+
color('Local Name:', 'yellow'),
|
|
71
|
+
map_null_terminated_utf8_string(response2.local_name),
|
|
72
|
+
)
|
|
90
73
|
|
|
91
74
|
|
|
92
75
|
# -----------------------------------------------------------------------------
|
|
93
76
|
async def get_le_info(host: Host) -> None:
|
|
94
77
|
print()
|
|
95
78
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
print(
|
|
102
|
-
color('LE Number Of Supported Advertising Sets:', 'yellow'),
|
|
103
|
-
response.return_parameters.num_supported_advertising_sets,
|
|
104
|
-
'\n',
|
|
105
|
-
)
|
|
79
|
+
print(
|
|
80
|
+
color('LE Number Of Supported Advertising Sets:', 'yellow'),
|
|
81
|
+
host.number_of_supported_advertising_sets,
|
|
82
|
+
'\n',
|
|
83
|
+
)
|
|
106
84
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
print(
|
|
113
|
-
color('LE Maximum Advertising Data Length:', 'yellow'),
|
|
114
|
-
response.return_parameters.max_advertising_data_length,
|
|
115
|
-
'\n',
|
|
116
|
-
)
|
|
85
|
+
print(
|
|
86
|
+
color('LE Maximum Advertising Data Length:', 'yellow'),
|
|
87
|
+
host.maximum_advertising_data_length,
|
|
88
|
+
'\n',
|
|
89
|
+
)
|
|
117
90
|
|
|
118
91
|
if host.supports_command(HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND):
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
92
|
+
response1 = await host.send_sync_command(
|
|
93
|
+
HCI_LE_Read_Maximum_Data_Length_Command()
|
|
94
|
+
)
|
|
95
|
+
print(
|
|
96
|
+
color('LE Maximum Data Length:', 'yellow'),
|
|
97
|
+
(
|
|
98
|
+
f'tx:{response1.supported_max_tx_octets}/'
|
|
99
|
+
f'{response1.supported_max_tx_time}, '
|
|
100
|
+
f'rx:{response1.supported_max_rx_octets}/'
|
|
101
|
+
f'{response1.supported_max_rx_time}'
|
|
102
|
+
),
|
|
103
|
+
)
|
|
131
104
|
|
|
132
105
|
if host.supports_command(HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND):
|
|
133
|
-
|
|
106
|
+
response2 = await host.send_sync_command(
|
|
134
107
|
HCI_LE_Read_Suggested_Default_Data_Length_Command()
|
|
135
108
|
)
|
|
136
|
-
|
|
109
|
+
print(
|
|
110
|
+
color('LE Suggested Default Data Length:', 'yellow'),
|
|
111
|
+
f'{response2.suggested_max_tx_octets}/'
|
|
112
|
+
f'{response2.suggested_max_tx_time}',
|
|
113
|
+
'\n',
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if host.supports_command(HCI_LE_READ_MINIMUM_SUPPORTED_CONNECTION_INTERVAL_COMMAND):
|
|
117
|
+
response3 = await host.send_sync_command(
|
|
118
|
+
HCI_LE_Read_Minimum_Supported_Connection_Interval_Command()
|
|
119
|
+
)
|
|
120
|
+
print(
|
|
121
|
+
color('LE Minimum Supported Connection Interval:', 'yellow'),
|
|
122
|
+
f'{response3.minimum_supported_connection_interval * 125} µs',
|
|
123
|
+
)
|
|
124
|
+
for group in range(len(response3.group_min)):
|
|
137
125
|
print(
|
|
138
|
-
|
|
139
|
-
f'{
|
|
140
|
-
f'{
|
|
126
|
+
f' Group {group}: '
|
|
127
|
+
f'{response3.group_min[group] * 125} µs to '
|
|
128
|
+
f'{response3.group_max[group] * 125} µs '
|
|
129
|
+
'by increments of '
|
|
130
|
+
f'{response3.group_stride[group] * 125} µs',
|
|
141
131
|
'\n',
|
|
142
132
|
)
|
|
143
133
|
|
|
@@ -151,37 +141,31 @@ async def get_flow_control_info(host: Host) -> None:
|
|
|
151
141
|
print()
|
|
152
142
|
|
|
153
143
|
if host.supports_command(HCI_READ_BUFFER_SIZE_COMMAND):
|
|
154
|
-
|
|
155
|
-
HCI_Read_Buffer_Size_Command(), check_result=True
|
|
156
|
-
)
|
|
144
|
+
response1 = await host.send_sync_command(HCI_Read_Buffer_Size_Command())
|
|
157
145
|
print(
|
|
158
146
|
color('ACL Flow Control:', 'yellow'),
|
|
159
|
-
f'{
|
|
160
|
-
f'packets of size {
|
|
147
|
+
f'{response1.hc_total_num_acl_data_packets} '
|
|
148
|
+
f'packets of size {response1.hc_acl_data_packet_length}',
|
|
161
149
|
)
|
|
162
150
|
|
|
163
151
|
if host.supports_command(HCI_LE_READ_BUFFER_SIZE_V2_COMMAND):
|
|
164
|
-
|
|
165
|
-
HCI_LE_Read_Buffer_Size_V2_Command(), check_result=True
|
|
166
|
-
)
|
|
152
|
+
response2 = await host.send_sync_command(HCI_LE_Read_Buffer_Size_V2_Command())
|
|
167
153
|
print(
|
|
168
154
|
color('LE ACL Flow Control:', 'yellow'),
|
|
169
|
-
f'{
|
|
170
|
-
f'packets of size {
|
|
155
|
+
f'{response2.total_num_le_acl_data_packets} '
|
|
156
|
+
f'packets of size {response2.le_acl_data_packet_length}',
|
|
171
157
|
)
|
|
172
158
|
print(
|
|
173
159
|
color('LE ISO Flow Control:', 'yellow'),
|
|
174
|
-
f'{
|
|
175
|
-
f'packets of size {
|
|
160
|
+
f'{response2.total_num_iso_data_packets} '
|
|
161
|
+
f'packets of size {response2.iso_data_packet_length}',
|
|
176
162
|
)
|
|
177
163
|
elif host.supports_command(HCI_LE_READ_BUFFER_SIZE_COMMAND):
|
|
178
|
-
|
|
179
|
-
HCI_LE_Read_Buffer_Size_Command(), check_result=True
|
|
180
|
-
)
|
|
164
|
+
response3 = await host.send_sync_command(HCI_LE_Read_Buffer_Size_Command())
|
|
181
165
|
print(
|
|
182
166
|
color('LE ACL Flow Control:', 'yellow'),
|
|
183
|
-
f'{
|
|
184
|
-
f'packets of size {
|
|
167
|
+
f'{response3.total_num_le_acl_data_packets} '
|
|
168
|
+
f'packets of size {response3.le_acl_data_packet_length}',
|
|
185
169
|
)
|
|
186
170
|
|
|
187
171
|
|
|
@@ -190,52 +174,44 @@ async def get_codecs_info(host: Host) -> None:
|
|
|
190
174
|
print()
|
|
191
175
|
|
|
192
176
|
if host.supports_command(HCI_Read_Local_Supported_Codecs_V2_Command.op_code):
|
|
193
|
-
|
|
194
|
-
HCI_Read_Local_Supported_Codecs_V2_Command()
|
|
177
|
+
response1 = await host.send_sync_command(
|
|
178
|
+
HCI_Read_Local_Supported_Codecs_V2_Command()
|
|
195
179
|
)
|
|
196
180
|
print(color('Codecs:', 'yellow'))
|
|
197
181
|
|
|
198
182
|
for codec_id, transport in zip(
|
|
199
|
-
|
|
200
|
-
|
|
183
|
+
response1.standard_codec_ids,
|
|
184
|
+
response1.standard_codec_transports,
|
|
201
185
|
):
|
|
202
|
-
|
|
203
|
-
transport
|
|
204
|
-
).name
|
|
205
|
-
codec_name = CodecID(codec_id).name
|
|
206
|
-
print(f' {codec_name} - {transport_name}')
|
|
186
|
+
print(f' {codec_id.name} - {transport.name}')
|
|
207
187
|
|
|
208
|
-
for
|
|
209
|
-
|
|
210
|
-
|
|
188
|
+
for vendor_codec_id, vendor_transport in zip(
|
|
189
|
+
response1.vendor_specific_codec_ids,
|
|
190
|
+
response1.vendor_specific_codec_transports,
|
|
211
191
|
):
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
).name
|
|
215
|
-
company = name_or_number(COMPANY_IDENTIFIERS, codec_id >> 16)
|
|
216
|
-
print(f' {company} / {codec_id & 0xFFFF} - {transport_name}')
|
|
192
|
+
company = name_or_number(COMPANY_IDENTIFIERS, vendor_codec_id >> 16)
|
|
193
|
+
print(f' {company} / {vendor_codec_id & 0xFFFF} - {vendor_transport.name}')
|
|
217
194
|
|
|
218
|
-
if not
|
|
195
|
+
if not response1.standard_codec_ids:
|
|
219
196
|
print(' No standard codecs')
|
|
220
|
-
if not
|
|
197
|
+
if not response1.vendor_specific_codec_ids:
|
|
221
198
|
print(' No Vendor-specific codecs')
|
|
222
199
|
|
|
223
200
|
if host.supports_command(HCI_Read_Local_Supported_Codecs_Command.op_code):
|
|
224
|
-
|
|
225
|
-
HCI_Read_Local_Supported_Codecs_Command()
|
|
201
|
+
response2 = await host.send_sync_command(
|
|
202
|
+
HCI_Read_Local_Supported_Codecs_Command()
|
|
226
203
|
)
|
|
227
204
|
print(color('Codecs (BR/EDR):', 'yellow'))
|
|
228
|
-
for codec_id in
|
|
229
|
-
|
|
230
|
-
print(f' {codec_name}')
|
|
205
|
+
for codec_id in response2.standard_codec_ids:
|
|
206
|
+
print(f' {codec_id.name}')
|
|
231
207
|
|
|
232
|
-
for
|
|
233
|
-
company = name_or_number(COMPANY_IDENTIFIERS,
|
|
234
|
-
print(f' {company} / {
|
|
208
|
+
for vendor_codec_id in response2.vendor_specific_codec_ids:
|
|
209
|
+
company = name_or_number(COMPANY_IDENTIFIERS, vendor_codec_id >> 16)
|
|
210
|
+
print(f' {company} / {vendor_codec_id & 0xFFFF}')
|
|
235
211
|
|
|
236
|
-
if not
|
|
212
|
+
if not response2.standard_codec_ids:
|
|
237
213
|
print(' No standard codecs')
|
|
238
|
-
if not
|
|
214
|
+
if not response2.vendor_specific_codec_ids:
|
|
239
215
|
print(' No Vendor-specific codecs')
|
|
240
216
|
|
|
241
217
|
|
|
@@ -85,7 +85,7 @@ class Loopback:
|
|
|
85
85
|
print(color('@@@ Received last packet', 'green'))
|
|
86
86
|
self.done.set()
|
|
87
87
|
|
|
88
|
-
async def run(self):
|
|
88
|
+
async def run(self) -> None:
|
|
89
89
|
"""Run a loopback throughput test"""
|
|
90
90
|
print(color('>>> Connecting to HCI...', 'green'))
|
|
91
91
|
async with await open_transport(self.transport) as (
|
|
@@ -100,11 +100,15 @@ class Loopback:
|
|
|
100
100
|
# make sure data can fit in one l2cap pdu
|
|
101
101
|
l2cap_header_size = 4
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
packet_queue = (
|
|
104
104
|
host.acl_packet_queue
|
|
105
105
|
if host.acl_packet_queue
|
|
106
106
|
else host.le_acl_packet_queue
|
|
107
|
-
)
|
|
107
|
+
)
|
|
108
|
+
if packet_queue is None:
|
|
109
|
+
print(color('!!! No packet queue', 'red'))
|
|
110
|
+
return
|
|
111
|
+
max_packet_size = packet_queue.max_packet_size - l2cap_header_size
|
|
108
112
|
if self.packet_size > max_packet_size:
|
|
109
113
|
print(
|
|
110
114
|
color(
|
|
@@ -128,20 +132,18 @@ class Loopback:
|
|
|
128
132
|
loopback_mode = LoopbackMode.LOCAL
|
|
129
133
|
|
|
130
134
|
print(color('### Setting loopback mode', 'blue'))
|
|
131
|
-
await host.
|
|
135
|
+
await host.send_sync_command(
|
|
132
136
|
HCI_Write_Loopback_Mode_Command(loopback_mode=LoopbackMode.LOCAL),
|
|
133
|
-
check_result=True,
|
|
134
137
|
)
|
|
135
138
|
|
|
136
139
|
print(color('### Checking loopback mode', 'blue'))
|
|
137
|
-
response = await host.
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
if response.return_parameters.loopback_mode != loopback_mode:
|
|
140
|
+
response = await host.send_sync_command(HCI_Read_Loopback_Mode_Command())
|
|
141
|
+
if response.loopback_mode != loopback_mode:
|
|
141
142
|
print(color('!!! Loopback mode mismatch', 'red'))
|
|
142
143
|
return
|
|
143
144
|
|
|
144
145
|
await self.connection_event.wait()
|
|
146
|
+
assert self.connection_handle is not None
|
|
145
147
|
print(color('### Connected', 'cyan'))
|
|
146
148
|
|
|
147
149
|
print(color('=== Start sending', 'magenta'))
|
bumble/apps/gg_bridge.py
CHANGED
bumble/apps/hci_bridge.py
CHANGED
|
@@ -81,7 +81,9 @@ async def async_main():
|
|
|
81
81
|
response = hci.HCI_Command_Complete_Event(
|
|
82
82
|
num_hci_command_packets=1,
|
|
83
83
|
command_opcode=hci_packet.op_code,
|
|
84
|
-
return_parameters=
|
|
84
|
+
return_parameters=hci.HCI_StatusReturnParameters(
|
|
85
|
+
status=hci.HCI_ErrorCode.SUCCESS
|
|
86
|
+
),
|
|
85
87
|
)
|
|
86
88
|
# Return a packet with 'respond to sender' set to True
|
|
87
89
|
return (bytes(response), True)
|
bumble/apps/l2cap_bridge.py
CHANGED
|
@@ -268,7 +268,7 @@ async def run(device_config, hci_transport, bridge):
|
|
|
268
268
|
await bridge.start(device)
|
|
269
269
|
|
|
270
270
|
# Wait until the transport terminates
|
|
271
|
-
await hci_source.
|
|
271
|
+
await hci_source.terminated
|
|
272
272
|
|
|
273
273
|
|
|
274
274
|
# -----------------------------------------------------------------------------
|
bumble/apps/rfcomm_bridge.py
CHANGED
|
@@ -421,7 +421,7 @@ async def run(device_config, hci_transport, bridge):
|
|
|
421
421
|
await bridge.start(device)
|
|
422
422
|
|
|
423
423
|
# Wait until the transport terminates
|
|
424
|
-
await hci_source.
|
|
424
|
+
await hci_source.terminated
|
|
425
425
|
except core.ConnectionError as error:
|
|
426
426
|
print(color(f"!!! Bluetooth connection failed: {error}", "red"))
|
|
427
427
|
except Exception as error:
|
bumble/apps/scan.py
CHANGED
|
@@ -22,7 +22,7 @@ import click
|
|
|
22
22
|
import bumble.logging
|
|
23
23
|
from bumble import data_types
|
|
24
24
|
from bumble.colors import color
|
|
25
|
-
from bumble.device import Advertisement, Device
|
|
25
|
+
from bumble.device import Advertisement, Device, DeviceConfiguration
|
|
26
26
|
from bumble.hci import HCI_LE_1M_PHY, HCI_LE_CODED_PHY, Address, HCI_Constant
|
|
27
27
|
from bumble.keys import JsonKeyStore
|
|
28
28
|
from bumble.smp import AddressResolver
|
|
@@ -144,8 +144,14 @@ async def scan(
|
|
|
144
144
|
device_config, hci_source, hci_sink
|
|
145
145
|
)
|
|
146
146
|
else:
|
|
147
|
-
device = Device.
|
|
148
|
-
|
|
147
|
+
device = Device.from_config_with_hci(
|
|
148
|
+
DeviceConfiguration(
|
|
149
|
+
name='Bumble',
|
|
150
|
+
address=Address('F0:F1:F2:F3:F4:F5'),
|
|
151
|
+
keystore='JsonKeyStore',
|
|
152
|
+
),
|
|
153
|
+
hci_source,
|
|
154
|
+
hci_sink,
|
|
149
155
|
)
|
|
150
156
|
|
|
151
157
|
await device.power_on()
|
|
@@ -190,7 +196,7 @@ async def scan(
|
|
|
190
196
|
scanning_phys=scanning_phys,
|
|
191
197
|
)
|
|
192
198
|
|
|
193
|
-
await hci_source.
|
|
199
|
+
await hci_source.terminated
|
|
194
200
|
|
|
195
201
|
|
|
196
202
|
# -----------------------------------------------------------------------------
|
bumble/apps/speaker/speaker.py
CHANGED