Bluetooth: Discovery Timer stability fixes
This change prevents running timers from being re-initialized
which causes system instablility. Also prevents starting a
discovery operation when already in progress.
CRs-fixed: 328673
Change-Id: Icda36a25fdcb40dab4f95f9cc39ca124b299e308
Signed-off-by: Brian Gix <bgix@codeaurora.org>
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7f2cff8..67a2900 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1,6 +1,7 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2010 Nokia Corporation
+ Copyright (c) 2011-2012 Code Aurora Forum. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
@@ -34,18 +35,9 @@
#define MGMT_VERSION 0
#define MGMT_REVISION 1
-enum scan_mode {
- SCAN_IDLE,
- SCAN_LE,
- SCAN_BR
-};
-
-struct disco_interleave {
- u16 index;
- enum scan_mode mode;
- int int_phase;
- int int_count;
-};
+#define SCAN_IDLE 0x00
+#define SCAN_LE 0x01
+#define SCAN_BR 0x02
struct pending_cmd {
struct list_head list;
@@ -1571,8 +1563,8 @@
if (!hdev)
goto not_found;
- del_timer_sync(&hdev->disc_le_timer);
- del_timer_sync(&hdev->disc_timer);
+ del_timer(&hdev->disco_le_timer);
+ del_timer(&hdev->disco_timer);
hci_dev_put(hdev);
not_found:
@@ -1858,8 +1850,8 @@
if (cmd->opcode == MGMT_OP_STOP_DISCOVERY) {
struct hci_dev *hdev = hci_dev_get(cmd->index);
if (hdev) {
- del_timer_sync(&hdev->disc_le_timer);
- del_timer_sync(&hdev->disc_timer);
+ del_timer(&hdev->disco_le_timer);
+ del_timer(&hdev->disco_timer);
hci_dev_put(hdev);
}
}
@@ -1883,7 +1875,7 @@
{
struct hci_dev *hdev;
struct hci_cp_le_set_scan_enable le_cp = {1, 0};
- struct pending_cmd *cmd;
+ struct mgmt_mode cp = {0};
int err = -1;
BT_DBG("");
@@ -1891,7 +1883,6 @@
hdev = hci_dev_get(index);
if (!hdev || !lmp_le_capable(hdev)) {
- struct mgmt_mode cp = {0};
mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
discovery_terminated, NULL);
@@ -1904,20 +1895,20 @@
return;
}
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
- if (cmd && cmd->param) {
- struct disco_interleave *ilp = cmd->param;
-
+ if (hdev->disco_state != SCAN_IDLE) {
err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
sizeof(le_cp), &le_cp);
- if (err >= 0 && hdev) {
- mod_timer(&hdev->disc_le_timer, jiffies +
- msecs_to_jiffies(ilp->int_phase * 1000));
- ilp->mode = SCAN_LE;
+ if (err >= 0) {
+ mod_timer(&hdev->disco_le_timer, jiffies +
+ msecs_to_jiffies(hdev->disco_int_phase * 1000));
+ hdev->disco_state = SCAN_LE;
} else
- ilp->mode = SCAN_IDLE;
+ hdev->disco_state = SCAN_IDLE;
}
+ if (hdev->disco_state == SCAN_IDLE)
+ mgmt_event(MGMT_EV_DISCOVERING, index, &cp, sizeof(cp), NULL);
+
if (err < 0)
mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
discovery_terminated, NULL);
@@ -1926,82 +1917,74 @@
hci_dev_put(hdev);
}
-static void disco_to(unsigned long data)
+void mgmt_disco_timeout(unsigned long data)
{
- struct disco_interleave *ilp = (void *)data;
- struct hci_dev *hdev;
+ struct hci_dev *hdev = (void *) data;
struct pending_cmd *cmd;
+ struct mgmt_mode cp = {0};
- BT_DBG("hci%d", ilp->index);
+ BT_DBG("hci%d", hdev->id);
- hdev = hci_dev_get(ilp->index);
+ hdev = hci_dev_get(hdev->id);
- if (hdev) {
- hci_dev_lock_bh(hdev);
- del_timer_sync(&hdev->disc_le_timer);
+ if (!hdev)
+ return;
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
+ hci_dev_lock_bh(hdev);
+ del_timer(&hdev->disco_le_timer);
- if (ilp->mode != SCAN_IDLE) {
- struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+ if (hdev->disco_state != SCAN_IDLE) {
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
- if (ilp->mode == SCAN_LE)
- hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
- sizeof(le_cp), &le_cp);
- else
- hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL,
- 0, NULL);
-
- ilp->mode = SCAN_IDLE;
- }
-
- if (cmd) {
- struct mgmt_mode cp = {0};
-
- mgmt_event(MGMT_EV_DISCOVERING, ilp->index, &cp,
- sizeof(cp), NULL);
- mgmt_pending_remove(cmd);
- }
-
- hci_dev_unlock_bh(hdev);
- hci_dev_put(hdev);
- }
-}
-
-static void disco_le_to(unsigned long data)
-{
- struct disco_interleave *ilp = (void *)data;
- struct hci_dev *hdev;
- struct pending_cmd *cmd;
- struct hci_cp_le_set_scan_enable le_cp = {0, 0};
-
- BT_DBG("hci%d", ilp->index);
-
- hdev = hci_dev_get(ilp->index);
-
- if (hdev) {
- hci_dev_lock_bh(hdev);
-
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
-
- if (ilp->mode == SCAN_LE)
+ if (hdev->disco_state == SCAN_LE)
hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
sizeof(le_cp), &le_cp);
+ else
+ hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
- /* re-start BR scan */
- if (cmd) {
- struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
- ilp->int_phase *= 2;
- ilp->int_count = 0;
- cp.num_rsp = (u8) ilp->int_phase;
- hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
- ilp->mode = SCAN_BR;
- } else
- ilp->mode = SCAN_IDLE;
-
- hci_dev_unlock_bh(hdev);
- hci_dev_put(hdev);
+ hdev->disco_state = SCAN_IDLE;
}
+
+ mgmt_event(MGMT_EV_DISCOVERING, hdev->id, &cp, sizeof(cp), NULL);
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev->id);
+ if (cmd)
+ mgmt_pending_remove(cmd);
+
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+}
+
+void mgmt_disco_le_timeout(unsigned long data)
+{
+ struct hci_dev *hdev = (void *)data;
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ BT_DBG("hci%d", hdev->id);
+
+ hdev = hci_dev_get(hdev->id);
+
+ if (!hdev)
+ return;
+
+ hci_dev_lock_bh(hdev);
+
+ if (hdev->disco_state == SCAN_LE)
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+
+ /* re-start BR scan */
+ if (hdev->disco_state != SCAN_IDLE) {
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
+ hdev->disco_int_phase *= 2;
+ hdev->disco_int_count = 0;
+ cp.num_rsp = (u8) hdev->disco_int_phase;
+ hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+ hdev->disco_state = SCAN_BR;
+ }
+
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
}
static int start_discovery(struct sock *sk, u16 index)
@@ -2019,6 +2002,11 @@
hci_dev_lock_bh(hdev);
+ if (hdev->disco_state && timer_pending(&hdev->disco_timer)) {
+ err = -EBUSY;
+ goto failed;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0);
if (!cmd) {
err = -ENOMEM;
@@ -2052,30 +2040,23 @@
if (err < 0)
mgmt_pending_remove(cmd);
else if (lmp_le_capable(hdev)) {
- struct disco_interleave il, *ilp;
-
- il.int_phase = 1;
- il.int_count = 0;
- il.index = index;
- il.mode = SCAN_BR;
- mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, &il,
- sizeof(struct disco_interleave));
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
- if (cmd) {
- ilp = cmd->param;
- setup_timer(&hdev->disc_le_timer, disco_le_to,
- (unsigned long) ilp);
- setup_timer(&hdev->disc_timer, disco_to,
- (unsigned long) ilp);
- mod_timer(&hdev->disc_timer,
- jiffies + msecs_to_jiffies(20000));
- }
+ hdev->disco_int_phase = 1;
+ hdev->disco_int_count = 0;
+ hdev->disco_state = SCAN_BR;
+ mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0);
+ del_timer(&hdev->disco_le_timer);
+ del_timer(&hdev->disco_timer);
+ mod_timer(&hdev->disco_timer,
+ jiffies + msecs_to_jiffies(20000));
}
failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
+ if (err < 0)
+ return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, -err);
+
return err;
}
@@ -2083,10 +2064,10 @@
{
struct hci_cp_le_set_scan_enable le_cp = {0, 0};
struct mgmt_mode mode_cp = {0};
- struct disco_interleave *ilp = NULL;
struct hci_dev *hdev;
struct pending_cmd *cmd = NULL;
int err = -EPERM;
+ u8 state;
BT_DBG("");
@@ -2096,38 +2077,32 @@
hci_dev_lock_bh(hdev);
- if (lmp_le_capable(hdev)) {
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
- }
+ state = hdev->disco_state;
+ hdev->disco_state = SCAN_IDLE;
+ del_timer(&hdev->disco_le_timer);
+ del_timer(&hdev->disco_timer);
- ilp = cmd->param;
- }
-
- if (lmp_le_capable(hdev) && ilp && (ilp->mode == SCAN_LE))
+ if (state == SCAN_LE) {
err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
sizeof(le_cp), &le_cp);
+ if (err >= 0) {
+ mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
+ discovery_terminated, NULL);
- if (err < 0) {
- if (!ilp || (ilp->mode == SCAN_BR))
- err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL,
- 0, NULL);
+ err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
+ NULL, 0);
+ }
}
- if (ilp) {
- ilp->mode = SCAN_IDLE;
- del_timer_sync(&hdev->disc_le_timer);
- del_timer_sync(&hdev->disc_timer);
- }
+ if (err < 0)
+ err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
if (err < 0 && cmd)
mgmt_pending_remove(cmd);
mgmt_event(MGMT_EV_DISCOVERING, index, &mode_cp, sizeof(mode_cp), NULL);
-failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -2822,7 +2797,7 @@
u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir)
{
struct mgmt_ev_device_found ev;
- struct pending_cmd *cmd;
+ struct hci_dev *hdev;
int err;
BT_DBG("le: %d", le);
@@ -2845,36 +2820,38 @@
if (err < 0)
return err;
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
- if (cmd) {
- struct disco_interleave *ilp = cmd->param;
- struct hci_dev *hdev = hci_dev_get(index);
+ hdev = hci_dev_get(index);
- ilp->int_count++;
- if (hdev && ilp->int_count >= ilp->int_phase) {
- /* Inquiry scan for General Discovery LAP */
- struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
- struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+ if (!hdev)
+ return 0;
- ilp->int_phase *= 2;
- ilp->int_count = 0;
- if (ilp->mode == SCAN_LE) {
- /* cancel LE scan */
- hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
- sizeof(le_cp), &le_cp);
- /* start BR scan */
- cp.num_rsp = (u8) ilp->int_phase;
- hci_send_cmd(hdev, HCI_OP_INQUIRY,
- sizeof(cp), &cp);
- ilp->mode = SCAN_BR;
- del_timer_sync(&hdev->disc_le_timer);
- }
+ if (hdev->disco_state == SCAN_IDLE)
+ goto done;
+
+ hdev->disco_int_count++;
+
+ if (hdev->disco_int_count >= hdev->disco_int_phase) {
+ /* Inquiry scan for General Discovery LAP */
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ hdev->disco_int_phase *= 2;
+ hdev->disco_int_count = 0;
+ if (hdev->disco_state == SCAN_LE) {
+ /* cancel LE scan */
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ /* start BR scan */
+ cp.num_rsp = (u8) hdev->disco_int_phase;
+ hci_send_cmd(hdev, HCI_OP_INQUIRY,
+ sizeof(cp), &cp);
+ hdev->disco_state = SCAN_BR;
+ del_timer_sync(&hdev->disco_le_timer);
}
-
- if (hdev)
- hci_dev_put(hdev);
}
+done:
+ hci_dev_put(hdev);
return 0;
}