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>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (c) 2000-2001, 2010-2011, Code Aurora Forum. All rights reserved.
|
||||
Copyright (c) 2000-2001, 2010-2012, Code Aurora Forum. All rights reserved.
|
||||
|
||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
|
||||
@@ -240,8 +240,11 @@ struct hci_dev {
|
||||
rwlock_t adv_entries_lock;
|
||||
struct timer_list adv_timer;
|
||||
|
||||
struct timer_list disc_timer;
|
||||
struct timer_list disc_le_timer;
|
||||
struct timer_list disco_timer;
|
||||
struct timer_list disco_le_timer;
|
||||
__u8 disco_state;
|
||||
int disco_int_phase;
|
||||
int disco_int_count;
|
||||
|
||||
struct hci_dev_stats stat;
|
||||
|
||||
@@ -1037,6 +1040,8 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
|
||||
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name);
|
||||
void mgmt_inquiry_started(u16 index);
|
||||
void mgmt_inquiry_complete_evt(u16 index, u8 status);
|
||||
void mgmt_disco_timeout(unsigned long data);
|
||||
void mgmt_disco_le_timeout(unsigned long data);
|
||||
int mgmt_encrypt_change(u16 index, bdaddr_t *bdaddr, u8 status);
|
||||
|
||||
/* LE SMP Management interface */
|
||||
|
||||
@@ -1464,6 +1464,10 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
skb_queue_head_init(&hdev->raw_q);
|
||||
|
||||
setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev);
|
||||
setup_timer(&hdev->disco_timer, mgmt_disco_timeout,
|
||||
(unsigned long) hdev);
|
||||
setup_timer(&hdev->disco_le_timer, mgmt_disco_le_timeout,
|
||||
(unsigned long) hdev);
|
||||
|
||||
for (i = 0; i < NUM_REASSEMBLY; i++)
|
||||
hdev->reassembly[i] = NULL;
|
||||
@@ -1575,8 +1579,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
hci_del_off_timer(hdev);
|
||||
del_timer(&hdev->adv_timer);
|
||||
del_timer(&hdev->cmd_timer);
|
||||
del_timer(&hdev->disc_timer);
|
||||
del_timer(&hdev->disc_le_timer);
|
||||
del_timer(&hdev->disco_timer);
|
||||
del_timer(&hdev->disco_le_timer);
|
||||
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
|
||||
|
||||
@@ -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 @@ static void discovery_terminated(struct pending_cmd *cmd, void *data)
|
||||
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 @@ static void discovery_rsp(struct pending_cmd *cmd, void *data)
|
||||
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 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status)
|
||||
{
|
||||
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 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status)
|
||||
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 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status)
|
||||
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 @@ done:
|
||||
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)
|
||||
return;
|
||||
|
||||
if (hdev) {
|
||||
hci_dev_lock_bh(hdev);
|
||||
del_timer_sync(&hdev->disc_le_timer);
|
||||
del_timer(&hdev->disco_le_timer);
|
||||
|
||||
cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
|
||||
|
||||
if (ilp->mode != SCAN_IDLE) {
|
||||
if (hdev->disco_state != SCAN_IDLE) {
|
||||
struct hci_cp_le_set_scan_enable le_cp = {0, 0};
|
||||
|
||||
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);
|
||||
hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
|
||||
|
||||
ilp->mode = SCAN_IDLE;
|
||||
hdev->disco_state = SCAN_IDLE;
|
||||
}
|
||||
|
||||
if (cmd) {
|
||||
struct mgmt_mode cp = {0};
|
||||
mgmt_event(MGMT_EV_DISCOVERING, hdev->id, &cp, sizeof(cp), NULL);
|
||||
|
||||
mgmt_event(MGMT_EV_DISCOVERING, ilp->index, &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);
|
||||
}
|
||||
}
|
||||
|
||||
static void disco_le_to(unsigned long data)
|
||||
void mgmt_disco_le_timeout(unsigned long data)
|
||||
{
|
||||
struct disco_interleave *ilp = (void *)data;
|
||||
struct hci_dev *hdev;
|
||||
struct pending_cmd *cmd;
|
||||
struct hci_dev *hdev = (void *)data;
|
||||
struct hci_cp_le_set_scan_enable le_cp = {0, 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)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
/* re-start BR scan */
|
||||
if (cmd) {
|
||||
if (hdev->disco_state != SCAN_IDLE) {
|
||||
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;
|
||||
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);
|
||||
ilp->mode = SCAN_BR;
|
||||
} else
|
||||
ilp->mode = SCAN_IDLE;
|
||||
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 @@ static int start_discovery(struct sock *sk, u16 index)
|
||||
|
||||
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 @@ static int start_discovery(struct sock *sk, u16 index)
|
||||
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,
|
||||
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 @@ static int stop_discovery(struct sock *sk, u16 index)
|
||||
{
|
||||
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 @@ static int stop_discovery(struct sock *sk, u16 index)
|
||||
|
||||
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 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
|
||||
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 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
|
||||
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) {
|
||||
if (!hdev)
|
||||
return 0;
|
||||
|
||||
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};
|
||||
|
||||
ilp->int_phase *= 2;
|
||||
ilp->int_count = 0;
|
||||
if (ilp->mode == SCAN_LE) {
|
||||
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) ilp->int_phase;
|
||||
cp.num_rsp = (u8) hdev->disco_int_phase;
|
||||
hci_send_cmd(hdev, HCI_OP_INQUIRY,
|
||||
sizeof(cp), &cp);
|
||||
ilp->mode = SCAN_BR;
|
||||
del_timer_sync(&hdev->disc_le_timer);
|
||||
hdev->disco_state = SCAN_BR;
|
||||
del_timer_sync(&hdev->disco_le_timer);
|
||||
}
|
||||
}
|
||||
|
||||
if (hdev)
|
||||
done:
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user