summaryrefslogtreecommitdiffstats
path: root/net/bluetooth/mgmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r--net/bluetooth/mgmt.c637
1 files changed, 457 insertions, 180 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 861e389f4b4c..074d83690a41 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -536,6 +536,156 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
return ptr;
}
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+
+ list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+ if (cmd->opcode == opcode)
+ return cmd;
+ }
+
+ return NULL;
+}
+
+static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+ if (name_len > 0) {
+ size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+ if (name_len > max_len) {
+ name_len = max_len;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ad_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ return ad_len;
+}
+
+static void update_scan_rsp_data(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_rsp_data cp;
+ u8 len;
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_scan_rsp_data(hdev, cp.data);
+
+ if (hdev->scan_rsp_data_len == len &&
+ memcmp(cp.data, hdev->scan_rsp_data, len) == 0)
+ return;
+
+ memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+ hdev->scan_rsp_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 get_adv_discov_flags(struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+
+ /* If there's a pending mgmt command the flags will not yet have
+ * their final values, so check for this first.
+ */
+ cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+ if (cmd) {
+ struct mgmt_mode *cp = cmd->param;
+ if (cp->val == 0x01)
+ return LE_AD_GENERAL;
+ else if (cp->val == 0x02)
+ return LE_AD_LIMITED;
+ } else {
+ if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+ return LE_AD_LIMITED;
+ else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ return LE_AD_GENERAL;
+ }
+
+ return 0;
+}
+
+static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0, flags = 0;
+
+ flags |= get_adv_discov_flags(hdev);
+
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ if (lmp_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_CTRL;
+ if (lmp_host_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_HOST;
+ } else {
+ flags |= LE_AD_NO_BREDR;
+ }
+
+ if (flags) {
+ BT_DBG("adv flags 0x%02x", flags);
+
+ ptr[0] = 2;
+ ptr[1] = EIR_FLAGS;
+ ptr[2] = flags;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+ ptr[0] = 2;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8) hdev->adv_tx_power;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ return ad_len;
+}
+
+static void update_adv_data(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_data cp;
+ u8 len;
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_adv_data(hdev, cp.data);
+
+ if (hdev->adv_data_len == len &&
+ memcmp(cp.data, hdev->adv_data, len) == 0)
+ return;
+
+ memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+ hdev->adv_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
static void create_eir(struct hci_dev *hdev, u8 *data)
{
u8 *ptr = data;
@@ -634,6 +784,9 @@ static void update_class(struct hci_request *req)
if (!hdev_is_powered(hdev))
return;
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return;
+
if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return;
@@ -641,6 +794,9 @@ static void update_class(struct hci_request *req)
cod[1] = hdev->major_class;
cod[2] = get_service_classes(hdev);
+ if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+ cod[1] |= 0x20;
+
if (memcmp(cod, hdev->dev_class, 3) == 0)
return;
@@ -765,18 +921,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
}
}
-static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
-{
- struct pending_cmd *cmd;
-
- list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
- if (cmd->opcode == opcode)
- return cmd;
- }
-
- return NULL;
-}
-
static void mgmt_pending_remove(struct pending_cmd *cmd)
{
list_del(&cmd->list);
@@ -939,6 +1083,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
struct mgmt_mode *cp;
+ struct hci_request req;
bool changed;
BT_DBG("status 0x%02x", status);
@@ -952,22 +1097,38 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
if (status) {
u8 mgmt_err = mgmt_status(status);
cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
goto remove_cmd;
}
cp = cmd->param;
- if (cp->val)
+ if (cp->val) {
changed = !test_and_set_bit(HCI_DISCOVERABLE,
&hdev->dev_flags);
- else
+
+ if (hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+ queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+ to);
+ }
+ } else {
changed = test_and_clear_bit(HCI_DISCOVERABLE,
&hdev->dev_flags);
+ }
send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
if (changed)
new_settings(hdev, cmd->sk);
+ /* When the discoverable mode gets changed, make sure
+ * that class of device has the limited discoverable
+ * bit correctly set.
+ */
+ hci_req_init(&req, hdev);
+ update_class(&req);
+ hci_req_run(&req, NULL);
+
remove_cmd:
mgmt_pending_remove(cmd);
@@ -982,22 +1143,27 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
struct pending_cmd *cmd;
struct hci_request req;
u16 timeout;
- u8 scan, status;
+ u8 scan;
int err;
BT_DBG("request for %s", hdev->name);
- status = mgmt_bredr_support(hdev);
- if (status)
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
- status);
+ MGMT_STATUS_REJECTED);
- if (cp->val != 0x00 && cp->val != 0x01)
+ if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_INVALID_PARAMS);
timeout = __le16_to_cpu(cp->timeout);
- if (!cp->val && timeout > 0)
+
+ /* Disabling discoverable requires that no timeout is set,
+ * and enabling limited discoverable requires a timeout.
+ */
+ if ((cp->val == 0x00 && timeout > 0) ||
+ (cp->val == 0x02 && timeout == 0))
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_INVALID_PARAMS);
@@ -1025,6 +1191,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
if (!hdev_is_powered(hdev)) {
bool changed = false;
+ /* Setting limited discoverable when powered off is
+ * not a valid operation since it requires a timeout
+ * and so no need to check HCI_LIMITED_DISCOVERABLE.
+ */
if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
changed = true;
@@ -1040,16 +1210,20 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
- if (hdev->discov_timeout > 0) {
- cancel_delayed_work(&hdev->discov_off);
- hdev->discov_timeout = 0;
- }
+ /* If the current mode is the same, then just update the timeout
+ * value with the new value. And if only the timeout gets updated,
+ * then no need for any HCI transactions.
+ */
+ if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) &&
+ (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE,
+ &hdev->dev_flags)) {
+ cancel_delayed_work(&hdev->discov_off);
+ hdev->discov_timeout = timeout;
- if (cp->val && timeout > 0) {
- hdev->discov_timeout = timeout;
+ if (cp->val && hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- msecs_to_jiffies(hdev->discov_timeout * 1000));
+ to);
}
err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -1062,24 +1236,66 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
+ /* Cancel any potential discoverable timeout that might be
+ * still active and store new timeout value. The arming of
+ * the timeout happens in the complete handler.
+ */
+ cancel_delayed_work(&hdev->discov_off);
+ hdev->discov_timeout = timeout;
+
+ /* Limited discoverable mode */
+ if (cp->val == 0x02)
+ set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ else
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+
hci_req_init(&req, hdev);
+ /* The procedure for LE-only controllers is much simpler - just
+ * update the advertising data.
+ */
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ goto update_ad;
+
scan = SCAN_PAGE;
- if (cp->val)
+ if (cp->val) {
+ struct hci_cp_write_current_iac_lap hci_cp;
+
+ if (cp->val == 0x02) {
+ /* Limited discoverable mode */
+ hci_cp.num_iac = 2;
+ hci_cp.iac_lap[0] = 0x00; /* LIAC */
+ hci_cp.iac_lap[1] = 0x8b;
+ hci_cp.iac_lap[2] = 0x9e;
+ hci_cp.iac_lap[3] = 0x33; /* GIAC */
+ hci_cp.iac_lap[4] = 0x8b;
+ hci_cp.iac_lap[5] = 0x9e;
+ } else {
+ /* General discoverable mode */
+ hci_cp.num_iac = 1;
+ hci_cp.iac_lap[0] = 0x33; /* GIAC */
+ hci_cp.iac_lap[1] = 0x8b;
+ hci_cp.iac_lap[2] = 0x9e;
+ }
+
+ hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ (hci_cp.num_iac * 3) + 1, &hci_cp);
+
scan |= SCAN_INQUIRY;
- else
- cancel_delayed_work(&hdev->discov_off);
+ } else {
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ }
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
+
+update_ad:
+ update_adv_data(&req);
err = hci_req_run(&req, set_discoverable_complete);
if (err < 0)
mgmt_pending_remove(cmd);
- if (cp->val)
- hdev->discov_timeout = timeout;
-
failed:
hci_dev_unlock(hdev);
return err;
@@ -1091,6 +1307,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
struct hci_cp_write_page_scan_activity acp;
u8 type;
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return;
+
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
@@ -1146,10 +1365,7 @@ static void enable_advertising(struct hci_request *req)
cp.min_interval = __constant_cpu_to_le16(0x0800);
cp.max_interval = __constant_cpu_to_le16(0x0800);
cp.type = get_adv_type(hdev);
- if (bacmp(&hdev->bdaddr, BDADDR_ANY))
- cp.own_address_type = ADDR_LE_DEV_PUBLIC;
- else
- cp.own_address_type = ADDR_LE_DEV_RANDOM;
+ cp.own_address_type = hdev->own_addr_type;
cp.channel_map = 0x07;
hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
@@ -1202,6 +1418,32 @@ unlock:
hci_dev_unlock(hdev);
}
+static int set_connectable_update_settings(struct hci_dev *hdev,
+ struct sock *sk, u8 val)
+{
+ bool changed = false;
+ int err;
+
+ if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ changed = true;
+
+ if (val) {
+ set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ } else {
+ clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
+ if (err < 0)
+ return err;
+
+ if (changed)
+ return new_settings(hdev, sk);
+
+ return 0;
+}
+
static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
@@ -1225,25 +1467,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
- bool changed = false;
-
- if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- changed = true;
-
- if (cp->val) {
- set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
- } else {
- clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
- clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
- }
-
- err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
- if (err < 0)
- goto failed;
-
- if (changed)
- err = new_settings(hdev, sk);
-
+ err = set_connectable_update_settings(hdev, sk, cp->val);
goto failed;
}
@@ -1262,16 +1486,24 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
hci_req_init(&req, hdev);
- if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
- cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
-
+ /* If BR/EDR is not enabled and we disable advertising as a
+ * by-product of disabling connectable, we need to update the
+ * advertising flags.
+ */
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ if (!cp->val) {
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ }
+ update_adv_data(&req);
+ } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
if (cp->val) {
scan = SCAN_PAGE;
} else {
scan = 0;
if (test_bit(HCI_ISCAN, &hdev->flags) &&
- hdev->discov_timeout > 0)
+ hdev->discov_timeout > 0)
cancel_delayed_work(&hdev->discov_off);
}
@@ -1297,8 +1529,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
if (err < 0) {
mgmt_pending_remove(cmd);
if (err == -ENODATA)
- err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE,
- hdev);
+ err = set_connectable_update_settings(hdev, sk,
+ cp->val);
goto failed;
}
@@ -1556,6 +1788,24 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
if (match.sk)
sock_put(match.sk);
+
+ /* Make sure the controller has a good default for
+ * advertising data. Restrict the update to when LE
+ * has actually been enabled. During power on, the
+ * update in powered_update_hci will take care of it.
+ */
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ struct hci_request req;
+
+ hci_dev_lock(hdev);
+
+ hci_req_init(&req, hdev);
+ update_adv_data(&req);
+ update_scan_rsp_data(&req);
+ hci_req_run(&req, NULL);
+
+ hci_dev_unlock(hdev);
+ }
}
static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -1623,18 +1873,18 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
+ hci_req_init(&req, hdev);
+
memset(&hci_cp, 0, sizeof(hci_cp));
if (val) {
hci_cp.le = val;
hci_cp.simul = lmp_le_br_capable(hdev);
+ } else {
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+ disable_advertising(&req);
}
- hci_req_init(&req, hdev);
-
- if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) && !val)
- disable_advertising(&req);
-
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
&hci_cp);
@@ -2772,8 +3022,11 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
update_eir(&req);
}
+ /* The name is stored in the scan response data and so
+ * no need to udpate the advertising data here.
+ */
if (lmp_le_capable(hdev))
- hci_update_ad(&req);
+ update_scan_rsp_data(&req);
err = hci_req_run(&req, set_name_complete);
if (err < 0)
@@ -3038,10 +3291,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
param_cp.type = LE_SCAN_ACTIVE;
param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
- if (bacmp(&hdev->bdaddr, BDADDR_ANY))
- param_cp.own_address_type = ADDR_LE_DEV_PUBLIC;
- else
- param_cp.own_address_type = ADDR_LE_DEV_RANDOM;
+ param_cp.own_address_type = hdev->own_addr_type;
hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
&param_cp);
@@ -3725,7 +3975,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
- /* We need to flip the bit already here so that hci_update_ad
+ /* We need to flip the bit already here so that update_adv_data
* generates the correct flags.
*/
set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
@@ -3735,7 +3985,10 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
set_bredr_scan(&req);
- hci_update_ad(&req);
+ /* Since only the advertising data flags will change, there
+ * is no need to update the scan response data.
+ */
+ update_adv_data(&req);
err = hci_req_run(&req, set_bredr_complete);
if (err < 0)
@@ -4036,9 +4289,6 @@ static int powered_update_hci(struct hci_dev *hdev)
cp.simul != lmp_host_le_br_capable(hdev))
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
sizeof(cp), &cp);
-
- /* In case BR/EDR was toggled during the AUTO_OFF phase */
- hci_update_ad(&req);
}
if (lmp_le_capable(hdev)) {
@@ -4047,6 +4297,15 @@ static int powered_update_hci(struct hci_dev *hdev)
hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
&hdev->static_addr);
+ /* Make sure the controller has a good default for
+ * advertising data. This also applies to the case
+ * where BR/EDR was toggled during the AUTO_OFF phase.
+ */
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ update_adv_data(&req);
+ update_scan_rsp_data(&req);
+ }
+
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
enable_advertising(&req);
}
@@ -4121,59 +4380,91 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
mgmt_pending_remove(cmd);
}
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+void mgmt_discoverable_timeout(struct hci_dev *hdev)
{
- bool changed = false;
- int err = 0;
+ struct hci_request req;
+
+ hci_dev_lock(hdev);
+
+ /* When discoverable timeout triggers, then just make sure
+ * the limited discoverable flag is cleared. Even in the case
+ * of a timeout triggered from general discoverable, it is
+ * safe to unconditionally clear the flag.
+ */
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+
+ hci_req_init(&req, hdev);
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ u8 scan = SCAN_PAGE;
+ hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
+ sizeof(scan), &scan);
+ }
+ update_class(&req);
+ update_adv_data(&req);
+ hci_req_run(&req, NULL);
+
+ hdev->discov_timeout = 0;
+
+ new_settings(hdev, NULL);
+
+ hci_dev_unlock(hdev);
+}
+
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+{
+ bool changed;
/* Nothing needed here if there's a pending command since that
* commands request completion callback takes care of everything
* necessary.
*/
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
- return 0;
+ return;
if (discoverable) {
- if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- changed = true;
+ changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
} else {
- if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- changed = true;
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
}
- if (changed)
- err = new_settings(hdev, NULL);
+ if (changed) {
+ struct hci_request req;
- return err;
+ /* In case this change in discoverable was triggered by
+ * a disabling of connectable there could be a need to
+ * update the advertising flags.
+ */
+ hci_req_init(&req, hdev);
+ update_adv_data(&req);
+ hci_req_run(&req, NULL);
+
+ new_settings(hdev, NULL);
+ }
}
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
{
- bool changed = false;
- int err = 0;
+ bool changed;
/* Nothing needed here if there's a pending command since that
* commands request completion callback takes care of everything
* necessary.
*/
if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
- return 0;
+ return;
- if (connectable) {
- if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- changed = true;
- } else {
- if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- changed = true;
- }
+ if (connectable)
+ changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ else
+ changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
if (changed)
- err = new_settings(hdev, NULL);
-
- return err;
+ new_settings(hdev, NULL);
}
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
{
u8 mgmt_err = mgmt_status(status);
@@ -4184,12 +4475,10 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
if (scan & SCAN_INQUIRY)
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
cmd_status_rsp, &mgmt_err);
-
- return 0;
}
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
- bool persistent)
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+ bool persistent)
{
struct mgmt_ev_new_link_key ev;
@@ -4202,10 +4491,10 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE);
ev.key.pin_len = key->pin_len;
- return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
+ mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
{
struct mgmt_ev_new_long_term_key ev;
@@ -4224,8 +4513,18 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
memcpy(ev.key.rand, key->rand, sizeof(key->rand));
memcpy(ev.key.val, key->val, sizeof(key->val));
- return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev),
- NULL);
+ mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
+}
+
+static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
+ u8 data_len)
+{
+ eir[eir_len++] = sizeof(type) + data_len;
+ eir[eir_len++] = type;
+ memcpy(&eir[eir_len], data, data_len);
+ eir_len += data_len;
+
+ return eir_len;
}
void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -4345,7 +4644,7 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
@@ -4353,52 +4652,45 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
ev.addr.type = BDADDR_BREDR;
ev.secure = secure;
- return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
- NULL);
+ mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
- int err;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
if (!cmd)
- return -ENOENT;
+ return;
bacpy(&rp.addr.bdaddr, bdaddr);
rp.addr.type = BDADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
-
- return err;
}
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
- int err;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
if (!cmd)
- return -ENOENT;
+ return;
bacpy(&rp.addr.bdaddr, bdaddr);
rp.addr.type = BDADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
-
- return err;
}
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -4500,8 +4792,8 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u8 status)
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, u8 status)
{
struct mgmt_ev_auth_failed ev;
@@ -4509,40 +4801,36 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev.addr.type = link_to_bdaddr(link_type, addr_type);
ev.status = mgmt_status(status);
- return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
+ mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
{
struct cmd_lookup match = { NULL, hdev };
- bool changed = false;
- int err = 0;
+ bool changed;
if (status) {
u8 mgmt_err = mgmt_status(status);
mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
cmd_status_rsp, &mgmt_err);
- return 0;
+ return;
}
- if (test_bit(HCI_AUTH, &hdev->flags)) {
- if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
- changed = true;
- } else {
- if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
- changed = true;
- }
+ if (test_bit(HCI_AUTH, &hdev->flags))
+ changed = !test_and_set_bit(HCI_LINK_SECURITY,
+ &hdev->dev_flags);
+ else
+ changed = test_and_clear_bit(HCI_LINK_SECURITY,
+ &hdev->dev_flags);
mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
&match);
if (changed)
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
static void clear_eir(struct hci_request *req)
@@ -4560,12 +4848,11 @@ static void clear_eir(struct hci_request *req)
hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
{
struct cmd_lookup match = { NULL, hdev };
struct hci_request req;
bool changed = false;
- int err = 0;
if (status) {
u8 mgmt_err = mgmt_status(status);
@@ -4573,13 +4860,12 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
&hdev->dev_flags)) {
clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
- err = new_settings(hdev, NULL);
+ new_settings(hdev, NULL);
}
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp,
&mgmt_err);
-
- return err;
+ return;
}
if (enable) {
@@ -4596,7 +4882,7 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
if (changed)
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
@@ -4609,8 +4895,6 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
clear_eir(&req);
hci_req_run(&req, NULL);
-
- return err;
}
static void sk_lookup(struct pending_cmd *cmd, void *data)
@@ -4623,33 +4907,30 @@ static void sk_lookup(struct pending_cmd *cmd, void *data)
}
}
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
- u8 status)
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+ u8 status)
{
struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
- int err = 0;
mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match);
mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
if (!status)
- err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
- 3, NULL);
+ mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
+ NULL);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
{
struct mgmt_cp_set_local_name ev;
struct pending_cmd *cmd;
if (status)
- return 0;
+ return;
memset(&ev, 0, sizeof(ev));
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
@@ -4663,42 +4944,38 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
* HCI dev don't send any mgmt signals.
*/
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
- return 0;
+ return;
}
- return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
}
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
- u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+ u8 *randomizer, u8 status)
{
struct pending_cmd *cmd;
- int err;
BT_DBG("%s status %u", hdev->name, status);
cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
if (!cmd)
- return -ENOENT;
+ return;
if (status) {
- err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
- mgmt_status(status));
+ cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ mgmt_status(status));
} else {
struct mgmt_rp_read_local_oob_data rp;
memcpy(rp.hash, hash, sizeof(rp.hash));
memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
- err = cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
- sizeof(rp));
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ 0, &rp, sizeof(rp));
}
mgmt_pending_remove(cmd);
-
- return err;
}
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,