@@ -339,6 +339,7 @@ enum {
#define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */
#define HCI_INIT_TIMEOUT msecs_to_jiffies(10000) /* 10 seconds */
#define HCI_CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
+#define HCI_NCMD_TIMEOUT msecs_to_jiffies(4000) /* 4 seconds */
#define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */
#define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
#define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */
@@ -470,6 +470,7 @@ struct hci_dev {
struct delayed_work service_cache;
struct delayed_work cmd_timer;
+ struct delayed_work ncmd_timer;
struct work_struct rx_work;
struct work_struct cmd_work;
@@ -2769,6 +2769,20 @@ static void hci_cmd_timeout(struct work_struct *work)
queue_work(hdev->workqueue, &hdev->cmd_work);
}
+/* HCI ncmd timer function */
+static void hci_ncmd_timeout(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ ncmd_timer.work);
+
+ bt_dev_err(hdev, "ncmd timeout");
+
+ if (hci_dev_do_close(hdev))
+ return;
+
+ hci_dev_do_open(hdev);
+}
+
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 bdaddr_type)
{
@@ -3831,6 +3845,7 @@ struct hci_dev *hci_alloc_dev(void)
init_waitqueue_head(&hdev->suspend_wait_q);
INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
+ INIT_DELAYED_WORK(&hdev->ncmd_timer, hci_ncmd_timeout);
hci_request_setup(hdev);
@@ -3635,6 +3635,11 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
if (*opcode != HCI_OP_NOP)
cancel_delayed_work(&hdev->cmd_timer);
+ if (!ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
+ schedule_delayed_work(&hdev->ncmd_timer, HCI_NCMD_TIMEOUT);
+ else
+ cancel_delayed_work(&hdev->ncmd_timer);
+
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
atomic_set(&hdev->cmd_cnt, 1);
@@ -3740,6 +3745,11 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
if (*opcode != HCI_OP_NOP)
cancel_delayed_work(&hdev->cmd_timer);
+ if (!ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
+ schedule_delayed_work(&hdev->ncmd_timer, HCI_NCMD_TIMEOUT);
+ else
+ cancel_delayed_work(&hdev->ncmd_timer);
+
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
atomic_set(&hdev->cmd_cnt, 1);