From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72B8860A5BEB for ; Wed, 4 Nov 2026 15:16:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=pIr78vxb; dkim=pass (2048-bit key, unprotected) header.d=deq.rocks header.i=@deq.rocks header.a=rsa-sha256 header.s=securedbylima-20251205 header.b=HHKIVzZh DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Unsubscribe-Post: List-Owner; bh=02oqJFuZOwkLLIvzoQGr87f78l4wKO7w6+PsJrwWorM=; b=pIr78vxbhcqp+L CEJKR4VA4+f/fV7DRgWh0KIqa463hfCsP8mRAPnRalzKJSG88fEWCXBHq7nxrze+gg6pOEimeBESC V8syyR8YHhO3I3fzlPoOQ1EnFqlTGIIJ+dwB/Mkxnqr9KETtFjuvCJDiX73fuLqSo42uQRqM4vRsB CXDi9RU32JrAUJTCoh0y8tKqe3ESj4Ih6lQ/PsYbDXo/gBaFsa1CHKPrI488/SJlm+YnndF2iciY+ FuaiCvIRx2QMHWfuD5wplsXhEpwLlNHdAdz/VdzlMQtB5+Le74TTdlq1yBHJwJ+WWm0qTdESJjucQ QUeR5jE+N14wjwwuy5Rw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.100.1 #2 (Red Hat Linux)) id 1xOcin-00000005k40-3cSR; Wed, 04 Nov 2026 15:16:17 +0000 Received: from bulk0.mail-out.lima-city.de ([91.216.248.212]) by bombadil.infradead.org with esmtps (Exim 4.100.1 #2 (Red Hat Linux)) id 1xOcih-00000005jxz-3dwN for linux-arm-kernel@lists.infradead.org; Wed, 04 Nov 2026 15:16:13 +0000 From: Andre Eikmeyer DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=deq.rocks; s=securedbylima-20251205; t=1793805368; bh=if5v2Iwk8z7S+RJXt6BMc3wLoPxWRgj+cfJl6R8agJ0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HHKIVzZhhCvPXVkAjBRn8lWoKZw17vGQ4RBEs96Wv8uqnx5IAqrHGvIwqS8FWpwe1 O4ZZK1mPJaP+lk/1COqOArRtpDBLo45Hgz6/KM/i4aIjO6oUKA5HBslX0E+ZVbj0d6 P6eJA7HKatTdlsTIOvITnzXGlU5ye752oaU4XqjQ9yg2mRPafjUx2xbtuzrfP4c5KY Cvfe9nT6BlPK4zN0ueRqCA2xKHFtT9To1YiLdW4UnFl7uhtYm81F9F1OBq/uunrosa n+SrrZYCP04q9u6Nht25xGTwadzwEkyNwbGC/T5HyDTix2d0eqchsH3kzz7PFzzQIw +CsZUbsJT17Xg== To: marcel@holtmann.org, luiz.dentz@gmail.com Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org, sven@kernel.org, j@jannau.net, neal@gompa.dev, asahi@lists.linux.dev, linux-arm-kernel@lists.infradead.org, 66ton99@gmail.com, josemiguelo.ochoa@gmail.com, aditya.garg@linux.dev, Andre Eikmeyer , Yossef Osman Subject: [PATCH 1/4] Bluetooth: btbcm: raise ACL priority on Apple Broadcom controllers Date: Wed, 4 Nov 2026 16:15:58 +0100 Message-ID: <20261104151601.70493-2-dev@deq.rocks> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261104151601.70493-1-dev@deq.rocks> References: <20261104151601.70493-1-dev@deq.rocks> X-Archived-By: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20261104_071612_221424_6C729E2F X-CRM114-Status: GOOD ( 21.17 ) X-Spam-Score: -2.8 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 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 Tested-by: Aditya Garg Signed-off-by: Andre Eikmeyer --- 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 #include +#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: