summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/bluetooth/hci.h2
-rw-r--r--include/net/bluetooth/hci_core.h1
-rw-r--r--include/net/bluetooth/mgmt.h6
-rw-r--r--net/bluetooth/mgmt.c49
4 files changed, 58 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 4fa08d7b997d..d7fd825ed2ce 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -978,6 +978,8 @@ struct hci_rp_le_read_local_features {
__u8 features[8];
} __packed;
+#define HCI_OP_LE_SET_RANDOM_ADDR 0x2005
+
#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007
struct hci_rp_le_read_adv_tx_power {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 26cc9f7858cd..e09c30577b3a 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -140,6 +140,7 @@ struct hci_dev {
__u8 bus;
__u8 dev_type;
bdaddr_t bdaddr;
+ bdaddr_t static_addr;
__u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
__u8 eir[HCI_MAX_EIR_LENGTH];
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 7347df800a2e..2ad433bb9a2e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -356,6 +356,12 @@ struct mgmt_cp_set_device_id {
#define MGMT_OP_SET_BREDR 0x002A
+#define MGMT_OP_SET_STATIC_ADDRESS 0x002B
+struct mgmt_cp_set_static_address {
+ bdaddr_t bdaddr;
+} __packed;
+#define MGMT_SET_STATIC_ADDRESS_SIZE 6
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 4ac31695946b..b87163238c10 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -76,6 +76,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_DEVICE_ID,
MGMT_OP_SET_ADVERTISING,
MGMT_OP_SET_BREDR,
+ MGMT_OP_SET_STATIC_ADDRESS,
};
static const u16 mgmt_events[] = {
@@ -3247,6 +3248,46 @@ unlock:
return err;
}
+static int set_static_address(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_static_address *cp = data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (hdev_is_powered(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_REJECTED);
+
+ if (bacmp(&cp->bdaddr, BDADDR_ANY)) {
+ if (!bacmp(&cp->bdaddr, BDADDR_NONE))
+ return cmd_status(sk, hdev->id,
+ MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ /* Two most significant bits shall be set */
+ if ((cp->bdaddr.b[5] & 0xc0) != 0xc0)
+ return cmd_status(sk, hdev->id,
+ MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_INVALID_PARAMS);
+ }
+
+ hci_dev_lock(hdev);
+
+ bacpy(&hdev->static_addr, &cp->bdaddr);
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0);
+
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
@@ -3576,6 +3617,7 @@ static const struct mgmt_handler {
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
{ set_advertising, false, MGMT_SETTING_SIZE },
{ set_bredr, false, MGMT_SETTING_SIZE },
+ { set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE },
};
@@ -3762,6 +3804,13 @@ static int powered_update_hci(struct hci_dev *hdev)
hci_update_ad(&req);
}
+ if (lmp_le_capable(hdev)) {
+ /* Set random address to static address if configured */
+ if (bacmp(&hdev->static_addr, BDADDR_ANY))
+ hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
+ &hdev->static_addr);
+ }
+
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
u8 adv = 0x01;