mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-25 16:11:45 +00:00
Bluetooth: Move LE scan disable/restart behind req_workqueue
To avoid any risks of races, place also these LE scan modification work callbacks behind the same work queue as the other LE scan changes. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
145a0913ef
commit
7c1fbed239
4 changed files with 183 additions and 173 deletions
|
@ -328,6 +328,8 @@ struct hci_dev {
|
||||||
struct work_struct tx_work;
|
struct work_struct tx_work;
|
||||||
|
|
||||||
struct work_struct bg_scan_update;
|
struct work_struct bg_scan_update;
|
||||||
|
struct delayed_work le_scan_disable;
|
||||||
|
struct delayed_work le_scan_restart;
|
||||||
|
|
||||||
struct sk_buff_head rx_q;
|
struct sk_buff_head rx_q;
|
||||||
struct sk_buff_head raw_q;
|
struct sk_buff_head raw_q;
|
||||||
|
@ -372,9 +374,6 @@ struct hci_dev {
|
||||||
|
|
||||||
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
|
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
|
||||||
|
|
||||||
struct delayed_work le_scan_disable;
|
|
||||||
struct delayed_work le_scan_restart;
|
|
||||||
|
|
||||||
__s8 adv_tx_power;
|
__s8 adv_tx_power;
|
||||||
__u8 adv_data[HCI_MAX_AD_LENGTH];
|
__u8 adv_data[HCI_MAX_AD_LENGTH];
|
||||||
__u8 adv_data_len;
|
__u8 adv_data_len;
|
||||||
|
|
|
@ -1527,9 +1527,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
|
||||||
if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
|
if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
|
||||||
cancel_delayed_work(&hdev->service_cache);
|
cancel_delayed_work(&hdev->service_cache);
|
||||||
|
|
||||||
cancel_delayed_work_sync(&hdev->le_scan_disable);
|
|
||||||
cancel_delayed_work_sync(&hdev->le_scan_restart);
|
|
||||||
|
|
||||||
if (hci_dev_test_flag(hdev, HCI_MGMT))
|
if (hci_dev_test_flag(hdev, HCI_MGMT))
|
||||||
cancel_delayed_work_sync(&hdev->rpa_expired);
|
cancel_delayed_work_sync(&hdev->rpa_expired);
|
||||||
|
|
||||||
|
@ -2889,169 +2886,6 @@ static void hci_conn_params_clear_all(struct hci_dev *hdev)
|
||||||
BT_DBG("All LE connection parameters were removed");
|
BT_DBG("All LE connection parameters were removed");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
|
||||||
{
|
|
||||||
if (status) {
|
|
||||||
BT_ERR("Failed to start inquiry: status %d", status);
|
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
|
|
||||||
u16 opcode)
|
|
||||||
{
|
|
||||||
/* General inquiry access code (GIAC) */
|
|
||||||
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
|
||||||
struct hci_cp_inquiry cp;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
BT_ERR("Failed to disable LE scanning: status %d", status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdev->discovery.scan_start = 0;
|
|
||||||
|
|
||||||
switch (hdev->discovery.type) {
|
|
||||||
case DISCOV_TYPE_LE:
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DISCOV_TYPE_INTERLEAVED:
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
|
|
||||||
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
|
|
||||||
&hdev->quirks)) {
|
|
||||||
/* If we were running LE only scan, change discovery
|
|
||||||
* state. If we were running both LE and BR/EDR inquiry
|
|
||||||
* simultaneously, and BR/EDR inquiry is already
|
|
||||||
* finished, stop discovery, otherwise BR/EDR inquiry
|
|
||||||
* will stop discovery when finished. If we will resolve
|
|
||||||
* remote device name, do not change discovery state.
|
|
||||||
*/
|
|
||||||
if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
|
|
||||||
hdev->discovery.state != DISCOVERY_RESOLVING)
|
|
||||||
hci_discovery_set_state(hdev,
|
|
||||||
DISCOVERY_STOPPED);
|
|
||||||
} else {
|
|
||||||
struct hci_request req;
|
|
||||||
|
|
||||||
hci_inquiry_cache_flush(hdev);
|
|
||||||
|
|
||||||
hci_req_init(&req, hdev);
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
memcpy(&cp.lap, lap, sizeof(cp.lap));
|
|
||||||
cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
|
|
||||||
hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
|
|
||||||
|
|
||||||
err = hci_req_run(&req, inquiry_complete);
|
|
||||||
if (err) {
|
|
||||||
BT_ERR("Inquiry request failed: err %d", err);
|
|
||||||
hci_discovery_set_state(hdev,
|
|
||||||
DISCOVERY_STOPPED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void le_scan_disable_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
|
||||||
le_scan_disable.work);
|
|
||||||
struct hci_request req;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
cancel_delayed_work_sync(&hdev->le_scan_restart);
|
|
||||||
|
|
||||||
hci_req_init(&req, hdev);
|
|
||||||
|
|
||||||
hci_req_add_le_scan_disable(&req);
|
|
||||||
|
|
||||||
err = hci_req_run(&req, le_scan_disable_work_complete);
|
|
||||||
if (err)
|
|
||||||
BT_ERR("Disable LE scanning request failed: err %d", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status,
|
|
||||||
u16 opcode)
|
|
||||||
{
|
|
||||||
unsigned long timeout, duration, scan_start, now;
|
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
BT_ERR("Failed to restart LE scan: status %d", status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
|
|
||||||
!hdev->discovery.scan_start)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* When the scan was started, hdev->le_scan_disable has been queued
|
|
||||||
* after duration from scan_start. During scan restart this job
|
|
||||||
* has been canceled, and we need to queue it again after proper
|
|
||||||
* timeout, to make sure that scan does not run indefinitely.
|
|
||||||
*/
|
|
||||||
duration = hdev->discovery.scan_duration;
|
|
||||||
scan_start = hdev->discovery.scan_start;
|
|
||||||
now = jiffies;
|
|
||||||
if (now - scan_start <= duration) {
|
|
||||||
int elapsed;
|
|
||||||
|
|
||||||
if (now >= scan_start)
|
|
||||||
elapsed = now - scan_start;
|
|
||||||
else
|
|
||||||
elapsed = ULONG_MAX - scan_start + now;
|
|
||||||
|
|
||||||
timeout = duration - elapsed;
|
|
||||||
} else {
|
|
||||||
timeout = 0;
|
|
||||||
}
|
|
||||||
queue_delayed_work(hdev->workqueue,
|
|
||||||
&hdev->le_scan_disable, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void le_scan_restart_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
|
||||||
le_scan_restart.work);
|
|
||||||
struct hci_request req;
|
|
||||||
struct hci_cp_le_set_scan_enable cp;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
/* If controller is not scanning we are done. */
|
|
||||||
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
|
||||||
return;
|
|
||||||
|
|
||||||
hci_req_init(&req, hdev);
|
|
||||||
|
|
||||||
hci_req_add_le_scan_disable(&req);
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
cp.enable = LE_SCAN_ENABLE;
|
|
||||||
cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
|
||||||
hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
|
||||||
|
|
||||||
err = hci_req_run(&req, le_scan_restart_work_complete);
|
|
||||||
if (err)
|
|
||||||
BT_ERR("Restart LE scan request failed: err %d", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the Identity Address of the controller.
|
/* Copy the Identity Address of the controller.
|
||||||
*
|
*
|
||||||
* If the controller has a public BD_ADDR, then by default use that one.
|
* If the controller has a public BD_ADDR, then by default use that one.
|
||||||
|
@ -3151,8 +2985,6 @@ struct hci_dev *hci_alloc_dev(void)
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
|
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
|
||||||
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
||||||
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
|
|
||||||
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
|
|
||||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
|
INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
|
||||||
|
|
||||||
skb_queue_head_init(&hdev->rx_q);
|
skb_queue_head_init(&hdev->rx_q);
|
||||||
|
|
|
@ -858,12 +858,191 @@ static void bg_scan_update(struct work_struct *work)
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||||
|
{
|
||||||
|
if (status) {
|
||||||
|
BT_ERR("Failed to start inquiry: status %d", status);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
/* General inquiry access code (GIAC) */
|
||||||
|
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
||||||
|
struct hci_cp_inquiry cp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
BT_ERR("Failed to disable LE scanning: status %d", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdev->discovery.scan_start = 0;
|
||||||
|
|
||||||
|
switch (hdev->discovery.type) {
|
||||||
|
case DISCOV_TYPE_LE:
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCOV_TYPE_INTERLEAVED:
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
|
||||||
|
&hdev->quirks)) {
|
||||||
|
/* If we were running LE only scan, change discovery
|
||||||
|
* state. If we were running both LE and BR/EDR inquiry
|
||||||
|
* simultaneously, and BR/EDR inquiry is already
|
||||||
|
* finished, stop discovery, otherwise BR/EDR inquiry
|
||||||
|
* will stop discovery when finished. If we will resolve
|
||||||
|
* remote device name, do not change discovery state.
|
||||||
|
*/
|
||||||
|
if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||||
|
hdev->discovery.state != DISCOVERY_RESOLVING)
|
||||||
|
hci_discovery_set_state(hdev,
|
||||||
|
DISCOVERY_STOPPED);
|
||||||
|
} else {
|
||||||
|
struct hci_request req;
|
||||||
|
|
||||||
|
hci_inquiry_cache_flush(hdev);
|
||||||
|
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
memcpy(&cp.lap, lap, sizeof(cp.lap));
|
||||||
|
cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
|
||||||
|
hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
err = hci_req_run(&req, inquiry_complete);
|
||||||
|
if (err) {
|
||||||
|
BT_ERR("Inquiry request failed: err %d", err);
|
||||||
|
hci_discovery_set_state(hdev,
|
||||||
|
DISCOVERY_STOPPED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_scan_disable(struct hci_request *req, unsigned long opt)
|
||||||
|
{
|
||||||
|
hci_req_add_le_scan_disable(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_scan_disable_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||||
|
le_scan_disable.work);
|
||||||
|
u8 status;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
cancel_delayed_work(&hdev->le_scan_restart);
|
||||||
|
|
||||||
|
err = hci_req_sync(hdev, le_scan_disable, 0, HCI_CMD_TIMEOUT, &status);
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
|
||||||
|
le_scan_disable_work_complete(hdev, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
unsigned long timeout, duration, scan_start, now;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
BT_ERR("Failed to restart LE scan: status %d", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
|
||||||
|
!hdev->discovery.scan_start)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
/* When the scan was started, hdev->le_scan_disable has been queued
|
||||||
|
* after duration from scan_start. During scan restart this job
|
||||||
|
* has been canceled, and we need to queue it again after proper
|
||||||
|
* timeout, to make sure that scan does not run indefinitely.
|
||||||
|
*/
|
||||||
|
duration = hdev->discovery.scan_duration;
|
||||||
|
scan_start = hdev->discovery.scan_start;
|
||||||
|
now = jiffies;
|
||||||
|
if (now - scan_start <= duration) {
|
||||||
|
int elapsed;
|
||||||
|
|
||||||
|
if (now >= scan_start)
|
||||||
|
elapsed = now - scan_start;
|
||||||
|
else
|
||||||
|
elapsed = ULONG_MAX - scan_start + now;
|
||||||
|
|
||||||
|
timeout = duration - elapsed;
|
||||||
|
} else {
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_delayed_work(hdev->req_workqueue,
|
||||||
|
&hdev->le_scan_disable, timeout);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_scan_restart(struct hci_request *req, unsigned long opt)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = req->hdev;
|
||||||
|
struct hci_cp_le_set_scan_enable cp;
|
||||||
|
|
||||||
|
/* If controller is not scanning we are done. */
|
||||||
|
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hci_req_add_le_scan_disable(req);
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
cp.enable = LE_SCAN_ENABLE;
|
||||||
|
cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
||||||
|
hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_scan_restart_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||||
|
le_scan_restart.work);
|
||||||
|
u8 status;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
err = hci_req_sync(hdev, le_scan_restart, 0, HCI_CMD_TIMEOUT, &status);
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
|
||||||
|
le_scan_restart_work_complete(hdev, status);
|
||||||
|
}
|
||||||
|
|
||||||
void hci_request_setup(struct hci_dev *hdev)
|
void hci_request_setup(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
|
INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
|
||||||
|
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
|
||||||
|
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hci_request_cancel_all(struct hci_dev *hdev)
|
void hci_request_cancel_all(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
cancel_work_sync(&hdev->bg_scan_update);
|
cancel_work_sync(&hdev->bg_scan_update);
|
||||||
|
cancel_delayed_work_sync(&hdev->le_scan_disable);
|
||||||
|
cancel_delayed_work_sync(&hdev->le_scan_restart);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4367,7 +4367,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
|
||||||
hdev->discovery.scan_duration = timeout;
|
hdev->discovery.scan_duration = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_delayed_work(hdev->workqueue,
|
queue_delayed_work(hdev->req_workqueue,
|
||||||
&hdev->le_scan_disable, timeout);
|
&hdev->le_scan_disable, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8389,7 +8389,7 @@ static void restart_le_scan(struct hci_dev *hdev)
|
||||||
hdev->discovery.scan_duration))
|
hdev->discovery.scan_duration))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
|
queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_restart,
|
||||||
DISCOV_LE_RESTART_DELAY);
|
DISCOV_LE_RESTART_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue