* [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue
@ 2015-11-11 6:11 Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 01/12] Bluetooth: Add stubs for synchronous HCI request functionality Johan Hedberg
` (11 more replies)
0 siblings, 12 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
Hi,
v2: updated version with feedback for 01/12 and 02/12 taken into
account.
>From the original cover letter:
The current bluetooth-next tree is not able to reliably and in a
race-free manner handle updates to the LE scan state. This results in
the kernel ending up out of sync with the actual LE scan state and
starts sending incorrect scan state HCI commands, often resulting in
"not allowed" HCI responses.
This is an initial set of patches to try solve the issue. I've tried to
retain as much as possible of the original logic so that it's easier to
see that nothing should get broken. Because of this, there are still
many simplifications and cleanups that can be done. However, since I've
already got 12 patches lined up I wanted to get them sent, reviewed and
merged. After that I can concentrate on the cleanups and I'll also be
looking into moving the adverising state changes to hci_request.c as
well.
The general solution that this set takes to solve the synchronization
issues with the LE scan state, is to move all of it behind the
hdev->req_workqueue and perform the actions synchronously. The main tool
I've used for this is the hci_req_sync() API which allows taking
existing asynchronous HCI request helpers and using them to perform the
same commands synchronously (by passing the helper as the callback to
hci_req_sync).
Since only one work callback is active at a time we get an easy way to
avoid races between the changes. All of the new synchronous code can be
found in hci_request.c which seemed like an appropriate place for it.
I've done a fair amount of testing with the patches, including
mgmt-tester, l2cap-tester and various bluetoothd tests (both with
controllers supporting simultaneous Inquiry+LE scan as well as those not
supporting it). However, because of the size of the changes it's
perfectly possible that I've missed or broken something, so it'd be good
if others could give the set a spin before merging it.
Johan
----------------------------------------------------------------
Johan Hedberg (12):
Bluetooth: Add stubs for synchronous HCI request functionality
Bluetooth: Run all background scan updates through req_workqueue
Bluetooth: Don't wait for HCI in Add/Remove Device
Bluetooth: Add HCI status return parameter to hci_req_sync()
Bluetooth: Use req_workqueue for explicit connect requests
Bluetooth: Use req_workqueue for background scanning when powering on
Bluetooth: Make __hci_update_background_scan private to hci_request.c
Bluetooth: Move LE scan disable/restart behind req_workqueue
Bluetooth: Add discovery type validity helper
Bluetooth: Add error return value to hci_req_sync callback
Bluetooth: Move Start Discovery to req_workqueue
Bluetooth: Move Stop Discovery to req_workqueue
include/net/bluetooth/hci.h | 3 +-
include/net/bluetooth/hci_core.h | 10 +-
net/bluetooth/hci_conn.c | 39 +--
net/bluetooth/hci_core.c | 243 ++++-------------
net/bluetooth/hci_request.c | 537 +++++++++++++++++++++++++++++++++++---
net/bluetooth/hci_request.h | 26 +-
net/bluetooth/mgmt.c | 503 +++++++----------------------------
7 files changed, 684 insertions(+), 677 deletions(-)
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 01/12] Bluetooth: Add stubs for synchronous HCI request functionality
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 02/12] Bluetooth: Run all background scan updates through req_workqueue Johan Hedberg
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
Prepare hci_request.c to have code for doing synchronous HCI requests,
such as LE scanning or advertising changes. The necessary work
callbacks will be set up in hci_request_setup() and cleaned up in
hci_request_cancel_all(). The former is used when an HCI device get
registered, and the latter each time it is powered off (or
unregistered).
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/hci_core.c | 4 ++++
net/bluetooth/hci_request.c | 8 ++++++++
net/bluetooth/hci_request.h | 3 +++
3 files changed, 15 insertions(+)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index ea7cad5a161c..d6fc485fe8a1 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1604,6 +1604,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
hci_req_sync_unlock(hdev);
+ hci_request_cancel_all(hdev);
+
hci_dev_put(hdev);
return 0;
}
@@ -3156,6 +3158,8 @@ struct hci_dev *hci_alloc_dev(void)
INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
+ hci_request_setup(hdev);
+
hci_init_sysfs(hdev);
discovery_init(hdev);
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index ae19bce89616..d48206277fe4 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -845,3 +845,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
return 0;
}
+
+void hci_request_setup(struct hci_dev *hdev)
+{
+}
+
+void hci_request_cancel_all(struct hci_dev *hdev)
+{
+}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 5b3240cf9eb7..9759b7175f8e 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -70,3 +70,6 @@ void __hci_update_background_scan(struct hci_request *req);
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
u8 reason);
+
+void hci_request_setup(struct hci_dev *hdev);
+void hci_request_cancel_all(struct hci_dev *hdev);
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 02/12] Bluetooth: Run all background scan updates through req_workqueue
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 01/12] Bluetooth: Add stubs for synchronous HCI request functionality Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 03/12] Bluetooth: Don't wait for HCI in Add/Remove Device Johan Hedberg
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
Instead of firing off a simple async request queue all background scan
updates through req_workqueue and use hci_req_sync() there to ensure
that no two updates overlap with each other.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/hci_core.h | 2 ++
net/bluetooth/hci_request.c | 39 +++++++++++++++++----------------------
net/bluetooth/hci_request.h | 6 +++++-
net/bluetooth/mgmt.c | 2 +-
4 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 15e6a2bffc2b..c2ca6a58d1e0 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -327,6 +327,8 @@ struct hci_dev {
struct work_struct cmd_work;
struct work_struct tx_work;
+ struct work_struct bg_scan_update;
+
struct sk_buff_head rx_q;
struct sk_buff_head raw_q;
struct sk_buff_head cmd_q;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index d48206277fe4..0adbb59ec2f0 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -731,28 +731,6 @@ void __hci_update_background_scan(struct hci_request *req)
}
}
-static void update_background_scan_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- if (status)
- BT_DBG("HCI request failed to update background scanning: "
- "status 0x%2.2x", status);
-}
-
-void hci_update_background_scan(struct hci_dev *hdev)
-{
- int err;
- struct hci_request req;
-
- hci_req_init(&req, hdev);
-
- __hci_update_background_scan(&req);
-
- err = hci_req_run(&req, update_background_scan_complete);
- if (err && err != -ENODATA)
- BT_ERR("Failed to run HCI request: err %d", err);
-}
-
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
u8 reason)
{
@@ -846,10 +824,27 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
return 0;
}
+static void update_bg_scan(struct hci_request *req, unsigned long opt)
+{
+ hci_dev_lock(req->hdev);
+ __hci_update_background_scan(req);
+ hci_dev_unlock(req->hdev);
+}
+
+static void bg_scan_update(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ bg_scan_update);
+
+ hci_req_sync(hdev, update_bg_scan, 0, HCI_CMD_TIMEOUT);
+}
+
void hci_request_setup(struct hci_dev *hdev)
{
+ INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
}
void hci_request_cancel_all(struct hci_dev *hdev)
{
+ cancel_work_sync(&hdev->bg_scan_update);
}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 9759b7175f8e..983e687fee22 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -64,12 +64,16 @@ void __hci_update_page_scan(struct hci_request *req);
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type);
-void hci_update_background_scan(struct hci_dev *hdev);
void __hci_update_background_scan(struct hci_request *req);
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
u8 reason);
+static inline void hci_update_background_scan(struct hci_dev *hdev)
+{
+ queue_work(hdev->req_workqueue, &hdev->bg_scan_update);
+}
+
void hci_request_setup(struct hci_dev *hdev);
void hci_request_cancel_all(struct hci_dev *hdev);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7f22119276f3..29c9fec814b4 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2510,8 +2510,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_req_init(&req, hdev);
update_adv_data(&req);
update_scan_rsp_data(&req);
- __hci_update_background_scan(&req);
hci_req_run(&req, NULL);
+ hci_update_background_scan(hdev);
}
unlock:
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 03/12] Bluetooth: Don't wait for HCI in Add/Remove Device
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 01/12] Bluetooth: Add stubs for synchronous HCI request functionality Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 02/12] Bluetooth: Run all background scan updates through req_workqueue Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 04/12] Bluetooth: Add HCI status return parameter to hci_req_sync() Johan Hedberg
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
There's no point in waiting for HCI activity in Add/Remove Device
since the effects of these calls are long-lasting and we can anyway
not report up to the application all HCI failures.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/mgmt.c | 161 ++++++++++++++++-----------------------------------
1 file changed, 50 insertions(+), 111 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 29c9fec814b4..27504949e995 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -6076,10 +6076,9 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
}
/* This function requires the caller holds hdev->lock */
-static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
+static int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr,
u8 addr_type, u8 auto_connect)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
params = hci_conn_params_add(hdev, addr, addr_type);
@@ -6099,26 +6098,17 @@ static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
*/
if (params->explicit_connect)
list_add(¶ms->action, &hdev->pend_le_conns);
-
- __hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_REPORT:
if (params->explicit_connect)
list_add(¶ms->action, &hdev->pend_le_conns);
else
list_add(¶ms->action, &hdev->pend_le_reports);
- __hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
- if (!is_connected(hdev, addr, addr_type)) {
+ if (!is_connected(hdev, addr, addr_type))
list_add(¶ms->action, &hdev->pend_le_conns);
- /* If we are in scan phase of connecting, we were
- * already added to pend_le_conns and scanning.
- */
- if (params->auto_connect != HCI_AUTO_CONN_EXPLICIT)
- __hci_update_background_scan(req);
- }
break;
}
@@ -6142,25 +6132,6 @@ static void device_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
}
-static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("status 0x%02x", status);
-
- hci_dev_lock(hdev);
-
- cmd = pending_find(MGMT_OP_ADD_DEVICE, hdev);
- if (!cmd)
- goto unlock;
-
- cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
-
-unlock:
- hci_dev_unlock(hdev);
-}
-
static int add_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
@@ -6198,9 +6169,10 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
if (cp->addr.type == BDADDR_BREDR) {
/* Only incoming connections action is supported for now */
if (cp->action != 0x01) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6229,33 +6201,31 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
* hci_conn_params_lookup.
*/
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
- err = cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults.
*/
- if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type,
+ if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
auto_conn) < 0) {
- err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_FAILED, &cp->addr,
+ sizeof(cp->addr));
goto unlock;
}
+ hci_update_background_scan(hdev);
+
added:
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
- err = hci_req_run(&req, add_device_complete);
- if (err < 0) {
- /* ENODATA means no HCI commands were needed (e.g. if
- * the adapter is powered off).
- */
- if (err == -ENODATA)
- err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
- mgmt_pending_remove(cmd);
- }
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_SUCCESS, &cp->addr,
+ sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
@@ -6273,55 +6243,25 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
}
-static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("status 0x%02x", status);
-
- hci_dev_lock(hdev);
-
- cmd = pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
- if (!cmd)
- goto unlock;
-
- cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
-
-unlock:
- hci_dev_unlock(hdev);
-}
-
static int remove_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_remove_device *cp = data;
- struct mgmt_pending_cmd *cmd;
- struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
- hci_req_init(&req, hdev);
-
hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
- goto unlock;
- }
-
- cmd->cmd_complete = addr_cmd_complete;
-
if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
struct hci_conn_params *params;
u8 addr_type;
if (!bdaddr_type_is_valid(cp->addr.type)) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6330,13 +6270,15 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
&cp->addr.bdaddr,
cp->addr.type);
if (err) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr,
+ sizeof(cp->addr));
goto unlock;
}
- __hci_update_page_scan(&req);
+ hci_update_page_scan(hdev);
device_removed(sk, hdev, &cp->addr.bdaddr,
cp->addr.type);
@@ -6351,33 +6293,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
* hci_conn_params_lookup.
*/
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
addr_type);
if (!params) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
list_del(¶ms->action);
list_del(¶ms->list);
kfree(params);
- __hci_update_background_scan(&req);
+ hci_update_background_scan(hdev);
device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
} else {
@@ -6385,9 +6330,10 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
struct bdaddr_list *b, *btmp;
if (cp->addr.type) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6397,7 +6343,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
kfree(b);
}
- __hci_update_page_scan(&req);
+ hci_update_page_scan(hdev);
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -6414,20 +6360,13 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
BT_DBG("All LE connection parameters were removed");
- __hci_update_background_scan(&req);
+ hci_update_background_scan(hdev);
}
complete:
- err = hci_req_run(&req, remove_device_complete);
- if (err < 0) {
- /* ENODATA means no HCI commands were needed (e.g. if
- * the adapter is powered off).
- */
- if (err == -ENODATA)
- err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
- mgmt_pending_remove(cmd);
- }
-
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_SUCCESS, &cp->addr,
+ sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
return err;
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 04/12] Bluetooth: Add HCI status return parameter to hci_req_sync()
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (2 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 03/12] Bluetooth: Don't wait for HCI in Add/Remove Device Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 05/12] Bluetooth: Use req_workqueue for explicit connect requests Johan Hedberg
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
In some cases it may be important to get the exact HCI status rather
than the converted HCI-to-errno value. Add an optional return
parameter to the hci_req_sync() API to allow for this. Since there are
no good HCI translation candidates for cancelation and timeout, use
the "unknown" status code for those cases.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/hci.h | 3 ++-
net/bluetooth/hci_core.c | 26 +++++++++++++-------------
net/bluetooth/hci_request.c | 12 +++++++++---
net/bluetooth/hci_request.h | 4 ++--
4 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 0205b80cc90b..cc2216727655 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -452,7 +452,8 @@ enum {
#define HCI_ERROR_REMOTE_POWER_OFF 0x15
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
-#define HCI_ERROR_INVALID_LL_PARAMS 0x1E
+#define HCI_ERROR_INVALID_LL_PARAMS 0x1e
+#define HCI_ERROR_UNSPECIFIED 0x1f
#define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c
/* Flow control modes */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d6fc485fe8a1..946d06465eff 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -763,14 +763,14 @@ static int __hci_init(struct hci_dev *hdev)
{
int err;
- err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
if (hci_dev_test_flag(hdev, HCI_SETUP))
hci_debugfs_create_basic(hdev);
- err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -781,11 +781,11 @@ static int __hci_init(struct hci_dev *hdev)
if (hdev->dev_type != HCI_BREDR)
return 0;
- err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
- err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -841,7 +841,7 @@ static int __hci_unconf_init(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return 0;
- err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -1199,7 +1199,7 @@ int hci_inquiry(void __user *arg)
if (do_inquiry) {
err = hci_req_sync(hdev, hci_inq_req, (unsigned long) &ir,
- timeo);
+ timeo, NULL);
if (err < 0)
goto done;
@@ -1565,7 +1565,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks) &&
!auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
set_bit(HCI_INIT, &hdev->flags);
- __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
+ __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT, NULL);
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -1662,7 +1662,7 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
atomic_set(&hdev->cmd_cnt, 1);
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
- ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+ ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT, NULL);
hci_req_sync_unlock(hdev);
return ret;
@@ -1797,7 +1797,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
switch (cmd) {
case HCISETAUTH:
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETENCRYPT:
@@ -1809,18 +1809,18 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!test_bit(HCI_AUTH, &hdev->flags)) {
/* Auth must be enabled first */
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
if (err)
break;
}
err = hci_req_sync(hdev, hci_encrypt_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETSCAN:
err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
/* Ensure that the connectable and discoverable states
* get correctly modified as this was a non-mgmt change.
@@ -1831,7 +1831,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
case HCISETLINKPOL:
err = hci_req_sync(hdev, hci_linkpol_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETLINKMODE:
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 0adbb59ec2f0..b1d4d5bba7c1 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -186,7 +186,7 @@ EXPORT_SYMBOL(__hci_cmd_sync);
/* Execute request and wait for completion. */
int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
unsigned long opt),
- unsigned long opt, __u32 timeout)
+ unsigned long opt, u32 timeout, u8 *hci_status)
{
struct hci_request req;
DECLARE_WAITQUEUE(wait, current);
@@ -231,14 +231,20 @@ int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
switch (hdev->req_status) {
case HCI_REQ_DONE:
err = -bt_to_errno(hdev->req_result);
+ if (hci_status)
+ *hci_status = hdev->req_result;
break;
case HCI_REQ_CANCELED:
err = -hdev->req_result;
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
break;
default:
err = -ETIMEDOUT;
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
break;
}
@@ -251,7 +257,7 @@ int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
int hci_req_sync(struct hci_dev *hdev, void (*req)(struct hci_request *req,
unsigned long opt),
- unsigned long opt, __u32 timeout)
+ unsigned long opt, u32 timeout, u8 *hci_status)
{
int ret;
@@ -260,7 +266,7 @@ int hci_req_sync(struct hci_dev *hdev, void (*req)(struct hci_request *req,
/* Serialize all requests */
hci_req_sync_lock(hdev);
- ret = __hci_req_sync(hdev, req, opt, timeout);
+ ret = __hci_req_sync(hdev, req, opt, timeout, hci_status);
hci_req_sync_unlock(hdev);
return ret;
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 983e687fee22..8441d12a62dd 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -46,10 +46,10 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
int hci_req_sync(struct hci_dev *hdev, void (*req)(struct hci_request *req,
unsigned long opt),
- unsigned long opt, __u32 timeout);
+ unsigned long opt, u32 timeout, u8 *hci_status);
int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
unsigned long opt),
- unsigned long opt, __u32 timeout);
+ unsigned long opt, u32 timeout, u8 *hci_status);
void hci_req_sync_cancel(struct hci_dev *hdev, int err);
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 05/12] Bluetooth: Use req_workqueue for explicit connect requests
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (3 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 04/12] Bluetooth: Add HCI status return parameter to hci_req_sync() Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 06/12] Bluetooth: Use req_workqueue for background scanning when powering on Johan Hedberg
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
Since explicit connect requests are also a sub-category of passive
scan updates, run them through the same workqueue as the other passive
scan changes.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/hci_conn.c | 39 ++++-----------------------------------
net/bluetooth/hci_request.c | 15 ++++++++++++++-
2 files changed, 18 insertions(+), 36 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fd6120a41138..1ed1e153b3fa 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -933,26 +933,6 @@ done:
return conn;
}
-static void hci_connect_le_scan_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- struct hci_conn *conn;
-
- if (!status)
- return;
-
- BT_ERR("Failed to add device to auto conn whitelist: status 0x%2.2x",
- status);
-
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
- if (conn)
- hci_le_conn_failed(conn, status);
-
- hci_dev_unlock(hdev);
-}
-
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
@@ -968,10 +948,9 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
}
/* This function requires the caller holds hdev->lock */
-static int hci_explicit_conn_params_set(struct hci_request *req,
+static int hci_explicit_conn_params_set(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
if (is_connected(hdev, addr, addr_type))
@@ -999,7 +978,6 @@ static int hci_explicit_conn_params_set(struct hci_request *req,
}
params->explicit_connect = true;
- __hci_update_background_scan(req);
BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
params->auto_connect);
@@ -1013,8 +991,6 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u16 conn_timeout, u8 role)
{
struct hci_conn *conn;
- struct hci_request req;
- int err;
/* Let's make sure that le is enabled.*/
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
@@ -1046,25 +1022,18 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
if (!conn)
return ERR_PTR(-ENOMEM);
- hci_req_init(&req, hdev);
-
- if (hci_explicit_conn_params_set(&req, dst, dst_type) < 0)
+ if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0)
return ERR_PTR(-EBUSY);
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
-
- err = hci_req_run(&req, hci_connect_le_scan_complete);
- if (err && err != -ENODATA) {
- hci_conn_del(conn);
- return ERR_PTR(err);
- }
-
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
+ hci_update_background_scan(hdev);
+
done:
hci_conn_hold(conn);
return conn;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index b1d4d5bba7c1..c0ea310a116a 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -841,8 +841,21 @@ static void bg_scan_update(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
bg_scan_update);
+ struct hci_conn *conn;
+ u8 status;
+ int err;
+
+ err = hci_req_sync(hdev, update_bg_scan, 0, HCI_CMD_TIMEOUT, &status);
+ if (!err)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (conn)
+ hci_le_conn_failed(conn, status);
- hci_req_sync(hdev, update_bg_scan, 0, HCI_CMD_TIMEOUT);
+ hci_dev_unlock(hdev);
}
void hci_request_setup(struct hci_dev *hdev)
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 06/12] Bluetooth: Use req_workqueue for background scanning when powering on
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (4 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 05/12] Bluetooth: Use req_workqueue for explicit connect requests Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 07/12] Bluetooth: Make __hci_update_background_scan private to hci_request.c Johan Hedberg
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
We can easily use the new req_workqueue based background scan update
for the power on case. This also removes the last external user of
__hci_update_background_scan().
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/mgmt.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 27504949e995..bb870c3aadae 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -7465,9 +7465,8 @@ void mgmt_index_removed(struct hci_dev *hdev)
}
/* This function requires the caller holds hdev->lock */
-static void restart_le_actions(struct hci_request *req)
+static void restart_le_actions(struct hci_dev *hdev)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *p;
list_for_each_entry(p, &hdev->le_conn_params, list) {
@@ -7488,8 +7487,6 @@ static void restart_le_actions(struct hci_request *req)
break;
}
}
-
- __hci_update_background_scan(req);
}
static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
@@ -7505,6 +7502,9 @@ static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
* decide if the public address or static address is used.
*/
smp_register(hdev);
+
+ restart_le_actions(hdev);
+ hci_update_background_scan(hdev);
}
hci_dev_lock(hdev);
@@ -7583,8 +7583,6 @@ static int powered_update_hci(struct hci_dev *hdev)
hdev->cur_adv_instance)
schedule_adv_instance(&req, hdev->cur_adv_instance,
true);
-
- restart_le_actions(&req);
}
link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 07/12] Bluetooth: Make __hci_update_background_scan private to hci_request.c
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (5 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 06/12] Bluetooth: Use req_workqueue for background scanning when powering on Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 08/12] Bluetooth: Move LE scan disable/restart behind req_workqueue Johan Hedberg
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
There are no more external users so this API can be made private.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/hci_request.c | 2 +-
net/bluetooth/hci_request.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index c0ea310a116a..8aa06cc545c3 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -670,7 +670,7 @@ void hci_update_page_scan(struct hci_dev *hdev)
*
* This function requires the caller holds hdev->lock.
*/
-void __hci_update_background_scan(struct hci_request *req)
+static void __hci_update_background_scan(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 8441d12a62dd..1f1194628652 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -64,8 +64,6 @@ void __hci_update_page_scan(struct hci_request *req);
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type);
-void __hci_update_background_scan(struct hci_request *req);
-
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
u8 reason);
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 08/12] Bluetooth: Move LE scan disable/restart behind req_workqueue
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (6 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 07/12] Bluetooth: Make __hci_update_background_scan private to hci_request.c Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 09/12] Bluetooth: Add discovery type validity helper Johan Hedberg
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
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>
---
include/net/bluetooth/hci_core.h | 5 +-
net/bluetooth/hci_core.c | 168 ------------------------------------
net/bluetooth/hci_request.c | 179 +++++++++++++++++++++++++++++++++++++++
net/bluetooth/mgmt.c | 4 +-
4 files changed, 183 insertions(+), 173 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index c2ca6a58d1e0..1f75aebbd8c4 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -328,6 +328,8 @@ struct hci_dev {
struct work_struct tx_work;
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 raw_q;
@@ -372,9 +374,6 @@ struct hci_dev {
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
- struct delayed_work le_scan_disable;
- struct delayed_work le_scan_restart;
-
__s8 adv_tx_power;
__u8 adv_data[HCI_MAX_AD_LENGTH];
__u8 adv_data_len;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 946d06465eff..37c1714f9062 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1522,9 +1522,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (hci_dev_test_and_clear_flag(hdev, HCI_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))
cancel_delayed_work_sync(&hdev->rpa_expired);
@@ -2884,169 +2881,6 @@ static void hci_conn_params_clear_all(struct hci_dev *hdev)
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.
*
* If the controller has a public BD_ADDR, then by default use that one.
@@ -3146,8 +2980,6 @@ struct hci_dev *hci_alloc_dev(void)
INIT_DELAYED_WORK(&hdev->power_off, hci_power_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);
skb_queue_head_init(&hdev->rx_q);
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 8aa06cc545c3..4588fe2bfc0e 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -858,12 +858,191 @@ static void bg_scan_update(struct work_struct *work)
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)
{
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)
{
cancel_work_sync(&hdev->bg_scan_update);
+ cancel_delayed_work_sync(&hdev->le_scan_disable);
+ cancel_delayed_work_sync(&hdev->le_scan_restart);
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index bb870c3aadae..a229cfd0530e 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -4367,7 +4367,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
hdev->discovery.scan_duration = timeout;
}
- queue_delayed_work(hdev->workqueue,
+ queue_delayed_work(hdev->req_workqueue,
&hdev->le_scan_disable, timeout);
}
@@ -8389,7 +8389,7 @@ static void restart_le_scan(struct hci_dev *hdev)
hdev->discovery.scan_duration))
return;
- queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+ queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_restart,
DISCOV_LE_RESTART_DELAY);
}
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 09/12] Bluetooth: Add discovery type validity helper
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (7 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 08/12] Bluetooth: Move LE scan disable/restart behind req_workqueue Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 10/12] Bluetooth: Add error return value to hci_req_sync callback Johan Hedberg
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
As preparation for moving the discovery HCI commands behind
req_workqueue, add a helper and do the validity checks of the given
discovery type before proceeding further. This way we don't need to do
them again in hci_request.c.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/mgmt.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a229cfd0530e..e634b4d85249 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -4375,6 +4375,33 @@ unlock:
hci_dev_unlock(hdev);
}
+static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type,
+ uint8_t *mgmt_status)
+{
+ switch (type) {
+ case DISCOV_TYPE_LE:
+ *mgmt_status = mgmt_le_support(hdev);
+ if (*mgmt_status)
+ return false;
+ break;
+ case DISCOV_TYPE_INTERLEAVED:
+ *mgmt_status = mgmt_le_support(hdev);
+ if (*mgmt_status)
+ return false;
+ /* Intentional fall-through */
+ case DISCOV_TYPE_BREDR:
+ *mgmt_status = mgmt_bredr_support(hdev);
+ if (*mgmt_status)
+ return false;
+ break;
+ default:
+ *mgmt_status = MGMT_STATUS_INVALID_PARAMS;
+ return false;
+ }
+
+ return true;
+}
+
static int start_discovery(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
@@ -4403,6 +4430,12 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
goto failed;
}
+ if (!discovery_type_is_valid(hdev, cp->type, &status)) {
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
+ goto failed;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
@@ -4502,6 +4535,13 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
goto failed;
}
+ if (!discovery_type_is_valid(hdev, cp->type, &status)) {
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
+ goto failed;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
hdev, data, len);
if (!cmd) {
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 10/12] Bluetooth: Add error return value to hci_req_sync callback
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (8 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 09/12] Bluetooth: Add discovery type validity helper Johan Hedberg
@ 2015-11-11 6:11 ` Johan Hedberg
2015-11-11 6:14 ` [PATCH v2 12/12] Bluetooth: Move Stop Discovery to req_workqueue Johan Hedberg
2015-11-11 6:26 ` [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Marcel Holtmann
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:11 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
In some circumstances it may be useful to abort the request through
checks done in the request callback. To make the feature possible this
patch changes the return value of the request callback from void to
int and aborts the request if a non-zero value is returned.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/hci_core.c | 45 ++++++++++++++++++++++++++++++++-------------
net/bluetooth/hci_request.c | 27 ++++++++++++++++++---------
net/bluetooth/hci_request.h | 8 ++++----
3 files changed, 54 insertions(+), 26 deletions(-)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 37c1714f9062..bca09e86481f 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -189,13 +189,14 @@ static void hci_debugfs_create_basic(struct hci_dev *hdev)
&vendor_diag_fops);
}
-static void hci_reset_req(struct hci_request *req, unsigned long opt)
+static int hci_reset_req(struct hci_request *req, unsigned long opt)
{
BT_DBG("%s %ld", req->hdev->name, opt);
/* Reset device */
set_bit(HCI_RESET, &req->hdev->flags);
hci_req_add(req, HCI_OP_RESET, 0, NULL);
+ return 0;
}
static void bredr_init(struct hci_request *req)
@@ -235,7 +236,7 @@ static void amp_init1(struct hci_request *req)
hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
}
-static void amp_init2(struct hci_request *req)
+static int amp_init2(struct hci_request *req)
{
/* Read Local Supported Features. Not all AMP controllers
* support this so it's placed conditionally in the second
@@ -243,9 +244,11 @@ static void amp_init2(struct hci_request *req)
*/
if (req->hdev->commands[14] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+
+ return 0;
}
-static void hci_init1_req(struct hci_request *req, unsigned long opt)
+static int hci_init1_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -268,6 +271,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
BT_ERR("Unknown device type %d", hdev->dev_type);
break;
}
+
+ return 0;
}
static void bredr_setup(struct hci_request *req)
@@ -422,7 +427,7 @@ static void hci_setup_event_mask(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
}
-static void hci_init2_req(struct hci_request *req, unsigned long opt)
+static int hci_init2_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -502,6 +507,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
&enable);
}
+
+ return 0;
}
static void hci_setup_link_policy(struct hci_request *req)
@@ -576,7 +583,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
}
-static void hci_init3_req(struct hci_request *req, unsigned long opt)
+static int hci_init3_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
u8 p;
@@ -704,9 +711,11 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_READ_LOCAL_EXT_FEATURES,
sizeof(cp), &cp);
}
+
+ return 0;
}
-static void hci_init4_req(struct hci_request *req, unsigned long opt)
+static int hci_init4_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -757,6 +766,8 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
sizeof(support), &support);
}
+
+ return 0;
}
static int __hci_init(struct hci_dev *hdev)
@@ -816,7 +827,7 @@ static int __hci_init(struct hci_dev *hdev)
return 0;
}
-static void hci_init0_req(struct hci_request *req, unsigned long opt)
+static int hci_init0_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -832,6 +843,8 @@ static void hci_init0_req(struct hci_request *req, unsigned long opt)
/* Read BD Address */
if (hdev->set_bdaddr)
hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
+
+ return 0;
}
static int __hci_unconf_init(struct hci_dev *hdev)
@@ -851,7 +864,7 @@ static int __hci_unconf_init(struct hci_dev *hdev)
return 0;
}
-static void hci_scan_req(struct hci_request *req, unsigned long opt)
+static int hci_scan_req(struct hci_request *req, unsigned long opt)
{
__u8 scan = opt;
@@ -859,9 +872,10 @@ static void hci_scan_req(struct hci_request *req, unsigned long opt)
/* Inquiry and Page scans */
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ return 0;
}
-static void hci_auth_req(struct hci_request *req, unsigned long opt)
+static int hci_auth_req(struct hci_request *req, unsigned long opt)
{
__u8 auth = opt;
@@ -869,9 +883,10 @@ static void hci_auth_req(struct hci_request *req, unsigned long opt)
/* Authentication */
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth);
+ return 0;
}
-static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
+static int hci_encrypt_req(struct hci_request *req, unsigned long opt)
{
__u8 encrypt = opt;
@@ -879,9 +894,10 @@ static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
/* Encryption */
hci_req_add(req, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
+ return 0;
}
-static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
+static int hci_linkpol_req(struct hci_request *req, unsigned long opt)
{
__le16 policy = cpu_to_le16(opt);
@@ -889,6 +905,7 @@ static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
/* Default link policy */
hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
+ return 0;
}
/* Get HCI device by index.
@@ -1133,7 +1150,7 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
return copied;
}
-static void hci_inq_req(struct hci_request *req, unsigned long opt)
+static int hci_inq_req(struct hci_request *req, unsigned long opt)
{
struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
struct hci_dev *hdev = req->hdev;
@@ -1142,13 +1159,15 @@ static void hci_inq_req(struct hci_request *req, unsigned long opt)
BT_DBG("%s", hdev->name);
if (test_bit(HCI_INQUIRY, &hdev->flags))
- return;
+ return 0;
/* Start Inquiry */
memcpy(&cp.lap, &ir->lap, 3);
cp.length = ir->length;
cp.num_rsp = ir->num_rsp;
hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+ return 0;
}
int hci_inquiry(void __user *arg)
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 4588fe2bfc0e..ecfa4105e00d 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -184,8 +184,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
EXPORT_SYMBOL(__hci_cmd_sync);
/* Execute request and wait for completion. */
-int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
- unsigned long opt),
+int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
+ unsigned long opt),
unsigned long opt, u32 timeout, u8 *hci_status)
{
struct hci_request req;
@@ -198,7 +198,12 @@ int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
hdev->req_status = HCI_REQ_PEND;
- func(&req, opt);
+ err = func(&req, opt);
+ if (err) {
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ return err;
+ }
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
@@ -255,8 +260,8 @@ int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
return err;
}
-int hci_req_sync(struct hci_dev *hdev, void (*req)(struct hci_request *req,
- unsigned long opt),
+int hci_req_sync(struct hci_dev *hdev, int (*req)(struct hci_request *req,
+ unsigned long opt),
unsigned long opt, u32 timeout, u8 *hci_status)
{
int ret;
@@ -830,11 +835,12 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
return 0;
}
-static void update_bg_scan(struct hci_request *req, unsigned long opt)
+static int update_bg_scan(struct hci_request *req, unsigned long opt)
{
hci_dev_lock(req->hdev);
__hci_update_background_scan(req);
hci_dev_unlock(req->hdev);
+ return 0;
}
static void bg_scan_update(struct work_struct *work)
@@ -932,9 +938,10 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status)
}
}
-static void le_scan_disable(struct hci_request *req, unsigned long opt)
+static int le_scan_disable(struct hci_request *req, unsigned long opt)
{
hci_req_add_le_scan_disable(req);
+ return 0;
}
static void le_scan_disable_work(struct work_struct *work)
@@ -1000,14 +1007,14 @@ unlock:
hci_dev_unlock(hdev);
}
-static void le_scan_restart(struct hci_request *req, unsigned long opt)
+static int 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;
+ return 0;
hci_req_add_le_scan_disable(req);
@@ -1015,6 +1022,8 @@ static void le_scan_restart(struct hci_request *req, unsigned long opt)
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);
+
+ return 0;
}
static void le_scan_restart_work(struct work_struct *work)
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 1f1194628652..1927013f5e67 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -44,11 +44,11 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
hci_req_complete_t *req_complete,
hci_req_complete_skb_t *req_complete_skb);
-int hci_req_sync(struct hci_dev *hdev, void (*req)(struct hci_request *req,
- unsigned long opt),
+int hci_req_sync(struct hci_dev *hdev, int (*req)(struct hci_request *req,
+ unsigned long opt),
unsigned long opt, u32 timeout, u8 *hci_status);
-int __hci_req_sync(struct hci_dev *hdev, void (*func)(struct hci_request *req,
- unsigned long opt),
+int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
+ unsigned long opt),
unsigned long opt, u32 timeout, u8 *hci_status);
void hci_req_sync_cancel(struct hci_dev *hdev, int err);
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 12/12] Bluetooth: Move Stop Discovery to req_workqueue
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (9 preceding siblings ...)
2015-11-11 6:11 ` [PATCH v2 10/12] Bluetooth: Add error return value to hci_req_sync callback Johan Hedberg
@ 2015-11-11 6:14 ` Johan Hedberg
2015-11-11 6:26 ` [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Marcel Holtmann
11 siblings, 0 replies; 13+ messages in thread
From: Johan Hedberg @ 2015-11-11 6:14 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
Since discovery also deals with LE scanning it makes sense to move it
behind the same req_workqueue as other LE scanning changes. This also
simplifies the logic since we do many of the actions in a synchronous
manner.
Part of this refactoring is moving hci_req_stop_discovery() to
hci_request.c. At the same time the function receives support for
properly handling the STOPPING state since that's the state we'll be
in when stopping through the req_workqueue.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_request.c | 62 ++++++++++++++++++++++++++++++++++
net/bluetooth/hci_request.h | 3 ++
net/bluetooth/mgmt.c | 72 +++-------------------------------------
4 files changed, 71 insertions(+), 67 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 72ea8a6d7d70..609f4a03899c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1475,6 +1475,7 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status);
+void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 13b5fb773ef5..cdf1c0fd5985 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -1222,6 +1222,62 @@ static void start_discovery(struct hci_dev *hdev, u8 *status)
timeout);
}
+bool hci_req_stop_discovery(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct discovery_state *d = &hdev->discovery;
+ struct hci_cp_remote_name_req_cancel cp;
+ struct inquiry_entry *e;
+ bool ret = false;
+
+ BT_DBG("%s state %u", hdev->name, hdev->discovery.state);
+
+ if (d->state == DISCOVERY_FINDING || d->state == DISCOVERY_STOPPING) {
+ if (test_bit(HCI_INQUIRY, &hdev->flags))
+ hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+ cancel_delayed_work(&hdev->le_scan_disable);
+ hci_req_add_le_scan_disable(req);
+ }
+
+ ret = true;
+ } else {
+ /* Passive scanning */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+ hci_req_add_le_scan_disable(req);
+ ret = true;
+ }
+ }
+
+ /* No further actions needed for LE-only discovery */
+ if (d->type == DISCOV_TYPE_LE)
+ return ret;
+
+ if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) {
+ e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
+ NAME_PENDING);
+ if (!e)
+ return ret;
+
+ bacpy(&cp.bdaddr, &e->data.bdaddr);
+ hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
+ &cp);
+ ret = true;
+ }
+
+ return ret;
+}
+
+static int stop_discovery(struct hci_request *req, unsigned long opt)
+{
+ hci_dev_lock(req->hdev);
+ hci_req_stop_discovery(req);
+ hci_dev_unlock(req->hdev);
+
+ return 0;
+}
+
static void discov_update(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
@@ -1237,6 +1293,12 @@ static void discov_update(struct work_struct *work)
else
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
break;
+ case DISCOVERY_STOPPING:
+ hci_req_sync(hdev, stop_discovery, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_stop_discovery_complete(hdev, status);
+ if (!status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ break;
case DISCOVERY_STOPPED:
default:
return;
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 1927013f5e67..6b9e59f7f7a9 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -58,6 +58,9 @@ struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
void hci_req_add_le_scan_disable(struct hci_request *req);
void hci_req_add_le_passive_scan(struct hci_request *req);
+/* Returns true if HCI commands were queued */
+bool hci_req_stop_discovery(struct hci_request *req);
+
void hci_update_page_scan(struct hci_dev *hdev);
void __hci_update_page_scan(struct hci_request *req);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 63b099471c92..8cdacef6b108 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1416,49 +1416,6 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode)
}
}
-static bool hci_stop_discovery(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_remote_name_req_cancel cp;
- struct inquiry_entry *e;
-
- switch (hdev->discovery.state) {
- case DISCOVERY_FINDING:
- if (test_bit(HCI_INQUIRY, &hdev->flags))
- hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
-
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- cancel_delayed_work(&hdev->le_scan_disable);
- hci_req_add_le_scan_disable(req);
- }
-
- return true;
-
- case DISCOVERY_RESOLVING:
- e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
- NAME_PENDING);
- if (!e)
- break;
-
- bacpy(&cp.bdaddr, &e->data.bdaddr);
- hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
- &cp);
-
- return true;
-
- default:
- /* Passive scanning */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- hci_req_add_le_scan_disable(req);
- return true;
- }
-
- break;
- }
-
- return false;
-}
-
static void advertising_added(struct sock *sk, struct hci_dev *hdev,
u8 instance)
{
@@ -1636,7 +1593,7 @@ static int clean_up_hci_state(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
disable_advertising(&req);
- discov_stopped = hci_stop_discovery(&req);
+ discov_stopped = hci_req_stop_discovery(&req);
list_for_each_entry(conn, &hdev->conn_hash.list, list) {
/* 0x15 == Terminated due to Power Off */
@@ -4377,7 +4334,7 @@ failed:
return err;
}
-static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
@@ -4391,9 +4348,6 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
mgmt_pending_remove(cmd);
}
- if (!status)
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-
hci_dev_unlock(hdev);
}
@@ -4402,7 +4356,6 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_stop_discovery *mgmt_cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
@@ -4431,24 +4384,9 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
cmd->cmd_complete = generic_cmd_complete;
- hci_req_init(&req, hdev);
-
- hci_stop_discovery(&req);
-
- err = hci_req_run(&req, stop_discovery_complete);
- if (!err) {
- hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
- goto unlock;
- }
-
- mgmt_pending_remove(cmd);
-
- /* If no HCI commands were sent we're done */
- if (err == -ENODATA) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
- &mgmt_cp->type, sizeof(mgmt_cp->type));
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- }
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
unlock:
hci_dev_unlock(hdev);
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
` (10 preceding siblings ...)
2015-11-11 6:14 ` [PATCH v2 12/12] Bluetooth: Move Stop Discovery to req_workqueue Johan Hedberg
@ 2015-11-11 6:26 ` Marcel Holtmann
11 siblings, 0 replies; 13+ messages in thread
From: Marcel Holtmann @ 2015-11-11 6:26 UTC (permalink / raw)
To: Johan Hedberg; +Cc: linux-bluetooth
Hi Johan,
> v2: updated version with feedback for 01/12 and 02/12 taken into
> account.
>
> From the original cover letter:
>
> The current bluetooth-next tree is not able to reliably and in a
> race-free manner handle updates to the LE scan state. This results in
> the kernel ending up out of sync with the actual LE scan state and
> starts sending incorrect scan state HCI commands, often resulting in
> "not allowed" HCI responses.
>
> This is an initial set of patches to try solve the issue. I've tried to
> retain as much as possible of the original logic so that it's easier to
> see that nothing should get broken. Because of this, there are still
> many simplifications and cleanups that can be done. However, since I've
> already got 12 patches lined up I wanted to get them sent, reviewed and
> merged. After that I can concentrate on the cleanups and I'll also be
> looking into moving the adverising state changes to hci_request.c as
> well.
>
> The general solution that this set takes to solve the synchronization
> issues with the LE scan state, is to move all of it behind the
> hdev->req_workqueue and perform the actions synchronously. The main tool
> I've used for this is the hci_req_sync() API which allows taking
> existing asynchronous HCI request helpers and using them to perform the
> same commands synchronously (by passing the helper as the callback to
> hci_req_sync).
>
> Since only one work callback is active at a time we get an easy way to
> avoid races between the changes. All of the new synchronous code can be
> found in hci_request.c which seemed like an appropriate place for it.
>
> I've done a fair amount of testing with the patches, including
> mgmt-tester, l2cap-tester and various bluetoothd tests (both with
> controllers supporting simultaneous Inquiry+LE scan as well as those not
> supporting it). However, because of the size of the changes it's
> perfectly possible that I've missed or broken something, so it'd be good
> if others could give the set a spin before merging it.
>
> Johan
>
> ----------------------------------------------------------------
> Johan Hedberg (12):
> Bluetooth: Add stubs for synchronous HCI request functionality
> Bluetooth: Run all background scan updates through req_workqueue
> Bluetooth: Don't wait for HCI in Add/Remove Device
> Bluetooth: Add HCI status return parameter to hci_req_sync()
> Bluetooth: Use req_workqueue for explicit connect requests
> Bluetooth: Use req_workqueue for background scanning when powering on
> Bluetooth: Make __hci_update_background_scan private to hci_request.c
> Bluetooth: Move LE scan disable/restart behind req_workqueue
> Bluetooth: Add discovery type validity helper
> Bluetooth: Add error return value to hci_req_sync callback
> Bluetooth: Move Start Discovery to req_workqueue
> Bluetooth: Move Stop Discovery to req_workqueue
>
> include/net/bluetooth/hci.h | 3 +-
> include/net/bluetooth/hci_core.h | 10 +-
> net/bluetooth/hci_conn.c | 39 +--
> net/bluetooth/hci_core.c | 243 ++++-------------
> net/bluetooth/hci_request.c | 537 +++++++++++++++++++++++++++++++++++---
> net/bluetooth/hci_request.h | 26 +-
> net/bluetooth/mgmt.c | 503 +++++++----------------------------
> 7 files changed, 684 insertions(+), 677 deletions(-)
patches 1-10 have been applied to bluetooth-next tree.
Regards
Marcel
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2015-11-11 6:26 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-11 6:11 [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 01/12] Bluetooth: Add stubs for synchronous HCI request functionality Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 02/12] Bluetooth: Run all background scan updates through req_workqueue Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 03/12] Bluetooth: Don't wait for HCI in Add/Remove Device Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 04/12] Bluetooth: Add HCI status return parameter to hci_req_sync() Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 05/12] Bluetooth: Use req_workqueue for explicit connect requests Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 06/12] Bluetooth: Use req_workqueue for background scanning when powering on Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 07/12] Bluetooth: Make __hci_update_background_scan private to hci_request.c Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 08/12] Bluetooth: Move LE scan disable/restart behind req_workqueue Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 09/12] Bluetooth: Add discovery type validity helper Johan Hedberg
2015-11-11 6:11 ` [PATCH v2 10/12] Bluetooth: Add error return value to hci_req_sync callback Johan Hedberg
2015-11-11 6:14 ` [PATCH v2 12/12] Bluetooth: Move Stop Discovery to req_workqueue Johan Hedberg
2015-11-11 6:26 ` [PATCH v2 00/12] Bluetooth: Move LE scan changes behind req_workqueue Marcel Holtmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).