Merge tag 'for-net-2025-11-11' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - hci_conn: Fix not cleaning up PA_LINK connections
 - hci_event: Fix not handling PA Sync Lost event
 - MGMT: cancel mesh send timer when hdev removed
 - 6lowpan: reset link-local header on ipv6 recv path
 - 6lowpan: fix BDADDR_LE vs ADDR_LE_DEV address type confusion
 - L2CAP: export l2cap_chan_hold for modules
 - 6lowpan: Don't hold spin lock over sleeping functions
 - 6lowpan: add missing l2cap_chan_lock()
 - btusb: reorder cleanup in btusb_disconnect to avoid UAF
 - btrtl: Avoid loading the config file on security chips

* tag 'for-net-2025-11-11' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: btrtl: Avoid loading the config file on security chips
  Bluetooth: hci_event: Fix not handling PA Sync Lost event
  Bluetooth: hci_conn: Fix not cleaning up PA_LINK connections
  Bluetooth: 6lowpan: add missing l2cap_chan_lock()
  Bluetooth: 6lowpan: Don't hold spin lock over sleeping functions
  Bluetooth: L2CAP: export l2cap_chan_hold for modules
  Bluetooth: 6lowpan: fix BDADDR_LE vs ADDR_LE_DEV address type confusion
  Bluetooth: 6lowpan: reset link-local header on ipv6 recv path
  Bluetooth: btusb: reorder cleanup in btusb_disconnect to avoid UAF
  Bluetooth: MGMT: cancel mesh send timer when hdev removed
====================

Link: https://patch.msgid.link/20251111141357.1983153-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-11-11 17:43:32 -08:00
9 changed files with 161 additions and 85 deletions

View File

@@ -50,7 +50,7 @@
#define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}}) #define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}}) #define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}}) #define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
#define RTL_PATCH_SNIPPETS 0x01 #define RTL_PATCH_SNIPPETS 0x01
#define RTL_PATCH_DUMMY_HEADER 0x02 #define RTL_PATCH_DUMMY_HEADER 0x02
@@ -534,7 +534,6 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
{ {
struct rtl_epatch_header_v2 *hdr; struct rtl_epatch_header_v2 *hdr;
int rc; int rc;
u8 reg_val[2];
u8 key_id; u8 key_id;
u32 num_sections; u32 num_sections;
struct rtl_section *section; struct rtl_section *section;
@@ -549,14 +548,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
.len = btrtl_dev->fw_len - 7, /* Cut the tail */ .len = btrtl_dev->fw_len - 7, /* Cut the tail */
}; };
rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val); key_id = btrtl_dev->key_id;
if (rc < 0)
return -EIO;
key_id = reg_val[0];
rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id);
btrtl_dev->key_id = key_id;
hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
if (!hdr) if (!hdr)
@@ -1070,6 +1062,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
u16 hci_rev, lmp_subver; u16 hci_rev, lmp_subver;
u8 hci_ver, lmp_ver, chip_type = 0; u8 hci_ver, lmp_ver, chip_type = 0;
int ret; int ret;
int rc;
u8 key_id;
u8 reg_val[2]; u8 reg_val[2];
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL); btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
@@ -1180,6 +1174,14 @@ next:
goto err_free; goto err_free;
} }
rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
if (rc < 0)
goto err_free;
key_id = reg_val[0];
btrtl_dev->key_id = key_id;
rtl_dev_info(hdev, "%s: key id %u", __func__, key_id);
btrtl_dev->fw_len = -EIO; btrtl_dev->fw_len = -EIO;
if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) { if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
snprintf(fw_name, sizeof(fw_name), "%s_v2.bin", snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
@@ -1202,7 +1204,7 @@ next:
goto err_free; goto err_free;
} }
if (btrtl_dev->ic_info->cfg_name) { if (btrtl_dev->ic_info->cfg_name && !btrtl_dev->key_id) {
if (postfix) { if (postfix) {
snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin", snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
btrtl_dev->ic_info->cfg_name, postfix); btrtl_dev->ic_info->cfg_name, postfix);

View File

@@ -4361,6 +4361,11 @@ static void btusb_disconnect(struct usb_interface *intf)
hci_unregister_dev(hdev); hci_unregister_dev(hdev);
if (data->oob_wake_irq)
device_init_wakeup(&data->udev->dev, false);
if (data->reset_gpio)
gpiod_put(data->reset_gpio);
if (intf == data->intf) { if (intf == data->intf) {
if (data->isoc) if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc); usb_driver_release_interface(&btusb_driver, data->isoc);
@@ -4371,17 +4376,11 @@ static void btusb_disconnect(struct usb_interface *intf)
usb_driver_release_interface(&btusb_driver, data->diag); usb_driver_release_interface(&btusb_driver, data->diag);
usb_driver_release_interface(&btusb_driver, data->intf); usb_driver_release_interface(&btusb_driver, data->intf);
} else if (intf == data->diag) { } else if (intf == data->diag) {
usb_driver_release_interface(&btusb_driver, data->intf);
if (data->isoc) if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc); usb_driver_release_interface(&btusb_driver, data->isoc);
usb_driver_release_interface(&btusb_driver, data->intf);
} }
if (data->oob_wake_irq)
device_init_wakeup(&data->udev->dev, false);
if (data->reset_gpio)
gpiod_put(data->reset_gpio);
hci_free_dev(hdev); hci_free_dev(hdev);
} }

View File

@@ -2783,6 +2783,11 @@ struct hci_ev_le_per_adv_report {
__u8 data[]; __u8 data[];
} __packed; } __packed;
#define HCI_EV_LE_PA_SYNC_LOST 0x10
struct hci_ev_le_pa_sync_lost {
__le16 handle;
} __packed;
#define LE_PA_DATA_COMPLETE 0x00 #define LE_PA_DATA_COMPLETE 0x00
#define LE_PA_DATA_MORE_TO_COME 0x01 #define LE_PA_DATA_MORE_TO_COME 0x01
#define LE_PA_DATA_TRUNCATED 0x02 #define LE_PA_DATA_TRUNCATED 0x02

View File

@@ -53,6 +53,11 @@ static bool enable_6lowpan;
static struct l2cap_chan *listen_chan; static struct l2cap_chan *listen_chan;
static DEFINE_MUTEX(set_lock); static DEFINE_MUTEX(set_lock);
enum {
LOWPAN_PEER_CLOSING,
LOWPAN_PEER_MAXBITS
};
struct lowpan_peer { struct lowpan_peer {
struct list_head list; struct list_head list;
struct rcu_head rcu; struct rcu_head rcu;
@@ -61,6 +66,8 @@ struct lowpan_peer {
/* peer addresses in various formats */ /* peer addresses in various formats */
unsigned char lladdr[ETH_ALEN]; unsigned char lladdr[ETH_ALEN];
struct in6_addr peer_addr; struct in6_addr peer_addr;
DECLARE_BITMAP(flags, LOWPAN_PEER_MAXBITS);
}; };
struct lowpan_btle_dev { struct lowpan_btle_dev {
@@ -289,6 +296,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
local_skb->pkt_type = PACKET_HOST; local_skb->pkt_type = PACKET_HOST;
local_skb->dev = dev; local_skb->dev = dev;
skb_reset_mac_header(local_skb);
skb_set_transport_header(local_skb, sizeof(struct ipv6hdr)); skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) { if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
@@ -919,7 +927,9 @@ static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
BT_DBG("peer %p chan %p", peer, peer->chan); BT_DBG("peer %p chan %p", peer, peer->chan);
l2cap_chan_lock(peer->chan);
l2cap_chan_close(peer->chan, ENOENT); l2cap_chan_close(peer->chan, ENOENT);
l2cap_chan_unlock(peer->chan);
return 0; return 0;
} }
@@ -956,10 +966,11 @@ static struct l2cap_chan *bt_6lowpan_listen(void)
} }
static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
struct l2cap_conn **conn) struct l2cap_conn **conn, bool disconnect)
{ {
struct hci_conn *hcon; struct hci_conn *hcon;
struct hci_dev *hdev; struct hci_dev *hdev;
int le_addr_type;
int n; int n;
n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu", n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
@@ -970,13 +981,32 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
if (n < 7) if (n < 7)
return -EINVAL; return -EINVAL;
if (disconnect) {
/* The "disconnect" debugfs command has used different address
* type constants than "connect" since 2015. Let's retain that
* for now even though it's obviously buggy...
*/
*addr_type += 1;
}
switch (*addr_type) {
case BDADDR_LE_PUBLIC:
le_addr_type = ADDR_LE_DEV_PUBLIC;
break;
case BDADDR_LE_RANDOM:
le_addr_type = ADDR_LE_DEV_RANDOM;
break;
default:
return -EINVAL;
}
/* The LE_PUBLIC address type is ignored because of BDADDR_ANY */ /* The LE_PUBLIC address type is ignored because of BDADDR_ANY */
hdev = hci_get_route(addr, BDADDR_ANY, BDADDR_LE_PUBLIC); hdev = hci_get_route(addr, BDADDR_ANY, BDADDR_LE_PUBLIC);
if (!hdev) if (!hdev)
return -ENOENT; return -ENOENT;
hci_dev_lock(hdev); hci_dev_lock(hdev);
hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type); hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type);
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
@@ -993,41 +1023,52 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
static void disconnect_all_peers(void) static void disconnect_all_peers(void)
{ {
struct lowpan_btle_dev *entry; struct lowpan_btle_dev *entry;
struct lowpan_peer *peer, *tmp_peer, *new_peer; struct lowpan_peer *peer;
struct list_head peers; int nchans;
INIT_LIST_HEAD(&peers); /* l2cap_chan_close() cannot be called from RCU, and lock ordering
* chan->lock > devices_lock prevents taking write side lock, so copy
/* We make a separate list of peers as the close_cb() will * then close.
* modify the device peers list so it is better not to mess
* with the same list at the same time.
*/ */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list)
list_for_each_entry_rcu(peer, &entry->peers, list)
clear_bit(LOWPAN_PEER_CLOSING, peer->flags);
rcu_read_unlock();
do {
struct l2cap_chan *chans[32];
int i;
nchans = 0;
spin_lock(&devices_lock);
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
list_for_each_entry_rcu(peer, &entry->peers, list) { list_for_each_entry_rcu(peer, &entry->peers, list) {
new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); if (test_and_set_bit(LOWPAN_PEER_CLOSING,
if (!new_peer) peer->flags))
break; continue;
new_peer->chan = peer->chan; l2cap_chan_hold(peer->chan);
INIT_LIST_HEAD(&new_peer->list); chans[nchans++] = peer->chan;
list_add(&new_peer->list, &peers); if (nchans >= ARRAY_SIZE(chans))
goto done;
} }
} }
rcu_read_unlock(); done:
spin_lock(&devices_lock);
list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
l2cap_chan_close(peer->chan, ENOENT);
list_del_rcu(&peer->list);
kfree_rcu(peer, rcu);
}
spin_unlock(&devices_lock); spin_unlock(&devices_lock);
for (i = 0; i < nchans; ++i) {
l2cap_chan_lock(chans[i]);
l2cap_chan_close(chans[i], ENOENT);
l2cap_chan_unlock(chans[i]);
l2cap_chan_put(chans[i]);
}
} while (nchans);
} }
struct set_enable { struct set_enable {
@@ -1050,7 +1091,9 @@ static void do_enable_set(struct work_struct *work)
mutex_lock(&set_lock); mutex_lock(&set_lock);
if (listen_chan) { if (listen_chan) {
l2cap_chan_lock(listen_chan);
l2cap_chan_close(listen_chan, 0); l2cap_chan_close(listen_chan, 0);
l2cap_chan_unlock(listen_chan);
l2cap_chan_put(listen_chan); l2cap_chan_put(listen_chan);
} }
@@ -1103,13 +1146,15 @@ static ssize_t lowpan_control_write(struct file *fp,
buf[buf_size] = '\0'; buf[buf_size] = '\0';
if (memcmp(buf, "connect ", 8) == 0) { if (memcmp(buf, "connect ", 8) == 0) {
ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn); ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn, false);
if (ret == -EINVAL) if (ret == -EINVAL)
return ret; return ret;
mutex_lock(&set_lock); mutex_lock(&set_lock);
if (listen_chan) { if (listen_chan) {
l2cap_chan_lock(listen_chan);
l2cap_chan_close(listen_chan, 0); l2cap_chan_close(listen_chan, 0);
l2cap_chan_unlock(listen_chan);
l2cap_chan_put(listen_chan); l2cap_chan_put(listen_chan);
listen_chan = NULL; listen_chan = NULL;
} }
@@ -1140,7 +1185,7 @@ static ssize_t lowpan_control_write(struct file *fp,
} }
if (memcmp(buf, "disconnect ", 11) == 0) { if (memcmp(buf, "disconnect ", 11) == 0) {
ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn); ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn, true);
if (ret < 0) if (ret < 0)
return ret; return ret;
@@ -1271,7 +1316,9 @@ static void __exit bt_6lowpan_exit(void)
debugfs_remove(lowpan_control_debugfs); debugfs_remove(lowpan_control_debugfs);
if (listen_chan) { if (listen_chan) {
l2cap_chan_lock(listen_chan);
l2cap_chan_close(listen_chan, 0); l2cap_chan_close(listen_chan, 0);
l2cap_chan_unlock(listen_chan);
l2cap_chan_put(listen_chan); l2cap_chan_put(listen_chan);
} }

View File

@@ -769,21 +769,23 @@ static void find_bis(struct hci_conn *conn, void *data)
d->count++; d->count++;
} }
static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *conn) static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
{ {
struct iso_list_data *d; struct iso_list_data *d;
int ret; int ret;
bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, conn->sync_handle); bt_dev_dbg(hdev, "hcon %p big 0x%2.2x sync_handle 0x%4.4x", conn,
conn->iso_qos.bcast.big, conn->sync_handle);
d = kzalloc(sizeof(*d), GFP_KERNEL); d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
d->big = big; d->big = conn->iso_qos.bcast.big;
d->sync_handle = conn->sync_handle; d->sync_handle = conn->sync_handle;
if (test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) { if (conn->type == PA_LINK &&
test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, PA_LINK, hci_conn_hash_list_flag(hdev, find_bis, PA_LINK,
HCI_CONN_PA_SYNC, d); HCI_CONN_PA_SYNC, d);
@@ -801,6 +803,9 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *c
d->big_sync_term = true; d->big_sync_term = true;
} }
if (!d->pa_sync_term && !d->big_sync_term)
return 0;
ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d, ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
terminate_big_destroy); terminate_big_destroy);
if (ret) if (ret)
@@ -852,8 +857,7 @@ static void bis_cleanup(struct hci_conn *conn)
hci_le_terminate_big(hdev, conn); hci_le_terminate_big(hdev, conn);
} else { } else {
hci_le_big_terminate(hdev, conn->iso_qos.bcast.big, hci_le_big_terminate(hdev, conn);
conn);
} }
} }
@@ -994,19 +998,20 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu; conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
break; break;
case CIS_LINK: case CIS_LINK:
case BIS_LINK:
case PA_LINK:
/* conn->src should reflect the local identity address */ /* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type); hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
/* set proper cleanup function */ if (conn->role == HCI_ROLE_MASTER)
if (!bacmp(dst, BDADDR_ANY))
conn->cleanup = bis_cleanup;
else if (conn->role == HCI_ROLE_MASTER)
conn->cleanup = cis_cleanup; conn->cleanup = cis_cleanup;
conn->mtu = hdev->iso_mtu ? hdev->iso_mtu : conn->mtu = hdev->iso_mtu;
hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu; break;
case PA_LINK:
case BIS_LINK:
/* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
conn->cleanup = bis_cleanup;
conn->mtu = hdev->iso_mtu;
break; break;
case SCO_LINK: case SCO_LINK:
if (lmp_esco_capable(hdev)) if (lmp_esco_capable(hdev))

View File

@@ -5843,6 +5843,29 @@ static void hci_le_enh_conn_complete_evt(struct hci_dev *hdev, void *data,
le16_to_cpu(ev->supervision_timeout)); le16_to_cpu(ev->supervision_timeout));
} }
static void hci_le_pa_sync_lost_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_ev_le_pa_sync_lost *ev = data;
u16 handle = le16_to_cpu(ev->handle);
struct hci_conn *conn;
bt_dev_dbg(hdev, "sync handle 0x%4.4x", handle);
hci_dev_lock(hdev);
/* 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);
hci_disconn_cfm(conn, HCI_ERROR_REMOTE_USER_TERM);
hci_conn_del(conn);
}
hci_dev_unlock(hdev);
}
static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@@ -7001,14 +7024,9 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
continue; continue;
} }
if (ev->status != 0x42) { if (ev->status != 0x42)
/* Mark PA sync as established */ /* Mark PA sync as established */
set_bit(HCI_CONN_PA_SYNC, &bis->flags); set_bit(HCI_CONN_PA_SYNC, &bis->flags);
/* Reset cleanup callback of PA Sync so it doesn't
* terminate the sync when deleting the connection.
*/
conn->cleanup = NULL;
}
bis->sync_handle = conn->sync_handle; bis->sync_handle = conn->sync_handle;
bis->iso_qos.bcast.big = ev->handle; bis->iso_qos.bcast.big = ev->handle;
@@ -7051,29 +7069,24 @@ static void hci_le_big_sync_lost_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct hci_evt_le_big_sync_lost *ev = data; struct hci_evt_le_big_sync_lost *ev = data;
struct hci_conn *bis, *conn; struct hci_conn *bis;
bool mgmt_conn; bool mgmt_conn = false;
bt_dev_dbg(hdev, "big handle 0x%2.2x", ev->handle); bt_dev_dbg(hdev, "big handle 0x%2.2x", ev->handle);
hci_dev_lock(hdev); hci_dev_lock(hdev);
/* Delete the pa sync connection */
bis = hci_conn_hash_lookup_pa_sync_big_handle(hdev, ev->handle);
if (bis) {
conn = hci_conn_hash_lookup_pa_sync_handle(hdev,
bis->sync_handle);
if (conn)
hci_conn_del(conn);
}
/* Delete each bis connection */ /* Delete each bis connection */
while ((bis = hci_conn_hash_lookup_big_state(hdev, ev->handle, while ((bis = hci_conn_hash_lookup_big_state(hdev, ev->handle,
BT_CONNECTED, BT_CONNECTED,
HCI_ROLE_SLAVE))) { HCI_ROLE_SLAVE))) {
mgmt_conn = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &bis->flags); if (!mgmt_conn) {
mgmt_device_disconnected(hdev, &bis->dst, bis->type, bis->dst_type, mgmt_conn = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED,
ev->reason, mgmt_conn); &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_disconn_cfm(bis, ev->reason);
@@ -7187,6 +7200,9 @@ static const struct hci_le_ev {
hci_le_per_adv_report_evt, hci_le_per_adv_report_evt,
sizeof(struct hci_ev_le_per_adv_report), sizeof(struct hci_ev_le_per_adv_report),
HCI_MAX_EVENT_SIZE), HCI_MAX_EVENT_SIZE),
/* [0x10 = HCI_EV_LE_PA_SYNC_LOST] */
HCI_LE_EV(HCI_EV_LE_PA_SYNC_LOST, hci_le_pa_sync_lost_evt,
sizeof(struct hci_ev_le_pa_sync_lost)),
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */ /* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt, HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
sizeof(struct hci_evt_le_ext_adv_set_term)), sizeof(struct hci_evt_le_ext_adv_set_term)),

View File

@@ -6999,7 +6999,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, conn)) 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) if (!err)

View File

@@ -497,6 +497,7 @@ void l2cap_chan_hold(struct l2cap_chan *c)
kref_get(&c->kref); kref_get(&c->kref);
} }
EXPORT_SYMBOL_GPL(l2cap_chan_hold);
struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c) struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c)
{ {

View File

@@ -9497,6 +9497,7 @@ void mgmt_index_removed(struct hci_dev *hdev)
cancel_delayed_work_sync(&hdev->discov_off); cancel_delayed_work_sync(&hdev->discov_off);
cancel_delayed_work_sync(&hdev->service_cache); cancel_delayed_work_sync(&hdev->service_cache);
cancel_delayed_work_sync(&hdev->rpa_expired); cancel_delayed_work_sync(&hdev->rpa_expired);
cancel_delayed_work_sync(&hdev->mesh_send_done);
} }
void mgmt_power_on(struct hci_dev *hdev, int err) void mgmt_power_on(struct hci_dev *hdev, int err)