Skip to content

Implement the non-enhanced SCO commands and events - #1013

Open
zxzxwu wants to merge 3 commits into
google:mainfrom
zxzxwu:sco-legacy-commands
Open

zxzxwu wants to merge 3 commits into
google:mainfrom
zxzxwu:sco-legacy-commands

Conversation

@zxzxwu

@zxzxwu zxzxwu commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

Implement the non-enhanced Setup/Accept/Reject Synchronous Connection commands, so that SCO and eSCO links can be set up with controllers that do not implement the enhanced commands, and let the SCO examples fall back to them.

Problem

Bumble had the non-enhanced Accept and Reject Synchronous Connection commands (0x0429, 0x042A) and the events that go with them, but not the non-enhanced Setup Synchronous Connection command (0x0428), so a host could not create a SCO or eSCO link on a controller that only implements the non-enhanced commands. Realtek RTL8761 based dongles (e.g. ASUS BT600) are such controllers: they report neither 0x043D nor 0x043E.

The SCO examples sent the enhanced commands unconditionally (one of them had a hand-rolled fallback with its own voice setting mapping), and the emulated controller only implemented the enhanced commands, so that path could not be tested without hardware.

Fix

  • bumble/hci.py: add the missing HCI_Setup_Synchronous_Connection_Command (0x0428), with a PacketType flag and SCO_PACKET_TYPES / ESCO_PACKET_TYPES masks, and VoiceSetting.codec_id(), which maps the Air_Coding_Format of a voice setting to the air mode (codec ID) of the Synchronous Connection Complete event (the two use different numbering).
  • bumble/controller.py: implement the non-enhanced Setup, Accept and Reject commands. The non-enhanced setup command does not say whether a SCO or an eSCO link is wanted: make it an eSCO link if any of the eSCO packet types is allowed, and a SCO link otherwise. Also report the air mode of the link in the Synchronous Connection Complete event (from the voice setting for the non-enhanced commands, from the coding format for the enhanced ones) instead of always reporting 0 — the USB transport uses that air mode to select the isochronous alternate setting (see Fix SCO over USB: align packets to the alternate setting, select it per SCO link #1011).
  • bumble/hfp.py: add EscoParameters.as_legacy_dict(), which reduces codec parameters to the parameters of the non-enhanced commands (the air coding format is taken from the transmit coding format: mSBC and LC3 are coded by the host, so they go over the air as transparent data; the rest of the coding format is reduced to the sample size, format and bit position of the PCM data on HCI), and setup_synchronous_connection_command() / accept_synchronous_connection_request_command(), which return the enhanced command when the controller supports it and the non-enhanced one otherwise.
  • examples/: run_esco_connection.py, run_hfp_gateway.py and run_hfp_handsfree.py use those builders, so they work with both kinds of controller (and the hand-rolled fallback in run_hfp_handsfree.py goes away).

Testing

  • Unit tests: the non-enhanced Setup command round-trips (SCO and eSCO packet types); VoiceSetting.codec_id(); the legacy parameter mapping (CVSD, mSBC, 8-bit PCM, sign and magnitude); a full eSCO setup and a full SCO setup over the emulated controller with the non-enhanced commands, a setup that the peer rejects, and the existing enhanced setups, all asserting the link type and the air mode. Full suite: 1045 passed, 2 skipped. black -S --check, ruff check, mypy and pylint -E are clean.

  • Realtek (2x ASUS BT600, 0b05:1d70, RTL8761CU), which only implements the non-enhanced commands: examples/run_esco_connection.py between the two dongles picks the non-enhanced commands automatically, and both sides report an eSCO link with CVSD air mode and 60-byte packets:

    HOST -> CONTROLLER: HCI_SETUP_SYNCHRONOUS_CONNECTION_COMMAND
    HOST -> CONTROLLER: HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND
    ### SCO CONNECTION: [0x0002] …
      link_type: ESCO
      air_mode:  CVSD
      rx_packet_length: 60   tx_packet_length: 60
    

    Requesting HV3 only (Core spec SCO packet types) still yields an eSCO link on this controller (link_type 0x02, tx_packet_length 540), which looks like a firmware quirk: bumble reports what the controller reports.

Notes

  • This does not change the enhanced commands or the behaviour of controllers that implement them; the builders only choose the command that the controller advertises.
  • SCO data on the dongles above still needs the alternate setting handling of Fix SCO over USB: align packets to the alternate setting, select it per SCO link #1011 (the link itself is set up with or without it).
  • The non-enhanced commands have no way to describe the codec, so the fallback cannot express everything the enhanced commands can: as_legacy_dict() documents what is kept and what is not.

Some controllers, such as the Realtek RTL8761, implement only the non-enhanced
Setup/Accept/Reject Synchronous Connection commands. bumble had the non-enhanced
accept and reject commands but not the setup one, so a host could not create a
SCO/eSCO link on those controllers.

Also add VoiceSetting.codec_id(), which maps the Air_Coding_Format of a voice
setting to the air mode (codec ID) of the Synchronous Connection Complete event:
the two use different numbering.
Implement the non-enhanced Setup, Accept and Reject Synchronous Connection
commands, which the enhanced commands already had. The non-enhanced setup
command does not say whether a SCO or an eSCO link is wanted: follow the spec
and make it an eSCO link if any of the eSCO packet types is allowed, a SCO link
otherwise.

Report the air mode of a link in the Synchronous Connection Complete event,
derived from the voice setting (non-enhanced commands) or from the coding
format (enhanced commands), instead of always reporting 0. The USB transport
uses that air mode to select the isochronous alternate setting.
Add EscoParameters.as_legacy_dict(), which maps codec parameters to the
parameters of the non-enhanced Setup/Accept Synchronous Connection commands:
the air coding format is taken from the transmit coding format (mSBC and LC3
are coded by the host, so they go over the air as transparent data), and the
rest of the coding format is reduced to the sample size, format and bit
position of the PCM data on HCI.

Add setup_synchronous_connection_command() and
accept_synchronous_connection_request_command(), which build the enhanced
command when the controller supports it and the non-enhanced one otherwise,
and use them in the SCO examples, so that they work with both kinds of
controller.

This branch has not been deployed

No deployments
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