* [PATCH 1/4] Bluetooth: btbcm: raise ACL priority on Apple Broadcom controllers
2026-11-04 15:15 [PATCH 0/4] Bluetooth: Broadcom controllers of the Apple T2 Macs Andre Eikmeyer
@ 2026-11-04 15:15 ` Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types Andre Eikmeyer
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Andre Eikmeyer @ 2026-11-04 15:15 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Andre Eikmeyer, Yossef Osman
On the MacBookPro15,1 (BCM4364B0 over UART) A2DP audio drops out as
soon as WLAN runs on 2.4 GHz with traffic. The host keeps the eight ACL
buffers of the controller filled, but the controller holds single ACL
packets for 160-200 ms, so the sink runs dry.
Sending the Broadcom vendor command 0xfc57 (HCI_BRCM_SET_ACL_PRIORITY,
handle and priority, as used by the Android Broadcom vendor library) with
priority high for the audio link stops the dropouts. Setting it back to
normal brings them back right away. The BCM4377 of the MacBookAir9,1
accepts the command as well.
Raise the priority for every BR/EDR ACL link once its Connection Complete
event arrives. This covers audio as well as HID links. hci_bcm does this
on Apple machines only, hci_bcm4377 always, since it only runs on Apple
machines. The PCIe driver receives events in hard interrupt context, so
the command is queued from a work item.
Tested-by: Yossef Osman <Yossef.Osman@proton.me>
Tested-by: Aditya Garg <aditya.garg@linux.dev>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
drivers/bluetooth/Kconfig | 1 +
drivers/bluetooth/btbcm.c | 72 +++++++++++++++++++++++++++++++++
drivers/bluetooth/btbcm.h | 7 ++++
drivers/bluetooth/hci_bcm.c | 18 ++++++++-
drivers/bluetooth/hci_bcm4377.c | 3 ++
5 files changed, 99 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index b18482b5677..3e2426abdff 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -318,6 +318,7 @@ config BT_HCIBCM4377
tristate "HCI BCM4377/4378/4387/4388 PCIe driver"
depends on PCI
select FW_LOADER
+ select BT_BCM
help
Support for Broadcom BCM4377/4378/4387/4388 Bluetooth chipsets
attached via PCIe. These are usually found in Apple machines.
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 065536d4ec7..0facfe62333 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -210,6 +210,78 @@ int btbcm_write_pcm_int_params(struct hci_dev *hdev,
}
EXPORT_SYMBOL_GPL(btbcm_write_pcm_int_params);
+#define BCM_SET_ACL_PRIORITY 0xfc57
+#define BCM_ACL_PRIORITY_HIGH 0x01
+
+struct bcm_set_acl_priority {
+ __le16 handle;
+ u8 priority;
+} __packed;
+
+struct btbcm_acl_priority_work {
+ struct work_struct work;
+ struct hci_dev *hdev;
+ u16 handle;
+};
+
+static int btbcm_set_acl_priority_sync(struct hci_dev *hdev, void *data)
+{
+ struct bcm_set_acl_priority cp;
+ int err;
+
+ cp.handle = cpu_to_le16(PTR_UINT(data));
+ cp.priority = BCM_ACL_PRIORITY_HIGH;
+
+ err = __hci_cmd_sync_status(hdev, BCM_SET_ACL_PRIORITY, sizeof(cp),
+ &cp, HCI_CMD_TIMEOUT);
+ if (err)
+ bt_dev_warn(hdev, "BCM: setting ACL priority for handle 0x%4.4x failed (%d)",
+ le16_to_cpu(cp.handle), err);
+ return err;
+}
+
+static void btbcm_acl_priority_work(struct work_struct *work)
+{
+ struct btbcm_acl_priority_work *w =
+ container_of(work, struct btbcm_acl_priority_work, work);
+
+ hci_cmd_sync_queue(w->hdev, btbcm_set_acl_priority_sync,
+ UINT_PTR(w->handle), NULL);
+ hci_dev_put(w->hdev);
+ kfree(w);
+}
+
+/* Give every BR/EDR ACL link high priority in the controller as soon as it
+ * is connected. Without it the BCM4364 coexistence arbitration lets 2.4 GHz
+ * WLAN traffic delay ACL packets by up to 200 ms, which drops A2DP audio.
+ * Called with each received event, also from atomic context.
+ */
+void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *)skb->data;
+ struct hci_ev_conn_complete *ev;
+ struct btbcm_acl_priority_work *w;
+
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT ||
+ skb->len < sizeof(*hdr) + sizeof(*ev) ||
+ hdr->evt != HCI_EV_CONN_COMPLETE)
+ return;
+
+ ev = (void *)(skb->data + sizeof(*hdr));
+ if (ev->status || ev->link_type != ACL_LINK)
+ return;
+
+ w = kzalloc_obj(*w, GFP_ATOMIC);
+ if (!w)
+ return;
+
+ INIT_WORK(&w->work, btbcm_acl_priority_work);
+ w->hdev = hci_dev_hold(hdev);
+ w->handle = __le16_to_cpu(ev->handle);
+ queue_work(system_dfl_wq, &w->work);
+}
+EXPORT_SYMBOL_GPL(btbcm_set_acl_priority_on_connect);
+
int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
{
const struct hci_command_hdr *cmd;
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
index b4cb24231a2..a4d47b242e9 100644
--- a/drivers/bluetooth/btbcm.h
+++ b/drivers/bluetooth/btbcm.h
@@ -59,6 +59,8 @@ int btbcm_read_pcm_int_params(struct hci_dev *hdev,
int btbcm_write_pcm_int_params(struct hci_dev *hdev,
const struct bcm_set_pcm_int_params *params);
+void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb);
+
int btbcm_setup_patchram(struct hci_dev *hdev);
int btbcm_setup_apple(struct hci_dev *hdev);
@@ -94,6 +96,11 @@ static inline int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw
return -EOPNOTSUPP;
}
+static inline void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+}
+
static inline int btbcm_setup_patchram(struct hci_dev *hdev)
{
return 0;
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index e70b9f3fd96..cf4f1455cf9 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -147,6 +147,7 @@ struct bcm_device {
bool drive_rts_on_open;
bool no_uart_clock_set;
bool use_autobaud_mode;
+ bool acl_priority;
u8 pcm_int_params[5];
u32 max_autobaud_speed;
};
@@ -680,10 +681,21 @@ static int bcm_setup(struct hci_uart *hu)
.lsize = 0, \
.maxlen = BCM_TYPE52_SIZE
+static int bcm_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct bcm_data *bcm = hu->priv;
+
+ if (bcm->dev && bcm->dev->acl_priority)
+ btbcm_set_acl_priority_on_connect(hdev, skb);
+
+ return hci_recv_frame(hdev, skb);
+}
+
static const struct h4_recv_pkt bcm_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
- { H4_RECV_EVENT, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = bcm_recv_event },
{ H4_RECV_ISO, .recv = hci_recv_frame },
{ BCM_RECV_LM_DIAG, .recv = hci_recv_diag },
{ BCM_RECV_NULL, .recv = hci_recv_diag },
@@ -1086,8 +1098,10 @@ static int bcm_get_resources(struct bcm_device *dev)
dev->name = dev_name(dev->dev);
- if (x86_apple_machine && !bcm_apple_get_resources(dev))
+ if (x86_apple_machine && !bcm_apple_get_resources(dev)) {
+ dev->acl_priority = true;
return 0;
+ }
dev->txco_clk = bcm_get_txco(dev->dev);
if (IS_ERR(dev->txco_clk))
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 66d49b47154..30fcadfae3f 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -22,6 +22,8 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include "btbcm.h"
+
enum bcm4377_chip {
BCM4377 = 0,
BCM4378,
@@ -689,6 +691,7 @@ static void bcm4377_handle_event(struct bcm4377_data *bcm4377,
memcpy(skb_put(skb, len), payload, len);
hci_skb_pkt_type(skb) = type;
+ btbcm_set_acl_priority_on_connect(bcm4377->hdev, skb);
hci_recv_frame(bcm4377->hdev, skb);
out:
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types
2026-11-04 15:15 [PATCH 0/4] Bluetooth: Broadcom controllers of the Apple T2 Macs Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 1/4] Bluetooth: btbcm: raise ACL priority on Apple Broadcom controllers Andre Eikmeyer
@ 2026-11-04 15:15 ` Andre Eikmeyer
2026-11-04 15:24 ` Luiz Augusto von Dentz
2026-11-04 15:16 ` [PATCH 3/4] Revert "Bluetooth: hci_event: strip Broadcom vendor bits from legacy adv report type" Andre Eikmeyer
2026-11-04 15:16 ` [PATCH 4/4] Bluetooth: set the strict duplicate filter quirk on Apple Broadcom controllers Andre Eikmeyer
3 siblings, 1 reply; 10+ messages in thread
From: Andre Eikmeyer @ 2026-11-04 15:15 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Andre Eikmeyer, Yossef Osman
The BCM4364B0 of the MacBookPro15,1 (UART) and the BCM4377 of the
MacBookAir9,1 (PCIe) set bits 4, 5 and 7 of the event type in LE
Advertising Report events. btmon on the 15,1 shows types 0x10, 0x13,
0x14, 0x20, 0x23, 0x24, 0x93 and 0xa0 next to plain 0x00, 0x03 and 0x04,
the same advertiser shows up as 0x13, 0x23 and 0x03. The kernel log of
the 9,1 lists 0x10 to 0x24 and 0x80, 0x83, 0x90, 0x93, 0xa0, 0xa3.
process_adv_report() drops every type above 0x04 with "Unknown
advertising packet type", so most advertisements never reach the host.
An AirDrop sender was reported as 0x23, 0x23 and 0x13 and only the
fourth report, 0x03, reached BlueZ 32 s after the first one.
Add btbcm_fixup_le_adv_report(), which clears the bits above the type,
and call it from hci_bcm on Apple machines and from hci_bcm4377 before
the event goes to the core.
Suggested-by: Jose Miguel Ochoa <josemiguelo.ochoa@gmail.com>
Tested-by: Yossef Osman <Yossef.Osman@proton.me>
Tested-by: Aditya Garg <aditya.garg@linux.dev>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
drivers/bluetooth/btbcm.c | 30 ++++++++++++++++++++++++++++++
drivers/bluetooth/btbcm.h | 5 +++++
drivers/bluetooth/hci_bcm.c | 5 +++++
drivers/bluetooth/hci_bcm4377.c | 1 +
4 files changed, 41 insertions(+)
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 0facfe62333..527a5c75fd6 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -282,6 +282,36 @@ void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb
}
EXPORT_SYMBOL_GPL(btbcm_set_acl_priority_on_connect);
+/* The BCM4364 and BCM4377 of the T2 Macs set bits above the event type of LE
+ * Advertising Reports, 0x13 and 0x23 for ADV_NONCONN_IND for example. The
+ * core drops reports of unknown types, so clear them.
+ */
+void btbcm_fixup_le_adv_report(struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *)skb->data;
+ struct hci_ev_le_advertising_info *info;
+ u8 *p, *end;
+ u8 num;
+
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT ||
+ skb->len < HCI_EVENT_HDR_SIZE + 2 || hdr->evt != HCI_EV_LE_META)
+ return;
+
+ p = skb->data + HCI_EVENT_HDR_SIZE;
+ if (p[0] != HCI_EV_LE_ADVERTISING_REPORT)
+ return;
+
+ num = p[1];
+ p += 2;
+ end = skb->data + skb->len;
+ while (num-- && p + sizeof(*info) + 1 <= end) {
+ info = (void *)p;
+ info->type &= 0x0f;
+ p += sizeof(*info) + info->length + 1;
+ }
+}
+EXPORT_SYMBOL_GPL(btbcm_fixup_le_adv_report);
+
int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
{
const struct hci_command_hdr *cmd;
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
index a4d47b242e9..56936e64b5f 100644
--- a/drivers/bluetooth/btbcm.h
+++ b/drivers/bluetooth/btbcm.h
@@ -60,6 +60,7 @@ int btbcm_write_pcm_int_params(struct hci_dev *hdev,
const struct bcm_set_pcm_int_params *params);
void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb);
+void btbcm_fixup_le_adv_report(struct sk_buff *skb);
int btbcm_setup_patchram(struct hci_dev *hdev);
int btbcm_setup_apple(struct hci_dev *hdev);
@@ -101,6 +102,10 @@ static inline void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev,
{
}
+static inline void btbcm_fixup_le_adv_report(struct sk_buff *skb)
+{
+}
+
static inline int btbcm_setup_patchram(struct hci_dev *hdev)
{
return 0;
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index cf4f1455cf9..1ae9619ee1e 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -148,6 +148,7 @@ struct bcm_device {
bool no_uart_clock_set;
bool use_autobaud_mode;
bool acl_priority;
+ bool adv_type_fixup;
u8 pcm_int_params[5];
u32 max_autobaud_speed;
};
@@ -686,6 +687,9 @@ static int bcm_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
struct hci_uart *hu = hci_get_drvdata(hdev);
struct bcm_data *bcm = hu->priv;
+ if (bcm->dev && bcm->dev->adv_type_fixup)
+ btbcm_fixup_le_adv_report(skb);
+
if (bcm->dev && bcm->dev->acl_priority)
btbcm_set_acl_priority_on_connect(hdev, skb);
@@ -1100,6 +1104,7 @@ static int bcm_get_resources(struct bcm_device *dev)
if (x86_apple_machine && !bcm_apple_get_resources(dev)) {
dev->acl_priority = true;
+ dev->adv_type_fixup = true;
return 0;
}
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 30fcadfae3f..8d99a7a8f76 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -691,6 +691,7 @@ static void bcm4377_handle_event(struct bcm4377_data *bcm4377,
memcpy(skb_put(skb, len), payload, len);
hci_skb_pkt_type(skb) = type;
+ btbcm_fixup_le_adv_report(skb);
btbcm_set_acl_priority_on_connect(bcm4377->hdev, skb);
hci_recv_frame(bcm4377->hdev, skb);
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types
2026-11-04 15:15 ` [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types Andre Eikmeyer
@ 2026-11-04 15:24 ` Luiz Augusto von Dentz
2026-11-04 15:34 ` Aditya Garg
2026-11-04 15:46 ` André Eikmeyer
0 siblings, 2 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-04 15:24 UTC (permalink / raw)
To: Andre Eikmeyer
Cc: marcel, linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Yossef Osman
Hi Andre,
On Wed, Nov 4, 2026 at 10:16 AM Andre Eikmeyer <dev@deq.rocks> wrote:
>
> The BCM4364B0 of the MacBookPro15,1 (UART) and the BCM4377 of the
> MacBookAir9,1 (PCIe) set bits 4, 5 and 7 of the event type in LE
> Advertising Report events. btmon on the 15,1 shows types 0x10, 0x13,
> 0x14, 0x20, 0x23, 0x24, 0x93 and 0xa0 next to plain 0x00, 0x03 and 0x04,
> the same advertiser shows up as 0x13, 0x23 and 0x03. The kernel log of
> the 9,1 lists 0x10 to 0x24 and 0x80, 0x83, 0x90, 0x93, 0xa0, 0xa3.
> process_adv_report() drops every type above 0x04 with "Unknown
> advertising packet type", so most advertisements never reach the host.
> An AirDrop sender was reported as 0x23, 0x23 and 0x13 and only the
> fourth report, 0x03, reached BlueZ 32 s after the first one.
>
> Add btbcm_fixup_le_adv_report(), which clears the bits above the type,
> and call it from hci_bcm on Apple machines and from hci_bcm4377 before
> the event goes to the core.
>
> Suggested-by: Jose Miguel Ochoa <josemiguelo.ochoa@gmail.com>
> Tested-by: Yossef Osman <Yossef.Osman@proton.me>
> Tested-by: Aditya Garg <aditya.garg@linux.dev>
> Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
> ---
> drivers/bluetooth/btbcm.c | 30 ++++++++++++++++++++++++++++++
> drivers/bluetooth/btbcm.h | 5 +++++
> drivers/bluetooth/hci_bcm.c | 5 +++++
> drivers/bluetooth/hci_bcm4377.c | 1 +
> 4 files changed, 41 insertions(+)
>
> diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
> index 0facfe62333..527a5c75fd6 100644
> --- a/drivers/bluetooth/btbcm.c
> +++ b/drivers/bluetooth/btbcm.c
> @@ -282,6 +282,36 @@ void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb
> }
> EXPORT_SYMBOL_GPL(btbcm_set_acl_priority_on_connect);
>
> +/* The BCM4364 and BCM4377 of the T2 Macs set bits above the event type of LE
> + * Advertising Reports, 0x13 and 0x23 for ADV_NONCONN_IND for example. The
> + * core drops reports of unknown types, so clear them.
> + */
> +void btbcm_fixup_le_adv_report(struct sk_buff *skb)
> +{
> + struct hci_event_hdr *hdr = (void *)skb->data;
> + struct hci_ev_le_advertising_info *info;
> + u8 *p, *end;
> + u8 num;
> +
> + if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT ||
> + skb->len < HCI_EVENT_HDR_SIZE + 2 || hdr->evt != HCI_EV_LE_META)
> + return;
> +
> + p = skb->data + HCI_EVENT_HDR_SIZE;
> + if (p[0] != HCI_EV_LE_ADVERTISING_REPORT)
> + return;
> +
> + num = p[1];
> + p += 2;
> + end = skb->data + skb->len;
> + while (num-- && p + sizeof(*info) + 1 <= end) {
> + info = (void *)p;
> + info->type &= 0x0f;
> + p += sizeof(*info) + info->length + 1;
> + }
> +}
> +EXPORT_SYMBOL_GPL(btbcm_fixup_le_adv_report);
I don't think I agree with this being cleaner then doing at
hci_event.though; not only is it more code but it also walks the
packet which is inefficient and may have security implications as
well.
> int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
> {
> const struct hci_command_hdr *cmd;
> diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
> index a4d47b242e9..56936e64b5f 100644
> --- a/drivers/bluetooth/btbcm.h
> +++ b/drivers/bluetooth/btbcm.h
> @@ -60,6 +60,7 @@ int btbcm_write_pcm_int_params(struct hci_dev *hdev,
> const struct bcm_set_pcm_int_params *params);
>
> void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb);
> +void btbcm_fixup_le_adv_report(struct sk_buff *skb);
>
> int btbcm_setup_patchram(struct hci_dev *hdev);
> int btbcm_setup_apple(struct hci_dev *hdev);
> @@ -101,6 +102,10 @@ static inline void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev,
> {
> }
>
> +static inline void btbcm_fixup_le_adv_report(struct sk_buff *skb)
> +{
> +}
> +
> static inline int btbcm_setup_patchram(struct hci_dev *hdev)
> {
> return 0;
> diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
> index cf4f1455cf9..1ae9619ee1e 100644
> --- a/drivers/bluetooth/hci_bcm.c
> +++ b/drivers/bluetooth/hci_bcm.c
> @@ -148,6 +148,7 @@ struct bcm_device {
> bool no_uart_clock_set;
> bool use_autobaud_mode;
> bool acl_priority;
> + bool adv_type_fixup;
> u8 pcm_int_params[5];
> u32 max_autobaud_speed;
> };
> @@ -686,6 +687,9 @@ static int bcm_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
> struct hci_uart *hu = hci_get_drvdata(hdev);
> struct bcm_data *bcm = hu->priv;
>
> + if (bcm->dev && bcm->dev->adv_type_fixup)
> + btbcm_fixup_le_adv_report(skb);
> +
> if (bcm->dev && bcm->dev->acl_priority)
> btbcm_set_acl_priority_on_connect(hdev, skb);
>
> @@ -1100,6 +1104,7 @@ static int bcm_get_resources(struct bcm_device *dev)
>
> if (x86_apple_machine && !bcm_apple_get_resources(dev)) {
> dev->acl_priority = true;
> + dev->adv_type_fixup = true;
> return 0;
> }
>
> diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
> index 30fcadfae3f..8d99a7a8f76 100644
> --- a/drivers/bluetooth/hci_bcm4377.c
> +++ b/drivers/bluetooth/hci_bcm4377.c
> @@ -691,6 +691,7 @@ static void bcm4377_handle_event(struct bcm4377_data *bcm4377,
>
> memcpy(skb_put(skb, len), payload, len);
> hci_skb_pkt_type(skb) = type;
> + btbcm_fixup_le_adv_report(skb);
> btbcm_set_acl_priority_on_connect(bcm4377->hdev, skb);
> hci_recv_frame(bcm4377->hdev, skb);
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types
2026-11-04 15:24 ` Luiz Augusto von Dentz
@ 2026-11-04 15:34 ` Aditya Garg
2026-11-04 15:46 ` André Eikmeyer
1 sibling, 0 replies; 10+ messages in thread
From: Aditya Garg @ 2026-11-04 15:34 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Andre Eikmeyer, marcel, linux-bluetooth, linux-kernel, sven, j,
neal, asahi, linux-arm-kernel, 66ton99, josemiguelo.ochoa,
Yossef Osman
> On 4 Nov 2026, at 8:55 PM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>
> Hi Andre,
>
>> On Wed, Nov 4, 2026 at 10:16 AM Andre Eikmeyer <dev@deq.rocks> wrote:
>>
>> The BCM4364B0 of the MacBookPro15,1 (UART) and the BCM4377 of the
>> MacBookAir9,1 (PCIe) set bits 4, 5 and 7 of the event type in LE
>> Advertising Report events. btmon on the 15,1 shows types 0x10, 0x13,
>> 0x14, 0x20, 0x23, 0x24, 0x93 and 0xa0 next to plain 0x00, 0x03 and 0x04,
>> the same advertiser shows up as 0x13, 0x23 and 0x03. The kernel log of
>> the 9,1 lists 0x10 to 0x24 and 0x80, 0x83, 0x90, 0x93, 0xa0, 0xa3.
>> process_adv_report() drops every type above 0x04 with "Unknown
>> advertising packet type", so most advertisements never reach the host.
>> An AirDrop sender was reported as 0x23, 0x23 and 0x13 and only the
>> fourth report, 0x03, reached BlueZ 32 s after the first one.
>>
>> Add btbcm_fixup_le_adv_report(), which clears the bits above the type,
>> and call it from hci_bcm on Apple machines and from hci_bcm4377 before
>> the event goes to the core.
>>
>> Suggested-by: Jose Miguel Ochoa <josemiguelo.ochoa@gmail.com>
>> Tested-by: Yossef Osman <Yossef.Osman@proton.me>
>> Tested-by: Aditya Garg <aditya.garg@linux.dev>
>> Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
>> ---
>> drivers/bluetooth/btbcm.c | 30 ++++++++++++++++++++++++++++++
>> drivers/bluetooth/btbcm.h | 5 +++++
>> drivers/bluetooth/hci_bcm.c | 5 +++++
>> drivers/bluetooth/hci_bcm4377.c | 1 +
>> 4 files changed, 41 insertions(+)
>>
>> diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
>> index 0facfe62333..527a5c75fd6 100644
>> --- a/drivers/bluetooth/btbcm.c
>> +++ b/drivers/bluetooth/btbcm.c
>> @@ -282,6 +282,36 @@ void btbcm_set_acl_priority_on_connect(struct hci_dev *hdev, struct sk_buff *skb
>> }
>> EXPORT_SYMBOL_GPL(btbcm_set_acl_priority_on_connect);
>>
>> +/* The BCM4364 and BCM4377 of the T2 Macs set bits above the event type of LE
>> + * Advertising Reports, 0x13 and 0x23 for ADV_NONCONN_IND for example. The
>> + * core drops reports of unknown types, so clear them.
>> + */
>> +void btbcm_fixup_le_adv_report(struct sk_buff *skb)
>> +{
>> + struct hci_event_hdr *hdr = (void *)skb->data;
>> + struct hci_ev_le_advertising_info *info;
>> + u8 *p, *end;
>> + u8 num;
>> +
>> + if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT ||
>> + skb->len < HCI_EVENT_HDR_SIZE + 2 || hdr->evt != HCI_EV_LE_META)
>> + return;
>> +
>> + p = skb->data + HCI_EVENT_HDR_SIZE;
>> + if (p[0] != HCI_EV_LE_ADVERTISING_REPORT)
>> + return;
>> +
>> + num = p[1];
>> + p += 2;
>> + end = skb->data + skb->len;
>> + while (num-- && p + sizeof(*info) + 1 <= end) {
>> + info = (void *)p;
>> + info->type &= 0x0f;
>> + p += sizeof(*info) + info->length + 1;
>> + }
>> +}
>> +EXPORT_SYMBOL_GPL(btbcm_fixup_le_adv_report);
>
> I don't think I agree with this being cleaner then doing at
> hci_event.though; not only is it more code but it also walks the
> packet which is inefficient and may have security implications as
> well.
The hci_event patch makes it apply for all Broadcom chips. The author who sent that patch themselves use a T2 Mac. I doubt all Broadcom chips need this because there have been no bug reports from normal PCs.
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types
2026-11-04 15:24 ` Luiz Augusto von Dentz
2026-11-04 15:34 ` Aditya Garg
@ 2026-11-04 15:46 ` André Eikmeyer
2026-11-04 16:29 ` Luiz Augusto von Dentz
1 sibling, 1 reply; 10+ messages in thread
From: André Eikmeyer @ 2026-11-04 15:46 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: marcel, linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Yossef Osman
On Wed, 4 Nov 2026 10:24:47 -0500
Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> I don't think I agree with this being cleaner then doing at
> hci_event.though; not only is it more code but it also walks the
> packet which is inefficient and may have security implications as
> well.
You are right, the driver should not parse the event a second time.
What I would like to keep is limiting the mask to the controllers that
set these bits, as hci_bcm4377 already does with
HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY:
- add HCI_QUIRK_FIXUP_LE_ADV_REPORT_TYPE
- set it in hci_bcm on Apple machines and in hci_bcm4377
- test the quirk in hci_le_adv_report_evt() instead of the
manufacturer, keeping the mask from 88397423671f
That drops patch 2 and replaces the revert in patch 3. Would that work
for you? Then I would send a v2 along these lines, with patch 1 and 4
unchanged.
Thanks,
Andre
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types
2026-11-04 15:46 ` André Eikmeyer
@ 2026-11-04 16:29 ` Luiz Augusto von Dentz
2026-11-04 16:47 ` André Eikmeyer
0 siblings, 1 reply; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-04 16:29 UTC (permalink / raw)
To: André Eikmeyer
Cc: marcel, linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Yossef Osman
Hi Andre,
On Wed, Nov 4, 2026 at 10:46 AM André Eikmeyer <dev@deq.rocks> wrote:
>
> On Wed, 4 Nov 2026 10:24:47 -0500
> Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>
> > I don't think I agree with this being cleaner then doing at
> > hci_event.though; not only is it more code but it also walks the
> > packet which is inefficient and may have security implications as
> > well.
>
> You are right, the driver should not parse the event a second time.
>
> What I would like to keep is limiting the mask to the controllers that
> set these bits, as hci_bcm4377 already does with
> HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY:
>
> - add HCI_QUIRK_FIXUP_LE_ADV_REPORT_TYPE
> - set it in hci_bcm on Apple machines and in hci_bcm4377
> - test the quirk in hci_le_adv_report_evt() instead of the
> manufacturer, keeping the mask from 88397423671f
>
> That drops patch 2 and replaces the revert in patch 3. Would that work
> for you? Then I would send a v2 along these lines, with patch 1 and 4
> unchanged.
Yes that would work. That said, I don't think it is worth adding a
quirk for this. Masking the reserved bits shouldn't cause any side
effects afaik, well except if those bits are later adopted by
Bluetooth SIG. Then these controllers would become
non-compliant—though they already are, as the bits are reserved and
not in use. In that case we would probably require a quirk to avoid a
conflict. So, is that what you are trying to fix?
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types
2026-11-04 16:29 ` Luiz Augusto von Dentz
@ 2026-11-04 16:47 ` André Eikmeyer
0 siblings, 0 replies; 10+ messages in thread
From: André Eikmeyer @ 2026-11-04 16:47 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: marcel, linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Yossef Osman
On Wed, 4 Nov 2026 11:29:11 -0500
Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Yes that would work. That said, I don't think it is worth adding a
> quirk for this. Masking the reserved bits shouldn't cause any side
> effects afaik, well except if those bits are later adopted by
> Bluetooth SIG. Then these controllers would become
> non-compliant—though they already are, as the bits are reserved and
> not in use. In that case we would probably require a quirk to avoid a
> conflict. So, is that what you are trying to fix?
No, nothing concrete beyond the scope, and I agree it is harmless as
long as the bits stay reserved. We only put it in the drivers because
our downstream ships driver modules through DKMS and not bluetooth.ko,
which let many users test it quickly. Let's keep 88397423671f as it
is then. I'll drop patch 2 and 3 and send a v2 with patch 1 and 4.
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 3/4] Revert "Bluetooth: hci_event: strip Broadcom vendor bits from legacy adv report type"
2026-11-04 15:15 [PATCH 0/4] Bluetooth: Broadcom controllers of the Apple T2 Macs Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 1/4] Bluetooth: btbcm: raise ACL priority on Apple Broadcom controllers Andre Eikmeyer
2026-11-04 15:15 ` [PATCH 2/4] Bluetooth: btbcm: clear vendor bits in LE advertising report types Andre Eikmeyer
@ 2026-11-04 15:16 ` Andre Eikmeyer
2026-11-04 15:16 ` [PATCH 4/4] Bluetooth: set the strict duplicate filter quirk on Apple Broadcom controllers Andre Eikmeyer
3 siblings, 0 replies; 10+ messages in thread
From: Andre Eikmeyer @ 2026-11-04 15:16 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Andre Eikmeyer, Yossef Osman
This reverts commit 88397423671f2b54119a6162a1834d207dcf1a81.
The commit clears the upper bits of the legacy LE Advertising Report
event type for every controller with manufacturer 15, Broadcom. The
reports of these bits, in its own commit message and here, all come
from the Broadcom controllers of the Apple T2 Macs.
The previous patch clears the bits in hci_bcm on Apple machines and in
hci_bcm4377, the drivers of these controllers, before the event reaches
the core. That makes the check in hci_le_adv_report_evt() redundant
for them.
Tested-by: Yossef Osman <Yossef.Osman@proton.me>
Tested-by: Aditya Garg <aditya.garg@linux.dev>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
net/bluetooth/hci_event.c | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 736dd2f741e..0675c224f6c 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -6683,17 +6683,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
- /* Broadcom ROM firmware (BCM4364B3 on Apple T2 Macs, no
- * patchram) sets vendor bits in the legacy Event_Type.
- */
- if (hdev->manufacturer == 15 && info->type > LE_ADV_SCAN_RSP) {
- /* 0x0f masks out upper vendor bits (0x10, 0x20, 0x80),
- * extracting the standard legacy advertising type
- * (0x00-0x04) stored in the low nibble (bits 0-3).
- */
- info->type &= 0x0f;
- }
-
if (info->length <= max_adv_len(hdev)) {
rssi = info->data[info->length];
process_adv_report(hdev, info->type, &info->bdaddr,
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 4/4] Bluetooth: set the strict duplicate filter quirk on Apple Broadcom controllers
2026-11-04 15:15 [PATCH 0/4] Bluetooth: Broadcom controllers of the Apple T2 Macs Andre Eikmeyer
` (2 preceding siblings ...)
2026-11-04 15:16 ` [PATCH 3/4] Revert "Bluetooth: hci_event: strip Broadcom vendor bits from legacy adv report type" Andre Eikmeyer
@ 2026-11-04 15:16 ` Andre Eikmeyer
3 siblings, 0 replies; 10+ messages in thread
From: Andre Eikmeyer @ 2026-11-04 15:16 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, sven, j, neal, asahi,
linux-arm-kernel, 66ton99, josemiguelo.ochoa, aditya.garg,
Andre Eikmeyer, Yossef Osman
btbcm_finalize() sets HCI_QUIRK_STRICT_DUPLICATE_FILTER, but bcm_setup()
returns before it when no patch file was loaded. The MacBookPro15,1 has
none for its BCM4364B0 ("BCM: firmware Patch file not found").
hci_bcm4377 does not use btbcm_finalize() and never sets the quirk. A
service discovery then scans with "Filter duplicates: Enabled", and the
controller reports an address once per discovery cycle of about 10.7 s.
A Mac that changed its advertisement data on the same address was
reported 6 s after the change, at the next cycle.
Set the quirk in hci_bcm on that path and in hci_bcm4377.
Tested-by: Yossef Osman <Yossef.Osman@proton.me>
Tested-by: Aditya Garg <aditya.garg@linux.dev>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
drivers/bluetooth/hci_bcm.c | 5 ++++-
drivers/bluetooth/hci_bcm4377.c | 5 +++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 1ae9619ee1e..ee077c8502d 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -597,8 +597,11 @@ static int bcm_setup(struct hci_uart *hu)
if (err)
return err;
- if (!fw_load_done)
+ if (!fw_load_done) {
+ /* btbcm_finalize() sets this after loading a patch file */
+ hci_set_quirk(hu->hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
return 0;
+ }
/* Init speed if any */
if (bcm->dev && bcm->dev->init_speed)
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 8d99a7a8f76..7105372898d 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -2392,6 +2392,11 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hdev->set_bdaddr = bcm4377_hci_set_bdaddr;
hdev->setup = bcm4377_hci_setup;
+ /* The controller reports an address once per scan with duplicate
+ * filtering, also after its advertisement data changed.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
+
if (bcm4377->hw->broken_mws_transport_config)
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG);
if (bcm4377->hw->broken_ext_scan)
^ permalink raw reply related [flat|nested] 10+ messages in thread