mirror of
https://github.com/google/bumble.git
synced 2026-05-09 04:08:02 +00:00
Bug: Edimax BLE Dongle Fails After Teardown and Re-Instantiation
This patch addresses an issue where the some RTK BLE dongles fail to perform an HCI reset after the transport is torn down and re-instantiated. To address that, we prevent crashing the background threads when invalid data comes in, and time out if no response is received within a fixed amount of time. When the timeout occurs, we retry the reset, and ultimately skip over reading the local version information if that fails.
This commit is contained in:
@@ -301,6 +301,8 @@ class Driver(common.Driver):
|
|||||||
fw_name: str = ""
|
fw_name: str = ""
|
||||||
config_name: str = ""
|
config_name: str = ""
|
||||||
|
|
||||||
|
POST_RESET_DELAY: float = 0.2
|
||||||
|
|
||||||
DRIVER_INFOS = [
|
DRIVER_INFOS = [
|
||||||
# 8723A
|
# 8723A
|
||||||
DriverInfo(
|
DriverInfo(
|
||||||
@@ -495,12 +497,22 @@ class Driver(common.Driver):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def driver_info_for_host(cls, host):
|
async def driver_info_for_host(cls, host):
|
||||||
await host.send_command(HCI_Reset_Command(), check_result=True)
|
try:
|
||||||
host.ready = True # Needed to let the host know the controller is ready.
|
await host.send_command(
|
||||||
|
HCI_Reset_Command(), check_result=True, response_timeout=cls.POST_RESET_DELAY
|
||||||
|
)
|
||||||
|
host.ready = True # Needed to let the host know the controller is ready.
|
||||||
|
except asyncio.exceptions.TimeoutError:
|
||||||
|
logger.warning("timeout waiting for hci reset, retrying")
|
||||||
|
await host.send_command(HCI_Reset_Command(), check_result=True)
|
||||||
|
host.ready = True
|
||||||
|
|
||||||
|
command = HCI_Read_Local_Version_Information_Command()
|
||||||
|
response = await host.send_command(command, check_result=True)
|
||||||
|
if response.command_opcode != command.op_code:
|
||||||
|
logger.error("failed to probe local version information")
|
||||||
|
return None
|
||||||
|
|
||||||
response = await host.send_command(
|
|
||||||
HCI_Read_Local_Version_Information_Command(), check_result=True
|
|
||||||
)
|
|
||||||
local_version = response.return_parameters
|
local_version = response.return_parameters
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|||||||
@@ -514,7 +514,7 @@ class Host(AbortableEventEmitter):
|
|||||||
if self.hci_sink:
|
if self.hci_sink:
|
||||||
self.hci_sink.on_packet(bytes(packet))
|
self.hci_sink.on_packet(bytes(packet))
|
||||||
|
|
||||||
async def send_command(self, command, check_result=False):
|
async def send_command(self, command, check_result=False, response_timeout: Optional[int] = None):
|
||||||
# Wait until we can send (only one pending command at a time)
|
# Wait until we can send (only one pending command at a time)
|
||||||
async with self.command_semaphore:
|
async with self.command_semaphore:
|
||||||
assert self.pending_command is None
|
assert self.pending_command is None
|
||||||
@@ -526,7 +526,8 @@ class Host(AbortableEventEmitter):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.send_hci_packet(command)
|
self.send_hci_packet(command)
|
||||||
response = await self.pending_response
|
await asyncio.wait_for(self.pending_response, timeout=response_timeout)
|
||||||
|
response = self.pending_response.result()
|
||||||
|
|
||||||
# Check the return parameters if required
|
# Check the return parameters if required
|
||||||
if check_result:
|
if check_result:
|
||||||
@@ -625,14 +626,21 @@ class Host(AbortableEventEmitter):
|
|||||||
|
|
||||||
# Packet Sink protocol (packets coming from the controller via HCI)
|
# Packet Sink protocol (packets coming from the controller via HCI)
|
||||||
def on_packet(self, packet: bytes) -> None:
|
def on_packet(self, packet: bytes) -> None:
|
||||||
hci_packet = hci.HCI_Packet.from_bytes(packet)
|
try:
|
||||||
|
hci_packet = hci.HCI_Packet.from_bytes(packet)
|
||||||
|
except Exception as error:
|
||||||
|
logger.warning(f'!!! error parsing packet from bytes: {error}')
|
||||||
|
return
|
||||||
|
|
||||||
if self.ready or (
|
if self.ready or (
|
||||||
isinstance(hci_packet, hci.HCI_Command_Complete_Event)
|
isinstance(hci_packet, hci.HCI_Command_Complete_Event)
|
||||||
and hci_packet.command_opcode == hci.HCI_RESET_COMMAND
|
and hci_packet.command_opcode == hci.HCI_RESET_COMMAND
|
||||||
):
|
):
|
||||||
self.on_hci_packet(hci_packet)
|
self.on_hci_packet(hci_packet)
|
||||||
else:
|
else:
|
||||||
logger.debug('reset not done, ignoring packet from controller')
|
logger.debug(
|
||||||
|
f'reset not done, ignoring packet from controller: {hci_packet}'
|
||||||
|
)
|
||||||
|
|
||||||
def on_transport_lost(self):
|
def on_transport_lost(self):
|
||||||
# Called by the source when the transport has been lost.
|
# Called by the source when the transport has been lost.
|
||||||
|
|||||||
Reference in New Issue
Block a user