]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/bluetooth/mgmt.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/blueto...
[karo-tx-linux.git] / net / bluetooth / mgmt.c
index cb3af4e4f9593b5139834531840d6aa8a4cb3fc0..1ce549bae241809104194b8ed545a81c35821586 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <linux/kernel.h>
 #include <linux/uaccess.h>
+#include <linux/module.h>
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -1052,6 +1053,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
                                                                key->pin_len);
        }
 
+       cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
+
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
 
@@ -1565,29 +1568,14 @@ unlock:
        return err;
 }
 
-static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
-                                                       u16 len, int success)
+static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
+                                       u16 mgmt_op, u16 hci_op, __le32 passkey)
 {
-       struct mgmt_cp_user_confirm_reply *cp = (void *) data;
-       u16 mgmt_op, hci_op;
        struct pending_cmd *cmd;
        struct hci_dev *hdev;
+       struct hci_conn *conn;
        int err;
 
-       BT_DBG("");
-
-       if (success) {
-               mgmt_op = MGMT_OP_USER_CONFIRM_REPLY;
-               hci_op = HCI_OP_USER_CONFIRM_REPLY;
-       } else {
-               mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY;
-               hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY;
-       }
-
-       if (len != sizeof(*cp))
-               return cmd_status(sk, index, mgmt_op,
-                                               MGMT_STATUS_INVALID_PARAMS);
-
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, mgmt_op,
@@ -1596,27 +1584,122 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
        hci_dev_lock_bh(hdev);
 
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, index, mgmt_op, ENETDOWN);
-               goto failed;
+               err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
+               goto done;
+       }
+
+       /*
+        * Check for an existing ACL link, if present pair via
+        * HCI commands.
+        *
+        * If no ACL link is present, check for an LE link and if
+        * present, pair via the SMP engine.
+        *
+        * If neither ACL nor LE links are present, fail with error.
+        */
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
+       if (!conn) {
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
+               if (!conn) {
+                       err = cmd_status(sk, index, mgmt_op,
+                                               MGMT_STATUS_NOT_CONNECTED);
+                       goto done;
+               }
+
+               /* Continue with pairing via SMP */
+
+               err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_SUCCESS);
+               goto done;
        }
 
-       cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
+       cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
        if (!cmd) {
                err = -ENOMEM;
-               goto failed;
+               goto done;
        }
 
-       err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr);
+       /* Continue with pairing via HCI */
+       if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
+               struct hci_cp_user_passkey_reply cp;
+
+               bacpy(&cp.bdaddr, bdaddr);
+               cp.passkey = passkey;
+               err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
+       } else
+               err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
+
        if (err < 0)
                mgmt_pending_remove(cmd);
 
-failed:
+done:
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
 
        return err;
 }
 
+static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
+{
+       struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+
+       BT_DBG("");
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
+                                               MGMT_STATUS_INVALID_PARAMS);
+
+       return user_pairing_resp(sk, index, &cp->bdaddr,
+                       MGMT_OP_USER_CONFIRM_REPLY,
+                       HCI_OP_USER_CONFIRM_REPLY, 0);
+}
+
+static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
+                                                                       u16 len)
+{
+       struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+
+       BT_DBG("");
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
+                                               MGMT_STATUS_INVALID_PARAMS);
+
+       return user_pairing_resp(sk, index, &cp->bdaddr,
+                       MGMT_OP_USER_CONFIRM_NEG_REPLY,
+                       HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
+}
+
+static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
+{
+       struct mgmt_cp_user_passkey_reply *cp = (void *) data;
+
+       BT_DBG("");
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
+                                                                       EINVAL);
+
+       return user_pairing_resp(sk, index, &cp->bdaddr,
+                       MGMT_OP_USER_PASSKEY_REPLY,
+                       HCI_OP_USER_PASSKEY_REPLY, cp->passkey);
+}
+
+static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
+                                                                       u16 len)
+{
+       struct mgmt_cp_user_passkey_neg_reply *cp = (void *) data;
+
+       BT_DBG("");
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+                                                                       EINVAL);
+
+       return user_pairing_resp(sk, index, &cp->bdaddr,
+                       MGMT_OP_USER_PASSKEY_NEG_REPLY,
+                       HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
+}
+
 static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
                                                                u16 len)
 {
@@ -1777,14 +1860,20 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
        return err;
 }
 
-static int start_discovery(struct sock *sk, u16 index)
+static int start_discovery(struct sock *sk, u16 index,
+                                               unsigned char *data, u16 len)
 {
+       struct mgmt_cp_start_discovery *cp = (void *) data;
        struct pending_cmd *cmd;
        struct hci_dev *hdev;
        int err;
 
        BT_DBG("hci%u", index);
 
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
+                                               MGMT_STATUS_INVALID_PARAMS);
+
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
@@ -2062,10 +2151,18 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                err = pair_device(sk, index, buf + sizeof(*hdr), len);
                break;
        case MGMT_OP_USER_CONFIRM_REPLY:
-               err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1);
+               err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len);
                break;
        case MGMT_OP_USER_CONFIRM_NEG_REPLY:
-               err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
+               err = user_confirm_neg_reply(sk, index, buf + sizeof(*hdr),
+                                                                       len);
+               break;
+       case MGMT_OP_USER_PASSKEY_REPLY:
+               err = user_passkey_reply(sk, index, buf + sizeof(*hdr), len);
+               break;
+       case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+               err = user_passkey_neg_reply(sk, index, buf + sizeof(*hdr),
+                                                                       len);
                break;
        case MGMT_OP_SET_LOCAL_NAME:
                err = set_local_name(sk, index, buf + sizeof(*hdr), len);
@@ -2081,7 +2178,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                                                                        len);
                break;
        case MGMT_OP_START_DISCOVERY:
-               err = start_discovery(sk, index);
+               err = start_discovery(sk, index, buf + sizeof(*hdr), len);
                break;
        case MGMT_OP_STOP_DISCOVERY:
                err = stop_discovery(sk, index);
@@ -2427,7 +2524,19 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                                        NULL);
 }
 
-static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+       struct mgmt_ev_user_passkey_request ev;
+
+       BT_DBG("%s", hdev->name);
+
+       bacpy(&ev.bdaddr, bdaddr);
+
+       return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
+                                                                       NULL);
+}
+
+static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                        u8 status, u8 opcode)
 {
        struct pending_cmd *cmd;
@@ -2450,17 +2559,31 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                                u8 status)
 {
-       return confirm_reply_complete(hdev, bdaddr, mgmt_status(status),
+       return user_pairing_resp_complete(hdev, bdaddr, status,
                                                MGMT_OP_USER_CONFIRM_REPLY);
 }
 
 int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
                                                bdaddr_t *bdaddr, u8 status)
 {
-       return confirm_reply_complete(hdev, bdaddr, mgmt_status(status),
+       return user_pairing_resp_complete(hdev, bdaddr, status,
                                        MGMT_OP_USER_CONFIRM_NEG_REPLY);
 }
 
+int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status)
+{
+       return user_pairing_resp_complete(hdev, bdaddr, status,
+                                               MGMT_OP_USER_PASSKEY_REPLY);
+}
+
+int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
+                                               bdaddr_t *bdaddr, u8 status)
+{
+       return user_pairing_resp_complete(hdev, bdaddr, status,
+                                       MGMT_OP_USER_PASSKEY_NEG_REPLY);
+}
+
 int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
 {
        struct mgmt_ev_auth_failed ev;