* [PATCH v3] Bluetooth: Add Broadcom channel priority commands
@ 2026-05-25 12:11 Sasha Finkelstein
2026-05-25 13:58 ` Joshua Peisach
0 siblings, 1 reply; 4+ messages in thread
From: Sasha Finkelstein @ 2026-05-25 12:11 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Marcel Holtmann,
Luiz Augusto von Dentz, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: linux-kernel, asahi, linux-arm-kernel, linux-bluetooth, netdev,
Sasha Finkelstein
Certain Broadcom bluetooth chips (bcm4377/bcm4378/bcm438) need ACL
streams carrying audio to be set as "high priority" using a vendor
specific command to prevent 10-ish second-long dropouts whenever
something does a device scan. This patch sends the command when the
socket priority is set to TC_PRIO_INTERACTIVE, as BlueZ does for audio.
From experimenting with the hardware - this command is not suitable for
per-skb priority switching, as prioritization is done on the handle
level, with this command reconfiguring certain radio timings, and
dropping to low priority in order to send a low packet on the same
handle as an audio stream is being played on causes the same kind of
dropout it is supposed to avoid. In addition, the hardware is rather
picky about when this command can be sent, as sending it during
connection open results in a timeout. The vendor stacks solve it by
having high-level visibility into what a connection is used for and
sending it from userspace when it is known that an audio stream is
about to start. As we can't have that visibility without introducing a
new ioctl, the socket priority is used as proxy.
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
Changes in v3:
- use a struct for command data
- Link to v2: https://lore.kernel.org/r/20260407-brcm-prio-v2-1-3f745edf49af@gmail.com
Changes in v2:
- new ioctl got nack-ed, so let's use sk_priority as the trigger
- Link to v1: https://lore.kernel.org/r/20260407-brcm-prio-v1-1-f38b17376640@gmail.com
---
MAINTAINERS | 2 ++
drivers/bluetooth/hci_bcm4377.c | 2 ++
include/net/bluetooth/hci_core.h | 15 +++++++++++++++
net/bluetooth/Kconfig | 7 +++++++
net/bluetooth/Makefile | 1 +
net/bluetooth/brcm.c | 38 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/brcm.h | 19 +++++++++++++++++++
net/bluetooth/hci_core.c | 4 ++++
8 files changed, 88 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 10e8253181d3..da4d12ec70d6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2581,6 +2581,8 @@ F: include/dt-bindings/pinctrl/apple.h
F: include/linux/mfd/macsmc.h
F: include/linux/soc/apple/*
F: include/uapi/drm/asahi_drm.h
+F: net/bluetooth/brcm.c
+F: net/bluetooth/brcm.h
ARM/ARTPEC MACHINE SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com>
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 925d0a635945..5f79920c0306 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -2397,6 +2397,8 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (bcm4377->hw->broken_le_ext_adv_report_phy)
hci_set_quirk(hdev, HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY);
+ hci_set_brcm_capable(hdev);
+
pci_set_drvdata(pdev, bcm4377);
hci_set_drvdata(hdev, bcm4377);
SET_HCIDEV_DEV(hdev, &pdev->dev);
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa600fbf9a53..53ebb2ae898f 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -642,6 +642,10 @@ struct hci_dev {
bool aosp_quality_report;
#endif
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+ bool brcm_capable;
+#endif
+
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
@@ -756,6 +760,10 @@ struct hci_conn {
unsigned int sent;
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+ bool brcm_high_prio;
+#endif
+
struct sk_buff_head data_q;
struct list_head chan_list;
@@ -1791,6 +1799,13 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev)
#endif
}
+static inline void hci_set_brcm_capable(struct hci_dev *hdev)
+{
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+ hdev->brcm_capable = true;
+#endif
+}
+
static inline void hci_devcd_setup(struct hci_dev *hdev)
{
#ifdef CONFIG_DEV_COREDUMP
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index ee6457d1a5ee..b611942c7b8f 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -107,6 +107,13 @@ config BT_AOSPEXT
This options enables support for the Android Open Source
Project defined HCI vendor extensions.
+config BT_BRCMEXT
+ bool "Enable Broadcom extensions"
+ depends on BT
+ help
+ This option enables support for the Broadcom defined HCI
+ vendor extensions.
+
config BT_DEBUGFS
bool "Export Bluetooth internals in debugfs"
depends on BT && DEBUG_FS
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 41049b280887..d402645dfb7d 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -23,5 +23,6 @@ bluetooth-$(CONFIG_BT_LE) += iso.o
bluetooth-$(CONFIG_BT_LEDS) += leds.o
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o
+bluetooth-$(CONFIG_BT_BRCMEXT) += brcm.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
diff --git a/net/bluetooth/brcm.c b/net/bluetooth/brcm.c
new file mode 100644
index 000000000000..299d83d465c3
--- /dev/null
+++ b/net/bluetooth/brcm.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 The Asahi Linux Contributors
+ */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "brcm.h"
+
+struct brcm_prio_cmd {
+ __le16 handle;
+ u8 enable;
+} __packed;
+
+int brcm_set_high_priority(struct hci_dev *hdev, struct hci_conn *conn,
+ bool enable)
+{
+ struct sk_buff *skb;
+ struct brcm_prio_cmd cmd;
+
+ if (!hdev->brcm_capable)
+ return 0;
+
+ if (conn->brcm_high_prio == enable)
+ return 0;
+
+ cmd.handle = cpu_to_le16(conn->handle);
+ cmd.enable = !!enable;
+
+ skb = hci_cmd_sync(hdev, 0xfc57, sizeof(cmd), &cmd, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ conn->brcm_high_prio = enable;
+ kfree_skb(skb);
+ return 0;
+}
diff --git a/net/bluetooth/brcm.h b/net/bluetooth/brcm.h
new file mode 100644
index 000000000000..2290fc6cf798
--- /dev/null
+++ b/net/bluetooth/brcm.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 The Asahi Linux Contributors
+ */
+
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+
+int brcm_set_high_priority(struct hci_dev *hdev, struct hci_conn *conn,
+ bool enable);
+
+#else
+
+static inline int brcm_set_high_priority(struct hci_dev *hdev,
+ struct hci_conn *conn, bool enable)
+{
+ return 0;
+}
+
+#endif
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index c46c1236ebfa..0e74bad496a2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -46,6 +46,7 @@
#include "msft.h"
#include "aosp.h"
#include "hci_codec.h"
+#include "brcm.h"
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
@@ -3696,6 +3697,9 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
skb = skb_dequeue(&chan->data_q);
+ if (skb->priority == TC_PRIO_INTERACTIVE)
+ brcm_set_high_priority(hdev, chan->conn, true);
+
hci_conn_enter_active_mode(chan->conn,
bt_cb(skb)->force_active);
---
base-commit: 8bc67e4db64aa72732c474b44ea8622062c903f0
change-id: 20260407-brcm-prio-b630e6cc3834
Best regards,
--
Sasha Finkelstein <k@chaosmail.tech>
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v3] Bluetooth: Add Broadcom channel priority commands
2026-05-25 12:11 [PATCH v3] Bluetooth: Add Broadcom channel priority commands Sasha Finkelstein
@ 2026-05-25 13:58 ` Joshua Peisach
2026-05-25 14:05 ` Sasha Finkelstein
0 siblings, 1 reply; 4+ messages in thread
From: Joshua Peisach @ 2026-05-25 13:58 UTC (permalink / raw)
To: Sasha Finkelstein, Sven Peter, Janne Grunau, Neal Gompa,
Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: linux-kernel, asahi, linux-arm-kernel, linux-bluetooth, netdev
On Mon May 25, 2026 at 8:11 AM EDT, Sasha Finkelstein wrote:
> --- /dev/null
> +++ b/net/bluetooth/brcm.c
> @@ -0,0 +1,38 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 The Asahi Linux Contributors
> + */
> +
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +
> +#include "brcm.h"
> +
> +struct brcm_prio_cmd {
> + __le16 handle;
> + u8 enable;
> +} __packed;
> +
> +int brcm_set_high_priority(struct hci_dev *hdev, struct hci_conn *conn,
> + bool enable)
> +{
> + struct sk_buff *skb;
> + struct brcm_prio_cmd cmd;
> +
> + if (!hdev->brcm_capable)
> + return 0;
> +
> + if (conn->brcm_high_prio == enable)
> + return 0;
> +
> + cmd.handle = cpu_to_le16(conn->handle);
> + cmd.enable = !!enable;
> +
Probably a dumb question, but worth asking - what is the purpose of the
"!!"? Wouldn't just passing "enable" be sufficient?
Also, currently, cmd.enable seems to just be set and used in the case
of boolean conditions. Would it make sense for the enable field in
brcm_prio_cmd to be a bool or does it not really matter?
> + skb = hci_cmd_sync(hdev, 0xfc57, sizeof(cmd), &cmd, HCI_CMD_TIMEOUT);
> + if (IS_ERR(skb))
> + return PTR_ERR(skb);
> +
> + conn->brcm_high_prio = enable;
> + kfree_skb(skb);
> + return 0;
> +}
> diff --git a/net/bluetooth/brcm.h b/net/bluetooth/brcm.h
> new file mode 100644
> index 000000000000..2290fc6cf798
> --- /dev/null
> +++ b/net/bluetooth/brcm.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2026 The Asahi Linux Contributors
> + */
> +
> +#if IS_ENABLED(CONFIG_BT_BRCMEXT)
> +
> +int brcm_set_high_priority(struct hci_dev *hdev, struct hci_conn *conn,
> + bool enable);
> +
> +#else
> +
> +static inline int brcm_set_high_priority(struct hci_dev *hdev,
> + struct hci_conn *conn, bool enable)
> +{
> + return 0;
> +}
> +
> +#endif
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index c46c1236ebfa..0e74bad496a2 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -46,6 +46,7 @@
> #include "msft.h"
> #include "aosp.h"
> #include "hci_codec.h"
> +#include "brcm.h"
>
> static void hci_rx_work(struct work_struct *work);
> static void hci_cmd_work(struct work_struct *work);
> @@ -3696,6 +3697,9 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
>
> skb = skb_dequeue(&chan->data_q);
>
> + if (skb->priority == TC_PRIO_INTERACTIVE)
> + brcm_set_high_priority(hdev, chan->conn, true);
> +
> hci_conn_enter_active_mode(chan->conn,
> bt_cb(skb)->force_active);
>
>
> ---
> base-commit: 8bc67e4db64aa72732c474b44ea8622062c903f0
> change-id: 20260407-brcm-prio-b630e6cc3834
>
> Best regards,
> --
> Sasha Finkelstein <k@chaosmail.tech>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3] Bluetooth: Add Broadcom channel priority commands
2026-05-25 13:58 ` Joshua Peisach
@ 2026-05-25 14:05 ` Sasha Finkelstein
2026-05-25 14:07 ` Joshua Peisach
0 siblings, 1 reply; 4+ messages in thread
From: Sasha Finkelstein @ 2026-05-25 14:05 UTC (permalink / raw)
To: Joshua Peisach
Cc: Sven Peter, Janne Grunau, Neal Gompa, Marcel Holtmann,
Luiz Augusto von Dentz, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, asahi,
linux-arm-kernel, linux-bluetooth, netdev
> On May 25, 2026, at 15:58, Joshua Peisach <jpeisach@ubuntu.com> wrote:
>
> On Mon May 25, 2026 at 8:11 AM EDT, Sasha Finkelstein wrote:
>>
>> + cmd.enable = !!enable;
>> +
>
> Probably a dumb question, but worth asking - what is the purpose of the
> "!!"? Wouldn't just passing "enable" be sufficient?
This is a way to enforce that the value is exactly 0 or 1, not 0 or non-zero.
>
> Also, currently, cmd.enable seems to just be set and used in the case
> of boolean conditions. Would it make sense for the enable field in
> brcm_prio_cmd to be a bool or does it not really matter?
>
Opposite, actually, this struct is fed to firmware, and it needs to be
specifically a u8.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3] Bluetooth: Add Broadcom channel priority commands
2026-05-25 14:05 ` Sasha Finkelstein
@ 2026-05-25 14:07 ` Joshua Peisach
0 siblings, 0 replies; 4+ messages in thread
From: Joshua Peisach @ 2026-05-25 14:07 UTC (permalink / raw)
To: Sasha Finkelstein, Joshua Peisach
Cc: Sven Peter, Janne Grunau, Neal Gompa, Marcel Holtmann,
Luiz Augusto von Dentz, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, asahi,
linux-arm-kernel, linux-bluetooth, netdev
On Mon May 25, 2026 at 10:05 AM EDT, Sasha Finkelstein wrote:
>
>> On May 25, 2026, at 15:58, Joshua Peisach <jpeisach@ubuntu.com> wrote:
>>
>> On Mon May 25, 2026 at 8:11 AM EDT, Sasha Finkelstein wrote:
>>>
>>> + cmd.enable = !!enable;
>>> +
>>
>> Probably a dumb question, but worth asking - what is the purpose of the
>> "!!"? Wouldn't just passing "enable" be sufficient?
>
> This is a way to enforce that the value is exactly 0 or 1, not 0 or non-zero.
>
>>
>> Also, currently, cmd.enable seems to just be set and used in the case
>> of boolean conditions. Would it make sense for the enable field in
>> brcm_prio_cmd to be a bool or does it not really matter?
>>
> Opposite, actually, this struct is fed to firmware, and it needs to be
> specifically a u8.
Got it, thanks!
Reviewed-by: Joshua Peisach <jpeisach@ubuntu.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-25 14:07 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-25 12:11 [PATCH v3] Bluetooth: Add Broadcom channel priority commands Sasha Finkelstein
2026-05-25 13:58 ` Joshua Peisach
2026-05-25 14:05 ` Sasha Finkelstein
2026-05-25 14:07 ` Joshua Peisach
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox