mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-28 07:54:36 -05:00
Merge tag 'for-net-next-2025-09-27' 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: - MAINTAINERS: add a sub-entry for the Qualcomm bluetooth driver - Avoid a couple dozen -Wflex-array-member-not-at-end warnings - bcsp: receive data only if registered - HCI: Fix using LE/ACL buffers for ISO packets - hci_core: Detect if an ISO link has stalled - ISO: Don't initiate CIS connections if there are no buffers - ISO: Use sk_sndtimeo as conn_timeout drivers: - btusb: Check for unexpected bytes when defragmenting HCI frames - btusb: Add new VID/PID 13d3/3627 for MT7925 - btusb: Add new VID/PID 13d3/3633 for MT7922 - btusb: Add USB ID 2001:332a for D-Link AX9U rev. A1 - btintel: Add support for BlazarIW core - btintel_pcie: Add support for _suspend() / _resume() - btintel_pcie: Define hdev->wakeup() callback - btintel_pcie: Add Bluetooth core/platform as comments - btintel_pcie: Add id of Scorpious, Panther Lake-H484 - btintel_pcie: Refactor Device Coredump * tag 'for-net-next-2025-09-27' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (30 commits) Bluetooth: Avoid a couple dozen -Wflex-array-member-not-at-end warnings Bluetooth: hci_sync: Fix using random address for BIG/PA advertisements Bluetooth: ISO: don't leak skb in ISO_CONT RX Bluetooth: ISO: free rx_skb if not consumed Bluetooth: ISO: Fix possible UAF on iso_conn_free Bluetooth: SCO: Fix UAF on sco_conn_free Bluetooth: bcsp: receive data only if registered Bluetooth: btusb: Add new VID/PID 13d3/3633 for MT7922 Bluetooth: btusb: Add new VID/PID 13d3/3627 for MT7925 Bluetooth: remove duplicate h4_recv_buf() in header Bluetooth: btusb: Check for unexpected bytes when defragmenting HCI frames Bluetooth: hci_core: Print information of hcon on hci_low_sent Bluetooth: hci_core: Print number of packets in conn->data_q Bluetooth: Add function and line information to bt_dbg Bluetooth: MGMT: Fix not exposing debug UUID on MGMT_OP_READ_EXP_FEATURES_INFO Bluetooth: hci_core: Detect if an ISO link has stalled Bluetooth: ISO: Use sk_sndtimeo as conn_timeout Bluetooth: HCI: Fix using LE/ACL buffers for ISO packets Bluetooth: ISO: Don't initiate CIS connections if there are no buffers MAINTAINERS: add a sub-entry for the Qualcomm bluetooth driver ... ==================== Link: https://patch.msgid.link/20250927154616.1032839-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -20686,6 +20686,13 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
|
||||
F: drivers/net/wwan/qcom_bam_dmux.c
|
||||
|
||||
QUALCOMM BLUETOOTH DRIVER
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/bluetooth/btqca.[ch]
|
||||
F: drivers/bluetooth/btqcomsmd.c
|
||||
F: drivers/bluetooth/hci_qca.c
|
||||
|
||||
QUALCOMM CAMERA SUBSYSTEM DRIVER
|
||||
M: Robert Foss <rfoss@kernel.org>
|
||||
M: Todor Tomov <todor.too@gmail.com>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "h4_recv.h"
|
||||
#include "hci_uart.h"
|
||||
|
||||
#define VERSION "0.11"
|
||||
|
||||
|
||||
@@ -484,6 +484,7 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
|
||||
case 0x1d: /* BlazarU (BzrU) */
|
||||
case 0x1e: /* BlazarI (Bzr) */
|
||||
case 0x1f: /* Scorpious Peak */
|
||||
case 0x22: /* BlazarIW (BzrIW) */
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
|
||||
@@ -3253,6 +3254,7 @@ void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x1f:
|
||||
case 0x22:
|
||||
hci_set_msft_opcode(hdev, 0xFC1E);
|
||||
break;
|
||||
default:
|
||||
@@ -3593,6 +3595,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x1f:
|
||||
case 0x22:
|
||||
/* Display version information of TLV type */
|
||||
btintel_version_info_tlv(hdev, &ver_tlv);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/devcoredump.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@@ -35,8 +36,13 @@
|
||||
|
||||
/* Intel Bluetooth PCIe device id table */
|
||||
static const struct pci_device_id btintel_pcie_table[] = {
|
||||
/* BlazarI, Wildcat Lake */
|
||||
{ BTINTEL_PCI_DEVICE(0x4D76, PCI_ANY_ID) },
|
||||
/* BlazarI, Lunar Lake */
|
||||
{ BTINTEL_PCI_DEVICE(0xA876, PCI_ANY_ID) },
|
||||
/* Scorpious, Panther Lake-H484 */
|
||||
{ BTINTEL_PCI_DEVICE(0xE376, PCI_ANY_ID) },
|
||||
/* Scorpious, Panther Lake-H404 */
|
||||
{ BTINTEL_PCI_DEVICE(0xE476, PCI_ANY_ID) },
|
||||
{ 0 }
|
||||
};
|
||||
@@ -554,25 +560,6 @@ static void btintel_pcie_mac_init(struct btintel_pcie_data *data)
|
||||
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
|
||||
}
|
||||
|
||||
static int btintel_pcie_add_dmp_data(struct hci_dev *hdev, const void *data, int size)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
skb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_put_data(skb, data, size);
|
||||
err = hci_devcd_append(hdev, skb);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to append data in the coredump");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btintel_pcie_get_mac_access(struct btintel_pcie_data *data)
|
||||
{
|
||||
u32 reg;
|
||||
@@ -617,30 +604,35 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data)
|
||||
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
|
||||
}
|
||||
|
||||
static void btintel_pcie_copy_tlv(struct sk_buff *skb, enum btintel_pcie_tlv_type type,
|
||||
void *data, int size)
|
||||
static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
|
||||
void *data, size_t size)
|
||||
{
|
||||
struct intel_tlv *tlv;
|
||||
|
||||
tlv = skb_put(skb, sizeof(*tlv) + size);
|
||||
tlv = dest;
|
||||
tlv->type = type;
|
||||
tlv->len = size;
|
||||
memcpy(tlv->val, data, tlv->len);
|
||||
return dest + sizeof(*tlv) + size;
|
||||
}
|
||||
|
||||
static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
{
|
||||
u32 offset, prev_size, wr_ptr_status, dump_size, i;
|
||||
u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
|
||||
struct btintel_pcie_dbgc *dbgc = &data->dbgc;
|
||||
u8 buf_idx, dump_time_len, fw_build;
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
u8 *pdata, *p, buf_idx;
|
||||
struct intel_tlv *tlv;
|
||||
struct timespec64 now;
|
||||
struct sk_buff *skb;
|
||||
struct tm tm_now;
|
||||
char buf[256];
|
||||
u16 hdr_len;
|
||||
int ret;
|
||||
char fw_build[128];
|
||||
char ts[128];
|
||||
char vendor[64];
|
||||
char driver[64];
|
||||
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
||||
wr_ptr_status = btintel_pcie_rd_dev_mem(data, BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS);
|
||||
offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
|
||||
@@ -657,88 +649,84 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(vendor, sizeof(vendor), "Vendor: Intel\n");
|
||||
snprintf(driver, sizeof(driver), "Driver: %s\n",
|
||||
data->dmp_hdr.driver_name);
|
||||
|
||||
ktime_get_real_ts64(&now);
|
||||
time64_to_tm(now.tv_sec, 0, &tm_now);
|
||||
dump_time_len = snprintf(buf, sizeof(buf), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
|
||||
snprintf(ts, sizeof(ts), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
|
||||
tm_now.tm_mday, tm_now.tm_mon + 1, tm_now.tm_year + 1900,
|
||||
tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
|
||||
|
||||
fw_build = snprintf(buf + dump_time_len, sizeof(buf) - dump_time_len,
|
||||
snprintf(fw_build, sizeof(fw_build),
|
||||
"Firmware Timestamp: Year %u WW %02u buildtype %u build %u",
|
||||
2000 + (data->dmp_hdr.fw_timestamp >> 8),
|
||||
data->dmp_hdr.fw_timestamp & 0xff, data->dmp_hdr.fw_build_type,
|
||||
data->dmp_hdr.fw_build_num);
|
||||
|
||||
hdr_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.fw_git_sha1) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
|
||||
sizeof(*tlv) + dump_time_len +
|
||||
sizeof(*tlv) + fw_build;
|
||||
data_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.fw_git_sha1) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
|
||||
sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
|
||||
sizeof(*tlv) + strlen(ts) +
|
||||
sizeof(*tlv) + strlen(fw_build) +
|
||||
sizeof(*tlv) + strlen(vendor) +
|
||||
sizeof(*tlv) + strlen(driver);
|
||||
|
||||
dump_size = hdr_len + sizeof(hdr_len);
|
||||
/*
|
||||
* sizeof(u32) - signature
|
||||
* sizeof(data_len) - to store tlv data size
|
||||
* data_len - TLV data
|
||||
*/
|
||||
dump_size = sizeof(u32) + sizeof(data_len) + data_len;
|
||||
|
||||
skb = alloc_skb(dump_size, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add debug buffers data length to dump size */
|
||||
dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
|
||||
|
||||
ret = hci_devcd_init(hdev, dump_size);
|
||||
if (ret) {
|
||||
bt_dev_err(hdev, "Failed to init devcoredump, err %d", ret);
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
pdata = vmalloc(dump_size);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
p = pdata;
|
||||
|
||||
skb_put_data(skb, &hdr_len, sizeof(hdr_len));
|
||||
*(u32 *)p = BTINTEL_PCIE_MAGIC_NUM;
|
||||
p += sizeof(u32);
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
|
||||
sizeof(data->dmp_hdr.cnvi_bt));
|
||||
*(u32 *)p = data_len;
|
||||
p += sizeof(u32);
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
|
||||
sizeof(data->dmp_hdr.write_ptr));
|
||||
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_VENDOR, vendor, strlen(vendor));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_DRIVER, driver, strlen(driver));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_DUMP_TIME, ts, strlen(ts));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_FW_BUILD, fw_build,
|
||||
strlen(fw_build));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
|
||||
sizeof(data->dmp_hdr.cnvi_bt));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
|
||||
sizeof(data->dmp_hdr.write_ptr));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
|
||||
sizeof(data->dmp_hdr.wrap_ctr));
|
||||
|
||||
data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data,
|
||||
BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND);
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
|
||||
sizeof(data->dmp_hdr.wrap_ctr));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
|
||||
sizeof(data->dmp_hdr.trigger_reason));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1,
|
||||
sizeof(data->dmp_hdr.fw_git_sha1));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
|
||||
sizeof(data->dmp_hdr.cnvr_top));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
|
||||
sizeof(data->dmp_hdr.cnvi_top));
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
|
||||
sizeof(data->dmp_hdr.trigger_reason));
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1,
|
||||
sizeof(data->dmp_hdr.fw_git_sha1));
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
|
||||
sizeof(data->dmp_hdr.cnvr_top));
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
|
||||
sizeof(data->dmp_hdr.cnvi_top));
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_DUMP_TIME, buf, dump_time_len);
|
||||
|
||||
btintel_pcie_copy_tlv(skb, BTINTEL_FW_BUILD, buf + dump_time_len, fw_build);
|
||||
|
||||
ret = hci_devcd_append(hdev, skb);
|
||||
if (ret)
|
||||
goto exit_err;
|
||||
|
||||
for (i = 0; i < dbgc->count; i++) {
|
||||
ret = btintel_pcie_add_dmp_data(hdev, dbgc->bufs[i].data,
|
||||
BTINTEL_PCIE_DBGC_BUFFER_SIZE);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
exit_err:
|
||||
hci_devcd_complete(hdev);
|
||||
return ret;
|
||||
memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
|
||||
dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btintel_pcie_dump_traces(struct hci_dev *hdev)
|
||||
@@ -760,51 +748,6 @@ static void btintel_pcie_dump_traces(struct hci_dev *hdev)
|
||||
bt_dev_err(hdev, "Failed to dump traces: (%d)", ret);
|
||||
}
|
||||
|
||||
static void btintel_pcie_dump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
|
||||
u16 len = skb->len;
|
||||
u16 *hdrlen_ptr;
|
||||
char buf[80];
|
||||
|
||||
hdrlen_ptr = skb_put_zero(skb, sizeof(len));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
|
||||
INTEL_HW_VARIANT(data->dmp_hdr.cnvi_bt));
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Firmware Build Number: %u\n",
|
||||
data->dmp_hdr.fw_build_num);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Driver: %s\n", data->dmp_hdr.driver_name);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Vendor: Intel\n");
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
*hdrlen_ptr = skb->len - len;
|
||||
}
|
||||
|
||||
static void btintel_pcie_dump_notify(struct hci_dev *hdev, int state)
|
||||
{
|
||||
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
switch (state) {
|
||||
case HCI_DEVCOREDUMP_IDLE:
|
||||
data->dmp_hdr.state = HCI_DEVCOREDUMP_IDLE;
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_ACTIVE:
|
||||
data->dmp_hdr.state = HCI_DEVCOREDUMP_ACTIVE;
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_TIMEOUT:
|
||||
case HCI_DEVCOREDUMP_ABORT:
|
||||
case HCI_DEVCOREDUMP_DONE:
|
||||
data->dmp_hdr.state = HCI_DEVCOREDUMP_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in
|
||||
* BTINTEL_PCIE_CSR_FUNC_CTRL_REG register and wait for MSI-X with
|
||||
* BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0.
|
||||
@@ -1378,6 +1321,11 @@ static void btintel_pcie_rx_work(struct work_struct *work)
|
||||
struct btintel_pcie_data, rx_work);
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
|
||||
btintel_pcie_dump_traces(data->hdev);
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) {
|
||||
/* Unlike usb products, controller will not send hardware
|
||||
* exception event on exception. Instead controller writes the
|
||||
@@ -1390,11 +1338,6 @@ static void btintel_pcie_rx_work(struct work_struct *work)
|
||||
clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
|
||||
}
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
|
||||
btintel_pcie_dump_traces(data->hdev);
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
|
||||
/* Process the sk_buf in queue and send to the HCI layer */
|
||||
while ((skb = skb_dequeue(&data->rx_skb_q))) {
|
||||
btintel_pcie_recv_frame(data, skb);
|
||||
@@ -2149,6 +2092,7 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
|
||||
switch (INTEL_HW_VARIANT(ver_tlv.cnvi_bt)) {
|
||||
case 0x1e: /* BzrI */
|
||||
case 0x1f: /* ScP */
|
||||
case 0x22: /* BzrIW */
|
||||
/* Display version information of TLV type */
|
||||
btintel_version_info_tlv(hdev, &ver_tlv);
|
||||
|
||||
@@ -2184,13 +2128,6 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
|
||||
if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
|
||||
data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
|
||||
|
||||
err = hci_devcd_register(hdev, btintel_pcie_dump_traces, btintel_pcie_dump_hdr,
|
||||
btintel_pcie_dump_notify);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to register coredump (%d)", err);
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
btintel_print_fseq_info(hdev);
|
||||
exit_error:
|
||||
kfree_skb(skb);
|
||||
@@ -2236,6 +2173,7 @@ btintel_pcie_get_recovery(struct pci_dev *pdev, struct device *dev)
|
||||
{
|
||||
struct btintel_pcie_dev_recovery *tmp, *data = NULL;
|
||||
const char *name = pci_name(pdev);
|
||||
const size_t name_len = strlen(name) + 1;
|
||||
struct hci_dev *hdev = to_hci_dev(dev);
|
||||
|
||||
spin_lock(&btintel_pcie_recovery_lock);
|
||||
@@ -2252,11 +2190,11 @@ btintel_pcie_get_recovery(struct pci_dev *pdev, struct device *dev)
|
||||
return data;
|
||||
}
|
||||
|
||||
data = kzalloc(struct_size(data, name, strlen(name) + 1), GFP_ATOMIC);
|
||||
data = kzalloc(struct_size(data, name, name_len), GFP_ATOMIC);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
strscpy_pad(data->name, name, strlen(name) + 1);
|
||||
strscpy(data->name, name, name_len);
|
||||
spin_lock(&btintel_pcie_recovery_lock);
|
||||
list_add_tail(&data->list, &btintel_pcie_recovery_list);
|
||||
spin_unlock(&btintel_pcie_recovery_lock);
|
||||
@@ -2319,7 +2257,6 @@ static void btintel_pcie_removal_work(struct work_struct *wk)
|
||||
btintel_pcie_synchronize_irqs(data);
|
||||
|
||||
flush_work(&data->rx_work);
|
||||
flush_work(&data->hdev->dump.dump_rx);
|
||||
|
||||
bt_dev_dbg(data->hdev, "Release bluetooth interface");
|
||||
btintel_pcie_release_hdev(data);
|
||||
@@ -2410,6 +2347,13 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
|
||||
btintel_pcie_reset(hdev);
|
||||
}
|
||||
|
||||
static bool btintel_pcie_wakeup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
return device_may_wakeup(&data->pdev->dev);
|
||||
}
|
||||
|
||||
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
|
||||
{
|
||||
int err;
|
||||
@@ -2435,6 +2379,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
|
||||
hdev->set_diag = btintel_set_diag;
|
||||
hdev->set_bdaddr = btintel_set_bdaddr;
|
||||
hdev->reset = btintel_pcie_reset;
|
||||
hdev->wakeup = btintel_pcie_wakeup;
|
||||
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
@@ -2573,11 +2518,100 @@ static void btintel_pcie_coredump(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct btintel_pcie_data *data;
|
||||
ktime_t start;
|
||||
u32 dxstate;
|
||||
int err;
|
||||
|
||||
data = pci_get_drvdata(pdev);
|
||||
|
||||
dxstate = (mesg.event == PM_EVENT_SUSPEND ?
|
||||
BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
|
||||
|
||||
data->gp0_received = false;
|
||||
|
||||
start = ktime_get();
|
||||
|
||||
/* Refer: 6.4.11.7 -> Platform power management */
|
||||
btintel_pcie_wr_sleep_cntrl(data, dxstate);
|
||||
err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
|
||||
msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
|
||||
if (err == 0) {
|
||||
bt_dev_err(data->hdev,
|
||||
"Timeout (%u ms) on alive interrupt for D3 entry",
|
||||
BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
bt_dev_dbg(data->hdev,
|
||||
"device entered into d3 state from d0 in %lld us",
|
||||
ktime_to_us(ktime_get() - start));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btintel_pcie_suspend(struct device *dev)
|
||||
{
|
||||
return btintel_pcie_suspend_late(dev, PMSG_SUSPEND);
|
||||
}
|
||||
|
||||
static int btintel_pcie_hibernate(struct device *dev)
|
||||
{
|
||||
return btintel_pcie_suspend_late(dev, PMSG_HIBERNATE);
|
||||
}
|
||||
|
||||
static int btintel_pcie_freeze(struct device *dev)
|
||||
{
|
||||
return btintel_pcie_suspend_late(dev, PMSG_FREEZE);
|
||||
}
|
||||
|
||||
static int btintel_pcie_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct btintel_pcie_data *data;
|
||||
ktime_t start;
|
||||
int err;
|
||||
|
||||
data = pci_get_drvdata(pdev);
|
||||
data->gp0_received = false;
|
||||
|
||||
start = ktime_get();
|
||||
|
||||
/* Refer: 6.4.11.7 -> Platform power management */
|
||||
btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
|
||||
err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
|
||||
msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
|
||||
if (err == 0) {
|
||||
bt_dev_err(data->hdev,
|
||||
"Timeout (%u ms) on alive interrupt for D0 entry",
|
||||
BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
bt_dev_dbg(data->hdev,
|
||||
"device entered into d0 state from d3 in %lld us",
|
||||
ktime_to_us(ktime_get() - start));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops btintel_pcie_pm_ops = {
|
||||
.suspend = btintel_pcie_suspend,
|
||||
.resume = btintel_pcie_resume,
|
||||
.freeze = btintel_pcie_freeze,
|
||||
.thaw = btintel_pcie_resume,
|
||||
.poweroff = btintel_pcie_hibernate,
|
||||
.restore = btintel_pcie_resume,
|
||||
};
|
||||
|
||||
static struct pci_driver btintel_pcie_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = btintel_pcie_table,
|
||||
.probe = btintel_pcie_probe,
|
||||
.remove = btintel_pcie_remove,
|
||||
.driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
|
||||
#ifdef CONFIG_DEV_COREDUMP
|
||||
.driver.coredump = btintel_pcie_coredump
|
||||
#endif
|
||||
|
||||
@@ -132,6 +132,8 @@ enum btintel_pcie_tlv_type {
|
||||
BTINTEL_CNVI_TOP,
|
||||
BTINTEL_DUMP_TIME,
|
||||
BTINTEL_FW_BUILD,
|
||||
BTINTEL_VENDOR,
|
||||
BTINTEL_DRIVER
|
||||
};
|
||||
|
||||
/* causes for the MBOX interrupts */
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "h4_recv.h"
|
||||
#include "hci_uart.h"
|
||||
#include "btmtk.h"
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "h4_recv.h"
|
||||
#include "hci_uart.h"
|
||||
#include "btmtk.h"
|
||||
|
||||
#define VERSION "0.2"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "h4_recv.h"
|
||||
#include "hci_uart.h"
|
||||
|
||||
#define MANUFACTURER_NXP 37
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ static struct usb_driver btusb_driver;
|
||||
#define BTUSB_INTEL_BROKEN_INITIAL_NCMD BIT(25)
|
||||
#define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26)
|
||||
#define BTUSB_ACTIONS_SEMI BIT(27)
|
||||
#define BTUSB_BARROT BIT(28)
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
@@ -522,6 +523,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
/* Realtek 8851BU Bluetooth devices */
|
||||
{ USB_DEVICE(0x3625, 0x010b), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2001, 0x332a), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek 8852AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK |
|
||||
@@ -698,6 +701,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3615), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3633), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x35f5, 0x7922), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
@@ -732,6 +737,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3627), .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 |
|
||||
@@ -810,6 +817,10 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x0cb5, 0xc547), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Barrot Technology Bluetooth devices */
|
||||
{ USB_DEVICE(0x33fa, 0x0010), .driver_info = BTUSB_BARROT },
|
||||
{ USB_DEVICE(0x33fa, 0x0012), .driver_info = BTUSB_BARROT },
|
||||
|
||||
/* Actions Semiconductor ATS2851 based devices */
|
||||
{ USB_DEVICE(0x10d7, 0xb012), .driver_info = BTUSB_ACTIONS_SEMI },
|
||||
|
||||
@@ -1192,6 +1203,18 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
}
|
||||
|
||||
if (!hci_skb_expect(skb)) {
|
||||
/* Each chunk should correspond to at least 1 or more
|
||||
* events so if there are still bytes left that doesn't
|
||||
* constitute a new event this is likely a bug in the
|
||||
* controller.
|
||||
*/
|
||||
if (count && count < HCI_EVENT_HDR_SIZE) {
|
||||
bt_dev_warn(data->hdev,
|
||||
"Unexpected continuation: %d bytes",
|
||||
count);
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/* Complete frame */
|
||||
btusb_recv_event(data, skb);
|
||||
skb = NULL;
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
*
|
||||
* Generic Bluetooth HCI UART driver
|
||||
*
|
||||
* Copyright (C) 2015-2018 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
struct h4_recv_pkt {
|
||||
u8 type; /* Packet type */
|
||||
u8 hlen; /* Header length */
|
||||
u8 loff; /* Data length offset in header */
|
||||
u8 lsize; /* Data length field size */
|
||||
u16 maxlen; /* Max overall packet length */
|
||||
int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
};
|
||||
|
||||
#define H4_RECV_ACL \
|
||||
.type = HCI_ACLDATA_PKT, \
|
||||
.hlen = HCI_ACL_HDR_SIZE, \
|
||||
.loff = 2, \
|
||||
.lsize = 2, \
|
||||
.maxlen = HCI_MAX_FRAME_SIZE \
|
||||
|
||||
#define H4_RECV_SCO \
|
||||
.type = HCI_SCODATA_PKT, \
|
||||
.hlen = HCI_SCO_HDR_SIZE, \
|
||||
.loff = 2, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_MAX_SCO_SIZE
|
||||
|
||||
#define H4_RECV_EVENT \
|
||||
.type = HCI_EVENT_PKT, \
|
||||
.hlen = HCI_EVENT_HDR_SIZE, \
|
||||
.loff = 1, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_MAX_EVENT_SIZE
|
||||
|
||||
#define H4_RECV_ISO \
|
||||
.type = HCI_ISODATA_PKT, \
|
||||
.hlen = HCI_ISO_HDR_SIZE, \
|
||||
.loff = 2, \
|
||||
.lsize = 2, \
|
||||
.maxlen = HCI_MAX_FRAME_SIZE
|
||||
|
||||
static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
|
||||
struct sk_buff *skb,
|
||||
const unsigned char *buffer,
|
||||
int count,
|
||||
const struct h4_recv_pkt *pkts,
|
||||
int pkts_count)
|
||||
{
|
||||
/* Check for error from previous call */
|
||||
if (IS_ERR(skb))
|
||||
skb = NULL;
|
||||
|
||||
while (count) {
|
||||
int i, len;
|
||||
|
||||
if (!skb) {
|
||||
for (i = 0; i < pkts_count; i++) {
|
||||
if (buffer[0] != (&pkts[i])->type)
|
||||
continue;
|
||||
|
||||
skb = bt_skb_alloc((&pkts[i])->maxlen,
|
||||
GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hci_skb_pkt_type(skb) = (&pkts[i])->type;
|
||||
hci_skb_expect(skb) = (&pkts[i])->hlen;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for invalid packet type */
|
||||
if (!skb)
|
||||
return ERR_PTR(-EILSEQ);
|
||||
|
||||
count -= 1;
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
|
||||
skb_put_data(skb, buffer, len);
|
||||
|
||||
count -= len;
|
||||
buffer += len;
|
||||
|
||||
/* Check for partial packet */
|
||||
if (skb->len < hci_skb_expect(skb))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < pkts_count; i++) {
|
||||
if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= pkts_count) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EILSEQ);
|
||||
}
|
||||
|
||||
if (skb->len == (&pkts[i])->hlen) {
|
||||
u16 dlen;
|
||||
|
||||
switch ((&pkts[i])->lsize) {
|
||||
case 0:
|
||||
/* No variable data length */
|
||||
dlen = 0;
|
||||
break;
|
||||
case 1:
|
||||
/* Single octet variable length */
|
||||
dlen = skb->data[(&pkts[i])->loff];
|
||||
hci_skb_expect(skb) += dlen;
|
||||
|
||||
if (skb_tailroom(skb) < dlen) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EMSGSIZE);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* Double octet variable length */
|
||||
dlen = get_unaligned_le16(skb->data +
|
||||
(&pkts[i])->loff);
|
||||
hci_skb_expect(skb) += dlen;
|
||||
|
||||
if (skb_tailroom(skb) < dlen) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EMSGSIZE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Unsupported variable length */
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EILSEQ);
|
||||
}
|
||||
|
||||
if (!dlen) {
|
||||
/* No more data, complete frame */
|
||||
(&pkts[i])->recv(hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
} else {
|
||||
/* Complete frame */
|
||||
(&pkts[i])->recv(hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
@@ -582,6 +582,9 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
||||
struct bcsp_struct *bcsp = hu->priv;
|
||||
const unsigned char *ptr;
|
||||
|
||||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
BT_DBG("hu %p count %d rx_state %d rx_count %ld",
|
||||
hu, count, bcsp->rx_state, bcsp->rx_count);
|
||||
|
||||
|
||||
@@ -272,7 +272,8 @@ void bt_err_ratelimited(const char *fmt, ...);
|
||||
#define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_FEATURE_DEBUG)
|
||||
#define BT_DBG(fmt, ...) bt_dbg(fmt "\n", ##__VA_ARGS__)
|
||||
#define BT_DBG(fmt, ...) \
|
||||
bt_dbg("%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
@@ -488,6 +488,7 @@ enum {
|
||||
#define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
|
||||
#define HCI_ACL_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */
|
||||
#define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */
|
||||
#define HCI_ISO_TX_TIMEOUT usecs_to_jiffies(0x7fffff) /* 8388607 usecs */
|
||||
|
||||
/* HCI data types */
|
||||
#define HCI_COMMAND_PKT 0x01
|
||||
|
||||
@@ -487,6 +487,7 @@ struct hci_dev {
|
||||
|
||||
unsigned long acl_last_tx;
|
||||
unsigned long le_last_tx;
|
||||
unsigned long iso_last_tx;
|
||||
|
||||
__u8 le_tx_def_phys;
|
||||
__u8 le_rx_def_phys;
|
||||
@@ -1587,16 +1588,18 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u16 setting, struct bt_codec *codec,
|
||||
u16 timeout);
|
||||
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
u16 timeout);
|
||||
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
|
||||
struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base);
|
||||
__u8 base_len, __u8 *base, u16 timeout);
|
||||
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
u16 timeout);
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, __u8 sid,
|
||||
struct bt_iso_qos *qos,
|
||||
__u8 data_len, __u8 *data);
|
||||
__u8 data_len, __u8 *data, u16 timeout);
|
||||
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, __u8 sid, struct bt_iso_qos *qos);
|
||||
int hci_conn_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
|
||||
|
||||
@@ -47,7 +47,7 @@ struct hci_drv_ev_cmd_complete {
|
||||
struct hci_drv_rp_read_info {
|
||||
__u8 driver_name[HCI_DRV_MAX_DRIVER_NAME_LENGTH];
|
||||
__le16 num_supported_commands;
|
||||
__le16 supported_commands[];
|
||||
__le16 supported_commands[] __counted_by_le(num_supported_commands);
|
||||
} __packed;
|
||||
|
||||
/* Driver specific OGF (Opcode Group Field)
|
||||
|
||||
@@ -53,10 +53,15 @@ struct mgmt_hdr {
|
||||
} __packed;
|
||||
|
||||
struct mgmt_tlv {
|
||||
__le16 type;
|
||||
__u8 length;
|
||||
/* New members MUST be added within the __struct_group() macro below. */
|
||||
__struct_group(mgmt_tlv_hdr, __hdr, __packed,
|
||||
__le16 type;
|
||||
__u8 length;
|
||||
);
|
||||
__u8 value[];
|
||||
} __packed;
|
||||
static_assert(offsetof(struct mgmt_tlv, value) == sizeof(struct mgmt_tlv_hdr),
|
||||
"struct member likely outside of __struct_group()");
|
||||
|
||||
struct mgmt_addr_info {
|
||||
bdaddr_t bdaddr;
|
||||
|
||||
@@ -924,10 +924,9 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
|
||||
case CIS_LINK:
|
||||
case BIS_LINK:
|
||||
case PA_LINK:
|
||||
if (hdev->iso_mtu)
|
||||
/* Dedicated ISO Buffer exists */
|
||||
break;
|
||||
fallthrough;
|
||||
if (!hdev->iso_mtu)
|
||||
return ERR_PTR(-ECONNREFUSED);
|
||||
break;
|
||||
case LE_LINK:
|
||||
if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
|
||||
return ERR_PTR(-ECONNREFUSED);
|
||||
@@ -1540,7 +1539,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 sid, struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base)
|
||||
__u8 base_len, __u8 *base, u16 timeout)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
int err;
|
||||
@@ -1582,6 +1581,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->sid = sid;
|
||||
conn->conn_timeout = timeout;
|
||||
|
||||
hci_conn_hold(conn);
|
||||
return conn;
|
||||
@@ -1922,7 +1922,8 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||
}
|
||||
|
||||
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos)
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
u16 timeout)
|
||||
{
|
||||
struct hci_conn *cis;
|
||||
|
||||
@@ -1937,6 +1938,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
cis->dst_type = dst_type;
|
||||
cis->iso_qos.ucast.cig = BT_ISO_QOS_CIG_UNSET;
|
||||
cis->iso_qos.ucast.cis = BT_ISO_QOS_CIS_UNSET;
|
||||
cis->conn_timeout = timeout;
|
||||
}
|
||||
|
||||
if (cis->state == BT_CONNECTED)
|
||||
@@ -2176,7 +2178,7 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
|
||||
struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base)
|
||||
__u8 base_len, __u8 *base, u16 timeout)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct hci_conn *parent;
|
||||
@@ -2197,7 +2199,7 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
|
||||
base, base_len);
|
||||
|
||||
/* We need hci_conn object using the BDADDR_ANY as dst */
|
||||
conn = hci_add_bis(hdev, dst, sid, qos, base_len, eir);
|
||||
conn = hci_add_bis(hdev, dst, sid, qos, base_len, eir, timeout);
|
||||
if (IS_ERR(conn))
|
||||
return conn;
|
||||
|
||||
@@ -2250,13 +2252,13 @@ static void bis_mark_per_adv(struct hci_conn *conn, void *data)
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, __u8 sid,
|
||||
struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base)
|
||||
__u8 base_len, __u8 *base, u16 timeout)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
int err;
|
||||
struct iso_list_data data;
|
||||
|
||||
conn = hci_bind_bis(hdev, dst, sid, qos, base_len, base);
|
||||
conn = hci_bind_bis(hdev, dst, sid, qos, base_len, base, timeout);
|
||||
if (IS_ERR(conn))
|
||||
return conn;
|
||||
|
||||
@@ -2299,7 +2301,8 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
}
|
||||
|
||||
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos)
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
u16 timeout)
|
||||
{
|
||||
struct hci_conn *le;
|
||||
struct hci_conn *cis;
|
||||
@@ -2323,7 +2326,7 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
hci_iso_qos_setup(hdev, le, &qos->ucast.in,
|
||||
le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys);
|
||||
|
||||
cis = hci_bind_cis(hdev, dst, dst_type, qos);
|
||||
cis = hci_bind_cis(hdev, dst, dst_type, qos, timeout);
|
||||
if (IS_ERR(cis)) {
|
||||
hci_conn_drop(le);
|
||||
return cis;
|
||||
|
||||
@@ -3267,6 +3267,8 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
|
||||
|
||||
spin_unlock_bh(&queue->lock);
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "chan %p queued %d", chan, skb_queue_len(queue));
|
||||
}
|
||||
|
||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
|
||||
@@ -3298,6 +3300,10 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
|
||||
hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
|
||||
|
||||
skb_queue_tail(&conn->data_q, skb);
|
||||
|
||||
bt_dev_dbg(hdev, "hcon %p queued %d", conn,
|
||||
skb_queue_len(&conn->data_q));
|
||||
|
||||
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||
}
|
||||
|
||||
@@ -3357,6 +3363,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
|
||||
__skb_queue_tail(queue, skb);
|
||||
} while (list);
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue));
|
||||
}
|
||||
|
||||
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
|
||||
@@ -3399,8 +3407,7 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
|
||||
case CIS_LINK:
|
||||
case BIS_LINK:
|
||||
case PA_LINK:
|
||||
cnt = hdev->iso_mtu ? hdev->iso_cnt :
|
||||
hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
cnt = hdev->iso_cnt;
|
||||
break;
|
||||
default:
|
||||
cnt = 0;
|
||||
@@ -3428,6 +3435,10 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
|
||||
skb_queue_empty(&c->data_q))
|
||||
continue;
|
||||
|
||||
bt_dev_dbg(hdev, "hcon %p state %s queued %d", c,
|
||||
state_to_string(c->state),
|
||||
skb_queue_len(&c->data_q));
|
||||
|
||||
if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
|
||||
continue;
|
||||
|
||||
@@ -3586,24 +3597,37 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
|
||||
|
||||
static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type)
|
||||
{
|
||||
unsigned long last_tx;
|
||||
unsigned long timeout;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case ACL_LINK:
|
||||
/* tx timeout must be longer than maximum link supervision
|
||||
* timeout (40.9 seconds)
|
||||
*/
|
||||
timeout = hdev->acl_last_tx + HCI_ACL_TX_TIMEOUT;
|
||||
break;
|
||||
case LE_LINK:
|
||||
last_tx = hdev->le_last_tx;
|
||||
/* tx timeout must be longer than maximum link supervision
|
||||
* timeout (40.9 seconds)
|
||||
*/
|
||||
timeout = hdev->le_last_tx + HCI_ACL_TX_TIMEOUT;
|
||||
break;
|
||||
case CIS_LINK:
|
||||
case BIS_LINK:
|
||||
case PA_LINK:
|
||||
/* tx timeout must be longer than the maximum transport latency
|
||||
* (8.388607 seconds)
|
||||
*/
|
||||
timeout = hdev->iso_last_tx + HCI_ISO_TX_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
last_tx = hdev->acl_last_tx;
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
/* tx timeout must be longer than maximum link supervision timeout
|
||||
* (40.9 seconds)
|
||||
*/
|
||||
if (!cnt && time_after(jiffies, last_tx + HCI_ACL_TX_TIMEOUT))
|
||||
if (!cnt && time_after(jiffies, timeout))
|
||||
hci_link_tx_to(hdev, type);
|
||||
}
|
||||
|
||||
@@ -3759,12 +3783,16 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type)
|
||||
if (!hci_conn_num(hdev, type))
|
||||
return;
|
||||
|
||||
cnt = hdev->iso_pkts ? &hdev->iso_cnt :
|
||||
hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
|
||||
cnt = &hdev->iso_cnt;
|
||||
|
||||
__check_timeout(hdev, *cnt, type);
|
||||
|
||||
while (*cnt && (conn = hci_low_sent(hdev, 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);
|
||||
hdev->iso_last_tx = jiffies;
|
||||
|
||||
conn->sent++;
|
||||
if (conn->sent == ~0)
|
||||
|
||||
@@ -4461,19 +4461,9 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
|
||||
case CIS_LINK:
|
||||
case BIS_LINK:
|
||||
case PA_LINK:
|
||||
if (hdev->iso_pkts) {
|
||||
hdev->iso_cnt += count;
|
||||
if (hdev->iso_cnt > hdev->iso_pkts)
|
||||
hdev->iso_cnt = hdev->iso_pkts;
|
||||
} else if (hdev->le_pkts) {
|
||||
hdev->le_cnt += count;
|
||||
if (hdev->le_cnt > hdev->le_pkts)
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
} else {
|
||||
hdev->acl_cnt += count;
|
||||
if (hdev->acl_cnt > hdev->acl_pkts)
|
||||
hdev->acl_cnt = hdev->acl_pkts;
|
||||
}
|
||||
hdev->iso_cnt += count;
|
||||
if (hdev->iso_cnt > hdev->iso_pkts)
|
||||
hdev->iso_cnt = hdev->iso_pkts;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1325,7 +1325,7 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct hci_cp_le_set_ext_adv_params cp;
|
||||
struct hci_rp_le_set_ext_adv_params rp;
|
||||
bool connectable;
|
||||
bool connectable, require_privacy;
|
||||
u32 flags;
|
||||
bdaddr_t random_addr;
|
||||
u8 own_addr_type;
|
||||
@@ -1363,10 +1363,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
return -EPERM;
|
||||
|
||||
/* Set require_privacy to true only when non-connectable
|
||||
* advertising is used. In that case it is fine to use a
|
||||
* non-resolvable private address.
|
||||
* advertising is used and it is not periodic.
|
||||
* In that case it is fine to use a non-resolvable private address.
|
||||
*/
|
||||
err = hci_get_random_address(hdev, !connectable,
|
||||
require_privacy = !connectable && !(adv && adv->periodic);
|
||||
|
||||
err = hci_get_random_address(hdev, require_privacy,
|
||||
adv_use_rpa(hdev, flags), adv,
|
||||
&own_addr_type, &random_addr);
|
||||
if (err < 0)
|
||||
|
||||
@@ -91,8 +91,8 @@ static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
|
||||
iso_sock_match_t match, void *data);
|
||||
|
||||
/* ---- ISO timers ---- */
|
||||
#define ISO_CONN_TIMEOUT (HZ * 40)
|
||||
#define ISO_DISCONN_TIMEOUT (HZ * 2)
|
||||
#define ISO_CONN_TIMEOUT secs_to_jiffies(20)
|
||||
#define ISO_DISCONN_TIMEOUT secs_to_jiffies(2)
|
||||
|
||||
static void iso_conn_free(struct kref *ref)
|
||||
{
|
||||
@@ -111,6 +111,8 @@ static void iso_conn_free(struct kref *ref)
|
||||
/* Ensure no more work items will run since hci_conn has been dropped */
|
||||
disable_delayed_work_sync(&conn->timeout_work);
|
||||
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
kfree(conn);
|
||||
}
|
||||
|
||||
@@ -367,7 +369,8 @@ static int iso_connect_bis(struct sock *sk)
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||
hcon = hci_bind_bis(hdev, &iso_pi(sk)->dst, iso_pi(sk)->bc_sid,
|
||||
&iso_pi(sk)->qos, iso_pi(sk)->base_len,
|
||||
iso_pi(sk)->base);
|
||||
iso_pi(sk)->base,
|
||||
READ_ONCE(sk->sk_sndtimeo));
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
@@ -376,7 +379,8 @@ static int iso_connect_bis(struct sock *sk)
|
||||
hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
iso_pi(sk)->bc_sid, &iso_pi(sk)->qos,
|
||||
iso_pi(sk)->base_len, iso_pi(sk)->base);
|
||||
iso_pi(sk)->base_len, iso_pi(sk)->base,
|
||||
READ_ONCE(sk->sk_sndtimeo));
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
@@ -458,11 +462,19 @@ static int iso_connect_cis(struct sock *sk)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Check if there are available buffers for output/TX. */
|
||||
if (iso_pi(sk)->qos.ucast.out.sdu && !hci_iso_count(hdev) &&
|
||||
(hdev->iso_pkts && !hdev->iso_cnt)) {
|
||||
err = -ENOBUFS;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Just bind if DEFER_SETUP has been set */
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||
hcon = hci_bind_cis(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
&iso_pi(sk)->qos);
|
||||
&iso_pi(sk)->qos,
|
||||
READ_ONCE(sk->sk_sndtimeo));
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
@@ -470,7 +482,8 @@ static int iso_connect_cis(struct sock *sk)
|
||||
} else {
|
||||
hcon = hci_connect_cis(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
&iso_pi(sk)->qos);
|
||||
&iso_pi(sk)->qos,
|
||||
READ_ONCE(sk->sk_sndtimeo));
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
@@ -750,6 +763,13 @@ static void iso_sock_kill(struct sock *sk)
|
||||
|
||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
|
||||
/* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
|
||||
if (iso_pi(sk)->conn) {
|
||||
iso_conn_lock(iso_pi(sk)->conn);
|
||||
iso_pi(sk)->conn->sk = NULL;
|
||||
iso_conn_unlock(iso_pi(sk)->conn);
|
||||
}
|
||||
|
||||
/* Kill poor orphan */
|
||||
bt_sock_unlink(&iso_sk_list, sk);
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
@@ -2407,7 +2427,7 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
conn->rx_len -= skb->len;
|
||||
return;
|
||||
break;
|
||||
|
||||
case ISO_END:
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
|
||||
@@ -4542,13 +4542,11 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_BT_FEATURE_DEBUG
|
||||
if (!hdev) {
|
||||
flags = bt_dbg_get() ? BIT(0) : 0;
|
||||
flags = bt_dbg_get() ? BIT(0) : 0;
|
||||
|
||||
memcpy(rp->features[idx].uuid, debug_uuid, 16);
|
||||
rp->features[idx].flags = cpu_to_le32(flags);
|
||||
idx++;
|
||||
}
|
||||
memcpy(rp->features[idx].uuid, debug_uuid, 16);
|
||||
rp->features[idx].flags = cpu_to_le32(flags);
|
||||
idx++;
|
||||
#endif
|
||||
|
||||
if (hdev && hci_dev_le_state_simultaneous(hdev)) {
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
#define HDEV_PARAM_U16(_param_name_) \
|
||||
struct {\
|
||||
struct mgmt_tlv entry; \
|
||||
struct mgmt_tlv_hdr entry; \
|
||||
__le16 value; \
|
||||
} __packed _param_name_
|
||||
|
||||
#define HDEV_PARAM_U8(_param_name_) \
|
||||
struct {\
|
||||
struct mgmt_tlv entry; \
|
||||
struct mgmt_tlv_hdr entry; \
|
||||
__u8 value; \
|
||||
} __packed _param_name_
|
||||
|
||||
|
||||
@@ -498,6 +498,13 @@ static void sco_sock_kill(struct sock *sk)
|
||||
|
||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
|
||||
/* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
|
||||
if (sco_pi(sk)->conn) {
|
||||
sco_conn_lock(sco_pi(sk)->conn);
|
||||
sco_pi(sk)->conn->sk = NULL;
|
||||
sco_conn_unlock(sco_pi(sk)->conn);
|
||||
}
|
||||
|
||||
/* Kill poor orphan */
|
||||
bt_sock_unlink(&sco_sk_list, sk);
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
|
||||
Reference in New Issue
Block a user