bluetooth-next pull request for net-next:

core:
 
  - HCI: Add initial support for PAST
  - hci_core: Introduce HCI_CONN_FLAG_PAST
  - ISO: Add support to bind to trigger PAST
  - HCI: Always use the identity address when initializing a connection
  - ISO: Attempt to resolve broadcast address
  - MGMT: Allow use of Set Device Flags without Add Device
  - ISO: Fix not updating BIS sender source address
  - HCI: Add support for LL Extended Feature Set
 
  driver:
 
  - btusb: Add new VID/PID 2b89/6275 for RTL8761BUV
  - btusb: MT7920: Add VID/PID 0489/e135
  - btusb: MT7922: Add VID/PID 0489/e170
  - btusb: Add new VID/PID 13d3/3533 for RTL8821CE
  - btusb: Add new VID/PID 0x0489/0xE12F for RTL8852BE-VT
  - btusb: Add new VID/PID 0x13d3/0x3618 for RTL8852BE-VT
  - btusb: Add new VID/PID 0x13d3/0x3619 for RTL8852BE-VT
  - btusb: Reclassify Qualcomm WCN6855 debug packets
  - btintel_pcie: Introduce HCI Driver protocol
  - btintel_pcie: Support for S4 (Hibernate)
  - btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling
  - dt-bindings: net: Convert Marvell 8897/8997 bindings to DT schema
  - btbcm: Use kmalloc_array() to prevent overflow
  - btrtl: Add the support for RTL8761CUV
  - hci_h5: avoid sending two SYNC messages
  - hci_h5: implement CRC data integrity
 
 MAINTAINERS:
 
  - Add Bartosz Golaszewski as Qualcomm hci_qca maintainer
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmkuCjwZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKdOvD/9ReyEoUaUZ6aKC7TO66fT0
 eo/sga036O3k9oeeECsEBOOgwOy86gbR7uGol/vV6mocm/Duql/pNqMrgC28Cxhy
 u+aGX7sf3s8Pc/e82Syx6u+QPKa6xz3Hnx1UdCM/HXb0nOkJUm3VHFmshFxmXE9g
 xyMWKtLp1DrXOZNLauR/p8fgAEhGQs8muICuWT/SrEbXZ4+coQoz2h6279IA9FGZ
 2qj/pcLIBFk0qePkiaZ5LJGjF0P0+S9uX9XlhF9yIsLIckH8qAbPsHpD1+RsbkId
 R4WYxIBTVeeUhvWQezodsZJa8HRFQendCeBq8QhOo6fEcprpxrb4/8NS6hipblfy
 rTeyKUoPmPZ/5/nvx9pbRSqqdVOVT/pEOzD5o66bppX7s7Xq3k/WIRqCKpM4znIp
 iZyUlaoX6J5R+SHaOLVzRun6oUzXnIHbIGbWopfQBtMgpDajTcxQJpsTWdFQW4El
 RP+N9xoF+OBa4pKumKCe11pzUJOkBislDIAjNvp8wmevfSfkmo8CbqK5WwfL0rar
 VeBDlkfPYLVNiJ30WKwmLC4ymRvzAFhC0R6LoDPw8OWTZ7VBc7ReCcHDp8GBWbew
 pKe7jMWCCOo1q9zx0KBAwghQ6ZbABnK6N6Fg/Wpb/kfz7YkqnsRWRJkI2BTotEdu
 +bIZ2MwuJ59ROnfPy0tNBw==
 =ndah
 -----END PGP SIGNATURE-----

Merge tag 'for-net-next-2025-12-01' 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 initial support for PAST
 - hci_core: Introduce HCI_CONN_FLAG_PAST
 - ISO: Add support to bind to trigger PAST
 - HCI: Always use the identity address when initializing a connection
 - ISO: Attempt to resolve broadcast address
 - MGMT: Allow use of Set Device Flags without Add Device
 - ISO: Fix not updating BIS sender source address
 - HCI: Add support for LL Extended Feature Set

 driver:

 - btusb: Add new VID/PID 2b89/6275 for RTL8761BUV
 - btusb: MT7920: Add VID/PID 0489/e135
 - btusb: MT7922: Add VID/PID 0489/e170
 - btusb: Add new VID/PID 13d3/3533 for RTL8821CE
 - btusb: Add new VID/PID 0x0489/0xE12F for RTL8852BE-VT
 - btusb: Add new VID/PID 0x13d3/0x3618 for RTL8852BE-VT
 - btusb: Add new VID/PID 0x13d3/0x3619 for RTL8852BE-VT
 - btusb: Reclassify Qualcomm WCN6855 debug packets
 - btintel_pcie: Introduce HCI Driver protocol
 - btintel_pcie: Support for S4 (Hibernate)
 - btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling
 - dt-bindings: net: Convert Marvell 8897/8997 bindings to DT schema
 - btbcm: Use kmalloc_array() to prevent overflow
 - btrtl: Add the support for RTL8761CUV
 - hci_h5: avoid sending two SYNC messages
 - hci_h5: implement CRC data integrity

MAINTAINERS:

 - Add Bartosz Golaszewski as Qualcomm hci_qca maintainer

* tag 'for-net-next-2025-12-01' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (29 commits)
  Bluetooth: btusb: Add new VID/PID 13d3/3533 for RTL8821CE
  Bluetooth: HCI: Add support for LL Extended Feature Set
  drivers/bluetooth: btbcm: Use kmalloc_array() to prevent overflow
  Bluetooth: btintel_pcie: Introduce HCI Driver protocol
  Bluetooth: btusb: add new custom firmwares
  Bluetooth: btusb: Add new VID/PID 0x13d3/0x3619 for RTL8852BE-VT
  Bluetooth: btusb: Add new VID/PID 0x13d3/0x3618 for RTL8852BE-VT
  Bluetooth: btusb: Add new VID/PID 0x0489/0xE12F for RTL8852BE-VT
  Bluetooth: iso: fix socket matching ambiguity between BIS and CIS
  Bluetooth: MAINTAINERS: Add Bartosz Golaszewski as Qualcomm hci_qca maintainer
  Bluetooth: btrtl: Add the support for RTL8761CUV
  Bluetooth: Remove redundant pm_runtime_mark_last_busy() calls
  dt-bindings: net: Convert Marvell 8897/8997 bindings to DT schema
  Bluetooth: btusb: Reclassify Qualcomm WCN6855 debug packets
  Bluetooth: btusb: Add new VID/PID 2b89/6275 for RTL8761BUV
  Bluetooth: btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling
  Bluetooth: btintel_pcie: Support for S4 (Hibernate)
  Bluetooth: btusb: MT7922: Add VID/PID 0489/e170
  Bluetooth: btusb: MT7920: Add VID/PID 0489/e135
  Bluetooth: ISO: Fix not updating BIS sender source address
  ...
====================

Link: https://patch.msgid.link/20251201213818.97249-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-12-01 17:10:52 -08:00
commit 4a18b6cd7c
23 changed files with 1174 additions and 308 deletions

View File

@ -0,0 +1,79 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/bluetooth/marvell,sd8897-bt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Marvell 8897/8997 (sd8897/sd8997) bluetooth devices (SDIO)
maintainers:
- Ariel D'Alessandro <ariel.dalessandro@collabora.com>
allOf:
- $ref: /schemas/net/bluetooth/bluetooth-controller.yaml#
properties:
compatible:
enum:
- marvell,sd8897-bt
- marvell,sd8997-bt
reg:
maxItems: 1
interrupts:
maxItems: 1
marvell,cal-data:
$ref: /schemas/types.yaml#/definitions/uint8-array
description:
Calibration data downloaded to the device during initialization.
maxItems: 28
marvell,wakeup-pin:
$ref: /schemas/types.yaml#/definitions/uint16
description:
Wakeup pin number of the bluetooth chip. Used by firmware to wakeup host
system.
marvell,wakeup-gap-ms:
$ref: /schemas/types.yaml#/definitions/uint16
description:
Wakeup latency of the host platform. Required by the chip sleep feature.
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
mmc {
vmmc-supply = <&wlan_en_reg>;
bus-width = <4>;
cap-power-off-card;
keep-power-in-suspend;
#address-cells = <1>;
#size-cells = <0>;
bluetooth@2 {
compatible = "marvell,sd8897-bt";
reg = <2>;
interrupt-parent = <&pio>;
interrupts = <119 IRQ_TYPE_LEVEL_LOW>;
marvell,cal-data = /bits/ 8 <
0x37 0x01 0x1c 0x00 0xff 0xff 0xff 0xff 0x01 0x7f 0x04 0x02
0x00 0x00 0xba 0xce 0xc0 0xc6 0x2d 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0xf0 0x00>;
marvell,wakeup-pin = /bits/ 16 <0x0d>;
marvell,wakeup-gap-ms = /bits/ 16 <0x64>;
};
};
...

View File

@ -14,7 +14,7 @@ Required properties:
Also, vendors that use btusb may have device additional properties, e.g:
Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt
Documentation/devicetree/bindings/net/bluetooth/marvell,sd8897-bt.yaml
Optional properties:

View File

@ -1,83 +0,0 @@
Marvell 8897/8997 (sd8897/sd8997) bluetooth devices (SDIO or USB based)
------
The 8997 devices supports multiple interfaces. When used on SDIO interfaces,
the btmrvl driver is used and when used on USB interface, the btusb driver is
used.
Required properties:
- compatible : should be one of the following:
* "marvell,sd8897-bt" (for SDIO)
* "marvell,sd8997-bt" (for SDIO)
* "usb1286,204e" (for USB)
Optional properties:
- marvell,cal-data: Calibration data downloaded to the device during
initialization. This is an array of 28 values(u8).
This is only applicable to SDIO devices.
- marvell,wakeup-pin: It represents wakeup pin number of the bluetooth chip.
firmware will use the pin to wakeup host system (u16).
- marvell,wakeup-gap-ms: wakeup gap represents wakeup latency of the host
platform. The value will be configured to firmware. This
is needed to work chip's sleep feature as expected (u16).
- interrupt-names: Used only for USB based devices (See below)
- interrupts : specifies the interrupt pin number to the cpu. For SDIO, the
driver will use the first interrupt specified in the interrupt
array. For USB based devices, the driver will use the interrupt
named "wakeup" from the interrupt-names and interrupt arrays.
The driver will request an irq based on this interrupt number.
During system suspend, the irq will be enabled so that the
bluetooth chip can wakeup host platform under certain
conditions. During system resume, the irq will be disabled
to make sure unnecessary interrupt is not received.
Example:
IRQ pin 119 is used as system wakeup source interrupt.
wakeup pin 13 and gap 100ms are configured so that firmware can wakeup host
using this device side pin and wakeup latency.
Example for SDIO device follows (calibration data is also available in
below example).
&mmc3 {
vmmc-supply = <&wlan_en_reg>;
bus-width = <4>;
cap-power-off-card;
keep-power-in-suspend;
#address-cells = <1>;
#size-cells = <0>;
btmrvl: bluetooth@2 {
compatible = "marvell,sd8897-bt";
reg = <2>;
interrupt-parent = <&pio>;
interrupts = <119 IRQ_TYPE_LEVEL_LOW>;
marvell,cal-data = /bits/ 8 <
0x37 0x01 0x1c 0x00 0xff 0xff 0xff 0xff 0x01 0x7f 0x04 0x02
0x00 0x00 0xba 0xce 0xc0 0xc6 0x2d 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0xf0 0x00>;
marvell,wakeup-pin = /bits/ 16 <0x0d>;
marvell,wakeup-gap-ms = /bits/ 16 <0x64>;
};
};
Example for USB device:
&usb_host1_ohci {
#address-cells = <1>;
#size-cells = <0>;
mvl_bt1: bt@1 {
compatible = "usb1286,204e";
reg = <1>;
interrupt-parent = <&gpio0>;
interrupt-names = "wakeup";
interrupts = <119 IRQ_TYPE_LEVEL_LOW>;
marvell,wakeup-pin = /bits/ 16 <0x0d>;
marvell,wakeup-gap-ms = /bits/ 16 <0x64>;
};
};

View File

@ -21071,6 +21071,7 @@ F: Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
F: drivers/net/wwan/qcom_bam_dmux.c
QUALCOMM BLUETOOTH DRIVER
M: Bartosz Golaszewski <brgl@bgdev.pl>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/bluetooth/btqca.[ch]

View File

@ -188,6 +188,7 @@ config BT_HCIUART_3WIRE
bool "Three-wire UART (H5) protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
select CRC_CCITT
help
The HCI Three-wire UART Transport Layer makes it possible to
user the Bluetooth HCI over a serial port interface. The HCI

View File

@ -642,7 +642,9 @@ int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud
snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid);
}
fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL);
fw_name = kmalloc_array(BCM_FW_NAME_COUNT_MAX,
sizeof(*fw_name),
GFP_KERNEL);
if (!fw_name)
return -ENOMEM;

View File

@ -19,6 +19,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_drv.h>
#include "btintel.h"
#include "btintel_pcie.h"
@ -825,6 +826,11 @@ static inline bool btintel_pcie_in_d0(struct btintel_pcie_data *data)
return !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY);
}
static inline bool btintel_pcie_in_device_halt(struct btintel_pcie_data *data)
{
return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED;
}
static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data,
u32 dxstate)
{
@ -2355,6 +2361,63 @@ static bool btintel_pcie_wakeup(struct hci_dev *hdev)
return device_may_wakeup(&data->pdev->dev);
}
static const struct {
u16 opcode;
const char *desc;
} btintel_pcie_hci_drv_supported_commands[] = {
/* Common commands */
{ HCI_DRV_OP_READ_INFO, "Read Info" },
};
static int btintel_pcie_hci_drv_read_info(struct hci_dev *hdev, void *data,
u16 data_len)
{
struct hci_drv_rp_read_info *rp;
size_t rp_size;
int err, i;
u16 opcode, num_supported_commands =
ARRAY_SIZE(btintel_pcie_hci_drv_supported_commands);
rp_size = sizeof(*rp) + num_supported_commands * 2;
rp = kmalloc(rp_size, GFP_KERNEL);
if (!rp)
return -ENOMEM;
strscpy_pad(rp->driver_name, KBUILD_MODNAME);
rp->num_supported_commands = cpu_to_le16(num_supported_commands);
for (i = 0; i < num_supported_commands; i++) {
opcode = btintel_pcie_hci_drv_supported_commands[i].opcode;
bt_dev_dbg(hdev,
"Supported HCI Drv command (0x%02x|0x%04x): %s",
hci_opcode_ogf(opcode),
hci_opcode_ocf(opcode),
btintel_pcie_hci_drv_supported_commands[i].desc);
rp->supported_commands[i] = cpu_to_le16(opcode);
}
err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
HCI_DRV_STATUS_SUCCESS,
rp, rp_size);
kfree(rp);
return err;
}
static const struct hci_drv_handler btintel_pcie_hci_drv_common_handlers[] = {
{ btintel_pcie_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE },
};
static const struct hci_drv_handler btintel_pcie_hci_drv_specific_handlers[] = {};
static struct hci_drv btintel_pcie_hci_drv = {
.common_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_common_handlers),
.common_handlers = btintel_pcie_hci_drv_common_handlers,
.specific_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_specific_handlers),
.specific_handlers = btintel_pcie_hci_drv_specific_handlers,
};
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
{
int err;
@ -2381,6 +2444,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
hdev->set_bdaddr = btintel_set_bdaddr;
hdev->reset = btintel_pcie_reset;
hdev->wakeup = btintel_pcie_wakeup;
hdev->hci_drv = &btintel_pcie_hci_drv;
err = hci_register_dev(hdev);
if (err < 0) {
@ -2519,6 +2583,48 @@ static void btintel_pcie_coredump(struct device *dev)
}
#endif
static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
{
int retry = 0, status;
u32 dx_intr_timeout_ms = 200;
do {
data->gp0_received = false;
btintel_pcie_wr_sleep_cntrl(data, dxstate);
status = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
msecs_to_jiffies(dx_intr_timeout_ms));
if (status)
return 0;
bt_dev_warn(data->hdev,
"Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
dx_intr_timeout_ms, dxstate, retry);
/* clear gp0 cause */
btintel_pcie_clr_reg_bits(data,
BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
/* A hardware bug may cause the alive interrupt to be missed.
* Check if the controller reached the expected state and retry
* the operation only if it hasn't.
*/
if (dxstate == BTINTEL_PCIE_STATE_D0) {
if (btintel_pcie_in_d0(data))
return 0;
} else {
if (btintel_pcie_in_d3(data))
return 0;
}
} while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES);
return -EBUSY;
}
static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
{
struct pci_dev *pdev = to_pci_dev(dev);
@ -2532,26 +2638,20 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
dxstate = (mesg.event == PM_EVENT_SUSPEND ?
BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
data->gp0_received = false;
data->pm_sx_event = mesg.event;
start = ktime_get();
/* Refer: 6.4.11.7 -> Platform power management */
btintel_pcie_wr_sleep_cntrl(data, dxstate);
err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
if (err == 0) {
bt_dev_err(data->hdev,
"Timeout (%u ms) on alive interrupt for D3 entry",
BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
return -EBUSY;
}
err = btintel_pcie_set_dxstate(data, dxstate);
if (err)
return err;
bt_dev_dbg(data->hdev,
"device entered into d3 state from d0 in %lld us",
ktime_to_us(ktime_get() - start));
return 0;
return err;
}
static int btintel_pcie_suspend(struct device *dev)
@ -2581,21 +2681,50 @@ static int btintel_pcie_resume(struct device *dev)
start = ktime_get();
/* Refer: 6.4.11.7 -> Platform power management */
btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
if (err == 0) {
bt_dev_err(data->hdev,
"Timeout (%u ms) on alive interrupt for D0 entry",
BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
return -EBUSY;
/* When the system enters S4 (hibernate) mode, bluetooth device loses
* power, which results in the erasure of its loaded firmware.
* Consequently, function level reset (flr) is required on system
* resume to bring the controller back into an operational state by
* initiating a new firmware download.
*/
if (data->pm_sx_event == PM_EVENT_FREEZE ||
data->pm_sx_event == PM_EVENT_HIBERNATE) {
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
btintel_pcie_reset(data->hdev);
return 0;
}
bt_dev_dbg(data->hdev,
"device entered into d0 state from d3 in %lld us",
ktime_to_us(ktime_get() - start));
return 0;
/* Refer: 6.4.11.7 -> Platform power management */
err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0);
if (err == 0) {
bt_dev_dbg(data->hdev,
"device entered into d0 state from d3 in %lld us",
ktime_to_us(ktime_get() - start));
return err;
}
/* Trigger function level reset if the controller is in error
* state during resume() to bring back the controller to
* operational mode
*/
data->boot_stage_cache = btintel_pcie_rd_reg32(data,
BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
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->workqueue, &data->rx_work);
}
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
btintel_pcie_reset(data->hdev);
}
return err;
}
static const struct dev_pm_ops btintel_pcie_pm_ops = {

View File

@ -158,6 +158,8 @@ enum msix_mbox_int_causes {
/* Default interrupt timeout in msec */
#define BTINTEL_DEFAULT_INTR_TIMEOUT_MS 3000
#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3
/* The number of descriptors in TX queues */
#define BTINTEL_PCIE_TX_DESCS_COUNT 32
@ -464,6 +466,7 @@ struct btintel_pcie_dump_header {
* @txq: TX Queue struct
* @rxq: RX Queue struct
* @alive_intr_ctxt: Alive interrupt context
* @pm_sx_event: PM event on which system got suspended
*/
struct btintel_pcie_data {
struct pci_dev *pdev;
@ -513,6 +516,7 @@ struct btintel_pcie_data {
u32 alive_intr_ctxt;
struct btintel_pcie_dbgc dbgc;
struct btintel_pcie_dump_header dmp_hdr;
u8 pm_sx_event;
};
static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,

View File

@ -615,7 +615,6 @@ static void btmtksdio_txrx_work(struct work_struct *work)
sdio_release_host(bdev->func);
pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
}

View File

@ -72,6 +72,7 @@ enum btrtl_chip_id {
CHIP_ID_8851B = 36,
CHIP_ID_8922A = 44,
CHIP_ID_8852BT = 47,
CHIP_ID_8761C = 51,
};
struct id_table {
@ -230,6 +231,14 @@ static const struct id_table ic_id_table[] = {
.cfg_name = "rtl_bt/rtl8761bu_config",
.hw_info = "rtl8761bu" },
/* 8761CU */
{ IC_INFO(RTL_ROM_LMP_8761A, 0x0e, 0, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8761cu_fw",
.cfg_name = "rtl_bt/rtl8761cu_config",
.hw_info = "rtl8761cu" },
/* 8822C with UART interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
.config_needed = true,
@ -344,7 +353,8 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
(ic_id_table[i].hci_rev != hci_rev))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
(ic_id_table[i].hci_ver != hci_ver))
(ic_id_table[i].hci_ver != hci_ver) &&
(ic_id_table[i].hci_ver != 0))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
(ic_id_table[i].hci_bus != hci_bus))
@ -662,6 +672,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
{ RTL_ROM_LMP_8851B, 36 }, /* 8851B */
{ RTL_ROM_LMP_8922A, 44 }, /* 8922A */
{ RTL_ROM_LMP_8852A, 47 }, /* 8852BT */
{ RTL_ROM_LMP_8761A, 51 }, /* 8761C */
};
if (btrtl_dev->fw_len <= 8)
@ -1305,6 +1316,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
case CHIP_ID_8851B:
case CHIP_ID_8922A:
case CHIP_ID_8852BT:
case CHIP_ID_8761C:
hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
/* RTL8852C needs to transmit mSBC data continuously without
@ -1524,6 +1536,8 @@ MODULE_FIRMWARE("rtl_bt/rtl8761b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761b_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761bu_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761bu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761cu_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761cu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821c_fw.bin");

View File

@ -504,6 +504,8 @@ static const struct usb_device_id quirks_table[] = {
/* Realtek 8821CE Bluetooth devices */
{ USB_DEVICE(0x13d3, 0x3529), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3533), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Realtek 8822CE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK |
@ -585,6 +587,12 @@ static const struct usb_device_id quirks_table[] = {
/* Realtek 8852BT/8852BE-VT Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x8520), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe12f), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3618), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3619), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Realtek 8922AE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK |
@ -621,6 +629,8 @@ static const struct usb_device_id quirks_table[] = {
/* Additional MediaTek MT7920 Bluetooth devices */
{ USB_DEVICE(0x0489, 0xe134), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe135), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3620), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3621), .driver_info = BTUSB_MEDIATEK |
@ -685,6 +695,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe153), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe170), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x3804), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK |
@ -781,6 +793,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x2b89, 0x8761), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
@ -1131,6 +1145,24 @@ static void btusb_qca_reset(struct hci_dev *hdev)
btusb_reset(hdev);
}
static u8 btusb_classify_qca_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
{
/* Some Qualcomm controllers, e.g., QCNFA765 with WCN6855 chip, send debug
* packets as ACL frames with connection handle 0x2EDC. These are not real
* ACL packets and should be reclassified as HCI_DIAG_PKT to prevent
* "ACL packet for unknown connection handle 3804" errors.
*/
if (skb->len >= 2) {
u16 handle = get_unaligned_le16(skb->data);
if (handle == 0x2EDC)
return HCI_DIAG_PKT;
}
/* Use default packet type for other packets */
return hci_skb_pkt_type(skb);
}
static inline void btusb_free_frags(struct btusb_data *data)
{
unsigned long flags;
@ -2808,6 +2840,19 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
/* Toggle the hard reset line. The MediaTek device is going to
* yank itself off the USB and then replug. The cleanup is handled
* correctly on the way out (standard USB disconnect), and the new
* device is detected cleanly and bound to the driver again like
* it should be.
*/
if (data->reset_gpio) {
gpiod_set_value_cansleep(data->reset_gpio, 1);
msleep(200);
gpiod_set_value_cansleep(data->reset_gpio, 0);
return 0;
}
err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
usb_queue_reset_device(data->intf);
@ -3261,6 +3306,7 @@ static const struct qca_device_info qca_devices_table[] = {
static const struct qca_custom_firmware qca_custom_btfws[] = {
{ 0x00130201, 0x030A, "QCA2066" },
{ 0x00130201, 0x030B, "QCA2066" },
{ },
};
@ -4226,6 +4272,7 @@ static int btusb_probe(struct usb_interface *intf,
data->recv_acl = btusb_recv_acl_qca;
hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL);
data->setup_on_usb = btusb_setup_qca;
hdev->classify_pkt_type = btusb_classify_qca_pkt_type;
hdev->shutdown = btusb_shutdown_qca;
hdev->set_bdaddr = btusb_set_bdaddr_wcn6855;
hdev->reset = btusb_qca_reset;

View File

@ -326,7 +326,6 @@ static irqreturn_t bcm_host_wake(int irq, void *data)
bt_dev_dbg(bdev, "Host wake IRQ");
pm_runtime_get(bdev->dev);
pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
return IRQ_HANDLED;
@ -710,7 +709,6 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
mutex_lock(&bcm_device_lock);
if (bcm->dev && bcm_device_exists(bcm->dev)) {
pm_runtime_get(bcm->dev->dev);
pm_runtime_mark_last_busy(bcm->dev->dev);
pm_runtime_put_autosuspend(bcm->dev->dev);
}
mutex_unlock(&bcm_device_lock);
@ -748,10 +746,8 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&bcm->txq);
if (bdev) {
pm_runtime_mark_last_busy(bdev->dev);
if (bdev)
pm_runtime_put_autosuspend(bdev->dev);
}
mutex_unlock(&bcm_device_lock);

View File

@ -7,6 +7,8 @@
*/
#include <linux/acpi.h>
#include <linux/bitrev.h>
#include <linux/crc-ccitt.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
@ -58,6 +60,7 @@ enum {
H5_TX_ACK_REQ, /* Pending ack to send */
H5_WAKEUP_DISABLE, /* Device cannot wake host */
H5_HW_FLOW_CONTROL, /* Use HW flow control */
H5_CRC, /* Use CRC */
};
struct h5 {
@ -141,8 +144,8 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
static u8 h5_cfg_field(struct h5 *h5)
{
/* Sliding window size (first 3 bits) */
return h5->tx_win & 0x07;
/* Sliding window size (first 3 bits) and CRC request (fifth bit). */
return (h5->tx_win & 0x07) | 0x10;
}
static void h5_timed_event(struct timer_list *t)
@ -213,7 +216,6 @@ static void h5_peer_reset(struct hci_uart *hu)
static int h5_open(struct hci_uart *hu)
{
struct h5 *h5;
const unsigned char sync[] = { 0x01, 0x7e };
BT_DBG("hu %p", hu);
@ -243,9 +245,11 @@ static int h5_open(struct hci_uart *hu)
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
/* Send initial sync request */
h5_link_control(hu, sync, sizeof(sync));
mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
/*
* Wait one jiffy because the UART layer won't set HCI_UART_PROTO_READY,
* which allows us to send link packets, until this function returns.
*/
mod_timer(&h5->timer, jiffies + 1);
return 0;
}
@ -360,8 +364,10 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
h5_link_control(hu, conf_rsp, 2);
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_rsp, 2) == 0) {
if (H5_HDR_LEN(hdr) > 2)
if (H5_HDR_LEN(hdr) > 2) {
h5->tx_win = (data[2] & 0x07);
assign_bit(H5_CRC, &h5->flags, data[2] & 0x10);
}
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
h5->state = H5_ACTIVE;
hci_uart_init_ready(hu);
@ -425,7 +431,24 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
static int h5_rx_crc(struct hci_uart *hu, unsigned char c)
{
h5_complete_rx_pkt(hu);
struct h5 *h5 = hu->priv;
const unsigned char *hdr = h5->rx_skb->data;
u16 crc;
__be16 crc_be;
crc = crc_ccitt(0xffff, hdr, 4 + H5_HDR_LEN(hdr));
crc = bitrev16(crc);
crc_be = cpu_to_be16(crc);
if (memcmp(&crc_be, hdr + 4 + H5_HDR_LEN(hdr), 2) != 0) {
bt_dev_err(hu->hdev, "Received packet with invalid CRC");
h5_reset_rx(h5);
} else {
/* Remove CRC bytes */
skb_trim(h5->rx_skb, 4 + H5_HDR_LEN(hdr));
h5_complete_rx_pkt(hu);
}
return 0;
}
@ -556,6 +579,7 @@ static void h5_reset_rx(struct h5 *h5)
h5->rx_func = h5_rx_delimiter;
h5->rx_pending = 0;
clear_bit(H5_RX_ESC, &h5->flags);
clear_bit(H5_CRC, &h5->flags);
}
static int h5_recv(struct hci_uart *hu, const void *data, int count)
@ -592,7 +616,6 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count)
if (hu->serdev) {
pm_runtime_get(&hu->serdev->dev);
pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
@ -634,7 +657,6 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
if (hu->serdev) {
pm_runtime_get_sync(&hu->serdev->dev);
pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
@ -686,6 +708,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
struct h5 *h5 = hu->priv;
struct sk_buff *nskb;
u8 hdr[4];
u16 crc;
int i;
if (!valid_packet_type(pkt_type)) {
@ -713,6 +736,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
/* Reliable packet? */
if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) {
hdr[0] |= 1 << 7;
hdr[0] |= (test_bit(H5_CRC, &h5->flags) && 1) << 6;
hdr[0] |= h5->tx_seq;
h5->tx_seq = (h5->tx_seq + 1) % 8;
}
@ -732,6 +756,15 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
for (i = 0; i < len; i++)
h5_slip_one_byte(nskb, data[i]);
if (H5_HDR_CRC(hdr)) {
crc = crc_ccitt(0xffff, hdr, 4);
crc = crc_ccitt(crc, data, len);
crc = bitrev16(crc);
h5_slip_one_byte(nskb, (crc >> 8) & 0xff);
h5_slip_one_byte(nskb, crc & 0xff);
}
h5_slip_delim(nskb);
return nskb;

View File

@ -280,7 +280,6 @@ static irqreturn_t intel_irq(int irq, void *dev_id)
/* Host/Controller are now LPM resumed, trigger a new delayed suspend */
pm_runtime_get(&idev->pdev->dev);
pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
return IRQ_HANDLED;
@ -371,7 +370,6 @@ static void intel_busy_work(struct work_struct *work)
list_for_each_entry(idev, &intel_device_list, list) {
if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {
pm_runtime_get(&idev->pdev->dev);
pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
break;
}
@ -1003,7 +1001,6 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
list_for_each_entry(idev, &intel_device_list, list) {
if (hu->tty->dev->parent == idev->pdev->dev.parent) {
pm_runtime_get_sync(&idev->pdev->dev);
pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
break;
}

View File

@ -647,10 +647,13 @@ enum {
#define HCI_LE_EXT_ADV 0x10
#define HCI_LE_PERIODIC_ADV 0x20
#define HCI_LE_CHAN_SEL_ALG2 0x40
#define HCI_LE_PAST_SENDER 0x01
#define HCI_LE_PAST_RECEIVER 0x02
#define HCI_LE_CIS_CENTRAL 0x10
#define HCI_LE_CIS_PERIPHERAL 0x20
#define HCI_LE_ISO_BROADCASTER 0x40
#define HCI_LE_ISO_SYNC_RECEIVER 0x80
#define HCI_LE_LL_EXT_FEATURE 0x80
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
@ -2068,6 +2071,44 @@ struct hci_cp_le_set_privacy_mode {
__u8 mode;
} __packed;
#define HCI_OP_LE_PAST 0x205a
struct hci_cp_le_past {
__le16 handle;
__le16 service_data;
__le16 sync_handle;
} __packed;
struct hci_rp_le_past {
__u8 status;
__le16 handle;
} __packed;
#define HCI_OP_LE_PAST_SET_INFO 0x205b
struct hci_cp_le_past_set_info {
__le16 handle;
__le16 service_data;
__u8 adv_handle;
} __packed;
struct hci_rp_le_past_set_info {
__u8 status;
__le16 handle;
} __packed;
#define HCI_OP_LE_PAST_PARAMS 0x205c
struct hci_cp_le_past_params {
__le16 handle;
__u8 mode;
__le16 skip;
__le16 sync_timeout;
__u8 cte_type;
} __packed;
struct hci_rp_le_past_params {
__u8 status;
__le16 handle;
} __packed;
#define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060
struct hci_rp_le_read_buffer_size_v2 {
__u8 status;
@ -2215,6 +2256,19 @@ struct hci_cp_le_set_host_feature {
__u8 bit_value;
} __packed;
#define HCI_OP_LE_READ_ALL_LOCAL_FEATURES 0x2087
struct hci_rp_le_read_all_local_features {
__u8 status;
__u8 page;
__u8 features[248];
} __packed;
#define HCI_OP_LE_READ_ALL_REMOTE_FEATURES 0x2088
struct hci_cp_le_read_all_remote_features {
__le16 handle;
__u8 pages;
} __packed;
/* ---- HCI Events ---- */
struct hci_ev_status {
__u8 status;
@ -2800,6 +2854,20 @@ struct hci_evt_le_ext_adv_set_term {
__u8 num_evts;
} __packed;
#define HCI_EV_LE_PAST_RECEIVED 0x18
struct hci_ev_le_past_received {
__u8 status;
__le16 handle;
__le16 service_data;
__le16 sync_handle;
__u8 sid;
__u8 bdaddr_type;
bdaddr_t bdaddr;
__u8 phy;
__le16 interval;
__u8 clock_accuracy;
} __packed;
#define HCI_EVT_LE_CIS_ESTABLISHED 0x19
struct hci_evt_le_cis_established {
__u8 status;
@ -2883,6 +2951,15 @@ struct hci_evt_le_big_info_adv_report {
__u8 encryption;
} __packed;
#define HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE 0x2b
struct hci_evt_le_read_all_remote_features_complete {
__u8 status;
__le16 handle;
__u8 max_pages;
__u8 valid_pages;
__u8 features[248];
} __packed;
#define HCI_EV_VENDOR 0xff
/* Internal events generated by Bluetooth stack */

View File

@ -166,6 +166,7 @@ enum hci_conn_flags {
HCI_CONN_FLAG_REMOTE_WAKEUP = BIT(0),
HCI_CONN_FLAG_DEVICE_PRIVACY = BIT(1),
HCI_CONN_FLAG_ADDRESS_RESOLUTION = BIT(2),
HCI_CONN_FLAG_PAST = BIT(3),
};
typedef u8 hci_conn_flags_t;
@ -377,7 +378,7 @@ struct hci_dev {
__u8 minor_class;
__u8 max_page;
__u8 features[HCI_MAX_PAGES][8];
__u8 le_features[8];
__u8 le_features[248];
__u8 le_accept_list_size;
__u8 le_resolv_list_size;
__u8 le_num_of_adv_sets;
@ -701,6 +702,7 @@ struct hci_conn {
__u8 attempt;
__u8 dev_class[3];
__u8 features[HCI_MAX_PAGES][8];
__u8 le_features[248];
__u16 pkt_type;
__u16 link_policy;
__u8 key_type;
@ -1570,9 +1572,9 @@ int hci_le_create_cis_pending(struct hci_dev *hdev);
int hci_conn_check_create_cis(struct hci_conn *conn);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role, u16 handle);
u8 dst_type, u8 role, u16 handle);
struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
bdaddr_t *dst, u8 role);
bdaddr_t *dst, u8 dst_type, u8 role);
void hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev);
@ -1601,6 +1603,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
struct bt_iso_qos *qos,
__u8 base_len, __u8 *base, u16 timeout);
int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type);
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos,
u16 timeout);
@ -2053,6 +2056,20 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define sync_recv_capable(dev) \
((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER)
#define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev))
#define past_sender_capable(dev) \
((dev)->le_features[3] & HCI_LE_PAST_SENDER)
#define past_receiver_capable(dev) \
((dev)->le_features[3] & HCI_LE_PAST_RECEIVER)
#define past_capable(dev) \
(past_sender_capable(dev) || past_receiver_capable(dev))
#define past_sender_enabled(dev) \
(le_enabled(dev) && past_sender_capable(dev))
#define past_receiver_enabled(dev) \
(le_enabled(dev) && past_receiver_capable(dev))
#define past_enabled(dev) \
(past_sender_enabled(dev) || past_receiver_enabled(dev))
#define ll_ext_feature_capable(dev) \
((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE)
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))

View File

@ -188,3 +188,6 @@ int hci_le_conn_update_sync(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);
int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
int hci_le_read_remote_features(struct hci_conn *conn);

View File

@ -119,6 +119,8 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_ISO_BROADCASTER BIT(20)
#define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21)
#define MGMT_SETTING_LL_PRIVACY BIT(22)
#define MGMT_SETTING_PAST_SENDER BIT(23)
#define MGMT_SETTING_PAST_RECEIVER BIT(24)
#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0

View File

@ -922,10 +922,12 @@ static int hci_conn_hash_alloc_unset(struct hci_dev *hdev)
U16_MAX, GFP_ATOMIC);
}
static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
bdaddr_t *dst, u8 dst_type,
u8 role, u16 handle)
{
struct hci_conn *conn;
struct smp_irk *irk = NULL;
switch (type) {
case ACL_LINK:
@ -937,12 +939,14 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
case PA_LINK:
if (!hdev->iso_mtu)
return ERR_PTR(-ECONNREFUSED);
irk = hci_get_irk(hdev, dst, dst_type);
break;
case LE_LINK:
if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
irk = hci_get_irk(hdev, dst, dst_type);
break;
case SCO_LINK:
case ESCO_LINK:
@ -960,7 +964,15 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
if (!conn)
return ERR_PTR(-ENOMEM);
bacpy(&conn->dst, dst);
/* If and IRK exists use its identity address */
if (!irk) {
bacpy(&conn->dst, dst);
conn->dst_type = dst_type;
} else {
bacpy(&conn->dst, &irk->bdaddr);
conn->dst_type = irk->addr_type;
}
bacpy(&conn->src, &hdev->bdaddr);
conn->handle = handle;
conn->hdev = hdev;
@ -1059,7 +1071,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
}
struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
bdaddr_t *dst, u8 role)
bdaddr_t *dst, u8 dst_type, u8 role)
{
int handle;
@ -1069,16 +1081,16 @@ struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
if (unlikely(handle < 0))
return ERR_PTR(-ECONNREFUSED);
return __hci_conn_add(hdev, type, dst, role, handle);
return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role, u16 handle)
u8 dst_type, u8 role, u16 handle)
{
if (handle > HCI_CONN_HANDLE_MAX)
return ERR_PTR(-EINVAL);
return __hci_conn_add(hdev, type, dst, role, handle);
return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
}
static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
@ -1410,14 +1422,13 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
if (conn) {
bacpy(&conn->dst, dst);
} else {
conn = hci_conn_add_unset(hdev, LE_LINK, dst, role);
conn = hci_conn_add_unset(hdev, LE_LINK, dst, dst_type, role);
if (IS_ERR(conn))
return conn;
hci_conn_hold(conn);
conn->pending_sec_level = sec_level;
}
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->conn_timeout = conn_timeout;
conn->le_adv_phy = phy;
@ -1587,7 +1598,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
memcmp(conn->le_per_adv_data, base, base_len)))
return ERR_PTR(-EADDRINUSE);
conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_MASTER);
conn = hci_conn_add_unset(hdev, BIS_LINK, dst, 0, HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
@ -1633,7 +1644,8 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
BT_DBG("requesting refresh of dst_addr");
conn = hci_conn_add_unset(hdev, LE_LINK, dst, HCI_ROLE_MASTER);
conn = hci_conn_add_unset(hdev, LE_LINK, dst, dst_type,
HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
@ -1644,7 +1656,6 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
@ -1681,7 +1692,8 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
acl = hci_conn_add_unset(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
acl = hci_conn_add_unset(hdev, ACL_LINK, dst, 0,
HCI_ROLE_MASTER);
if (IS_ERR(acl))
return acl;
}
@ -1750,7 +1762,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
if (!sco) {
sco = hci_conn_add_unset(hdev, type, dst, HCI_ROLE_MASTER);
sco = hci_conn_add_unset(hdev, type, dst, 0, HCI_ROLE_MASTER);
if (IS_ERR(sco)) {
hci_conn_drop(acl);
return sco;
@ -1942,7 +1954,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig,
qos->ucast.cis);
if (!cis) {
cis = hci_conn_add_unset(hdev, CIS_LINK, dst,
cis = hci_conn_add_unset(hdev, CIS_LINK, dst, dst_type,
HCI_ROLE_MASTER);
if (IS_ERR(cis))
return cis;
@ -2133,12 +2145,11 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
bt_dev_dbg(hdev, "dst %pMR type %d sid %d", dst, dst_type, sid);
conn = hci_conn_add_unset(hdev, PA_LINK, dst, HCI_ROLE_SLAVE);
conn = hci_conn_add_unset(hdev, PA_LINK, dst, dst_type, HCI_ROLE_SLAVE);
if (IS_ERR(conn))
return conn;
conn->iso_qos = *qos;
conn->dst_type = dst_type;
conn->sid = sid;
conn->state = BT_LISTEN;
conn->conn_timeout = msecs_to_jiffies(qos->bcast.sync_timeout * 10);
@ -2245,6 +2256,18 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
return conn;
}
int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type)
{
struct hci_conn *le;
/* Lookup existing LE connection to rebind to */
le = hci_conn_hash_lookup_le(conn->hdev, dst, dst_type);
if (!le)
return -EINVAL;
return hci_past_sync(conn, le);
}
static void bis_mark_per_adv(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;

View File

@ -2267,7 +2267,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
} else {
if (!conn) {
conn = hci_conn_add_unset(hdev, ACL_LINK, &cp->bdaddr,
HCI_ROLE_MASTER);
0, HCI_ROLE_MASTER);
if (IS_ERR(conn))
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
}
@ -2886,12 +2886,8 @@ static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
if (conn->state == BT_CONFIG) {
hci_connect_cfm(conn, status);
hci_conn_drop(conn);
}
}
if (conn && conn->state == BT_CONFIG)
hci_connect_cfm(conn, status);
hci_dev_unlock(hdev);
}
@ -3123,7 +3119,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
&ev->bdaddr,
BDADDR_BREDR)) {
conn = hci_conn_add_unset(hdev, ev->link_type,
&ev->bdaddr, HCI_ROLE_SLAVE);
&ev->bdaddr, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(conn)) {
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
goto unlock;
@ -3299,7 +3296,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type,
&ev->bdaddr);
if (!conn) {
conn = hci_conn_add_unset(hdev, ev->link_type, &ev->bdaddr,
conn = hci_conn_add_unset(hdev, ev->link_type, &ev->bdaddr, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(conn)) {
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
@ -3914,11 +3911,49 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
return rp->status;
}
static u8 hci_cc_le_read_all_local_features(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_rp_le_read_all_local_features *rp = data;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
if (rp->status)
return rp->status;
memcpy(hdev->le_features, rp->features, 248);
return rp->status;
}
static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status)
{
bt_dev_dbg(hdev, "status 0x%2.2x", status);
}
static void hci_cs_le_read_all_remote_features(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_read_remote_features *cp;
struct hci_conn *conn;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
if (!status)
return;
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES);
if (!cp)
return;
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn && conn->state == BT_CONFIG)
hci_connect_cfm(conn, status);
hci_dev_unlock(hdev);
}
static u8 hci_cc_set_per_adv_param(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@ -4170,6 +4205,9 @@ static const struct hci_cc {
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_EVENT_SIZE),
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
sizeof(struct hci_rp_le_setup_iso_path)),
HCI_CC(HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
hci_cc_le_read_all_local_features,
sizeof(struct hci_rp_le_read_all_local_features)),
};
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
@ -4324,6 +4362,8 @@ static const struct hci_cs {
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
HCI_CS(HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
hci_cs_le_read_all_remote_features),
};
static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
@ -5644,6 +5684,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
struct hci_conn *conn;
struct smp_irk *irk;
u8 addr_type;
int err;
hci_dev_lock(hdev);
@ -5670,14 +5711,13 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
if (status)
goto unlock;
conn = hci_conn_add_unset(hdev, LE_LINK, bdaddr, role);
conn = hci_conn_add_unset(hdev, LE_LINK, bdaddr, bdaddr_type,
role);
if (IS_ERR(conn)) {
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
goto unlock;
}
conn->dst_type = bdaddr_type;
/* If we didn't have a hci_conn object previously
* but we're in central role this must be something
* initiated using an accept list. Since accept list based
@ -5775,26 +5815,8 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
/* The remote features procedure is defined for central
* role only. So only in case of an initiated connection
* request the remote features.
*
* If the local controller supports peripheral-initiated features
* exchange, then requesting the remote features in peripheral
* role is possible. Otherwise just transition into the
* connected state without requesting the remote features.
*/
if (conn->out ||
(hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
struct hci_cp_le_read_remote_features cp;
cp.handle = __cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
sizeof(cp), &cp);
hci_conn_hold(conn);
} else {
err = hci_le_read_remote_features(conn);
if (err) {
conn->state = BT_CONNECTED;
hci_connect_cfm(conn, status);
}
@ -5936,6 +5958,71 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
{
struct hci_cp_le_pa_term_sync cp;
memset(&cp, 0, sizeof(cp));
cp.handle = handle;
return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
}
static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_ev_le_past_received *ev = data;
int mask = hdev->link_mode;
__u8 flags = 0;
struct hci_conn *pa_sync, *conn;
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
hci_dev_lock(hdev);
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
conn = hci_conn_hash_lookup_create_pa_sync(hdev);
if (!conn) {
bt_dev_err(hdev,
"Unable to find connection for dst %pMR sid 0x%2.2x",
&ev->bdaddr, ev->sid);
goto unlock;
}
conn->sync_handle = le16_to_cpu(ev->sync_handle);
conn->sid = HCI_SID_INVALID;
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, PA_LINK,
&flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_pa_term_sync(hdev, ev->sync_handle);
goto unlock;
}
if (!(flags & HCI_PROTO_DEFER))
goto unlock;
/* Add connection to indicate PA sync event */
pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
goto unlock;
pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
if (ev->status) {
set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
/* Notify iso layer */
hci_connect_cfm(pa_sync, ev->status);
}
unlock:
hci_dev_unlock(hdev);
}
static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@ -6412,16 +6499,6 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
{
struct hci_cp_le_pa_term_sync cp;
memset(&cp, 0, sizeof(cp));
cp.handle = handle;
return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
}
static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@ -6460,7 +6537,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
goto unlock;
/* Add connection to indicate PA sync event */
pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
@ -6553,7 +6630,6 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
conn->state = BT_CONNECTED;
hci_connect_cfm(conn, status);
hci_conn_drop(conn);
}
}
@ -6901,7 +6977,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
if (!cis) {
cis = hci_conn_add(hdev, CIS_LINK, &acl->dst,
cis = hci_conn_add(hdev, CIS_LINK, &acl->dst, acl->dst_type,
HCI_ROLE_SLAVE, cis_handle);
if (IS_ERR(cis)) {
hci_le_reject_cis(hdev, ev->cis_handle);
@ -7018,7 +7094,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "ignore too large handle %u", handle);
continue;
}
bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY,
bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE, handle);
if (IS_ERR(bis))
continue;
@ -7131,6 +7207,50 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
void *data, struct sk_buff *skb)
{
struct hci_evt_le_read_all_remote_features_complete *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)
goto unlock;
if (!ev->status)
memcpy(conn->le_features, ev->features, 248);
if (conn->state == BT_CONFIG) {
__u8 status;
/* If the local controller supports peripheral-initiated
* features exchange, but the remote controller does
* not, then it is possible that the error code 0x1a
* for unsupported remote feature gets returned.
*
* In this specific case, allow the connection to
* transition into connected state and mark it as
* successful.
*/
if (!conn->out &&
ev->status == HCI_ERROR_UNSUPPORTED_REMOTE_FEATURE &&
(hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
status = 0x00;
else
status = ev->status;
conn->state = BT_CONNECTED;
hci_connect_cfm(conn, status);
}
unlock:
hci_dev_unlock(hdev);
}
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
[_op] = { \
.func = _func, \
@ -7206,6 +7326,10 @@ static const struct hci_le_ev {
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
sizeof(struct hci_evt_le_ext_adv_set_term)),
/* [0x18 = HCI_EVT_LE_PAST_RECEIVED] */
HCI_LE_EV(HCI_EV_LE_PAST_RECEIVED,
hci_le_past_received_evt,
sizeof(struct hci_ev_le_past_received)),
/* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */
HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established_evt,
sizeof(struct hci_evt_le_cis_established)),
@ -7232,6 +7356,12 @@ static const struct hci_le_ev {
hci_le_big_info_adv_report_evt,
sizeof(struct hci_evt_le_big_info_adv_report),
HCI_MAX_EVENT_SIZE),
/* [0x2b = HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE] */
HCI_LE_EV_VL(HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
hci_le_read_all_remote_features_evt,
sizeof(struct
hci_evt_le_read_all_remote_features_complete),
HCI_MAX_EVENT_SIZE),
};
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,

View File

@ -4011,8 +4011,19 @@ static int hci_le_read_buffer_size_sync(struct hci_dev *hdev)
/* Read LE Local Supported Features */
static int hci_le_read_local_features_sync(struct hci_dev *hdev)
{
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
0, NULL, HCI_CMD_TIMEOUT);
int err;
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
0, NULL, HCI_CMD_TIMEOUT);
if (err)
return err;
if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(2))
return __hci_cmd_sync_status(hdev,
HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
0, NULL, HCI_CMD_TIMEOUT);
return err;
}
/* Read LE Supported States */
@ -4324,6 +4335,10 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
if (ll_privacy_capable(hdev))
hdev->conn_flags |= HCI_CONN_FLAG_ADDRESS_RESOLUTION;
/* Mark PAST if supported */
if (past_capable(hdev))
hdev->conn_flags |= HCI_CONN_FLAG_PAST;
/* If the controller supports Extended Scanner Filter
* Policies, enable the corresponding event.
*/
@ -4393,6 +4408,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
if (ext_adv_capable(hdev))
events[2] |= 0x02; /* LE Advertising Set Terminated */
if (past_receiver_capable(hdev))
events[2] |= 0x80; /* LE PAST Received */
if (cis_capable(hdev)) {
events[3] |= 0x01; /* LE CIS Established */
if (cis_peripheral_capable(hdev))
@ -7006,7 +7024,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
goto unlock;
/* Add connection to indicate PA sync error */
pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
@ -7021,10 +7039,41 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
hci_dev_unlock(hdev);
}
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
struct hci_conn *acl, struct bt_iso_qos *qos)
{
struct hci_cp_le_past_params cp;
int err;
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(acl->handle);
/* An HCI_LE_Periodic_Advertising_Sync_Transfer_Received event is sent
* to the Host. HCI_LE_Periodic_Advertising_Report events will be
* enabled with duplicate filtering enabled.
*/
cp.mode = 0x03;
cp.skip = cpu_to_le16(qos->bcast.skip);
cp.sync_timeout = cpu_to_le16(qos->bcast.sync_timeout);
cp.cte_type = qos->bcast.sync_cte_type;
/* HCI_LE_PAST_PARAMS command returns a command complete event so it
* cannot wait for HCI_EV_LE_PAST_RECEIVED.
*/
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST_PARAMS,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
if (err)
return err;
/* Wait for HCI_EV_LE_PAST_RECEIVED event */
return __hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, 0, NULL,
HCI_EV_LE_PAST_RECEIVED,
conn->conn_timeout, NULL);
}
static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
{
struct hci_cp_le_pa_create_sync cp;
struct hci_conn *conn = data;
struct hci_conn *conn = data, *le;
struct bt_iso_qos *qos = &conn->iso_qos;
int err;
@ -7056,6 +7105,24 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
hci_update_passive_scan_sync(hdev);
/* Check if PAST is possible:
*
* 1. Check if an ACL connection with the destination address exists
* 2. Check if that HCI_CONN_FLAG_PAST has been set which indicates that
* user really intended to use PAST.
*/
le = hci_conn_hash_lookup_le(hdev, &conn->dst, conn->dst_type);
if (le) {
struct hci_conn_params *params;
params = hci_conn_params_lookup(hdev, &le->dst, le->dst_type);
if (params && params->flags & HCI_CONN_FLAG_PAST) {
err = hci_le_past_params_sync(hdev, conn, le, qos);
if (!err)
goto done;
}
}
/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
* it.
*/
@ -7172,3 +7239,182 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
create_big_complete);
}
struct past_data {
struct hci_conn *conn;
struct hci_conn *le;
};
static void past_complete(struct hci_dev *hdev, void *data, int err)
{
struct past_data *past = data;
bt_dev_dbg(hdev, "err %d", err);
kfree(past);
}
static int hci_le_past_set_info_sync(struct hci_dev *hdev, void *data)
{
struct past_data *past = data;
struct hci_cp_le_past_set_info cp;
hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, past->conn) ||
!hci_conn_valid(hdev, past->le)) {
hci_dev_unlock(hdev);
return -ECANCELED;
}
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(past->le->handle);
cp.adv_handle = past->conn->iso_qos.bcast.bis;
hci_dev_unlock(hdev);
return __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST_SET_INFO,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
static int hci_le_past_sync(struct hci_dev *hdev, void *data)
{
struct past_data *past = data;
struct hci_cp_le_past cp;
hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, past->conn) ||
!hci_conn_valid(hdev, past->le)) {
hci_dev_unlock(hdev);
return -ECANCELED;
}
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(past->le->handle);
cp.sync_handle = cpu_to_le16(past->conn->sync_handle);
hci_dev_unlock(hdev);
return __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
{
struct past_data *data;
int err;
if (conn->type != BIS_LINK && conn->type != PA_LINK)
return -EINVAL;
if (!past_sender_capable(conn->hdev))
return -EOPNOTSUPP;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->conn = conn;
data->le = le;
if (conn->role == HCI_ROLE_MASTER)
err = hci_cmd_sync_queue_once(conn->hdev,
hci_le_past_set_info_sync, data,
past_complete);
else
err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
data, past_complete);
if (err)
kfree(data);
return err;
}
static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
{
struct hci_conn *conn = data;
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
return;
hci_conn_drop(conn);
}
static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
void *data)
{
struct hci_conn *conn = data;
struct hci_cp_le_read_all_remote_features cp;
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(conn->handle);
cp.pages = 10; /* Attempt to read all pages */
/* Wait for HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE event otherwise
* hci_conn_drop may run prematurely causing a disconnection.
*/
return __hci_cmd_sync_status_sk(hdev,
HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
sizeof(cp), &cp,
HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
HCI_CMD_TIMEOUT, NULL);
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
static int hci_le_read_remote_features_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
struct hci_cp_le_read_remote_features cp;
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
/* Check if LL Extended Feature Set is supported and
* HCI_OP_LE_READ_ALL_REMOTE_FEATURES is supported then use that to read
* all features.
*/
if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(3))
return hci_le_read_all_remote_features_sync(hdev, data);
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(conn->handle);
/* Wait for HCI_EV_LE_REMOTE_FEAT_COMPLETE event otherwise
* hci_conn_drop may run prematurely causing a disconnection.
*/
return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
sizeof(cp), &cp,
HCI_EV_LE_REMOTE_FEAT_COMPLETE,
HCI_CMD_TIMEOUT, NULL);
}
int hci_le_read_remote_features(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
int err;
/* The remote features procedure is defined for central
* role only. So only in case of an initiated connection
* request the remote features.
*
* If the local controller supports peripheral-initiated features
* exchange, then requesting the remote features in peripheral
* role is possible. Otherwise just transition into the
* connected state without requesting the remote features.
*/
if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
err = hci_cmd_sync_queue_once(hdev,
hci_le_read_remote_features_sync,
hci_conn_hold(conn),
le_read_features_complete);
else
err = -EOPNOTSUPP;
return err;
}

View File

@ -80,14 +80,15 @@ static struct bt_iso_qos default_qos;
static bool check_ucast_qos(struct bt_iso_qos *qos);
static bool check_bcast_qos(struct bt_iso_qos *qos);
static bool iso_match_sid(struct sock *sk, void *data);
static bool iso_match_sid_past(struct sock *sk, void *data);
static bool iso_match_sync_handle(struct sock *sk, void *data);
static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data);
static void iso_sock_disconn(struct sock *sk);
typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
enum bt_sock_state state,
static struct sock *iso_get_sock(struct hci_dev *hdev, bdaddr_t *src,
bdaddr_t *dst, enum bt_sock_state state,
iso_sock_match_t match, void *data);
/* ---- ISO timers ---- */
@ -637,8 +638,8 @@ static struct sock *__iso_get_sock_listen_by_sid(bdaddr_t *ba, bdaddr_t *bc,
* match func data - pass -1 to ignore
* Returns closest match.
*/
static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
enum bt_sock_state state,
static struct sock *iso_get_sock(struct hci_dev *hdev, bdaddr_t *src,
bdaddr_t *dst, enum bt_sock_state state,
iso_sock_match_t match, void *data)
{
struct sock *sk = NULL, *sk1 = NULL;
@ -650,8 +651,25 @@ static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
continue;
/* Match Broadcast destination */
if (bacmp(dst, BDADDR_ANY) && bacmp(&iso_pi(sk)->dst, dst))
continue;
if (bacmp(dst, BDADDR_ANY) && bacmp(&iso_pi(sk)->dst, dst)) {
struct smp_irk *irk1, *irk2;
/* Check if destination is an RPA that we can resolve */
irk1 = hci_find_irk_by_rpa(hdev, dst);
if (!irk1)
continue;
/* Match with identity address */
if (bacmp(&iso_pi(sk)->dst, &irk1->bdaddr)) {
/* Check if socket destination address is also
* an RPA and if the IRK matches.
*/
irk2 = hci_find_irk_by_rpa(hdev,
&iso_pi(sk)->dst);
if (!irk2 || irk1 != irk2)
continue;
}
}
/* Use Match function if provided */
if (match && !match(sk, data))
@ -986,20 +1004,14 @@ static int iso_sock_bind_bc(struct socket *sock, struct sockaddr_unsized *addr,
return 0;
}
static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
/* Must be called on the locked socket. */
static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
int addr_len)
{
int err = 0;
if (sk->sk_type != SOCK_SEQPACKET) {
err = -EINVAL;
goto done;
}
if (addr_len != sizeof(*sa) + sizeof(*sa->iso_bc)) {
err = -EINVAL;
goto done;
}
if (!test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
return -EBADFD;
if (sa->iso_bc->bc_num_bis > ISO_MAX_NUM_BIS) {
err = -EINVAL;
@ -1022,6 +1034,77 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
return err;
}
static struct hci_dev *iso_conn_get_hdev(struct iso_conn *conn)
{
struct hci_dev *hdev = NULL;
iso_conn_lock(conn);
if (conn->hcon)
hdev = hci_dev_hold(conn->hcon->hdev);
iso_conn_unlock(conn);
return hdev;
}
/* Must be called on the locked socket. */
static int iso_sock_rebind_bc(struct sock *sk, struct sockaddr_iso *sa,
int addr_len)
{
struct hci_dev *hdev;
struct hci_conn *bis;
int err;
if (sk->sk_type != SOCK_SEQPACKET || !iso_pi(sk)->conn)
return -EINVAL;
/* Check if it is really a Broadcast address being requested */
if (addr_len != sizeof(*sa) + sizeof(*sa->iso_bc))
return -EINVAL;
/* Check if the address hasn't changed then perhaps only the number of
* bis has changed.
*/
if (!bacmp(&iso_pi(sk)->dst, &sa->iso_bc->bc_bdaddr) ||
!bacmp(&sa->iso_bc->bc_bdaddr, BDADDR_ANY))
return iso_sock_rebind_bis(sk, sa, addr_len);
/* Check if the address type is of LE type */
if (!bdaddr_type_is_le(sa->iso_bc->bc_bdaddr_type))
return -EINVAL;
hdev = iso_conn_get_hdev(iso_pi(sk)->conn);
if (!hdev)
return -EINVAL;
bis = iso_pi(sk)->conn->hcon;
/* Release the socket before lookups since that requires hci_dev_lock
* which shall not be acquired while holding sock_lock for proper
* ordering.
*/
release_sock(sk);
hci_dev_lock(bis->hdev);
lock_sock(sk);
if (!iso_pi(sk)->conn || iso_pi(sk)->conn->hcon != bis) {
/* raced with iso_conn_del() or iso_disconn_sock() */
err = -ENOTCONN;
goto unlock;
}
BT_DBG("sk %p %pMR type %u", sk, &sa->iso_bc->bc_bdaddr,
sa->iso_bc->bc_bdaddr_type);
err = hci_past_bis(bis, &sa->iso_bc->bc_bdaddr,
le_addr_type(sa->iso_bc->bc_bdaddr_type));
unlock:
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
}
static int iso_sock_bind(struct socket *sock, struct sockaddr_unsized *addr,
int addr_len)
{
@ -1037,13 +1120,12 @@ static int iso_sock_bind(struct socket *sock, struct sockaddr_unsized *addr,
lock_sock(sk);
/* Allow the user to bind a PA sync socket to a number
* of BISes to sync to.
*/
if ((sk->sk_state == BT_CONNECT2 ||
sk->sk_state == BT_CONNECTED) &&
test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
err = iso_sock_bind_pa_sk(sk, sa, addr_len);
if ((sk->sk_state == BT_CONNECT2 || sk->sk_state == BT_CONNECTED) &&
addr_len > sizeof(*sa)) {
/* Allow the user to rebind to a different address using
* PAST procedures.
*/
err = iso_sock_rebind_bc(sk, sa, addr_len);
goto done;
}
@ -1939,6 +2021,11 @@ static bool iso_match_pa_sync_flag(struct sock *sk, void *data)
return test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
}
static bool iso_match_dst(struct sock *sk, void *data)
{
return !bacmp(&iso_pi(sk)->dst, (bdaddr_t *)data);
}
static void iso_conn_ready(struct iso_conn *conn)
{
struct sock *parent = NULL;
@ -1947,23 +2034,45 @@ static void iso_conn_ready(struct iso_conn *conn)
struct hci_ev_le_pa_sync_established *ev2 = NULL;
struct hci_ev_le_per_adv_report *ev3 = NULL;
struct hci_conn *hcon;
struct hci_dev *hdev;
BT_DBG("conn %p", conn);
if (sk) {
/* Attempt to update source address in case of BIS Sender if
* the advertisement is using a random address.
*/
if (conn->hcon->type == BIS_LINK &&
conn->hcon->role == HCI_ROLE_MASTER &&
!bacmp(&conn->hcon->dst, BDADDR_ANY)) {
struct hci_conn *bis = conn->hcon;
struct adv_info *adv;
adv = hci_find_adv_instance(bis->hdev,
bis->iso_qos.bcast.bis);
if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
lock_sock(sk);
iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
bacpy(&iso_pi(sk)->src, &adv->random_addr);
release_sock(sk);
}
}
iso_sock_ready(conn->sk);
} else {
hcon = conn->hcon;
if (!hcon)
return;
hdev = hcon->hdev;
if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags)) {
/* A BIS slave hcon is notified to the ISO layer
* after the Command Complete for the LE Setup
* ISO Data Path command is received. Get the
* parent socket that matches the hcon BIG handle.
*/
parent = iso_get_sock(&hcon->src, &hcon->dst,
parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
BT_LISTEN, iso_match_big_hcon,
hcon);
} else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
@ -1971,12 +2080,12 @@ static void iso_conn_ready(struct iso_conn *conn)
HCI_EVT_LE_BIG_SYNC_ESTABLISHED);
/* Get reference to PA sync parent socket, if it exists */
parent = iso_get_sock(&hcon->src, &hcon->dst,
parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
BT_LISTEN,
iso_match_pa_sync_flag,
NULL);
if (!parent && ev)
parent = iso_get_sock(&hcon->src,
parent = iso_get_sock(hdev, &hcon->src,
&hcon->dst,
BT_LISTEN,
iso_match_big, ev);
@ -1984,7 +2093,7 @@ static void iso_conn_ready(struct iso_conn *conn)
ev2 = hci_recv_event_data(hcon->hdev,
HCI_EV_LE_PA_SYNC_ESTABLISHED);
if (ev2)
parent = iso_get_sock(&hcon->src,
parent = iso_get_sock(hdev, &hcon->src,
&hcon->dst,
BT_LISTEN,
iso_match_sid, ev2);
@ -1992,7 +2101,7 @@ static void iso_conn_ready(struct iso_conn *conn)
ev3 = hci_recv_event_data(hcon->hdev,
HCI_EV_LE_PER_ADV_REPORT);
if (ev3)
parent = iso_get_sock(&hcon->src,
parent = iso_get_sock(hdev, &hcon->src,
&hcon->dst,
BT_LISTEN,
iso_match_sync_handle_pa_report,
@ -2000,8 +2109,8 @@ static void iso_conn_ready(struct iso_conn *conn)
}
if (!parent)
parent = iso_get_sock(&hcon->src, BDADDR_ANY,
BT_LISTEN, NULL, NULL);
parent = iso_get_sock(hdev, &hcon->src, BDADDR_ANY,
BT_LISTEN, iso_match_dst, BDADDR_ANY);
if (!parent)
return;
@ -2090,6 +2199,16 @@ static bool iso_match_sid(struct sock *sk, void *data)
return ev->sid == iso_pi(sk)->bc_sid;
}
static bool iso_match_sid_past(struct sock *sk, void *data)
{
struct hci_ev_le_past_received *ev = data;
if (iso_pi(sk)->bc_sid == HCI_SID_INVALID)
return true;
return ev->sid == iso_pi(sk)->bc_sid;
}
static bool iso_match_sync_handle(struct sock *sk, void *data)
{
struct hci_evt_le_big_info_adv_report *ev = data;
@ -2109,6 +2228,7 @@ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data)
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
{
struct hci_ev_le_pa_sync_established *ev1;
struct hci_ev_le_past_received *ev1a;
struct hci_evt_le_big_info_adv_report *ev2;
struct hci_ev_le_per_adv_report *ev3;
struct sock *sk;
@ -2122,6 +2242,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
* SID to listen to and once sync is established its handle needs to
* be stored in iso_pi(sk)->sync_handle so it can be matched once
* receiving the BIG Info.
* 1a. HCI_EV_LE_PAST_RECEIVED: alternative to 1.
* 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a
* a BIG Info it attempts to check if there any listening socket with
* the same sync_handle and if it does then attempt to create a sync.
@ -2131,7 +2252,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
*/
ev1 = hci_recv_event_data(hdev, HCI_EV_LE_PA_SYNC_ESTABLISHED);
if (ev1) {
sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sid, ev1);
if (sk && !ev1->status) {
iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
@ -2141,10 +2262,22 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
goto done;
}
ev1a = hci_recv_event_data(hdev, HCI_EV_LE_PAST_RECEIVED);
if (ev1a) {
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sid_past, ev1a);
if (sk && !ev1a->status) {
iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
iso_pi(sk)->bc_sid = ev1a->sid;
}
goto done;
}
ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
if (ev2) {
/* Check if BIGInfo report has already been handled */
sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_CONNECTED,
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_CONNECTED,
iso_match_sync_handle, ev2);
if (sk) {
sock_put(sk);
@ -2153,10 +2286,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
}
/* Try to get PA sync socket, if it exists */
sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_CONNECT2,
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_CONNECT2,
iso_match_sync_handle, ev2);
if (!sk)
sk = iso_get_sock(&hdev->bdaddr, bdaddr,
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr,
BT_LISTEN,
iso_match_sync_handle,
ev2);
@ -2195,7 +2328,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
u8 *base;
struct hci_conn *hcon;
sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sync_handle_pa_report, ev3);
if (!sk)
goto done;
@ -2245,8 +2378,8 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
hcon->le_per_adv_data_len = 0;
}
} else {
sk = iso_get_sock(&hdev->bdaddr, BDADDR_ANY,
BT_LISTEN, NULL, NULL);
sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY,
BT_LISTEN, iso_match_dst, BDADDR_ANY);
}
done:

View File

@ -852,6 +852,12 @@ static u32 get_supported_settings(struct hci_dev *hdev)
if (ll_privacy_capable(hdev))
settings |= MGMT_SETTING_LL_PRIVACY;
if (past_sender_capable(hdev))
settings |= MGMT_SETTING_PAST_SENDER;
if (past_receiver_capable(hdev))
settings |= MGMT_SETTING_PAST_RECEIVER;
settings |= MGMT_SETTING_PHY_CONFIGURATION;
return settings;
@ -937,6 +943,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (ll_privacy_enabled(hdev))
settings |= MGMT_SETTING_LL_PRIVACY;
if (past_sender_enabled(hdev))
settings |= MGMT_SETTING_PAST_SENDER;
if (past_receiver_enabled(hdev))
settings |= MGMT_SETTING_PAST_RECEIVER;
return settings;
}
@ -5110,6 +5122,69 @@ static void device_flags_changed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_FLAGS_CHANGED, hdev, &ev, sizeof(ev), sk);
}
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
if (!conn)
return false;
if (conn->dst_type != type)
return false;
if (conn->state != BT_CONNECTED)
return false;
return true;
}
/* This function requires the caller holds hdev->lock */
static struct hci_conn_params *hci_conn_params_set(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type,
u8 auto_connect)
{
struct hci_conn_params *params;
params = hci_conn_params_add(hdev, addr, addr_type);
if (!params)
return NULL;
if (params->auto_connect == auto_connect)
return params;
hci_pend_le_list_del_init(params);
switch (auto_connect) {
case HCI_AUTO_CONN_DISABLED:
case HCI_AUTO_CONN_LINK_LOSS:
/* If auto connect is being disabled when we're trying to
* connect to device, keep connecting.
*/
if (params->explicit_connect)
hci_pend_le_list_add(params, &hdev->pend_le_conns);
break;
case HCI_AUTO_CONN_REPORT:
if (params->explicit_connect)
hci_pend_le_list_add(params, &hdev->pend_le_conns);
else
hci_pend_le_list_add(params, &hdev->pend_le_reports);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
if (!is_connected(hdev, addr, addr_type))
hci_pend_le_list_add(params, &hdev->pend_le_conns);
break;
}
params->auto_connect = auto_connect;
bt_dev_dbg(hdev, "addr %pMR (type %u) auto_connect %u",
addr, addr_type, auto_connect);
return params;
}
static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
@ -5153,9 +5228,16 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
if (!params) {
bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
&cp->addr.bdaddr, le_addr_type(cp->addr.type));
goto unlock;
/* Create a new hci_conn_params if it doesn't exist */
params = hci_conn_params_set(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type),
HCI_AUTO_CONN_DISABLED);
if (!params) {
bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
&cp->addr.bdaddr,
le_addr_type(cp->addr.type));
goto unlock;
}
}
supported_flags = hdev->conn_flags;
@ -7542,68 +7624,6 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
return err;
}
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
if (!conn)
return false;
if (conn->dst_type != type)
return false;
if (conn->state != BT_CONNECTED)
return false;
return true;
}
/* This function requires the caller holds hdev->lock */
static int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr,
u8 addr_type, u8 auto_connect)
{
struct hci_conn_params *params;
params = hci_conn_params_add(hdev, addr, addr_type);
if (!params)
return -EIO;
if (params->auto_connect == auto_connect)
return 0;
hci_pend_le_list_del_init(params);
switch (auto_connect) {
case HCI_AUTO_CONN_DISABLED:
case HCI_AUTO_CONN_LINK_LOSS:
/* If auto connect is being disabled when we're trying to
* connect to device, keep connecting.
*/
if (params->explicit_connect)
hci_pend_le_list_add(params, &hdev->pend_le_conns);
break;
case HCI_AUTO_CONN_REPORT:
if (params->explicit_connect)
hci_pend_le_list_add(params, &hdev->pend_le_conns);
else
hci_pend_le_list_add(params, &hdev->pend_le_reports);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
if (!is_connected(hdev, addr, addr_type))
hci_pend_le_list_add(params, &hdev->pend_le_conns);
break;
}
params->auto_connect = auto_connect;
bt_dev_dbg(hdev, "addr %pMR (type %u) auto_connect %u",
addr, addr_type, auto_connect);
return 0;
}
static void device_added(struct sock *sk, struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type, u8 action)
{
@ -7715,17 +7735,13 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults.
*/
if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
auto_conn) < 0) {
params = hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
auto_conn);
if (!params) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_FAILED, &cp->addr,
sizeof(cp->addr));
goto unlock;
} else {
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
addr_type);
if (params)
current_flags = params->flags;
}
cmd = mgmt_pending_new(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);