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/drivers/intel.py
CHANGED
|
@@ -89,52 +89,55 @@ HCI_INTEL_WRITE_BOOT_PARAMS_COMMAND = hci.hci_vendor_command_op_code(0x000E)
|
|
|
89
89
|
hci.HCI_Command.register_commands(globals())
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
@hci.HCI_Command.command
|
|
93
92
|
@dataclasses.dataclass
|
|
94
|
-
class
|
|
95
|
-
|
|
93
|
+
class HCI_Intel_Read_Version_ReturnParameters(hci.HCI_StatusReturnParameters):
|
|
94
|
+
tlv: bytes = hci.field(metadata=hci.metadata('*'))
|
|
95
|
+
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
]
|
|
97
|
+
@hci.HCI_SyncCommand.sync_command(HCI_Intel_Read_Version_ReturnParameters)
|
|
98
|
+
@dataclasses.dataclass
|
|
99
|
+
class HCI_Intel_Read_Version_Command(
|
|
100
|
+
hci.HCI_SyncCommand[HCI_Intel_Read_Version_ReturnParameters]
|
|
101
|
+
):
|
|
102
|
+
param0: int = dataclasses.field(metadata=hci.metadata(1))
|
|
101
103
|
|
|
102
104
|
|
|
103
|
-
@hci.
|
|
105
|
+
@hci.HCI_SyncCommand.sync_command(hci.HCI_StatusReturnParameters)
|
|
104
106
|
@dataclasses.dataclass
|
|
105
|
-
class Hci_Intel_Secure_Send_Command(
|
|
107
|
+
class Hci_Intel_Secure_Send_Command(
|
|
108
|
+
hci.HCI_SyncCommand[hci.HCI_StatusReturnParameters]
|
|
109
|
+
):
|
|
106
110
|
data_type: int = dataclasses.field(metadata=hci.metadata(1))
|
|
107
111
|
data: bytes = dataclasses.field(metadata=hci.metadata("*"))
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
|
|
114
|
+
@dataclasses.dataclass
|
|
115
|
+
class HCI_Intel_Reset_ReturnParameters(hci.HCI_ReturnParameters):
|
|
116
|
+
data: bytes = hci.field(metadata=hci.metadata('*'))
|
|
112
117
|
|
|
113
118
|
|
|
114
|
-
@hci.
|
|
119
|
+
@hci.HCI_SyncCommand.sync_command(HCI_Intel_Reset_ReturnParameters)
|
|
115
120
|
@dataclasses.dataclass
|
|
116
|
-
class HCI_Intel_Reset_Command(hci.
|
|
121
|
+
class HCI_Intel_Reset_Command(hci.HCI_SyncCommand[HCI_Intel_Reset_ReturnParameters]):
|
|
117
122
|
reset_type: int = dataclasses.field(metadata=hci.metadata(1))
|
|
118
123
|
patch_enable: int = dataclasses.field(metadata=hci.metadata(1))
|
|
119
124
|
ddc_reload: int = dataclasses.field(metadata=hci.metadata(1))
|
|
120
125
|
boot_option: int = dataclasses.field(metadata=hci.metadata(1))
|
|
121
126
|
boot_address: int = dataclasses.field(metadata=hci.metadata(4))
|
|
122
127
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
|
|
129
|
+
@dataclasses.dataclass
|
|
130
|
+
class HCI_Intel_Write_Device_Config_ReturnParameters(hci.HCI_StatusReturnParameters):
|
|
131
|
+
params: bytes = hci.field(metadata=hci.metadata('*'))
|
|
126
132
|
|
|
127
133
|
|
|
128
|
-
@hci.
|
|
134
|
+
@hci.HCI_SyncCommand.sync_command(HCI_Intel_Write_Device_Config_ReturnParameters)
|
|
129
135
|
@dataclasses.dataclass
|
|
130
|
-
class
|
|
136
|
+
class HCI_Intel_Write_Device_Config_Command(
|
|
137
|
+
hci.HCI_SyncCommand[HCI_Intel_Write_Device_Config_ReturnParameters]
|
|
138
|
+
):
|
|
131
139
|
data: bytes = dataclasses.field(metadata=hci.metadata("*"))
|
|
132
140
|
|
|
133
|
-
return_parameters_fields = [
|
|
134
|
-
("status", hci.STATUS_SPEC),
|
|
135
|
-
("params", "*"),
|
|
136
|
-
]
|
|
137
|
-
|
|
138
141
|
|
|
139
142
|
# -----------------------------------------------------------------------------
|
|
140
143
|
# Functions
|
|
@@ -402,7 +405,7 @@ class Driver(common.Driver):
|
|
|
402
405
|
self.host.on_hci_event_packet(event)
|
|
403
406
|
return
|
|
404
407
|
|
|
405
|
-
if not event.return_parameters == hci.HCI_SUCCESS:
|
|
408
|
+
if not event.return_parameters.status == hci.HCI_SUCCESS:
|
|
406
409
|
raise DriverError("HCI_Command_Complete_Event error")
|
|
407
410
|
|
|
408
411
|
if self.max_in_flight_firmware_load_commands != event.num_hci_command_packets:
|
|
@@ -641,8 +644,8 @@ class Driver(common.Driver):
|
|
|
641
644
|
while ddc_data:
|
|
642
645
|
ddc_len = 1 + ddc_data[0]
|
|
643
646
|
ddc_payload = ddc_data[:ddc_len]
|
|
644
|
-
await self.host.
|
|
645
|
-
|
|
647
|
+
await self.host.send_sync_command(
|
|
648
|
+
HCI_Intel_Write_Device_Config_Command(data=ddc_payload)
|
|
646
649
|
)
|
|
647
650
|
ddc_data = ddc_data[ddc_len:]
|
|
648
651
|
|
|
@@ -660,31 +663,34 @@ class Driver(common.Driver):
|
|
|
660
663
|
|
|
661
664
|
async def read_device_info(self) -> dict[ValueType, Any]:
|
|
662
665
|
self.host.ready = True
|
|
663
|
-
|
|
664
|
-
if not (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
666
|
+
response1 = await self.host.send_sync_command_raw(hci.HCI_Reset_Command())
|
|
667
|
+
if not isinstance(
|
|
668
|
+
response1.return_parameters, hci.HCI_StatusReturnParameters
|
|
669
|
+
) or response1.return_parameters.status not in (
|
|
670
|
+
hci.HCI_UNKNOWN_HCI_COMMAND_ERROR,
|
|
671
|
+
hci.HCI_SUCCESS,
|
|
668
672
|
):
|
|
669
673
|
# When the controller is in operational mode, the response is a
|
|
670
674
|
# successful response.
|
|
671
675
|
# When the controller is in bootloader mode,
|
|
672
676
|
# HCI_UNKNOWN_HCI_COMMAND_ERROR is the expected response. Anything
|
|
673
677
|
# else is a failure.
|
|
674
|
-
logger.warning(f"unexpected response: {
|
|
678
|
+
logger.warning(f"unexpected response: {response1}")
|
|
675
679
|
raise DriverError("unexpected HCI response")
|
|
676
680
|
|
|
677
681
|
# Read the firmware version.
|
|
678
|
-
|
|
682
|
+
response2 = await self.host.send_sync_command_raw(
|
|
679
683
|
HCI_Intel_Read_Version_Command(param0=0xFF)
|
|
680
684
|
)
|
|
681
|
-
if
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
+
if (
|
|
686
|
+
not isinstance(
|
|
687
|
+
response2.return_parameters, HCI_Intel_Read_Version_ReturnParameters
|
|
688
|
+
)
|
|
689
|
+
or response2.return_parameters.status != 0
|
|
690
|
+
):
|
|
685
691
|
raise DriverError("HCI_Intel_Read_Version_Command error")
|
|
686
692
|
|
|
687
|
-
tlvs = _parse_tlv(
|
|
693
|
+
tlvs = _parse_tlv(response2.return_parameters.tlv) # type: ignore
|
|
688
694
|
|
|
689
695
|
# Convert the list to a dict. That's Ok here because we only expect each type
|
|
690
696
|
# to appear just once.
|
bumble/drivers/rtk.py
CHANGED
|
@@ -16,6 +16,7 @@ Support for Realtek USB dongles.
|
|
|
16
16
|
Based on various online bits of information, including the Linux kernel.
|
|
17
17
|
(see `drivers/bluetooth/btrtl.c`)
|
|
18
18
|
"""
|
|
19
|
+
from __future__ import annotations
|
|
19
20
|
|
|
20
21
|
import asyncio
|
|
21
22
|
import enum
|
|
@@ -31,10 +32,14 @@ import weakref
|
|
|
31
32
|
# Imports
|
|
32
33
|
# -----------------------------------------------------------------------------
|
|
33
34
|
from dataclasses import dataclass, field
|
|
35
|
+
from typing import TYPE_CHECKING
|
|
34
36
|
|
|
35
37
|
from bumble import core, hci
|
|
36
38
|
from bumble.drivers import common
|
|
37
39
|
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from bumble.host import Host
|
|
42
|
+
|
|
38
43
|
# -----------------------------------------------------------------------------
|
|
39
44
|
# Logging
|
|
40
45
|
# -----------------------------------------------------------------------------
|
|
@@ -124,6 +129,7 @@ RTK_USB_PRODUCTS = {
|
|
|
124
129
|
(0x2357, 0x0604),
|
|
125
130
|
(0x2550, 0x8761),
|
|
126
131
|
(0x2B89, 0x8761),
|
|
132
|
+
(0x2C0A, 0x8761),
|
|
127
133
|
(0x7392, 0xC611),
|
|
128
134
|
# Realtek 8761CUV
|
|
129
135
|
(0x0B05, 0x1BF6),
|
|
@@ -188,23 +194,36 @@ HCI_RTK_DROP_FIRMWARE_COMMAND = hci.hci_vendor_command_op_code(0x66)
|
|
|
188
194
|
hci.HCI_Command.register_commands(globals())
|
|
189
195
|
|
|
190
196
|
|
|
191
|
-
@hci.HCI_Command.command
|
|
192
197
|
@dataclass
|
|
193
|
-
class
|
|
194
|
-
|
|
198
|
+
class HCI_RTK_Read_ROM_Version_ReturnParameters(hci.HCI_StatusReturnParameters):
|
|
199
|
+
version: int = field(metadata=hci.metadata(1))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@hci.HCI_SyncCommand.sync_command(HCI_RTK_Read_ROM_Version_ReturnParameters)
|
|
203
|
+
@dataclass
|
|
204
|
+
class HCI_RTK_Read_ROM_Version_Command(
|
|
205
|
+
hci.HCI_SyncCommand[HCI_RTK_Read_ROM_Version_ReturnParameters]
|
|
206
|
+
):
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class HCI_RTK_Download_ReturnParameters(hci.HCI_StatusReturnParameters):
|
|
212
|
+
index: int = field(metadata=hci.metadata(1))
|
|
195
213
|
|
|
196
214
|
|
|
197
|
-
@hci.
|
|
215
|
+
@hci.HCI_SyncCommand.sync_command(HCI_RTK_Download_ReturnParameters)
|
|
198
216
|
@dataclass
|
|
199
|
-
class HCI_RTK_Download_Command(hci.
|
|
217
|
+
class HCI_RTK_Download_Command(hci.HCI_SyncCommand[HCI_RTK_Download_ReturnParameters]):
|
|
200
218
|
index: int = field(metadata=hci.metadata(1))
|
|
201
219
|
payload: bytes = field(metadata=hci.metadata(RTK_FRAGMENT_LENGTH))
|
|
202
|
-
return_parameters_fields = [("status", hci.STATUS_SPEC), ("index", 1)]
|
|
203
220
|
|
|
204
221
|
|
|
205
|
-
@hci.
|
|
222
|
+
@hci.HCI_SyncCommand.sync_command(hci.HCI_GenericReturnParameters)
|
|
206
223
|
@dataclass
|
|
207
|
-
class HCI_RTK_Drop_Firmware_Command(
|
|
224
|
+
class HCI_RTK_Drop_Firmware_Command(
|
|
225
|
+
hci.HCI_SyncCommand[hci.HCI_GenericReturnParameters]
|
|
226
|
+
):
|
|
208
227
|
pass
|
|
209
228
|
|
|
210
229
|
|
|
@@ -490,7 +509,7 @@ class Driver(common.Driver):
|
|
|
490
509
|
return None
|
|
491
510
|
|
|
492
511
|
@staticmethod
|
|
493
|
-
def check(host):
|
|
512
|
+
def check(host: Host) -> bool:
|
|
494
513
|
if not host.hci_metadata:
|
|
495
514
|
logger.debug("USB metadata not found")
|
|
496
515
|
return False
|
|
@@ -514,37 +533,44 @@ class Driver(common.Driver):
|
|
|
514
533
|
return True
|
|
515
534
|
|
|
516
535
|
@staticmethod
|
|
517
|
-
async def get_loaded_firmware_version(host):
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
536
|
+
async def get_loaded_firmware_version(host: Host) -> int | None:
|
|
537
|
+
response1 = await host.send_sync_command_raw(HCI_RTK_Read_ROM_Version_Command())
|
|
538
|
+
if (
|
|
539
|
+
not isinstance(
|
|
540
|
+
response1.return_parameters, HCI_RTK_Read_ROM_Version_ReturnParameters
|
|
541
|
+
)
|
|
542
|
+
or response1.return_parameters.status != hci.HCI_SUCCESS
|
|
543
|
+
):
|
|
521
544
|
return None
|
|
522
545
|
|
|
523
|
-
|
|
524
|
-
hci.HCI_Read_Local_Version_Information_Command()
|
|
525
|
-
)
|
|
526
|
-
return (
|
|
527
|
-
response.return_parameters.hci_subversion << 16
|
|
528
|
-
| response.return_parameters.lmp_subversion
|
|
546
|
+
response2 = await host.send_sync_command(
|
|
547
|
+
hci.HCI_Read_Local_Version_Information_Command()
|
|
529
548
|
)
|
|
549
|
+
return response2.hci_subversion << 16 | response2.lmp_subversion
|
|
530
550
|
|
|
531
551
|
@classmethod
|
|
532
|
-
async def driver_info_for_host(cls, host):
|
|
552
|
+
async def driver_info_for_host(cls, host: Host) -> DriverInfo | None:
|
|
533
553
|
try:
|
|
534
|
-
await host.
|
|
554
|
+
await host.send_sync_command(
|
|
535
555
|
hci.HCI_Reset_Command(),
|
|
536
|
-
check_result=True,
|
|
537
556
|
response_timeout=cls.POST_RESET_DELAY,
|
|
538
557
|
)
|
|
539
558
|
host.ready = True # Needed to let the host know the controller is ready.
|
|
540
559
|
except asyncio.exceptions.TimeoutError:
|
|
541
560
|
logger.warning("timeout waiting for hci reset, retrying")
|
|
542
|
-
await host.
|
|
561
|
+
await host.send_sync_command(hci.HCI_Reset_Command())
|
|
543
562
|
host.ready = True
|
|
544
563
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
564
|
+
response = await host.send_sync_command_raw(
|
|
565
|
+
hci.HCI_Read_Local_Version_Information_Command()
|
|
566
|
+
)
|
|
567
|
+
if (
|
|
568
|
+
not isinstance(
|
|
569
|
+
response.return_parameters,
|
|
570
|
+
hci.HCI_Read_Local_Version_Information_ReturnParameters,
|
|
571
|
+
)
|
|
572
|
+
or response.return_parameters.status != hci.HCI_SUCCESS
|
|
573
|
+
):
|
|
548
574
|
logger.error("failed to probe local version information")
|
|
549
575
|
return None
|
|
550
576
|
|
|
@@ -569,7 +595,7 @@ class Driver(common.Driver):
|
|
|
569
595
|
return driver_info
|
|
570
596
|
|
|
571
597
|
@classmethod
|
|
572
|
-
async def for_host(cls, host, force=False):
|
|
598
|
+
async def for_host(cls, host: Host, force: bool = False):
|
|
573
599
|
# Check that a driver is needed for this host
|
|
574
600
|
if not force and not cls.check(host):
|
|
575
601
|
return None
|
|
@@ -624,15 +650,21 @@ class Driver(common.Driver):
|
|
|
624
650
|
|
|
625
651
|
# TODO: load the firmware
|
|
626
652
|
|
|
627
|
-
async def download_for_rtl8723b(self):
|
|
653
|
+
async def download_for_rtl8723b(self) -> int | None:
|
|
628
654
|
if self.driver_info.has_rom_version:
|
|
629
|
-
|
|
630
|
-
HCI_RTK_Read_ROM_Version_Command()
|
|
655
|
+
response1 = await self.host.send_sync_command_raw(
|
|
656
|
+
HCI_RTK_Read_ROM_Version_Command()
|
|
631
657
|
)
|
|
632
|
-
if
|
|
658
|
+
if (
|
|
659
|
+
not isinstance(
|
|
660
|
+
response1.return_parameters,
|
|
661
|
+
HCI_RTK_Read_ROM_Version_ReturnParameters,
|
|
662
|
+
)
|
|
663
|
+
or response1.return_parameters.status != hci.HCI_SUCCESS
|
|
664
|
+
):
|
|
633
665
|
logger.warning("can't get ROM version")
|
|
634
666
|
return None
|
|
635
|
-
rom_version =
|
|
667
|
+
rom_version = response1.return_parameters.version
|
|
636
668
|
logger.debug(f"ROM version before download: {rom_version:04X}")
|
|
637
669
|
else:
|
|
638
670
|
rom_version = 0
|
|
@@ -667,21 +699,25 @@ class Driver(common.Driver):
|
|
|
667
699
|
fragment_offset = fragment_index * RTK_FRAGMENT_LENGTH
|
|
668
700
|
fragment = payload[fragment_offset : fragment_offset + RTK_FRAGMENT_LENGTH]
|
|
669
701
|
logger.debug(f"downloading fragment {fragment_index}")
|
|
670
|
-
await self.host.
|
|
671
|
-
HCI_RTK_Download_Command(index=download_index, payload=fragment)
|
|
672
|
-
check_result=True,
|
|
702
|
+
await self.host.send_sync_command(
|
|
703
|
+
HCI_RTK_Download_Command(index=download_index, payload=fragment)
|
|
673
704
|
)
|
|
674
705
|
|
|
675
706
|
logger.debug("download complete!")
|
|
676
707
|
|
|
677
708
|
# Read the version again
|
|
678
|
-
|
|
679
|
-
HCI_RTK_Read_ROM_Version_Command()
|
|
709
|
+
response2 = await self.host.send_sync_command_raw(
|
|
710
|
+
HCI_RTK_Read_ROM_Version_Command()
|
|
680
711
|
)
|
|
681
|
-
if
|
|
712
|
+
if (
|
|
713
|
+
not isinstance(
|
|
714
|
+
response2.return_parameters, HCI_RTK_Read_ROM_Version_ReturnParameters
|
|
715
|
+
)
|
|
716
|
+
or response2.return_parameters.status != hci.HCI_SUCCESS
|
|
717
|
+
):
|
|
682
718
|
logger.warning("can't get ROM version")
|
|
683
719
|
else:
|
|
684
|
-
rom_version =
|
|
720
|
+
rom_version = response2.return_parameters.version
|
|
685
721
|
logger.debug(f"ROM version after download: {rom_version:02X}")
|
|
686
722
|
|
|
687
723
|
return firmware.version
|
|
@@ -703,7 +739,7 @@ class Driver(common.Driver):
|
|
|
703
739
|
|
|
704
740
|
async def init_controller(self):
|
|
705
741
|
await self.download_firmware()
|
|
706
|
-
await self.host.
|
|
742
|
+
await self.host.send_sync_command(hci.HCI_Reset_Command())
|
|
707
743
|
logger.info(f"loaded FW image {self.driver_info.fw_name}")
|
|
708
744
|
|
|
709
745
|
|