bluetooth-next pull request for net-next:
- Add new VID/PID for Mediatek MT7922 - Add support multiple BIS/BIG - Add support for Intel Gale Peak - Add support for Qualcomm WCN3988 - Add support for BT_PKT_STATUS for ISO sockets - Various fixes for experimental ISO support - Load FW v2 for RTL8852C - Add support for NXP AW693 chipset - Add support for Mediatek MT2925 -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmTWigQZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKd8sD/92kBczbO3v+nSNyiYcbVmB x3Z7x1l2ExxHnPdW8xBmEzHlDErYB/KBKYdJWM8y6Bam5z1lnsX7LflXSy+bhZeX iOFYl94Gh/9/ooyYOwwYUKC2fLKWT54PLg1TcJzyfp8uUizQNWAg9QD7vjvxe7lN HXrW6CaA4Oohcq2YXagZV1h6Q/jl3BjcfEe7N0E6YYjeonplsJsv6rYG8Ku5n0Pi 9YhB5IkX5zszTGKBSSWURKvaJjbFd7pr3mYkgLZG2pIMGQcUAFJZ9kL7de9xeBWI TRfgehZZPB2bUac1LxGLcAfONTmzUmo3/trjL1opdxreVCAX565JlaVSJwd0zuQk cBrmtU3Q8peFSOgJRb1Ci5junE8tqjEWzFRIgw7/wL1Ys3mrbbVDDGKqPhwhvjdq grOBf6UGaDpEO797yWWpBl5DLV3klMQDi4v84J0yTdvf4GXF8t8fuZU+zIpknVou BwdeeF33yzqtk01BjomQcLVOrrGOP7+Salc5g7eEVU1jZnaw0MH9aH+o6R2JYtP8 uIiH4QOUJh7NA543F+/wPdZU+OV1E+Io+b34pTZ1oIyM2UT9Dy57Tex/DDKq2UCe 69WV6aVM+FTt2VSMUS2J0XrXkxbI4f6/ABOLht5hHKxT1m6LhOh8mCSTof+UENrr G0sVoCodRrSljSMS/VltTA== =akZ8 -----END PGP SIGNATURE----- Merge tag 'for-net-next-2023-08-11' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next bluetooth-next pull request for net-next: - Add new VID/PID for Mediatek MT7922 - Add support multiple BIS/BIG - Add support for Intel Gale Peak - Add support for Qualcomm WCN3988 - Add support for BT_PKT_STATUS for ISO sockets - Various fixes for experimental ISO support - Load FW v2 for RTL8852C - Add support for NXP AW693 chipset - Add support for Mediatek MT2925
This commit is contained in:
commit
3d3829363b
@ -19,6 +19,7 @@ properties:
|
||||
- qcom,qca2066-bt
|
||||
- qcom,qca6174-bt
|
||||
- qcom,qca9377-bt
|
||||
- qcom,wcn3988-bt
|
||||
- qcom,wcn3990-bt
|
||||
- qcom,wcn3991-bt
|
||||
- qcom,wcn3998-bt
|
||||
@ -111,6 +112,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,wcn3988-bt
|
||||
- qcom,wcn3990-bt
|
||||
- qcom,wcn3991-bt
|
||||
- qcom,wcn3998-bt
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}})
|
||||
#define BDADDR_BCM2076B1 (&(bdaddr_t) {{0x79, 0x56, 0x00, 0xa0, 0x76, 0x20}})
|
||||
#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
|
||||
#define BDADDR_BCM43430A1 (&(bdaddr_t) {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}})
|
||||
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
|
||||
#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
|
||||
#define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}})
|
||||
@ -115,6 +116,9 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
*
|
||||
* The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller
|
||||
* with no configured address.
|
||||
*
|
||||
* The address AA:AA:AA:AA:AA:AA indicates a BCM43430A1 controller
|
||||
* with no configured address.
|
||||
*/
|
||||
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM20702A1) ||
|
||||
@ -124,6 +128,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4334B0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A1) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
|
||||
/* Try falling back to BDADDR EFI variable */
|
||||
if (btbcm_set_bdaddr_from_efi(hdev) != 0) {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
@ -27,6 +28,11 @@
|
||||
|
||||
#define BTINTEL_PPAG_NAME "PPAG"
|
||||
|
||||
enum {
|
||||
DSM_SET_WDISABLE2_DELAY = 1,
|
||||
DSM_SET_RESET_METHOD = 3,
|
||||
};
|
||||
|
||||
/* structure to store the PPAG data read from ACPI table */
|
||||
struct btintel_ppag {
|
||||
u32 domain;
|
||||
@ -49,6 +55,10 @@ static struct {
|
||||
u32 fw_build_num;
|
||||
} coredump_info;
|
||||
|
||||
static const guid_t btintel_guid_dsm =
|
||||
GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233,
|
||||
0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9);
|
||||
|
||||
int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_rp_read_bd_addr *bda;
|
||||
@ -470,6 +480,7 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
|
||||
case 0x18: /* Slr */
|
||||
case 0x19: /* Slr-F */
|
||||
case 0x1b: /* Mgr */
|
||||
case 0x1c: /* Gale Peak (GaP) */
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
|
||||
@ -2444,6 +2455,116 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int btintel_acpi_reset_method(struct hci_dev *hdev)
|
||||
{
|
||||
int ret = 0;
|
||||
acpi_status status;
|
||||
union acpi_object *p, *ref;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
bt_dev_err(hdev, "Failed to run _PRR method");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
p = buffer.pointer;
|
||||
|
||||
if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) {
|
||||
bt_dev_err(hdev, "Invalid arguments");
|
||||
ret = -EINVAL;
|
||||
goto exit_on_error;
|
||||
}
|
||||
|
||||
ref = &p->package.elements[0];
|
||||
if (ref->type != ACPI_TYPE_LOCAL_REFERENCE) {
|
||||
bt_dev_err(hdev, "Invalid object type: 0x%x", ref->type);
|
||||
ret = -EINVAL;
|
||||
goto exit_on_error;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_object(ref->reference.handle, "_RST", NULL, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
bt_dev_err(hdev, "Failed to run_RST method");
|
||||
ret = -ENODEV;
|
||||
goto exit_on_error;
|
||||
}
|
||||
|
||||
exit_on_error:
|
||||
kfree(buffer.pointer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btintel_set_dsm_reset_method(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *ver_tlv)
|
||||
{
|
||||
struct btintel_data *data = hci_get_priv(hdev);
|
||||
acpi_handle handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
|
||||
u8 reset_payload[4] = {0x01, 0x00, 0x01, 0x00};
|
||||
union acpi_object *obj, argv4;
|
||||
enum {
|
||||
RESET_TYPE_WDISABLE2,
|
||||
RESET_TYPE_VSEC
|
||||
};
|
||||
|
||||
handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
|
||||
|
||||
if (!handle) {
|
||||
bt_dev_dbg(hdev, "No support for bluetooth device in ACPI firmware");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!acpi_has_method(handle, "_PRR")) {
|
||||
bt_dev_err(hdev, "No support for _PRR ACPI method");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ver_tlv->cnvi_top & 0xfff) {
|
||||
case 0x910: /* GalePeak2 */
|
||||
reset_payload[2] = RESET_TYPE_VSEC;
|
||||
break;
|
||||
default:
|
||||
/* WDISABLE2 is the default reset method */
|
||||
reset_payload[2] = RESET_TYPE_WDISABLE2;
|
||||
|
||||
if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0,
|
||||
BIT(DSM_SET_WDISABLE2_DELAY))) {
|
||||
bt_dev_err(hdev, "No dsm support to set reset delay");
|
||||
return;
|
||||
}
|
||||
argv4.integer.type = ACPI_TYPE_INTEGER;
|
||||
/* delay required to toggle BT power */
|
||||
argv4.integer.value = 160;
|
||||
obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
|
||||
DSM_SET_WDISABLE2_DELAY, &argv4);
|
||||
if (!obj) {
|
||||
bt_dev_err(hdev, "Failed to call dsm to set reset delay");
|
||||
return;
|
||||
}
|
||||
ACPI_FREE(obj);
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "DSM reset method type: 0x%02x", reset_payload[2]);
|
||||
|
||||
if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0,
|
||||
DSM_SET_RESET_METHOD)) {
|
||||
bt_dev_warn(hdev, "No support for dsm to set reset method");
|
||||
return;
|
||||
}
|
||||
argv4.buffer.type = ACPI_TYPE_BUFFER;
|
||||
argv4.buffer.length = sizeof(reset_payload);
|
||||
argv4.buffer.pointer = reset_payload;
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
|
||||
DSM_SET_RESET_METHOD, &argv4);
|
||||
if (!obj) {
|
||||
bt_dev_err(hdev, "Failed to call dsm to set reset method");
|
||||
return;
|
||||
}
|
||||
ACPI_FREE(obj);
|
||||
data->acpi_reset_method = btintel_acpi_reset_method;
|
||||
}
|
||||
|
||||
static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *ver)
|
||||
{
|
||||
@ -2528,6 +2649,7 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
hci_set_msft_opcode(hdev, 0xFC1E);
|
||||
break;
|
||||
default:
|
||||
@ -2742,6 +2864,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
/* Display version information of TLV type */
|
||||
btintel_version_info_tlv(hdev, &ver_tlv);
|
||||
|
||||
@ -2757,6 +2880,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
/* Setup MSFT Extension support */
|
||||
btintel_set_msft_opcode(hdev,
|
||||
INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
|
||||
btintel_set_dsm_reset_method(hdev, &ver_tlv);
|
||||
|
||||
err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
|
||||
btintel_register_devcoredump_support(hdev);
|
||||
@ -2824,6 +2948,80 @@ int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_configure_setup);
|
||||
|
||||
static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct intel_tlv *tlv = (void *)&skb->data[5];
|
||||
|
||||
/* The first event is always an event type TLV */
|
||||
if (tlv->type != INTEL_TLV_TYPE_ID)
|
||||
goto recv_frame;
|
||||
|
||||
switch (tlv->val[0]) {
|
||||
case INTEL_TLV_SYSTEM_EXCEPTION:
|
||||
case INTEL_TLV_FATAL_EXCEPTION:
|
||||
case INTEL_TLV_DEBUG_EXCEPTION:
|
||||
case INTEL_TLV_TEST_EXCEPTION:
|
||||
/* Generate devcoredump from exception */
|
||||
if (!hci_devcd_init(hdev, skb->len)) {
|
||||
hci_devcd_append(hdev, skb);
|
||||
hci_devcd_complete(hdev);
|
||||
} else {
|
||||
bt_dev_err(hdev, "Failed to generate devcoredump");
|
||||
kfree_skb(skb);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
|
||||
}
|
||||
|
||||
recv_frame:
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
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) {
|
||||
const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
|
||||
unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
|
||||
|
||||
if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
|
||||
switch (skb->data[2]) {
|
||||
case 0x02:
|
||||
/* When switching to the operational firmware
|
||||
* the device sends a vendor specific event
|
||||
* indicating that the bootup completed.
|
||||
*/
|
||||
btintel_bootup(hdev, ptr, len);
|
||||
break;
|
||||
case 0x06:
|
||||
/* When the firmware loading completes the
|
||||
* device sends out a vendor specific event
|
||||
* indicating the result of the firmware
|
||||
* loading.
|
||||
*/
|
||||
btintel_secure_send_result(hdev, ptr, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle all diagnostics events separately. May still call
|
||||
* hci_recv_frame.
|
||||
*/
|
||||
if (len >= sizeof(diagnostics_hdr) &&
|
||||
memcmp(&skb->data[2], diagnostics_hdr,
|
||||
sizeof(diagnostics_hdr)) == 0) {
|
||||
return btintel_diagnostics(hdev, skb);
|
||||
}
|
||||
}
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_recv_event);
|
||||
|
||||
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len)
|
||||
{
|
||||
const struct intel_bootup *evt = ptr;
|
||||
|
@ -166,12 +166,14 @@ enum {
|
||||
INTEL_BROKEN_SHUTDOWN_LED,
|
||||
INTEL_ROM_LEGACY,
|
||||
INTEL_ROM_LEGACY_NO_WBS_SUPPORT,
|
||||
INTEL_ACPI_RESET_ACTIVE,
|
||||
|
||||
__INTEL_NUM_FLAGS,
|
||||
};
|
||||
|
||||
struct btintel_data {
|
||||
DECLARE_BITMAP(flags, __INTEL_NUM_FLAGS);
|
||||
int (*acpi_reset_method)(struct hci_dev *hdev);
|
||||
};
|
||||
|
||||
#define btintel_set_flag(hdev, nr) \
|
||||
@ -220,6 +222,7 @@ int btintel_read_boot_params(struct hci_dev *hdev,
|
||||
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
|
||||
const struct firmware *fw, u32 *boot_param);
|
||||
int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name);
|
||||
int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
|
||||
void btintel_secure_send_result(struct hci_dev *hdev,
|
||||
const void *ptr, unsigned int len);
|
||||
|
@ -53,10 +53,61 @@ struct btmtk_section_map {
|
||||
};
|
||||
} __packed;
|
||||
|
||||
static void btmtk_coredump(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __hci_cmd_send(hdev, 0xfd5b, 0, NULL);
|
||||
if (err < 0)
|
||||
bt_dev_err(hdev, "Coredump failed (%d)", err);
|
||||
}
|
||||
|
||||
static void btmtk_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btmediatek_data *data = hci_get_priv(hdev);
|
||||
char buf[80];
|
||||
|
||||
snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
|
||||
data->dev_id);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
|
||||
data->cd_info.fw_version);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Driver: %s\n",
|
||||
data->cd_info.driver_name);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Vendor: MediaTek\n");
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static void btmtk_coredump_notify(struct hci_dev *hdev, int state)
|
||||
{
|
||||
struct btmediatek_data *data = hci_get_priv(hdev);
|
||||
|
||||
switch (state) {
|
||||
case HCI_DEVCOREDUMP_IDLE:
|
||||
data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_ACTIVE:
|
||||
data->cd_info.state = HCI_DEVCOREDUMP_ACTIVE;
|
||||
break;
|
||||
case HCI_DEVCOREDUMP_TIMEOUT:
|
||||
case HCI_DEVCOREDUMP_ABORT:
|
||||
case HCI_DEVCOREDUMP_DONE:
|
||||
data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
|
||||
btmtk_reset_sync(hdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
struct btmtk_patch_header *hdr;
|
||||
struct btmtk_global_desc *globaldesc = NULL;
|
||||
struct btmtk_section_map *sectionmap;
|
||||
const struct firmware *fw;
|
||||
@ -75,9 +126,13 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_bin_ptr = fw_ptr;
|
||||
hdr = (struct btmtk_patch_header *)fw_ptr;
|
||||
globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
|
||||
section_num = le32_to_cpu(globaldesc->section_num);
|
||||
|
||||
bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s",
|
||||
le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
|
||||
|
||||
for (i = 0; i < section_num; i++) {
|
||||
first_block = 1;
|
||||
fw_ptr = fw_bin_ptr;
|
||||
@ -280,6 +335,83 @@ int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_set_bdaddr);
|
||||
|
||||
void btmtk_reset_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmediatek_data *reset_work = hci_get_priv(hdev);
|
||||
int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
err = hci_cmd_sync_queue(hdev, reset_work->reset_sync, NULL, NULL);
|
||||
if (err)
|
||||
bt_dev_err(hdev, "failed to reset (%d)", err);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_reset_sync);
|
||||
|
||||
int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
|
||||
u32 fw_version)
|
||||
{
|
||||
struct btmediatek_data *data = hci_get_priv(hdev);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
data->cd_info.fw_version = fw_version;
|
||||
data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
|
||||
data->cd_info.driver_name = name;
|
||||
|
||||
return hci_devcd_register(hdev, btmtk_coredump, btmtk_coredump_hdr,
|
||||
btmtk_coredump_notify);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_register_coredump);
|
||||
|
||||
int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btmediatek_data *data = hci_get_priv(hdev);
|
||||
int err;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
|
||||
return 0;
|
||||
|
||||
switch (data->cd_info.state) {
|
||||
case HCI_DEVCOREDUMP_IDLE:
|
||||
err = hci_devcd_init(hdev, MTK_COREDUMP_SIZE);
|
||||
if (err < 0)
|
||||
break;
|
||||
data->cd_info.cnt = 0;
|
||||
|
||||
/* It is supposed coredump can be done within 5 seconds */
|
||||
schedule_delayed_work(&hdev->dump.dump_timeout,
|
||||
msecs_to_jiffies(5000));
|
||||
fallthrough;
|
||||
case HCI_DEVCOREDUMP_ACTIVE:
|
||||
default:
|
||||
err = hci_devcd_append(hdev, skb);
|
||||
if (err < 0)
|
||||
break;
|
||||
data->cd_info.cnt++;
|
||||
|
||||
/* Mediatek coredump data would be more than MTK_COREDUMP_NUM */
|
||||
if (data->cd_info.cnt > MTK_COREDUMP_NUM &&
|
||||
skb->len > MTK_COREDUMP_END_LEN)
|
||||
if (!memcmp((char *)&skb->data[skb->len - MTK_COREDUMP_END_LEN],
|
||||
MTK_COREDUMP_END, MTK_COREDUMP_END_LEN - 1)) {
|
||||
bt_dev_info(hdev, "Mediatek coredump end");
|
||||
hci_devcd_complete(hdev);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
kfree_skb(skb);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_process_coredump);
|
||||
|
||||
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
|
||||
MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
|
||||
MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION);
|
||||
@ -289,3 +421,4 @@ MODULE_FIRMWARE(FIRMWARE_MT7622);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7961);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7925);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
|
||||
#define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
|
||||
|
||||
#define HCI_EV_WMT 0xe4
|
||||
#define HCI_WMT_MAX_EVENT_SIZE 64
|
||||
@ -21,6 +22,11 @@
|
||||
#define MT7921_DLSTATUS 0x7c053c10
|
||||
#define BT_DL_STATE BIT(1)
|
||||
|
||||
#define MTK_COREDUMP_SIZE (1024 * 1000)
|
||||
#define MTK_COREDUMP_END "coredump end"
|
||||
#define MTK_COREDUMP_END_LEN (sizeof(MTK_COREDUMP_END))
|
||||
#define MTK_COREDUMP_NUM 255
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_PATCH_DWNLD = 0x1,
|
||||
BTMTK_WMT_TEST = 0x2,
|
||||
@ -119,6 +125,21 @@ struct btmtk_hci_wmt_params {
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
|
||||
|
||||
struct btmtk_coredump_info {
|
||||
const char *driver_name;
|
||||
u32 fw_version;
|
||||
u16 cnt;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct btmediatek_data {
|
||||
u32 dev_id;
|
||||
btmtk_reset_sync_func_t reset_sync;
|
||||
struct btmtk_coredump_info cd_info;
|
||||
};
|
||||
|
||||
typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *,
|
||||
struct btmtk_hci_wmt_params *);
|
||||
|
||||
@ -131,6 +152,13 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
|
||||
int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync);
|
||||
|
||||
void btmtk_reset_sync(struct hci_dev *hdev);
|
||||
|
||||
int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
|
||||
u32 fw_version);
|
||||
|
||||
int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
#else
|
||||
|
||||
static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
|
||||
@ -151,4 +179,18 @@ static int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void btmtk_reset_sync(struct hci_dev *hdev)
|
||||
{
|
||||
}
|
||||
|
||||
static int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
|
||||
u32 fw_version)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -29,16 +29,25 @@
|
||||
#define BTNXPUART_CHECK_BOOT_SIGNATURE 3
|
||||
#define BTNXPUART_SERDEV_OPEN 4
|
||||
|
||||
#define FIRMWARE_W8987 "nxp/uartuart8987_bt.bin"
|
||||
#define FIRMWARE_W8997 "nxp/uartuart8997_bt_v4.bin"
|
||||
#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin"
|
||||
#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin"
|
||||
#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se"
|
||||
#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin"
|
||||
#define FIRMWARE_W8987 "nxp/uartuart8987_bt.bin"
|
||||
#define FIRMWARE_W8997 "nxp/uartuart8997_bt_v4.bin"
|
||||
#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin"
|
||||
#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin"
|
||||
#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se"
|
||||
#define FIRMWARE_AW693 "nxp/uartaw693_bt.bin"
|
||||
#define FIRMWARE_SECURE_AW693 "nxp/uartaw693_bt.bin.se"
|
||||
#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin"
|
||||
|
||||
#define CHIP_ID_W9098 0x5c03
|
||||
#define CHIP_ID_IW416 0x7201
|
||||
#define CHIP_ID_IW612 0x7601
|
||||
#define CHIP_ID_AW693 0x8200
|
||||
|
||||
#define FW_SECURE_MASK 0xc0
|
||||
#define FW_OPEN 0x00
|
||||
#define FW_AUTH_ILLEGAL 0x40
|
||||
#define FW_AUTH_PLAIN 0x80
|
||||
#define FW_AUTH_ENC 0xc0
|
||||
|
||||
#define HCI_NXP_PRI_BAUDRATE 115200
|
||||
#define HCI_NXP_SEC_BAUDRATE 3000000
|
||||
@ -665,6 +674,9 @@ static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name)
|
||||
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
|
||||
int err = 0;
|
||||
|
||||
if (!fw_name)
|
||||
return -ENOENT;
|
||||
|
||||
if (!strlen(nxpdev->fw_name)) {
|
||||
snprintf(nxpdev->fw_name, MAX_FW_FILE_NAME_LEN, "%s", fw_name);
|
||||
|
||||
@ -812,7 +824,8 @@ free_skb:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid)
|
||||
static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid,
|
||||
u8 loader_ver)
|
||||
{
|
||||
char *fw_name = NULL;
|
||||
|
||||
@ -826,6 +839,14 @@ static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid)
|
||||
case CHIP_ID_IW612:
|
||||
fw_name = FIRMWARE_IW612;
|
||||
break;
|
||||
case CHIP_ID_AW693:
|
||||
if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
|
||||
fw_name = FIRMWARE_AW693;
|
||||
else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
|
||||
fw_name = FIRMWARE_SECURE_AW693;
|
||||
else
|
||||
bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unknown chip signature %04x", chipid);
|
||||
break;
|
||||
@ -838,13 +859,15 @@ static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
struct v3_start_ind *req = skb_pull_data(skb, sizeof(*req));
|
||||
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
|
||||
u16 chip_id;
|
||||
u8 loader_ver;
|
||||
|
||||
if (!process_boot_signature(nxpdev))
|
||||
goto free_skb;
|
||||
|
||||
chip_id = le16_to_cpu(req->chip_id);
|
||||
loader_ver = req->loader_ver;
|
||||
if (!nxp_request_firmware(hdev, nxp_get_fw_name_from_chipid(hdev,
|
||||
chip_id)))
|
||||
chip_id, loader_ver)))
|
||||
nxp_send_ack(NXP_ACK_V3, hdev);
|
||||
|
||||
free_skb:
|
||||
|
@ -594,14 +594,20 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
/* Firmware files to download are based on ROM version.
|
||||
* ROM version is derived from last two bytes of soc_ver.
|
||||
*/
|
||||
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
|
||||
if (soc_type == QCA_WCN3988)
|
||||
rom_ver = ((soc_ver & 0x00000f00) >> 0x05) | (soc_ver & 0x0000000f);
|
||||
else
|
||||
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
|
||||
|
||||
if (soc_type == QCA_WCN6750)
|
||||
qca_send_patch_config_cmd(hdev);
|
||||
|
||||
/* Download rampatch file */
|
||||
config.type = TLV_TYPE_PATCH;
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
if (soc_type == QCA_WCN3988) {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/apbtfw%02x.tlv", rom_ver);
|
||||
} else if (qca_is_wcn399x(soc_type)) {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crbtfw%02x.tlv", rom_ver);
|
||||
} else if (soc_type == QCA_QCA6390) {
|
||||
@ -636,6 +642,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
if (firmware_name)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/%s", firmware_name);
|
||||
else if (soc_type == QCA_WCN3988)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/apnv%02x.bin", rom_ver);
|
||||
else if (qca_is_wcn399x(soc_type)) {
|
||||
if (le32_to_cpu(ver.soc_id) == QCA_WCN3991_SOC_ID) {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
|
@ -142,6 +142,7 @@ enum qca_btsoc_type {
|
||||
QCA_INVALID = -1,
|
||||
QCA_AR3002,
|
||||
QCA_ROME,
|
||||
QCA_WCN3988,
|
||||
QCA_WCN3990,
|
||||
QCA_WCN3998,
|
||||
QCA_WCN3991,
|
||||
@ -162,8 +163,15 @@ int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
|
||||
static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
|
||||
{
|
||||
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3991 ||
|
||||
soc_type == QCA_WCN3998;
|
||||
switch (soc_type) {
|
||||
case QCA_WCN3988:
|
||||
case QCA_WCN3990:
|
||||
case QCA_WCN3991:
|
||||
case QCA_WCN3998:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
|
||||
{
|
||||
|
@ -32,6 +32,8 @@
|
||||
#define RTL_ROM_LMP_8851B 0x8851
|
||||
#define RTL_CONFIG_MAGIC 0x8723ab55
|
||||
|
||||
#define RTL_VSC_OP_COREDUMP 0xfcff
|
||||
|
||||
#define IC_MATCH_FL_LMPSUBV (1 << 0)
|
||||
#define IC_MATCH_FL_HCIREV (1 << 1)
|
||||
#define IC_MATCH_FL_HCIVER (1 << 2)
|
||||
@ -81,6 +83,7 @@ struct id_table {
|
||||
bool has_msft_ext;
|
||||
char *fw_name;
|
||||
char *cfg_name;
|
||||
char *hw_info;
|
||||
};
|
||||
|
||||
struct btrtl_device_info {
|
||||
@ -101,22 +104,25 @@ static const struct id_table ic_id_table[] = {
|
||||
{ IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = false,
|
||||
.fw_name = "rtl_bt/rtl8723a_fw.bin",
|
||||
.cfg_name = NULL },
|
||||
.fw_name = "rtl_bt/rtl8723a_fw",
|
||||
.cfg_name = NULL,
|
||||
.hw_info = "rtl8723au" },
|
||||
|
||||
/* 8723BS */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723bs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723bs_config" },
|
||||
.fw_name = "rtl_bt/rtl8723bs_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723bs_config",
|
||||
.hw_info = "rtl8723bs" },
|
||||
|
||||
/* 8723B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723b_config" },
|
||||
.fw_name = "rtl_bt/rtl8723b_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723b_config",
|
||||
.hw_info = "rtl8723bu" },
|
||||
|
||||
/* 8723CS-CG */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
|
||||
@ -126,8 +132,9 @@ static const struct id_table ic_id_table[] = {
|
||||
.hci_bus = HCI_UART,
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723cs_cg_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723cs_cg_config" },
|
||||
.fw_name = "rtl_bt/rtl8723cs_cg_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723cs_cg_config",
|
||||
.hw_info = "rtl8723cs-cg" },
|
||||
|
||||
/* 8723CS-VF */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
|
||||
@ -137,8 +144,9 @@ static const struct id_table ic_id_table[] = {
|
||||
.hci_bus = HCI_UART,
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723cs_vf_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723cs_vf_config" },
|
||||
.fw_name = "rtl_bt/rtl8723cs_vf_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723cs_vf_config",
|
||||
.hw_info = "rtl8723cs-vf" },
|
||||
|
||||
/* 8723CS-XX */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
|
||||
@ -148,139 +156,157 @@ static const struct id_table ic_id_table[] = {
|
||||
.hci_bus = HCI_UART,
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723cs_xx_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723cs_xx_config" },
|
||||
.fw_name = "rtl_bt/rtl8723cs_xx_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723cs_xx_config",
|
||||
.hw_info = "rtl8723cs" },
|
||||
|
||||
/* 8723D */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723d_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config" },
|
||||
.fw_name = "rtl_bt/rtl8723d_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config",
|
||||
.hw_info = "rtl8723du" },
|
||||
|
||||
/* 8723DS */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723ds_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723ds_config" },
|
||||
.fw_name = "rtl_bt/rtl8723ds_fw",
|
||||
.cfg_name = "rtl_bt/rtl8723ds_config",
|
||||
.hw_info = "rtl8723ds" },
|
||||
|
||||
/* 8821A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8821a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821a_config" },
|
||||
.fw_name = "rtl_bt/rtl8821a_fw",
|
||||
.cfg_name = "rtl_bt/rtl8821a_config",
|
||||
.hw_info = "rtl8821au" },
|
||||
|
||||
/* 8821C */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8821c_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821c_config" },
|
||||
.fw_name = "rtl_bt/rtl8821c_fw",
|
||||
.cfg_name = "rtl_bt/rtl8821c_config",
|
||||
.hw_info = "rtl8821cu" },
|
||||
|
||||
/* 8821CS */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8821cs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821cs_config" },
|
||||
.fw_name = "rtl_bt/rtl8821cs_fw",
|
||||
.cfg_name = "rtl_bt/rtl8821cs_config",
|
||||
.hw_info = "rtl8821cs" },
|
||||
|
||||
/* 8761A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761a_config" },
|
||||
.fw_name = "rtl_bt/rtl8761a_fw",
|
||||
.cfg_name = "rtl_bt/rtl8761a_config",
|
||||
.hw_info = "rtl8761au" },
|
||||
|
||||
/* 8761B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8761b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761b_config" },
|
||||
.fw_name = "rtl_bt/rtl8761b_fw",
|
||||
.cfg_name = "rtl_bt/rtl8761b_config",
|
||||
.hw_info = "rtl8761btv" },
|
||||
|
||||
/* 8761BU */
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761bu_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761bu_config" },
|
||||
.fw_name = "rtl_bt/rtl8761bu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8761bu_config",
|
||||
.hw_info = "rtl8761bu" },
|
||||
|
||||
/* 8822C with UART interface */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8822cs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822cs_config" },
|
||||
.fw_name = "rtl_bt/rtl8822cs_fw",
|
||||
.cfg_name = "rtl_bt/rtl8822cs_config",
|
||||
.hw_info = "rtl8822cs" },
|
||||
|
||||
/* 8822C with UART interface */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8822cs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822cs_config" },
|
||||
.fw_name = "rtl_bt/rtl8822cs_fw",
|
||||
.cfg_name = "rtl_bt/rtl8822cs_config",
|
||||
.hw_info = "rtl8822cs" },
|
||||
|
||||
/* 8822C with USB interface */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8822cu_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822cu_config" },
|
||||
.fw_name = "rtl_bt/rtl8822cu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8822cu_config",
|
||||
.hw_info = "rtl8822cu" },
|
||||
|
||||
/* 8822B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8822b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822b_config" },
|
||||
.fw_name = "rtl_bt/rtl8822b_fw",
|
||||
.cfg_name = "rtl_bt/rtl8822b_config",
|
||||
.hw_info = "rtl8822bu" },
|
||||
|
||||
/* 8852A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8852au_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8852au_config" },
|
||||
.fw_name = "rtl_bt/rtl8852au_fw",
|
||||
.cfg_name = "rtl_bt/rtl8852au_config",
|
||||
.hw_info = "rtl8852au" },
|
||||
|
||||
/* 8852B with UART interface */
|
||||
{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8852bs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8852bs_config" },
|
||||
.fw_name = "rtl_bt/rtl8852bs_fw",
|
||||
.cfg_name = "rtl_bt/rtl8852bs_config",
|
||||
.hw_info = "rtl8852bs" },
|
||||
|
||||
/* 8852B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8852bu_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8852bu_config" },
|
||||
.fw_name = "rtl_bt/rtl8852bu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8852bu_config",
|
||||
.hw_info = "rtl8852bu" },
|
||||
|
||||
/* 8852C */
|
||||
{ IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8852cu_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8852cu_config" },
|
||||
.fw_name = "rtl_bt/rtl8852cu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8852cu_config",
|
||||
.hw_info = "rtl8852cu" },
|
||||
|
||||
/* 8851B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = false,
|
||||
.fw_name = "rtl_bt/rtl8851bu_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8851bu_config" },
|
||||
.fw_name = "rtl_bt/rtl8851bu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8851bu_config",
|
||||
.hw_info = "rtl8851bu" },
|
||||
};
|
||||
|
||||
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
|
||||
@ -590,6 +616,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
unsigned char **_buf)
|
||||
{
|
||||
static const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
|
||||
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
|
||||
struct rtl_epatch_header *epatch_info;
|
||||
unsigned char *buf;
|
||||
int i, len;
|
||||
@ -705,8 +732,10 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
|
||||
epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
|
||||
num_patches = le16_to_cpu(epatch_info->num_patches);
|
||||
|
||||
BT_DBG("fw_version=%x, num_patches=%d",
|
||||
le32_to_cpu(epatch_info->fw_version), num_patches);
|
||||
coredump_info->rtl_dump.fw_version = le32_to_cpu(epatch_info->fw_version);
|
||||
|
||||
/* After the rtl_epatch_header there is a funky patch metadata section.
|
||||
* Assuming 2 patches, the layout is:
|
||||
@ -903,6 +932,53 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btrtl_coredump(struct hci_dev *hdev)
|
||||
{
|
||||
static const u8 param[] = { 0x00, 0x00 };
|
||||
|
||||
__hci_cmd_send(hdev, RTL_VSC_OP_COREDUMP, sizeof(param), param);
|
||||
}
|
||||
|
||||
static void btrtl_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
|
||||
char buf[80];
|
||||
|
||||
if (coredump_info->rtl_dump.controller)
|
||||
snprintf(buf, sizeof(buf), "Controller Name: %s\n",
|
||||
coredump_info->rtl_dump.controller);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "Controller Name: Unknown\n");
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
|
||||
coredump_info->rtl_dump.fw_version);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info->rtl_dump.driver_name);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Vendor: Realtek\n");
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static int btrtl_register_devcoredump_support(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_devcd_register(hdev, btrtl_coredump, btrtl_dmp_hdr, NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
|
||||
{
|
||||
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
|
||||
|
||||
coredump_info->rtl_dump.driver_name = driver_name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_set_driver_name);
|
||||
|
||||
static bool rtl_has_chip_type(u16 lmp_subver)
|
||||
{
|
||||
switch (lmp_subver) {
|
||||
@ -964,15 +1040,16 @@ EXPORT_SYMBOL_GPL(btrtl_free);
|
||||
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
const char *postfix)
|
||||
{
|
||||
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
|
||||
struct btrtl_device_info *btrtl_dev;
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *resp;
|
||||
struct hci_command_hdr *cmd;
|
||||
char fw_name[40];
|
||||
char cfg_name[40];
|
||||
u16 hci_rev, lmp_subver;
|
||||
u8 hci_ver, lmp_ver, chip_type = 0;
|
||||
int ret;
|
||||
u16 opcode;
|
||||
u8 cmd[2];
|
||||
u8 reg_val[2];
|
||||
|
||||
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
|
||||
@ -1041,15 +1118,14 @@ next:
|
||||
btrtl_dev->drop_fw = false;
|
||||
|
||||
if (btrtl_dev->drop_fw) {
|
||||
opcode = hci_opcode_pack(0x3f, 0x66);
|
||||
cmd[0] = opcode & 0xff;
|
||||
cmd[1] = opcode >> 8;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
|
||||
skb = bt_skb_alloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (!skb)
|
||||
goto err_free;
|
||||
|
||||
skb_put_data(skb, cmd, sizeof(cmd));
|
||||
cmd = skb_put(skb, HCI_COMMAND_HDR_SIZE);
|
||||
cmd->opcode = cpu_to_le16(0xfc66);
|
||||
cmd->plen = 0;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
|
||||
|
||||
ret = hdev->send(hdev, skb);
|
||||
@ -1079,8 +1155,26 @@ next:
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
btrtl_dev->fw_len = rtl_load_file(hdev, btrtl_dev->ic_info->fw_name,
|
||||
&btrtl_dev->fw_data);
|
||||
if (!btrtl_dev->ic_info->fw_name) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
btrtl_dev->fw_len = -EIO;
|
||||
if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
|
||||
snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
|
||||
btrtl_dev->ic_info->fw_name);
|
||||
btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
|
||||
&btrtl_dev->fw_data);
|
||||
}
|
||||
|
||||
if (btrtl_dev->fw_len < 0) {
|
||||
snprintf(fw_name, sizeof(fw_name), "%s.bin",
|
||||
btrtl_dev->ic_info->fw_name);
|
||||
btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
|
||||
&btrtl_dev->fw_data);
|
||||
}
|
||||
|
||||
if (btrtl_dev->fw_len < 0) {
|
||||
rtl_dev_err(hdev, "firmware file %s not found",
|
||||
btrtl_dev->ic_info->fw_name);
|
||||
@ -1113,6 +1207,9 @@ next:
|
||||
if (btrtl_dev->ic_info->has_msft_ext)
|
||||
hci_set_msft_opcode(hdev, 0xFCF0);
|
||||
|
||||
if (btrtl_dev->ic_info)
|
||||
coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
|
||||
|
||||
return btrtl_dev;
|
||||
|
||||
err_free:
|
||||
@ -1125,6 +1222,8 @@ EXPORT_SYMBOL_GPL(btrtl_initialize);
|
||||
int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/* Match a set of subver values that correspond to stock firmware,
|
||||
* which is not compatible with standard btusb.
|
||||
* If matched, upload an alternative firmware that does conform to
|
||||
@ -1133,12 +1232,14 @@ int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
*/
|
||||
if (!btrtl_dev->ic_info) {
|
||||
rtl_dev_info(hdev, "assuming no firmware upload needed");
|
||||
return 0;
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch (btrtl_dev->ic_info->lmp_subver) {
|
||||
case RTL_ROM_LMP_8723A:
|
||||
return btrtl_setup_rtl8723a(hdev, btrtl_dev);
|
||||
err = btrtl_setup_rtl8723a(hdev, btrtl_dev);
|
||||
break;
|
||||
case RTL_ROM_LMP_8723B:
|
||||
case RTL_ROM_LMP_8821A:
|
||||
case RTL_ROM_LMP_8761A:
|
||||
@ -1146,11 +1247,18 @@ int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
case RTL_ROM_LMP_8852A:
|
||||
case RTL_ROM_LMP_8703B:
|
||||
case RTL_ROM_LMP_8851B:
|
||||
return btrtl_setup_rtl8723b(hdev, btrtl_dev);
|
||||
err = btrtl_setup_rtl8723b(hdev, btrtl_dev);
|
||||
break;
|
||||
default:
|
||||
rtl_dev_info(hdev, "assuming no firmware upload needed");
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
if (!err)
|
||||
err = btrtl_register_devcoredump_support(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_download_firmware);
|
||||
|
||||
@ -1180,6 +1288,10 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
|
||||
if (btrtl_dev->project_id == CHIP_ID_8852C)
|
||||
btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP);
|
||||
|
||||
if (btrtl_dev->project_id == CHIP_ID_8852A ||
|
||||
btrtl_dev->project_id == CHIP_ID_8852C)
|
||||
set_bit(HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER, &hdev->quirks);
|
||||
|
||||
hci_set_aosp_capable(hdev);
|
||||
break;
|
||||
default:
|
||||
@ -1398,4 +1510,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw_v2.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");
|
||||
|
@ -109,8 +109,16 @@ enum {
|
||||
__REALTEK_NUM_FLAGS,
|
||||
};
|
||||
|
||||
struct rtl_dump_info {
|
||||
const char *driver_name;
|
||||
char *controller;
|
||||
u32 fw_version;
|
||||
};
|
||||
|
||||
struct btrealtek_data {
|
||||
DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS);
|
||||
|
||||
struct rtl_dump_info rtl_dump;
|
||||
};
|
||||
|
||||
#define btrealtek_set_flag(hdev, nr) \
|
||||
@ -139,6 +147,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev,
|
||||
unsigned int *controller_baudrate,
|
||||
u32 *device_baudrate, bool *flow_control);
|
||||
void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
|
||||
|
||||
#else
|
||||
|
||||
@ -182,4 +191,8 @@ static inline int btrtl_get_uart_settings(struct hci_dev *hdev,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static inline void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -476,6 +476,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0033), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0035), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0036), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED |
|
||||
BTUSB_INTEL_NO_WBS_SUPPORT |
|
||||
@ -625,9 +626,24 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0489, 0xe0e4), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0f1), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0f5), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0f6), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe102), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x04ca, 0x3804), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* Additional Realtek 8723AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
|
||||
@ -860,10 +876,26 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct gpio_desc *reset_gpio = data->reset_gpio;
|
||||
struct btintel_data *intel_data = hci_get_priv(hdev);
|
||||
|
||||
if (++data->cmd_timeout_cnt < 5)
|
||||
return;
|
||||
|
||||
if (intel_data->acpi_reset_method) {
|
||||
if (test_and_set_bit(INTEL_ACPI_RESET_ACTIVE, intel_data->flags)) {
|
||||
bt_dev_err(hdev, "acpi: last reset failed ? Not resetting again");
|
||||
return;
|
||||
}
|
||||
|
||||
bt_dev_err(hdev, "Initiating acpi reset method");
|
||||
/* If ACPI reset method fails, lets try with legacy GPIO
|
||||
* toggling
|
||||
*/
|
||||
if (!intel_data->acpi_reset_method(hdev)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reset_gpio) {
|
||||
btusb_reset(hdev);
|
||||
return;
|
||||
@ -887,10 +919,49 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev)
|
||||
gpiod_set_value_cansleep(reset_gpio, 0);
|
||||
}
|
||||
|
||||
#define RTK_DEVCOREDUMP_CODE_MEMDUMP 0x01
|
||||
#define RTK_DEVCOREDUMP_CODE_HW_ERR 0x02
|
||||
#define RTK_DEVCOREDUMP_CODE_CMD_TIMEOUT 0x03
|
||||
|
||||
#define RTK_SUB_EVENT_CODE_COREDUMP 0x34
|
||||
|
||||
struct rtk_dev_coredump_hdr {
|
||||
u8 type;
|
||||
u8 code;
|
||||
u8 reserved[2];
|
||||
} __packed;
|
||||
|
||||
static inline void btusb_rtl_alloc_devcoredump(struct hci_dev *hdev,
|
||||
struct rtk_dev_coredump_hdr *hdr, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(len + sizeof(*hdr), GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_put_data(skb, hdr, sizeof(*hdr));
|
||||
if (len)
|
||||
skb_put_data(skb, buf, len);
|
||||
|
||||
if (!hci_devcd_init(hdev, skb->len)) {
|
||||
hci_devcd_append(hdev, skb);
|
||||
hci_devcd_complete(hdev);
|
||||
} else {
|
||||
bt_dev_err(hdev, "RTL: Failed to generate devcoredump");
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct gpio_desc *reset_gpio = data->reset_gpio;
|
||||
struct rtk_dev_coredump_hdr hdr = {
|
||||
.type = RTK_DEVCOREDUMP_CODE_CMD_TIMEOUT,
|
||||
};
|
||||
|
||||
btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
|
||||
|
||||
if (++data->cmd_timeout_cnt < 5)
|
||||
return;
|
||||
@ -917,6 +988,18 @@ static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
|
||||
gpiod_set_value_cansleep(reset_gpio, 0);
|
||||
}
|
||||
|
||||
static void btusb_rtl_hw_error(struct hci_dev *hdev, u8 code)
|
||||
{
|
||||
struct rtk_dev_coredump_hdr hdr = {
|
||||
.type = RTK_DEVCOREDUMP_CODE_HW_ERR,
|
||||
.code = code,
|
||||
};
|
||||
|
||||
bt_dev_err(hdev, "RTL: hw err, trigger devcoredump (%d)", code);
|
||||
|
||||
btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
|
||||
}
|
||||
|
||||
static void btusb_qca_cmd_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
@ -2409,79 +2492,6 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
|
||||
return btusb_recv_bulk(data, buffer, count);
|
||||
}
|
||||
|
||||
static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct intel_tlv *tlv = (void *)&skb->data[5];
|
||||
|
||||
/* The first event is always an event type TLV */
|
||||
if (tlv->type != INTEL_TLV_TYPE_ID)
|
||||
goto recv_frame;
|
||||
|
||||
switch (tlv->val[0]) {
|
||||
case INTEL_TLV_SYSTEM_EXCEPTION:
|
||||
case INTEL_TLV_FATAL_EXCEPTION:
|
||||
case INTEL_TLV_DEBUG_EXCEPTION:
|
||||
case INTEL_TLV_TEST_EXCEPTION:
|
||||
/* Generate devcoredump from exception */
|
||||
if (!hci_devcd_init(hdev, skb->len)) {
|
||||
hci_devcd_append(hdev, skb);
|
||||
hci_devcd_complete(hdev);
|
||||
} else {
|
||||
bt_dev_err(hdev, "Failed to generate devcoredump");
|
||||
kfree_skb(skb);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
|
||||
}
|
||||
|
||||
recv_frame:
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static int btusb_recv_event_intel(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) {
|
||||
const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
|
||||
unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
|
||||
|
||||
if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
|
||||
switch (skb->data[2]) {
|
||||
case 0x02:
|
||||
/* When switching to the operational firmware
|
||||
* the device sends a vendor specific event
|
||||
* indicating that the bootup completed.
|
||||
*/
|
||||
btintel_bootup(hdev, ptr, len);
|
||||
break;
|
||||
case 0x06:
|
||||
/* When the firmware loading completes the
|
||||
* device sends out a vendor specific event
|
||||
* indicating the result of the firmware
|
||||
* loading.
|
||||
*/
|
||||
btintel_secure_send_result(hdev, ptr, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle all diagnostics events separately. May still call
|
||||
* hci_recv_frame.
|
||||
*/
|
||||
if (len >= sizeof(diagnostics_hdr) &&
|
||||
memcmp(&skb->data[2], diagnostics_hdr,
|
||||
sizeof(diagnostics_hdr)) == 0) {
|
||||
return btusb_intel_diagnostics(hdev, skb);
|
||||
}
|
||||
}
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct urb *urb;
|
||||
@ -2562,6 +2572,25 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
|
||||
struct rtk_dev_coredump_hdr hdr = {
|
||||
.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
|
||||
};
|
||||
|
||||
bt_dev_dbg(hdev, "RTL: received coredump vendor evt, len %u",
|
||||
skb->len);
|
||||
|
||||
btusb_rtl_alloc_devcoredump(hdev, &hdr, skb->data, skb->len);
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
/* UHW CR mapping */
|
||||
#define MTK_BT_MISC 0x70002510
|
||||
#define MTK_BT_SUBSYS_RST 0x70002610
|
||||
@ -2571,8 +2600,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
|
||||
#define MTK_EP_RST_OPT 0x74011890
|
||||
#define MTK_EP_RST_IN_OUT_OPT 0x00010001
|
||||
#define MTK_BT_RST_DONE 0x00000100
|
||||
#define MTK_BT_RESET_WAIT_MS 100
|
||||
#define MTK_BT_RESET_NUM_TRIES 10
|
||||
#define MTK_BT_RESET_REG_CONNV3 0x70028610
|
||||
#define MTK_BT_READ_DEV_ID 0x70010200
|
||||
|
||||
|
||||
static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
{
|
||||
@ -2943,6 +2973,88 @@ static int btusb_mtk_id_get(struct btusb_data *data, u32 reg, u32 *id)
|
||||
return btusb_mtk_reg_read(data, reg, id);
|
||||
}
|
||||
|
||||
static u32 btusb_mtk_reset_done(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
u32 val = 0;
|
||||
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
|
||||
|
||||
return val & MTK_BT_RST_DONE;
|
||||
}
|
||||
|
||||
static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct btmediatek_data *mediatek;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
/* It's MediaTek specific bluetooth reset mechanism via USB */
|
||||
if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
|
||||
bt_dev_err(hdev, "last reset failed? Not resetting again");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
err = usb_autopm_get_interface(data->intf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
btusb_stop_traffic(data);
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
mediatek = hci_get_priv(hdev);
|
||||
|
||||
if (mediatek->dev_id == 0x7925) {
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
val |= (1 << 5);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
val &= 0xFFFF00FF;
|
||||
val |= (1 << 13);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, 0x00010001);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
val |= (1 << 0);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
|
||||
msleep(100);
|
||||
} else {
|
||||
/* It's Device EndPoint Reset Option Register */
|
||||
bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
|
||||
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
|
||||
|
||||
/* Reset the bluetooth chip via USB interface. */
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
|
||||
/* MT7921 need to delay 20ms between toggle reset bit */
|
||||
msleep(20);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
|
||||
}
|
||||
|
||||
err = readx_poll_timeout(btusb_mtk_reset_done, hdev, val,
|
||||
val & MTK_BT_RST_DONE, 20000, 1000000);
|
||||
if (err < 0)
|
||||
bt_dev_err(hdev, "Reset timeout");
|
||||
|
||||
btusb_mtk_id_get(data, 0x70010200, &val);
|
||||
if (!val)
|
||||
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
|
||||
|
||||
usb_queue_reset_device(data->intf);
|
||||
|
||||
clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
@ -2953,10 +3065,11 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
struct sk_buff *skb;
|
||||
const char *fwname;
|
||||
int err, status;
|
||||
u32 dev_id;
|
||||
u32 dev_id = 0;
|
||||
char fw_bin_name[64];
|
||||
u32 fw_version = 0;
|
||||
u8 param;
|
||||
struct btmediatek_data *mediatek;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
@ -2966,7 +3079,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!dev_id) {
|
||||
if (!dev_id || dev_id != 0x7663) {
|
||||
err = btusb_mtk_id_get(data, 0x70010200, &dev_id);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
@ -2979,6 +3092,14 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
mediatek = hci_get_priv(hdev);
|
||||
mediatek->dev_id = dev_id;
|
||||
mediatek->reset_sync = btusb_mtk_reset;
|
||||
|
||||
err = btmtk_register_coredump(hdev, btusb_driver.name, fw_version);
|
||||
if (err < 0)
|
||||
bt_dev_err(hdev, "Failed to register coredump (%d)", err);
|
||||
|
||||
switch (dev_id) {
|
||||
case 0x7663:
|
||||
fwname = FIRMWARE_MT7663;
|
||||
@ -2988,9 +3109,16 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
break;
|
||||
case 0x7922:
|
||||
case 0x7961:
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
case 0x7925:
|
||||
if (dev_id == 0x7925)
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
else
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
|
||||
err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
|
||||
btusb_mtk_hci_wmt_sync);
|
||||
if (err < 0) {
|
||||
@ -3128,67 +3256,11 @@ static int btusb_mtk_shutdown(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btusb_mtk_cmd_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
u32 val;
|
||||
int err, retry = 0;
|
||||
|
||||
/* It's MediaTek specific bluetooth reset mechanism via USB */
|
||||
if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
|
||||
bt_dev_err(hdev, "last reset failed? Not resetting again");
|
||||
return;
|
||||
}
|
||||
|
||||
err = usb_autopm_get_interface(data->intf);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
btusb_stop_traffic(data);
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
|
||||
/* It's Device EndPoint Reset Option Register */
|
||||
bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
|
||||
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
|
||||
|
||||
/* Reset the bluetooth chip via USB interface. */
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
|
||||
/* MT7921 need to delay 20ms between toggle reset bit */
|
||||
msleep(20);
|
||||
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
|
||||
|
||||
/* Poll the register until reset is completed */
|
||||
do {
|
||||
btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
|
||||
if (val & MTK_BT_RST_DONE) {
|
||||
bt_dev_dbg(hdev, "Bluetooth Reset Successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "Polling Bluetooth Reset CR");
|
||||
retry++;
|
||||
msleep(MTK_BT_RESET_WAIT_MS);
|
||||
} while (retry < MTK_BT_RESET_NUM_TRIES);
|
||||
|
||||
btusb_mtk_id_get(data, 0x70010200, &val);
|
||||
if (!val)
|
||||
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
|
||||
|
||||
usb_queue_reset_device(data->intf);
|
||||
|
||||
clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
|
||||
}
|
||||
|
||||
static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
|
||||
struct sk_buff *skb_cd;
|
||||
|
||||
switch (handle) {
|
||||
case 0xfc6f: /* Firmware dump from device */
|
||||
@ -3196,6 +3268,15 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
* suspend and thus disable auto-suspend.
|
||||
*/
|
||||
usb_disable_autosuspend(data->udev);
|
||||
|
||||
/* We need to forward the diagnostic packet to userspace daemon
|
||||
* for backward compatibility, so we have to clone the packet
|
||||
* extraly for the in-kernel coredump support.
|
||||
*/
|
||||
skb_cd = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb_cd)
|
||||
btmtk_process_coredump(hdev, skb_cd);
|
||||
|
||||
fallthrough;
|
||||
case 0x05ff: /* Firmware debug logging 1 */
|
||||
case 0x05fe: /* Firmware debug logging 2 */
|
||||
@ -4196,11 +4277,16 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
priv_size += sizeof(struct btintel_data);
|
||||
|
||||
/* Override the rx handlers */
|
||||
data->recv_event = btusb_recv_event_intel;
|
||||
data->recv_event = btintel_recv_event;
|
||||
data->recv_bulk = btusb_recv_bulk_intel;
|
||||
} else if (id->driver_info & BTUSB_REALTEK) {
|
||||
/* Allocate extra space for Realtek device */
|
||||
priv_size += sizeof(struct btrealtek_data);
|
||||
|
||||
data->recv_event = btusb_recv_event_realtek;
|
||||
} else if (id->driver_info & BTUSB_MEDIATEK) {
|
||||
/* Allocate extra space for Mediatek device */
|
||||
priv_size += sizeof(struct btmediatek_data);
|
||||
}
|
||||
|
||||
data->recv_acl = hci_recv_frame;
|
||||
@ -4307,7 +4393,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
hdev->setup = btusb_mtk_setup;
|
||||
hdev->shutdown = btusb_mtk_shutdown;
|
||||
hdev->manufacturer = 70;
|
||||
hdev->cmd_timeout = btusb_mtk_cmd_timeout;
|
||||
hdev->cmd_timeout = btmtk_reset_sync;
|
||||
hdev->set_bdaddr = btmtk_set_bdaddr;
|
||||
set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
@ -4364,9 +4450,11 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) &&
|
||||
(id->driver_info & BTUSB_REALTEK)) {
|
||||
btrtl_set_driver_name(hdev, btusb_driver.name);
|
||||
hdev->setup = btusb_setup_realtek;
|
||||
hdev->shutdown = btrtl_shutdown_realtek;
|
||||
hdev->cmd_timeout = btusb_rtl_cmd_timeout;
|
||||
hdev->hw_error = btusb_rtl_hw_error;
|
||||
|
||||
/* Realtek devices need to set remote wakeup on auto-suspend */
|
||||
set_bit(BTUSB_WAKEUP_AUTOSUSPEND, &data->flags);
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
@ -770,7 +770,8 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||
break;
|
||||
|
||||
case HCIUARTGETPROTO:
|
||||
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
|
||||
if (test_bit(HCI_UART_PROTO_SET, &hu->flags) &&
|
||||
test_bit(HCI_UART_PROTO_READY, &hu->flags))
|
||||
err = hu->proto->id;
|
||||
else
|
||||
err = -EUNATCH;
|
||||
|
@ -734,7 +734,11 @@ static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
clk_prepare_enable(sysclk);
|
||||
err = clk_prepare_enable(sysclk);
|
||||
if (err) {
|
||||
dev_err(dev, "could not enable sysclk: %d", err);
|
||||
return err;
|
||||
}
|
||||
btdev->sysclk_speed = clk_get_rate(sysclk);
|
||||
clk_disable_unprepare(sysclk);
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -117,9 +117,7 @@ enum qca_memdump_states {
|
||||
QCA_MEMDUMP_TIMEOUT,
|
||||
};
|
||||
|
||||
struct qca_memdump_data {
|
||||
char *memdump_buf_head;
|
||||
char *memdump_buf_tail;
|
||||
struct qca_memdump_info {
|
||||
u32 current_seq_no;
|
||||
u32 received_dump;
|
||||
u32 ram_dump_size;
|
||||
@ -160,13 +158,15 @@ struct qca_data {
|
||||
struct work_struct ws_tx_vote_off;
|
||||
struct work_struct ctrl_memdump_evt;
|
||||
struct delayed_work ctrl_memdump_timeout;
|
||||
struct qca_memdump_data *qca_memdump;
|
||||
struct qca_memdump_info *qca_memdump;
|
||||
unsigned long flags;
|
||||
struct completion drop_ev_comp;
|
||||
wait_queue_head_t suspend_wait_q;
|
||||
enum qca_memdump_states memdump_state;
|
||||
struct mutex hci_memdump_lock;
|
||||
|
||||
u16 fw_version;
|
||||
u16 controller_id;
|
||||
/* For debugging purpose */
|
||||
u64 ibs_sent_wacks;
|
||||
u64 ibs_sent_slps;
|
||||
@ -233,6 +233,7 @@ static void qca_regulator_disable(struct qca_serdev *qcadev);
|
||||
static void qca_power_shutdown(struct hci_uart *hu);
|
||||
static int qca_power_off(struct hci_dev *hdev);
|
||||
static void qca_controller_memdump(struct work_struct *work);
|
||||
static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
|
||||
static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu)
|
||||
{
|
||||
@ -980,6 +981,28 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
char buf[80];
|
||||
|
||||
snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n",
|
||||
qca->controller_id);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n",
|
||||
qca->fw_version);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Vendor:Qualcomm\n");
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Driver: %s\n",
|
||||
hu->serdev->dev.driver->name);
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static void qca_controller_memdump(struct work_struct *work)
|
||||
{
|
||||
struct qca_data *qca = container_of(work, struct qca_data,
|
||||
@ -987,13 +1010,11 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
struct hci_uart *hu = qca->hu;
|
||||
struct sk_buff *skb;
|
||||
struct qca_memdump_event_hdr *cmd_hdr;
|
||||
struct qca_memdump_data *qca_memdump = qca->qca_memdump;
|
||||
struct qca_memdump_info *qca_memdump = qca->qca_memdump;
|
||||
struct qca_dump_size *dump;
|
||||
char *memdump_buf;
|
||||
char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
|
||||
u16 seq_no;
|
||||
u32 dump_size;
|
||||
u32 rx_size;
|
||||
int ret = 0;
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
|
||||
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
|
||||
@ -1009,7 +1030,7 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
}
|
||||
|
||||
if (!qca_memdump) {
|
||||
qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
|
||||
qca_memdump = kzalloc(sizeof(struct qca_memdump_info),
|
||||
GFP_ATOMIC);
|
||||
if (!qca_memdump) {
|
||||
mutex_unlock(&qca->hci_memdump_lock);
|
||||
@ -1035,44 +1056,49 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
set_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
dump = (void *) skb->data;
|
||||
dump_size = __le32_to_cpu(dump->dump_size);
|
||||
if (!(dump_size)) {
|
||||
qca_memdump->ram_dump_size = __le32_to_cpu(dump->dump_size);
|
||||
if (!(qca_memdump->ram_dump_size)) {
|
||||
bt_dev_err(hu->hdev, "Rx invalid memdump size");
|
||||
kfree(qca_memdump);
|
||||
kfree_skb(skb);
|
||||
mutex_unlock(&qca->hci_memdump_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_delayed_work(qca->workqueue,
|
||||
&qca->ctrl_memdump_timeout,
|
||||
msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
|
||||
skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
|
||||
qca_memdump->current_seq_no = 0;
|
||||
qca_memdump->received_dump = 0;
|
||||
ret = hci_devcd_init(hu->hdev, qca_memdump->ram_dump_size);
|
||||
bt_dev_info(hu->hdev, "hci_devcd_init Return:%d",
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
kfree(qca->qca_memdump);
|
||||
qca->qca_memdump = NULL;
|
||||
qca->memdump_state = QCA_MEMDUMP_COLLECTED;
|
||||
cancel_delayed_work(&qca->ctrl_memdump_timeout);
|
||||
clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
mutex_unlock(&qca->hci_memdump_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
|
||||
dump_size);
|
||||
queue_delayed_work(qca->workqueue,
|
||||
&qca->ctrl_memdump_timeout,
|
||||
msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
|
||||
);
|
||||
qca_memdump->ram_dump_size);
|
||||
|
||||
skb_pull(skb, sizeof(dump_size));
|
||||
memdump_buf = vmalloc(dump_size);
|
||||
qca_memdump->ram_dump_size = dump_size;
|
||||
qca_memdump->memdump_buf_head = memdump_buf;
|
||||
qca_memdump->memdump_buf_tail = memdump_buf;
|
||||
}
|
||||
|
||||
memdump_buf = qca_memdump->memdump_buf_tail;
|
||||
|
||||
/* If sequence no 0 is missed then there is no point in
|
||||
* accepting the other sequences.
|
||||
*/
|
||||
if (!memdump_buf) {
|
||||
if (!test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
|
||||
bt_dev_err(hu->hdev, "QCA: Discarding other packets");
|
||||
kfree(qca_memdump);
|
||||
kfree_skb(skb);
|
||||
qca->qca_memdump = NULL;
|
||||
mutex_unlock(&qca->hci_memdump_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* There could be chance of missing some packets from
|
||||
* the controller. In such cases let us store the dummy
|
||||
* packets in the buffer.
|
||||
@ -1082,8 +1108,8 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
* bits, so skip this checking for missing packet.
|
||||
*/
|
||||
while ((seq_no > qca_memdump->current_seq_no + 1) &&
|
||||
(soc_type != QCA_QCA6390) &&
|
||||
seq_no != QCA_LAST_SEQUENCE_NUM) {
|
||||
(soc_type != QCA_QCA6390) &&
|
||||
seq_no != QCA_LAST_SEQUENCE_NUM) {
|
||||
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
|
||||
qca_memdump->current_seq_no);
|
||||
rx_size = qca_memdump->received_dump;
|
||||
@ -1094,43 +1120,38 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
qca_memdump->received_dump);
|
||||
break;
|
||||
}
|
||||
memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
|
||||
memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
|
||||
hci_devcd_append_pattern(hu->hdev, 0x00,
|
||||
QCA_DUMP_PACKET_SIZE);
|
||||
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
|
||||
qca_memdump->current_seq_no++;
|
||||
}
|
||||
|
||||
rx_size = qca_memdump->received_dump + skb->len;
|
||||
rx_size = qca_memdump->received_dump + skb->len;
|
||||
if (rx_size <= qca_memdump->ram_dump_size) {
|
||||
if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
|
||||
(seq_no != qca_memdump->current_seq_no))
|
||||
(seq_no != qca_memdump->current_seq_no)) {
|
||||
bt_dev_err(hu->hdev,
|
||||
"QCA memdump unexpected packet %d",
|
||||
seq_no);
|
||||
}
|
||||
bt_dev_dbg(hu->hdev,
|
||||
"QCA memdump packet %d with length %d",
|
||||
seq_no, skb->len);
|
||||
memcpy(memdump_buf, (unsigned char *)skb->data,
|
||||
skb->len);
|
||||
memdump_buf = memdump_buf + skb->len;
|
||||
qca_memdump->memdump_buf_tail = memdump_buf;
|
||||
qca_memdump->current_seq_no = seq_no + 1;
|
||||
qca_memdump->received_dump += skb->len;
|
||||
hci_devcd_append(hu->hdev, skb);
|
||||
qca_memdump->current_seq_no += 1;
|
||||
qca_memdump->received_dump = rx_size;
|
||||
} else {
|
||||
bt_dev_err(hu->hdev,
|
||||
"QCA memdump received %d, no space for packet %d",
|
||||
qca_memdump->received_dump, seq_no);
|
||||
"QCA memdump received no space for packet %d",
|
||||
qca_memdump->current_seq_no);
|
||||
}
|
||||
qca->qca_memdump = qca_memdump;
|
||||
kfree_skb(skb);
|
||||
|
||||
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
|
||||
bt_dev_info(hu->hdev,
|
||||
"QCA memdump Done, received %d, total %d",
|
||||
qca_memdump->received_dump,
|
||||
qca_memdump->ram_dump_size);
|
||||
memdump_buf = qca_memdump->memdump_buf_head;
|
||||
dev_coredumpv(&hu->serdev->dev, memdump_buf,
|
||||
qca_memdump->received_dump, GFP_KERNEL);
|
||||
"QCA memdump Done, received %d, total %d",
|
||||
qca_memdump->received_dump,
|
||||
qca_memdump->ram_dump_size);
|
||||
hci_devcd_complete(hu->hdev);
|
||||
cancel_delayed_work(&qca->ctrl_memdump_timeout);
|
||||
kfree(qca->qca_memdump);
|
||||
qca->qca_memdump = NULL;
|
||||
@ -1541,8 +1562,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
|
||||
mutex_lock(&qca->hci_memdump_lock);
|
||||
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
|
||||
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
|
||||
hci_devcd_abort(hu->hdev);
|
||||
if (qca->qca_memdump) {
|
||||
vfree(qca->qca_memdump->memdump_buf_head);
|
||||
kfree(qca->qca_memdump);
|
||||
qca->qca_memdump = NULL;
|
||||
}
|
||||
@ -1706,6 +1727,17 @@ static int qca_power_on(struct hci_dev *hdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hci_coredump_qca(struct hci_dev *hdev)
|
||||
{
|
||||
static const u8 param[] = { 0x26 };
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc0c, 1, param, HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR(skb))
|
||||
bt_dev_err(hdev, "%s: trigger crash failed (%ld)", __func__, PTR_ERR(skb));
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int qca_setup(struct hci_uart *hu)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
@ -1820,6 +1852,9 @@ out:
|
||||
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
|
||||
else
|
||||
hu->hdev->set_bdaddr = qca_set_bdaddr;
|
||||
qca->fw_version = le16_to_cpu(ver.patch_ver);
|
||||
qca->controller_id = le16_to_cpu(ver.rom_ver);
|
||||
hci_devcd_register(hdev, hci_coredump_qca, qca_dmp_hdr, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1839,6 +1874,17 @@ static const struct hci_uart_proto qca_proto = {
|
||||
.dequeue = qca_dequeue,
|
||||
};
|
||||
|
||||
static const struct qca_device_data qca_soc_data_wcn3988 __maybe_unused = {
|
||||
.soc_type = QCA_WCN3988,
|
||||
.vregs = (struct qca_vreg []) {
|
||||
{ "vddio", 15000 },
|
||||
{ "vddxo", 80000 },
|
||||
{ "vddrf", 300000 },
|
||||
{ "vddch0", 450000 },
|
||||
},
|
||||
.num_vregs = 4,
|
||||
};
|
||||
|
||||
static const struct qca_device_data qca_soc_data_wcn3990 __maybe_unused = {
|
||||
.soc_type = QCA_WCN3990,
|
||||
.vregs = (struct qca_vreg []) {
|
||||
@ -2363,6 +2409,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = {
|
||||
{ .compatible = "qcom,qca6174-bt" },
|
||||
{ .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
|
||||
{ .compatible = "qcom,qca9377-bt" },
|
||||
{ .compatible = "qcom,wcn3988-bt", .data = &qca_soc_data_wcn3988},
|
||||
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
|
||||
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
|
||||
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
|
||||
@ -2384,6 +2431,18 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEV_COREDUMP
|
||||
static void hciqca_coredump(struct device *dev)
|
||||
{
|
||||
struct serdev_device *serdev = to_serdev_device(dev);
|
||||
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
|
||||
struct hci_uart *hu = &qcadev->serdev_hu;
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
|
||||
if (hdev->dump.coredump)
|
||||
hdev->dump.coredump(hdev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver qca_serdev_driver = {
|
||||
.probe = qca_serdev_probe,
|
||||
@ -2394,6 +2453,9 @@ static struct serdev_device_driver qca_serdev_driver = {
|
||||
.acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
|
||||
.shutdown = qca_serdev_shutdown,
|
||||
.pm = &qca_pm_ops,
|
||||
#ifdef CONFIG_DEV_COREDUMP
|
||||
.coredump = hciqca_coredump,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -386,6 +386,7 @@ struct bt_sock {
|
||||
enum {
|
||||
BT_SK_DEFER_SETUP,
|
||||
BT_SK_SUSPEND,
|
||||
BT_SK_PKT_STATUS
|
||||
};
|
||||
|
||||
struct bt_sock_list {
|
||||
@ -400,6 +401,8 @@ int bt_sock_register(int proto, const struct net_proto_family *ops);
|
||||
void bt_sock_unregister(int proto);
|
||||
void bt_sock_link(struct bt_sock_list *l, struct sock *s);
|
||||
void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
|
||||
struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
|
||||
struct proto *prot, int proto, gfp_t prio, int kern);
|
||||
int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
||||
int flags);
|
||||
int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
@ -430,10 +433,6 @@ struct l2cap_ctrl {
|
||||
struct l2cap_chan *chan;
|
||||
};
|
||||
|
||||
struct sco_ctrl {
|
||||
u8 pkt_status;
|
||||
};
|
||||
|
||||
struct hci_dev;
|
||||
|
||||
typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
|
||||
@ -464,16 +463,18 @@ struct bt_skb_cb {
|
||||
u8 force_active;
|
||||
u16 expect;
|
||||
u8 incoming:1;
|
||||
u8 pkt_status:2;
|
||||
union {
|
||||
struct l2cap_ctrl l2cap;
|
||||
struct sco_ctrl sco;
|
||||
struct hci_ctrl hci;
|
||||
struct mgmt_ctrl mgmt;
|
||||
struct scm_creds creds;
|
||||
};
|
||||
};
|
||||
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
|
||||
|
||||
#define hci_skb_pkt_type(skb) bt_cb((skb))->pkt_type
|
||||
#define hci_skb_pkt_status(skb) bt_cb((skb))->pkt_status
|
||||
#define hci_skb_expect(skb) bt_cb((skb))->expect
|
||||
#define hci_skb_opcode(skb) bt_cb((skb))->hci.opcode
|
||||
#define hci_skb_event(skb) bt_cb((skb))->hci.req_event
|
||||
|
@ -309,6 +309,16 @@ enum {
|
||||
* to support it.
|
||||
*/
|
||||
HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT,
|
||||
|
||||
/* When this quirk is set, MSFT extension monitor tracking by
|
||||
* address filter is supported. Since tracking quantity of each
|
||||
* pattern is limited, this feature supports tracking multiple
|
||||
* devices concurrently if controller supports multiple
|
||||
* address filters.
|
||||
*
|
||||
* This quirk must be set before hci_register_dev is called.
|
||||
*/
|
||||
HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER,
|
||||
};
|
||||
|
||||
/* HCI device flags */
|
||||
@ -577,6 +587,7 @@ enum {
|
||||
#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
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
|
@ -321,8 +321,8 @@ struct adv_monitor {
|
||||
|
||||
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
||||
|
||||
#define HCI_CONN_HANDLE_UNSET 0xffff
|
||||
#define HCI_CONN_HANDLE_MAX 0x0eff
|
||||
#define HCI_CONN_HANDLE_UNSET(_handle) (_handle > HCI_CONN_HANDLE_MAX)
|
||||
|
||||
/* Min encryption key size to match with SMP */
|
||||
#define HCI_MIN_ENC_KEY_SIZE 7
|
||||
@ -739,6 +739,7 @@ struct hci_conn {
|
||||
unsigned long flags;
|
||||
|
||||
enum conn_reasons conn_reason;
|
||||
__u8 abort_reason;
|
||||
|
||||
__u32 clock;
|
||||
__u16 clock_accuracy;
|
||||
@ -758,7 +759,6 @@ struct hci_conn {
|
||||
struct delayed_work auto_accept_work;
|
||||
struct delayed_work idle_work;
|
||||
struct delayed_work le_conn_timeout;
|
||||
struct work_struct le_scan_cleanup;
|
||||
|
||||
struct device dev;
|
||||
struct dentry *debugfs;
|
||||
@ -974,6 +974,10 @@ enum {
|
||||
HCI_CONN_SCANNING,
|
||||
HCI_CONN_AUTH_FAILURE,
|
||||
HCI_CONN_PER_ADV,
|
||||
HCI_CONN_BIG_CREATED,
|
||||
HCI_CONN_CREATE_CIS,
|
||||
HCI_CONN_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC_FAILED,
|
||||
};
|
||||
|
||||
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
|
||||
@ -1093,8 +1097,7 @@ static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
|
||||
bdaddr_t *ba,
|
||||
__u8 big, __u8 bis)
|
||||
bdaddr_t *ba, __u8 bis)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
@ -1105,7 +1108,33 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
|
||||
if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
|
||||
continue;
|
||||
|
||||
if (c->iso_qos.bcast.big == big && c->iso_qos.bcast.bis == bis) {
|
||||
if (c->iso_qos.bcast.bis == bis) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *
|
||||
hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
|
||||
bdaddr_t *ba,
|
||||
__u8 big, __u8 bis)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (bacmp(&c->dst, ba) || c->type != ISO_LINK ||
|
||||
!test_bit(HCI_CONN_PER_ADV, &c->flags))
|
||||
continue;
|
||||
|
||||
if (c->iso_qos.bcast.big == big &&
|
||||
c->iso_qos.bcast.bis == bis) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
@ -1190,7 +1219,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type != ISO_LINK)
|
||||
if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
|
||||
continue;
|
||||
|
||||
/* Match CIG ID if set */
|
||||
@ -1222,7 +1251,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type != ISO_LINK)
|
||||
if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
|
||||
continue;
|
||||
|
||||
if (handle == c->iso_qos.ucast.cig) {
|
||||
@ -1259,6 +1288,29 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *hdev,
|
||||
__u8 handle)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type != ISO_LINK)
|
||||
continue;
|
||||
|
||||
if (handle == c->iso_qos.bcast.big) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
|
||||
__u8 type, __u16 state)
|
||||
{
|
||||
@ -1320,11 +1372,33 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns true if an le connection is in the scanning state */
|
||||
static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == LE_LINK && c->state == BT_CONNECT &&
|
||||
test_bit(HCI_CONN_SCANNING, &c->flags)) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int hci_disconnect(struct hci_conn *conn, __u8 reason);
|
||||
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status);
|
||||
bool hci_iso_setup_path(struct hci_conn *conn);
|
||||
int hci_le_create_cis(struct hci_conn *conn);
|
||||
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);
|
||||
@ -1351,6 +1425,9 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u16 setting, struct bt_codec *codec);
|
||||
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base);
|
||||
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
@ -1369,6 +1446,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
|
||||
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
|
||||
|
||||
void hci_conn_failed(struct hci_conn *conn, u8 status);
|
||||
u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle);
|
||||
|
||||
/*
|
||||
* hci_conn_get() and hci_conn_put() are used to control the life-time of an
|
||||
@ -1745,6 +1823,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
/* Extended advertising support */
|
||||
#define ext_adv_capable(dev) (((dev)->le_features[1] & HCI_LE_EXT_ADV))
|
||||
|
||||
/* Maximum advertising length */
|
||||
#define max_adv_len(dev) \
|
||||
(ext_adv_capable(dev) ? HCI_MAX_EXT_AD_LENGTH : HCI_MAX_AD_LENGTH)
|
||||
|
||||
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E page 1789:
|
||||
*
|
||||
* C24: Mandatory if the LE Controller supports Connection State and either
|
||||
@ -1765,6 +1847,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
#define cis_peripheral_capable(dev) \
|
||||
((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL)
|
||||
#define bis_capable(dev) ((dev)->le_features[3] & HCI_LE_ISO_BROADCASTER)
|
||||
#define sync_recv_capable(dev) ((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER)
|
||||
|
||||
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
|
||||
(!test_bit(HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, &(dev)->quirks)))
|
||||
|
@ -5,6 +5,9 @@
|
||||
* Copyright (C) 2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#define UINT_PTR(_handle) ((void *)((uintptr_t)_handle))
|
||||
#define PTR_UINT(_ptr) ((uintptr_t)((void *)_ptr))
|
||||
|
||||
typedef int (*hci_cmd_sync_work_func_t)(struct hci_dev *hdev, void *data);
|
||||
typedef void (*hci_cmd_sync_work_destroy_t)(struct hci_dev *hdev, void *data,
|
||||
int err);
|
||||
@ -124,7 +127,7 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
|
||||
|
||||
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
int hci_le_create_cis_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_le_create_cis_sync(struct hci_dev *hdev);
|
||||
|
||||
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
|
||||
|
||||
|
@ -111,6 +111,8 @@ struct mgmt_rp_read_index_list {
|
||||
#define MGMT_SETTING_WIDEBAND_SPEECH BIT(17)
|
||||
#define MGMT_SETTING_CIS_CENTRAL BIT(18)
|
||||
#define MGMT_SETTING_CIS_PERIPHERAL BIT(19)
|
||||
#define MGMT_SETTING_ISO_BROADCASTER BIT(20)
|
||||
#define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21)
|
||||
|
||||
#define MGMT_OP_READ_INFO 0x0004
|
||||
#define MGMT_READ_INFO_SIZE 0
|
||||
|
@ -46,6 +46,4 @@ struct sco_conninfo {
|
||||
__u8 dev_class[3];
|
||||
};
|
||||
|
||||
#define SCO_CMSG_PKT_STATUS 0x01
|
||||
|
||||
#endif /* __SCO_H */
|
||||
|
@ -140,6 +140,35 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto,
|
||||
return err;
|
||||
}
|
||||
|
||||
struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
|
||||
struct proto *prot, int proto, gfp_t prio, int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, prot, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
/* Init peer information so it can be properly monitored */
|
||||
if (!kern) {
|
||||
spin_lock(&sk->sk_peer_lock);
|
||||
sk->sk_peer_pid = get_pid(task_tgid(current));
|
||||
sk->sk_peer_cred = get_current_cred();
|
||||
spin_unlock(&sk->sk_peer_lock);
|
||||
}
|
||||
|
||||
return sk;
|
||||
}
|
||||
EXPORT_SYMBOL(bt_sock_alloc);
|
||||
|
||||
void bt_sock_link(struct bt_sock_list *l, struct sock *sk)
|
||||
{
|
||||
write_lock(&l->lock);
|
||||
@ -158,6 +187,9 @@ EXPORT_SYMBOL(bt_sock_unlink);
|
||||
|
||||
void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
|
||||
{
|
||||
const struct cred *old_cred;
|
||||
struct pid *old_pid;
|
||||
|
||||
BT_DBG("parent %p, sk %p", parent, sk);
|
||||
|
||||
sock_hold(sk);
|
||||
@ -170,6 +202,19 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
|
||||
list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q);
|
||||
bt_sk(sk)->parent = parent;
|
||||
|
||||
/* Copy credentials from parent since for incoming connections the
|
||||
* socket is allocated by the kernel.
|
||||
*/
|
||||
spin_lock(&sk->sk_peer_lock);
|
||||
old_pid = sk->sk_peer_pid;
|
||||
old_cred = sk->sk_peer_cred;
|
||||
sk->sk_peer_pid = get_pid(parent->sk_peer_pid);
|
||||
sk->sk_peer_cred = get_cred(parent->sk_peer_cred);
|
||||
spin_unlock(&sk->sk_peer_lock);
|
||||
|
||||
put_pid(old_pid);
|
||||
put_cred(old_cred);
|
||||
|
||||
if (bh)
|
||||
bh_unlock_sock(sk);
|
||||
else
|
||||
@ -288,8 +333,12 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
||||
bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
|
||||
&msg->msg_namelen);
|
||||
|
||||
if (bt_sk(sk)->skb_put_cmsg)
|
||||
bt_sk(sk)->skb_put_cmsg(skb, msg, sk);
|
||||
if (test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags)) {
|
||||
u8 pkt_status = hci_skb_pkt_status(skb);
|
||||
|
||||
put_cmsg(msg, SOL_BLUETOOTH, BT_SCM_PKT_STATUS,
|
||||
sizeof(pkt_status), &pkt_status);
|
||||
}
|
||||
}
|
||||
|
||||
skb_free_datagram(sk, skb);
|
||||
|
@ -28,7 +28,6 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
|
||||
int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
|
||||
|
||||
void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
|
||||
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
|
||||
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
|
||||
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
|
@ -205,21 +205,13 @@ static int bnep_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &bnep_proto, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
sock->ops = &bnep_sock_ops;
|
||||
|
||||
sock->state = SS_UNCONNECTED;
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = protocol;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
bt_sock_link(&bnep_sk_list, sk);
|
||||
return 0;
|
||||
}
|
||||
|
@ -100,8 +100,7 @@ void hci_devcd_reset(struct hci_dev *hdev)
|
||||
/* Call with hci_dev_lock only. */
|
||||
static void hci_devcd_free(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->dump.head)
|
||||
vfree(hdev->dump.head);
|
||||
vfree(hdev->dump.head);
|
||||
|
||||
hci_devcd_reset(hdev);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1074,9 +1074,9 @@ void hci_uuids_clear(struct hci_dev *hdev)
|
||||
|
||||
void hci_link_keys_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct link_key *key;
|
||||
struct link_key *key, *tmp;
|
||||
|
||||
list_for_each_entry(key, &hdev->link_keys, list) {
|
||||
list_for_each_entry_safe(key, tmp, &hdev->link_keys, list) {
|
||||
list_del_rcu(&key->list);
|
||||
kfree_rcu(key, rcu);
|
||||
}
|
||||
@ -1084,9 +1084,9 @@ void hci_link_keys_clear(struct hci_dev *hdev)
|
||||
|
||||
void hci_smp_ltks_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct smp_ltk *k;
|
||||
struct smp_ltk *k, *tmp;
|
||||
|
||||
list_for_each_entry(k, &hdev->long_term_keys, list) {
|
||||
list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
|
||||
list_del_rcu(&k->list);
|
||||
kfree_rcu(k, rcu);
|
||||
}
|
||||
@ -1094,9 +1094,9 @@ void hci_smp_ltks_clear(struct hci_dev *hdev)
|
||||
|
||||
void hci_smp_irks_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct smp_irk *k;
|
||||
struct smp_irk *k, *tmp;
|
||||
|
||||
list_for_each_entry(k, &hdev->identity_resolving_keys, list) {
|
||||
list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
|
||||
list_del_rcu(&k->list);
|
||||
kfree_rcu(k, rcu);
|
||||
}
|
||||
@ -1104,9 +1104,9 @@ void hci_smp_irks_clear(struct hci_dev *hdev)
|
||||
|
||||
void hci_blocked_keys_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct blocked_key *b;
|
||||
struct blocked_key *b, *tmp;
|
||||
|
||||
list_for_each_entry(b, &hdev->blocked_keys, list) {
|
||||
list_for_each_entry_safe(b, tmp, &hdev->blocked_keys, list) {
|
||||
list_del_rcu(&b->list);
|
||||
kfree_rcu(b, rcu);
|
||||
}
|
||||
@ -1949,15 +1949,15 @@ int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
|
||||
|
||||
switch (hci_get_adv_monitor_offload_ext(hdev)) {
|
||||
case HCI_ADV_MONITOR_EXT_NONE:
|
||||
bt_dev_dbg(hdev, "%s add monitor %d status %d", hdev->name,
|
||||
bt_dev_dbg(hdev, "add monitor %d status %d",
|
||||
monitor->handle, status);
|
||||
/* Message was not forwarded to controller - not an error */
|
||||
break;
|
||||
|
||||
case HCI_ADV_MONITOR_EXT_MSFT:
|
||||
status = msft_add_monitor_pattern(hdev, monitor);
|
||||
bt_dev_dbg(hdev, "%s add monitor %d msft status %d", hdev->name,
|
||||
monitor->handle, status);
|
||||
bt_dev_dbg(hdev, "add monitor %d msft status %d",
|
||||
handle, status);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1976,15 +1976,15 @@ static int hci_remove_adv_monitor(struct hci_dev *hdev,
|
||||
|
||||
switch (hci_get_adv_monitor_offload_ext(hdev)) {
|
||||
case HCI_ADV_MONITOR_EXT_NONE: /* also goes here when powered off */
|
||||
bt_dev_dbg(hdev, "%s remove monitor %d status %d", hdev->name,
|
||||
bt_dev_dbg(hdev, "remove monitor %d status %d",
|
||||
monitor->handle, status);
|
||||
goto free_monitor;
|
||||
|
||||
case HCI_ADV_MONITOR_EXT_MSFT:
|
||||
handle = monitor->handle;
|
||||
status = msft_remove_monitor(hdev, monitor);
|
||||
bt_dev_dbg(hdev, "%s remove monitor %d msft status %d",
|
||||
hdev->name, handle, status);
|
||||
bt_dev_dbg(hdev, "remove monitor %d msft status %d",
|
||||
handle, status);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2436,6 +2436,9 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
|
||||
if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/* To avoid a potential race with hci_unregister_dev. */
|
||||
hci_dev_hold(hdev);
|
||||
|
||||
if (action == PM_SUSPEND_PREPARE)
|
||||
ret = hci_suspend_dev(hdev);
|
||||
else if (action == PM_POST_SUSPEND)
|
||||
@ -2445,6 +2448,7 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
|
||||
bt_dev_err(hdev, "Suspend notifier action (%lu) failed: %d",
|
||||
action, ret);
|
||||
|
||||
hci_dev_put(hdev);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
@ -3891,7 +3895,7 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
if (conn) {
|
||||
/* Send to upper protocol */
|
||||
bt_cb(skb)->sco.pkt_status = flags & 0x03;
|
||||
hci_skb_pkt_status(skb) = flags & 0x03;
|
||||
sco_recv_scodata(conn, skb);
|
||||
return;
|
||||
} else {
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/kstrtox.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -1152,7 +1153,7 @@ static ssize_t force_no_mitm_write(struct file *file,
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
if (strtobool(buf, &enable))
|
||||
if (kstrtobool(buf, &enable))
|
||||
return -EINVAL;
|
||||
|
||||
if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM))
|
||||
|
@ -1639,7 +1639,7 @@ static u8 hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, void *data,
|
||||
|
||||
hci_dev_set_flag(hdev, HCI_LE_ADV);
|
||||
|
||||
if (adv)
|
||||
if (adv && !adv->periodic)
|
||||
adv->enabled = true;
|
||||
|
||||
conn = hci_lookup_le_connect(hdev);
|
||||
@ -1747,7 +1747,7 @@ static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
{
|
||||
struct discovery_state *d = &hdev->discovery;
|
||||
|
||||
if (len > HCI_MAX_AD_LENGTH)
|
||||
if (len > max_adv_len(hdev))
|
||||
return;
|
||||
|
||||
bacpy(&d->last_adv_addr, bdaddr);
|
||||
@ -3173,19 +3173,15 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
|
||||
* As the connection handle is set here for the first time, it indicates
|
||||
* whether the connection is already set up.
|
||||
*/
|
||||
if (conn->handle != HCI_CONN_HANDLE_UNSET) {
|
||||
if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
|
||||
bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for existing connection");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
if (conn->handle > HCI_CONN_HANDLE_MAX) {
|
||||
bt_dev_err(hdev, "Invalid handle: 0x%4.4x > 0x%4.4x",
|
||||
conn->handle, HCI_CONN_HANDLE_MAX);
|
||||
status = HCI_ERROR_INVALID_PARAMETERS;
|
||||
status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
|
||||
if (status)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (conn->type == ACL_LINK) {
|
||||
conn->state = BT_CONFIG;
|
||||
@ -3803,6 +3799,22 @@ static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)
|
||||
{
|
||||
struct hci_conn *conn, *tmp;
|
||||
|
||||
lockdep_assert_held(&hdev->lock);
|
||||
|
||||
list_for_each_entry_safe(conn, tmp, &hdev->conn_hash.list, list) {
|
||||
if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY) ||
|
||||
conn->state == BT_OPEN || conn->iso_qos.ucast.cig != cig)
|
||||
continue;
|
||||
|
||||
if (HCI_CONN_HANDLE_UNSET(conn->handle))
|
||||
hci_conn_failed(conn, status);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@ -3810,6 +3822,7 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||
struct hci_cp_le_set_cig_params *cp;
|
||||
struct hci_conn *conn;
|
||||
u8 status = rp->status;
|
||||
bool pending = false;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
@ -3823,12 +3836,15 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E page 2554
|
||||
*
|
||||
* If the Status return parameter is non-zero, then the state of the CIG
|
||||
* and its CIS configurations shall not be changed by the command. If
|
||||
* the CIG did not already exist, it shall not be created.
|
||||
*/
|
||||
if (status) {
|
||||
while ((conn = hci_conn_hash_lookup_cig(hdev, rp->cig_id))) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
/* Keep current configuration, fail only the unbound CIS */
|
||||
hci_unbound_cis_failed(hdev, rp->cig_id, status);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@ -3848,17 +3864,17 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||
if (conn->state != BT_BOUND && conn->state != BT_CONNECT)
|
||||
continue;
|
||||
|
||||
conn->handle = __le16_to_cpu(rp->handle[i]);
|
||||
if (hci_conn_set_handle(conn, __le16_to_cpu(rp->handle[i])))
|
||||
continue;
|
||||
|
||||
bt_dev_dbg(hdev, "%p handle 0x%4.4x parent %p", conn,
|
||||
conn->handle, conn->parent);
|
||||
|
||||
/* Create CIS if LE is already connected */
|
||||
if (conn->parent && conn->parent->state == BT_CONNECTED)
|
||||
hci_le_create_cis(conn);
|
||||
if (conn->state == BT_CONNECT)
|
||||
pending = true;
|
||||
}
|
||||
|
||||
unlock:
|
||||
if (pending)
|
||||
hci_le_create_cis_pending(hdev);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return rp->status;
|
||||
@ -3938,24 +3954,47 @@ static u8 hci_cc_le_set_per_adv_enable(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_status *rp = data;
|
||||
__u8 *sent;
|
||||
struct hci_cp_le_set_per_adv_enable *cp;
|
||||
struct adv_info *adv = NULL, *n;
|
||||
u8 per_adv_cnt = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return rp->status;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE);
|
||||
if (!sent)
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE);
|
||||
if (!cp)
|
||||
return rp->status;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (*sent)
|
||||
hci_dev_set_flag(hdev, HCI_LE_PER_ADV);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_LE_PER_ADV);
|
||||
adv = hci_find_adv_instance(hdev, cp->handle);
|
||||
|
||||
if (cp->enable) {
|
||||
hci_dev_set_flag(hdev, HCI_LE_PER_ADV);
|
||||
|
||||
if (adv)
|
||||
adv->enabled = true;
|
||||
} else {
|
||||
/* If just one instance was disabled check if there are
|
||||
* any other instance enabled before clearing HCI_LE_PER_ADV.
|
||||
* The current periodic adv instance will be marked as
|
||||
* disabled once extended advertising is also disabled.
|
||||
*/
|
||||
list_for_each_entry_safe(adv, n, &hdev->adv_instances,
|
||||
list) {
|
||||
if (adv->periodic && adv->enabled)
|
||||
per_adv_cnt++;
|
||||
}
|
||||
|
||||
if (per_adv_cnt > 1)
|
||||
goto unlock;
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_LE_PER_ADV);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return rp->status;
|
||||
@ -4224,6 +4263,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, void *data,
|
||||
static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_le_create_cis *cp;
|
||||
bool pending = false;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
@ -4246,12 +4286,18 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (conn) {
|
||||
if (test_and_clear_bit(HCI_CONN_CREATE_CIS,
|
||||
&conn->flags))
|
||||
pending = true;
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (pending)
|
||||
hci_le_create_cis_pending(hdev);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -4999,18 +5045,15 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
|
||||
* As the connection handle is set here for the first time, it indicates
|
||||
* whether the connection is already set up.
|
||||
*/
|
||||
if (conn->handle != HCI_CONN_HANDLE_UNSET) {
|
||||
if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
|
||||
bt_dev_err(hdev, "Ignoring HCI_Sync_Conn_Complete event for existing connection");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 0x00:
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
if (conn->handle > HCI_CONN_HANDLE_MAX) {
|
||||
bt_dev_err(hdev, "Invalid handle: 0x%4.4x > 0x%4.4x",
|
||||
conn->handle, HCI_CONN_HANDLE_MAX);
|
||||
status = HCI_ERROR_INVALID_PARAMETERS;
|
||||
status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
|
||||
if (status) {
|
||||
conn->state = BT_CLOSED;
|
||||
break;
|
||||
}
|
||||
@ -5863,7 +5906,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
* As the connection handle is set here for the first time, it indicates
|
||||
* whether the connection is already set up.
|
||||
*/
|
||||
if (conn->handle != HCI_CONN_HANDLE_UNSET) {
|
||||
if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
|
||||
bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for existing connection");
|
||||
goto unlock;
|
||||
}
|
||||
@ -6216,8 +6259,9 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ext_adv && len > HCI_MAX_AD_LENGTH) {
|
||||
bt_dev_err_ratelimited(hdev, "legacy adv larger than 31 bytes");
|
||||
if (len > max_adv_len(hdev)) {
|
||||
bt_dev_err_ratelimited(hdev,
|
||||
"adv larger than maximum supported");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6282,7 +6326,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
*/
|
||||
conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, bdaddr_resolved,
|
||||
type);
|
||||
if (!ext_adv && conn && type == LE_ADV_IND && len <= HCI_MAX_AD_LENGTH) {
|
||||
if (!ext_adv && conn && type == LE_ADV_IND &&
|
||||
len <= max_adv_len(hdev)) {
|
||||
/* Store report for later inclusion by
|
||||
* mgmt_device_connected
|
||||
*/
|
||||
@ -6423,7 +6468,7 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
info->length + 1))
|
||||
break;
|
||||
|
||||
if (info->length <= HCI_MAX_AD_LENGTH) {
|
||||
if (info->length <= max_adv_len(hdev)) {
|
||||
rssi = info->data[info->length];
|
||||
process_adv_report(hdev, info->type, &info->bdaddr,
|
||||
info->bdaddr_type, NULL, 0, rssi,
|
||||
@ -6790,6 +6835,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||
struct hci_evt_le_cis_established *ev = data;
|
||||
struct hci_conn *conn;
|
||||
struct bt_iso_qos *qos;
|
||||
bool pending = false;
|
||||
u16 handle = __le16_to_cpu(ev->handle);
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
@ -6813,6 +6859,8 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
qos = &conn->iso_qos;
|
||||
|
||||
pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, &conn->flags);
|
||||
|
||||
/* Convert ISO Interval (1.25 ms slots) to SDU Interval (us) */
|
||||
qos->ucast.in.interval = le16_to_cpu(ev->interval) * 1250;
|
||||
qos->ucast.out.interval = qos->ucast.in.interval;
|
||||
@ -6854,10 +6902,14 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
|
||||
unlock:
|
||||
if (pending)
|
||||
hci_le_create_cis_pending(hdev);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -6936,6 +6988,7 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
||||
{
|
||||
struct hci_evt_le_create_big_complete *ev = data;
|
||||
struct hci_conn *conn;
|
||||
__u8 i = 0;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||
|
||||
@ -6944,33 +6997,46 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
rcu_read_lock();
|
||||
|
||||
conn = hci_conn_hash_lookup_big(hdev, ev->handle);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
/* Connect all BISes that are bound to the BIG */
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
if (bacmp(&conn->dst, BDADDR_ANY) ||
|
||||
conn->type != ISO_LINK ||
|
||||
conn->iso_qos.bcast.big != ev->handle)
|
||||
continue;
|
||||
|
||||
if (conn->type != ISO_LINK) {
|
||||
bt_dev_err(hdev,
|
||||
"Invalid connection link type handle 0x%2.2x",
|
||||
ev->handle);
|
||||
goto unlock;
|
||||
if (hci_conn_set_handle(conn,
|
||||
__le16_to_cpu(ev->bis_handle[i++])))
|
||||
continue;
|
||||
|
||||
if (!ev->status) {
|
||||
conn->state = BT_CONNECTED;
|
||||
set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
|
||||
rcu_read_unlock();
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
hci_iso_setup_path(conn);
|
||||
rcu_read_lock();
|
||||
continue;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
rcu_read_unlock();
|
||||
hci_conn_del(conn);
|
||||
rcu_read_lock();
|
||||
}
|
||||
|
||||
if (ev->num_bis)
|
||||
conn->handle = __le16_to_cpu(ev->bis_handle[0]);
|
||||
if (!ev->status && !i)
|
||||
/* If no BISes have been connected for the BIG,
|
||||
* terminate. This is in case all bound connections
|
||||
* have been closed before the BIG creation
|
||||
* has completed.
|
||||
*/
|
||||
hci_le_terminate_big_sync(hdev, ev->handle,
|
||||
HCI_ERROR_LOCAL_HOST_TERM);
|
||||
|
||||
if (!ev->status) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
hci_iso_setup_path(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -6987,9 +7053,6 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||
flex_array_size(ev, bis, ev->num_bis)))
|
||||
return;
|
||||
|
||||
if (ev->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (i = 0; i < ev->num_bis; i++) {
|
||||
@ -7013,9 +7076,25 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||
bis->iso_qos.bcast.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
|
||||
bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu);
|
||||
|
||||
hci_iso_setup_path(bis);
|
||||
if (!ev->status) {
|
||||
set_bit(HCI_CONN_BIG_SYNC, &bis->flags);
|
||||
hci_iso_setup_path(bis);
|
||||
}
|
||||
}
|
||||
|
||||
/* In case BIG sync failed, notify each failed connection to
|
||||
* the user after all hci connections have been added
|
||||
*/
|
||||
if (ev->status)
|
||||
for (i = 0; i < ev->num_bis; i++) {
|
||||
u16 handle = le16_to_cpu(ev->bis[i]);
|
||||
|
||||
bis = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
|
||||
set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
|
||||
hci_connect_cfm(bis, ev->status);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
|
@ -629,27 +629,6 @@ static void hci_req_start_scan(struct hci_request *req, u8 type, u16 interval,
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if an le connection is in the scanning state */
|
||||
static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == LE_LINK && c->state == BT_CONNECT &&
|
||||
test_bit(HCI_CONN_SCANNING, &c->flags)) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa);
|
||||
static int hci_update_random_address(struct hci_request *req,
|
||||
bool require_privacy, bool use_rpa,
|
||||
|
@ -264,6 +264,53 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
kfree_skb(skb_copy);
|
||||
}
|
||||
|
||||
static void hci_sock_copy_creds(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct scm_creds *creds;
|
||||
|
||||
if (!sk || WARN_ON(!skb))
|
||||
return;
|
||||
|
||||
creds = &bt_cb(skb)->creds;
|
||||
|
||||
/* Check if peer credentials is set */
|
||||
if (!sk->sk_peer_pid) {
|
||||
/* Check if parent peer credentials is set */
|
||||
if (bt_sk(sk)->parent && bt_sk(sk)->parent->sk_peer_pid)
|
||||
sk = bt_sk(sk)->parent;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if scm_creds already set */
|
||||
if (creds->pid == pid_vnr(sk->sk_peer_pid))
|
||||
return;
|
||||
|
||||
memset(creds, 0, sizeof(*creds));
|
||||
|
||||
creds->pid = pid_vnr(sk->sk_peer_pid);
|
||||
if (sk->sk_peer_cred) {
|
||||
creds->uid = sk->sk_peer_cred->uid;
|
||||
creds->gid = sk->sk_peer_cred->gid;
|
||||
}
|
||||
}
|
||||
|
||||
static struct sk_buff *hci_skb_clone(struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return NULL;
|
||||
|
||||
hci_sock_copy_creds(skb->sk, nskb);
|
||||
|
||||
return nskb;
|
||||
}
|
||||
|
||||
/* Send frame to sockets with specific channel */
|
||||
static void __hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
int flag, struct sock *skip_sk)
|
||||
@ -289,7 +336,7 @@ static void __hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
if (hci_pi(sk)->channel != channel)
|
||||
continue;
|
||||
|
||||
nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
nskb = hci_skb_clone(skb);
|
||||
if (!nskb)
|
||||
continue;
|
||||
|
||||
@ -356,6 +403,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (!skb_copy)
|
||||
return;
|
||||
|
||||
hci_sock_copy_creds(skb->sk, skb_copy);
|
||||
|
||||
/* Put header before the data */
|
||||
hdr = skb_push(skb_copy, HCI_MON_HDR_SIZE);
|
||||
hdr->opcode = opcode;
|
||||
@ -531,10 +580,12 @@ static struct sk_buff *create_monitor_ctrl_open(struct sock *sk)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb = bt_skb_alloc(14 + TASK_COMM_LEN , GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(14 + TASK_COMM_LEN, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
hci_sock_copy_creds(sk, skb);
|
||||
|
||||
flags = hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) ? 0x1 : 0x0;
|
||||
|
||||
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
|
||||
@ -580,6 +631,8 @@ static struct sk_buff *create_monitor_ctrl_close(struct sock *sk)
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
hci_sock_copy_creds(sk, skb);
|
||||
|
||||
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
|
||||
|
||||
__net_timestamp(skb);
|
||||
@ -606,6 +659,8 @@ static struct sk_buff *create_monitor_ctrl_command(struct sock *sk, u16 index,
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
hci_sock_copy_creds(sk, skb);
|
||||
|
||||
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
|
||||
put_unaligned_le16(opcode, skb_put(skb, 2));
|
||||
|
||||
@ -638,6 +693,8 @@ send_monitor_note(struct sock *sk, const char *fmt, ...)
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
hci_sock_copy_creds(sk, skb);
|
||||
|
||||
va_start(args, fmt);
|
||||
vsprintf(skb_put(skb, len), fmt, args);
|
||||
*(u8 *)skb_put(skb, 1) = 0;
|
||||
@ -1494,6 +1551,7 @@ static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
|
||||
static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
size_t len, int flags)
|
||||
{
|
||||
struct scm_cookie scm;
|
||||
struct sock *sk = sock->sk;
|
||||
struct sk_buff *skb;
|
||||
int copied, err;
|
||||
@ -1538,11 +1596,16 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&scm, 0, sizeof(scm));
|
||||
scm.creds = bt_cb(skb)->creds;
|
||||
|
||||
skb_free_datagram(sk, skb);
|
||||
|
||||
if (flags & MSG_TRUNC)
|
||||
copied = skblen;
|
||||
|
||||
scm_recv(sock, msg, &scm, flags);
|
||||
|
||||
return err ? : copied;
|
||||
}
|
||||
|
||||
@ -2143,18 +2206,12 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
|
||||
sock->ops = &hci_sock_ops;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &hci_sk_proto, protocol, GFP_ATOMIC,
|
||||
kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = protocol;
|
||||
|
||||
sock->state = SS_UNCONNECTED;
|
||||
sk->sk_state = BT_OPEN;
|
||||
sk->sk_destruct = hci_sock_destruct;
|
||||
|
||||
bt_sock_link(&hci_sk_list, sk);
|
||||
|
@ -3,6 +3,7 @@
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2021 Intel Corporation
|
||||
* Copyright 2023 NXP
|
||||
*/
|
||||
|
||||
#include <linux/property.h>
|
||||
@ -1319,9 +1320,11 @@ int hci_start_ext_adv_sync(struct hci_dev *hdev, u8 instance)
|
||||
static int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct hci_cp_le_set_per_adv_enable cp;
|
||||
struct adv_info *adv = NULL;
|
||||
|
||||
/* If periodic advertising already disabled there is nothing to do. */
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_PER_ADV))
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !adv->periodic || !adv->enabled)
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
@ -1386,9 +1389,11 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
static int hci_enable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct hci_cp_le_set_per_adv_enable cp;
|
||||
struct adv_info *adv = NULL;
|
||||
|
||||
/* If periodic advertising already enabled there is nothing to do. */
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_PER_ADV))
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (adv && adv->periodic && adv->enabled)
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
@ -1458,22 +1463,19 @@ int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len,
|
||||
sync_interval);
|
||||
if (IS_ERR(adv))
|
||||
return PTR_ERR(adv);
|
||||
adv->pending = false;
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only start advertising if instance 0 or if a dedicated instance has
|
||||
* been added.
|
||||
*/
|
||||
if (!adv || added) {
|
||||
err = hci_start_ext_adv_sync(hdev, instance);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
/* Start advertising */
|
||||
err = hci_start_ext_adv_sync(hdev, instance);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
err = hci_adv_bcast_annoucement(hdev, adv);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
}
|
||||
err = hci_adv_bcast_annoucement(hdev, adv);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
err = hci_set_per_adv_params_sync(hdev, instance, min_interval,
|
||||
max_interval);
|
||||
@ -2670,27 +2672,6 @@ done:
|
||||
return filter_policy;
|
||||
}
|
||||
|
||||
/* Returns true if an le connection is in the scanning state */
|
||||
static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == LE_LINK && c->state == BT_CONNECT &&
|
||||
test_bit(HCI_CONN_SCANNING, &c->flags)) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
|
||||
u16 interval, u16 window,
|
||||
u8 own_addr_type, u8 filter_policy)
|
||||
@ -4133,10 +4114,13 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
if (bis_capable(hdev)) {
|
||||
events[1] |= 0x20; /* LE PA Report */
|
||||
events[1] |= 0x40; /* LE PA Sync Established */
|
||||
events[3] |= 0x04; /* LE Create BIG Complete */
|
||||
events[3] |= 0x08; /* LE Terminate BIG Complete */
|
||||
events[3] |= 0x10; /* LE BIG Sync Established */
|
||||
events[3] |= 0x20; /* LE BIG Sync Loss */
|
||||
events[4] |= 0x02; /* LE BIG Info Advertising Report */
|
||||
}
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK,
|
||||
@ -5269,26 +5253,64 @@ static int hci_disconnect_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
}
|
||||
|
||||
static int hci_le_connect_cancel_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
struct hci_conn *conn, u8 reason)
|
||||
{
|
||||
/* Return reason if scanning since the connection shall probably be
|
||||
* cleanup directly.
|
||||
*/
|
||||
if (test_bit(HCI_CONN_SCANNING, &conn->flags))
|
||||
return 0;
|
||||
return reason;
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
|
||||
if (conn->role == HCI_ROLE_SLAVE ||
|
||||
test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN_CANCEL,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u8 reason)
|
||||
{
|
||||
if (conn->type == LE_LINK)
|
||||
return hci_le_connect_cancel_sync(hdev, conn);
|
||||
return hci_le_connect_cancel_sync(hdev, conn, reason);
|
||||
|
||||
if (conn->type == ISO_LINK) {
|
||||
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
|
||||
* page 1857:
|
||||
*
|
||||
* If this command is issued for a CIS on the Central and the
|
||||
* CIS is successfully terminated before being established,
|
||||
* then an HCI_LE_CIS_Established event shall also be sent for
|
||||
* this CIS with the Status Operation Cancelled by Host (0x44).
|
||||
*/
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
return hci_disconnect_sync(hdev, conn, reason);
|
||||
|
||||
/* CIS with no Create CIS sent have nothing to cancel */
|
||||
if (bacmp(&conn->dst, BDADDR_ANY))
|
||||
return HCI_ERROR_LOCAL_HOST_TERM;
|
||||
|
||||
/* There is no way to cancel a BIS without terminating the BIG
|
||||
* which is done later on connection cleanup.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
|
||||
return 0;
|
||||
|
||||
/* Wait for HCI_EV_CONN_COMPLETE, not HCI_EV_CMD_STATUS, when the
|
||||
* reason is anything but HCI_ERROR_REMOTE_POWER_OFF. This reason is
|
||||
* used when suspending or powering off, where we don't want to wait
|
||||
* for the peer's response.
|
||||
*/
|
||||
if (reason != HCI_ERROR_REMOTE_POWER_OFF)
|
||||
return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN_CANCEL,
|
||||
6, &conn->dst,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
HCI_CMD_TIMEOUT, NULL);
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_CREATE_CONN_CANCEL,
|
||||
6, &conn->dst, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
@ -5312,11 +5334,27 @@ static int hci_reject_sco_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_le_reject_cis_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u8 reason)
|
||||
{
|
||||
struct hci_cp_le_reject_cis cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.reason = reason;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_REJECT_CIS,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u8 reason)
|
||||
{
|
||||
struct hci_cp_reject_conn_req cp;
|
||||
|
||||
if (conn->type == ISO_LINK)
|
||||
return hci_le_reject_cis_sync(hdev, conn, reason);
|
||||
|
||||
if (conn->type == SCO_LINK || conn->type == ESCO_LINK)
|
||||
return hci_reject_sco_sync(hdev, conn, reason);
|
||||
|
||||
@ -5330,31 +5368,52 @@ static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
|
||||
int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
u16 handle = conn->handle;
|
||||
|
||||
switch (conn->state) {
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
return hci_disconnect_sync(hdev, conn, reason);
|
||||
err = hci_disconnect_sync(hdev, conn, reason);
|
||||
break;
|
||||
case BT_CONNECT:
|
||||
err = hci_connect_cancel_sync(hdev, conn);
|
||||
/* Cleanup hci_conn object if it cannot be cancelled as it
|
||||
* likelly means the controller and host stack are out of sync.
|
||||
*/
|
||||
if (err) {
|
||||
hci_dev_lock(hdev);
|
||||
hci_conn_failed(conn, err);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
return err;
|
||||
err = hci_connect_cancel_sync(hdev, conn, reason);
|
||||
break;
|
||||
case BT_CONNECT2:
|
||||
return hci_reject_conn_sync(hdev, conn, reason);
|
||||
err = hci_reject_conn_sync(hdev, conn, reason);
|
||||
break;
|
||||
case BT_OPEN:
|
||||
case BT_BOUND:
|
||||
hci_dev_lock(hdev);
|
||||
hci_conn_failed(conn, reason);
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
default:
|
||||
conn->state = BT_CLOSED;
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Cleanup hci_conn object if it cannot be cancelled as it
|
||||
* likelly means the controller and host stack are out of sync
|
||||
* or in case of LE it was still scanning so it can be cleanup
|
||||
* safely.
|
||||
*/
|
||||
if (err) {
|
||||
struct hci_conn *c;
|
||||
|
||||
/* Check if the connection hasn't been cleanup while waiting
|
||||
* commands to complete.
|
||||
*/
|
||||
c = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!c || c != conn)
|
||||
return 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_conn_failed(conn, err);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_disconnect_all_sync(struct hci_dev *hdev, u8 reason)
|
||||
@ -6253,63 +6312,99 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
|
||||
done:
|
||||
if (err == -ETIMEDOUT)
|
||||
hci_le_connect_cancel_sync(hdev, conn);
|
||||
hci_le_connect_cancel_sync(hdev, conn, 0x00);
|
||||
|
||||
/* Re-enable advertising after the connection attempt is finished. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_le_create_cis_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
int hci_le_create_cis_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct {
|
||||
struct hci_cp_le_create_cis cp;
|
||||
struct hci_cis cis[0x1f];
|
||||
} cmd;
|
||||
u8 cig;
|
||||
struct hci_conn *hcon = conn;
|
||||
struct hci_conn *conn;
|
||||
u8 cig = BT_ISO_QOS_CIG_UNSET;
|
||||
|
||||
/* The spec allows only one pending LE Create CIS command at a time. If
|
||||
* the command is pending now, don't do anything. We check for pending
|
||||
* connections after each CIS Established event.
|
||||
*
|
||||
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
|
||||
* page 2566:
|
||||
*
|
||||
* If the Host issues this command before all the
|
||||
* HCI_LE_CIS_Established events from the previous use of the
|
||||
* command have been generated, the Controller shall return the
|
||||
* error code Command Disallowed (0x0C).
|
||||
*
|
||||
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
|
||||
* page 2567:
|
||||
*
|
||||
* When the Controller receives the HCI_LE_Create_CIS command, the
|
||||
* Controller sends the HCI_Command_Status event to the Host. An
|
||||
* HCI_LE_CIS_Established event will be generated for each CIS when it
|
||||
* is established or if it is disconnected or considered lost before
|
||||
* being established; until all the events are generated, the command
|
||||
* remains pending.
|
||||
*/
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cis[0].acl_handle = cpu_to_le16(conn->parent->handle);
|
||||
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
|
||||
cmd.cp.num_cis++;
|
||||
cig = conn->iso_qos.ucast.cig;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* Wait until previous Create CIS has completed */
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Find CIG with all CIS ready */
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
struct hci_conn *link;
|
||||
|
||||
if (hci_conn_check_create_cis(conn))
|
||||
continue;
|
||||
|
||||
cig = conn->iso_qos.ucast.cig;
|
||||
|
||||
list_for_each_entry_rcu(link, &hdev->conn_hash.list, list) {
|
||||
if (hci_conn_check_create_cis(link) > 0 &&
|
||||
link->iso_qos.ucast.cig == cig &&
|
||||
link->state != BT_CONNECTED) {
|
||||
cig = BT_ISO_QOS_CIG_UNSET;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cig != BT_ISO_QOS_CIG_UNSET)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cig == BT_ISO_QOS_CIG_UNSET)
|
||||
goto done;
|
||||
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
|
||||
|
||||
if (conn == hcon || conn->type != ISO_LINK ||
|
||||
conn->state == BT_CONNECTED ||
|
||||
if (hci_conn_check_create_cis(conn) ||
|
||||
conn->iso_qos.ucast.cig != cig)
|
||||
continue;
|
||||
|
||||
/* Check if all CIS(s) belonging to a CIG are ready */
|
||||
if (!conn->parent || conn->parent->state != BT_CONNECTED ||
|
||||
conn->state != BT_CONNECT) {
|
||||
cmd.cp.num_cis = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Group all CIS with state BT_CONNECT since the spec don't
|
||||
* allow to send them individually:
|
||||
*
|
||||
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
|
||||
* page 2566:
|
||||
*
|
||||
* If the Host issues this command before all the
|
||||
* HCI_LE_CIS_Established events from the previous use of the
|
||||
* command have been generated, the Controller shall return the
|
||||
* error code Command Disallowed (0x0C).
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE_CIS, &conn->flags);
|
||||
cis->acl_handle = cpu_to_le16(conn->parent->handle);
|
||||
cis->cis_handle = cpu_to_le16(conn->handle);
|
||||
cmd.cp.num_cis++;
|
||||
|
||||
if (cmd.cp.num_cis >= ARRAY_SIZE(cmd.cis))
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
rcu_read_unlock();
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@ -6433,7 +6528,7 @@ int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
|
||||
static int _update_adv_data_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
u8 instance = PTR_ERR(data);
|
||||
u8 instance = PTR_UINT(data);
|
||||
|
||||
return hci_update_adv_data_sync(hdev, instance);
|
||||
}
|
||||
@ -6441,5 +6536,5 @@ static int _update_adv_data_sync(struct hci_dev *hdev, void *data)
|
||||
int hci_update_adv_data(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
return hci_cmd_sync_queue(hdev, _update_adv_data_sync,
|
||||
ERR_PTR(instance), NULL);
|
||||
UINT_PTR(instance), NULL);
|
||||
}
|
||||
|
@ -256,21 +256,13 @@ static int hidp_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &hidp_proto, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
sock->ops = &hidp_sock_ops;
|
||||
|
||||
sock->state = SS_UNCONNECTED;
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = protocol;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
bt_sock_link(&hidp_sk_list, sk);
|
||||
|
||||
return 0;
|
||||
|
@ -48,6 +48,11 @@ static void iso_sock_kill(struct sock *sk);
|
||||
#define EIR_SERVICE_DATA_LENGTH 4
|
||||
#define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH)
|
||||
|
||||
/* iso_pinfo flags values */
|
||||
enum {
|
||||
BT_SK_BIG_SYNC,
|
||||
};
|
||||
|
||||
struct iso_pinfo {
|
||||
struct bt_sock bt;
|
||||
bdaddr_t src;
|
||||
@ -58,7 +63,7 @@ struct iso_pinfo {
|
||||
__u8 bc_num_bis;
|
||||
__u8 bc_bis[ISO_MAX_NUM_BIS];
|
||||
__u16 sync_handle;
|
||||
__u32 flags;
|
||||
unsigned long flags;
|
||||
struct bt_iso_qos qos;
|
||||
bool qos_user_set;
|
||||
__u8 base_len;
|
||||
@ -287,13 +292,24 @@ static int iso_connect_bis(struct sock *sk)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
&iso_pi(sk)->qos, iso_pi(sk)->base_len,
|
||||
iso_pi(sk)->base);
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
/* Just bind if DEFER_SETUP has been set */
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||
hcon = hci_bind_bis(hdev, &iso_pi(sk)->dst,
|
||||
&iso_pi(sk)->qos, iso_pi(sk)->base_len,
|
||||
iso_pi(sk)->base);
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
} else {
|
||||
hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
&iso_pi(sk)->qos, iso_pi(sk)->base_len,
|
||||
iso_pi(sk)->base);
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
@ -317,6 +333,9 @@ static int iso_connect_bis(struct sock *sk)
|
||||
if (hcon->state == BT_CONNECTED) {
|
||||
iso_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
} else if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||
iso_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECT;
|
||||
} else {
|
||||
sk->sk_state = BT_CONNECT;
|
||||
iso_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
@ -600,18 +619,6 @@ static void iso_sock_kill(struct sock *sk)
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static void iso_conn_defer_reject(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_cp_le_reject_cis cp;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.reason = HCI_ERROR_REJ_BAD_ADDR;
|
||||
hci_send_cmd(conn->hdev, HCI_OP_LE_REJECT_CIS, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void __iso_sock_close(struct sock *sk)
|
||||
{
|
||||
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
|
||||
@ -621,6 +628,7 @@ static void __iso_sock_close(struct sock *sk)
|
||||
iso_sock_cleanup_listen(sk);
|
||||
break;
|
||||
|
||||
case BT_CONNECT:
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
if (iso_pi(sk)->conn->hcon) {
|
||||
@ -636,21 +644,6 @@ static void __iso_sock_close(struct sock *sk)
|
||||
break;
|
||||
|
||||
case BT_CONNECT2:
|
||||
if (iso_pi(sk)->conn->hcon)
|
||||
iso_conn_defer_reject(iso_pi(sk)->conn->hcon);
|
||||
iso_chan_del(sk, ECONNRESET);
|
||||
break;
|
||||
case BT_CONNECT:
|
||||
/* In case of DEFER_SETUP the hcon would be bound to CIG which
|
||||
* needs to be removed so just call hci_conn_del so the cleanup
|
||||
* callback do what is needed.
|
||||
*/
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
|
||||
iso_pi(sk)->conn->hcon) {
|
||||
hci_conn_del(iso_pi(sk)->conn->hcon);
|
||||
iso_pi(sk)->conn->hcon = NULL;
|
||||
}
|
||||
|
||||
iso_chan_del(sk, ECONNRESET);
|
||||
break;
|
||||
case BT_DISCONN:
|
||||
@ -724,21 +717,13 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &iso_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &iso_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
|
||||
|
||||
sk->sk_destruct = iso_sock_destruct;
|
||||
sk->sk_sndtimeo = ISO_CONN_TIMEOUT;
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
/* Set address type as public as default src address is BDADDR_ANY */
|
||||
iso_pi(sk)->src_type = BDADDR_LE_PUBLIC;
|
||||
|
||||
@ -1202,6 +1187,12 @@ static bool check_io_qos(struct bt_iso_io_qos *qos)
|
||||
|
||||
static bool check_ucast_qos(struct bt_iso_qos *qos)
|
||||
{
|
||||
if (qos->ucast.cig > 0xef && qos->ucast.cig != BT_ISO_QOS_CIG_UNSET)
|
||||
return false;
|
||||
|
||||
if (qos->ucast.cis > 0xef && qos->ucast.cis != BT_ISO_QOS_CIS_UNSET)
|
||||
return false;
|
||||
|
||||
if (qos->ucast.sca > 0x07)
|
||||
return false;
|
||||
|
||||
@ -1291,6 +1282,18 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
if (copy_from_sockptr(&opt, optval, sizeof(u32))) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt)
|
||||
set_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
|
||||
else
|
||||
clear_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
|
||||
break;
|
||||
|
||||
case BT_ISO_QOS:
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
|
||||
sk->sk_state != BT_CONNECT2) {
|
||||
@ -1376,6 +1379,12 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
|
||||
(int __user *)optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case BT_ISO_QOS:
|
||||
qos = iso_sock_get_qos(sk);
|
||||
|
||||
@ -1563,6 +1572,12 @@ static void iso_conn_ready(struct iso_conn *conn)
|
||||
hci_conn_hold(hcon);
|
||||
iso_chan_add(conn, sk, parent);
|
||||
|
||||
if (ev && ((struct hci_evt_le_big_sync_estabilished *)ev)->status) {
|
||||
/* Trigger error signal on child socket */
|
||||
sk->sk_err = ECONNREFUSED;
|
||||
sk->sk_error_report(sk);
|
||||
}
|
||||
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
|
||||
sk->sk_state = BT_CONNECT2;
|
||||
else
|
||||
@ -1631,15 +1646,17 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
|
||||
iso_pi(sk)->bc_num_bis = ev2->num_bis;
|
||||
|
||||
err = hci_le_big_create_sync(hdev,
|
||||
&iso_pi(sk)->qos,
|
||||
iso_pi(sk)->sync_handle,
|
||||
iso_pi(sk)->bc_num_bis,
|
||||
iso_pi(sk)->bc_bis);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "hci_le_big_create_sync: %d",
|
||||
err);
|
||||
sk = NULL;
|
||||
if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
|
||||
err = hci_le_big_create_sync(hdev,
|
||||
&iso_pi(sk)->qos,
|
||||
iso_pi(sk)->sync_handle,
|
||||
iso_pi(sk)->bc_num_bis,
|
||||
iso_pi(sk)->bc_bis);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "hci_le_big_create_sync: %d",
|
||||
err);
|
||||
sk = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1676,13 +1693,18 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
}
|
||||
|
||||
/* Create CIS if pending */
|
||||
hci_le_create_cis(hcon);
|
||||
hci_le_create_cis_pending(hcon->hdev);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
|
||||
|
||||
if (!status) {
|
||||
/* Similar to the success case, if HCI_CONN_BIG_SYNC_FAILED is set,
|
||||
* queue the failed bis connection into the accept queue of the
|
||||
* listening socket and wake up userspace, to inform the user about
|
||||
* the BIG sync failed event.
|
||||
*/
|
||||
if (!status || test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
|
||||
struct iso_conn *conn;
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
@ -1757,6 +1779,7 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
|
||||
if (len == skb->len) {
|
||||
/* Complete frame received */
|
||||
hci_skb_pkt_status(skb) = flags & 0x03;
|
||||
iso_recv_frame(conn, skb);
|
||||
return;
|
||||
}
|
||||
@ -1778,6 +1801,7 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
if (!conn->rx_skb)
|
||||
goto drop;
|
||||
|
||||
hci_skb_pkt_status(conn->rx_skb) = flags & 0x03;
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
conn->rx_len = len - skb->len;
|
||||
|
@ -178,21 +178,6 @@ done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void l2cap_sock_init_pid(struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
|
||||
/* Only L2CAP_MODE_EXT_FLOWCTL ever need to access the PID in order to
|
||||
* group the channels being requested.
|
||||
*/
|
||||
if (chan->mode != L2CAP_MODE_EXT_FLOWCTL)
|
||||
return;
|
||||
|
||||
spin_lock(&sk->sk_peer_lock);
|
||||
sk->sk_peer_pid = get_pid(task_tgid(current));
|
||||
spin_unlock(&sk->sk_peer_lock);
|
||||
}
|
||||
|
||||
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
|
||||
int alen, int flags)
|
||||
{
|
||||
@ -268,8 +253,6 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
|
||||
chan->mode != L2CAP_MODE_EXT_FLOWCTL)
|
||||
chan->mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
|
||||
l2cap_sock_init_pid(sk);
|
||||
|
||||
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
|
||||
&la.l2_bdaddr, la.l2_bdaddr_type);
|
||||
if (err)
|
||||
@ -325,8 +308,6 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
||||
goto done;
|
||||
}
|
||||
|
||||
l2cap_sock_init_pid(sk);
|
||||
|
||||
sk->sk_max_ack_backlog = backlog;
|
||||
sk->sk_ack_backlog = 0;
|
||||
|
||||
@ -1858,21 +1839,13 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
|
||||
|
||||
sk->sk_destruct = l2cap_sock_destruct;
|
||||
sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan) {
|
||||
sk_free(sk);
|
||||
|
@ -944,6 +944,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
||||
if (cis_peripheral_capable(hdev))
|
||||
settings |= MGMT_SETTING_CIS_PERIPHERAL;
|
||||
|
||||
if (bis_capable(hdev))
|
||||
settings |= MGMT_SETTING_ISO_BROADCASTER;
|
||||
|
||||
if (sync_recv_capable(hdev))
|
||||
settings |= MGMT_SETTING_ISO_SYNC_RECEIVER;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@ -3580,18 +3586,6 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int abort_conn_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
u16 handle = PTR_ERR(data);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
return hci_abort_conn_sync(hdev, conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
}
|
||||
|
||||
static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 len)
|
||||
{
|
||||
@ -3642,8 +3636,7 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
le_addr_type(addr->type));
|
||||
|
||||
if (conn->conn_reason == CONN_REASON_PAIR_DEVICE)
|
||||
hci_cmd_sync_queue(hdev, abort_conn_sync, ERR_PTR(conn->handle),
|
||||
NULL);
|
||||
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
@ -8435,8 +8428,8 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
supported_flags = get_supported_adv_flags(hdev);
|
||||
|
||||
rp->supported_flags = cpu_to_le32(supported_flags);
|
||||
rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_adv_data_len = max_adv_len(hdev);
|
||||
rp->max_scan_rsp_len = max_adv_len(hdev);
|
||||
rp->max_instances = hdev->le_num_of_adv_sets;
|
||||
rp->num_instances = hdev->adv_instance_cnt;
|
||||
|
||||
@ -8472,7 +8465,7 @@ static u8 calculate_name_len(struct hci_dev *hdev)
|
||||
static u8 tlv_data_max_len(struct hci_dev *hdev, u32 adv_flags,
|
||||
bool is_adv_data)
|
||||
{
|
||||
u8 max_len = HCI_MAX_AD_LENGTH;
|
||||
u8 max_len = max_adv_len(hdev);
|
||||
|
||||
if (is_adv_data) {
|
||||
if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
|
||||
|
@ -91,6 +91,33 @@ struct msft_ev_le_monitor_device {
|
||||
struct msft_monitor_advertisement_handle_data {
|
||||
__u8 msft_handle;
|
||||
__u16 mgmt_handle;
|
||||
__s8 rssi_high;
|
||||
__s8 rssi_low;
|
||||
__u8 rssi_low_interval;
|
||||
__u8 rssi_sampling_period;
|
||||
__u8 cond_type;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
enum monitor_addr_filter_state {
|
||||
AF_STATE_IDLE,
|
||||
AF_STATE_ADDING,
|
||||
AF_STATE_ADDED,
|
||||
AF_STATE_REMOVING,
|
||||
};
|
||||
|
||||
#define MSFT_MONITOR_ADVERTISEMENT_TYPE_ADDR 0x04
|
||||
struct msft_monitor_addr_filter_data {
|
||||
__u8 msft_handle;
|
||||
__u8 pattern_handle; /* address filters pertain to */
|
||||
__u16 mgmt_handle;
|
||||
int state;
|
||||
__s8 rssi_high;
|
||||
__s8 rssi_low;
|
||||
__u8 rssi_low_interval;
|
||||
__u8 rssi_sampling_period;
|
||||
__u8 addr_type;
|
||||
bdaddr_t bdaddr;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
@ -99,9 +126,12 @@ struct msft_data {
|
||||
__u8 evt_prefix_len;
|
||||
__u8 *evt_prefix;
|
||||
struct list_head handle_map;
|
||||
struct list_head address_filters;
|
||||
__u8 resuming;
|
||||
__u8 suspending;
|
||||
__u8 filter_enabled;
|
||||
/* To synchronize add/remove address filter and monitor device event.*/
|
||||
struct mutex filter_lock;
|
||||
};
|
||||
|
||||
bool msft_monitor_supported(struct hci_dev *hdev)
|
||||
@ -180,6 +210,24 @@ static struct msft_monitor_advertisement_handle_data *msft_find_handle_data
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds msft->filter_lock */
|
||||
static struct msft_monitor_addr_filter_data *msft_find_address_data
|
||||
(struct hci_dev *hdev, u8 addr_type, bdaddr_t *addr,
|
||||
u8 pattern_handle)
|
||||
{
|
||||
struct msft_monitor_addr_filter_data *entry;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
|
||||
list_for_each_entry(entry, &msft->address_filters, list) {
|
||||
if (entry->pattern_handle == pattern_handle &&
|
||||
addr_type == entry->addr_type &&
|
||||
!bacmp(addr, &entry->bdaddr))
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static int msft_monitor_device_del(struct hci_dev *hdev, __u16 mgmt_handle,
|
||||
bdaddr_t *bdaddr, __u8 addr_type,
|
||||
@ -240,6 +288,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
|
||||
|
||||
handle_data->mgmt_handle = monitor->handle;
|
||||
handle_data->msft_handle = rp->handle;
|
||||
handle_data->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN;
|
||||
INIT_LIST_HEAD(&handle_data->list);
|
||||
list_add(&handle_data->list, &msft->handle_map);
|
||||
|
||||
@ -254,6 +303,70 @@ unlock:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
static void msft_remove_addr_filters_sync(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
struct msft_monitor_addr_filter_data *address_filter, *n;
|
||||
struct msft_cp_le_cancel_monitor_advertisement cp;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
struct list_head head;
|
||||
struct sk_buff *skb;
|
||||
|
||||
INIT_LIST_HEAD(&head);
|
||||
|
||||
/* Cancel all corresponding address monitors */
|
||||
mutex_lock(&msft->filter_lock);
|
||||
|
||||
list_for_each_entry_safe(address_filter, n, &msft->address_filters,
|
||||
list) {
|
||||
if (address_filter->pattern_handle != handle)
|
||||
continue;
|
||||
|
||||
list_del(&address_filter->list);
|
||||
|
||||
/* Keep the address filter and let
|
||||
* msft_add_address_filter_sync() remove and free the address
|
||||
* filter.
|
||||
*/
|
||||
if (address_filter->state == AF_STATE_ADDING) {
|
||||
address_filter->state = AF_STATE_REMOVING;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Keep the address filter and let
|
||||
* msft_cancel_address_filter_sync() remove and free the address
|
||||
* filter
|
||||
*/
|
||||
if (address_filter->state == AF_STATE_REMOVING)
|
||||
continue;
|
||||
|
||||
list_add_tail(&address_filter->list, &head);
|
||||
}
|
||||
|
||||
mutex_unlock(&msft->filter_lock);
|
||||
|
||||
list_for_each_entry_safe(address_filter, n, &head, list) {
|
||||
list_del(&address_filter->list);
|
||||
|
||||
cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
|
||||
cp.handle = address_filter->msft_handle;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR_OR_NULL(skb)) {
|
||||
kfree(address_filter);
|
||||
continue;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
bt_dev_dbg(hdev, "MSFT: Canceled device %pMR address filter",
|
||||
&address_filter->bdaddr);
|
||||
|
||||
kfree(address_filter);
|
||||
}
|
||||
}
|
||||
|
||||
static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
||||
u16 opcode,
|
||||
struct adv_monitor *monitor,
|
||||
@ -263,6 +376,7 @@ static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int status = 0;
|
||||
u8 msft_handle;
|
||||
|
||||
rp = (struct msft_rp_le_cancel_monitor_advertisement *)skb->data;
|
||||
if (skb->len < sizeof(*rp)) {
|
||||
@ -293,11 +407,17 @@ static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
||||
NULL, 0, false);
|
||||
}
|
||||
|
||||
msft_handle = handle_data->msft_handle;
|
||||
|
||||
list_del(&handle_data->list);
|
||||
kfree(handle_data);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
msft_remove_addr_filters_sync(hdev, msft_handle);
|
||||
} else {
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
done:
|
||||
return status;
|
||||
@ -394,12 +514,14 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
|
||||
{
|
||||
struct msft_cp_le_monitor_advertisement *cp;
|
||||
struct msft_le_monitor_advertisement_pattern_data *pattern_data;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct msft_le_monitor_advertisement_pattern *pattern;
|
||||
struct adv_pattern *entry;
|
||||
size_t total_size = sizeof(*cp) + sizeof(*pattern_data);
|
||||
ptrdiff_t offset = 0;
|
||||
u8 pattern_count = 0;
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (!msft_monitor_pattern_valid(monitor))
|
||||
return -EINVAL;
|
||||
@ -436,16 +558,31 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
|
||||
|
||||
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, total_size, cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
kfree(cp);
|
||||
|
||||
if (IS_ERR_OR_NULL(skb)) {
|
||||
if (!skb)
|
||||
return -EIO;
|
||||
return PTR_ERR(skb);
|
||||
err = PTR_ERR(skb);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
return msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode,
|
||||
monitor, skb);
|
||||
err = msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode,
|
||||
monitor, skb);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
|
||||
if (!handle_data) {
|
||||
err = -ENODATA;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
handle_data->rssi_high = cp->rssi_high;
|
||||
handle_data->rssi_low = cp->rssi_low;
|
||||
handle_data->rssi_low_interval = cp->rssi_low_interval;
|
||||
handle_data->rssi_sampling_period = cp->rssi_sampling_period;
|
||||
|
||||
out_free:
|
||||
kfree(cp);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
@ -538,6 +675,7 @@ void msft_do_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data, *tmp;
|
||||
struct msft_monitor_addr_filter_data *address_filter, *n;
|
||||
struct adv_monitor *monitor;
|
||||
|
||||
if (!msft)
|
||||
@ -559,6 +697,14 @@ void msft_do_close(struct hci_dev *hdev)
|
||||
kfree(handle_data);
|
||||
}
|
||||
|
||||
mutex_lock(&msft->filter_lock);
|
||||
list_for_each_entry_safe(address_filter, n, &msft->address_filters,
|
||||
list) {
|
||||
list_del(&address_filter->list);
|
||||
kfree(address_filter);
|
||||
}
|
||||
mutex_unlock(&msft->filter_lock);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* Clear any devices that are being monitored and notify device lost */
|
||||
@ -568,6 +714,49 @@ void msft_do_close(struct hci_dev *hdev)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int msft_cancel_address_filter_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct msft_monitor_addr_filter_data *address_filter = data;
|
||||
struct msft_cp_le_cancel_monitor_advertisement cp;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
if (!msft) {
|
||||
bt_dev_err(hdev, "MSFT: msft data is freed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The address filter has been removed by hci dev close */
|
||||
if (!test_bit(HCI_UP, &hdev->flags))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&msft->filter_lock);
|
||||
list_del(&address_filter->list);
|
||||
mutex_unlock(&msft->filter_lock);
|
||||
|
||||
cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
|
||||
cp.handle = address_filter->msft_handle;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR_OR_NULL(skb)) {
|
||||
bt_dev_err(hdev, "MSFT: Failed to cancel address (%pMR) filter",
|
||||
&address_filter->bdaddr);
|
||||
err = -EIO;
|
||||
goto done;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
bt_dev_dbg(hdev, "MSFT: Canceled device %pMR address filter",
|
||||
&address_filter->bdaddr);
|
||||
|
||||
done:
|
||||
kfree(address_filter);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void msft_register(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft = NULL;
|
||||
@ -581,7 +770,9 @@ void msft_register(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&msft->handle_map);
|
||||
INIT_LIST_HEAD(&msft->address_filters);
|
||||
hdev->msft_data = msft;
|
||||
mutex_init(&msft->filter_lock);
|
||||
}
|
||||
|
||||
void msft_unregister(struct hci_dev *hdev)
|
||||
@ -596,6 +787,7 @@ void msft_unregister(struct hci_dev *hdev)
|
||||
hdev->msft_data = NULL;
|
||||
|
||||
kfree(msft->evt_prefix);
|
||||
mutex_destroy(&msft->filter_lock);
|
||||
kfree(msft);
|
||||
}
|
||||
|
||||
@ -645,11 +837,149 @@ static void *msft_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
return data;
|
||||
}
|
||||
|
||||
static int msft_add_address_filter_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct msft_monitor_addr_filter_data *address_filter = data;
|
||||
struct msft_rp_le_monitor_advertisement *rp;
|
||||
struct msft_cp_le_monitor_advertisement *cp;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
struct sk_buff *skb = NULL;
|
||||
bool remove = false;
|
||||
size_t size;
|
||||
|
||||
if (!msft) {
|
||||
bt_dev_err(hdev, "MSFT: msft data is freed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The address filter has been removed by hci dev close */
|
||||
if (!test_bit(HCI_UP, &hdev->flags))
|
||||
return -ENODEV;
|
||||
|
||||
/* We are safe to use the address filter from now on.
|
||||
* msft_monitor_device_evt() wouldn't delete this filter because it's
|
||||
* not been added by now.
|
||||
* And all other functions that requiring hci_req_sync_lock wouldn't
|
||||
* touch this filter before this func completes because it's protected
|
||||
* by hci_req_sync_lock.
|
||||
*/
|
||||
|
||||
if (address_filter->state == AF_STATE_REMOVING) {
|
||||
mutex_lock(&msft->filter_lock);
|
||||
list_del(&address_filter->list);
|
||||
mutex_unlock(&msft->filter_lock);
|
||||
kfree(address_filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = sizeof(*cp) +
|
||||
sizeof(address_filter->addr_type) +
|
||||
sizeof(address_filter->bdaddr);
|
||||
cp = kzalloc(size, GFP_KERNEL);
|
||||
if (!cp) {
|
||||
bt_dev_err(hdev, "MSFT: Alloc cmd param err");
|
||||
remove = true;
|
||||
goto done;
|
||||
}
|
||||
cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT;
|
||||
cp->rssi_high = address_filter->rssi_high;
|
||||
cp->rssi_low = address_filter->rssi_low;
|
||||
cp->rssi_low_interval = address_filter->rssi_low_interval;
|
||||
cp->rssi_sampling_period = address_filter->rssi_sampling_period;
|
||||
cp->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_ADDR;
|
||||
cp->data[0] = address_filter->addr_type;
|
||||
memcpy(&cp->data[1], &address_filter->bdaddr,
|
||||
sizeof(address_filter->bdaddr));
|
||||
|
||||
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, size, cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR_OR_NULL(skb)) {
|
||||
bt_dev_err(hdev, "Failed to enable address %pMR filter",
|
||||
&address_filter->bdaddr);
|
||||
skb = NULL;
|
||||
remove = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rp = skb_pull_data(skb, sizeof(*rp));
|
||||
if (!rp || rp->sub_opcode != MSFT_OP_LE_MONITOR_ADVERTISEMENT ||
|
||||
rp->status)
|
||||
remove = true;
|
||||
|
||||
done:
|
||||
mutex_lock(&msft->filter_lock);
|
||||
|
||||
if (remove) {
|
||||
bt_dev_warn(hdev, "MSFT: Remove address (%pMR) filter",
|
||||
&address_filter->bdaddr);
|
||||
list_del(&address_filter->list);
|
||||
kfree(address_filter);
|
||||
} else {
|
||||
address_filter->state = AF_STATE_ADDED;
|
||||
address_filter->msft_handle = rp->handle;
|
||||
bt_dev_dbg(hdev, "MSFT: Address %pMR filter enabled",
|
||||
&address_filter->bdaddr);
|
||||
}
|
||||
mutex_unlock(&msft->filter_lock);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds msft->filter_lock */
|
||||
static struct msft_monitor_addr_filter_data *msft_add_address_filter
|
||||
(struct hci_dev *hdev, u8 addr_type, bdaddr_t *bdaddr,
|
||||
struct msft_monitor_advertisement_handle_data *handle_data)
|
||||
{
|
||||
struct msft_monitor_addr_filter_data *address_filter = NULL;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int err;
|
||||
|
||||
address_filter = kzalloc(sizeof(*address_filter), GFP_KERNEL);
|
||||
if (!address_filter)
|
||||
return NULL;
|
||||
|
||||
address_filter->state = AF_STATE_ADDING;
|
||||
address_filter->msft_handle = 0xff;
|
||||
address_filter->pattern_handle = handle_data->msft_handle;
|
||||
address_filter->mgmt_handle = handle_data->mgmt_handle;
|
||||
address_filter->rssi_high = handle_data->rssi_high;
|
||||
address_filter->rssi_low = handle_data->rssi_low;
|
||||
address_filter->rssi_low_interval = handle_data->rssi_low_interval;
|
||||
address_filter->rssi_sampling_period = handle_data->rssi_sampling_period;
|
||||
address_filter->addr_type = addr_type;
|
||||
bacpy(&address_filter->bdaddr, bdaddr);
|
||||
|
||||
/* With the above AF_STATE_ADDING, duplicated address filter can be
|
||||
* avoided when receiving monitor device event (found/lost) frequently
|
||||
* for the same device.
|
||||
*/
|
||||
list_add_tail(&address_filter->list, &msft->address_filters);
|
||||
|
||||
err = hci_cmd_sync_queue(hdev, msft_add_address_filter_sync,
|
||||
address_filter, NULL);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "MSFT: Add address %pMR filter err", bdaddr);
|
||||
list_del(&address_filter->list);
|
||||
kfree(address_filter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "MSFT: Add device %pMR address filter",
|
||||
&address_filter->bdaddr);
|
||||
|
||||
return address_filter;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct msft_monitor_addr_filter_data *n, *address_filter = NULL;
|
||||
struct msft_ev_le_monitor_device *ev;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
u16 mgmt_handle = 0xffff;
|
||||
u8 addr_type;
|
||||
|
||||
ev = msft_skb_pull(hdev, skb, MSFT_EV_LE_MONITOR_DEVICE, sizeof(*ev));
|
||||
@ -662,9 +992,53 @@ static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
ev->monitor_state, &ev->bdaddr);
|
||||
|
||||
handle_data = msft_find_handle_data(hdev, ev->monitor_handle, false);
|
||||
if (!handle_data)
|
||||
return;
|
||||
|
||||
if (!test_bit(HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER, &hdev->quirks)) {
|
||||
if (!handle_data)
|
||||
return;
|
||||
mgmt_handle = handle_data->mgmt_handle;
|
||||
goto report_state;
|
||||
}
|
||||
|
||||
if (handle_data) {
|
||||
/* Don't report any device found/lost event from pattern
|
||||
* monitors. Pattern monitor always has its address filters for
|
||||
* tracking devices.
|
||||
*/
|
||||
|
||||
address_filter = msft_find_address_data(hdev, ev->addr_type,
|
||||
&ev->bdaddr,
|
||||
handle_data->msft_handle);
|
||||
if (address_filter)
|
||||
return;
|
||||
|
||||
if (ev->monitor_state && handle_data->cond_type ==
|
||||
MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN)
|
||||
msft_add_address_filter(hdev, ev->addr_type,
|
||||
&ev->bdaddr, handle_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* This device event is not from pattern monitor.
|
||||
* Report it if there is a corresponding address_filter for it.
|
||||
*/
|
||||
list_for_each_entry(n, &msft->address_filters, list) {
|
||||
if (n->state == AF_STATE_ADDED &&
|
||||
n->msft_handle == ev->monitor_handle) {
|
||||
mgmt_handle = n->mgmt_handle;
|
||||
address_filter = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!address_filter) {
|
||||
bt_dev_warn(hdev, "MSFT: Unexpected device event %pMR, %u, %u",
|
||||
&ev->bdaddr, ev->monitor_handle, ev->monitor_state);
|
||||
return;
|
||||
}
|
||||
|
||||
report_state:
|
||||
switch (ev->addr_type) {
|
||||
case ADDR_LE_DEV_PUBLIC:
|
||||
addr_type = BDADDR_LE_PUBLIC;
|
||||
@ -681,12 +1055,18 @@ static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev->monitor_state)
|
||||
msft_device_found(hdev, &ev->bdaddr, addr_type,
|
||||
handle_data->mgmt_handle);
|
||||
else
|
||||
msft_device_lost(hdev, &ev->bdaddr, addr_type,
|
||||
handle_data->mgmt_handle);
|
||||
if (ev->monitor_state) {
|
||||
msft_device_found(hdev, &ev->bdaddr, addr_type, mgmt_handle);
|
||||
} else {
|
||||
if (address_filter && address_filter->state == AF_STATE_ADDED) {
|
||||
address_filter->state = AF_STATE_REMOVING;
|
||||
hci_cmd_sync_queue(hdev,
|
||||
msft_cancel_address_filter_sync,
|
||||
address_filter,
|
||||
NULL);
|
||||
}
|
||||
msft_device_lost(hdev, &ev->bdaddr, addr_type, mgmt_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
|
||||
@ -724,7 +1104,9 @@ void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
|
||||
|
||||
switch (*evt) {
|
||||
case MSFT_EV_LE_MONITOR_DEVICE:
|
||||
mutex_lock(&msft->filter_lock);
|
||||
msft_monitor_device_evt(hdev, skb);
|
||||
mutex_unlock(&msft->filter_lock);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -268,18 +268,16 @@ static struct proto rfcomm_proto = {
|
||||
.obj_size = sizeof(struct rfcomm_pinfo)
|
||||
};
|
||||
|
||||
static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio, int kern)
|
||||
static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern)
|
||||
{
|
||||
struct rfcomm_dlc *d;
|
||||
struct sock *sk;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &rfcomm_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
|
||||
|
||||
d = rfcomm_dlc_alloc(prio);
|
||||
if (!d) {
|
||||
sk_free(sk);
|
||||
@ -298,11 +296,6 @@ static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int
|
||||
sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
|
||||
sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
bt_sock_link(&rfcomm_sk_list, sk);
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
@ -68,7 +68,6 @@ struct sco_pinfo {
|
||||
bdaddr_t dst;
|
||||
__u32 flags;
|
||||
__u16 setting;
|
||||
__u8 cmsg_mask;
|
||||
struct bt_codec codec;
|
||||
struct sco_conn *conn;
|
||||
};
|
||||
@ -471,15 +470,6 @@ static void sco_sock_close(struct sock *sk)
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
static void sco_skb_put_cmsg(struct sk_buff *skb, struct msghdr *msg,
|
||||
struct sock *sk)
|
||||
{
|
||||
if (sco_pi(sk)->cmsg_mask & SCO_CMSG_PKT_STATUS)
|
||||
put_cmsg(msg, SOL_BLUETOOTH, BT_SCM_PKT_STATUS,
|
||||
sizeof(bt_cb(skb)->sco.pkt_status),
|
||||
&bt_cb(skb)->sco.pkt_status);
|
||||
}
|
||||
|
||||
static void sco_sock_init(struct sock *sk, struct sock *parent)
|
||||
{
|
||||
BT_DBG("sk %p", sk);
|
||||
@ -488,8 +478,6 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->flags = bt_sk(parent)->flags;
|
||||
security_sk_clone(parent, sk);
|
||||
} else {
|
||||
bt_sk(sk)->skb_put_cmsg = sco_skb_put_cmsg;
|
||||
}
|
||||
}
|
||||
|
||||
@ -504,21 +492,13 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto, kern);
|
||||
sk = bt_sock_alloc(net, sock, &sco_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
|
||||
|
||||
sk->sk_destruct = sco_sock_destruct;
|
||||
sk->sk_sndtimeo = SCO_CONN_TIMEOUT;
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
|
||||
sco_pi(sk)->codec.id = BT_CODEC_CVSD;
|
||||
sco_pi(sk)->codec.cid = 0xffff;
|
||||
@ -915,9 +895,9 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
if (opt)
|
||||
sco_pi(sk)->cmsg_mask |= SCO_CMSG_PKT_STATUS;
|
||||
set_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
|
||||
else
|
||||
sco_pi(sk)->cmsg_mask &= SCO_CMSG_PKT_STATUS;
|
||||
clear_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
|
||||
break;
|
||||
|
||||
case BT_CODEC:
|
||||
@ -1048,7 +1028,6 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
int len, err = 0;
|
||||
struct bt_voice voice;
|
||||
u32 phys;
|
||||
int pkt_status;
|
||||
int buf_len;
|
||||
struct codec_list *c;
|
||||
u8 num_codecs, i, __user *ptr;
|
||||
@ -1102,9 +1081,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
pkt_status = (sco_pi(sk)->cmsg_mask & SCO_CMSG_PKT_STATUS);
|
||||
|
||||
if (put_user(pkt_status, (int __user *)optval))
|
||||
if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
|
||||
(int __user *)optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user