@@ -270,6 +270,9 @@ struct adv_monitor {
/* Default authenticated payload timeout 30s */
#define DEFAULT_AUTH_PAYLOAD_TIMEOUT 0x0bb8
+/* Time to keep ACL packets without a corresponding handle queued (2s) */
+#define PENDING_ACL_TIMEOUT msecs_to_jiffies(2000)
+
struct amp_assoc {
__u16 len;
__u16 offset;
@@ -538,6 +541,9 @@ struct hci_dev {
struct delayed_work rpa_expired;
bdaddr_t rpa;
+ struct delayed_work remove_pending_acl;
+ struct sk_buff_head pending_acl_q;
+
#if IS_ENABLED(CONFIG_BT_LEDS)
struct led_trigger *power_led;
#endif
@@ -1773,6 +1779,8 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 *bdaddr_type);
+void hci_process_pending_acl(struct hci_dev *hdev, struct hci_conn *conn);
+
#define SCO_AIRMODE_MASK 0x0003
#define SCO_AIRMODE_CVSD 0x0000
#define SCO_AIRMODE_TRANSP 0x0003
@@ -1786,6 +1786,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q);
skb_queue_purge(&hdev->raw_q);
+ skb_queue_purge(&hdev->pending_acl_q);
/* Drop last sent command */
if (hdev->sent_cmd) {
@@ -3518,6 +3519,78 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
return NOTIFY_STOP;
}
+static void hci_add_pending_acl(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ skb_queue_tail(&hdev->pending_acl_q, skb);
+
+ queue_delayed_work(hdev->workqueue, &hdev->remove_pending_acl,
+ PENDING_ACL_TIMEOUT);
+}
+
+void hci_process_pending_acl(struct hci_dev *hdev, struct hci_conn *conn)
+{
+ struct sk_buff *skb, *tmp;
+ struct hci_acl_hdr *hdr;
+ u16 handle, flags;
+ bool reset_timer = false;
+
+ skb_queue_walk_safe(&hdev->pending_acl_q, skb, tmp) {
+ hdr = (struct hci_acl_hdr *)skb->data;
+ handle = __le16_to_cpu(hdr->handle);
+ flags = hci_flags(handle);
+ handle = hci_handle(handle);
+
+ if (handle != conn->handle)
+ continue;
+
+ __skb_unlink(skb, &hdev->pending_acl_q);
+ skb_pull(skb, HCI_ACL_HDR_SIZE);
+
+ l2cap_recv_acldata(conn, skb, flags);
+ reset_timer = true;
+ }
+
+ if (reset_timer)
+ mod_delayed_work(hdev->workqueue, &hdev->remove_pending_acl,
+ PENDING_ACL_TIMEOUT);
+}
+
+/* Remove the oldest pending ACL, and all pending ACLs with the same handle */
+static void hci_remove_pending_acl(struct work_struct *work)
+{
+ struct hci_dev *hdev;
+ struct sk_buff *skb, *tmp;
+ struct hci_acl_hdr *hdr;
+ u16 handle, oldest_handle;
+
+ hdev = container_of(work, struct hci_dev, remove_pending_acl.work);
+ skb = skb_dequeue(&hdev->pending_acl_q);
+
+ if (!skb)
+ return;
+
+ hdr = (struct hci_acl_hdr *)skb->data;
+ oldest_handle = hci_handle(__le16_to_cpu(hdr->handle));
+ kfree_skb(skb);
+
+ bt_dev_err(hdev, "ACL packet for unknown connection handle %d",
+ oldest_handle);
+
+ skb_queue_walk_safe(&hdev->pending_acl_q, skb, tmp) {
+ hdr = (struct hci_acl_hdr *)skb->data;
+ handle = hci_handle(__le16_to_cpu(hdr->handle));
+
+ if (handle == oldest_handle) {
+ __skb_unlink(skb, &hdev->pending_acl_q);
+ kfree_skb(skb);
+ }
+ }
+
+ if (!skb_queue_empty(&hdev->pending_acl_q))
+ queue_delayed_work(hdev->workqueue, &hdev->remove_pending_acl,
+ PENDING_ACL_TIMEOUT);
+}
+
/* Alloc HCI device */
struct hci_dev *hci_alloc_dev(void)
{
@@ -3610,10 +3683,12 @@ struct hci_dev *hci_alloc_dev(void)
INIT_WORK(&hdev->suspend_prepare, hci_prepare_suspend);
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
+ INIT_DELAYED_WORK(&hdev->remove_pending_acl, hci_remove_pending_acl);
skb_queue_head_init(&hdev->rx_q);
skb_queue_head_init(&hdev->cmd_q);
skb_queue_head_init(&hdev->raw_q);
+ skb_queue_head_init(&hdev->pending_acl_q);
init_waitqueue_head(&hdev->req_wait_q);
init_waitqueue_head(&hdev->suspend_wait_q);
@@ -4662,8 +4737,6 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
struct hci_conn *conn;
__u16 handle, flags;
- skb_pull(skb, HCI_ACL_HDR_SIZE);
-
handle = __le16_to_cpu(hdr->handle);
flags = hci_flags(handle);
handle = hci_handle(handle);
@@ -4678,17 +4751,16 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_unlock(hdev);
if (conn) {
+ skb_pull(skb, HCI_ACL_HDR_SIZE);
+
hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF);
/* Send to upper protocol */
l2cap_recv_acldata(conn, skb, flags);
return;
} else {
- bt_dev_err(hdev, "ACL packet for unknown connection handle %d",
- handle);
+ hci_add_pending_acl(hdev, skb);
}
-
- kfree_skb(skb);
}
/* SCO data packet */
@@ -2627,6 +2627,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp),
&cp);
}
+
+ hci_process_pending_acl(hdev, conn);
} else {
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)