bluetooth pull request for net:

Core:
 
  - hci: put the peer's on-air address on air when we cannot resolve
  - hci: keep dst_type with dst when reusing an LE connection
  - hci_core: Fix queuing tx_work after workqueue is drained
  - hci_sync: Serialize local codec list cleanup
  - hci_codec: validate vendor codec count length
  - eir: validate service data length before reading UUID
  - RFCOMM: avoid socket lock inversion in listener cleanup
  - ISO: Fix parent socket leak in iso_conn_ready()
  - ISO: set BT_LISTEN before requesting a BIG sync
  - coredump: Quiesce dump work on unregister
 
 Drivers:
 
  - btintel_pcie: validate TX skb length in send_sync
  - btmtk: fix wrong status for short WMT FUNC_CTRL events
  - btmtksdio, btmtkuart: validate WMT event length before struct access
  - hci_qca: Do not write to the serial port after it is closed
  - btusb: fix NXP IW610 composite device handling
  - btintel_pcie: fix off-by-one bounds check in RX submit
  - btmtksdio: Fix PM runtime reference leak in shutdown
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmqpmw8ZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKZC2D/kBM2/ZEFhSwp6Lu7orXKHn
 AQOwoh0uv4TBZaR8V6ogSLdWmdv4rb1Ayjj2SFkNNNwmuUkPYhG30DySw73C7QpV
 uza4SAPO9OF3RRzXvBiVpxF4TmlWYMbApAuMtaa/NmZfZ38pv6T66cFsxhc+lfje
 qNUWkCyI9/4hY7rIxL2dcBQ3yLQwMYwKCwdMwj2/rMX4BN7IXVsMqZ4HBHmCx3Ht
 M2GGnng4DNFgCN/ZcUMLASRR61At3Iop39E8NLnnnVqJgn6p2DOx+G+gnYpdnUOy
 zdpm/yLSJD2maDVRlxVWlf071jpUVCjCY2HvjnvnBTKDgqf9GD/3SVTrv3f/3gxt
 /NVHwnHjZkhMt2sY//NVo4dGD0s8PJjEAdWzm/c7lZ97/8AHsSFMtBe53Jc/xy9l
 lvmTV3FrTkzf08VT5QyrTjj9dQjOcJfeOqfOHJrRQSfjqvNCR7OZjjDLavgv+3q/
 Op5bE0RY/47qa7yWE7gyjyVFeAoK2C4XsXDIc8cHOdJlozlqKV0jVQUvs9+wbl8r
 0ULFdcRAs3/OIybYBW4qhOGjZjS/ilK4CjCNGjEPMD1IEz6SutJPxtp1Cmh9KFZT
 3u5vNsbXB/Oa5yM+m5KZwxoRhYG6aCivCLruEWVe8tco8nWlcz1iPRPBs+bB2cvX
 5tJwh5QCT35FUBo3j+0PvQ==
 =dGOw
 -----END PGP SIGNATURE-----

Merge tag 'for-net-2026-09-15' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

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

Core:

 - hci: put the peer's on-air address on air when we cannot resolve
 - hci: keep dst_type with dst when reusing an LE connection
 - hci_core: Fix queuing tx_work after workqueue is drained
 - hci_sync: Serialize local codec list cleanup
 - hci_codec: validate vendor codec count length
 - eir: validate service data length before reading UUID
 - RFCOMM: avoid socket lock inversion in listener cleanup
 - ISO: Fix parent socket leak in iso_conn_ready()
 - ISO: set BT_LISTEN before requesting a BIG sync
 - coredump: Quiesce dump work on unregister

Drivers:

 - btintel_pcie: validate TX skb length in send_sync
 - btmtk: fix wrong status for short WMT FUNC_CTRL events
 - btmtksdio, btmtkuart: validate WMT event length before struct access
 - hci_qca: Do not write to the serial port after it is closed
 - btusb: fix NXP IW610 composite device handling
 - btintel_pcie: fix off-by-one bounds check in RX submit
 - btmtksdio: Fix PM runtime reference leak in shutdown

* tag 'for-net-2026-09-15' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: RFCOMM: avoid socket lock inversion in listener cleanup
  Bluetooth: keep dst_type with dst when reusing an LE connection
  Bluetooth: btintel_pcie: fix off-by-one bounds check in RX submit
  Bluetooth: btmtksdio: Fix PM runtime reference leak in shutdown
  Bluetooth: btmtksdio, btmtkuart: validate WMT event length before struct access
  Bluetooth: btmtk: fix wrong status for short WMT FUNC_CTRL events
  Bluetooth: ISO: set BT_LISTEN before requesting a BIG sync
  Bluetooth: ISO: Fix parent socket leak in iso_conn_ready()
  Bluetooth: hci_sync: Serialize local codec list cleanup
  Bluetooth: hci_qca: Do not write to the serial port after it is closed
  Bluetooth: hci_codec: validate vendor codec count length
  Bluetooth: put the peer's on-air address on air when we cannot resolve
  Bluetooth: coredump: Quiesce dump work on unregister
  Bluetooth: btintel_pcie: validate TX skb length in send_sync
  Bluetooth: hci_core: Fix queuing tx_work after workqueue is drained
  Bluetooth: eir: validate service data length before reading UUID
  Bluetooth: btusb: fix NXP IW610 composite device handling
====================

Link: https://patch.msgid.link/20260915192441.1130583-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-15 16:40:55 -07:00
commit c5e367a8a3
15 changed files with 242 additions and 68 deletions

View File

@ -404,6 +404,12 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data,
if (tfd_index > txq->count)
return -ERANGE;
if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) {
bt_dev_err(hdev, "TX skb too large (%u > %u)", skb->len,
BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN);
return -EMSGSIZE;
}
/* Firmware raises alive interrupt on HCI_OP_RESET or
* BTINTEL_HCI_OP_RESET
*/
@ -502,7 +508,7 @@ static int btintel_pcie_submit_rx(struct btintel_pcie_data *data)
frbd_index = data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM];
if (frbd_index > rxq->count)
if (frbd_index >= rxq->count)
return -ERANGE;
/* Prepare for RX submit. It updates the FRBD with the address of DMA

View File

@ -721,7 +721,12 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
case BTMTK_WMT_FUNC_CTRL:
if (!skb_pull_data(data->evt_skb,
sizeof(wmt_evt_funcc->status))) {
status = BTMTK_WMT_ON_UNDONE;
/* A plain enable/disable request is acked with just
* the WMT header and no trailing status word; the
* result is carried in the header's own flag byte.
*/
status = wmt_evt->whdr.flag ? BTMTK_WMT_ON_UNDONE :
BTMTK_WMT_ON_DONE;
break;
}

View File

@ -217,7 +217,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
}
/* Parse and handle the return WMT event */
wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data;
wmt_evt = skb_pull_data(bdev->evt_skb, sizeof(*wmt_evt));
if (!wmt_evt) {
bt_dev_err(hdev, "WMT event too short (%u bytes)",
bdev->evt_skb->len);
err = -EINVAL;
goto err_free_skb;
}
if (wmt_evt->whdr.op != hdr->op) {
bt_dev_err(hdev, "Wrong op received %d expected %d",
wmt_evt->whdr.op, hdr->op);
@ -233,6 +240,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
status = BTMTK_WMT_PATCH_DONE;
break;
case BTMTK_WMT_FUNC_CTRL:
if (!skb_pull_data(bdev->evt_skb,
sizeof(wmt_evt_funcc->status))) {
/* A plain enable/disable request is acked with just
* the WMT header and no trailing status word; the
* result is carried in the header's own flag byte.
*/
status = wmt_evt->whdr.flag ? BTMTK_WMT_ON_UNDONE :
BTMTK_WMT_ON_DONE;
break;
}
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
status = BTMTK_WMT_ON_DONE;
@ -1244,10 +1262,8 @@ static int btmtksdio_shutdown(struct hci_dev *hdev)
wmt_params.status = NULL;
err = mtk_hci_wmt_sync(hdev, &wmt_params);
if (err < 0) {
if (err < 0)
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
return err;
}
ignore_wmt_cmd:
pm_runtime_put_noidle(bdev->dev);

View File

@ -151,7 +151,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
}
/* Parse and handle the return WMT event */
wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data;
wmt_evt = skb_pull_data(bdev->evt_skb, sizeof(*wmt_evt));
if (!wmt_evt) {
bt_dev_err(hdev, "WMT event too short (%u bytes)",
bdev->evt_skb->len);
err = -EINVAL;
goto err_free_wc;
}
if (wmt_evt->whdr.op != hdr->op) {
bt_dev_err(hdev, "Wrong op received %d expected %d",
wmt_evt->whdr.op, hdr->op);
@ -167,6 +174,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
status = BTMTK_WMT_PATCH_DONE;
break;
case BTMTK_WMT_FUNC_CTRL:
if (!skb_pull_data(bdev->evt_skb,
sizeof(wmt_evt_funcc->status))) {
/* A plain enable/disable request is acked with just
* the WMT header and no trailing status word; the
* result is carried in the header's own flag byte.
*/
status = wmt_evt->whdr.flag ? BTMTK_WMT_ON_UNDONE :
BTMTK_WMT_ON_DONE;
break;
}
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
status = BTMTK_WMT_ON_DONE;

View File

@ -71,6 +71,15 @@ static struct usb_driver btusb_driver;
#define BTUSB_BROKEN_EXT_SCAN BIT(29)
static const struct usb_device_id btusb_table[] = {
/*
* NXP IW610 (0471:0215): the composite device reports Bluetooth
* class at the whole-device level, so the generic entry below
* would also match this WiFi vendor interface. Ignore it here
* first so mwifiex-nxp can bind it instead.
*/
{ USB_DEVICE_AND_INTERFACE_INFO(0x0471, 0x0215, 0xff, 0xff, 0xff),
.driver_info = BTUSB_IGNORE },
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
@ -477,6 +486,14 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
{ USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL },
/*
* NXP IW610 BT interfaces (Marvell-lineage silicon, same quirk as
* the 0x1286 entries above). Scoped to the BT interface class,
* not just VID/PID -- see the btusb_table entry above.
*/
{ USB_DEVICE_AND_INTERFACE_INFO(0x0471, 0x0215, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_MARVELL },
/* Intel Bluetooth devices */
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0026), .driver_info = BTUSB_INTEL_COMBINED },

View File

@ -2228,8 +2228,8 @@ static void qca_power_off(struct hci_uart *hu)
bool sw_ctrl_state;
struct qca_power *power;
/* From this point we go into power off state. But serial port is
* still open, stop queueing the IBS data and flush all the buffered
/* From this point we go into power off state. But serial port may
* still be open, stop queueing the IBS data and flush all the buffered
* data in skb's.
*/
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
@ -2251,8 +2251,14 @@ static void qca_power_off(struct hci_uart *hu)
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
/* Both of these write to the serial port which may have
* already been closed by hci_uart_close(), which closes
* the port if HCI_QUIRK_NON_PERSISTENT_SETUP is set.
*/
if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
}
break;
default:
break;

View File

@ -70,6 +70,7 @@ struct hci_devcoredump {
const char *hci_devcd_state_name(enum devcoredump_state state);
void hci_devcd_reset(struct hci_dev *hdev);
void hci_devcd_shutdown(struct hci_dev *hdev);
void hci_devcd_rx(struct work_struct *work);
void hci_devcd_timeout(struct work_struct *work);
@ -89,6 +90,7 @@ static inline const char *hci_devcd_state_name(enum devcoredump_state state)
}
static inline void hci_devcd_reset(struct hci_dev *hdev) {}
static inline void hci_devcd_shutdown(struct hci_dev *hdev) {}
static inline void hci_devcd_rx(struct work_struct *work) {}
static inline void hci_devcd_timeout(struct work_struct *work) {}

View File

@ -104,6 +104,22 @@ static void hci_devcd_free(struct hci_dev *hdev)
hci_devcd_reset(hdev);
}
void hci_devcd_shutdown(struct hci_dev *hdev)
{
unsigned long flags;
spin_lock_irqsave(&hdev->dump.dump_q.lock, flags);
hdev->dump.supported = false;
spin_unlock_irqrestore(&hdev->dump.dump_q.lock, flags);
disable_work_sync(&hdev->dump.dump_rx);
disable_delayed_work_sync(&hdev->dump.dump_timeout);
hci_dev_lock(hdev);
hci_devcd_free(hdev);
hci_dev_unlock(hdev);
}
/* Call with hci_dev_lock only. */
static int hci_devcd_alloc(struct hci_dev *hdev, u32 size)
{
@ -442,7 +458,29 @@ EXPORT_SYMBOL(hci_devcd_register);
static inline bool hci_devcd_enabled(struct hci_dev *hdev)
{
return hdev->dump.supported;
return READ_ONCE(hdev->dump.supported);
}
static int hci_devcd_queue(struct hci_dev *hdev, struct sk_buff *skb)
{
unsigned long flags;
int err = 0;
spin_lock_irqsave(&hdev->dump.dump_q.lock, flags);
if (!hdev->dump.supported)
err = -EOPNOTSUPP;
else
__skb_queue_tail(&hdev->dump.dump_q, skb);
spin_unlock_irqrestore(&hdev->dump.dump_q.lock, flags);
if (err) {
kfree_skb(skb);
return err;
}
queue_work(hdev->workqueue, &hdev->dump.dump_rx);
return 0;
}
int hci_devcd_init(struct hci_dev *hdev, u32 dump_size)
@ -459,10 +497,7 @@ int hci_devcd_init(struct hci_dev *hdev, u32 dump_size)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_INIT;
put_unaligned_le32(dump_size, skb_put(skb, 4));
skb_queue_tail(&hdev->dump.dump_q, skb);
queue_work(hdev->workqueue, &hdev->dump.dump_rx);
return 0;
return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_init);
@ -478,10 +513,7 @@ int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_SKB;
skb_queue_tail(&hdev->dump.dump_q, skb);
queue_work(hdev->workqueue, &hdev->dump.dump_rx);
return 0;
return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_append);
@ -503,10 +535,7 @@ int hci_devcd_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_PATTERN;
skb_put_data(skb, &p, sizeof(p));
skb_queue_tail(&hdev->dump.dump_q, skb);
queue_work(hdev->workqueue, &hdev->dump.dump_rx);
return 0;
return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_append_pattern);
@ -523,10 +552,7 @@ int hci_devcd_complete(struct hci_dev *hdev)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_COMPLETE;
skb_queue_tail(&hdev->dump.dump_q, skb);
queue_work(hdev->workqueue, &hdev->dump.dump_rx);
return 0;
return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_complete);
@ -543,10 +569,7 @@ int hci_devcd_abort(struct hci_dev *hdev)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_ABORT;
skb_queue_tail(&hdev->dump.dump_q, skb);
queue_work(hdev->workqueue, &hdev->dump.dump_rx);
return 0;
return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_abort);

View File

@ -373,7 +373,15 @@ void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
size_t dlen;
while ((eir = eir_get_data(eir, eir_len, EIR_SERVICE_DATA, &dlen))) {
u16 value = get_unaligned_le16(eir);
u16 value;
if (dlen < sizeof(value)) {
eir += dlen;
eir_len = eir_end - eir;
continue;
}
value = get_unaligned_le16(eir);
if (uuid == value) {
if (len)

View File

@ -145,11 +145,12 @@ void hci_read_supported_codecs(struct hci_dev *hdev)
skb_pull(skb, sizeof(rp->status));
std_codecs = (void *)skb->data;
std_codecs = skb_pull_data(skb, sizeof(*std_codecs));
if (!std_codecs)
goto error;
/* validate codecs length before accessing */
if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num))
if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num))
goto error;
/* enumerate codec capabilities of standard codecs */
@ -161,15 +162,14 @@ void hci_read_supported_codecs(struct hci_dev *hdev)
LOCAL_CODEC_ACL_MASK | LOCAL_CODEC_SCO_MASK, &caps);
}
skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num));
skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num));
vnd_codecs = (void *)skb->data;
vnd_codecs = skb_pull_data(skb, sizeof(*vnd_codecs));
if (!vnd_codecs)
goto error;
/* validate vendor codecs length before accessing */
if (skb->len <
flex_array_size(vnd_codecs, codec, vnd_codecs->num)
+ sizeof(vnd_codecs->num))
if (skb->len < flex_array_size(vnd_codecs, codec, vnd_codecs->num))
goto error;
/* enumerate vendor codec capabilities */
@ -214,11 +214,12 @@ void hci_read_supported_codecs_v2(struct hci_dev *hdev)
skb_pull(skb, sizeof(rp->status));
std_codecs = (void *)skb->data;
std_codecs = skb_pull_data(skb, sizeof(*std_codecs));
if (!std_codecs)
goto error;
/* check for payload data length before accessing */
if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num))
if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num))
goto error;
memset(&caps, 0, sizeof(caps));
@ -229,15 +230,14 @@ void hci_read_supported_codecs_v2(struct hci_dev *hdev)
&caps);
}
skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num));
skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num));
vnd_codecs = (void *)skb->data;
vnd_codecs = skb_pull_data(skb, sizeof(*vnd_codecs));
if (!vnd_codecs)
goto error;
/* check for payload data length before accessing */
if (skb->len <
flex_array_size(vnd_codecs, codec, vnd_codecs->num)
+ sizeof(vnd_codecs->num))
if (skb->len < flex_array_size(vnd_codecs, codec, vnd_codecs->num))
goto error;
for (i = 0; i < vnd_codecs->num; i++) {

View File

@ -1023,6 +1023,19 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
irk = hci_get_irk(hdev, dst, dst_type);
/* An identity address only reaches a peer advertising an RPA
* if the controller translates it. Unless address resolution
* is enabled and this peer is programmed into the resolving
* list, keep the RPA the peer is on air with;
* le_conn_complete_evt() resolves it back once the link is
* up.
*/
if (irk &&
(!hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION) ||
!hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
&irk->bdaddr,
irk->addr_type)))
irk = NULL;
break;
case SCO_LINK:
case ESCO_LINK:
@ -1505,7 +1518,15 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
}
if (conn) {
/* dst may just have been swapped for the peer's RPA above, and
* dst_type describes dst -- it has to travel with it. Leaving
* the identity type behind makes the pair describe a peer that
* does not exist, and nothing downstream repairs it:
* hci_bdaddr_is_rpa() tests the type before the address, so
* the RPA is never treated as one.
*/
bacpy(&conn->dst, dst);
conn->dst_type = dst_type;
} else {
conn = hci_conn_add_unset(hdev, LE_LINK, dst, dst_type, role);
if (IS_ERR(conn))

View File

@ -2673,6 +2673,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
disable_work_sync(&hdev->error_reset);
disable_delayed_work_sync(&hdev->cmd_timer);
disable_delayed_work_sync(&hdev->ncmd_timer);
hci_devcd_shutdown(hdev);
hci_cmd_sync_clear(hdev);
@ -3236,6 +3237,17 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
bt_dev_dbg(hdev, "chan %p queued %d", chan, skb_queue_len(queue));
}
/* Queue hdev->tx_work, unless hdev->workqueue is being drained by
* hci_dev_close_sync(), which would otherwise WARN and drop the work.
*/
static void hci_sched_tx(struct hci_dev *hdev)
{
rcu_read_lock();
if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
queue_work(hdev->workqueue, &hdev->tx_work);
rcu_read_unlock();
}
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
{
struct hci_dev *hdev = chan->conn->hdev;
@ -3244,7 +3256,7 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
hci_queue_acl(chan, &chan->data_q, skb, flags);
queue_work(hdev->workqueue, &hdev->tx_work);
hci_sched_tx(hdev);
}
/* Send SCO data */
@ -3269,7 +3281,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
bt_dev_dbg(hdev, "hcon %p queued %d", conn,
skb_queue_len(&conn->data_q));
queue_work(hdev->workqueue, &hdev->tx_work);
hci_sched_tx(hdev);
}
/* Send ISO data */
@ -3340,7 +3352,7 @@ void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
hci_queue_iso(conn, &conn->data_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
hci_sched_tx(hdev);
}
/* ---- HCI TX task (outgoing data) ---- */

View File

@ -5673,7 +5673,9 @@ int hci_dev_close_sync(struct hci_dev *hdev)
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
bacpy(&hdev->random_addr, BDADDR_ANY);
hci_dev_lock(hdev);
hci_codec_list_clear(&hdev->local_codecs);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;

View File

@ -819,19 +819,24 @@ static void iso_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_error_queue);
}
static void iso_sock_cleanup_listen(struct sock *parent)
/* Close not yet accepted channels */
static void iso_sock_flush_accept_q(struct sock *parent)
{
struct sock *sk;
BT_DBG("parent %p", parent);
/* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
iso_sock_close(sk);
iso_sock_kill(sk);
/* Drop the reference handed back by bt_accept_dequeue(). */
sock_put(sk);
}
}
static void iso_sock_cleanup_listen(struct sock *parent)
{
BT_DBG("parent %p", parent);
iso_sock_flush_accept_q(parent);
/* If listening socket has a hcon, properly disconnect it */
if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon) {
@ -1737,6 +1742,13 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
switch (sk->sk_state) {
case BT_CONNECT2:
if (test_bit(BT_SK_PA_SYNC, &pi->flags)) {
/* Move to BT_LISTEN before requesting the BIG
* sync: the BIS connections are matched to a
* parent socket in BT_LISTEN state, and they
* may be notified before the request returns.
*/
sk->sk_state = BT_LISTEN;
release_sock(sk);
err = iso_conn_big_sync(sk);
lock_sock(sk);
@ -1745,12 +1757,20 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
* connection may have been torn down
* meanwhile and iso_chan_del() may have
* already moved the socket to BT_CLOSED.
* Only move on to BT_LISTEN if the BIG sync
* was actually started and nothing else has
* changed the state.
* Only move back if the BIG sync could not be
* started and nothing else has changed the
* state.
*/
if (!err && sk->sk_state == BT_CONNECT2)
sk->sk_state = BT_LISTEN;
if (err && sk->sk_state == BT_LISTEN) {
/* Discard any child socket that may
* have been queued while the socket
* was in BT_LISTEN, as the cleanup of
* BT_CONNECT2 doesn't drain the
* accept queue.
*/
iso_sock_flush_accept_q(sk);
sk->sk_state = BT_CONNECT2;
}
} else {
iso_conn_defer_accept(pi->conn->hcon);
sk->sk_state = BT_CONFIG;
@ -1760,12 +1780,22 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
break;
case BT_CONNECTED:
if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
/* As above, the BIS connections may be
* notified before the request returns.
*/
sk->sk_state = BT_LISTEN;
release_sock(sk);
err = iso_conn_big_sync(sk);
lock_sock(sk);
if (!err && sk->sk_state == BT_CONNECTED)
sk->sk_state = BT_LISTEN;
if (err && sk->sk_state == BT_LISTEN) {
/* As above, don't leave any child
* socket behind in the accept queue.
*/
iso_sock_flush_accept_q(sk);
sk->sk_state = BT_CONNECTED;
}
early_ret = true;
}
@ -2289,6 +2319,7 @@ static void iso_conn_ready(struct iso_conn *conn)
BTPROTO_ISO, GFP_ATOMIC, 0);
if (!sk) {
release_sock(parent);
sock_put(parent);
return;
}

View File

@ -242,9 +242,7 @@ static void __rfcomm_sock_close(struct sock *sk)
*/
static void rfcomm_sock_close(struct sock *sk)
{
lock_sock(sk);
__rfcomm_sock_close(sk);
release_sock(sk);
}
static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
@ -905,6 +903,7 @@ static int rfcomm_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsig
static int rfcomm_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
bool cleanup_listen = false;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
@ -915,9 +914,17 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
lock_sock(sk);
if (!sk->sk_shutdown) {
sk->sk_shutdown = SHUTDOWN_MASK;
if (sk->sk_state == BT_LISTEN) {
/* Block new children before cleaning up without sk lock. */
sk->sk_state = BT_CLOSED;
cleanup_listen = true;
}
release_sock(sk);
__rfcomm_sock_close(sk);
if (cleanup_listen)
rfcomm_sock_cleanup_listen(sk);
else
__rfcomm_sock_close(sk);
lock_sock(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&