Commit cf75ad8b authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Marcel Holtmann

Bluetooth: hci_sync: Convert MGMT_SET_POWERED

This make use of hci_cmd_sync_queue when MGMT_SET_POWERED is used so all
commands are run within hdev->cmd_sync_work instead of
hdev->power_on_work and hdev->power_off_work.

In addition to that the power on sequence now takes into account if
local IRK needs to be programmed in the resolving list.

Tested with:

tools/mgmt-tester -s "Set powered"

Test Summary
------------
Set powered on - Success                             Passed
Set powered on - Invalid parameters 1                Passed
Set powered on - Invalid parameters 2                Passed
Set powered on - Invalid parameters 3                Passed
Set powered on - Invalid index                       Passed
Set powered on - Privacy and Advertising             Passed
Set powered off - Success                            Passed
Set powered off - Class of Device                    Passed
Set powered off - Invalid parameters 1               Passed
Set powered off - Invalid parameters 2               Passed
Set powered off - Invalid parameters 3               Passed
Total: 11, Passed: 11 (100.0%), Failed: 0, Not Run: 0
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 5bee2fd6
...@@ -66,3 +66,9 @@ int hci_disable_advertising_sync(struct hci_dev *hdev); ...@@ -66,3 +66,9 @@ int hci_disable_advertising_sync(struct hci_dev *hdev);
int hci_update_passive_scan_sync(struct hci_dev *hdev); int hci_update_passive_scan_sync(struct hci_dev *hdev);
int hci_update_passive_scan(struct hci_dev *hdev); int hci_update_passive_scan(struct hci_dev *hdev);
int hci_dev_open_sync(struct hci_dev *hdev);
int hci_dev_close_sync(struct hci_dev *hdev);
int hci_powered_update_sync(struct hci_dev *hdev);
int hci_set_powered_sync(struct hci_dev *hdev, u8 val);
...@@ -1315,14 +1315,13 @@ static void hci_dev_get_bd_addr_from_property(struct hci_dev *hdev) ...@@ -1315,14 +1315,13 @@ static void hci_dev_get_bd_addr_from_property(struct hci_dev *hdev)
bacpy(&hdev->public_addr, &ba); bacpy(&hdev->public_addr, &ba);
} }
static int hci_dev_do_open(struct hci_dev *hdev) /* TODO: Move this function into hci_sync.c */
int hci_dev_open_sync(struct hci_dev *hdev)
{ {
int ret = 0; int ret = 0;
BT_DBG("%s %p", hdev->name, hdev); BT_DBG("%s %p", hdev->name, hdev);
hci_req_sync_lock(hdev);
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) { if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
ret = -ENODEV; ret = -ENODEV;
goto done; goto done;
...@@ -1489,8 +1488,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) ...@@ -1489,8 +1488,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
hci_dev_test_flag(hdev, HCI_MGMT) && hci_dev_test_flag(hdev, HCI_MGMT) &&
hdev->dev_type == HCI_PRIMARY) { hdev->dev_type == HCI_PRIMARY) {
ret = __hci_req_hci_power_on(hdev); ret = hci_powered_update_sync(hdev);
mgmt_power_on(hdev, ret);
} }
} else { } else {
/* Init failed, cleanup */ /* Init failed, cleanup */
...@@ -1522,6 +1520,19 @@ static int hci_dev_do_open(struct hci_dev *hdev) ...@@ -1522,6 +1520,19 @@ static int hci_dev_do_open(struct hci_dev *hdev)
} }
done: done:
return ret;
}
static int hci_dev_do_open(struct hci_dev *hdev)
{
int ret = 0;
BT_DBG("%s %p", hdev->name, hdev);
hci_req_sync_lock(hdev);
ret = hci_dev_open_sync(hdev);
hci_req_sync_unlock(hdev); hci_req_sync_unlock(hdev);
return ret; return ret;
} }
...@@ -1600,7 +1611,8 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev) ...@@ -1600,7 +1611,8 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
BT_DBG("All LE pending actions cleared"); BT_DBG("All LE pending actions cleared");
} }
int hci_dev_do_close(struct hci_dev *hdev) /* TODO: Move this function into hci_sync.c */
int hci_dev_close_sync(struct hci_dev *hdev)
{ {
bool auto_off; bool auto_off;
int err = 0; int err = 0;
...@@ -1611,7 +1623,6 @@ int hci_dev_do_close(struct hci_dev *hdev) ...@@ -1611,7 +1623,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
cancel_delayed_work(&hdev->ncmd_timer); cancel_delayed_work(&hdev->ncmd_timer);
hci_request_cancel_all(hdev); hci_request_cancel_all(hdev);
hci_req_sync_lock(hdev);
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) && if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
...@@ -1623,7 +1634,6 @@ int hci_dev_do_close(struct hci_dev *hdev) ...@@ -1623,7 +1634,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
cancel_delayed_work_sync(&hdev->cmd_timer); cancel_delayed_work_sync(&hdev->cmd_timer);
hci_req_sync_unlock(hdev);
return err; return err;
} }
...@@ -1729,9 +1739,22 @@ int hci_dev_do_close(struct hci_dev *hdev) ...@@ -1729,9 +1739,22 @@ int hci_dev_do_close(struct hci_dev *hdev)
bacpy(&hdev->random_addr, BDADDR_ANY); bacpy(&hdev->random_addr, BDADDR_ANY);
hci_codec_list_clear(&hdev->local_codecs); hci_codec_list_clear(&hdev->local_codecs);
hci_dev_put(hdev);
return err;
}
int hci_dev_do_close(struct hci_dev *hdev)
{
int err;
BT_DBG("%s %p", hdev->name, hdev);
hci_req_sync_lock(hdev);
err = hci_dev_close_sync(hdev);
hci_req_sync_unlock(hdev); hci_req_sync_unlock(hdev);
hci_dev_put(hdev);
return err; return err;
} }
...@@ -2133,9 +2156,7 @@ static void hci_power_on(struct work_struct *work) ...@@ -2133,9 +2156,7 @@ static void hci_power_on(struct work_struct *work)
hci_dev_test_flag(hdev, HCI_MGMT) && hci_dev_test_flag(hdev, HCI_MGMT) &&
hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
cancel_delayed_work(&hdev->power_off); cancel_delayed_work(&hdev->power_off);
hci_req_sync_lock(hdev); err = hci_powered_update_sync(hdev);
err = __hci_req_hci_power_on(hdev);
hci_req_sync_unlock(hdev);
mgmt_power_on(hdev, err); mgmt_power_on(hdev, err);
return; return;
} }
......
...@@ -1794,7 +1794,8 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) ...@@ -1794,7 +1794,8 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(cp), &cp); hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(cp), &cp);
if (own_addr_type == ADDR_LE_DEV_RANDOM && if ((own_addr_type == ADDR_LE_DEV_RANDOM ||
own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) &&
bacmp(&random_addr, BDADDR_ANY)) { bacmp(&random_addr, BDADDR_ANY)) {
struct hci_cp_le_set_adv_set_rand_addr cp; struct hci_cp_le_set_adv_set_rand_addr cp;
......
This diff is collapsed.
...@@ -1155,16 +1155,6 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) ...@@ -1155,16 +1155,6 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
sizeof(settings)); sizeof(settings));
} }
static void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode)
{
bt_dev_dbg(hdev, "status 0x%02x", status);
if (hci_conn_count(hdev) == 0) {
cancel_delayed_work(&hdev->power_off);
queue_work(hdev->req_workqueue, &hdev->power_off.work);
}
}
void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, u8 instance) void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, u8 instance)
{ {
struct mgmt_ev_advertising_added ev; struct mgmt_ev_advertising_added ev;
...@@ -1192,38 +1182,77 @@ static void cancel_adv_timeout(struct hci_dev *hdev) ...@@ -1192,38 +1182,77 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
} }
} }
static int clean_up_hci_state(struct hci_dev *hdev) /* This function requires the caller holds hdev->lock */
static void restart_le_actions(struct hci_dev *hdev)
{ {
struct hci_request req; struct hci_conn_params *p;
struct hci_conn *conn;
bool discov_stopped;
int err;
hci_req_init(&req, hdev); list_for_each_entry(p, &hdev->le_conn_params, list) {
/* Needed for AUTO_OFF case where might not "really"
* have been powered off.
*/
list_del_init(&p->action);
if (test_bit(HCI_ISCAN, &hdev->flags) || switch (p->auto_connect) {
test_bit(HCI_PSCAN, &hdev->flags)) { case HCI_AUTO_CONN_DIRECT:
u8 scan = 0x00; case HCI_AUTO_CONN_ALWAYS:
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); list_add(&p->action, &hdev->pend_le_conns);
break;
case HCI_AUTO_CONN_REPORT:
list_add(&p->action, &hdev->pend_le_reports);
break;
default:
break;
}
} }
}
hci_req_clear_adv_instance(hdev, NULL, NULL, 0x00, false); static int new_settings(struct hci_dev *hdev, struct sock *skip)
{
__le32 ev = cpu_to_le32(get_current_settings(hdev));
if (hci_dev_test_flag(hdev, HCI_LE_ADV)) return mgmt_limited_event(MGMT_EV_NEW_SETTINGS, hdev, &ev,
__hci_req_disable_advertising(&req); sizeof(ev), HCI_MGMT_SETTING_EVENTS, skip);
}
static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
{
struct mgmt_pending_cmd *cmd = data;
struct mgmt_mode *cp = cmd->param;
discov_stopped = hci_req_stop_discovery(&req); bt_dev_dbg(hdev, "err %d", err);
list_for_each_entry(conn, &hdev->conn_hash.list, list) { if (!err) {
/* 0x15 == Terminated due to Power Off */ if (cp->val) {
__hci_abort_conn(&req, conn, 0x15); hci_dev_lock(hdev);
restart_le_actions(hdev);
hci_update_passive_scan(hdev);
hci_dev_unlock(hdev);
} }
err = hci_req_run(&req, clean_up_hci_complete); send_settings_rsp(cmd->sk, cmd->opcode, hdev);
if (!err && discov_stopped)
hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
return err; /* Only call new_setting for power on as power off is deferred
* to hdev->power_off work which does call hci_dev_do_close.
*/
if (cp->val)
new_settings(hdev, cmd->sk);
} else {
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED,
mgmt_status(err));
}
mgmt_pending_free(cmd);
}
static int set_powered_sync(struct hci_dev *hdev, void *data)
{
struct mgmt_pending_cmd *cmd = data;
struct mgmt_mode *cp = cmd->param;
BT_DBG("%s", hdev->name);
return hci_set_powered_sync(hdev, cp->val);
} }
static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
...@@ -1252,43 +1281,20 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -1252,43 +1281,20 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed; goto failed;
} }
cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); cmd = mgmt_pending_new(sk, MGMT_OP_SET_POWERED, hdev, data, len);
if (!cmd) { if (!cmd) {
err = -ENOMEM; err = -ENOMEM;
goto failed; goto failed;
} }
if (cp->val) { err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd,
queue_work(hdev->req_workqueue, &hdev->power_on); mgmt_set_powered_complete);
err = 0;
} else {
/* Disconnect connections, stop scans, etc */
err = clean_up_hci_state(hdev);
if (!err)
queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
HCI_POWER_OFF_TIMEOUT);
/* ENODATA means there were no HCI commands queued */
if (err == -ENODATA) {
cancel_delayed_work(&hdev->power_off);
queue_work(hdev->req_workqueue, &hdev->power_off.work);
err = 0;
}
}
failed: failed:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
return err; return err;
} }
static int new_settings(struct hci_dev *hdev, struct sock *skip)
{
__le32 ev = cpu_to_le32(get_current_settings(hdev));
return mgmt_limited_event(MGMT_EV_NEW_SETTINGS, hdev, &ev,
sizeof(ev), HCI_MGMT_SETTING_EVENTS, skip);
}
int mgmt_new_settings(struct hci_dev *hdev) int mgmt_new_settings(struct hci_dev *hdev)
{ {
return new_settings(hdev, NULL); return new_settings(hdev, NULL);
...@@ -8720,31 +8726,6 @@ void mgmt_index_removed(struct hci_dev *hdev) ...@@ -8720,31 +8726,6 @@ void mgmt_index_removed(struct hci_dev *hdev)
HCI_MGMT_EXT_INDEX_EVENTS); HCI_MGMT_EXT_INDEX_EVENTS);
} }
/* This function requires the caller holds hdev->lock */
static void restart_le_actions(struct hci_dev *hdev)
{
struct hci_conn_params *p;
list_for_each_entry(p, &hdev->le_conn_params, list) {
/* Needed for AUTO_OFF case where might not "really"
* have been powered off.
*/
list_del_init(&p->action);
switch (p->auto_connect) {
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
list_add(&p->action, &hdev->pend_le_conns);
break;
case HCI_AUTO_CONN_REPORT:
list_add(&p->action, &hdev->pend_le_reports);
break;
default:
break;
}
}
}
void mgmt_power_on(struct hci_dev *hdev, int err) void mgmt_power_on(struct hci_dev *hdev, int err)
{ {
struct cmd_lookup match = { NULL, hdev }; struct cmd_lookup match = { NULL, hdev };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment