mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-14 00:39:30 -04:00
Merge tag 'for-net-2025-09-22' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - Fix build after header cleanup - hci_sync: Fix hci_resume_advertising_sync - hci_event: Fix UAF in hci_conn_tx_dequeue - hci_event: Fix UAF in hci_acl_create_conn_sync - MGMT: Fix possible UAFs * tag 'for-net-2025-09-22' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: MGMT: Fix possible UAFs Bluetooth: hci_event: Fix UAF in hci_acl_create_conn_sync Bluetooth: hci_event: Fix UAF in hci_conn_tx_dequeue Bluetooth: hci_sync: Fix hci_resume_advertising_sync Bluetooth: Fix build after header cleanup ==================== Link: https://patch.msgid.link/20250922143315.3007176-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -312,7 +312,9 @@ config BT_HCIBCM4377
|
||||
|
||||
config BT_HCIBPA10X
|
||||
tristate "HCI BPA10x USB driver"
|
||||
depends on BT_HCIUART
|
||||
depends on USB
|
||||
select BT_HCIUART_H4
|
||||
help
|
||||
Bluetooth HCI BPA10x USB driver.
|
||||
This driver provides support for the Digianswer BPA 100/105 Bluetooth
|
||||
@@ -437,8 +439,10 @@ config BT_MTKSDIO
|
||||
|
||||
config BT_MTKUART
|
||||
tristate "MediaTek HCI UART driver"
|
||||
depends on BT_HCIUART
|
||||
depends on SERIAL_DEV_BUS
|
||||
depends on USB || !BT_HCIBTUSB_MTK
|
||||
select BT_HCIUART_H4
|
||||
select BT_MTK
|
||||
help
|
||||
MediaTek Bluetooth HCI UART driver.
|
||||
@@ -483,7 +487,9 @@ config BT_VIRTIO
|
||||
|
||||
config BT_NXPUART
|
||||
tristate "NXP protocol support"
|
||||
depends on BT_HCIUART
|
||||
depends on SERIAL_DEV_BUS
|
||||
select BT_HCIUART_H4
|
||||
select CRC32
|
||||
select CRC8
|
||||
help
|
||||
|
||||
@@ -121,10 +121,6 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
|
||||
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
|
||||
unsigned int oper_speed);
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_H4
|
||||
int h4_init(void);
|
||||
int h4_deinit(void);
|
||||
|
||||
struct h4_recv_pkt {
|
||||
u8 type; /* Packet type */
|
||||
u8 hlen; /* Header length */
|
||||
@@ -162,6 +158,10 @@ struct h4_recv_pkt {
|
||||
.lsize = 2, \
|
||||
.maxlen = HCI_MAX_FRAME_SIZE \
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_H4
|
||||
int h4_init(void);
|
||||
int h4_deinit(void);
|
||||
|
||||
struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
const unsigned char *buffer, int count,
|
||||
const struct h4_recv_pkt *pkts, int pkts_count);
|
||||
|
||||
@@ -1245,6 +1245,27 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_role(struct hci_dev *hdev,
|
||||
__u8 type, __u8 role,
|
||||
bdaddr_t *ba)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == type && c->role == role && !bacmp(&c->dst, ba)) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev,
|
||||
bdaddr_t *ba,
|
||||
__u8 ba_type)
|
||||
|
||||
@@ -3087,8 +3087,18 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* Check for existing connection:
|
||||
*
|
||||
* 1. If it doesn't exist then it must be receiver/slave role.
|
||||
* 2. If it does exist confirm that it is connecting/BT_CONNECT in case
|
||||
* of initiator/master role since there could be a collision where
|
||||
* either side is attempting to connect or something like a fuzzing
|
||||
* testing is trying to play tricks to destroy the hcon object before
|
||||
* it even attempts to connect (e.g. hcon->state == BT_OPEN).
|
||||
*/
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
if (!conn ||
|
||||
(conn->role == HCI_ROLE_MASTER && conn->state != BT_CONNECT)) {
|
||||
/* In case of error status and there is no connection pending
|
||||
* just unlock as there is nothing to cleanup.
|
||||
*/
|
||||
@@ -4391,6 +4401,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
bt_dev_dbg(hdev, "num %d", ev->num);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (i = 0; i < ev->num; i++) {
|
||||
struct hci_comp_pkts_info *info = &ev->handles[i];
|
||||
struct hci_conn *conn;
|
||||
@@ -4472,6 +4484,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
|
||||
}
|
||||
|
||||
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_mode_change_evt(struct hci_dev *hdev, void *data,
|
||||
@@ -5634,8 +5648,18 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
*/
|
||||
hci_dev_clear_flag(hdev, HCI_LE_ADV);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
|
||||
if (!conn) {
|
||||
/* Check for existing connection:
|
||||
*
|
||||
* 1. If it doesn't exist then use the role to create a new object.
|
||||
* 2. If it does exist confirm that it is connecting/BT_CONNECT in case
|
||||
* of initiator/master role since there could be a collision where
|
||||
* either side is attempting to connect or something like a fuzzing
|
||||
* testing is trying to play tricks to destroy the hcon object before
|
||||
* it even attempts to connect (e.g. hcon->state == BT_OPEN).
|
||||
*/
|
||||
conn = hci_conn_hash_lookup_role(hdev, LE_LINK, role, bdaddr);
|
||||
if (!conn ||
|
||||
(conn->role == HCI_ROLE_MASTER && conn->state != BT_CONNECT)) {
|
||||
/* In case of error status and there is no connection pending
|
||||
* just unlock as there is nothing to cleanup.
|
||||
*/
|
||||
|
||||
@@ -2594,6 +2594,13 @@ static int hci_resume_advertising_sync(struct hci_dev *hdev)
|
||||
hci_remove_ext_adv_instance_sync(hdev, adv->instance,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* If current advertising instance is set to instance 0x00
|
||||
* then we need to re-enable it.
|
||||
*/
|
||||
if (!hdev->cur_adv_instance)
|
||||
err = hci_enable_ext_advertising_sync(hdev,
|
||||
hdev->cur_adv_instance);
|
||||
} else {
|
||||
/* Schedule for most recent instance to be restarted and begin
|
||||
* the software rotation loop
|
||||
|
||||
@@ -1323,8 +1323,7 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
|
||||
struct mgmt_mode *cp;
|
||||
|
||||
/* Make sure cmd still outstanding. */
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_SET_POWERED, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
cp = cmd->param;
|
||||
@@ -1351,23 +1350,29 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
|
||||
mgmt_status(err));
|
||||
}
|
||||
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int set_powered_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_mode *cp;
|
||||
struct mgmt_mode cp;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
/* Make sure cmd still outstanding. */
|
||||
if (cmd != pending_find(MGMT_OP_SET_POWERED, hdev))
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
cp = cmd->param;
|
||||
memcpy(&cp, cmd->param, sizeof(cp));
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
return hci_set_powered_sync(hdev, cp->val);
|
||||
return hci_set_powered_sync(hdev, cp.val);
|
||||
}
|
||||
|
||||
static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
@@ -1516,8 +1521,7 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
/* Make sure cmd still outstanding. */
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
@@ -1539,12 +1543,15 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
|
||||
new_settings(hdev, cmd->sk);
|
||||
|
||||
done:
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int set_discoverable_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
if (!mgmt_pending_listed(hdev, data))
|
||||
return -ECANCELED;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
return hci_update_discoverable_sync(hdev);
|
||||
@@ -1691,8 +1698,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
/* Make sure cmd still outstanding. */
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
@@ -1707,7 +1713,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
|
||||
new_settings(hdev, cmd->sk);
|
||||
|
||||
done:
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
@@ -1743,6 +1749,9 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
|
||||
|
||||
static int set_connectable_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
if (!mgmt_pending_listed(hdev, data))
|
||||
return -ECANCELED;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
return hci_update_connectable_sync(hdev);
|
||||
@@ -1919,14 +1928,17 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct cmd_lookup match = { NULL, hdev };
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_mode *cp = cmd->param;
|
||||
u8 enable = cp->val;
|
||||
struct mgmt_mode *cp;
|
||||
u8 enable;
|
||||
bool changed;
|
||||
|
||||
/* Make sure cmd still outstanding. */
|
||||
if (err == -ECANCELED || cmd != pending_find(MGMT_OP_SET_SSP, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
cp = cmd->param;
|
||||
enable = cp->val;
|
||||
|
||||
if (err) {
|
||||
u8 mgmt_err = mgmt_status(err);
|
||||
|
||||
@@ -1935,8 +1947,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
|
||||
new_settings(hdev, NULL);
|
||||
}
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, true,
|
||||
cmd_status_rsp, &mgmt_err);
|
||||
mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1946,7 +1957,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
|
||||
changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED);
|
||||
}
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, true, settings_rsp, &match);
|
||||
settings_rsp(cmd, &match);
|
||||
|
||||
if (changed)
|
||||
new_settings(hdev, match.sk);
|
||||
@@ -1960,14 +1971,25 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
|
||||
static int set_ssp_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_mode *cp = cmd->param;
|
||||
struct mgmt_mode cp;
|
||||
bool changed = false;
|
||||
int err;
|
||||
|
||||
if (cp->val)
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memcpy(&cp, cmd->param, sizeof(cp));
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (cp.val)
|
||||
changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED);
|
||||
|
||||
err = hci_write_ssp_mode_sync(hdev, cp->val);
|
||||
err = hci_write_ssp_mode_sync(hdev, cp.val);
|
||||
|
||||
if (!err && changed)
|
||||
hci_dev_clear_flag(hdev, HCI_SSP_ENABLED);
|
||||
@@ -2060,32 +2082,50 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
|
||||
static void set_le_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct cmd_lookup match = { NULL, hdev };
|
||||
u8 status = mgmt_status(err);
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (status) {
|
||||
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, true, cmd_status_rsp,
|
||||
&status);
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, data))
|
||||
return;
|
||||
|
||||
if (status) {
|
||||
mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, true, settings_rsp, &match);
|
||||
settings_rsp(cmd, &match);
|
||||
|
||||
new_settings(hdev, match.sk);
|
||||
|
||||
if (match.sk)
|
||||
sock_put(match.sk);
|
||||
|
||||
done:
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int set_le_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_mode *cp = cmd->param;
|
||||
u8 val = !!cp->val;
|
||||
struct mgmt_mode cp;
|
||||
u8 val;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memcpy(&cp, cmd->param, sizeof(cp));
|
||||
val = !!cp.val;
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!val) {
|
||||
hci_clear_adv_instance_sync(hdev, NULL, 0x00, true);
|
||||
|
||||
@@ -2127,7 +2167,12 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
u8 status = mgmt_status(err);
|
||||
struct sock *sk = cmd->sk;
|
||||
struct sock *sk;
|
||||
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
sk = cmd->sk;
|
||||
|
||||
if (status) {
|
||||
mgmt_pending_foreach(MGMT_OP_SET_MESH_RECEIVER, hdev, true,
|
||||
@@ -2142,24 +2187,37 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
|
||||
static int set_mesh_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_set_mesh *cp = cmd->param;
|
||||
size_t len = cmd->param_len;
|
||||
struct mgmt_cp_set_mesh cp;
|
||||
size_t len;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memcpy(&cp, cmd->param, sizeof(cp));
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
len = cmd->param_len;
|
||||
|
||||
memset(hdev->mesh_ad_types, 0, sizeof(hdev->mesh_ad_types));
|
||||
|
||||
if (cp->enable)
|
||||
if (cp.enable)
|
||||
hci_dev_set_flag(hdev, HCI_MESH);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_MESH);
|
||||
|
||||
hdev->le_scan_interval = __le16_to_cpu(cp->period);
|
||||
hdev->le_scan_window = __le16_to_cpu(cp->window);
|
||||
hdev->le_scan_interval = __le16_to_cpu(cp.period);
|
||||
hdev->le_scan_window = __le16_to_cpu(cp.window);
|
||||
|
||||
len -= sizeof(*cp);
|
||||
len -= sizeof(cp);
|
||||
|
||||
/* If filters don't fit, forward all adv pkts */
|
||||
if (len <= sizeof(hdev->mesh_ad_types))
|
||||
memcpy(hdev->mesh_ad_types, cp->ad_types, len);
|
||||
memcpy(hdev->mesh_ad_types, cp.ad_types, len);
|
||||
|
||||
hci_update_passive_scan_sync(hdev);
|
||||
return 0;
|
||||
@@ -3867,15 +3925,16 @@ static int name_changed_sync(struct hci_dev *hdev, void *data)
|
||||
static void set_name_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_set_local_name *cp = cmd->param;
|
||||
struct mgmt_cp_set_local_name *cp;
|
||||
u8 status = mgmt_status(err);
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_SET_LOCAL_NAME, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
cp = cmd->param;
|
||||
|
||||
if (status) {
|
||||
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
|
||||
status);
|
||||
@@ -3887,16 +3946,27 @@ static void set_name_complete(struct hci_dev *hdev, void *data, int err)
|
||||
hci_cmd_sync_queue(hdev, name_changed_sync, NULL, NULL);
|
||||
}
|
||||
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int set_name_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_set_local_name *cp = cmd->param;
|
||||
struct mgmt_cp_set_local_name cp;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memcpy(&cp, cmd->param, sizeof(cp));
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (lmp_bredr_capable(hdev)) {
|
||||
hci_update_name_sync(hdev, cp->name);
|
||||
hci_update_name_sync(hdev, cp.name);
|
||||
hci_update_eir_sync(hdev);
|
||||
}
|
||||
|
||||
@@ -4048,12 +4118,10 @@ int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip)
|
||||
static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct sk_buff *skb = cmd->skb;
|
||||
struct sk_buff *skb;
|
||||
u8 status = mgmt_status(err);
|
||||
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev))
|
||||
return;
|
||||
skb = cmd->skb;
|
||||
|
||||
if (!status) {
|
||||
if (!skb)
|
||||
@@ -4080,7 +4148,7 @@ static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err)
|
||||
if (skb && !IS_ERR(skb))
|
||||
kfree_skb(skb);
|
||||
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int set_default_phy_sync(struct hci_dev *hdev, void *data)
|
||||
@@ -4088,7 +4156,9 @@ static int set_default_phy_sync(struct hci_dev *hdev, void *data)
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_set_phy_configuration *cp = cmd->param;
|
||||
struct hci_cp_le_set_default_phy cp_phy;
|
||||
u32 selected_phys = __le32_to_cpu(cp->selected_phys);
|
||||
u32 selected_phys;
|
||||
|
||||
selected_phys = __le32_to_cpu(cp->selected_phys);
|
||||
|
||||
memset(&cp_phy, 0, sizeof(cp_phy));
|
||||
|
||||
@@ -4228,7 +4298,7 @@ static int set_phy_configuration(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data,
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data,
|
||||
len);
|
||||
if (!cmd)
|
||||
err = -ENOMEM;
|
||||
@@ -5189,7 +5259,17 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
|
||||
{
|
||||
struct mgmt_rp_add_adv_patterns_monitor rp;
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct adv_monitor *monitor = cmd->user_data;
|
||||
struct adv_monitor *monitor;
|
||||
|
||||
/* This is likely the result of hdev being closed and mgmt_index_removed
|
||||
* is attempting to clean up any pending command so
|
||||
* hci_adv_monitors_clear is about to be called which will take care of
|
||||
* freeing the adv_monitor instances.
|
||||
*/
|
||||
if (status == -ECANCELED && !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
monitor = cmd->user_data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@@ -5215,9 +5295,20 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
|
||||
static int mgmt_add_adv_patterns_monitor_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct adv_monitor *monitor = cmd->user_data;
|
||||
struct adv_monitor *mon;
|
||||
|
||||
return hci_add_adv_monitor(hdev, monitor);
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
mon = cmd->user_data;
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return hci_add_adv_monitor(hdev, mon);
|
||||
}
|
||||
|
||||
static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
|
||||
@@ -5484,7 +5575,8 @@ static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
|
||||
status);
|
||||
}
|
||||
|
||||
static void read_local_oob_data_complete(struct hci_dev *hdev, void *data, int err)
|
||||
static void read_local_oob_data_complete(struct hci_dev *hdev, void *data,
|
||||
int err)
|
||||
{
|
||||
struct mgmt_rp_read_local_oob_data mgmt_rp;
|
||||
size_t rp_size = sizeof(mgmt_rp);
|
||||
@@ -5504,7 +5596,8 @@ static void read_local_oob_data_complete(struct hci_dev *hdev, void *data, int e
|
||||
bt_dev_dbg(hdev, "status %d", status);
|
||||
|
||||
if (status) {
|
||||
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, status);
|
||||
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
status);
|
||||
goto remove;
|
||||
}
|
||||
|
||||
@@ -5786,17 +5879,12 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
|
||||
if (cmd != pending_find(MGMT_OP_START_DISCOVERY, hdev) &&
|
||||
cmd != pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev) &&
|
||||
cmd != pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
|
||||
cmd->param, 1);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
hci_discovery_set_state(hdev, err ? DISCOVERY_STOPPED:
|
||||
DISCOVERY_FINDING);
|
||||
@@ -5804,6 +5892,9 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
static int start_discovery_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
if (!mgmt_pending_listed(hdev, data))
|
||||
return -ECANCELED;
|
||||
|
||||
return hci_start_discovery_sync(hdev);
|
||||
}
|
||||
|
||||
@@ -6009,15 +6100,14 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_STOP_DISCOVERY, hdev))
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
|
||||
return;
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
|
||||
cmd->param, 1);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
if (!err)
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
@@ -6025,6 +6115,9 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
static int stop_discovery_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
if (!mgmt_pending_listed(hdev, data))
|
||||
return -ECANCELED;
|
||||
|
||||
return hci_stop_discovery_sync(hdev);
|
||||
}
|
||||
|
||||
@@ -6234,14 +6327,18 @@ static void enable_advertising_instance(struct hci_dev *hdev, int err)
|
||||
|
||||
static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct cmd_lookup match = { NULL, hdev };
|
||||
u8 instance;
|
||||
struct adv_info *adv_instance;
|
||||
u8 status = mgmt_status(err);
|
||||
|
||||
if (err == -ECANCELED || !mgmt_pending_valid(hdev, data))
|
||||
return;
|
||||
|
||||
if (status) {
|
||||
mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, true,
|
||||
cmd_status_rsp, &status);
|
||||
mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, status);
|
||||
mgmt_pending_free(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6250,8 +6347,7 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING);
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, true, settings_rsp,
|
||||
&match);
|
||||
settings_rsp(cmd, &match);
|
||||
|
||||
new_settings(hdev, match.sk);
|
||||
|
||||
@@ -6283,10 +6379,23 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
|
||||
static int set_adv_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_mode *cp = cmd->param;
|
||||
u8 val = !!cp->val;
|
||||
struct mgmt_mode cp;
|
||||
u8 val;
|
||||
|
||||
if (cp->val == 0x02)
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!__mgmt_pending_listed(hdev, cmd)) {
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
memcpy(&cp, cmd->param, sizeof(cp));
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
val = !!cp.val;
|
||||
|
||||
if (cp.val == 0x02)
|
||||
hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
|
||||
@@ -8039,10 +8148,6 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
|
||||
u8 status = mgmt_status(err);
|
||||
u16 eir_len;
|
||||
|
||||
if (err == -ECANCELED ||
|
||||
cmd != pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev))
|
||||
return;
|
||||
|
||||
if (!status) {
|
||||
if (!skb)
|
||||
status = MGMT_STATUS_FAILED;
|
||||
@@ -8149,7 +8254,7 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
|
||||
kfree_skb(skb);
|
||||
|
||||
kfree(mgmt_rp);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
|
||||
@@ -8158,7 +8263,7 @@ static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
|
||||
cp, sizeof(*cp));
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -320,6 +320,52 @@ void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
|
||||
{
|
||||
struct mgmt_pending_cmd *tmp;
|
||||
|
||||
lockdep_assert_held(&hdev->mgmt_pending_lock);
|
||||
|
||||
if (!cmd)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(tmp, &hdev->mgmt_pending, list) {
|
||||
if (cmd == tmp)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
|
||||
{
|
||||
bool listed;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
listed = __mgmt_pending_listed(hdev, cmd);
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return listed;
|
||||
}
|
||||
|
||||
bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
|
||||
{
|
||||
bool listed;
|
||||
|
||||
if (!cmd)
|
||||
return false;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
listed = __mgmt_pending_listed(hdev, cmd);
|
||||
if (listed)
|
||||
list_del(&cmd->list);
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return listed;
|
||||
}
|
||||
|
||||
void mgmt_mesh_foreach(struct hci_dev *hdev,
|
||||
void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
|
||||
void *data, struct sock *sk)
|
||||
|
||||
@@ -65,6 +65,9 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
|
||||
void *data, u16 len);
|
||||
void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
|
||||
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
|
||||
bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
|
||||
bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
|
||||
bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
|
||||
void mgmt_mesh_foreach(struct hci_dev *hdev,
|
||||
void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
|
||||
void *data, struct sock *sk);
|
||||
|
||||
Reference in New Issue
Block a user