Skip to content

Bound HCI requests by their timeout instead of 10 reads - #65

Merged
colemancda merged 1 commit into
masterfrom
fix/hci-request-timeout
Oct 28, 2026
Merged

colemancda merged 1 commit into
masterfrom
fix/hci-request-timeout

Conversation

@colemancda

Copy link
Copy Markdown
Member

sendRequest() ignored its timeout argument and gave up after reading 10 events. Its socket filter also passes LE meta events (needed for commands that complete with an LE subevent). So while the controller is scanning, advertising reports can use up all 10 reads before the command's response arrives, and the request fails with ETIMEDOUT even though the controller answered.

This made any HCI command unreliable during a scan, including the LE Set Scan Enable that disables scanning. When that failed (the error is ignored in lowEnergyScan), the controller kept scanning, and the next scan failed again.

This change waits for each event with poll() for the time remaining before timeout, and keeps reading until the matching response arrives or the timeout expires. A response that never arrives now also ends in ETIMEDOUT instead of blocking forever in read().

Testing

  • Reproduced with a USB dongle receiving about 180–265 advertising reports per second: Read BD ADDR and the scan start, stop and restart sequence failed with Connection timed out before this change, and succeed after it. Debian 13, kernel 6.12, CSR 4.0 dongle, Swift 6.4.1.
  • swift build passes.

Related issues not fixed here

  • AsyncIndefiniteStream.stop() (Bluetooth) doesn't wait for the producer task. Its final scan-disable request can overlap the caller's next request on the same HCI socket, and both swap the socket filter.
  • After LE scanning through raw HCI commands, the kernel stops starting the passive scan that L2CAP LE connections depend on until the adapter is power-cycled. gatttool is affected the same way.

sendRequest() ignored its timeout and gave up after reading 10 events.
Its filter also passes LE meta events, so while the controller is
scanning, advertising reports used up all 10 reads before the command's
response arrived and the request failed with ETIMEDOUT. This made any
HCI command unreliable during a scan, including the command that
disables scanning.

Wait for each event with poll() for the time remaining before the
timeout, and keep reading until the matching response arrives or the
timeout expires.
@colemancda
colemancda merged commit 93efd3f into master Oct 28, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant