Re: [PATCH v2] Bluetooth: hci_event: Add HCI_Write_Link_Supervision_Timeout command/event structures

Paul Menzel <[email protected]> Fri, 24 Jul 2026 11:23:26 +0200
Newsgroups org.kernel.vger.linux-bluetooth
Message-ID <[email protected]>
Dear Chen,


Thank you for your patch.

Am 24.07.26 um 10:54 schrieb [email protected]:
> From: Chen Zhang <[email protected]>
> 
> Some speakers do not actively initiate disconnection when power off. As

… powering …

> a result, the center can only wait for a timeout to disconnect, with the

What does “the center” refer to?

> default timeout being 20s. In certain scenarios, this can significantly
> impact user experience. For example, if a speaker is playing music and
> is then turned off, it takes 20s before the music starts playing from
> the center.
> 
> Define HCI_Write_Link_Supervision_Timeout command, event structure, and
> corresponding event handler function, set the timeout to 5s after ACL
> link is established.

Why five seconds and not less? How do Chromium OS or Android do it?

Please also document one test setup up, where you could reproduce it.

> Signed-off-by: Chen Zhang <[email protected]>
> ---
>   include/net/bluetooth/hci.h      | 10 +++++++
>   include/net/bluetooth/hci_core.h |  1 +
>   net/bluetooth/hci_event.c        | 50 ++++++++++++++++++++++++++++++++
>   3 files changed, 61 insertions(+)
> 
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index 50f0eef71fb1..18a5a17eb218 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -1239,6 +1239,16 @@ struct hci_cp_host_buffer_size {
>   	__le16   sco_max_pkt;
>   } __packed;
>   
> +#define HCI_OP_WRITE_LINK_SUPERVISION_TIMEOUT	0x0c37
> +struct hci_cp_write_link_supervision_timeout {
> +	__le16	 handle;
> +	__le16	 timeout;

How about appending the unit for the timeout: timeout_s.

> +} __packed;
> +struct hci_rp_write_link_supervision_timeout {
> +	__u8	 status;
> +	__le16	 handle;
> +} __packed;
> +
>   #define HCI_OP_READ_NUM_SUPPORTED_IAC	0x0c38
>   struct hci_rp_read_num_supported_iac {
>   	__u8	status;
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e7133ff87fbf..866903a9b5c8 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -702,6 +702,7 @@ struct hci_conn {
>   	__u8		le_features[248];
>   	__u16		pkt_type;
>   	__u16		link_policy;
> +	__u16		link_supervision_timeout;

Ditto.

>   	__u8		key_type;
>   	__u8		auth_type;
>   	__u8		sec_level;
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 741d658e9630..653184d0dfc7 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -606,6 +606,44 @@ static u8 hci_cc_write_voice_setting(struct hci_dev *hdev, void *data,
>   	return rp->status;
>   }
>   
> +static u8 hci_cc_write_link_supervision_timeout(struct hci_dev *hdev, void *data,
> +						struct sk_buff *skb)
> +{
> +	struct hci_ev_status *rp = data;
> +	struct hci_cp_write_link_supervision_timeout *sent;
> +	struct hci_conn *conn;
> +	u16 handle;
> +	u8 status = rp->status;
> +
> +	bt_dev_dbg(hdev, "status 0x%2.2x", status);
> +
> +	sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_SUPERVISION_TIMEOUT);
> +	if (!sent)
> +		return status;
> +
> +	handle = le16_to_cpu(sent->handle);
> +
> +	hci_dev_lock(hdev);
> +
> +	conn = hci_conn_hash_lookup_handle(hdev, handle);
> +	if (!conn) {
> +		status = 0xFF;
> +		goto done;
> +	}
> +
> +	if (!status) {
> +		conn->link_supervision_timeout = __le16_to_cpu(sent->timeout);
> +		bt_dev_dbg(hdev, "handle 0x%4.4x timeout set to 0x%4.4x (%u ms)",
> +			   handle, conn->link_supervision_timeout,
> +			   conn->link_supervision_timeout * 5 / 8);
> +	}
> +
> +done:
> +	hci_dev_unlock(hdev);
> +
> +	return status;
> +}
> +
>   static u8 hci_cc_read_num_supported_iac(struct hci_dev *hdev, void *data,
>   					struct sk_buff *skb)
>   {
> @@ -3240,6 +3278,16 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
>   			hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp),
>   				     &cp);
>   		}
> +
> +		if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
> +			struct hci_cp_write_link_supervision_timeout cp;
> +
> +			cp.handle = ev->handle;
> +			cp.timeout = 0x1F40;	/* 8000 * 0.625ms = 5000ms */
> +			hci_send_cmd(hdev, HCI_OP_WRITE_LINK_SUPERVISION_TIMEOUT,
> +				     sizeof(cp), &cp);
> +		}
> +
>   	}
>   
>   	if (conn->type == ACL_LINK)
> @@ -4097,6 +4145,8 @@ static const struct hci_cc {
>   	HCI_CC_STATUS(HCI_OP_WRITE_CLASS_OF_DEV, hci_cc_write_class_of_dev),
>   	HCI_CC(HCI_OP_READ_VOICE_SETTING, hci_cc_read_voice_setting,
>   	       sizeof(struct hci_rp_read_voice_setting)),
> +	HCI_CC(HCI_OP_WRITE_LINK_SUPERVISION_TIMEOUT, hci_cc_write_link_supervision_timeout,
> +	       sizeof(struct hci_rp_write_link_supervision_timeout)),
>   	HCI_CC_STATUS(HCI_OP_WRITE_VOICE_SETTING, hci_cc_write_voice_setting),
>   	HCI_CC(HCI_OP_READ_NUM_SUPPORTED_IAC, hci_cc_read_num_supported_iac,
>   	       sizeof(struct hci_rp_read_num_supported_iac)),


Kind regards,

Paul