Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -2529,6 +2529,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>
Expand Down
2 changes: 2 additions & 0 deletions drivers/bluetooth/hci_bcm4377.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions include/net/bluetooth/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ struct l2cap_ctrl {
};

struct hci_dev;
struct hci_conn;

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

int hci_conn_setsockopt(struct hci_conn *conn, struct sock *sk, int level,
int optname, sockptr_t optval, unsigned int optlen);

#define HCI_REQ_START BIT(0)
#define HCI_REQ_SKB BIT(1)

Expand Down
11 changes: 11 additions & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1791,6 +1795,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
Expand Down
7 changes: 7 additions & 0 deletions net/bluetooth/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,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
Expand Down
1 change: 1 addition & 0 deletions net/bluetooth/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,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
29 changes: 29 additions & 0 deletions net/bluetooth/brcm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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"

int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable)
{
struct sk_buff *skb;
u8 cmd[3];

if (!hdev->brcm_capable)
return 0;

cmd[0] = handle;
cmd[1] = handle >> 8;
cmd[2] = !!enable;

skb = hci_cmd_sync(hdev, 0xfc57, sizeof(cmd), cmd, HCI_CMD_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);

kfree_skb(skb);
return 0;
}
17 changes: 17 additions & 0 deletions net/bluetooth/brcm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* 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, u16 handle, bool enable);

#else

static inline int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable)
{
return 0;
}

#endif
27 changes: 27 additions & 0 deletions net/bluetooth/hci_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <net/bluetooth/iso.h>
#include <net/bluetooth/mgmt.h>

#include "brcm.h"
#include "smp.h"
#include "eir.h"

Expand Down Expand Up @@ -2958,6 +2959,32 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
return phys;
}

int hci_conn_setsockopt(struct hci_conn *conn, struct sock *sk, int level,
int optname, sockptr_t optval, unsigned int optlen) {
int val;
bool old_high, new_high, changed;

if (level != SOL_SOCKET)
return 0;

if (optname != SO_PRIORITY)
return 0;

if (optlen < sizeof(int))
return -EINVAL;

if (copy_from_sockptr(&val, optval, sizeof(val)))
return -EFAULT;

old_high = sk->sk_priority >= TC_PRIO_INTERACTIVE;
new_high = val >= TC_PRIO_INTERACTIVE;
changed = old_high != new_high;
if (!changed)
return 0;

return brcm_set_high_priority(conn->hdev, conn->handle, new_high);
}

static int abort_conn_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
Expand Down
13 changes: 13 additions & 0 deletions net/bluetooth/l2cap_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,16 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,

BT_DBG("sk %p", sk);

if (level == SOL_SOCKET) {
conn = chan->conn;
if (conn)
err = hci_conn_setsockopt(conn->hcon, sock->sk, level,
optname, optval, optlen);
if (err)
return err;
return sock_setsockopt(sock, level, optname, optval, optlen);
}

if (level == SOL_L2CAP)
return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);

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

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

if (sock)
set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags);

chan = l2cap_chan_create();
if (!chan) {
sk_free(sk);
Expand Down