bluetooth-next pull request for net-next:

core:
 
  - Add support for SIOCETHTOOL ETHTOOL_GET_TS_INFO
  - Separate CIS_LINK and BIS_LINK link types
  - Introduce HCI Driver protocol
 
 drivers:
 
  - btintel_pcie: Do not generate coredump for diagnostic events
  - btusb: Add HCI Drv commands for configuring altsetting
  - btusb: Add RTL8851BE device 0x0bda:0xb850
  - btusb: Add new VID/PID 13d3/3584 for MT7922
  - btusb: Add new VID/PID 13d3/3630 and 13d3/3613 for MT7925
  - btnxpuart: Implement host-wakeup feature
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmgvWiwZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKezlD/4uKp4yrCPAO/tO0FFvh752
 7oVmBzqe6GDunl+Isz6/GSWc5sD0OVdhMg7QL+zhi3hjluyGh9N3rUE9Qw/Q3h8Q
 JkMXWAVNHq+Dr88RqLVro335D2XP8mgiTLEKwSDh5Fdip3xOz+itoQZI5wYqriQg
 exNU1l04ZzrwMWicAJULvFFPz9q/556cUq0x9k7OJ6GaHOmUQ0Y7BPMFAQ0/uHAA
 8Y9qiXlJQKzeYDz9rUvAf6Gd+21k0cAU4QSYt+ZDLGBAuH0iK4zgu56uiHadVLRb
 bm5hlO/lrUD7Hw/swSJ2wZYMKpPINPP6Cr2kpC66kmXZYWx7YJaQQCN8GEtwbEVh
 t3q9Y7zQXjppQ/tIG/WJuWlZ84DiWsm5na3k/q61LfihQ5VPL96RtlJKXD492Dxz
 vFXRFN5F6lMcDP5Ujji6S8O0H5P1bDz9XbITcGHxEDjAbOnThBND7g+10mmZ1MRw
 GWQTnnsrYaU+gaUdj9Nr5o7kPp2KSXvGkG8F407RDvF2fjbbwTNEQgkt7vF9CbPN
 KkJAwnPM+JhSuxGaIVcKpoKJ2gZA/fNXjr9d6hD6v/U+SksNsMovxtdZMaL4Mx/n
 gV8W7RwhUNeJ8NvneHJ12bRhtb7x/IYJsQ6ARgDTenNlSxd56uc3Zt7nmDdIvUfF
 BuEJDucjnJLnCsrUj6MNuw==
 =ZrGM
 -----END PGP SIGNATURE-----

Merge tag 'for-net-next-2025-05-22' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

Luiz Augusto von Dentz says:

====================
bluetooth-next pull request for net-next:

core:
 - Add support for SIOCETHTOOL ETHTOOL_GET_TS_INFO
 - Separate CIS_LINK and BIS_LINK link types
 - Introduce HCI Driver protocol

drivers:
 - btintel_pcie: Do not generate coredump for diagnostic events
 - btusb: Add HCI Drv commands for configuring altsetting
 - btusb: Add RTL8851BE device 0x0bda:0xb850
 - btusb: Add new VID/PID 13d3/3584 for MT7922
 - btusb: Add new VID/PID 13d3/3630 and 13d3/3613 for MT7925
 - btnxpuart: Implement host-wakeup feature

* tag 'for-net-next-2025-05-22' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (23 commits)
  Bluetooth: btintel: Check dsbr size from EFI variable
  Bluetooth: MGMT: iterate over mesh commands in mgmt_mesh_foreach()
  Bluetooth: btusb: Add new VID/PID 13d3/3584 for MT7922
  Bluetooth: btusb: use skb_pull to avoid unsafe access in QCA dump handling
  Bluetooth: L2CAP: Fix not checking l2cap_chan security level
  Bluetooth: separate CIS_LINK and BIS_LINK link types
  Bluetooth: btusb: Add new VID/PID 13d3/3630 for MT7925
  Bluetooth: add support for SIOCETHTOOL ETHTOOL_GET_TS_INFO
  Bluetooth: btintel_pcie: Dump debug registers on error
  Bluetooth: ISO: Fix getpeername not returning sockaddr_iso_bc fields
  Bluetooth: ISO: Fix not using SID from adv report
  Revert "Bluetooth: btusb: add sysfs attribute to control USB alt setting"
  Revert "Bluetooth: btusb: Configure altsetting for HCI_USER_CHANNEL"
  Bluetooth: btusb: Add HCI Drv commands for configuring altsetting
  Bluetooth: Introduce HCI Driver protocol
  Bluetooth: btnxpuart: Implement host-wakeup feature
  dt-bindings: net: bluetooth: nxp: Add support for host-wakeup
  Bluetooth: btusb: Add RTL8851BE device 0x0bda:0xb850
  Bluetooth: hci_uart: Remove unnecessary NULL check before release_firmware()
  Bluetooth: btmtksdio: Fix wakeup source leaks on device unbind
  ...
====================

Link: https://patch.msgid.link/20250522171048.3307873-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-05-22 13:46:13 -07:00
commit 43a1ce8f42
27 changed files with 874 additions and 215 deletions

View File

@ -48,6 +48,18 @@ properties:
description:
The GPIO number of the NXP chipset used for BT_WAKE_IN.
interrupts:
maxItems: 1
description:
Host wakeup by falling edge interrupt on this pin which is
connected to BT_WAKE_OUT pin of the NXP chipset.
interrupt-names:
items:
- const: wakeup
wakeup-source: true
nxp,wakeout-pin:
$ref: /schemas/types.yaml#/definitions/uint8
description:
@ -61,6 +73,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
serial {
bluetooth {
compatible = "nxp,88w8987-bt";
@ -70,5 +83,9 @@ examples:
nxp,wakein-pin = /bits/ 8 <18>;
nxp,wakeout-pin = /bits/ 8 <19>;
local-bd-address = [66 55 44 33 22 11];
interrupt-parent = <&gpio>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "wakeup";
wakeup-source;
};
};

View File

@ -56,18 +56,6 @@ config BT_HCIBTUSB_POLL_SYNC
Say Y here to enable USB poll_sync for Bluetooth USB devices by
default.
config BT_HCIBTUSB_AUTO_ISOC_ALT
bool "Automatically adjust alternate setting for Isoc endpoints"
depends on BT_HCIBTUSB
default y if CHROME_PLATFORMS
help
Say Y here to automatically adjusting the alternate setting for
HCI_USER_CHANNEL whenever a SCO link is established.
When enabled, btusb intercepts the HCI_EV_SYNC_CONN_COMPLETE packets
and configures isoc endpoint alternate setting automatically when
HCI_USER_CHANNEL is in use.
config BT_HCIBTUSB_BCM
bool "Broadcom protocol support"
depends on BT_HCIBTUSB

View File

@ -2719,7 +2719,7 @@ static int btintel_uefi_get_dsbr(u32 *dsbr_var)
} __packed data;
efi_status_t status;
unsigned long data_size = 0;
unsigned long data_size = sizeof(data);
efi_guid_t guid = EFI_GUID(0xe65d8884, 0xd4af, 0x4b20, 0x8d, 0x03,
0x77, 0x2e, 0xcc, 0x3d, 0xa5, 0x31);
@ -2729,16 +2729,10 @@ static int btintel_uefi_get_dsbr(u32 *dsbr_var)
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return -EOPNOTSUPP;
status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size,
NULL);
if (status != EFI_BUFFER_TOO_SMALL || !data_size)
return -EIO;
status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size,
&data);
if (status != EFI_SUCCESS)
if (status != EFI_SUCCESS || data_size != sizeof(data))
return -ENXIO;
*dsbr_var = data.dsbr;
@ -3688,7 +3682,7 @@ int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
}
EXPORT_SYMBOL_GPL(btintel_configure_setup);
int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
struct intel_tlv *tlv = (void *)&skb->data[5];
@ -3716,7 +3710,6 @@ int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
recv_frame:
return hci_recv_frame(hdev, skb);
}
EXPORT_SYMBOL_GPL(btintel_diagnostics);
int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{

View File

@ -277,7 +277,6 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
int btintel_shutdown_combined(struct hci_dev *hdev);
void btintel_hw_error(struct hci_dev *hdev, u8 code);
void btintel_print_fseq_info(struct hci_dev *hdev);
int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@ -411,9 +410,4 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
static inline void btintel_print_fseq_info(struct hci_dev *hdev)
{
}
static inline int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
#endif

View File

@ -208,6 +208,96 @@ static void btintel_pcie_prepare_tx(struct txq *txq, u16 tfd_index,
memcpy(buf->data, skb->data, tfd->size);
}
static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev)
{
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
u16 cr_hia, cr_tia;
u32 reg, mbox_reg;
struct sk_buff *skb;
u8 buf[80];
skb = alloc_skb(1024, GFP_ATOMIC);
if (!skb)
return;
snprintf(buf, sizeof(buf), "%s", "---- Dump of debug registers ---");
bt_dev_dbg(hdev, "%s", buf);
skb_put_data(skb, buf, strlen(buf));
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
snprintf(buf, sizeof(buf), "boot stage: 0x%8.8x", reg);
bt_dev_dbg(hdev, "%s", buf);
skb_put_data(skb, buf, strlen(buf));
data->boot_stage_cache = reg;
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG);
snprintf(buf, sizeof(buf), "ipc status: 0x%8.8x", reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
snprintf(buf, sizeof(buf), "ipc control: 0x%8.8x", reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG);
snprintf(buf, sizeof(buf), "ipc sleep control: 0x%8.8x", reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
/*Read the Mail box status and registers*/
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MBOX_STATUS_REG);
snprintf(buf, sizeof(buf), "mbox status: 0x%8.8x", reg);
skb_put_data(skb, buf, strlen(buf));
if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1) {
mbox_reg = btintel_pcie_rd_reg32(data,
BTINTEL_PCIE_CSR_MBOX_1_REG);
snprintf(buf, sizeof(buf), "mbox_1: 0x%8.8x", mbox_reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
}
if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2) {
mbox_reg = btintel_pcie_rd_reg32(data,
BTINTEL_PCIE_CSR_MBOX_2_REG);
snprintf(buf, sizeof(buf), "mbox_2: 0x%8.8x", mbox_reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
}
if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3) {
mbox_reg = btintel_pcie_rd_reg32(data,
BTINTEL_PCIE_CSR_MBOX_3_REG);
snprintf(buf, sizeof(buf), "mbox_3: 0x%8.8x", mbox_reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
}
if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4) {
mbox_reg = btintel_pcie_rd_reg32(data,
BTINTEL_PCIE_CSR_MBOX_4_REG);
snprintf(buf, sizeof(buf), "mbox_4: 0x%8.8x", mbox_reg);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
}
cr_hia = data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM];
cr_tia = data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
snprintf(buf, sizeof(buf), "rxq: cr_tia: %u cr_hia: %u", cr_tia, cr_hia);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
cr_tia = data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM];
snprintf(buf, sizeof(buf), "txq: cr_tia: %u cr_hia: %u", cr_tia, cr_hia);
skb_put_data(skb, buf, strlen(buf));
bt_dev_dbg(hdev, "%s", buf);
snprintf(buf, sizeof(buf), "--------------------------------");
bt_dev_dbg(hdev, "%s", buf);
hci_recv_diag(hdev, skb);
}
static int btintel_pcie_send_sync(struct btintel_pcie_data *data,
struct sk_buff *skb)
{
@ -237,8 +327,11 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data,
/* Wait for the complete interrupt - URBD0 */
ret = wait_event_timeout(data->tx_wait_q, data->tx_wait_done,
msecs_to_jiffies(BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS));
if (!ret)
if (!ret) {
bt_dev_err(data->hdev, "tx completion timeout");
btintel_pcie_dump_debug_registers(data->hdev);
return -ETIME;
}
return 0;
}
@ -756,6 +849,26 @@ static int btintel_pcie_read_device_mem(struct btintel_pcie_data *data,
return 0;
}
static inline bool btintel_pcie_in_lockdown(struct btintel_pcie_data *data)
{
return (data->boot_stage_cache &
BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN) ||
(data->boot_stage_cache &
BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN);
}
static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data)
{
return (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR) ||
(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER);
}
static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
{
bt_dev_err(data->hdev, "Received gp1 mailbox interrupt");
btintel_pcie_dump_debug_registers(data->hdev);
}
/* This function handles the MSI-X interrupt for gp0 cause (bit 0 in
* BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES) which is sent for boot stage and image response.
*/
@ -779,6 +892,18 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
if (reg != data->img_resp_cache)
data->img_resp_cache = reg;
if (btintel_pcie_in_error(data)) {
bt_dev_err(data->hdev, "Controller in error state");
btintel_pcie_dump_debug_registers(data->hdev);
return;
}
if (btintel_pcie_in_lockdown(data)) {
bt_dev_err(data->hdev, "Controller in lockdown state");
btintel_pcie_dump_debug_registers(data->hdev);
return;
}
data->gp0_received = true;
old_ctxt = data->alive_intr_ctxt;
@ -889,7 +1014,6 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
static int btintel_pcie_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *)skb->data;
const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
@ -945,15 +1069,6 @@ static int btintel_pcie_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
}
}
/* Handle all diagnostics events separately. May still call
* hci_recv_frame.
*/
if (len >= sizeof(diagnostics_hdr) &&
memcmp(&skb->data[2], diagnostics_hdr,
sizeof(diagnostics_hdr)) == 0) {
return btintel_diagnostics(hdev, skb);
}
/* This is a debug event that comes from IML and OP image when it
* starts execution. There is no need pass this event to stack.
*/
@ -1343,6 +1458,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP)
btintel_pcie_msix_hw_exp_handler(data);
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1)
btintel_pcie_msix_gp1_handler(data);
/* This interrupt is triggered by the firmware after updating
* boot_stage register and image_response register
*/
@ -2028,6 +2146,7 @@ static int btintel_pcie_setup(struct hci_dev *hdev)
while ((err = btintel_pcie_setup_internal(hdev)) && fw_dl_retry++ < 1) {
bt_dev_err(hdev, "Firmware download retry count: %d",
fw_dl_retry);
btintel_pcie_dump_debug_registers(hdev);
err = btintel_pcie_reset_bt(data);
if (err) {
bt_dev_err(hdev, "Failed to do shr reset: %d", err);

View File

@ -12,10 +12,17 @@
#define BTINTEL_PCIE_CSR_HW_REV_REG (BTINTEL_PCIE_CSR_BASE + 0x028)
#define BTINTEL_PCIE_CSR_RF_ID_REG (BTINTEL_PCIE_CSR_BASE + 0x09C)
#define BTINTEL_PCIE_CSR_BOOT_STAGE_REG (BTINTEL_PCIE_CSR_BASE + 0x108)
#define BTINTEL_PCIE_CSR_IPC_CONTROL_REG (BTINTEL_PCIE_CSR_BASE + 0x10C)
#define BTINTEL_PCIE_CSR_IPC_STATUS_REG (BTINTEL_PCIE_CSR_BASE + 0x110)
#define BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG (BTINTEL_PCIE_CSR_BASE + 0x114)
#define BTINTEL_PCIE_CSR_CI_ADDR_LSB_REG (BTINTEL_PCIE_CSR_BASE + 0x118)
#define BTINTEL_PCIE_CSR_CI_ADDR_MSB_REG (BTINTEL_PCIE_CSR_BASE + 0x11C)
#define BTINTEL_PCIE_CSR_IMG_RESPONSE_REG (BTINTEL_PCIE_CSR_BASE + 0x12C)
#define BTINTEL_PCIE_CSR_MBOX_1_REG (BTINTEL_PCIE_CSR_BASE + 0x170)
#define BTINTEL_PCIE_CSR_MBOX_2_REG (BTINTEL_PCIE_CSR_BASE + 0x174)
#define BTINTEL_PCIE_CSR_MBOX_3_REG (BTINTEL_PCIE_CSR_BASE + 0x178)
#define BTINTEL_PCIE_CSR_MBOX_4_REG (BTINTEL_PCIE_CSR_BASE + 0x17C)
#define BTINTEL_PCIE_CSR_MBOX_STATUS_REG (BTINTEL_PCIE_CSR_BASE + 0x180)
#define BTINTEL_PCIE_PRPH_DEV_ADDR_REG (BTINTEL_PCIE_CSR_BASE + 0x440)
#define BTINTEL_PCIE_PRPH_DEV_RD_REG (BTINTEL_PCIE_CSR_BASE + 0x458)
#define BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR (BTINTEL_PCIE_CSR_BASE + 0x460)
@ -41,6 +48,9 @@
#define BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW (BIT(2))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN (BIT(10))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN (BIT(11))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR (BIT(12))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER (BIT(13))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ALIVE (BIT(23))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY (BIT(24))
@ -89,6 +99,7 @@ enum msix_fh_int_causes {
/* Causes for the HW register interrupts */
enum msix_hw_int_causes {
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */
};
@ -121,6 +132,14 @@ enum btintel_pcie_tlv_type {
BTINTEL_FW_BUILD,
};
/* causes for the MBOX interrupts */
enum msix_mbox_int_causes {
BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1 = BIT(0), /* cause MBOX1 */
BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2 = BIT(1), /* cause MBOX2 */
BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3 = BIT(2), /* cause MBOX3 */
BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */
};
#define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7)
/* Minimum and Maximum number of MSI-X Vector

View File

@ -100,7 +100,9 @@ static int btmrvl_sdio_probe_of(struct device *dev,
}
/* Configure wakeup (enabled by default) */
device_init_wakeup(dev, true);
ret = devm_device_init_wakeup(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to init wakeup\n");
}
}

View File

@ -1414,7 +1414,7 @@ static int btmtksdio_probe(struct sdio_func *func,
*/
pm_runtime_put_noidle(bdev->dev);
err = device_init_wakeup(bdev->dev, true);
err = devm_device_init_wakeup(bdev->dev);
if (err)
bt_dev_err(hdev, "failed to initialize device wakeup");

View File

@ -17,6 +17,7 @@
#include <linux/crc32.h>
#include <linux/string_helpers.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@ -143,7 +144,9 @@ struct ps_data {
bool driver_sent_cmd;
u16 h2c_ps_interval;
u16 c2h_ps_interval;
bool wakeup_source;
struct gpio_desc *h2c_ps_gpio;
s32 irq_handler;
struct hci_dev *hdev;
struct work_struct work;
struct timer_list ps_timer;
@ -476,12 +479,21 @@ static void ps_timeout_func(struct timer_list *t)
}
}
static irqreturn_t ps_host_wakeup_irq_handler(int irq, void *priv)
{
struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)priv;
bt_dev_dbg(nxpdev->hdev, "Host wakeup interrupt");
return IRQ_HANDLED;
}
static int ps_setup(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct serdev_device *serdev = nxpdev->serdev;
struct ps_data *psdata = &nxpdev->psdata;
int ret;
/* Out-Of-Band Device Wakeup */
psdata->h2c_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "device-wakeup",
GPIOD_OUT_LOW);
if (IS_ERR(psdata->h2c_ps_gpio)) {
@ -493,11 +505,37 @@ static int ps_setup(struct hci_dev *hdev)
if (device_property_read_u8(&serdev->dev, "nxp,wakein-pin", &psdata->h2c_wakeup_gpio)) {
psdata->h2c_wakeup_gpio = 0xff; /* 0xff: use default pin/gpio */
} else if (!psdata->h2c_ps_gpio) {
bt_dev_warn(hdev, "nxp,wakein-pin property without device-wakeup GPIO");
bt_dev_warn(hdev, "nxp,wakein-pin property without device-wakeup-gpios");
psdata->h2c_wakeup_gpio = 0xff;
}
device_property_read_u8(&serdev->dev, "nxp,wakeout-pin", &psdata->c2h_wakeup_gpio);
/* Out-Of-Band Host Wakeup */
if (of_property_read_bool(serdev->dev.of_node, "wakeup-source")) {
psdata->irq_handler = of_irq_get_byname(serdev->dev.of_node, "wakeup");
bt_dev_info(nxpdev->hdev, "irq_handler: %d", psdata->irq_handler);
if (psdata->irq_handler > 0)
psdata->wakeup_source = true;
}
if (device_property_read_u8(&serdev->dev, "nxp,wakeout-pin", &psdata->c2h_wakeup_gpio)) {
psdata->c2h_wakeup_gpio = 0xff;
if (psdata->wakeup_source) {
bt_dev_warn(hdev, "host wakeup interrupt without nxp,wakeout-pin");
psdata->wakeup_source = false;
}
} else if (!psdata->wakeup_source) {
bt_dev_warn(hdev, "nxp,wakeout-pin property without host wakeup interrupt");
psdata->c2h_wakeup_gpio = 0xff;
}
if (psdata->wakeup_source) {
ret = devm_request_irq(&serdev->dev, psdata->irq_handler,
ps_host_wakeup_irq_handler,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
dev_name(&serdev->dev), nxpdev);
disable_irq(psdata->irq_handler);
device_init_wakeup(&serdev->dev, true);
}
psdata->hdev = hdev;
INIT_WORK(&psdata->work, ps_work_func);
@ -637,12 +675,10 @@ static void ps_init(struct hci_dev *hdev)
psdata->ps_state = PS_STATE_AWAKE;
if (psdata->c2h_wakeup_gpio) {
if (psdata->c2h_wakeup_gpio != 0xff)
psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_GPIO;
} else {
else
psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
psdata->c2h_wakeup_gpio = 0xff;
}
psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
if (psdata->h2c_ps_gpio)
@ -1821,6 +1857,11 @@ static int nxp_serdev_suspend(struct device *dev)
struct ps_data *psdata = &nxpdev->psdata;
ps_control(psdata->hdev, PS_STATE_SLEEP);
if (psdata->wakeup_source) {
enable_irq_wake(psdata->irq_handler);
enable_irq(psdata->irq_handler);
}
return 0;
}
@ -1829,6 +1870,11 @@ static int nxp_serdev_resume(struct device *dev)
struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
struct ps_data *psdata = &nxpdev->psdata;
if (psdata->wakeup_source) {
disable_irq(psdata->irq_handler);
disable_irq_wake(psdata->irq_handler);
}
ps_control(psdata->hdev, PS_STATE_AWAKE);
return 0;
}

View File

@ -21,6 +21,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_drv.h>
#include "btintel.h"
#include "btbcm.h"
@ -34,7 +35,6 @@ static bool force_scofix;
static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
static bool enable_poll_sync = IS_ENABLED(CONFIG_BT_HCIBTUSB_POLL_SYNC);
static bool reset = true;
static bool auto_isoc_alt = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTO_ISOC_ALT);
static struct usb_driver btusb_driver;
@ -513,6 +513,7 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
/* Realtek 8851BE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0xb850), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3600), .driver_info = BTUSB_REALTEK },
/* Realtek 8852AE Bluetooth devices */
@ -678,6 +679,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3584), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3605), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3607), .driver_info = BTUSB_MEDIATEK |
@ -716,8 +719,12 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3608), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3628), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3630), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8723AE Bluetooth devices */
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
@ -1118,42 +1125,6 @@ static inline void btusb_free_frags(struct btusb_data *data)
spin_unlock_irqrestore(&data->rxlock, flags);
}
static void btusb_sco_connected(struct btusb_data *data, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
struct hci_ev_sync_conn_complete *ev =
(void *) skb->data + sizeof(*hdr);
struct hci_dev *hdev = data->hdev;
unsigned int notify_air_mode;
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT)
return;
if (skb->len < sizeof(*hdr) || hdr->evt != HCI_EV_SYNC_CONN_COMPLETE)
return;
if (skb->len != sizeof(*hdr) + sizeof(*ev) || ev->status)
return;
switch (ev->air_mode) {
case BT_CODEC_CVSD:
notify_air_mode = HCI_NOTIFY_ENABLE_SCO_CVSD;
break;
case BT_CODEC_TRANSPARENT:
notify_air_mode = HCI_NOTIFY_ENABLE_SCO_TRANSP;
break;
default:
return;
}
bt_dev_info(hdev, "enabling SCO with air mode %u", ev->air_mode);
data->sco_num = 1;
data->air_mode = notify_air_mode;
schedule_work(&data->work);
}
static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
{
if (data->intr_interval) {
@ -1161,10 +1132,6 @@ static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
schedule_delayed_work(&data->rx_work, 0);
}
/* Configure altsetting for HCI_USER_CHANNEL on SCO connected */
if (auto_isoc_alt && hci_dev_test_flag(data->hdev, HCI_USER_CHANNEL))
btusb_sco_connected(data, skb);
return data->recv_event(data->hdev, skb);
}
@ -3753,31 +3720,133 @@ static const struct file_operations force_poll_sync_fops = {
.llseek = default_llseek,
};
static ssize_t isoc_alt_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct btusb_data *data = dev_get_drvdata(dev);
#define BTUSB_HCI_DRV_OP_SUPPORTED_ALTSETTINGS \
hci_opcode_pack(HCI_DRV_OGF_DRIVER_SPECIFIC, 0x0000)
#define BTUSB_HCI_DRV_SUPPORTED_ALTSETTINGS_SIZE 0
struct btusb_hci_drv_rp_supported_altsettings {
__u8 num;
__u8 altsettings[];
} __packed;
return sysfs_emit(buf, "%d\n", data->isoc_altsetting);
#define BTUSB_HCI_DRV_OP_SWITCH_ALTSETTING \
hci_opcode_pack(HCI_DRV_OGF_DRIVER_SPECIFIC, 0x0001)
#define BTUSB_HCI_DRV_SWITCH_ALTSETTING_SIZE 1
struct btusb_hci_drv_cmd_switch_altsetting {
__u8 altsetting;
} __packed;
static const struct {
u16 opcode;
const char *desc;
} btusb_hci_drv_supported_commands[] = {
/* Common commands */
{ HCI_DRV_OP_READ_INFO, "Read Info" },
/* Driver specific commands */
{ BTUSB_HCI_DRV_OP_SUPPORTED_ALTSETTINGS, "Supported Altsettings" },
{ BTUSB_HCI_DRV_OP_SWITCH_ALTSETTING, "Switch Altsetting" },
};
static int btusb_hci_drv_read_info(struct hci_dev *hdev, void *data,
u16 data_len)
{
struct hci_drv_rp_read_info *rp;
size_t rp_size;
int err, i;
u16 opcode, num_supported_commands =
ARRAY_SIZE(btusb_hci_drv_supported_commands);
rp_size = sizeof(*rp) + num_supported_commands * 2;
rp = kmalloc(rp_size, GFP_KERNEL);
if (!rp)
return -ENOMEM;
strscpy_pad(rp->driver_name, btusb_driver.name);
rp->num_supported_commands = cpu_to_le16(num_supported_commands);
for (i = 0; i < num_supported_commands; i++) {
opcode = btusb_hci_drv_supported_commands[i].opcode;
bt_dev_info(hdev,
"Supported HCI Drv command (0x%02x|0x%04x): %s",
hci_opcode_ogf(opcode),
hci_opcode_ocf(opcode),
btusb_hci_drv_supported_commands[i].desc);
rp->supported_commands[i] = cpu_to_le16(opcode);
}
err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
HCI_DRV_STATUS_SUCCESS, rp, rp_size);
kfree(rp);
return err;
}
static ssize_t isoc_alt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
static int btusb_hci_drv_supported_altsettings(struct hci_dev *hdev, void *data,
u16 data_len)
{
struct btusb_data *data = dev_get_drvdata(dev);
int alt;
int ret;
struct btusb_data *drvdata = hci_get_drvdata(hdev);
struct btusb_hci_drv_rp_supported_altsettings *rp;
size_t rp_size;
int err;
u8 i;
if (kstrtoint(buf, 10, &alt))
return -EINVAL;
/* There are at most 7 alt (0 - 6) */
rp = kmalloc(sizeof(*rp) + 7, GFP_KERNEL);
ret = btusb_switch_alt_setting(data->hdev, alt);
return ret < 0 ? ret : count;
rp->num = 0;
if (!drvdata->isoc)
goto done;
for (i = 0; i <= 6; i++) {
if (btusb_find_altsetting(drvdata, i))
rp->altsettings[rp->num++] = i;
}
done:
rp_size = sizeof(*rp) + rp->num;
err = hci_drv_cmd_complete(hdev, BTUSB_HCI_DRV_OP_SUPPORTED_ALTSETTINGS,
HCI_DRV_STATUS_SUCCESS, rp, rp_size);
kfree(rp);
return err;
}
static DEVICE_ATTR_RW(isoc_alt);
static int btusb_hci_drv_switch_altsetting(struct hci_dev *hdev, void *data,
u16 data_len)
{
struct btusb_hci_drv_cmd_switch_altsetting *cmd = data;
u8 status;
if (cmd->altsetting > 6) {
status = HCI_DRV_STATUS_INVALID_PARAMETERS;
} else {
if (btusb_switch_alt_setting(hdev, cmd->altsetting))
status = HCI_DRV_STATUS_UNSPECIFIED_ERROR;
else
status = HCI_DRV_STATUS_SUCCESS;
}
return hci_drv_cmd_status(hdev, BTUSB_HCI_DRV_OP_SWITCH_ALTSETTING,
status);
}
static const struct hci_drv_handler btusb_hci_drv_common_handlers[] = {
{ btusb_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE },
};
static const struct hci_drv_handler btusb_hci_drv_specific_handlers[] = {
{ btusb_hci_drv_supported_altsettings,
BTUSB_HCI_DRV_SUPPORTED_ALTSETTINGS_SIZE },
{ btusb_hci_drv_switch_altsetting,
BTUSB_HCI_DRV_SWITCH_ALTSETTING_SIZE },
};
static struct hci_drv btusb_hci_drv = {
.common_handler_count = ARRAY_SIZE(btusb_hci_drv_common_handlers),
.common_handlers = btusb_hci_drv_common_handlers,
.specific_handler_count = ARRAY_SIZE(btusb_hci_drv_specific_handlers),
.specific_handlers = btusb_hci_drv_specific_handlers,
};
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@ -3918,12 +3987,13 @@ static int btusb_probe(struct usb_interface *intf,
data->reset_gpio = reset_gpio;
}
hdev->open = btusb_open;
hdev->close = btusb_close;
hdev->flush = btusb_flush;
hdev->send = btusb_send_frame;
hdev->notify = btusb_notify;
hdev->wakeup = btusb_wakeup;
hdev->open = btusb_open;
hdev->close = btusb_close;
hdev->flush = btusb_flush;
hdev->send = btusb_send_frame;
hdev->notify = btusb_notify;
hdev->wakeup = btusb_wakeup;
hdev->hci_drv = &btusb_hci_drv;
#ifdef CONFIG_PM
err = btusb_config_oob_wake(hdev);
@ -4142,10 +4212,6 @@ static int btusb_probe(struct usb_interface *intf,
data->isoc, data);
if (err < 0)
goto out_free_dev;
err = device_create_file(&intf->dev, &dev_attr_isoc_alt);
if (err)
goto out_free_dev;
}
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) {
@ -4192,10 +4258,8 @@ static void btusb_disconnect(struct usb_interface *intf)
hdev = data->hdev;
usb_set_intfdata(data->intf, NULL);
if (data->isoc) {
device_remove_file(&intf->dev, &dev_attr_isoc_alt);
if (data->isoc)
usb_set_intfdata(data->isoc, NULL);
}
if (data->diag)
usb_set_intfdata(data->diag, NULL);

View File

@ -313,8 +313,7 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name)
goto exit;
exit:
if (firmware)
release_firmware(firmware);
release_firmware(firmware);
return ret;
}

View File

@ -29,6 +29,7 @@
#include <linux/poll.h>
#include <net/sock.h>
#include <linux/seq_file.h>
#include <linux/ethtool.h>
#define BT_SUBSYS_VERSION 2
#define BT_SUBSYS_REVISION 22
@ -448,6 +449,9 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
hci_req_complete_t *req_complete,
hci_req_complete_skb_t *req_complete_skb);
int hci_ethtool_ts_info(unsigned int index, int sk_proto,
struct kernel_ethtool_ts_info *ts_info);
#define HCI_REQ_START BIT(0)
#define HCI_REQ_SKB BIT(1)

View File

@ -494,6 +494,7 @@ enum {
#define HCI_EVENT_PKT 0x04
#define HCI_ISODATA_PKT 0x05
#define HCI_DIAG_PKT 0xf0
#define HCI_DRV_PKT 0xf1
#define HCI_VENDOR_PKT 0xff
/* HCI packet types */
@ -557,7 +558,8 @@ enum {
#define ESCO_LINK 0x02
/* Low Energy links do not have defined link type. Use invented one */
#define LE_LINK 0x80
#define ISO_LINK 0x82
#define CIS_LINK 0x82
#define BIS_LINK 0x83
#define INVALID_LINK 0xff
/* LMP features */

View File

@ -31,6 +31,7 @@
#include <linux/rculist.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_drv.h>
#include <net/bluetooth/hci_sync.h>
#include <net/bluetooth/hci_sock.h>
#include <net/bluetooth/coredump.h>
@ -613,6 +614,8 @@ struct hci_dev {
struct list_head monitored_devices;
bool advmon_pend_notify;
struct hci_drv *hci_drv;
#if IS_ENABLED(CONFIG_BT_LEDS)
struct led_trigger *power_led;
#endif
@ -996,7 +999,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
case ESCO_LINK:
h->sco_num++;
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
h->iso_num++;
break;
}
@ -1022,7 +1026,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
case ESCO_LINK:
h->sco_num--;
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
h->iso_num--;
break;
}
@ -1039,7 +1044,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
case SCO_LINK:
case ESCO_LINK:
return h->sco_num;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
return h->iso_num;
default:
return 0;
@ -1100,7 +1106,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
if (bacmp(&c->dst, ba) || c->type != BIS_LINK)
continue;
if (c->iso_qos.bcast.bis == bis) {
@ -1122,7 +1128,7 @@ hci_conn_hash_lookup_create_pa_sync(struct hci_dev *hdev)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK)
if (c->type != BIS_LINK)
continue;
if (!test_bit(HCI_CONN_CREATE_PA_SYNC, &c->flags))
@ -1148,8 +1154,8 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (bacmp(&c->dst, ba) || c->type != ISO_LINK ||
!test_bit(HCI_CONN_PER_ADV, &c->flags))
if (bacmp(&c->dst, ba) || c->type != BIS_LINK ||
!test_bit(HCI_CONN_PER_ADV, &c->flags))
continue;
if (c->iso_qos.bcast.big == big &&
@ -1238,7 +1244,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
if (c->type != CIS_LINK)
continue;
/* Match CIG ID if set */
@ -1270,7 +1276,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
if (c->type != CIS_LINK)
continue;
if (handle == c->iso_qos.ucast.cig) {
@ -1293,17 +1299,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK)
continue;
/* An ISO_LINK hcon with BDADDR_ANY as destination
* address is a Broadcast connection. A Broadcast
* slave connection is associated with a PA train,
* so the sync_handle can be used to differentiate
* from unicast.
*/
if (bacmp(&c->dst, BDADDR_ANY) &&
c->sync_handle == HCI_SYNC_HANDLE_INVALID)
if (c->type != BIS_LINK)
continue;
if (handle == c->iso_qos.bcast.big) {
@ -1327,7 +1323,7 @@ hci_conn_hash_lookup_big_sync_pend(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK)
if (c->type != BIS_LINK)
continue;
if (handle == c->iso_qos.bcast.big && num_bis == c->num_bis) {
@ -1350,8 +1346,8 @@ hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK ||
c->state != state)
if (c->type != BIS_LINK || bacmp(&c->dst, BDADDR_ANY) ||
c->state != state)
continue;
if (handle == c->iso_qos.bcast.big) {
@ -1374,8 +1370,8 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK ||
!test_bit(HCI_CONN_PA_SYNC, &c->flags))
if (c->type != BIS_LINK ||
!test_bit(HCI_CONN_PA_SYNC, &c->flags))
continue;
if (c->iso_qos.bcast.big == big) {
@ -1397,7 +1393,7 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK)
if (c->type != BIS_LINK)
continue;
/* Ignore the listen hcon, we are looking
@ -2012,7 +2008,8 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
case ESCO_LINK:
return sco_connect_ind(hdev, bdaddr, flags);
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
return iso_connect_ind(hdev, bdaddr, flags);
default:

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2025 Google Corporation
*/
#ifndef __HCI_DRV_H
#define __HCI_DRV_H
#include <linux/types.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci.h>
struct hci_drv_cmd_hdr {
__le16 opcode;
__le16 len;
} __packed;
struct hci_drv_ev_hdr {
__le16 opcode;
__le16 len;
} __packed;
#define HCI_DRV_EV_CMD_STATUS 0x0000
struct hci_drv_ev_cmd_status {
__le16 opcode;
__u8 status;
} __packed;
#define HCI_DRV_EV_CMD_COMPLETE 0x0001
struct hci_drv_ev_cmd_complete {
__le16 opcode;
__u8 status;
__u8 data[];
} __packed;
#define HCI_DRV_STATUS_SUCCESS 0x00
#define HCI_DRV_STATUS_UNSPECIFIED_ERROR 0x01
#define HCI_DRV_STATUS_UNKNOWN_COMMAND 0x02
#define HCI_DRV_STATUS_INVALID_PARAMETERS 0x03
#define HCI_DRV_MAX_DRIVER_NAME_LENGTH 32
/* Common commands that make sense on all drivers start from 0x0000 */
#define HCI_DRV_OP_READ_INFO 0x0000
#define HCI_DRV_READ_INFO_SIZE 0
struct hci_drv_rp_read_info {
__u8 driver_name[HCI_DRV_MAX_DRIVER_NAME_LENGTH];
__le16 num_supported_commands;
__le16 supported_commands[];
} __packed;
/* Driver specific OGF (Opcode Group Field)
* Commands in this group may have different meanings across different drivers.
*/
#define HCI_DRV_OGF_DRIVER_SPECIFIC 0x01
int hci_drv_cmd_status(struct hci_dev *hdev, u16 cmd, u8 status);
int hci_drv_cmd_complete(struct hci_dev *hdev, u16 cmd, u8 status, void *rp,
size_t rp_len);
int hci_drv_process_cmd(struct hci_dev *hdev, struct sk_buff *cmd_skb);
struct hci_drv_handler {
int (*func)(struct hci_dev *hdev, void *data, u16 data_len);
size_t data_len;
};
struct hci_drv {
size_t common_handler_count;
const struct hci_drv_handler *common_handlers;
size_t specific_handler_count;
const struct hci_drv_handler *specific_handlers;
};
#endif /* __HCI_DRV_H */

View File

@ -51,6 +51,8 @@ struct hci_mon_hdr {
#define HCI_MON_CTRL_EVENT 17
#define HCI_MON_ISO_TX_PKT 18
#define HCI_MON_ISO_RX_PKT 19
#define HCI_MON_DRV_TX_PKT 20
#define HCI_MON_DRV_RX_PKT 21
struct hci_mon_new_index {
__u8 type;

View File

@ -14,7 +14,8 @@ bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
ecdh_helper.o mgmt_util.o mgmt_config.o hci_codec.o eir.o hci_sync.o
ecdh_helper.o mgmt_util.o mgmt_config.o hci_codec.o eir.o hci_sync.o \
hci_drv.o
bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o

View File

@ -34,6 +34,9 @@
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include "leds.h"
#include "selftest.h"
@ -563,6 +566,86 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock,
}
EXPORT_SYMBOL(bt_sock_poll);
static int bt_ethtool_get_ts_info(struct sock *sk, unsigned int index,
void __user *useraddr)
{
struct ethtool_ts_info info;
struct kernel_ethtool_ts_info ts_info = {};
int ret;
ret = hci_ethtool_ts_info(index, sk->sk_protocol, &ts_info);
if (ret == -ENODEV)
return ret;
else if (ret < 0)
return -EIO;
memset(&info, 0, sizeof(info));
info.cmd = ETHTOOL_GET_TS_INFO;
info.so_timestamping = ts_info.so_timestamping;
info.phc_index = ts_info.phc_index;
info.tx_types = ts_info.tx_types;
info.rx_filters = ts_info.rx_filters;
if (copy_to_user(useraddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int bt_ethtool(struct sock *sk, const struct ifreq *ifr,
void __user *useraddr)
{
unsigned int index;
u32 ethcmd;
int n;
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
if (sscanf(ifr->ifr_name, "hci%u%n", &index, &n) != 1 ||
n != strlen(ifr->ifr_name))
return -ENODEV;
switch (ethcmd) {
case ETHTOOL_GET_TS_INFO:
return bt_ethtool_get_ts_info(sk, index, useraddr);
}
return -EOPNOTSUPP;
}
static int bt_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg)
{
struct sock *sk = sock->sk;
struct ifreq ifr = {};
void __user *data;
char *colon;
int ret = -ENOIOCTLCMD;
if (get_user_ifreq(&ifr, &data, arg))
return -EFAULT;
ifr.ifr_name[IFNAMSIZ - 1] = 0;
colon = strchr(ifr.ifr_name, ':');
if (colon)
*colon = 0;
switch (cmd) {
case SIOCETHTOOL:
ret = bt_ethtool(sk, &ifr, data);
break;
}
if (colon)
*colon = ':';
if (put_user_ifreq(&ifr, arg))
return -EFAULT;
return ret;
}
int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
@ -595,6 +678,10 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
err = put_user(amount, (int __user *)arg);
break;
case SIOCETHTOOL:
err = bt_dev_ioctl(sock, cmd, (void __user *)arg);
break;
default:
err = -ENOIOCTLCMD;
break;

View File

@ -785,7 +785,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *c
d->sync_handle = conn->sync_handle;
if (test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, ISO_LINK,
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
HCI_CONN_PA_SYNC, d);
if (!d->count)
@ -795,7 +795,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *c
}
if (test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, ISO_LINK,
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
HCI_CONN_BIG_SYNC, d);
if (!d->count)
@ -885,9 +885,11 @@ static void cis_cleanup(struct hci_conn *conn)
/* Check if ISO connection is a CIS and remove CIG if there are
* no other connections using it.
*/
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d);
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d);
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_BOUND, &d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECT,
&d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECTED,
&d);
if (d.count)
return;
@ -910,7 +912,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
if (!hdev->acl_mtu)
return ERR_PTR(-ECONNREFUSED);
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
if (hdev->iso_mtu)
/* Dedicated ISO Buffer exists */
break;
@ -974,7 +977,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
/* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
@ -1071,7 +1075,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
if (HCI_CONN_HANDLE_UNSET(conn->handle))
hci_conn_failed(conn, reason);
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
if ((conn->state != BT_CONNECTED &&
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
@ -1146,7 +1151,8 @@ void hci_conn_del(struct hci_conn *conn)
hdev->acl_cnt += conn->sent;
} else {
/* Unacked ISO frames */
if (conn->type == ISO_LINK) {
if (conn->type == CIS_LINK ||
conn->type == BIS_LINK) {
if (hdev->iso_pkts)
hdev->iso_cnt += conn->sent;
else if (hdev->le_pkts)
@ -1532,7 +1538,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
memcmp(conn->le_per_adv_data, base, base_len)))
return ERR_PTR(-EADDRINUSE);
conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
@ -1740,7 +1746,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
data.count = 0;
/* Create a BIS for each bound connection */
hci_conn_hash_list_state(hdev, bis_list, ISO_LINK,
hci_conn_hash_list_state(hdev, bis_list, BIS_LINK,
BT_BOUND, &data);
cp.handle = qos->bcast.big;
@ -1829,12 +1835,12 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
for (data.cig = 0x00; data.cig < 0xf0; data.cig++) {
data.count = 0;
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECT, &data);
if (data.count)
continue;
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECTED, &data);
if (!data.count)
break;
@ -1884,7 +1890,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig,
qos->ucast.cis);
if (!cis) {
cis = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
cis = hci_conn_add_unset(hdev, CIS_LINK, dst,
HCI_ROLE_MASTER);
if (IS_ERR(cis))
return cis;
cis->cleanup = cis_cleanup;
@ -1976,7 +1983,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)
int hci_conn_check_create_cis(struct hci_conn *conn)
{
if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY))
if (conn->type != CIS_LINK)
return -EINVAL;
if (!conn->parent || conn->parent->state != BT_CONNECTED ||
@ -2070,7 +2077,9 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
{
struct hci_conn *conn;
conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_SLAVE);
bt_dev_dbg(hdev, "dst %pMR type %d sid %d", dst, dst_type, sid);
conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_SLAVE);
if (IS_ERR(conn))
return conn;
@ -2219,7 +2228,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
* the start periodic advertising and create BIG commands have
* been queued
*/
hci_conn_hash_list_state(hdev, bis_mark_per_adv, ISO_LINK,
hci_conn_hash_list_state(hdev, bis_mark_per_adv, BIS_LINK,
BT_BOUND, &data);
/* Queue start periodic advertising and create BIG */
@ -2951,7 +2960,8 @@ void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb)
* TODO: SCO support without flowctl (needs to be done in drivers)
*/
switch (conn->type) {
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
case ACL_LINK:
case LE_LINK:
break;
@ -3047,3 +3057,36 @@ u8 *hci_conn_key_enc_size(struct hci_conn *conn)
return NULL;
}
int hci_ethtool_ts_info(unsigned int index, int sk_proto,
struct kernel_ethtool_ts_info *info)
{
struct hci_dev *hdev;
hdev = hci_dev_get(index);
if (!hdev)
return -ENODEV;
info->so_timestamping =
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
info->phc_index = -1;
info->tx_types = BIT(HWTSTAMP_TX_OFF);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
switch (sk_proto) {
case BTPROTO_ISO:
case BTPROTO_L2CAP:
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
break;
case BTPROTO_SCO:
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
if (hci_dev_test_flag(hdev, HCI_SCO_FLOWCTL))
info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
break;
}
hci_dev_put(hdev);
return 0;
}

View File

@ -2898,12 +2898,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_ACLDATA_PKT:
/* Detect if ISO packet has been sent as ACL */
if (hci_conn_num(hdev, ISO_LINK)) {
if (hci_conn_num(hdev, CIS_LINK) ||
hci_conn_num(hdev, BIS_LINK)) {
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
__u8 type;
type = hci_conn_lookup_type(hdev, hci_handle(handle));
if (type == ISO_LINK)
if (type == CIS_LINK || type == BIS_LINK)
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
}
break;
@ -2911,6 +2912,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_ISODATA_PKT:
break;
case HCI_DRV_PKT:
break;
default:
kfree_skb(skb);
return -EINVAL;
@ -3019,6 +3022,15 @@ static int hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return -EINVAL;
}
if (hci_skb_pkt_type(skb) == HCI_DRV_PKT) {
/* Intercept HCI Drv packet here and don't go with hdev->send
* callback.
*/
err = hci_drv_process_cmd(hdev, skb);
kfree_skb(skb);
return err;
}
err = hdev->send(hdev, skb);
if (err < 0) {
bt_dev_err(hdev, "sending frame failed (%d)", err);
@ -3345,7 +3357,8 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
case LE_LINK:
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
cnt = hdev->iso_mtu ? hdev->iso_cnt :
hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
@ -3359,7 +3372,7 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
}
static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
int *quote)
__u8 type2, int *quote)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL, *c;
@ -3371,7 +3384,8 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != type || skb_queue_empty(&c->data_q))
if ((c->type != type && c->type != type2) ||
skb_queue_empty(&c->data_q))
continue;
if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
@ -3579,7 +3593,7 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
else
cnt = &hdev->sco_cnt;
while (*cnt && (conn = hci_low_sent(hdev, type, &quote))) {
while (*cnt && (conn = hci_low_sent(hdev, type, type, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_conn_frame(hdev, conn, skb);
@ -3707,12 +3721,14 @@ static void hci_sched_iso(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
if (!hci_conn_num(hdev, ISO_LINK))
if (!hci_conn_num(hdev, CIS_LINK) &&
!hci_conn_num(hdev, BIS_LINK))
return;
cnt = hdev->iso_pkts ? &hdev->iso_cnt :
hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, &quote))) {
while (*cnt && (conn = hci_low_sent(hdev, CIS_LINK, BIS_LINK,
&quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_conn_frame(hdev, conn, skb);
@ -4057,10 +4073,13 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
return;
}
err = hci_send_frame(hdev, skb);
if (err < 0) {
hci_cmd_sync_cancel_sync(hdev, -err);
return;
if (hci_skb_opcode(skb) != HCI_OP_NOP) {
err = hci_send_frame(hdev, skb);
if (err < 0) {
hci_cmd_sync_cancel_sync(hdev, -err);
return;
}
atomic_dec(&hdev->cmd_cnt);
}
if (hdev->req_status == HCI_REQ_PEND &&
@ -4068,8 +4087,6 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(hdev->req_skb);
hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
}
atomic_dec(&hdev->cmd_cnt);
}
static void hci_cmd_work(struct work_struct *work)

105
net/bluetooth/hci_drv.c Normal file
View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 Google Corporation
*/
#include <linux/skbuff.h>
#include <linux/types.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_drv.h>
int hci_drv_cmd_status(struct hci_dev *hdev, u16 cmd, u8 status)
{
struct hci_drv_ev_hdr *hdr;
struct hci_drv_ev_cmd_status *ev;
struct sk_buff *skb;
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = skb_put(skb, sizeof(*hdr));
hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_STATUS);
hdr->len = __cpu_to_le16(sizeof(*ev));
ev = skb_put(skb, sizeof(*ev));
ev->opcode = __cpu_to_le16(cmd);
ev->status = status;
hci_skb_pkt_type(skb) = HCI_DRV_PKT;
return hci_recv_frame(hdev, skb);
}
EXPORT_SYMBOL(hci_drv_cmd_status);
int hci_drv_cmd_complete(struct hci_dev *hdev, u16 cmd, u8 status, void *rp,
size_t rp_len)
{
struct hci_drv_ev_hdr *hdr;
struct hci_drv_ev_cmd_complete *ev;
struct sk_buff *skb;
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = skb_put(skb, sizeof(*hdr));
hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_COMPLETE);
hdr->len = __cpu_to_le16(sizeof(*ev) + rp_len);
ev = skb_put(skb, sizeof(*ev));
ev->opcode = __cpu_to_le16(cmd);
ev->status = status;
skb_put_data(skb, rp, rp_len);
hci_skb_pkt_type(skb) = HCI_DRV_PKT;
return hci_recv_frame(hdev, skb);
}
EXPORT_SYMBOL(hci_drv_cmd_complete);
int hci_drv_process_cmd(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_drv_cmd_hdr *hdr;
const struct hci_drv_handler *handler = NULL;
u16 opcode, len, ogf, ocf;
hdr = skb_pull_data(skb, sizeof(*hdr));
if (!hdr)
return -EILSEQ;
opcode = __le16_to_cpu(hdr->opcode);
len = __le16_to_cpu(hdr->len);
if (len != skb->len)
return -EILSEQ;
ogf = hci_opcode_ogf(opcode);
ocf = hci_opcode_ocf(opcode);
if (!hdev->hci_drv)
return hci_drv_cmd_status(hdev, opcode,
HCI_DRV_STATUS_UNKNOWN_COMMAND);
if (ogf != HCI_DRV_OGF_DRIVER_SPECIFIC) {
if (opcode < hdev->hci_drv->common_handler_count)
handler = &hdev->hci_drv->common_handlers[opcode];
} else {
if (ocf < hdev->hci_drv->specific_handler_count)
handler = &hdev->hci_drv->specific_handlers[ocf];
}
if (!handler || !handler->func)
return hci_drv_cmd_status(hdev, opcode,
HCI_DRV_STATUS_UNKNOWN_COMMAND);
if (len != handler->data_len)
return hci_drv_cmd_status(hdev, opcode,
HCI_DRV_STATUS_INVALID_PARAMETERS);
return handler->func(hdev, skb->data, len);
}
EXPORT_SYMBOL(hci_drv_process_cmd);

View File

@ -3804,7 +3804,7 @@ static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)
lockdep_assert_held(&hdev->lock);
list_for_each_entry_safe(conn, tmp, &hdev->conn_hash.list, list) {
if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY) ||
if (conn->type != CIS_LINK ||
conn->state == BT_OPEN || conn->iso_qos.ucast.cig != cig)
continue;
@ -4467,7 +4467,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
break;
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
if (hdev->iso_pkts) {
hdev->iso_cnt += count;
if (hdev->iso_cnt > hdev->iso_pkts)
@ -6351,6 +6352,17 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
info->secondary_phy &= 0x1f;
}
/* Check if PA Sync is pending and if the hci_conn SID has not
* been set update it.
*/
if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
struct hci_conn *conn;
conn = hci_conn_hash_lookup_create_pa_sync(hdev);
if (conn && conn->sid == HCI_SID_INVALID)
conn->sid = info->sid;
}
if (legacy_evt_type != LE_ADV_INVALID) {
process_adv_report(hdev, legacy_evt_type, &info->bdaddr,
info->bdaddr_type, NULL, 0,
@ -6402,7 +6414,8 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
conn->sync_handle = le16_to_cpu(ev->handle);
conn->sid = HCI_SID_INVALID;
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ISO_LINK, &flags);
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, BIS_LINK,
&flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_pa_term_sync(hdev, ev->handle);
goto unlock;
@ -6412,7 +6425,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
goto unlock;
/* Add connection to indicate PA sync event */
pa_sync = hci_conn_add_unset(hdev, ISO_LINK, BDADDR_ANY,
pa_sync = hci_conn_add_unset(hdev, BIS_LINK, BDADDR_ANY,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
@ -6443,7 +6456,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, BIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT))
goto unlock;
@ -6727,7 +6740,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
if (conn->type != ISO_LINK) {
if (conn->type != CIS_LINK) {
bt_dev_err(hdev,
"Invalid connection link type handle 0x%4.4x",
handle);
@ -6845,7 +6858,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
if (!acl)
goto unlock;
mask = hci_proto_connect_ind(hdev, &acl->dst, ISO_LINK, &flags);
mask = hci_proto_connect_ind(hdev, &acl->dst, CIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_reject_cis(hdev, ev->cis_handle);
goto unlock;
@ -6853,8 +6866,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
if (!cis) {
cis = hci_conn_add(hdev, ISO_LINK, &acl->dst, HCI_ROLE_SLAVE,
cis_handle);
cis = hci_conn_add(hdev, CIS_LINK, &acl->dst,
HCI_ROLE_SLAVE, cis_handle);
if (IS_ERR(cis)) {
hci_le_reject_cis(hdev, ev->cis_handle);
goto unlock;
@ -6969,7 +6982,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "ignore too large handle %u", handle);
continue;
}
bis = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY,
HCI_ROLE_SLAVE, handle);
if (IS_ERR(bis))
continue;
@ -7025,7 +7038,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, BIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT))
goto unlock;
@ -7155,7 +7168,8 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
/* Only match event if command OGF is for LE */
if (hdev->req_skb &&
hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) == 0x08 &&
(hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) == 0x08 ||
hci_skb_opcode(hdev->req_skb) == HCI_OP_NOP) &&
hci_skb_event(hdev->req_skb) == ev->subevent) {
*opcode = hci_skb_opcode(hdev->req_skb);
hci_req_cmd_complete(hdev, *opcode, 0x00, req_complete,
@ -7511,8 +7525,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
goto done;
}
hci_dev_lock(hdev);
kfree_skb(hdev->recv_event);
hdev->recv_event = skb_clone(skb, GFP_KERNEL);
hci_dev_unlock(hdev);
event = hdr->evt;
if (!event) {

View File

@ -234,7 +234,8 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT)
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT)
continue;
} else {
/* Don't send frame to other channel types */
@ -391,6 +392,12 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
else
opcode = cpu_to_le16(HCI_MON_ISO_TX_PKT);
break;
case HCI_DRV_PKT:
if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_DRV_RX_PKT);
else
opcode = cpu_to_le16(HCI_MON_DRV_TX_PKT);
break;
case HCI_DIAG_PKT:
opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
break;
@ -1860,7 +1867,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
err = -EINVAL;
goto drop;
}

View File

@ -2860,7 +2860,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
if (sent) {
struct hci_conn *conn;
conn = hci_conn_hash_lookup_ba(hdev, ISO_LINK,
conn = hci_conn_hash_lookup_ba(hdev, BIS_LINK,
&sent->bdaddr);
if (conn) {
struct bt_iso_qos *qos = &conn->iso_qos;
@ -5477,7 +5477,7 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
if (conn->type == LE_LINK)
return hci_le_connect_cancel_sync(hdev, conn, reason);
if (conn->type == ISO_LINK) {
if (conn->type == CIS_LINK) {
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
* page 1857:
*
@ -5490,9 +5490,10 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
return hci_disconnect_sync(hdev, conn, reason);
/* CIS with no Create CIS sent have nothing to cancel */
if (bacmp(&conn->dst, BDADDR_ANY))
return HCI_ERROR_LOCAL_HOST_TERM;
return HCI_ERROR_LOCAL_HOST_TERM;
}
if (conn->type == BIS_LINK) {
/* There is no way to cancel a BIS without terminating the BIG
* which is done later on connection cleanup.
*/
@ -5554,9 +5555,12 @@ static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
{
struct hci_cp_reject_conn_req cp;
if (conn->type == ISO_LINK)
if (conn->type == CIS_LINK)
return hci_le_reject_cis_sync(hdev, conn, reason);
if (conn->type == BIS_LINK)
return -EINVAL;
if (conn->type == SCO_LINK || conn->type == ESCO_LINK)
return hci_reject_sco_sync(hdev, conn, reason);
@ -6898,20 +6902,37 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
{
struct hci_conn *conn = data;
struct hci_conn *pa_sync;
bt_dev_dbg(hdev, "err %d", err);
if (!err)
return;
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
if (err == -ECANCELED)
return;
hci_dev_lock(hdev);
hci_update_passive_scan_sync(hdev);
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
if (!hci_conn_valid(hdev, conn))
clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
if (!err)
goto unlock;
/* Add connection to indicate PA sync error */
pa_sync = hci_conn_add_unset(hdev, BIS_LINK, BDADDR_ANY,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
goto unlock;
set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
/* Notify iso layer */
hci_connect_cfm(pa_sync, bt_status(err));
unlock:
hci_dev_unlock(hdev);
}
@ -6925,9 +6946,23 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
if (conn->sync_handle != HCI_SYNC_HANDLE_INVALID)
return -EINVAL;
if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC))
return -EBUSY;
/* Stop scanning if SID has not been set and active scanning is enabled
* so we use passive scanning which will be scanning using the allow
* list programmed to contain only the connection address.
*/
if (conn->sid == HCI_SID_INVALID &&
hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
hci_scan_disable_sync(hdev);
hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
}
/* Mark HCI_CONN_CREATE_PA_SYNC so hci_update_passive_scan_sync can
* program the address in the allow list so PA advertisements can be
* received.
@ -6936,6 +6971,14 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
hci_update_passive_scan_sync(hdev);
/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
* it.
*/
if (conn->sid == HCI_SID_INVALID)
__hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, 0, NULL,
HCI_EV_LE_EXT_ADV_REPORT,
conn->conn_timeout, NULL);
memset(&cp, 0, sizeof(cp));
cp.options = qos->bcast.options;
cp.sid = conn->sid;

View File

@ -941,7 +941,7 @@ static int iso_sock_bind_bc(struct socket *sock, struct sockaddr *addr,
iso_pi(sk)->dst_type = sa->iso_bc->bc_bdaddr_type;
if (sa->iso_bc->bc_sid > 0x0f)
if (sa->iso_bc->bc_sid > 0x0f && sa->iso_bc->bc_sid != HCI_SID_INVALID)
return -EINVAL;
iso_pi(sk)->bc_sid = sa->iso_bc->bc_sid;
@ -1330,6 +1330,7 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
{
struct sockaddr_iso *sa = (struct sockaddr_iso *)addr;
struct sock *sk = sock->sk;
int len = sizeof(struct sockaddr_iso);
BT_DBG("sock %p, sk %p", sock, sk);
@ -1338,12 +1339,20 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
if (peer) {
bacpy(&sa->iso_bdaddr, &iso_pi(sk)->dst);
sa->iso_bdaddr_type = iso_pi(sk)->dst_type;
if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
sa->iso_bc->bc_sid = iso_pi(sk)->bc_sid;
sa->iso_bc->bc_num_bis = iso_pi(sk)->bc_num_bis;
memcpy(sa->iso_bc->bc_bis, iso_pi(sk)->bc_bis,
ISO_MAX_NUM_BIS);
len += sizeof(struct sockaddr_iso_bc);
}
} else {
bacpy(&sa->iso_bdaddr, &iso_pi(sk)->src);
sa->iso_bdaddr_type = iso_pi(sk)->src_type;
}
return sizeof(struct sockaddr_iso);
return len;
}
static int iso_sock_sendmsg(struct socket *sock, struct msghdr *msg,
@ -1988,11 +1997,13 @@ static void iso_conn_ready(struct iso_conn *conn)
hcon->dst_type = iso_pi(parent)->dst_type;
}
if (ev3) {
if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
iso_pi(sk)->qos = iso_pi(parent)->qos;
hcon->iso_qos = iso_pi(sk)->qos;
iso_pi(sk)->bc_sid = iso_pi(parent)->bc_sid;
iso_pi(sk)->bc_num_bis = iso_pi(parent)->bc_num_bis;
memcpy(iso_pi(sk)->bc_bis, iso_pi(parent)->bc_bis, ISO_MAX_NUM_BIS);
memcpy(iso_pi(sk)->bc_bis, iso_pi(parent)->bc_bis,
ISO_MAX_NUM_BIS);
set_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
}
@ -2029,6 +2040,9 @@ static bool iso_match_sid(struct sock *sk, void *data)
{
struct hci_ev_le_pa_sync_established *ev = data;
if (iso_pi(sk)->bc_sid == HCI_SID_INVALID)
return true;
return ev->sid == iso_pi(sk)->bc_sid;
}
@ -2075,8 +2089,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (ev1) {
sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sid, ev1);
if (sk && !ev1->status)
if (sk && !ev1->status) {
iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
iso_pi(sk)->bc_sid = ev1->sid;
}
goto done;
}
@ -2203,7 +2219,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
{
if (hcon->type != ISO_LINK) {
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK) {
if (hcon->type != LE_LINK)
return;
@ -2244,7 +2260,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
{
if (hcon->type != ISO_LINK)
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason);

View File

@ -3221,7 +3221,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
static u8 link_to_bdaddr(u8 link_type, u8 addr_type)
{
switch (link_type) {
case ISO_LINK:
case CIS_LINK:
case BIS_LINK:
case LE_LINK:
switch (addr_type) {
case ADDR_LE_DEV_PUBLIC:

View File

@ -304,7 +304,7 @@ void mgmt_mesh_foreach(struct hci_dev *hdev,
{
struct mgmt_mesh_tx *mesh_tx, *tmp;
list_for_each_entry_safe(mesh_tx, tmp, &hdev->mgmt_pending, list) {
list_for_each_entry_safe(mesh_tx, tmp, &hdev->mesh_pending, list) {
if (!sk || mesh_tx->sk == sk)
cb(mesh_tx, data);
}