mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
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:
commit
c5e367a8a3
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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) ---- */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user