trouble icon indicating copy to clipboard operation
trouble copied to clipboard

`BleHost(Disconnected)` when setting connection param

Open HaoboGu opened this issue 8 months ago • 10 comments

I'm testing nrf-sdc updating connection parameter after GattConnection is established. I use

conn.raw().update_connection_params(&stack,
                &ConnectParams {
                    min_connection_interval: Duration::from_micros(7500),
                    max_connection_interval: Duration::from_micros(7500),
                    max_latency: 99,
                    event_length: Duration::from_secs(0),
                    supervision_timeout: Duration::from_secs(5),
                })` 

to update connections parameters. But this function always returns BleHost(Disconnected). I'm not sure that whether the connection parameters are updated, but according to

https://github.com/embassy-rs/trouble/blob/d17132618413f0fa840fee32af4ab23ca125bec2/host/src/connection.rs#L415-L417

it's because that the conn handler is not recognized by the controller, which seems wrong. The data exchange of the connection runs well before and after calling update_connection_params

HaoboGu avatar Apr 02 '25 08:04 HaoboGu

Is this on the central side or on the peripheral side you're calling this? I'm using this successfully, so I'm surprised that you get this error. Could you share more on how you create the GattConnection?

lulf avatar Apr 02 '25 12:04 lulf

On the peripheral side.

I create the connection use:

async fn advertise<'a, 'b, C: Controller>(
    name: &'a str,
    peripheral: &mut Peripheral<'a, C>,
    server: &'b Server<'_>,
) -> Result<GattConnection<'a, 'b>, BleHostError<C::Error>> {
    // Wait for 10ms to ensure the USB is checked
    embassy_time::Timer::after_millis(10).await;
    let mut advertiser_data = [0; 31];
    AdStructure::encode_slice(
        &[
            AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
            AdStructure::ServiceUuids16(&[BATTERY.to_le_bytes(), HUMAN_INTERFACE_DEVICE.to_le_bytes()]),
            AdStructure::CompleteLocalName(name.as_bytes()),
            AdStructure::Unknown {
                ty: 0x19, // Appearance
                data: &KEYBOARD.to_le_bytes(),
            },
        ],
        &mut advertiser_data[..],
    )?;

    let advertise_config = AdvertisementParameters {
        primary_phy: PhyKind::Le2M,
        secondary_phy: PhyKind::Le2M,
        tx_power: TxPower::Plus8dBm,
        timeout: Some(Duration::from_secs(120)),
        interval_min: Duration::from_millis(200),
        interval_max: Duration::from_millis(200),
        ..Default::default()
    };

    info!("[adv] advertising");
    let advertiser = peripheral
        .advertise(
            &advertise_config,
            Advertisement::ConnectableScannableUndirected {
                adv_data: &advertiser_data[..],
                scan_data: &[],
            },
        )
        .await?;
    let conn = advertiser.accept().await?.with_attribute_server(server)?;
    info!("[adv] connection established");
    Ok(conn)
}

The attribute server is a little bit complex, can be found here:

https://github.com/HaoboGu/rmk/blob/98af3181d2d06ecd6335840319e4a585a1ec25eb/rmk/src/ble/trouble/ble_server.rs#L10

HaoboGu avatar Apr 02 '25 13:04 HaoboGu

I also got some attribute not found errors, when connecting to linux as a peripheral, the following are HCI log on the linux(central):

Image

The linux machine reports malformed att response

Image

HaoboGu avatar Apr 02 '25 13:04 HaoboGu

tmp.log

The full btsnoop log which can be opened by wireshark

HaoboGu avatar Apr 02 '25 13:04 HaoboGu

Forgot to ask, which controller, nrf-sdc?

lulf avatar Apr 02 '25 13:04 lulf

Forgot to ask, which controller, nrf-sdc?

yes, nrf-sdc

HaoboGu avatar Apr 02 '25 14:04 HaoboGu

After more investigation, I found this config only fails on Linux and Android. It works on macOS, iOS and Windows.

HaoboGu avatar Apr 03 '25 06:04 HaoboGu

Hi, you might want to check the nrf softdevice controller docs for this command: https://github.com/nrfconnect/sdk-nrfxlib/blob/main/softdevice_controller/include/sdc_hci_cmd_le.h#L3471-L3477

  If the Host issues this command on a Connection_Handle where the Controller is the
  Peripheral and either the local or peer Controller does not support the Connection
  Parameters Request procedure (see [Vol 6] Part B, Section 5.1.7), then the Controller
  shall return an error which should use the error code Unsupported Feature or
  Parameter Value (0x11) if the local Controller does not support that procedure and
  Unsupported Remote Feature (0x1A) if the local Controller supports that procedure but
  the peer Controller does not.

So could be that there are some bluetooth version differences.

lulf avatar Apr 03 '25 10:04 lulf

It's quite weird, sometimes I get UnknownHCICommand as well:

ERROR [set_conn_params] error: "BleHost(Hci(Unknown HCI Command))"
└─ rmk::ble::trouble::set_conn_params::{async_fn#0} @ /Users/haobogu/Projects/keyboard/rmk/rmk/src/ble/trouble/mod.rs:483 

It seems that different centrals(windows/linux/..) will result in different error message. I totally have no idea about what happened..

HaoboGu avatar Apr 04 '25 14:04 HaoboGu

Just did some more testing. On macOS/iOS, it returns BleHost(Disconnected), and on Linux/Android it returns BleHost(UnknownHCICommand).

HaoboGu avatar Apr 22 '25 08:04 HaoboGu

@lulf Now I can reproduce it on my trouble fork, see this commit:

https://github.com/haobogu/trouble/commit/887563959f975a64c46cb2083d3bb2bffa0f40c4

The only difference is setting conn param after the connection is established and several logs.

I got the following log:

0.002258 [INFO ] Our address = FF:E4:05:1A:8F:FF (trouble_example_apps apps/src/fmt.rs:143)
0.002624 [INFO ] Starting advertising and GATT service (trouble_example_apps apps/src/fmt.rs:143)
0.002838 [INFO ] [host] using packet pool with MTU 251 capacity 16 (trouble_host host/src/fmt.rs:143)
0.002899 [INFO ] [host] filter accept list size: 8 (trouble_host host/src/fmt.rs:143)
0.002929 [INFO ] [host] setting txq to 3, fragmenting at 72 (trouble_host host/src/fmt.rs:143)
0.002990 [INFO ] [host] configuring host buffers (1 packets of size 255) (trouble_host host/src/fmt.rs:143)
0.003051 [INFO ] [host] initialized (trouble_host host/src/fmt.rs:143)
0.003173 [TRACE] [host] enabling advertising (trouble_host host/src/fmt.rs:117)
0.003326 [INFO ] [adv] advertising (trouble_example_apps apps/src/fmt.rs:143)
4.788879 [TRACE] [host] connection with handle ConnHandle(0) established to BdAddr([d2, 49, 6d, d4, b7, 6b]) (trouble_host host/src/fmt.rs:117)
4.789031 [TRACE] [link][poll_accept] connection accepted: state: state = Connected, conn = Some(ConnHandle(0)), flow = 3, role = Some(Peripheral), peer = Some(BdAddr(BdAddr([D2, 49, 6D, D4, B7, 6B])) Irk(None)), ref = 0, sar = PacketReassembly { state: None } (trouble_host host/src/fmt.rs:117)
4.789276 [TRACE] [gatt 0] connecting to server (trouble_host host/src/fmt.rs:117)
4.789306 [TRACE] [server] searching for peer BdAddr(BdAddr([D2, 49, 6D, D4, B7, 6B])) Irk(None) (trouble_host host/src/fmt.rs:117)
4.789398 [INFO ] [adv] connection established (trouble_example_apps apps/src/fmt.rs:143)
4.789459 [INFO ] Updating connection parameters for connection ConnHandle(0): LeConnUpdate("LeConnUpdateParams" { "handle": ConnHandle(0), "conn_interval_min": Duration(12), "conn_interval_max": Duration(12), "max_latency": 25, "supervision_timeout": Duration(600), "min_ce_length": Duration(0), "max_ce_length": Duration(0), }) (trouble_host host/src/fmt.rs:143)
4.790130 [ERROR] [set_conn_params] error: "BleHost(Hci(Unsupported Feature or Parameter Value))" (trouble_example_apps apps/src/fmt.rs:169)
4.791076 [INFO ] [custom_task] notifying connection of tick 1 (trouble_example_apps apps/src/fmt.rs:143)
4.791137 [INFO ] [custom_task] RSSI: 127 (trouble_example_apps apps/src/fmt.rs:143)
4.908752 [WARN ] [host] unsupported l2cap channel id 58 (trouble_host host/src/fmt.rs:156)
4.908782 [WARN ] [host] encountered error processing ACL data for ConnHandle(0): NotSupported (trouble_host host/src/fmt.rs:156)
4.936218 [DEBUG] exchange_att_mtu: 185, current default: 247 (trouble_host host/src/fmt.rs:130)
4.936279 [INFO ] [host] agreed att MTU of 185 (trouble_host host/src/fmt.rs:143)
6.791198 [INFO ] [custom_task] notifying connection of tick 2 (trouble_example_apps apps/src/fmt.rs:143)
6.791259 [INFO ] [custom_task] RSSI: -55 (trouble_example_apps apps/src/fmt.rs:143)
8.791320 [INFO ] [custom_task] notifying connection of tick 3 (trouble_example_apps apps/src/fmt.rs:143

by running:

cargo run --release --features nrf52840 --target thumbv7em-none-eabihf --bin ble_bas_peripheral_sec --features="security"

The parameter LeConnUpdateParams printed is correct, but the connection parameter setting always fails due to Unsupported Feature or Parameter Value.

When using TrouBLE as central, setting connection parameter with the same value can be successful. The error only appears when the TrouBLE acts as BLE peripheral.

I also tried esp32, no error for esp32.

Do you have any ideas?

HaoboGu avatar Jun 09 '25 12:06 HaoboGu

Thanks for the reproducer! I will give it a spin soon, I'm a bit busy with other things this week (and maybe the next!).

It might be related to having to poll the controller for events before issuing a command, I'm not sure.

lulf avatar Jun 10 '25 13:06 lulf

I think I finally figure it out.

According to Accessory Design Guidelines for Apple Devices

The accessory should request connection parameters appropriate for its use case by sending an L2CAP Connection Parameter Update Request at the appropriate time. See the Bluetooth 4.0 specification, Volume 3, Part A, Section 4.20 for details

In current implementation, only LeConnUpdate is called when updating connection parameters, which is fine for centrals, but for peripherals, it might not be supported(?).

It seems that nrf-sdc doesn't support Connection Parameters Request procedure, see this devzone qa

So, according to https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/low-energy-controller/link-layer-specification.html#UUID-caad3e58-fdc5-5274-c856-04f0b711ec98, the work around is to use L2CAP LE Signaling channel to send L2CAP_CONNECTION_PARAMETER_UPDATE_REQ instead.

Now I have no idea about how to implement this, would you give me a pointer? Thanks

HaoboGu avatar Jun 17 '25 17:06 HaoboGu

Interesting find! You can use https://github.com/embassy-rs/trouble/blob/main/host/src/host.rs#L498 to send an l2cap signal packet (probably makes sense to do that inside the connection.rs or host.rs in some utility fn that checks the supported features). To encode the signal packet, you define a type and implement the trait, see here: https://github.com/embassy-rs/trouble/blob/main/host/src/types/l2cap.rs#L126

The logic should probably use these commands to check what features are supported by the connection first:

https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-3ad76ac5-3812-cca4-a4f5-f73e96cebcba

https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-86223376-28a9-e454-15f2-e420aee8c462

lulf avatar Jun 17 '25 17:06 lulf