|
@@ -242,6 +242,63 @@ static int read_index_list(struct sock *sk)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static u32 get_supported_settings(struct hci_dev *hdev)
|
|
|
+{
|
|
|
+ u32 settings = 0;
|
|
|
+
|
|
|
+ settings |= MGMT_SETTING_POWERED;
|
|
|
+ settings |= MGMT_SETTING_CONNECTABLE;
|
|
|
+ settings |= MGMT_SETTING_FAST_CONNECTABLE;
|
|
|
+ settings |= MGMT_SETTING_DISCOVERABLE;
|
|
|
+ settings |= MGMT_SETTING_PAIRABLE;
|
|
|
+
|
|
|
+ if (hdev->features[6] & LMP_SIMPLE_PAIR)
|
|
|
+ settings |= MGMT_SETTING_SSP;
|
|
|
+
|
|
|
+ if (!(hdev->features[4] & LMP_NO_BREDR)) {
|
|
|
+ settings |= MGMT_SETTING_BREDR;
|
|
|
+ settings |= MGMT_SETTING_LINK_SECURITY;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hdev->features[4] & LMP_LE)
|
|
|
+ settings |= MGMT_SETTING_LE;
|
|
|
+
|
|
|
+ return settings;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 get_current_settings(struct hci_dev *hdev)
|
|
|
+{
|
|
|
+ u32 settings = 0;
|
|
|
+
|
|
|
+ if (test_bit(HCI_UP, &hdev->flags))
|
|
|
+ settings |= MGMT_SETTING_POWERED;
|
|
|
+ else
|
|
|
+ return settings;
|
|
|
+
|
|
|
+ if (test_bit(HCI_PSCAN, &hdev->flags))
|
|
|
+ settings |= MGMT_SETTING_CONNECTABLE;
|
|
|
+
|
|
|
+ if (test_bit(HCI_ISCAN, &hdev->flags))
|
|
|
+ settings |= MGMT_SETTING_DISCOVERABLE;
|
|
|
+
|
|
|
+ if (test_bit(HCI_PAIRABLE, &hdev->flags))
|
|
|
+ settings |= MGMT_SETTING_PAIRABLE;
|
|
|
+
|
|
|
+ if (!(hdev->features[4] & LMP_NO_BREDR))
|
|
|
+ settings |= MGMT_SETTING_BREDR;
|
|
|
+
|
|
|
+ if (hdev->extfeatures[0] & LMP_HOST_LE)
|
|
|
+ settings |= MGMT_SETTING_LE;
|
|
|
+
|
|
|
+ if (test_bit(HCI_AUTH, &hdev->flags))
|
|
|
+ settings |= MGMT_SETTING_LINK_SECURITY;
|
|
|
+
|
|
|
+ if (hdev->ssp_mode > 0)
|
|
|
+ settings |= MGMT_SETTING_SSP;
|
|
|
+
|
|
|
+ return settings;
|
|
|
+}
|
|
|
+
|
|
|
static int read_controller_info(struct sock *sk, u16 index)
|
|
|
{
|
|
|
struct mgmt_rp_read_info rp;
|
|
@@ -263,26 +320,16 @@ static int read_controller_info(struct sock *sk, u16 index)
|
|
|
|
|
|
memset(&rp, 0, sizeof(rp));
|
|
|
|
|
|
- rp.type = hdev->dev_type;
|
|
|
+ bacpy(&rp.bdaddr, &hdev->bdaddr);
|
|
|
|
|
|
- rp.powered = test_bit(HCI_UP, &hdev->flags);
|
|
|
- rp.connectable = test_bit(HCI_PSCAN, &hdev->flags);
|
|
|
- rp.discoverable = test_bit(HCI_ISCAN, &hdev->flags);
|
|
|
- rp.pairable = test_bit(HCI_PSCAN, &hdev->flags);
|
|
|
+ rp.version = hdev->hci_ver;
|
|
|
|
|
|
- if (test_bit(HCI_AUTH, &hdev->flags))
|
|
|
- rp.sec_mode = 3;
|
|
|
- else if (hdev->ssp_mode > 0)
|
|
|
- rp.sec_mode = 4;
|
|
|
- else
|
|
|
- rp.sec_mode = 2;
|
|
|
+ put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
|
|
|
+
|
|
|
+ rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
|
|
|
+ rp.current_settings = cpu_to_le32(get_current_settings(hdev));
|
|
|
|
|
|
- bacpy(&rp.bdaddr, &hdev->bdaddr);
|
|
|
- memcpy(rp.features, hdev->features, 8);
|
|
|
memcpy(rp.dev_class, hdev->dev_class, 3);
|
|
|
- put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
|
|
|
- rp.hci_ver = hdev->hci_ver;
|
|
|
- put_unaligned_le16(hdev->hci_rev, &rp.hci_rev);
|
|
|
|
|
|
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
|
|
|
|
|
@@ -365,13 +412,11 @@ static void mgmt_pending_remove(struct pending_cmd *cmd)
|
|
|
mgmt_pending_free(cmd);
|
|
|
}
|
|
|
|
|
|
-static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
|
|
|
+static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
|
|
|
{
|
|
|
- struct mgmt_mode rp;
|
|
|
+ __le32 settings = cpu_to_le32(get_current_settings(hdev));
|
|
|
|
|
|
- rp.val = val;
|
|
|
-
|
|
|
- return cmd_complete(sk, index, opcode, &rp, sizeof(rp));
|
|
|
+ return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings));
|
|
|
}
|
|
|
|
|
|
static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|
@@ -398,7 +443,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|
|
|
|
|
up = test_bit(HCI_UP, &hdev->flags);
|
|
|
if ((cp->val && up) || (!cp->val && !up)) {
|
|
|
- err = send_mode_rsp(sk, index, MGMT_OP_SET_POWERED, cp->val);
|
|
|
+ err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
|
|
|
goto failed;
|
|
|
}
|
|
|
|
|
@@ -466,8 +511,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
|
|
|
|
|
|
if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
|
|
|
test_bit(HCI_PSCAN, &hdev->flags)) {
|
|
|
- err = send_mode_rsp(sk, index, MGMT_OP_SET_DISCOVERABLE,
|
|
|
- cp->val);
|
|
|
+ err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
|
|
|
goto failed;
|
|
|
}
|
|
|
|
|
@@ -536,8 +580,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
|
|
|
}
|
|
|
|
|
|
if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
|
|
|
- err = send_mode_rsp(sk, index, MGMT_OP_SET_CONNECTABLE,
|
|
|
- cp->val);
|
|
|
+ err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
|
|
|
goto failed;
|
|
|
}
|
|
|
|
|
@@ -595,8 +638,9 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
|
|
|
static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
|
|
|
u16 len)
|
|
|
{
|
|
|
- struct mgmt_mode *cp, ev;
|
|
|
+ struct mgmt_mode *cp;
|
|
|
struct hci_dev *hdev;
|
|
|
+ __le32 ev;
|
|
|
int err;
|
|
|
|
|
|
cp = (void *) data;
|
|
@@ -619,13 +663,13 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
|
|
|
else
|
|
|
clear_bit(HCI_PAIRABLE, &hdev->flags);
|
|
|
|
|
|
- err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, index, cp->val);
|
|
|
+ err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
|
|
|
if (err < 0)
|
|
|
goto failed;
|
|
|
|
|
|
- ev.val = cp->val;
|
|
|
+ ev = cpu_to_le32(get_current_settings(hdev));
|
|
|
|
|
|
- err = mgmt_event(MGMT_EV_PAIRABLE, hdev, &ev, sizeof(ev), sk);
|
|
|
+ err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
|
|
|
|
|
|
failed:
|
|
|
hci_dev_unlock(hdev);
|
|
@@ -2234,17 +2278,14 @@ int mgmt_index_removed(struct hci_dev *hdev)
|
|
|
struct cmd_lookup {
|
|
|
u8 val;
|
|
|
struct sock *sk;
|
|
|
+ struct hci_dev *hdev;
|
|
|
};
|
|
|
|
|
|
-static void mode_rsp(struct pending_cmd *cmd, void *data)
|
|
|
+static void settings_rsp(struct pending_cmd *cmd, void *data)
|
|
|
{
|
|
|
- struct mgmt_mode *cp = cmd->param;
|
|
|
struct cmd_lookup *match = data;
|
|
|
|
|
|
- if (cp->val != match->val)
|
|
|
- return;
|
|
|
-
|
|
|
- send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val);
|
|
|
+ send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
|
|
|
|
|
|
list_del(&cmd->list);
|
|
|
|
|
@@ -2258,20 +2299,21 @@ static void mode_rsp(struct pending_cmd *cmd, void *data)
|
|
|
|
|
|
int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|
|
{
|
|
|
- struct mgmt_mode ev;
|
|
|
- struct cmd_lookup match = { powered, NULL };
|
|
|
+ struct cmd_lookup match = { powered, NULL, hdev };
|
|
|
+ __le32 ev;
|
|
|
int ret;
|
|
|
|
|
|
- mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, mode_rsp, &match);
|
|
|
+ mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
|
|
|
|
|
|
if (!powered) {
|
|
|
u8 status = ENETDOWN;
|
|
|
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
|
|
|
}
|
|
|
|
|
|
- ev.val = powered;
|
|
|
+ ev = cpu_to_le32(get_current_settings(hdev));
|
|
|
|
|
|
- ret = mgmt_event(MGMT_EV_POWERED, hdev, &ev, sizeof(ev), match.sk);
|
|
|
+ ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
|
|
|
+ match.sk);
|
|
|
|
|
|
if (match.sk)
|
|
|
sock_put(match.sk);
|
|
@@ -2281,17 +2323,16 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|
|
|
|
|
int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
|
|
|
{
|
|
|
- struct mgmt_mode ev;
|
|
|
- struct cmd_lookup match = { discoverable, NULL };
|
|
|
+ struct cmd_lookup match = { discoverable, NULL, hdev };
|
|
|
+ __le32 ev;
|
|
|
int ret;
|
|
|
|
|
|
- mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mode_rsp, &match);
|
|
|
+ mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
|
|
|
|
|
|
- ev.val = discoverable;
|
|
|
+ ev = cpu_to_le32(get_current_settings(hdev));
|
|
|
|
|
|
- ret = mgmt_event(MGMT_EV_DISCOVERABLE, hdev, &ev, sizeof(ev),
|
|
|
+ ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
|
|
|
match.sk);
|
|
|
-
|
|
|
if (match.sk)
|
|
|
sock_put(match.sk);
|
|
|
|
|
@@ -2300,15 +2341,16 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
|
|
|
|
|
|
int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
|
|
|
{
|
|
|
- struct mgmt_mode ev;
|
|
|
- struct cmd_lookup match = { connectable, NULL };
|
|
|
+ __le32 ev;
|
|
|
+ struct cmd_lookup match = { connectable, NULL, hdev };
|
|
|
int ret;
|
|
|
|
|
|
- mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mode_rsp, &match);
|
|
|
+ mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
|
|
|
+ &match);
|
|
|
|
|
|
- ev.val = connectable;
|
|
|
+ ev = cpu_to_le32(get_current_settings(hdev));
|
|
|
|
|
|
- ret = mgmt_event(MGMT_EV_CONNECTABLE, hdev, &ev, sizeof(ev), match.sk);
|
|
|
+ ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
|
|
|
|
|
|
if (match.sk)
|
|
|
sock_put(match.sk);
|