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:
Ford Peprah
2024-08-14 13:58:02 -04:00
parent ac0cff43b6
commit aa658418bc
2 changed files with 29 additions and 9 deletions

View File

@@ -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(

View File

@@ -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.