summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-30 10:59:26 +0000
committerMark Brown <broonie@kernel.org>2026-10-30 10:59:26 +0000
commitf7fd1491821bd8e6e0a013fa8d402e55e0bac6eb (patch)
tree7f4e2678b71b56ba753641e183ccd74fc599af2f
parentaf007b6a981e350d3a6c2dc8d15913eef5d2fc81 (diff)
parent24a4c2f1f7cea608a3f8a94c023bb798093d1755 (diff)
downloadlinux-next-f7fd1491821bd8e6e0a013fa8d402e55e0bac6eb.tar.gz
linux-next-f7fd1491821bd8e6e0a013fa8d402e55e0bac6eb.zip
Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
-rw-r--r--drivers/bluetooth/Makefile2
-rw-r--r--drivers/bluetooth/btintel.c24
-rw-r--r--drivers/bluetooth/btintel_pcie.c591
-rw-r--r--drivers/bluetooth/btintel_pcie.h46
-rw-r--r--drivers/bluetooth/btintel_pcie_debug.c331
-rw-r--r--drivers/bluetooth/btintel_pcie_debug.h171
-rw-r--r--drivers/bluetooth/btmtk.c44
-rw-r--r--drivers/bluetooth/btqca.c3
-rw-r--r--drivers/bluetooth/hci_nokia.c9
-rw-r--r--include/net/bluetooth/hci.h3
-rw-r--r--include/net/bluetooth/hci_core.h106
-rw-r--r--net/bluetooth/hci_conn.c92
-rw-r--r--net/bluetooth/hci_core.c136
-rw-r--r--net/bluetooth/hci_event.c197
-rw-r--r--net/bluetooth/hci_sync.c186
-rw-r--r--net/bluetooth/iso.c20
-rw-r--r--net/bluetooth/l2cap_core.c18
-rw-r--r--net/bluetooth/mgmt.c22
-rw-r--r--net/bluetooth/smp.c24
19 files changed, 1672 insertions, 353 deletions
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index d5eb2568af2e..7d933755b291 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_INTEL) += btintel.o
-obj-$(CONFIG_BT_INTEL_PCIE) += btintel_pcie.o btintel.o
+obj-$(CONFIG_BT_INTEL_PCIE) += btintel_pcie.o btintel.o btintel_pcie_debug.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 0709fb3212f9..929018428c19 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -1201,7 +1201,29 @@ static int btintel_download_firmware_payload(struct hci_dev *hdev,
err = -EINVAL;
while (fw_ptr - fw->data < fw->size) {
- struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
+ struct hci_command_hdr *cmd;
+ size_t remain = fw->size - (fw_ptr - fw->data);
+
+ /* Ensure the header fits before dereferencing */
+ if (frag_len + sizeof(*cmd) > remain) {
+ bt_dev_err(hdev,
+ "Firmware buffer overrun at header (frag_len %u, need %zu, remain %zu)",
+ frag_len, frag_len + sizeof(*cmd), remain);
+ err = -EINVAL;
+ goto done;
+ }
+
+ cmd = (void *)(fw_ptr + frag_len);
+
+ /* Ensure the full command (header + payload) fits before use */
+ if (frag_len + sizeof(*cmd) + cmd->plen > remain) {
+ bt_dev_err(hdev,
+ "Firmware buffer overrun at payload (frag_len %u, plen %u, need %zu, remain %zu)",
+ frag_len, cmd->plen,
+ frag_len + sizeof(*cmd) + cmd->plen, remain);
+ err = -EINVAL;
+ goto done;
+ }
frag_len += sizeof(*cmd) + cmd->plen;
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 91b1ee9d6010..df95d16a163c 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -205,6 +205,108 @@ static inline bool btintel_pcie_dbg_to_wifi(struct btintel_pcie_data *data)
return data->dbg_path_cache != BTINTEL_PCIE_DRAM;
}
+/* Name of the optional INI file used to override the default dbg_path
+ * (data->dbg_path_cache) and yoyo ini settings.
+ */
+#define BTINTEL_PCIE_DBG_INI_FILE "ibt_bt_dbg_cfg.ini"
+
+/* Loads optional "DBG_PATH=<value>" and ENABLE_INI=<value>
+ * overrides from BTINTEL_PCIE_DBG_INI_FILE
+ * and applies them to data->dbg_path_cache and data->enable_ini.
+ *
+ * DBG_PATH accepts BTINTEL_PCIE_WIFI_DBGC (0x06, default) and
+ * BTINTEL_PCIE_DRAM (0x01, legacy); an invalid or missing entry leaves
+ * the cached value set by the caller unchanged.
+ *
+ * ENABLE_INI accepts 1 (enabled) or 0 (disabled); an invalid or missing
+ * entry leaves data->enable_ini unchanged.
+ */
+static void btintel_pcie_load_dbg_path_from_ini(struct btintel_pcie_data *data)
+{
+ const struct firmware *fw;
+ const char *ini_file = BTINTEL_PCIE_DBG_INI_FILE;
+ char *buf, *cursor, *line;
+ bool dbg_path_applied = false;
+ int err;
+
+ err = request_firmware_direct(&fw, ini_file, &data->pdev->dev);
+ if (err < 0) {
+ BT_DBG("%s not found (%d), using dbg_path=0x%02x",
+ ini_file, err, data->dbg_path_cache);
+ if (data->dbg_path_cache != BTINTEL_PCIE_DRAM)
+ data->enable_ini = false;
+ return;
+ }
+
+ buf = kmemdup_nul(fw->data, fw->size, GFP_KERNEL);
+ release_firmware(fw);
+ if (!buf)
+ return;
+
+ cursor = buf;
+ while ((line = strsep(&cursor, "\n"))) {
+ char *eq;
+ char *val;
+ uint parsed;
+
+ line = strim(line);
+ if (!line[0] || line[0] == '#' || line[0] == ';')
+ continue;
+
+ eq = strchr(line, '=');
+ if (!eq)
+ continue;
+
+ *eq = '\0';
+ line = strim(line);
+ val = strim(eq + 1);
+
+ /* Accept "DBG_PATH"/"dbg_path"
+ * case-insensitively as the key.
+ */
+ if (!strcasecmp(line, "DBG_PATH")) {
+ BT_INFO("Read DBG_PATH from %s: '%s'", ini_file, val);
+
+ /* base 0 lets kstrtouint accept "0x06", "6", etc. */
+ if (kstrtouint(val, 0, &parsed) == 0 &&
+ (parsed == BTINTEL_PCIE_DRAM ||
+ parsed == BTINTEL_PCIE_WIFI_DBGC)) {
+ data->dbg_path_cache = parsed;
+ dbg_path_applied = true;
+ BT_INFO("WRT dbg_path accepted: 0x%02x",
+ data->dbg_path_cache);
+ } else {
+ BT_WARN("Invalid DBG_PATH='%s' in %s, using dbg_path=0x%02x",
+ val, ini_file, data->dbg_path_cache);
+ }
+ } else if (!strcasecmp(line, "ENABLE_INI")) {
+ BT_INFO("Read ENABLE_INI from %s: '%s'", ini_file, val);
+
+ if (kstrtouint(val, 0, &parsed) == 0 &&
+ (parsed == 0 || parsed == 1)) {
+ data->enable_ini = !!parsed;
+ BT_INFO("WRT enable_ini accepted: %u",
+ data->enable_ini);
+ } else {
+ BT_WARN("Invalid ENABLE_INI='%s' in %s, using enable_ini=%u",
+ val, ini_file, data->enable_ini);
+ }
+ }
+ }
+
+ if (data->dbg_path_cache != BTINTEL_PCIE_DRAM)
+ data->enable_ini = false;
+
+ if (dbg_path_applied)
+ BT_DBG("Loaded dbg_path=0x%02x from %s", data->dbg_path_cache, ini_file);
+ else
+ BT_WARN("No valid DBG_PATH in %s, dbg_path=0x%02x", ini_file, data->dbg_path_cache);
+
+ BT_DBG("ENABLE_INI effective state: %u", data->enable_ini);
+
+ kfree(buf);
+}
+
/* Helper function to allocate and setup a debug buffer group
* @data: driver data structure
* @buf: pointer to data_buf array pointer
@@ -220,7 +322,8 @@ static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
void **v_addr,
struct btintel_pcie_dbgc_ctxt_buf *frag,
u32 buf_index,
- u32 buf_count)
+ u32 buf_count,
+ u32 per_buf_size)
{
struct data_buf *b;
int i;
@@ -233,8 +336,7 @@ static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
}
*v_addr = dmam_alloc_coherent(&data->pdev->dev,
- buf_count *
- BTINTEL_PCIE_DBGC_BUFFER_SIZE,
+ buf_count * per_buf_size,
p_addr,
GFP_KERNEL | __GFP_NOWARN);
if (!*v_addr) {
@@ -244,16 +346,40 @@ static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
for (i = 0; i < buf_count; i++) {
b = &(*buf)[i];
- b->data_p_addr = *p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
- b->data = *v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ b->data_p_addr = *p_addr + i * per_buf_size;
+ b->data = *v_addr + i * per_buf_size;
frag[i].buf_addr_lsb = lower_32_bits(b->data_p_addr);
frag[i].buf_addr_msb = upper_32_bits(b->data_p_addr);
- frag[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ frag[i].buf_size = per_buf_size;
}
+ BT_INFO("dbgc buf %u allocated: frag_num=%u per_buf_size=%u total_size=%u",
+ buf_index + 1, buf_count, per_buf_size, buf_count * per_buf_size);
+
return 0;
}
+/* Returns false if YoYo is enabled and @alloc_id is not configured */
+static bool btintel_pcie_get_mdbgc_cfg(struct btintel_pcie_data *data,
+ u32 alloc_id, u32 *count, u32 *size)
+{
+ const struct btintel_pcie_buf_alloc *ba;
+
+ ba = btintel_pcie_yoyo_get_buf_alloc(&data->yoyo, alloc_id);
+ if (ba) {
+ *count = ba->max_num_frags;
+ *size = ba->req_buf_size / ba->max_num_frags;
+ return true;
+ }
+
+ if (data->yoyo.enabled)
+ return false;
+
+ *count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+ *size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ return true;
+}
+
/* This function initializes the memory for MDBGC buffers and formats the
* DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
* size as the payload
@@ -263,10 +389,9 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
struct btintel_pcie_mdbgc_ctxt db_frag;
u32 frag_size = sizeof(db_frag);
void *frag_v_addr;
+ u32 buf_count, buf_size;
int err;
- data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
-
/* Allocate fragment context structure */
frag_v_addr = dmam_alloc_coherent(&data->pdev->dev, frag_size,
&data->mdbgc.frag_p_addr,
@@ -285,46 +410,71 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
db_frag.ver = BTINTEL_PCIE_MDBGC_FRAG_VERSION;
/* Allocate DBGC buffer 1 */
- db_frag.buf1_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_1;
- db_frag.buf1_count = data->mdbgc.count;
- err = btintel_pcie_alloc_dbgc_buf(data,
- &data->mdbgc.buf1,
- &data->mdbgc.buf1_p_addr,
- &data->mdbgc.buf1_v_addr,
- db_frag.buf1,
- 0,
- data->mdbgc.count);
- if (err)
- return err;
+ if (btintel_pcie_get_mdbgc_cfg(data, BTINTEL_PCIE_ALLOC_ID_DBGC1,
+ &buf_count, &buf_size)) {
+ db_frag.buf1_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_1;
+ data->mdbgc.buf1_count = buf_count;
+ data->mdbgc.buf1_size = buf_size;
+ db_frag.buf1_count = buf_count;
+
+ err = btintel_pcie_alloc_dbgc_buf(data,
+ &data->mdbgc.buf1,
+ &data->mdbgc.buf1_p_addr,
+ &data->mdbgc.buf1_v_addr,
+ db_frag.buf1,
+ 0,
+ buf_count,
+ buf_size);
+ if (err)
+ return err;
+ }
/* Allocate DBGC buffer 2 */
- db_frag.buf2_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_2;
- db_frag.buf2_count = data->mdbgc.count;
- err = btintel_pcie_alloc_dbgc_buf(data,
- &data->mdbgc.buf2,
- &data->mdbgc.buf2_p_addr,
- &data->mdbgc.buf2_v_addr,
- db_frag.buf2,
- 1,
- data->mdbgc.count);
- if (err)
- return err;
+ if (btintel_pcie_get_mdbgc_cfg(data, BTINTEL_PCIE_ALLOC_ID_DBGC2,
+ &buf_count, &buf_size)) {
+ db_frag.buf2_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_2;
+ data->mdbgc.buf2_count = buf_count;
+ data->mdbgc.buf2_size = buf_size;
+ db_frag.buf2_count = buf_count;
+
+ err = btintel_pcie_alloc_dbgc_buf(data,
+ &data->mdbgc.buf2,
+ &data->mdbgc.buf2_p_addr,
+ &data->mdbgc.buf2_v_addr,
+ db_frag.buf2,
+ 1,
+ buf_count,
+ buf_size);
+ if (err)
+ return err;
+ }
/* Allocate DBGC buffer 3 */
- db_frag.buf3_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_3;
- db_frag.buf3_count = data->mdbgc.count;
- err = btintel_pcie_alloc_dbgc_buf(data,
- &data->mdbgc.buf3,
- &data->mdbgc.buf3_p_addr,
- &data->mdbgc.buf3_v_addr,
- db_frag.buf3,
- 2,
- data->mdbgc.count);
- if (err)
- return err;
+ if (btintel_pcie_get_mdbgc_cfg(data, BTINTEL_PCIE_ALLOC_ID_DBGC3,
+ &buf_count, &buf_size)) {
+ db_frag.buf3_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_3;
+ data->mdbgc.buf3_count = buf_count;
+ data->mdbgc.buf3_size = buf_size;
+ db_frag.buf3_count = buf_count;
+
+ err = btintel_pcie_alloc_dbgc_buf(data,
+ &data->mdbgc.buf3,
+ &data->mdbgc.buf3_p_addr,
+ &data->mdbgc.buf3_v_addr,
+ db_frag.buf3,
+ 2,
+ buf_count,
+ buf_size);
+ if (err)
+ return err;
+ }
/* Copy fragment to DMA coherent memory */
memcpy(data->mdbgc.frag_v_addr, &db_frag, sizeof(db_frag));
+ BT_INFO("MDBGC: buf1 frag=%u size=%u; buf2 frag=%u size=%u; buf3 frag=%u size=%u",
+ db_frag.buf1_count, db_frag.buf1_count * db_frag.buf1[0].buf_size,
+ db_frag.buf2_count, db_frag.buf2_count * db_frag.buf2[0].buf_size,
+ db_frag.buf3_count, db_frag.buf3_count * db_frag.buf3[0].buf_size);
return 0;
}
@@ -336,9 +486,21 @@ static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
{
struct btintel_pcie_dbgc_ctxt db_frag;
u32 frag_size = sizeof(db_frag);
+ const struct btintel_pcie_buf_alloc *ba;
+ u32 buf_count, buf_size;
int err;
- data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+ ba = btintel_pcie_yoyo_get_buf_alloc(&data->yoyo,
+ BTINTEL_PCIE_ALLOC_ID_DBGC1);
+ buf_count = ba ? ba->max_num_frags : BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+ buf_size = ba ? ba->req_buf_size / ba->max_num_frags :
+ BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+
+ BT_INFO("dbgc setup: frag_num=%u per_buf_size=%u total_size=%u",
+ buf_count, buf_size, buf_count * buf_size);
+
+ data->dbgc.count = buf_count;
+ data->dbgc.buf_size = buf_size;
/* Allocate fragment context structure */
data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
@@ -356,8 +518,9 @@ static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
memset(&db_frag, 0, sizeof(db_frag));
db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
- db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
- db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
+ db_frag.total_size = sizeof(db_frag.num_buf) +
+ buf_count * sizeof(db_frag.bufs[0]);
+ db_frag.num_buf = buf_count;
/* Allocate DBGC buffers */
err = btintel_pcie_alloc_dbgc_buf(data,
@@ -366,7 +529,8 @@ static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
&data->dbgc.buf_v_addr,
db_frag.bufs,
0,
- data->dbgc.count);
+ buf_count,
+ buf_size);
if (err)
return err;
@@ -859,7 +1023,8 @@ static u32 btintel_pcie_dump_list_total_size(struct list_head *list)
}
static int btintel_pcie_dump_dram(struct list_head *list,
- u8 count, struct data_buf *bufs,
+ u8 count, u32 buf_size,
+ struct data_buf *bufs,
u32 write_ptr, u32 wrap_ctr, u32 cur_frag,
u32 region_id, const char *name)
{
@@ -871,7 +1036,7 @@ static int btintel_pcie_dump_dram(struct list_head *list,
int i;
mon_hdr_size = sizeof(*mon_dump);
- ranges_size = count * (sizeof(*range) + BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+ ranges_size = count * (sizeof(*range) + buf_size);
payload_size = mon_hdr_size + ranges_size;
total_size = sizeof(*dump_data) + payload_size;
@@ -900,12 +1065,10 @@ static int btintel_pcie_dump_dram(struct list_head *list,
range = (void *)mon_dump->data;
for (i = 0; i < count; i++) {
- range->range_data_size =
- cpu_to_le32(BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+ range->range_data_size = cpu_to_le32(buf_size);
range->dram_base_addr = cpu_to_le64(bufs[i].data_p_addr);
- memcpy(range->data, bufs[i].data,
- BTINTEL_PCIE_DBGC_BUFFER_SIZE);
- range = (void *)range->data + BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ memcpy(range->data, bufs[i].data, buf_size);
+ range = (void *)range->data + buf_size;
}
list_add_tail(&entry->list, list);
@@ -914,7 +1077,8 @@ static int btintel_pcie_dump_dram(struct list_head *list,
static int
btintel_pcie_dump_dram_monitor(struct btintel_pcie_data *data,
- struct list_head *list, u8 count,
+ struct list_head *list, u8 count,
+ u32 buf_size,
struct data_buf *bufs, u32 status_reg,
u32 wrap_reg, u8 allocation_id, u32 region_id,
const char *name)
@@ -922,6 +1086,9 @@ btintel_pcie_dump_dram_monitor(struct btintel_pcie_data *data,
u32 allocation_offset, write_ptr, wrap_ctr, wr_ptr_status;
u8 cur_frag;
+ if (!count)
+ return -ENOENT;
+
allocation_offset = allocation_id *
BTINTEL_PCIE_DBGC_ALLOCATION_OFFSET;
wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg +
@@ -943,8 +1110,8 @@ btintel_pcie_dump_dram_monitor(struct btintel_pcie_data *data,
"allocation=%u wr_ptr_status=0x%08x write_ptr=0x%06x cur_frag=%u wrap_ctr=0x%08x",
allocation_id, wr_ptr_status, write_ptr, cur_frag, wrap_ctr);
- return btintel_pcie_dump_dram(list, count, bufs, write_ptr, wrap_ctr,
- cur_frag, region_id, name);
+ return btintel_pcie_dump_dram(list, count, buf_size, bufs, write_ptr,
+ wrap_ctr, cur_frag, region_id, name);
}
static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data,
@@ -1225,6 +1392,7 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
u8 hw_variant;
u32 file_len;
u8 count;
+ u32 buf_size;
int ret;
LIST_HEAD(dump_list);
@@ -1233,10 +1401,12 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
if (btintel_pcie_is_mdbgc_supported(data)) {
mdbgc = &data->mdbgc;
- count = mdbgc->count;
+ count = mdbgc->buf1_count;
+ buf_size = mdbgc->buf1_size;
} else {
dbgc = &data->dbgc;
count = dbgc->count;
+ buf_size = dbgc->buf_size;
}
hw_variant = INTEL_HW_VARIANT(data->cnvi);
@@ -1256,7 +1426,9 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
}
if (btintel_pcie_is_mdbgc_supported(data)) {
- ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+ ret = btintel_pcie_dump_dram_monitor(data, &dump_list,
+ mdbgc->buf1_count,
+ mdbgc->buf1_size,
mdbgc->buf1, status_reg,
wrap_reg,
BTINTEL_PCIE_MDBGC_ALLOCATIONID_1,
@@ -1265,10 +1437,12 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
if (!ret)
regions_mask |=
BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
- else
+ else if (ret != -ENOENT)
bt_dev_warn(hdev, "Failed to dump DRAM buf1: %d", ret);
- ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+ ret = btintel_pcie_dump_dram_monitor(data, &dump_list,
+ mdbgc->buf2_count,
+ mdbgc->buf2_size,
mdbgc->buf2, status_reg,
wrap_reg,
BTINTEL_PCIE_MDBGC_ALLOCATIONID_2,
@@ -1277,10 +1451,12 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
if (!ret)
regions_mask |=
BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR2);
- else
+ else if (ret != -ENOENT)
bt_dev_warn(hdev, "Failed to dump DRAM buf2: %d", ret);
- ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+ ret = btintel_pcie_dump_dram_monitor(data, &dump_list,
+ mdbgc->buf3_count,
+ mdbgc->buf3_size,
mdbgc->buf3, status_reg,
wrap_reg,
BTINTEL_PCIE_MDBGC_ALLOCATIONID_3,
@@ -1289,11 +1465,12 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
if (!ret)
regions_mask |=
BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR3);
- else
+ else if (ret != -ENOENT)
bt_dev_warn(hdev, "Failed to dump DRAM buf3: %d", ret);
} else {
ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
- dbgc->bufs, status_reg,
+ buf_size, dbgc->bufs,
+ status_reg,
wrap_reg,
BTINTEL_PCIE_MDBGC_ALLOCATIONID_1,
BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
@@ -2710,15 +2887,149 @@ static inline bool btintel_pcie_is_txackq_empty(struct btintel_pcie_data *data)
return data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] == data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
}
+static bool btintel_pcie_hw_rfkill_doorbell(struct btintel_pcie_data *data,
+ bool state)
+{
+ unsigned long flags;
+ u32 reg;
+ int err;
+
+ /* Set under ipc_lock so a late GP2 for a previous doorbell cannot
+ * complete this one.
+ */
+ spin_lock_irqsave(&data->ipc_lock, flags);
+ WRITE_ONCE(data->rfkill_wait_state, state);
+ WRITE_ONCE(data->rfkill_wait_done, false);
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
+ if (state)
+ reg |= BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL;
+ else
+ reg &= ~BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL;
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG, reg);
+ spin_unlock_irqrestore(&data->ipc_lock, flags);
+
+ err = wait_event_timeout(data->rfkill_wait_q,
+ READ_ONCE(data->rfkill_wait_done),
+ msecs_to_jiffies(BTINTEL_PCIE_HW_RFKILL_INTERRUPT_TIMEOUT));
+ if (!err) {
+ bt_dev_err(data->hdev, "Timeout (%u ms) on alive interrupt for RFKILL",
+ BTINTEL_PCIE_HW_RFKILL_INTERRUPT_TIMEOUT);
+ return false;
+ }
+
+ return true;
+}
+
+static void btintel_pcie_hw_rfkill_delay_work(struct work_struct *wk)
+{
+ struct btintel_pcie_data *data =
+ container_of(wk, struct btintel_pcie_data, hw_rfkill_work.work);
+ u32 func_ctrl, ipc_ctrl;
+ bool set_rfkill, first_check, released;
+
+ if (!data->hdev)
+ return;
+
+ /* Firmware is not ready for the rfkill handshake before setup
+ * completes; setup schedules its own state check afterwards.
+ */
+ if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
+ return;
+
+ /* Do not report an initial clear when rfkill was never asserted.
+ * After resume, however, a previously asserted rfkill must be
+ * cleared and reported to the HCI stack.
+ */
+ first_check = test_and_clear_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK,
+ &data->flags);
+
+ func_ctrl = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ ipc_ctrl = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
+
+ set_rfkill = !(func_ctrl & BTINTEL_PCIE_CSR_FUNC_CTRL_HW_RFKILL) &&
+ !(ipc_ctrl & BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL);
+ if (set_rfkill) {
+ bt_dev_dbg(data->hdev, "hw rfkill on");
+ set_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags);
+ hci_rfkill_set_hw_state(data->hdev, true);
+ } else {
+ bt_dev_dbg(data->hdev, "hw rfkill off");
+ /* Nothing changed at the initial post-setup check, so there
+ * is no transition to report.
+ */
+ if (first_check &&
+ !test_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags))
+ return;
+
+ /* Switch still asserted; IPC_CONTROL only holds the ack that
+ * shutdown sent, so this is not a release.
+ */
+ if (!(func_ctrl & BTINTEL_PCIE_CSR_FUNC_CTRL_HW_RFKILL))
+ return;
+
+ mutex_lock(&data->rfkill_doorbell_lock);
+ released = btintel_pcie_hw_rfkill_doorbell(data, false);
+ if (released)
+ clear_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags);
+ mutex_unlock(&data->rfkill_doorbell_lock);
+
+ if (released)
+ hci_rfkill_set_hw_state(data->hdev, false);
+ }
+}
+
+static void btintel_delayed_hw_rfkill(struct btintel_pcie_data *data,
+ bool delay)
+{
+ mod_delayed_work(system_wq, &data->hw_rfkill_work,
+ delay ? msecs_to_jiffies(BTINTEL_PCIE_HW_RFKILL_DELAY) : 0);
+}
+
+static void btintel_pcie_msix_gp2_handler(struct btintel_pcie_data *data)
+{
+ unsigned long flags;
+ u32 reg, boot_stage_reg;
+ bool state, done;
+
+ spin_lock_irqsave(&data->ipc_lock, flags);
+ boot_stage_reg = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG);
+ state = READ_ONCE(data->rfkill_wait_state);
+
+ if (state &&
+ (reg & BTINTEL_PCIE_CSR_IPC_STATUS_HW_RFKILL) &&
+ (boot_stage_reg & BTINTEL_PCIE_CSR_BOOT_STAGE_HW_RFKILL_STATE)) {
+ bt_dev_dbg(data->hdev,
+ "firmware done handling HW RF-KILL SET state update");
+ WRITE_ONCE(data->rfkill_wait_done, true);
+ }
+
+ if (!state &&
+ !(reg & BTINTEL_PCIE_CSR_IPC_STATUS_HW_RFKILL) &&
+ !(boot_stage_reg & BTINTEL_PCIE_CSR_BOOT_STAGE_HW_RFKILL_STATE)) {
+ bt_dev_dbg(data->hdev,
+ "firmware done handling HW RF-KILL CLEAR state update");
+ WRITE_ONCE(data->rfkill_wait_done, true);
+ }
+
+ done = READ_ONCE(data->rfkill_wait_done);
+ spin_unlock_irqrestore(&data->ipc_lock, flags);
+
+ if (done)
+ wake_up(&data->rfkill_wait_q);
+}
+
static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
{
struct msix_entry *entry = dev_id;
struct btintel_pcie_data *data = btintel_pcie_get_data(entry);
- u32 intr_fh, intr_hw;
+ u32 intr_fh, intr_hw, hw_mask;
spin_lock(&data->irq_lock);
intr_fh = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES);
intr_hw = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES);
+ hw_mask = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK);
/* Clear causes registers to avoid being handling the same cause */
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES, intr_fh);
@@ -2740,6 +3051,8 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1)
btintel_pcie_msix_gp1_handler(data);
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP2)
+ btintel_pcie_msix_gp2_handler(data);
/* For TX */
if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0) {
@@ -2758,6 +3071,11 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG)
btintel_pcie_msix_fw_trigger_handler(data);
+ /* A masked cause still latches; ignore it once masked. */
+ if ((intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL) &&
+ !(hw_mask & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL))
+ btintel_delayed_hw_rfkill(data, false);
+
/* This interrupt is triggered by the firmware after updating
* boot_stage register and image_response register
*/
@@ -2835,8 +3153,10 @@ static struct btintel_pcie_causes_list causes_list[] = {
{ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x00 },
{ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP2, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x22 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 },
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x25 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x27 },
};
/* This function configures the interrupt masks for both HW_INT_CAUSES and
@@ -3185,7 +3505,7 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
err = btintel_pcie_setup_dbgc(data);
}
if (err)
- goto exit_error_txq;
+ goto exit_error_rxq;
/* Setup Context Information */
data->ci = v_addr;
@@ -3194,8 +3514,13 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
/* Initialize the CI */
btintel_pcie_init_ci(data, data->ci);
+ data->pcie_alloc_init = true;
+
return 0;
+exit_error_rxq:
+ /* RX setup succeeded, so release its buffers before the shared unwind. */
+ btintel_pcie_free_rxq_bufs(data, &data->rxq);
exit_error_txq:
btintel_pcie_free_txq_bufs(data, &data->txq);
exit_error_pool:
@@ -3353,6 +3678,25 @@ static void btintel_pcie_disable_interrupts(struct btintel_pcie_data *data)
spin_unlock(&data->irq_lock);
}
+/* irq_lock is never taken in hardirq context: the MSI-X handler is a
+ * threaded IRQ with no primary handler, so plain spin_lock() is enough.
+ */
+static void btintel_pcie_mask_hw_rfkill(struct btintel_pcie_data *data)
+{
+ spin_lock(&data->irq_lock);
+ btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK,
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL);
+ spin_unlock(&data->irq_lock);
+}
+
+static void btintel_pcie_unmask_hw_rfkill(struct btintel_pcie_data *data)
+{
+ spin_lock(&data->irq_lock);
+ btintel_pcie_clr_reg_bits(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK,
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL);
+ spin_unlock(&data->irq_lock);
+}
+
static void btintel_pcie_enable_interrupts(struct btintel_pcie_data *data)
{
spin_lock(&data->irq_lock);
@@ -3367,6 +3711,32 @@ static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data)
synchronize_irq(data->msix_entries[i].vector);
}
+static void btintel_pcie_probe_cleanup(struct btintel_pcie_data *data)
+{
+ struct pci_dev *pdev = data->pdev;
+
+ btintel_pcie_disable_interrupts(data);
+ btintel_pcie_synchronize_irqs(data);
+
+ disable_work_sync(&data->coredump_work);
+ disable_work_sync(&data->hwexp_work);
+ disable_work_sync(&data->fwtrigger_work);
+ disable_work_sync(&data->mbox_work);
+ disable_delayed_work_sync(&data->hw_rfkill_work);
+
+ flush_work(&data->rx_work);
+
+ btintel_pcie_reset_bt(data);
+ for (int i = 0; i < data->alloc_vecs; i++) {
+ struct msix_entry *msix_entry;
+
+ msix_entry = &data->msix_entries[i];
+ devm_free_irq(&pdev->dev, msix_entry->vector, msix_entry);
+ }
+
+ pci_free_irq_vectors(pdev);
+}
+
static int btintel_pcie_get_debug_info_addr(struct hci_dev *hdev)
{
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
@@ -3554,8 +3924,12 @@ static int btintel_pcie_setup(struct hci_dev *hdev)
btintel_pcie_start_rx(data);
}
- if (!err)
+ if (!err) {
set_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags);
+ set_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK, &data->flags);
+ btintel_pcie_unmask_hw_rfkill(data);
+ btintel_delayed_hw_rfkill(data, true);
+ }
return err;
}
@@ -3783,18 +4157,20 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
if (!data)
goto out;
+ btintel_pcie_mask_hw_rfkill(data);
+ btintel_pcie_synchronize_irqs(data);
+ disable_delayed_work_sync(&data->hw_rfkill_work);
btintel_pcie_disable_interrupts(data);
btintel_pcie_synchronize_irqs(data);
flush_work(&data->rx_work);
- /* Drain any in-flight dump workers and block new ones across reset.
- * Safe from self-deadlock: they all run on a separate wq.
+ /* Drain any in-flight dump/rfkill workers and block new ones across
+ * reset. Safe from self-deadlock: they all run on a separate wq.
*/
disable_work_sync(&data->coredump_work);
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
disable_work_sync(&data->mbox_work);
-
bt_dev_dbg(data->hdev, "Release bluetooth interface");
/* Both reset paths follow the same contract: on success they
@@ -3819,6 +4195,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
enable_work(&data->hwexp_work);
enable_work(&data->fwtrigger_work);
enable_work(&data->mbox_work);
+ enable_delayed_work(&data->hw_rfkill_work);
}
out:
@@ -4009,6 +4386,30 @@ static struct hci_drv btintel_pcie_hci_drv = {
.specific_handlers = btintel_pcie_hci_drv_specific_handlers,
};
+static int btintel_pcie_shutdown(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+ /* Keep HW rfkill monitoring while HCI is down. On an active
+ * assertion, ack it right before the mandatory HCI reset.
+ *
+ * The cached flag is enough: firmware accepts HCI reset in any
+ * HW rfkill state, also in the middle of the doorbell flow. An
+ * assertion the worker has not seen yet is then handled like one
+ * while HCI is down; the worker still reports the block and
+ * handles the release.
+ *
+ * Not called from remove: hci_unregister_dev() sets HCI_UNREGISTER
+ * first, and hci_dev_close_sync() then skips hdev->shutdown().
+ */
+ mutex_lock(&data->rfkill_doorbell_lock);
+ if (test_bit(BTINTEL_PCIE_HWRFKILL_ON, &data->flags))
+ btintel_pcie_hw_rfkill_doorbell(data, true);
+ mutex_unlock(&data->rfkill_doorbell_lock);
+
+ return btintel_shutdown_combined(hdev);
+}
+
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
{
int err;
@@ -4028,7 +4429,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
hdev->close = btintel_pcie_close;
hdev->send = btintel_pcie_send_frame;
hdev->setup = btintel_pcie_setup;
- hdev->shutdown = btintel_shutdown_combined;
+ hdev->shutdown = btintel_pcie_shutdown;
hdev->hw_error = btintel_pcie_hw_error;
hdev->set_diag = btintel_set_diag;
hdev->set_bdaddr = btintel_set_bdaddr;
@@ -4056,6 +4457,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int err;
+ bool pcie_configured = false;
struct btintel_pcie_data *data;
if (!pdev)
@@ -4069,6 +4471,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
spin_lock_init(&data->irq_lock);
spin_lock_init(&data->hci_rx_lock);
+ spin_lock_init(&data->ipc_lock);
+ mutex_init(&data->rfkill_doorbell_lock);
init_waitqueue_head(&data->gp0_wait_q);
data->gp0_received = false;
@@ -4076,6 +4480,9 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
init_waitqueue_head(&data->tx_wait_q);
data->tx_wait_done = false;
+ init_waitqueue_head(&data->rfkill_wait_q);
+ data->rfkill_wait_done = false;
+
init_waitqueue_head(&data->mbox_parse_wait_q);
data->workqueue = alloc_ordered_workqueue(KBUILD_MODNAME, WQ_HIGHPRI);
@@ -4090,6 +4497,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
skb_queue_head_init(&data->rx_skb_q);
INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
+ INIT_DELAYED_WORK(&data->hw_rfkill_work,
+ btintel_pcie_hw_rfkill_delay_work);
INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
@@ -4099,15 +4508,30 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
data->boot_stage_cache = 0x00;
data->img_resp_cache = 0x00;
data->dbg_path_cache = BTINTEL_PCIE_WIFI_DBGC;
+ data->pcie_alloc_init = false;
+ data->enable_ini = false;
+ btintel_pcie_yoyo_init(&data->yoyo);
/* FLR can be invoked by echoing to debugfs path, so explicitly
* initialized
*/
data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
+
+ btintel_pcie_load_dbg_path_from_ini(data);
+
err = btintel_pcie_config_pcie(pdev, data);
if (err)
goto exit_error;
+ pcie_configured = true;
+
pci_set_drvdata(pdev, data);
+ if (data->enable_ini) {
+ err = btintel_pcie_yoyo_load_and_parse(&data->yoyo, &pdev->dev);
+ if (err) {
+ BT_WARN("YoYo is not configured (%d)", err);
+ btintel_pcie_yoyo_cleanup(&data->yoyo);
+ }
+ }
err = btintel_pcie_alloc(data);
if (err)
@@ -4130,15 +4554,22 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
if (err)
goto exit_error;
+ data->yoyo.hdev = data->hdev;
bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
data->cnvr);
return 0;
exit_error:
- /* reset device before exit */
- btintel_pcie_reset_bt(data);
+ btintel_pcie_yoyo_cleanup(&data->yoyo);
+
+ if (pcie_configured)
+ btintel_pcie_probe_cleanup(data);
+
+ if (data->pcie_alloc_init)
+ btintel_pcie_free(data);
destroy_workqueue(data->dump_workqueue);
+ destroy_workqueue(data->workqueue);
pci_clear_master(pdev);
@@ -4175,6 +4606,12 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
if (current_work() != &data->reset_work)
disable_work_sync(&data->reset_work);
+ btintel_pcie_mask_hw_rfkill(data);
+ btintel_pcie_synchronize_irqs(data);
+ disable_delayed_work_sync(&data->hw_rfkill_work);
+
+ btintel_pcie_yoyo_cleanup(&data->yoyo);
+
btintel_pcie_disable_interrupts(data);
btintel_pcie_synchronize_irqs(data);
@@ -4317,6 +4754,10 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
data = pci_get_drvdata(pdev);
+ btintel_pcie_mask_hw_rfkill(data);
+ btintel_pcie_synchronize_irqs(data);
+ cancel_delayed_work_sync(&data->hw_rfkill_work);
+
/* S0ix (s2idle) uses D3_HOT; S3, freeze and hibernate use D3_COLD. */
if (mesg.event == PM_EVENT_SUSPEND &&
pm_suspend_target_state == PM_SUSPEND_TO_IDLE)
@@ -4330,6 +4771,9 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
err = btintel_pcie_set_dxstate(data, dxstate);
if (err) {
+ btintel_pcie_unmask_hw_rfkill(data);
+ set_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK, &data->flags);
+ btintel_delayed_hw_rfkill(data, true);
bt_dev_err(data->hdev, "Failed to set dxstate:%u (%d)",
dxstate, err);
return err;
@@ -4384,6 +4828,9 @@ static int btintel_pcie_resume_event(struct device *dev, pm_message_t mesg)
if (err) {
bt_dev_err(data->hdev, "Failed to set D0 state (%d)", err);
} else {
+ btintel_pcie_unmask_hw_rfkill(data);
+ set_bit(BTINTEL_PCIE_HW_RFKILL_STATE_CHECK, &data->flags);
+ btintel_delayed_hw_rfkill(data, true);
bt_dev_dbg(data->hdev,
"device entered into d0 state from d3 in %lld us",
ktime_to_us(ktime_get() - start));
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 3030b4e8b750..fff31ac8f87a 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -6,6 +6,8 @@
* Copyright (C) 2024 Intel Corporation
*/
+#include "btintel_pcie_debug.h"
+
/* Control and Status Register(BTINTEL_PCIE_CSR) */
#define BTINTEL_PCIE_CSR_BASE (0x000)
#define BTINTEL_PCIE_CSR_FUNC_CTRL_REG (BTINTEL_PCIE_CSR_BASE + 0x024)
@@ -33,6 +35,7 @@
#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT (BIT(6))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT (BIT(7))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS (BIT(20))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_HW_RFKILL (BIT(27))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ (BIT(21))
@@ -40,6 +43,12 @@
#define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON (BIT(29))
#define BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET (BIT(31))
+/* BTINTEL PCIE CSR IPC Control Register */
+#define BTINTEL_PCIE_CSR_IPC_CONTROL_HW_RFKILL (BIT(7))
+
+/* BTINTEL PCIE CSR IPC Status Register */
+#define BTINTEL_PCIE_CSR_IPC_STATUS_HW_RFKILL (BIT(7))
+
/* Value for BTINTEL_PCIE_CSR_BOOT_STAGE register */
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM (BIT(0))
#define BTINTEL_PCIE_CSR_BOOT_STAGE_IML (BIT(1))
@@ -53,6 +62,7 @@
#define BTINTEL_PCIE_CSR_BOOT_STAGE_ALIVE (BIT(23))
/* Reflects live D-state. Updated by hardware on every D-state transition. */
#define BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY (BIT(24))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_HW_RFKILL_STATE (BIT(25))
#define BTINTEL_PCIE_CSR_DOORBELL_MBOX_READ_CONFIRM (BIT(4))
@@ -135,8 +145,10 @@ enum msix_fh_int_causes {
enum msix_hw_int_causes {
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP2 = BIT(2), /* cause 34 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG = BIT(5), /* cause 37 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HW_RFKILL = BIT(7), /* cause 39 */
};
/* PCIe device states
@@ -155,6 +167,8 @@ enum {
BTINTEL_PCIE_COREDUMP_INPROGRESS,
BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
+ BTINTEL_PCIE_HWRFKILL_ON,
+ BTINTEL_PCIE_HW_RFKILL_STATE_CHECK,
BTINTEL_PCIE_SETUP_DONE,
BTINTEL_PCIE_MAIL_BOX_INTR,
BTINTEL_PCIE_MBOX_PARSE_PENDING,
@@ -215,6 +229,12 @@ enum btintel_pcie_mbox_msg {
#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3
+/* HW rfkill interrupt timeout */
+#define BTINTEL_PCIE_HW_RFKILL_INTERRUPT_TIMEOUT 200
+
+/* HW rfkill check timeout */
+#define BTINTEL_PCIE_HW_RFKILL_DELAY 1000
+
/* The number of descriptors in TX queues */
#define BTINTEL_PCIE_TX_DESCS_COUNT 32
@@ -466,6 +486,7 @@ struct rxq {
*/
struct btintel_pcie_dbgc {
u16 count;
+ u32 buf_size;
void *frag_v_addr;
dma_addr_t frag_p_addr;
@@ -477,7 +498,12 @@ struct btintel_pcie_dbgc {
};
struct btintel_pcie_mdbgc {
- u32 count;
+ u32 buf1_count;
+ u32 buf1_size;
+ u32 buf2_count;
+ u32 buf2_size;
+ u32 buf3_count;
+ u32 buf3_size;
void *frag_v_addr;
dma_addr_t frag_p_addr;
@@ -680,6 +706,7 @@ struct btintel_pcie_ini_dump_info {
* @flags: driver state
* @irq_lock: spinlock for MSI-X
* @hci_rx_lock: spinlock for HCI RX flow
+ * @ipc_lock: protects IPC control writes and the rfkill wait state
* @base_addr: pci base address (from BAR)
* @msix_entries: array of MSI-X entries
* @msix_enabled: true if MSI-X is enabled;
@@ -696,6 +723,11 @@ struct btintel_pcie_ini_dump_info {
* @gp0_wait_q: wait_q for gp0 interrupt
* @tx_wait_done: condition for tx interrupt
* @tx_wait_q: wait_q for tx interrupt
+ * @rfkill_wait_done: condition for HW rfkill GP2 interrupt
+ * @rfkill_wait_state: requested HW rfkill transition (set or clear)
+ * being waited on, used to validate the GP2 acknowledgment
+ * @rfkill_wait_q: wait_q for HW rfkill GP2 interrupt
+ * @rfkill_doorbell_lock: serializes HW rfkill doorbell transitions
* @workqueue: workqueue for RX work
* @rx_skb_q: SKB queue for RX packet
* @rx_work: RX work struct to process the RX packet in @rx_skb_q
@@ -723,6 +755,8 @@ struct btintel_pcie_data {
spinlock_t irq_lock;
/* lock to serialize rx events */
spinlock_t hci_rx_lock;
+ /* lock to serialize IPC control register updates */
+ spinlock_t ipc_lock;
void __iomem *base_addr;
@@ -737,6 +771,8 @@ struct btintel_pcie_data {
u32 boot_stage_cache;
u32 img_resp_cache;
u32 dbg_path_cache;
+ bool enable_ini;
+ struct btintel_pcie_yoyo_ctx yoyo;
u32 cnvi;
u32 cnvr;
@@ -747,9 +783,16 @@ struct btintel_pcie_data {
bool tx_wait_done;
wait_queue_head_t tx_wait_q;
+ bool rfkill_wait_done;
+ bool rfkill_wait_state;
+ wait_queue_head_t rfkill_wait_q;
+ /* serializes HW rfkill doorbell transitions */
+ struct mutex rfkill_doorbell_lock;
+
struct workqueue_struct *workqueue;
struct sk_buff_head rx_skb_q;
struct work_struct rx_work;
+ struct delayed_work hw_rfkill_work;
struct work_struct reset_work;
struct workqueue_struct *dump_workqueue;
@@ -783,6 +826,7 @@ struct btintel_pcie_data {
wait_queue_head_t mbox_parse_wait_q;
/* Timestamp captured in GP1 handler when mbox interrupt is received */
ktime_t mbox_intr_ts;
+ bool pcie_alloc_init;
};
static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
diff --git a/drivers/bluetooth/btintel_pcie_debug.c b/drivers/bluetooth/btintel_pcie_debug.c
new file mode 100644
index 000000000000..9f2d3f32bd99
--- /dev/null
+++ b/drivers/bluetooth/btintel_pcie_debug.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Bluetooth YoYo Debug Framework
+ *
+ * Copyright (C) 2026 Intel Corporation
+ *
+ * Runtime-configurable TLV-based debug collection framework for loading and
+ * parsing the optional YoYo firmware configuration used to enable debug
+ * regions, triggers, HCI commands, and configuration sets.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/unaligned.h>
+#include <net/bluetooth/bluetooth.h>
+#include "btintel_pcie_debug.h"
+
+/* TLV-specific parser DEBUG_INFO TLV type */
+static int btintel_pcie_yoyo_parse_dbg_info(struct btintel_pcie_yoyo_ctx *ctx,
+ const struct btintel_pcie_tlv_dbg_info *info,
+ u32 len)
+{
+ const u8 *data = (const u8 *)info;
+ u32 domain, image_type, core_type, version;
+
+ if (len != sizeof(*info) - sizeof(struct btintel_pcie_tlv_hdr))
+ return -EBADMSG;
+
+ image_type = get_unaligned_le32(data +
+ offsetof(struct btintel_pcie_tlv_dbg_info,
+ image_type));
+ core_type = get_unaligned_le32(data +
+ offsetof(struct btintel_pcie_tlv_dbg_info,
+ core_type));
+ domain = get_unaligned_le32(data +
+ offsetof(struct btintel_pcie_tlv_dbg_info,
+ dbg_hdr.domain));
+ version = get_unaligned_le32(data +
+ offsetof(struct btintel_pcie_tlv_dbg_info,
+ dbg_hdr.tlv_data_version));
+
+ if (domain != BTINTEL_PCIE_DOMAIN_BT ||
+ version != BTINTEL_PCIE_DBG_INFO_TLV_VERSION) {
+ BT_WARN("YoYo: unsupported domain/version (%u/%u), disabling",
+ domain, version);
+ return -EINVAL;
+ }
+
+ if (image_type != BTINTEL_PCIE_IMAGE_TLV_TYPE_EXTERNAL) {
+ BT_WARN("YoYo: unsupported image_type 0x%x, disabling", image_type);
+ return -EINVAL;
+ }
+
+ if (core_type != BTINTEL_PCIE_CORE_TYPE_BT) {
+ BT_WARN("YoYo: non BT core_type %u, disabling", core_type);
+ return -EINVAL;
+ }
+
+ ctx->dbg_info_present = true;
+ BT_DBG("YoYo: config='%.64s' image=%u core=%u",
+ info->config_name, image_type, core_type);
+ return 0;
+}
+
+static int btintel_pcie_yoyo_parse_buf_alloc(struct btintel_pcie_yoyo_ctx *ctx,
+ const struct btintel_pcie_tlv_buf_alloc *tlv,
+ u32 len)
+{
+ u32 alloc_id, buf_location, domain, idx, max_num_frags, min_buf_size;
+ u32 req_buf_size, version;
+
+ if (len != sizeof(*tlv) - sizeof(struct btintel_pcie_tlv_hdr)) {
+ BT_WARN("YoYo: unexpected buf alloc TLV length %u", len);
+ return -EBADMSG;
+ }
+
+ domain = get_unaligned_le32(&tlv->dbg_hdr.domain);
+ version = get_unaligned_le32(&tlv->dbg_hdr.tlv_data_version);
+ if (domain != BTINTEL_PCIE_DOMAIN_BT ||
+ version != BTINTEL_PCIE_BUF_ALLOC_TLV_VERSION) {
+ BT_WARN("YoYo: unsupported buf allocation domain/version (%u/%u)",
+ domain, version);
+ return -EINVAL;
+ }
+
+ alloc_id = get_unaligned_le32(&tlv->alloc_id);
+ if (alloc_id < BTINTEL_PCIE_ALLOC_ID_DBGC1 ||
+ alloc_id > BTINTEL_PCIE_ALLOC_ID_DBGC3) {
+ BT_WARN("YoYo: invalid buf allocation id %u", alloc_id);
+ return -EINVAL;
+ }
+
+ idx = alloc_id - BTINTEL_PCIE_ALLOC_ID_DBGC1;
+ buf_location = get_unaligned_le32(&tlv->buf_location);
+ max_num_frags = get_unaligned_le32(&tlv->max_num_frags);
+ min_buf_size = get_unaligned_le32(&tlv->min_buf_size);
+ req_buf_size = get_unaligned_le32(&tlv->req_buf_size);
+
+ if (buf_location != BTINTEL_PCIE_BUF_LOC_DRAM) {
+ BT_WARN("YoYo: buf allocation id=%u unsupported buffer location %u",
+ alloc_id, buf_location);
+ return -EINVAL;
+ }
+
+ if (!max_num_frags || max_num_frags > BTINTEL_PCIE_MAX_BUF_FRAGS ||
+ min_buf_size < BTINTEL_PCIE_MIN_BUF_SIZE ||
+ min_buf_size > BTINTEL_PCIE_MAX_BUF_SIZE ||
+ req_buf_size > BTINTEL_PCIE_MAX_BUF_SIZE ||
+ req_buf_size % max_num_frags ||
+ req_buf_size / max_num_frags < min_buf_size) {
+ BT_WARN("YoYo: buf allocation id=%u req size=%u max frags=%u min size=%u",
+ alloc_id, req_buf_size, max_num_frags, min_buf_size);
+ return -EINVAL;
+ }
+
+ ctx->buf_alloc[idx].alloc_id = alloc_id;
+ ctx->buf_alloc[idx].buf_location = buf_location;
+ ctx->buf_alloc[idx].req_buf_size = req_buf_size;
+ ctx->buf_alloc[idx].max_num_frags = max_num_frags;
+ ctx->buf_alloc[idx].min_buf_size = min_buf_size;
+ ctx->buf_alloc[idx].present = true;
+
+ if (idx + 1 > ctx->num_buf_allocs)
+ ctx->num_buf_allocs = idx + 1;
+
+ BT_DBG("YoYo: buf allocation id=%u loc=%u req size=%u max frags=%u min size=%u",
+ alloc_id, ctx->buf_alloc[idx].buf_location,
+ ctx->buf_alloc[idx].req_buf_size,
+ ctx->buf_alloc[idx].max_num_frags,
+ ctx->buf_alloc[idx].min_buf_size);
+
+ return 0;
+}
+
+/*
+ * Parse the optional YoYo firmware TLV stream and validate each record.
+ * This is the entry point for the runtime debug configuration payload.
+ */
+static int btintel_pcie_yoyo_parse_tlv_buffer(struct btintel_pcie_yoyo_ctx *ctx,
+ const u8 *buf, size_t buf_len)
+{
+ const struct btintel_pcie_tlv_hdr *tlv;
+ const u8 *ptr = buf;
+ size_t remaining = buf_len;
+ u32 count = 0;
+ int ret = 0;
+
+ while (remaining >= sizeof(*tlv)) {
+ size_t tlv_size;
+ u32 type, len;
+
+ tlv = (const struct btintel_pcie_tlv_hdr *)ptr;
+ type = get_unaligned_le32(&tlv->type);
+ len = get_unaligned_le32(&tlv->length);
+
+ if (len > remaining - sizeof(*tlv)) {
+ BT_ERR("YoYo: tlv %u truncated (len=%u rem=%zu)", count,
+ len, remaining);
+ return -EBADMSG;
+ }
+ tlv_size = sizeof(*tlv) + len;
+
+ switch (type) {
+ case BTINTEL_PCIE_TLV_TYPE_DBG_INFO:
+ ret = btintel_pcie_yoyo_parse_dbg_info(ctx,
+ (const void *)tlv, len);
+ if (ret) {
+ ctx->dbg_info_present = false;
+ }
+ break;
+ case BTINTEL_PCIE_TLV_TYPE_DBG_BUF_ALLOC:
+ ret = btintel_pcie_yoyo_parse_buf_alloc(ctx,
+ (const void *)tlv, len);
+ break;
+ case BTINTEL_PCIE_TLV_TYPE_DBG_REGION:
+ case BTINTEL_PCIE_TLV_TYPE_DBG_TRIGGER:
+ case BTINTEL_PCIE_TLV_TYPE_DBG_HCMD:
+ case BTINTEL_PCIE_TLV_TYPE_DBG_CONFIG_SET:
+ case BTINTEL_PCIE_TLV_TYPE_DBG_PRESET_OVERRIDE:
+ BT_DBG("YoYo: valid tlv type 0x%x len=%u", type, len);
+ break;
+ default: /* Log and continue, do not fail*/
+ BT_DBG("YoYo: unknown tlv type 0x%x len=%u", type, len);
+ break;
+ }
+
+ ptr += tlv_size;
+ remaining -= tlv_size;
+
+ if (ret) {
+ BT_ERR("YoYo: error parsing tlv type 0x%x (ret=%d)", type, ret);
+ return ret;
+ }
+
+ count++;
+ }
+
+ /* If no TLVs were parsed, the YoYo binary is considered invalid */
+ if (remaining || count == 0) {
+ BT_ERR("YoYo: trailing %zu bytes after tlv stream", remaining);
+ return -EBADMSG;
+ }
+ if (!ctx->num_buf_allocs) {
+ BT_ERR("YoYo: mandatory buffer allocation TLV missing");
+ return -EBADMSG;
+ }
+
+ BT_DBG("YoYo: parsed %u tlv's", count);
+ return 0;
+}
+
+/**
+ * btintel_pcie_yoyo_init - Initialize the YoYo context
+ * @ctx: Pre-allocated context (typically embedded in driver data)
+ */
+int btintel_pcie_yoyo_init(struct btintel_pcie_yoyo_ctx *ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+ memset(ctx, 0, sizeof(*ctx));
+ INIT_LIST_HEAD(&ctx->region_list);
+ INIT_LIST_HEAD(&ctx->trigger_list);
+ INIT_LIST_HEAD(&ctx->hcmd_list);
+ INIT_LIST_HEAD(&ctx->config_set_list);
+
+ /* set it to true after successful TLV parsing */
+ ctx->enabled = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_pcie_yoyo_init);
+
+/**
+ * btintel_pcie_yoyo_cleanup - Clean up the YoYo context
+ * @ctx: YoYo context to clean up
+ */
+void btintel_pcie_yoyo_cleanup(struct btintel_pcie_yoyo_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ ctx->enabled = false;
+ ctx->dbg_info_present = false;
+ ctx->num_buf_allocs = 0;
+ memset(ctx->buf_alloc, 0, sizeof(ctx->buf_alloc));
+}
+EXPORT_SYMBOL_GPL(btintel_pcie_yoyo_cleanup);
+
+/**
+ * btintel_pcie_yoyo_load_and_parse - Load ibt_debug_yoyo.bin and parse TLVs
+ * @ctx: Initialized YoYo context
+ * @dev: Device for firmware loading (typically &pdev->dev)
+ *
+ * Uses request_firmware_direct, since the YoYo binary
+ * is optional. On success, sets ctx->enabled.
+ */
+int btintel_pcie_yoyo_load_and_parse(struct btintel_pcie_yoyo_ctx *ctx,
+ struct device *dev)
+{
+ const struct firmware *fw;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = request_firmware_direct(&fw, BTINTEL_PCIE_YOYO_FW_NAME, dev);
+ if (ret) {
+ BT_WARN("YoYo: failed to load %s (%d)",
+ BTINTEL_PCIE_YOYO_FW_NAME, ret);
+ return ret;
+ }
+
+ /* Sanity check firmware size */
+ if (fw->size < sizeof(struct btintel_pcie_tlv_hdr)) {
+ release_firmware(fw);
+ return -EBADMSG;
+ }
+
+ BT_DBG("YoYo: loaded %s (%zu bytes)", BTINTEL_PCIE_YOYO_FW_NAME,
+ fw->size);
+
+ ret = btintel_pcie_yoyo_parse_tlv_buffer(ctx, fw->data, fw->size);
+ release_firmware(fw);
+
+ /* File Corruption / TLV validation failed */
+ if (ret) {
+ BT_ERR("YoYo: tlv parsing failed (%d)", ret);
+ return ret;
+ }
+ if (!ctx->dbg_info_present) {
+ BT_ERR("YoYo: Invalid BT debug Info tlv, using legacy DBGC defaults");
+ return -EBADMSG;
+ }
+
+ ctx->enabled = true;
+
+ BT_INFO("YoYo: feature enabled and configured");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_pcie_yoyo_load_and_parse);
+
+/**
+ * btintel_pcie_yoyo_get_buf_alloc - Query buffer allocation config for a DBGC
+ * @ctx: YoYo context
+ * @alloc_id: Allocation ID (1=DBGC1, 2=DBGC2, 3=DBGC3)
+ *
+ * Return the parsed configuration, or NULL if no matching TLV was present.
+ */
+const struct btintel_pcie_buf_alloc *
+btintel_pcie_yoyo_get_buf_alloc(struct btintel_pcie_yoyo_ctx *ctx, u32 alloc_id)
+{
+ u32 idx;
+
+ if (!ctx || !ctx->enabled ||
+ alloc_id < BTINTEL_PCIE_ALLOC_ID_DBGC1 ||
+ alloc_id > BTINTEL_PCIE_ALLOC_ID_DBGC3)
+ return NULL;
+
+ idx = alloc_id - BTINTEL_PCIE_ALLOC_ID_DBGC1;
+ if (!ctx->buf_alloc[idx].present)
+ return NULL;
+
+ return &ctx->buf_alloc[idx];
+}
+EXPORT_SYMBOL_GPL(btintel_pcie_yoyo_get_buf_alloc);
+
+MODULE_DESCRIPTION("Intel Bluetooth yoyo debug framework");
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btintel_pcie_debug.h b/drivers/bluetooth/btintel_pcie_debug.h
new file mode 100644
index 000000000000..5ed368fb13d1
--- /dev/null
+++ b/drivers/bluetooth/btintel_pcie_debug.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Intel Bluetooth YoYo Debug Framework
+ *
+ * Copyright (C) 2026 Intel Corporation
+ *
+ */
+
+#ifndef __BTINTEL_PCIE_DEBUG_H__
+#define __BTINTEL_PCIE_DEBUG_H__
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+/* External YoYo binary file */
+#define BTINTEL_PCIE_YOYO_FW_NAME "intel/ibt_debug_yoyo.bin"
+
+/* TLV type codes */
+#define BTINTEL_PCIE_TLV_TYPE_DBG_INFO 0x1000005
+#define BTINTEL_PCIE_TLV_TYPE_DBG_BUF_ALLOC 0x1000006
+#define BTINTEL_PCIE_TLV_TYPE_DBG_HCMD 0x1000007
+#define BTINTEL_PCIE_TLV_TYPE_DBG_REGION 0x1000008
+#define BTINTEL_PCIE_TLV_TYPE_DBG_TRIGGER 0x1000009
+#define BTINTEL_PCIE_TLV_TYPE_DBG_CONFIG_SET 0x100000A
+#define BTINTEL_PCIE_TLV_TYPE_DBG_PRESET_OVERRIDE 0x100000B
+
+/* Supported TLV type versions */
+#define BTINTEL_PCIE_DBG_INFO_TLV_VERSION 2
+#define BTINTEL_PCIE_BUF_ALLOC_TLV_VERSION 1
+/* Debug Info TLV */
+#define BTINTEL_PCIE_CONFIG_NAME_LEN 64
+#define BTINTEL_PCIE_DOMAIN_BT 0
+#define BTINTEL_PCIE_CORE_TYPE_BT 1
+/* Buffer allocation TLV */
+#define BTINTEL_PCIE_MAX_BUF_ALLOC 3
+#define BTINTEL_PCIE_MAX_BUF_FRAGS 16
+#define BTINTEL_PCIE_MIN_BUF_SIZE (32 * 1024)
+#define BTINTEL_PCIE_MAX_BUF_SIZE (4 * 1024 * 1024)
+
+enum btintel_pcie_image_tlv_type {
+ BTINTEL_PCIE_IMAGE_TLV_TYPE_EXTERNAL = 0x1,
+ BTINTEL_PCIE_IMAGE_TLV_TYPE_INTERNAL = 0x2,
+ BTINTEL_PCIE_IMAGE_TLV_TYPE_UNKNOWN = 0xff,
+};
+
+/* Allocation IDs */
+enum btintel_dbg_alloc_id {
+ BTINTEL_PCIE_ALLOC_ID_INVALID = 0,
+ BTINTEL_PCIE_ALLOC_ID_DBGC1 = 1,
+ BTINTEL_PCIE_ALLOC_ID_DBGC2 = 2,
+ BTINTEL_PCIE_ALLOC_ID_DBGC3 = 3,
+ BTINTEL_PCIE_ALLOC_ID_MAX,
+};
+
+/* Buffer locations */
+enum btintel_dbg_buf_location {
+ BTINTEL_PCIE_BUF_LOC_INVALID = 0,
+ BTINTEL_PCIE_BUF_LOC_SRAM = 1,
+ BTINTEL_PCIE_BUF_LOC_DRAM = 2,
+ BTINTEL_PCIE_BUF_LOC_NPK = 3,
+ BTINTEL_PCIE_BUF_LOC_MAX,
+};
+
+/* TLV header */
+struct btintel_pcie_tlv_hdr {
+ __le32 type;
+ __le32 length;
+} __packed;
+
+/* Debug header common to all debug TLVs */
+struct btintel_pcie_dbg_hdr {
+ __le32 tlv_data_version;
+ __le32 domain;
+} __packed;
+
+/* Address/value pair */
+struct btintel_pcie_addr_value {
+ __le32 address;
+ __le32 value;
+} __packed;
+
+/*
+ * struct btintel_pcie_tlv_dbg_info - YoYo DEBUG_INFO TLV
+ * @hdr: TLV type and payload length
+ * @dbg_hdr: Debug TLV version and domain
+ * @image_type: Firmware image type
+ * @core_type: Firmware core type
+ * @config_name: Fixed-size name of the firmware configuration
+ *
+ * Describes the firmware image targeted by the YoYo configuration. Fields
+ * are stored in little-endian format in the packed firmware TLV.
+ */
+struct btintel_pcie_tlv_dbg_info {
+ struct btintel_pcie_tlv_hdr hdr;
+ struct btintel_pcie_dbg_hdr dbg_hdr;
+ __le32 image_type;
+ __le32 core_type;
+ u8 config_name[BTINTEL_PCIE_CONFIG_NAME_LEN];
+} __packed;
+
+/*
+ * struct btintel_pcie_tlv_buf_alloc - YoYo buffer allocation TLV
+ * @hdr: Common TLV header
+ * @dbg_hdr: Debug TLV version and domain
+ * @alloc_id: DBGC allocation identifier (DBGC1..DBGC3)
+ * @buf_location: Requested buffer location (SRAM / DRAM / NPK)
+ * @req_buf_size: Total requested allocation size in bytes
+ * @max_num_frags: Maximum number of buffer fragments (1..16)
+ * @min_buf_size: Minimum buffer size in bytes
+ *
+ * Describes the buffer sizing override for one DBGC allocation.
+ */
+struct btintel_pcie_tlv_buf_alloc {
+ struct btintel_pcie_tlv_hdr hdr;
+ struct btintel_pcie_dbg_hdr dbg_hdr;
+ __le32 alloc_id;
+ __le32 buf_location;
+ __le32 req_buf_size;
+ __le32 max_num_frags;
+ __le32 min_buf_size;
+} __packed;
+
+/* Parsed buffer allocation entry in the YoYo context */
+struct btintel_pcie_buf_alloc {
+ u32 alloc_id;
+ u32 buf_location;
+ u32 req_buf_size;
+ u32 max_num_frags;
+ u32 min_buf_size;
+ bool present;
+};
+
+/*
+ * struct btintel_pcie_yoyo_ctx- Runtime state for YoYo debug configuration
+ * @enabled: Whether the loaded YoYo configuration passed validation
+ * @region_list: Parsed debug-region entries
+ * @trigger_list: Parsed debug-trigger entries
+ * @hcmd_list: Parsed HCI command entries
+ * @config_set_list: Parsed configuration-set entries
+ * @buf_alloc: Parsed buffer allocation entries
+ * @num_buf_allocs: Number of parsed buffer allocation entries
+ * @hdev: Associated HCI device; NULL until successful HCI registration
+ *
+ * Stores the validation state and parsed TLV data for the optional YoYo
+ * firmware configuration.
+ */
+struct btintel_pcie_yoyo_ctx {
+ bool enabled;
+ bool dbg_info_present;
+ struct list_head region_list;
+ struct list_head trigger_list;
+ struct list_head hcmd_list;
+ struct list_head config_set_list;
+ struct btintel_pcie_buf_alloc buf_alloc[BTINTEL_PCIE_MAX_BUF_ALLOC];
+ int num_buf_allocs;
+ struct hci_dev *hdev;
+};
+
+int btintel_pcie_yoyo_init(struct btintel_pcie_yoyo_ctx *ctx);
+void btintel_pcie_yoyo_cleanup(struct btintel_pcie_yoyo_ctx *ctx);
+
+/* Load and parse the external .bin file */
+int btintel_pcie_yoyo_load_and_parse(struct btintel_pcie_yoyo_ctx *ctx,
+ struct device *dev);
+
+const struct btintel_pcie_buf_alloc *
+btintel_pcie_yoyo_get_buf_alloc(struct btintel_pcie_yoyo_ctx *ctx, u32 alloc_id);
+
+#endif /* __BTINTEL_PCIE_DEBUG_H__ */
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 75483d8230e1..bf9bfed3da23 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -7,6 +7,7 @@
#include <linux/usb.h>
#include <linux/iopoll.h>
#include <linux/overflow.h>
+#include <linux/string.h>
#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -30,7 +31,7 @@
#define BTMTK_CBMCU_FLAG_ENABLE_PATCH 0xF1
/* It is for mt79xx iso data transmission setting */
-#define MTK_ISO_THRESHOLD 264
+#define MTK_ISO_THRESHOLD 342
struct btmtk_patch_header {
u8 datetime[16];
@@ -1556,8 +1557,15 @@ static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
struct btmtk_data *btmtk_data = hci_get_priv(hdev);
struct sk_buff *skb;
unsigned long flags;
+ unsigned int iso_mtu;
int err = 0;
+ /* The controller reports the largest Data_Total_Length it will send in
+ * LE Read Buffer Size [v2]. Fall back to HCI_MAX_ISO_SIZE while that
+ * command has not completed yet.
+ */
+ iso_mtu = hdev->iso_mtu ? hdev->iso_mtu : HCI_MAX_ISO_SIZE;
+
spin_lock_irqsave(&btmtk_data->isorxlock, flags);
skb = btmtk_data->isopkt_skb;
@@ -1565,7 +1573,16 @@ static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
int len;
if (!skb) {
- skb = bt_skb_alloc(HCI_MAX_ISO_SIZE, GFP_ATOMIC);
+ /* Drop the endpoint's zero-filled padding before
+ * allocating an skb for it; a zero Data_Total_Length
+ * check below would miss a padding remainder
+ * shorter than an ISO header.
+ */
+ if (mem_is_zero(buffer, count))
+ break;
+
+ skb = bt_skb_alloc(HCI_ISO_HDR_SIZE + iso_mtu,
+ GFP_ATOMIC);
if (!skb) {
err = -ENOMEM;
break;
@@ -1588,7 +1605,24 @@ static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
/* Complete ISO header */
hci_skb_expect(skb) = __le16_to_cpu(dlen);
- if (skb_tailroom(skb) < hci_skb_expect(skb)) {
+ if (!hci_skb_expect(skb)) {
+ /* Zero padding was ruled out above, so a
+ * zero Data_Total_Length here is invalid.
+ */
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+
+ /* Reject a length the controller said it would never
+ * send. The skb_tailroom() test is kept because a
+ * fragment started before hdev->iso_mtu was populated
+ * sits in a smaller skb than iso_mtu now allows.
+ */
+ if (hci_skb_expect(skb) > iso_mtu ||
+ skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -1707,7 +1741,7 @@ static int btmtk_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
static int btmtk_usb_isointf_init(struct hci_dev *hdev)
{
struct btmtk_data *btmtk_data = hci_get_priv(hdev);
- u8 iso_param[2] = { 0x08, 0x01 };
+ __le16 iso_param = cpu_to_le16(MTK_ISO_THRESHOLD);
struct sk_buff *skb;
int err;
@@ -1722,7 +1756,7 @@ static int btmtk_usb_isointf_init(struct hci_dev *hdev)
return err;
}
- skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), iso_param,
+ skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), &iso_param,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to apply iso setting (%ld)", PTR_ERR(skb));
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 22b08ab05b82..002307000d3c 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -426,7 +426,8 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
tlv_nvm->data[0] |= 0x80;
/* UART Baud Rate */
- if (soc_type >= QCA_WCN3991)
+ if (soc_type == QCA_WCN3950 ||
+ soc_type >= QCA_WCN3991)
tlv_nvm->data[1] = nvm_baud_rate;
else
tlv_nvm->data[2] = nvm_baud_rate;
diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c
index be2923231e71..2866d4b54a53 100644
--- a/drivers/bluetooth/hci_nokia.c
+++ b/drivers/bluetooth/hci_nokia.c
@@ -531,12 +531,9 @@ static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
/* Packets must be word aligned */
- if (skb->len % 2) {
- err = skb_pad(skb, 1);
- if (err)
- return err;
- skb_put(skb, 1);
- }
+ err = skb_put_padto(skb, round_up(skb->len, 2));
+ if (err)
+ return err;
skb_queue_tail(&btdev->txq, skb);
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 9dc714d9145e..1c992e87534d 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -473,6 +473,9 @@ enum {
HCI_MESH,
HCI_MESH_SENDING,
+ HCI_HW_RFKILLED,
+ HCI_SW_RFKILLED,
+
__HCI_NUM_FLAGS,
};
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa04dec45153..9d5d3ab4a075 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -503,6 +503,7 @@ struct hci_dev {
struct list_head cmd_sync_work_list;
struct mutex cmd_sync_work_lock;
struct mutex unregister_lock;
+ struct mutex rfkill_lock;
struct work_struct cmd_sync_cancel_work;
struct work_struct reenable_adv_work;
@@ -586,6 +587,7 @@ struct hci_dev {
struct device dev;
struct rfkill *rfkill;
+ struct work_struct rfkill_block;
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
hci_conn_flags_t conn_flags;
@@ -680,6 +682,45 @@ enum conn_reasons {
CONN_REASON_ISO_CONNECT,
};
+enum {
+ HCI_CONN_AUTH_PEND,
+ HCI_CONN_ENCRYPT_PEND,
+ HCI_CONN_RSWITCH_PEND,
+ HCI_CONN_MODE_CHANGE_PEND,
+ HCI_CONN_SCO_SETUP_PEND,
+ HCI_CONN_MGMT_CONNECTED,
+ HCI_CONN_SSP_ENABLED,
+ HCI_CONN_SC_ENABLED,
+ HCI_CONN_AES_CCM,
+ HCI_CONN_POWER_SAVE,
+ HCI_CONN_FLUSH_KEY,
+ HCI_CONN_ENCRYPT,
+ HCI_CONN_AUTH,
+ HCI_CONN_SECURE,
+ HCI_CONN_FIPS,
+ HCI_CONN_STK_ENCRYPT,
+ HCI_CONN_AUTH_INITIATOR,
+ HCI_CONN_DROP,
+ HCI_CONN_CANCEL,
+ HCI_CONN_PARAM_REMOVAL_PEND,
+ HCI_CONN_NEW_LINK_KEY,
+ HCI_CONN_SCANNING,
+ HCI_CONN_AUTH_FAILURE,
+ HCI_CONN_PER_ADV,
+ HCI_CONN_BIG_CREATED,
+ HCI_CONN_CREATE,
+ HCI_CONN_CREATE_CIS,
+ HCI_CONN_CREATE_BIG_SYNC,
+ HCI_CONN_BIG_SYNC,
+ HCI_CONN_BIG_SYNC_FAILED,
+ HCI_CONN_CREATE_PA_SYNC,
+ HCI_CONN_PA_SYNC,
+ HCI_CONN_PA_SYNC_FAILED,
+ HCI_CONN_ENC_KEY_SIZE_PEND,
+
+ __HCI_CONN_NUM_FLAGS,
+};
+
struct hci_conn {
struct list_head list;
@@ -750,7 +791,7 @@ struct hci_conn {
__u8 num_bis;
__u8 bis[HCI_MAX_ISO_BIS];
- unsigned long flags;
+ DECLARE_BITMAP(flags, __HCI_CONN_NUM_FLAGS);
enum conn_reasons conn_reason;
__u8 abort_reason;
@@ -980,55 +1021,18 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
bool name_known);
void hci_inquiry_cache_flush(struct hci_dev *hdev);
-/* ----- HCI Connections ----- */
-enum {
- HCI_CONN_AUTH_PEND,
- HCI_CONN_ENCRYPT_PEND,
- HCI_CONN_RSWITCH_PEND,
- HCI_CONN_MODE_CHANGE_PEND,
- HCI_CONN_SCO_SETUP_PEND,
- HCI_CONN_MGMT_CONNECTED,
- HCI_CONN_SSP_ENABLED,
- HCI_CONN_SC_ENABLED,
- HCI_CONN_AES_CCM,
- HCI_CONN_POWER_SAVE,
- HCI_CONN_FLUSH_KEY,
- HCI_CONN_ENCRYPT,
- HCI_CONN_AUTH,
- HCI_CONN_SECURE,
- HCI_CONN_FIPS,
- HCI_CONN_STK_ENCRYPT,
- HCI_CONN_AUTH_INITIATOR,
- HCI_CONN_DROP,
- HCI_CONN_CANCEL,
- HCI_CONN_PARAM_REMOVAL_PEND,
- HCI_CONN_NEW_LINK_KEY,
- HCI_CONN_SCANNING,
- HCI_CONN_AUTH_FAILURE,
- HCI_CONN_PER_ADV,
- HCI_CONN_BIG_CREATED,
- HCI_CONN_CREATE,
- HCI_CONN_CREATE_CIS,
- HCI_CONN_CREATE_BIG_SYNC,
- HCI_CONN_BIG_SYNC,
- HCI_CONN_BIG_SYNC_FAILED,
- HCI_CONN_CREATE_PA_SYNC,
- HCI_CONN_PA_SYNC,
- HCI_CONN_PA_SYNC_FAILED,
-};
-
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
return hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
- test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
+ test_bit(HCI_CONN_SSP_ENABLED, conn->flags);
}
static inline bool hci_conn_sc_enabled(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
return hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
- test_bit(HCI_CONN_SC_ENABLED, &conn->flags);
+ test_bit(HCI_CONN_SC_ENABLED, conn->flags);
}
static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
@@ -1219,7 +1223,7 @@ hci_conn_hash_lookup_create_pa_sync(struct hci_dev *hdev)
if (c->type != PA_LINK)
continue;
- if (!test_bit(HCI_CONN_CREATE_PA_SYNC, &c->flags))
+ if (!test_bit(HCI_CONN_CREATE_PA_SYNC, c->flags))
continue;
rcu_read_unlock();
@@ -1245,7 +1249,7 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
list_for_each_entry_rcu(c, &h->list, list) {
if (bacmp(&c->dst, ba) || c->type != BIS_LINK ||
- !test_bit(HCI_CONN_PER_ADV, &c->flags))
+ !test_bit(HCI_CONN_PER_ADV, c->flags))
continue;
if (c->iso_qos.bcast.big == big &&
@@ -1502,7 +1506,7 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != BIS_LINK ||
- !test_bit(HCI_CONN_PA_SYNC, &c->flags))
+ !test_bit(HCI_CONN_PA_SYNC, c->flags))
continue;
if (c->iso_qos.bcast.big == big) {
@@ -1580,7 +1584,7 @@ static inline void hci_conn_hash_list_flag(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type == type && test_bit(flag, &c->flags))
+ if (c->type == type && test_bit(flag, c->flags))
func(c, data);
}
@@ -1598,7 +1602,7 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
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)) {
+ !test_bit(HCI_CONN_SCANNING, c->flags)) {
rcu_read_unlock();
return c;
}
@@ -1619,7 +1623,7 @@ static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev)
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)) {
+ test_bit(HCI_CONN_SCANNING, c->flags)) {
rcu_read_unlock();
return true;
}
@@ -1822,6 +1826,8 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, u8 src_type);
struct hci_dev *hci_alloc_dev_priv(int sizeof_priv);
+void hci_rfkill_set_hw_state(struct hci_dev *hdev, bool blocked);
+
static inline struct hci_dev *hci_alloc_dev(void)
{
return hci_alloc_dev_priv(0);
@@ -2239,10 +2245,10 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
struct hci_cb *cb;
__u8 encrypt;
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags))
return;
- encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
+ encrypt = test_bit(HCI_CONN_ENCRYPT, conn->flags) ? 0x01 : 0x00;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
@@ -2271,9 +2277,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
return;
}
- if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
+ if (!test_bit(HCI_CONN_ENCRYPT, conn->flags))
encrypt = 0x00;
- else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
+ else if (test_bit(HCI_CONN_AES_CCM, conn->flags))
encrypt = 0x02;
else
encrypt = 0x01;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 29da3fe2b953..b007f623b2ec 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -138,10 +138,10 @@ static void hci_conn_cleanup(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
- if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
+ if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
- if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
+ if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, conn->flags))
hci_remove_link_key(hdev, &conn->dst);
hci_chan_list_flush(conn);
@@ -685,7 +685,7 @@ static void hci_conn_idle(struct work_struct *work)
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
- if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags)) {
struct hci_cp_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
@@ -814,7 +814,7 @@ static int hci_le_terminate_big(struct hci_dev *hdev, struct hci_conn *conn)
d->big = conn->iso_qos.bcast.big;
d->bis = conn->iso_qos.bcast.bis;
- d->big_term = test_and_clear_bit(HCI_CONN_BIG_CREATED, &conn->flags);
+ d->big_term = test_and_clear_bit(HCI_CONN_BIG_CREATED, conn->flags);
ret = hci_cmd_sync_queue(hdev, terminate_big_sync, d,
terminate_big_destroy);
@@ -867,7 +867,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
d->sync_handle = conn->sync_handle;
if (conn->type == PA_LINK &&
- test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
+ test_and_clear_bit(HCI_CONN_PA_SYNC, conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, PA_LINK,
HCI_CONN_PA_SYNC, d);
@@ -877,7 +877,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
d->count = 0;
}
- if (test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags)) {
+ if (test_and_clear_bit(HCI_CONN_BIG_SYNC, conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
HCI_CONN_BIG_SYNC, d);
@@ -912,7 +912,7 @@ static void bis_cleanup(struct hci_conn *conn)
bt_dev_dbg(hdev, "conn %p", conn);
if (conn->role == HCI_ROLE_MASTER) {
- if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags))
+ if (!test_and_clear_bit(HCI_CONN_PER_ADV, conn->flags))
return;
/* Check if ISO connection is a BIS and terminate advertising
@@ -1087,7 +1087,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
conn->sid = HCI_SID_INVALID;
- set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
+ set_bit(HCI_CONN_POWER_SAVE, conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
/* Set Default Authenticated payload timeout to 30s */
@@ -1217,8 +1217,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
case BIS_LINK:
case PA_LINK:
if ((conn->state != BT_CONNECTED &&
- !test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
- test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
+ !test_bit(HCI_CONN_CREATE_CIS, conn->flags)) ||
+ test_bit(HCI_CONN_BIG_CREATED, conn->flags))
hci_conn_failed(conn, reason);
break;
}
@@ -1234,8 +1234,8 @@ static void hci_conn_unlink(struct hci_conn *conn)
struct hci_link *link, *t;
conn->sec_level = BT_SECURITY_SDP;
- clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
- clear_bit(HCI_CONN_AES_CCM, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT, conn->flags);
+ clear_bit(HCI_CONN_AES_CCM, conn->flags);
/* mgmt_security_level_changed() reports the reset only if
* this connection was actually reported to userspace as
* connected (it checks HCI_CONN_MGMT_CONNECTED itself), so it
@@ -1254,7 +1254,7 @@ static void hci_conn_unlink(struct hci_conn *conn)
*/
if (test_bit(HCI_UP, &hdev->flags))
mgmt_security_level_changed(conn);
- clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
+ clear_bit(HCI_CONN_MGMT_CONNECTED, conn->flags);
list_for_each_entry_safe(link, t, &conn->link_list, list) {
struct hci_conn *child = link->conn;
@@ -1458,8 +1458,8 @@ void hci_conn_failed(struct hci_conn *conn, u8 status)
/* In case of BIG/PA sync failed, clear conn flags so that
* the conns will be correctly cleaned up by ISO layer
*/
- test_and_clear_bit(HCI_CONN_BIG_SYNC_FAILED, &conn->flags);
- test_and_clear_bit(HCI_CONN_PA_SYNC_FAILED, &conn->flags);
+ test_and_clear_bit(HCI_CONN_BIG_SYNC_FAILED, conn->flags);
+ test_and_clear_bit(HCI_CONN_PA_SYNC_FAILED, conn->flags);
conn->state = BT_CLOSED;
hci_connect_cfm(conn, status);
@@ -1524,9 +1524,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
* to connect.
*/
conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
- if (conn && !test_bit(HCI_CONN_SCANNING, &conn->flags)) {
+ if (conn && !test_bit(HCI_CONN_SCANNING, conn->flags))
return ERR_PTR(-EBUSY);
- }
/* Check if the destination address has been resolved by the controller
* since if it did then the identity address shall be used.
@@ -1614,8 +1613,9 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev,
if (!params)
return -ENOMEM;
- /* If we created new params, mark them to be deleted in
- * hci_connect_le_scan_cleanup. It's different case than
+ /* If we created new params, mark them to be deleted once the
+ * explicit connect is done, see le_conn_complete_evt() and
+ * hci_connect_le_scan_cleanup(). It's different case than
* existing disabled params, those will stay after cleanup.
*/
params->auto_connect = HCI_AUTO_CONN_EXPLICIT;
@@ -1797,7 +1797,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
}
conn->state = BT_CONNECT;
- set_bit(HCI_CONN_SCANNING, &conn->flags);
+ set_bit(HCI_CONN_SCANNING, conn->flags);
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
@@ -1923,12 +1923,12 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
- set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
+ set_bit(HCI_CONN_POWER_SAVE, acl->flags);
hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
- if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) {
+ if (test_bit(HCI_CONN_MODE_CHANGE_PEND, acl->flags)) {
/* defer SCO setup until mode change completed */
- set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags);
+ set_bit(HCI_CONN_SCO_SETUP_PEND, acl->flags);
return sco;
}
@@ -2220,7 +2220,7 @@ int hci_le_create_cis_pending(struct hci_dev *hdev)
rcu_read_lock();
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
- if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) {
+ if (test_bit(HCI_CONN_CREATE_CIS, conn->flags)) {
rcu_read_unlock();
return -EBUSY;
}
@@ -2448,7 +2448,7 @@ static void bis_mark_per_adv(struct hci_conn *conn, void *data)
d->bis != conn->iso_qos.bcast.bis)
return;
- set_bit(HCI_CONN_PER_ADV, &conn->flags);
+ set_bit(HCI_CONN_PER_ADV, conn->flags);
}
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
@@ -2568,7 +2568,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
*/
if (hci_dev_test_flag(conn->hdev, HCI_SC_ONLY)) {
if (!hci_conn_sc_enabled(conn) ||
- !test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
+ !test_bit(HCI_CONN_AES_CCM, conn->flags) ||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)
return 0;
}
@@ -2584,14 +2584,14 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
* not shortened)
*/
if (conn->sec_level == BT_SECURITY_FIPS &&
- !test_bit(HCI_CONN_AES_CCM, &conn->flags)) {
+ !test_bit(HCI_CONN_AES_CCM, conn->flags)) {
bt_dev_err(conn->hdev,
"Invalid security: Missing AES-CCM usage");
return 0;
}
if (hci_conn_ssp_enabled(conn) &&
- !test_bit(HCI_CONN_ENCRYPT, &conn->flags))
+ !test_bit(HCI_CONN_ENCRYPT, conn->flags))
return 0;
return 1;
@@ -2607,7 +2607,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
if (sec_level > conn->sec_level)
conn->pending_sec_level = sec_level;
- else if (test_bit(HCI_CONN_AUTH, &conn->flags))
+ else if (test_bit(HCI_CONN_AUTH, conn->flags))
return 1;
/* Make sure we preserve an existing MITM requirement*/
@@ -2615,7 +2615,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
conn->auth_type = auth_type;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, conn->flags)) {
struct hci_cp_auth_requested cp;
cp.handle = cpu_to_le16(conn->handle);
@@ -2625,8 +2625,8 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
/* Set the ENCRYPT_PEND to trigger encryption after
* authentication.
*/
- if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
- set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+ if (!test_bit(HCI_CONN_ENCRYPT, conn->flags))
+ set_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
}
return 0;
@@ -2637,7 +2637,7 @@ static void hci_conn_encrypt(struct hci_conn *conn)
{
BT_DBG("hcon %p", conn);
- if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, conn->flags)) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = cpu_to_le16(conn->handle);
cp.encrypt = 0x01;
@@ -2665,7 +2665,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
return 1;
/* For other security levels we need the link key. */
- if (!test_bit(HCI_CONN_AUTH, &conn->flags))
+ if (!test_bit(HCI_CONN_AUTH, conn->flags))
goto auth;
switch (conn->key_type) {
@@ -2705,17 +2705,17 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
}
auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags))
return 0;
if (initiator)
- set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
+ set_bit(HCI_CONN_AUTH_INITIATOR, conn->flags);
if (!hci_conn_auth(conn, sec_level, auth_type))
return 0;
encrypt:
- if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) {
+ if (test_bit(HCI_CONN_ENCRYPT, conn->flags)) {
/* Ensure that the encryption key size has been read,
* otherwise stall the upper layer responses.
*/
@@ -2758,7 +2758,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
if (role == conn->role)
return 1;
- if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, conn->flags)) {
struct hci_cp_switch_role cp;
bacpy(&cp.bdaddr, &conn->dst);
cp.role = role;
@@ -2779,10 +2779,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
if (conn->mode != HCI_CM_SNIFF)
goto timer;
- if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active)
+ if (!test_bit(HCI_CONN_POWER_SAVE, conn->flags) && !force_active)
goto timer;
- if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags)) {
struct hci_cp_exit_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp);
@@ -2822,16 +2822,16 @@ static u32 get_link_mode(struct hci_conn *conn)
if (conn->role == HCI_ROLE_MASTER)
link_mode |= HCI_LM_MASTER;
- if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
+ if (test_bit(HCI_CONN_ENCRYPT, conn->flags))
link_mode |= HCI_LM_ENCRYPT;
- if (test_bit(HCI_CONN_AUTH, &conn->flags))
+ if (test_bit(HCI_CONN_AUTH, conn->flags))
link_mode |= HCI_LM_AUTH;
- if (test_bit(HCI_CONN_SECURE, &conn->flags))
+ if (test_bit(HCI_CONN_SECURE, conn->flags))
link_mode |= HCI_LM_SECURE;
- if (test_bit(HCI_CONN_FIPS, &conn->flags))
+ if (test_bit(HCI_CONN_FIPS, conn->flags))
link_mode |= HCI_LM_FIPS;
return link_mode;
@@ -2945,7 +2945,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
BT_DBG("%s hcon %p", hdev->name, conn);
- if (test_bit(HCI_CONN_DROP, &conn->flags)) {
+ if (test_bit(HCI_CONN_DROP, conn->flags)) {
BT_DBG("Refusing to create new hci_chan");
return NULL;
}
@@ -2975,7 +2975,7 @@ void hci_chan_del(struct hci_chan *chan)
synchronize_rcu();
/* Prevent new hci_chan's to be created for this hci_conn */
- set_bit(HCI_CONN_DROP, &conn->flags);
+ set_bit(HCI_CONN_DROP, conn->flags);
hci_conn_put(conn);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 209244fd9391..2a29cfdee972 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -863,15 +863,13 @@ static int hci_dev_do_poweroff(struct hci_dev *hdev)
return err;
}
-static int hci_rfkill_set_block(void *data, bool blocked)
+static int __hci_rfkill_set_block(struct hci_dev *hdev, bool blocked)
{
- struct hci_dev *hdev = data;
int err;
- BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
+ lockdep_assert_held(&hdev->rfkill_lock);
- if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
- return -EBUSY;
+ BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
if (blocked == hci_dev_test_flag(hdev, HCI_RFKILLED))
return 0;
@@ -879,8 +877,17 @@ static int hci_rfkill_set_block(void *data, bool blocked)
if (blocked) {
hci_dev_set_flag(hdev, HCI_RFKILLED);
+ /* Leave a user channel up; it closes the device on release
+ * and HCI_RFKILLED then blocks the next power on.
+ *
+ * During setup/config, HCI_RFKILLED is checked by the power
+ * on path, which closes the device. A block between that
+ * check and clearing HCI_SETUP is not enforced, as for a
+ * soft block; changing that is left to the setup flow.
+ */
if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
- !hci_dev_test_flag(hdev, HCI_CONFIG)) {
+ !hci_dev_test_flag(hdev, HCI_CONFIG) &&
+ !hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
err = hci_dev_do_poweroff(hdev);
if (err) {
bt_dev_err(hdev, "Error when powering off device on rfkill (%d)",
@@ -894,12 +901,100 @@ static int hci_rfkill_set_block(void *data, bool blocked)
}
}
} else {
+ /* Stay blocked while HW rfkill is still asserted. Do not use
+ * rfkill_blocked() here: inside set_block it still reports
+ * the soft block being removed.
+ */
+ if (hci_dev_test_flag(hdev, HCI_HW_RFKILLED))
+ return 0;
+
hci_dev_clear_flag(hdev, HCI_RFKILLED);
}
return 0;
}
+static int hci_rfkill_set_block(void *data, bool blocked)
+{
+ struct hci_dev *hdev = data;
+ int err;
+
+ mutex_lock(&hdev->rfkill_lock);
+ /* A user channel owns the device, so refuse soft rfkill. HW rfkill
+ * cannot be refused and is applied by hdev->rfkill_block instead.
+ */
+ if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
+ err = -EBUSY;
+ } else {
+ /* Track only accepted soft state; rfkill core reverts a
+ * refused one after this callback returns.
+ */
+ if (blocked)
+ hci_dev_set_flag(hdev, HCI_SW_RFKILLED);
+ else
+ hci_dev_clear_flag(hdev, HCI_SW_RFKILLED);
+ err = __hci_rfkill_set_block(hdev, blocked);
+ }
+ mutex_unlock(&hdev->rfkill_lock);
+
+ return err;
+}
+
+/**
+ * hci_rfkill_set_hw_state - report a hardware rfkill switch change
+ * @hdev: HCI device
+ * @blocked: true if the hardware switch is asserted
+ *
+ * May sleep. Must not be called with hdev->req_lock held, as a block
+ * powers the device off under hdev->rfkill_lock. @hdev must have been
+ * registered with hci_register_dev().
+ */
+void hci_rfkill_set_hw_state(struct hci_dev *hdev, bool blocked)
+{
+ mutex_lock(&hdev->rfkill_lock);
+ mutex_lock(&hdev->unregister_lock);
+ if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ goto unlock;
+
+ if (blocked)
+ hci_dev_set_flag(hdev, HCI_HW_RFKILLED);
+ else
+ hci_dev_clear_flag(hdev, HCI_HW_RFKILLED);
+
+ if (!IS_ERR_OR_NULL(hdev->rfkill)) {
+ /* Clear HCI_RFKILLED before rfkill core notifies userspace,
+ * so a power-on triggered by the unblock event does not see
+ * a stale flag.
+ */
+ if (!blocked && !hci_dev_test_flag(hdev, HCI_SW_RFKILLED))
+ hci_dev_clear_flag(hdev, HCI_RFKILLED);
+
+ rfkill_set_hw_state(hdev->rfkill, blocked);
+ }
+
+ /* Enforce the hardware state even without an rfkill device */
+ schedule_work(&hdev->rfkill_block);
+unlock:
+ mutex_unlock(&hdev->unregister_lock);
+ mutex_unlock(&hdev->rfkill_lock);
+}
+EXPORT_SYMBOL(hci_rfkill_set_hw_state);
+
+static void hci_rfkill_block_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev, rfkill_block);
+ bool blocked;
+
+ /* Use only state settled under rfkill_lock, not rfkill core's,
+ * which may hold a soft change that set_block is about to revert.
+ */
+ mutex_lock(&hdev->rfkill_lock);
+ blocked = hci_dev_test_flag(hdev, HCI_HW_RFKILLED) ||
+ hci_dev_test_flag(hdev, HCI_SW_RFKILLED);
+ __hci_rfkill_set_block(hdev, blocked);
+ mutex_unlock(&hdev->rfkill_lock);
+}
+
static const struct rfkill_ops hci_rfkill_ops = {
.set_block = hci_rfkill_set_block,
};
@@ -1252,7 +1347,6 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
key = kzalloc_obj(*key);
if (!key)
return NULL;
- list_add_rcu(&key->list, &hdev->link_keys);
}
BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
@@ -1276,6 +1370,9 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
else
key->type = type;
+ if (!old_key)
+ list_add_rcu(&key->list, &hdev->link_keys);
+
if (persistent)
*persistent = hci_persistent_key(hdev, conn, type,
old_key_type);
@@ -1297,7 +1394,6 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
key = kzalloc_obj(*key);
if (!key)
return NULL;
- list_add_rcu(&key->list, &hdev->long_term_keys);
}
bacpy(&key->bdaddr, bdaddr);
@@ -1309,15 +1405,19 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
key->enc_size = enc_size;
key->type = type;
+ if (!old_key)
+ list_add_rcu(&key->list, &hdev->long_term_keys);
+
return key;
}
struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type, u8 val[16], bdaddr_t *rpa)
{
- struct smp_irk *irk;
+ struct smp_irk *irk, *old_irk;
- irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
+ old_irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
+ irk = old_irk;
if (!irk) {
irk = kzalloc_obj(*irk);
if (!irk)
@@ -1326,12 +1426,14 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
bacpy(&irk->bdaddr, bdaddr);
irk->addr_type = addr_type;
- list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
}
memcpy(irk->val, val, 16);
bacpy(&irk->rpa, rpa);
+ if (!old_irk)
+ list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
+
return irk;
}
@@ -2495,6 +2597,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
mutex_init(&hdev->req_lock);
mutex_init(&hdev->mgmt_pending_lock);
mutex_init(&hdev->remote_oob_lock);
+ mutex_init(&hdev->rfkill_lock);
ida_init(&hdev->unset_handle_ida);
@@ -2518,6 +2621,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
INIT_LIST_HEAD(&hdev->monitored_devices);
INIT_LIST_HEAD(&hdev->local_codecs);
+ INIT_WORK(&hdev->rfkill_block, hci_rfkill_block_work);
INIT_WORK(&hdev->rx_work, hci_rx_work);
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
INIT_WORK(&hdev->tx_work, hci_tx_work);
@@ -2683,6 +2787,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
disable_work_sync(&hdev->tx_work);
disable_work_sync(&hdev->power_on);
disable_work_sync(&hdev->error_reset);
+ disable_work_sync(&hdev->rfkill_block);
disable_delayed_work_sync(&hdev->cmd_timer);
disable_delayed_work_sync(&hdev->ncmd_timer);
hci_devcd_shutdown(hdev);
@@ -2897,8 +3002,14 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
u8 dev_pkt_type;
+ /* Pairs with synchronize_rcu() in hci_dev_close_sync(), so rx_work is
+ * not queued once the workqueue is being drained.
+ */
+ rcu_read_lock();
+
if (!hdev || (!test_bit(HCI_UP, &hdev->flags)
&& !test_bit(HCI_INIT, &hdev->flags))) {
+ rcu_read_unlock();
kfree_skb(skb);
return -ENXIO;
}
@@ -2934,6 +3045,7 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_DRV_PKT:
break;
default:
+ rcu_read_unlock();
kfree_skb(skb);
return -EINVAL;
}
@@ -2947,6 +3059,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
skb_queue_tail(&hdev->rx_q, skb);
queue_work(hdev->workqueue, &hdev->rx_work);
+ rcu_read_unlock();
+
return 0;
}
EXPORT_SYMBOL(hci_recv_frame);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 1f80f2cf6b2c..d05934fef4d9 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -724,6 +724,8 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
goto done;
}
+ clear_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->flags);
+
/* While unexpected, the read_enc_key_size command may fail. The most
* secure approach is to then assume the key size is 0 to force a
* disconnection.
@@ -732,8 +734,8 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
bt_dev_err(hdev, "failed to read key size for handle %u",
handle);
conn->enc_key_size = 0;
- clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
- clear_bit(HCI_CONN_AES_CCM, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT, conn->flags);
+ clear_bit(HCI_CONN_AES_CCM, conn->flags);
/* Downgrade to reflect the connection no longer being
* encrypted, instead of advertising a stale elevated
* security level alongside MGMT_CONN_SEC_ENCRYPT_NONE.
@@ -761,8 +763,8 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
* state at a later time.
*/
status = HCI_ERROR_AUTH_FAILURE;
- clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
- clear_bit(HCI_CONN_AES_CCM, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT, conn->flags);
+ clear_bit(HCI_CONN_AES_CCM, conn->flags);
/* Downgrade to reflect the connection no longer being
* encrypted, instead of advertising a stale elevated
* security level alongside MGMT_CONN_SEC_ENCRYPT_NONE.
@@ -2595,10 +2597,10 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
if (!hci_outgoing_auth_needed(hdev, conn))
goto unlock;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, conn->flags)) {
struct hci_cp_auth_requested auth_cp;
- set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
+ set_bit(HCI_CONN_AUTH_INITIATOR, conn->flags);
auth_cp.handle = __cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
@@ -2738,9 +2740,9 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
+ clear_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags);
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, conn->flags))
hci_sco_setup(conn, status);
}
@@ -2765,9 +2767,9 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
+ clear_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags);
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, conn->flags))
hci_sco_setup(conn, status);
}
@@ -2824,10 +2826,10 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
* still needs it set to know whether to report the security level
* reset, so it is consumed there instead of here.
*/
- mgmt_conn = test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
+ mgmt_conn = test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags);
if (conn->type == ACL_LINK) {
- if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
+ if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, conn->flags))
hci_remove_link_key(hdev, &conn->dst);
}
@@ -3078,7 +3080,7 @@ static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
if (conn)
- clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+ clear_bit(HCI_CONN_RSWITCH_PEND, conn->flags);
hci_dev_unlock(hdev);
}
@@ -3190,6 +3192,7 @@ static int hci_read_enc_key_size(struct hci_dev *hdev, struct hci_conn *conn)
{
struct hci_cp_read_enc_key_size cp;
u8 *key_enc_size = hci_conn_key_enc_size(conn);
+ int err;
if (!read_key_size_capable(hdev)) {
conn->enc_key_size = HCI_LINK_KEY_SIZE;
@@ -3211,7 +3214,14 @@ static int hci_read_enc_key_size(struct hci_dev *hdev, struct hci_conn *conn)
else
conn->enc_key_size = hdev->min_enc_key_size;
- return hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
+ /* L2CAP holds incoming data back until the key size is known */
+ set_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->flags);
+
+ err = hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
+ if (err)
+ clear_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->flags);
+
+ return err;
}
static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
@@ -3309,19 +3319,19 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
hci_conn_add_sysfs(conn);
if (test_bit(HCI_AUTH, &hdev->flags))
- set_bit(HCI_CONN_AUTH, &conn->flags);
+ set_bit(HCI_CONN_AUTH, conn->flags);
if (test_bit(HCI_ENCRYPT, &hdev->flags))
- set_bit(HCI_CONN_ENCRYPT, &conn->flags);
+ set_bit(HCI_CONN_ENCRYPT, conn->flags);
/* "Link key request" completed ahead of "connect request" completes */
- if (ev->encr_mode == 1 && !test_bit(HCI_CONN_ENCRYPT, &conn->flags) &&
+ if (ev->encr_mode == 1 && !test_bit(HCI_CONN_ENCRYPT, conn->flags) &&
ev->link_type == ACL_LINK) {
struct link_key *key;
key = hci_find_link_key(hdev, &ev->bdaddr);
if (key) {
- set_bit(HCI_CONN_ENCRYPT, &conn->flags);
+ set_bit(HCI_CONN_ENCRYPT, conn->flags);
hci_read_enc_key_size(hdev, conn);
hci_encrypt_cfm(conn, ev->status);
}
@@ -3525,9 +3535,9 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
* still needs it set to know whether to report the security level
* reset, so it is consumed there instead of here.
*/
- mgmt_connected = test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
+ mgmt_connected = test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags);
- if (test_bit(HCI_CONN_AUTH_FAILURE, &conn->flags))
+ if (test_bit(HCI_CONN_AUTH_FAILURE, conn->flags))
reason = MGMT_DEV_DISCONN_AUTH_FAILURE;
else
reason = hci_to_mgmt_reason(ev->reason);
@@ -3536,7 +3546,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
reason, mgmt_connected);
if (conn->type == ACL_LINK) {
- if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
+ if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, conn->flags))
hci_remove_link_key(hdev, &conn->dst);
hci_update_scan(hdev);
@@ -3607,8 +3617,8 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
goto unlock;
if (!ev->status) {
- clear_bit(HCI_CONN_AUTH_FAILURE, &conn->flags);
- set_bit(HCI_CONN_AUTH, &conn->flags);
+ clear_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
+ set_bit(HCI_CONN_AUTH, conn->flags);
/* Authentication alone does not imply encryption: for SSP
* capable devices, encryption is only requested below, and
* for legacy devices it is requested by a separate
@@ -3621,12 +3631,12 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
conn->sec_level = conn->pending_sec_level;
} else {
if (ev->status == HCI_ERROR_PIN_OR_KEY_MISSING)
- set_bit(HCI_CONN_AUTH_FAILURE, &conn->flags);
+ set_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
mgmt_auth_failed(conn, ev->status);
}
- clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
+ clear_bit(HCI_CONN_AUTH_PEND, conn->flags);
if (conn->state == BT_CONFIG) {
if (!ev->status && hci_conn_ssp_enabled(conn)) {
@@ -3648,7 +3658,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
hci_conn_drop(conn);
}
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags)) {
if (!ev->status) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = ev->handle;
@@ -3656,7 +3666,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
&cp);
} else {
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
hci_encrypt_cfm(conn, ev->status);
}
}
@@ -3693,10 +3703,10 @@ check_auth:
if (!hci_outgoing_auth_needed(hdev, conn))
goto unlock;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, conn->flags)) {
struct hci_cp_auth_requested cp;
- set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
+ set_bit(HCI_CONN_AUTH_INITIATOR, conn->flags);
cp.handle = __cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
@@ -3723,17 +3733,17 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
if (!ev->status) {
if (ev->encrypt) {
/* Encryption implies authentication */
- set_bit(HCI_CONN_AUTH, &conn->flags);
- set_bit(HCI_CONN_ENCRYPT, &conn->flags);
+ set_bit(HCI_CONN_AUTH, conn->flags);
+ set_bit(HCI_CONN_ENCRYPT, conn->flags);
conn->sec_level = conn->pending_sec_level;
/* P-256 authentication key implies FIPS */
if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256)
- set_bit(HCI_CONN_FIPS, &conn->flags);
+ set_bit(HCI_CONN_FIPS, conn->flags);
if ((conn->type == ACL_LINK && ev->encrypt == 0x02) ||
conn->type == LE_LINK)
- set_bit(HCI_CONN_AES_CCM, &conn->flags);
+ set_bit(HCI_CONN_AES_CCM, conn->flags);
/* For ACL links the encryption key size is still
* validated below via hci_read_enc_key_size(). Its
@@ -3745,8 +3755,8 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
if (conn->type != ACL_LINK)
mgmt_security_level_changed(conn);
} else {
- clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
- clear_bit(HCI_CONN_AES_CCM, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT, conn->flags);
+ clear_bit(HCI_CONN_AES_CCM, conn->flags);
/* Elevated security levels (MEDIUM and above) require
* encryption, so downgrade to reflect the connection
* no longer being encrypted before reporting, instead
@@ -3767,7 +3777,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
hci_adv_instances_set_rpa_expired(hdev, true);
}
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
/* Check link security requirements are met */
if (!hci_conn_check_link_mode(conn))
@@ -3775,7 +3785,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
if (ev->status && conn->state == BT_CONNECTED) {
if (ev->status == HCI_ERROR_PIN_OR_KEY_MISSING)
- set_bit(HCI_CONN_AUTH_FAILURE, &conn->flags);
+ set_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
/* Notify upper layers so they can cleanup before
* disconnecting.
@@ -3813,8 +3823,8 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
* type can be either LE or ACL and controller must support LMP Ping.
* Ensure for AES-CCM encryption as well.
*/
- if (test_bit(HCI_CONN_ENCRYPT, &conn->flags) &&
- test_bit(HCI_CONN_AES_CCM, &conn->flags) &&
+ if (test_bit(HCI_CONN_ENCRYPT, conn->flags) &&
+ test_bit(HCI_CONN_AES_CCM, conn->flags) &&
((conn->type == ACL_LINK && lmp_ping_capable(hdev)) ||
(conn->type == LE_LINK && (hdev->le_features[0] & HCI_LE_PING)))) {
struct hci_cp_write_auth_payload_to cp;
@@ -3846,9 +3856,9 @@ static void hci_change_link_key_complete_evt(struct hci_dev *hdev, void *data,
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (conn) {
if (!ev->status)
- set_bit(HCI_CONN_SECURE, &conn->flags);
+ set_bit(HCI_CONN_SECURE, conn->flags);
- clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
+ clear_bit(HCI_CONN_AUTH_PEND, conn->flags);
hci_key_change_cfm(conn, ev->status);
}
@@ -4074,7 +4084,7 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
hci_connect_cfm(conn, rp->status);
/* Notify device connected in case it is a BIG Sync */
- if (!rp->status && test_bit(HCI_CONN_BIG_SYNC, &conn->flags))
+ if (!rp->status && test_bit(HCI_CONN_BIG_SYNC, conn->flags))
mgmt_device_connected(hdev, conn, NULL, 0);
break;
@@ -4504,7 +4514,7 @@ 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))
+ conn->flags))
pending = true;
conn->state = BT_CLOSED;
hci_connect_cfm(conn, status);
@@ -4625,7 +4635,7 @@ static void hci_role_change_evt(struct hci_dev *hdev, void *data,
if (!ev->status)
conn->role = ev->role;
- clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+ clear_bit(HCI_CONN_RSWITCH_PEND, conn->flags);
hci_role_switch_cfm(conn, ev->status, ev->role);
}
@@ -4737,12 +4747,12 @@ static void hci_mode_change_evt(struct hci_dev *hdev, void *data,
conn->mode = ev->mode;
if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND,
- &conn->flags)) {
- assign_bit(HCI_CONN_POWER_SAVE, &conn->flags,
+ conn->flags)) {
+ assign_bit(HCI_CONN_POWER_SAVE, conn->flags,
conn->mode == HCI_CM_ACTIVE);
}
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, conn->flags))
hci_sco_setup(conn, ev->status);
}
@@ -4770,7 +4780,7 @@ static void hci_pin_code_request_evt(struct hci_dev *hdev, void *data,
}
if (!hci_dev_test_flag(hdev, HCI_BONDABLE) &&
- !test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) {
+ !test_bit(HCI_CONN_AUTH_INITIATOR, conn->flags)) {
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
sizeof(ev->bdaddr), &ev->bdaddr);
} else if (hci_dev_test_flag(hdev, HCI_MGMT)) {
@@ -4845,7 +4855,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, void *data,
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
- clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+ clear_bit(HCI_CONN_NEW_LINK_KEY, conn->flags);
if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
@@ -4908,7 +4918,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, void *data,
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
hci_conn_drop(conn);
- set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+ set_bit(HCI_CONN_NEW_LINK_KEY, conn->flags);
conn_set_key(conn, ev->key_type, conn->pin_length);
if (!hci_dev_test_flag(hdev, HCI_MGMT))
@@ -4939,7 +4949,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
- assign_bit(HCI_CONN_FLUSH_KEY, &conn->flags, !persistent);
+ assign_bit(HCI_CONN_FLUSH_KEY, conn->flags, !persistent);
unlock:
hci_dev_unlock(hdev);
@@ -5117,7 +5127,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, void *data,
ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
if (ev->features[0] & LMP_HOST_SSP) {
- set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
+ set_bit(HCI_CONN_SSP_ENABLED, conn->flags);
} else {
/* It is mandatory by the Bluetooth specification that
* Extended Inquiry Results are only used when Secure
@@ -5127,17 +5137,17 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, void *data,
* To make these devices work, the internal SSP
* enabled flag needs to be cleared if the remote host
* features do not indicate SSP support */
- clear_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
+ clear_bit(HCI_CONN_SSP_ENABLED, conn->flags);
}
if (ev->features[0] & LMP_HOST_SC)
- set_bit(HCI_CONN_SC_ENABLED, &conn->flags);
+ set_bit(HCI_CONN_SC_ENABLED, conn->flags);
}
if (conn->state != BT_CONFIG)
goto unlock;
- if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
+ if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags)) {
struct hci_cp_remote_name_req cp;
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
@@ -5373,7 +5383,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, void *data,
mgmt_security_level_changed(conn);
}
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
if (ev->status && conn->state == BT_CONNECTED) {
hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
@@ -5474,7 +5484,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, void *data,
goto unlock;
/* Assume remote supports SSP since it has triggered this event */
- set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
+ set_bit(HCI_CONN_SSP_ENABLED, conn->flags);
hci_conn_hold(conn);
@@ -5485,7 +5495,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, void *data,
* pairing or if the remote is not requesting bonding.
*/
if (hci_dev_test_flag(hdev, HCI_BONDABLE) ||
- test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) ||
+ test_bit(HCI_CONN_AUTH_INITIATOR, conn->flags) ||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
struct hci_cp_io_capability_reply cp;
@@ -5595,7 +5605,7 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, void *data,
* local IO capability is not NoInputNoOutput, use JUST_WORKS
* method (mgmt_user_confirm with confirm_hint set to 1).
*/
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) &&
+ if (!test_bit(HCI_CONN_AUTH_PEND, conn->flags) &&
conn->io_capability != HCI_IO_NO_INPUT_OUTPUT) {
bt_dev_dbg(hdev, "Confirming auto-accept as acceptor");
confirm_hint = 1;
@@ -5738,7 +5748,7 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev, void *data,
* initiated the authentication. A traditional auth_complete
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
+ if (!test_bit(HCI_CONN_AUTH_PEND, conn->flags) && ev->status)
mgmt_auth_failed(conn, ev->status);
hci_conn_drop(conn);
@@ -5984,7 +5994,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
goto unlock;
/* Drop the connection if it has been aborted */
- if (test_bit(HCI_CONN_CANCEL, &conn->flags)) {
+ if (test_bit(HCI_CONN_CANCEL, conn->flags)) {
hci_conn_drop(conn);
goto unlock;
}
@@ -6041,6 +6051,28 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_conn_put(params->conn);
params->conn = NULL;
}
+
+ /* An explicit connect request is complete once connected, so
+ * clean it up here: hci_connect_le_scan_cleanup() no longer
+ * finds the params since they have just been removed from
+ * pend_le_conns.
+ */
+ if (params->explicit_connect) {
+ params->explicit_connect = false;
+
+ switch (params->auto_connect) {
+ case HCI_AUTO_CONN_EXPLICIT:
+ hci_conn_params_del(hdev, &conn->dst,
+ conn->dst_type);
+ break;
+ case HCI_AUTO_CONN_REPORT:
+ hci_pend_le_list_add(params,
+ &hdev->pend_le_reports);
+ break;
+ default:
+ break;
+ }
+ }
}
/* If we are central and have subrate parameters stored, queue a
@@ -6101,8 +6133,8 @@ static void hci_le_pa_sync_lost_evt(struct hci_dev *hdev, void *data,
/* Delete the pa sync connection */
conn = hci_conn_hash_lookup_pa_sync_handle(hdev, handle);
if (conn) {
- clear_bit(HCI_CONN_BIG_SYNC, &conn->flags);
- clear_bit(HCI_CONN_PA_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_BIG_SYNC, conn->flags);
+ clear_bit(HCI_CONN_PA_SYNC, conn->flags);
hci_disconn_cfm(conn, HCI_ERROR_REMOTE_USER_TERM);
hci_conn_del(conn);
}
@@ -6236,7 +6268,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
if (ev->status) {
- set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
+ set_bit(HCI_CONN_PA_SYNC_FAILED, pa_sync->flags);
/* Notify iso layer */
hci_connect_cfm(pa_sync, ev->status);
@@ -6611,6 +6643,17 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+ /* Broadcom ROM firmware (BCM4364B3 on Apple T2 Macs, no
+ * patchram) sets vendor bits in the legacy Event_Type.
+ */
+ if (hdev->manufacturer == 15 && info->type > LE_ADV_SCAN_RSP) {
+ /* 0x0f masks out upper vendor bits (0x10, 0x20, 0x80),
+ * extracting the standard legacy advertising type
+ * (0x00-0x04) stored in the low nibble (bits 0-3).
+ */
+ info->type &= 0x0f;
+ }
+
if (info->length <= max_adv_len(hdev)) {
rssi = info->data[info->length];
process_adv_report(hdev, info->type, &info->bdaddr,
@@ -6758,7 +6801,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
- clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_CREATE_PA_SYNC, conn->flags);
conn->sync_handle = le16_to_cpu(ev->handle);
conn->sid = HCI_SID_INVALID;
@@ -6783,7 +6826,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
pa_sync->sync_handle = le16_to_cpu(ev->handle);
if (ev->status) {
- set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
+ set_bit(HCI_CONN_PA_SYNC_FAILED, pa_sync->flags);
/* Notify iso layer */
hci_connect_cfm(pa_sync, ev->status);
@@ -6827,7 +6870,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,
goto unlock;
if (ev->data_status == LE_PA_DATA_COMPLETE &&
- !test_and_set_bit(HCI_CONN_PA_SYNC, &pa_sync->flags)) {
+ !test_and_set_bit(HCI_CONN_PA_SYNC, pa_sync->flags)) {
/* Notify iso layer */
hci_connect_cfm(pa_sync, 0);
@@ -6940,11 +6983,11 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, void *data,
* using a distributed LTK.
*/
if (ltk->type == SMP_STK) {
- set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
+ set_bit(HCI_CONN_STK_ENCRYPT, conn->flags);
list_del_rcu(&ltk->list);
kfree_rcu(ltk, rcu);
} else {
- clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
+ clear_bit(HCI_CONN_STK_ENCRYPT, conn->flags);
}
hci_dev_unlock(hdev);
@@ -7140,7 +7183,7 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data,
qos = &conn->iso_qos;
- pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, &conn->flags);
+ pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, conn->flags);
/* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 6, Part G
* page 3075:
@@ -7337,7 +7380,7 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
}
conn->state = BT_CONNECTED;
- set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
+ set_bit(HCI_CONN_BIG_CREATED, conn->flags);
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
hci_iso_setup_path(conn);
@@ -7382,7 +7425,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
- clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_CREATE_BIG_SYNC, conn->flags);
conn->num_bis = 0;
memset(conn->bis, 0, sizeof(conn->bis));
@@ -7405,7 +7448,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
if (ev->status != 0x42)
/* Mark PA sync as established */
- set_bit(HCI_CONN_PA_SYNC, &bis->flags);
+ set_bit(HCI_CONN_PA_SYNC, bis->flags);
bis->sync_handle = conn->sync_handle;
bis->iso_qos.bcast.big = ev->handle;
@@ -7418,7 +7461,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
if (!ev->status) {
bis->state = BT_CONNECTED;
- set_bit(HCI_CONN_BIG_SYNC, &bis->flags);
+ set_bit(HCI_CONN_BIG_SYNC, bis->flags);
hci_debugfs_create_conn(bis);
hci_conn_add_sysfs(bis);
hci_iso_setup_path(bis);
@@ -7436,7 +7479,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
if (!bis)
continue;
- set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
+ set_bit(HCI_CONN_BIG_SYNC_FAILED, bis->flags);
hci_connect_cfm(bis, ev->status);
}
@@ -7466,13 +7509,13 @@ static void hci_le_big_sync_lost_evt(struct hci_dev *hdev, void *data,
* it is consumed there instead of here.
*/
mgmt_conn = test_bit(HCI_CONN_MGMT_CONNECTED,
- &bis->flags);
+ bis->flags);
mgmt_device_disconnected(hdev, &bis->dst, bis->type,
bis->dst_type, ev->reason,
mgmt_conn);
}
- clear_bit(HCI_CONN_BIG_SYNC, &bis->flags);
+ clear_bit(HCI_CONN_BIG_SYNC, bis->flags);
hci_disconn_cfm(bis, ev->reason);
hci_conn_del(bis);
}
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 03f706c1f815..9d71ce96bd8e 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -2448,15 +2448,17 @@ static int hci_le_del_resolve_list_sync(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 bdaddr_type)
{
struct hci_cp_le_del_from_resolv_list cp;
- struct bdaddr_list_with_irk *entry;
+ bool exists;
if (!ll_privacy_capable(hdev))
return 0;
/* Check if the IRK has been programmed */
- entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list, bdaddr,
- bdaddr_type);
- if (!entry)
+ hci_dev_lock(hdev);
+ exists = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
+ bdaddr, bdaddr_type);
+ hci_dev_unlock(hdev);
+ if (!exists)
return 0;
cp.bdaddr_type = bdaddr_type;
@@ -2512,8 +2514,8 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
{
struct hci_cp_le_add_to_resolv_list cp;
struct smp_irk *irk;
- struct bdaddr_list_with_irk *entry;
struct hci_conn_params *p;
+ bool exists;
if (!ll_privacy_capable(hdev))
return 0;
@@ -2536,10 +2538,11 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
return 0;
/* Check if the IK has _not_ been programmed yet. */
- entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
- &params->addr,
- params->addr_type);
- if (entry)
+ hci_dev_lock(hdev);
+ exists = hci_bdaddr_list_lookup_with_irk(
+ &hdev->le_resolv_list, &params->addr, params->addr_type);
+ hci_dev_unlock(hdev);
+ if (exists)
return 0;
cp.bdaddr_type = params->addr_type;
@@ -2733,7 +2736,7 @@ static int hci_pause_advertising_sync(struct hci_dev *hdev)
static int hci_resume_advertising_sync(struct hci_dev *hdev)
{
struct adv_info *adv, *tmp;
- int err;
+ int err = 0;
/* If advertising has not been paused there is nothing to do. */
if (!hdev->advertising_paused)
@@ -2766,13 +2769,19 @@ static int hci_resume_advertising_sync(struct hci_dev *hdev)
*/
if (hci_dev_test_and_clear_flag(hdev, HCI_LE_ADV_0))
err = hci_enable_ext_advertising_sync(hdev, 0x00);
- } else {
+ } else if (hdev->cur_adv_instance) {
/* Schedule for most recent instance to be restarted and begin
* the software rotation loop
*/
err = hci_schedule_adv_instance_sync(hdev,
hdev->cur_adv_instance,
true);
+ } else if (hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+ !hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+ /* hci_schedule_adv_instance_sync() rejects instance 0x00 while
+ * HCI_ADVERTISING is set, so enable it directly.
+ */
+ err = hci_start_adv_sync(hdev, 0x00);
}
hdev->advertising_paused = false;
@@ -2879,6 +2888,31 @@ static struct conn_params *conn_params_copy(struct list_head *list, size_t *n)
return p;
}
+static struct conn_params *le_accept_list_copy(struct hci_dev *hdev,
+ size_t *n)
+{
+ struct bdaddr_list *entry;
+ struct conn_params *params;
+ size_t i = 0;
+
+ hci_dev_lock(hdev);
+
+ *n = list_count_nodes(&hdev->le_accept_list);
+ params = kvzalloc_objs(struct conn_params, *n);
+ if (!params)
+ goto unlock;
+
+ list_for_each_entry(entry, &hdev->le_accept_list, list) {
+ bacpy(&params[i].addr, &entry->bdaddr);
+ params[i].addr_type = entry->bdaddr_type;
+ i++;
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+ return params;
+}
+
/* Clear LE Accept List */
static int hci_le_clear_accept_list_sync(struct hci_dev *hdev)
{
@@ -2909,7 +2943,6 @@ static int hci_le_clear_accept_list_sync(struct hci_dev *hdev)
static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
{
struct conn_params *params;
- struct bdaddr_list *b, *t;
u8 num_entries = 0;
bool pend_conn, pend_report;
u8 filter_policy;
@@ -2974,20 +3007,27 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
* report. If not present in either list, then remove it from
* the controller.
*/
- list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
+ params = le_accept_list_copy(hdev, &n);
+ if (!params) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < n; i++) {
rcu_read_lock();
- if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) {
+ if (hci_conn_hash_lookup_le(hdev, &params[i].addr,
+ params[i].addr_type)) {
rcu_read_unlock();
continue;
}
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
- &b->bdaddr,
- b->bdaddr_type);
- pend_report = hci_pend_le_action_lookup(&hdev->pend_le_reports,
- &b->bdaddr,
- b->bdaddr_type);
+ &params[i].addr,
+ params[i].addr_type);
+ pend_report = hci_pend_le_action_lookup(
+ &hdev->pend_le_reports, &params[i].addr,
+ params[i].addr_type);
rcu_read_unlock();
@@ -2995,14 +3035,16 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
* remove it from the acceptlist.
*/
if (!pend_conn && !pend_report) {
- hci_le_del_accept_list_sync(hdev, &b->bdaddr,
- b->bdaddr_type);
+ hci_le_del_accept_list_sync(hdev, &params[i].addr,
+ params[i].addr_type);
continue;
}
num_entries++;
}
+ kvfree(params);
+
/* Since all no longer valid accept list entries have been
* removed, walk through the list of pending connections
* and ensure that any new device gets programmed into
@@ -3505,9 +3547,13 @@ int hci_write_ssp_mode_sync(struct hci_dev *hdev, u8 mode)
return hci_write_sc_support_sync(hdev, 0x01);
}
+static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u16 bit,
+ u8 value);
+
int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul)
{
struct hci_cp_write_le_host_supported cp;
+ int err;
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) ||
!lmp_bredr_capable(hdev))
@@ -3525,8 +3571,16 @@ int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul)
cp.le = le;
cp.simul = simul;
- return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
- sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+ err = __hci_cmd_sync_status(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+ if (err || !cis_capable(hdev))
+ return err;
+
+ /* The CIS host feature is set during init based on LE being enabled,
+ * which is not repeated when LE is enabled afterwards, e.g. while the
+ * controller is still auto powered on, so update it here.
+ */
+ return hci_le_set_host_feature_sync(hdev, 32, iso_enabled(hdev));
}
static int hci_powered_update_adv_sync(struct hci_dev *hdev)
@@ -4828,6 +4882,9 @@ static int hci_le_set_def_rate_sync(struct hci_dev *hdev)
struct hci_cp_le_set_def_rate cp;
u16 interval_min = 0x000a; /* 1.25 ms */
u16 interval_max = 0x0078; /* 15 ms */
+ u16 subrate_max = 0x0004;
+ u16 max_latency = 0x0000;
+ u32 supv_timeout;
if (!le_sci_capable(hdev))
return 0;
@@ -4852,10 +4909,31 @@ static int hci_le_set_def_rate_sync(struct hci_dev *hdev)
cp.interval_min = cpu_to_le16(interval_min);
cp.interval_max = cpu_to_le16(interval_max);
cp.subrate_min = cpu_to_le16(0x0001);
- cp.subrate_max = cpu_to_le16(0x0004);
- cp.max_latency = cpu_to_le16(0x0000);
+ cp.subrate_max = cpu_to_le16(subrate_max);
+ cp.max_latency = cpu_to_le16(max_latency);
cp.cont_num = cpu_to_le16(0x0001);
- cp.supv_timeout = cpu_to_le16(0x000c); /* 120 ms */
+
+ /* HOGP v1.2 recommends a Supervision_Timeout greater than or equal to
+ * (1 + Max_Latency) x Subrate_Max x Connection_Interval_Max x 2, but
+ * the Core spec treats the equality as invalid:
+ *
+ * BLUETOOTH CORE SPECIFICATION Version 6.3 | Vol 4, Part E
+ * 7.8.158. LE Set Default Rate Parameters command
+ *
+ * If Connection_Interval_Max x Subrate_Max x (Max_Latency + 1) is
+ * greater than or equal to Supervision_Timeout x 40, the Controller
+ * shall return the error code Invalid HCI Command Parameters (0x12).
+ *
+ * So default to 2 s, 10x lower than the 20 s used for non-SCI
+ * connections while still well above the minimum required, but make
+ * sure it stays strictly above the minimum in case interval_max has
+ * been raised.
+ */
+ supv_timeout = (u32)interval_max * subrate_max * (max_latency + 1);
+ supv_timeout = supv_timeout / 40 + 1;
+ supv_timeout = clamp_t(u32, max_t(u32, supv_timeout, 0x00c8),
+ 0x000a, 0x0c80);
+ cp.supv_timeout = cpu_to_le16(supv_timeout);
/* The connection event length recommended in requests by a Peripheral
* uses units of 125 us with a valid range of 0x0001 to 0x7CFF
@@ -5637,6 +5715,11 @@ int hci_dev_close_sync(struct hci_dev *hdev)
return err;
}
+ /* Wait for hci_recv_frame() callers that still saw HCI_UP, so that
+ * none of them queues rx_work while the workqueue is drained below.
+ */
+ synchronize_rcu();
+
hci_leds_update_powered(hdev, false);
/* Flush RX and TX works */
@@ -5913,11 +5996,11 @@ static int hci_le_connect_cancel_sync(struct hci_dev *hdev,
/* Return reason if scanning since the connection shall probably be
* cleanup directly.
*/
- if (test_bit(HCI_CONN_SCANNING, &conn->flags))
+ if (test_bit(HCI_CONN_SCANNING, conn->flags))
return reason;
if (conn->role == HCI_ROLE_SLAVE ||
- test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
+ test_and_set_bit(HCI_CONN_CANCEL, conn->flags))
return 0;
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN_CANCEL,
@@ -5939,7 +6022,7 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
* 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))
+ 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 */
@@ -6310,6 +6393,7 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
u8 filter_policy = 0x00;
/* Default is to enable duplicates filter */
u8 filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+ bool paused;
int err;
bt_dev_dbg(hdev, "");
@@ -6333,6 +6417,12 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
if (err)
goto failed;
+ /* LE Set Random Address is disallowed while advertising is enabled. */
+ paused = !hdev->advertising_paused;
+ err = hci_pause_advertising_sync(hdev);
+ if (err)
+ goto failed;
+
/* All active scans will be done with either a resolvable private
* address (when privacy feature has been enabled) or non-resolvable
* private address.
@@ -6342,6 +6432,9 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
if (err < 0)
own_addr_type = ADDR_LE_DEV_PUBLIC;
+ if (paused)
+ hci_resume_advertising_sync(hdev);
+
if (hci_is_adv_monitoring(hdev) ||
(hci_test_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER) &&
hdev->discovery.result_filtering)) {
@@ -6365,9 +6458,8 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
return err;
failed:
- /* Resume advertising if it was paused */
- if (ll_privacy_capable(hdev))
- hci_resume_advertising_sync(hdev);
+ /* No-op when advertising was not paused. */
+ hci_resume_advertising_sync(hdev);
/* Resume passive scanning */
hci_update_passive_scan_sync(hdev);
@@ -6928,7 +7020,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
bt_dev_dbg(hdev, "conn %p", conn);
- clear_bit(HCI_CONN_SCANNING, &conn->flags);
+ clear_bit(HCI_CONN_SCANNING, conn->flags);
conn->state = BT_CONNECT;
/* If requested to connect as peripheral use directed advertising */
@@ -6995,7 +7087,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
/* Mark create connection in flight so hci_cancel_connect_sync() can
* cancel it while blocking on the connection complete event.
*/
- set_bit(HCI_CONN_CREATE, &conn->flags);
+ set_bit(HCI_CONN_CREATE, conn->flags);
/* Send command LE Extended Create Connection if supported */
if (use_ext_conn(hdev)) {
@@ -7032,7 +7124,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
done:
- clear_bit(HCI_CONN_CREATE, &conn->flags);
+ clear_bit(HCI_CONN_CREATE, conn->flags);
if (err == -ETIMEDOUT)
hci_le_connect_cancel_sync(hdev, conn, 0x00);
@@ -7079,7 +7171,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
/* 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))
+ if (test_bit(HCI_CONN_CREATE_CIS, conn->flags))
goto done;
}
@@ -7115,7 +7207,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
conn->iso_qos.ucast.cig != cig)
continue;
- set_bit(HCI_CONN_CREATE_CIS, &conn->flags);
+ 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);
timeout = conn->conn_timeout;
@@ -7318,14 +7410,14 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
/* Mark create connection in flight so hci_cancel_connect_sync() can
* cancel it while blocking on the connection complete event.
*/
- set_bit(HCI_CONN_CREATE, &conn->flags);
+ set_bit(HCI_CONN_CREATE, conn->flags);
err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
sizeof(cp), &cp,
HCI_EV_CONN_COMPLETE,
conn->conn_timeout, NULL);
- clear_bit(HCI_CONN_CREATE, &conn->flags);
+ clear_bit(HCI_CONN_CREATE, conn->flags);
return err;
}
@@ -7414,7 +7506,7 @@ static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
mutex_lock(&hdev->cmd_sync_work_lock);
/* In flight: this connection owns the pending request, cancel it. */
- if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
+ if (test_bit(HCI_CONN_CREATE, conn->flags)) {
hci_cmd_sync_cancel(hdev, ECANCELED);
goto unlock;
}
@@ -7445,7 +7537,7 @@ static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
*/
mutex_lock(&hdev->cmd_sync_work_lock);
- if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
+ if (test_bit(HCI_CONN_CREATE, conn->flags)) {
hci_cmd_sync_cancel(hdev, ECANCELED);
goto unlock;
}
@@ -7472,7 +7564,7 @@ static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
*/
mutex_lock(&hdev->cmd_sync_work_lock);
- if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
+ if (test_bit(HCI_CONN_CREATE_CIS, conn->flags))
hci_cmd_sync_cancel(hdev, ECANCELED);
mutex_unlock(&hdev->cmd_sync_work_lock);
@@ -7609,7 +7701,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
hci_dev_lock(hdev);
if (hci_conn_valid(hdev, conn))
- clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_CREATE_PA_SYNC, conn->flags);
if (!err)
goto unlock;
@@ -7621,7 +7713,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
if (IS_ERR(pa_sync))
goto unlock;
- set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
+ set_bit(HCI_CONN_PA_SYNC_FAILED, pa_sync->flags);
/* Notify iso layer */
hci_connect_cfm(pa_sync, bt_status(err));
@@ -7694,7 +7786,7 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
* program the address in the allow list so PA advertisements can be
* received.
*/
- set_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
+ set_bit(HCI_CONN_CREATE_PA_SYNC, conn->flags);
hci_update_passive_scan_sync(hdev);
@@ -7797,7 +7889,7 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
if (err == -ECANCELED)
goto done;
- clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_CREATE_BIG_SYNC, conn->flags);
done:
hci_conn_put(conn);
@@ -7814,7 +7906,7 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
- set_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+ set_bit(HCI_CONN_CREATE_BIG_SYNC, conn->flags);
memset(cp, 0, sizeof(*cp));
cp->handle = qos->bcast.big;
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index c1fdd9846eeb..06f1e7ba2cb1 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -912,7 +912,7 @@ static void iso_sock_disconn(struct sock *sk)
struct sock *bis_sk;
struct hci_conn *hcon = iso_pi(sk)->conn->hcon;
- if (test_bit(HCI_CONN_BIG_CREATED, &hcon->flags)) {
+ if (test_bit(HCI_CONN_BIG_CREATED, hcon->flags)) {
bis_sk = iso_get_sock_big(sk, &iso_pi(sk)->src,
&iso_pi(sk)->dst,
iso_pi(sk)->qos.bcast.big);
@@ -957,8 +957,8 @@ static void __iso_sock_close(struct sock *sk)
case BT_CONNECT2:
if (iso_pi(sk)->conn->hcon &&
- (test_bit(HCI_CONN_PA_SYNC, &iso_pi(sk)->conn->hcon->flags) ||
- test_bit(HCI_CONN_PA_SYNC_FAILED, &iso_pi(sk)->conn->hcon->flags)))
+ (test_bit(HCI_CONN_PA_SYNC, iso_pi(sk)->conn->hcon->flags) ||
+ test_bit(HCI_CONN_PA_SYNC_FAILED, iso_pi(sk)->conn->hcon->flags)))
iso_sock_disconn(sk);
else
iso_chan_del(sk, ECONNRESET);
@@ -2289,7 +2289,7 @@ static void iso_conn_ready(struct iso_conn *conn)
hdev = hcon->hdev;
- if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags)) {
+ if (test_bit(HCI_CONN_BIG_SYNC, hcon->flags)) {
/* A BIS slave hcon is notified to the ISO layer
* after the Command Complete for the LE Setup
* ISO Data Path command is received. Get the
@@ -2298,7 +2298,7 @@ static void iso_conn_ready(struct iso_conn *conn)
parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
BT_LISTEN, iso_match_big_hcon,
hcon);
- } else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
+ } else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, hcon->flags)) {
ev = hci_recv_event_data(hcon->hdev,
HCI_EVT_LE_BIG_SYNC_ESTABLISHED);
@@ -2312,7 +2312,7 @@ static void iso_conn_ready(struct iso_conn *conn)
&hcon->dst,
BT_LISTEN,
iso_match_big, ev);
- } else if (test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
+ } else if (test_bit(HCI_CONN_PA_SYNC_FAILED, hcon->flags)) {
ev2 = hci_recv_event_data(hcon->hdev,
HCI_EV_LE_PA_SYNC_ESTABLISHED);
if (ev2)
@@ -2320,7 +2320,7 @@ static void iso_conn_ready(struct iso_conn *conn)
&hcon->dst,
BT_LISTEN,
iso_match_sid, ev2);
- } else if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
+ } else if (test_bit(HCI_CONN_PA_SYNC, hcon->flags)) {
ev3 = hci_recv_event_data(hcon->hdev,
HCI_EV_LE_PER_ADV_REPORT);
if (ev3)
@@ -2376,7 +2376,7 @@ static void iso_conn_ready(struct iso_conn *conn)
hcon->dst_type = le_addr_type(iso_pi(parent)->dst_type);
}
- if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
+ if (test_bit(HCI_CONN_PA_SYNC, hcon->flags)) {
iso_pi(sk)->qos = iso_pi(parent)->qos;
hcon->iso_qos = iso_pi(sk)->qos;
iso_pi(sk)->bc_sid = iso_pi(parent)->bc_sid;
@@ -2676,8 +2676,8 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
* into the accept queue of the listening socket and wake up
* userspace, to inform the user about the event.
*/
- if (!status || test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags) ||
- test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
+ if (!status || test_bit(HCI_CONN_BIG_SYNC_FAILED, hcon->flags) ||
+ test_bit(HCI_CONN_PA_SYNC_FAILED, hcon->flags)) {
struct iso_conn *conn;
conn = iso_conn_add(hcon);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4dd56e0074fd..83b622a42811 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1575,7 +1575,7 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
if (chan->sec_level == BT_SECURITY_FIPS)
min_key_size = 16;
- return (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags) ||
+ return (!test_bit(HCI_CONN_ENCRYPT, hcon->flags) ||
hcon->enc_key_size >= min_key_size);
}
@@ -7389,7 +7389,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
u16 cid, len;
__le16 psm;
- if (hcon->state != BT_CONNECTED) {
+ /* Also hold data back while the encryption key size is read, as
+ * the security of the link, e.g. for SMP over BR/EDR, depends on it.
+ */
+ if (hcon->state != BT_CONNECTED ||
+ test_bit(HCI_CONN_ENC_KEY_SIZE_PEND, hcon->flags)) {
BT_DBG("queueing pending rx skb");
skb_queue_tail(&conn->pending_rx, skb);
return;
@@ -7452,7 +7456,11 @@ static void process_pending_rx(struct work_struct *work)
mutex_lock(&conn->lock);
- while ((skb = skb_dequeue(&conn->pending_rx)))
+ /* l2cap_recv_frame() would queue it all again while the encryption
+ * key size is read, l2cap_security_cfm() schedules this once known.
+ */
+ while (!test_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->hcon->flags) &&
+ (skb = skb_dequeue(&conn->pending_rx)))
l2cap_recv_frame(conn, skb);
mutex_unlock(&conn->lock);
@@ -8041,6 +8049,10 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
}
mutex_unlock(&conn->lock);
+
+ /* Process data held back while the encryption key size was read */
+ if (!skb_queue_empty(&conn->pending_rx))
+ queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
}
/* Append fragment into frame respecting the maximum len of rx_skb */
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 3b9c82bd2f02..3cb4f9ece83c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -304,6 +304,8 @@ static u8 mgmt_errno_status(int err)
return MGMT_STATUS_DISCONNECTED;
case -ECANCELED:
return MGMT_STATUS_CANCELLED;
+ case -ERFKILL:
+ return MGMT_STATUS_RFKILLED;
}
return MGMT_STATUS_FAILED;
@@ -3241,7 +3243,7 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
/* Defer clearing up the connection parameters until closing to
* give a chance of keeping them if a repairing happens.
*/
- set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+ set_bit(HCI_CONN_PARAM_REMOVAL_PEND, conn->flags);
/* Disable auto-connection parameters if present */
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, addr_type);
@@ -3416,7 +3418,7 @@ static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data,
i = 0;
list_for_each_entry(c, &hdev->conn_hash.list, list) {
- if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
+ if (test_bit(HCI_CONN_MGMT_CONNECTED, c->flags))
i++;
}
@@ -3428,7 +3430,7 @@ static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data,
i = 0;
list_for_each_entry(c, &hdev->conn_hash.list, list) {
- if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
+ if (!test_bit(HCI_CONN_MGMT_CONNECTED, c->flags))
continue;
bacpy(&rp->addr[i].bdaddr, &c->dst);
rp->addr[i].type = link_to_bdaddr(c->type, c->dst_type);
@@ -3628,7 +3630,7 @@ static int pairing_complete(struct mgmt_pending_cmd *cmd, u8 status)
/* The device is paired so there is no need to remove
* its connection parameters anymore.
*/
- clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+ clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, conn->flags);
hci_conn_put(conn);
@@ -10128,7 +10130,7 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u16 eir_len = 0;
u32 flags = 0;
- if (test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ if (test_and_set_bit(HCI_CONN_MGMT_CONNECTED, conn->flags))
return;
/* allocate buff for LE or BR/EDR adv */
@@ -10177,7 +10179,7 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
* since userspace did not know about them yet, so report it now.
* Unencrypted connections have no change to report.
*/
- if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
+ if (test_bit(HCI_CONN_ENCRYPT, conn->flags))
mgmt_security_level_changed(conn);
}
@@ -10289,7 +10291,7 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status)
* still needs it set to know whether to report the security level
* reset, so it is consumed there instead of here.
*/
- if (test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
+ if (test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags)) {
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
conn->dst_type,
hci_to_mgmt_reason(status), true);
@@ -10930,7 +10932,7 @@ void mgmt_security_level_changed(struct hci_conn *conn)
u8 *tlv_data;
u8 tlv_len;
- if (!test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ if (!test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags))
return;
/* Only ACL, LE and BIS links are tracked as mgmt connections, so
@@ -10961,9 +10963,9 @@ void mgmt_security_level_changed(struct hci_conn *conn)
tlv = (void *)tlv_data;
tlv->type = cpu_to_le16(MGMT_SEC_LEVEL_CHANGED_PARAM_ENC_TYPE);
tlv->length = sizeof(__u8);
- if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
+ if (!test_bit(HCI_CONN_ENCRYPT, conn->flags))
tlv->value[0] = MGMT_CONN_SEC_ENCRYPT_NONE;
- else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
+ else if (test_bit(HCI_CONN_AES_CCM, conn->flags))
tlv->value[0] = MGMT_CONN_SEC_ENCRYPT_AES_CCM;
else
tlv->value[0] = MGMT_CONN_SEC_ENCRYPT_E0;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 5dac8b2eae42..984b2d0b5e3e 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -984,12 +984,12 @@ static u8 smp_random(struct smp_chan *smp)
smp_s1(smp->tk, smp->rrnd, smp->prnd, stk);
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags))
return SMP_UNSPECIFIED;
hci_le_start_enc(hcon, ediv, rand, stk, smp->enc_key_size);
hcon->enc_key_size = smp->enc_key_size;
- set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
+ set_bit(HCI_CONN_STK_ENCRYPT, hcon->flags);
} else {
u8 stk[16], auth;
__le64 rand = 0;
@@ -1028,7 +1028,7 @@ static void smp_notify_keys(struct l2cap_conn *conn)
persistent = false;
else
persistent = !test_bit(HCI_CONN_FLUSH_KEY,
- &hcon->flags);
+ hcon->flags);
} else {
/* The LTKs, IRKs and CSRKs should be persistent only if
* both sides had the bonding bit set in their
@@ -1753,7 +1753,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
/* SMP over BR/EDR requires special treatment */
if (conn->hcon->type == ACL_LINK) {
/* We must have a BR/EDR SC link */
- if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) &&
+ if (!test_bit(HCI_CONN_AES_CCM, conn->hcon->flags) &&
!hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
return SMP_CROSS_TRANSP_NOT_ALLOWED;
@@ -2210,14 +2210,14 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
if (smp_ltk_sec_level(key) < sec_level)
return false;
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags))
return true;
hci_le_start_enc(hcon, key->ediv, key->rand, key->val, key->enc_size);
hcon->enc_key_size = key->enc_size;
/* We never store STKs for initiator role, so clear this flag */
- clear_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
+ clear_bit(HCI_CONN_STK_ENCRYPT, hcon->flags);
return true;
}
@@ -2235,7 +2235,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
* don't have an LTK (e.g. because of key distribution bits).
*/
if (key_pref == SMP_USE_LTK &&
- test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+ test_bit(HCI_CONN_STK_ENCRYPT, hcon->flags) &&
hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
return false;
@@ -3084,11 +3084,11 @@ static void bredr_pairing(struct l2cap_chan *chan)
bt_dev_dbg(hdev, "chan %p", chan);
/* Only new pairings are interesting */
- if (!test_bit(HCI_CONN_NEW_LINK_KEY, &hcon->flags))
+ if (!test_bit(HCI_CONN_NEW_LINK_KEY, hcon->flags))
return;
/* Don't bother if we're not encrypted */
- if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ if (!test_bit(HCI_CONN_ENCRYPT, hcon->flags))
return;
/* Only initiator may initiate SMP over BR/EDR */
@@ -3100,7 +3100,7 @@ static void bredr_pairing(struct l2cap_chan *chan)
return;
/* BR/EDR must use Secure Connections for SMP */
- if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) &&
+ if (!test_bit(HCI_CONN_AES_CCM, hcon->flags) &&
!hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
return;
@@ -3149,7 +3149,7 @@ static void smp_resume_cb(struct l2cap_chan *chan)
if (!smp)
return;
- if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ if (!test_bit(HCI_CONN_ENCRYPT, hcon->flags))
return;
cancel_delayed_work(&smp->security_timer);
@@ -3172,7 +3172,7 @@ static void smp_ready_cb(struct l2cap_chan *chan)
*/
conn->smp = chan;
- if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, hcon->flags))
bredr_pairing(chan);
}