mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 09:20:13 -04:00
Merge tag 'for-net-next-2026-08-07' 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: - HCI: Add support for Shorter Connection Interval (SCI) feature - af_bluetooth: Add minimal context analysis annotations drivers: - btusb: Add ASUS USB-BT540 for Realtek 8761CU - btusb: Add ASUS USB-BT600 for Realtek 8761CU - btusb: Add USB ID 13d3:3625 for MediaTek MT7922 - btusb: Add support for 1357:c123 Realtek 8852BE device - btusb: Add new VID/PID 0x0489/0xe156 for MT7902 - btintel: Add Bluetooth SAR revision 2 support - btintel_pcie: Add vendor_reset PCI sysfs for PLDR - btnxpuart: Add M.2 Bluetooth device support using pwrseq * tag 'for-net-next-2026-08-07' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (84 commits) Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept Bluetooth: MSFT: validate evt_prefix_len against the response length Bluetooth: ISO: zero the sockaddr before returning it in getname Bluetooth: ISO: do not force BT_LISTEN after a failed BIG sync Bluetooth: hci_sync: Disable legacy instance's ext adv before setup snapshot Bluetooth: hci_event: fix out-of-bounds read in LE PA report reassembly Bluetooth: btmtksdio: fix usage_count leak when autosuspend_delay is negative Bluetooth: MGMT: reject HCI_CMD_SYNC params_len above 255 Bluetooth: btnxpuart: Add M.2 Bluetooth device support using pwrseq Bluetooth: MGMT: free the HCI command when it is cancelled Bluetooth: MGMT: free the mesh send cancel command when it is cancelled Bluetooth: hci_sync: free the advertising instance on the failure and cancel paths Bluetooth: hci_conn: fix the SCO setup context lifetime Bluetooth: hci_sync: Fix accept list UAF during suspend Bluetooth: hci_event: Use 255 as max event payload length in hci_ev_table[] Bluetooth: hci_event: Introduce handle_ev_vendor() for HCI_EV_VENDOR Bluetooth: btnxpuart: Simplify nxp_set_ind_reset() by __hci_reset_dev() Bluetooth: hci_core: Introduce __hci_reset_dev() with a hardware error code Bluetooth: coredump: Expose header size and end marker to drivers Bluetooth: btintel: Remove redundant (hdr->plen > 0) in btintel_recv_event() ... ==================== Link: https://patch.msgid.link/20260807200215.982570-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
15
Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
Normal file
15
Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
Normal file
@@ -0,0 +1,15 @@
|
||||
What: /sys/bus/pci/devices/<BDF>/vendor_reset
|
||||
Date: 22-Jul-2026
|
||||
KernelVersion: 6.17
|
||||
Contact: linux-bluetooth@vger.kernel.org
|
||||
Description: This read-write attribute allows userspace to trigger a
|
||||
Product Level Device Reset (PLDR) on Intel PCIe Bluetooth
|
||||
controllers. Reading the attribute displays the supported
|
||||
reset type. Writing integer 0 triggers PLDR. Any other
|
||||
input is rejected with -EINVAL.
|
||||
|
||||
PLDR resets the entire on-chip platform shared between
|
||||
Bluetooth and WiFi. This means any driver attached to
|
||||
the WiFi device that shares hardware with this Bluetooth
|
||||
device will be released, the platform will be reset, and
|
||||
both the Bluetooth and WiFi devices will be re-probed.
|
||||
@@ -4719,6 +4719,7 @@ S: Supported
|
||||
W: http://www.bluez.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
|
||||
F: Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
|
||||
F: Documentation/devicetree/bindings/net/bluetooth/
|
||||
F: drivers/bluetooth/
|
||||
|
||||
|
||||
@@ -50,3 +50,5 @@ hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_AML) += hci_aml.o
|
||||
hci_uart-objs := $(hci_uart-y)
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
@@ -301,6 +301,11 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
|
||||
return -EILSEQ;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
bt_dev_err(data->hdev, "unknown packet type 0x%02x",
|
||||
pkt_type);
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
|
||||
@@ -319,6 +324,13 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
|
||||
}
|
||||
}
|
||||
|
||||
if (len > skb_tailroom(data->reassembly)) {
|
||||
bt_dev_err(data->hdev, "block exceeds packet length");
|
||||
kfree_skb(data->reassembly);
|
||||
data->reassembly = NULL;
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
skb_put_data(data->reassembly, buf, len);
|
||||
|
||||
@@ -353,6 +365,13 @@ static void bfusb_rx_complete(struct urb *urb)
|
||||
skb_put(skb, count);
|
||||
|
||||
while (count) {
|
||||
if (count < 2) {
|
||||
bt_dev_err(data->hdev, "short block header");
|
||||
kfree_skb(data->reassembly);
|
||||
data->reassembly = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr = buf[0] | (buf[1] << 8);
|
||||
|
||||
if (hdr & 0x4000) {
|
||||
@@ -360,16 +379,28 @@ static void bfusb_rx_complete(struct urb *urb)
|
||||
count -= 2;
|
||||
buf += 2;
|
||||
} else {
|
||||
if (count < 3) {
|
||||
bt_dev_err(data->hdev, "short block header");
|
||||
kfree_skb(data->reassembly);
|
||||
data->reassembly = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
len = (buf[2] == 0) ? 256 : buf[2];
|
||||
count -= 3;
|
||||
buf += 3;
|
||||
}
|
||||
|
||||
if (count < len)
|
||||
if (count < len) {
|
||||
bt_dev_err(data->hdev, "block extends over URB buffer ranges");
|
||||
kfree_skb(data->reassembly);
|
||||
data->reassembly = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((hdr & 0xe1) == 0xc1)
|
||||
bfusb_recv_block(data, hdr, buf, len);
|
||||
if ((hdr & 0xe1) == 0xc1 &&
|
||||
bfusb_recv_block(data, hdr, buf, len) < 0)
|
||||
data->hdev->stat.err_rx++;
|
||||
|
||||
count -= len;
|
||||
buf += len;
|
||||
|
||||
@@ -51,6 +51,7 @@ enum {
|
||||
#define BTINTEL_BT_DOMAIN 0x12
|
||||
#define BTINTEL_SAR_LEGACY 0
|
||||
#define BTINTEL_SAR_INC_PWR 1
|
||||
#define BTINTEL_SAR_REV2 2
|
||||
#define BTINTEL_SAR_INC_PWR_SUPPORTED 0
|
||||
|
||||
#define CMD_WRITE_BOOT_PARAMS 0xfc0e
|
||||
@@ -2745,9 +2746,7 @@ static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
* based on their connection handle value range.
|
||||
*/
|
||||
if (iso_capable(hdev) && hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) {
|
||||
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
|
||||
|
||||
if (hci_handle(handle) >= BTINTEL_ISODATA_HANDLE_BASE)
|
||||
if (hci_acl_handle(skb) >= BTINTEL_ISODATA_HANDLE_BASE)
|
||||
return HCI_ISODATA_PKT;
|
||||
}
|
||||
|
||||
@@ -3104,6 +3103,111 @@ static int btintel_set_mutual_sar(struct hci_dev *hdev, struct btintel_sar_inc_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* btintel_send_sar_rev2_band - send DDC command for one Rev2 sub-band
|
||||
*
|
||||
* Each DDC 0x0311-0x0316 carries 2 bytes: [ChainA_value, ChainB_value].
|
||||
* cmd->len = 4 (2 id + 2 data)
|
||||
* HCI total = 5 bytes (1 len + 4)
|
||||
*/
|
||||
static int btintel_send_sar_rev2_band(struct hci_dev *hdev,
|
||||
struct btintel_cp_ddc_write *cmd,
|
||||
u16 id, u8 chain_a, u8 chain_b)
|
||||
{
|
||||
cmd->len = 4;
|
||||
cmd->id = cpu_to_le16(id);
|
||||
cmd->data[0] = chain_a;
|
||||
cmd->data[1] = chain_b;
|
||||
return btintel_send_sar_ddc(hdev, cmd, 5);
|
||||
}
|
||||
|
||||
static int btintel_set_sar_rev2(struct hci_dev *hdev,
|
||||
struct btintel_sar_rev2 *sar)
|
||||
{
|
||||
struct btintel_cp_ddc_write *cmd;
|
||||
struct sk_buff *skb;
|
||||
u8 buffer[64];
|
||||
u8 enable;
|
||||
int ret;
|
||||
|
||||
cmd = (void *)buffer;
|
||||
|
||||
/* DDC 0x019e: enable/disable increased power mode SAR (1 byte) */
|
||||
cmd->len = 3;
|
||||
cmd->id = cpu_to_le16(0x019e);
|
||||
cmd->data[0] = (sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED) ?
|
||||
0x01 : 0x00;
|
||||
ret = btintel_send_sar_ddc(hdev, cmd, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* DDC 0x0311-0x0316: per sub-band ChainA + ChainB limits */
|
||||
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0311,
|
||||
sar->chain_a.subband_2g4,
|
||||
sar->chain_b.subband_2g4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0312,
|
||||
sar->chain_a.subband_5g2,
|
||||
sar->chain_b.subband_5g2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* 0x0313 and 0x0314 both carry the 5G8/5G9 value */
|
||||
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0313,
|
||||
sar->chain_a.subband_5g8_5g9,
|
||||
sar->chain_b.subband_5g8_5g9);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0314,
|
||||
sar->chain_a.subband_5g8_5g9,
|
||||
sar->chain_b.subband_5g8_5g9);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0315,
|
||||
sar->chain_a.subband_6g1,
|
||||
sar->chain_b.subband_6g1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0316,
|
||||
sar->chain_a.subband_6g3,
|
||||
sar->chain_b.subband_6g3);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Notify firmware that SAR initialisation is complete */
|
||||
enable = 0x01;
|
||||
skb = __hci_cmd_sync(hdev, 0xfe25, sizeof(enable), &enable, HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_warn(hdev, "Failed to send Intel SAR Rev2 Enable (%ld)",
|
||||
PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btintel_sar_rev2_send_to_device(struct hci_dev *hdev,
|
||||
struct btintel_sar_rev2 *sar,
|
||||
struct intel_version_tlv *ver)
|
||||
{
|
||||
u16 cnvi = ver->cnvi_top & 0xfff;
|
||||
u16 cnvr = ver->cnvr_top & 0xfff;
|
||||
|
||||
if (cnvi < BTINTEL_CNVI_BLAZARI || cnvr != BTINTEL_CNVR_WHP2) {
|
||||
bt_dev_dbg(hdev, "BT SAR Rev2 not supported on this platform (cnvi=0x%x cnvr=0x%x)",
|
||||
cnvi, cnvr);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "Applying Bluetooth SAR Rev2");
|
||||
return btintel_set_sar_rev2(hdev, sar);
|
||||
}
|
||||
|
||||
static int btintel_sar_send_to_device(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar,
|
||||
struct intel_version_tlv *ver)
|
||||
{
|
||||
@@ -3130,6 +3234,7 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv *
|
||||
{
|
||||
union acpi_object *bt_pkg, *buffer = NULL;
|
||||
struct btintel_sar_inc_pwr sar;
|
||||
struct btintel_sar_rev2 sar_rev2;
|
||||
acpi_status status;
|
||||
u8 revision;
|
||||
int ret;
|
||||
@@ -3150,14 +3255,96 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv *
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buffer->package.elements[0].type != ACPI_TYPE_INTEGER) {
|
||||
bt_dev_warn(hdev, "BT_SAR: unexpected ACPI type for revision field");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
revision = buffer->package.elements[0].integer.value;
|
||||
|
||||
if (revision > BTINTEL_SAR_INC_PWR) {
|
||||
if (revision > BTINTEL_SAR_REV2) {
|
||||
bt_dev_dbg(hdev, "BT_SAR: revision: 0x%2.2x not supported", revision);
|
||||
ret = -EOPNOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (revision == BTINTEL_SAR_REV2 && bt_pkg->package.count == 13) {
|
||||
/* Element layout: 0 = domain ID (BTINTEL_BT_DOMAIN, 0x12),
|
||||
* 1 = bt_sar_bios (u32), 2 = inc_power_mode (u32),
|
||||
* 3..12 = per-chain sub-band limits (u8 each).
|
||||
*/
|
||||
static const u64 rev2_max[13] = {
|
||||
U8_MAX, /* domain ID */
|
||||
U32_MAX, U32_MAX, /* bt_sar_bios, inc_power_mode */
|
||||
U8_MAX, U8_MAX, U8_MAX, U8_MAX, U8_MAX, /* chain A */
|
||||
U8_MAX, U8_MAX, U8_MAX, U8_MAX, U8_MAX, /* chain B */
|
||||
};
|
||||
union acpi_object *e;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
e = &bt_pkg->package.elements[i];
|
||||
if (e->type != ACPI_TYPE_INTEGER) {
|
||||
bt_dev_warn(hdev, "BT SAR Rev2: unexpected ACPI type at element %d",
|
||||
i);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (e->integer.value > rev2_max[i]) {
|
||||
bt_dev_warn(hdev, "BT SAR Rev2: element %d value 0x%llx out of range",
|
||||
i, e->integer.value);
|
||||
ret = -ERANGE;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&sar_rev2, 0, sizeof(sar_rev2));
|
||||
sar_rev2.revision = revision;
|
||||
sar_rev2.bt_sar_bios = bt_pkg->package.elements[1].integer.value;
|
||||
|
||||
if (sar_rev2.bt_sar_bios != 1) {
|
||||
bt_dev_warn(hdev, "Bluetooth SAR Rev2 is not enabled");
|
||||
ret = -EOPNOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
|
||||
sar_rev2.inc_power_mode = bt_pkg->package.elements[2].integer.value;
|
||||
|
||||
sar_rev2.chain_a.subband_2g4 = bt_pkg->package.elements[3].integer.value;
|
||||
sar_rev2.chain_a.subband_5g2 = bt_pkg->package.elements[4].integer.value;
|
||||
sar_rev2.chain_a.subband_5g8_5g9 = bt_pkg->package.elements[5].integer.value;
|
||||
sar_rev2.chain_a.subband_6g1 = bt_pkg->package.elements[6].integer.value;
|
||||
sar_rev2.chain_a.subband_6g3 = bt_pkg->package.elements[7].integer.value;
|
||||
|
||||
sar_rev2.chain_b.subband_2g4 = bt_pkg->package.elements[8].integer.value;
|
||||
sar_rev2.chain_b.subband_5g2 = bt_pkg->package.elements[9].integer.value;
|
||||
sar_rev2.chain_b.subband_5g8_5g9 = bt_pkg->package.elements[10].integer.value;
|
||||
sar_rev2.chain_b.subband_6g1 = bt_pkg->package.elements[11].integer.value;
|
||||
sar_rev2.chain_b.subband_6g3 = bt_pkg->package.elements[12].integer.value;
|
||||
|
||||
bt_dev_dbg(hdev, "BT SAR Rev2: revision=%u bt_sar_bios=%u inc_power_mode=%u",
|
||||
sar_rev2.revision, sar_rev2.bt_sar_bios, sar_rev2.inc_power_mode);
|
||||
bt_dev_dbg(hdev, "BT SAR Rev2 Chain A: 2g4=%u 5g2=%u 5g8_5g9=%u 6g1=%u 6g3=%u",
|
||||
sar_rev2.chain_a.subband_2g4, sar_rev2.chain_a.subband_5g2,
|
||||
sar_rev2.chain_a.subband_5g8_5g9, sar_rev2.chain_a.subband_6g1,
|
||||
sar_rev2.chain_a.subband_6g3);
|
||||
bt_dev_dbg(hdev, "BT SAR Rev2 Chain B: 2g4=%u 5g2=%u 5g8_5g9=%u 6g1=%u 6g3=%u",
|
||||
sar_rev2.chain_b.subband_2g4, sar_rev2.chain_b.subband_5g2,
|
||||
sar_rev2.chain_b.subband_5g8_5g9, sar_rev2.chain_b.subband_6g1,
|
||||
sar_rev2.chain_b.subband_6g3);
|
||||
|
||||
ret = btintel_sar_rev2_send_to_device(hdev, &sar_rev2, ver);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (revision == BTINTEL_SAR_REV2) {
|
||||
bt_dev_warn(hdev, "BT SAR Rev2: unexpected ACPI package count %d (expected 13)",
|
||||
bt_pkg->package.count);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(&sar, 0, sizeof(sar));
|
||||
|
||||
if (revision == BTINTEL_SAR_LEGACY && bt_pkg->package.count == 8) {
|
||||
@@ -3804,8 +3991,7 @@ int btintel_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 };
|
||||
|
||||
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
|
||||
hdr->plen > 0) {
|
||||
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff) {
|
||||
const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
|
||||
unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
|
||||
|
||||
@@ -3834,7 +4020,7 @@ int btintel_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) &&
|
||||
if (len + 1 >= sizeof(diagnostics_hdr) &&
|
||||
memcmp(&skb->data[2], diagnostics_hdr,
|
||||
sizeof(diagnostics_hdr)) == 0) {
|
||||
return btintel_diagnostics(hdev, skb);
|
||||
|
||||
@@ -65,6 +65,7 @@ struct intel_tlv {
|
||||
|
||||
/* CNVR */
|
||||
#define BTINTEL_CNVR_FMP2 0x910
|
||||
#define BTINTEL_CNVR_WHP2 0xA10 /* Whale Peak2 - Panther Lake */
|
||||
|
||||
#define BTINTEL_IMG_BOOTLOADER 0x01 /* Bootloader image */
|
||||
#define BTINTEL_IMG_IML 0x02 /* Intermediate image */
|
||||
@@ -204,6 +205,23 @@ struct btintel_sar_inc_pwr {
|
||||
u8 le_lr;
|
||||
};
|
||||
|
||||
/* Bluetooth SAR feature (BRDS), Revision 2 - per-chain sub-band power limits */
|
||||
struct btintel_sar_band_limits {
|
||||
u8 subband_2g4;
|
||||
u8 subband_5g2;
|
||||
u8 subband_5g8_5g9;
|
||||
u8 subband_6g1;
|
||||
u8 subband_6g3;
|
||||
};
|
||||
|
||||
struct btintel_sar_rev2 {
|
||||
u8 revision;
|
||||
u32 bt_sar_bios; /* 1: BIOS-managed SAR enabled */
|
||||
u32 inc_power_mode; /* 0: supported, 1: disabled */
|
||||
struct btintel_sar_band_limits chain_a;
|
||||
struct btintel_sar_band_limits chain_b;
|
||||
};
|
||||
|
||||
#define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
|
||||
#define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16))
|
||||
#define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff)
|
||||
|
||||
@@ -1446,72 +1446,134 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Queue a coredump dump_traces() pass.
|
||||
*
|
||||
* Returns true if a new coredump was queued, false if one was already
|
||||
* in-flight (the BTINTEL_PCIE_COREDUMP_INPROGRESS bit serves as the
|
||||
* single-writer guard for the @coredump_work item) or the workqueue is
|
||||
* disabled (reset / remove in progress).
|
||||
*
|
||||
* Always queue this AFTER any companion event-reader work (hwexp /
|
||||
* fwtrigger) so that, on the ordered @dump_workqueue, the event reader
|
||||
* runs first and populates dmp_hdr.event_type / event_id before
|
||||
* dump_traces consumes them.
|
||||
*/
|
||||
static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data,
|
||||
u16 trigger_reason)
|
||||
{
|
||||
if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
return false;
|
||||
|
||||
data->dmp_hdr.trigger_reason = trigger_reason;
|
||||
|
||||
if (queue_work(data->dump_workqueue, &data->coredump_work))
|
||||
return true;
|
||||
|
||||
/* Workqueue is disabled (reset/remove drained it). Release the
|
||||
* guard so a later trigger, after re-probe, can succeed.
|
||||
*/
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data)
|
||||
{
|
||||
bt_dev_dbg(data->hdev, "Received firmware smart trigger cause");
|
||||
|
||||
if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags))
|
||||
/* Per-work guard: deduplicate concurrent FW-trigger interrupts.
|
||||
* Cleared at the tail of btintel_pcie_fwtrigger_worker().
|
||||
*/
|
||||
if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
|
||||
&data->flags))
|
||||
return;
|
||||
|
||||
/* Trigger device core dump when there is FW assert */
|
||||
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
|
||||
if (!queue_work(data->dump_workqueue, &data->fwtrigger_work)) {
|
||||
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_work(data->coredump_workqueue, &data->coredump_work);
|
||||
/* Queue coredump after the fwtrigger event reader so dmp_hdr.event_*
|
||||
* is populated before dump_traces consumes it.
|
||||
*/
|
||||
btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
|
||||
}
|
||||
|
||||
static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
|
||||
{
|
||||
bt_dev_err(data->hdev, "Received hw exception interrupt");
|
||||
|
||||
/* CORE_HALTED is the single-writer guard for this handler. It is
|
||||
* set once on first HW exception and cleared only by re-probe
|
||||
* (data is reallocated), so it also serializes hwexp_work
|
||||
* scheduling without needing a separate bit.
|
||||
*/
|
||||
if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags))
|
||||
return;
|
||||
/* Queue companion coredump first so it is appended after hwexp_work
|
||||
* on the ordered @dump_workqueue (preserves the original
|
||||
* coredump-then-hwexp ordering).
|
||||
*/
|
||||
btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
|
||||
|
||||
/* Trigger device core dump when there is HW exception */
|
||||
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
|
||||
|
||||
queue_work(data->coredump_workqueue, &data->coredump_work);
|
||||
queue_work(data->dump_workqueue, &data->hwexp_work);
|
||||
}
|
||||
|
||||
static void btintel_pcie_coredump_worker(struct work_struct *work)
|
||||
{
|
||||
struct btintel_pcie_data *data = container_of(work,
|
||||
struct btintel_pcie_data, coredump_work);
|
||||
int err;
|
||||
|
||||
/* hdev is NULL until setup_hdev() succeeds, and is cleared on
|
||||
* teardown after disable_work_sync() drains us; bail in that case.
|
||||
*/
|
||||
if (!data->hdev)
|
||||
goto out;
|
||||
|
||||
btintel_pcie_dump_traces(data->hdev);
|
||||
out:
|
||||
/* Release guard last so a new trigger can run only after this
|
||||
* pass has fully completed (including dev_coredumpv()).
|
||||
*/
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
|
||||
static void btintel_pcie_hwexp_worker(struct work_struct *work)
|
||||
{
|
||||
struct btintel_pcie_data *data = container_of(work,
|
||||
struct btintel_pcie_data, hwexp_work);
|
||||
|
||||
if (!data->hdev)
|
||||
return;
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) {
|
||||
err = btintel_pcie_dump_fwtrigger_event(data);
|
||||
if (err)
|
||||
bt_dev_warn(data->hdev, "failed to log fwtrigger event");
|
||||
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
/* Unlike usb products, controller will not send hardware exception
|
||||
* event on exception. Instead controller writes the hardware event
|
||||
* to device memory along with optional debug events, raises MSIX
|
||||
* and halts. Driver shall read the exception event from device
|
||||
* memory and passes it to the stack for further processing.
|
||||
*
|
||||
* Re-entry is gated by BTINTEL_PCIE_CORE_HALTED in the IRQ
|
||||
* handler, which is only cleared by re-probe; no per-work bit
|
||||
* is needed here.
|
||||
*/
|
||||
btintel_pcie_read_hwexp(data);
|
||||
}
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
|
||||
btintel_pcie_dump_traces(data->hdev);
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
static void btintel_pcie_fwtrigger_worker(struct work_struct *work)
|
||||
{
|
||||
struct btintel_pcie_data *data = container_of(work,
|
||||
struct btintel_pcie_data, fwtrigger_work);
|
||||
int err;
|
||||
|
||||
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
|
||||
* hardware event to device memory along with optional debug
|
||||
* events, raises MSIX and halts. Driver shall read the
|
||||
* exception event from device memory and passes it stack for
|
||||
* further processing.
|
||||
*/
|
||||
btintel_pcie_read_hwexp(data);
|
||||
clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
|
||||
}
|
||||
if (!data->hdev)
|
||||
goto out;
|
||||
|
||||
err = btintel_pcie_dump_fwtrigger_event(data);
|
||||
if (err)
|
||||
bt_dev_warn(data->hdev, "failed to log fwtrigger event");
|
||||
out:
|
||||
/* Release guard last; matches set in fw_trigger handler. */
|
||||
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
|
||||
static void btintel_pcie_rx_work(struct work_struct *work)
|
||||
@@ -2363,7 +2425,6 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
|
||||
INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
|
||||
err = -EINVAL;
|
||||
goto exit_error;
|
||||
break;
|
||||
}
|
||||
|
||||
data->dmp_hdr.cnvi_top = ver_tlv.cnvi_top;
|
||||
@@ -2488,8 +2549,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
|
||||
}
|
||||
}
|
||||
|
||||
static void btintel_pcie_reset(struct hci_dev *hdev);
|
||||
|
||||
static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
|
||||
{
|
||||
union acpi_object *obj, argv4;
|
||||
@@ -2650,20 +2709,22 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
|
||||
btintel_pcie_synchronize_irqs(data);
|
||||
|
||||
flush_work(&data->rx_work);
|
||||
/* Drain any in-flight coredump and block new ones across reset.
|
||||
* Safe from self-deadlock: coredump_work runs on a separate wq.
|
||||
/* Drain any in-flight dump workers and block new ones across reset.
|
||||
* Safe from self-deadlock: they all run on a separate wq.
|
||||
*/
|
||||
disable_work_sync(&data->coredump_work);
|
||||
disable_work_sync(&data->hwexp_work);
|
||||
disable_work_sync(&data->fwtrigger_work);
|
||||
|
||||
bt_dev_dbg(data->hdev, "Release bluetooth interface");
|
||||
|
||||
/* Both reset paths follow the same contract: on success they
|
||||
* destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs
|
||||
* the coredump_work with disable count 0), so enable_work() must
|
||||
* the dump workers with disable count 0), so enable_work() must
|
||||
* NOT be called on the success path. Only the FLR path can fail
|
||||
* with 'data' still alive, in which case we balance the
|
||||
* disable_work_sync() above so a later successful reset is not
|
||||
* permanently blocked.
|
||||
* disable_work_sync() calls above so a later successful reset is
|
||||
* not permanently blocked.
|
||||
*
|
||||
* pci_lock_rescan_remove() (held above) serializes against PCI
|
||||
* device addition/removal (hotplug), so no device can be added to
|
||||
@@ -2674,64 +2735,134 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (btintel_pcie_perform_flr(data))
|
||||
if (btintel_pcie_perform_flr(data)) {
|
||||
enable_work(&data->coredump_work);
|
||||
enable_work(&data->hwexp_work);
|
||||
enable_work(&data->fwtrigger_work);
|
||||
}
|
||||
|
||||
out:
|
||||
pci_dev_put(pdev);
|
||||
pci_unlock_rescan_remove();
|
||||
}
|
||||
|
||||
static void btintel_pcie_reset(struct hci_dev *hdev)
|
||||
/* Schedule a device reset of the requested type.
|
||||
*
|
||||
* BTINTEL_PCIE_RECOVERY_IN_PROGRESS serializes all reset requesters
|
||||
* (sysfs reset attribute, hci_cmd_timeout(), hw_error, resume error
|
||||
* path, etc.) so that:
|
||||
*
|
||||
* - dev_data->reset_type is written by exactly one caller (the
|
||||
* thread that wins test_and_set_bit), eliminating the race where
|
||||
* a second hw_error could clobber an already-scheduled reset's
|
||||
* type;
|
||||
* - the write happens AFTER the bit is set, so reset_work observes
|
||||
* it through schedule_work()'s memory ordering;
|
||||
* - losers return without touching reset_type or scheduling the
|
||||
* work, so concurrent triggers are silently coalesced into the
|
||||
* in-flight one (whose recovery will reinitialize the device
|
||||
* regardless of the dropped trigger's variant).
|
||||
*
|
||||
* The bit is cleared only by .remove() / re-probe via fresh devm
|
||||
* allocation, which is the intended one-shot semantics: a reset
|
||||
* tears down and re-probes 'data', so there is no "in-flight"
|
||||
* reset to follow up after device_reprobe() succeeds.
|
||||
*/
|
||||
static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
|
||||
enum btintel_pcie_reset_type type)
|
||||
{
|
||||
struct btintel_pcie_data *data;
|
||||
|
||||
data = hci_get_drvdata(hdev);
|
||||
|
||||
if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
|
||||
return;
|
||||
|
||||
data->reset_type = type;
|
||||
|
||||
pci_dev_get(data->pdev);
|
||||
schedule_work(&data->reset_work);
|
||||
}
|
||||
|
||||
static void btintel_pcie_hci_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
|
||||
}
|
||||
|
||||
static ssize_t vendor_reset_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned int val;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct btintel_pcie_data *data = pci_get_drvdata(pdev);
|
||||
|
||||
if (!data || !data->hdev)
|
||||
return -ENODEV;
|
||||
|
||||
if (kstrtouint(buf, 10, &val) || val != 0) {
|
||||
bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bt_dev_info(data->hdev, "PLDR triggered via sysfs");
|
||||
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t vendor_reset_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "0 - PLDR\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(vendor_reset);
|
||||
|
||||
static struct attribute *btintel_pcie_attrs[] = {
|
||||
&dev_attr_vendor_reset.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(btintel_pcie);
|
||||
|
||||
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
|
||||
{
|
||||
struct btintel_pcie_dev_recovery *data;
|
||||
struct btintel_pcie_dev_recovery *rec;
|
||||
struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev);
|
||||
struct pci_dev *pdev = dev_data->pdev;
|
||||
enum btintel_pcie_reset_type type;
|
||||
time64_t retry_window;
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &dev_data->flags))
|
||||
return;
|
||||
|
||||
btintel_pcie_dump_debug_registers(hdev);
|
||||
|
||||
data = btintel_pcie_get_recovery(pdev, &hdev->dev);
|
||||
if (!data)
|
||||
rec = btintel_pcie_get_recovery(pdev, &hdev->dev);
|
||||
if (!rec)
|
||||
return;
|
||||
|
||||
if (code == 0x13)
|
||||
dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR;
|
||||
else
|
||||
dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
|
||||
type = (code == 0x13) ? BTINTEL_PCIE_IOSF_PRR_PLDR
|
||||
: BTINTEL_PCIE_IOSF_PRR_FLR;
|
||||
|
||||
bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code,
|
||||
dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
|
||||
retry_window = ktime_get_boottime_seconds() - data->last_error;
|
||||
type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
|
||||
retry_window = ktime_get_boottime_seconds() - rec->last_error;
|
||||
|
||||
if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
|
||||
data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
|
||||
rec->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
|
||||
bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d",
|
||||
BTINTEL_PCIE_FLR_MAX_RETRY, data->count);
|
||||
BTINTEL_PCIE_FLR_MAX_RETRY, rec->count);
|
||||
bt_dev_dbg(hdev, "Boot time: %lld seconds",
|
||||
ktime_get_boottime_seconds());
|
||||
bt_dev_dbg(hdev, "last error at: %lld seconds",
|
||||
data->last_error);
|
||||
rec->last_error);
|
||||
return;
|
||||
}
|
||||
btintel_pcie_inc_recovery_count(pdev, &hdev->dev);
|
||||
btintel_pcie_reset(hdev);
|
||||
btintel_pcie_request_reset(dev_data, type);
|
||||
}
|
||||
|
||||
static bool btintel_pcie_wakeup(struct hci_dev *hdev)
|
||||
@@ -2821,7 +2952,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
|
||||
hdev->hw_error = btintel_pcie_hw_error;
|
||||
hdev->set_diag = btintel_set_diag;
|
||||
hdev->set_bdaddr = btintel_set_bdaddr;
|
||||
hdev->reset = btintel_pcie_reset;
|
||||
hdev->reset = btintel_pcie_hci_reset;
|
||||
hdev->wakeup = btintel_pcie_wakeup;
|
||||
hdev->hci_drv = &btintel_pcie_hci_drv;
|
||||
|
||||
@@ -2869,8 +3000,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
|
||||
if (!data->workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
|
||||
if (!data->coredump_workqueue) {
|
||||
data->dump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
|
||||
if (!data->dump_workqueue) {
|
||||
destroy_workqueue(data->workqueue);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -2879,6 +3010,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
|
||||
INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
|
||||
INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
|
||||
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
|
||||
INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
|
||||
INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker);
|
||||
|
||||
data->boot_stage_cache = 0x00;
|
||||
data->img_resp_cache = 0x00;
|
||||
@@ -2921,7 +3054,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
|
||||
/* reset device before exit */
|
||||
btintel_pcie_reset_bt(data);
|
||||
|
||||
destroy_workqueue(data->coredump_workqueue);
|
||||
destroy_workqueue(data->dump_workqueue);
|
||||
|
||||
pci_clear_master(pdev);
|
||||
|
||||
@@ -2940,12 +3073,14 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Permanently block coredump triggers and drain the worker before
|
||||
* tearing down. Must run before cancel_work_sync(&reset_work) so
|
||||
* the disable counter stays >= 1 even after reset_work()'s
|
||||
/* Permanently block all dump triggers and drain the workers before
|
||||
* tearing down. Must run before disable_work_sync(&reset_work) so
|
||||
* the disable counters stay >= 1 even after reset_work()'s
|
||||
* balanced enable_work() (counter 2 -> 1, never reaching 0).
|
||||
*/
|
||||
disable_work_sync(&data->coredump_work);
|
||||
disable_work_sync(&data->hwexp_work);
|
||||
disable_work_sync(&data->fwtrigger_work);
|
||||
|
||||
/* Cancel pending reset work. Skip only when remove() is called from
|
||||
* within the reset work itself (PLDR device_reprobe path) to avoid
|
||||
@@ -2973,7 +3108,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
|
||||
|
||||
btintel_pcie_release_hdev(data);
|
||||
|
||||
destroy_workqueue(data->coredump_workqueue);
|
||||
destroy_workqueue(data->dump_workqueue);
|
||||
destroy_workqueue(data->workqueue);
|
||||
|
||||
btintel_pcie_free(data);
|
||||
@@ -2992,16 +3127,8 @@ static void btintel_pcie_coredump(struct device *dev)
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
return;
|
||||
|
||||
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
|
||||
/* queue_work() returns false if the work is disabled (reset or
|
||||
* remove in progress); clear the in-progress bit so a later
|
||||
* trigger can succeed once the work is re-enabled.
|
||||
*/
|
||||
if (!queue_work(data->coredump_workqueue, &data->coredump_work))
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
btintel_pcie_queue_coredump(data,
|
||||
BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3113,8 +3240,7 @@ static int btintel_pcie_resume(struct device *dev)
|
||||
if (data->pm_sx_event == PM_EVENT_FREEZE ||
|
||||
data->pm_sx_event == PM_EVENT_HIBERNATE) {
|
||||
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
|
||||
data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
|
||||
btintel_pcie_reset(data->hdev);
|
||||
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3138,14 +3264,10 @@ static int btintel_pcie_resume(struct device *dev)
|
||||
if (btintel_pcie_in_error(data) ||
|
||||
btintel_pcie_in_device_halt(data)) {
|
||||
bt_dev_err(data->hdev, "Controller in error state for D0 entry");
|
||||
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS,
|
||||
&data->flags)) {
|
||||
data->dmp_hdr.trigger_reason =
|
||||
BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
|
||||
queue_work(data->coredump_workqueue, &data->coredump_work);
|
||||
}
|
||||
btintel_pcie_queue_coredump(data,
|
||||
BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT);
|
||||
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
|
||||
btintel_pcie_reset(data->hdev);
|
||||
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -3165,6 +3287,7 @@ static struct pci_driver btintel_pcie_driver = {
|
||||
.probe = btintel_pcie_probe,
|
||||
.remove = btintel_pcie_remove,
|
||||
.driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
|
||||
.dev_groups = btintel_pcie_groups,
|
||||
#ifdef CONFIG_DEV_COREDUMP
|
||||
.driver.coredump = btintel_pcie_coredump
|
||||
#endif
|
||||
|
||||
@@ -118,7 +118,6 @@ enum {
|
||||
|
||||
enum {
|
||||
BTINTEL_PCIE_CORE_HALTED,
|
||||
BTINTEL_PCIE_HWEXP_INPROGRESS,
|
||||
BTINTEL_PCIE_COREDUMP_INPROGRESS,
|
||||
BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
|
||||
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
|
||||
@@ -466,8 +465,11 @@ struct btintel_pcie_dump_header {
|
||||
* @workqueue: workqueue for RX work
|
||||
* @rx_skb_q: SKB queue for RX packet
|
||||
* @rx_work: RX work struct to process the RX packet in @rx_skb_q
|
||||
* @coredump_workqueue: dedicated workqueue for coredump collection
|
||||
* @coredump_work: work struct for coredump trace collection
|
||||
* @dump_workqueue: dedicated ordered workqueue serializing the coredump,
|
||||
* hardware exception, and firmware-trigger dump workers
|
||||
* @coredump_work: work struct for DRAM trace coredump collection
|
||||
* @hwexp_work: work struct for hardware exception event read
|
||||
* @fwtrigger_work: work struct for firmware-triggered diagnostic event read
|
||||
* @dma_pool: DMA pool for descriptors, index array and ci
|
||||
* @dma_p_addr: DMA address for pool
|
||||
* @dma_v_addr: address of pool
|
||||
@@ -516,8 +518,10 @@ struct btintel_pcie_data {
|
||||
struct work_struct rx_work;
|
||||
struct work_struct reset_work;
|
||||
|
||||
struct workqueue_struct *coredump_workqueue;
|
||||
struct workqueue_struct *dump_workqueue;
|
||||
struct work_struct coredump_work;
|
||||
struct work_struct hwexp_work;
|
||||
struct work_struct fwtrigger_work;
|
||||
|
||||
struct dma_pool *dma_pool;
|
||||
dma_addr_t dma_p_addr;
|
||||
|
||||
@@ -43,10 +43,17 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||
|
||||
if (skb->len < sizeof(*hdr))
|
||||
return true;
|
||||
|
||||
if (hdr->evt == HCI_EV_CMD_COMPLETE) {
|
||||
struct hci_ev_cmd_complete *ec;
|
||||
u16 opcode;
|
||||
|
||||
if (hdr->plen < sizeof(*ec) ||
|
||||
skb->len < HCI_EVENT_HDR_SIZE + sizeof(*ec))
|
||||
return true;
|
||||
|
||||
ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
|
||||
opcode = __le16_to_cpu(ec->opcode);
|
||||
|
||||
@@ -74,6 +81,9 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
struct btmrvl_event *event;
|
||||
int ret = 0;
|
||||
|
||||
if (skb->len <= offsetof(typeof(*event), data[0]))
|
||||
return -EINVAL;
|
||||
|
||||
event = (struct btmrvl_event *) skb->data;
|
||||
if (event->ec != 0xff) {
|
||||
BT_DBG("Not Marvell Event=%x", event->ec);
|
||||
@@ -83,6 +93,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
|
||||
switch (event->data[0]) {
|
||||
case BT_EVENT_AUTO_SLEEP_MODE:
|
||||
if (skb->len <= offsetof(typeof(*event), data[2]))
|
||||
return -EINVAL;
|
||||
if (!event->data[2]) {
|
||||
if (event->data[1] == BT_PS_ENABLE)
|
||||
adapter->psmode = 1;
|
||||
@@ -96,6 +108,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
break;
|
||||
|
||||
case BT_EVENT_HOST_SLEEP_CONFIG:
|
||||
if (skb->len <= offsetof(typeof(*event), data[3]))
|
||||
return -EINVAL;
|
||||
if (!event->data[3])
|
||||
BT_DBG("gpio=%x, gap=%x", event->data[1],
|
||||
event->data[2]);
|
||||
@@ -104,6 +118,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
break;
|
||||
|
||||
case BT_EVENT_HOST_SLEEP_ENABLE:
|
||||
if (skb->len <= offsetof(typeof(*event), data[1]))
|
||||
return -EINVAL;
|
||||
if (!event->data[1]) {
|
||||
adapter->hs_state = HS_ACTIVATED;
|
||||
if (adapter->psmode)
|
||||
@@ -116,6 +132,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
break;
|
||||
|
||||
case BT_EVENT_MODULE_CFG_REQ:
|
||||
if (skb->len <= offsetof(typeof(*event), data[2]))
|
||||
return -EINVAL;
|
||||
if (priv->btmrvl_dev.sendcmdflag &&
|
||||
event->data[1] == MODULE_BRINGUP_REQ) {
|
||||
BT_DBG("EVENT:%s",
|
||||
@@ -133,6 +151,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
||||
break;
|
||||
|
||||
case BT_EVENT_POWER_STATE:
|
||||
if (skb->len <= offsetof(typeof(*event), data[1]))
|
||||
return -EINVAL;
|
||||
if (event->data[1] == BT_PS_SLEEP)
|
||||
adapter->ps_state = PS_SLEEP;
|
||||
BT_DBG("EVENT:%s",
|
||||
|
||||
@@ -799,7 +799,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
||||
skb_pull(skb, SDIO_HEADER_LEN);
|
||||
|
||||
if (btmrvl_process_event(priv, skb))
|
||||
hci_recv_frame(hdev, skb);
|
||||
kfree_skb(skb);
|
||||
|
||||
hdev->stat.byte_rx += buf_len;
|
||||
break;
|
||||
|
||||
@@ -1480,6 +1480,9 @@ static void btmtksdio_remove(struct sdio_func *func)
|
||||
if (test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
|
||||
btmtksdio_close(hdev);
|
||||
|
||||
if (bdev->data->pm_runtime_supported)
|
||||
pm_runtime_dont_use_autosuspend(bdev->dev);
|
||||
|
||||
/* Be consistent the state in btmtksdio_probe */
|
||||
pm_runtime_get_noresume(bdev->dev);
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pwrseq/consumer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/firmware.h>
|
||||
@@ -211,6 +213,7 @@ struct btnxpuart_dev {
|
||||
|
||||
struct ps_data psdata;
|
||||
struct btnxpuart_data *nxp_data;
|
||||
struct pwrseq_desc *pwrseq;
|
||||
struct reset_control *pdn;
|
||||
struct hci_uart hu;
|
||||
};
|
||||
@@ -1331,19 +1334,7 @@ static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
|
||||
|
||||
static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
static const u8 ir_hw_err[] = { HCI_EV_HARDWARE_ERROR,
|
||||
0x01, BTNXPUART_IR_HW_ERR };
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(3, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
|
||||
skb_put_data(skb, ir_hw_err, 3);
|
||||
|
||||
/* Inject Hardware Error to upper stack */
|
||||
return hci_recv_frame(hdev, skb);
|
||||
return __hci_reset_dev(hdev, BTNXPUART_IR_HW_ERR);
|
||||
}
|
||||
|
||||
/* Firmware dump */
|
||||
@@ -1872,11 +1863,26 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (of_graph_is_present(dev_of_node(&serdev->ctrl->dev))) {
|
||||
struct pwrseq_desc *pwrseq;
|
||||
|
||||
pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
|
||||
if (IS_ERR(pwrseq))
|
||||
return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq),
|
||||
"failed to get pwrseq\n");
|
||||
|
||||
nxpdev->pwrseq = pwrseq;
|
||||
err = pwrseq_power_on(pwrseq);
|
||||
if (err)
|
||||
goto err_pwrseq_put;
|
||||
}
|
||||
|
||||
/* Initialize and register HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
dev_err(&serdev->dev, "Can't allocate HCI device\n");
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto err_pwrseq_put;
|
||||
}
|
||||
|
||||
reset_control_deassert(nxpdev->pdn);
|
||||
@@ -1907,23 +1913,31 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
|
||||
if (bacmp(&ba, BDADDR_ANY))
|
||||
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
dev_err(&serdev->dev, "Can't register HCI device\n");
|
||||
goto probe_fail;
|
||||
}
|
||||
|
||||
if (ps_setup(hdev))
|
||||
goto probe_fail;
|
||||
if (ps_setup(hdev)) {
|
||||
err = -ENODEV;
|
||||
goto probe_fail_unregister;
|
||||
}
|
||||
|
||||
hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr,
|
||||
nxp_coredump_notify);
|
||||
|
||||
return 0;
|
||||
|
||||
probe_fail_unregister:
|
||||
hci_unregister_dev(hdev);
|
||||
probe_fail:
|
||||
reset_control_assert(nxpdev->pdn);
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
err_pwrseq_put:
|
||||
if (nxpdev->pwrseq)
|
||||
pwrseq_put(nxpdev->pwrseq);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nxp_serdev_remove(struct serdev_device *serdev)
|
||||
@@ -1950,6 +1964,8 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
|
||||
ps_cleanup(nxpdev);
|
||||
hci_unregister_dev(hdev);
|
||||
reset_control_assert(nxpdev->pdn);
|
||||
if (nxpdev->pwrseq)
|
||||
pwrseq_put(nxpdev->pwrseq);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
|
||||
@@ -190,25 +190,6 @@ static int qca_send_patch_config_cmd(struct hci_dev *hdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int qca_send_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(hdev, "QCA HCI_RESET");
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "QCA Reset failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
|
||||
{
|
||||
u8 cmd;
|
||||
@@ -990,11 +971,12 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
}
|
||||
|
||||
/* Perform HCI reset */
|
||||
err = qca_send_reset(hdev);
|
||||
err = __hci_reset_sync(hdev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err);
|
||||
return err;
|
||||
}
|
||||
bt_dev_dbg(hdev, "QCA HCI_RESET succeed");
|
||||
|
||||
switch (soc_type) {
|
||||
case QCA_WCN3991:
|
||||
@@ -1029,8 +1011,7 @@ int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
baswap(&bdaddr_swapped, bdaddr);
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6,
|
||||
&bdaddr_swapped, HCI_EV_VENDOR,
|
||||
HCI_INIT_TIMEOUT);
|
||||
&bdaddr_swapped, 0, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err);
|
||||
|
||||
@@ -107,7 +107,6 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
|
||||
return -ENOMEM;
|
||||
|
||||
h_adapter->priv = priv;
|
||||
ops->set_bt_context(priv, h_adapter);
|
||||
h_adapter->proto_ops = ops;
|
||||
|
||||
hdev = hci_alloc_dev();
|
||||
@@ -136,6 +135,8 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ops->set_bt_context(priv, h_adapter);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
h_adapter->hdev = NULL;
|
||||
|
||||
@@ -297,6 +297,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3503), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* QCA WCN6855 chipset */
|
||||
{ USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
@@ -679,6 +681,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3606), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
/* MediaTek MT7902 Bluetooth devices */
|
||||
{ USB_DEVICE(0x0489, 0xe156), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0e8d, 0x1ede), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3579), .driver_info = BTUSB_MEDIATEK |
|
||||
@@ -796,6 +800,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, 0x3625), .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 |
|
||||
@@ -850,6 +856,12 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Additional Realtek 8761CU Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Additional Realtek 8821AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
|
||||
@@ -882,6 +894,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0bda, 0xc123), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x1357, 0xc123), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cb5, 0xc547), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
@@ -937,6 +951,10 @@ struct qca_dump_info {
|
||||
u16 ram_dump_seqno;
|
||||
};
|
||||
|
||||
struct btqca_data {
|
||||
struct qca_dump_info qca_dump;
|
||||
};
|
||||
|
||||
#define BTUSB_MAX_ISOC_FRAMES 10
|
||||
|
||||
#define BTUSB_INTR_RUNNING 0
|
||||
@@ -1010,6 +1028,7 @@ struct btusb_data {
|
||||
bool usb_alt6_packet_flow;
|
||||
int isoc_altsetting;
|
||||
int suspend_count;
|
||||
const struct usb_device_id *match_id;
|
||||
|
||||
int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
int (*recv_acl)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
@@ -1022,8 +1041,6 @@ struct btusb_data {
|
||||
int (*disconnect)(struct hci_dev *hdev);
|
||||
|
||||
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
|
||||
|
||||
struct qca_dump_info qca_dump;
|
||||
};
|
||||
|
||||
static void btusb_reset(struct hci_dev *hdev)
|
||||
@@ -1235,14 +1252,16 @@ static inline void btusb_free_frags(struct btusb_data *data)
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
}
|
||||
|
||||
static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
|
||||
static int btusb_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
if (data->intr_interval) {
|
||||
/* Trigger dequeue immediately if an event is received */
|
||||
schedule_delayed_work(&data->rx_work, 0);
|
||||
}
|
||||
|
||||
return data->recv_event(data->hdev, skb);
|
||||
return data->recv_event(hdev, skb);
|
||||
}
|
||||
|
||||
static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
@@ -1302,7 +1321,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
}
|
||||
|
||||
/* Complete frame */
|
||||
btusb_recv_event(data, skb);
|
||||
btusb_recv_event(data->hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1313,13 +1332,15 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
|
||||
static int btusb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
/* Only queue ACL packet if intr_interval is set as it means
|
||||
* force_poll_sync has been enabled.
|
||||
*/
|
||||
if (!data->intr_interval)
|
||||
return data->recv_acl(data->hdev, skb);
|
||||
return data->recv_acl(hdev, skb);
|
||||
|
||||
skb_queue_tail(&data->acl_q, skb);
|
||||
schedule_delayed_work(&data->rx_work, data->intr_interval);
|
||||
@@ -1358,10 +1379,8 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
hci_skb_expect(skb) -= len;
|
||||
|
||||
if (skb->len == HCI_ACL_HDR_SIZE) {
|
||||
__le16 dlen = hci_acl_hdr(skb)->dlen;
|
||||
|
||||
/* Complete ACL header */
|
||||
hci_skb_expect(skb) = __le16_to_cpu(dlen);
|
||||
hci_skb_expect(skb) = hci_acl_dlen(skb);
|
||||
|
||||
if (skb_tailroom(skb) < hci_skb_expect(skb)) {
|
||||
kfree_skb(skb);
|
||||
@@ -1374,7 +1393,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
|
||||
if (!hci_skb_expect(skb)) {
|
||||
/* Complete frame */
|
||||
btusb_recv_acl(data, skb);
|
||||
btusb_recv_acl(data->hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
@@ -2053,6 +2072,14 @@ static void btusb_stop_traffic(struct btusb_data *data)
|
||||
usb_kill_anchored_urbs(&data->ctrl_anchor);
|
||||
}
|
||||
|
||||
static void btusb_prepare_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
btusb_stop_traffic(data);
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
}
|
||||
|
||||
static int btusb_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
@@ -2783,7 +2810,7 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
|
||||
static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
if (skb->len >= HCI_EVENT_HDR_SIZE + 1 &&
|
||||
skb->data[0] == HCI_VENDOR_PKT &&
|
||||
skb->data[0] == HCI_EV_VENDOR &&
|
||||
skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
|
||||
struct rtk_dev_coredump_hdr hdr = {
|
||||
.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
|
||||
@@ -2897,8 +2924,7 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
|
||||
/* Release MediaTek ISO data interface */
|
||||
btusb_mtk_release_iso_intf(hdev);
|
||||
|
||||
btusb_stop_traffic(data);
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
btusb_prepare_reset(hdev);
|
||||
|
||||
/* Toggle the hard reset line. The MediaTek device is going to
|
||||
* yank itself off the USB and then replug. The cleanup is handled
|
||||
@@ -3072,14 +3098,15 @@ static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
|
||||
static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr)
|
||||
{
|
||||
bdaddr_t bdaddr_swapped;
|
||||
struct sk_buff *skb;
|
||||
u8 buf[6];
|
||||
long ret;
|
||||
|
||||
memcpy(buf, bdaddr, sizeof(bdaddr_t));
|
||||
baswap(&bdaddr_swapped, bdaddr);
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(buf), buf,
|
||||
HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
|
||||
skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(bdaddr_swapped),
|
||||
&bdaddr_swapped, HCI_EV_CMD_COMPLETE,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "Change address command failed (%ld)", ret);
|
||||
@@ -3115,14 +3142,15 @@ struct qca_dump_hdr {
|
||||
static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
char buf[128];
|
||||
struct btusb_data *btdata = hci_get_drvdata(hdev);
|
||||
struct btqca_data *btqca_data = hci_get_priv(hdev);
|
||||
struct qca_dump_info *qca_dump_ptr = &btqca_data->qca_dump;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n",
|
||||
btdata->qca_dump.controller_id);
|
||||
qca_dump_ptr->controller_id);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n",
|
||||
btdata->qca_dump.fw_version);
|
||||
qca_dump_ptr->fw_version);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n",
|
||||
@@ -3130,7 +3158,7 @@ static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n",
|
||||
btdata->qca_dump.id_vendor, btdata->qca_dump.id_product);
|
||||
qca_dump_ptr->id_vendor, qca_dump_ptr->id_product);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n",
|
||||
@@ -3159,6 +3187,8 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
struct qca_dump_hdr *dump_hdr;
|
||||
struct btusb_data *btdata = hci_get_drvdata(hdev);
|
||||
struct btqca_data *btqca_data = hci_get_priv(hdev);
|
||||
struct qca_dump_info *qca_dump_ptr = &btqca_data->qca_dump;
|
||||
struct usb_device *udev = btdata->udev;
|
||||
|
||||
pkt_type = hci_skb_pkt_type(skb);
|
||||
@@ -3186,8 +3216,8 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto out;
|
||||
}
|
||||
|
||||
btdata->qca_dump.ram_dump_size = dump_size;
|
||||
btdata->qca_dump.ram_dump_seqno = 0;
|
||||
qca_dump_ptr->ram_dump_size = dump_size;
|
||||
qca_dump_ptr->ram_dump_seqno = 0;
|
||||
|
||||
skb_pull(skb, offsetof(struct qca_dump_hdr, data0));
|
||||
|
||||
@@ -3199,29 +3229,29 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
skb_pull(skb, offsetof(struct qca_dump_hdr, data));
|
||||
}
|
||||
|
||||
if (!btdata->qca_dump.ram_dump_size) {
|
||||
if (!qca_dump_ptr->ram_dump_size) {
|
||||
ret = -EINVAL;
|
||||
bt_dev_err(hdev, "memdump is not active");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) {
|
||||
dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1);
|
||||
if ((seqno > qca_dump_ptr->ram_dump_seqno + 1) && seqno != QCA_LAST_SEQUENCE_NUM) {
|
||||
dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - qca_dump_ptr->ram_dump_seqno - 1);
|
||||
hci_devcd_append_pattern(hdev, 0x0, dump_size);
|
||||
bt_dev_err(hdev,
|
||||
"expected memdump seqno(%u) is not received(%u)\n",
|
||||
btdata->qca_dump.ram_dump_seqno, seqno);
|
||||
btdata->qca_dump.ram_dump_seqno = seqno;
|
||||
qca_dump_ptr->ram_dump_seqno, seqno);
|
||||
qca_dump_ptr->ram_dump_seqno = seqno;
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hci_devcd_append(hdev, skb);
|
||||
btdata->qca_dump.ram_dump_seqno++;
|
||||
qca_dump_ptr->ram_dump_seqno++;
|
||||
if (seqno == QCA_LAST_SEQUENCE_NUM) {
|
||||
bt_dev_info(hdev,
|
||||
"memdump done: pkts(%u), total(%u)\n",
|
||||
btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size);
|
||||
qca_dump_ptr->ram_dump_seqno, qca_dump_ptr->ram_dump_size);
|
||||
|
||||
hci_devcd_complete(hdev);
|
||||
goto out;
|
||||
@@ -3229,10 +3259,10 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return ret;
|
||||
|
||||
out:
|
||||
if (btdata->qca_dump.ram_dump_size)
|
||||
if (qca_dump_ptr->ram_dump_size)
|
||||
usb_enable_autosuspend(udev);
|
||||
btdata->qca_dump.ram_dump_size = 0;
|
||||
btdata->qca_dump.ram_dump_seqno = 0;
|
||||
qca_dump_ptr->ram_dump_size = 0;
|
||||
qca_dump_ptr->ram_dump_seqno = 0;
|
||||
clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags);
|
||||
|
||||
if (ret < 0)
|
||||
@@ -3257,7 +3287,7 @@ static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto out;
|
||||
|
||||
event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
|
||||
if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
|
||||
if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR)
|
||||
goto out;
|
||||
|
||||
dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
|
||||
@@ -3283,7 +3313,7 @@ static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return false;
|
||||
|
||||
event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
|
||||
if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
|
||||
if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR)
|
||||
goto out;
|
||||
|
||||
dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
|
||||
@@ -3695,8 +3725,12 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
|
||||
btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
|
||||
if (btdata->match_id->driver_info & BTUSB_QCA_WCN6855) {
|
||||
struct btqca_data *btqca_data = hci_get_priv(hdev);
|
||||
|
||||
btqca_data->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
|
||||
btqca_data->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
|
||||
}
|
||||
|
||||
if (!(status & QCA_SYSCFG_UPDATED)) {
|
||||
err = btusb_setup_qca_load_nvm(hdev, &ver, info);
|
||||
@@ -3877,16 +3911,13 @@ static bool btusb_wakeup(struct hci_dev *hdev)
|
||||
|
||||
static int btusb_shutdown_qca(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = __hci_reset_sync(hdev);
|
||||
if (err)
|
||||
bt_dev_err(hdev, "HCI reset during shutdown failed");
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t force_poll_sync_read(struct file *file, char __user *user_buf,
|
||||
@@ -4070,7 +4101,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
struct btusb_data *data;
|
||||
struct hci_dev *hdev;
|
||||
unsigned ifnum_base;
|
||||
int err, priv_size;
|
||||
int err, priv_size = 0;
|
||||
|
||||
BT_DBG("intf %p id %p", intf, id);
|
||||
|
||||
@@ -4089,7 +4120,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
id = match;
|
||||
}
|
||||
|
||||
if (id->driver_info == BTUSB_IGNORE)
|
||||
if (id->driver_info & BTUSB_IGNORE)
|
||||
return -ENODEV;
|
||||
|
||||
if (id->driver_info & BTUSB_ATH3012) {
|
||||
@@ -4107,6 +4138,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->match_id = id;
|
||||
err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep,
|
||||
&data->bulk_tx_ep, &data->intr_ep, NULL);
|
||||
if (err)
|
||||
@@ -4140,8 +4172,6 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
init_usb_anchor(&data->ctrl_anchor);
|
||||
spin_lock_init(&data->rxlock);
|
||||
|
||||
priv_size = 0;
|
||||
|
||||
data->recv_event = hci_recv_frame;
|
||||
data->recv_bulk = btusb_recv_bulk;
|
||||
|
||||
@@ -4160,6 +4190,9 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
} else if (id->driver_info & BTUSB_MEDIATEK) {
|
||||
/* Allocate extra space for Mediatek device */
|
||||
priv_size += sizeof(struct btmtk_data);
|
||||
} else if (id->driver_info & BTUSB_QCA_WCN6855) {
|
||||
/* Allocate extra space for QCA WCN6855 device */
|
||||
priv_size += sizeof(struct btqca_data);
|
||||
}
|
||||
|
||||
data->recv_acl = hci_recv_frame;
|
||||
@@ -4302,8 +4335,10 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_QCA_WCN6855) {
|
||||
data->qca_dump.id_vendor = id->idVendor;
|
||||
data->qca_dump.id_product = id->idProduct;
|
||||
struct btqca_data *btqca_data = hci_get_priv(hdev);
|
||||
|
||||
btqca_data->qca_dump.id_vendor = id->idVendor;
|
||||
btqca_data->qca_dump.id_product = id->idProduct;
|
||||
data->recv_event = btusb_recv_evt_qca;
|
||||
data->recv_acl = btusb_recv_acl_qca;
|
||||
hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL);
|
||||
|
||||
@@ -247,7 +247,7 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name)
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
|
||||
const struct firmware *firmware = NULL;
|
||||
struct aml_fw_len *fw_len = NULL;
|
||||
const struct aml_fw_len *fw_len = NULL;
|
||||
u8 *iccm_start = NULL, *dccm_start = NULL;
|
||||
u32 iccm_len, dccm_len;
|
||||
u32 value = 0;
|
||||
@@ -281,7 +281,21 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fw_len = (struct aml_fw_len *)firmware->data;
|
||||
if (firmware->size < sizeof(*fw_len)) {
|
||||
bt_dev_err(hdev, "Firmware is too small for its header");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fw_len = (const struct aml_fw_len *)firmware->data;
|
||||
if (fw_len->iccm_len < amldev->aml_dev_data->iccm_offset ||
|
||||
fw_len->iccm_len > firmware->size - sizeof(*fw_len) ||
|
||||
fw_len->dccm_len > firmware->size - sizeof(*fw_len) -
|
||||
fw_len->iccm_len) {
|
||||
bt_dev_err(hdev, "Invalid firmware segment lengths");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Download ICCM */
|
||||
iccm_start = (u8 *)(firmware->data) + sizeof(struct aml_fw_len)
|
||||
|
||||
@@ -1314,174 +1314,174 @@ static struct bcm_device_data bcm43430_device_data = {
|
||||
};
|
||||
|
||||
static const struct acpi_device_id bcm_acpi_match[] = {
|
||||
{ "BCM2E00" },
|
||||
{ "BCM2E01" },
|
||||
{ "BCM2E02" },
|
||||
{ "BCM2E03" },
|
||||
{ "BCM2E04" },
|
||||
{ "BCM2E05" },
|
||||
{ "BCM2E06" },
|
||||
{ "BCM2E07" },
|
||||
{ "BCM2E08" },
|
||||
{ "BCM2E09" },
|
||||
{ "BCM2E0A" },
|
||||
{ "BCM2E0B" },
|
||||
{ "BCM2E0C" },
|
||||
{ "BCM2E0D" },
|
||||
{ "BCM2E0E" },
|
||||
{ "BCM2E0F" },
|
||||
{ "BCM2E10" },
|
||||
{ "BCM2E11" },
|
||||
{ "BCM2E12" },
|
||||
{ "BCM2E13" },
|
||||
{ "BCM2E14" },
|
||||
{ "BCM2E15" },
|
||||
{ "BCM2E16" },
|
||||
{ "BCM2E17" },
|
||||
{ "BCM2E18" },
|
||||
{ "BCM2E19" },
|
||||
{ "BCM2E1A" },
|
||||
{ "BCM2E1B" },
|
||||
{ "BCM2E1C" },
|
||||
{ "BCM2E1D" },
|
||||
{ "BCM2E1F" },
|
||||
{ "BCM2E20" },
|
||||
{ "BCM2E21" },
|
||||
{ "BCM2E22" },
|
||||
{ "BCM2E23" },
|
||||
{ "BCM2E24" },
|
||||
{ "BCM2E25" },
|
||||
{ "BCM2E26" },
|
||||
{ "BCM2E27" },
|
||||
{ "BCM2E28" },
|
||||
{ "BCM2E29" },
|
||||
{ "BCM2E2A" },
|
||||
{ "BCM2E2B" },
|
||||
{ "BCM2E2C" },
|
||||
{ "BCM2E2D" },
|
||||
{ "BCM2E2E" },
|
||||
{ "BCM2E2F" },
|
||||
{ "BCM2E30" },
|
||||
{ "BCM2E31" },
|
||||
{ "BCM2E32" },
|
||||
{ "BCM2E33" },
|
||||
{ "BCM2E34" },
|
||||
{ "BCM2E35" },
|
||||
{ "BCM2E36" },
|
||||
{ "BCM2E37" },
|
||||
{ "BCM2E38" },
|
||||
{ "BCM2E39" },
|
||||
{ "BCM2E3A" },
|
||||
{ "BCM2E3B" },
|
||||
{ "BCM2E3C" },
|
||||
{ "BCM2E3D" },
|
||||
{ "BCM2E3E" },
|
||||
{ "BCM2E3F" },
|
||||
{ "BCM2E40" },
|
||||
{ "BCM2E41" },
|
||||
{ "BCM2E42" },
|
||||
{ "BCM2E43" },
|
||||
{ "BCM2E44" },
|
||||
{ "BCM2E45" },
|
||||
{ "BCM2E46" },
|
||||
{ "BCM2E47" },
|
||||
{ "BCM2E48" },
|
||||
{ "BCM2E49" },
|
||||
{ "BCM2E4A" },
|
||||
{ "BCM2E4B" },
|
||||
{ "BCM2E4C" },
|
||||
{ "BCM2E4D" },
|
||||
{ "BCM2E4E" },
|
||||
{ "BCM2E4F" },
|
||||
{ "BCM2E50" },
|
||||
{ "BCM2E51" },
|
||||
{ "BCM2E52" },
|
||||
{ "BCM2E53" },
|
||||
{ "BCM2E54" },
|
||||
{ "BCM2E55" },
|
||||
{ "BCM2E56" },
|
||||
{ "BCM2E57" },
|
||||
{ "BCM2E58" },
|
||||
{ "BCM2E59" },
|
||||
{ "BCM2E5A" },
|
||||
{ "BCM2E5B" },
|
||||
{ "BCM2E5C" },
|
||||
{ "BCM2E5D" },
|
||||
{ "BCM2E5E" },
|
||||
{ "BCM2E5F" },
|
||||
{ "BCM2E60" },
|
||||
{ "BCM2E61" },
|
||||
{ "BCM2E62" },
|
||||
{ "BCM2E63" },
|
||||
{ "BCM2E64" },
|
||||
{ "BCM2E65" },
|
||||
{ "BCM2E66" },
|
||||
{ "BCM2E67" },
|
||||
{ "BCM2E68" },
|
||||
{ "BCM2E69" },
|
||||
{ "BCM2E6B" },
|
||||
{ "BCM2E6D" },
|
||||
{ "BCM2E6E" },
|
||||
{ "BCM2E6F" },
|
||||
{ "BCM2E70" },
|
||||
{ "BCM2E71" },
|
||||
{ "BCM2E72" },
|
||||
{ "BCM2E73" },
|
||||
{ "BCM2E74", (long)&bcm43430_device_data },
|
||||
{ "BCM2E75", (long)&bcm43430_device_data },
|
||||
{ "BCM2E76" },
|
||||
{ "BCM2E77" },
|
||||
{ "BCM2E78" },
|
||||
{ "BCM2E79" },
|
||||
{ "BCM2E7A" },
|
||||
{ "BCM2E7B", (long)&bcm43430_device_data },
|
||||
{ "BCM2E7C" },
|
||||
{ "BCM2E7D" },
|
||||
{ "BCM2E7E" },
|
||||
{ "BCM2E7F" },
|
||||
{ "BCM2E80", (long)&bcm43430_device_data },
|
||||
{ "BCM2E81" },
|
||||
{ "BCM2E82" },
|
||||
{ "BCM2E83" },
|
||||
{ "BCM2E84" },
|
||||
{ "BCM2E85" },
|
||||
{ "BCM2E86" },
|
||||
{ "BCM2E87" },
|
||||
{ "BCM2E88" },
|
||||
{ "BCM2E89", (long)&bcm43430_device_data },
|
||||
{ "BCM2E8A" },
|
||||
{ "BCM2E8B" },
|
||||
{ "BCM2E8C" },
|
||||
{ "BCM2E8D" },
|
||||
{ "BCM2E8E" },
|
||||
{ "BCM2E90" },
|
||||
{ "BCM2E92" },
|
||||
{ "BCM2E93" },
|
||||
{ "BCM2E94", (long)&bcm43430_device_data },
|
||||
{ "BCM2E95" },
|
||||
{ "BCM2E96" },
|
||||
{ "BCM2E97" },
|
||||
{ "BCM2E98" },
|
||||
{ "BCM2E99", (long)&bcm43430_device_data },
|
||||
{ "BCM2E9A" },
|
||||
{ "BCM2E9B", (long)&bcm43430_device_data },
|
||||
{ "BCM2E9C" },
|
||||
{ "BCM2E9D" },
|
||||
{ "BCM2E9F", (long)&bcm43430_device_data },
|
||||
{ "BCM2EA0" },
|
||||
{ "BCM2EA1" },
|
||||
{ "BCM2EA2", (long)&bcm43430_device_data },
|
||||
{ "BCM2EA3", (long)&bcm43430_device_data },
|
||||
{ "BCM2EA4", (long)&bcm43430_device_data }, /* bcm43455 */
|
||||
{ "BCM2EA5" },
|
||||
{ "BCM2EA6" },
|
||||
{ "BCM2EA7" },
|
||||
{ "BCM2EA8" },
|
||||
{ "BCM2EA9" },
|
||||
{ "BCM2EAA", (long)&bcm43430_device_data },
|
||||
{ "BCM2EAB", (long)&bcm43430_device_data },
|
||||
{ "BCM2EAC", (long)&bcm43430_device_data },
|
||||
{ },
|
||||
{ .id = "BCM2E00" },
|
||||
{ .id = "BCM2E01" },
|
||||
{ .id = "BCM2E02" },
|
||||
{ .id = "BCM2E03" },
|
||||
{ .id = "BCM2E04" },
|
||||
{ .id = "BCM2E05" },
|
||||
{ .id = "BCM2E06" },
|
||||
{ .id = "BCM2E07" },
|
||||
{ .id = "BCM2E08" },
|
||||
{ .id = "BCM2E09" },
|
||||
{ .id = "BCM2E0A" },
|
||||
{ .id = "BCM2E0B" },
|
||||
{ .id = "BCM2E0C" },
|
||||
{ .id = "BCM2E0D" },
|
||||
{ .id = "BCM2E0E" },
|
||||
{ .id = "BCM2E0F" },
|
||||
{ .id = "BCM2E10" },
|
||||
{ .id = "BCM2E11" },
|
||||
{ .id = "BCM2E12" },
|
||||
{ .id = "BCM2E13" },
|
||||
{ .id = "BCM2E14" },
|
||||
{ .id = "BCM2E15" },
|
||||
{ .id = "BCM2E16" },
|
||||
{ .id = "BCM2E17" },
|
||||
{ .id = "BCM2E18" },
|
||||
{ .id = "BCM2E19" },
|
||||
{ .id = "BCM2E1A" },
|
||||
{ .id = "BCM2E1B" },
|
||||
{ .id = "BCM2E1C" },
|
||||
{ .id = "BCM2E1D" },
|
||||
{ .id = "BCM2E1F" },
|
||||
{ .id = "BCM2E20" },
|
||||
{ .id = "BCM2E21" },
|
||||
{ .id = "BCM2E22" },
|
||||
{ .id = "BCM2E23" },
|
||||
{ .id = "BCM2E24" },
|
||||
{ .id = "BCM2E25" },
|
||||
{ .id = "BCM2E26" },
|
||||
{ .id = "BCM2E27" },
|
||||
{ .id = "BCM2E28" },
|
||||
{ .id = "BCM2E29" },
|
||||
{ .id = "BCM2E2A" },
|
||||
{ .id = "BCM2E2B" },
|
||||
{ .id = "BCM2E2C" },
|
||||
{ .id = "BCM2E2D" },
|
||||
{ .id = "BCM2E2E" },
|
||||
{ .id = "BCM2E2F" },
|
||||
{ .id = "BCM2E30" },
|
||||
{ .id = "BCM2E31" },
|
||||
{ .id = "BCM2E32" },
|
||||
{ .id = "BCM2E33" },
|
||||
{ .id = "BCM2E34" },
|
||||
{ .id = "BCM2E35" },
|
||||
{ .id = "BCM2E36" },
|
||||
{ .id = "BCM2E37" },
|
||||
{ .id = "BCM2E38" },
|
||||
{ .id = "BCM2E39" },
|
||||
{ .id = "BCM2E3A" },
|
||||
{ .id = "BCM2E3B" },
|
||||
{ .id = "BCM2E3C" },
|
||||
{ .id = "BCM2E3D" },
|
||||
{ .id = "BCM2E3E" },
|
||||
{ .id = "BCM2E3F" },
|
||||
{ .id = "BCM2E40" },
|
||||
{ .id = "BCM2E41" },
|
||||
{ .id = "BCM2E42" },
|
||||
{ .id = "BCM2E43" },
|
||||
{ .id = "BCM2E44" },
|
||||
{ .id = "BCM2E45" },
|
||||
{ .id = "BCM2E46" },
|
||||
{ .id = "BCM2E47" },
|
||||
{ .id = "BCM2E48" },
|
||||
{ .id = "BCM2E49" },
|
||||
{ .id = "BCM2E4A" },
|
||||
{ .id = "BCM2E4B" },
|
||||
{ .id = "BCM2E4C" },
|
||||
{ .id = "BCM2E4D" },
|
||||
{ .id = "BCM2E4E" },
|
||||
{ .id = "BCM2E4F" },
|
||||
{ .id = "BCM2E50" },
|
||||
{ .id = "BCM2E51" },
|
||||
{ .id = "BCM2E52" },
|
||||
{ .id = "BCM2E53" },
|
||||
{ .id = "BCM2E54" },
|
||||
{ .id = "BCM2E55" },
|
||||
{ .id = "BCM2E56" },
|
||||
{ .id = "BCM2E57" },
|
||||
{ .id = "BCM2E58" },
|
||||
{ .id = "BCM2E59" },
|
||||
{ .id = "BCM2E5A" },
|
||||
{ .id = "BCM2E5B" },
|
||||
{ .id = "BCM2E5C" },
|
||||
{ .id = "BCM2E5D" },
|
||||
{ .id = "BCM2E5E" },
|
||||
{ .id = "BCM2E5F" },
|
||||
{ .id = "BCM2E60" },
|
||||
{ .id = "BCM2E61" },
|
||||
{ .id = "BCM2E62" },
|
||||
{ .id = "BCM2E63" },
|
||||
{ .id = "BCM2E64" },
|
||||
{ .id = "BCM2E65" },
|
||||
{ .id = "BCM2E66" },
|
||||
{ .id = "BCM2E67" },
|
||||
{ .id = "BCM2E68" },
|
||||
{ .id = "BCM2E69" },
|
||||
{ .id = "BCM2E6B" },
|
||||
{ .id = "BCM2E6D" },
|
||||
{ .id = "BCM2E6E" },
|
||||
{ .id = "BCM2E6F" },
|
||||
{ .id = "BCM2E70" },
|
||||
{ .id = "BCM2E71" },
|
||||
{ .id = "BCM2E72" },
|
||||
{ .id = "BCM2E73" },
|
||||
{ .id = "BCM2E74", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E75", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E76" },
|
||||
{ .id = "BCM2E77" },
|
||||
{ .id = "BCM2E78" },
|
||||
{ .id = "BCM2E79" },
|
||||
{ .id = "BCM2E7A" },
|
||||
{ .id = "BCM2E7B", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E7C" },
|
||||
{ .id = "BCM2E7D" },
|
||||
{ .id = "BCM2E7E" },
|
||||
{ .id = "BCM2E7F" },
|
||||
{ .id = "BCM2E80", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E81" },
|
||||
{ .id = "BCM2E82" },
|
||||
{ .id = "BCM2E83" },
|
||||
{ .id = "BCM2E84" },
|
||||
{ .id = "BCM2E85" },
|
||||
{ .id = "BCM2E86" },
|
||||
{ .id = "BCM2E87" },
|
||||
{ .id = "BCM2E88" },
|
||||
{ .id = "BCM2E89", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E8A" },
|
||||
{ .id = "BCM2E8B" },
|
||||
{ .id = "BCM2E8C" },
|
||||
{ .id = "BCM2E8D" },
|
||||
{ .id = "BCM2E8E" },
|
||||
{ .id = "BCM2E90" },
|
||||
{ .id = "BCM2E92" },
|
||||
{ .id = "BCM2E93" },
|
||||
{ .id = "BCM2E94", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E95" },
|
||||
{ .id = "BCM2E96" },
|
||||
{ .id = "BCM2E97" },
|
||||
{ .id = "BCM2E98" },
|
||||
{ .id = "BCM2E99", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E9A" },
|
||||
{ .id = "BCM2E9B", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2E9C" },
|
||||
{ .id = "BCM2E9D" },
|
||||
{ .id = "BCM2E9F", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2EA0" },
|
||||
{ .id = "BCM2EA1" },
|
||||
{ .id = "BCM2EA2", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2EA3", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2EA4", .driver_data = (long)&bcm43430_device_data }, /* bcm43455 */
|
||||
{ .id = "BCM2EA5" },
|
||||
{ .id = "BCM2EA6" },
|
||||
{ .id = "BCM2EA7" },
|
||||
{ .id = "BCM2EA8" },
|
||||
{ .id = "BCM2EA9" },
|
||||
{ .id = "BCM2EAA", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2EAB", .driver_data = (long)&bcm43430_device_data },
|
||||
{ .id = "BCM2EAC", .driver_data = (long)&bcm43430_device_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
|
||||
#endif
|
||||
|
||||
@@ -194,7 +194,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (hciextn && chan == 5) {
|
||||
if (hciextn && chan == 5 && len > HCI_COMMAND_HDR_SIZE) {
|
||||
__le16 opcode = ((struct hci_command_hdr *)data)->opcode;
|
||||
|
||||
/* Vendor specific commands */
|
||||
@@ -402,6 +402,9 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu)
|
||||
u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
|
||||
|
||||
/* spot "conf" pkts and reply with a "conf rsp" pkt */
|
||||
if (bcsp->rx_skb->len < 8)
|
||||
return;
|
||||
|
||||
if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
|
||||
!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
|
||||
struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
|
||||
|
||||
@@ -1124,10 +1124,10 @@ static const struct h5_device_data h5_data_rtl8723bs = {
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id h5_acpi_match[] = {
|
||||
#ifdef CONFIG_BT_HCIUART_RTL
|
||||
{ "OBDA0623", (kernel_ulong_t)&h5_data_rtl8723bs },
|
||||
{ "OBDA8723", (kernel_ulong_t)&h5_data_rtl8723bs },
|
||||
{ .id = "OBDA0623", .driver_data = (kernel_ulong_t)&h5_data_rtl8723bs },
|
||||
{ .id = "OBDA8723", .driver_data = (kernel_ulong_t)&h5_data_rtl8723bs },
|
||||
#endif
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
|
||||
#endif
|
||||
|
||||
@@ -1057,8 +1057,8 @@ static const struct hci_uart_proto intel_proto = {
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id intel_acpi_match[] = {
|
||||
{ "INT33E1", 0 },
|
||||
{ "INT33E3", 0 },
|
||||
{ .id = "INT33E1" },
|
||||
{ .id = "INT33E3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
|
||||
|
||||
@@ -163,6 +163,12 @@ static void hci_uart_write_work(struct work_struct *work)
|
||||
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
len = tty->ops->write(tty, skb->data, skb->len);
|
||||
if (len < 0 || len > skb->len) {
|
||||
hdev->stat.err_tx++;
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
||||
hdev->stat.byte_tx += len;
|
||||
|
||||
skb_pull(skb, len);
|
||||
@@ -756,9 +762,9 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
|
||||
hu->proto = p;
|
||||
|
||||
err = hci_uart_register_dev(hu);
|
||||
if (err) {
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
set_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
clear_bit(HCI_UART_PROTO_INIT, &hu->flags);
|
||||
|
||||
@@ -354,9 +354,29 @@ static int nokia_setup_fw(struct hci_uart *hu)
|
||||
u16 opcode;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (pkt_size > fw_size - 2) {
|
||||
err = -EINVAL;
|
||||
dev_err(dev, "%s: Malformed firmware packet\n",
|
||||
hu->hdev->name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch (pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
if (pkt_size < 1 + HCI_COMMAND_HDR_SIZE) {
|
||||
err = -EINVAL;
|
||||
dev_err(dev, "%s: Malformed firmware command\n",
|
||||
hu->hdev->name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
cmd = (struct hci_command_hdr *)(fw_ptr + 3);
|
||||
if (cmd->plen > pkt_size - 1 - HCI_COMMAND_HDR_SIZE) {
|
||||
err = -EINVAL;
|
||||
dev_err(dev, "%s: Truncated firmware command\n",
|
||||
hu->hdev->name);
|
||||
goto done;
|
||||
}
|
||||
opcode = le16_to_cpu(cmd->opcode);
|
||||
|
||||
skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,
|
||||
|
||||
@@ -1239,8 +1239,8 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
* received we store dump into a file before closing hci. This
|
||||
* dump will help in triaging the issues.
|
||||
*/
|
||||
if ((skb->data[0] == HCI_VENDOR_PKT) &&
|
||||
(get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE))
|
||||
if (skb->data[0] == HCI_EV_VENDOR &&
|
||||
get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE)
|
||||
return qca_controller_memdump_event(hdev, skb);
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
@@ -2792,12 +2792,12 @@ MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
|
||||
{ "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 },
|
||||
{ "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ },
|
||||
{ .id = "QCOM2066", .driver_data = (kernel_ulong_t)&qca_soc_data_qca2066 },
|
||||
{ .id = "QCOM6390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ .id = "DLA16390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ .id = "DLB16390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ .id = "DLB26390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
|
||||
#endif
|
||||
|
||||
@@ -120,9 +120,13 @@ static int virtbt_setup_zephyr(struct hci_dev *hdev)
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
|
||||
/* Bounded print: the backend controls skb->len. */
|
||||
if (skb->len > 1) {
|
||||
int len = skb->len - 1;
|
||||
|
||||
hci_set_fw_info(hdev, "%s", skb->data + 1);
|
||||
bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1));
|
||||
hci_set_fw_info(hdev, "%.*s", len, skb->data + 1);
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
#define DEVCOREDUMP_TIMEOUT msecs_to_jiffies(10000) /* 10 sec */
|
||||
|
||||
/*
|
||||
* Max header size, shared by both the devcoredump core and
|
||||
* the dmp_hdr() registered by driver via hci_devcd_register()
|
||||
*/
|
||||
#define HCI_DEVCD_HDR_SIZE_MAX 512
|
||||
#define HCI_DEVCD_HDR_END_MARKER "--- Start dump ---\n"
|
||||
|
||||
typedef void (*coredump_t)(struct hci_dev *hdev);
|
||||
typedef void (*dmp_hdr_t)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
typedef void (*notify_change_t)(struct hci_dev *hdev, int state);
|
||||
@@ -60,6 +67,8 @@ struct hci_devcoredump {
|
||||
|
||||
#ifdef CONFIG_DEV_COREDUMP
|
||||
|
||||
const char *hci_devcd_state_name(enum devcoredump_state state);
|
||||
|
||||
void hci_devcd_reset(struct hci_dev *hdev);
|
||||
void hci_devcd_rx(struct work_struct *work);
|
||||
void hci_devcd_timeout(struct work_struct *work);
|
||||
@@ -74,6 +83,11 @@ int hci_devcd_abort(struct hci_dev *hdev);
|
||||
|
||||
#else
|
||||
|
||||
static inline const char *hci_devcd_state_name(enum devcoredump_state state)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void hci_devcd_reset(struct hci_dev *hdev) {}
|
||||
static inline void hci_devcd_rx(struct work_struct *work) {}
|
||||
static inline void hci_devcd_timeout(struct work_struct *work) {}
|
||||
|
||||
@@ -653,6 +653,8 @@ enum {
|
||||
#define HCI_LE_LL_EXT_FEATURE 0x80
|
||||
#define HCI_LE_CS 0x40
|
||||
#define HCI_LE_CS_HOST 0x80
|
||||
#define HCI_LE_SCI 0x01 /* byte 9 - Shorter Connection Intervals */
|
||||
#define HCI_LE_SCI_HOST 0x02 /* byte 9 - Shorter Connection Intervals (Host) */
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
@@ -2489,6 +2491,46 @@ struct hci_cp_le_set_host_feature_v2 {
|
||||
__u8 bit_value;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_CONN_RATE 0x20a1
|
||||
struct hci_cp_le_conn_rate {
|
||||
__le16 handle;
|
||||
__le16 interval_min;
|
||||
__le16 interval_max;
|
||||
__le16 subrate_min;
|
||||
__le16 subrate_max;
|
||||
__le16 max_latency;
|
||||
__le16 cont_num;
|
||||
__le16 supv_timeout;
|
||||
__le16 min_ce_len;
|
||||
__le16 max_ce_len;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_DEF_RATE 0x20a2
|
||||
struct hci_cp_le_set_def_rate {
|
||||
__le16 interval_min;
|
||||
__le16 interval_max;
|
||||
__le16 subrate_min;
|
||||
__le16 subrate_max;
|
||||
__le16 max_latency;
|
||||
__le16 cont_num;
|
||||
__le16 supv_timeout;
|
||||
__le16 min_ce_len;
|
||||
__le16 max_ce_len;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_CONN_INTERVAL 0x20a3
|
||||
struct hci_le_conn_interval_group {
|
||||
__le16 min;
|
||||
__le16 max;
|
||||
__le16 stride;
|
||||
} __packed;
|
||||
|
||||
struct hci_rp_le_read_conn_interval {
|
||||
__u8 status;
|
||||
__u8 num_grps;
|
||||
struct hci_le_conn_interval_group grps[];
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
struct hci_ev_status {
|
||||
__u8 status;
|
||||
@@ -3303,6 +3345,17 @@ struct hci_evt_le_cs_test_end_complete {
|
||||
__u8 status;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_CONN_RATE_CHANGE 0x37
|
||||
struct hci_evt_le_conn_rate_change {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__le16 interval;
|
||||
__le16 subrate;
|
||||
__le16 latency;
|
||||
__le16 cont_number;
|
||||
__le16 supv_timeout;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_VENDOR 0xff
|
||||
|
||||
/* Internal events generated by Bluetooth stack */
|
||||
@@ -3329,6 +3382,7 @@ struct hci_ev_si_security {
|
||||
/* ---- HCI Packet structures ---- */
|
||||
#define HCI_COMMAND_HDR_SIZE 3
|
||||
#define HCI_EVENT_HDR_SIZE 2
|
||||
#define HCI_MAX_EVENT_PLEN 255
|
||||
#define HCI_ACL_HDR_SIZE 4
|
||||
#define HCI_SCO_HDR_SIZE 3
|
||||
#define HCI_ISO_HDR_SIZE 4
|
||||
@@ -3407,6 +3461,16 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb)
|
||||
#define hci_handle(h) (h & 0x0fff)
|
||||
#define hci_flags(h) (h >> 12)
|
||||
|
||||
static inline __u16 hci_acl_handle(const struct sk_buff *skb)
|
||||
{
|
||||
return hci_handle(__le16_to_cpu(hci_acl_hdr(skb)->handle));
|
||||
}
|
||||
|
||||
static inline __u16 hci_acl_dlen(const struct sk_buff *skb)
|
||||
{
|
||||
return __le16_to_cpu(hci_acl_hdr(skb)->dlen);
|
||||
}
|
||||
|
||||
/* ISO handle and flags pack/unpack */
|
||||
#define hci_iso_flags_pb(f) (f & 0x0003)
|
||||
#define hci_iso_flags_ts(f) ((f >> 2) & 0x0001)
|
||||
|
||||
@@ -416,6 +416,7 @@ struct hci_dev {
|
||||
__u16 le_conn_max_interval;
|
||||
__u16 le_conn_latency;
|
||||
__u16 le_supv_timeout;
|
||||
__u16 le_min_rate_interval;
|
||||
__u16 le_def_tx_len;
|
||||
__u16 le_def_tx_time;
|
||||
__u16 le_max_tx_len;
|
||||
@@ -645,6 +646,8 @@ struct hci_dev {
|
||||
int (*setup)(struct hci_dev *hdev);
|
||||
int (*shutdown)(struct hci_dev *hdev);
|
||||
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
/* Handle HCI_EV_VENDOR; return true if handled, false otherwise */
|
||||
bool (*handle_ev_vendor)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
void (*notify)(struct hci_dev *hdev, unsigned int evt);
|
||||
void (*hw_error)(struct hci_dev *hdev, u8 code);
|
||||
int (*post_init)(struct hci_dev *hdev);
|
||||
@@ -720,6 +723,11 @@ struct hci_conn {
|
||||
__u16 le_conn_interval;
|
||||
__u16 le_conn_latency;
|
||||
__u16 le_supv_timeout;
|
||||
__u16 le_rate_interval;
|
||||
__u16 le_subrate;
|
||||
__u16 le_rate_latency;
|
||||
__u16 le_cont_num;
|
||||
__u16 le_rate_supv_timeout;
|
||||
__u8 le_adv_data[HCI_MAX_EXT_AD_LENGTH];
|
||||
__u8 le_adv_data_len;
|
||||
__u8 le_per_adv_data[HCI_MAX_PER_AD_TOT_LEN];
|
||||
@@ -769,7 +777,7 @@ struct hci_conn {
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t proto_lock; /* lock guarding protocol data */
|
||||
void *l2cap_data;
|
||||
void *l2cap_data __guarded_by(&proto_lock, &hdev->lock);
|
||||
void *sco_data;
|
||||
void *iso_data __guarded_by(&proto_lock);
|
||||
|
||||
@@ -812,6 +820,14 @@ struct hci_conn_params {
|
||||
u16 conn_latency;
|
||||
u16 supervision_timeout;
|
||||
|
||||
u16 rate_min_interval;
|
||||
u16 rate_max_interval;
|
||||
u16 subrate_min;
|
||||
u16 subrate_max;
|
||||
u16 max_latency;
|
||||
u16 cont_num;
|
||||
u16 rate_supv_timeout;
|
||||
|
||||
enum {
|
||||
HCI_AUTO_CONN_DISABLED,
|
||||
HCI_AUTO_CONN_REPORT,
|
||||
@@ -1771,7 +1787,13 @@ int hci_register_suspend_notifier(struct hci_dev *hdev);
|
||||
int hci_unregister_suspend_notifier(struct hci_dev *hdev);
|
||||
int hci_suspend_dev(struct hci_dev *hdev);
|
||||
int hci_resume_dev(struct hci_dev *hdev);
|
||||
int hci_reset_dev(struct hci_dev *hdev);
|
||||
int __hci_reset_dev(struct hci_dev *hdev, u8 hw_err_code);
|
||||
|
||||
static inline int hci_reset_dev(struct hci_dev *hdev)
|
||||
{
|
||||
return __hci_reset_dev(hdev, 0);
|
||||
}
|
||||
|
||||
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
__printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
|
||||
@@ -2079,6 +2101,11 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
#define le_cs_host_capable(dev) \
|
||||
((dev)->le_features[5] & HCI_LE_CS_HOST)
|
||||
|
||||
#define le_sci_capable(dev) \
|
||||
((dev)->le_features[9] & HCI_LE_SCI)
|
||||
#define le_sci_enabled(dev) \
|
||||
(le_enabled(dev) && le_sci_capable(dev))
|
||||
|
||||
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
|
||||
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
|
||||
|
||||
@@ -2494,6 +2521,8 @@ void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
|
||||
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
|
||||
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
|
||||
bdaddr_t *bdaddr, u8 addr_type);
|
||||
void mgmt_conn_subrate_notify(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u8 status);
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason);
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
|
||||
@@ -59,6 +59,7 @@ int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
const void *param, u8 event, u32 timeout,
|
||||
struct sock *sk);
|
||||
int __hci_reset_sync(struct hci_dev *hdev);
|
||||
int hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
const void *param, u32 timeout);
|
||||
|
||||
@@ -84,9 +85,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
|
||||
struct hci_cmd_sync_work_entry *entry);
|
||||
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy);
|
||||
bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
|
||||
hci_cmd_sync_work_func_t func, void *data,
|
||||
hci_cmd_sync_work_destroy_t destroy);
|
||||
|
||||
int hci_update_eir_sync(struct hci_dev *hdev);
|
||||
int hci_update_class_sync(struct hci_dev *hdev);
|
||||
@@ -185,6 +183,7 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
struct hci_conn_params *params);
|
||||
int hci_le_conn_rate_request(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
@@ -118,6 +118,7 @@ struct mgmt_rp_read_index_list {
|
||||
#define MGMT_SETTING_LL_PRIVACY BIT(22)
|
||||
#define MGMT_SETTING_PAST_SENDER BIT(23)
|
||||
#define MGMT_SETTING_PAST_RECEIVER BIT(24)
|
||||
#define MGMT_SETTING_SCI BIT(25)
|
||||
|
||||
#define MGMT_OP_READ_INFO 0x0004
|
||||
#define MGMT_READ_INFO_SIZE 0
|
||||
@@ -893,6 +894,23 @@ struct mgmt_cp_hci_cmd_sync {
|
||||
} __packed;
|
||||
#define MGMT_HCI_CMD_SYNC_SIZE 6
|
||||
|
||||
#define MGMT_OP_LOAD_CONN_SUBRATE 0x005C
|
||||
struct mgmt_conn_subrate {
|
||||
struct mgmt_addr_info addr;
|
||||
__le16 min_interval;
|
||||
__le16 max_interval;
|
||||
__le16 subrate_min;
|
||||
__le16 subrate_max;
|
||||
__le16 max_latency;
|
||||
__le16 cont_num;
|
||||
__le16 supv_timeout;
|
||||
} __packed;
|
||||
struct mgmt_cp_load_conn_subrate {
|
||||
__le16 param_count;
|
||||
struct mgmt_conn_subrate params[] __counted_by_le(param_count);
|
||||
} __packed;
|
||||
#define MGMT_LOAD_CONN_SUBRATE_SIZE 2
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
__le16 opcode;
|
||||
@@ -1192,3 +1210,14 @@ struct mgmt_ev_mesh_device_found {
|
||||
struct mgmt_ev_mesh_pkt_cmplt {
|
||||
__u8 handle;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CONN_SUBRATE 0x0033
|
||||
struct mgmt_ev_conn_subrate {
|
||||
struct mgmt_addr_info addr;
|
||||
__u8 status;
|
||||
__le16 interval;
|
||||
__le16 subrate;
|
||||
__le16 latency;
|
||||
__le16 cont_num;
|
||||
__le16 supv_timeout;
|
||||
} __packed;
|
||||
|
||||
@@ -1007,6 +1007,8 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
*conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
|
||||
|
||||
BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
|
||||
|
||||
@@ -76,7 +76,7 @@ config BT_LE_L2CAP_ECRED
|
||||
|
||||
config BT_6LOWPAN
|
||||
tristate "Bluetooth 6LoWPAN support"
|
||||
depends on BT_LE && 6LOWPAN
|
||||
depends on BT && BT_LE && 6LOWPAN
|
||||
help
|
||||
IPv6 compression over Bluetooth Low Energy.
|
||||
|
||||
|
||||
@@ -25,3 +25,5 @@ bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
|
||||
bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o
|
||||
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
|
||||
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
@@ -209,6 +209,7 @@ bool bt_sock_linked(struct bt_sock_list *l, struct sock *s)
|
||||
EXPORT_SYMBOL(bt_sock_linked);
|
||||
|
||||
void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
|
||||
__context_unsafe(/* conditional locking */)
|
||||
{
|
||||
const struct cred *old_cred;
|
||||
struct pid *old_pid;
|
||||
@@ -815,7 +816,8 @@ EXPORT_SYMBOL(bt_sock_wait_ready);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void *bt_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(seq->private->l->lock)
|
||||
__acquires_shared(&((struct bt_sock_list *)
|
||||
pde_data(file_inode(seq->file)))->lock)
|
||||
{
|
||||
struct bt_sock_list *l = pde_data(file_inode(seq->file));
|
||||
|
||||
@@ -831,7 +833,8 @@ static void *bt_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
}
|
||||
|
||||
static void bt_seq_stop(struct seq_file *seq, void *v)
|
||||
__releases(seq->private->l->lock)
|
||||
__releases_shared(&((struct bt_sock_list *)
|
||||
pde_data(file_inode(seq->file)))->lock)
|
||||
{
|
||||
struct bt_sock_list *l = pde_data(file_inode(seq->file));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config BT_BNEP
|
||||
tristate "BNEP protocol support"
|
||||
depends on BT_BREDR
|
||||
depends on BT && BT_BREDR
|
||||
select CRC32
|
||||
help
|
||||
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
|
||||
|
||||
@@ -6,3 +6,5 @@
|
||||
obj-$(CONFIG_BT_BNEP) += bnep.o
|
||||
|
||||
bnep-objs := core.o sock.o netdev.o
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
@@ -30,10 +30,9 @@ struct hci_devcoredump_skb_pattern {
|
||||
|
||||
#define DBG_UNEXPECTED_STATE() \
|
||||
bt_dev_dbg(hdev, \
|
||||
"Unexpected packet (%d) for state (%d). ", \
|
||||
hci_dmp_cb(skb)->pkt_type, hdev->dump.state)
|
||||
|
||||
#define MAX_DEVCOREDUMP_HDR_SIZE 512 /* bytes */
|
||||
"Unexpected packet (%d) for state %s.", \
|
||||
hci_dmp_cb(skb)->pkt_type, \
|
||||
hci_devcd_state_name(hdev->dump.state))
|
||||
|
||||
static int hci_devcd_update_hdr_state(char *buf, size_t size, int state)
|
||||
{
|
||||
@@ -50,8 +49,9 @@ static int hci_devcd_update_hdr_state(char *buf, size_t size, int state)
|
||||
/* Call with hci_dev_lock only. */
|
||||
static int hci_devcd_update_state(struct hci_dev *hdev, int state)
|
||||
{
|
||||
bt_dev_dbg(hdev, "Updating devcoredump state from %d to %d.",
|
||||
hdev->dump.state, state);
|
||||
bt_dev_dbg(hdev, "Updating devcoredump state from %s to %s.",
|
||||
hci_devcd_state_name(hdev->dump.state),
|
||||
hci_devcd_state_name(state));
|
||||
|
||||
hdev->dump.state = state;
|
||||
|
||||
@@ -61,7 +61,6 @@ static int hci_devcd_update_state(struct hci_dev *hdev, int state)
|
||||
|
||||
static int hci_devcd_mkheader(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
char dump_start[] = "--- Start dump ---\n";
|
||||
char hdr[80];
|
||||
int hdr_len;
|
||||
|
||||
@@ -72,7 +71,7 @@ static int hci_devcd_mkheader(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (hdev->dump.dmp_hdr)
|
||||
hdev->dump.dmp_hdr(hdev, skb);
|
||||
|
||||
skb_put_data(skb, dump_start, strlen(dump_start));
|
||||
skb_put_data(skb, HCI_DEVCD_HDR_END_MARKER, strlen(HCI_DEVCD_HDR_END_MARKER));
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
@@ -152,7 +151,7 @@ static int hci_devcd_prepare(struct hci_dev *hdev, u32 dump_size)
|
||||
int dump_hdr_size;
|
||||
int err = 0;
|
||||
|
||||
skb = alloc_skb(MAX_DEVCOREDUMP_HDR_SIZE, GFP_ATOMIC);
|
||||
skb = alloc_skb(HCI_DEVCD_HDR_SIZE_MAX, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -245,7 +244,7 @@ static void hci_devcd_dump(struct hci_dev *hdev)
|
||||
struct sk_buff *skb;
|
||||
u32 size;
|
||||
|
||||
bt_dev_dbg(hdev, "state %d", hdev->dump.state);
|
||||
bt_dev_dbg(hdev, "state %s", hci_devcd_state_name(hdev->dump.state));
|
||||
|
||||
size = hdev->dump.tail - hdev->dump.head;
|
||||
|
||||
@@ -368,8 +367,9 @@ void hci_devcd_rx(struct work_struct *work)
|
||||
break;
|
||||
|
||||
default:
|
||||
bt_dev_dbg(hdev, "Unknown packet (%d) for state (%d). ",
|
||||
hci_dmp_cb(skb)->pkt_type, hdev->dump.state);
|
||||
bt_dev_dbg(hdev, "Unknown packet (%d) for state %s.",
|
||||
hci_dmp_cb(skb)->pkt_type,
|
||||
hci_devcd_state_name(hdev->dump.state));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -390,7 +390,6 @@ void hci_devcd_rx(struct work_struct *work)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(hci_devcd_rx);
|
||||
|
||||
void hci_devcd_timeout(struct work_struct *work)
|
||||
{
|
||||
@@ -416,7 +415,6 @@ void hci_devcd_timeout(struct work_struct *work)
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_devcd_timeout);
|
||||
|
||||
int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump,
|
||||
dmp_hdr_t dmp_hdr, notify_change_t notify_change)
|
||||
@@ -551,3 +549,31 @@ int hci_devcd_abort(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_devcd_abort);
|
||||
|
||||
const char *hci_devcd_state_name(enum devcoredump_state state)
|
||||
{
|
||||
const char *state_name = "Unknown";
|
||||
|
||||
switch (state) {
|
||||
case HCI_DEVCOREDUMP_IDLE:
|
||||
state_name = "IDLE";
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_ACTIVE:
|
||||
state_name = "ACTIVE";
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_DONE:
|
||||
state_name = "DONE";
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_ABORT:
|
||||
state_name = "ABORT";
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_TIMEOUT:
|
||||
state_name = "TIMEOUT";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return state_name;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_devcd_state_name);
|
||||
|
||||
@@ -283,8 +283,6 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
|
||||
struct hci_cp_enhanced_setup_sync_conn cp;
|
||||
const struct sco_param *param;
|
||||
|
||||
kfree(conn_handle);
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
@@ -453,6 +451,15 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hci_enhanced_setup_sync_destroy(struct hci_dev *hdev, void *data,
|
||||
int err)
|
||||
{
|
||||
struct conn_handle_t *conn_handle = data;
|
||||
|
||||
hci_conn_put(conn_handle->conn);
|
||||
kfree(conn_handle);
|
||||
}
|
||||
|
||||
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||
{
|
||||
int result;
|
||||
@@ -464,12 +471,15 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||
if (!conn_handle)
|
||||
return false;
|
||||
|
||||
conn_handle->conn = conn;
|
||||
conn_handle->conn = hci_conn_get(conn);
|
||||
conn_handle->handle = handle;
|
||||
result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_setup_sync,
|
||||
conn_handle, NULL);
|
||||
if (result < 0)
|
||||
conn_handle,
|
||||
hci_enhanced_setup_sync_destroy);
|
||||
if (result < 0) {
|
||||
hci_conn_put(conn);
|
||||
kfree(conn_handle);
|
||||
}
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ static DEFINE_IDA(hci_index_ida);
|
||||
/* Get HCI device by index.
|
||||
* Device is held on return. */
|
||||
static struct hci_dev *__hci_dev_get(int index, int *srcu_index)
|
||||
__context_unsafe(/* conditional locking */)
|
||||
{
|
||||
struct hci_dev *hdev = NULL, *d;
|
||||
|
||||
@@ -89,11 +90,13 @@ struct hci_dev *hci_dev_get(int index)
|
||||
}
|
||||
|
||||
static struct hci_dev *hci_dev_get_srcu(int index, int *srcu_index)
|
||||
__context_unsafe(/* conditional locking vs return */)
|
||||
{
|
||||
return __hci_dev_get(index, srcu_index);
|
||||
}
|
||||
|
||||
static void hci_dev_put_srcu(struct hci_dev *hdev, int srcu_index)
|
||||
__context_unsafe(/* conditional locking vs return */)
|
||||
{
|
||||
srcu_read_unlock(&hdev->srcu, srcu_index);
|
||||
hci_dev_put(hdev);
|
||||
@@ -2849,9 +2852,9 @@ int hci_resume_dev(struct hci_dev *hdev)
|
||||
EXPORT_SYMBOL(hci_resume_dev);
|
||||
|
||||
/* Reset HCI device */
|
||||
int hci_reset_dev(struct hci_dev *hdev)
|
||||
int __hci_reset_dev(struct hci_dev *hdev, u8 hw_err_code)
|
||||
{
|
||||
static const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, 0x00 };
|
||||
const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, hw_err_code };
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(3, GFP_ATOMIC);
|
||||
@@ -2866,7 +2869,7 @@ int hci_reset_dev(struct hci_dev *hdev)
|
||||
/* Send Hardware Error to upper stack */
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_reset_dev);
|
||||
EXPORT_SYMBOL(__hci_reset_dev);
|
||||
|
||||
static u8 hci_dev_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
@@ -2901,10 +2904,9 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (hci_conn_num(hdev, CIS_LINK) ||
|
||||
hci_conn_num(hdev, BIS_LINK) ||
|
||||
hci_conn_num(hdev, PA_LINK)) {
|
||||
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
|
||||
__u8 type;
|
||||
|
||||
type = hci_conn_lookup_type(hdev, hci_handle(handle));
|
||||
type = hci_conn_lookup_type(hdev, hci_acl_handle(skb));
|
||||
if (type == CIS_LINK || type == BIS_LINK ||
|
||||
type == PA_LINK)
|
||||
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
|
||||
|
||||
@@ -1161,16 +1161,12 @@ static ssize_t force_no_mitm_write(struct file *file,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
if (kstrtobool(buf, &enable))
|
||||
return -EINVAL;
|
||||
err = kstrtobool_from_user(user_buf, count, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM))
|
||||
return -EALREADY;
|
||||
|
||||
@@ -269,7 +269,10 @@ static u8 hci_cc_reset(struct hci_dev *hdev, void *data, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_status *rp = data;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
if (rp->status)
|
||||
bt_dev_err(hdev, "status 0x%2.2x", rp->status);
|
||||
else
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
clear_bit(HCI_RESET, &hdev->flags);
|
||||
|
||||
@@ -294,8 +297,10 @@ static u8 hci_cc_reset(struct hci_dev *hdev, void *data, struct sk_buff *skb)
|
||||
|
||||
hdev->ssp_debug_mode = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_bdaddr_list_clear(&hdev->le_accept_list);
|
||||
hci_bdaddr_list_clear(&hdev->le_resolv_list);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
@@ -1238,6 +1243,39 @@ static u8 hci_cc_le_read_local_features(struct hci_dev *hdev, void *data,
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_read_conn_interval(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_conn_interval *rp = data;
|
||||
u16 min_interval = 0;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return rp->status;
|
||||
|
||||
if (skb->len < flex_array_size(rp, grps, rp->num_grps)) {
|
||||
bt_dev_err(hdev, "Invalid response length for 0x%4.4x",
|
||||
HCI_OP_LE_READ_CONN_INTERVAL);
|
||||
return HCI_ERROR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
/* Store the smallest minimum supported connection interval reported by
|
||||
* the controller so the default rate parameters can be clamped to it.
|
||||
*/
|
||||
for (i = 0; i < rp->num_grps; i++) {
|
||||
u16 min = le16_to_cpu(rp->grps[i].min);
|
||||
|
||||
if (!min_interval || min < min_interval)
|
||||
min_interval = min;
|
||||
}
|
||||
|
||||
hdev->le_min_rate_interval = min_interval;
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -3827,8 +3865,10 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_CIG_PARAMS);
|
||||
if (!rp->status && (!cp || rp->num_handles != cp->num_cis ||
|
||||
rp->cig_id != cp->cig_id)) {
|
||||
if (!rp->status &&
|
||||
(!cp || rp->num_handles != cp->num_cis ||
|
||||
rp->cig_id != cp->cig_id ||
|
||||
skb->len < array_size(rp->num_handles, sizeof(*rp->handle)))) {
|
||||
bt_dev_err(hdev, "unexpected Set CIG Parameters response data");
|
||||
status = HCI_ERROR_UNSPECIFIED;
|
||||
}
|
||||
@@ -4150,6 +4190,9 @@ static const struct hci_cc {
|
||||
sizeof(struct hci_rp_le_read_buffer_size)),
|
||||
HCI_CC(HCI_OP_LE_READ_LOCAL_FEATURES, hci_cc_le_read_local_features,
|
||||
sizeof(struct hci_rp_le_read_local_features)),
|
||||
HCI_CC_VL(HCI_OP_LE_READ_CONN_INTERVAL, hci_cc_le_read_conn_interval,
|
||||
sizeof(struct hci_rp_le_read_conn_interval),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
HCI_CC(HCI_OP_LE_READ_ADV_TX_POWER, hci_cc_le_read_adv_tx_power,
|
||||
sizeof(struct hci_rp_le_read_adv_tx_power)),
|
||||
HCI_CC(HCI_OP_USER_CONFIRM_REPLY, hci_cc_user_confirm_reply,
|
||||
@@ -5864,6 +5907,17 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are central and have subrate parameters stored, queue a
|
||||
* connection rate request to apply them.
|
||||
*/
|
||||
if (conn->role == HCI_ROLE_MASTER && le_sci_capable(hdev)) {
|
||||
struct hci_conn_params *p;
|
||||
|
||||
p = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||
if (p && p->subrate_max)
|
||||
hci_le_conn_rate_request(hdev, conn);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_update_passive_scan(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -6604,6 +6658,13 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
bt_dev_dbg(hdev, "sync_handle 0x%4.4x", le16_to_cpu(ev->sync_handle));
|
||||
|
||||
/* The reassembly in iso_connect_ind() copies ev->length bytes from the
|
||||
* stored event, so make sure the event actually carries that many data
|
||||
* bytes before it is consumed.
|
||||
*/
|
||||
if (!hci_le_ev_skb_pull(hdev, skb, HCI_EV_LE_PER_ADV_REPORT, ev->length))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, PA_LINK, &flags);
|
||||
@@ -7360,6 +7421,36 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_conn_rate_change_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_conn_rate_change *ev = data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
/* Only update the stored rate parameters on success; on
|
||||
* failure the values in the event are not valid. Userspace is
|
||||
* notified either way.
|
||||
*/
|
||||
if (!ev->status) {
|
||||
conn->le_rate_interval = le16_to_cpu(ev->interval);
|
||||
conn->le_subrate = le16_to_cpu(ev->subrate);
|
||||
conn->le_rate_latency = le16_to_cpu(ev->latency);
|
||||
conn->le_cont_num = le16_to_cpu(ev->cont_number);
|
||||
conn->le_rate_supv_timeout =
|
||||
le16_to_cpu(ev->supv_timeout);
|
||||
}
|
||||
mgmt_conn_subrate_notify(hdev, conn, ev->status);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
|
||||
[_op] = { \
|
||||
.func = _func, \
|
||||
@@ -7471,6 +7562,9 @@ static const struct hci_le_ev {
|
||||
sizeof(struct
|
||||
hci_evt_le_read_all_remote_features_complete),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
/* [0x37 = HCI_EVT_LE_CONN_RATE_CHANGE] */
|
||||
HCI_LE_EV(HCI_EVT_LE_CONN_RATE_CHANGE, hci_le_conn_rate_change_evt,
|
||||
sizeof(struct hci_evt_le_conn_rate_change)),
|
||||
};
|
||||
|
||||
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
||||
@@ -7517,6 +7611,14 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
||||
subev->func(hdev, data, skb);
|
||||
}
|
||||
|
||||
static void hci_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
|
||||
{
|
||||
if (hdev->handle_ev_vendor && hdev->handle_ev_vendor(hdev, skb))
|
||||
return;
|
||||
|
||||
msft_vendor_evt(hdev, data, skb);
|
||||
}
|
||||
|
||||
static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
||||
u8 event, struct sk_buff *skb)
|
||||
{
|
||||
@@ -7633,7 +7735,7 @@ static const struct hci_ev {
|
||||
HCI_EV_STATUS(HCI_EV_INQUIRY_COMPLETE, hci_inquiry_complete_evt),
|
||||
/* [0x02 = HCI_EV_INQUIRY_RESULT] */
|
||||
HCI_EV_VL(HCI_EV_INQUIRY_RESULT, hci_inquiry_result_evt,
|
||||
sizeof(struct hci_ev_inquiry_result), HCI_MAX_EVENT_SIZE),
|
||||
sizeof(struct hci_ev_inquiry_result), HCI_MAX_EVENT_PLEN),
|
||||
/* [0x03 = HCI_EV_CONN_COMPLETE] */
|
||||
HCI_EV(HCI_EV_CONN_COMPLETE, hci_conn_complete_evt,
|
||||
sizeof(struct hci_ev_conn_complete)),
|
||||
@@ -7661,7 +7763,7 @@ static const struct hci_ev {
|
||||
sizeof(struct hci_ev_remote_features)),
|
||||
/* [0x0e = HCI_EV_CMD_COMPLETE] */
|
||||
HCI_EV_REQ_VL(HCI_EV_CMD_COMPLETE, hci_cmd_complete_evt,
|
||||
sizeof(struct hci_ev_cmd_complete), HCI_MAX_EVENT_SIZE),
|
||||
sizeof(struct hci_ev_cmd_complete), HCI_MAX_EVENT_PLEN),
|
||||
/* [0x0f = HCI_EV_CMD_STATUS] */
|
||||
HCI_EV_REQ(HCI_EV_CMD_STATUS, hci_cmd_status_evt,
|
||||
sizeof(struct hci_ev_cmd_status)),
|
||||
@@ -7673,7 +7775,7 @@ static const struct hci_ev {
|
||||
sizeof(struct hci_ev_role_change)),
|
||||
/* [0x13 = HCI_EV_NUM_COMP_PKTS] */
|
||||
HCI_EV_VL(HCI_EV_NUM_COMP_PKTS, hci_num_comp_pkts_evt,
|
||||
sizeof(struct hci_ev_num_comp_pkts), HCI_MAX_EVENT_SIZE),
|
||||
sizeof(struct hci_ev_num_comp_pkts), HCI_MAX_EVENT_PLEN),
|
||||
/* [0x14 = HCI_EV_MODE_CHANGE] */
|
||||
HCI_EV(HCI_EV_MODE_CHANGE, hci_mode_change_evt,
|
||||
sizeof(struct hci_ev_mode_change)),
|
||||
@@ -7699,7 +7801,7 @@ static const struct hci_ev {
|
||||
HCI_EV_VL(HCI_EV_INQUIRY_RESULT_WITH_RSSI,
|
||||
hci_inquiry_result_with_rssi_evt,
|
||||
sizeof(struct hci_ev_inquiry_result_rssi),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
HCI_MAX_EVENT_PLEN),
|
||||
/* [0x23 = HCI_EV_REMOTE_EXT_FEATURES] */
|
||||
HCI_EV(HCI_EV_REMOTE_EXT_FEATURES, hci_remote_ext_features_evt,
|
||||
sizeof(struct hci_ev_remote_ext_features)),
|
||||
@@ -7709,7 +7811,7 @@ static const struct hci_ev {
|
||||
/* [0x2f = HCI_EV_EXTENDED_INQUIRY_RESULT] */
|
||||
HCI_EV_VL(HCI_EV_EXTENDED_INQUIRY_RESULT,
|
||||
hci_extended_inquiry_result_evt,
|
||||
sizeof(struct hci_ev_ext_inquiry_result), HCI_MAX_EVENT_SIZE),
|
||||
sizeof(struct hci_ev_ext_inquiry_result), HCI_MAX_EVENT_PLEN),
|
||||
/* [0x30 = HCI_EV_KEY_REFRESH_COMPLETE] */
|
||||
HCI_EV(HCI_EV_KEY_REFRESH_COMPLETE, hci_key_refresh_complete_evt,
|
||||
sizeof(struct hci_ev_key_refresh_complete)),
|
||||
@@ -7742,9 +7844,9 @@ static const struct hci_ev {
|
||||
sizeof(struct hci_ev_remote_host_features)),
|
||||
/* [0x3e = HCI_EV_LE_META] */
|
||||
HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt,
|
||||
sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_SIZE),
|
||||
sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_PLEN),
|
||||
/* [0xff = HCI_EV_VENDOR] */
|
||||
HCI_EV_VL(HCI_EV_VENDOR, msft_vendor_evt, 0, HCI_MAX_EVENT_SIZE),
|
||||
HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_PLEN),
|
||||
};
|
||||
|
||||
static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb,
|
||||
|
||||
@@ -540,8 +540,6 @@ static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
u8 instance = *(u8 *)data;
|
||||
|
||||
kfree(data);
|
||||
|
||||
hci_clear_adv_instance_sync(hdev, NULL, instance, false);
|
||||
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
@@ -550,6 +548,12 @@ static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adv_timeout_expire_destroy(struct hci_dev *hdev, void *data,
|
||||
int err)
|
||||
{
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static void adv_timeout_expire(struct work_struct *work)
|
||||
{
|
||||
u8 *inst_ptr;
|
||||
@@ -570,7 +574,9 @@ static void adv_timeout_expire(struct work_struct *work)
|
||||
goto unlock;
|
||||
|
||||
*inst_ptr = hdev->cur_adv_instance;
|
||||
hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr, NULL);
|
||||
if (hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr,
|
||||
adv_timeout_expire_destroy) < 0)
|
||||
kfree(inst_ptr);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -860,32 +866,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_cancel_entry);
|
||||
|
||||
/* Dequeue one HCI command entry:
|
||||
*
|
||||
* - Lookup and cancel first entry that matches.
|
||||
*/
|
||||
bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
|
||||
hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
|
||||
if (!entry) {
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_dequeue_once);
|
||||
|
||||
/* Dequeue HCI command entry:
|
||||
*
|
||||
* - Lookup and cancel any entry that matches by function callback or data or
|
||||
@@ -1170,6 +1150,32 @@ int hci_update_random_address_sync(struct hci_dev *hdev, bool require_privacy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_disable_ext_adv_legacy_instance_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_le_set_ext_adv_enable *cp;
|
||||
struct hci_cp_ext_adv_set *set;
|
||||
u8 data[sizeof(*cp) + sizeof(*set) * 1];
|
||||
u8 size;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ADV_0))
|
||||
return 0;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
cp = (void *)data;
|
||||
set = (void *)cp->data;
|
||||
|
||||
cp->num_of_sets = 0x01;
|
||||
cp->enable = 0x00;
|
||||
|
||||
set->handle = 0x00;
|
||||
|
||||
size = sizeof(*cp) + sizeof(*set) * cp->num_of_sets;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_ENABLE,
|
||||
size, data, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_disable_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct hci_cp_le_set_ext_adv_enable *cp;
|
||||
@@ -1395,6 +1401,10 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
err = hci_disable_ext_adv_legacy_instance_sync(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
adv = NULL;
|
||||
}
|
||||
|
||||
@@ -3780,18 +3790,20 @@ static const struct hci_init_stage hci_init0[] = {
|
||||
|
||||
int hci_reset_sync(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
set_bit(HCI_RESET, &hdev->flags);
|
||||
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
|
||||
HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Send a raw HCI reset for use by vendor drivers */
|
||||
int __hci_reset_sync(struct hci_dev *hdev)
|
||||
{
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
}
|
||||
EXPORT_SYMBOL(__hci_reset_sync);
|
||||
|
||||
static int hci_init0_sync(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
@@ -4565,6 +4577,10 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
|
||||
events[6] |= 0x02; /* LE CS Subevent Result Continue event */
|
||||
events[6] |= 0x04; /* LE CS Test End Complete event */
|
||||
}
|
||||
|
||||
if (le_sci_capable(hdev))
|
||||
events[6] |= 0x40; /* LE Connection Rate Change event */
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK,
|
||||
sizeof(events), events, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
@@ -4733,12 +4749,59 @@ static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u16 bit, u8 value)
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_le_read_conn_interval_sync(struct hci_dev *hdev)
|
||||
{
|
||||
if (!le_sci_capable(hdev))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_CONN_INTERVAL,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_le_set_def_rate_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_le_set_def_rate cp;
|
||||
u16 interval_min = 0x000a; /* 1.25 ms */
|
||||
u16 interval_max = 0x0078; /* 15 ms */
|
||||
|
||||
if (!le_sci_capable(hdev))
|
||||
return 0;
|
||||
|
||||
/* Clamp the interval range to the controller's minimum supported
|
||||
* connection interval (read via HCI_OP_LE_READ_CONN_INTERVAL) so the
|
||||
* default rate parameters are not rejected. The maximum is raised as
|
||||
* well if needed to keep interval_min <= interval_max.
|
||||
*/
|
||||
if (hdev->le_min_rate_interval > interval_min) {
|
||||
interval_min = hdev->le_min_rate_interval;
|
||||
if (interval_min > interval_max)
|
||||
interval_max = interval_min;
|
||||
}
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
/* Use the HIDS 1.2 recommended Full Range mode values as the default
|
||||
* rate parameters (see HOGP v1.2 spec). Connection intervals are in
|
||||
* units of 0.125 ms and the supervision timeout is in units of 10 ms.
|
||||
*/
|
||||
cp.interval_min = cpu_to_le16(interval_min);
|
||||
cp.interval_max = cpu_to_le16(interval_max);
|
||||
cp.subrate_min = cpu_to_le16(0x0001);
|
||||
cp.subrate_max = cpu_to_le16(0x0004);
|
||||
cp.max_latency = cpu_to_le16(0x0000);
|
||||
cp.cont_num = cpu_to_le16(0x0001);
|
||||
cp.supv_timeout = cpu_to_le16(0x000c); /* 120 ms */
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DEF_RATE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Set Host Features, each feature needs to be sent separately since
|
||||
* HCI_OP_LE_SET_HOST_FEATURE doesn't support setting all of them at once.
|
||||
*/
|
||||
static int hci_le_set_host_features_sync(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (cis_capable(hdev)) {
|
||||
/* Connected Isochronous Channels (Host Support) */
|
||||
@@ -4749,9 +4812,16 @@ static int hci_le_set_host_features_sync(struct hci_dev *hdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (le_cs_capable(hdev))
|
||||
if (le_cs_capable(hdev)) {
|
||||
/* Channel Sounding (Host Support) */
|
||||
err = hci_le_set_host_feature_sync(hdev, 47, 0x01);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (le_sci_capable(hdev))
|
||||
/* Shorter Connection Intervals (Host Support) */
|
||||
err = hci_le_set_host_feature_sync(hdev, 73, 0x01);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -4784,6 +4854,10 @@ static const struct hci_init_stage le_init3[] = {
|
||||
HCI_INIT(hci_set_le_support_sync),
|
||||
/* HCI_OP_LE_SET_HOST_FEATURE */
|
||||
HCI_INIT(hci_le_set_host_features_sync),
|
||||
/* HCI_OP_LE_READ_CONN_INTERVAL */
|
||||
HCI_INIT(hci_le_read_conn_interval_sync),
|
||||
/* HCI_OP_LE_SET_DEF_RATE */
|
||||
HCI_INIT(hci_le_set_def_rate_sync),
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -6313,6 +6387,8 @@ static int hci_pause_discovery_sync(struct hci_dev *hdev)
|
||||
static int hci_update_event_filter_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct bdaddr_list_with_flags *b;
|
||||
bdaddr_t *accept_list;
|
||||
size_t i, num_entries = 0;
|
||||
u8 scan = SCAN_DISABLED;
|
||||
bool scanning = test_bit(HCI_PSCAN, &hdev->flags);
|
||||
int err;
|
||||
@@ -6329,23 +6405,49 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev)
|
||||
/* Always clear event filter when starting */
|
||||
hci_clear_event_filter_sync(hdev);
|
||||
|
||||
list_for_each_entry(b, &hdev->accept_list, list) {
|
||||
if (!(b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP))
|
||||
continue;
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr);
|
||||
list_for_each_entry(b, &hdev->accept_list, list)
|
||||
if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)
|
||||
num_entries++;
|
||||
|
||||
err = hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP,
|
||||
HCI_CONN_SETUP_ALLOW_BDADDR,
|
||||
&b->bdaddr,
|
||||
HCI_CONN_SETUP_AUTO_ON);
|
||||
if (!num_entries) {
|
||||
hci_dev_unlock(hdev);
|
||||
goto update_scan;
|
||||
}
|
||||
|
||||
accept_list = kmalloc_array(num_entries, sizeof(*accept_list),
|
||||
GFP_KERNEL);
|
||||
if (!accept_list) {
|
||||
hci_dev_unlock(hdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(b, &hdev->accept_list, list)
|
||||
if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)
|
||||
bacpy(&accept_list[i++], &b->bdaddr);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
bt_dev_dbg(hdev, "Adding event filters for %pMR",
|
||||
&accept_list[i]);
|
||||
|
||||
err = hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP,
|
||||
HCI_CONN_SETUP_ALLOW_BDADDR,
|
||||
&accept_list[i],
|
||||
HCI_CONN_SETUP_AUTO_ON);
|
||||
if (err)
|
||||
bt_dev_err(hdev, "Failed to set event filter for %pMR",
|
||||
&b->bdaddr);
|
||||
&accept_list[i]);
|
||||
else
|
||||
scan = SCAN_PAGE;
|
||||
}
|
||||
|
||||
kfree(accept_list);
|
||||
|
||||
update_scan:
|
||||
if (scan && !scanning)
|
||||
hci_write_scan_enable_sync(hdev, scan);
|
||||
else if (!scan && scanning)
|
||||
@@ -7317,6 +7419,75 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_le_conn_rate_request_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
struct hci_conn_params *params;
|
||||
struct hci_cp_le_conn_rate cp;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* The request was queued asynchronously so re-validate the connection
|
||||
* and its parameters under hdev->lock. The connection may have been
|
||||
* torn down, or may not have a valid handle yet (still connecting),
|
||||
* and the parameters may have been removed in the meantime (e.g. by
|
||||
* Load Connection Parameters). Snapshot the rate values so the
|
||||
* blocking command below can run without holding hdev->lock.
|
||||
*/
|
||||
if (!hci_conn_valid(hdev, conn) ||
|
||||
HCI_CONN_HANDLE_UNSET(conn->handle)) {
|
||||
hci_dev_unlock(hdev);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||
if (!params) {
|
||||
hci_dev_unlock(hdev);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.interval_min = cpu_to_le16(params->rate_min_interval);
|
||||
cp.interval_max = cpu_to_le16(params->rate_max_interval);
|
||||
cp.subrate_min = cpu_to_le16(params->subrate_min);
|
||||
cp.subrate_max = cpu_to_le16(params->subrate_max);
|
||||
cp.max_latency = cpu_to_le16(params->max_latency);
|
||||
cp.cont_num = cpu_to_le16(params->cont_num);
|
||||
cp.supv_timeout = cpu_to_le16(params->rate_supv_timeout);
|
||||
cp.min_ce_len = cpu_to_le16(0x0000);
|
||||
cp.max_ce_len = cpu_to_le16(0x0000);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CONN_RATE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static void hci_le_conn_rate_request_destroy(struct hci_dev *hdev, void *data,
|
||||
int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_le_conn_rate_request(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Hold a reference to the connection so it cannot be freed while the
|
||||
* request is pending or running on the cmd_sync worker.
|
||||
*/
|
||||
err = hci_cmd_sync_queue(hdev, hci_le_conn_rate_request_sync,
|
||||
hci_conn_get(conn),
|
||||
hci_le_conn_rate_request_destroy);
|
||||
if (err < 0)
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config BT_HIDP
|
||||
tristate "HIDP protocol support"
|
||||
depends on BT_BREDR && HID
|
||||
depends on BT && BT_BREDR && HID
|
||||
help
|
||||
HIDP (Human Interface Device Protocol) is a transport layer
|
||||
for HID reports. HIDP is required for the Bluetooth Human
|
||||
|
||||
@@ -6,3 +6,5 @@
|
||||
obj-$(CONFIG_BT_HIDP) += hidp.o
|
||||
|
||||
hidp-objs := core.o sock.o
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
@@ -1536,6 +1536,7 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
memset(sa, 0, sizeof(struct sockaddr_iso));
|
||||
addr->sa_family = AF_BLUETOOTH;
|
||||
|
||||
if (peer) {
|
||||
@@ -1546,6 +1547,7 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
sa->iso_bdaddr_type = iso_pi(sk)->dst_type;
|
||||
|
||||
if (hcon && (hcon->type == BIS_LINK || hcon->type == PA_LINK)) {
|
||||
memset(sa->iso_bc, 0, sizeof(struct sockaddr_iso_bc));
|
||||
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,
|
||||
@@ -1658,9 +1660,9 @@ static void iso_conn_defer_accept(struct hci_conn *conn)
|
||||
hci_send_cmd(hdev, HCI_OP_LE_ACCEPT_CIS, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void iso_conn_big_sync(struct sock *sk)
|
||||
static int iso_conn_big_sync(struct sock *sk)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
struct hci_dev *hdev;
|
||||
struct iso_conn *conn;
|
||||
bdaddr_t src, dst;
|
||||
@@ -1675,7 +1677,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
hdev = hci_get_route(&dst, &src, src_type);
|
||||
|
||||
if (!hdev)
|
||||
return;
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
/* hci_le_big_create_sync requires hdev lock to be held, since
|
||||
* it enqueues the HCI LE BIG Create Sync command via
|
||||
@@ -1691,8 +1693,10 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
* both before dereferencing conn->hcon.
|
||||
*/
|
||||
conn = iso_pi(sk)->conn;
|
||||
if (!conn || !conn->hcon)
|
||||
if (!conn || !conn->hcon) {
|
||||
err = -ENOTCONN;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
|
||||
err = hci_conn_big_create_sync(hdev, conn->hcon,
|
||||
@@ -1708,6 +1712,8 @@ static void iso_conn_big_sync(struct sock *sk)
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
@@ -1732,10 +1738,19 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
case BT_CONNECT2:
|
||||
if (test_bit(BT_SK_PA_SYNC, &pi->flags)) {
|
||||
release_sock(sk);
|
||||
iso_conn_big_sync(sk);
|
||||
err = iso_conn_big_sync(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
sk->sk_state = BT_LISTEN;
|
||||
/* The socket lock was dropped, so the
|
||||
* connection may have been torn down
|
||||
* meanwhile and iso_chan_del() may have
|
||||
* already moved the socket to BT_CLOSED.
|
||||
* Only move on to BT_LISTEN if the BIG sync
|
||||
* was actually started and nothing else has
|
||||
* changed the state.
|
||||
*/
|
||||
if (!err && sk->sk_state == BT_CONNECT2)
|
||||
sk->sk_state = BT_LISTEN;
|
||||
} else {
|
||||
iso_conn_defer_accept(pi->conn->hcon);
|
||||
sk->sk_state = BT_CONFIG;
|
||||
@@ -1746,10 +1761,11 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
case BT_CONNECTED:
|
||||
if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
|
||||
release_sock(sk);
|
||||
iso_conn_big_sync(sk);
|
||||
err = iso_conn_big_sync(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
sk->sk_state = BT_LISTEN;
|
||||
if (!err && sk->sk_state == BT_CONNECTED)
|
||||
sk->sk_state = BT_LISTEN;
|
||||
early_ret = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1791,6 +1791,7 @@ static void l2cap_unregister_all_users(struct l2cap_conn *conn)
|
||||
}
|
||||
|
||||
static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_chan *chan, *l;
|
||||
@@ -1833,7 +1834,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
||||
hci_chan_del(conn->hchan);
|
||||
conn->hchan = NULL;
|
||||
|
||||
spin_lock(&hcon->proto_lock);
|
||||
hcon->l2cap_data = NULL;
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
mutex_unlock(&conn->lock);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
@@ -7150,6 +7154,7 @@ static void process_pending_rx(struct work_struct *work)
|
||||
}
|
||||
|
||||
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct hci_chan *hchan;
|
||||
@@ -7168,8 +7173,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
||||
}
|
||||
|
||||
kref_init(&conn->ref);
|
||||
hcon->l2cap_data = conn;
|
||||
conn->hcon = hci_conn_get(hcon);
|
||||
conn->hchan = hchan;
|
||||
|
||||
BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
|
||||
@@ -7198,6 +7201,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
||||
|
||||
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
spin_lock(&hcon->proto_lock);
|
||||
conn->hcon = hci_conn_get(hcon);
|
||||
hcon->l2cap_data = conn;
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -7352,6 +7360,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
||||
goto done;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = l2cap_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
@@ -7522,6 +7532,7 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
|
||||
}
|
||||
|
||||
static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct hci_dev *hdev = hcon->hdev;
|
||||
struct l2cap_conn *conn;
|
||||
@@ -7582,16 +7593,22 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
|
||||
int l2cap_disconn_ind(struct hci_conn *hcon)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_conn *conn;
|
||||
int ret = HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
BT_DBG("hcon %p", hcon);
|
||||
|
||||
if (!conn)
|
||||
return HCI_ERROR_REMOTE_USER_TERM;
|
||||
return conn->disc_reason;
|
||||
spin_lock(&hcon->proto_lock);
|
||||
conn = hcon->l2cap_data;
|
||||
if (conn)
|
||||
ret = conn->disc_reason;
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
|
||||
return;
|
||||
@@ -7619,6 +7636,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
|
||||
}
|
||||
|
||||
static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_chan *chan;
|
||||
@@ -7803,6 +7821,8 @@ int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
hci_conn_enter_active_mode(hcon, BT_POWER_FORCE_ACTIVE_OFF);
|
||||
|
||||
conn = hcon->l2cap_data;
|
||||
|
||||
@@ -1365,6 +1365,7 @@ static int __l2cap_wait_ack(struct sock *sk, struct l2cap_chan *chan)
|
||||
}
|
||||
|
||||
static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
__context_unsafe(/* complex chan->conn locking */)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
@@ -861,6 +861,9 @@ static u32 get_supported_settings(struct hci_dev *hdev)
|
||||
if (past_receiver_capable(hdev))
|
||||
settings |= MGMT_SETTING_PAST_RECEIVER;
|
||||
|
||||
if (le_sci_capable(hdev))
|
||||
settings |= MGMT_SETTING_SCI;
|
||||
|
||||
settings |= MGMT_SETTING_PHY_CONFIGURATION;
|
||||
|
||||
return settings;
|
||||
@@ -952,6 +955,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
||||
if (past_receiver_enabled(hdev))
|
||||
settings |= MGMT_SETTING_PAST_RECEIVER;
|
||||
|
||||
if (le_sci_enabled(hdev))
|
||||
settings |= MGMT_SETTING_SCI;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -2431,11 +2437,15 @@ static int send_cancel(struct hci_dev *hdev, void *data)
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
|
||||
0, NULL, 0);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_cancel_destroy(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
mgmt_pending_free(data);
|
||||
}
|
||||
|
||||
static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
@@ -2456,7 +2466,8 @@ static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev,
|
||||
if (!cmd)
|
||||
err = -ENOMEM;
|
||||
else
|
||||
err = hci_cmd_sync_queue(hdev, send_cancel, cmd, NULL);
|
||||
err = hci_cmd_sync_queue(hdev, send_cancel, cmd,
|
||||
send_cancel_destroy);
|
||||
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
|
||||
@@ -2642,7 +2653,7 @@ static int send_hci_cmd_sync(struct hci_dev *hdev, void *data)
|
||||
if (IS_ERR(skb)) {
|
||||
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
|
||||
mgmt_status(PTR_ERR(skb)));
|
||||
goto done;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, 0,
|
||||
@@ -2650,12 +2661,14 @@ static int send_hci_cmd_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
done:
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_hci_cmd_sync_destroy(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
mgmt_pending_free(data);
|
||||
}
|
||||
|
||||
static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
@@ -2668,12 +2681,21 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
/* The HCI command header carries the parameter length in a u8, a
|
||||
* larger value would be truncated there while the parameters are
|
||||
* still appended to the frame in full.
|
||||
*/
|
||||
if (le16_to_cpu(cp->params_len) > U8_MAX)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_HCI_CMD_SYNC, hdev, data, len);
|
||||
if (!cmd)
|
||||
err = -ENOMEM;
|
||||
else
|
||||
err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd, NULL);
|
||||
err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd,
|
||||
send_hci_cmd_sync_destroy);
|
||||
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
|
||||
@@ -3870,6 +3892,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
|
||||
}
|
||||
|
||||
if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) {
|
||||
lockdep_assert_held(&conn->hdev->lock);
|
||||
|
||||
err = smp_user_confirm_reply(conn, mgmt_op, passkey);
|
||||
if (!err)
|
||||
err = mgmt_cmd_complete(sk, hdev->id, mgmt_op,
|
||||
@@ -8170,6 +8194,118 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static int load_conn_subrate(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 len)
|
||||
{
|
||||
struct mgmt_cp_load_conn_subrate *cp = data;
|
||||
const u16 max_param_count = ((U16_MAX - sizeof(*cp)) /
|
||||
sizeof(struct mgmt_conn_subrate));
|
||||
u16 param_count, expected_len;
|
||||
int i;
|
||||
|
||||
if (!lmp_le_capable(hdev) || !le_sci_capable(hdev))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
param_count = __le16_to_cpu(cp->param_count);
|
||||
if (param_count > max_param_count) {
|
||||
bt_dev_err(hdev, "too big param_count value %u", param_count);
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
expected_len = struct_size(cp, params, param_count);
|
||||
if (expected_len != len) {
|
||||
bt_dev_err(hdev, "expected %u bytes, got %u bytes",
|
||||
expected_len, len);
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "param_count %u", param_count);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (i = 0; i < param_count; i++) {
|
||||
struct mgmt_conn_subrate *param = &cp->params[i];
|
||||
struct hci_conn_params *hci_param;
|
||||
u16 min, max, subrate_min, subrate_max;
|
||||
u16 max_latency, cont_num, supv_timeout;
|
||||
u8 addr_type;
|
||||
|
||||
bt_dev_dbg(hdev, "Adding subrate %pMR (type %u)",
|
||||
¶m->addr.bdaddr, param->addr.type);
|
||||
|
||||
if (param->addr.type == BDADDR_LE_PUBLIC) {
|
||||
addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
} else if (param->addr.type == BDADDR_LE_RANDOM) {
|
||||
addr_type = ADDR_LE_DEV_RANDOM;
|
||||
} else {
|
||||
bt_dev_err(hdev, "ignoring invalid connection subrate parameters");
|
||||
continue;
|
||||
}
|
||||
|
||||
min = le16_to_cpu(param->min_interval);
|
||||
max = le16_to_cpu(param->max_interval);
|
||||
subrate_min = le16_to_cpu(param->subrate_min);
|
||||
subrate_max = le16_to_cpu(param->subrate_max);
|
||||
max_latency = le16_to_cpu(param->max_latency);
|
||||
cont_num = le16_to_cpu(param->cont_num);
|
||||
supv_timeout = le16_to_cpu(param->supv_timeout);
|
||||
|
||||
/* Validate the parameters before storing them. Reject
|
||||
* logically inconsistent values instead of forwarding them to
|
||||
* the controller.
|
||||
*/
|
||||
if (min > max || subrate_min > subrate_max ||
|
||||
subrate_min < 1 || supv_timeout < 1) {
|
||||
bt_dev_err(hdev, "ignoring invalid connection subrate parameters");
|
||||
continue;
|
||||
}
|
||||
|
||||
hci_param = hci_conn_params_add(hdev, ¶m->addr.bdaddr,
|
||||
addr_type);
|
||||
if (!hci_param) {
|
||||
bt_dev_err(hdev, "failed to add connection parameters");
|
||||
continue;
|
||||
}
|
||||
|
||||
hci_param->rate_min_interval = min;
|
||||
hci_param->rate_max_interval = max;
|
||||
hci_param->subrate_min = subrate_min;
|
||||
hci_param->subrate_max = subrate_max;
|
||||
hci_param->max_latency = max_latency;
|
||||
hci_param->cont_num = cont_num;
|
||||
hci_param->rate_supv_timeout = supv_timeout;
|
||||
|
||||
/* If the device is connected as central check if the
|
||||
* connection rate parameters need to be updated.
|
||||
*/
|
||||
if (!i && param_count == 1) {
|
||||
struct hci_conn *conn;
|
||||
|
||||
conn = hci_conn_hash_lookup_le(hdev,
|
||||
&hci_param->addr,
|
||||
addr_type);
|
||||
if (conn && conn->state == BT_CONNECTED &&
|
||||
conn->role == HCI_ROLE_MASTER &&
|
||||
(conn->le_rate_interval < min ||
|
||||
conn->le_rate_interval > max ||
|
||||
conn->le_subrate < subrate_min ||
|
||||
conn->le_subrate > subrate_max ||
|
||||
conn->le_rate_latency != max_latency ||
|
||||
conn->le_cont_num != cont_num ||
|
||||
conn->le_rate_supv_timeout != supv_timeout))
|
||||
hci_le_conn_rate_request(hdev, conn);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE, 0,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static int set_external_config(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
@@ -9587,6 +9723,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ mesh_send_cancel, MGMT_MESH_SEND_CANCEL_SIZE },
|
||||
{ mgmt_hci_cmd_sync, MGMT_HCI_CMD_SYNC_SIZE, HCI_MGMT_VAR_LEN },
|
||||
{ load_conn_subrate, MGMT_LOAD_CONN_SUBRATE_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
};
|
||||
|
||||
void mgmt_index_added(struct hci_dev *hdev)
|
||||
@@ -10735,6 +10873,23 @@ int mgmt_init(void)
|
||||
return hci_mgmt_chan_register(&chan);
|
||||
}
|
||||
|
||||
void mgmt_conn_subrate_notify(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u8 status)
|
||||
{
|
||||
struct mgmt_ev_conn_subrate ev;
|
||||
|
||||
bacpy(&ev.addr.bdaddr, &conn->dst);
|
||||
ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
|
||||
ev.status = mgmt_status(status);
|
||||
ev.interval = cpu_to_le16(conn->le_rate_interval);
|
||||
ev.subrate = cpu_to_le16(conn->le_subrate);
|
||||
ev.latency = cpu_to_le16(conn->le_rate_latency);
|
||||
ev.cont_num = cpu_to_le16(conn->le_cont_num);
|
||||
ev.supv_timeout = cpu_to_le16(conn->le_rate_supv_timeout);
|
||||
|
||||
mgmt_event(MGMT_EV_CONN_SUBRATE, hdev, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
||||
void mgmt_exit(void)
|
||||
{
|
||||
hci_mgmt_chan_unregister(&chan);
|
||||
|
||||
@@ -165,6 +165,11 @@ static bool read_supported_features(struct hci_dev *hdev,
|
||||
if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
|
||||
goto failed;
|
||||
|
||||
if (skb->len < sizeof(*rp) + rp->evt_prefix_len) {
|
||||
bt_dev_err(hdev, "MSFT event prefix length mismatch");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (rp->evt_prefix_len > 0) {
|
||||
msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
|
||||
GFP_KERNEL);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config BT_RFCOMM
|
||||
tristate "RFCOMM protocol support"
|
||||
depends on BT_BREDR
|
||||
depends on BT && BT_BREDR
|
||||
help
|
||||
RFCOMM provides connection oriented stream transport. RFCOMM
|
||||
support is required for Dialup Networking, OBEX and other Bluetooth
|
||||
|
||||
@@ -7,3 +7,5 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm.o
|
||||
|
||||
rfcomm-y := core.o sock.o
|
||||
rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
@@ -1331,7 +1331,10 @@ static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s,
|
||||
return s;
|
||||
}
|
||||
|
||||
void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
/* Must be called with rfcomm_mutex held, so that the session cannot be
|
||||
* unlinked from under us.
|
||||
*/
|
||||
static void __rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
struct sock *sk = d->session->sock->sk;
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
|
||||
@@ -1353,6 +1356,21 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
|
||||
}
|
||||
|
||||
void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
rfcomm_lock();
|
||||
|
||||
/* rfcomm_recv_disc() sets the dlc state to BT_CLOSED before calling
|
||||
* __rfcomm_dlc_close(), so the RFCOMM_DEFER_SETUP handshake there is
|
||||
* skipped and the session can already be unlinked by the time the
|
||||
* deferred accept runs from rfcomm_sock_recvmsg().
|
||||
*/
|
||||
if (d->session)
|
||||
__rfcomm_dlc_accept(d);
|
||||
|
||||
rfcomm_unlock();
|
||||
}
|
||||
|
||||
static void rfcomm_check_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
if (rfcomm_check_security(d)) {
|
||||
@@ -1365,7 +1383,7 @@ static void rfcomm_check_accept(struct rfcomm_dlc *d)
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
__rfcomm_dlc_accept(d);
|
||||
} else {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
@@ -1958,7 +1976,7 @@ static void rfcomm_process_dlcs(struct rfcomm_session *s)
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
__rfcomm_dlc_accept(d);
|
||||
}
|
||||
continue;
|
||||
} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
|
||||
|
||||
@@ -60,6 +60,7 @@ static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
|
||||
__must_hold(&d->lock)
|
||||
{
|
||||
struct sock *sk = d->owner, *parent;
|
||||
|
||||
|
||||
@@ -2327,12 +2327,15 @@ static void smp_send_security_req(struct smp_chan *smp, __u8 auth)
|
||||
|
||||
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_conn *conn;
|
||||
struct l2cap_chan *chan;
|
||||
struct smp_chan *smp;
|
||||
__u8 authreq;
|
||||
int ret;
|
||||
|
||||
/* Caller shall ensure there can be no race with l2cap_conn_del() */
|
||||
conn = context_unsafe(hcon->l2cap_data);
|
||||
|
||||
bt_dev_dbg(hcon->hdev, "conn %p hcon %p level 0x%2.2x", conn, hcon,
|
||||
sec_level);
|
||||
|
||||
@@ -2421,6 +2424,8 @@ int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = hcon->l2cap_data;
|
||||
if (!conn)
|
||||
goto done;
|
||||
|
||||
@@ -180,11 +180,13 @@ enum smp_key_pref {
|
||||
|
||||
/* SMP Commands */
|
||||
int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type);
|
||||
u8 addr_type)
|
||||
__must_hold(&hdev->lock);
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
|
||||
enum smp_key_pref key_pref);
|
||||
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
|
||||
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
|
||||
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey)
|
||||
__must_hold(&conn->hdev->lock);
|
||||
|
||||
bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
|
||||
const bdaddr_t *bdaddr);
|
||||
|
||||
Reference in New Issue
Block a user