Skip to content

Commit 5f8b283

Browse files
WhatAmISupposedToPutHerejannau
authored andcommitted
Bluetooth: Add Broadcom channel priority commands
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. Signed-off-by: Sasha Finkelstein <[email protected]>
1 parent b5c37ef commit 5f8b283

10 files changed

Lines changed: 113 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,8 @@ F: include/dt-bindings/pinctrl/apple.h
25292529
F: include/linux/mfd/macsmc.h
25302530
F: include/linux/soc/apple/*
25312531
F: include/uapi/drm/asahi_drm.h
2532+
F: net/bluetooth/brcm.c
2533+
F: net/bluetooth/brcm.h
25322534

25332535
ARM/ARTPEC MACHINE SUPPORT
25342536
M: Jesper Nilsson <[email protected]>

drivers/bluetooth/hci_bcm4377.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,8 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
23972397
if (bcm4377->hw->broken_le_ext_adv_report_phy)
23982398
hci_set_quirk(hdev, HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY);
23992399

2400+
hci_set_brcm_capable(hdev);
2401+
24002402
pci_set_drvdata(pdev, bcm4377);
24012403
hci_set_drvdata(hdev, bcm4377);
24022404
SET_HCIDEV_DEV(hdev, &pdev->dev);

include/net/bluetooth/bluetooth.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ struct l2cap_ctrl {
448448
};
449449

450450
struct hci_dev;
451+
struct hci_conn;
451452

452453
typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
453454
typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
@@ -460,6 +461,9 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
460461
int hci_ethtool_ts_info(unsigned int index, int sk_proto,
461462
struct kernel_ethtool_ts_info *ts_info);
462463

464+
int hci_conn_setsockopt(struct hci_conn *conn, struct sock *sk, int level,
465+
int optname, sockptr_t optval, unsigned int optlen);
466+
463467
#define HCI_REQ_START BIT(0)
464468
#define HCI_REQ_SKB BIT(1)
465469

include/net/bluetooth/hci_core.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,10 @@ struct hci_dev {
642642
bool aosp_quality_report;
643643
#endif
644644

645+
#if IS_ENABLED(CONFIG_BT_BRCMEXT)
646+
bool brcm_capable;
647+
#endif
648+
645649
int (*open)(struct hci_dev *hdev);
646650
int (*close)(struct hci_dev *hdev);
647651
int (*flush)(struct hci_dev *hdev);
@@ -1791,6 +1795,13 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev)
17911795
#endif
17921796
}
17931797

1798+
static inline void hci_set_brcm_capable(struct hci_dev *hdev)
1799+
{
1800+
#if IS_ENABLED(CONFIG_BT_BRCMEXT)
1801+
hdev->brcm_capable = true;
1802+
#endif
1803+
}
1804+
17941805
static inline void hci_devcd_setup(struct hci_dev *hdev)
17951806
{
17961807
#ifdef CONFIG_DEV_COREDUMP

net/bluetooth/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ config BT_AOSPEXT
110110
This options enables support for the Android Open Source
111111
Project defined HCI vendor extensions.
112112

113+
config BT_BRCMEXT
114+
bool "Enable Broadcom extensions"
115+
depends on BT
116+
help
117+
This option enables support for the Broadcom defined HCI
118+
vendor extensions.
119+
113120
config BT_DEBUGFS
114121
bool "Export Bluetooth internals in debugfs"
115122
depends on BT && DEBUG_FS

net/bluetooth/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ bluetooth-$(CONFIG_BT_LE) += iso.o
2424
bluetooth-$(CONFIG_BT_LEDS) += leds.o
2525
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
2626
bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o
27+
bluetooth-$(CONFIG_BT_BRCMEXT) += brcm.o
2728
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
2829
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o

net/bluetooth/brcm.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2026 The Asahi Linux Contributors
4+
*/
5+
6+
#include <net/bluetooth/bluetooth.h>
7+
#include <net/bluetooth/hci_core.h>
8+
9+
#include "brcm.h"
10+
11+
int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable)
12+
{
13+
struct sk_buff *skb;
14+
u8 cmd[3];
15+
16+
if (!hdev->brcm_capable)
17+
return 0;
18+
19+
cmd[0] = handle;
20+
cmd[1] = handle >> 8;
21+
cmd[2] = !!enable;
22+
23+
skb = hci_cmd_sync(hdev, 0xfc57, sizeof(cmd), cmd, HCI_CMD_TIMEOUT);
24+
if (IS_ERR(skb))
25+
return PTR_ERR(skb);
26+
27+
kfree_skb(skb);
28+
return 0;
29+
}

net/bluetooth/brcm.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (C) 2026 The Asahi Linux Contributors
4+
*/
5+
6+
#if IS_ENABLED(CONFIG_BT_BRCMEXT)
7+
8+
int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable);
9+
10+
#else
11+
12+
static inline int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable)
13+
{
14+
return 0;
15+
}
16+
17+
#endif

net/bluetooth/hci_conn.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <net/bluetooth/iso.h>
3636
#include <net/bluetooth/mgmt.h>
3737

38+
#include "brcm.h"
3839
#include "smp.h"
3940
#include "eir.h"
4041

@@ -2958,6 +2959,32 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
29582959
return phys;
29592960
}
29602961

2962+
int hci_conn_setsockopt(struct hci_conn *conn, struct sock *sk, int level,
2963+
int optname, sockptr_t optval, unsigned int optlen) {
2964+
int val;
2965+
bool old_high, new_high, changed;
2966+
2967+
if (level != SOL_SOCKET)
2968+
return 0;
2969+
2970+
if (optname != SO_PRIORITY)
2971+
return 0;
2972+
2973+
if (optlen < sizeof(int))
2974+
return -EINVAL;
2975+
2976+
if (copy_from_sockptr(&val, optval, sizeof(val)))
2977+
return -EFAULT;
2978+
2979+
old_high = sk->sk_priority >= TC_PRIO_INTERACTIVE;
2980+
new_high = val >= TC_PRIO_INTERACTIVE;
2981+
changed = old_high != new_high;
2982+
if (!changed)
2983+
return 0;
2984+
2985+
return brcm_set_high_priority(conn->hdev, conn->handle, new_high);
2986+
}
2987+
29612988
static int abort_conn_sync(struct hci_dev *hdev, void *data)
29622989
{
29632990
struct hci_conn *conn = data;

net/bluetooth/l2cap_sock.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,16 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
891891

892892
BT_DBG("sk %p", sk);
893893

894+
if (level == SOL_SOCKET) {
895+
conn = chan->conn;
896+
if (conn)
897+
err = hci_conn_setsockopt(conn->hcon, sock->sk, level,
898+
optname, optval, optlen);
899+
if (err)
900+
return err;
901+
return sock_setsockopt(sock, level, optname, optval, optlen);
902+
}
903+
894904
if (level == SOL_L2CAP)
895905
return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);
896906

@@ -1914,6 +1924,9 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
19141924

19151925
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
19161926

1927+
if (sock)
1928+
set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags);
1929+
19171930
chan = l2cap_chan_create();
19181931
if (!chan) {
19191932
sk_free(sk);

0 commit comments

Comments
 (0)