mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-24 21:09:41 +08:00
fix(tools/uploader): handle USB CDC reconnect race during reboot (#27419)
After sending reboot-to-bootloader, the PX4 USB CDC node briefly disappears while the bootloader re-enumerates. Reopening the serial port can land on a half-broken descriptor and the next tcdrain() raises termios.error (5, 'Input/output error'). That bare OSError escaped every retry layer and crashed the uploader, even though a manual re-run would succeed once enumeration settled. Convert OSError/SerialException from flush() and reset_buffers() into the module's ConnectionError, matching how send()/recv() already behave, and let the identify retry loops in _try_identify also catch ConnectionError so a single transient I/O hiccup doesn't abort the upload. Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
This commit is contained in:
@@ -520,14 +520,31 @@ class SerialTransport:
|
||||
|
||||
def flush(self) -> None:
|
||||
"""Flush output buffer."""
|
||||
if self._port is not None:
|
||||
if self._port is None:
|
||||
return
|
||||
# tcdrain() can raise termios.error (a bare OSError) if the device
|
||||
# disappeared mid-flush — common right after a reboot-to-bootloader
|
||||
# when the USB CDC node is being torn down and re-enumerated.
|
||||
try:
|
||||
self._port.flush()
|
||||
except (OSError, serial.SerialException) as e:
|
||||
raise ConnectionError(
|
||||
f"Flush failed: {e}", port=self.port_name, operation="flush"
|
||||
)
|
||||
|
||||
def reset_buffers(self) -> None:
|
||||
"""Reset input and output buffers."""
|
||||
if self._port is not None:
|
||||
if self._port is None:
|
||||
return
|
||||
try:
|
||||
self._port.reset_input_buffer()
|
||||
self._port.reset_output_buffer()
|
||||
except (OSError, serial.SerialException) as e:
|
||||
raise ConnectionError(
|
||||
f"Buffer reset failed: {e}",
|
||||
port=self.port_name,
|
||||
operation="reset_buffers",
|
||||
)
|
||||
|
||||
def set_baudrate(self, baudrate: int) -> None:
|
||||
"""Change baud rate.
|
||||
@@ -1737,7 +1754,7 @@ class Uploader:
|
||||
port=transport.port_name,
|
||||
)
|
||||
return True
|
||||
except (ProtocolError, TimeoutError):
|
||||
except (ProtocolError, TimeoutError, ConnectionError):
|
||||
pass
|
||||
|
||||
# Try rebooting at each baud rate
|
||||
@@ -1802,8 +1819,10 @@ class Uploader:
|
||||
port=transport.port_name,
|
||||
)
|
||||
return True
|
||||
except (ProtocolError, TimeoutError):
|
||||
# Board may still be rebooting, wait a bit and retry
|
||||
except (ProtocolError, TimeoutError, ConnectionError):
|
||||
# Board may still be rebooting, wait a bit and retry.
|
||||
# ConnectionError covers the USB CDC node briefly going
|
||||
# away as the bootloader re-enumerates.
|
||||
time.sleep(0.3)
|
||||
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user