mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 03:53:37 +02:00
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:
commit
43a1ce8f42
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
76
include/net/bluetooth/hci_drv.h
Normal file
76
include/net/bluetooth/hci_drv.h
Normal 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 */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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(ðcmd, 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, "e))) {
|
||||
while (*cnt && (conn = hci_low_sent(hdev, type, type, "e))) {
|
||||
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, "e))) {
|
||||
while (*cnt && (conn = hci_low_sent(hdev, CIS_LINK, BIS_LINK,
|
||||
"e))) {
|
||||
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
105
net/bluetooth/hci_drv.c
Normal 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);
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user