Re: [RFC PATCH] Bluetooth: hci_event: negotiate LE Data Length after features

From: cochcoder

Date: Thu Oct 15 2026 - 14:02:45 EST


Thanks for pointing that out! Initialization already programs the suggested default data length. I also need to correct my previous submission notes, the N8 returns to environmental sound while Bluetooth remains connected. The longer idle transition showed continued host audio before STOP. Instead of a demonstrated DLE negotiation failure.

I’ll withdraw this RFC. I don’t have evidence of an affected controller that justifies a manual-DLE quirk.



On Tuesday, October 13th, 2026 at 10:30 AM, Luiz Augusto von Dentz <luiz.dentz@xxxxxxxxx> wrote:

> Hi Kyle,
>
> On Mon, Oct 12, 2026 at 11:35 PM Kyle Pepper <cochcoder@xxxxxxxxx> wrote:
> >
> > Request the controller transmit limits once both peers support DLE.
> > Queue the connection with a reference so pending requests coalesce and
> > connection deletion cancels them. Check the connection under the device
> > lock before constructing the command.
> >
> > Assisted-by: OpenCode:gpt-6.1-sol
> > Signed-off-by: Kyle Pepper <cochcoder@xxxxxxxxx>
> > ---
> > Submission review notes (not part of the commit message):
> > The user reports that retaining the explicit DLE request enables the N8
> > to disconnect automatically after audio stops, whereas removing it
> > leaves the device connected. This idle-transition behavior is the
> > reported reason for retaining the topic, not an in-stream timeout.
> > October 9 no-DLE trials already negotiate TX 251 octets / 2120 us and
> > stream successfully; startup throughput alone does not test idle behavior.
> > The supplied captures do not establish a controlled post-STOP comparison
> > or the causal mechanism. This patch requests data length during setup;
> > it does not itself implement STOP or issue a disconnect command.
> > The claimed ASHA idle-disconnect requirement needs a protocol citation.
> > The corrected ownership implementation is included in the working kernel.
> > Native disconnect/handle reuse, LL ordering and post-snapshot races remain
> > unproved. Submit as an independent RFC with these limitations disclosed.
> >
> > include/net/bluetooth/hci_sync.h | 1 +
> > net/bluetooth/hci_event.c | 10 +++++++
> > net/bluetooth/hci_sync.c | 47 ++++++++++++++++++++++++++++++++
> > 3 files changed, 58 insertions(+)
> >
> > diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
> > index 59cb918d1..5a37d3e50 100644
> > --- a/include/net/bluetooth/hci_sync.h
> > +++ b/include/net/bluetooth/hci_sync.h
> > @@ -194,3 +194,4 @@ int hci_le_read_remote_features(struct hci_conn *conn);
> >
> > int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type);
> > int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys);
> > +int hci_le_set_data_len(struct hci_conn *conn);
> > diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> > index c055d16cf..4035f2659 100644
> > --- a/net/bluetooth/hci_event.c
> > +++ b/net/bluetooth/hci_event.c
> > @@ -6846,6 +6846,16 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
> > conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED;
> > conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED;
> > }
> > +
> > + /* Request the controller maximum when both sides support
> > + * LL Data Length Extension. The Link Layer negotiates
> > + * the remote limits (Core v5.4, Vol 6, Part B,
> > + * Sec. 4.5.10).
> > + */
> > + if ((hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) &&
> > + (conn->le_features[0] & HCI_LE_DATA_LEN_EXT) &&
> > + hdev->le_max_tx_len > 0x001b)
> > + hci_le_set_data_len(conn);
>
> We use HCI_OP_LE_WRITE_DEF_DATA_LEN to setup this for every
> connection, so either the controller has a bug and doesn't trigger
> trigger DLE or something else is off here. In case it is really a
> specific controller then we might need a quirk which once set by the
> driver indicates the host has to perform the DLE procedure manually
> like above.
>
> > }
> >
> > if (conn->state == BT_CONFIG) {
> > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> > index efbd2e538..20c690007 100644
> > --- a/net/bluetooth/hci_sync.c
> > +++ b/net/bluetooth/hci_sync.c
> > @@ -8123,3 +8123,50 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
> >
> > return (err == -EEXIST) ? 0 : err;
> > }
> > +
> > +static void le_set_data_len_complete(struct hci_dev *hdev, void *data,
> > + int err)
> > +{
> > + struct hci_conn *conn = data;
> > +
> > + bt_dev_dbg(hdev, "err %d", err);
> > +
> > + hci_conn_put(conn);
> > +}
> > +
> > +static int hci_le_set_data_len_sync(struct hci_dev *hdev, void *data)
> > +{
> > + struct hci_conn *conn = data;
> > + struct hci_cp_le_set_data_len cp;
> > +
> > + hci_dev_lock(hdev);
> > +
> > + if (!hci_conn_valid(hdev, conn) || conn->type != LE_LINK ||
> > + (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)) {
> > + hci_dev_unlock(hdev);
> > + return -ECANCELED;
> > + }
> > +
> > + cp.handle = cpu_to_le16(conn->handle);
> > + cp.tx_len = cpu_to_le16(hdev->le_max_tx_len);
> > + cp.tx_time = cpu_to_le16(hdev->le_max_tx_time);
> > +
> > + hci_dev_unlock(hdev);
> > +
> > + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DATA_LEN,
> > + sizeof(cp), &cp, HCI_CMD_TIMEOUT);
> > +}
> > +
> > +int hci_le_set_data_len(struct hci_conn *conn)
> > +{
> > + struct hci_dev *hdev = conn->hdev;
> > + int err;
> > +
> > + err = hci_cmd_sync_queue_once(hdev, hci_le_set_data_len_sync,
> > + hci_conn_get(conn),
> > + le_set_data_len_complete);
> > + if (err)
> > + hci_conn_put(conn);
> > +
> > + return (err == -EEXIST) ? 0 : err;
> > +}
> >
> > base-commit: 964430c038967fd06643e866f68115664e1a1bd1
> >
>
>
> --
> Luiz Augusto von Dentz
>